From 6046625012beea790b22151031f8ff212948416d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 03:20:09 +0000 Subject: [PATCH 0001/2357] build(deps): bump follow-redirects in /tests/integration-tests Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] --- tests/integration-tests/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration-tests/yarn.lock b/tests/integration-tests/yarn.lock index 5fedc66202d..5632f40d187 100644 --- a/tests/integration-tests/yarn.lock +++ b/tests/integration-tests/yarn.lock @@ -5092,9 +5092,9 @@ flatmap@0.0.3: integrity sha1-Hxik2TgVLUlZZfnJWNkjqy3WabQ= follow-redirects@^1.12.1, follow-redirects@^1.14.0: - version "1.14.7" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" - integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + version "1.14.8" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" + integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== for-each@^0.3.3: version "0.3.3" From 9e9c6e85b0d31a75c3357105bd2f6350d87bbd82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Feb 2022 21:11:14 +0000 Subject: [PATCH 0002/2357] build(deps): bump pretty_assertions from 1.0.0 to 1.1.0 Bumps [pretty_assertions](https://github.com/colin-kiegel/rust-pretty-assertions) from 1.0.0 to 1.1.0. - [Release notes](https://github.com/colin-kiegel/rust-pretty-assertions/releases) - [Changelog](https://github.com/colin-kiegel/rust-pretty-assertions/blob/main/CHANGELOG.md) - [Commits](https://github.com/colin-kiegel/rust-pretty-assertions/compare/v1.0.0...v1.1.0) --- updated-dependencies: - dependency-name: pretty_assertions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- core/Cargo.toml | 2 +- graphql/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc62bfad2ab..847f0e43e76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1543,7 +1543,7 @@ dependencies = [ "hex", "lazy_static", "lru_time_cache", - "pretty_assertions 1.0.0", + "pretty_assertions 1.1.0", "semver 1.0.4", "serde", "serde_json", @@ -1569,7 +1569,7 @@ dependencies = [ "lazy_static", "once_cell", "parking_lot 0.11.2", - "pretty_assertions 1.0.0", + "pretty_assertions 1.1.0", "stable-hash", "test-store", ] @@ -3084,9 +3084,9 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0cfe1b2403f172ba0f234e500906ee0a3e493fb81092dac23ebefe129301cc" +checksum = "76d5b548b725018ab5496482b45cb8bef21e9fed1858a6d674e3a8a0f0bb5d50" dependencies = [ "ansi_term", "ctor", diff --git a/core/Cargo.toml b/core/Cargo.toml index b9cf9e85fa2..5a6332dc92c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -31,5 +31,5 @@ walkdir = "2.3.2" test-store = { path = "../store/test-store" } hex = "0.4.3" graphql-parser = "0.4.0" -pretty_assertions = "1.0.0" +pretty_assertions = "1.1.0" anyhow = "1.0" diff --git a/graphql/Cargo.toml b/graphql/Cargo.toml index 7e1e95784da..71cd06af2fe 100644 --- a/graphql/Cargo.toml +++ b/graphql/Cargo.toml @@ -19,6 +19,6 @@ parking_lot = "0.11" anyhow = "1.0" [dev-dependencies] -pretty_assertions = "1.0.0" +pretty_assertions = "1.1.0" test-store = { path = "../store/test-store" } graph-chain-ethereum = { path = "../chain/ethereum" } From 2106f1c701a0b7f31320b855282f4508c165b5cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Pace?= Date: Wed, 16 Feb 2022 16:59:30 -0300 Subject: [PATCH 0003/2357] Release 0.25.1 (#3253) --- Cargo.lock | 36 ++++++++++++++++++------------------ NEWS.md | 9 +++++++++ chain/ethereum/Cargo.toml | 2 +- chain/near/Cargo.toml | 2 +- core/Cargo.toml | 2 +- graph/Cargo.toml | 2 +- graphql/Cargo.toml | 2 +- mock/Cargo.toml | 2 +- node/Cargo.toml | 2 +- runtime/derive/Cargo.toml | 2 +- runtime/test/Cargo.toml | 2 +- runtime/wasm/Cargo.toml | 2 +- server/http/Cargo.toml | 2 +- server/index-node/Cargo.toml | 2 +- server/json-rpc/Cargo.toml | 2 +- server/metrics/Cargo.toml | 2 +- server/websocket/Cargo.toml | 2 +- store/postgres/Cargo.toml | 2 +- store/test-store/Cargo.toml | 2 +- tests/Cargo.toml | 2 +- 20 files changed, 45 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 847f0e43e76..9a6b4af8255 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1416,7 +1416,7 @@ dependencies = [ [[package]] name = "graph" -version = "0.25.0" +version = "0.25.1" dependencies = [ "Inflector", "anyhow", @@ -1479,7 +1479,7 @@ dependencies = [ [[package]] name = "graph-chain-ethereum" -version = "0.25.0" +version = "0.25.1" dependencies = [ "anyhow", "dirs-next", @@ -1505,7 +1505,7 @@ dependencies = [ [[package]] name = "graph-chain-near" -version = "0.25.0" +version = "0.25.1" dependencies = [ "base64 0.13.0", "diesel", @@ -1524,7 +1524,7 @@ dependencies = [ [[package]] name = "graph-core" -version = "0.25.0" +version = "0.25.1" dependencies = [ "anyhow", "async-stream", @@ -1554,7 +1554,7 @@ dependencies = [ [[package]] name = "graph-graphql" -version = "0.25.0" +version = "0.25.1" dependencies = [ "Inflector", "anyhow", @@ -1576,14 +1576,14 @@ dependencies = [ [[package]] name = "graph-mock" -version = "0.25.0" +version = "0.25.1" dependencies = [ "graph", ] [[package]] name = "graph-node" -version = "0.25.0" +version = "0.25.1" dependencies = [ "assert_cli", "clap", @@ -1620,7 +1620,7 @@ dependencies = [ [[package]] name = "graph-runtime-derive" -version = "0.25.0" +version = "0.25.1" dependencies = [ "anyhow", "quote", @@ -1629,7 +1629,7 @@ dependencies = [ [[package]] name = "graph-runtime-test" -version = "0.25.0" +version = "0.25.1" dependencies = [ "graph", "graph-chain-ethereum", @@ -1643,7 +1643,7 @@ dependencies = [ [[package]] name = "graph-runtime-wasm" -version = "0.25.0" +version = "0.25.1" dependencies = [ "anyhow", "async-trait", @@ -1670,7 +1670,7 @@ dependencies = [ [[package]] name = "graph-server-http" -version = "0.25.0" +version = "0.25.1" dependencies = [ "futures 0.1.31", "graph", @@ -1684,7 +1684,7 @@ dependencies = [ [[package]] name = "graph-server-index-node" -version = "0.25.0" +version = "0.25.1" dependencies = [ "either", "futures 0.3.16", @@ -1700,7 +1700,7 @@ dependencies = [ [[package]] name = "graph-server-json-rpc" -version = "0.25.0" +version = "0.25.1" dependencies = [ "graph", "jsonrpc-http-server", @@ -1710,7 +1710,7 @@ dependencies = [ [[package]] name = "graph-server-metrics" -version = "0.25.0" +version = "0.25.1" dependencies = [ "futures 0.1.31", "graph", @@ -1722,7 +1722,7 @@ dependencies = [ [[package]] name = "graph-server-websocket" -version = "0.25.0" +version = "0.25.1" dependencies = [ "anyhow", "futures 0.1.31", @@ -1738,7 +1738,7 @@ dependencies = [ [[package]] name = "graph-store-postgres" -version = "0.25.0" +version = "0.25.1" dependencies = [ "Inflector", "anyhow", @@ -1777,7 +1777,7 @@ dependencies = [ [[package]] name = "graph-tests" -version = "0.25.0" +version = "0.25.1" dependencies = [ "anyhow", "bollard", @@ -4326,7 +4326,7 @@ dependencies = [ [[package]] name = "test-store" -version = "0.25.0" +version = "0.25.1" dependencies = [ "diesel", "graph", diff --git a/NEWS.md b/NEWS.md index 85256917324..b433e25cc86 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,15 @@ where whitespace characters were part of the terms. - Adds support for Solidity Custom Errors (issue #2577) +## 0.25.1 + +This release only adds two fixes: + +- The first is to address an issue with decoding the input of some calls [#3194](https://github.com/graphprotocol/graph-node/issues/3194) where subgraphs that would try to index contracts related to those would fail. Now they can advance normally. +- The second one is to fix a non-determinism issue with the retry mechanism for errors. Whenever a non-deterministic error happened, we would keep retrying to process the block, however we should've clear the `EntityCache` on each run so that the error entity changes don't get transacted/saved in the database in the next run. This could make the POI generation non-deterministic for subgraphs that failed and retried for non-deterministic reasons, adding a new entry to the database for the POI. + +We strongly recommend updating to this version as quickly as possible. + ## 0.25.0 ### Api Version 0.0.6 diff --git a/chain/ethereum/Cargo.toml b/chain/ethereum/Cargo.toml index 8f0aa30b255..bb582909a8c 100644 --- a/chain/ethereum/Cargo.toml +++ b/chain/ethereum/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-chain-ethereum" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/chain/near/Cargo.toml b/chain/near/Cargo.toml index 1acdf23ae48..84752960965 100644 --- a/chain/near/Cargo.toml +++ b/chain/near/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-chain-near" -version = "0.25.0" +version = "0.25.1" edition = "2018" [build-dependencies] diff --git a/core/Cargo.toml b/core/Cargo.toml index 5a6332dc92c..6767542677e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-core" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 26eca2466e0..6f0d3435bab 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/graphql/Cargo.toml b/graphql/Cargo.toml index 71cd06af2fe..263dc3763be 100644 --- a/graphql/Cargo.toml +++ b/graphql/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-graphql" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/mock/Cargo.toml b/mock/Cargo.toml index 8f3703f7d46..fc49af46822 100644 --- a/mock/Cargo.toml +++ b/mock/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-mock" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/node/Cargo.toml b/node/Cargo.toml index bb664e7a953..e1ae82fc5cd 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-node" -version = "0.25.0" +version = "0.25.1" edition = "2018" default-run = "graph-node" diff --git a/runtime/derive/Cargo.toml b/runtime/derive/Cargo.toml index 99506b05c8e..4fbab71561b 100644 --- a/runtime/derive/Cargo.toml +++ b/runtime/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-runtime-derive" -version = "0.25.0" +version = "0.25.1" edition = "2018" [lib] diff --git a/runtime/test/Cargo.toml b/runtime/test/Cargo.toml index d5569fa6d48..7b4c1f0360a 100644 --- a/runtime/test/Cargo.toml +++ b/runtime/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-runtime-test" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/runtime/wasm/Cargo.toml b/runtime/wasm/Cargo.toml index aab0155a3ca..05b75bf8b23 100644 --- a/runtime/wasm/Cargo.toml +++ b/runtime/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-runtime-wasm" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/server/http/Cargo.toml b/server/http/Cargo.toml index 9f7987ec9ab..45cf9fa43c8 100644 --- a/server/http/Cargo.toml +++ b/server/http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-server-http" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/server/index-node/Cargo.toml b/server/index-node/Cargo.toml index 89aee3b0b4b..74028929009 100644 --- a/server/index-node/Cargo.toml +++ b/server/index-node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-server-index-node" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/server/json-rpc/Cargo.toml b/server/json-rpc/Cargo.toml index 74d025c3ac5..45250ac8ac9 100644 --- a/server/json-rpc/Cargo.toml +++ b/server/json-rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-server-json-rpc" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/server/metrics/Cargo.toml b/server/metrics/Cargo.toml index d9573455c16..ee729ffb9af 100644 --- a/server/metrics/Cargo.toml +++ b/server/metrics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-server-metrics" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/server/websocket/Cargo.toml b/server/websocket/Cargo.toml index 14bbdcaba6e..97a6cee8b40 100644 --- a/server/websocket/Cargo.toml +++ b/server/websocket/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-server-websocket" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/store/postgres/Cargo.toml b/store/postgres/Cargo.toml index 039a1e5c6e0..59b9aa7de89 100644 --- a/store/postgres/Cargo.toml +++ b/store/postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-store-postgres" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dependencies] diff --git a/store/test-store/Cargo.toml b/store/test-store/Cargo.toml index fbd4fdb29f2..e5009e88f6b 100644 --- a/store/test-store/Cargo.toml +++ b/store/test-store/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test-store" -version = "0.25.0" +version = "0.25.1" authors = ["Leonardo Yvens "] edition = "2018" description = "Provides static store instance for tests." diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 3e5a27a8c91..045943e30a5 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-tests" -version = "0.25.0" +version = "0.25.1" edition = "2018" [dev-dependencies] From debf653f222a5f46441071114fa5830cddf77d76 Mon Sep 17 00:00:00 2001 From: tilacog Date: Wed, 16 Feb 2022 16:07:13 -0300 Subject: [PATCH 0004/2357] chain/ethereum: Return early if there are no transaction hashes This early return prevents an unecessary iteration over the block transactions, where each would be checked for containment against an empty vector. --- chain/ethereum/src/ethereum_adapter.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/chain/ethereum/src/ethereum_adapter.rs b/chain/ethereum/src/ethereum_adapter.rs index 4062e5a0628..b1fe5b05060 100644 --- a/chain/ethereum/src/ethereum_adapter.rs +++ b/chain/ethereum/src/ethereum_adapter.rs @@ -1664,6 +1664,11 @@ async fn filter_call_triggers_from_unsuccessful_transactions( "failed to obtain transaction hash from call triggers" ))?; + // Return early if there are no transaction hashes + if transaction_hashes.is_empty() { + return Ok(block); + } + // And obtain all Transaction values for the calls in this block. let transactions: Vec<&Transaction> = { match &block.block { From e8dcdff09f5e2621457d4da20ff599ffb02b2266 Mon Sep 17 00:00:00 2001 From: tilacog Date: Wed, 16 Feb 2022 20:54:06 -0300 Subject: [PATCH 0005/2357] chain/ethereum: Remove redundant check --- chain/ethereum/src/ethereum_adapter.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/chain/ethereum/src/ethereum_adapter.rs b/chain/ethereum/src/ethereum_adapter.rs index b1fe5b05060..a2b39313e10 100644 --- a/chain/ethereum/src/ethereum_adapter.rs +++ b/chain/ethereum/src/ethereum_adapter.rs @@ -1690,11 +1690,6 @@ async fn filter_call_triggers_from_unsuccessful_transactions( bail!("failed to find transactions in block for the given call triggers") } - // Return early if there are no transactions to inspect - if transactions.is_empty() { - return Ok(block); - } - // We'll also need the receipts for those transactions. In this step we collect all receipts // we have in store for the current block. let mut receipts = chain_store From 668fc2f2475081206f654259f840d7bc63464ba4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Feb 2022 15:29:47 +0000 Subject: [PATCH 0006/2357] build(deps): bump simple-get in /tests/integration-tests Bumps [simple-get](https://github.com/feross/simple-get) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/feross/simple-get/releases) - [Commits](https://github.com/feross/simple-get/compare/v2.8.1...v2.8.2) --- updated-dependencies: - dependency-name: simple-get dependency-type: indirect ... Signed-off-by: dependabot[bot] --- tests/integration-tests/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration-tests/yarn.lock b/tests/integration-tests/yarn.lock index 5632f40d187..052332b6cbf 100644 --- a/tests/integration-tests/yarn.lock +++ b/tests/integration-tests/yarn.lock @@ -10060,9 +10060,9 @@ simple-concat@^1.0.0: integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== simple-get@^2.7.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" - integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== + version "2.8.2" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.2.tgz#5708fb0919d440657326cd5fe7d2599d07705019" + integrity sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw== dependencies: decompress-response "^3.3.0" once "^1.3.1" From 89cd83201a40813a9c3e48ba21381fbdc99f437c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Feb 2022 14:54:06 +0000 Subject: [PATCH 0007/2357] build(deps): bump semver from 1.0.4 to 1.0.5 Bumps [semver](https://github.com/dtolnay/semver) from 1.0.4 to 1.0.5. - [Release notes](https://github.com/dtolnay/semver/releases) - [Commits](https://github.com/dtolnay/semver/compare/1.0.4...1.0.5) --- updated-dependencies: - dependency-name: semver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 16 ++++++++-------- chain/ethereum/Cargo.toml | 2 +- core/Cargo.toml | 2 +- graph/Cargo.toml | 2 +- runtime/wasm/Cargo.toml | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9a6b4af8255..3231bf77a0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1450,7 +1450,7 @@ dependencies = [ "prost-types", "rand 0.6.5", "reqwest", - "semver 1.0.4", + "semver 1.0.5", "serde", "serde_derive", "serde_json", @@ -1496,7 +1496,7 @@ dependencies = [ "mockall", "prost", "prost-types", - "semver 1.0.4", + "semver 1.0.5", "serde", "test-store", "tiny-keccak 1.5.0", @@ -1544,7 +1544,7 @@ dependencies = [ "lazy_static", "lru_time_cache", "pretty_assertions 1.1.0", - "semver 1.0.4", + "semver 1.0.5", "serde", "serde_json", "serde_yaml", @@ -1636,7 +1636,7 @@ dependencies = [ "graph-core", "graph-mock", "graph-runtime-wasm", - "semver 1.0.4", + "semver 1.0.5", "test-store", "wasmtime", ] @@ -1661,7 +1661,7 @@ dependencies = [ "never", "parity-wasm", "pwasm-utils", - "semver 1.0.4", + "semver 1.0.5", "strum", "strum_macros", "uuid 0.8.2", @@ -3687,7 +3687,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.4", + "semver 1.0.5", ] [[package]] @@ -3843,9 +3843,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" +checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" dependencies = [ "serde", ] diff --git a/chain/ethereum/Cargo.toml b/chain/ethereum/Cargo.toml index bb582909a8c..22f64655d07 100644 --- a/chain/ethereum/Cargo.toml +++ b/chain/ethereum/Cargo.toml @@ -17,7 +17,7 @@ dirs-next = "2.0" anyhow = "1.0" tiny-keccak = "1.5.0" hex = "0.4.3" -semver = "1.0.3" +semver = "1.0.5" itertools = "0.10.3" diff --git a/core/Cargo.toml b/core/Cargo.toml index 6767542677e..dba675b22d0 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,7 +17,7 @@ graph-chain-ethereum = { path = "../chain/ethereum" } graph-chain-near = { path = "../chain/near" } lazy_static = "1.2.0" lru_time_cache = "0.11" -semver = "1.0.3" +semver = "1.0.5" serde = "1.0" serde_json = "1.0" serde_yaml = "0.8" diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 6f0d3435bab..1745371384a 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -27,7 +27,7 @@ num-bigint = { version = "^0.2.6", features = ["serde"] } num_cpus = "1.13.1" num-traits = "0.2.14" rand = "0.6.1" -semver = {version = "1.0.3", features = ["serde"]} +semver = {version = "1.0.5", features = ["serde"]} serde = { version = "1.0.126", features = ["rc"] } serde_derive = "1.0.125" serde_json = { version = "1.0", features = ["arbitrary_precision"] } diff --git a/runtime/wasm/Cargo.toml b/runtime/wasm/Cargo.toml index 05b75bf8b23..5290d97c6e5 100644 --- a/runtime/wasm/Cargo.toml +++ b/runtime/wasm/Cargo.toml @@ -13,7 +13,7 @@ graph = { path = "../../graph" } graph-graphql = { path = "../../graphql" } bs58 = "0.4.0" graph-runtime-derive = { path = "../derive" } -semver = "1.0.3" +semver = "1.0.5" lazy_static = "1.4" uuid = { version = "0.8.1", features = ["v4"] } strum = "0.21.0" From 675de4ad55940e4b389a4b9b31c986f6889d6f8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 14:45:06 +0000 Subject: [PATCH 0008/2357] build(deps): bump url-parse in /tests/integration-tests Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.3 to 1.5.7. - [Release notes](https://github.com/unshiftio/url-parse/releases) - [Commits](https://github.com/unshiftio/url-parse/compare/1.5.3...1.5.7) --- updated-dependencies: - dependency-name: url-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] --- tests/integration-tests/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration-tests/yarn.lock b/tests/integration-tests/yarn.lock index 052332b6cbf..05b6d60afe7 100644 --- a/tests/integration-tests/yarn.lock +++ b/tests/integration-tests/yarn.lock @@ -10979,9 +10979,9 @@ url-parse-lax@^3.0.0: prepend-http "^2.0.0" url-parse@^1.4.3: - version "1.5.3" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" - integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ== + version "1.5.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.7.tgz#00780f60dbdae90181f51ed85fb24109422c932a" + integrity sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" From 2fb0dfe205d0afc1bfc8624c1c97fd477dadddb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Pace?= Date: Sat, 19 Feb 2022 08:34:20 -0300 Subject: [PATCH 0009/2357] Refactor Instance Manager - Part 1 (#3245) * instance_manager: Move out IndexingInputs to its own module * instance_manager: Move out Metrics to its own module * instance_manager: Move out Context/State to its own module * instance_manager: Move out Error to its own module * instance_manager: Move out Runner to its own module --- core/src/subgraph/context.rs | 30 + core/src/subgraph/error.rs | 34 + core/src/subgraph/inputs.rs | 22 + core/src/subgraph/instance_manager.rs | 1047 +------------------------ core/src/subgraph/metrics.rs | 83 ++ core/src/subgraph/mod.rs | 5 + core/src/subgraph/runner.rs | 926 ++++++++++++++++++++++ 7 files changed, 1115 insertions(+), 1032 deletions(-) create mode 100644 core/src/subgraph/context.rs create mode 100644 core/src/subgraph/error.rs create mode 100644 core/src/subgraph/inputs.rs create mode 100644 core/src/subgraph/metrics.rs create mode 100644 core/src/subgraph/runner.rs diff --git a/core/src/subgraph/context.rs b/core/src/subgraph/context.rs new file mode 100644 index 00000000000..7263784ad27 --- /dev/null +++ b/core/src/subgraph/context.rs @@ -0,0 +1,30 @@ +use crate::subgraph::metrics::SubgraphInstanceMetrics; +use crate::subgraph::SubgraphInstance; +use graph::{ + blockchain::{block_stream::BlockStreamMetrics, Blockchain}, + components::store::DeploymentId, + prelude::{CancelGuard, Entity, EntityKey, HostMetrics, Logger, RuntimeHostBuilder}, + util::lfu_cache::LfuCache, +}; +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; + +pub type SharedInstanceKeepAliveMap = Arc>>; + +pub struct IndexingState, C: Blockchain> { + pub logger: Logger, + pub instance: SubgraphInstance, + pub instances: SharedInstanceKeepAliveMap, + pub filter: C::TriggerFilter, + pub entity_lfu_cache: LfuCache>, +} + +pub struct IndexingContext, C: Blockchain> { + /// Mutable state that may be modified while indexing a subgraph. + pub state: IndexingState, + /// Sensors to measure the execution of the subgraph instance + pub subgraph_metrics: Arc, + /// Sensors to measure the execution of the subgraph's runtime hosts + pub host_metrics: Arc, + pub block_stream_metrics: Arc, +} diff --git a/core/src/subgraph/error.rs b/core/src/subgraph/error.rs new file mode 100644 index 00000000000..a8528fd81f7 --- /dev/null +++ b/core/src/subgraph/error.rs @@ -0,0 +1,34 @@ +use graph::data::subgraph::schema::SubgraphError; +use graph::prelude::{thiserror, Error, StoreError}; + +#[derive(thiserror::Error, Debug)] +pub enum BlockProcessingError { + #[error("{0:#}")] + Unknown(Error), + + // The error had a deterministic cause but, for a possibly non-deterministic reason, we chose to + // halt processing due to the error. + #[error("{0}")] + Deterministic(SubgraphError), + + #[error("subgraph stopped while processing triggers")] + Canceled, +} + +impl BlockProcessingError { + pub fn is_deterministic(&self) -> bool { + matches!(self, BlockProcessingError::Deterministic(_)) + } +} + +impl From for BlockProcessingError { + fn from(e: Error) -> Self { + BlockProcessingError::Unknown(e) + } +} + +impl From for BlockProcessingError { + fn from(e: StoreError) -> Self { + BlockProcessingError::Unknown(e.into()) + } +} diff --git a/core/src/subgraph/inputs.rs b/core/src/subgraph/inputs.rs new file mode 100644 index 00000000000..23b4384e8b2 --- /dev/null +++ b/core/src/subgraph/inputs.rs @@ -0,0 +1,22 @@ +use graph::components::store::WritableStore; +use graph::{ + blockchain::Blockchain, + components::store::{DeploymentLocator, SubgraphFork}, + data::subgraph::{SubgraphFeature, UnifiedMappingApiVersion}, + prelude::BlockNumber, +}; +use std::collections::BTreeSet; +use std::sync::Arc; + +pub struct IndexingInputs { + pub deployment: DeploymentLocator, + pub features: BTreeSet, + pub start_blocks: Vec, + pub stop_block: Option, + pub store: Arc, + pub debug_fork: Option>, + pub triggers_adapter: Arc, + pub chain: Arc, + pub templates: Arc>, + pub unified_api_version: UnifiedMappingApiVersion, +} diff --git a/core/src/subgraph/instance_manager.rs b/core/src/subgraph/instance_manager.rs index dad9225eb36..b3aaf8cebf0 100644 --- a/core/src/subgraph/instance_manager.rs +++ b/core/src/subgraph/instance_manager.rs @@ -1,90 +1,19 @@ -use super::loader::load_dynamic_data_sources; -use super::SubgraphInstance; -use atomic_refcell::AtomicRefCell; -use fail::fail_point; -use graph::blockchain::block_stream::{BlockStream, BufferedBlockStream}; -use graph::blockchain::{BlockchainKind, DataSource}; -use graph::data::store::scalar::Bytes; -use graph::data::subgraph::{UnifiedMappingApiVersion, MAX_SPEC_VERSION}; +use crate::subgraph::context::{IndexingContext, IndexingState, SharedInstanceKeepAliveMap}; +use crate::subgraph::inputs::IndexingInputs; +use crate::subgraph::loader::load_dynamic_data_sources; +use crate::subgraph::metrics::{SubgraphInstanceManagerMetrics, SubgraphInstanceMetrics}; +use crate::subgraph::runner::SubgraphRunner; +use crate::subgraph::SubgraphInstance; +use graph::blockchain::block_stream::BlockStreamMetrics; +use graph::blockchain::BlockchainKind; +use graph::blockchain::NodeCapabilities; +use graph::blockchain::{Blockchain, TriggerFilter as _}; +use graph::data::subgraph::MAX_SPEC_VERSION; use graph::prelude::{SubgraphInstanceManager as SubgraphInstanceManagerTrait, *}; -use graph::util::{backoff::ExponentialBackoff, lfu_cache::LfuCache}; -use graph::{blockchain::block_stream::BlockStreamMetrics, components::store::WritableStore}; -use graph::{blockchain::block_stream::BlockWithTriggers, data::subgraph::SubgraphFeature}; -use graph::{ - blockchain::NodeCapabilities, - blockchain::TriggersAdapter, - data::subgraph::schema::{SubgraphError, SubgraphHealth, POI_OBJECT}, -}; -use graph::{ - blockchain::{block_stream::BlockStreamEvent, Blockchain, TriggerFilter as _}, - components::subgraph::{CausalityRegion, MappingError, ProofOfIndexing, SharedProofOfIndexing}, -}; -use graph::{ - blockchain::{Block, BlockchainMap}, - components::store::{DeploymentId, DeploymentLocator, ModificationsAndCache, SubgraphFork}, -}; -use lazy_static::lazy_static; -use std::collections::{BTreeSet, HashMap}; -use std::sync::{Arc, RwLock}; -use std::time::{Duration, Instant}; +use graph::util::lfu_cache::LfuCache; +use graph::{blockchain::BlockchainMap, components::store::DeploymentLocator}; use tokio::task; -const MINUTE: Duration = Duration::from_secs(60); - -const BUFFERED_BLOCK_STREAM_SIZE: usize = 100; -const BUFFERED_FIREHOSE_STREAM_SIZE: usize = 1; - -lazy_static! { - // Keep deterministic errors non-fatal even if the subgraph is pending. - // Used for testing Graph Node itself. - pub static ref DISABLE_FAIL_FAST: bool = - std::env::var("GRAPH_DISABLE_FAIL_FAST").is_ok(); - - /// Ceiling for the backoff retry of non-deterministic errors, in seconds. - pub static ref SUBGRAPH_ERROR_RETRY_CEIL_SECS: Duration = - std::env::var("GRAPH_SUBGRAPH_ERROR_RETRY_CEIL_SECS") - .unwrap_or((MINUTE * 30).as_secs().to_string()) - .parse::() - .map(Duration::from_secs) - .expect("invalid GRAPH_SUBGRAPH_ERROR_RETRY_CEIL_SECS"); -} - -type SharedInstanceKeepAliveMap = Arc>>; - -struct IndexingInputs { - deployment: DeploymentLocator, - features: BTreeSet, - start_blocks: Vec, - stop_block: Option, - store: Arc, - debug_fork: Option>, - triggers_adapter: Arc, - chain: Arc, - templates: Arc>, - unified_api_version: UnifiedMappingApiVersion, -} - -struct IndexingState, C: Blockchain> { - logger: Logger, - instance: SubgraphInstance, - instances: SharedInstanceKeepAliveMap, - filter: C::TriggerFilter, - entity_lfu_cache: LfuCache>, -} - -struct IndexingContext, C: Blockchain> { - /// Mutable state that may be modified while indexing a subgraph. - pub state: IndexingState, - - /// Sensors to measure the execution of the subgraph instance - pub subgraph_metrics: Arc, - - /// Sensors to measure the execution of the subgraph's runtime hosts - pub host_metrics: Arc, - - pub block_stream_metrics: Arc, -} - pub struct SubgraphInstanceManager { logger_factory: LoggerFactory, subgraph_store: Arc, @@ -95,86 +24,6 @@ pub struct SubgraphInstanceManager { link_resolver: Arc, } -struct SubgraphInstanceManagerMetrics { - pub subgraph_count: Box, -} - -impl SubgraphInstanceManagerMetrics { - pub fn new(registry: Arc) -> Self { - let subgraph_count = registry - .new_gauge( - "deployment_count", - "Counts the number of deployments currently being indexed by the graph-node.", - HashMap::new(), - ) - .expect("failed to create `deployment_count` gauge"); - Self { subgraph_count } - } -} - -struct SubgraphInstanceMetrics { - pub block_trigger_count: Box, - pub block_processing_duration: Box, - pub block_ops_transaction_duration: Box, - - trigger_processing_duration: Box, -} - -impl SubgraphInstanceMetrics { - pub fn new(registry: Arc, subgraph_hash: &str) -> Self { - let block_trigger_count = registry - .new_deployment_histogram( - "deployment_block_trigger_count", - "Measures the number of triggers in each block for a subgraph deployment", - subgraph_hash, - vec![1.0, 5.0, 10.0, 20.0, 50.0], - ) - .expect("failed to create `deployment_block_trigger_count` histogram"); - let trigger_processing_duration = registry - .new_deployment_histogram( - "deployment_trigger_processing_duration", - "Measures duration of trigger processing for a subgraph deployment", - subgraph_hash, - vec![0.01, 0.05, 0.1, 0.5, 1.5, 5.0, 10.0, 30.0, 120.0], - ) - .expect("failed to create `deployment_trigger_processing_duration` histogram"); - let block_processing_duration = registry - .new_deployment_histogram( - "deployment_block_processing_duration", - "Measures duration of block processing for a subgraph deployment", - subgraph_hash, - vec![0.05, 0.2, 0.7, 1.5, 4.0, 10.0, 60.0, 120.0, 240.0], - ) - .expect("failed to create `deployment_block_processing_duration` histogram"); - let block_ops_transaction_duration = registry - .new_deployment_histogram( - "deployment_transact_block_operations_duration", - "Measures duration of commiting all the entity operations in a block and updating the subgraph pointer", - subgraph_hash, - vec![0.01, 0.05, 0.1, 0.3, 0.7, 2.0], - ) - .expect("failed to create `deployment_transact_block_operations_duration_{}"); - - Self { - block_trigger_count, - block_processing_duration, - trigger_processing_duration, - block_ops_transaction_duration, - } - } - - pub fn observe_trigger_processing_duration(&self, duration: f64) { - self.trigger_processing_duration.observe(duration); - } - - pub fn unregister(&self, registry: Arc) { - registry.unregister(self.block_processing_duration.clone()); - registry.unregister(self.block_trigger_count.clone()); - registry.unregister(self.trigger_processing_duration.clone()); - registry.unregister(self.block_ops_transaction_duration.clone()); - } -} - #[async_trait] impl SubgraphInstanceManagerTrait for SubgraphInstanceManager where @@ -448,7 +297,8 @@ where // it has a dedicated OS thread so the OS will handle the preemption. See // https://github.com/tokio-rs/tokio/issues/3493. graph::spawn_thread(deployment.to_string(), move || { - if let Err(e) = graph::block_on(task::unconstrained(run_subgraph(ctx, inputs))) { + let runner = SubgraphRunner::new(inputs, ctx); + if let Err(e) = graph::block_on(task::unconstrained(runner.run())) { error!( &logger, "Subgraph instance failed to run: {}", @@ -461,870 +311,3 @@ where Ok(()) } } - -async fn new_block_stream( - inputs: Arc>, - filter: C::TriggerFilter, - block_stream_metrics: Arc, -) -> Result>, Error> { - let chain = inputs.chain.cheap_clone(); - let is_firehose = chain.is_firehose_supported(); - - let buffer_size = match is_firehose { - true => BUFFERED_FIREHOSE_STREAM_SIZE, - false => BUFFERED_BLOCK_STREAM_SIZE, - }; - - let block_stream = match is_firehose { - true => chain.new_firehose_block_stream( - inputs.deployment.clone(), - inputs.store.block_cursor(), - inputs.start_blocks.clone(), - Arc::new(filter.clone()), - block_stream_metrics.clone(), - inputs.unified_api_version.clone(), - ), - false => { - let current_ptr = inputs.store.block_ptr(); - - chain.new_polling_block_stream( - inputs.deployment.clone(), - inputs.start_blocks.clone(), - current_ptr, - Arc::new(filter.clone()), - block_stream_metrics.clone(), - inputs.unified_api_version.clone(), - ) - } - } - .await?; - - Ok(BufferedBlockStream::spawn_from_stream( - block_stream, - buffer_size, - )) -} - -async fn run_subgraph( - mut ctx: IndexingContext, - inputs: IndexingInputs, -) -> Result<(), Error> -where - T: RuntimeHostBuilder, - C: Blockchain, -{ - // Clone a few things for different parts of the async processing - let inputs = Arc::new(inputs); - let subgraph_metrics = ctx.subgraph_metrics.cheap_clone(); - let store_for_err = inputs.store.cheap_clone(); - let logger = ctx.state.logger.cheap_clone(); - let id_for_err = inputs.deployment.hash.clone(); - let mut should_try_unfail_deterministic = true; - let mut should_try_unfail_non_deterministic = true; - let mut synced = false; - - // Exponential backoff that starts with two minutes and keeps - // increasing its timeout exponentially until it reaches the ceiling. - let mut backoff = ExponentialBackoff::new(MINUTE * 2, *SUBGRAPH_ERROR_RETRY_CEIL_SECS); - - loop { - debug!(logger, "Starting or restarting subgraph"); - - let block_stream_canceler = CancelGuard::new(); - let block_stream_cancel_handle = block_stream_canceler.handle(); - - let metrics = ctx.block_stream_metrics.clone(); - let filter = ctx.state.filter.clone(); - let stream_inputs = inputs.clone(); - let mut block_stream = new_block_stream(stream_inputs, filter, metrics.cheap_clone()) - .await? - .map_err(CancelableError::Error) - .cancelable(&block_stream_canceler, || Err(CancelableError::Cancel)); - let chain = inputs.chain.clone(); - let chain_store = chain.chain_store(); - - // Keep the stream's cancel guard around to be able to shut it down - // when the subgraph deployment is unassigned - ctx.state - .instances - .write() - .unwrap() - .insert(inputs.deployment.id, block_stream_canceler); - - debug!(logger, "Starting block stream"); - - // Process events from the stream as long as no restart is needed - loop { - let event = { - let _section = metrics.stopwatch.start_section("scan_blocks"); - - block_stream.next().await - }; - - let (block, cursor) = match event { - Some(Ok(BlockStreamEvent::ProcessBlock(block, cursor))) => (block, cursor), - Some(Ok(BlockStreamEvent::Revert(subgraph_ptr, parent_ptr, cursor))) => { - info!( - logger, - "Reverting block to get back to main chain"; - "block_number" => format!("{}", subgraph_ptr.number), - "block_hash" => format!("{}", subgraph_ptr.hash) - ); - - if let Err(e) = inputs - .store - .revert_block_operations(parent_ptr, cursor.as_deref()) - { - error!( - &logger, - "Could not revert block. Retrying"; - "block_number" => format!("{}", subgraph_ptr.number), - "block_hash" => format!("{}", subgraph_ptr.hash), - "error" => e.to_string(), - ); - - // Exit inner block stream consumption loop and go up to loop that restarts subgraph - break; - } - - ctx.block_stream_metrics - .reverted_blocks - .set(subgraph_ptr.number as f64); - - // Revert the in-memory state: - // - Remove hosts for reverted dynamic data sources. - // - Clear the entity cache. - // - // Note that we do not currently revert the filters, which means the filters - // will be broader than necessary. This is not ideal for performance, but is not - // incorrect since we will discard triggers that match the filters but do not - // match any data sources. - ctx.state.instance.revert_data_sources(subgraph_ptr.number); - ctx.state.entity_lfu_cache = LfuCache::new(); - continue; - } - - // Log and drop the errors from the block_stream - // The block stream will continue attempting to produce blocks - Some(Err(e)) => { - if block_stream_cancel_handle.is_canceled() { - debug!(&logger, "Subgraph block stream shut down cleanly"); - return Ok(()); - } - - debug!( - &logger, - "Block stream produced a non-fatal error"; - "error" => format!("{}", e), - ); - continue; - } - // Scenario where this can happen: 1504c9d8-36e4-45bb-b4f2-71cf58789ed9 - None => unreachable!("The block stream stopped producing blocks"), - }; - - let block_ptr = block.ptr(); - - if block.trigger_count() > 0 { - subgraph_metrics - .block_trigger_count - .observe(block.trigger_count() as f64); - } - - let start = Instant::now(); - let deployment_failed = ctx.block_stream_metrics.deployment_failed.clone(); - - // If a subgraph failed for deterministic reasons, before processing a new block, we - // revert the deployment head. It should lead to the same result since the error was - // deterministic. - // - // As an optimization we check this only on the first run. - if should_try_unfail_deterministic { - should_try_unfail_deterministic = false; - - if let Some(current_ptr) = inputs.store.block_ptr() { - if let Some(parent_ptr) = - inputs.triggers_adapter.parent_ptr(¤t_ptr).await? - { - // This reverts the deployment head to the parent_ptr if - // deterministic errors happened. - // - // There's no point in calling it if we have no current or parent block - // pointers, because there would be: no block to revert to or to search - // errors from (first execution). - inputs - .store - .unfail_deterministic_error(¤t_ptr, &parent_ptr)?; - } - } - } - - let res = process_block( - &logger, - inputs.triggers_adapter.cheap_clone(), - &mut ctx, - &inputs, - block_stream_cancel_handle.clone(), - block, - cursor.into(), - ) - .await; - - let elapsed = start.elapsed().as_secs_f64(); - subgraph_metrics.block_processing_duration.observe(elapsed); - - match res { - Ok(needs_restart) => { - // Once synced, no need to try to update the status again. - if !synced && is_deployment_synced(&block_ptr, chain_store.cached_head_ptr()?) { - // Updating the sync status is an one way operation. - // This state change exists: not synced -> synced - // This state change does NOT: synced -> not synced - inputs.store.deployment_synced()?; - - // Stop trying to update the sync status. - synced = true; - - // Stop recording time-to-sync metrics. - ctx.block_stream_metrics.stopwatch.disable(); - } - - // Keep trying to unfail subgraph for everytime it advances block(s) until it's - // health is not Failed anymore. - if should_try_unfail_non_deterministic { - // If the deployment head advanced, we can unfail - // the non-deterministic error (if there's any). - inputs.store.unfail_non_deterministic_error(&block_ptr)?; - - match inputs.store.health(&inputs.deployment.hash).await? { - SubgraphHealth::Failed => { - // If the unfail call didn't change the subgraph health, we keep - // `should_try_unfail_non_deterministic` as `true` until it's - // actually unfailed. - } - SubgraphHealth::Healthy | SubgraphHealth::Unhealthy => { - // Stop trying to unfail. - should_try_unfail_non_deterministic = false; - deployment_failed.set(0.0); - backoff.reset(); - } - }; - } - - if needs_restart { - // Cancel the stream for real - ctx.state - .instances - .write() - .unwrap() - .remove(&inputs.deployment.id); - - // And restart the subgraph - break; - } - - if let Some(stop_block) = &inputs.stop_block { - if block_ptr.number >= *stop_block { - info!(&logger, "stop block reached for subgraph"); - return Ok(()); - } - } - } - Err(BlockProcessingError::Canceled) => { - debug!(&logger, "Subgraph block stream shut down cleanly"); - return Ok(()); - } - - // Handle unexpected stream errors by marking the subgraph as failed. - Err(e) => { - // Clear entity cache when a subgraph fails. - // - // This is done to be safe and sure that there's no state that's - // out of sync from the database. - // - // Without it, POI changes on failure would be kept in the entity cache - // and be transacted incorrectly in the next run. - ctx.state.entity_lfu_cache = LfuCache::new(); - - deployment_failed.set(1.0); - - let message = format!("{:#}", e).replace("\n", "\t"); - let err = anyhow!("{}, code: {}", message, LogCode::SubgraphSyncingFailure); - let deterministic = e.is_deterministic(); - - let error = SubgraphError { - subgraph_id: id_for_err.clone(), - message, - block_ptr: Some(block_ptr), - handler: None, - deterministic, - }; - - match deterministic { - true => { - // Fail subgraph: - // - Change status/health. - // - Save the error to the database. - store_for_err - .fail_subgraph(error) - .await - .context("Failed to set subgraph status to `failed`")?; - - return Err(err); - } - false => { - // Shouldn't fail subgraph if it's already failed for non-deterministic - // reasons. - // - // If we don't do this check we would keep adding the same error to the - // database. - let should_fail_subgraph = - inputs.store.health(&inputs.deployment.hash).await? - != SubgraphHealth::Failed; - - if should_fail_subgraph { - // Fail subgraph: - // - Change status/health. - // - Save the error to the database. - store_for_err - .fail_subgraph(error) - .await - .context("Failed to set subgraph status to `failed`")?; - } - - // Retry logic below: - - // Cancel the stream for real. - ctx.state - .instances - .write() - .unwrap() - .remove(&inputs.deployment.id); - - error!(logger, "Subgraph failed with non-deterministic error: {}", e; - "attempt" => backoff.attempt, - "retry_delay_s" => backoff.delay().as_secs()); - - // Sleep before restarting. - backoff.sleep_async().await; - - should_try_unfail_non_deterministic = true; - - // And restart the subgraph. - break; - } - } - } - } - } - } -} - -#[derive(thiserror::Error, Debug)] -enum BlockProcessingError { - #[error("{0:#}")] - Unknown(Error), - - // The error had a deterministic cause but, for a possibly non-deterministic reason, we chose to - // halt processing due to the error. - #[error("{0}")] - Deterministic(SubgraphError), - - #[error("subgraph stopped while processing triggers")] - Canceled, -} - -impl BlockProcessingError { - fn is_deterministic(&self) -> bool { - matches!(self, BlockProcessingError::Deterministic(_)) - } -} - -impl From for BlockProcessingError { - fn from(e: Error) -> Self { - BlockProcessingError::Unknown(e) - } -} - -impl From for BlockProcessingError { - fn from(e: StoreError) -> Self { - BlockProcessingError::Unknown(e.into()) - } -} - -/// Processes a block and returns the updated context and a boolean flag indicating -/// whether new dynamic data sources have been added to the subgraph. -async fn process_block, C: Blockchain>( - logger: &Logger, - triggers_adapter: Arc, - ctx: &mut IndexingContext, - inputs: &IndexingInputs, - block_stream_cancel_handle: CancelHandle, - block: BlockWithTriggers, - firehose_cursor: Option, -) -> Result { - let triggers = block.trigger_data; - let block = Arc::new(block.block); - let block_ptr = block.ptr(); - - let logger = logger.new(o!( - "block_number" => format!("{:?}", block_ptr.number), - "block_hash" => format!("{}", block_ptr.hash) - )); - - if triggers.len() == 1 { - debug!(&logger, "1 candidate trigger in this block"); - } else if triggers.len() > 1 { - debug!( - &logger, - "{} candidate triggers in this block", - triggers.len() - ); - } - - let metrics = ctx.subgraph_metrics.clone(); - - let proof_of_indexing = if inputs.store.clone().supports_proof_of_indexing().await? { - Some(Arc::new(AtomicRefCell::new(ProofOfIndexing::new( - block_ptr.number, - )))) - } else { - None - }; - - // There are currently no other causality regions since offchain data is not supported. - let causality_region = CausalityRegion::from_network(ctx.state.instance.network()); - - // Process events one after the other, passing in entity operations - // collected previously to every new event being processed - let mut block_state = match process_triggers( - &logger, - BlockState::new( - inputs.store.clone(), - std::mem::take(&mut ctx.state.entity_lfu_cache), - ), - proof_of_indexing.cheap_clone(), - ctx.subgraph_metrics.clone(), - &ctx.state.instance, - &block, - triggers, - &causality_region, - &inputs.debug_fork, - ) - .await - { - // Triggers processed with no errors or with only deterministic errors. - Ok(block_state) => block_state, - - // Some form of unknown or non-deterministic error ocurred. - Err(MappingError::Unknown(e)) => return Err(BlockProcessingError::Unknown(e)), - Err(MappingError::PossibleReorg(e)) => { - info!(logger, - "Possible reorg detected, retrying"; - "error" => format!("{:#}", e), - ); - - // In case of a possible reorg, we want this function to do nothing and restart the - // block stream so it has a chance to detect the reorg. - // - // The `ctx` is unchanged at this point, except for having cleared the entity cache. - // Losing the cache is a bit annoying but not an issue for correctness. - // - // See also b21fa73b-6453-4340-99fb-1a78ec62efb1. - return Ok(true); - } - }; - - // If new data sources have been created, restart the subgraph after this block. - // This is necessary to re-create the block stream. - let needs_restart = block_state.has_created_data_sources(); - let host_metrics = ctx.host_metrics.clone(); - - // This loop will: - // 1. Instantiate created data sources. - // 2. Process those data sources for the current block. - // Until no data sources are created or MAX_DATA_SOURCES is hit. - - // Note that this algorithm processes data sources spawned on the same block _breadth - // first_ on the tree implied by the parent-child relationship between data sources. Only a - // very contrived subgraph would be able to observe this. - while block_state.has_created_data_sources() { - // Instantiate dynamic data sources, removing them from the block state. - let (data_sources, runtime_hosts) = create_dynamic_data_sources( - logger.clone(), - ctx, - &inputs, - host_metrics.clone(), - block_state.drain_created_data_sources(), - )?; - - let filter = C::TriggerFilter::from_data_sources(data_sources.iter()); - - // Reprocess the triggers from this block that match the new data sources - let block_with_triggers = triggers_adapter - .triggers_in_block(&logger, block.as_ref().clone(), &filter) - .await?; - - let triggers = block_with_triggers.trigger_data; - - if triggers.len() == 1 { - info!( - &logger, - "1 trigger found in this block for the new data sources" - ); - } else if triggers.len() > 1 { - info!( - &logger, - "{} triggers found in this block for the new data sources", - triggers.len() - ); - } - - // Add entity operations for the new data sources to the block state - // and add runtimes for the data sources to the subgraph instance. - persist_dynamic_data_sources( - logger.clone(), - ctx, - &mut block_state.entity_cache, - data_sources, - ); - - // Process the triggers in each host in the same order the - // corresponding data sources have been created. - for trigger in triggers { - block_state = SubgraphInstance::::process_trigger_in_runtime_hosts( - &logger, - &runtime_hosts, - &block, - &trigger, - block_state, - proof_of_indexing.cheap_clone(), - &causality_region, - &inputs.debug_fork, - ) - .await - .map_err(|e| { - // This treats a `PossibleReorg` as an ordinary error which will fail the subgraph. - // This can cause an unnecessary subgraph failure, to fix it we need to figure out a - // way to revert the effect of `create_dynamic_data_sources` so we may return a - // clean context as in b21fa73b-6453-4340-99fb-1a78ec62efb1. - match e { - MappingError::PossibleReorg(e) | MappingError::Unknown(e) => { - BlockProcessingError::Unknown(e) - } - } - })?; - } - } - - let has_errors = block_state.has_errors(); - let is_non_fatal_errors_active = inputs.features.contains(&SubgraphFeature::NonFatalErrors); - - // Apply entity operations and advance the stream - - // Avoid writing to store if block stream has been canceled - if block_stream_cancel_handle.is_canceled() { - return Err(BlockProcessingError::Canceled); - } - - if let Some(proof_of_indexing) = proof_of_indexing { - let proof_of_indexing = Arc::try_unwrap(proof_of_indexing).unwrap().into_inner(); - update_proof_of_indexing( - proof_of_indexing, - &ctx.host_metrics.stopwatch, - &inputs.deployment.hash, - &mut block_state.entity_cache, - ) - .await?; - } - - let section = ctx.host_metrics.stopwatch.start_section("as_modifications"); - let ModificationsAndCache { - modifications: mut mods, - data_sources, - entity_lfu_cache: cache, - } = block_state - .entity_cache - .as_modifications() - .map_err(|e| BlockProcessingError::Unknown(e.into()))?; - section.end(); - - // Put the cache back in the ctx, asserting that the placeholder cache was not used. - assert!(ctx.state.entity_lfu_cache.is_empty()); - ctx.state.entity_lfu_cache = cache; - - if !mods.is_empty() { - info!(&logger, "Applying {} entity operation(s)", mods.len()); - } - - let err_count = block_state.deterministic_errors.len(); - for (i, e) in block_state.deterministic_errors.iter().enumerate() { - let message = format!("{:#}", e).replace("\n", "\t"); - error!(&logger, "Subgraph error {}/{}", i + 1, err_count; - "error" => message, - "code" => LogCode::SubgraphSyncingFailure - ); - } - - // Transact entity operations into the store and update the - // subgraph's block stream pointer - let _section = ctx.host_metrics.stopwatch.start_section("transact_block"); - let stopwatch = ctx.host_metrics.stopwatch.clone(); - let start = Instant::now(); - - let store = &inputs.store; - - // If a deterministic error has happened, make the PoI to be the only entity that'll be stored. - if has_errors && !is_non_fatal_errors_active { - let is_poi_entity = - |entity_mod: &EntityModification| entity_mod.entity_key().entity_type.is_poi(); - mods.retain(is_poi_entity); - // Confidence check - assert!( - mods.len() == 1, - "There should be only one PoI EntityModification" - ); - } - - let BlockState { - deterministic_errors, - .. - } = block_state; - - let first_error = deterministic_errors.first().cloned(); - - match store.transact_block_operations( - block_ptr, - firehose_cursor, - mods, - stopwatch, - data_sources, - deterministic_errors, - ) { - Ok(_) => { - // For subgraphs with `nonFatalErrors` feature disabled, we consider - // any error as fatal. - // - // So we do an early return to make the subgraph stop processing blocks. - // - // In this scenario the only entity that is stored/transacted is the PoI, - // all of the others are discarded. - if has_errors && !is_non_fatal_errors_active { - // Only the first error is reported. - return Err(BlockProcessingError::Deterministic(first_error.unwrap())); - } - - let elapsed = start.elapsed().as_secs_f64(); - metrics.block_ops_transaction_duration.observe(elapsed); - - // To prevent a buggy pending version from replacing a current version, if errors are - // present the subgraph will be unassigned. - if has_errors && !*DISABLE_FAIL_FAST && !store.is_deployment_synced().await? { - store - .unassign_subgraph() - .map_err(|e| BlockProcessingError::Unknown(e.into()))?; - - // Use `Canceled` to avoiding setting the subgraph health to failed, an error was - // just transacted so it will be already be set to unhealthy. - return Err(BlockProcessingError::Canceled); - } - - Ok(needs_restart) - } - - Err(e) => Err(anyhow!("Error while processing block stream for a subgraph: {}", e).into()), - } -} - -/// Transform the proof of indexing changes into entity updates that will be -/// inserted when as_modifications is called. -async fn update_proof_of_indexing( - proof_of_indexing: ProofOfIndexing, - stopwatch: &StopwatchMetrics, - deployment_id: &DeploymentHash, - entity_cache: &mut EntityCache, -) -> Result<(), Error> { - let _section_guard = stopwatch.start_section("update_proof_of_indexing"); - - let mut proof_of_indexing = proof_of_indexing.take(); - - for (causality_region, stream) in proof_of_indexing.drain() { - // Create the special POI entity key specific to this causality_region - let entity_key = EntityKey { - subgraph_id: deployment_id.clone(), - entity_type: POI_OBJECT.to_owned(), - entity_id: causality_region, - }; - - // Grab the current digest attribute on this entity - let prev_poi = - entity_cache - .get(&entity_key) - .map_err(Error::from)? - .map(|entity| match entity.get("digest") { - Some(Value::Bytes(b)) => b.clone(), - _ => panic!("Expected POI entity to have a digest and for it to be bytes"), - }); - - // Finish the POI stream, getting the new POI value. - let updated_proof_of_indexing = stream.pause(prev_poi.as_deref()); - let updated_proof_of_indexing: Bytes = (&updated_proof_of_indexing[..]).into(); - - // Put this onto an entity with the same digest attribute - // that was expected before when reading. - let new_poi_entity = entity! { - id: entity_key.entity_id.clone(), - digest: updated_proof_of_indexing, - }; - - entity_cache.set(entity_key, new_poi_entity)?; - } - - Ok(()) -} - -async fn process_triggers( - logger: &Logger, - mut block_state: BlockState, - proof_of_indexing: SharedProofOfIndexing, - subgraph_metrics: Arc, - instance: &SubgraphInstance>, - block: &Arc, - triggers: Vec, - causality_region: &str, - debug_fork: &Option>, -) -> Result, MappingError> { - use graph::blockchain::TriggerData; - - for trigger in triggers.into_iter() { - let start = Instant::now(); - block_state = instance - .process_trigger( - &logger, - block, - &trigger, - block_state, - proof_of_indexing.cheap_clone(), - causality_region, - debug_fork, - ) - .await - .map_err(move |mut e| { - let error_context = trigger.error_context(); - if !error_context.is_empty() { - e = e.context(error_context); - } - e.context("failed to process trigger".to_string()) - })?; - let elapsed = start.elapsed().as_secs_f64(); - subgraph_metrics.observe_trigger_processing_duration(elapsed); - } - Ok(block_state) -} - -fn create_dynamic_data_sources, C: Blockchain>( - logger: Logger, - ctx: &mut IndexingContext, - inputs: &IndexingInputs, - host_metrics: Arc, - created_data_sources: Vec>, -) -> Result<(Vec, Vec>), Error> { - let mut data_sources = vec![]; - let mut runtime_hosts = vec![]; - - for info in created_data_sources { - // Try to instantiate a data source from the template - let data_source = C::DataSource::try_from(info)?; - - // Try to create a runtime host for the data source - let host = ctx.state.instance.add_dynamic_data_source( - &logger, - data_source.clone(), - inputs.templates.clone(), - host_metrics.clone(), - )?; - - match host { - Some(host) => { - data_sources.push(data_source); - runtime_hosts.push(host); - } - None => { - fail_point!("error_on_duplicate_ds", |_| Err(anyhow!("duplicate ds"))); - warn!( - logger, - "no runtime hosted created, there is already a runtime host instantiated for \ - this data source"; - "name" => &data_source.name(), - "address" => &data_source.address() - .map(|address| hex::encode(address)) - .unwrap_or("none".to_string()), - ) - } - } - } - - Ok((data_sources, runtime_hosts)) -} - -fn persist_dynamic_data_sources, C: Blockchain>( - logger: Logger, - ctx: &mut IndexingContext, - entity_cache: &mut EntityCache, - data_sources: Vec, -) { - if !data_sources.is_empty() { - debug!( - logger, - "Creating {} dynamic data source(s)", - data_sources.len() - ); - } - - // Add entity operations to the block state in order to persist - // the dynamic data sources - for data_source in data_sources.iter() { - debug!( - logger, - "Persisting data_source"; - "name" => &data_source.name(), - "address" => &data_source.address().map(|address| hex::encode(address)).unwrap_or("none".to_string()), - ); - entity_cache.add_data_source(data_source); - } - - // Merge filters from data sources into the block stream builder - ctx.state.filter.extend(data_sources.iter()); -} - -/// Checks if the Deployment BlockPtr is at least one block behind to the chain head. -fn is_deployment_synced(deployment_head_ptr: &BlockPtr, chain_head_ptr: Option) -> bool { - matches!((deployment_head_ptr, &chain_head_ptr), (b1, Some(b2)) if b1.number >= (b2.number - 1)) -} - -#[test] -fn test_is_deployment_synced() { - let block_0 = BlockPtr::try_from(( - "bd34884280958002c51d3f7b5f853e6febeba33de0f40d15b0363006533c924f", - 0, - )) - .unwrap(); - let block_1 = BlockPtr::try_from(( - "8511fa04b64657581e3f00e14543c1d522d5d7e771b54aa3060b662ade47da13", - 1, - )) - .unwrap(); - let block_2 = BlockPtr::try_from(( - "b98fb783b49de5652097a989414c767824dff7e7fd765a63b493772511db81c1", - 2, - )) - .unwrap(); - - assert!(!is_deployment_synced(&block_0, None)); - assert!(!is_deployment_synced(&block_2, None)); - - assert!(!is_deployment_synced(&block_0, Some(block_2.clone()))); - - assert!(is_deployment_synced(&block_1, Some(block_2.clone()))); - assert!(is_deployment_synced(&block_2, Some(block_2.clone()))); -} diff --git a/core/src/subgraph/metrics.rs b/core/src/subgraph/metrics.rs new file mode 100644 index 00000000000..8209f622ded --- /dev/null +++ b/core/src/subgraph/metrics.rs @@ -0,0 +1,83 @@ +use graph::prelude::{Gauge, Histogram, MetricsRegistry}; +use std::collections::HashMap; +use std::sync::Arc; + +pub struct SubgraphInstanceMetrics { + pub block_trigger_count: Box, + pub block_processing_duration: Box, + pub block_ops_transaction_duration: Box, + + trigger_processing_duration: Box, +} + +impl SubgraphInstanceMetrics { + pub fn new(registry: Arc, subgraph_hash: &str) -> Self { + let block_trigger_count = registry + .new_deployment_histogram( + "deployment_block_trigger_count", + "Measures the number of triggers in each block for a subgraph deployment", + subgraph_hash, + vec![1.0, 5.0, 10.0, 20.0, 50.0], + ) + .expect("failed to create `deployment_block_trigger_count` histogram"); + let trigger_processing_duration = registry + .new_deployment_histogram( + "deployment_trigger_processing_duration", + "Measures duration of trigger processing for a subgraph deployment", + subgraph_hash, + vec![0.01, 0.05, 0.1, 0.5, 1.5, 5.0, 10.0, 30.0, 120.0], + ) + .expect("failed to create `deployment_trigger_processing_duration` histogram"); + let block_processing_duration = registry + .new_deployment_histogram( + "deployment_block_processing_duration", + "Measures duration of block processing for a subgraph deployment", + subgraph_hash, + vec![0.05, 0.2, 0.7, 1.5, 4.0, 10.0, 60.0, 120.0, 240.0], + ) + .expect("failed to create `deployment_block_processing_duration` histogram"); + let block_ops_transaction_duration = registry + .new_deployment_histogram( + "deployment_transact_block_operations_duration", + "Measures duration of commiting all the entity operations in a block and updating the subgraph pointer", + subgraph_hash, + vec![0.01, 0.05, 0.1, 0.3, 0.7, 2.0], + ) + .expect("failed to create `deployment_transact_block_operations_duration_{}"); + + Self { + block_trigger_count, + block_processing_duration, + trigger_processing_duration, + block_ops_transaction_duration, + } + } + + pub fn observe_trigger_processing_duration(&self, duration: f64) { + self.trigger_processing_duration.observe(duration); + } + + pub fn unregister(&self, registry: Arc) { + registry.unregister(self.block_processing_duration.clone()); + registry.unregister(self.block_trigger_count.clone()); + registry.unregister(self.trigger_processing_duration.clone()); + registry.unregister(self.block_ops_transaction_duration.clone()); + } +} + +pub struct SubgraphInstanceManagerMetrics { + pub subgraph_count: Box, +} + +impl SubgraphInstanceManagerMetrics { + pub fn new(registry: Arc) -> Self { + let subgraph_count = registry + .new_gauge( + "deployment_count", + "Counts the number of deployments currently being indexed by the graph-node.", + HashMap::new(), + ) + .expect("failed to create `deployment_count` gauge"); + Self { subgraph_count } + } +} diff --git a/core/src/subgraph/mod.rs b/core/src/subgraph/mod.rs index 8e463d128a6..33a4fa09026 100644 --- a/core/src/subgraph/mod.rs +++ b/core/src/subgraph/mod.rs @@ -1,8 +1,13 @@ +mod context; +mod error; +mod inputs; mod instance; mod instance_manager; mod loader; +mod metrics; mod provider; mod registrar; +mod runner; pub use self::instance::SubgraphInstance; pub use self::instance_manager::SubgraphInstanceManager; diff --git a/core/src/subgraph/runner.rs b/core/src/subgraph/runner.rs new file mode 100644 index 00000000000..68d61939a45 --- /dev/null +++ b/core/src/subgraph/runner.rs @@ -0,0 +1,926 @@ +use crate::subgraph::context::IndexingContext; +use crate::subgraph::error::BlockProcessingError; +use crate::subgraph::inputs::IndexingInputs; +use crate::subgraph::metrics::SubgraphInstanceMetrics; +use crate::subgraph::SubgraphInstance; +use atomic_refcell::AtomicRefCell; +use fail::fail_point; +use graph::blockchain::block_stream::{ + BlockStream, BlockStreamEvent, BlockStreamMetrics, BlockWithTriggers, BufferedBlockStream, +}; +use graph::blockchain::{Block, Blockchain, DataSource, TriggerFilter as _, TriggersAdapter}; +use graph::components::{ + store::{ModificationsAndCache, SubgraphFork}, + subgraph::{CausalityRegion, MappingError, ProofOfIndexing, SharedProofOfIndexing}, +}; +use graph::data::store::scalar::Bytes; +use graph::data::subgraph::{ + schema::{SubgraphError, SubgraphHealth, POI_OBJECT}, + SubgraphFeature, +}; +use graph::prelude::*; +use graph::util::{backoff::ExponentialBackoff, lfu_cache::LfuCache}; +use lazy_static::lazy_static; +use std::convert::TryFrom; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +const MINUTE: Duration = Duration::from_secs(60); +const BUFFERED_BLOCK_STREAM_SIZE: usize = 100; +const BUFFERED_FIREHOSE_STREAM_SIZE: usize = 1; + +lazy_static! { + // Keep deterministic errors non-fatal even if the subgraph is pending. + // Used for testing Graph Node itself. + pub static ref DISABLE_FAIL_FAST: bool = + std::env::var("GRAPH_DISABLE_FAIL_FAST").is_ok(); + + /// Ceiling for the backoff retry of non-deterministic errors, in seconds. + pub static ref SUBGRAPH_ERROR_RETRY_CEIL_SECS: Duration = + std::env::var("GRAPH_SUBGRAPH_ERROR_RETRY_CEIL_SECS") + .unwrap_or((MINUTE * 30).as_secs().to_string()) + .parse::() + .map(Duration::from_secs) + .expect("invalid GRAPH_SUBGRAPH_ERROR_RETRY_CEIL_SECS"); +} + +async fn new_block_stream( + inputs: Arc>, + filter: C::TriggerFilter, + block_stream_metrics: Arc, +) -> Result>, Error> { + let chain = inputs.chain.cheap_clone(); + let is_firehose = chain.is_firehose_supported(); + + let buffer_size = match is_firehose { + true => BUFFERED_FIREHOSE_STREAM_SIZE, + false => BUFFERED_BLOCK_STREAM_SIZE, + }; + + let block_stream = match is_firehose { + true => chain.new_firehose_block_stream( + inputs.deployment.clone(), + inputs.store.block_cursor(), + inputs.start_blocks.clone(), + Arc::new(filter.clone()), + block_stream_metrics.clone(), + inputs.unified_api_version.clone(), + ), + false => { + let current_ptr = inputs.store.block_ptr(); + + chain.new_polling_block_stream( + inputs.deployment.clone(), + inputs.start_blocks.clone(), + current_ptr, + Arc::new(filter.clone()), + block_stream_metrics.clone(), + inputs.unified_api_version.clone(), + ) + } + } + .await?; + + Ok(BufferedBlockStream::spawn_from_stream( + block_stream, + buffer_size, + )) +} + +pub struct SubgraphRunner> { + ctx: IndexingContext, + inputs: Arc>, +} + +impl SubgraphRunner +where + C: Blockchain, + T: RuntimeHostBuilder, +{ + pub fn new(inputs: IndexingInputs, ctx: IndexingContext) -> Self { + Self { + inputs: Arc::new(inputs), + ctx, + } + } + + pub async fn run(mut self) -> Result<(), Error> { + // Clone a few things for different parts of the async processing + let subgraph_metrics = self.ctx.subgraph_metrics.cheap_clone(); + let store_for_err = self.inputs.store.cheap_clone(); + let logger = self.ctx.state.logger.cheap_clone(); + let id_for_err = self.inputs.deployment.hash.clone(); + let mut should_try_unfail_deterministic = true; + let mut should_try_unfail_non_deterministic = true; + let mut synced = false; + + // Exponential backoff that starts with two minutes and keeps + // increasing its timeout exponentially until it reaches the ceiling. + let mut backoff = ExponentialBackoff::new(MINUTE * 2, *SUBGRAPH_ERROR_RETRY_CEIL_SECS); + + loop { + debug!(logger, "Starting or restarting subgraph"); + + let block_stream_canceler = CancelGuard::new(); + let block_stream_cancel_handle = block_stream_canceler.handle(); + + let metrics = self.ctx.block_stream_metrics.clone(); + let filter = self.ctx.state.filter.clone(); + let stream_inputs = self.inputs.clone(); + let mut block_stream = new_block_stream(stream_inputs, filter, metrics.cheap_clone()) + .await? + .map_err(CancelableError::Error) + .cancelable(&block_stream_canceler, || Err(CancelableError::Cancel)); + let chain = self.inputs.chain.clone(); + let chain_store = chain.chain_store(); + + // Keep the stream's cancel guard around to be able to shut it down + // when the subgraph deployment is unassigned + self.ctx + .state + .instances + .write() + .unwrap() + .insert(self.inputs.deployment.id, block_stream_canceler); + + debug!(logger, "Starting block stream"); + + // Process events from the stream as long as no restart is needed + loop { + let event = { + let _section = metrics.stopwatch.start_section("scan_blocks"); + + block_stream.next().await + }; + + let (block, cursor) = match event { + Some(Ok(BlockStreamEvent::ProcessBlock(block, cursor))) => (block, cursor), + Some(Ok(BlockStreamEvent::Revert(subgraph_ptr, parent_ptr, cursor))) => { + info!( + logger, + "Reverting block to get back to main chain"; + "block_number" => format!("{}", subgraph_ptr.number), + "block_hash" => format!("{}", subgraph_ptr.hash) + ); + + if let Err(e) = self + .inputs + .store + .revert_block_operations(parent_ptr, cursor.as_deref()) + { + error!( + &logger, + "Could not revert block. Retrying"; + "block_number" => format!("{}", subgraph_ptr.number), + "block_hash" => format!("{}", subgraph_ptr.hash), + "error" => e.to_string(), + ); + + // Exit inner block stream consumption loop and go up to loop that restarts subgraph + break; + } + + self.ctx + .block_stream_metrics + .reverted_blocks + .set(subgraph_ptr.number as f64); + + // Revert the in-memory state: + // - Remove hosts for reverted dynamic data sources. + // - Clear the entity cache. + // + // Note that we do not currently revert the filters, which means the filters + // will be broader than necessary. This is not ideal for performance, but is not + // incorrect since we will discard triggers that match the filters but do not + // match any data sources. + self.ctx + .state + .instance + .revert_data_sources(subgraph_ptr.number); + self.ctx.state.entity_lfu_cache = LfuCache::new(); + continue; + } + + // Log and drop the errors from the block_stream + // The block stream will continue attempting to produce blocks + Some(Err(e)) => { + if block_stream_cancel_handle.is_canceled() { + debug!(&logger, "Subgraph block stream shut down cleanly"); + return Ok(()); + } + + debug!( + &logger, + "Block stream produced a non-fatal error"; + "error" => format!("{}", e), + ); + continue; + } + // Scenario where this can happen: 1504c9d8-36e4-45bb-b4f2-71cf58789ed9 + None => unreachable!("The block stream stopped producing blocks"), + }; + + let block_ptr = block.ptr(); + + if block.trigger_count() > 0 { + subgraph_metrics + .block_trigger_count + .observe(block.trigger_count() as f64); + } + + let start = Instant::now(); + let deployment_failed = self.ctx.block_stream_metrics.deployment_failed.clone(); + + // If a subgraph failed for deterministic reasons, before processing a new block, we + // revert the deployment head. It should lead to the same result since the error was + // deterministic. + // + // As an optimization we check this only on the first run. + if should_try_unfail_deterministic { + should_try_unfail_deterministic = false; + + if let Some(current_ptr) = self.inputs.store.block_ptr() { + if let Some(parent_ptr) = self + .inputs + .triggers_adapter + .parent_ptr(¤t_ptr) + .await? + { + // This reverts the deployment head to the parent_ptr if + // deterministic errors happened. + // + // There's no point in calling it if we have no current or parent block + // pointers, because there would be: no block to revert to or to search + // errors from (first execution). + self.inputs + .store + .unfail_deterministic_error(¤t_ptr, &parent_ptr)?; + } + } + } + + let res = self + .process_block( + &logger, + self.inputs.triggers_adapter.cheap_clone(), + block_stream_cancel_handle.clone(), + block, + cursor.into(), + ) + .await; + + let elapsed = start.elapsed().as_secs_f64(); + subgraph_metrics.block_processing_duration.observe(elapsed); + + match res { + Ok(needs_restart) => { + // Once synced, no need to try to update the status again. + if !synced + && is_deployment_synced(&block_ptr, chain_store.cached_head_ptr()?) + { + // Updating the sync status is an one way operation. + // This state change exists: not synced -> synced + // This state change does NOT: synced -> not synced + self.inputs.store.deployment_synced()?; + + // Stop trying to update the sync status. + synced = true; + + // Stop recording time-to-sync metrics. + self.ctx.block_stream_metrics.stopwatch.disable(); + } + + // Keep trying to unfail subgraph for everytime it advances block(s) until it's + // health is not Failed anymore. + if should_try_unfail_non_deterministic { + // If the deployment head advanced, we can unfail + // the non-deterministic error (if there's any). + self.inputs + .store + .unfail_non_deterministic_error(&block_ptr)?; + + match self + .inputs + .store + .health(&self.inputs.deployment.hash) + .await? + { + SubgraphHealth::Failed => { + // If the unfail call didn't change the subgraph health, we keep + // `should_try_unfail_non_deterministic` as `true` until it's + // actually unfailed. + } + SubgraphHealth::Healthy | SubgraphHealth::Unhealthy => { + // Stop trying to unfail. + should_try_unfail_non_deterministic = false; + deployment_failed.set(0.0); + backoff.reset(); + } + }; + } + + if needs_restart { + // Cancel the stream for real + self.ctx + .state + .instances + .write() + .unwrap() + .remove(&self.inputs.deployment.id); + + // And restart the subgraph + break; + } + + if let Some(stop_block) = &self.inputs.stop_block { + if block_ptr.number >= *stop_block { + info!(&logger, "stop block reached for subgraph"); + return Ok(()); + } + } + } + Err(BlockProcessingError::Canceled) => { + debug!(&logger, "Subgraph block stream shut down cleanly"); + return Ok(()); + } + + // Handle unexpected stream errors by marking the subgraph as failed. + Err(e) => { + // Clear entity cache when a subgraph fails. + // + // This is done to be safe and sure that there's no state that's + // out of sync from the database. + // + // Without it, POI changes on failure would be kept in the entity cache + // and be transacted incorrectly in the next run. + self.ctx.state.entity_lfu_cache = LfuCache::new(); + + deployment_failed.set(1.0); + + let message = format!("{:#}", e).replace("\n", "\t"); + let err = anyhow!("{}, code: {}", message, LogCode::SubgraphSyncingFailure); + let deterministic = e.is_deterministic(); + + let error = SubgraphError { + subgraph_id: id_for_err.clone(), + message, + block_ptr: Some(block_ptr), + handler: None, + deterministic, + }; + + match deterministic { + true => { + // Fail subgraph: + // - Change status/health. + // - Save the error to the database. + store_for_err + .fail_subgraph(error) + .await + .context("Failed to set subgraph status to `failed`")?; + + return Err(err); + } + false => { + // Shouldn't fail subgraph if it's already failed for non-deterministic + // reasons. + // + // If we don't do this check we would keep adding the same error to the + // database. + let should_fail_subgraph = self + .inputs + .store + .health(&self.inputs.deployment.hash) + .await? + != SubgraphHealth::Failed; + + if should_fail_subgraph { + // Fail subgraph: + // - Change status/health. + // - Save the error to the database. + store_for_err + .fail_subgraph(error) + .await + .context("Failed to set subgraph status to `failed`")?; + } + + // Retry logic below: + + // Cancel the stream for real. + self.ctx + .state + .instances + .write() + .unwrap() + .remove(&self.inputs.deployment.id); + + error!(logger, "Subgraph failed with non-deterministic error: {}", e; + "attempt" => backoff.attempt, + "retry_delay_s" => backoff.delay().as_secs()); + + // Sleep before restarting. + backoff.sleep_async().await; + + should_try_unfail_non_deterministic = true; + + // And restart the subgraph. + break; + } + } + } + } + } + } + } + + /// Processes a block and returns the updated context and a boolean flag indicating + /// whether new dynamic data sources have been added to the subgraph. + async fn process_block( + &mut self, + logger: &Logger, + triggers_adapter: Arc, + block_stream_cancel_handle: CancelHandle, + block: BlockWithTriggers, + firehose_cursor: Option, + ) -> Result { + let triggers = block.trigger_data; + let block = Arc::new(block.block); + let block_ptr = block.ptr(); + + let logger = logger.new(o!( + "block_number" => format!("{:?}", block_ptr.number), + "block_hash" => format!("{}", block_ptr.hash) + )); + + if triggers.len() == 1 { + debug!(&logger, "1 candidate trigger in this block"); + } else if triggers.len() > 1 { + debug!( + &logger, + "{} candidate triggers in this block", + triggers.len() + ); + } + + let metrics = self.ctx.subgraph_metrics.clone(); + + let proof_of_indexing = if self + .inputs + .store + .clone() + .supports_proof_of_indexing() + .await? + { + Some(Arc::new(AtomicRefCell::new(ProofOfIndexing::new( + block_ptr.number, + )))) + } else { + None + }; + + // There are currently no other causality regions since offchain data is not supported. + let causality_region = CausalityRegion::from_network(self.ctx.state.instance.network()); + + // Process events one after the other, passing in entity operations + // collected previously to every new event being processed + let mut block_state = match Self::process_triggers( + &logger, + BlockState::new( + self.inputs.store.clone(), + std::mem::take(&mut self.ctx.state.entity_lfu_cache), + ), + proof_of_indexing.cheap_clone(), + self.ctx.subgraph_metrics.clone(), + &self.ctx.state.instance, + &block, + triggers, + &causality_region, + &self.inputs.debug_fork, + ) + .await + { + // Triggers processed with no errors or with only deterministic errors. + Ok(block_state) => block_state, + + // Some form of unknown or non-deterministic error ocurred. + Err(MappingError::Unknown(e)) => return Err(BlockProcessingError::Unknown(e)), + Err(MappingError::PossibleReorg(e)) => { + info!(logger, + "Possible reorg detected, retrying"; + "error" => format!("{:#}", e), + ); + + // In case of a possible reorg, we want this function to do nothing and restart the + // block stream so it has a chance to detect the reorg. + // + // The `ctx` is unchanged at this point, except for having cleared the entity cache. + // Losing the cache is a bit annoying but not an issue for correctness. + // + // See also b21fa73b-6453-4340-99fb-1a78ec62efb1. + return Ok(true); + } + }; + + // If new data sources have been created, restart the subgraph after this block. + // This is necessary to re-create the block stream. + let needs_restart = block_state.has_created_data_sources(); + let host_metrics = self.ctx.host_metrics.clone(); + + // This loop will: + // 1. Instantiate created data sources. + // 2. Process those data sources for the current block. + // Until no data sources are created or MAX_DATA_SOURCES is hit. + + // Note that this algorithm processes data sources spawned on the same block _breadth + // first_ on the tree implied by the parent-child relationship between data sources. Only a + // very contrived subgraph would be able to observe this. + while block_state.has_created_data_sources() { + // Instantiate dynamic data sources, removing them from the block state. + let (data_sources, runtime_hosts) = self.create_dynamic_data_sources( + logger.clone(), + host_metrics.clone(), + block_state.drain_created_data_sources(), + )?; + + let filter = C::TriggerFilter::from_data_sources(data_sources.iter()); + + // Reprocess the triggers from this block that match the new data sources + let block_with_triggers = triggers_adapter + .triggers_in_block(&logger, block.as_ref().clone(), &filter) + .await?; + + let triggers = block_with_triggers.trigger_data; + + if triggers.len() == 1 { + info!( + &logger, + "1 trigger found in this block for the new data sources" + ); + } else if triggers.len() > 1 { + info!( + &logger, + "{} triggers found in this block for the new data sources", + triggers.len() + ); + } + + // Add entity operations for the new data sources to the block state + // and add runtimes for the data sources to the subgraph instance. + self.persist_dynamic_data_sources( + logger.clone(), + &mut block_state.entity_cache, + data_sources, + ); + + // Process the triggers in each host in the same order the + // corresponding data sources have been created. + for trigger in triggers { + block_state = SubgraphInstance::::process_trigger_in_runtime_hosts( + &logger, + &runtime_hosts, + &block, + &trigger, + block_state, + proof_of_indexing.cheap_clone(), + &causality_region, + &self.inputs.debug_fork, + ) + .await + .map_err(|e| { + // This treats a `PossibleReorg` as an ordinary error which will fail the subgraph. + // This can cause an unnecessary subgraph failure, to fix it we need to figure out a + // way to revert the effect of `create_dynamic_data_sources` so we may return a + // clean context as in b21fa73b-6453-4340-99fb-1a78ec62efb1. + match e { + MappingError::PossibleReorg(e) | MappingError::Unknown(e) => { + BlockProcessingError::Unknown(e) + } + } + })?; + } + } + + let has_errors = block_state.has_errors(); + let is_non_fatal_errors_active = self + .inputs + .features + .contains(&SubgraphFeature::NonFatalErrors); + + // Apply entity operations and advance the stream + + // Avoid writing to store if block stream has been canceled + if block_stream_cancel_handle.is_canceled() { + return Err(BlockProcessingError::Canceled); + } + + if let Some(proof_of_indexing) = proof_of_indexing { + let proof_of_indexing = Arc::try_unwrap(proof_of_indexing).unwrap().into_inner(); + update_proof_of_indexing( + proof_of_indexing, + &self.ctx.host_metrics.stopwatch, + &self.inputs.deployment.hash, + &mut block_state.entity_cache, + ) + .await?; + } + + let section = self + .ctx + .host_metrics + .stopwatch + .start_section("as_modifications"); + let ModificationsAndCache { + modifications: mut mods, + data_sources, + entity_lfu_cache: cache, + } = block_state + .entity_cache + .as_modifications() + .map_err(|e| BlockProcessingError::Unknown(e.into()))?; + section.end(); + + // Put the cache back in the ctx, asserting that the placeholder cache was not used. + assert!(self.ctx.state.entity_lfu_cache.is_empty()); + self.ctx.state.entity_lfu_cache = cache; + + if !mods.is_empty() { + info!(&logger, "Applying {} entity operation(s)", mods.len()); + } + + let err_count = block_state.deterministic_errors.len(); + for (i, e) in block_state.deterministic_errors.iter().enumerate() { + let message = format!("{:#}", e).replace("\n", "\t"); + error!(&logger, "Subgraph error {}/{}", i + 1, err_count; + "error" => message, + "code" => LogCode::SubgraphSyncingFailure + ); + } + + // Transact entity operations into the store and update the + // subgraph's block stream pointer + let _section = self + .ctx + .host_metrics + .stopwatch + .start_section("transact_block"); + let stopwatch = self.ctx.host_metrics.stopwatch.clone(); + let start = Instant::now(); + + let store = &self.inputs.store; + + // If a deterministic error has happened, make the PoI to be the only entity that'll be stored. + if has_errors && !is_non_fatal_errors_active { + let is_poi_entity = + |entity_mod: &EntityModification| entity_mod.entity_key().entity_type.is_poi(); + mods.retain(is_poi_entity); + // Confidence check + assert!( + mods.len() == 1, + "There should be only one PoI EntityModification" + ); + } + + let BlockState { + deterministic_errors, + .. + } = block_state; + + let first_error = deterministic_errors.first().cloned(); + + match store.transact_block_operations( + block_ptr, + firehose_cursor, + mods, + stopwatch, + data_sources, + deterministic_errors, + ) { + Ok(_) => { + // For subgraphs with `nonFatalErrors` feature disabled, we consider + // any error as fatal. + // + // So we do an early return to make the subgraph stop processing blocks. + // + // In this scenario the only entity that is stored/transacted is the PoI, + // all of the others are discarded. + if has_errors && !is_non_fatal_errors_active { + // Only the first error is reported. + return Err(BlockProcessingError::Deterministic(first_error.unwrap())); + } + + let elapsed = start.elapsed().as_secs_f64(); + metrics.block_ops_transaction_duration.observe(elapsed); + + // To prevent a buggy pending version from replacing a current version, if errors are + // present the subgraph will be unassigned. + if has_errors && !*DISABLE_FAIL_FAST && !store.is_deployment_synced().await? { + store + .unassign_subgraph() + .map_err(|e| BlockProcessingError::Unknown(e.into()))?; + + // Use `Canceled` to avoiding setting the subgraph health to failed, an error was + // just transacted so it will be already be set to unhealthy. + return Err(BlockProcessingError::Canceled); + } + + Ok(needs_restart) + } + + Err(e) => { + Err(anyhow!("Error while processing block stream for a subgraph: {}", e).into()) + } + } + } + + async fn process_triggers( + logger: &Logger, + mut block_state: BlockState, + proof_of_indexing: SharedProofOfIndexing, + subgraph_metrics: Arc, + instance: &SubgraphInstance>, + block: &Arc, + triggers: Vec, + causality_region: &str, + debug_fork: &Option>, + ) -> Result, MappingError> { + use graph::blockchain::TriggerData; + + for trigger in triggers.into_iter() { + let start = Instant::now(); + block_state = instance + .process_trigger( + &logger, + block, + &trigger, + block_state, + proof_of_indexing.cheap_clone(), + causality_region, + debug_fork, + ) + .await + .map_err(move |mut e| { + let error_context = trigger.error_context(); + if !error_context.is_empty() { + e = e.context(error_context); + } + e.context("failed to process trigger".to_string()) + })?; + let elapsed = start.elapsed().as_secs_f64(); + subgraph_metrics.observe_trigger_processing_duration(elapsed); + } + Ok(block_state) + } + + fn create_dynamic_data_sources( + &mut self, + logger: Logger, + host_metrics: Arc, + created_data_sources: Vec>, + ) -> Result<(Vec, Vec>), Error> { + let mut data_sources = vec![]; + let mut runtime_hosts = vec![]; + + for info in created_data_sources { + // Try to instantiate a data source from the template + let data_source = C::DataSource::try_from(info)?; + + // Try to create a runtime host for the data source + let host = self.ctx.state.instance.add_dynamic_data_source( + &logger, + data_source.clone(), + self.inputs.templates.clone(), + host_metrics.clone(), + )?; + + match host { + Some(host) => { + data_sources.push(data_source); + runtime_hosts.push(host); + } + None => { + fail_point!("error_on_duplicate_ds", |_| Err(anyhow!("duplicate ds"))); + warn!( + logger, + "no runtime hosted created, there is already a runtime host instantiated for \ + this data source"; + "name" => &data_source.name(), + "address" => &data_source.address() + .map(|address| hex::encode(address)) + .unwrap_or("none".to_string()), + ) + } + } + } + + Ok((data_sources, runtime_hosts)) + } + + fn persist_dynamic_data_sources( + &mut self, + logger: Logger, + entity_cache: &mut EntityCache, + data_sources: Vec, + ) { + if !data_sources.is_empty() { + debug!( + logger, + "Creating {} dynamic data source(s)", + data_sources.len() + ); + } + + // Add entity operations to the block state in order to persist + // the dynamic data sources + for data_source in data_sources.iter() { + debug!( + logger, + "Persisting data_source"; + "name" => &data_source.name(), + "address" => &data_source.address().map(|address| hex::encode(address)).unwrap_or("none".to_string()), + ); + entity_cache.add_data_source(data_source); + } + + // Merge filters from data sources into the block stream builder + self.ctx.state.filter.extend(data_sources.iter()); + } +} + +/// Transform the proof of indexing changes into entity updates that will be +/// inserted when as_modifications is called. +async fn update_proof_of_indexing( + proof_of_indexing: ProofOfIndexing, + stopwatch: &StopwatchMetrics, + deployment_id: &DeploymentHash, + entity_cache: &mut EntityCache, +) -> Result<(), Error> { + let _section_guard = stopwatch.start_section("update_proof_of_indexing"); + + let mut proof_of_indexing = proof_of_indexing.take(); + + for (causality_region, stream) in proof_of_indexing.drain() { + // Create the special POI entity key specific to this causality_region + let entity_key = EntityKey { + subgraph_id: deployment_id.clone(), + entity_type: POI_OBJECT.to_owned(), + entity_id: causality_region, + }; + + // Grab the current digest attribute on this entity + let prev_poi = + entity_cache + .get(&entity_key) + .map_err(Error::from)? + .map(|entity| match entity.get("digest") { + Some(Value::Bytes(b)) => b.clone(), + _ => panic!("Expected POI entity to have a digest and for it to be bytes"), + }); + + // Finish the POI stream, getting the new POI value. + let updated_proof_of_indexing = stream.pause(prev_poi.as_deref()); + let updated_proof_of_indexing: Bytes = (&updated_proof_of_indexing[..]).into(); + + // Put this onto an entity with the same digest attribute + // that was expected before when reading. + let new_poi_entity = entity! { + id: entity_key.entity_id.clone(), + digest: updated_proof_of_indexing, + }; + + entity_cache.set(entity_key, new_poi_entity)?; + } + + Ok(()) +} + +/// Checks if the Deployment BlockPtr is at least one block behind to the chain head. +fn is_deployment_synced(deployment_head_ptr: &BlockPtr, chain_head_ptr: Option) -> bool { + matches!((deployment_head_ptr, &chain_head_ptr), (b1, Some(b2)) if b1.number >= (b2.number - 1)) +} + +#[test] +fn test_is_deployment_synced() { + let block_0 = BlockPtr::try_from(( + "bd34884280958002c51d3f7b5f853e6febeba33de0f40d15b0363006533c924f", + 0, + )) + .unwrap(); + let block_1 = BlockPtr::try_from(( + "8511fa04b64657581e3f00e14543c1d522d5d7e771b54aa3060b662ade47da13", + 1, + )) + .unwrap(); + let block_2 = BlockPtr::try_from(( + "b98fb783b49de5652097a989414c767824dff7e7fd765a63b493772511db81c1", + 2, + )) + .unwrap(); + + assert!(!is_deployment_synced(&block_0, None)); + assert!(!is_deployment_synced(&block_2, None)); + + assert!(!is_deployment_synced(&block_0, Some(block_2.clone()))); + + assert!(is_deployment_synced(&block_1, Some(block_2.clone()))); + assert!(is_deployment_synced(&block_2, Some(block_2.clone()))); +} From ebd7354f7cced493e119d0c90b76058d64e12413 Mon Sep 17 00:00:00 2001 From: Victor Velev <22171622+VIVelev@users.noreply.github.com> Date: Mon, 21 Feb 2022 09:57:39 -0500 Subject: [PATCH 0010/2357] node: make terminating slash in fork-base optional (#3259) --- node/src/bin/manager.rs | 25 ++++++++++++++++++------- node/src/main.rs | 13 ++++++++++++- store/postgres/src/fork.rs | 2 +- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index 88df7126f48..2aab4f8ab7f 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -615,14 +615,25 @@ async fn main() { Ok(node) => node, }; - let fork_base = match opt.fork_base { - Some(url) => match Url::parse(&url) { - Err(e) => { - eprintln!("invalid fork base URL: {}", e); - std::process::exit(1); + let fork_base = match &opt.fork_base { + Some(url) => { + // Make sure the endpoint ends with a terminating slash. + let url = if !url.ends_with("/") { + let mut url = url.clone(); + url.push('/'); + Url::parse(&url) + } else { + Url::parse(url) + }; + + match url { + Err(e) => { + eprintln!("invalid fork base URL: {}", e); + std::process::exit(1); + } + Ok(url) => Some(url), } - Ok(url) => Some(url), - }, + } None => None, }; diff --git a/node/src/main.rs b/node/src/main.rs index ff0cd943a7c..0ee7ec22f13 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -132,7 +132,18 @@ async fn main() { // Obtain the fork base URL let fork_base = match &opt.fork_base { - Some(url) => Some(Url::parse(url).expect("Failed to parse the fork base URL")), + Some(url) => { + // Make sure the endpoint ends with a terminating slash. + let url = if !url.ends_with("/") { + let mut url = url.clone(); + url.push('/'); + Url::parse(&url) + } else { + Url::parse(url) + }; + + Some(url.expect("Failed to parse the fork base URL")) + } None => { warn!( logger, diff --git a/store/postgres/src/fork.rs b/store/postgres/src/fork.rs index 19a87da378a..1b11627b523 100644 --- a/store/postgres/src/fork.rs +++ b/store/postgres/src/fork.rs @@ -33,7 +33,7 @@ struct Variables { /// SubgraphFork represents a simple subgraph forking mechanism /// which lazily fetches entities from a remote subgraph's store -/// associated with a GraphQL endpoint at `fork_url`. +/// associated with a GraphQL `endpoint`. /// /// Since this mechanism is used for debug forks, entities are /// fetched only once per id in order to avoid fetching an entity From 42f4d3e48d6ea9d40a49178b31fbd4d8afaf06ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Feb 2022 21:07:08 +0000 Subject: [PATCH 0011/2357] build(deps): bump anyhow from 1.0.53 to 1.0.54 Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.53 to 1.0.54. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.53...1.0.54) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- store/postgres/Cargo.toml | 2 +- tests/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3231bf77a0a..31f540c7cf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.53" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" +checksum = "7a99269dff3bc004caa411f38845c20303f1e393ca2bd6581576fa3a7f59577d" [[package]] name = "arc-swap" diff --git a/store/postgres/Cargo.toml b/store/postgres/Cargo.toml index 59b9aa7de89..d1b57f43d57 100644 --- a/store/postgres/Cargo.toml +++ b/store/postgres/Cargo.toml @@ -29,7 +29,7 @@ uuid = { version = "0.8.1", features = ["v4"] } stable-hash = { git = "https://github.com/graphprotocol/stable-hash" } backtrace = "0.3" diesel_derives = "1.4.1" -anyhow = "1.0.53" +anyhow = "1.0.54" git-testament = "0.2.0" itertools = "0.10.3" pin-utils = "0.1" diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 045943e30a5..3bbae7612ad 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -9,5 +9,5 @@ tokio = {version = "1.16.1", features = ["rt", "macros", "process"]} tokio-stream = "0.1" futures = "0.3.13" port_check = "0.1.5" -anyhow = "1.0.53" +anyhow = "1.0.54" lazy_static = "1.4.0" From 99047529d8b845338a20ae41433f6605ee1e2912 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Feb 2022 21:07:18 +0000 Subject: [PATCH 0012/2357] build(deps): bump semver from 1.0.5 to 1.0.6 Bumps [semver](https://github.com/dtolnay/semver) from 1.0.5 to 1.0.6. - [Release notes](https://github.com/dtolnay/semver/releases) - [Commits](https://github.com/dtolnay/semver/compare/1.0.5...1.0.6) --- updated-dependencies: - dependency-name: semver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 16 ++++++++-------- chain/ethereum/Cargo.toml | 2 +- core/Cargo.toml | 2 +- graph/Cargo.toml | 2 +- runtime/wasm/Cargo.toml | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 31f540c7cf7..4fccdff3939 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1450,7 +1450,7 @@ dependencies = [ "prost-types", "rand 0.6.5", "reqwest", - "semver 1.0.5", + "semver 1.0.6", "serde", "serde_derive", "serde_json", @@ -1496,7 +1496,7 @@ dependencies = [ "mockall", "prost", "prost-types", - "semver 1.0.5", + "semver 1.0.6", "serde", "test-store", "tiny-keccak 1.5.0", @@ -1544,7 +1544,7 @@ dependencies = [ "lazy_static", "lru_time_cache", "pretty_assertions 1.1.0", - "semver 1.0.5", + "semver 1.0.6", "serde", "serde_json", "serde_yaml", @@ -1636,7 +1636,7 @@ dependencies = [ "graph-core", "graph-mock", "graph-runtime-wasm", - "semver 1.0.5", + "semver 1.0.6", "test-store", "wasmtime", ] @@ -1661,7 +1661,7 @@ dependencies = [ "never", "parity-wasm", "pwasm-utils", - "semver 1.0.5", + "semver 1.0.6", "strum", "strum_macros", "uuid 0.8.2", @@ -3687,7 +3687,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.5", + "semver 1.0.6", ] [[package]] @@ -3843,9 +3843,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" +checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" dependencies = [ "serde", ] diff --git a/chain/ethereum/Cargo.toml b/chain/ethereum/Cargo.toml index 22f64655d07..ca24e1b695f 100644 --- a/chain/ethereum/Cargo.toml +++ b/chain/ethereum/Cargo.toml @@ -17,7 +17,7 @@ dirs-next = "2.0" anyhow = "1.0" tiny-keccak = "1.5.0" hex = "0.4.3" -semver = "1.0.5" +semver = "1.0.6" itertools = "0.10.3" diff --git a/core/Cargo.toml b/core/Cargo.toml index dba675b22d0..4394b4530a4 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,7 +17,7 @@ graph-chain-ethereum = { path = "../chain/ethereum" } graph-chain-near = { path = "../chain/near" } lazy_static = "1.2.0" lru_time_cache = "0.11" -semver = "1.0.5" +semver = "1.0.6" serde = "1.0" serde_json = "1.0" serde_yaml = "0.8" diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 1745371384a..5be4ded0e3a 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -27,7 +27,7 @@ num-bigint = { version = "^0.2.6", features = ["serde"] } num_cpus = "1.13.1" num-traits = "0.2.14" rand = "0.6.1" -semver = {version = "1.0.5", features = ["serde"]} +semver = {version = "1.0.6", features = ["serde"]} serde = { version = "1.0.126", features = ["rc"] } serde_derive = "1.0.125" serde_json = { version = "1.0", features = ["arbitrary_precision"] } diff --git a/runtime/wasm/Cargo.toml b/runtime/wasm/Cargo.toml index 5290d97c6e5..bb0353a044b 100644 --- a/runtime/wasm/Cargo.toml +++ b/runtime/wasm/Cargo.toml @@ -13,7 +13,7 @@ graph = { path = "../../graph" } graph-graphql = { path = "../../graphql" } bs58 = "0.4.0" graph-runtime-derive = { path = "../derive" } -semver = "1.0.5" +semver = "1.0.6" lazy_static = "1.4" uuid = { version = "0.8.1", features = ["v4"] } strum = "0.21.0" From 4426f9c6ebbfc046c772a8865240a89cdee2c8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Pace?= Date: Tue, 22 Feb 2022 07:27:44 -0300 Subject: [PATCH 0013/2357] instance_manager: Skip block updates when triggers are empty and threshold isn't met (#3223) This should primarily improve the performance of indexing on: - NEAR + firehose (we used to update on every pointer) - sparse subgraphs (where entity updates would happen with a big gap between block numbers) --- core/src/subgraph/runner.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/src/subgraph/runner.rs b/core/src/subgraph/runner.rs index 68d61939a45..2fbdc05a50c 100644 --- a/core/src/subgraph/runner.rs +++ b/core/src/subgraph/runner.rs @@ -26,6 +26,8 @@ use std::sync::Arc; use std::time::{Duration, Instant}; const MINUTE: Duration = Duration::from_secs(60); +const SKIP_PTR_UPDATES_THRESHOLD: Duration = Duration::from_secs(60 * 5); + const BUFFERED_BLOCK_STREAM_SIZE: usize = 100; const BUFFERED_FIREHOSE_STREAM_SIZE: usize = 1; @@ -113,6 +115,7 @@ where let mut should_try_unfail_deterministic = true; let mut should_try_unfail_non_deterministic = true; let mut synced = false; + let mut skip_ptr_updates_timer = Instant::now(); // Exponential backoff that starts with two minutes and keeps // increasing its timeout exponentially until it reaches the ceiling. @@ -228,6 +231,14 @@ where .observe(block.trigger_count() as f64); } + if block.trigger_count() == 0 + && skip_ptr_updates_timer.elapsed() <= SKIP_PTR_UPDATES_THRESHOLD + { + continue; + } else { + skip_ptr_updates_timer = Instant::now(); + } + let start = Instant::now(); let deployment_failed = self.ctx.block_stream_metrics.deployment_failed.clone(); From dfc97584f34d4f7d9ec67c2de72b75a86c66a5c3 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Tue, 22 Feb 2022 10:20:52 +0000 Subject: [PATCH 0014/2357] node: Improve error when the node id is not in the config --- node/src/config.rs | 2 +- node/src/store_builder.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/src/config.rs b/node/src/config.rs index c56b69a1d89..109a89d21c2 100644 --- a/node/src/config.rs +++ b/node/src/config.rs @@ -325,7 +325,7 @@ impl PoolSize { .map(|rule| rule.size) .ok_or_else(|| { anyhow!( - "no rule matches `{}` for the pool of shard {}", + "no rule matches node id `{}` for the pool of shard {}", node.as_str(), name ) diff --git a/node/src/store_builder.rs b/node/src/store_builder.rs index 4a67dbcfd19..b49b62548ca 100644 --- a/node/src/store_builder.rs +++ b/node/src/store_builder.rs @@ -194,11 +194,11 @@ impl StoreBuilder { ) -> ConnectionPool { let logger = logger.new(o!("pool" => "main")); let pool_size = shard.pool_size.size_for(node, name).expect(&format!( - "we can determine the pool size for store {}", + "cannot determine the pool size for store {}", name )); let fdw_pool_size = shard.fdw_pool_size.size_for(node, name).expect(&format!( - "we can determine the fdw pool size for store {}", + "cannot determine the fdw pool size for store {}", name )); info!( From 5d4006f8d9e14d08aa5ee4f91a6f44fa2cb23b9c Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Wed, 19 Jan 2022 12:39:43 -0300 Subject: [PATCH 0015/2357] runtime: Add gas costs to host operations on wasm memory --- Cargo.toml | 5 +- chain/ethereum/src/runtime/abi.rs | 201 ++++--- chain/ethereum/src/runtime/runtime_adapter.rs | 6 +- chain/ethereum/src/trigger.rs | 21 +- chain/near/src/runtime/abi.rs | 272 +++++---- chain/near/src/trigger.rs | 29 +- graph/src/blockchain/mock.rs | 2 + graph/src/blockchain/mod.rs | 9 +- graph/src/data/subgraph/api_version.rs | 2 +- graph/src/runtime/asc_heap.rs | 46 +- graph/src/runtime/asc_ptr.rs | 34 +- graph/src/runtime/mod.rs | 4 + runtime/test/src/test.rs | 267 +++++---- runtime/test/src/test/abi.rs | 151 ++--- .../api_version_0_0_5/array_blowup.ts | 20 + .../api_version_0_0_5/array_blowup.wasm | Bin 0 -> 6708 bytes .../api_version_0_0_5/common/types.ts | 558 +++++++++--------- runtime/wasm/src/asc_abi/class.rs | 34 +- runtime/wasm/src/asc_abi/v0_0_4.rs | 19 +- runtime/wasm/src/asc_abi/v0_0_5.rs | 15 +- runtime/wasm/src/gas_rules.rs | 11 +- runtime/wasm/src/module/mod.rs | 277 +++++---- runtime/wasm/src/to_from/external.rs | 117 ++-- runtime/wasm/src/to_from/mod.rs | 55 +- 24 files changed, 1203 insertions(+), 952 deletions(-) create mode 100644 runtime/test/wasm_test/api_version_0_0_5/array_blowup.ts create mode 100644 runtime/test/wasm_test/api_version_0_0_5/array_blowup.wasm diff --git a/Cargo.toml b/Cargo.toml index 66edf4c25b7..cec99ebbe69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,8 @@ members = [ ] # Incremental compilation on Rust 1.58 causes an ICE on build. As soon as graph node builds again, these can be removed. -[profile.dev] -incremental = false [profile.test] incremental = false + +[profile.dev] +incremental = false diff --git a/chain/ethereum/src/runtime/abi.rs b/chain/ethereum/src/runtime/abi.rs index 4050f81a7d1..a6748715f70 100644 --- a/chain/ethereum/src/runtime/abi.rs +++ b/chain/ethereum/src/runtime/abi.rs @@ -1,4 +1,5 @@ use graph::prelude::{ethabi, BigInt}; +use graph::runtime::gas::GasCounter; use graph::runtime::{asc_get, asc_new, AscPtr, DeterministicHostError, FromAscObj, ToAscObj}; use graph::runtime::{AscHeap, AscIndexId, AscType, IndexForAscTypeId}; use graph_runtime_derive::AscType; @@ -33,10 +34,11 @@ impl ToAscObj for Vec { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Ok(AscLogParamArray(Array::new(&*content, heap)?)) + Ok(AscLogParamArray(Array::new(&*content, heap, gas)?)) } } @@ -62,13 +64,14 @@ impl FromAscObj for UnresolvedContractCall { fn from_asc_obj( asc_call: AscUnresolvedContractCall_0_0_4, heap: &H, + gas: &GasCounter, ) -> Result { Ok(UnresolvedContractCall { - contract_name: asc_get(heap, asc_call.contract_name)?, - contract_address: asc_get(heap, asc_call.contract_address)?, - function_name: asc_get(heap, asc_call.function_name)?, - function_signature: Some(asc_get(heap, asc_call.function_signature)?), - function_args: asc_get(heap, asc_call.function_args)?, + contract_name: asc_get(heap, asc_call.contract_name, gas)?, + contract_address: asc_get(heap, asc_call.contract_address, gas)?, + function_name: asc_get(heap, asc_call.function_name, gas)?, + function_signature: Some(asc_get(heap, asc_call.function_signature, gas)?), + function_args: asc_get(heap, asc_call.function_args, gas)?, }) } } @@ -86,13 +89,14 @@ impl FromAscObj for UnresolvedContractCall { fn from_asc_obj( asc_call: AscUnresolvedContractCall, heap: &H, + gas: &GasCounter, ) -> Result { Ok(UnresolvedContractCall { - contract_name: asc_get(heap, asc_call.contract_name)?, - contract_address: asc_get(heap, asc_call.contract_address)?, - function_name: asc_get(heap, asc_call.function_name)?, + contract_name: asc_get(heap, asc_call.contract_name, gas)?, + contract_address: asc_get(heap, asc_call.contract_address, gas)?, + function_name: asc_get(heap, asc_call.function_name, gas)?, function_signature: None, - function_args: asc_get(heap, asc_call.function_args)?, + function_args: asc_get(heap, asc_call.function_args, gas)?, }) } } @@ -275,24 +279,29 @@ impl ToAscObj for EthereumBlockData { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscEthereumBlock { - hash: asc_new(heap, &self.hash)?, - parent_hash: asc_new(heap, &self.parent_hash)?, - uncles_hash: asc_new(heap, &self.uncles_hash)?, - author: asc_new(heap, &self.author)?, - state_root: asc_new(heap, &self.state_root)?, - transactions_root: asc_new(heap, &self.transactions_root)?, - receipts_root: asc_new(heap, &self.receipts_root)?, - number: asc_new(heap, &BigInt::from(self.number))?, - gas_used: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_used))?, - gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_limit))?, - timestamp: asc_new(heap, &BigInt::from_unsigned_u256(&self.timestamp))?, - difficulty: asc_new(heap, &BigInt::from_unsigned_u256(&self.difficulty))?, - total_difficulty: asc_new(heap, &BigInt::from_unsigned_u256(&self.total_difficulty))?, + hash: asc_new(heap, &self.hash, gas)?, + parent_hash: asc_new(heap, &self.parent_hash, gas)?, + uncles_hash: asc_new(heap, &self.uncles_hash, gas)?, + author: asc_new(heap, &self.author, gas)?, + state_root: asc_new(heap, &self.state_root, gas)?, + transactions_root: asc_new(heap, &self.transactions_root, gas)?, + receipts_root: asc_new(heap, &self.receipts_root, gas)?, + number: asc_new(heap, &BigInt::from(self.number), gas)?, + gas_used: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_used), gas)?, + gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_limit), gas)?, + timestamp: asc_new(heap, &BigInt::from_unsigned_u256(&self.timestamp), gas)?, + difficulty: asc_new(heap, &BigInt::from_unsigned_u256(&self.difficulty), gas)?, + total_difficulty: asc_new( + heap, + &BigInt::from_unsigned_u256(&self.total_difficulty), + gas, + )?, size: self .size - .map(|size| asc_new(heap, &BigInt::from_unsigned_u256(&size))) + .map(|size| asc_new(heap, &BigInt::from_unsigned_u256(&size), gas)) .unwrap_or(Ok(AscPtr::null()))?, }) } @@ -302,28 +311,33 @@ impl ToAscObj for EthereumBlockData { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscEthereumBlock_0_0_6 { - hash: asc_new(heap, &self.hash)?, - parent_hash: asc_new(heap, &self.parent_hash)?, - uncles_hash: asc_new(heap, &self.uncles_hash)?, - author: asc_new(heap, &self.author)?, - state_root: asc_new(heap, &self.state_root)?, - transactions_root: asc_new(heap, &self.transactions_root)?, - receipts_root: asc_new(heap, &self.receipts_root)?, - number: asc_new(heap, &BigInt::from(self.number))?, - gas_used: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_used))?, - gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_limit))?, - timestamp: asc_new(heap, &BigInt::from_unsigned_u256(&self.timestamp))?, - difficulty: asc_new(heap, &BigInt::from_unsigned_u256(&self.difficulty))?, - total_difficulty: asc_new(heap, &BigInt::from_unsigned_u256(&self.total_difficulty))?, + hash: asc_new(heap, &self.hash, gas)?, + parent_hash: asc_new(heap, &self.parent_hash, gas)?, + uncles_hash: asc_new(heap, &self.uncles_hash, gas)?, + author: asc_new(heap, &self.author, gas)?, + state_root: asc_new(heap, &self.state_root, gas)?, + transactions_root: asc_new(heap, &self.transactions_root, gas)?, + receipts_root: asc_new(heap, &self.receipts_root, gas)?, + number: asc_new(heap, &BigInt::from(self.number), gas)?, + gas_used: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_used), gas)?, + gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_limit), gas)?, + timestamp: asc_new(heap, &BigInt::from_unsigned_u256(&self.timestamp), gas)?, + difficulty: asc_new(heap, &BigInt::from_unsigned_u256(&self.difficulty), gas)?, + total_difficulty: asc_new( + heap, + &BigInt::from_unsigned_u256(&self.total_difficulty), + gas, + )?, size: self .size - .map(|size| asc_new(heap, &BigInt::from_unsigned_u256(&size))) + .map(|size| asc_new(heap, &BigInt::from_unsigned_u256(&size), gas)) .unwrap_or(Ok(AscPtr::null()))?, base_fee_per_block: self .base_fee_per_gas - .map(|base_fee| asc_new(heap, &BigInt::from_unsigned_u256(&base_fee))) + .map(|base_fee| asc_new(heap, &BigInt::from_unsigned_u256(&base_fee), gas)) .unwrap_or(Ok(AscPtr::null()))?, }) } @@ -333,18 +347,19 @@ impl ToAscObj for EthereumTransactionData { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscEthereumTransaction_0_0_1 { - hash: asc_new(heap, &self.hash)?, - index: asc_new(heap, &BigInt::from(self.index))?, - from: asc_new(heap, &self.from)?, + hash: asc_new(heap, &self.hash, gas)?, + index: asc_new(heap, &BigInt::from(self.index), gas)?, + from: asc_new(heap, &self.from, gas)?, to: self .to - .map(|to| asc_new(heap, &to)) + .map(|to| asc_new(heap, &to, gas)) .unwrap_or(Ok(AscPtr::null()))?, - value: asc_new(heap, &BigInt::from_unsigned_u256(&self.value))?, - gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_limit))?, - gas_price: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_price))?, + value: asc_new(heap, &BigInt::from_unsigned_u256(&self.value), gas)?, + gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_limit), gas)?, + gas_price: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_price), gas)?, }) } } @@ -353,19 +368,20 @@ impl ToAscObj for EthereumTransactionData { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscEthereumTransaction_0_0_2 { - hash: asc_new(heap, &self.hash)?, - index: asc_new(heap, &BigInt::from(self.index))?, - from: asc_new(heap, &self.from)?, + hash: asc_new(heap, &self.hash, gas)?, + index: asc_new(heap, &BigInt::from(self.index), gas)?, + from: asc_new(heap, &self.from, gas)?, to: self .to - .map(|to| asc_new(heap, &to)) + .map(|to| asc_new(heap, &to, gas)) .unwrap_or(Ok(AscPtr::null()))?, - value: asc_new(heap, &BigInt::from_unsigned_u256(&self.value))?, - gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_limit))?, - gas_price: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_price))?, - input: asc_new(heap, &*self.input)?, + value: asc_new(heap, &BigInt::from_unsigned_u256(&self.value), gas)?, + gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_limit), gas)?, + gas_price: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_price), gas)?, + input: asc_new(heap, &*self.input, gas)?, }) } } @@ -374,20 +390,21 @@ impl ToAscObj for EthereumTransactionData { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscEthereumTransaction_0_0_6 { - hash: asc_new(heap, &self.hash)?, - index: asc_new(heap, &BigInt::from(self.index))?, - from: asc_new(heap, &self.from)?, + hash: asc_new(heap, &self.hash, gas)?, + index: asc_new(heap, &BigInt::from(self.index), gas)?, + from: asc_new(heap, &self.from, gas)?, to: self .to - .map(|to| asc_new(heap, &to)) + .map(|to| asc_new(heap, &to, gas)) .unwrap_or(Ok(AscPtr::null()))?, - value: asc_new(heap, &BigInt::from_unsigned_u256(&self.value))?, - gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_limit))?, - gas_price: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_price))?, - input: asc_new(heap, &*self.input)?, - nonce: asc_new(heap, &BigInt::from_unsigned_u256(&self.nonce))?, + value: asc_new(heap, &BigInt::from_unsigned_u256(&self.value), gas)?, + gas_limit: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_limit), gas)?, + gas_price: asc_new(heap, &BigInt::from_unsigned_u256(&self.gas_price), gas)?, + input: asc_new(heap, &*self.input, gas)?, + nonce: asc_new(heap, &BigInt::from_unsigned_u256(&self.nonce), gas)?, }) } } @@ -402,22 +419,24 @@ where fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { Ok(AscEthereumEvent { - address: asc_new(heap, &self.address)?, - log_index: asc_new(heap, &BigInt::from_unsigned_u256(&self.log_index))?, + address: asc_new(heap, &self.address, gas)?, + log_index: asc_new(heap, &BigInt::from_unsigned_u256(&self.log_index), gas)?, transaction_log_index: asc_new( heap, &BigInt::from_unsigned_u256(&self.transaction_log_index), + gas, )?, log_type: self .log_type .clone() - .map(|log_type| asc_new(heap, &log_type)) + .map(|log_type| asc_new(heap, &log_type, gas)) .unwrap_or(Ok(AscPtr::null()))?, - block: asc_new::(heap, &self.block)?, - transaction: asc_new::(heap, &self.transaction)?, - params: asc_new(heap, &self.params)?, + block: asc_new::(heap, &self.block, gas)?, + transaction: asc_new::(heap, &self.transaction, gas)?, + params: asc_new(heap, &self.params, gas)?, }) } } @@ -426,13 +445,14 @@ impl ToAscObj for EthereumCallData { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscEthereumCall { - address: asc_new(heap, &self.to)?, - block: asc_new(heap, &self.block)?, - transaction: asc_new(heap, &self.transaction)?, - inputs: asc_new(heap, &self.inputs)?, - outputs: asc_new(heap, &self.outputs)?, + address: asc_new(heap, &self.to, gas)?, + block: asc_new(heap, &self.block, gas)?, + transaction: asc_new(heap, &self.transaction, gas)?, + inputs: asc_new(heap, &self.inputs, gas)?, + outputs: asc_new(heap, &self.outputs, gas)?, }) } } @@ -443,17 +463,18 @@ impl ToAscObj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result< AscEthereumCall_0_0_3, DeterministicHostError, > { Ok(AscEthereumCall_0_0_3 { - to: asc_new(heap, &self.to)?, - from: asc_new(heap, &self.from)?, - block: asc_new(heap, &self.block)?, - transaction: asc_new(heap, &self.transaction)?, - inputs: asc_new(heap, &self.inputs)?, - outputs: asc_new(heap, &self.outputs)?, + to: asc_new(heap, &self.to, gas)?, + from: asc_new(heap, &self.from, gas)?, + block: asc_new(heap, &self.block, gas)?, + transaction: asc_new(heap, &self.transaction, gas)?, + inputs: asc_new(heap, &self.inputs, gas)?, + outputs: asc_new(heap, &self.outputs, gas)?, }) } } @@ -464,17 +485,18 @@ impl ToAscObj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result< AscEthereumCall_0_0_3, DeterministicHostError, > { Ok(AscEthereumCall_0_0_3 { - to: asc_new(heap, &self.to)?, - from: asc_new(heap, &self.from)?, - block: asc_new(heap, &self.block)?, - transaction: asc_new(heap, &self.transaction)?, - inputs: asc_new(heap, &self.inputs)?, - outputs: asc_new(heap, &self.outputs)?, + to: asc_new(heap, &self.to, gas)?, + from: asc_new(heap, &self.from, gas)?, + block: asc_new(heap, &self.block, gas)?, + transaction: asc_new(heap, &self.transaction, gas)?, + inputs: asc_new(heap, &self.inputs, gas)?, + outputs: asc_new(heap, &self.outputs, gas)?, }) } } @@ -483,10 +505,11 @@ impl ToAscObj for ethabi::LogParam { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscLogParam { - name: asc_new(heap, self.name.as_str())?, - value: asc_new(heap, &self.value)?, + name: asc_new(heap, self.name.as_str(), gas)?, + value: asc_new(heap, &self.value, gas)?, }) } } diff --git a/chain/ethereum/src/runtime/runtime_adapter.rs b/chain/ethereum/src/runtime/runtime_adapter.rs index 55c2d4a01e0..1febd90b94a 100644 --- a/chain/ethereum/src/runtime/runtime_adapter.rs +++ b/chain/ethereum/src/runtime/runtime_adapter.rs @@ -77,9 +77,9 @@ fn ethereum_call( // function signature; subgraphs using an apiVersion < 0.0.4 don't pass // the signature along with the call. let call: UnresolvedContractCall = if ctx.heap.api_version() >= Version::new(0, 0, 4) { - asc_get::<_, AscUnresolvedContractCall_0_0_4, _>(ctx.heap, wasm_ptr.into())? + asc_get::<_, AscUnresolvedContractCall_0_0_4, _>(ctx.heap, wasm_ptr.into(), &ctx.gas)? } else { - asc_get::<_, AscUnresolvedContractCall, _>(ctx.heap, wasm_ptr.into())? + asc_get::<_, AscUnresolvedContractCall, _>(ctx.heap, wasm_ptr.into(), &ctx.gas)? }; let result = eth_call( @@ -91,7 +91,7 @@ fn ethereum_call( abis, )?; match result { - Some(tokens) => Ok(asc_new(ctx.heap, tokens.as_slice())?), + Some(tokens) => Ok(asc_new(ctx.heap, tokens.as_slice(), &ctx.gas)?), None => Ok(AscPtr::null()), } } diff --git a/chain/ethereum/src/trigger.rs b/chain/ethereum/src/trigger.rs index a280f417d41..bc03890e1d0 100644 --- a/chain/ethereum/src/trigger.rs +++ b/chain/ethereum/src/trigger.rs @@ -15,6 +15,7 @@ use graph::prelude::BlockNumber; use graph::prelude::BlockPtr; use graph::prelude::{CheapClone, EthereumCall}; use graph::runtime::asc_new; +use graph::runtime::gas::GasCounter; use graph::runtime::AscHeap; use graph::runtime::AscPtr; use graph::runtime::DeterministicHostError; @@ -104,7 +105,11 @@ impl std::fmt::Debug for MappingTrigger { } impl blockchain::MappingTrigger for MappingTrigger { - fn to_asc_ptr(self, heap: &mut H) -> Result, DeterministicHostError> { + fn to_asc_ptr( + self, + heap: &mut H, + gas: &GasCounter, + ) -> Result, DeterministicHostError> { Ok(match self { MappingTrigger::Log { block, @@ -127,18 +132,20 @@ impl blockchain::MappingTrigger for MappingTrigger { AscEthereumEvent, _, _, - >(heap, ðereum_event_data)? + >(heap, ðereum_event_data, gas)? .erase() } else if api_version >= Version::new(0, 0, 2) { asc_new::, _, _>( heap, ðereum_event_data, + gas )? .erase() } else { asc_new::, _, _>( heap, ðereum_event_data, + gas )? .erase() } @@ -163,25 +170,25 @@ impl blockchain::MappingTrigger for MappingTrigger { AscEthereumCall_0_0_3, _, _, - >(heap, &call)? + >(heap, &call, gas)? .erase() } else if heap.api_version() >= Version::new(0, 0, 3) { asc_new::< AscEthereumCall_0_0_3, _, _, - >(heap, &call)? + >(heap, &call, gas)? .erase() } else { - asc_new::(heap, &call)?.erase() + asc_new::(heap, &call, gas)?.erase() } } MappingTrigger::Block { block } => { let block = EthereumBlockData::from(block.as_ref()); if heap.api_version() >= Version::new(0, 0, 6) { - asc_new::(heap, &block)?.erase() + asc_new::(heap, &block, gas)?.erase() } else { - asc_new::(heap, &block)?.erase() + asc_new::(heap, &block, gas)?.erase() } } }) diff --git a/chain/near/src/runtime/abi.rs b/chain/near/src/runtime/abi.rs index ddc16c2da60..fdd3be12e6f 100644 --- a/chain/near/src/runtime/abi.rs +++ b/chain/near/src/runtime/abi.rs @@ -1,6 +1,7 @@ use crate::codec; use crate::trigger::ReceiptWithOutcome; use graph::anyhow::anyhow; +use graph::runtime::gas::GasCounter; use graph::runtime::{asc_new, AscHeap, AscPtr, DeterministicHostError, ToAscObj}; use graph_runtime_wasm::asc_abi::class::{Array, AscEnum, EnumPayload, Uint8Array}; @@ -10,11 +11,12 @@ impl ToAscObj for codec::Block { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscBlock { - author: asc_new(heap, &self.author)?, - header: asc_new(heap, self.header())?, - chunks: asc_new(heap, &self.chunk_headers)?, + author: asc_new(heap, &self.author, gas)?, + header: asc_new(heap, self.header(), gas)?, + chunks: asc_new(heap, &self.chunk_headers, gas)?, }) } } @@ -23,38 +25,39 @@ impl ToAscObj for codec::BlockHeader { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - let chunk_mask = Array::new(self.chunk_mask.as_ref(), heap)?; + let chunk_mask = Array::new(self.chunk_mask.as_ref(), heap, gas)?; Ok(AscBlockHeader { height: self.height, prev_height: self.prev_height, - epoch_id: asc_new(heap, self.epoch_id.as_ref().unwrap())?, - next_epoch_id: asc_new(heap, self.next_epoch_id.as_ref().unwrap())?, - hash: asc_new(heap, self.hash.as_ref().unwrap())?, - prev_hash: asc_new(heap, self.prev_hash.as_ref().unwrap())?, - prev_state_root: asc_new(heap, self.prev_state_root.as_ref().unwrap())?, - chunk_receipts_root: asc_new(heap, self.chunk_receipts_root.as_ref().unwrap())?, - chunk_headers_root: asc_new(heap, self.chunk_headers_root.as_ref().unwrap())?, - chunk_tx_root: asc_new(heap, self.chunk_tx_root.as_ref().unwrap())?, - outcome_root: asc_new(heap, self.outcome_root.as_ref().unwrap())?, + epoch_id: asc_new(heap, self.epoch_id.as_ref().unwrap(), gas)?, + next_epoch_id: asc_new(heap, self.next_epoch_id.as_ref().unwrap(), gas)?, + hash: asc_new(heap, self.hash.as_ref().unwrap(), gas)?, + prev_hash: asc_new(heap, self.prev_hash.as_ref().unwrap(), gas)?, + prev_state_root: asc_new(heap, self.prev_state_root.as_ref().unwrap(), gas)?, + chunk_receipts_root: asc_new(heap, self.chunk_receipts_root.as_ref().unwrap(), gas)?, + chunk_headers_root: asc_new(heap, self.chunk_headers_root.as_ref().unwrap(), gas)?, + chunk_tx_root: asc_new(heap, self.chunk_tx_root.as_ref().unwrap(), gas)?, + outcome_root: asc_new(heap, self.outcome_root.as_ref().unwrap(), gas)?, chunks_included: self.chunks_included, - challenges_root: asc_new(heap, self.challenges_root.as_ref().unwrap())?, + challenges_root: asc_new(heap, self.challenges_root.as_ref().unwrap(), gas)?, timestamp_nanosec: self.timestamp_nanosec, - random_value: asc_new(heap, self.random_value.as_ref().unwrap())?, - validator_proposals: asc_new(heap, &self.validator_proposals)?, - chunk_mask: AscPtr::alloc_obj(chunk_mask, heap)?, - gas_price: asc_new(heap, self.gas_price.as_ref().unwrap())?, + random_value: asc_new(heap, self.random_value.as_ref().unwrap(), gas)?, + validator_proposals: asc_new(heap, &self.validator_proposals, gas)?, + chunk_mask: AscPtr::alloc_obj(chunk_mask, heap, gas)?, + gas_price: asc_new(heap, self.gas_price.as_ref().unwrap(), gas)?, block_ordinal: self.block_ordinal, - total_supply: asc_new(heap, self.total_supply.as_ref().unwrap())?, - challenges_result: asc_new(heap, &self.challenges_result)?, - last_final_block: asc_new(heap, self.last_final_block.as_ref().unwrap())?, - last_ds_final_block: asc_new(heap, self.last_ds_final_block.as_ref().unwrap())?, - next_bp_hash: asc_new(heap, self.next_bp_hash.as_ref().unwrap())?, - block_merkle_root: asc_new(heap, self.block_merkle_root.as_ref().unwrap())?, - epoch_sync_data_hash: asc_new(heap, self.epoch_sync_data_hash.as_slice())?, - approvals: asc_new(heap, &self.approvals)?, - signature: asc_new(heap, &self.signature.as_ref().unwrap())?, + total_supply: asc_new(heap, self.total_supply.as_ref().unwrap(), gas)?, + challenges_result: asc_new(heap, &self.challenges_result, gas)?, + last_final_block: asc_new(heap, self.last_final_block.as_ref().unwrap(), gas)?, + last_ds_final_block: asc_new(heap, self.last_ds_final_block.as_ref().unwrap(), gas)?, + next_bp_hash: asc_new(heap, self.next_bp_hash.as_ref().unwrap(), gas)?, + block_merkle_root: asc_new(heap, self.block_merkle_root.as_ref().unwrap(), gas)?, + epoch_sync_data_hash: asc_new(heap, self.epoch_sync_data_hash.as_slice(), gas)?, + approvals: asc_new(heap, &self.approvals, gas)?, + signature: asc_new(heap, &self.signature.as_ref().unwrap(), gas)?, latest_protocol_version: self.latest_protocol_version, }) } @@ -64,23 +67,24 @@ impl ToAscObj for codec::ChunkHeader { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscChunkHeader { - chunk_hash: asc_new(heap, self.chunk_hash.as_slice())?, - signature: asc_new(heap, &self.signature.as_ref().unwrap())?, - prev_block_hash: asc_new(heap, self.prev_block_hash.as_slice())?, - prev_state_root: asc_new(heap, self.prev_state_root.as_slice())?, - encoded_merkle_root: asc_new(heap, self.encoded_merkle_root.as_slice())?, + chunk_hash: asc_new(heap, self.chunk_hash.as_slice(), gas)?, + signature: asc_new(heap, &self.signature.as_ref().unwrap(), gas)?, + prev_block_hash: asc_new(heap, self.prev_block_hash.as_slice(), gas)?, + prev_state_root: asc_new(heap, self.prev_state_root.as_slice(), gas)?, + encoded_merkle_root: asc_new(heap, self.encoded_merkle_root.as_slice(), gas)?, encoded_length: self.encoded_length, height_created: self.height_created, height_included: self.height_included, shard_id: self.shard_id, gas_used: self.gas_used, gas_limit: self.gas_limit, - balance_burnt: asc_new(heap, self.balance_burnt.as_ref().unwrap())?, - outgoing_receipts_root: asc_new(heap, self.outgoing_receipts_root.as_slice())?, - tx_root: asc_new(heap, self.tx_root.as_slice())?, - validator_proposals: asc_new(heap, &self.validator_proposals)?, + balance_burnt: asc_new(heap, self.balance_burnt.as_ref().unwrap(), gas)?, + outgoing_receipts_root: asc_new(heap, self.outgoing_receipts_root.as_slice(), gas)?, + tx_root: asc_new(heap, self.tx_root.as_slice(), gas)?, + validator_proposals: asc_new(heap, &self.validator_proposals, gas)?, _padding: 0, }) @@ -91,10 +95,11 @@ impl ToAscObj for Vec { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Ok(AscChunkHeaderArray(Array::new(&*content, heap)?)) + Ok(AscChunkHeaderArray(Array::new(&*content, heap, gas)?)) } } @@ -102,11 +107,12 @@ impl ToAscObj for ReceiptWithOutcome { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscReceiptWithOutcome { - outcome: asc_new(heap, &self.outcome)?, - receipt: asc_new(heap, &self.receipt)?, - block: asc_new(heap, self.block.as_ref())?, + outcome: asc_new(heap, &self.outcome, gas)?, + receipt: asc_new(heap, &self.receipt, gas)?, + block: asc_new(heap, self.block.as_ref(), gas)?, }) } } @@ -115,6 +121,7 @@ impl ToAscObj for codec::Receipt { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { let action = match self.receipt.as_ref().unwrap() { codec::receipt::Receipt::Action(action) => action, @@ -126,15 +133,15 @@ impl ToAscObj for codec::Receipt { }; Ok(AscActionReceipt { - id: asc_new(heap, &self.receipt_id.as_ref().unwrap())?, - predecessor_id: asc_new(heap, &self.predecessor_id)?, - receiver_id: asc_new(heap, &self.receiver_id)?, - signer_id: asc_new(heap, &action.signer_id)?, - signer_public_key: asc_new(heap, action.signer_public_key.as_ref().unwrap())?, - gas_price: asc_new(heap, action.gas_price.as_ref().unwrap())?, - output_data_receivers: asc_new(heap, &action.output_data_receivers)?, - input_data_ids: asc_new(heap, &action.input_data_ids)?, - actions: asc_new(heap, &action.actions)?, + id: asc_new(heap, &self.receipt_id.as_ref().unwrap(), gas)?, + predecessor_id: asc_new(heap, &self.predecessor_id, gas)?, + receiver_id: asc_new(heap, &self.receiver_id, gas)?, + signer_id: asc_new(heap, &action.signer_id, gas)?, + signer_public_key: asc_new(heap, action.signer_public_key.as_ref().unwrap(), gas)?, + gas_price: asc_new(heap, action.gas_price.as_ref().unwrap(), gas)?, + output_data_receivers: asc_new(heap, &action.output_data_receivers, gas)?, + input_data_ids: asc_new(heap, &action.input_data_ids, gas)?, + actions: asc_new(heap, &action.actions, gas)?, }) } } @@ -143,36 +150,40 @@ impl ToAscObj for codec::Action { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { let (kind, payload) = match self.action.as_ref().unwrap() { codec::action::Action::CreateAccount(action) => ( AscActionKind::CreateAccount, - asc_new(heap, action)?.to_payload(), + asc_new(heap, action, gas)?.to_payload(), ), codec::action::Action::DeployContract(action) => ( AscActionKind::DeployContract, - asc_new(heap, action)?.to_payload(), + asc_new(heap, action, gas)?.to_payload(), ), codec::action::Action::FunctionCall(action) => ( AscActionKind::FunctionCall, - asc_new(heap, action)?.to_payload(), + asc_new(heap, action, gas)?.to_payload(), + ), + codec::action::Action::Transfer(action) => ( + AscActionKind::Transfer, + asc_new(heap, action, gas)?.to_payload(), + ), + codec::action::Action::Stake(action) => ( + AscActionKind::Stake, + asc_new(heap, action, gas)?.to_payload(), + ), + codec::action::Action::AddKey(action) => ( + AscActionKind::AddKey, + asc_new(heap, action, gas)?.to_payload(), ), - codec::action::Action::Transfer(action) => { - (AscActionKind::Transfer, asc_new(heap, action)?.to_payload()) - } - codec::action::Action::Stake(action) => { - (AscActionKind::Stake, asc_new(heap, action)?.to_payload()) - } - codec::action::Action::AddKey(action) => { - (AscActionKind::AddKey, asc_new(heap, action)?.to_payload()) - } codec::action::Action::DeleteKey(action) => ( AscActionKind::DeleteKey, - asc_new(heap, action)?.to_payload(), + asc_new(heap, action, gas)?.to_payload(), ), codec::action::Action::DeleteAccount(action) => ( AscActionKind::DeleteAccount, - asc_new(heap, action)?.to_payload(), + asc_new(heap, action, gas)?.to_payload(), ), }; @@ -188,10 +199,11 @@ impl ToAscObj for Vec { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Ok(AscActionEnumArray(Array::new(&*content, heap)?)) + Ok(AscActionEnumArray(Array::new(&*content, heap, gas)?)) } } @@ -199,6 +211,7 @@ impl ToAscObj for codec::CreateAccountAction { fn to_asc_obj( &self, _heap: &mut H, + _gas: &GasCounter, ) -> Result { Ok(AscCreateAccountAction {}) } @@ -208,9 +221,10 @@ impl ToAscObj for codec::DeployContractAction { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscDeployContractAction { - code: asc_new(heap, self.code.as_slice())?, + code: asc_new(heap, self.code.as_slice(), gas)?, }) } } @@ -219,12 +233,13 @@ impl ToAscObj for codec::FunctionCallAction { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscFunctionCallAction { - method_name: asc_new(heap, &self.method_name)?, - args: asc_new(heap, self.args.as_slice())?, + method_name: asc_new(heap, &self.method_name, gas)?, + args: asc_new(heap, self.args.as_slice(), gas)?, gas: self.gas, - deposit: asc_new(heap, self.deposit.as_ref().unwrap())?, + deposit: asc_new(heap, self.deposit.as_ref().unwrap(), gas)?, _padding: 0, }) } @@ -234,9 +249,10 @@ impl ToAscObj for codec::TransferAction { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscTransferAction { - deposit: asc_new(heap, self.deposit.as_ref().unwrap())?, + deposit: asc_new(heap, self.deposit.as_ref().unwrap(), gas)?, }) } } @@ -245,10 +261,11 @@ impl ToAscObj for codec::StakeAction { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscStakeAction { - stake: asc_new(heap, self.stake.as_ref().unwrap())?, - public_key: asc_new(heap, self.public_key.as_ref().unwrap())?, + stake: asc_new(heap, self.stake.as_ref().unwrap(), gas)?, + public_key: asc_new(heap, self.public_key.as_ref().unwrap(), gas)?, }) } } @@ -257,10 +274,11 @@ impl ToAscObj for codec::AddKeyAction { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscAddKeyAction { - public_key: asc_new(heap, self.public_key.as_ref().unwrap())?, - access_key: asc_new(heap, self.access_key.as_ref().unwrap())?, + public_key: asc_new(heap, self.public_key.as_ref().unwrap(), gas)?, + access_key: asc_new(heap, self.access_key.as_ref().unwrap(), gas)?, }) } } @@ -269,10 +287,11 @@ impl ToAscObj for codec::AccessKey { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscAccessKey { nonce: self.nonce, - permission: asc_new(heap, self.permission.as_ref().unwrap())?, + permission: asc_new(heap, self.permission.as_ref().unwrap(), gas)?, _padding: 0, }) } @@ -282,15 +301,16 @@ impl ToAscObj for codec::AccessKeyPermission { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { let (kind, payload) = match self.permission.as_ref().unwrap() { codec::access_key_permission::Permission::FunctionCall(permission) => ( AscAccessKeyPermissionKind::FunctionCall, - asc_new(heap, permission)?.to_payload(), + asc_new(heap, permission, gas)?.to_payload(), ), codec::access_key_permission::Permission::FullAccess(permission) => ( AscAccessKeyPermissionKind::FullAccess, - asc_new(heap, permission)?.to_payload(), + asc_new(heap, permission, gas)?.to_payload(), ), }; @@ -306,15 +326,16 @@ impl ToAscObj for codec::FunctionCallPermission { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscFunctionCallPermission { // The `allowance` field is one of the few fields that can actually be None for real allowance: match self.allowance.as_ref() { - Some(allowance) => asc_new(heap, allowance)?, + Some(allowance) => asc_new(heap, allowance, gas)?, None => AscPtr::null(), }, - receiver_id: asc_new(heap, &self.receiver_id)?, - method_names: asc_new(heap, &self.method_names)?, + receiver_id: asc_new(heap, &self.receiver_id, gas)?, + method_names: asc_new(heap, &self.method_names, gas)?, }) } } @@ -323,6 +344,7 @@ impl ToAscObj for codec::FullAccessPermission { fn to_asc_obj( &self, _heap: &mut H, + _gas: &GasCounter, ) -> Result { Ok(AscFullAccessPermission {}) } @@ -332,9 +354,10 @@ impl ToAscObj for codec::DeleteKeyAction { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscDeleteKeyAction { - public_key: asc_new(heap, self.public_key.as_ref().unwrap())?, + public_key: asc_new(heap, self.public_key.as_ref().unwrap(), gas)?, }) } } @@ -343,9 +366,10 @@ impl ToAscObj for codec::DeleteAccountAction { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscDeleteAccountAction { - beneficiary_id: asc_new(heap, &self.beneficiary_id)?, + beneficiary_id: asc_new(heap, &self.beneficiary_id, gas)?, }) } } @@ -354,10 +378,11 @@ impl ToAscObj for codec::DataReceiver { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscDataReceiver { - data_id: asc_new(heap, self.data_id.as_ref().unwrap())?, - receiver_id: asc_new(heap, &self.receiver_id)?, + data_id: asc_new(heap, self.data_id.as_ref().unwrap(), gas)?, + receiver_id: asc_new(heap, &self.receiver_id, gas)?, }) } } @@ -366,10 +391,11 @@ impl ToAscObj for Vec { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Ok(AscDataReceiverArray(Array::new(&*content, heap)?)) + Ok(AscDataReceiverArray(Array::new(&*content, heap, gas)?)) } } @@ -377,19 +403,20 @@ impl ToAscObj for codec::ExecutionOutcomeWithId { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { let outcome = self.outcome.as_ref().unwrap(); Ok(AscExecutionOutcome { - proof: asc_new(heap, &self.proof.as_ref().unwrap().path)?, - block_hash: asc_new(heap, self.block_hash.as_ref().unwrap())?, - id: asc_new(heap, self.id.as_ref().unwrap())?, - logs: asc_new(heap, &outcome.logs)?, - receipt_ids: asc_new(heap, &outcome.receipt_ids)?, + proof: asc_new(heap, &self.proof.as_ref().unwrap().path, gas)?, + block_hash: asc_new(heap, self.block_hash.as_ref().unwrap(), gas)?, + id: asc_new(heap, self.id.as_ref().unwrap(), gas)?, + logs: asc_new(heap, &outcome.logs, gas)?, + receipt_ids: asc_new(heap, &outcome.receipt_ids, gas)?, gas_burnt: outcome.gas_burnt, - tokens_burnt: asc_new(heap, outcome.tokens_burnt.as_ref().unwrap())?, - executor_id: asc_new(heap, &outcome.executor_id)?, - status: asc_new(heap, outcome.status.as_ref().unwrap())?, + tokens_burnt: asc_new(heap, outcome.tokens_burnt.as_ref().unwrap(), gas)?, + executor_id: asc_new(heap, &outcome.executor_id, gas)?, + status: asc_new(heap, outcome.status.as_ref().unwrap(), gas)?, }) } } @@ -398,6 +425,7 @@ impl ToAscObj for codec::execution_outcome::Status { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { let (kind, payload) = match self { codec::execution_outcome::Status::SuccessValue(value) => { @@ -405,12 +433,12 @@ impl ToAscObj for codec::execution_outcome::Status { ( AscSuccessStatusKind::Value, - asc_new(heap, bytes.as_slice())?.to_payload(), + asc_new(heap, bytes.as_slice(), gas)?.to_payload(), ) } codec::execution_outcome::Status::SuccessReceiptId(receipt_id) => ( AscSuccessStatusKind::ReceiptId, - asc_new(heap, receipt_id.id.as_ref().unwrap())?.to_payload(), + asc_new(heap, receipt_id.id.as_ref().unwrap(), gas)?.to_payload(), ), codec::execution_outcome::Status::Failure(_) => { return Err(DeterministicHostError::from(anyhow!( @@ -436,9 +464,10 @@ impl ToAscObj for codec::MerklePathItem { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscMerklePathItem { - hash: asc_new(heap, self.hash.as_ref().unwrap())?, + hash: asc_new(heap, self.hash.as_ref().unwrap(), gas)?, direction: match self.direction { 0 => AscDirection::Left, 1 => AscDirection::Right, @@ -457,10 +486,11 @@ impl ToAscObj for Vec { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Ok(AscMerklePathItemArray(Array::new(&*content, heap)?)) + Ok(AscMerklePathItemArray(Array::new(&*content, heap, gas)?)) } } @@ -468,6 +498,7 @@ impl ToAscObj for codec::Signature { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscSignature { kind: match self.r#type { @@ -480,7 +511,7 @@ impl ToAscObj for codec::Signature { ))) } }, - bytes: asc_new(heap, self.bytes.as_slice())?, + bytes: asc_new(heap, self.bytes.as_slice(), gas)?, }) } } @@ -489,10 +520,11 @@ impl ToAscObj for Vec { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Ok(AscSignatureArray(Array::new(&*content, heap)?)) + Ok(AscSignatureArray(Array::new(&*content, heap, gas)?)) } } @@ -500,6 +532,7 @@ impl ToAscObj for codec::PublicKey { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscPublicKey { kind: match self.r#type { @@ -512,7 +545,7 @@ impl ToAscObj for codec::PublicKey { ))) } }, - bytes: asc_new(heap, self.bytes.as_slice())?, + bytes: asc_new(heap, self.bytes.as_slice(), gas)?, }) } } @@ -521,11 +554,12 @@ impl ToAscObj for codec::ValidatorStake { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscValidatorStake { - account_id: asc_new(heap, &self.account_id)?, - public_key: asc_new(heap, self.public_key.as_ref().unwrap())?, - stake: asc_new(heap, self.stake.as_ref().unwrap())?, + account_id: asc_new(heap, &self.account_id, gas)?, + public_key: asc_new(heap, self.public_key.as_ref().unwrap(), gas)?, + stake: asc_new(heap, self.stake.as_ref().unwrap(), gas)?, }) } } @@ -534,10 +568,11 @@ impl ToAscObj for Vec { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Ok(AscValidatorStakeArray(Array::new(&*content, heap)?)) + Ok(AscValidatorStakeArray(Array::new(&*content, heap, gas)?)) } } @@ -545,9 +580,10 @@ impl ToAscObj for codec::SlashedValidator { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscSlashedValidator { - account_id: asc_new(heap, &self.account_id)?, + account_id: asc_new(heap, &self.account_id, gas)?, is_double_sign: self.is_double_sign, }) } @@ -557,10 +593,11 @@ impl ToAscObj for Vec { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Ok(AscSlashedValidatorArray(Array::new(&*content, heap)?)) + Ok(AscSlashedValidatorArray(Array::new(&*content, heap, gas)?)) } } @@ -568,8 +605,9 @@ impl ToAscObj for codec::CryptoHash { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - self.bytes.to_asc_obj(heap) + self.bytes.to_asc_obj(heap, gas) } } @@ -577,10 +615,11 @@ impl ToAscObj for Vec { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Ok(AscCryptoHashArray(Array::new(&*content, heap)?)) + Ok(AscCryptoHashArray(Array::new(&*content, heap, gas)?)) } } @@ -588,10 +627,11 @@ impl ToAscObj for codec::BigInt { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { // Bytes are reversed to align with BigInt bytes endianess let reversed: Vec = self.bytes.iter().rev().map(|x| *x).collect(); - reversed.to_asc_obj(heap) + reversed.to_asc_obj(heap, gas) } } diff --git a/chain/near/src/trigger.rs b/chain/near/src/trigger.rs index c38464f80ab..bd725d2a41e 100644 --- a/chain/near/src/trigger.rs +++ b/chain/near/src/trigger.rs @@ -6,6 +6,7 @@ use graph::prelude::hex; use graph::prelude::web3::types::H256; use graph::prelude::BlockNumber; use graph::runtime::asc_new; +use graph::runtime::gas::GasCounter; use graph::runtime::AscHeap; use graph::runtime::AscPtr; use graph::runtime::DeterministicHostError; @@ -39,10 +40,14 @@ impl std::fmt::Debug for NearTrigger { } impl blockchain::MappingTrigger for NearTrigger { - fn to_asc_ptr(self, heap: &mut H) -> Result, DeterministicHostError> { + fn to_asc_ptr( + self, + heap: &mut H, + gas: &GasCounter, + ) -> Result, DeterministicHostError> { Ok(match self { - NearTrigger::Block(block) => asc_new(heap, block.as_ref())?.erase(), - NearTrigger::Receipt(receipt) => asc_new(heap, receipt.as_ref())?.erase(), + NearTrigger::Block(block) => asc_new(heap, block.as_ref(), gas)?.erase(), + NearTrigger::Receipt(receipt) => asc_new(heap, receipt.as_ref(), gas)?.erase(), }) } } @@ -149,6 +154,7 @@ mod tests { anyhow::anyhow, data::subgraph::API_VERSION_0_0_5, prelude::{hex, BigInt}, + runtime::gas::GasCounter, }; #[test] @@ -156,7 +162,7 @@ mod tests { let mut heap = BytesHeap::new(API_VERSION_0_0_5); let trigger = NearTrigger::Block(Arc::new(block())); - let result = blockchain::MappingTrigger::to_asc_ptr(trigger, &mut heap); + let result = blockchain::MappingTrigger::to_asc_ptr(trigger, &mut heap, &GasCounter::new()); assert!(result.is_ok()); } @@ -169,7 +175,7 @@ mod tests { receipt: receipt().unwrap(), })); - let result = blockchain::MappingTrigger::to_asc_ptr(trigger, &mut heap); + let result = blockchain::MappingTrigger::to_asc_ptr(trigger, &mut heap, &GasCounter::new()); assert!(result.is_ok()); } @@ -431,12 +437,21 @@ mod tests { } impl AscHeap for BytesHeap { - fn raw_new(&mut self, bytes: &[u8]) -> Result { + fn raw_new( + &mut self, + bytes: &[u8], + _gas: &GasCounter, + ) -> Result { self.memory.extend_from_slice(bytes); Ok((self.memory.len() - bytes.len()) as u32) } - fn get(&self, offset: u32, size: u32) -> Result, DeterministicHostError> { + fn get( + &self, + offset: u32, + size: u32, + _gas: &GasCounter, + ) -> Result, DeterministicHostError> { let memory_byte_count = self.memory.len(); if memory_byte_count == 0 { return Err(DeterministicHostError::from(anyhow!( diff --git a/graph/src/blockchain/mock.rs b/graph/src/blockchain/mock.rs index 30c57a162ba..651e2545df7 100644 --- a/graph/src/blockchain/mock.rs +++ b/graph/src/blockchain/mock.rs @@ -1,6 +1,7 @@ use crate::{ components::{link_resolver::LinkResolver, store::BlockNumber}, prelude::DataSourceTemplateInfo, + runtime::gas::GasCounter, }; use anyhow::Error; use async_trait::async_trait; @@ -216,6 +217,7 @@ impl MappingTrigger for MockMappingTrigger { fn to_asc_ptr( self, _heap: &mut H, + _gas: &GasCounter, ) -> Result, crate::runtime::DeterministicHostError> { todo!() } diff --git a/graph/src/blockchain/mod.rs b/graph/src/blockchain/mod.rs index 769574d13fc..cf2f5205109 100644 --- a/graph/src/blockchain/mod.rs +++ b/graph/src/blockchain/mod.rs @@ -256,7 +256,11 @@ pub trait TriggerData { pub trait MappingTrigger: Send + Sync { /// A flexible interface for writing a type to AS memory, any pointer can be returned. /// Use `AscPtr::erased` to convert `AscPtr` into `AscPtr<()>`. - fn to_asc_ptr(self, heap: &mut H) -> Result, DeterministicHostError>; + fn to_asc_ptr( + self, + heap: &mut H, + gas: &GasCounter, + ) -> Result, DeterministicHostError>; } pub struct HostFnCtx<'a> { @@ -415,7 +419,8 @@ impl TriggerWithHandler { pub fn to_asc_ptr( self, heap: &mut H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { - self.trigger.to_asc_ptr(heap) + self.trigger.to_asc_ptr(heap, gas) } } diff --git a/graph/src/data/subgraph/api_version.rs b/graph/src/data/subgraph/api_version.rs index 0a7d0eb243c..ed8d3964546 100644 --- a/graph/src/data/subgraph/api_version.rs +++ b/graph/src/data/subgraph/api_version.rs @@ -25,7 +25,7 @@ lazy_static! { .ok() .and_then(|api_version_str| Version::parse(&api_version_str).ok()) .unwrap_or(SPEC_VERSION_0_0_4); - pub(super) static ref MAX_API_VERSION: semver::Version = std::env::var("GRAPH_MAX_API_VERSION") + pub static ref MAX_API_VERSION: semver::Version = std::env::var("GRAPH_MAX_API_VERSION") .ok() .and_then(|api_version_str| semver::Version::parse(&api_version_str).ok()) .unwrap_or(semver::Version::new(0, 0, 6)); diff --git a/graph/src/runtime/asc_heap.rs b/graph/src/runtime/asc_heap.rs index 1941b72fb23..80bd2ea55fa 100644 --- a/graph/src/runtime/asc_heap.rs +++ b/graph/src/runtime/asc_heap.rs @@ -1,15 +1,22 @@ use semver::Version; -use super::{AscIndexId, AscPtr, AscType, DeterministicHostError, IndexForAscTypeId}; +use super::{ + gas::GasCounter, AscIndexId, AscPtr, AscType, DeterministicHostError, IndexForAscTypeId, +}; /// A type that can read and write to the Asc heap. Call `asc_new` and `asc_get` /// for reading and writing Rust structs from and to Asc. /// /// The implementor must provide the direct Asc interface with `raw_new` and `get`. pub trait AscHeap { /// Allocate new space and write `bytes`, return the allocated address. - fn raw_new(&mut self, bytes: &[u8]) -> Result; + fn raw_new(&mut self, bytes: &[u8], gas: &GasCounter) -> Result; - fn get(&self, offset: u32, size: u32) -> Result, DeterministicHostError>; + fn get( + &self, + offset: u32, + size: u32, + gas: &GasCounter, + ) -> Result, DeterministicHostError>; fn api_version(&self) -> Version; @@ -27,13 +34,14 @@ pub trait AscHeap { pub fn asc_new( heap: &mut H, rust_obj: &T, + gas: &GasCounter, ) -> Result, DeterministicHostError> where C: AscType + AscIndexId, T: ToAscObj, { - let obj = rust_obj.to_asc_obj(heap)?; - AscPtr::alloc_obj(obj, heap) + let obj = rust_obj.to_asc_obj(heap, gas)?; + AscPtr::alloc_obj(obj, heap, gas) } /// Read the rust representation of an Asc object of class `C`. @@ -43,48 +51,63 @@ where pub fn asc_get( heap: &H, asc_ptr: AscPtr, + gas: &GasCounter, ) -> Result where C: AscType + AscIndexId, T: FromAscObj, { - T::from_asc_obj(asc_ptr.read_ptr(heap)?, heap) + T::from_asc_obj(asc_ptr.read_ptr(heap, gas)?, heap, gas) } pub fn try_asc_get( heap: &H, asc_ptr: AscPtr, + gas: &GasCounter, ) -> Result where C: AscType + AscIndexId, T: TryFromAscObj, { - T::try_from_asc_obj(asc_ptr.read_ptr(heap)?, heap) + T::try_from_asc_obj(asc_ptr.read_ptr(heap, gas)?, heap, gas) } /// Type that can be converted to an Asc object of class `C`. pub trait ToAscObj { - fn to_asc_obj(&self, heap: &mut H) -> Result; + fn to_asc_obj( + &self, + heap: &mut H, + gas: &GasCounter, + ) -> Result; } impl ToAscObj for bool { fn to_asc_obj( &self, _heap: &mut H, + _gas: &GasCounter, ) -> Result { Ok(*self) } } impl> ToAscObj for &T { - fn to_asc_obj(&self, heap: &mut H) -> Result { - (*self).to_asc_obj(heap) + fn to_asc_obj( + &self, + heap: &mut H, + gas: &GasCounter, + ) -> Result { + (*self).to_asc_obj(heap, gas) } } /// Type that can be converted from an Asc object of class `C`. pub trait FromAscObj { - fn from_asc_obj(obj: C, heap: &H) -> Result + fn from_asc_obj( + obj: C, + heap: &H, + gas: &GasCounter, + ) -> Result where Self: Sized; } @@ -93,5 +116,6 @@ pub trait TryFromAscObj: Sized { fn try_from_asc_obj( obj: C, heap: &H, + gas: &GasCounter, ) -> Result; } diff --git a/graph/src/runtime/asc_ptr.rs b/graph/src/runtime/asc_ptr.rs index 9bf5a72dc16..3f620aeb7d1 100644 --- a/graph/src/runtime/asc_ptr.rs +++ b/graph/src/runtime/asc_ptr.rs @@ -1,3 +1,4 @@ +use super::gas::GasCounter; use super::{padding_to_16, DeterministicHostError}; use super::{AscHeap, AscIndexId, AscType, IndexForAscTypeId}; @@ -52,12 +53,16 @@ impl AscPtr { } /// Read from `self` into the Rust struct `C`. - pub fn read_ptr(self, heap: &H) -> Result { + pub fn read_ptr( + self, + heap: &H, + gas: &GasCounter, + ) -> Result { let len = match heap.api_version() { - version if version <= Version::new(0, 0, 4) => C::asc_size(self, heap), - _ => self.read_len(heap), + version if version <= Version::new(0, 0, 4) => C::asc_size(self, heap, gas), + _ => self.read_len(heap, gas), }?; - let bytes = heap.get(self.0, len)?; + let bytes = heap.get(self.0, len, gas)?; C::from_asc_bytes(&bytes, &heap.api_version()) } @@ -65,13 +70,14 @@ impl AscPtr { pub fn alloc_obj( asc_obj: C, heap: &mut H, + gas: &GasCounter, ) -> Result, DeterministicHostError> where C: AscIndexId, { match heap.api_version() { version if version <= Version::new(0, 0, 4) => { - let heap_ptr = heap.raw_new(&asc_obj.to_asc_bytes()?)?; + let heap_ptr = heap.raw_new(&asc_obj.to_asc_bytes()?, gas)?; Ok(AscPtr::new(heap_ptr)) } _ => { @@ -90,7 +96,7 @@ impl AscPtr { )?; let header_len = header.len() as u32; - let heap_ptr = heap.raw_new(&[header, bytes].concat())?; + let heap_ptr = heap.raw_new(&[header, bytes].concat(), gas)?; // Use header length as offset. so the AscPtr points directly at the content. Ok(AscPtr::new(heap_ptr + header_len)) @@ -100,9 +106,13 @@ impl AscPtr { /// Helper used by arrays and strings to read their length. /// Only used for version <= 0.0.4. - pub fn read_u32(&self, heap: &H) -> Result { + pub fn read_u32( + &self, + heap: &H, + gas: &GasCounter, + ) -> Result { // Read the bytes pointed to by `self` as the bytes of a `u32`. - let raw_bytes = heap.get(self.0, size_of::() as u32)?; + let raw_bytes = heap.get(self.0, size_of::() as u32, gas)?; let mut u32_bytes: [u8; size_of::()] = [0; size_of::()]; u32_bytes.copy_from_slice(&raw_bytes); Ok(u32::from_le_bytes(u32_bytes)) @@ -152,7 +162,11 @@ impl AscPtr { /// - rt_size: u32 /// This function returns the `rt_size`. /// Only used for version >= 0.0.5. - pub fn read_len(&self, heap: &H) -> Result { + pub fn read_len( + &self, + heap: &H, + gas: &GasCounter, + ) -> Result { // We're trying to read the pointer below, we should check it's // not null before using it. self.check_is_not_null()?; @@ -163,7 +177,7 @@ impl AscPtr { self.0 )) })?; - let raw_bytes = heap.get(start_of_rt_size, size_of::() as u32)?; + let raw_bytes = heap.get(start_of_rt_size, size_of::() as u32, gas)?; let mut u32_bytes: [u8; size_of::()] = [0; size_of::()]; u32_bytes.copy_from_slice(&raw_bytes); Ok(u32::from_le_bytes(u32_bytes)) diff --git a/graph/src/runtime/mod.rs b/graph/src/runtime/mod.rs index c1c8e92c2f3..2cb211a3a8e 100644 --- a/graph/src/runtime/mod.rs +++ b/graph/src/runtime/mod.rs @@ -17,6 +17,8 @@ use std::convert::TryInto; use std::fmt; use std::mem::size_of; +use self::gas::GasCounter; + /// Marker trait for AssemblyScript types that the id should /// be in the header. pub trait AscIndexId { @@ -57,6 +59,7 @@ pub trait AscType: Sized { fn asc_size( _ptr: AscPtr, _heap: &H, + _gas: &GasCounter, ) -> Result { Ok(std::mem::size_of::() as u32) } @@ -246,6 +249,7 @@ impl ToAscObj for IndexForAscTypeId { fn to_asc_obj( &self, _heap: &mut H, + _gas: &GasCounter, ) -> Result { Ok(*self as u32) } diff --git a/runtime/test/src/test.rs b/runtime/test/src/test.rs index 23969228eae..283a80a2853 100644 --- a/runtime/test/src/test.rs +++ b/runtime/test/src/test.rs @@ -2,8 +2,8 @@ use graph::data::store::scalar; use graph::data::subgraph::*; use graph::prelude::web3::types::U256; use graph::prelude::*; -use graph::runtime::AscPtr; -use graph::runtime::{asc_get, asc_new, try_asc_get}; +use graph::runtime::{asc_get, asc_new, try_asc_get, AscIndexId, AscType}; +use graph::runtime::{AscPtr, ToAscObj}; use graph::{components::store::*, ipfs_client::IpfsClient}; use graph_chain_ethereum::{Chain, DataSource}; use graph_mock::MockMetricsRegistry; @@ -118,25 +118,52 @@ fn test_module( test_valid_module_and_store(subgraph_id, data_source, api_version).0 } +// A test module using the latest API version +fn test_module_latest(subgraph_id: &str, wasm_file: &str) -> WasmInstance { + let version = MAX_API_VERSION.clone(); + let ds = mock_data_source( + &wasm_file_path(wasm_file, API_VERSION_0_0_5.clone()), + version.clone(), + ); + test_valid_module_and_store(subgraph_id, ds, version).0 +} + trait WasmInstanceExt { - fn invoke_export0_void(&self, f: &str); + fn invoke_export0_void(&self, f: &str) -> Result<(), wasmtime::Trap>; fn invoke_export0(&self, f: &str) -> AscPtr; - fn invoke_export1(&self, f: &str, arg: AscPtr) -> AscPtr; - fn invoke_export2(&self, f: &str, arg0: AscPtr, arg1: AscPtr) -> AscPtr; - fn invoke_export2_void( - &self, + fn invoke_export1(&mut self, f: &str, arg: &T) -> AscPtr + where + C: AscType + AscIndexId, + T: ToAscObj + ?Sized; + fn invoke_export2(&mut self, f: &str, arg0: &T1, arg1: &T2) -> AscPtr + where + C1: AscType + AscIndexId, + C2: AscType + AscIndexId, + T1: ToAscObj + ?Sized, + T2: ToAscObj + ?Sized; + fn invoke_export2_void( + &mut self, f: &str, - arg0: AscPtr, - arg1: AscPtr, - ) -> Result<(), wasmtime::Trap>; - fn takes_ptr_returns_val(&mut self, fn_name: &str, v: AscPtr

) -> V; + arg0: &T1, + arg1: &T2, + ) -> Result<(), wasmtime::Trap> + where + C1: AscType + AscIndexId, + C2: AscType + AscIndexId, + T1: ToAscObj + ?Sized, + T2: ToAscObj + ?Sized; + fn invoke_export1_val(&mut self, func: &str, v: &T) -> V + where + C: AscType + AscIndexId, + T: ToAscObj + ?Sized; + fn takes_ptr_returns_ptr(&self, f: &str, arg: AscPtr) -> AscPtr; fn takes_val_returns_ptr

(&mut self, fn_name: &str, val: impl wasmtime::WasmTy) -> AscPtr

; } impl WasmInstanceExt for WasmInstance { - fn invoke_export0_void(&self, f: &str) { + fn invoke_export0_void(&self, f: &str) -> Result<(), wasmtime::Trap> { let func = self.get_func(f).typed().unwrap().clone(); - let _: () = func.call(()).unwrap(); + func.call(()) } fn invoke_export0(&self, f: &str) -> AscPtr { @@ -145,31 +172,67 @@ impl WasmInstanceExt for WasmInstance { ptr.into() } - fn invoke_export1(&self, f: &str, arg: AscPtr) -> AscPtr { + fn takes_ptr_returns_ptr(&self, f: &str, arg: AscPtr) -> AscPtr { let func = self.get_func(f).typed().unwrap().clone(); let ptr: u32 = func.call(arg.wasm_ptr()).unwrap(); ptr.into() } - fn invoke_export2(&self, f: &str, arg0: AscPtr, arg1: AscPtr) -> AscPtr { + fn invoke_export1(&mut self, f: &str, arg: &T) -> AscPtr + where + C: AscType + AscIndexId, + T: ToAscObj + ?Sized, + { + let func = self.get_func(f).typed().unwrap().clone(); + let gas = self.gas.cheap_clone(); + let ptr = asc_new(self, arg, &gas).unwrap(); + let ptr: u32 = func.call(ptr.wasm_ptr()).unwrap(); + ptr.into() + } + + fn invoke_export2(&mut self, f: &str, arg0: &T1, arg1: &T2) -> AscPtr + where + C1: AscType + AscIndexId, + C2: AscType + AscIndexId, + T1: ToAscObj + ?Sized, + T2: ToAscObj + ?Sized, + { let func = self.get_func(f).typed().unwrap().clone(); + let gas = self.gas.cheap_clone(); + let arg0 = asc_new(self, arg0, &gas).unwrap(); + let arg1 = asc_new(self, arg1, &gas).unwrap(); let ptr: u32 = func.call((arg0.wasm_ptr(), arg1.wasm_ptr())).unwrap(); ptr.into() } - fn invoke_export2_void( - &self, + fn invoke_export2_void( + &mut self, f: &str, - arg0: AscPtr, - arg1: AscPtr, - ) -> Result<(), wasmtime::Trap> { + arg0: &T1, + arg1: &T2, + ) -> Result<(), wasmtime::Trap> + where + C1: AscType + AscIndexId, + C2: AscType + AscIndexId, + T1: ToAscObj + ?Sized, + T2: ToAscObj + ?Sized, + { let func = self.get_func(f).typed().unwrap().clone(); + let gas = self.gas.cheap_clone(); + let arg0 = asc_new(self, arg0, &gas).unwrap(); + let arg1 = asc_new(self, arg1, &gas).unwrap(); func.call((arg0.wasm_ptr(), arg1.wasm_ptr())) } - fn takes_ptr_returns_val(&mut self, fn_name: &str, v: AscPtr

) -> V { - let func = self.get_func(fn_name).typed().unwrap().clone(); - func.call(v.wasm_ptr()).unwrap() + fn invoke_export1_val(&mut self, func: &str, v: &T) -> V + where + C: AscType + AscIndexId, + T: ToAscObj + ?Sized, + { + let func = self.get_func(func).typed().unwrap().clone(); + let gas = self.gas.cheap_clone(); + let ptr = asc_new(self, v, &gas).unwrap(); + func.call(ptr.wasm_ptr()).unwrap() } fn takes_val_returns_ptr

(&mut self, fn_name: &str, val: impl wasmtime::WasmTy) -> AscPtr

{ @@ -191,27 +254,23 @@ fn test_json_conversions(api_version: Version, gas_used: u64) { // test u64 conversion let number = 9223372036850770800; - let number_ptr = asc_new(&mut module, &number.to_string()).unwrap(); - let converted: i64 = module.takes_ptr_returns_val("testToU64", number_ptr); + let converted: i64 = module.invoke_export1_val("testToU64", &number.to_string()); assert_eq!(number, u64::from_le_bytes(converted.to_le_bytes())); // test i64 conversion let number = -9223372036850770800; - let number_ptr = asc_new(&mut module, &number.to_string()).unwrap(); - let converted: i64 = module.takes_ptr_returns_val("testToI64", number_ptr); + let converted: i64 = module.invoke_export1_val("testToI64", &number.to_string()); assert_eq!(number, converted); // test f64 conversion let number = -9223372036850770.92345034; - let number_ptr = asc_new(&mut module, &number.to_string()).unwrap(); - let converted: f64 = module.takes_ptr_returns_val("testToF64", number_ptr); + let converted: f64 = module.invoke_export1_val("testToF64", &number.to_string()); assert_eq!(number, converted); // test BigInt conversion let number = "-922337203685077092345034"; - let number_ptr = asc_new(&mut module, number).unwrap(); - let big_int_obj: AscPtr = module.invoke_export1("testToBigInt", number_ptr); - let bytes: Vec = asc_get(&module, big_int_obj).unwrap(); + let big_int_obj: AscPtr = module.invoke_export1("testToBigInt", number); + let bytes: Vec = asc_get(&module, big_int_obj, &module.gas).unwrap(); assert_eq!( scalar::BigInt::from_str(number).unwrap(), @@ -223,12 +282,12 @@ fn test_json_conversions(api_version: Version, gas_used: u64) { #[tokio::test] async fn json_conversions_v0_0_4() { - test_json_conversions(API_VERSION_0_0_4, 51937534); + test_json_conversions(API_VERSION_0_0_4, 52976429); } #[tokio::test] async fn json_conversions_v0_0_5() { - test_json_conversions(API_VERSION_0_0_5, 912148); + test_json_conversions(API_VERSION_0_0_5, 2289897); } fn test_json_parsing(api_version: Version, gas_used: u64) { @@ -244,30 +303,28 @@ fn test_json_parsing(api_version: Version, gas_used: u64) { // Parse invalid JSON and handle the error gracefully let s = "foo"; // Invalid because there are no quotes around `foo` let bytes: &[u8] = s.as_ref(); - let bytes_ptr = asc_new(&mut module, bytes).unwrap(); - let return_value: AscPtr = module.invoke_export1("handleJsonError", bytes_ptr); - let output: String = asc_get(&module, return_value).unwrap(); + let return_value: AscPtr = module.invoke_export1("handleJsonError", bytes); + let output: String = asc_get(&module, return_value, &module.gas).unwrap(); assert_eq!(output, "ERROR: true"); // Parse valid JSON and get it back let s = "\"foo\""; // Valid because there are quotes around `foo` let bytes: &[u8] = s.as_ref(); - let bytes_ptr = asc_new(&mut module, bytes).unwrap(); - let return_value: AscPtr = module.invoke_export1("handleJsonError", bytes_ptr); + let return_value: AscPtr = module.invoke_export1("handleJsonError", bytes); - let output: String = asc_get(&module, return_value).unwrap(); + let output: String = asc_get(&module, return_value, &module.gas).unwrap(); assert_eq!(output, "OK: foo, ERROR: false"); assert_eq!(module.gas_used(), gas_used); } #[tokio::test] async fn json_parsing_v0_0_4() { - test_json_parsing(API_VERSION_0_0_4, 2062683); + test_json_parsing(API_VERSION_0_0_4, 2722284); } #[tokio::test] async fn json_parsing_v0_0_5() { - test_json_parsing(API_VERSION_0_0_5, 2693805); + test_json_parsing(API_VERSION_0_0_5, 3862933); } async fn test_ipfs_cat(api_version: Version) { @@ -288,9 +345,8 @@ async fn test_ipfs_cat(api_version: Version) { ), api_version, ); - let arg = asc_new(&mut module, &hash).unwrap(); - let converted: AscPtr = module.invoke_export1("ipfsCatString", arg); - let data: String = asc_get(&module, converted).unwrap(); + let converted: AscPtr = module.invoke_export1("ipfsCatString", &hash); + let data: String = asc_get(&module, converted, &module.gas).unwrap(); assert_eq!(data, "42"); }) .join() @@ -361,8 +417,9 @@ async fn run_ipfs_map( ), api_version, ); - let value = asc_new(&mut module, &hash).unwrap(); - let user_data = asc_new(&mut module, USER_DATA).unwrap(); + let gas = module.gas.cheap_clone(); + let value = asc_new(&mut module, &hash, &gas).unwrap(); + let user_data = asc_new(&mut module, USER_DATA, &gas).unwrap(); // Invoke the callback let func = module.get_func("ipfsMap").typed().unwrap().clone(); @@ -498,9 +555,8 @@ async fn test_ipfs_fail(api_version: Version) { api_version, ); - let hash = asc_new(&mut module, "invalid hash").unwrap(); assert!(module - .invoke_export1::<_, AscString>("ipfsCat", hash) + .invoke_export1::<_, _, AscString>("ipfsCat", "invalid hash") .is_null()); }) .join() @@ -527,10 +583,9 @@ fn test_crypto_keccak256(api_version: Version) { api_version, ); let input: &[u8] = "eth".as_ref(); - let input: AscPtr = asc_new(&mut module, input).unwrap(); let hash: AscPtr = module.invoke_export1("hash", input); - let hash: Vec = asc_get(&module, hash).unwrap(); + let hash: Vec = asc_get(&module, hash, &module.gas).unwrap(); assert_eq!( hex::encode(hash), "4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0" @@ -559,23 +614,20 @@ fn test_big_int_to_hex(api_version: Version, gas_used: u64) { // Convert zero to hex let zero = BigInt::from_unsigned_u256(&U256::zero()); - let zero: AscPtr = asc_new(&mut module, &zero).unwrap(); - let zero_hex_ptr: AscPtr = module.invoke_export1("big_int_to_hex", zero); - let zero_hex_str: String = asc_get(&module, zero_hex_ptr).unwrap(); + let zero_hex_ptr: AscPtr = module.invoke_export1("big_int_to_hex", &zero); + let zero_hex_str: String = asc_get(&module, zero_hex_ptr, &module.gas).unwrap(); assert_eq!(zero_hex_str, "0x0"); // Convert 1 to hex let one = BigInt::from_unsigned_u256(&U256::one()); - let one: AscPtr = asc_new(&mut module, &one).unwrap(); - let one_hex_ptr: AscPtr = module.invoke_export1("big_int_to_hex", one); - let one_hex_str: String = asc_get(&module, one_hex_ptr).unwrap(); + let one_hex_ptr: AscPtr = module.invoke_export1("big_int_to_hex", &one); + let one_hex_str: String = asc_get(&module, one_hex_ptr, &module.gas).unwrap(); assert_eq!(one_hex_str, "0x1"); // Convert U256::max_value() to hex let u256_max = BigInt::from_unsigned_u256(&U256::max_value()); - let u256_max: AscPtr = asc_new(&mut module, &u256_max).unwrap(); - let u256_max_hex_ptr: AscPtr = module.invoke_export1("big_int_to_hex", u256_max); - let u256_max_hex_str: String = asc_get(&module, u256_max_hex_ptr).unwrap(); + let u256_max_hex_ptr: AscPtr = module.invoke_export1("big_int_to_hex", &u256_max); + let u256_max_hex_str: String = asc_get(&module, u256_max_hex_ptr, &module.gas).unwrap(); assert_eq!( u256_max_hex_str, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" @@ -586,12 +638,12 @@ fn test_big_int_to_hex(api_version: Version, gas_used: u64) { #[tokio::test] async fn big_int_to_hex_v0_0_4() { - test_big_int_to_hex(API_VERSION_0_0_4, 51770565); + test_big_int_to_hex(API_VERSION_0_0_4, 53113760); } #[tokio::test] async fn big_int_to_hex_v0_0_5() { - test_big_int_to_hex(API_VERSION_0_0_5, 962685); + test_big_int_to_hex(API_VERSION_0_0_5, 2858580); } fn test_big_int_arithmetic(api_version: Version, gas_used: u64) { @@ -606,56 +658,44 @@ fn test_big_int_arithmetic(api_version: Version, gas_used: u64) { // 0 + 1 = 1 let zero = BigInt::from(0); - let zero: AscPtr = asc_new(&mut module, &zero).unwrap(); let one = BigInt::from(1); - let one: AscPtr = asc_new(&mut module, &one).unwrap(); - let result_ptr: AscPtr = module.invoke_export2("plus", zero, one); - let result: BigInt = asc_get(&module, result_ptr).unwrap(); + let result_ptr: AscPtr = module.invoke_export2("plus", &zero, &one); + let result: BigInt = asc_get(&module, result_ptr, &module.gas).unwrap(); assert_eq!(result, BigInt::from(1)); // 127 + 1 = 128 let zero = BigInt::from(127); - let zero: AscPtr = asc_new(&mut module, &zero).unwrap(); let one = BigInt::from(1); - let one: AscPtr = asc_new(&mut module, &one).unwrap(); - let result_ptr: AscPtr = module.invoke_export2("plus", zero, one); - let result: BigInt = asc_get(&module, result_ptr).unwrap(); + let result_ptr: AscPtr = module.invoke_export2("plus", &zero, &one); + let result: BigInt = asc_get(&module, result_ptr, &module.gas).unwrap(); assert_eq!(result, BigInt::from(128)); // 5 - 10 = -5 let five = BigInt::from(5); - let five: AscPtr = asc_new(&mut module, &five).unwrap(); let ten = BigInt::from(10); - let ten: AscPtr = asc_new(&mut module, &ten).unwrap(); - let result_ptr: AscPtr = module.invoke_export2("minus", five, ten); - let result: BigInt = asc_get(&module, result_ptr).unwrap(); + let result_ptr: AscPtr = module.invoke_export2("minus", &five, &ten); + let result: BigInt = asc_get(&module, result_ptr, &module.gas).unwrap(); assert_eq!(result, BigInt::from(-5)); // -20 * 5 = -100 let minus_twenty = BigInt::from(-20); - let minus_twenty: AscPtr = asc_new(&mut module, &minus_twenty).unwrap(); let five = BigInt::from(5); - let five: AscPtr = asc_new(&mut module, &five).unwrap(); - let result_ptr: AscPtr = module.invoke_export2("times", minus_twenty, five); - let result: BigInt = asc_get(&module, result_ptr).unwrap(); + let result_ptr: AscPtr = module.invoke_export2("times", &minus_twenty, &five); + let result: BigInt = asc_get(&module, result_ptr, &module.gas).unwrap(); assert_eq!(result, BigInt::from(-100)); // 5 / 2 = 2 let five = BigInt::from(5); - let five: AscPtr = asc_new(&mut module, &five).unwrap(); let two = BigInt::from(2); - let two: AscPtr = asc_new(&mut module, &two).unwrap(); - let result_ptr: AscPtr = module.invoke_export2("dividedBy", five, two); - let result: BigInt = asc_get(&module, result_ptr).unwrap(); + let result_ptr: AscPtr = module.invoke_export2("dividedBy", &five, &two); + let result: BigInt = asc_get(&module, result_ptr, &module.gas).unwrap(); assert_eq!(result, BigInt::from(2)); // 5 % 2 = 1 let five = BigInt::from(5); - let five: AscPtr = asc_new(&mut module, &five).unwrap(); let two = BigInt::from(2); - let two: AscPtr = asc_new(&mut module, &two).unwrap(); - let result_ptr: AscPtr = module.invoke_export2("mod", five, two); - let result: BigInt = asc_get(&module, result_ptr).unwrap(); + let result_ptr: AscPtr = module.invoke_export2("mod", &five, &two); + let result: BigInt = asc_get(&module, result_ptr, &module.gas).unwrap(); assert_eq!(result, BigInt::from(1)); assert_eq!(module.gas_used(), gas_used); @@ -663,12 +703,12 @@ fn test_big_int_arithmetic(api_version: Version, gas_used: u64) { #[tokio::test] async fn big_int_arithmetic_v0_0_4() { - test_big_int_arithmetic(API_VERSION_0_0_4, 52099180); + test_big_int_arithmetic(API_VERSION_0_0_4, 54962411); } #[tokio::test] async fn big_int_arithmetic_v0_0_5() { - test_big_int_arithmetic(API_VERSION_0_0_5, 3035221); + test_big_int_arithmetic(API_VERSION_0_0_5, 7318364); } fn test_abort(api_version: Version, error_msg: &str) { @@ -711,9 +751,8 @@ fn test_bytes_to_base58(api_version: Version, gas_used: u64) { ); let bytes = hex::decode("12207D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89") .unwrap(); - let bytes_ptr = asc_new(&mut module, bytes.as_slice()).unwrap(); - let result_ptr: AscPtr = module.invoke_export1("bytes_to_base58", bytes_ptr); - let base58: String = asc_get(&module, result_ptr).unwrap(); + let result_ptr: AscPtr = module.invoke_export1("bytes_to_base58", bytes.as_slice()); + let base58: String = asc_get(&module, result_ptr, &module.gas).unwrap(); assert_eq!(base58, "QmWmyoMoctfbAaiEs2G46gpeUmhqFRDW6KWo64y5r581Vz"); assert_eq!(module.gas_used(), gas_used); @@ -721,12 +760,12 @@ fn test_bytes_to_base58(api_version: Version, gas_used: u64) { #[tokio::test] async fn bytes_to_base58_v0_0_4() { - test_bytes_to_base58(API_VERSION_0_0_4, 51577627); + test_bytes_to_base58(API_VERSION_0_0_4, 52301689); } #[tokio::test] async fn bytes_to_base58_v0_0_5() { - test_bytes_to_base58(API_VERSION_0_0_5, 477157); + test_bytes_to_base58(API_VERSION_0_0_5, 1310019); } fn test_data_source_create(api_version: Version, gas_used: u64) { @@ -743,10 +782,8 @@ fn test_data_source_create(api_version: Version, gas_used: u64) { api_version.clone(), ); - let name = asc_new(&mut module, &name).unwrap(); - let params = asc_new(&mut module, params.as_slice()).unwrap(); module.instance_ctx_mut().ctx.state.enter_handler(); - module.invoke_export2_void("dataSourceCreate", name, params)?; + module.invoke_export2_void("dataSourceCreate", &name, ¶ms)?; module.instance_ctx_mut().ctx.state.exit_handler(); assert_eq!(module.gas_used(), gas_used); @@ -777,12 +814,12 @@ fn test_data_source_create(api_version: Version, gas_used: u64) { #[tokio::test] async fn data_source_create_v0_0_4() { - test_data_source_create(API_VERSION_0_0_4, 151393645); + test_data_source_create(API_VERSION_0_0_4, 152102833); } #[tokio::test] async fn data_source_create_v0_0_5() { - test_data_source_create(API_VERSION_0_0_5, 100440279); + test_data_source_create(API_VERSION_0_0_5, 101450079); } fn test_ens_name_by_hash(api_version: Version) { @@ -798,14 +835,12 @@ fn test_ens_name_by_hash(api_version: Version) { let hash = "0x7f0c1b04d1a4926f9c635a030eeb611d4c26e5e73291b32a1c7a4ac56935b5b3"; let name = "dealdrafts"; test_store::insert_ens_name(hash, name); - let val = asc_new(&mut module, hash).unwrap(); - let converted: AscPtr = module.invoke_export1("nameByHash", val); - let data: String = asc_get(&module, converted).unwrap(); + let converted: AscPtr = module.invoke_export1("nameByHash", hash); + let data: String = asc_get(&module, converted, &module.gas).unwrap(); assert_eq!(data, name); - let hash = asc_new(&mut module, "impossible keccak hash").unwrap(); assert!(module - .invoke_export1::<_, AscString>("nameByHash", hash) + .invoke_export1::<_, _, AscString>("nameByHash", "impossible keccak hash") .is_null()); } @@ -843,22 +878,20 @@ fn test_entity_store(api_version: Version) { .unwrap(); let get_user = move |module: &mut WasmInstance, id: &str| -> Option { - let id = asc_new(module, id).unwrap(); let entity_ptr: AscPtr = module.invoke_export1("getUser", id); if entity_ptr.is_null() { None } else { Some(Entity::from( - try_asc_get::, _, _>(module, entity_ptr).unwrap(), + try_asc_get::, _, _>(module, entity_ptr, &module.gas) + .unwrap(), )) } }; let load_and_set_user_name = |module: &mut WasmInstance, id: &str, name: &str| { - let id_ptr = asc_new(module, id).unwrap(); - let name_ptr = asc_new(module, name).unwrap(); module - .invoke_export2_void("loadAndSetUserName", id_ptr, name_ptr) + .invoke_export2_void("loadAndSetUserName", id, name) .unwrap(); }; @@ -966,7 +999,7 @@ fn test_allocate_global(api_version: Version) { ); // Assert globals can be allocated and don't break the heap - module.invoke_export0_void("assert_global_works"); + module.invoke_export0_void("assert_global_works").unwrap(); } #[tokio::test] @@ -989,7 +1022,7 @@ fn test_null_ptr_read(api_version: Version) { api_version, ); - module.invoke_export0_void("nullPtrRead"); + module.invoke_export0_void("nullPtrRead").unwrap(); } #[tokio::test] @@ -1008,7 +1041,7 @@ fn test_safe_null_ptr_read(api_version: Version) { api_version, ); - module.invoke_export0_void("safeNullPtrRead"); + module.invoke_export0_void("safeNullPtrRead").unwrap(); } #[tokio::test] @@ -1016,3 +1049,15 @@ fn test_safe_null_ptr_read(api_version: Version) { async fn safe_null_ptr_read_0_0_5() { test_safe_null_ptr_read(API_VERSION_0_0_5); } + +#[ignore] // Ignored because of long run time in debug build. +#[tokio::test] +async fn test_array_blowup() { + let module = test_module_latest("ArrayBlowup", "array_blowup.wasm"); + + assert!(module + .invoke_export0_void("arrayBlowup") + .unwrap_err() + .to_string() + .contains("Gas limit exceeded. Used: 11286295575421")); +} diff --git a/runtime/test/src/test/abi.rs b/runtime/test/src/test/abi.rs index 7172c5c6319..e468191c7b0 100644 --- a/runtime/test/src/test/abi.rs +++ b/runtime/test/src/test/abi.rs @@ -75,11 +75,8 @@ fn test_abi_array(api_version: Version, gas_used: u64) { "3".to_owned(), "4".to_owned(), ]; - let vec_obj: AscPtr>> = asc_new(&mut module, &*vec).unwrap(); - - let new_vec_obj: AscPtr>> = - module.invoke_export1("test_array", vec_obj); - let new_vec: Vec = asc_get(&module, new_vec_obj).unwrap(); + let new_vec_obj: AscPtr>> = module.invoke_export1("test_array", &vec); + let new_vec: Vec = asc_get(&module, new_vec_obj, &module.gas).unwrap(); assert_eq!(module.gas_used(), gas_used); assert_eq!( @@ -96,12 +93,12 @@ fn test_abi_array(api_version: Version, gas_used: u64) { #[tokio::test] async fn abi_array_v0_0_4() { - test_abi_array(API_VERSION_0_0_4, 200657); + test_abi_array(API_VERSION_0_0_4, 695935); } #[tokio::test] async fn abi_array_v0_0_5() { - test_abi_array(API_VERSION_0_0_5, 722564); + test_abi_array(API_VERSION_0_0_5, 1636130); } fn test_abi_subarray(api_version: Version) { @@ -115,11 +112,9 @@ fn test_abi_subarray(api_version: Version) { ); let vec: Vec = vec![1, 2, 3, 4]; - let vec_obj: AscPtr> = asc_new(&mut module, &*vec).unwrap(); - let new_vec_obj: AscPtr> = - module.invoke_export1("byte_array_third_quarter", vec_obj); - let new_vec: Vec = asc_get(&module, new_vec_obj).unwrap(); + module.invoke_export1("byte_array_third_quarter", vec.as_slice()); + let new_vec: Vec = asc_get(&module, new_vec_obj, &module.gas).unwrap(); assert_eq!(new_vec, vec![3]); } @@ -145,13 +140,10 @@ fn test_abi_bytes_and_fixed_bytes(api_version: Version) { ); let bytes1: Vec = vec![42, 45, 7, 245, 45]; let bytes2: Vec = vec![3, 12, 0, 1, 255]; - - let bytes1_ptr = asc_new::(&mut module, &*bytes1).unwrap(); - let bytes2_ptr = asc_new::(&mut module, &*bytes2).unwrap(); - let new_vec_obj: AscPtr = module.invoke_export2("concat", bytes1_ptr, bytes2_ptr); + let new_vec_obj: AscPtr = module.invoke_export2("concat", &*bytes1, &*bytes2); // This should be bytes1 and bytes2 concatenated. - let new_vec: Vec = asc_get(&module, new_vec_obj).unwrap(); + let new_vec: Vec = asc_get(&module, new_vec_obj, &module.gas).unwrap(); let mut concated = bytes1.clone(); concated.extend(bytes2.clone()); @@ -177,51 +169,43 @@ fn test_abi_ethabi_token_identity(api_version: Version) { ), api_version, ); + let gas = module.gas.cheap_clone(); // Token::Address let address = H160([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); let token_address = Token::Address(address); - let token_address_ptr = asc_new(&mut module, &token_address).unwrap(); let new_address_obj: AscPtr = - module.invoke_export1("token_to_address", token_address_ptr); + module.invoke_export1("token_to_address", &token_address); - let new_token_ptr = module.invoke_export1("token_from_address", new_address_obj); - let new_token = asc_get(&module, new_token_ptr).unwrap(); + let new_token_ptr = module.takes_ptr_returns_ptr("token_from_address", new_address_obj); + let new_token = asc_get(&module, new_token_ptr, &gas).unwrap(); assert_eq!(token_address, new_token); // Token::Bytes let token_bytes = Token::Bytes(vec![42, 45, 7, 245, 45]); - - let token_bytes_ptr = asc_new(&mut module, &token_bytes).unwrap(); - let new_bytes_obj: AscPtr = - module.invoke_export1("token_to_bytes", token_bytes_ptr); - - let new_token_ptr = module.invoke_export1("token_from_bytes", new_bytes_obj); - let new_token = asc_get(&module, new_token_ptr).unwrap(); + let new_bytes_obj: AscPtr = module.invoke_export1("token_to_bytes", &token_bytes); + let new_token_ptr = module.takes_ptr_returns_ptr("token_from_bytes", new_bytes_obj); + let new_token = asc_get(&module, new_token_ptr, &gas).unwrap(); assert_eq!(token_bytes, new_token); // Token::Int let int_token = Token::Int(U256([256, 453452345, 0, 42])); + let new_int_obj: AscPtr = module.invoke_export1("token_to_int", &int_token); - let int_token_ptr = asc_new(&mut module, &int_token).unwrap(); - let new_int_obj: AscPtr = module.invoke_export1("token_to_int", int_token_ptr); - - let new_token_ptr = module.invoke_export1("token_from_int", new_int_obj); - let new_token = asc_get(&module, new_token_ptr).unwrap(); + let new_token_ptr = module.takes_ptr_returns_ptr("token_from_int", new_int_obj); + let new_token = asc_get(&module, new_token_ptr, &gas).unwrap(); assert_eq!(int_token, new_token); // Token::Uint let uint_token = Token::Uint(U256([256, 453452345, 0, 42])); - let uint_token_ptr = asc_new(&mut module, &uint_token).unwrap(); - let new_uint_obj: AscPtr = module.invoke_export1("token_to_uint", uint_token_ptr); - - let new_token_ptr = module.invoke_export1("token_from_uint", new_uint_obj); - let new_token = asc_get(&module, new_token_ptr).unwrap(); + let new_uint_obj: AscPtr = module.invoke_export1("token_to_uint", &uint_token); + let new_token_ptr = module.takes_ptr_returns_ptr("token_from_uint", new_uint_obj); + let new_token = asc_get(&module, new_token_ptr, &module.gas).unwrap(); assert_eq!(uint_token, new_token); assert_ne!(uint_token, int_token); @@ -229,37 +213,31 @@ fn test_abi_ethabi_token_identity(api_version: Version) { // Token::Bool let token_bool = Token::Bool(true); - let token_bool_ptr = asc_new(&mut module, &token_bool).unwrap(); + let token_bool_ptr = asc_new(&mut module, &token_bool, &gas).unwrap(); let func = module.get_func("token_to_bool").typed().unwrap().clone(); let boolean: i32 = func.call(token_bool_ptr.wasm_ptr()).unwrap(); let new_token_ptr = module.takes_val_returns_ptr("token_from_bool", boolean); - let new_token = asc_get(&module, new_token_ptr).unwrap(); + let new_token = asc_get(&module, new_token_ptr, &module.gas).unwrap(); assert_eq!(token_bool, new_token); // Token::String let token_string = Token::String("漢字Go🇧🇷".into()); - - let token_string_ptr = asc_new(&mut module, &token_string).unwrap(); - let new_string_obj: AscPtr = - module.invoke_export1("token_to_string", token_string_ptr); - - let new_token_ptr = module.invoke_export1("token_from_string", new_string_obj); - let new_token = asc_get(&module, new_token_ptr).unwrap(); + let new_string_obj: AscPtr = module.invoke_export1("token_to_string", &token_string); + let new_token_ptr = module.takes_ptr_returns_ptr("token_from_string", new_string_obj); + let new_token = asc_get(&module, new_token_ptr, &module.gas).unwrap(); assert_eq!(token_string, new_token); // Token::Array let token_array = Token::Array(vec![token_address, token_bytes, token_bool]); let token_array_nested = Token::Array(vec![token_string, token_array]); - - let new_array_ptr = asc_new(&mut module, &token_array_nested).unwrap(); let new_array_obj: AscEnumArray = - module.invoke_export1("token_to_array", new_array_ptr); + module.invoke_export1("token_to_array", &token_array_nested); - let new_token_ptr = module.invoke_export1("token_from_array", new_array_obj); - let new_token: Token = asc_get(&module, new_token_ptr).unwrap(); + let new_token_ptr = module.takes_ptr_returns_ptr("token_from_array", new_array_obj); + let new_token: Token = asc_get(&module, new_token_ptr, &module.gas).unwrap(); assert_eq!(new_token, token_array_nested); } @@ -292,53 +270,51 @@ fn test_abi_store_value(api_version: Version) { let func = module.get_func("value_null").typed().unwrap().clone(); let ptr: u32 = func.call(()).unwrap(); let null_value_ptr: AscPtr> = ptr.into(); - let null_value: Value = try_asc_get(&module, null_value_ptr).unwrap(); + let null_value: Value = try_asc_get(&module, null_value_ptr, &module.gas).unwrap(); assert_eq!(null_value, Value::Null); // Value::String let string = "some string"; - let string_ptr = asc_new(&mut module, string).unwrap(); - let new_value_ptr = module.invoke_export1("value_from_string", string_ptr); - let new_value: Value = try_asc_get(&module, new_value_ptr).unwrap(); + let new_value_ptr = module.invoke_export1("value_from_string", string); + let new_value: Value = try_asc_get(&module, new_value_ptr, &module.gas).unwrap(); assert_eq!(new_value, Value::from(string)); // Value::Int let int = i32::min_value(); let new_value_ptr = module.takes_val_returns_ptr("value_from_int", int); - let new_value: Value = try_asc_get(&module, new_value_ptr).unwrap(); + let new_value: Value = try_asc_get(&module, new_value_ptr, &module.gas).unwrap(); assert_eq!(new_value, Value::Int(int)); // Value::BigDecimal let big_decimal = BigDecimal::from_str("3.14159001").unwrap(); - let big_decimal_ptr = asc_new(&mut module, &big_decimal).unwrap(); - let new_value_ptr = module.invoke_export1("value_from_big_decimal", big_decimal_ptr); - let new_value: Value = try_asc_get(&module, new_value_ptr).unwrap(); + let new_value_ptr = module.invoke_export1("value_from_big_decimal", &big_decimal); + let new_value: Value = try_asc_get(&module, new_value_ptr, &module.gas).unwrap(); assert_eq!(new_value, Value::BigDecimal(big_decimal)); let big_decimal = BigDecimal::new(10.into(), 5); - let big_decimal_ptr = asc_new(&mut module, &big_decimal).unwrap(); - let new_value_ptr = module.invoke_export1("value_from_big_decimal", big_decimal_ptr); - let new_value: Value = try_asc_get(&module, new_value_ptr).unwrap(); + let new_value_ptr = module.invoke_export1("value_from_big_decimal", &big_decimal); + let new_value: Value = try_asc_get(&module, new_value_ptr, &module.gas).unwrap(); assert_eq!(new_value, Value::BigDecimal(1_000_000.into())); // Value::Bool let boolean = true; let new_value_ptr = module.takes_val_returns_ptr("value_from_bool", if boolean { 1 } else { 0 }); - let new_value: Value = try_asc_get(&module, new_value_ptr).unwrap(); + let new_value: Value = try_asc_get(&module, new_value_ptr, &module.gas).unwrap(); assert_eq!(new_value, Value::Bool(boolean)); // Value::List + let gas = module.gas.cheap_clone(); let func = module .get_func("array_from_values") .typed() .unwrap() .clone(); let new_value_ptr: u32 = func - .call((asc_new(&mut module, string).unwrap().wasm_ptr(), int)) + .call((asc_new(&mut module, string, &gas).unwrap().wasm_ptr(), int)) .unwrap(); let new_value_ptr = AscPtr::from(new_value_ptr); - let new_value: Value = try_asc_get(&module, new_value_ptr).unwrap(); + let new_value: Value = try_asc_get(&module, new_value_ptr, &gas).unwrap(); assert_eq!( new_value, Value::List(vec![Value::from(string), Value::Int(int)]) @@ -348,9 +324,8 @@ fn test_abi_store_value(api_version: Version) { Value::String("foo".to_owned()), Value::String("bar".to_owned()), ]; - let array_ptr = asc_new(&mut module, array).unwrap(); - let new_value_ptr = module.invoke_export1("value_from_array", array_ptr); - let new_value: Value = try_asc_get(&module, new_value_ptr).unwrap(); + let new_value_ptr = module.invoke_export1("value_from_array", array); + let new_value: Value = try_asc_get(&module, new_value_ptr, &module.gas).unwrap(); assert_eq!( new_value, Value::List(vec![ @@ -361,16 +336,14 @@ fn test_abi_store_value(api_version: Version) { // Value::Bytes let bytes: &[u8] = &[0, 2, 5]; - let bytes_ptr: AscPtr = asc_new(&mut module, bytes).unwrap(); - let new_value_ptr = module.invoke_export1("value_from_bytes", bytes_ptr); - let new_value: Value = try_asc_get(&module, new_value_ptr).unwrap(); + let new_value_ptr = module.invoke_export1("value_from_bytes", bytes); + let new_value: Value = try_asc_get(&module, new_value_ptr, &module.gas).unwrap(); assert_eq!(new_value, Value::Bytes(bytes.into())); // Value::BigInt let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; - let bytes_ptr: AscPtr = asc_new(&mut module, bytes).unwrap(); - let new_value_ptr = module.invoke_export1("value_from_bigint", bytes_ptr); - let new_value: Value = try_asc_get(&module, new_value_ptr).unwrap(); + let new_value_ptr = module.invoke_export1("value_from_bigint", bytes); + let new_value: Value = try_asc_get(&module, new_value_ptr, &module.gas).unwrap(); assert_eq!( new_value, Value::BigInt(::graph::data::store::scalar::BigInt::from_unsigned_bytes_le(bytes)) @@ -399,11 +372,10 @@ fn test_abi_h160(api_version: Version) { let address = H160::zero(); // As an `Uint8Array` - let array_buffer: AscPtr = asc_new(&mut module, &address).unwrap(); - let new_address_obj: AscPtr = module.invoke_export1("test_address", array_buffer); + let new_address_obj: AscPtr = module.invoke_export1("test_address", &address); // This should have 1 added to the first and last byte. - let new_address: H160 = asc_get(&module, new_address_obj).unwrap(); + let new_address: H160 = asc_get(&module, new_address_obj, &module.gas).unwrap(); assert_eq!( new_address, @@ -431,10 +403,8 @@ fn test_string(api_version: Version) { api_version, ); let string = " 漢字Double_Me🇧🇷 "; - let trimmed_string_ptr = asc_new(&mut module, string).unwrap(); - let trimmed_string_obj: AscPtr = - module.invoke_export1("repeat_twice", trimmed_string_ptr); - let doubled_string: String = asc_get(&module, trimmed_string_obj).unwrap(); + let trimmed_string_obj: AscPtr = module.invoke_export1("repeat_twice", string); + let doubled_string: String = asc_get(&module, trimmed_string_obj, &module.gas).unwrap(); assert_eq!(doubled_string, string.repeat(2)); } @@ -460,19 +430,17 @@ fn test_abi_big_int(api_version: Version) { // Test passing in 0 and increment it by 1 let old_uint = U256::zero(); - let array_buffer: AscPtr = - asc_new(&mut module, &BigInt::from_unsigned_u256(&old_uint)).unwrap(); - let new_uint_obj: AscPtr = module.invoke_export1("test_uint", array_buffer); - let new_uint: BigInt = asc_get(&module, new_uint_obj).unwrap(); + let new_uint_obj: AscPtr = + module.invoke_export1("test_uint", &BigInt::from_unsigned_u256(&old_uint)); + let new_uint: BigInt = asc_get(&module, new_uint_obj, &module.gas).unwrap(); assert_eq!(new_uint, BigInt::from(1 as i32)); let new_uint = new_uint.to_unsigned_u256(); assert_eq!(new_uint, U256([1, 0, 0, 0])); // Test passing in -50 and increment it by 1 let old_uint = BigInt::from(-50); - let array_buffer: AscPtr = asc_new(&mut module, &old_uint).unwrap(); - let new_uint_obj: AscPtr = module.invoke_export1("test_uint", array_buffer); - let new_uint: BigInt = asc_get(&module, new_uint_obj).unwrap(); + let new_uint_obj: AscPtr = module.invoke_export1("test_uint", &old_uint); + let new_uint: BigInt = asc_get(&module, new_uint_obj, &module.gas).unwrap(); assert_eq!(new_uint, BigInt::from(-49 as i32)); let new_uint_from_u256 = BigInt::from_signed_u256(&new_uint.to_signed_u256()); assert_eq!(new_uint, new_uint_from_u256); @@ -500,9 +468,8 @@ fn test_big_int_to_string(api_version: Version) { let big_int_str = "30145144166666665000000000000000000"; let big_int = BigInt::from_str(big_int_str).unwrap(); - let ptr: AscPtr = asc_new(&mut module, &big_int).unwrap(); - let string_obj: AscPtr = module.invoke_export1("big_int_to_string", ptr); - let string: String = asc_get(&module, string_obj).unwrap(); + let string_obj: AscPtr = module.invoke_export1("big_int_to_string", &big_int); + let string: String = asc_get(&module, string_obj, &module.gas).unwrap(); assert_eq!(string, big_int_str); } @@ -532,7 +499,7 @@ fn test_invalid_discriminant(api_version: Version) { .unwrap() .clone(); let ptr: u32 = func.call(()).unwrap(); - let _value: Value = try_asc_get(&module, ptr.into()).unwrap(); + let _value: Value = try_asc_get(&module, ptr.into(), &module.gas).unwrap(); } // This should panic rather than exhibiting UB. It's hard to test for UB, but diff --git a/runtime/test/wasm_test/api_version_0_0_5/array_blowup.ts b/runtime/test/wasm_test/api_version_0_0_5/array_blowup.ts new file mode 100644 index 00000000000..a7891afe0b8 --- /dev/null +++ b/runtime/test/wasm_test/api_version_0_0_5/array_blowup.ts @@ -0,0 +1,20 @@ +export * from './common/global' +import { Bytes, Entity, Value } from './common/types' + +/** Definitions copied from graph-ts/index.ts */ +declare namespace store { + function set(entity: string, id: string, data: Entity): void +} + +export function arrayBlowup(): void { + // 1 GB array. + let s = changetype(new Bytes(1_000_000_000).fill(1)); + + // Repeated 100 times. + let a = new Array(100).fill(s); + + let entity = new Entity(); + entity.set("field", Value.fromBytesArray(a)); + store.set("NonExisting", "foo", entity) +} + diff --git a/runtime/test/wasm_test/api_version_0_0_5/array_blowup.wasm b/runtime/test/wasm_test/api_version_0_0_5/array_blowup.wasm new file mode 100644 index 0000000000000000000000000000000000000000..80c5abaec3026f8b8e8ac345b84a8f0c11b2dcb8 GIT binary patch literal 6708 zcmd5=J#1XZ5#FEo?%v(rk&hxpQ6wep9VwZzME!?090S(Up&ZI^oY-;Fg*r(`na7_a zk4M@94AT~zz=aD3NRh&Y3zu;pASzr0fr}tX6}U(M19q1*Ns&r`@_qZ>yB|ihT_ivr zF*Ccfvoo`^v%7N?we58&gpeomH{_n&yP@`o_1@l|xFN)i01dRj6&M%ll+yK$-LQ7E z*=mcRRBN?rcb9IiHDBA=ENr)%t@^p`dRr(xI3T3LU#Ouxr*){Mkh!=p@l8qQ{74h)xqdPIQLoEYU2{IimAK7l`dIyj*W-6{pO_jrNm%K*8F(3wn5$ zFD1ONhc|a6;pH;kD=Qs+`OGePnZl@-nQ1KaGBcIM;nbaLYt33a6E%I@OtzGnpI^E2 zo%*en_1c;)XDjp`&NrKDdSJ9S5$q>y%S_2+VCZn~ zr0d(wjf8(FTa7oIZo+V%mWAE=M*I7uxf&tN8HguMzEW zZgeb_eR;jsYCqeAzS^z!v$eG~eIyH3j~8PnQ}G|SYMXE&^JBjYN3*r1az3o%NBSps zJ)RgzNBToLnR(FT@Ud*Wy0ec@r?WF}dZN-Z^TgD_VqInS#6GEHX+3%>#~&%tRZ%_r zNC7grXZvF{mF?_H_3fRt_5or(g45%v=0vn((hr^-NC$mBz4D|!HQ2|vIHw=$=Ul#| zXZksFSM=#VHH%N`$NNMUq2^3K2WrmtbD(Cnk8=$&=lVI2Ip3G;J!kSlapm?>b9t$a zkcnc1Y;4}Dwd=x!OG_K|*MtV$T-gw%{L<3S23zIA($cNw+FJcqTMU?`rB=JWvUIby zUDp?L>-F_!>#h>HrR_G7TnrZfXa{~OWw>XBd+_k#L;ZYk)qNPb&m&iH$6_JOl&DG< zG^z>`!wt8&<$~3!XjJtxq8hlk?FPxLhL~srt8KXIpr+y8dh3lX7w=X>aKq~pSMl6@ z7b0QW5XKm*r-hYP%qd}&d0r}d@%Mf&{*3p5ZeS#C#Hy-kUOCJOtS6CGR?evs2$-?e zkTd^KO1`P~)K#k%%EH3RQ-VY_;Imv!y2_~+vO#!3rm}%k>2Tn5M=;kB)Q|`|{K)ei zRIfU{RYjOWE8Ro6<=(#cPPA6_OAFyzuCxtgxn`Pm_R2oXty$)$8yx~L;K zMl)Wbl#nS^Z8$YlO_% z7qL^)>L)b!2Lz6h#0eRr$SNjEXQ8Gfv6to<1Yri-AYVWReO`3$7u`ScNGE$YgB41r z+Km|{s$zjTW+?G5Jm`sp`At#kJy@3rBm$}?;DW$*%SHuGaNFoH!apknH&bCBD~h&} z8hQ&lz3VYu#{?K+TKQJM0Q*+N82eU%_AoHl-G6(oa|X(W6L z$txj=49h~s_9LREu|^-Mp-!YE=0t6z#@KGF6SNSM!PE&ejb9;=3=(D<%5pXAkvur6 zCG{xhp^=gBTkIp_x7aTS2Q+i`P{&zb0WqD8dz{U|Suim|X6kG)k;Dj@sj~xcwk!D+ z&dRQ{(DT(<=;JxuaZzK!7+s8dT+G8oFcIk<7fm9GNcXrn2p7AO2fK*ON6PkPPm&eg zTu9QbdO)W9UytfEyM1~fwJjK(g(W8^JEM<22mgiDeFGn|)Blw|d;>k%Es^O#VjOTR zxa<4w4D;83Pq^Iwcz)6Ic;eA^gIf-f#LepPV<>X7PA4xsqKjXCcvpEn=Ni{4+Fy;x z*zCo}g%j8u-*^oO_SXtmTqU{2ZTKIRfuF_7Ah)kF$WdjtGRR#OzC`F$2FixGjY5T@ z!RDN7qXYqKaROC9$a3VGf4uQrL$UA@t9W}LgHD{o%YcTW%lH|)g`nlPuBDGeo`)v4 zP=66bpT;0n9rT@AQBLL8&eZz!sZ25PY+;{HWt zjl*(^Z=?CS#M{j0JwoZFcCB25f2ect312W4_?Qp73qA=D4d#(%p0*KBUsq;1vp7*@ zDZol6dVT^WK~-951F&U`E3$!|L|w8PGhu?~U`ALx-^fprfLjW;B4a*?WO2`xkij}B zC`vAfHSYyqUX)ze*HdYag(H*Q*Ib@N?yX<^?JwF1D8fGnk{--^L$nKf=p}srjG&mQ zKt7EJRw=H-d#hO9qC#mnw5`MvjEh|^QA?}PPF0?8{pgk4`-x>JK@(;%*UOSn_I*v8 z(tSy3zWqTZfNeBNJP)*;6m3>thke`{Z5tq4sKn>AW-_?I<+DHquFx9{_-HT$H6O^?3GwFB2 zJ?&EoPbi4acZfgP6u(vxJ0Wb<4mv{Dp-kZjdDGi9x^B z*t5hS3Pc)|D7#@$vF6R;ywKS^wovUMf5@=;IOI8s+$Tj`b>mLh2tQ^#bt9`YDt2-g z`*yzwDKwws*e6BY)DbR_1-KEshQEfof|D2*^8ChZ!rMXwQ z6txwUM%7xyuM-lnS}AbR&y^Ll;B}%FkkDtCs1%GXEEr76yf2|z%`+JJM8Z_55=+O9 zVOL!dn-L_N;Yol?;*%nSgUsiyQc+bi)g{#+yR1Oq!Qs(hzg z=Jexd&dwSq2aOYeGGG8O2#5h8UQC#DbUk^Gk~*zS-?5KdB6q0MZhJ%Wxy4{w*YehVRAUru`&UO!Osv6 zqhjh(a23OH`jYz~o)$QsYTTGFa>s;wFEa9J;eHtzWR4Is04LV)*A%xzO|-=dS{qQ; z25o|_f!h*yL|wSwCv!iG%t_8Y4tNveE8?a&51BUbHt-Jm=Rmh%tzhI?SSy$*&X@!& zLi;Xsa^7vPzYfkF$iFJweaOSRPYY(2@~8SN(2o{o-i7`hv5dbu`l+P{iKTaorox%y z2h2>Re^oScQn+6i%@C!FfMxhohgY|;PK?5f&x+;@N2dYw`ZmU27Zy?3fw!c~!1Rdz zBR=mLBj3Pkjj40qPXG(hSQqPn4L_$1?>Eu2;O+QmS*(Wr4fMGWHS{^h!XN7Iu9}=} za9)LFO5BCTQWD>iIVNFk6!1NaZouyAUYh$!@7#SzV#S}9Oqr57z%nG8{-5vVC(yM3 zes}{Olnib&Ap!ttm{J9*@Y;M>Hems6h~6)oSHSXLMqLCkaNbU-ge?0KarS()eP5;& z9p68iQtTdX&JPC6QM4MK|zQSAP9(7cqXy_czj fNBk7s!8eZ;@Vl2%#Z}`|%otL5>?!jNpK|{OW4nT@ literal 0 HcmV?d00001 diff --git a/runtime/test/wasm_test/api_version_0_0_5/common/types.ts b/runtime/test/wasm_test/api_version_0_0_5/common/types.ts index c90ab655a29..73a6189c13b 100644 --- a/runtime/test/wasm_test/api_version_0_0_5/common/types.ts +++ b/runtime/test/wasm_test/api_version_0_0_5/common/types.ts @@ -22,14 +22,14 @@ export type FixedBytes = Uint8Array; * Enum for supported value types. */ export enum ValueKind { - STRING = 0, - INT = 1, - BIG_DECIMAL = 2, - BOOL = 3, - ARRAY = 4, - NULL = 5, - BYTES = 6, - BIG_INT = 7, + STRING = 0, + INT = 1, + BIG_DECIMAL = 2, + BOOL = 3, + ARRAY = 4, + NULL = 5, + BYTES = 6, + BIG_INT = 7, } // Big enough to fit any pointer or native `this.data`. export type Payload = u64 @@ -37,354 +37,354 @@ export type Payload = u64 * A dynamically typed value. */ export class Value { - kind: ValueKind - data: Payload + kind: ValueKind + data: Payload - toAddress(): Address { - assert(this.kind == ValueKind.BYTES, 'Value is not an address.') - return changetype

(this.data as u32) - } + toAddress(): Address { + assert(this.kind == ValueKind.BYTES, 'Value is not an address.') + return changetype
(this.data as u32) + } - toBoolean(): boolean { - if (this.kind == ValueKind.NULL) { - return false; - } - assert(this.kind == ValueKind.BOOL, 'Value is not a boolean.') - return this.data != 0 + toBoolean(): boolean { + if (this.kind == ValueKind.NULL) { + return false; } + assert(this.kind == ValueKind.BOOL, 'Value is not a boolean.') + return this.data != 0 + } - toBytes(): Bytes { - assert(this.kind == ValueKind.BYTES, 'Value is not a byte array.') - return changetype(this.data as u32) - } + toBytes(): Bytes { + assert(this.kind == ValueKind.BYTES, 'Value is not a byte array.') + return changetype(this.data as u32) + } - toI32(): i32 { - if (this.kind == ValueKind.NULL) { - return 0; - } - assert(this.kind == ValueKind.INT, 'Value is not an i32.') - return this.data as i32 + toI32(): i32 { + if (this.kind == ValueKind.NULL) { + return 0; } + assert(this.kind == ValueKind.INT, 'Value is not an i32.') + return this.data as i32 + } - toString(): string { - assert(this.kind == ValueKind.STRING, 'Value is not a string.') - return changetype(this.data as u32) - } + toString(): string { + assert(this.kind == ValueKind.STRING, 'Value is not a string.') + return changetype(this.data as u32) + } - toBigInt(): BigInt { - assert(this.kind == ValueKind.BIGINT, 'Value is not a BigInt.') - return changetype(this.data as u32) - } + toBigInt(): BigInt { + assert(this.kind == ValueKind.BIGINT, 'Value is not a BigInt.') + return changetype(this.data as u32) + } - toBigDecimal(): BigDecimal { - assert(this.kind == ValueKind.BIGDECIMAL, 'Value is not a BigDecimal.') - return changetype(this.data as u32) - } + toBigDecimal(): BigDecimal { + assert(this.kind == ValueKind.BIGDECIMAL, 'Value is not a BigDecimal.') + return changetype(this.data as u32) + } - toArray(): Array { - assert(this.kind == ValueKind.ARRAY, 'Value is not an array.') - return changetype>(this.data as u32) - } + toArray(): Array { + assert(this.kind == ValueKind.ARRAY, 'Value is not an array.') + return changetype>(this.data as u32) + } - toBooleanArray(): Array { - let values = this.toArray() - let output = new Array(values.length) - for (let i: i32; i < values.length; i++) { - output[i] = values[i].toBoolean() - } - return output + toBooleanArray(): Array { + let values = this.toArray() + let output = new Array(values.length) + for (let i: i32; i < values.length; i++) { + output[i] = values[i].toBoolean() } + return output + } - toBytesArray(): Array { - let values = this.toArray() - let output = new Array(values.length) - for (let i: i32 = 0; i < values.length; i++) { - output[i] = values[i].toBytes() - } - return output + toBytesArray(): Array { + let values = this.toArray() + let output = new Array(values.length) + for (let i: i32 = 0; i < values.length; i++) { + output[i] = values[i].toBytes() } + return output + } - toStringArray(): Array { - let values = this.toArray() - let output = new Array(values.length) - for (let i: i32 = 0; i < values.length; i++) { - output[i] = values[i].toString() - } - return output + toStringArray(): Array { + let values = this.toArray() + let output = new Array(values.length) + for (let i: i32 = 0; i < values.length; i++) { + output[i] = values[i].toString() } + return output + } - toI32Array(): Array { - let values = this.toArray() - let output = new Array(values.length) - for (let i: i32 = 0; i < values.length; i++) { - output[i] = values[i].toI32() - } - return output + toI32Array(): Array { + let values = this.toArray() + let output = new Array(values.length) + for (let i: i32 = 0; i < values.length; i++) { + output[i] = values[i].toI32() } + return output + } - toBigIntArray(): Array { - let values = this.toArray() - let output = new Array(values.length) - for (let i: i32 = 0; i < values.length; i++) { - output[i] = values[i].toBigInt() - } - return output + toBigIntArray(): Array { + let values = this.toArray() + let output = new Array(values.length) + for (let i: i32 = 0; i < values.length; i++) { + output[i] = values[i].toBigInt() } + return output + } - toBigDecimalArray(): Array { - let values = this.toArray() - let output = new Array(values.length) - for (let i: i32 = 0; i < values.length; i++) { - output[i] = values[i].toBigDecimal() - } - return output + toBigDecimalArray(): Array { + let values = this.toArray() + let output = new Array(values.length) + for (let i: i32 = 0; i < values.length; i++) { + output[i] = values[i].toBigDecimal() } + return output + } - static fromBooleanArray(input: Array): Value { - let output = new Array(input.length) - for (let i: i32 = 0; i < input.length; i++) { - output[i] = Value.fromBoolean(input[i]) - } - return Value.fromArray(output) + static fromBooleanArray(input: Array): Value { + let output = new Array(input.length) + for (let i: i32 = 0; i < input.length; i++) { + output[i] = Value.fromBoolean(input[i]) } + return Value.fromArray(output) + } - static fromBytesArray(input: Array): Value { - let output = new Array(input.length) - for (let i: i32 = 0; i < input.length; i++) { - output[i] = Value.fromBytes(input[i]) - } - return Value.fromArray(output) + static fromBytesArray(input: Array): Value { + let output = new Array(input.length) + for (let i: i32 = 0; i < input.length; i++) { + output[i] = Value.fromBytes(input[i]) } + return Value.fromArray(output) + } - static fromI32Array(input: Array): Value { - let output = new Array(input.length) - for (let i: i32 = 0; i < input.length; i++) { - output[i] = Value.fromI32(input[i]) - } - return Value.fromArray(output) + static fromI32Array(input: Array): Value { + let output = new Array(input.length) + for (let i: i32 = 0; i < input.length; i++) { + output[i] = Value.fromI32(input[i]) } + return Value.fromArray(output) + } - static fromBigIntArray(input: Array): Value { - let output = new Array(input.length) - for (let i: i32 = 0; i < input.length; i++) { - output[i] = Value.fromBigInt(input[i]) - } - return Value.fromArray(output) + static fromBigIntArray(input: Array): Value { + let output = new Array(input.length) + for (let i: i32 = 0; i < input.length; i++) { + output[i] = Value.fromBigInt(input[i]) } + return Value.fromArray(output) + } - static fromBigDecimalArray(input: Array): Value { - let output = new Array(input.length) - for (let i: i32 = 0; i < input.length; i++) { - output[i] = Value.fromBigDecimal(input[i]) - } - return Value.fromArray(output) + static fromBigDecimalArray(input: Array): Value { + let output = new Array(input.length) + for (let i: i32 = 0; i < input.length; i++) { + output[i] = Value.fromBigDecimal(input[i]) } + return Value.fromArray(output) + } - static fromStringArray(input: Array): Value { - let output = new Array(input.length) - for (let i: i32 = 0; i < input.length; i++) { - output[i] = Value.fromString(input[i]) - } - return Value.fromArray(output) + static fromStringArray(input: Array): Value { + let output = new Array(input.length) + for (let i: i32 = 0; i < input.length; i++) { + output[i] = Value.fromString(input[i]) } + return Value.fromArray(output) + } - static fromArray(input: Array): Value { - let value = new Value() - value.kind = ValueKind.ARRAY - value.data = input as u64 - return value - } + static fromArray(input: Array): Value { + let value = new Value() + value.kind = ValueKind.ARRAY + value.data = changetype(input) as u64 + return value + } - static fromBigInt(n: BigInt): Value { - let value = new Value() - value.kind = ValueKind.BIGINT - value.data = n as u64 - return value - } + static fromBigInt(n: BigInt): Value { + let value = new Value() + value.kind = ValueKind.BIGINT + value.data = n as u64 + return value + } - static fromBoolean(b: boolean): Value { - let value = new Value() - value.kind = ValueKind.BOOL - value.data = b ? 1 : 0 - return value - } + static fromBoolean(b: boolean): Value { + let value = new Value() + value.kind = ValueKind.BOOL + value.data = b ? 1 : 0 + return value + } - static fromBytes(bytes: Bytes): Value { - let value = new Value() - value.kind = ValueKind.BYTES - value.data = bytes as u64 - return value - } + static fromBytes(bytes: Bytes): Value { + let value = new Value() + value.kind = ValueKind.BYTES + value.data = changetype(bytes) as u64 + return value + } - static fromNull(): Value { - let value = new Value() - value.kind = ValueKind.NULL - return value - } + static fromNull(): Value { + let value = new Value() + value.kind = ValueKind.NULL + return value + } - static fromI32(n: i32): Value { - let value = new Value() - value.kind = ValueKind.INT - value.data = n as u64 - return value - } + static fromI32(n: i32): Value { + let value = new Value() + value.kind = ValueKind.INT + value.data = n as u64 + return value + } - static fromString(s: string): Value { - let value = new Value() - value.kind = ValueKind.STRING - value.data = changetype(s) - return value - } + static fromString(s: string): Value { + let value = new Value() + value.kind = ValueKind.STRING + value.data = changetype(s) + return value + } - static fromBigDecimal(n: BigDecimal): Value { - let value = new Value() - value.kind = ValueKind.BIGDECIMAL - value.data = n as u64 - return value - } + static fromBigDecimal(n: BigDecimal): Value { + let value = new Value() + value.kind = ValueKind.BIGDECIMAL + value.data = n as u64 + return value + } } /** An arbitrary size integer represented as an array of bytes. */ export class BigInt extends Uint8Array { - toHex(): string { - return typeConversion.bigIntToHex(this) - } + toHex(): string { + return typeConversion.bigIntToHex(this) + } - toHexString(): string { - return typeConversion.bigIntToHex(this) - } + toHexString(): string { + return typeConversion.bigIntToHex(this) + } - toString(): string { - return typeConversion.bigIntToString(this) - } + toString(): string { + return typeConversion.bigIntToString(this) + } - static fromI32(x: i32): BigInt { - return typeConversion.i32ToBigInt(x) as BigInt - } + static fromI32(x: i32): BigInt { + return typeConversion.i32ToBigInt(x) as BigInt + } - toI32(): i32 { - return typeConversion.bigIntToI32(this) - } + toI32(): i32 { + return typeConversion.bigIntToI32(this) + } - @operator('+') - plus(other: BigInt): BigInt { - return bigInt.plus(this, other) - } + @operator('+') + plus(other: BigInt): BigInt { + return bigInt.plus(this, other) + } - @operator('-') - minus(other: BigInt): BigInt { - return bigInt.minus(this, other) - } + @operator('-') + minus(other: BigInt): BigInt { + return bigInt.minus(this, other) + } - @operator('*') - times(other: BigInt): BigInt { - return bigInt.times(this, other) - } + @operator('*') + times(other: BigInt): BigInt { + return bigInt.times(this, other) + } - @operator('/') - div(other: BigInt): BigInt { - return bigInt.dividedBy(this, other) - } + @operator('/') + div(other: BigInt): BigInt { + return bigInt.dividedBy(this, other) + } - divDecimal(other: BigDecimal): BigDecimal { - return bigInt.dividedByDecimal(this, other) - } + divDecimal(other: BigDecimal): BigDecimal { + return bigInt.dividedByDecimal(this, other) + } - @operator('%') - mod(other: BigInt): BigInt { - return bigInt.mod(this, other) - } + @operator('%') + mod(other: BigInt): BigInt { + return bigInt.mod(this, other) + } - @operator('==') - equals(other: BigInt): boolean { - if (this.length !== other.length) { - return false; - } - for (let i = 0; i < this.length; i++) { - if (this[i] !== other[i]) { - return false; - } - } - return true; + @operator('==') + equals(other: BigInt): boolean { + if (this.length !== other.length) { + return false; } - - toBigDecimal(): BigDecimal { - return new BigDecimal(this) + for (let i = 0; i < this.length; i++) { + if (this[i] !== other[i]) { + return false; + } } + return true; + } + + toBigDecimal(): BigDecimal { + return new BigDecimal(this) + } } export class BigDecimal { - exp!: BigInt - digits!: BigInt + exp!: BigInt + digits!: BigInt - constructor(bigInt: BigInt) { - this.digits = bigInt - this.exp = BigInt.fromI32(0) - } + constructor(bigInt: BigInt) { + this.digits = bigInt + this.exp = BigInt.fromI32(0) + } - static fromString(s: string): BigDecimal { - return bigDecimal.fromString(s) - } + static fromString(s: string): BigDecimal { + return bigDecimal.fromString(s) + } - toString(): string { - return bigDecimal.toString(this) - } + toString(): string { + return bigDecimal.toString(this) + } - truncate(decimals: i32): BigDecimal { - let digitsRightOfZero = this.digits.toString().length + this.exp.toI32() - let newDigitLength = decimals + digitsRightOfZero - let truncateLength = this.digits.toString().length - newDigitLength - if (truncateLength < 0) { - return this - } else { - for (let i = 0; i < truncateLength; i++) { - this.digits = this.digits.div(BigInt.fromI32(10)) - } - this.exp = BigInt.fromI32(decimals* -1) - return this - } + truncate(decimals: i32): BigDecimal { + let digitsRightOfZero = this.digits.toString().length + this.exp.toI32() + let newDigitLength = decimals + digitsRightOfZero + let truncateLength = this.digits.toString().length - newDigitLength + if (truncateLength < 0) { + return this + } else { + for (let i = 0; i < truncateLength; i++) { + this.digits = this.digits.div(BigInt.fromI32(10)) + } + this.exp = BigInt.fromI32(decimals * -1) + return this } + } - @operator('+') - plus(other: BigDecimal): BigDecimal { - return bigDecimal.plus(this, other) - } + @operator('+') + plus(other: BigDecimal): BigDecimal { + return bigDecimal.plus(this, other) + } - @operator('-') - minus(other: BigDecimal): BigDecimal { - return bigDecimal.minus(this, other) - } + @operator('-') + minus(other: BigDecimal): BigDecimal { + return bigDecimal.minus(this, other) + } - @operator('*') - times(other: BigDecimal): BigDecimal { - return bigDecimal.times(this, other) - } + @operator('*') + times(other: BigDecimal): BigDecimal { + return bigDecimal.times(this, other) + } - @operator('/') - div(other: BigDecimal): BigDecimal { - return bigDecimal.dividedBy(this, other) - } + @operator('/') + div(other: BigDecimal): BigDecimal { + return bigDecimal.dividedBy(this, other) + } - @operator('==') - equals(other: BigDecimal): boolean { - return bigDecimal.equals(this, other) - } + @operator('==') + equals(other: BigDecimal): boolean { + return bigDecimal.equals(this, other) + } } export enum TokenKind { - ADDRESS = 0, - FIXED_BYTES = 1, - BYTES = 2, - INT = 3, - UINT = 4, - BOOL = 5, - STRING = 6, - FIXED_ARRAY = 7, - ARRAY = 8 + ADDRESS = 0, + FIXED_BYTES = 1, + BYTES = 2, + INT = 3, + UINT = 4, + BOOL = 5, + STRING = 6, + FIXED_ARRAY = 7, + ARRAY = 8 } export class Token { - kind: TokenKind - data: Payload + kind: TokenKind + data: Payload } // Sequence of 4 `u64`s. diff --git a/runtime/wasm/src/asc_abi/class.rs b/runtime/wasm/src/asc_abi/class.rs index 5845c035fbd..2fa2b2528b3 100644 --- a/runtime/wasm/src/asc_abi/class.rs +++ b/runtime/wasm/src/asc_abi/class.rs @@ -3,7 +3,7 @@ use semver::Version; use graph::{ data::store, - runtime::{AscHeap, AscIndexId, AscType, AscValue, IndexForAscTypeId}, + runtime::{gas::GasCounter, AscHeap, AscIndexId, AscType, AscValue, IndexForAscTypeId}, }; use graph::{prelude::serde_json, runtime::DeterministicHostError}; use graph::{prelude::slog, runtime::AscPtr}; @@ -61,8 +61,9 @@ impl AscType for ArrayBuffer { fn asc_size( ptr: AscPtr, heap: &H, + gas: &GasCounter, ) -> Result { - v0_0_4::ArrayBuffer::asc_size(AscPtr::new(ptr.wasm_ptr()), heap) + v0_0_4::ArrayBuffer::asc_size(AscPtr::new(ptr.wasm_ptr()), heap, gas) } fn content_len(&self, asc_bytes: &[u8]) -> usize { @@ -88,13 +89,14 @@ impl TypedArray { pub fn new( content: &[T], heap: &mut H, + gas: &GasCounter, ) -> Result { match heap.api_version() { version if version <= Version::new(0, 0, 4) => Ok(Self::ApiVersion0_0_4( - v0_0_4::TypedArray::new(content, heap)?, + v0_0_4::TypedArray::new(content, heap, gas)?, )), _ => Ok(Self::ApiVersion0_0_5(v0_0_5::TypedArray::new( - content, heap, + content, heap, gas, )?)), } } @@ -102,10 +104,11 @@ impl TypedArray { pub(crate) fn to_vec( &self, heap: &H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { match self { - Self::ApiVersion0_0_4(t) => t.to_vec(heap), - Self::ApiVersion0_0_5(t) => t.to_vec(heap), + Self::ApiVersion0_0_4(t) => t.to_vec(heap, gas), + Self::ApiVersion0_0_5(t) => t.to_vec(heap, gas), } } } @@ -231,8 +234,9 @@ impl AscType for AscString { fn asc_size( ptr: AscPtr, heap: &H, + gas: &GasCounter, ) -> Result { - v0_0_4::AscString::asc_size(AscPtr::new(ptr.wasm_ptr()), heap) + v0_0_4::AscString::asc_size(AscPtr::new(ptr.wasm_ptr()), heap, gas) } fn content_len(&self, asc_bytes: &[u8]) -> usize { @@ -254,22 +258,26 @@ impl Array { pub fn new( content: &[T], heap: &mut H, + gas: &GasCounter, ) -> Result { match heap.api_version() { - version if version <= Version::new(0, 0, 4) => { - Ok(Self::ApiVersion0_0_4(v0_0_4::Array::new(content, heap)?)) - } - _ => Ok(Self::ApiVersion0_0_5(v0_0_5::Array::new(content, heap)?)), + version if version <= Version::new(0, 0, 4) => Ok(Self::ApiVersion0_0_4( + v0_0_4::Array::new(content, heap, gas)?, + )), + _ => Ok(Self::ApiVersion0_0_5(v0_0_5::Array::new( + content, heap, gas, + )?)), } } pub(crate) fn to_vec( &self, heap: &H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { match self { - Self::ApiVersion0_0_4(a) => a.to_vec(heap), - Self::ApiVersion0_0_5(a) => a.to_vec(heap), + Self::ApiVersion0_0_4(a) => a.to_vec(heap, gas), + Self::ApiVersion0_0_5(a) => a.to_vec(heap, gas), } } } diff --git a/runtime/wasm/src/asc_abi/v0_0_4.rs b/runtime/wasm/src/asc_abi/v0_0_4.rs index 644cc944502..92f14edf362 100644 --- a/runtime/wasm/src/asc_abi/v0_0_4.rs +++ b/runtime/wasm/src/asc_abi/v0_0_4.rs @@ -1,3 +1,4 @@ +use graph::runtime::gas::GasCounter; use std::convert::TryInto as _; use std::marker::PhantomData; use std::mem::{size_of, size_of_val}; @@ -124,8 +125,9 @@ impl AscType for ArrayBuffer { fn asc_size( ptr: AscPtr, heap: &H, + gas: &GasCounter, ) -> Result { - let byte_length = ptr.read_u32(heap)?; + let byte_length = ptr.read_u32(heap, gas)?; let byte_length_size = size_of::() as u32; let padding_size = size_of::() as u32; Ok(byte_length_size + padding_size + byte_length) @@ -150,6 +152,7 @@ impl TypedArray { pub(crate) fn new( content: &[T], heap: &mut H, + gas: &GasCounter, ) -> Result { let buffer = class::ArrayBuffer::new(content, heap.api_version())?; let buffer_byte_length = if let class::ArrayBuffer::ApiVersion0_0_4(ref a) = buffer { @@ -157,7 +160,7 @@ impl TypedArray { } else { unreachable!("Only the correct ArrayBuffer will be constructed") }; - let ptr = AscPtr::alloc_obj(buffer, heap)?; + let ptr = AscPtr::alloc_obj(buffer, heap, gas)?; Ok(TypedArray { byte_length: buffer_byte_length, buffer: AscPtr::new(ptr.wasm_ptr()), @@ -169,8 +172,9 @@ impl TypedArray { pub(crate) fn to_vec( &self, heap: &H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { - self.buffer.read_ptr(heap)?.get( + self.buffer.read_ptr(heap, gas)?.get( self.byte_offset, self.byte_length / size_of::() as u32, heap.api_version(), @@ -275,8 +279,9 @@ impl AscType for AscString { fn asc_size( ptr: AscPtr, heap: &H, + gas: &GasCounter, ) -> Result { - let length = ptr.read_u32(heap)?; + let length = ptr.read_u32(heap, gas)?; let length_size = size_of::() as u32; let code_point_size = size_of::() as u32; let data_size = code_point_size.checked_mul(length); @@ -301,9 +306,10 @@ impl Array { pub fn new( content: &[T], heap: &mut H, + gas: &GasCounter, ) -> Result { let arr_buffer = class::ArrayBuffer::new(content, heap.api_version())?; - let arr_buffer_ptr = AscPtr::alloc_obj(arr_buffer, heap)?; + let arr_buffer_ptr = AscPtr::alloc_obj(arr_buffer, heap, gas)?; Ok(Array { buffer: AscPtr::new(arr_buffer_ptr.wasm_ptr()), // If this cast would overflow, the above line has already panicked. @@ -315,9 +321,10 @@ impl Array { pub(crate) fn to_vec( &self, heap: &H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { self.buffer - .read_ptr(heap)? + .read_ptr(heap, gas)? .get(0, self.length, heap.api_version()) } } diff --git a/runtime/wasm/src/asc_abi/v0_0_5.rs b/runtime/wasm/src/asc_abi/v0_0_5.rs index 59b1bfa37d8..31503af0b5a 100644 --- a/runtime/wasm/src/asc_abi/v0_0_5.rs +++ b/runtime/wasm/src/asc_abi/v0_0_5.rs @@ -4,6 +4,7 @@ use std::mem::{size_of, size_of_val}; use anyhow::anyhow; use semver::Version; +use graph::runtime::gas::GasCounter; use graph::runtime::{AscHeap, AscPtr, AscType, AscValue, DeterministicHostError, HEADER_SIZE}; use graph_runtime_derive::AscType; @@ -114,10 +115,11 @@ impl TypedArray { pub(crate) fn new( content: &[T], heap: &mut H, + gas: &GasCounter, ) -> Result { let buffer = class::ArrayBuffer::new(content, heap.api_version())?; let byte_length = content.len() as u32; - let ptr = AscPtr::alloc_obj(buffer, heap)?; + let ptr = AscPtr::alloc_obj(buffer, heap, gas)?; Ok(TypedArray { buffer: AscPtr::new(ptr.wasm_ptr()), // new AscPtr necessary to convert type parameter data_start: ptr.wasm_ptr(), @@ -129,6 +131,7 @@ impl TypedArray { pub(crate) fn to_vec( &self, heap: &H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { // We're trying to read the pointer below, we should check it's // not null before using it. @@ -150,7 +153,7 @@ impl TypedArray { )) })?; - self.buffer.read_ptr(heap)?.get( + self.buffer.read_ptr(heap, gas)?.get( data_start_with_offset, self.byte_length / size_of::() as u32, heap.api_version(), @@ -262,10 +265,11 @@ impl Array { pub fn new( content: &[T], heap: &mut H, + gas: &GasCounter, ) -> Result { let arr_buffer = class::ArrayBuffer::new(content, heap.api_version())?; - let buffer = AscPtr::alloc_obj(arr_buffer, heap)?; - let buffer_data_length = buffer.read_len(heap)?; + let buffer = AscPtr::alloc_obj(arr_buffer, heap, gas)?; + let buffer_data_length = buffer.read_len(heap, gas)?; Ok(Array { buffer: AscPtr::new(buffer.wasm_ptr()), buffer_data_start: buffer.wasm_ptr(), @@ -278,6 +282,7 @@ impl Array { pub(crate) fn to_vec( &self, heap: &H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { // We're trying to read the pointer below, we should check it's // not null before using it. @@ -299,7 +304,7 @@ impl Array { )) })?; - self.buffer.read_ptr(heap)?.get( + self.buffer.read_ptr(heap, gas)?.get( buffer_data_start_with_offset, self.length as u32, heap.api_version(), diff --git a/runtime/wasm/src/gas_rules.rs b/runtime/wasm/src/gas_rules.rs index 45e01f4b7a6..363480da33a 100644 --- a/runtime/wasm/src/gas_rules.rs +++ b/runtime/wasm/src/gas_rules.rs @@ -4,6 +4,9 @@ use graph::runtime::gas::CONST_MAX_GAS_PER_HANDLER; use parity_wasm::elements::Instruction; use pwasm_utils::rules::{MemoryGrowCost, Rules}; +pub const GAS_COST_STORE: u32 = 2263; +pub const GAS_COST_LOAD: u32 = 1573; + pub struct GasRules; impl Rules for GasRules { @@ -14,8 +17,8 @@ impl Rules for GasRules { // from the table under the "Schedule" dropdown. Each decimal is multiplied by 10. // Note that those were calculated for wasi, not wasmtime, so they are likely very conservative. I64Const(_) => 16, - I64Load(_, _) => 1573, - I64Store(_, _) => 2263, + I64Load(_, _) => GAS_COST_LOAD, + I64Store(_, _) => GAS_COST_STORE, Select => 61, Instruction::If(_) => 79, Br(_) => 30, @@ -78,7 +81,7 @@ impl Rules for GasRules { | I64Load16S(_, _) | I64Load16U(_, _) | I64Load32S(_, _) - | I64Load32U(_, _) => 1573, + | I64Load32U(_, _) => GAS_COST_LOAD, I32Store(_, _) | F32Store(_, _) @@ -87,7 +90,7 @@ impl Rules for GasRules { | I32Store16(_, _) | I64Store8(_, _) | I64Store16(_, _) - | I64Store32(_, _) => 2263, + | I64Store32(_, _) => GAS_COST_STORE, I32Const(_) | F32Const(_) | F64Const(_) => 16, I32Eqz => 26, diff --git a/runtime/wasm/src/module/mod.rs b/runtime/wasm/src/module/mod.rs index 34785e0b613..7d06e3e5842 100644 --- a/runtime/wasm/src/module/mod.rs +++ b/runtime/wasm/src/module/mod.rs @@ -27,6 +27,7 @@ pub use stopwatch::TimeoutStopwatch; use crate::asc_abi::class::*; use crate::error::DeterminismLevel; +use crate::gas_rules::{GAS_COST_LOAD, GAS_COST_STORE}; pub use crate::host_exports; use crate::host_exports::HostExports; use crate::mapping::MappingContext; @@ -54,7 +55,7 @@ pub struct WasmInstance { pub instance_ctx: Rc>>>, // A reference to the gas counter used for reporting the gas used. - gas: GasCounter, + pub gas: GasCounter, } impl Drop for WasmInstance { @@ -66,13 +67,18 @@ impl Drop for WasmInstance { /// Proxies to the WasmInstanceContext. impl AscHeap for WasmInstance { - fn raw_new(&mut self, bytes: &[u8]) -> Result { + fn raw_new(&mut self, bytes: &[u8], gas: &GasCounter) -> Result { let mut ctx = RefMut::map(self.instance_ctx.borrow_mut(), |i| i.as_mut().unwrap()); - ctx.raw_new(bytes) + ctx.raw_new(bytes, gas) } - fn get(&self, offset: u32, size: u32) -> Result, DeterministicHostError> { - self.instance_ctx().get(offset, size) + fn get( + &self, + offset: u32, + size: u32, + gas: &GasCounter, + ) -> Result, DeterministicHostError> { + self.instance_ctx().get(offset, size, gas) } fn api_version(&self) -> Version { @@ -94,8 +100,9 @@ impl WasmInstance { value: &serde_json::Value, user_data: &store::Value, ) -> Result, anyhow::Error> { - let value = asc_new(&mut self, value)?; - let user_data = asc_new(&mut self, user_data)?; + let gas = GasCounter::new(); + let value = asc_new(&mut self, value, &gas)?; + let user_data = asc_new(&mut self, user_data, &gas)?; self.instance_ctx_mut().ctx.state.enter_handler(); @@ -117,7 +124,8 @@ impl WasmInstance { trigger: TriggerWithHandler, ) -> Result<(BlockState, Gas), MappingError> { let handler_name = trigger.handler_name().to_owned(); - let asc_trigger = trigger.to_asc_ptr(&mut self)?; + let gas = self.gas.clone(); + let asc_trigger = trigger.to_asc_ptr(&mut self, &gas)?; self.invoke_handler(&handler_name, asc_trigger) } @@ -588,7 +596,11 @@ impl WasmInstance { } impl AscHeap for WasmInstanceContext { - fn raw_new(&mut self, bytes: &[u8]) -> Result { + fn raw_new(&mut self, bytes: &[u8], gas: &GasCounter) -> Result { + // The cost of writing to wasm memory from the host is the same as of writing from wasm + // using load instructions. + gas.consume_host_fn(Gas::new(GAS_COST_STORE as u64 * bytes.len() as u64))?; + // We request large chunks from the AssemblyScript allocator to use as arenas that we // manage directly. @@ -631,7 +643,16 @@ impl AscHeap for WasmInstanceContext { Ok(ptr as u32) } - fn get(&self, offset: u32, size: u32) -> Result, DeterministicHostError> { + fn get( + &self, + offset: u32, + size: u32, + gas: &GasCounter, + ) -> Result, DeterministicHostError> { + // The cost of reading wasm memory from the host is the same as of reading from wasm using + // load instructions. + gas.consume_host_fn(Gas::new(GAS_COST_LOAD as u64 * size as u64))?; + let offset = offset as usize; let size = size as usize; @@ -791,11 +812,11 @@ impl WasmInstanceContext { column_number: u32, ) -> Result { let message = match message_ptr.is_null() { - false => Some(asc_get(self, message_ptr)?), + false => Some(asc_get(self, message_ptr, gas)?), true => None, }; let file_name = match file_name_ptr.is_null() { - false => Some(asc_get(self, file_name_ptr)?), + false => Some(asc_get(self, file_name_ptr, gas)?), true => None, }; let line_number = match line_number { @@ -823,9 +844,9 @@ impl WasmInstanceContext { let stopwatch = &self.host_metrics.stopwatch; stopwatch.start_section("host_export_store_set__wasm_instance_context_store_set"); - let entity = asc_get(self, entity_ptr)?; - let id = asc_get(self, id_ptr)?; - let data = try_asc_get(self, data_ptr)?; + let entity = asc_get(self, entity_ptr, gas)?; + let id = asc_get(self, id_ptr, gas)?; + let data = try_asc_get(self, data_ptr, gas)?; self.ctx.host_exports.store_set( &self.ctx.logger, @@ -848,8 +869,8 @@ impl WasmInstanceContext { entity_ptr: AscPtr, id_ptr: AscPtr, ) -> Result<(), HostExportError> { - let entity = asc_get(self, entity_ptr)?; - let id = asc_get(self, id_ptr)?; + let entity = asc_get(self, entity_ptr, gas)?; + let id = asc_get(self, id_ptr, gas)?; self.ctx.host_exports.store_remove( &self.ctx.logger, &mut self.ctx.state, @@ -872,8 +893,8 @@ impl WasmInstanceContext { .cheap_clone() .time_host_fn_execution_region("store_get"); - let entity_type: String = asc_get(self, entity_ptr)?; - let id: String = asc_get(self, id_ptr)?; + let entity_type: String = asc_get(self, entity_ptr, gas)?; + let id: String = asc_get(self, id_ptr, gas)?; let entity_option = self.ctx.host_exports.store_get( &mut self.ctx.state, entity_type.clone(), @@ -887,7 +908,7 @@ impl WasmInstanceContext { .host_metrics .stopwatch .start_section("store_get_asc_new"); - asc_new(self, &entity.sorted())? + asc_new(self, &entity.sorted(), gas)? } None => match &self.ctx.debug_fork { Some(fork) => { @@ -903,7 +924,7 @@ impl WasmInstanceContext { .host_metrics .stopwatch .start_section("store_get_asc_new"); - let entity = asc_new(self, &entity.sorted())?; + let entity = asc_new(self, &entity.sorted(), gas)?; self.store_set(gas, entity_ptr, id_ptr, entity)?; entity } @@ -925,10 +946,10 @@ impl WasmInstanceContext { ) -> Result, DeterministicHostError> { let string = self.ctx.host_exports.bytes_to_string( &self.ctx.logger, - asc_get(self, bytes_ptr)?, + asc_get(self, bytes_ptr, gas)?, gas, )?; - asc_new(self, &string) + asc_new(self, &string, gas) } /// Converts bytes to a hex string. @@ -941,13 +962,13 @@ impl WasmInstanceContext { gas: &GasCounter, bytes_ptr: AscPtr, ) -> Result, DeterministicHostError> { - let bytes: Vec = asc_get(self, bytes_ptr)?; + let bytes: Vec = asc_get(self, bytes_ptr, gas)?; gas.consume_host_fn(gas::DEFAULT_GAS_OP.with_args(gas::complexity::Size, &bytes))?; // Even an empty string must be prefixed with `0x`. // Encodes each byte as a two hex digits. let hex = format!("0x{}", hex::encode(bytes)); - asc_new(self, &hex) + asc_new(self, &hex, gas) } /// function typeConversion.bigIntToString(n: Uint8Array): string @@ -956,9 +977,9 @@ impl WasmInstanceContext { gas: &GasCounter, big_int_ptr: AscPtr, ) -> Result, DeterministicHostError> { - let n: BigInt = asc_get(self, big_int_ptr)?; + let n: BigInt = asc_get(self, big_int_ptr, gas)?; gas.consume_host_fn(gas::DEFAULT_GAS_OP.with_args(gas::complexity::Size, &n))?; - asc_new(self, &n.to_string()) + asc_new(self, &n.to_string(), gas) } /// function bigInt.fromString(x: string): BigInt @@ -970,8 +991,8 @@ impl WasmInstanceContext { let result = self .ctx .host_exports - .big_int_from_string(asc_get(self, string_ptr)?, gas)?; - asc_new(self, &result) + .big_int_from_string(asc_get(self, string_ptr, gas)?, gas)?; + asc_new(self, &result, gas) } /// function typeConversion.bigIntToHex(n: Uint8Array): string @@ -980,9 +1001,9 @@ impl WasmInstanceContext { gas: &GasCounter, big_int_ptr: AscPtr, ) -> Result, DeterministicHostError> { - let n: BigInt = asc_get(self, big_int_ptr)?; + let n: BigInt = asc_get(self, big_int_ptr, gas)?; let hex = self.ctx.host_exports.big_int_to_hex(n, gas)?; - asc_new(self, &hex) + asc_new(self, &hex, gas) } /// function typeConversion.stringToH160(s: String): H160 @@ -991,9 +1012,9 @@ impl WasmInstanceContext { gas: &GasCounter, str_ptr: AscPtr, ) -> Result, DeterministicHostError> { - let s: String = asc_get(self, str_ptr)?; + let s: String = asc_get(self, str_ptr, gas)?; let h160 = self.ctx.host_exports.string_to_h160(&s, gas)?; - asc_new(self, &h160) + asc_new(self, &h160, gas) } /// function json.fromBytes(bytes: Bytes): JSONValue @@ -1002,7 +1023,7 @@ impl WasmInstanceContext { gas: &GasCounter, bytes_ptr: AscPtr, ) -> Result>, DeterministicHostError> { - let bytes: Vec = asc_get(self, bytes_ptr)?; + let bytes: Vec = asc_get(self, bytes_ptr, gas)?; let result = self .ctx .host_exports @@ -1014,7 +1035,7 @@ impl WasmInstanceContext { ) }) .map_err(DeterministicHostError::from)?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function json.try_fromBytes(bytes: Bytes): Result @@ -1024,7 +1045,7 @@ impl WasmInstanceContext { bytes_ptr: AscPtr, ) -> Result>, bool>>, DeterministicHostError> { - let bytes: Vec = asc_get(self, bytes_ptr)?; + let bytes: Vec = asc_get(self, bytes_ptr, gas)?; let result = self .ctx .host_exports @@ -1041,7 +1062,7 @@ impl WasmInstanceContext { // result type expected by mappings true }); - asc_new(self, &result) + asc_new(self, &result, gas) } /// function ipfs.cat(link: String): Bytes @@ -1050,8 +1071,8 @@ impl WasmInstanceContext { gas: &GasCounter, link_ptr: AscPtr, ) -> Result, HostExportError> { - // Not enabled on the network, no gas consumed. - drop(gas); + // Note on gas: There is no gas costing for the ipfs call itself, + // since it's not enabled on the network. if !self.experimental_features.allow_non_deterministic_ipfs { return Err(HostExportError::Deterministic(anyhow!( @@ -1059,15 +1080,15 @@ impl WasmInstanceContext { ))); } - let link = asc_get(self, link_ptr)?; + let link = asc_get(self, link_ptr, gas)?; let ipfs_res = self.ctx.host_exports.ipfs_cat(&self.ctx.logger, link); match ipfs_res { - Ok(bytes) => asc_new(self, &*bytes).map_err(Into::into), + Ok(bytes) => asc_new(self, &*bytes, gas).map_err(Into::into), // Return null in case of error. Err(e) => { info!(&self.ctx.logger, "Failed ipfs.cat, returning `null`"; - "link" => asc_get::(self, link_ptr)?, + "link" => asc_get::(self, link_ptr, gas)?, "error" => e.to_string()); Ok(AscPtr::null()) } @@ -1083,10 +1104,9 @@ impl WasmInstanceContext { user_data: AscPtr>, flags: AscPtr>>, ) -> Result<(), HostExportError> { - // Does not consume gas because this is not a part of deterministic APIs. + // Note on gas: // Ideally we would consume gas the same as ipfs_cat and then share // gas across the spawned modules for callbacks. - drop(gas); if !self.experimental_features.allow_non_deterministic_ipfs { return Err(HostExportError::Deterministic(anyhow!( @@ -1094,11 +1114,11 @@ impl WasmInstanceContext { ))); } - let link: String = asc_get(self, link_ptr)?; - let callback: String = asc_get(self, callback)?; - let user_data: store::Value = try_asc_get(self, user_data)?; + let link: String = asc_get(self, link_ptr, gas)?; + let callback: String = asc_get(self, callback, gas)?; + let user_data: store::Value = try_asc_get(self, user_data, gas)?; - let flags = asc_get(self, flags)?; + let flags = asc_get(self, flags, gas)?; // Pause the timeout while running ipfs_map, ensure it will be restarted by using a guard. self.timeout_stopwatch.lock().unwrap().stop(); @@ -1139,7 +1159,7 @@ impl WasmInstanceContext { ) -> Result { self.ctx .host_exports - .json_to_i64(asc_get(self, json_ptr)?, gas) + .json_to_i64(asc_get(self, json_ptr, gas)?, gas) } /// Expects a decimal string. @@ -1151,7 +1171,7 @@ impl WasmInstanceContext { ) -> Result { self.ctx .host_exports - .json_to_u64(asc_get(self, json_ptr)?, gas) + .json_to_u64(asc_get(self, json_ptr, gas)?, gas) } /// Expects a decimal string. @@ -1163,7 +1183,7 @@ impl WasmInstanceContext { ) -> Result { self.ctx .host_exports - .json_to_f64(asc_get(self, json_ptr)?, gas) + .json_to_f64(asc_get(self, json_ptr, gas)?, gas) } /// Expects a decimal string. @@ -1176,8 +1196,8 @@ impl WasmInstanceContext { let big_int = self .ctx .host_exports - .json_to_big_int(asc_get(self, json_ptr)?, gas)?; - asc_new(self, &*big_int) + .json_to_big_int(asc_get(self, json_ptr, gas)?, gas)?; + asc_new(self, &*big_int, gas) } /// function crypto.keccak256(input: Bytes): Bytes @@ -1189,8 +1209,8 @@ impl WasmInstanceContext { let input = self .ctx .host_exports - .crypto_keccak_256(asc_get(self, input_ptr)?, gas)?; - asc_new(self, input.as_ref()) + .crypto_keccak_256(asc_get(self, input_ptr, gas)?, gas)?; + asc_new(self, input.as_ref(), gas) } /// function bigInt.plus(x: BigInt, y: BigInt): BigInt @@ -1201,11 +1221,11 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result, DeterministicHostError> { let result = self.ctx.host_exports.big_int_plus( - asc_get(self, x_ptr)?, - asc_get(self, y_ptr)?, + asc_get(self, x_ptr, gas)?, + asc_get(self, y_ptr, gas)?, gas, )?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function bigInt.minus(x: BigInt, y: BigInt): BigInt @@ -1216,11 +1236,11 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result, DeterministicHostError> { let result = self.ctx.host_exports.big_int_minus( - asc_get(self, x_ptr)?, - asc_get(self, y_ptr)?, + asc_get(self, x_ptr, gas)?, + asc_get(self, y_ptr, gas)?, gas, )?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function bigInt.times(x: BigInt, y: BigInt): BigInt @@ -1231,11 +1251,11 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result, DeterministicHostError> { let result = self.ctx.host_exports.big_int_times( - asc_get(self, x_ptr)?, - asc_get(self, y_ptr)?, + asc_get(self, x_ptr, gas)?, + asc_get(self, y_ptr, gas)?, gas, )?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function bigInt.dividedBy(x: BigInt, y: BigInt): BigInt @@ -1246,11 +1266,11 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result, DeterministicHostError> { let result = self.ctx.host_exports.big_int_divided_by( - asc_get(self, x_ptr)?, - asc_get(self, y_ptr)?, + asc_get(self, x_ptr, gas)?, + asc_get(self, y_ptr, gas)?, gas, )?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function bigInt.dividedByDecimal(x: BigInt, y: BigDecimal): BigDecimal @@ -1260,12 +1280,12 @@ impl WasmInstanceContext { x_ptr: AscPtr, y_ptr: AscPtr, ) -> Result, DeterministicHostError> { - let x = BigDecimal::new(asc_get(self, x_ptr)?, 0); + let x = BigDecimal::new(asc_get(self, x_ptr, gas)?, 0); let result = self.ctx .host_exports - .big_decimal_divided_by(x, try_asc_get(self, y_ptr)?, gas)?; - asc_new(self, &result) + .big_decimal_divided_by(x, try_asc_get(self, y_ptr, gas)?, gas)?; + asc_new(self, &result, gas) } /// function bigInt.mod(x: BigInt, y: BigInt): BigInt @@ -1275,11 +1295,12 @@ impl WasmInstanceContext { x_ptr: AscPtr, y_ptr: AscPtr, ) -> Result, DeterministicHostError> { - let result = - self.ctx - .host_exports - .big_int_mod(asc_get(self, x_ptr)?, asc_get(self, y_ptr)?, gas)?; - asc_new(self, &result) + let result = self.ctx.host_exports.big_int_mod( + asc_get(self, x_ptr, gas)?, + asc_get(self, y_ptr, gas)?, + gas, + )?; + asc_new(self, &result, gas) } /// function bigInt.pow(x: BigInt, exp: u8): BigInt @@ -1293,8 +1314,8 @@ impl WasmInstanceContext { let result = self .ctx .host_exports - .big_int_pow(asc_get(self, x_ptr)?, exp, gas)?; - asc_new(self, &result) + .big_int_pow(asc_get(self, x_ptr, gas)?, exp, gas)?; + asc_new(self, &result, gas) } /// function bigInt.bitOr(x: BigInt, y: BigInt): BigInt @@ -1305,11 +1326,11 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result, DeterministicHostError> { let result = self.ctx.host_exports.big_int_bit_or( - asc_get(self, x_ptr)?, - asc_get(self, y_ptr)?, + asc_get(self, x_ptr, gas)?, + asc_get(self, y_ptr, gas)?, gas, )?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function bigInt.bitAnd(x: BigInt, y: BigInt): BigInt @@ -1320,11 +1341,11 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result, DeterministicHostError> { let result = self.ctx.host_exports.big_int_bit_and( - asc_get(self, x_ptr)?, - asc_get(self, y_ptr)?, + asc_get(self, x_ptr, gas)?, + asc_get(self, y_ptr, gas)?, gas, )?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function bigInt.leftShift(x: BigInt, bits: u8): BigInt @@ -1335,11 +1356,11 @@ impl WasmInstanceContext { bits: u32, ) -> Result, DeterministicHostError> { let bits = u8::try_from(bits).map_err(|e| DeterministicHostError::from(Error::from(e)))?; - let result = self - .ctx - .host_exports - .big_int_left_shift(asc_get(self, x_ptr)?, bits, gas)?; - asc_new(self, &result) + let result = + self.ctx + .host_exports + .big_int_left_shift(asc_get(self, x_ptr, gas)?, bits, gas)?; + asc_new(self, &result, gas) } /// function bigInt.rightShift(x: BigInt, bits: u8): BigInt @@ -1350,11 +1371,11 @@ impl WasmInstanceContext { bits: u32, ) -> Result, DeterministicHostError> { let bits = u8::try_from(bits).map_err(|e| DeterministicHostError::from(Error::from(e)))?; - let result = self - .ctx - .host_exports - .big_int_right_shift(asc_get(self, x_ptr)?, bits, gas)?; - asc_new(self, &result) + let result = + self.ctx + .host_exports + .big_int_right_shift(asc_get(self, x_ptr, gas)?, bits, gas)?; + asc_new(self, &result, gas) } /// function typeConversion.bytesToBase58(bytes: Bytes): string @@ -1366,8 +1387,8 @@ impl WasmInstanceContext { let result = self .ctx .host_exports - .bytes_to_base58(asc_get(self, bytes_ptr)?, gas)?; - asc_new(self, &result) + .bytes_to_base58(asc_get(self, bytes_ptr, gas)?, gas)?; + asc_new(self, &result, gas) } /// function bigDecimal.toString(x: BigDecimal): string @@ -1379,8 +1400,8 @@ impl WasmInstanceContext { let result = self .ctx .host_exports - .big_decimal_to_string(try_asc_get(self, big_decimal_ptr)?, gas)?; - asc_new(self, &result) + .big_decimal_to_string(try_asc_get(self, big_decimal_ptr, gas)?, gas)?; + asc_new(self, &result, gas) } /// function bigDecimal.fromString(x: string): BigDecimal @@ -1392,8 +1413,8 @@ impl WasmInstanceContext { let result = self .ctx .host_exports - .big_decimal_from_string(asc_get(self, string_ptr)?, gas)?; - asc_new(self, &result) + .big_decimal_from_string(asc_get(self, string_ptr, gas)?, gas)?; + asc_new(self, &result, gas) } /// function bigDecimal.plus(x: BigDecimal, y: BigDecimal): BigDecimal @@ -1404,11 +1425,11 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result, DeterministicHostError> { let result = self.ctx.host_exports.big_decimal_plus( - try_asc_get(self, x_ptr)?, - try_asc_get(self, y_ptr)?, + try_asc_get(self, x_ptr, gas)?, + try_asc_get(self, y_ptr, gas)?, gas, )?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function bigDecimal.minus(x: BigDecimal, y: BigDecimal): BigDecimal @@ -1419,11 +1440,11 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result, DeterministicHostError> { let result = self.ctx.host_exports.big_decimal_minus( - try_asc_get(self, x_ptr)?, - try_asc_get(self, y_ptr)?, + try_asc_get(self, x_ptr, gas)?, + try_asc_get(self, y_ptr, gas)?, gas, )?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function bigDecimal.times(x: BigDecimal, y: BigDecimal): BigDecimal @@ -1434,11 +1455,11 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result, DeterministicHostError> { let result = self.ctx.host_exports.big_decimal_times( - try_asc_get(self, x_ptr)?, - try_asc_get(self, y_ptr)?, + try_asc_get(self, x_ptr, gas)?, + try_asc_get(self, y_ptr, gas)?, gas, )?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function bigDecimal.dividedBy(x: BigDecimal, y: BigDecimal): BigDecimal @@ -1449,11 +1470,11 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result, DeterministicHostError> { let result = self.ctx.host_exports.big_decimal_divided_by( - try_asc_get(self, x_ptr)?, - try_asc_get(self, y_ptr)?, + try_asc_get(self, x_ptr, gas)?, + try_asc_get(self, y_ptr, gas)?, gas, )?; - asc_new(self, &result) + asc_new(self, &result, gas) } /// function bigDecimal.equals(x: BigDecimal, y: BigDecimal): bool @@ -1464,8 +1485,8 @@ impl WasmInstanceContext { y_ptr: AscPtr, ) -> Result { self.ctx.host_exports.big_decimal_equals( - try_asc_get(self, x_ptr)?, - try_asc_get(self, y_ptr)?, + try_asc_get(self, x_ptr, gas)?, + try_asc_get(self, y_ptr, gas)?, gas, ) } @@ -1477,8 +1498,8 @@ impl WasmInstanceContext { name_ptr: AscPtr, params_ptr: AscPtr>>, ) -> Result<(), HostExportError> { - let name: String = asc_get(self, name_ptr)?; - let params: Vec = asc_get(self, params_ptr)?; + let name: String = asc_get(self, name_ptr, gas)?; + let params: Vec = asc_get(self, params_ptr, gas)?; self.ctx.host_exports.data_source_create( &self.ctx.logger, &mut self.ctx.state, @@ -1498,9 +1519,9 @@ impl WasmInstanceContext { params_ptr: AscPtr>>, context_ptr: AscPtr, ) -> Result<(), HostExportError> { - let name: String = asc_get(self, name_ptr)?; - let params: Vec = asc_get(self, params_ptr)?; - let context: HashMap<_, _> = try_asc_get(self, context_ptr)?; + let name: String = asc_get(self, name_ptr, gas)?; + let params: Vec = asc_get(self, params_ptr, gas)?; + let context: HashMap<_, _> = try_asc_get(self, context_ptr, gas)?; self.ctx.host_exports.data_source_create( &self.ctx.logger, &mut self.ctx.state, @@ -1520,6 +1541,7 @@ impl WasmInstanceContext { asc_new( self, self.ctx.host_exports.data_source_address(gas)?.as_slice(), + gas, ) } @@ -1528,7 +1550,7 @@ impl WasmInstanceContext { &mut self, gas: &GasCounter, ) -> Result, DeterministicHostError> { - asc_new(self, &self.ctx.host_exports.data_source_network(gas)?) + asc_new(self, &self.ctx.host_exports.data_source_network(gas)?, gas) } /// function dataSource.context(): DataSourceContext @@ -1539,6 +1561,7 @@ impl WasmInstanceContext { asc_new( self, &self.ctx.host_exports.data_source_context(gas)?.sorted(), + gas, ) } @@ -1557,11 +1580,11 @@ impl WasmInstanceContext { ))); } - let hash: String = asc_get(self, hash_ptr)?; + let hash: String = asc_get(self, hash_ptr, gas)?; let name = self.ctx.host_exports.ens_name_by_hash(&*hash)?; // map `None` to `null`, and `Some(s)` to a runtime string - name.map(|name| asc_new(self, &*name).map_err(Into::into)) + name.map(|name| asc_new(self, &*name, gas).map_err(Into::into)) .unwrap_or(Ok(AscPtr::null())) } @@ -1572,7 +1595,7 @@ impl WasmInstanceContext { msg: AscPtr, ) -> Result<(), DeterministicHostError> { let level = LogLevel::from(level).into(); - let msg: String = asc_get(self, msg)?; + let msg: String = asc_get(self, msg, gas)?; self.ctx .host_exports .log_log(&self.ctx.logger, level, msg, gas) @@ -1587,10 +1610,10 @@ impl WasmInstanceContext { let data = self .ctx .host_exports - .ethereum_encode(asc_get(self, token_ptr)?, gas); + .ethereum_encode(asc_get(self, token_ptr, gas)?, gas); // return `null` if it fails - data.map(|bytes| asc_new(self, &*bytes)) + data.map(|bytes| asc_new(self, &*bytes, gas)) .unwrap_or(Ok(AscPtr::null())) } @@ -1602,14 +1625,14 @@ impl WasmInstanceContext { data_ptr: AscPtr, ) -> Result>, DeterministicHostError> { let result = self.ctx.host_exports.ethereum_decode( - asc_get(self, types_ptr)?, - asc_get(self, data_ptr)?, + asc_get(self, types_ptr, gas)?, + asc_get(self, data_ptr, gas)?, gas, ); // return `null` if it fails result - .map(|param| asc_new(self, ¶m)) + .map(|param| asc_new(self, ¶m, gas)) .unwrap_or(Ok(AscPtr::null())) } diff --git a/runtime/wasm/src/to_from/external.rs b/runtime/wasm/src/to_from/external.rs index ad36a25b645..54ae6442f09 100644 --- a/runtime/wasm/src/to_from/external.rs +++ b/runtime/wasm/src/to_from/external.rs @@ -1,5 +1,6 @@ use ethabi; +use graph::runtime::gas::GasCounter; use graph::runtime::{ asc_get, asc_new, try_asc_get, AscIndexId, AscPtr, AscType, AscValue, ToAscObj, }; @@ -17,8 +18,9 @@ impl ToAscObj for web3::H160 { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - self.0.to_asc_obj(heap) + self.0.to_asc_obj(heap, gas) } } @@ -26,8 +28,9 @@ impl FromAscObj for web3::H160 { fn from_asc_obj( typed_array: Uint8Array, heap: &H, + gas: &GasCounter, ) -> Result { - let data = <[u8; 20]>::from_asc_obj(typed_array, heap)?; + let data = <[u8; 20]>::from_asc_obj(typed_array, heap, gas)?; Ok(Self(data)) } } @@ -36,8 +39,9 @@ impl FromAscObj for web3::H256 { fn from_asc_obj( typed_array: Uint8Array, heap: &H, + gas: &GasCounter, ) -> Result { - let data = <[u8; 32]>::from_asc_obj(typed_array, heap)?; + let data = <[u8; 32]>::from_asc_obj(typed_array, heap, gas)?; Ok(Self(data)) } } @@ -46,8 +50,9 @@ impl ToAscObj for web3::H256 { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - self.0.to_asc_obj(heap) + self.0.to_asc_obj(heap, gas) } } @@ -55,10 +60,11 @@ impl ToAscObj for web3::U128 { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { let mut bytes: [u8; 16] = [0; 16]; self.to_little_endian(&mut bytes); - bytes.to_asc_obj(heap) + bytes.to_asc_obj(heap, gas) } } @@ -66,9 +72,10 @@ impl ToAscObj for BigInt { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { let bytes = self.to_signed_bytes_le(); - bytes.to_asc_obj(heap) + bytes.to_asc_obj(heap, gas) } } @@ -76,8 +83,9 @@ impl FromAscObj for BigInt { fn from_asc_obj( array_buffer: AscBigInt, heap: &H, + gas: &GasCounter, ) -> Result { - let bytes = >::from_asc_obj(array_buffer, heap)?; + let bytes = >::from_asc_obj(array_buffer, heap, gas)?; Ok(BigInt::from_signed_bytes_le(&bytes)) } } @@ -86,13 +94,14 @@ impl ToAscObj for BigDecimal { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { // From the docs: "Note that a positive exponent indicates a negative power of 10", // so "exponent" is the opposite of what you'd expect. let (digits, negative_exp) = self.as_bigint_and_exponent(); Ok(AscBigDecimal { - exp: asc_new(heap, &BigInt::from(-negative_exp))?, - digits: asc_new(heap, &BigInt::from(digits))?, + exp: asc_new(heap, &BigInt::from(-negative_exp), gas)?, + digits: asc_new(heap, &BigInt::from(digits), gas)?, }) } } @@ -101,9 +110,10 @@ impl TryFromAscObj for BigDecimal { fn try_from_asc_obj( big_decimal: AscBigDecimal, heap: &H, + gas: &GasCounter, ) -> Result { - let digits: BigInt = asc_get(heap, big_decimal.digits)?; - let exp: BigInt = asc_get(heap, big_decimal.exp)?; + let digits: BigInt = asc_get(heap, big_decimal.digits, gas)?; + let exp: BigInt = asc_get(heap, big_decimal.exp, gas)?; let bytes = exp.to_signed_bytes_le(); let mut byte_array = if exp >= 0.into() { [0; 8] } else { [255; 8] }; @@ -131,10 +141,11 @@ impl ToAscObj>> for Vec { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result>, DeterministicHostError> { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Ok(Array::new(&*content, heap)?) + Ok(Array::new(&*content, heap, gas)?) } } @@ -142,27 +153,28 @@ impl ToAscObj> for ethabi::Token { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { use ethabi::Token::*; let kind = EthereumValueKind::get_kind(self); let payload = match self { - Address(address) => asc_new::(heap, address)?.to_payload(), + Address(address) => asc_new::(heap, address, gas)?.to_payload(), FixedBytes(bytes) | Bytes(bytes) => { - asc_new::(heap, &**bytes)?.to_payload() + asc_new::(heap, &**bytes, gas)?.to_payload() } Int(uint) => { let n = BigInt::from_signed_u256(&uint); - asc_new(heap, &n)?.to_payload() + asc_new(heap, &n, gas)?.to_payload() } Uint(uint) => { let n = BigInt::from_unsigned_u256(&uint); - asc_new(heap, &n)?.to_payload() + asc_new(heap, &n, gas)?.to_payload() } Bool(b) => *b as u64, - String(string) => asc_new(heap, &**string)?.to_payload(), - FixedArray(tokens) | Array(tokens) => asc_new(heap, &**tokens)?.to_payload(), - Tuple(tokens) => asc_new(heap, &**tokens)?.to_payload(), + String(string) => asc_new(heap, &**string, gas)?.to_payload(), + FixedArray(tokens) | Array(tokens) => asc_new(heap, &**tokens, gas)?.to_payload(), + Tuple(tokens) => asc_new(heap, &**tokens, gas)?.to_payload(), }; Ok(AscEnum { @@ -177,6 +189,7 @@ impl FromAscObj> for ethabi::Token { fn from_asc_obj( asc_enum: AscEnum, heap: &H, + gas: &GasCounter, ) -> Result { use ethabi::Token; @@ -185,41 +198,41 @@ impl FromAscObj> for ethabi::Token { EthereumValueKind::Bool => Token::Bool(bool::from(payload)), EthereumValueKind::Address => { let ptr: AscPtr = AscPtr::from(payload); - Token::Address(asc_get(heap, ptr)?) + Token::Address(asc_get(heap, ptr, gas)?) } EthereumValueKind::FixedBytes => { let ptr: AscPtr = AscPtr::from(payload); - Token::FixedBytes(asc_get(heap, ptr)?) + Token::FixedBytes(asc_get(heap, ptr, gas)?) } EthereumValueKind::Bytes => { let ptr: AscPtr = AscPtr::from(payload); - Token::Bytes(asc_get(heap, ptr)?) + Token::Bytes(asc_get(heap, ptr, gas)?) } EthereumValueKind::Int => { let ptr: AscPtr = AscPtr::from(payload); - let n: BigInt = asc_get(heap, ptr)?; + let n: BigInt = asc_get(heap, ptr, gas)?; Token::Int(n.to_signed_u256()) } EthereumValueKind::Uint => { let ptr: AscPtr = AscPtr::from(payload); - let n: BigInt = asc_get(heap, ptr)?; + let n: BigInt = asc_get(heap, ptr, gas)?; Token::Uint(n.to_unsigned_u256()) } EthereumValueKind::String => { let ptr: AscPtr = AscPtr::from(payload); - Token::String(asc_get(heap, ptr)?) + Token::String(asc_get(heap, ptr, gas)?) } EthereumValueKind::FixedArray => { let ptr: AscEnumArray = AscPtr::from(payload); - Token::FixedArray(asc_get(heap, ptr)?) + Token::FixedArray(asc_get(heap, ptr, gas)?) } EthereumValueKind::Array => { let ptr: AscEnumArray = AscPtr::from(payload); - Token::Array(asc_get(heap, ptr)?) + Token::Array(asc_get(heap, ptr, gas)?) } EthereumValueKind::Tuple => { let ptr: AscEnumArray = AscPtr::from(payload); - Token::Tuple(asc_get(heap, ptr)?) + Token::Tuple(asc_get(heap, ptr, gas)?) } }) } @@ -229,6 +242,7 @@ impl TryFromAscObj> for store::Value { fn try_from_asc_obj( asc_enum: AscEnum, heap: &H, + gas: &GasCounter, ) -> Result { use self::store::Value; @@ -236,27 +250,27 @@ impl TryFromAscObj> for store::Value { Ok(match asc_enum.kind { StoreValueKind::String => { let ptr: AscPtr = AscPtr::from(payload); - Value::String(asc_get(heap, ptr)?) + Value::String(asc_get(heap, ptr, gas)?) } StoreValueKind::Int => Value::Int(i32::from(payload)), StoreValueKind::BigDecimal => { let ptr: AscPtr = AscPtr::from(payload); - Value::BigDecimal(try_asc_get(heap, ptr)?) + Value::BigDecimal(try_asc_get(heap, ptr, gas)?) } StoreValueKind::Bool => Value::Bool(bool::from(payload)), StoreValueKind::Array => { let ptr: AscEnumArray = AscPtr::from(payload); - Value::List(try_asc_get(heap, ptr)?) + Value::List(try_asc_get(heap, ptr, gas)?) } StoreValueKind::Null => Value::Null, StoreValueKind::Bytes => { let ptr: AscPtr = AscPtr::from(payload); - let array: Vec = asc_get(heap, ptr)?; + let array: Vec = asc_get(heap, ptr, gas)?; Value::Bytes(array.as_slice().into()) } StoreValueKind::BigInt => { let ptr: AscPtr = AscPtr::from(payload); - let array: Vec = asc_get(heap, ptr)?; + let array: Vec = asc_get(heap, ptr, gas)?; Value::BigInt(store::scalar::BigInt::from_signed_bytes_le(&array)) } }) @@ -267,22 +281,24 @@ impl ToAscObj> for store::Value { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { use self::store::Value; let payload = match self { - Value::String(string) => asc_new(heap, string.as_str())?.into(), + Value::String(string) => asc_new(heap, string.as_str(), gas)?.into(), Value::Int(n) => EnumPayload::from(*n), - Value::BigDecimal(n) => asc_new(heap, n)?.into(), + Value::BigDecimal(n) => asc_new(heap, n, gas)?.into(), Value::Bool(b) => EnumPayload::from(*b), - Value::List(array) => asc_new(heap, array.as_slice())?.into(), + Value::List(array) => asc_new(heap, array.as_slice(), gas)?.into(), Value::Null => EnumPayload(0), Value::Bytes(bytes) => { - let bytes_obj: AscPtr = asc_new(heap, bytes.as_slice())?; + let bytes_obj: AscPtr = asc_new(heap, bytes.as_slice(), gas)?; bytes_obj.into() } Value::BigInt(big_int) => { - let bytes_obj: AscPtr = asc_new(heap, &*big_int.to_signed_bytes_le())?; + let bytes_obj: AscPtr = + asc_new(heap, &*big_int.to_signed_bytes_le(), gas)?; bytes_obj.into() } }; @@ -299,9 +315,10 @@ impl ToAscObj for serde_json::Map { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscTypedMap { - entries: asc_new(heap, &*self.iter().collect::>())?, + entries: asc_new(heap, &*self.iter().collect::>(), gas)?, }) } } @@ -311,9 +328,10 @@ impl ToAscObj for Vec<(String, store::Value)> { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { Ok(AscTypedMap { - entries: asc_new(heap, self.as_slice())?, + entries: asc_new(heap, self.as_slice(), gas)?, }) } } @@ -322,16 +340,17 @@ impl ToAscObj> for serde_json::Value { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { use serde_json::Value; let payload = match self { Value::Null => EnumPayload(0), Value::Bool(b) => EnumPayload::from(*b), - Value::Number(number) => asc_new(heap, &*number.to_string())?.into(), - Value::String(string) => asc_new(heap, string.as_str())?.into(), - Value::Array(array) => asc_new(heap, array.as_slice())?.into(), - Value::Object(object) => asc_new(heap, object)?.into(), + Value::Number(number) => asc_new(heap, &*number.to_string(), gas)?.into(), + Value::String(string) => asc_new(heap, string.as_str(), gas)?.into(), + Value::Array(array) => asc_new(heap, array.as_slice(), gas)?.into(), + Value::Object(object) => asc_new(heap, object, gas)?.into(), }; Ok(AscEnum { @@ -359,6 +378,7 @@ impl ToAscObj> for AscWrapped { fn to_asc_obj( &self, _heap: &mut H, + _gas: &GasCounter, ) -> Result, DeterministicHostError> { Ok(*self) } @@ -373,13 +393,14 @@ where fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result, bool>, DeterministicHostError> { Ok(match self { Ok(value) => AscResult { value: { - let inner = asc_new(heap, value)?; + let inner = asc_new(heap, value, gas)?; let wrapped = AscWrapped { inner }; - asc_new(heap, &wrapped)? + asc_new(heap, &wrapped, gas)? }, error: AscPtr::null(), }, @@ -387,7 +408,7 @@ where value: AscPtr::null(), error: { let wrapped = AscWrapped { inner: true }; - asc_new(heap, &wrapped)? + asc_new(heap, &wrapped, gas)? }, }, }) diff --git a/runtime/wasm/src/to_from/mod.rs b/runtime/wasm/src/to_from/mod.rs index d1cc2b33f68..a8262c9c50f 100644 --- a/runtime/wasm/src/to_from/mod.rs +++ b/runtime/wasm/src/to_from/mod.rs @@ -4,6 +4,7 @@ use std::iter::FromIterator; use graph::runtime::asc_get; use graph::runtime::asc_new; +use graph::runtime::gas::GasCounter; use graph::runtime::try_asc_get; use graph::runtime::{ AscHeap, AscIndexId, AscPtr, AscType, AscValue, DeterministicHostError, FromAscObj, ToAscObj, @@ -20,8 +21,9 @@ impl ToAscObj> for [T] { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { - TypedArray::new(self, heap) + TypedArray::new(self, heap, gas) } } @@ -29,8 +31,9 @@ impl FromAscObj> for Vec { fn from_asc_obj( typed_array: TypedArray, heap: &H, + gas: &GasCounter, ) -> Result { - typed_array.to_vec(heap) + typed_array.to_vec(heap, gas) } } @@ -38,9 +41,10 @@ impl FromAscObj> for [T; 32] { fn from_asc_obj( typed_array: TypedArray, heap: &H, + gas: &GasCounter, ) -> Result { let mut array: [T; 32] = [T::default(); 32]; - let v = typed_array.to_vec(heap)?; + let v = typed_array.to_vec(heap, gas)?; array.copy_from_slice(&v); Ok(array) } @@ -50,9 +54,10 @@ impl FromAscObj> for [T; 20] { fn from_asc_obj( typed_array: TypedArray, heap: &H, + gas: &GasCounter, ) -> Result { let mut array: [T; 20] = [T::default(); 20]; - let v = typed_array.to_vec(heap)?; + let v = typed_array.to_vec(heap, gas)?; array.copy_from_slice(&v); Ok(array) } @@ -62,9 +67,10 @@ impl FromAscObj> for [T; 16] { fn from_asc_obj( typed_array: TypedArray, heap: &H, + gas: &GasCounter, ) -> Result { let mut array: [T; 16] = [T::default(); 16]; - let v = typed_array.to_vec(heap)?; + let v = typed_array.to_vec(heap, gas)?; array.copy_from_slice(&v); Ok(array) } @@ -74,9 +80,10 @@ impl FromAscObj> for [T; 4] { fn from_asc_obj( typed_array: TypedArray, heap: &H, + gas: &GasCounter, ) -> Result { let mut array: [T; 4] = [T::default(); 4]; - let v = typed_array.to_vec(heap)?; + let v = typed_array.to_vec(heap, gas)?; array.copy_from_slice(&v); Ok(array) } @@ -86,6 +93,7 @@ impl ToAscObj for str { fn to_asc_obj( &self, heap: &mut H, + _gas: &GasCounter, ) -> Result { AscString::new(&self.encode_utf16().collect::>(), heap.api_version()) } @@ -95,8 +103,9 @@ impl ToAscObj for String { fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result { - self.as_str().to_asc_obj(heap) + self.as_str().to_asc_obj(heap, gas) } } @@ -104,6 +113,7 @@ impl FromAscObj for String { fn from_asc_obj( asc_string: AscString, _: &H, + _gas: &GasCounter, ) -> Result { let mut string = String::from_utf16(asc_string.content()) .map_err(|e| DeterministicHostError::from(anyhow::Error::from(e)))?; @@ -120,8 +130,9 @@ impl TryFromAscObj for String { fn try_from_asc_obj( asc_string: AscString, heap: &H, + gas: &GasCounter, ) -> Result { - Ok(Self::from_asc_obj(asc_string, heap)?) + Ok(Self::from_asc_obj(asc_string, heap, gas)?) } } @@ -129,10 +140,11 @@ impl> ToAscObj>> for [T] fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result>, DeterministicHostError> { - let content: Result, _> = self.iter().map(|x| asc_new(heap, x)).collect(); + let content: Result, _> = self.iter().map(|x| asc_new(heap, x, gas)).collect(); let content = content?; - Array::new(&*content, heap) + Array::new(&*content, heap, gas) } } @@ -140,11 +152,12 @@ impl> FromAscObj>> for fn from_asc_obj( array: Array>, heap: &H, + gas: &GasCounter, ) -> Result { array - .to_vec(heap)? + .to_vec(heap, gas)? .into_iter() - .map(|x| asc_get(heap, x)) + .map(|x| asc_get(heap, x, gas)) .collect() } } @@ -153,11 +166,12 @@ impl> TryFromAscObj fn try_from_asc_obj( array: Array>, heap: &H, + gas: &GasCounter, ) -> Result { array - .to_vec(heap)? + .to_vec(heap, gas)? .into_iter() - .map(|x| try_asc_get(heap, x)) + .map(|x| try_asc_get(heap, x, gas)) .collect() } } @@ -172,10 +186,11 @@ impl< fn try_from_asc_obj( asc_entry: AscTypedMapEntry, heap: &H, + gas: &GasCounter, ) -> Result { Ok(( - try_asc_get(heap, asc_entry.key)?, - try_asc_get(heap, asc_entry.value)?, + try_asc_get(heap, asc_entry.key, gas)?, + try_asc_get(heap, asc_entry.value, gas)?, )) } } @@ -186,10 +201,11 @@ impl, U: ToAscO fn to_asc_obj( &self, heap: &mut H, + gas: &GasCounter, ) -> Result, DeterministicHostError> { Ok(AscTypedMapEntry { - key: asc_new(heap, &self.0)?, - value: asc_new(heap, &self.1)?, + key: asc_new(heap, &self.0, gas)?, + value: asc_new(heap, &self.1, gas)?, }) } } @@ -207,8 +223,9 @@ where fn try_from_asc_obj( asc_map: AscTypedMap, heap: &H, + gas: &GasCounter, ) -> Result { - let entries: Vec<(T, U)> = try_asc_get(heap, asc_map.entries)?; + let entries: Vec<(T, U)> = try_asc_get(heap, asc_map.entries, gas)?; Ok(HashMap::from_iter(entries.into_iter())) } } From 4d02ae080f43a33c077025b1a8bdb1ff5b80155e Mon Sep 17 00:00:00 2001 From: Dotan Simha Date: Wed, 23 Feb 2022 17:03:26 +0200 Subject: [PATCH 0016/2357] graphql: use new GraphiQL version (#3252) --- server/http/assets/graphiql.css | 1741 - server/http/assets/graphiql.min.js | 1 - server/http/assets/index.html | 61459 ++++++++++++++++++++++++++- server/http/src/service.rs | 24 - 4 files changed, 61306 insertions(+), 1919 deletions(-) delete mode 100644 server/http/assets/graphiql.css delete mode 100644 server/http/assets/graphiql.min.js diff --git a/server/http/assets/graphiql.css b/server/http/assets/graphiql.css deleted file mode 100644 index 222e806fb3e..00000000000 --- a/server/http/assets/graphiql.css +++ /dev/null @@ -1,1741 +0,0 @@ -.graphiql-container, -.graphiql-container button, -.graphiql-container input { - color: #141823; - font-family: - system, - -apple-system, - 'San Francisco', - '.SFNSDisplay-Regular', - 'Segoe UI', - Segoe, - 'Segoe WP', - 'Helvetica Neue', - helvetica, - 'Lucida Grande', - arial, - sans-serif; - font-size: 14px; -} - -.graphiql-container { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - height: 100%; - margin: 0; - overflow: hidden; - width: 100%; -} - -.graphiql-container .editorWrap { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; - overflow-x: hidden; -} - -.graphiql-container .title { - font-size: 18px; -} - -.graphiql-container .title em { - font-family: georgia; - font-size: 19px; -} - -.graphiql-container .topBarWrap { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; -} - -.graphiql-container .topBar { - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - background: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e2e2e2)); - background: linear-gradient(#f7f7f7, #e2e2e2); - border-bottom: 1px solid #d0d0d0; - cursor: default; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; - height: 34px; - overflow-y: visible; - padding: 7px 14px 6px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.graphiql-container .toolbar { - overflow-x: visible; - display: -webkit-box; - display: -ms-flexbox; - display: flex; -} - -.graphiql-container .docExplorerShow, -.graphiql-container .historyShow { - background: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e2e2e2)); - background: linear-gradient(#f7f7f7, #e2e2e2); - border-radius: 0; - border-bottom: 1px solid #d0d0d0; - border-right: none; - border-top: none; - color: #3B5998; - cursor: pointer; - font-size: 14px; - margin: 0; - outline: 0; - padding: 2px 20px 0 18px; -} - -.graphiql-container .docExplorerShow { - border-left: 1px solid rgba(0, 0, 0, 0.2); -} - -.graphiql-container .historyShow { - border-right: 1px solid rgba(0, 0, 0, 0.2); - border-left: 0; -} - -.graphiql-container .docExplorerShow:before { - border-left: 2px solid #3B5998; - border-top: 2px solid #3B5998; - content: ''; - display: inline-block; - height: 9px; - margin: 0 3px -1px 0; - position: relative; - -webkit-transform: rotate(-45deg); - transform: rotate(-45deg); - width: 9px; -} - -.graphiql-container .editorBar { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; -} - -.graphiql-container .queryWrap { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; -} - -.graphiql-container .resultWrap { - border-left: solid 1px #e0e0e0; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; - position: relative; -} - -.graphiql-container .docExplorerWrap, -.graphiql-container .historyPaneWrap { - background: white; - -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15); - box-shadow: 0 0 8px rgba(0, 0, 0, 0.15); - position: relative; - z-index: 3; -} - -.graphiql-container .historyPaneWrap { - min-width: 230px; - z-index: 5; -} - -.graphiql-container .docExplorerResizer { - cursor: col-resize; - height: 100%; - left: -5px; - position: absolute; - top: 0; - width: 10px; - z-index: 10; -} - -.graphiql-container .docExplorerHide { - cursor: pointer; - font-size: 18px; - margin: -7px -8px -6px 0; - padding: 18px 16px 15px 12px; -} - -.graphiql-container div .query-editor { - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; - position: relative; -} - -.graphiql-container .variable-editor { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - height: 29px; - position: relative; -} - -.graphiql-container .variable-editor-title { - background: #eeeeee; - border-bottom: 1px solid #d6d6d6; - border-top: 1px solid #e0e0e0; - color: #777; - font-variant: small-caps; - font-weight: bold; - letter-spacing: 1px; - line-height: 14px; - padding: 6px 0 8px 43px; - text-transform: lowercase; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.graphiql-container .codemirrorWrap { - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; - height: 100%; - position: relative; -} - -.graphiql-container .result-window { - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; - height: 100%; - position: relative; -} - -.graphiql-container .footer { - background: #f6f7f8; - border-left: 1px solid #e0e0e0; - border-top: 1px solid #e0e0e0; - margin-left: 12px; - position: relative; -} - -.graphiql-container .footer:before { - background: #eeeeee; - bottom: 0; - content: " "; - left: -13px; - position: absolute; - top: -1px; - width: 12px; -} - -/* No `.graphiql-container` here so themes can overwrite */ -.result-window .CodeMirror { - background: #f6f7f8; -} - -.graphiql-container .result-window .CodeMirror-gutters { - background-color: #eeeeee; - border-color: #e0e0e0; - cursor: col-resize; -} - -.graphiql-container .result-window .CodeMirror-foldgutter, -.graphiql-container .result-window .CodeMirror-foldgutter-open:after, -.graphiql-container .result-window .CodeMirror-foldgutter-folded:after { - padding-left: 3px; -} - -.graphiql-container .toolbar-button { - background: #fdfdfd; - background: -webkit-gradient(linear, left top, left bottom, from(#f9f9f9), to(#ececec)); - background: linear-gradient(#f9f9f9, #ececec); - border-radius: 3px; - -webkit-box-shadow: - inset 0 0 0 1px rgba(0,0,0,0.20), - 0 1px 0 rgba(255,255,255, 0.7), - inset 0 1px #fff; - box-shadow: - inset 0 0 0 1px rgba(0,0,0,0.20), - 0 1px 0 rgba(255,255,255, 0.7), - inset 0 1px #fff; - color: #555; - cursor: pointer; - display: inline-block; - margin: 0 5px; - padding: 3px 11px 5px; - text-decoration: none; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 150px; -} - -.graphiql-container .toolbar-button:active { - background: -webkit-gradient(linear, left top, left bottom, from(#ececec), to(#d5d5d5)); - background: linear-gradient(#ececec, #d5d5d5); - -webkit-box-shadow: - 0 1px 0 rgba(255, 255, 255, 0.7), - inset 0 0 0 1px rgba(0,0,0,0.10), - inset 0 1px 1px 1px rgba(0, 0, 0, 0.12), - inset 0 0 5px rgba(0, 0, 0, 0.1); - box-shadow: - 0 1px 0 rgba(255, 255, 255, 0.7), - inset 0 0 0 1px rgba(0,0,0,0.10), - inset 0 1px 1px 1px rgba(0, 0, 0, 0.12), - inset 0 0 5px rgba(0, 0, 0, 0.1); -} - -.graphiql-container .toolbar-button.error { - background: -webkit-gradient(linear, left top, left bottom, from(#fdf3f3), to(#e6d6d7)); - background: linear-gradient(#fdf3f3, #e6d6d7); - color: #b00; -} - -.graphiql-container .toolbar-button-group { - margin: 0 5px; - white-space: nowrap; -} - -.graphiql-container .toolbar-button-group > * { - margin: 0; -} - -.graphiql-container .toolbar-button-group > *:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.graphiql-container .toolbar-button-group > *:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - margin-left: -1px; -} - -.graphiql-container .execute-button-wrap { - height: 34px; - margin: 0 14px 0 28px; - position: relative; -} - -.graphiql-container .execute-button { - background: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd), to(#d2d3d6)); - background: linear-gradient(#fdfdfd, #d2d3d6); - border-radius: 17px; - border: 1px solid rgba(0,0,0,0.25); - -webkit-box-shadow: 0 1px 0 #fff; - box-shadow: 0 1px 0 #fff; - cursor: pointer; - fill: #444; - height: 34px; - margin: 0; - padding: 0; - width: 34px; -} - -.graphiql-container .execute-button svg { - pointer-events: none; -} - -.graphiql-container .execute-button:active { - background: -webkit-gradient(linear, left top, left bottom, from(#e6e6e6), to(#c3c3c3)); - background: linear-gradient(#e6e6e6, #c3c3c3); - -webkit-box-shadow: - 0 1px 0 #fff, - inset 0 0 2px rgba(0, 0, 0, 0.2), - inset 0 0 6px rgba(0, 0, 0, 0.1); - box-shadow: - 0 1px 0 #fff, - inset 0 0 2px rgba(0, 0, 0, 0.2), - inset 0 0 6px rgba(0, 0, 0, 0.1); -} - -.graphiql-container .execute-button:focus { - outline: 0; -} - -.graphiql-container .toolbar-menu, -.graphiql-container .toolbar-select { - position: relative; -} - -.graphiql-container .execute-options, -.graphiql-container .toolbar-menu-items, -.graphiql-container .toolbar-select-options { - background: #fff; - -webkit-box-shadow: - 0 0 0 1px rgba(0,0,0,0.1), - 0 2px 4px rgba(0,0,0,0.25); - box-shadow: - 0 0 0 1px rgba(0,0,0,0.1), - 0 2px 4px rgba(0,0,0,0.25); - margin: 0; - padding: 6px 0; - position: absolute; - z-index: 100; -} - -.graphiql-container .execute-options { - min-width: 100px; - top: 37px; - left: -1px; -} - -.graphiql-container .toolbar-menu-items { - left: 1px; - margin-top: -1px; - min-width: 110%; - top: 100%; - visibility: hidden; -} - -.graphiql-container .toolbar-menu-items.open { - visibility: visible; -} - -.graphiql-container .toolbar-select-options { - left: 0; - min-width: 100%; - top: -5px; - visibility: hidden; -} - -.graphiql-container .toolbar-select-options.open { - visibility: visible; -} - -.graphiql-container .execute-options > li, -.graphiql-container .toolbar-menu-items > li, -.graphiql-container .toolbar-select-options > li { - cursor: pointer; - display: block; - margin: none; - max-width: 300px; - overflow: hidden; - padding: 2px 20px 4px 11px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.graphiql-container .execute-options > li.selected, -.graphiql-container .toolbar-menu-items > li.hover, -.graphiql-container .toolbar-menu-items > li:active, -.graphiql-container .toolbar-menu-items > li:hover, -.graphiql-container .toolbar-select-options > li.hover, -.graphiql-container .toolbar-select-options > li:active, -.graphiql-container .toolbar-select-options > li:hover, -.graphiql-container .history-contents > p:hover, -.graphiql-container .history-contents > p:active { - background: #e10098; - color: #fff; -} - -.graphiql-container .toolbar-select-options > li > svg { - display: inline; - fill: #666; - margin: 0 -6px 0 6px; - pointer-events: none; - vertical-align: middle; -} - -.graphiql-container .toolbar-select-options > li.hover > svg, -.graphiql-container .toolbar-select-options > li:active > svg, -.graphiql-container .toolbar-select-options > li:hover > svg { - fill: #fff; -} - -.graphiql-container .CodeMirror-scroll { - overflow-scrolling: touch; -} - -.graphiql-container .CodeMirror { - color: #141823; - font-family: - 'Consolas', - 'Inconsolata', - 'Droid Sans Mono', - 'Monaco', - monospace; - font-size: 13px; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; -} - -.graphiql-container .CodeMirror-lines { - padding: 20px 0; -} - -.CodeMirror-hint-information .content { - box-orient: vertical; - color: #141823; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif; - font-size: 13px; - line-clamp: 3; - line-height: 16px; - max-height: 48px; - overflow: hidden; - text-overflow: -o-ellipsis-lastline; -} - -.CodeMirror-hint-information .content p:first-child { - margin-top: 0; -} - -.CodeMirror-hint-information .content p:last-child { - margin-bottom: 0; -} - -.CodeMirror-hint-information .infoType { - color: #CA9800; - cursor: pointer; - display: inline; - margin-right: 0.5em; -} - -.autoInsertedLeaf.cm-property { - -webkit-animation-duration: 6s; - animation-duration: 6s; - -webkit-animation-name: insertionFade; - animation-name: insertionFade; - border-bottom: 2px solid rgba(255, 255, 255, 0); - border-radius: 2px; - margin: -2px -4px -1px; - padding: 2px 4px 1px; -} - -@-webkit-keyframes insertionFade { - from, to { - background: rgba(255, 255, 255, 0); - border-color: rgba(255, 255, 255, 0); - } - - 15%, 85% { - background: #fbffc9; - border-color: #f0f3c0; - } -} - -@keyframes insertionFade { - from, to { - background: rgba(255, 255, 255, 0); - border-color: rgba(255, 255, 255, 0); - } - - 15%, 85% { - background: #fbffc9; - border-color: #f0f3c0; - } -} - -div.CodeMirror-lint-tooltip { - background-color: white; - border-radius: 2px; - border: 0; - color: #141823; - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); - font-family: - system, - -apple-system, - 'San Francisco', - '.SFNSDisplay-Regular', - 'Segoe UI', - Segoe, - 'Segoe WP', - 'Helvetica Neue', - helvetica, - 'Lucida Grande', - arial, - sans-serif; - font-size: 13px; - line-height: 16px; - max-width: 430px; - opacity: 0; - padding: 8px 10px; - -webkit-transition: opacity 0.15s; - transition: opacity 0.15s; - white-space: pre-wrap; -} - -div.CodeMirror-lint-tooltip > * { - padding-left: 23px; -} - -div.CodeMirror-lint-tooltip > * + * { - margin-top: 12px; -} - -/* COLORS */ - -.graphiql-container .CodeMirror-foldmarker { - border-radius: 4px; - background: #08f; - background: -webkit-gradient(linear, left top, left bottom, from(#43A8FF), to(#0F83E8)); - background: linear-gradient(#43A8FF, #0F83E8); - -webkit-box-shadow: - 0 1px 1px rgba(0, 0, 0, 0.2), - inset 0 0 0 1px rgba(0, 0, 0, 0.1); - box-shadow: - 0 1px 1px rgba(0, 0, 0, 0.2), - inset 0 0 0 1px rgba(0, 0, 0, 0.1); - color: white; - font-family: arial; - font-size: 12px; - line-height: 0; - margin: 0 3px; - padding: 0px 4px 1px; - text-shadow: 0 -1px rgba(0, 0, 0, 0.1); -} - -.graphiql-container div.CodeMirror span.CodeMirror-matchingbracket { - color: #555; - text-decoration: underline; -} - -.graphiql-container div.CodeMirror span.CodeMirror-nonmatchingbracket { - color: #f00; -} - -/* Comment */ -.cm-comment { - color: #999; -} - -/* Punctuation */ -.cm-punctuation { - color: #555; -} - -/* Keyword */ -.cm-keyword { - color: #B11A04; -} - -/* OperationName, FragmentName */ -.cm-def { - color: #D2054E; -} - -/* FieldName */ -.cm-property { - color: #1F61A0; -} - -/* FieldAlias */ -.cm-qualifier { - color: #1C92A9; -} - -/* ArgumentName and ObjectFieldName */ -.cm-attribute { - color: #8B2BB9; -} - -/* Number */ -.cm-number { - color: #2882F9; -} - -/* String */ -.cm-string { - color: #D64292; -} - -/* Boolean */ -.cm-builtin { - color: #D47509; -} - -/* EnumValue */ -.cm-string-2 { - color: #0B7FC7; -} - -/* Variable */ -.cm-variable { - color: #397D13; -} - -/* Directive */ -.cm-meta { - color: #B33086; -} - -/* Type */ -.cm-atom { - color: #CA9800; -} -/* BASICS */ - -.CodeMirror { - /* Set height, width, borders, and global font properties here */ - color: black; - font-family: monospace; - height: 300px; -} - -/* PADDING */ - -.CodeMirror-lines { - padding: 4px 0; /* Vertical padding around content */ -} -.CodeMirror pre { - padding: 0 4px; /* Horizontal padding of content */ -} - -.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { - background-color: white; /* The little square between H and V scrollbars */ -} - -/* GUTTER */ - -.CodeMirror-gutters { - border-right: 1px solid #ddd; - background-color: #f7f7f7; - white-space: nowrap; -} -.CodeMirror-linenumbers {} -.CodeMirror-linenumber { - color: #999; - min-width: 20px; - padding: 0 3px 0 5px; - text-align: right; - white-space: nowrap; -} - -.CodeMirror-guttermarker { color: black; } -.CodeMirror-guttermarker-subtle { color: #999; } - -/* CURSOR */ - -.CodeMirror .CodeMirror-cursor { - border-left: 1px solid black; -} -/* Shown when moving in bi-directional text */ -.CodeMirror div.CodeMirror-secondarycursor { - border-left: 1px solid silver; -} -.CodeMirror.cm-fat-cursor div.CodeMirror-cursor { - background: #7e7; - border: 0; - width: auto; -} -.CodeMirror.cm-fat-cursor div.CodeMirror-cursors { - z-index: 1; -} - -.cm-animate-fat-cursor { - -webkit-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; - border: 0; - width: auto; -} -@-webkit-keyframes blink { - 0% { background: #7e7; } - 50% { background: none; } - 100% { background: #7e7; } -} -@keyframes blink { - 0% { background: #7e7; } - 50% { background: none; } - 100% { background: #7e7; } -} - -/* Can style cursor different in overwrite (non-insert) mode */ -div.CodeMirror-overwrite div.CodeMirror-cursor {} - -.cm-tab { display: inline-block; text-decoration: inherit; } - -.CodeMirror-ruler { - border-left: 1px solid #ccc; - position: absolute; -} - -/* DEFAULT THEME */ - -.cm-s-default .cm-keyword {color: #708;} -.cm-s-default .cm-atom {color: #219;} -.cm-s-default .cm-number {color: #164;} -.cm-s-default .cm-def {color: #00f;} -.cm-s-default .cm-variable, -.cm-s-default .cm-punctuation, -.cm-s-default .cm-property, -.cm-s-default .cm-operator {} -.cm-s-default .cm-variable-2 {color: #05a;} -.cm-s-default .cm-variable-3 {color: #085;} -.cm-s-default .cm-comment {color: #a50;} -.cm-s-default .cm-string {color: #a11;} -.cm-s-default .cm-string-2 {color: #f50;} -.cm-s-default .cm-meta {color: #555;} -.cm-s-default .cm-qualifier {color: #555;} -.cm-s-default .cm-builtin {color: #30a;} -.cm-s-default .cm-bracket {color: #997;} -.cm-s-default .cm-tag {color: #170;} -.cm-s-default .cm-attribute {color: #00c;} -.cm-s-default .cm-header {color: blue;} -.cm-s-default .cm-quote {color: #090;} -.cm-s-default .cm-hr {color: #999;} -.cm-s-default .cm-link {color: #00c;} - -.cm-negative {color: #d44;} -.cm-positive {color: #292;} -.cm-header, .cm-strong {font-weight: bold;} -.cm-em {font-style: italic;} -.cm-link {text-decoration: underline;} -.cm-strikethrough {text-decoration: line-through;} - -.cm-s-default .cm-error {color: #f00;} -.cm-invalidchar {color: #f00;} - -.CodeMirror-composing { border-bottom: 2px solid; } - -/* Default styles for common addons */ - -div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} -div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} -.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } -.CodeMirror-activeline-background {background: #e8f2ff;} - -/* STOP */ - -/* The rest of this file contains styles related to the mechanics of - the editor. You probably shouldn't touch them. */ - -.CodeMirror { - background: white; - overflow: hidden; - position: relative; -} - -.CodeMirror-scroll { - height: 100%; - /* 30px is the magic margin used to hide the element's real scrollbars */ - /* See overflow: hidden in .CodeMirror */ - margin-bottom: -30px; margin-right: -30px; - outline: none; /* Prevent dragging from highlighting the element */ - overflow: scroll !important; /* Things will break if this is overridden */ - padding-bottom: 30px; - position: relative; -} -.CodeMirror-sizer { - border-right: 30px solid transparent; - position: relative; -} - -/* The fake, visible scrollbars. Used to force redraw during scrolling - before actual scrolling happens, thus preventing shaking and - flickering artifacts. */ -.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { - display: none; - position: absolute; - z-index: 6; -} -.CodeMirror-vscrollbar { - overflow-x: hidden; - overflow-y: scroll; - right: 0; top: 0; -} -.CodeMirror-hscrollbar { - bottom: 0; left: 0; - overflow-x: scroll; - overflow-y: hidden; -} -.CodeMirror-scrollbar-filler { - right: 0; bottom: 0; -} -.CodeMirror-gutter-filler { - left: 0; bottom: 0; -} - -.CodeMirror-gutters { - min-height: 100%; - position: absolute; left: 0; top: 0; - z-index: 3; -} -.CodeMirror-gutter { - display: inline-block; - height: 100%; - margin-bottom: -30px; - vertical-align: top; - white-space: normal; - /* Hack to make IE7 behave */ - *zoom:1; - *display:inline; -} -.CodeMirror-gutter-wrapper { - background: none !important; - border: none !important; - position: absolute; - z-index: 4; -} -.CodeMirror-gutter-background { - position: absolute; - top: 0; bottom: 0; - z-index: 4; -} -.CodeMirror-gutter-elt { - cursor: default; - position: absolute; - z-index: 4; -} -.CodeMirror-gutter-wrapper { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.CodeMirror-lines { - cursor: text; - min-height: 1px; /* prevents collapsing before first draw */ -} -.CodeMirror pre { - -webkit-tap-highlight-color: transparent; - /* Reset some styles that the rest of the page might have set */ - background: transparent; - border-radius: 0; - border-width: 0; - color: inherit; - font-family: inherit; - font-size: inherit; - -webkit-font-variant-ligatures: none; - font-variant-ligatures: none; - line-height: inherit; - margin: 0; - overflow: visible; - position: relative; - white-space: pre; - word-wrap: normal; - z-index: 2; -} -.CodeMirror-wrap pre { - word-wrap: break-word; - white-space: pre-wrap; - word-break: normal; -} - -.CodeMirror-linebackground { - position: absolute; - left: 0; right: 0; top: 0; bottom: 0; - z-index: 0; -} - -.CodeMirror-linewidget { - overflow: auto; - position: relative; - z-index: 2; -} - -.CodeMirror-widget {} - -.CodeMirror-code { - outline: none; -} - -/* Force content-box sizing for the elements where we expect it */ -.CodeMirror-scroll, -.CodeMirror-sizer, -.CodeMirror-gutter, -.CodeMirror-gutters, -.CodeMirror-linenumber { - -webkit-box-sizing: content-box; - box-sizing: content-box; -} - -.CodeMirror-measure { - height: 0; - overflow: hidden; - position: absolute; - visibility: hidden; - width: 100%; -} - -.CodeMirror-cursor { position: absolute; } -.CodeMirror-measure pre { position: static; } - -div.CodeMirror-cursors { - position: relative; - visibility: hidden; - z-index: 3; -} -div.CodeMirror-dragcursors { - visibility: visible; -} - -.CodeMirror-focused div.CodeMirror-cursors { - visibility: visible; -} - -.CodeMirror-selected { background: #d9d9d9; } -.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } -.CodeMirror-crosshair { cursor: crosshair; } -.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } -.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } -.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } - -.cm-searching { - background: #ffa; - background: rgba(255, 255, 0, .4); -} - -/* IE7 hack to prevent it from returning funny offsetTops on the spans */ -.CodeMirror span { *vertical-align: text-bottom; } - -/* Used to force a border model for a node */ -.cm-force-border { padding-right: .1px; } - -@media print { - /* Hide the cursor when printing */ - .CodeMirror div.CodeMirror-cursors { - visibility: hidden; - } -} - -/* See issue #2901 */ -.cm-tab-wrap-hack:after { content: ''; } - -/* Help users use markselection to safely style text background */ -span.CodeMirror-selectedtext { background: none; } - -.CodeMirror-dialog { - background: inherit; - color: inherit; - left: 0; right: 0; - overflow: hidden; - padding: .1em .8em; - position: absolute; - z-index: 15; -} - -.CodeMirror-dialog-top { - border-bottom: 1px solid #eee; - top: 0; -} - -.CodeMirror-dialog-bottom { - border-top: 1px solid #eee; - bottom: 0; -} - -.CodeMirror-dialog input { - background: transparent; - border: 1px solid #d3d6db; - color: inherit; - font-family: monospace; - outline: none; - width: 20em; -} - -.CodeMirror-dialog button { - font-size: 70%; -} -.graphiql-container .doc-explorer { - background: white; -} - -.graphiql-container .doc-explorer-title-bar, -.graphiql-container .history-title-bar { - cursor: default; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - height: 34px; - line-height: 14px; - padding: 8px 8px 5px; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.graphiql-container .doc-explorer-title, -.graphiql-container .history-title { - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; - font-weight: bold; - overflow-x: hidden; - padding: 10px 0 10px 10px; - text-align: center; - text-overflow: ellipsis; - -webkit-user-select: initial; - -moz-user-select: initial; - -ms-user-select: initial; - user-select: initial; - white-space: nowrap; -} - -.graphiql-container .doc-explorer-back { - color: #3B5998; - cursor: pointer; - margin: -7px 0 -6px -8px; - overflow-x: hidden; - padding: 17px 12px 16px 16px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.doc-explorer-narrow .doc-explorer-back { - width: 0; -} - -.graphiql-container .doc-explorer-back:before { - border-left: 2px solid #3B5998; - border-top: 2px solid #3B5998; - content: ''; - display: inline-block; - height: 9px; - margin: 0 3px -1px 0; - position: relative; - -webkit-transform: rotate(-45deg); - transform: rotate(-45deg); - width: 9px; -} - -.graphiql-container .doc-explorer-rhs { - position: relative; -} - -.graphiql-container .doc-explorer-contents, -.graphiql-container .history-contents { - background-color: #ffffff; - border-top: 1px solid #d6d6d6; - bottom: 0; - left: 0; - overflow-y: auto; - padding: 20px 15px; - position: absolute; - right: 0; - top: 47px; -} - -.graphiql-container .doc-explorer-contents { - min-width: 300px; -} - -.graphiql-container .doc-type-description p:first-child , -.graphiql-container .doc-type-description blockquote:first-child { - margin-top: 0; -} - -.graphiql-container .doc-explorer-contents a { - cursor: pointer; - text-decoration: none; -} - -.graphiql-container .doc-explorer-contents a:hover { - text-decoration: underline; -} - -.graphiql-container .doc-value-description > :first-child { - margin-top: 4px; -} - -.graphiql-container .doc-value-description > :last-child { - margin-bottom: 4px; -} - -.graphiql-container .doc-category { - margin: 20px 0; -} - -.graphiql-container .doc-category-title { - border-bottom: 1px solid #e0e0e0; - color: #777; - cursor: default; - font-size: 14px; - font-variant: small-caps; - font-weight: bold; - letter-spacing: 1px; - margin: 0 -15px 10px 0; - padding: 10px 0; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.graphiql-container .doc-category-item { - margin: 12px 0; - color: #555; -} - -.graphiql-container .keyword { - color: #B11A04; -} - -.graphiql-container .type-name { - color: #CA9800; -} - -.graphiql-container .field-name { - color: #1F61A0; -} - -.graphiql-container .field-short-description { - color: #999; - margin-left: 5px; - overflow: hidden; - text-overflow: ellipsis; -} - -.graphiql-container .enum-value { - color: #0B7FC7; -} - -.graphiql-container .arg-name { - color: #8B2BB9; -} - -.graphiql-container .arg { - display: block; - margin-left: 1em; -} - -.graphiql-container .arg:first-child:last-child, -.graphiql-container .arg:first-child:nth-last-child(2), -.graphiql-container .arg:first-child:nth-last-child(2) ~ .arg { - display: inherit; - margin: inherit; -} - -.graphiql-container .arg:first-child:nth-last-child(2):after { - content: ', '; -} - -.graphiql-container .arg-default-value { - color: #43A047; -} - -.graphiql-container .doc-deprecation { - background: #fffae8; - -webkit-box-shadow: inset 0 0 1px #bfb063; - box-shadow: inset 0 0 1px #bfb063; - color: #867F70; - line-height: 16px; - margin: 8px -8px; - max-height: 80px; - overflow: hidden; - padding: 8px; - border-radius: 3px; -} - -.graphiql-container .doc-deprecation:before { - content: 'Deprecated:'; - color: #c79b2e; - cursor: default; - display: block; - font-size: 9px; - font-weight: bold; - letter-spacing: 1px; - line-height: 1; - padding-bottom: 5px; - text-transform: uppercase; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.graphiql-container .doc-deprecation > :first-child { - margin-top: 0; -} - -.graphiql-container .doc-deprecation > :last-child { - margin-bottom: 0; -} - -.graphiql-container .show-btn { - -webkit-appearance: initial; - display: block; - border-radius: 3px; - border: solid 1px #ccc; - text-align: center; - padding: 8px 12px 10px; - width: 100%; - -webkit-box-sizing: border-box; - box-sizing: border-box; - background: #fbfcfc; - color: #555; - cursor: pointer; -} - -.graphiql-container .search-box { - border-bottom: 1px solid #d3d6db; - display: block; - font-size: 14px; - margin: -15px -15px 12px 0; - position: relative; -} - -.graphiql-container .search-box:before { - content: '\26b2'; - cursor: pointer; - display: block; - font-size: 24px; - position: absolute; - top: -2px; - -webkit-transform: rotate(-45deg); - transform: rotate(-45deg); - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.graphiql-container .search-box .search-box-clear { - background-color: #d0d0d0; - border-radius: 12px; - color: #fff; - cursor: pointer; - font-size: 11px; - padding: 1px 5px 2px; - position: absolute; - right: 3px; - top: 8px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.graphiql-container .search-box .search-box-clear:hover { - background-color: #b9b9b9; -} - -.graphiql-container .search-box > input { - border: none; - -webkit-box-sizing: border-box; - box-sizing: border-box; - font-size: 14px; - outline: none; - padding: 6px 24px 8px 20px; - width: 100%; -} - -.graphiql-container .error-container { - font-weight: bold; - left: 0; - letter-spacing: 1px; - opacity: 0.5; - position: absolute; - right: 0; - text-align: center; - text-transform: uppercase; - top: 50%; - -webkit-transform: translate(0, -50%); - transform: translate(0, -50%); -} -.CodeMirror-foldmarker { - color: blue; - cursor: pointer; - font-family: arial; - line-height: .3; - text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px; -} -.CodeMirror-foldgutter { - width: .7em; -} -.CodeMirror-foldgutter-open, -.CodeMirror-foldgutter-folded { - cursor: pointer; -} -.CodeMirror-foldgutter-open:after { - content: "\25BE"; -} -.CodeMirror-foldgutter-folded:after { - content: "\25B8"; -} -.graphiql-container .history-contents { - font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace; - padding: 0; -} - -.graphiql-container .history-contents p { - font-size: 12px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - margin: 0; - padding: 8px; - border-bottom: 1px solid #e0e0e0; -} - -.graphiql-container .history-contents p:hover { - cursor: pointer; -} -.CodeMirror-info { - background: white; - border-radius: 2px; - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #555; - font-family: - system, - -apple-system, - 'San Francisco', - '.SFNSDisplay-Regular', - 'Segoe UI', - Segoe, - 'Segoe WP', - 'Helvetica Neue', - helvetica, - 'Lucida Grande', - arial, - sans-serif; - font-size: 13px; - line-height: 16px; - margin: 8px -8px; - max-width: 400px; - opacity: 0; - overflow: hidden; - padding: 8px 8px; - position: fixed; - -webkit-transition: opacity 0.15s; - transition: opacity 0.15s; - z-index: 50; -} - -.CodeMirror-info :first-child { - margin-top: 0; -} - -.CodeMirror-info :last-child { - margin-bottom: 0; -} - -.CodeMirror-info p { - margin: 1em 0; -} - -.CodeMirror-info .info-description { - color: #777; - line-height: 16px; - margin-top: 1em; - max-height: 80px; - overflow: hidden; -} - -.CodeMirror-info .info-deprecation { - background: #fffae8; - -webkit-box-shadow: inset 0 1px 1px -1px #bfb063; - box-shadow: inset 0 1px 1px -1px #bfb063; - color: #867F70; - line-height: 16px; - margin: -8px; - margin-top: 8px; - max-height: 80px; - overflow: hidden; - padding: 8px; -} - -.CodeMirror-info .info-deprecation-label { - color: #c79b2e; - cursor: default; - display: block; - font-size: 9px; - font-weight: bold; - letter-spacing: 1px; - line-height: 1; - padding-bottom: 5px; - text-transform: uppercase; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.CodeMirror-info .info-deprecation-label + * { - margin-top: 0; -} - -.CodeMirror-info a { - text-decoration: none; -} - -.CodeMirror-info a:hover { - text-decoration: underline; -} - -.CodeMirror-info .type-name { - color: #CA9800; -} - -.CodeMirror-info .field-name { - color: #1F61A0; -} - -.CodeMirror-info .enum-value { - color: #0B7FC7; -} - -.CodeMirror-info .arg-name { - color: #8B2BB9; -} - -.CodeMirror-info .directive-name { - color: #B33086; -} -.CodeMirror-jump-token { - text-decoration: underline; - cursor: pointer; -} -/* The lint marker gutter */ -.CodeMirror-lint-markers { - width: 16px; -} - -.CodeMirror-lint-tooltip { - background-color: infobackground; - border-radius: 4px 4px 4px 4px; - border: 1px solid black; - color: infotext; - font-family: monospace; - font-size: 10pt; - max-width: 600px; - opacity: 0; - overflow: hidden; - padding: 2px 5px; - position: fixed; - -webkit-transition: opacity .4s; - transition: opacity .4s; - white-space: pre-wrap; - z-index: 100; -} - -.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning { - background-position: left bottom; - background-repeat: repeat-x; -} - -.CodeMirror-lint-mark-error { - background-image: - url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") - ; -} - -.CodeMirror-lint-mark-warning { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); -} - -.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { - background-position: center center; - background-repeat: no-repeat; - cursor: pointer; - display: inline-block; - height: 16px; - position: relative; - vertical-align: middle; - width: 16px; -} - -.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning { - background-position: top left; - background-repeat: no-repeat; - padding-left: 18px; -} - -.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); -} - -.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); -} - -.CodeMirror-lint-marker-multiple { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); - background-position: right bottom; - background-repeat: no-repeat; - width: 100%; height: 100%; -} -.graphiql-container .spinner-container { - height: 36px; - left: 50%; - position: absolute; - top: 50%; - -webkit-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); - width: 36px; - z-index: 10; -} - -.graphiql-container .spinner { - -webkit-animation: rotation .6s infinite linear; - animation: rotation .6s infinite linear; - border-bottom: 6px solid rgba(150, 150, 150, .15); - border-left: 6px solid rgba(150, 150, 150, .15); - border-radius: 100%; - border-right: 6px solid rgba(150, 150, 150, .15); - border-top: 6px solid rgba(150, 150, 150, .8); - display: inline-block; - height: 24px; - position: absolute; - vertical-align: middle; - width: 24px; -} - -@-webkit-keyframes rotation { - from { -webkit-transform: rotate(0deg); transform: rotate(0deg); } - to { -webkit-transform: rotate(359deg); transform: rotate(359deg); } -} - -@keyframes rotation { - from { -webkit-transform: rotate(0deg); transform: rotate(0deg); } - to { -webkit-transform: rotate(359deg); transform: rotate(359deg); } -} -.CodeMirror-hints { - background: white; - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); - font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace; - font-size: 13px; - list-style: none; - margin-left: -6px; - margin: 0; - max-height: 14.5em; - overflow-y: auto; - overflow: hidden; - padding: 0; - position: absolute; - z-index: 10; -} - -.CodeMirror-hint { - border-top: solid 1px #f7f7f7; - color: #141823; - cursor: pointer; - margin: 0; - max-width: 300px; - overflow: hidden; - padding: 2px 6px; - white-space: pre; -} - -li.CodeMirror-hint-active { - background-color: #08f; - border-top-color: white; - color: white; -} - -.CodeMirror-hint-information { - border-top: solid 1px #c0c0c0; - max-width: 300px; - padding: 4px 6px; - position: relative; - z-index: 1; -} - -.CodeMirror-hint-information:first-child { - border-bottom: solid 1px #c0c0c0; - border-top: none; - margin-bottom: -1px; -} - -.CodeMirror-hint-deprecation { - background: #fffae8; - -webkit-box-shadow: inset 0 1px 1px -1px #bfb063; - box-shadow: inset 0 1px 1px -1px #bfb063; - color: #867F70; - font-family: - system, - -apple-system, - 'San Francisco', - '.SFNSDisplay-Regular', - 'Segoe UI', - Segoe, - 'Segoe WP', - 'Helvetica Neue', - helvetica, - 'Lucida Grande', - arial, - sans-serif; - font-size: 13px; - line-height: 16px; - margin-top: 4px; - max-height: 80px; - overflow: hidden; - padding: 6px; -} - -.CodeMirror-hint-deprecation .deprecation-label { - color: #c79b2e; - cursor: default; - display: block; - font-size: 9px; - font-weight: bold; - letter-spacing: 1px; - line-height: 1; - padding-bottom: 5px; - text-transform: uppercase; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.CodeMirror-hint-deprecation .deprecation-label + * { - margin-top: 0; -} - -.CodeMirror-hint-deprecation :last-child { - margin-bottom: 0; -} diff --git a/server/http/assets/graphiql.min.js b/server/http/assets/graphiql.min.js deleted file mode 100644 index fd7b1585d89..00000000000 --- a/server/http/assets/graphiql.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(f){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=f();else if("function"==typeof define&&define.amd)define([],f);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).GraphiQL=f()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n||e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o1&&e.setState({navStack:e.state.navStack.slice(0,-1)})},e.handleClickTypeOrField=function(t){e.showDoc(t)},e.handleSearch=function(t){e.showSearch(t)},e.state={navStack:[initialNav]},e}return _inherits(t,e),_createClass(t,[{key:"shouldComponentUpdate",value:function(e,t){return this.props.schema!==e.schema||this.state.navStack!==t.navStack}},{key:"render",value:function(){var e=this.props.schema,t=this.state.navStack,a=t[t.length-1],r=void 0;r=void 0===e?_react2.default.createElement("div",{className:"spinner-container"},_react2.default.createElement("div",{className:"spinner"})):e?a.search?_react2.default.createElement(_SearchResults2.default,{searchValue:a.search,withinType:a.def,schema:e,onClickType:this.handleClickTypeOrField,onClickField:this.handleClickTypeOrField}):1===t.length?_react2.default.createElement(_SchemaDoc2.default,{schema:e,onClickType:this.handleClickTypeOrField}):(0,_graphql.isType)(a.def)?_react2.default.createElement(_TypeDoc2.default,{schema:e,type:a.def,onClickType:this.handleClickTypeOrField,onClickField:this.handleClickTypeOrField}):_react2.default.createElement(_FieldDoc2.default,{field:a.def,onClickType:this.handleClickTypeOrField}):_react2.default.createElement("div",{className:"error-container"},"No Schema Available");var c=1===t.length||(0,_graphql.isType)(a.def)&&a.def.getFields,l=void 0;return t.length>1&&(l=t[t.length-2].name),_react2.default.createElement("div",{className:"doc-explorer",key:a.name},_react2.default.createElement("div",{className:"doc-explorer-title-bar"},l&&_react2.default.createElement("div",{className:"doc-explorer-back",onClick:this.handleNavBackClick},l),_react2.default.createElement("div",{className:"doc-explorer-title"},a.title||a.name),_react2.default.createElement("div",{className:"doc-explorer-rhs"},this.props.children)),_react2.default.createElement("div",{className:"doc-explorer-contents"},c&&_react2.default.createElement(_SearchBox2.default,{value:a.search,placeholder:"Search "+a.name+"...",onSearch:this.handleSearch}),r))}},{key:"showDoc",value:function(e){var t=this.state.navStack;t[t.length-1].def!==e&&this.setState({navStack:t.concat([{name:e.name,def:e}])})}},{key:"showDocForReference",value:function(e){"Type"===e.kind?this.showDoc(e.type):"Field"===e.kind?this.showDoc(e.field):"Argument"===e.kind&&e.field?this.showDoc(e.field):"EnumValue"===e.kind&&e.type&&this.showDoc(e.type)}},{key:"showSearch",value:function(e){var t=this.state.navStack.slice(),a=t[t.length-1];t[t.length-1]=_extends({},a,{search:e}),this.setState({navStack:t})}},{key:"reset",value:function(){this.setState({navStack:[initialNav]})}}]),t}(_react2.default.Component)).propTypes={schema:_propTypes2.default.instanceOf(_graphql.GraphQLSchema)}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./DocExplorer/FieldDoc":4,"./DocExplorer/SchemaDoc":6,"./DocExplorer/SearchBox":7,"./DocExplorer/SearchResults":8,"./DocExplorer/TypeDoc":9,graphql:95,"prop-types":233}],2:[function(require,module,exports){(function(global){"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function Argument(e){var t=e.arg,r=e.onClickType,a=e.showDefaultValue;return _react2.default.createElement("span",{className:"arg"},_react2.default.createElement("span",{className:"arg-name"},t.name),": ",_react2.default.createElement(_TypeLink2.default,{type:t.type,onClick:r}),!1!==a&&_react2.default.createElement(_DefaultValue2.default,{field:t}))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=Argument;var _react2=_interopRequireDefault("undefined"!=typeof window?window.React:void 0!==global?global.React:null),_propTypes2=_interopRequireDefault(require("prop-types")),_TypeLink2=_interopRequireDefault(require("./TypeLink")),_DefaultValue2=_interopRequireDefault(require("./DefaultValue"));Argument.propTypes={arg:_propTypes2.default.object.isRequired,onClickType:_propTypes2.default.func.isRequired,showDefaultValue:_propTypes2.default.bool}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./DefaultValue":3,"./TypeLink":10,"prop-types":233}],3:[function(require,module,exports){(function(global){"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function DefaultValue(e){var r=e.field,t=r.type,a=r.defaultValue;return void 0!==a?_react2.default.createElement("span",null," = ",_react2.default.createElement("span",{className:"arg-default-value"},(0,_graphql.print)((0,_graphql.astFromValue)(a,t)))):null}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=DefaultValue;var _react2=_interopRequireDefault("undefined"!=typeof window?window.React:void 0!==global?global.React:null),_propTypes2=_interopRequireDefault(require("prop-types")),_graphql=require("graphql");DefaultValue.propTypes={field:_propTypes2.default.object.isRequired}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{graphql:95,"prop-types":233}],4:[function(require,module,exports){(function(global){"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var r=0;r0&&(r=_react2.default.createElement("div",{className:"doc-category"},_react2.default.createElement("div",{className:"doc-category-title"},"arguments"),t.args.map(function(t){return _react2.default.createElement("div",{key:t.name,className:"doc-category-item"},_react2.default.createElement("div",null,_react2.default.createElement(_Argument2.default,{arg:t,onClickType:e.props.onClickType})),_react2.default.createElement(_MarkdownContent2.default,{className:"doc-value-description",markdown:t.description}))}))),_react2.default.createElement("div",null,_react2.default.createElement(_MarkdownContent2.default,{className:"doc-type-description",markdown:t.description||"No Description"}),t.deprecationReason&&_react2.default.createElement(_MarkdownContent2.default,{className:"doc-deprecation",markdown:t.deprecationReason}),_react2.default.createElement("div",{className:"doc-category"},_react2.default.createElement("div",{className:"doc-category-title"},"type"),_react2.default.createElement(_TypeLink2.default,{type:t.type,onClick:this.props.onClickType})),r)}}]),t}(_react2.default.Component);FieldDoc.propTypes={field:_propTypes2.default.object,onClickType:_propTypes2.default.func},exports.default=FieldDoc}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./Argument":2,"./MarkdownContent":5,"./TypeLink":10,"prop-types":233}],5:[function(require,module,exports){(function(global){"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var r=0;r=100)return"break";var i=c[r];if(t!==i&&isMatch(r,e)&&l.push(_react2.default.createElement("div",{className:"doc-category-item",key:r},_react2.default.createElement(_TypeLink2.default,{type:i,onClick:n}))),i.getFields){var s=i.getFields();Object.keys(s).forEach(function(l){var c=s[l],p=void 0;if(!isMatch(l,e)){if(!c.args||!c.args.length)return;if(0===(p=c.args.filter(function(t){return isMatch(t.name,e)})).length)return}var f=_react2.default.createElement("div",{className:"doc-category-item",key:r+"."+l},t!==i&&[_react2.default.createElement(_TypeLink2.default,{key:"type",type:i,onClick:n}),"."],_react2.default.createElement("a",{className:"field-name",onClick:function(e){return a(c,i,e)}},c.name),p&&["(",_react2.default.createElement("span",{key:"args"},p.map(function(e){return _react2.default.createElement(_Argument2.default,{key:e.name,arg:e,onClickType:n,showDefaultValue:!1})})),")"]);t===i?o.push(f):u.push(f)})}}();s=!0);}catch(e){p=!0,f=e}finally{try{!s&&_.return&&_.return()}finally{if(p)throw f}}return o.length+l.length+u.length===0?_react2.default.createElement("span",{className:"doc-alert-text"},"No results found."):t&&l.length+u.length>0?_react2.default.createElement("div",null,o,_react2.default.createElement("div",{className:"doc-category"},_react2.default.createElement("div",{className:"doc-category-title"},"other results"),l,u)):_react2.default.createElement("div",null,o,l,u)}}]),t}(_react2.default.Component);SearchResults.propTypes={schema:_propTypes2.default.object,withinType:_propTypes2.default.object,searchValue:_propTypes2.default.string,onClickType:_propTypes2.default.func,onClickField:_propTypes2.default.func},exports.default=SearchResults}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./Argument":2,"./TypeLink":10,"prop-types":233}],9:[function(require,module,exports){(function(global){"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function Field(e){var t=e.type,a=e.field,r=e.onClickType,n=e.onClickField;return _react2.default.createElement("div",{className:"doc-category-item"},_react2.default.createElement("a",{className:"field-name",onClick:function(e){return n(a,t,e)}},a.name),a.args&&a.args.length>0&&["(",_react2.default.createElement("span",{key:"args"},a.args.map(function(e){return _react2.default.createElement(_Argument2.default,{key:e.name,arg:e,onClickType:r})})),")"],": ",_react2.default.createElement(_TypeLink2.default,{type:a.type,onClick:r}),_react2.default.createElement(_DefaultValue2.default,{field:a}),a.description&&_react2.default.createElement(_MarkdownContent2.default,{className:"field-short-description",markdown:a.description}),a.deprecationReason&&_react2.default.createElement(_MarkdownContent2.default,{className:"doc-deprecation",markdown:a.deprecationReason}))}function EnumValue(e){var t=e.value;return _react2.default.createElement("div",{className:"doc-category-item"},_react2.default.createElement("div",{className:"enum-value"},t.name),_react2.default.createElement(_MarkdownContent2.default,{className:"doc-value-description",markdown:t.description}),t.deprecationReason&&_react2.default.createElement(_MarkdownContent2.default,{className:"doc-deprecation",markdown:t.deprecationReason}))}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var a=0;a0&&(l=_react2.default.createElement("div",{className:"doc-category"},_react2.default.createElement("div",{className:"doc-category-title"},n),c.map(function(e){return _react2.default.createElement("div",{key:e.name,className:"doc-category-item"},_react2.default.createElement(_TypeLink2.default,{type:e,onClick:a}))})));var o=void 0,i=void 0;if(t.getFields){var u=t.getFields(),p=Object.keys(u).map(function(e){return u[e]});o=_react2.default.createElement("div",{className:"doc-category"},_react2.default.createElement("div",{className:"doc-category-title"},"fields"),p.filter(function(e){return!e.isDeprecated}).map(function(e){return _react2.default.createElement(Field,{key:e.name,type:t,field:e,onClickType:a,onClickField:r})}));var s=p.filter(function(e){return e.isDeprecated});s.length>0&&(i=_react2.default.createElement("div",{className:"doc-category"},_react2.default.createElement("div",{className:"doc-category-title"},"deprecated fields"),this.state.showDeprecated?s.map(function(e){return _react2.default.createElement(Field,{key:e.name,type:t,field:e,onClickType:a,onClickField:r})}):_react2.default.createElement("button",{className:"show-btn",onClick:this.handleShowDeprecated},"Show deprecated fields...")))}var d=void 0,f=void 0;if(t instanceof _graphql.GraphQLEnumType){var m=t.getValues();d=_react2.default.createElement("div",{className:"doc-category"},_react2.default.createElement("div",{className:"doc-category-title"},"values"),m.filter(function(e){return!e.isDeprecated}).map(function(e){return _react2.default.createElement(EnumValue,{key:e.name,value:e})}));var _=m.filter(function(e){return e.isDeprecated});_.length>0&&(f=_react2.default.createElement("div",{className:"doc-category"},_react2.default.createElement("div",{className:"doc-category-title"},"deprecated values"),this.state.showDeprecated?_.map(function(e){return _react2.default.createElement(EnumValue,{key:e.name,value:e})}):_react2.default.createElement("button",{className:"show-btn",onClick:this.handleShowDeprecated},"Show deprecated values...")))}return _react2.default.createElement("div",null,_react2.default.createElement(_MarkdownContent2.default,{className:"doc-type-description",markdown:t.description||"No Description"}),t instanceof _graphql.GraphQLObjectType&&l,o,i,d,f,!(t instanceof _graphql.GraphQLObjectType)&&l)}}]),t}(_react2.default.Component);TypeDoc.propTypes={schema:_propTypes2.default.instanceOf(_graphql.GraphQLSchema),type:_propTypes2.default.object,onClickType:_propTypes2.default.func,onClickField:_propTypes2.default.func},exports.default=TypeDoc,Field.propTypes={type:_propTypes2.default.object,field:_propTypes2.default.object,onClickType:_propTypes2.default.func,onClickField:_propTypes2.default.func},EnumValue.propTypes={value:_propTypes2.default.object}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./Argument":2,"./DefaultValue":3,"./MarkdownContent":5,"./TypeLink":10,graphql:95,"prop-types":233}],10:[function(require,module,exports){(function(global){"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function renderType(e,t){return e instanceof _graphql.GraphQLNonNull?_react2.default.createElement("span",null,renderType(e.ofType,t),"!"):e instanceof _graphql.GraphQLList?_react2.default.createElement("span",null,"[",renderType(e.ofType,t),"]"):_react2.default.createElement("a",{className:"type-name",onClick:function(r){return t(e,r)}},e.name)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var r=0;r1,r=null;if(o&&n){var u=this.state.highlight;r=_react2.default.createElement("ul",{className:"execute-options"},t.map(function(t){return _react2.default.createElement("li",{key:t.name?t.name.value:"*",className:t===u&&"selected"||null,onMouseOver:function(){return e.setState({highlight:t})},onMouseOut:function(){return e.setState({highlight:null})},onMouseUp:function(){return e._onOptionSelected(t)}},t.name?t.name.value:"")}))}var a=void 0;!this.props.isRunning&&o||(a=this._onClick);var i=void 0;this.props.isRunning||!o||n||(i=this._onOptionsOpen);var s=this.props.isRunning?_react2.default.createElement("path",{d:"M 10 10 L 23 10 L 23 23 L 10 23 z"}):_react2.default.createElement("path",{d:"M 11 9 L 24 16 L 11 23 z"});return _react2.default.createElement("div",{className:"execute-button-wrap"},_react2.default.createElement("button",{type:"button",className:"execute-button",onMouseDown:i,onClick:a,title:"Execute Query (Ctrl-Enter)"},_react2.default.createElement("svg",{width:"34",height:"34"},s)),r)}}]),t}(_react2.default.Component)).propTypes={onRun:_propTypes2.default.func,onStop:_propTypes2.default.func,isRunning:_propTypes2.default.bool,operations:_propTypes2.default.array}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"prop-types":233}],12:[function(require,module,exports){(function(global){"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function isPromise(e){return"object"===(void 0===e?"undefined":_typeof(e))&&"function"==typeof e.then}function observableToPromise(e){return isObservable(e)?new Promise(function(t,r){var o=e.subscribe(function(e){t(e),o.unsubscribe()},r,function(){r(new Error("no value resolved"))})}):e}function isObservable(e){return"object"===(void 0===e?"undefined":_typeof(e))&&"function"==typeof e.subscribe}Object.defineProperty(exports,"__esModule",{value:!0}),exports.GraphiQL=void 0;var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_extends=Object.assign||function(e){for(var t=1;t0){var o=this.getQueryEditor();o.operation(function(){var e=o.getCursor(),i=o.indexFromPos(e);o.setValue(r);var n=0,a=t.map(function(e){var t=e.index,r=e.string;return o.markText(o.posFromIndex(t+n),o.posFromIndex(t+(n+=r.length)),{className:"autoInsertedLeaf",clearOnEnter:!0,title:"Automatically added leaf fields"})});setTimeout(function(){return a.forEach(function(e){return e.clear()})},7e3);var s=i;t.forEach(function(e){var t=e.index,r=e.string;t=i){e=a.name&&a.name.value;break}}}this.handleRunQuery(e)}}},{key:"_didClickDragBar",value:function(e){if(0!==e.button||e.ctrlKey)return!1;var t=e.target;if(0!==t.className.indexOf("CodeMirror-gutter"))return!1;for(var r=_reactDom2.default.findDOMNode(this.resultComponent);t;){if(t===r)return!0;t=t.parentNode}return!1}}]),t}(_react2.default.Component);GraphiQL.propTypes={fetcher:_propTypes2.default.func.isRequired,schema:_propTypes2.default.instanceOf(_graphql.GraphQLSchema),query:_propTypes2.default.string,variables:_propTypes2.default.string,operationName:_propTypes2.default.string,response:_propTypes2.default.string,storage:_propTypes2.default.shape({getItem:_propTypes2.default.func,setItem:_propTypes2.default.func,removeItem:_propTypes2.default.func}),defaultQuery:_propTypes2.default.string,onEditQuery:_propTypes2.default.func,onEditVariables:_propTypes2.default.func,onEditOperationName:_propTypes2.default.func,onToggleDocs:_propTypes2.default.func,getDefaultFieldNames:_propTypes2.default.func,editorTheme:_propTypes2.default.string,onToggleHistory:_propTypes2.default.func,ResultsTooltip:_propTypes2.default.any};var _initialiseProps=function(){var e=this;this.handleClickReference=function(t){e.setState({docExplorerOpen:!0},function(){e.docExplorerComponent.showDocForReference(t)})},this.handleRunQuery=function(t){var r=++e._editorQueryID,o=e.autoCompleteLeafs()||e.state.query,i=e.state.variables,n=e.state.operationName;t&&t!==n&&(n=t,e.handleEditOperationName(n));try{e.setState({isWaitingForResponse:!0,response:null,operationName:n});var a=e._fetchQuery(o,i,n,function(t){r===e._editorQueryID&&e.setState({isWaitingForResponse:!1,response:JSON.stringify(t,null,2)})});e.setState({subscription:a})}catch(t){e.setState({isWaitingForResponse:!1,response:t.message})}},this.handleStopQuery=function(){var t=e.state.subscription;e.setState({isWaitingForResponse:!1,subscription:null}),t&&t.unsubscribe()},this.handlePrettifyQuery=function(){var t=e.getQueryEditor();t.setValue((0,_graphql.print)((0,_graphql.parse)(t.getValue())))},this.handleEditQuery=(0,_debounce2.default)(100,function(t){var r=e._updateQueryFacts(t,e.state.operationName,e.state.operations,e.state.schema);if(e.setState(_extends({query:t},r)),e.props.onEditQuery)return e.props.onEditQuery(t)}),this._updateQueryFacts=function(t,r,o,i){var n=(0,_getQueryFacts2.default)(i,t);if(n){var a=(0,_getSelectedOperationName2.default)(o,r,n.operations),s=e.props.onEditOperationName;return s&&r!==a&&s(a),_extends({operationName:a},n)}},this.handleEditVariables=function(t){e.setState({variables:t}),e.props.onEditVariables&&e.props.onEditVariables(t)},this.handleEditOperationName=function(t){var r=e.props.onEditOperationName;r&&r(t)},this.handleHintInformationRender=function(t){t.addEventListener("click",e._onClickHintInformation);var r=void 0;t.addEventListener("DOMNodeRemoved",r=function(){t.removeEventListener("DOMNodeRemoved",r),t.removeEventListener("click",e._onClickHintInformation)})},this.handleEditorRunQuery=function(){e._runQueryAtCursor()},this._onClickHintInformation=function(t){if("typeName"===t.target.className){var r=t.target.innerHTML,o=e.state.schema;if(o){var i=o.getType(r);i&&e.setState({docExplorerOpen:!0},function(){e.docExplorerComponent.showDoc(i)})}}},this.handleToggleDocs=function(){"function"==typeof e.props.onToggleDocs&&e.props.onToggleDocs(!e.state.docExplorerOpen),e.setState({docExplorerOpen:!e.state.docExplorerOpen})},this.handleToggleHistory=function(){"function"==typeof e.props.onToggleHistory&&e.props.onToggleHistory(!e.state.historyPaneOpen),e.setState({historyPaneOpen:!e.state.historyPaneOpen})},this.handleSelectHistoryQuery=function(t,r,o){e.handleEditQuery(t),e.handleEditVariables(r),e.handleEditOperationName(o)},this.handleResizeStart=function(t){if(e._didClickDragBar(t)){t.preventDefault();var r=t.clientX-(0,_elementPosition.getLeft)(t.target),o=function(t){if(0===t.buttons)return i();var o=_reactDom2.default.findDOMNode(e.editorBarComponent),n=t.clientX-(0,_elementPosition.getLeft)(o)-r,a=o.clientWidth-n;e.setState({editorFlex:n/a})},i=function(e){function t(){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}(function(){document.removeEventListener("mousemove",o),document.removeEventListener("mouseup",i),o=null,i=null});document.addEventListener("mousemove",o),document.addEventListener("mouseup",i)}},this.handleResetResize=function(){e.setState({editorFlex:1})},this.handleDocsResizeStart=function(t){t.preventDefault();var r=e.state.docExplorerWidth,o=t.clientX-(0,_elementPosition.getLeft)(t.target),i=function(t){if(0===t.buttons)return n();var r=_reactDom2.default.findDOMNode(e),i=t.clientX-(0,_elementPosition.getLeft)(r)-o,a=r.clientWidth-i;a<100?e.setState({docExplorerOpen:!1}):e.setState({docExplorerOpen:!0,docExplorerWidth:Math.min(a,650)})},n=function(e){function t(){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}(function(){e.state.docExplorerOpen||e.setState({docExplorerWidth:r}),document.removeEventListener("mousemove",i),document.removeEventListener("mouseup",n),i=null,n=null});document.addEventListener("mousemove",i),document.addEventListener("mouseup",n)},this.handleDocsResetResize=function(){e.setState({docExplorerWidth:DEFAULT_DOC_EXPLORER_WIDTH})},this.handleVariableResizeStart=function(t){t.preventDefault();var r=!1,o=e.state.variableEditorOpen,i=e.state.variableEditorHeight,n=t.clientY-(0,_elementPosition.getTop)(t.target),a=function(t){if(0===t.buttons)return s();r=!0;var o=_reactDom2.default.findDOMNode(e.editorBarComponent),a=t.clientY-(0,_elementPosition.getTop)(o)-n,l=o.clientHeight-a;l<60?e.setState({variableEditorOpen:!1,variableEditorHeight:i}):e.setState({variableEditorOpen:!0,variableEditorHeight:l})},s=function(e){function t(){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}(function(){r||e.setState({variableEditorOpen:!o}),document.removeEventListener("mousemove",a),document.removeEventListener("mouseup",s),a=null,s=null});document.addEventListener("mousemove",a),document.addEventListener("mouseup",s)}};GraphiQL.Logo=function(e){return _react2.default.createElement("div",{className:"title"},e.children||_react2.default.createElement("span",null,"Graph",_react2.default.createElement("em",null,"i"),"QL"))},GraphiQL.Toolbar=function(e){return _react2.default.createElement("div",{className:"toolbar"},e.children)},GraphiQL.QueryEditor=_QueryEditor.QueryEditor,GraphiQL.VariableEditor=_VariableEditor.VariableEditor,GraphiQL.ResultViewer=_ResultViewer.ResultViewer,GraphiQL.Button=_ToolbarButton.ToolbarButton,GraphiQL.ToolbarButton=_ToolbarButton.ToolbarButton,GraphiQL.Group=_ToolbarGroup.ToolbarGroup,GraphiQL.Menu=_ToolbarMenu.ToolbarMenu,GraphiQL.MenuItem=_ToolbarMenu.ToolbarMenuItem,GraphiQL.Select=_ToolbarSelect.ToolbarSelect,GraphiQL.SelectOption=_ToolbarSelect.ToolbarSelectOption,GraphiQL.Footer=function(e){return _react2.default.createElement("div",{className:"footer"},e.children)};var defaultQuery='# Welcome to GraphiQL\n#\n# GraphiQL is an in-browser tool for writing, validating, and\n# testing GraphQL queries.\n#\n# Type queries into this side of the screen, and you will see intelligent\n# typeaheads aware of the current GraphQL type schema and live syntax and\n# validation errors highlighted within the text.\n#\n# GraphQL queries typically start with a "{" character. Lines that starts\n# with a # are ignored.\n#\n# An example GraphQL query might look like:\n#\n# {\n# field(arg: "value") {\n# subField\n# }\n# }\n#\n# Keyboard shortcuts:\n#\n# Prettify Query: Shift-Ctrl-P (or press the prettify button above)\n#\n# Run Query: Ctrl-Enter (or press the play button above)\n#\n# Auto Complete: Ctrl-Space (or just start typing)\n#\n\n'}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../utility/CodeMirrorSizer":23,"../utility/StorageAPI":25,"../utility/debounce":26,"../utility/elementPosition":27,"../utility/fillLeafs":28,"../utility/find":29,"../utility/getQueryFacts":30,"../utility/getSelectedOperationName":31,"../utility/introspectionQueries":32,"./DocExplorer":1,"./ExecuteButton":11,"./QueryEditor":14,"./QueryHistory":15,"./ResultViewer":16,"./ToolbarButton":17,"./ToolbarGroup":18,"./ToolbarMenu":19,"./ToolbarSelect":20,"./VariableEditor":21,graphql:95,"prop-types":233}],13:[function(require,module,exports){(function(global){"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var r=0;r20&&this.historyStore.shift();var r=this.historyStore.items,o=this.favoriteStore.items,i=r.concat(o);this.setState({queries:i})}}},{key:"render",value:function(){var e=this,t=this.state.queries.slice().reverse().map(function(t,r){return _react2.default.createElement(_HistoryQuery2.default,_extends({handleToggleFavorite:e.toggleFavorite,key:r,onSelect:e.props.onSelectQuery},t))});return _react2.default.createElement("div",null,_react2.default.createElement("div",{className:"history-title-bar"},_react2.default.createElement("div",{className:"history-title"},"History"),_react2.default.createElement("div",{className:"doc-explorer-rhs"},this.props.children)),_react2.default.createElement("div",{className:"history-contents"},t))}}]),t}(_react2.default.Component)).propTypes={query:_propTypes2.default.string,variables:_propTypes2.default.string,operationName:_propTypes2.default.string,queryID:_propTypes2.default.number,onSelectQuery:_propTypes2.default.func,storage:_propTypes2.default.object};var _initialiseProps=function(){var e=this;this.toggleFavorite=function(t,r,o,i){var a={query:t,variables:r,operationName:o};e.favoriteStore.contains(a)?i&&(a.favorite=!1,e.favoriteStore.delete(a)):(a.favorite=!0,e.favoriteStore.push(a));var s=e.historyStore.items,n=e.favoriteStore.items,u=s.concat(n);e.setState({queries:u})}}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../utility/QueryStore":24,"./HistoryQuery":13,graphql:95,"prop-types":233}],16:[function(require,module,exports){(function(global){"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _classCallCheck(e,r){if(!(e instanceof r))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,r){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!r||"object"!=typeof r&&"function"!=typeof r?e:r}function _inherits(e,r){if("function"!=typeof r&&null!==r)throw new TypeError("Super expression must either be null or a function, not "+typeof r);e.prototype=Object.create(r&&r.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),r&&(Object.setPrototypeOf?Object.setPrototypeOf(e,r):e.__proto__=r)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.ResultViewer=void 0;var _createClass=function(){function e(e,r){for(var t=0;t=65&&t<=90||!r.shiftKey&&t>=48&&t<=57||r.shiftKey&&189===t||r.shiftKey&&222===t)&&o.editor.execCommand("autocomplete")},o._onEdit=function(){o.ignoreChangeEvent||(o.cachedValue=o.editor.getValue(),o.props.onEdit&&o.props.onEdit(o.cachedValue))},o._onHasCompletion=function(e,r){(0,_onHasCompletion2.default)(e,r,o.props.onHintInformationRender)},o.cachedValue=e.value||"",o}return _inherits(r,e),_createClass(r,[{key:"componentDidMount",value:function(){var e=this,r=require("codemirror");require("codemirror/addon/hint/show-hint"),require("codemirror/addon/edit/matchbrackets"),require("codemirror/addon/edit/closebrackets"),require("codemirror/addon/fold/brace-fold"),require("codemirror/addon/fold/foldgutter"),require("codemirror/addon/lint/lint"),require("codemirror/addon/search/searchcursor"),require("codemirror/addon/search/jump-to-line"),require("codemirror/addon/dialog/dialog"),require("codemirror/keymap/sublime"),require("codemirror-graphql/variables/hint"),require("codemirror-graphql/variables/lint"),require("codemirror-graphql/variables/mode"),this.editor=r(this._node,{value:this.props.value||"",lineNumbers:!0,tabSize:2,mode:"graphql-variables",theme:this.props.editorTheme||"graphiql",keyMap:"sublime",autoCloseBrackets:!0,matchBrackets:!0,showCursorWhenSelecting:!0,readOnly:!!this.props.readOnly&&"nocursor",foldGutter:{minFoldSize:4},lint:{variableToType:this.props.variableToType},hintOptions:{variableToType:this.props.variableToType,closeOnUnfocus:!1,completeSingle:!1},gutters:["CodeMirror-linenumbers","CodeMirror-foldgutter"],extraKeys:{"Cmd-Space":function(){return e.editor.showHint({completeSingle:!1})},"Ctrl-Space":function(){return e.editor.showHint({completeSingle:!1})},"Alt-Space":function(){return e.editor.showHint({completeSingle:!1})},"Shift-Space":function(){return e.editor.showHint({completeSingle:!1})},"Cmd-Enter":function(){e.props.onRunQuery&&e.props.onRunQuery()},"Ctrl-Enter":function(){e.props.onRunQuery&&e.props.onRunQuery()},"Shift-Ctrl-P":function(){e.props.onPrettifyQuery&&e.props.onPrettifyQuery()},"Cmd-F":"findPersistent","Ctrl-F":"findPersistent","Ctrl-Left":"goSubwordLeft","Ctrl-Right":"goSubwordRight","Alt-Left":"goGroupLeft","Alt-Right":"goGroupRight"}}),this.editor.on("change",this._onEdit),this.editor.on("keyup",this._onKeyUp),this.editor.on("hasCompletion",this._onHasCompletion)}},{key:"componentDidUpdate",value:function(e){var r=require("codemirror");this.ignoreChangeEvent=!0,this.props.variableToType!==e.variableToType&&(this.editor.options.lint.variableToType=this.props.variableToType,this.editor.options.hintOptions.variableToType=this.props.variableToType,r.signal(this.editor,"change",this.editor)),this.props.value!==e.value&&this.props.value!==this.cachedValue&&(this.cachedValue=this.props.value,this.editor.setValue(this.props.value)),this.ignoreChangeEvent=!1}},{key:"componentWillUnmount",value:function(){this.editor.off("change",this._onEdit),this.editor.off("keyup",this._onKeyUp),this.editor.off("hasCompletion",this._onHasCompletion),this.editor=null}},{key:"render",value:function(){var e=this;return _react2.default.createElement("div",{className:"codemirrorWrap",ref:function(r){e._node=r}})}},{key:"getCodeMirror",value:function(){return this.editor}},{key:"getClientHeight",value:function(){return this._node&&this._node.clientHeight}}]),r}(_react2.default.Component)).propTypes={variableToType:_propTypes2.default.object,value:_propTypes2.default.string,onEdit:_propTypes2.default.func,readOnly:_propTypes2.default.bool,onHintInformationRender:_propTypes2.default.func,onPrettifyQuery:_propTypes2.default.func,onRunQuery:_propTypes2.default.func,editorTheme:_propTypes2.default.string}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../utility/onHasCompletion":34,codemirror:65,"codemirror-graphql/variables/hint":49,"codemirror-graphql/variables/lint":50,"codemirror-graphql/variables/mode":51,"codemirror/addon/dialog/dialog":53,"codemirror/addon/edit/closebrackets":54,"codemirror/addon/edit/matchbrackets":55,"codemirror/addon/fold/brace-fold":56,"codemirror/addon/fold/foldgutter":58,"codemirror/addon/hint/show-hint":59,"codemirror/addon/lint/lint":60,"codemirror/addon/search/jump-to-line":61,"codemirror/addon/search/searchcursor":63,"codemirror/keymap/sublime":64,"prop-types":233}],22:[function(require,module,exports){"use strict";module.exports=require("./components/GraphiQL").GraphiQL},{"./components/GraphiQL":12}],23:[function(require,module,exports){"use strict";function _classCallCheck(e,r){if(!(e instanceof r))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,r){for(var t=0;t'+e.name+""}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=function(e,n,r){var t=void 0,a=void 0;require("codemirror").on(n,"select",function(e,n){if(!t){var o=n.parentNode;(t=document.createElement("div")).className="CodeMirror-hint-information",o.appendChild(t),(a=document.createElement("div")).className="CodeMirror-hint-deprecation",o.appendChild(a);var i=void 0;o.addEventListener("DOMNodeRemoved",i=function(e){e.target===o&&(o.removeEventListener("DOMNodeRemoved",i),t=null,a=null,i=null)})}var d=e.description?md.render(e.description):"Self descriptive.",p=e.type?''+renderType(e.type)+"":"";if(t.innerHTML='
'+("

"===d.slice(0,3)?"

"+p+d.slice(3):p+d)+"

",e.isDeprecated){var l=e.deprecationReason?md.render(e.deprecationReason):"";a.innerHTML='Deprecated'+l,a.style.display="block"}else a.style.display="none";r&&r(t)})};var _graphql=require("graphql"),md=new(function(e){return e&&e.__esModule?e:{default:e}}(require("markdown-it")).default)},{codemirror:65,graphql:95,"markdown-it":172}],35:[function(require,module,exports){(function(global){"use strict";function compare(a,b){if(a===b)return 0;for(var x=a.length,y=b.length,i=0,len=Math.min(x,y);i=0;i--)if(ka[i]!==kb[i])return!1;for(i=ka.length-1;i>=0;i--)if(key=ka[i],!_deepEqual(a[key],b[key],strict,actualVisitedObjects))return!1;return!0}function notDeepStrictEqual(actual,expected,message){_deepEqual(actual,expected,!0)&&fail(actual,expected,message,"notDeepStrictEqual",notDeepStrictEqual)}function expectedException(actual,expected){if(!actual||!expected)return!1;if("[object RegExp]"==Object.prototype.toString.call(expected))return expected.test(actual);try{if(actual instanceof expected)return!0}catch(e){}return!Error.isPrototypeOf(expected)&&!0===expected.call({},actual)}function _tryBlock(block){var error;try{block()}catch(e){error=e}return error}function _throws(shouldThrow,block,expected,message){var actual;if("function"!=typeof block)throw new TypeError('"block" argument must be a function');"string"==typeof expected&&(message=expected,expected=null),actual=_tryBlock(block),message=(expected&&expected.name?" ("+expected.name+").":".")+(message?" "+message:"."),shouldThrow&&!actual&&fail(actual,expected,"Missing expected exception"+message);var userProvidedMessage="string"==typeof message,isUnwantedException=!shouldThrow&&util.isError(actual),isUnexpectedException=!shouldThrow&&actual&&!expected;if((isUnwantedException&&userProvidedMessage&&expectedException(actual,expected)||isUnexpectedException)&&fail(actual,expected,"Got unwanted exception"+message),shouldThrow&&actual&&expected&&!expectedException(actual,expected)||!shouldThrow&&actual)throw actual}var util=require("util/"),hasOwn=Object.prototype.hasOwnProperty,pSlice=Array.prototype.slice,functionsHaveNames="foo"===function(){}.name,assert=module.exports=ok,regex=/\s*function\s+([^\(\s]*)\s*/;assert.AssertionError=function(options){this.name="AssertionError",this.actual=options.actual,this.expected=options.expected,this.operator=options.operator,options.message?(this.message=options.message,this.generatedMessage=!1):(this.message=getMessage(this),this.generatedMessage=!0);var stackStartFunction=options.stackStartFunction||fail;if(Error.captureStackTrace)Error.captureStackTrace(this,stackStartFunction);else{var err=new Error;if(err.stack){var out=err.stack,fn_name=getName(stackStartFunction),idx=out.indexOf("\n"+fn_name);if(idx>=0){var next_line=out.indexOf("\n",idx+1);out=out.substring(next_line+1)}this.stack=out}}},util.inherits(assert.AssertionError,Error),assert.fail=fail,assert.ok=ok,assert.equal=function(actual,expected,message){actual!=expected&&fail(actual,expected,message,"==",assert.equal)},assert.notEqual=function(actual,expected,message){actual==expected&&fail(actual,expected,message,"!=",assert.notEqual)},assert.deepEqual=function(actual,expected,message){_deepEqual(actual,expected,!1)||fail(actual,expected,message,"deepEqual",assert.deepEqual)},assert.deepStrictEqual=function(actual,expected,message){_deepEqual(actual,expected,!0)||fail(actual,expected,message,"deepStrictEqual",assert.deepStrictEqual)},assert.notDeepEqual=function(actual,expected,message){_deepEqual(actual,expected,!1)&&fail(actual,expected,message,"notDeepEqual",assert.notDeepEqual)},assert.notDeepStrictEqual=notDeepStrictEqual,assert.strictEqual=function(actual,expected,message){actual!==expected&&fail(actual,expected,message,"===",assert.strictEqual)},assert.notStrictEqual=function(actual,expected,message){actual===expected&&fail(actual,expected,message,"!==",assert.notStrictEqual)},assert.throws=function(block,error,message){_throws(!0,block,error,message)},assert.doesNotThrow=function(block,error,message){_throws(!1,block,error,message)},assert.ifError=function(err){if(err)throw err};var objectKeys=Object.keys||function(obj){var keys=[];for(var key in obj)hasOwn.call(obj,key)&&keys.push(key);return keys}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"util/":244}],36:[function(require,module,exports){"use strict";var _codemirror2=function(obj){return obj&&obj.__esModule?obj:{default:obj}}(require("codemirror")),_graphqlLanguageServiceInterface=require("graphql-language-service-interface");_codemirror2.default.registerHelper("hint","graphql",function(editor,options){var schema=options.schema;if(schema){var cur=editor.getCursor(),token=editor.getTokenAt(cur),rawResults=(0,_graphqlLanguageServiceInterface.getAutocompleteSuggestions)(schema,editor.getValue(),cur,token),tokenStart=null!==token.type&&/"|\w/.test(token.string[0])?token.start:token.end,results={list:rawResults.map(function(item){return{text:item.label,type:schema.getType(item.detail),description:item.documentation,isDeprecated:item.isDeprecated,deprecationReason:item.deprecationReason}}),from:{line:cur.line,column:tokenStart},to:{line:cur.line,column:token.end}};return results&&results.list&&results.list.length>0&&(results.from=_codemirror2.default.Pos(results.from.line,results.from.column),results.to=_codemirror2.default.Pos(results.to.line,results.to.column),_codemirror2.default.signal(editor,"hasCompletion",editor,results,token)),results}})},{codemirror:65,"graphql-language-service-interface":76}],37:[function(require,module,exports){"use strict";function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function renderField(into,typeInfo,options){renderQualifiedField(into,typeInfo,options),renderTypeAnnotation(into,typeInfo,options,typeInfo.type)}function renderQualifiedField(into,typeInfo,options){var fieldName=typeInfo.fieldDef.name;"__"!==fieldName.slice(0,2)&&(renderType(into,typeInfo,options,typeInfo.parentType),text(into,".")),text(into,fieldName,"field-name",options,(0,_SchemaReference.getFieldReference)(typeInfo))}function renderDirective(into,typeInfo,options){text(into,"@"+typeInfo.directiveDef.name,"directive-name",options,(0,_SchemaReference.getDirectiveReference)(typeInfo))}function renderArg(into,typeInfo,options){typeInfo.directiveDef?renderDirective(into,typeInfo,options):typeInfo.fieldDef&&renderQualifiedField(into,typeInfo,options);var name=typeInfo.argDef.name;text(into,"("),text(into,name,"arg-name",options,(0,_SchemaReference.getArgumentReference)(typeInfo)),renderTypeAnnotation(into,typeInfo,options,typeInfo.inputType),text(into,")")}function renderTypeAnnotation(into,typeInfo,options,t){text(into,": "),renderType(into,typeInfo,options,t)}function renderEnumValue(into,typeInfo,options){var name=typeInfo.enumValue.name;renderType(into,typeInfo,options,typeInfo.inputType),text(into,"."),text(into,name,"enum-value",options,(0,_SchemaReference.getEnumValueReference)(typeInfo))}function renderType(into,typeInfo,options,t){t instanceof _graphql.GraphQLNonNull?(renderType(into,typeInfo,options,t.ofType),text(into,"!")):t instanceof _graphql.GraphQLList?(text(into,"["),renderType(into,typeInfo,options,t.ofType),text(into,"]")):text(into,t.name,"type-name",options,(0,_SchemaReference.getTypeReference)(typeInfo,t))}function renderDescription(into,options,def){var description=def.description;if(description){var descriptionDiv=document.createElement("div");descriptionDiv.className="info-description",options.renderDescription?descriptionDiv.innerHTML=options.renderDescription(description):descriptionDiv.appendChild(document.createTextNode(description)),into.appendChild(descriptionDiv)}renderDeprecation(into,options,def)}function renderDeprecation(into,options,def){var reason=def.deprecationReason;if(reason){var deprecationDiv=document.createElement("div");deprecationDiv.className="info-deprecation",options.renderDescription?deprecationDiv.innerHTML=options.renderDescription(reason):deprecationDiv.appendChild(document.createTextNode(reason));var label=document.createElement("span");label.className="info-deprecation-label",label.appendChild(document.createTextNode("Deprecated: ")),deprecationDiv.insertBefore(label,deprecationDiv.firstChild),into.appendChild(deprecationDiv)}}function text(into,content,className,options,ref){if(className){var onClick=options.onClick,node=document.createElement(onClick?"a":"span");onClick&&(node.href="javascript:void 0",node.addEventListener("click",function(e){onClick(ref,e)})),node.className=className,node.appendChild(document.createTextNode(content)),into.appendChild(node)}else into.appendChild(document.createTextNode(content))}var _graphql=require("graphql"),_codemirror2=_interopRequireDefault(require("codemirror")),_getTypeInfo2=_interopRequireDefault(require("./utils/getTypeInfo")),_SchemaReference=require("./utils/SchemaReference");require("./utils/info-addon"),_codemirror2.default.registerHelper("info","graphql",function(token,options){if(options.schema&&token.state){var state=token.state,kind=state.kind,step=state.step,typeInfo=(0,_getTypeInfo2.default)(options.schema,token.state);if("Field"===kind&&0===step&&typeInfo.fieldDef||"AliasedField"===kind&&2===step&&typeInfo.fieldDef){var into=document.createElement("div");return renderField(into,typeInfo,options),renderDescription(into,options,typeInfo.fieldDef),into}if("Directive"===kind&&1===step&&typeInfo.directiveDef){var _into=document.createElement("div");return renderDirective(_into,typeInfo,options),renderDescription(_into,options,typeInfo.directiveDef),_into}if("Argument"===kind&&0===step&&typeInfo.argDef){var _into2=document.createElement("div");return renderArg(_into2,typeInfo,options),renderDescription(_into2,options,typeInfo.argDef),_into2}if("EnumValue"===kind&&typeInfo.enumValue&&typeInfo.enumValue.description){var _into3=document.createElement("div");return renderEnumValue(_into3,typeInfo,options),renderDescription(_into3,options,typeInfo.enumValue),_into3}if("NamedType"===kind&&typeInfo.type&&typeInfo.type.description){var _into4=document.createElement("div");return renderType(_into4,typeInfo,options,typeInfo.type),renderDescription(_into4,options,typeInfo.type),_into4}}})},{"./utils/SchemaReference":42,"./utils/getTypeInfo":44,"./utils/info-addon":46,codemirror:65,graphql:95}],38:[function(require,module,exports){"use strict";function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}var _codemirror2=_interopRequireDefault(require("codemirror")),_getTypeInfo2=_interopRequireDefault(require("./utils/getTypeInfo")),_SchemaReference=require("./utils/SchemaReference");require("./utils/jump-addon"),_codemirror2.default.registerHelper("jump","graphql",function(token,options){if(options.schema&&options.onClick&&token.state){var state=token.state,kind=state.kind,step=state.step,typeInfo=(0,_getTypeInfo2.default)(options.schema,state);return"Field"===kind&&0===step&&typeInfo.fieldDef||"AliasedField"===kind&&2===step&&typeInfo.fieldDef?(0,_SchemaReference.getFieldReference)(typeInfo):"Directive"===kind&&1===step&&typeInfo.directiveDef?(0,_SchemaReference.getDirectiveReference)(typeInfo):"Argument"===kind&&0===step&&typeInfo.argDef?(0,_SchemaReference.getArgumentReference)(typeInfo):"EnumValue"===kind&&typeInfo.enumValue?(0,_SchemaReference.getEnumValueReference)(typeInfo):"NamedType"===kind&&typeInfo.type?(0,_SchemaReference.getTypeReference)(typeInfo):void 0}})},{"./utils/SchemaReference":42,"./utils/getTypeInfo":44,"./utils/jump-addon":48,codemirror:65}],39:[function(require,module,exports){"use strict";var _codemirror2=function(obj){return obj&&obj.__esModule?obj:{default:obj}}(require("codemirror")),_graphqlLanguageServiceInterface=require("graphql-language-service-interface"),SEVERITY=["error","warning","information","hint"],TYPE={"GraphQL: Validation":"validation","GraphQL: Deprecation":"deprecation","GraphQL: Syntax":"syntax"};_codemirror2.default.registerHelper("lint","graphql",function(text,options){var schema=options.schema;return(0,_graphqlLanguageServiceInterface.getDiagnostics)(text,schema).map(function(error){return{message:error.message,severity:SEVERITY[error.severity-1],type:TYPE[error.source],from:_codemirror2.default.Pos(error.range.start.line,error.range.start.character),to:_codemirror2.default.Pos(error.range.end.line,error.range.end.character)}})})},{codemirror:65,"graphql-language-service-interface":76}],40:[function(require,module,exports){"use strict";function indent(state,textAfter){var levels=state.levels;return(levels&&0!==levels.length?levels[levels.length-1]-(this.electricInput.test(textAfter)?1:0):state.indentLevel)*this.config.indentUnit}var _codemirror2=function(obj){return obj&&obj.__esModule?obj:{default:obj}}(require("codemirror")),_graphqlLanguageServiceParser=require("graphql-language-service-parser");_codemirror2.default.defineMode("graphql",function(config){var parser=(0,_graphqlLanguageServiceParser.onlineParser)({eatWhitespace:function(stream){return stream.eatWhile(_graphqlLanguageServiceParser.isIgnored)},lexRules:_graphqlLanguageServiceParser.LexRules,parseRules:_graphqlLanguageServiceParser.ParseRules,editorConfig:{tabSize:config.tabSize}});return{config:config,startState:parser.startState,token:parser.token,indent:indent,electricInput:/^\s*[})\]]/,fold:"brace",lineComment:"#",closeBrackets:{pairs:'()[]{}""',explode:"()[]{}"}}})},{codemirror:65,"graphql-language-service-parser":80}],41:[function(require,module,exports){"use strict";function indent(state,textAfter){var levels=state.levels;return(levels&&0!==levels.length?levels[levels.length-1]-(this.electricInput.test(textAfter)?1:0):state.indentLevel)*this.config.indentUnit}var _codemirror2=function(obj){return obj&&obj.__esModule?obj:{default:obj}}(require("codemirror")),_graphqlLanguageServiceParser=require("graphql-language-service-parser");_codemirror2.default.defineMode("graphql-results",function(config){var parser=(0,_graphqlLanguageServiceParser.onlineParser)({eatWhitespace:function(stream){return stream.eatSpace()},lexRules:LexRules,parseRules:ParseRules,editorConfig:{tabSize:config.tabSize}});return{config:config,startState:parser.startState,token:parser.token,indent:indent,electricInput:/^\s*[}\]]/,fold:"brace",closeBrackets:{pairs:'[]{}""',explode:"[]{}"}}});var LexRules={Punctuation:/^\[|]|\{|\}|:|,/,Number:/^-?(?:0|(?:[1-9][0-9]*))(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?/,String:/^"(?:[^"\\]|\\(?:"|\/|\\|b|f|n|r|t|u[0-9a-fA-F]{4}))*"?/,Keyword:/^true|false|null/},ParseRules={Document:[(0,_graphqlLanguageServiceParser.p)("{"),(0,_graphqlLanguageServiceParser.list)("Entry",(0,_graphqlLanguageServiceParser.p)(",")),(0,_graphqlLanguageServiceParser.p)("}")],Entry:[(0,_graphqlLanguageServiceParser.t)("String","def"),(0,_graphqlLanguageServiceParser.p)(":"),"Value"],Value:function(token){switch(token.kind){case"Number":return"NumberValue";case"String":return"StringValue";case"Punctuation":switch(token.value){case"[":return"ListValue";case"{":return"ObjectValue"}return null;case"Keyword":switch(token.value){case"true":case"false":return"BooleanValue";case"null":return"NullValue"}return null}},NumberValue:[(0,_graphqlLanguageServiceParser.t)("Number","number")],StringValue:[(0,_graphqlLanguageServiceParser.t)("String","string")],BooleanValue:[(0,_graphqlLanguageServiceParser.t)("Keyword","builtin")],NullValue:[(0,_graphqlLanguageServiceParser.t)("Keyword","keyword")],ListValue:[(0,_graphqlLanguageServiceParser.p)("["),(0,_graphqlLanguageServiceParser.list)("Value",(0,_graphqlLanguageServiceParser.p)(",")),(0,_graphqlLanguageServiceParser.p)("]")],ObjectValue:[(0,_graphqlLanguageServiceParser.p)("{"),(0,_graphqlLanguageServiceParser.list)("ObjectField",(0,_graphqlLanguageServiceParser.p)(",")),(0,_graphqlLanguageServiceParser.p)("}")],ObjectField:[(0,_graphqlLanguageServiceParser.t)("String","property"),(0,_graphqlLanguageServiceParser.p)(":"),"Value"]}},{codemirror:65,"graphql-language-service-parser":80}],42:[function(require,module,exports){"use strict";function isMetaField(fieldDef){return"__"===fieldDef.name.slice(0,2)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getFieldReference=function(typeInfo){return{kind:"Field",schema:typeInfo.schema,field:typeInfo.fieldDef,type:isMetaField(typeInfo.fieldDef)?null:typeInfo.parentType}},exports.getDirectiveReference=function(typeInfo){return{kind:"Directive",schema:typeInfo.schema,directive:typeInfo.directiveDef}},exports.getArgumentReference=function(typeInfo){return typeInfo.directiveDef?{kind:"Argument",schema:typeInfo.schema,argument:typeInfo.argDef,directive:typeInfo.directiveDef}:{kind:"Argument",schema:typeInfo.schema,argument:typeInfo.argDef,field:typeInfo.fieldDef,type:isMetaField(typeInfo.fieldDef)?null:typeInfo.parentType}},exports.getEnumValueReference=function(typeInfo){return{kind:"EnumValue",value:typeInfo.enumValue,type:(0,_graphql.getNamedType)(typeInfo.inputType)}},exports.getTypeReference=function(typeInfo,type){return{kind:"Type",schema:typeInfo.schema,type:type||typeInfo.type}};var _graphql=require("graphql")},{graphql:95}],43:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=function(stack,fn){for(var reverseStateStack=[],state=stack;state&&state.kind;)reverseStateStack.push(state),state=state.prevState;for(var i=reverseStateStack.length-1;i>=0;i--)fn(reverseStateStack[i])}},{}],44:[function(require,module,exports){"use strict";function getFieldDef(schema,type,fieldName){return fieldName===_introspection.SchemaMetaFieldDef.name&&schema.getQueryType()===type?_introspection.SchemaMetaFieldDef:fieldName===_introspection.TypeMetaFieldDef.name&&schema.getQueryType()===type?_introspection.TypeMetaFieldDef:fieldName===_introspection.TypeNameMetaFieldDef.name&&(0,_graphql.isCompositeType)(type)?_introspection.TypeNameMetaFieldDef:type.getFields?type.getFields()[fieldName]:void 0}function find(array,predicate){for(var i=0;itext.length&&(proximity-=suggestion.length-text.length-1,proximity+=0===suggestion.indexOf(text)?0:.5),proximity}function lexicalDistance(a,b){var i=void 0,j=void 0,d=[],aLength=a.length,bLength=b.length;for(i=0;i<=aLength;i++)d[i]=[i];for(j=1;j<=bLength;j++)d[0][j]=j;for(i=1;i<=aLength;i++)for(j=1;j<=bLength;j++){var cost=a[i-1]===b[j-1]?0:1;d[i][j]=Math.min(d[i-1][j]+1,d[i][j-1]+1,d[i-1][j-1]+cost),i>1&&j>1&&a[i-1]===b[j-2]&&a[i-2]===b[j-1]&&(d[i][j]=Math.min(d[i][j],d[i-2][j-2]+cost))}return d[aLength][bLength]}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=function(cursor,token,list){var hints=filterAndSortList(list,normalizeText(token.string));if(hints){var tokenStart=null!==token.type&&/"|\w/.test(token.string[0])?token.start:token.end;return{list:hints,from:{line:cursor.line,column:tokenStart},to:{line:cursor.line,column:token.end}}}}},{}],46:[function(require,module,exports){"use strict";function createState(options){return{options:options instanceof Function?{render:options}:!0===options?{}:options}}function getHoverTime(cm){var options=cm.state.info.options;return options&&options.hoverTime||500}function onMouseOver(cm,e){var state=cm.state.info,target=e.target||e.srcElement;if("SPAN"===target.nodeName&&void 0===state.hoverTimeout){var box=target.getBoundingClientRect(),hoverTime=getHoverTime(cm);state.hoverTimeout=setTimeout(onHover,hoverTime);var onMouseMove=function(){clearTimeout(state.hoverTimeout),state.hoverTimeout=setTimeout(onHover,hoverTime)},onMouseOut=function onMouseOut(){_codemirror2.default.off(document,"mousemove",onMouseMove),_codemirror2.default.off(cm.getWrapperElement(),"mouseout",onMouseOut),clearTimeout(state.hoverTimeout),state.hoverTimeout=void 0},onHover=function(){_codemirror2.default.off(document,"mousemove",onMouseMove),_codemirror2.default.off(cm.getWrapperElement(),"mouseout",onMouseOut),state.hoverTimeout=void 0,onMouseHover(cm,box)};_codemirror2.default.on(document,"mousemove",onMouseMove),_codemirror2.default.on(cm.getWrapperElement(),"mouseout",onMouseOut)}}function onMouseHover(cm,box){var pos=cm.coordsChar({left:(box.left+box.right)/2,top:(box.top+box.bottom)/2}),options=cm.state.info.options,render=options.render||cm.getHelper(pos,"info");if(render){var token=cm.getTokenAt(pos,!0);if(token){var info=render(token,options,cm);info&&showPopup(cm,box,info)}}}function showPopup(cm,box,info){var popup=document.createElement("div");popup.className="CodeMirror-info",popup.appendChild(info),document.body.appendChild(popup);var popupBox=popup.getBoundingClientRect(),popupStyle=popup.currentStyle||window.getComputedStyle(popup),popupWidth=popupBox.right-popupBox.left+parseFloat(popupStyle.marginLeft)+parseFloat(popupStyle.marginRight),popupHeight=popupBox.bottom-popupBox.top+parseFloat(popupStyle.marginTop)+parseFloat(popupStyle.marginBottom),topPos=box.bottom;popupHeight>window.innerHeight-box.bottom-15&&box.top>window.innerHeight-box.bottom&&(topPos=box.top-popupHeight),topPos<0&&(topPos=box.bottom);var leftPos=Math.max(0,window.innerWidth-popupWidth-15);leftPos>box.left&&(leftPos=box.left),popup.style.opacity=1,popup.style.top=topPos+"px",popup.style.left=leftPos+"px";var popupTimeout=void 0,onMouseOverPopup=function(){clearTimeout(popupTimeout)},onMouseOut=function(){clearTimeout(popupTimeout),popupTimeout=setTimeout(hidePopup,200)},hidePopup=function(){_codemirror2.default.off(popup,"mouseover",onMouseOverPopup),_codemirror2.default.off(popup,"mouseout",onMouseOut),_codemirror2.default.off(cm.getWrapperElement(),"mouseout",onMouseOut),popup.style.opacity?(popup.style.opacity=0,setTimeout(function(){popup.parentNode&&popup.parentNode.removeChild(popup)},600)):popup.parentNode&&popup.parentNode.removeChild(popup)};_codemirror2.default.on(popup,"mouseover",onMouseOverPopup),_codemirror2.default.on(popup,"mouseout",onMouseOut),_codemirror2.default.on(cm.getWrapperElement(),"mouseout",onMouseOut)}var _codemirror2=function(obj){return obj&&obj.__esModule?obj:{default:obj}}(require("codemirror"));_codemirror2.default.defineOption("info",!1,function(cm,options,old){if(old&&old!==_codemirror2.default.Init){var oldOnMouseOver=cm.state.info.onMouseOver;_codemirror2.default.off(cm.getWrapperElement(),"mouseover",oldOnMouseOver),clearTimeout(cm.state.info.hoverTimeout),delete cm.state.info}if(options){var state=cm.state.info=createState(options);state.onMouseOver=onMouseOver.bind(null,cm),_codemirror2.default.on(cm.getWrapperElement(),"mouseover",state.onMouseOver)}})},{codemirror:65}],47:[function(require,module,exports){"use strict";function parseObj(){var nodeStart=start,members=[];if(expect("{"),!skip("}")){do{members.push(parseMember())}while(skip(","));expect("}")}return{kind:"Object",start:nodeStart,end:lastEnd,members:members}}function parseMember(){var nodeStart=start,key="String"===kind?curToken():null;expect("String"),expect(":");var value=parseVal();return{kind:"Member",start:nodeStart,end:lastEnd,key:key,value:value}}function parseArr(){var nodeStart=start,values=[];if(expect("["),!skip("]")){do{values.push(parseVal())}while(skip(","));expect("]")}return{kind:"Array",start:nodeStart,end:lastEnd,values:values}}function parseVal(){switch(kind){case"[":return parseArr();case"{":return parseObj();case"String":case"Number":case"Boolean":case"Null":var token=curToken();return lex(),token}return expect("Value")}function curToken(){return{kind:kind,start:start,end:end,value:JSON.parse(string.slice(start,end))}}function expect(str){if(kind!==str){var found=void 0;if("EOF"===kind)found="[end of file]";else if(end-start>1)found="`"+string.slice(start,end)+"`";else{var match=string.slice(start).match(/^.+?\b/);found="`"+(match?match[0]:string[start])+"`"}throw syntaxError("Expected "+str+" but found "+found+".")}lex()}function syntaxError(message){return{message:message,start:start,end:end}}function skip(k){if(kind===k)return lex(),!0}function ch(){end31;)if(92===code)switch(ch(),code){case 34:case 47:case 92:case 98:case 102:case 110:case 114:case 116:ch();break;case 117:ch(),readHex(),readHex(),readHex(),readHex();break;default:throw syntaxError("Bad character escape sequence.")}else{if(end===strLen)throw syntaxError("Unterminated string.");ch()}if(34!==code)throw syntaxError("Unterminated string.");ch()}function readHex(){if(code>=48&&code<=57||code>=65&&code<=70||code>=97&&code<=102)return ch();throw syntaxError("Expected hexadecimal digit.")}function readNumber(){45===code&&ch(),48===code?ch():readDigits(),46===code&&(ch(),readDigits()),69!==code&&101!==code||(ch(),43!==code&&45!==code||ch(),readDigits())}function readDigits(){if(code<48||code>57)throw syntaxError("Expected decimal digit.");do{ch()}while(code>=48&&code<=57)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=function(str){string=str,strLen=str.length,start=end=lastEnd=-1,ch(),lex();var ast=parseObj();return expect("EOF"),ast};var string=void 0,strLen=void 0,start=void 0,end=void 0,lastEnd=void 0,code=void 0,kind=void 0},{}],48:[function(require,module,exports){"use strict";function onMouseOver(cm,event){var target=event.target||event.srcElement;if("SPAN"===target.nodeName){var box=target.getBoundingClientRect(),cursor={left:(box.left+box.right)/2,top:(box.top+box.bottom)/2};cm.state.jump.cursor=cursor,cm.state.jump.isHoldingModifier&&enableJumpMode(cm)}}function onMouseOut(cm){cm.state.jump.isHoldingModifier||!cm.state.jump.cursor?cm.state.jump.isHoldingModifier&&cm.state.jump.marker&&disableJumpMode(cm):cm.state.jump.cursor=null}function onKeyDown(cm,event){if(!cm.state.jump.isHoldingModifier&&isJumpModifier(event.key)){cm.state.jump.isHoldingModifier=!0,cm.state.jump.cursor&&enableJumpMode(cm);var onClick=function(clickEvent){var destination=cm.state.jump.destination;destination&&cm.state.jump.options.onClick(destination,clickEvent)},onMouseDown=function(_,downEvent){cm.state.jump.destination&&(downEvent.codemirrorIgnore=!0)};_codemirror2.default.on(document,"keyup",function onKeyUp(upEvent){upEvent.code===event.code&&(cm.state.jump.isHoldingModifier=!1,cm.state.jump.marker&&disableJumpMode(cm),_codemirror2.default.off(document,"keyup",onKeyUp),_codemirror2.default.off(document,"click",onClick),cm.off("mousedown",onMouseDown))}),_codemirror2.default.on(document,"click",onClick),cm.on("mousedown",onMouseDown)}}function isJumpModifier(key){return key===(isMac?"Meta":"Control")}function enableJumpMode(cm){if(!cm.state.jump.marker){var cursor=cm.state.jump.cursor,pos=cm.coordsChar(cursor),token=cm.getTokenAt(pos,!0),options=cm.state.jump.options,getDestination=options.getDestination||cm.getHelper(pos,"jump");if(getDestination){var destination=getDestination(token,options,cm);if(destination){var marker=cm.markText({line:pos.line,ch:token.start},{line:pos.line,ch:token.end},{className:"CodeMirror-jump-token"});cm.state.jump.marker=marker,cm.state.jump.destination=destination}}}}function disableJumpMode(cm){var marker=cm.state.jump.marker;cm.state.jump.marker=null,cm.state.jump.destination=null,marker.clear()}var _codemirror2=function(obj){return obj&&obj.__esModule?obj:{default:obj}}(require("codemirror"));_codemirror2.default.defineOption("jump",!1,function(cm,options,old){if(old&&old!==_codemirror2.default.Init){var oldOnMouseOver=cm.state.jump.onMouseOver;_codemirror2.default.off(cm.getWrapperElement(),"mouseover",oldOnMouseOver);var oldOnMouseOut=cm.state.jump.onMouseOut;_codemirror2.default.off(cm.getWrapperElement(),"mouseout",oldOnMouseOut),_codemirror2.default.off(document,"keydown",cm.state.jump.onKeyDown),delete cm.state.jump}if(options){var state=cm.state.jump={options:options,onMouseOver:onMouseOver.bind(null,cm),onMouseOut:onMouseOut.bind(null,cm),onKeyDown:onKeyDown.bind(null,cm)};_codemirror2.default.on(cm.getWrapperElement(),"mouseover",state.onMouseOver),_codemirror2.default.on(cm.getWrapperElement(),"mouseout",state.onMouseOut),_codemirror2.default.on(document,"keydown",state.onKeyDown)}});var isMac=navigator&&-1!==navigator.appVersion.indexOf("Mac")},{codemirror:65}],49:[function(require,module,exports){"use strict";function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function getVariablesHint(cur,token,options){var state="Invalid"===token.state.kind?token.state.prevState:token.state,kind=state.kind,step=state.step;if("Document"===kind&&0===step)return(0,_hintList2.default)(cur,token,[{text:"{"}]);var variableToType=options.variableToType;if(variableToType){var typeInfo=getTypeInfo(variableToType,token.state);if("Document"===kind||"Variable"===kind&&0===step){var variableNames=Object.keys(variableToType);return(0,_hintList2.default)(cur,token,variableNames.map(function(name){return{text:'"'+name+'": ',type:variableToType[name]}}))}if(("ObjectValue"===kind||"ObjectField"===kind&&0===step)&&typeInfo.fields){var inputFields=Object.keys(typeInfo.fields).map(function(fieldName){return typeInfo.fields[fieldName]});return(0,_hintList2.default)(cur,token,inputFields.map(function(field){return{text:'"'+field.name+'": ',type:field.type,description:field.description}}))}if("StringValue"===kind||"NumberValue"===kind||"BooleanValue"===kind||"NullValue"===kind||"ListValue"===kind&&1===step||"ObjectField"===kind&&2===step||"Variable"===kind&&2===step){var namedInputType=(0,_graphql.getNamedType)(typeInfo.type);if(namedInputType instanceof _graphql.GraphQLInputObjectType)return(0,_hintList2.default)(cur,token,[{text:"{"}]);if(namedInputType instanceof _graphql.GraphQLEnumType){var valueMap=namedInputType.getValues(),values=Object.keys(valueMap).map(function(name){return valueMap[name]});return(0,_hintList2.default)(cur,token,values.map(function(value){return{text:'"'+value.name+'"',type:namedInputType,description:value.description}}))}if(namedInputType===_graphql.GraphQLBoolean)return(0,_hintList2.default)(cur,token,[{text:"true",type:_graphql.GraphQLBoolean,description:"Not false."},{text:"false",type:_graphql.GraphQLBoolean,description:"Not true."}])}}}function getTypeInfo(variableToType,tokenState){var info={type:null,fields:null};return(0,_forEachState2.default)(tokenState,function(state){if("Variable"===state.kind)info.type=variableToType[state.name];else if("ListValue"===state.kind){var nullableType=(0,_graphql.getNullableType)(info.type);info.type=nullableType instanceof _graphql.GraphQLList?nullableType.ofType:null}else if("ObjectValue"===state.kind){var objectType=(0,_graphql.getNamedType)(info.type);info.fields=objectType instanceof _graphql.GraphQLInputObjectType?objectType.getFields():null}else if("ObjectField"===state.kind){var objectField=state.name&&info.fields?info.fields[state.name]:null;info.type=objectField&&objectField.type}}),info}var _codemirror2=_interopRequireDefault(require("codemirror")),_graphql=require("graphql"),_forEachState2=_interopRequireDefault(require("../utils/forEachState")),_hintList2=_interopRequireDefault(require("../utils/hintList"));_codemirror2.default.registerHelper("hint","graphql-variables",function(editor,options){var cur=editor.getCursor(),token=editor.getTokenAt(cur),results=getVariablesHint(cur,token,options);return results&&results.list&&results.list.length>0&&(results.from=_codemirror2.default.Pos(results.from.line,results.from.column),results.to=_codemirror2.default.Pos(results.to.line,results.to.column),_codemirror2.default.signal(editor,"hasCompletion",editor,results,token)),results})},{"../utils/forEachState":43,"../utils/hintList":45,codemirror:65,graphql:95}],50:[function(require,module,exports){"use strict";function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function validateVariables(editor,variableToType,variablesAST){var errors=[];return variablesAST.members.forEach(function(member){var variableName=member.key.value,type=variableToType[variableName];type?validateValue(type,member.value).forEach(function(_ref){var node=_ref[0],message=_ref[1];errors.push(lintError(editor,node,message))}):errors.push(lintError(editor,member.key,'Variable "$'+variableName+'" does not appear in any GraphQL query.'))}),errors}function validateValue(type,valueAST){if(type instanceof _graphql.GraphQLNonNull)return"Null"===valueAST.kind?[[valueAST,'Type "'+type+'" is non-nullable and cannot be null.']]:validateValue(type.ofType,valueAST);if("Null"===valueAST.kind)return[];if(type instanceof _graphql.GraphQLList){var itemType=type.ofType;return"Array"===valueAST.kind?mapCat(valueAST.values,function(item){return validateValue(itemType,item)}):validateValue(itemType,valueAST)}if(type instanceof _graphql.GraphQLInputObjectType){if("Object"!==valueAST.kind)return[[valueAST,'Type "'+type+'" must be an Object.']];var providedFields=Object.create(null),fieldErrors=mapCat(valueAST.members,function(member){var fieldName=member.key.value;providedFields[fieldName]=!0;var inputField=type.getFields()[fieldName];return inputField?validateValue(inputField?inputField.type:void 0,member.value):[[member.key,'Type "'+type+'" does not have a field "'+fieldName+'".']]});return Object.keys(type.getFields()).forEach(function(fieldName){providedFields[fieldName]||type.getFields()[fieldName].type instanceof _graphql.GraphQLNonNull&&fieldErrors.push([valueAST,'Object of type "'+type+'" is missing required field "'+fieldName+'".'])}),fieldErrors}return"Boolean"===type.name&&"Boolean"!==valueAST.kind||"String"===type.name&&"String"!==valueAST.kind||"ID"===type.name&&"Number"!==valueAST.kind&&"String"!==valueAST.kind||"Float"===type.name&&"Number"!==valueAST.kind||"Int"===type.name&&("Number"!==valueAST.kind||(0|valueAST.value)!==valueAST.value)?[[valueAST,'Expected value of type "'+type+'".']]:(type instanceof _graphql.GraphQLEnumType||type instanceof _graphql.GraphQLScalarType)&&("String"!==valueAST.kind&&"Number"!==valueAST.kind&&"Boolean"!==valueAST.kind&&"Null"!==valueAST.kind||isNullish(type.parseValue(valueAST.value)))?[[valueAST,'Expected value of type "'+type+'".']]:[]}function lintError(editor,node,message){return{message:message,severity:"error",type:"validation",from:editor.posFromIndex(node.start),to:editor.posFromIndex(node.end)}}function isNullish(value){return null===value||void 0===value||value!==value}function mapCat(array,mapper){return Array.prototype.concat.apply([],array.map(mapper))}var _codemirror2=_interopRequireDefault(require("codemirror")),_graphql=require("graphql"),_jsonParse2=_interopRequireDefault(require("../utils/jsonParse"));_codemirror2.default.registerHelper("lint","graphql-variables",function(text,options,editor){if(!text)return[];var ast=void 0;try{ast=(0,_jsonParse2.default)(text)}catch(syntaxError){if(syntaxError.stack)throw syntaxError;return[lintError(editor,syntaxError,syntaxError.message)]}var variableToType=options.variableToType;return variableToType?validateVariables(editor,variableToType,ast):[]})},{"../utils/jsonParse":47,codemirror:65,graphql:95}],51:[function(require,module,exports){"use strict";function indent(state,textAfter){var levels=state.levels;return(levels&&0!==levels.length?levels[levels.length-1]-(this.electricInput.test(textAfter)?1:0):state.indentLevel)*this.config.indentUnit}function namedKey(style){return{style:style,match:function(token){return"String"===token.kind},update:function(state,token){state.name=token.value.slice(1,-1)}}}var _codemirror2=function(obj){return obj&&obj.__esModule?obj:{default:obj}}(require("codemirror")),_graphqlLanguageServiceParser=require("graphql-language-service-parser");_codemirror2.default.defineMode("graphql-variables",function(config){var parser=(0,_graphqlLanguageServiceParser.onlineParser)({eatWhitespace:function(stream){return stream.eatSpace()},lexRules:LexRules,parseRules:ParseRules,editorConfig:{tabSize:config.tabSize}});return{config:config,startState:parser.startState,token:parser.token,indent:indent,electricInput:/^\s*[}\]]/,fold:"brace",closeBrackets:{pairs:'[]{}""',explode:"[]{}"}}});var LexRules={Punctuation:/^\[|]|\{|\}|:|,/,Number:/^-?(?:0|(?:[1-9][0-9]*))(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?/,String:/^"(?:[^"\\]|\\(?:"|\/|\\|b|f|n|r|t|u[0-9a-fA-F]{4}))*"?/,Keyword:/^true|false|null/},ParseRules={Document:[(0,_graphqlLanguageServiceParser.p)("{"),(0,_graphqlLanguageServiceParser.list)("Variable",(0,_graphqlLanguageServiceParser.opt)((0,_graphqlLanguageServiceParser.p)(","))),(0,_graphqlLanguageServiceParser.p)("}")],Variable:[namedKey("variable"),(0,_graphqlLanguageServiceParser.p)(":"),"Value"],Value:function(token){switch(token.kind){case"Number":return"NumberValue";case"String":return"StringValue";case"Punctuation":switch(token.value){case"[":return"ListValue";case"{":return"ObjectValue"}return null;case"Keyword":switch(token.value){case"true":case"false":return"BooleanValue";case"null":return"NullValue"}return null}},NumberValue:[(0,_graphqlLanguageServiceParser.t)("Number","number")],StringValue:[(0,_graphqlLanguageServiceParser.t)("String","string")],BooleanValue:[(0,_graphqlLanguageServiceParser.t)("Keyword","builtin")],NullValue:[(0,_graphqlLanguageServiceParser.t)("Keyword","keyword")],ListValue:[(0,_graphqlLanguageServiceParser.p)("["),(0,_graphqlLanguageServiceParser.list)("Value",(0,_graphqlLanguageServiceParser.opt)((0,_graphqlLanguageServiceParser.p)(","))),(0,_graphqlLanguageServiceParser.p)("]")],ObjectValue:[(0,_graphqlLanguageServiceParser.p)("{"),(0,_graphqlLanguageServiceParser.list)("ObjectField",(0,_graphqlLanguageServiceParser.opt)((0,_graphqlLanguageServiceParser.p)(","))),(0,_graphqlLanguageServiceParser.p)("}")],ObjectField:[namedKey("attribute"),(0,_graphqlLanguageServiceParser.p)(":"),"Value"]}},{codemirror:65,"graphql-language-service-parser":80}],52:[function(require,module,exports){!function(mod){mod("object"==typeof exports&&"object"==typeof module?require("../../lib/codemirror"):CodeMirror)}(function(CodeMirror){"use strict";function firstNonWS(str){var found=str.search(nonWS);return-1==found?0:found}function probablyInsideString(cm,pos,line){return/\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line,0)))&&!/^[\'\"\`]/.test(line)}function getMode(cm,pos){var mode=cm.getMode();return!1!==mode.useInnerComments&&mode.innerMode?cm.getModeAt(pos):mode}var noOptions={},nonWS=/[^\s\u00a0]/,Pos=CodeMirror.Pos;CodeMirror.commands.toggleComment=function(cm){cm.toggleComment()},CodeMirror.defineExtension("toggleComment",function(options){options||(options=noOptions);for(var cm=this,minLine=1/0,ranges=this.listSelections(),mode=null,i=ranges.length-1;i>=0;i--){var from=ranges[i].from(),to=ranges[i].to();from.line>=minLine||(to.line>=minLine&&(to=Pos(minLine,0)),minLine=from.line,null==mode?cm.uncomment(from,to,options)?mode="un":(cm.lineComment(from,to,options),mode="line"):"un"==mode?cm.uncomment(from,to,options):cm.lineComment(from,to,options))}}),CodeMirror.defineExtension("lineComment",function(from,to,options){options||(options=noOptions);var self=this,mode=getMode(self,from),firstLine=self.getLine(from.line);if(null!=firstLine&&!probablyInsideString(self,from,firstLine)){var commentString=options.lineComment||mode.lineComment;if(commentString){var end=Math.min(0!=to.ch||to.line==from.line?to.line+1:to.line,self.lastLine()+1),pad=null==options.padding?" ":options.padding,blankLines=options.commentBlankLines||from.line==to.line;self.operation(function(){if(options.indent){for(var baseString=null,i=from.line;iwhitespace.length)&&(baseString=whitespace)}for(i=from.line;iend||self.operation(function(){if(0!=options.fullLines){var lastLineHasText=nonWS.test(self.getLine(end));self.replaceRange(pad+endString,Pos(end)),self.replaceRange(startString+pad,Pos(from.line,0));var lead=options.blockCommentLead||mode.blockCommentLead;if(null!=lead)for(var i=from.line+1;i<=end;++i)(i!=end||lastLineHasText)&&self.replaceRange(lead+pad,Pos(i,0))}else self.replaceRange(endString,to),self.replaceRange(startString,from)})}}else(options.lineComment||mode.lineComment)&&0!=options.fullLines&&self.lineComment(from,to,options)}),CodeMirror.defineExtension("uncomment",function(from,to,options){options||(options=noOptions);var didSomething,self=this,mode=getMode(self,from),end=Math.min(0!=to.ch||to.line==from.line?to.line:to.line-1,self.lastLine()),start=Math.min(from.line,end),lineString=options.lineComment||mode.lineComment,lines=[],pad=null==options.padding?" ":options.padding;lineComment:if(lineString){for(var i=start;i<=end;++i){var line=self.getLine(i),found=line.indexOf(lineString);if(found>-1&&!/comment/.test(self.getTokenTypeAt(Pos(i,found+1)))&&(found=-1),-1==found&&nonWS.test(line))break lineComment;if(found>-1&&nonWS.test(line.slice(0,found)))break lineComment;lines.push(line)}if(self.operation(function(){for(var i=start;i<=end;++i){var line=lines[i-start],pos=line.indexOf(lineString),endPos=pos+lineString.length;pos<0||(line.slice(endPos,endPos+pad.length)==pad&&(endPos+=pad.length),didSomething=!0,self.replaceRange("",Pos(i,pos),Pos(i,endPos)))}}),didSomething)return!0}var startString=options.blockCommentStart||mode.blockCommentStart,endString=options.blockCommentEnd||mode.blockCommentEnd;if(!startString||!endString)return!1;var lead=options.blockCommentLead||mode.blockCommentLead,startLine=self.getLine(start),open=startLine.indexOf(startString);if(-1==open)return!1;var endLine=end==start?startLine:self.getLine(end),close=endLine.indexOf(endString,end==start?open+startString.length:0);-1==close&&start!=end&&(endLine=self.getLine(--end),close=endLine.indexOf(endString));var insideStart=Pos(start,open+1),insideEnd=Pos(end,close+1);if(-1==close||!/comment/.test(self.getTokenTypeAt(insideStart))||!/comment/.test(self.getTokenTypeAt(insideEnd))||self.getRange(insideStart,insideEnd,"\n").indexOf(endString)>-1)return!1;var lastStart=startLine.lastIndexOf(startString,from.ch),firstEnd=-1==lastStart?-1:startLine.slice(0,from.ch).indexOf(endString,lastStart+startString.length);if(-1!=lastStart&&-1!=firstEnd&&firstEnd+endString.length!=from.ch)return!1;firstEnd=endLine.indexOf(endString,to.ch);var almostLastStart=endLine.slice(to.ch).lastIndexOf(startString,firstEnd-to.ch);return lastStart=-1==firstEnd||-1==almostLastStart?-1:to.ch+almostLastStart,(-1==firstEnd||-1==lastStart||lastStart==to.ch)&&(self.operation(function(){self.replaceRange("",Pos(end,close-(pad&&endLine.slice(close-pad.length,close)==pad?pad.length:0)),Pos(end,close+endString.length));var openEnd=open+startString.length;if(pad&&startLine.slice(openEnd,openEnd+pad.length)==pad&&(openEnd+=pad.length),self.replaceRange("",Pos(start,open),Pos(start,openEnd)),lead)for(var i=start+1;i<=end;++i){var line=self.getLine(i),found=line.indexOf(lead);if(-1!=found&&!nonWS.test(line.slice(0,found))){var foundEnd=found+lead.length;pad&&line.slice(foundEnd,foundEnd+pad.length)==pad&&(foundEnd+=pad.length),self.replaceRange("",Pos(i,found),Pos(i,foundEnd))}}}),!0)})})},{"../../lib/codemirror":65}],53:[function(require,module,exports){!function(mod){mod("object"==typeof exports&&"object"==typeof module?require("../../lib/codemirror"):CodeMirror)}(function(CodeMirror){function dialogDiv(cm,template,bottom){var dialog;return dialog=cm.getWrapperElement().appendChild(document.createElement("div")),dialog.className=bottom?"CodeMirror-dialog CodeMirror-dialog-bottom":"CodeMirror-dialog CodeMirror-dialog-top","string"==typeof template?dialog.innerHTML=template:dialog.appendChild(template),dialog}function closeNotification(cm,newVal){cm.state.currentNotificationClose&&cm.state.currentNotificationClose(),cm.state.currentNotificationClose=newVal}CodeMirror.defineExtension("openDialog",function(template,callback,options){function close(newVal){if("string"==typeof newVal)inp.value=newVal;else{if(closed)return;closed=!0,dialog.parentNode.removeChild(dialog),me.focus(),options.onClose&&options.onClose(dialog)}}options||(options={}),closeNotification(this,null);var button,dialog=dialogDiv(this,template,options.bottom),closed=!1,me=this,inp=dialog.getElementsByTagName("input")[0];return inp?(inp.focus(),options.value&&(inp.value=options.value,!1!==options.selectValueOnOpen&&inp.select()),options.onInput&&CodeMirror.on(inp,"input",function(e){options.onInput(e,inp.value,close)}),options.onKeyUp&&CodeMirror.on(inp,"keyup",function(e){options.onKeyUp(e,inp.value,close)}),CodeMirror.on(inp,"keydown",function(e){options&&options.onKeyDown&&options.onKeyDown(e,inp.value,close)||((27==e.keyCode||!1!==options.closeOnEnter&&13==e.keyCode)&&(inp.blur(),CodeMirror.e_stop(e),close()),13==e.keyCode&&callback(inp.value,e))}),!1!==options.closeOnBlur&&CodeMirror.on(inp,"blur",close)):(button=dialog.getElementsByTagName("button")[0])&&(CodeMirror.on(button,"click",function(){close(),me.focus()}),!1!==options.closeOnBlur&&CodeMirror.on(button,"blur",close),button.focus()),close}),CodeMirror.defineExtension("openConfirm",function(template,callbacks,options){function close(){closed||(closed=!0,dialog.parentNode.removeChild(dialog),me.focus())}closeNotification(this,null);var dialog=dialogDiv(this,template,options&&options.bottom),buttons=dialog.getElementsByTagName("button"),closed=!1,me=this,blurring=1;buttons[0].focus();for(var i=0;i0;return{anchor:new Pos(sel.anchor.line,sel.anchor.ch+(inverted?-1:1)),head:new Pos(sel.head.line,sel.head.ch+(inverted?1:-1))}}function handleChar(cm,ch){var conf=getConfig(cm);if(!conf||cm.getOption("disableInput"))return CodeMirror.Pass;var pairs=getOption(conf,"pairs"),pos=pairs.indexOf(ch);if(-1==pos)return CodeMirror.Pass;for(var type,triples=getOption(conf,"triples"),identical=pairs.charAt(pos+1)==ch,ranges=cm.listSelections(),opening=pos%2==0,i=0;i1&&triples.indexOf(ch)>=0&&cm.getRange(Pos(cur.line,cur.ch-2),cur)==ch+ch&&(cur.ch<=2||cm.getRange(Pos(cur.line,cur.ch-3),Pos(cur.line,cur.ch-2))!=ch))curType="addFour";else if(identical){if(CodeMirror.isWordChar(next)||!enteringString(cm,cur,ch))return CodeMirror.Pass;curType="both"}else{if(!opening||cm.getLine(cur.line).length!=cur.ch&&!isClosingBracket(next,pairs)&&!/\s/.test(next))return CodeMirror.Pass;curType="both"}else curType=identical&&stringStartsAfter(cm,cur)?"both":triples.indexOf(ch)>=0&&cm.getRange(cur,Pos(cur.line,cur.ch+3))==ch+ch+ch?"skipThree":"skip";if(type){if(type!=curType)return CodeMirror.Pass}else type=curType}var left=pos%2?pairs.charAt(pos-1):ch,right=pos%2?ch:pairs.charAt(pos+1);cm.operation(function(){if("skip"==type)cm.execCommand("goCharRight");else if("skipThree"==type)for(i=0;i<3;i++)cm.execCommand("goCharRight");else if("surround"==type){for(var sels=cm.getSelections(),i=0;i-1&&pos%2==1}function charsAround(cm,pos){var str=cm.getRange(Pos(pos.line,pos.ch-1),Pos(pos.line,pos.ch+1));return 2==str.length?str:null}function enteringString(cm,pos,ch){var line=cm.getLine(pos.line),token=cm.getTokenAt(pos);if(/\bstring2?\b/.test(token.type)||stringStartsAfter(cm,pos))return!1;var stream=new CodeMirror.StringStream(line.slice(0,pos.ch)+ch+line.slice(pos.ch),4);for(stream.pos=stream.start=token.start;;){var type1=cm.getMode().token(stream,token.state);if(stream.pos>=pos.ch+1)return/\bstring2?\b/.test(type1);stream.start=stream.pos}}function stringStartsAfter(cm,pos){var token=cm.getTokenAt(Pos(pos.line,pos.ch+1));return/\bstring/.test(token.type)&&token.start==pos.ch}var defaults={pairs:"()[]{}''\"\"",triples:"",explode:"[]{}"},Pos=CodeMirror.Pos;CodeMirror.defineOption("autoCloseBrackets",!1,function(cm,val,old){old&&old!=CodeMirror.Init&&(cm.removeKeyMap(keyMap),cm.state.closeBrackets=null),val&&(cm.state.closeBrackets=val,cm.addKeyMap(keyMap))});for(var bind=defaults.pairs+"`",keyMap={Backspace:function(cm){var conf=getConfig(cm);if(!conf||cm.getOption("disableInput"))return CodeMirror.Pass;for(var pairs=getOption(conf,"pairs"),ranges=cm.listSelections(),i=0;i=0;i--){var cur=ranges[i].head;cm.replaceRange("",Pos(cur.line,cur.ch-1),Pos(cur.line,cur.ch+1),"+delete")}},Enter:function(cm){var conf=getConfig(cm),explode=conf&&getOption(conf,"explode");if(!explode||cm.getOption("disableInput"))return CodeMirror.Pass;for(var ranges=cm.listSelections(),i=0;i=0&&matching[line.text.charAt(pos)]||matching[line.text.charAt(++pos)];if(!match)return null;var dir=">"==match.charAt(1)?1:-1;if(config&&config.strict&&dir>0!=(pos==where.ch))return null;var style=cm.getTokenTypeAt(Pos(where.line,pos+1)),found=scanForBracket(cm,Pos(where.line,pos+(dir>0?1:0)),dir,style||null,config);return null==found?null:{from:Pos(where.line,pos),to:found&&found.pos,match:found&&found.ch==match.charAt(0),forward:dir>0}}function scanForBracket(cm,where,dir,style,config){for(var maxScanLen=config&&config.maxScanLineLength||1e4,maxScanLines=config&&config.maxScanLines||1e3,stack=[],re=config&&config.bracketRegex?config.bracketRegex:/[(){}[\]]/,lineEnd=dir>0?Math.min(where.line+maxScanLines,cm.lastLine()+1):Math.max(cm.firstLine()-1,where.line-maxScanLines),lineNo=where.line;lineNo!=lineEnd;lineNo+=dir){var line=cm.getLine(lineNo);if(line){var pos=dir>0?0:line.length-1,end=dir>0?line.length:-1;if(!(line.length>maxScanLen))for(lineNo==where.line&&(pos=where.ch-(dir<0?1:0));pos!=end;pos+=dir){var ch=line.charAt(pos);if(re.test(ch)&&(void 0===style||cm.getTokenTypeAt(Pos(lineNo,pos+1))==style))if(">"==matching[ch].charAt(1)==dir>0)stack.push(ch);else{if(!stack.length)return{pos:Pos(lineNo,pos),ch:ch};stack.pop()}}}}return lineNo-dir!=(dir>0?cm.lastLine():cm.firstLine())&&null}function matchBrackets(cm,autoclear,config){for(var maxHighlightLen=cm.state.matchBrackets.maxHighlightLineLength||1e3,marks=[],ranges=cm.listSelections(),i=0;i",")":"(<","[":"]>","]":"[<","{":"}>","}":"{<"},currentlyHighlighted=null;CodeMirror.defineOption("matchBrackets",!1,function(cm,val,old){old&&old!=CodeMirror.Init&&(cm.off("cursorActivity",doMatchBrackets),currentlyHighlighted&&(currentlyHighlighted(),currentlyHighlighted=null)),val&&(cm.state.matchBrackets="object"==typeof val?val:{},cm.on("cursorActivity",doMatchBrackets))}),CodeMirror.defineExtension("matchBrackets",function(){matchBrackets(this,!0)}),CodeMirror.defineExtension("findMatchingBracket",function(pos,config,oldConfig){return(oldConfig||"boolean"==typeof config)&&(oldConfig?(oldConfig.strict=config,config=oldConfig):config=config?{strict:!0}:null),findMatchingBracket(this,pos,config)}),CodeMirror.defineExtension("scanForBracket",function(pos,dir,style,config){return scanForBracket(this,pos,dir,style,config)})})},{"../../lib/codemirror":65}],56:[function(require,module,exports){!function(mod){mod("object"==typeof exports&&"object"==typeof module?require("../../lib/codemirror"):CodeMirror)}(function(CodeMirror){"use strict";CodeMirror.registerHelper("fold","brace",function(cm,start){function findOpening(openCh){for(var at=start.ch,pass=0;;){var found=at<=0?-1:lineText.lastIndexOf(openCh,at-1);if(-1!=found){if(1==pass&&foundcm.lastLine())return null;var start=cm.getTokenAt(CodeMirror.Pos(line,1));if(/\S/.test(start.string)||(start=cm.getTokenAt(CodeMirror.Pos(line,start.end+1))),"keyword"!=start.type||"import"!=start.string)return null;for(var i=line,e=Math.min(cm.lastLine(),line+10);i<=e;++i){var semi=cm.getLine(i).indexOf(";");if(-1!=semi)return{startCh:start.end,end:CodeMirror.Pos(i,semi)}}}var prev,startLine=start.line,has=hasImport(startLine);if(!has||hasImport(startLine-1)||(prev=hasImport(startLine-2))&&prev.end.line==startLine-1)return null;for(var end=has.end;;){var next=hasImport(end.line+1);if(null==next)break;end=next.end}return{from:cm.clipPos(CodeMirror.Pos(startLine,has.startCh+1)),to:end}}),CodeMirror.registerHelper("fold","include",function(cm,start){function hasInclude(line){if(linecm.lastLine())return null;var start=cm.getTokenAt(CodeMirror.Pos(line,1));return/\S/.test(start.string)||(start=cm.getTokenAt(CodeMirror.Pos(line,start.end+1))),"meta"==start.type&&"#include"==start.string.slice(0,8)?start.start+8:void 0}var startLine=start.line,has=hasInclude(startLine);if(null==has||null!=hasInclude(startLine-1))return null;for(var end=startLine;null!=hasInclude(end+1);)++end;return{from:CodeMirror.Pos(startLine,has+1),to:cm.clipPos(CodeMirror.Pos(end))}})})},{"../../lib/codemirror":65}],57:[function(require,module,exports){!function(mod){mod("object"==typeof exports&&"object"==typeof module?require("../../lib/codemirror"):CodeMirror)}(function(CodeMirror){"use strict";function doFold(cm,pos,options,force){function getRange(allowFolded){var range=finder(cm,pos);if(!range||range.to.line-range.from.linecm.firstLine();)pos=CodeMirror.Pos(pos.line-1,0),range=getRange(!1);if(range&&!range.cleared&&"unfold"!==force){var myWidget=makeWidget(cm,options);CodeMirror.on(myWidget,"mousedown",function(e){myRange.clear(),CodeMirror.e_preventDefault(e)});var myRange=cm.markText(range.from,range.to,{replacedWith:myWidget,clearOnEnter:getOption(cm,options,"clearOnEnter"),__isFold:!0});myRange.on("clear",function(from,to){CodeMirror.signal(cm,"unfold",cm,from,to)}),CodeMirror.signal(cm,"fold",cm,range.from,range.to)}}function makeWidget(cm,options){var widget=getOption(cm,options,"widget");if("string"==typeof widget){var text=document.createTextNode(widget);(widget=document.createElement("span")).appendChild(text),widget.className="CodeMirror-foldmarker"}return widget}function getOption(cm,options,name){if(options&&void 0!==options[name])return options[name];var editorOptions=cm.options.foldOptions;return editorOptions&&void 0!==editorOptions[name]?editorOptions[name]:defaultOptions[name]}CodeMirror.newFoldFunction=function(rangeFinder,widget){return function(cm,pos){doFold(cm,pos,{rangeFinder:rangeFinder,widget:widget})}},CodeMirror.defineExtension("foldCode",function(pos,options,force){doFold(this,pos,options,force)}),CodeMirror.defineExtension("isFolded",function(pos){for(var marks=this.findMarksAt(pos),i=0;i=minSize&&(mark=marker(opts.indicatorOpen))}cm.setGutterMarker(line,opts.gutter,mark),++cur})}function updateInViewport(cm){var vp=cm.getViewport(),state=cm.state.foldGutter;state&&(cm.operation(function(){updateFoldInfo(cm,vp.from,vp.to)}),state.from=vp.from,state.to=vp.to)}function onGutterClick(cm,line,gutter){var state=cm.state.foldGutter;if(state){var opts=state.options;if(gutter==opts.gutter){var folded=isFolded(cm,line);folded?folded.clear():cm.foldCode(Pos(line,0),opts.rangeFinder)}}}function onChange(cm){var state=cm.state.foldGutter;if(state){var opts=state.options;state.from=state.to=0,clearTimeout(state.changeUpdate),state.changeUpdate=setTimeout(function(){updateInViewport(cm)},opts.foldOnChangeTimeSpan||600)}}function onViewportChange(cm){var state=cm.state.foldGutter;if(state){var opts=state.options;clearTimeout(state.changeUpdate),state.changeUpdate=setTimeout(function(){var vp=cm.getViewport();state.from==state.to||vp.from-state.to>20||state.from-vp.to>20?updateInViewport(cm):cm.operation(function(){vp.fromstate.to&&(updateFoldInfo(cm,state.to,vp.to),state.to=vp.to)})},opts.updateViewportTimeSpan||400)}}function onFold(cm,from){var state=cm.state.foldGutter;if(state){var line=from.line;line>=state.from&&line0&&old.to.ch-old.from.ch!=nw.to.ch-nw.from.ch}function parseOptions(cm,pos,options){var editor=cm.options.hintOptions,out={};for(var prop in defaultOptions)out[prop]=defaultOptions[prop];if(editor)for(var prop in editor)void 0!==editor[prop]&&(out[prop]=editor[prop]);if(options)for(var prop in options)void 0!==options[prop]&&(out[prop]=options[prop]);return out.hint.resolve&&(out.hint=out.hint.resolve(cm,pos)),out}function getText(completion){return"string"==typeof completion?completion:completion.text}function buildKeyMap(completion,handle){function addBinding(key,val){var bound;bound="string"!=typeof val?function(cm){return val(cm,handle)}:baseMap.hasOwnProperty(val)?baseMap[val]:val,ourMap[key]=bound}var baseMap={Up:function(){handle.moveFocus(-1)},Down:function(){handle.moveFocus(1)},PageUp:function(){handle.moveFocus(1-handle.menuSize(),!0)},PageDown:function(){handle.moveFocus(handle.menuSize()-1,!0)},Home:function(){handle.setFocus(0)},End:function(){handle.setFocus(handle.length-1)},Enter:handle.pick,Tab:handle.pick,Esc:handle.close},custom=completion.options.customKeys,ourMap=custom?{}:baseMap;if(custom)for(var key in custom)custom.hasOwnProperty(key)&&addBinding(key,custom[key]);var extra=completion.options.extraKeys;if(extra)for(var key in extra)extra.hasOwnProperty(key)&&addBinding(key,extra[key]);return ourMap}function getHintElement(hintsElement,el){for(;el&&el!=hintsElement;){if("LI"===el.nodeName.toUpperCase()&&el.parentNode==hintsElement)return el;el=el.parentNode}}function Widget(completion,data){this.completion=completion,this.data=data,this.picked=!1;var widget=this,cm=completion.cm,hints=this.hints=document.createElement("ul");hints.className="CodeMirror-hints",this.selectedHint=data.selectedHint||0;for(var completions=data.list,i=0;ihints.clientHeight+1,startScroll=cm.getScrollInfo();if(overlapY>0){var height=box.bottom-box.top;if(pos.top-(pos.bottom-box.top)-height>0)hints.style.top=(top=pos.top-height)+"px",below=!1;else if(height>winH){hints.style.height=winH-5+"px",hints.style.top=(top=pos.bottom-box.top)+"px";var cursor=cm.getCursor();data.from.ch!=cursor.ch&&(pos=cm.cursorCoords(cursor),hints.style.left=(left=pos.left)+"px",box=hints.getBoundingClientRect())}}var overlapX=box.right-winW;if(overlapX>0&&(box.right-box.left>winW&&(hints.style.width=winW-5+"px",overlapX-=box.right-box.left-winW),hints.style.left=(left=pos.left-overlapX)+"px"),scrolls)for(var node=hints.firstChild;node;node=node.nextSibling)node.style.paddingRight=cm.display.nativeBarWidth+"px";if(cm.addKeyMap(this.keyMap=buildKeyMap(completion,{moveFocus:function(n,avoidWrap){widget.changeActive(widget.selectedHint+n,avoidWrap)},setFocus:function(n){widget.changeActive(n)},menuSize:function(){return widget.screenAmount()},length:completions.length,close:function(){completion.close()},pick:function(){widget.pick()},data:data})),completion.options.closeOnUnfocus){var closingOnBlur;cm.on("blur",this.onBlur=function(){closingOnBlur=setTimeout(function(){completion.close()},100)}),cm.on("focus",this.onFocus=function(){clearTimeout(closingOnBlur)})}return cm.on("scroll",this.onScroll=function(){var curScroll=cm.getScrollInfo(),editor=cm.getWrapperElement().getBoundingClientRect(),newTop=top+startScroll.top-curScroll.top,point=newTop-(window.pageYOffset||(document.documentElement||document.body).scrollTop);if(below||(point+=hints.offsetHeight),point<=editor.top||point>=editor.bottom)return completion.close();hints.style.top=newTop+"px",hints.style.left=left+startScroll.left-curScroll.left+"px"}),CodeMirror.on(hints,"dblclick",function(e){var t=getHintElement(hints,e.target||e.srcElement);t&&null!=t.hintId&&(widget.changeActive(t.hintId),widget.pick())}),CodeMirror.on(hints,"click",function(e){var t=getHintElement(hints,e.target||e.srcElement);t&&null!=t.hintId&&(widget.changeActive(t.hintId),completion.options.completeOnSingleClick&&widget.pick())}),CodeMirror.on(hints,"mousedown",function(){setTimeout(function(){cm.focus()},20)}),CodeMirror.signal(data,"select",completions[0],hints.firstChild),!0}function applicableHelpers(cm,helpers){if(!cm.somethingSelected())return helpers;for(var result=[],i=0;i1)){if(this.somethingSelected()){if(!options.hint.supportsSelection)return;for(var i=0;i=this.data.list.length?i=avoidWrap?this.data.list.length-1:0:i<0&&(i=avoidWrap?0:this.data.list.length-1),this.selectedHint!=i){var node=this.hints.childNodes[this.selectedHint];node.className=node.className.replace(" "+ACTIVE_HINT_ELEMENT_CLASS,""),(node=this.hints.childNodes[this.selectedHint=i]).className+=" "+ACTIVE_HINT_ELEMENT_CLASS,node.offsetTopthis.hints.scrollTop+this.hints.clientHeight&&(this.hints.scrollTop=node.offsetTop+node.offsetHeight-this.hints.clientHeight+3),CodeMirror.signal(this.data,"select",this.data.list[this.selectedHint],node)}},screenAmount:function(){return Math.floor(this.hints.clientHeight/this.hints.firstChild.offsetHeight)||1}},CodeMirror.registerHelper("hint","auto",{resolve:function(cm,pos){var words,helpers=cm.getHelpers(pos,"hint");if(helpers.length){var resolved=function(cm,callback,options){function run(i){if(i==app.length)return callback(null);fetchHints(app[i],cm,options,function(result){result&&result.list.length>0?callback(result):run(i+1)})}var app=applicableHelpers(cm,helpers);run(0)};return resolved.async=!0,resolved.supportsSelection=!0,resolved}return(words=cm.getHelper(cm.getCursor(),"hintWords"))?function(cm){return CodeMirror.hint.fromList(cm,{words:words})}:CodeMirror.hint.anyword?function(cm,options){return CodeMirror.hint.anyword(cm,options)}:function(){}}}),CodeMirror.registerHelper("hint","fromList",function(cm,options){var cur=cm.getCursor(),token=cm.getTokenAt(cur),to=CodeMirror.Pos(cur.line,token.end);if(token.string&&/\w/.test(token.string[token.string.length-1]))var term=token.string,from=CodeMirror.Pos(cur.line,token.start);else var term="",from=to;for(var found=[],i=0;i,]/,closeOnUnfocus:!0,completeOnSingleClick:!0,container:null,customKeys:null,extraKeys:null};CodeMirror.defineOption("hintOptions",null)})},{"../../lib/codemirror":65}],60:[function(require,module,exports){!function(mod){mod("object"==typeof exports&&"object"==typeof module?require("../../lib/codemirror"):CodeMirror)}(function(CodeMirror){"use strict";function showTooltip(e,content){function position(e){if(!tt.parentNode)return CodeMirror.off(document,"mousemove",position);tt.style.top=Math.max(0,e.clientY-tt.offsetHeight-5)+"px",tt.style.left=e.clientX+5+"px"}var tt=document.createElement("div");return tt.className="CodeMirror-lint-tooltip",tt.appendChild(content.cloneNode(!0)),document.body.appendChild(tt),CodeMirror.on(document,"mousemove",position),position(e),null!=tt.style.opacity&&(tt.style.opacity=1),tt}function rm(elt){elt.parentNode&&elt.parentNode.removeChild(elt)}function hideTooltip(tt){tt.parentNode&&(null==tt.style.opacity&&rm(tt),tt.style.opacity=0,setTimeout(function(){rm(tt)},600))}function showTooltipFor(e,content,node){function hide(){CodeMirror.off(node,"mouseout",hide),tooltip&&(hideTooltip(tooltip),tooltip=null)}var tooltip=showTooltip(e,content),poll=setInterval(function(){if(tooltip)for(var n=node;;n=n.parentNode){if(n&&11==n.nodeType&&(n=n.host),n==document.body)return;if(!n){hide();break}}if(!tooltip)return clearInterval(poll)},400);CodeMirror.on(node,"mouseout",hide)}function LintState(cm,options,hasGutter){this.marked=[],this.options=options,this.timeout=null,this.hasGutter=hasGutter,this.onMouseOver=function(e){onMouseOver(cm,e)},this.waitingFor=0}function parseOptions(_cm,options){return options instanceof Function?{getAnnotations:options}:(options&&!0!==options||(options={}),options)}function clearMarks(cm){var state=cm.state.lint;state.hasGutter&&cm.clearGutter(GUTTER_ID);for(var i=0;i1,state.options.tooltips))}}options.onUpdateLinting&&options.onUpdateLinting(annotationsNotSorted,annotations,cm)}function onChange(cm){var state=cm.state.lint;state&&(clearTimeout(state.timeout),state.timeout=setTimeout(function(){startLinting(cm)},state.options.delay||500))}function popupTooltips(annotations,e){for(var target=e.target||e.srcElement,tooltip=document.createDocumentFragment(),i=0;i (Use line:column or scroll% syntax)',"Jump to line:",cur.line+1+":"+cur.ch,function(posStr){if(posStr){var match;if(match=/^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr))cm.setCursor(interpretLine(cm,match[1]),Number(match[2]));else if(match=/^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)){var line=Math.round(cm.lineCount()*Number(match[1])/100);/^[-+]/.test(match[1])&&(line=cur.line+line+1),cm.setCursor(line-1,cur.ch)}else(match=/^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr))&&cm.setCursor(interpretLine(cm,match[1]),cur.ch)}})},CodeMirror.keyMap.default["Alt-G"]="jumpToLine"})},{"../../lib/codemirror":65,"../dialog/dialog":53}],62:[function(require,module,exports){!function(mod){"object"==typeof exports&&"object"==typeof module?mod(require("../../lib/codemirror"),require("./searchcursor"),require("../dialog/dialog")):mod(CodeMirror)}(function(CodeMirror){"use strict";function searchOverlay(query,caseInsensitive){return"string"==typeof query?query=new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&"),caseInsensitive?"gi":"g"):query.global||(query=new RegExp(query.source,query.ignoreCase?"gi":"g")),{token:function(stream){query.lastIndex=stream.pos;var match=query.exec(stream.string);if(match&&match.index==stream.pos)return stream.pos+=match[0].length||1,"searching";match?stream.pos=match.index:stream.skipToEnd()}}}function SearchState(){this.posFrom=this.posTo=this.lastQuery=this.query=null,this.overlay=null}function getSearchState(cm){return cm.state.search||(cm.state.search=new SearchState)}function queryCaseInsensitive(query){return"string"==typeof query&&query==query.toLowerCase()}function getSearchCursor(cm,query,pos){return cm.getSearchCursor(query,pos,{caseFold:queryCaseInsensitive(query),multiline:!0})}function persistentDialog(cm,text,deflt,onEnter,onKeyDown){cm.openDialog(text,onEnter,{value:deflt,selectValueOnOpen:!0,closeOnEnter:!1,onClose:function(){clearSearch(cm)},onKeyDown:onKeyDown})}function dialog(cm,text,shortText,deflt,f){cm.openDialog?cm.openDialog(text,f,{value:deflt,selectValueOnOpen:!0}):f(prompt(shortText,deflt))}function confirmDialog(cm,text,shortText,fs){cm.openConfirm?cm.openConfirm(text,fs):confirm(shortText)&&fs[0]()}function parseString(string){return string.replace(/\\(.)/g,function(_,ch){return"n"==ch?"\n":"r"==ch?"\r":ch})}function parseQuery(query){var isRE=query.match(/^\/(.*)\/([a-z]*)$/);if(isRE)try{query=new RegExp(isRE[1],-1==isRE[2].indexOf("i")?"":"i")}catch(e){}else query=parseString(query);return("string"==typeof query?""==query:query.test(""))&&(query=/x^/),query}function startSearch(cm,state,query){state.queryText=query,state.query=parseQuery(query),cm.removeOverlay(state.overlay,queryCaseInsensitive(state.query)),state.overlay=searchOverlay(state.query,queryCaseInsensitive(state.query)),cm.addOverlay(state.overlay),cm.showMatchesOnScrollbar&&(state.annotate&&(state.annotate.clear(),state.annotate=null),state.annotate=cm.showMatchesOnScrollbar(state.query,queryCaseInsensitive(state.query)))}function doSearch(cm,rev,persistent,immediate){var state=getSearchState(cm);if(state.query)return findNext(cm,rev);var q=cm.getSelection()||state.lastQuery;if(persistent&&cm.openDialog){var hiding=null,searchNext=function(query,event){CodeMirror.e_stop(event),query&&(query!=state.queryText&&(startSearch(cm,state,query),state.posFrom=state.posTo=cm.getCursor()),hiding&&(hiding.style.opacity=1),findNext(cm,event.shiftKey,function(_,to){var dialog;to.line<3&&document.querySelector&&(dialog=cm.display.wrapper.querySelector(".CodeMirror-dialog"))&&dialog.getBoundingClientRect().bottom-4>cm.cursorCoords(to,"window").top&&((hiding=dialog).style.opacity=.4)}))};persistentDialog(cm,queryDialog,q,searchNext,function(event,query){var keyName=CodeMirror.keyName(event),cmd=CodeMirror.keyMap[cm.getOption("keyMap")][keyName];cmd||(cmd=cm.getOption("extraKeys")[keyName]),"findNext"==cmd||"findPrev"==cmd||"findPersistentNext"==cmd||"findPersistentPrev"==cmd?(CodeMirror.e_stop(event),startSearch(cm,getSearchState(cm),query),cm.execCommand(cmd)):"find"!=cmd&&"findPersistent"!=cmd||(CodeMirror.e_stop(event),searchNext(query,event))}),immediate&&q&&(startSearch(cm,state,q),findNext(cm,rev))}else dialog(cm,queryDialog,"Search for:",q,function(query){query&&!state.query&&cm.operation(function(){startSearch(cm,state,query),state.posFrom=state.posTo=cm.getCursor(),findNext(cm,rev)})})}function findNext(cm,rev,callback){cm.operation(function(){var state=getSearchState(cm),cursor=getSearchCursor(cm,state.query,rev?state.posFrom:state.posTo);(cursor.find(rev)||(cursor=getSearchCursor(cm,state.query,rev?CodeMirror.Pos(cm.lastLine()):CodeMirror.Pos(cm.firstLine(),0))).find(rev))&&(cm.setSelection(cursor.from(),cursor.to()),cm.scrollIntoView({from:cursor.from(),to:cursor.to()},20),state.posFrom=cursor.from(),state.posTo=cursor.to(),callback&&callback(cursor.from(),cursor.to()))})}function clearSearch(cm){cm.operation(function(){var state=getSearchState(cm);state.lastQuery=state.query,state.query&&(state.query=state.queryText=null,cm.removeOverlay(state.overlay),state.annotate&&(state.annotate.clear(),state.annotate=null))})}function replaceAll(cm,query,text){cm.operation(function(){for(var cursor=getSearchCursor(cm,query);cursor.findNext();)if("string"!=typeof query){var match=cm.getRange(cursor.from(),cursor.to()).match(query);cursor.replace(text.replace(/\$(\d)/g,function(_,i){return match[i]}))}else cursor.replace(text)})}function replace(cm,all){if(!cm.getOption("readOnly")){var query=cm.getSelection()||getSearchState(cm).lastQuery,dialogText=''+(all?"Replace all:":"Replace:")+"";dialog(cm,dialogText+replaceQueryDialog,dialogText,query,function(query){query&&(query=parseQuery(query),dialog(cm,replacementQueryDialog,"Replace with:","",function(text){if(text=parseString(text),all)replaceAll(cm,query,text);else{clearSearch(cm);var cursor=getSearchCursor(cm,query,cm.getCursor("from")),advance=function(){var match,start=cursor.from();!(match=cursor.findNext())&&(cursor=getSearchCursor(cm,query),!(match=cursor.findNext())||start&&cursor.from().line==start.line&&cursor.from().ch==start.ch)||(cm.setSelection(cursor.from(),cursor.to()),cm.scrollIntoView({from:cursor.from(),to:cursor.to()}),confirmDialog(cm,doReplaceConfirm,"Replace?",[function(){doReplace(match)},advance,function(){replaceAll(cm,query,text)}]))},doReplace=function(match){cursor.replace("string"==typeof query?text:text.replace(/\$(\d)/g,function(_,i){return match[i]})),advance()};advance()}}))})}}var queryDialog='Search: (Use /re/ syntax for regexp search)',replaceQueryDialog=' (Use /re/ syntax for regexp search)',replacementQueryDialog='With: ',doReplaceConfirm='Replace? ';CodeMirror.commands.find=function(cm){clearSearch(cm),doSearch(cm)},CodeMirror.commands.findPersistent=function(cm){clearSearch(cm),doSearch(cm,!1,!0)},CodeMirror.commands.findPersistentNext=function(cm){doSearch(cm,!1,!0,!0)},CodeMirror.commands.findPersistentPrev=function(cm){doSearch(cm,!0,!0,!0)},CodeMirror.commands.findNext=doSearch,CodeMirror.commands.findPrev=function(cm){doSearch(cm,!0)},CodeMirror.commands.clearSearch=clearSearch,CodeMirror.commands.replace=replace,CodeMirror.commands.replaceAll=function(cm){replace(cm,!0)}})},{"../../lib/codemirror":65,"../dialog/dialog":53,"./searchcursor":63}],63:[function(require,module,exports){!function(mod){mod("object"==typeof exports&&"object"==typeof module?require("../../lib/codemirror"):CodeMirror)}(function(CodeMirror){"use strict";function regexpFlags(regexp){var flags=regexp.flags;return null!=flags?flags:(regexp.ignoreCase?"i":"")+(regexp.global?"g":"")+(regexp.multiline?"m":"")}function ensureGlobal(regexp){return regexp.global?regexp:new RegExp(regexp.source,regexpFlags(regexp)+"g")}function maybeMultiline(regexp){return/\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)}function searchRegexpForward(doc,regexp,start){regexp=ensureGlobal(regexp);for(var line=start.line,ch=start.ch,last=doc.lastLine();line<=last;line++,ch=0){regexp.lastIndex=ch;var string=doc.getLine(line),match=regexp.exec(string);if(match)return{from:Pos(line,match.index),to:Pos(line,match.index+match[0].length),match:match}}}function searchRegexpForwardMultiline(doc,regexp,start){if(!maybeMultiline(regexp))return searchRegexpForward(doc,regexp,start);regexp=ensureGlobal(regexp);for(var string,chunk=1,line=start.line,last=doc.lastLine();line<=last;){for(var i=0;i=first;line--,ch=-1){var string=doc.getLine(line);ch>-1&&(string=string.slice(0,ch));var match=lastMatchIn(string,regexp);if(match)return{from:Pos(line,match.index),to:Pos(line,match.index+match[0].length),match:match}}}function searchRegexpBackwardMultiline(doc,regexp,start){regexp=ensureGlobal(regexp);for(var string,chunk=1,line=start.line,first=doc.firstLine();line>=first;){for(var i=0;ipos))return pos1;--pos1}}}function searchStringForward(doc,query,start,caseFold){if(!query.length)return null;var fold=caseFold?doFold:noFold,lines=fold(query).split(/\r|\n\r?/);search:for(var line=start.line,ch=start.ch,last=doc.lastLine()+1-lines.length;line<=last;line++,ch=0){var orig=doc.getLine(line).slice(ch),string=fold(orig);if(1==lines.length){var found=string.indexOf(lines[0]);if(-1==found)continue search;var start=adjustPos(orig,string,found)+ch;return{from:Pos(line,adjustPos(orig,string,found)+ch),to:Pos(line,adjustPos(orig,string,found+lines[0].length)+ch)}}var cutFrom=string.length-lines[0].length;if(string.slice(cutFrom)==lines[0]){for(var i=1;i=first;line--,ch=-1){var orig=doc.getLine(line);ch>-1&&(orig=orig.slice(0,ch));var string=fold(orig);if(1==lines.length){var found=string.lastIndexOf(lines[0]);if(-1==found)continue search;return{from:Pos(line,adjustPos(orig,string,found)),to:Pos(line,adjustPos(orig,string,found+lines[0].length))}}var lastLine=lines[lines.length-1];if(string.slice(0,lastLine.length)==lastLine){for(var i=1,start=line-lines.length+1;i0);)ranges.push({anchor:cur.from(),head:cur.to()});ranges.length&&this.setSelections(ranges,0)})})},{"../../lib/codemirror":65}],64:[function(require,module,exports){!function(mod){"object"==typeof exports&&"object"==typeof module?mod(require("../lib/codemirror"),require("../addon/search/searchcursor"),require("../addon/edit/matchbrackets")):mod(CodeMirror)}(function(CodeMirror){"use strict";function findPosSubword(doc,start,dir){if(dir<0&&0==start.ch)return doc.clipPos(Pos(start.line-1));var line=doc.getLine(start.line);if(dir>0&&start.ch>=line.length)return doc.clipPos(Pos(start.line+1,0));for(var type,state="start",pos=start.ch,e=dir<0?0:line.length,i=0;pos!=e;pos+=dir,i++){var next=line.charAt(dir<0?pos-1:pos),cat="_"!=next&&CodeMirror.isWordChar(next)?"w":"o";if("w"==cat&&next.toUpperCase()==next&&(cat="W"),"start"==state)"o"!=cat&&(state="in",type=cat);else if("in"==state&&type!=cat){if("w"==type&&"W"==cat&&dir<0&&pos--,"W"==type&&"w"==cat&&dir>0){type="w";continue}break}}return Pos(start.line,pos)}function moveSubword(cm,dir){cm.extendSelectionsBy(function(range){return cm.display.shift||cm.doc.extend||range.empty()?findPosSubword(cm.doc,range.head,dir):dir<0?range.from():range.to()})}function insertLine(cm,above){if(cm.isReadOnly())return CodeMirror.Pass;cm.operation(function(){for(var len=cm.listSelections().length,newSelection=[],last=-1,i=0;i=0;i--){var range=ranges[indices[i]];if(!(at&&CodeMirror.cmpPos(range.head,at)>0)){var word=wordAt(cm,range.head);at=word.from,cm.replaceRange(mod(word.word),word.from,word.to)}}})}function getTarget(cm){var from=cm.getCursor("from"),to=cm.getCursor("to");if(0==CodeMirror.cmpPos(from,to)){var word=wordAt(cm,from);if(!word.word)return;from=word.from,to=word.to}return{from:from,to:to,query:cm.getRange(from,to),word:word}}function findAndGoTo(cm,forward){var target=getTarget(cm);if(target){var query=target.query,cur=cm.getSearchCursor(query,forward?target.to:target.from);(forward?cur.findNext():cur.findPrevious())?cm.setSelection(cur.from(),cur.to()):(cur=cm.getSearchCursor(query,forward?Pos(cm.firstLine(),0):cm.clipPos(Pos(cm.lastLine()))),(forward?cur.findNext():cur.findPrevious())?cm.setSelection(cur.from(),cur.to()):target.word&&cm.setSelection(target.from,target.to))}}var map=CodeMirror.keyMap.sublime={fallthrough:"default"},cmds=CodeMirror.commands,Pos=CodeMirror.Pos,mac=CodeMirror.keyMap.default==CodeMirror.keyMap.macDefault,ctrl=mac?"Cmd-":"Ctrl-",goSubwordCombo=mac?"Ctrl-":"Alt-";cmds[map[goSubwordCombo+"Left"]="goSubwordLeft"]=function(cm){moveSubword(cm,-1)},cmds[map[goSubwordCombo+"Right"]="goSubwordRight"]=function(cm){moveSubword(cm,1)},mac&&(map["Cmd-Left"]="goLineStartSmart");var scrollLineCombo=mac?"Ctrl-Alt-":"Ctrl-";cmds[map[scrollLineCombo+"Up"]="scrollLineUp"]=function(cm){var info=cm.getScrollInfo();if(!cm.somethingSelected()){var visibleBottomLine=cm.lineAtHeight(info.top+info.clientHeight,"local");cm.getCursor().line>=visibleBottomLine&&cm.execCommand("goLineUp")}cm.scrollTo(null,info.top-cm.defaultTextHeight())},cmds[map[scrollLineCombo+"Down"]="scrollLineDown"]=function(cm){var info=cm.getScrollInfo();if(!cm.somethingSelected()){var visibleTopLine=cm.lineAtHeight(info.top,"local")+1;cm.getCursor().line<=visibleTopLine&&cm.execCommand("goLineDown")}cm.scrollTo(null,info.top+cm.defaultTextHeight())},cmds[map["Shift-"+ctrl+"L"]="splitSelectionByLine"]=function(cm){for(var ranges=cm.listSelections(),lineRanges=[],i=0;ifrom.line&&line==to.line&&0==to.ch||lineRanges.push({anchor:line==from.line?from:Pos(line,0),head:line==to.line?to:Pos(line)});cm.setSelections(lineRanges,0)},map["Shift-Tab"]="indentLess",cmds[map.Esc="singleSelectionTop"]=function(cm){var range=cm.listSelections()[0];cm.setSelection(range.anchor,range.head,{scroll:!1})},cmds[map[ctrl+"L"]="selectLine"]=function(cm){for(var ranges=cm.listSelections(),extended=[],i=0;iat?linesToMove.push(from,to):linesToMove.length&&(linesToMove[linesToMove.length-1]=to),at=to}cm.operation(function(){for(var i=0;icm.lastLine()?cm.replaceRange("\n"+line,Pos(cm.lastLine()),null,"+swapLine"):cm.replaceRange(line+"\n",Pos(to,0),null,"+swapLine")}cm.setSelections(newSels),cm.scrollIntoView()})},cmds[map[swapLineCombo+"Down"]="swapLineDown"]=function(cm){if(cm.isReadOnly())return CodeMirror.Pass;for(var ranges=cm.listSelections(),linesToMove=[],at=cm.lastLine()+1,i=ranges.length-1;i>=0;i--){var range=ranges[i],from=range.to().line+1,to=range.from().line;0!=range.to().ch||range.empty()||from--,from=0;i-=2){var from=linesToMove[i],to=linesToMove[i+1],line=cm.getLine(from);from==cm.lastLine()?cm.replaceRange("",Pos(from-1),Pos(from),"+swapLine"):cm.replaceRange("",Pos(from,0),Pos(from+1,0),"+swapLine"),cm.replaceRange(line+"\n",Pos(to,0),null,"+swapLine")}cm.scrollIntoView()})},cmds[map[ctrl+"/"]="toggleCommentIndented"]=function(cm){cm.toggleComment({indent:!0})},cmds[map[ctrl+"J"]="joinLines"]=function(cm){for(var ranges=cm.listSelections(),joined=[],i=0;i=0;i--){var cursor=cursors[i].head,toStartOfLine=cm.getRange({line:cursor.line,ch:0},cursor),column=CodeMirror.countColumn(toStartOfLine,null,cm.getOption("tabSize")),deletePos=cm.findPosH(cursor,-1,"char",!1);if(toStartOfLine&&!/\S/.test(toStartOfLine)&&column%indentUnit==0){var prevIndent=new Pos(cursor.line,CodeMirror.findColumn(toStartOfLine,column-indentUnit,indentUnit));prevIndent.ch!=cursor.ch&&(deletePos=prevIndent)}cm.replaceRange("",deletePos,cursor,"+delete")}})},cmds[map[cK+ctrl+"K"]="delLineRight"]=function(cm){cm.operation(function(){for(var ranges=cm.listSelections(),i=ranges.length-1;i>=0;i--)cm.replaceRange("",ranges[i].anchor,Pos(ranges[i].to().line),"+delete");cm.scrollIntoView()})},cmds[map[cK+ctrl+"U"]="upcaseAtCursor"]=function(cm){modifyWordOrSelection(cm,function(str){return str.toUpperCase()})},cmds[map[cK+ctrl+"L"]="downcaseAtCursor"]=function(cm){modifyWordOrSelection(cm,function(str){return str.toLowerCase()})},cmds[map[cK+ctrl+"Space"]="setSublimeMark"]=function(cm){cm.state.sublimeMark&&cm.state.sublimeMark.clear(),cm.state.sublimeMark=cm.setBookmark(cm.getCursor())},cmds[map[cK+ctrl+"A"]="selectToSublimeMark"]=function(cm){var found=cm.state.sublimeMark&&cm.state.sublimeMark.find();found&&cm.setSelection(cm.getCursor(),found)},cmds[map[cK+ctrl+"W"]="deleteToSublimeMark"]=function(cm){var found=cm.state.sublimeMark&&cm.state.sublimeMark.find();if(found){var from=cm.getCursor(),to=found;if(CodeMirror.cmpPos(from,to)>0){var tmp=to;to=from,from=tmp}cm.state.sublimeKilled=cm.getRange(from,to),cm.replaceRange("",from,to)}},cmds[map[cK+ctrl+"X"]="swapWithSublimeMark"]=function(cm){var found=cm.state.sublimeMark&&cm.state.sublimeMark.find();found&&(cm.state.sublimeMark.clear(),cm.state.sublimeMark=cm.setBookmark(cm.getCursor()),cm.setCursor(found))},cmds[map[cK+ctrl+"Y"]="sublimeYank"]=function(cm){null!=cm.state.sublimeKilled&&cm.replaceSelection(cm.state.sublimeKilled,null,"paste")},map[cK+ctrl+"G"]="clearBookmarks",cmds[map[cK+ctrl+"C"]="showInCenter"]=function(cm){var pos=cm.cursorCoords(null,"local");cm.scrollTo(null,(pos.top+pos.bottom)/2-cm.getScrollInfo().clientHeight/2)};var selectLinesCombo=mac?"Ctrl-Shift-":"Ctrl-Alt-";cmds[map[selectLinesCombo+"Up"]="selectLinesUpward"]=function(cm){cm.operation(function(){for(var ranges=cm.listSelections(),i=0;icm.firstLine()&&cm.addSelection(Pos(range.head.line-1,range.head.ch))}})},cmds[map[selectLinesCombo+"Down"]="selectLinesDownward"]=function(cm){cm.operation(function(){for(var ranges=cm.listSelections(),i=0;i0;--count)e.removeChild(e.firstChild);return e}function removeChildrenAndAdd(parent,e){return removeChildren(parent).appendChild(e)}function elt(tag,content,className,style){var e=document.createElement(tag);if(className&&(e.className=className),style&&(e.style.cssText=style),"string"==typeof content)e.appendChild(document.createTextNode(content));else if(content)for(var i=0;i=end)return n+(end-i);n+=nextTab-i,n+=tabSize-n%tabSize,i=nextTab+1}}function indexOf(array,elt){for(var i=0;i=goal)return pos+Math.min(skipped,goal-col);if(col+=nextTab-pos,col+=tabSize-col%tabSize,pos=nextTab+1,col>=goal)return pos}}function spaceStr(n){for(;spaceStrs.length<=n;)spaceStrs.push(lst(spaceStrs)+" ");return spaceStrs[n]}function lst(arr){return arr[arr.length-1]}function map(array,f){for(var out=[],i=0;i"€"&&(ch.toUpperCase()!=ch.toLowerCase()||nonASCIISingleCaseWordChar.test(ch))}function isWordChar(ch,helper){return helper?!!(helper.source.indexOf("\\w")>-1&&isWordCharBasic(ch))||helper.test(ch):isWordCharBasic(ch)}function isEmpty(obj){for(var n in obj)if(obj.hasOwnProperty(n)&&obj[n])return!1;return!0}function isExtendingChar(ch){return ch.charCodeAt(0)>=768&&extendingChars.test(ch)}function skipExtendingChars(str,pos,dir){for(;(dir<0?pos>0:pos=doc.size)throw new Error("There is no line "+(n+doc.first)+" in the document.");for(var chunk=doc;!chunk.lines;)for(var i=0;;++i){var child=chunk.children[i],sz=child.chunkSize();if(n=doc.first&&llast?Pos(last,getLine(doc,last).text.length):clipToLen(pos,getLine(doc,pos.line).text.length)}function clipToLen(pos,linelen){var ch=pos.ch;return null==ch||ch>linelen?Pos(pos.line,linelen):ch<0?Pos(pos.line,0):pos}function clipPosArray(doc,array){for(var out=[],i=0;i=startCh:span.to>startCh);(nw||(nw=[])).push(new MarkedSpan(marker,span.from,endsAfter?null:span.to))}}return nw}function markedSpansAfter(old,endCh,isInsert){var nw;if(old)for(var i=0;i=endCh:span.to>endCh)||span.from==endCh&&"bookmark"==marker.type&&(!isInsert||span.marker.insertLeft)){var startsBefore=null==span.from||(marker.inclusiveLeft?span.from<=endCh:span.from0&&first)for(var i$2=0;i$20)){var newParts=[j,1],dfrom=cmp(p.from,m.from),dto=cmp(p.to,m.to);(dfrom<0||!mk.inclusiveLeft&&!dfrom)&&newParts.push({from:p.from,to:m.from}),(dto>0||!mk.inclusiveRight&&!dto)&&newParts.push({from:m.to,to:p.to}),parts.splice.apply(parts,newParts),j+=newParts.length-3}}return parts}function detachMarkedSpans(line){var spans=line.markedSpans;if(spans){for(var i=0;i=0&&toCmp<=0||fromCmp<=0&&toCmp>=0)&&(fromCmp<=0&&(sp.marker.inclusiveRight&&marker.inclusiveLeft?cmp(found.to,from)>=0:cmp(found.to,from)>0)||fromCmp>=0&&(sp.marker.inclusiveRight&&marker.inclusiveLeft?cmp(found.from,to)<=0:cmp(found.from,to)<0)))return!0}}}function visualLine(line){for(var merged;merged=collapsedSpanAtStart(line);)line=merged.find(-1,!0).line;return line}function visualLineEnd(line){for(var merged;merged=collapsedSpanAtEnd(line);)line=merged.find(1,!0).line;return line}function visualLineContinued(line){for(var merged,lines;merged=collapsedSpanAtEnd(line);)line=merged.find(1,!0).line,(lines||(lines=[])).push(line);return lines}function visualLineNo(doc,lineN){var line=getLine(doc,lineN),vis=visualLine(line);return line==vis?lineN:lineNo(vis)}function visualLineEndNo(doc,lineN){if(lineN>doc.lastLine())return lineN;var merged,line=getLine(doc,lineN);if(!lineIsHidden(doc,line))return lineN;for(;merged=collapsedSpanAtEnd(line);)line=merged.find(1,!0).line;return lineNo(line)+1}function lineIsHidden(doc,line){var sps=sawCollapsedSpans&&line.markedSpans;if(sps)for(var sp=void 0,i=0;id.maxLineLength&&(d.maxLineLength=len,d.maxLine=line)})}function iterateBidiSections(order,from,to,f){if(!order)return f(from,to,"ltr");for(var found=!1,i=0;ifrom||from==to&&part.to==from)&&(f(Math.max(part.from,from),Math.min(part.to,to),1==part.level?"rtl":"ltr"),found=!0)}found||f(from,to,"ltr")}function getBidiPartAt(order,ch,sticky){var found;bidiOther=null;for(var i=0;ich)return i;cur.to==ch&&(cur.from!=cur.to&&"before"==sticky?found=i:bidiOther=i),cur.from==ch&&(cur.from!=cur.to&&"before"!=sticky?found=i:bidiOther=i)}return null!=found?found:bidiOther}function getOrder(line,direction){var order=line.order;return null==order&&(order=line.order=bidiOrdering(line.text,direction)),order}function moveCharLogically(line,ch,dir){var target=skipExtendingChars(line.text,ch+dir,dir);return target<0||target>line.text.length?null:target}function moveLogically(line,start,dir){var ch=moveCharLogically(line,start.ch,dir);return null==ch?null:new Pos(start.line,ch,dir<0?"after":"before")}function endOfLine(visually,cm,lineObj,lineNo,dir){if(visually){var order=getOrder(lineObj,cm.doc.direction);if(order){var ch,part=dir<0?lst(order):order[0],sticky=dir<0==(1==part.level)?"after":"before";if(part.level>0){var prep=prepareMeasureForLine(cm,lineObj);ch=dir<0?lineObj.text.length-1:0;var targetTop=measureCharPrepared(cm,prep,ch).top;ch=findFirst(function(ch){return measureCharPrepared(cm,prep,ch).top==targetTop},dir<0==(1==part.level)?part.from:part.to-1,ch),"before"==sticky&&(ch=moveCharLogically(lineObj,ch,1))}else ch=dir<0?part.to:part.from;return new Pos(lineNo,ch,sticky)}}return new Pos(lineNo,dir<0?lineObj.text.length:0,dir<0?"before":"after")}function moveVisually(cm,line,start,dir){var bidi=getOrder(line,cm.doc.direction);if(!bidi)return moveLogically(line,start,dir);start.ch>=line.text.length?(start.ch=line.text.length,start.sticky="before"):start.ch<=0&&(start.ch=0,start.sticky="after");var partPos=getBidiPartAt(bidi,start.ch,start.sticky),part=bidi[partPos];if("ltr"==cm.doc.direction&&part.level%2==0&&(dir>0?part.to>start.ch:part.from=part.from&&ch>=wrappedLineExtent.begin)){var sticky=moveInStorageOrder?"before":"after";return new Pos(start.line,ch,sticky)}}var searchInVisualLine=function(partPos,dir,wrappedLineExtent){for(var getRes=function(ch,moveInStorageOrder){return moveInStorageOrder?new Pos(start.line,mv(ch,1),"before"):new Pos(start.line,ch,"after")};partPos>=0&&partPos0==(1!=part.level),ch=moveInStorageOrder?wrappedLineExtent.begin:mv(wrappedLineExtent.end,-1);if(part.from<=ch&&ch0?wrappedLineExtent.end:mv(wrappedLineExtent.begin,-1);return null==nextCh||dir>0&&nextCh==line.text.length||!(res=searchInVisualLine(dir>0?0:bidi.length-1,dir,getWrappedLineExtent(nextCh)))?null:res}function getHandlers(emitter,type){return emitter._handlers&&emitter._handlers[type]||noHandlers}function off(emitter,type,f){if(emitter.removeEventListener)emitter.removeEventListener(type,f,!1);else if(emitter.detachEvent)emitter.detachEvent("on"+type,f);else{var map$$1=emitter._handlers,arr=map$$1&&map$$1[type];if(arr){var index=indexOf(arr,f);index>-1&&(map$$1[type]=arr.slice(0,index).concat(arr.slice(index+1)))}}}function signal(emitter,type){var handlers=getHandlers(emitter,type);if(handlers.length)for(var args=Array.prototype.slice.call(arguments,2),i=0;i0}function eventMixin(ctor){ctor.prototype.on=function(type,f){on(this,type,f)},ctor.prototype.off=function(type,f){off(this,type,f)}}function e_preventDefault(e){e.preventDefault?e.preventDefault():e.returnValue=!1}function e_stopPropagation(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0}function e_defaultPrevented(e){return null!=e.defaultPrevented?e.defaultPrevented:0==e.returnValue}function e_stop(e){e_preventDefault(e),e_stopPropagation(e)}function e_target(e){return e.target||e.srcElement}function e_button(e){var b=e.which;return null==b&&(1&e.button?b=1:2&e.button?b=3:4&e.button&&(b=2)),mac&&e.ctrlKey&&1==b&&(b=3),b}function zeroWidthElement(measure){if(null==zwspSupported){var test=elt("span","​");removeChildrenAndAdd(measure,elt("span",[test,document.createTextNode("x")])),0!=measure.firstChild.offsetHeight&&(zwspSupported=test.offsetWidth<=1&&test.offsetHeight>2&&!(ie&&ie_version<8))}var node=zwspSupported?elt("span","​"):elt("span"," ",null,"display: inline-block; width: 1px; margin-right: -1px");return node.setAttribute("cm-text",""),node}function hasBadBidiRects(measure){if(null!=badBidiRects)return badBidiRects;var txt=removeChildrenAndAdd(measure,document.createTextNode("AخA")),r0=range(txt,0,1).getBoundingClientRect(),r1=range(txt,1,2).getBoundingClientRect();return removeChildren(measure),!(!r0||r0.left==r0.right)&&(badBidiRects=r1.right-r0.right<3)}function hasBadZoomedRects(measure){if(null!=badZoomedRects)return badZoomedRects;var node=removeChildrenAndAdd(measure,elt("span","x")),normal=node.getBoundingClientRect(),fromRange=range(node,0,1).getBoundingClientRect();return badZoomedRects=Math.abs(normal.left-fromRange.left)>1}function defineMode(name,mode){arguments.length>2&&(mode.dependencies=Array.prototype.slice.call(arguments,2)),modes[name]=mode}function resolveMode(spec){if("string"==typeof spec&&mimeModes.hasOwnProperty(spec))spec=mimeModes[spec];else if(spec&&"string"==typeof spec.name&&mimeModes.hasOwnProperty(spec.name)){var found=mimeModes[spec.name];"string"==typeof found&&(found={name:found}),(spec=createObj(found,spec)).name=found.name}else{if("string"==typeof spec&&/^[\w\-]+\/[\w\-]+\+xml$/.test(spec))return resolveMode("application/xml");if("string"==typeof spec&&/^[\w\-]+\/[\w\-]+\+json$/.test(spec))return resolveMode("application/json")}return"string"==typeof spec?{name:spec}:spec||{name:"null"}}function getMode(options,spec){spec=resolveMode(spec);var mfactory=modes[spec.name];if(!mfactory)return getMode(options,"text/plain");var modeObj=mfactory(options,spec);if(modeExtensions.hasOwnProperty(spec.name)){var exts=modeExtensions[spec.name];for(var prop in exts)exts.hasOwnProperty(prop)&&(modeObj.hasOwnProperty(prop)&&(modeObj["_"+prop]=modeObj[prop]),modeObj[prop]=exts[prop])}if(modeObj.name=spec.name,spec.helperType&&(modeObj.helperType=spec.helperType),spec.modeProps)for(var prop$1 in spec.modeProps)modeObj[prop$1]=spec.modeProps[prop$1];return modeObj}function extendMode(mode,properties){copyObj(properties,modeExtensions.hasOwnProperty(mode)?modeExtensions[mode]:modeExtensions[mode]={})}function copyState(mode,state){if(!0===state)return state;if(mode.copyState)return mode.copyState(state);var nstate={};for(var n in state){var val=state[n];val instanceof Array&&(val=val.concat([])),nstate[n]=val}return nstate}function innerMode(mode,state){for(var info;mode.innerMode&&(info=mode.innerMode(state))&&info.mode!=mode;)state=info.state,mode=info.mode;return info||{mode:mode,state:state}}function startState(mode,a1,a2){return!mode.startState||mode.startState(a1,a2)}function highlightLine(cm,line,context,forceToEnd){var st=[cm.state.modeGen],lineClasses={};runMode(cm,line.text,cm.doc.mode,context,function(end,style){return st.push(end,style)},lineClasses,forceToEnd);for(var state=context.state,o=0;oend&&st.splice(i,1,end,st[i+1],i_end),i+=2,at=Math.min(end,i_end)}if(style)if(overlay.opaque)st.splice(start,i-start,end,"overlay "+style),i=start+2;else for(;startcm.options.maxHighlightLength&©State(cm.doc.mode,context.state),result=highlightLine(cm,line,context);resetState&&(context.state=resetState),line.stateAfter=context.save(!resetState),line.styles=result.styles,result.classes?line.styleClasses=result.classes:line.styleClasses&&(line.styleClasses=null),updateFrontier===cm.doc.highlightFrontier&&(cm.doc.modeFrontier=Math.max(cm.doc.modeFrontier,++cm.doc.highlightFrontier))}return line.styles}function getContextBefore(cm,n,precise){var doc=cm.doc,display=cm.display;if(!doc.mode.startState)return new Context(doc,!0,n);var start=findStartLine(cm,n,precise),saved=start>doc.first&&getLine(doc,start-1).stateAfter,context=saved?Context.fromSaved(doc,saved,start):new Context(doc,startState(doc.mode),start);return doc.iter(start,n,function(line){processLine(cm,line.text,context);var pos=context.line;line.stateAfter=pos==n-1||pos%5==0||pos>=display.viewFrom&&posstream.start)return style}throw new Error("Mode "+mode.name+" failed to advance stream.")}function takeToken(cm,pos,precise,asArray){var style,tokens,doc=cm.doc,mode=doc.mode,line=getLine(doc,(pos=clipPos(doc,pos)).line),context=getContextBefore(cm,pos.line,precise),stream=new StringStream(line.text,cm.options.tabSize,context);for(asArray&&(tokens=[]);(asArray||stream.poscm.options.maxHighlightLength?(flattenSpans=!1,forceToEnd&&processLine(cm,text,context,stream.pos),stream.pos=text.length,style=null):style=extractLineClasses(readToken(mode,stream,context.state,inner),lineClasses),inner){var mName=inner[0].name;mName&&(style="m-"+(style?mName+" "+style:mName))}if(!flattenSpans||curStyle!=style){for(;curStartlim;--search){if(search<=doc.first)return doc.first;var line=getLine(doc,search-1),after=line.stateAfter;if(after&&(!precise||search+(after instanceof SavedContext?after.lookAhead:0)<=doc.modeFrontier))return search;var indented=countColumn(line.text,null,cm.options.tabSize);(null==minline||minindent>indented)&&(minline=search-1,minindent=indented)}return minline}function retreatFrontier(doc,n){if(doc.modeFrontier=Math.min(doc.modeFrontier,n),!(doc.highlightFrontierstart;line--){var saved=getLine(doc,line).stateAfter;if(saved&&(!(saved instanceof SavedContext)||line+saved.lookAhead1&&!/ /.test(text))return text;for(var spaceBefore=trailingBefore,result="",i=0;istart&&part.from<=start);i++);if(part.to>=end)return inner(builder,text,style,startStyle,endStyle,title,css);inner(builder,text.slice(0,part.to-start),style,startStyle,null,title,css),startStyle=null,text=text.slice(part.to-start),start=part.to}}}function buildCollapsedSpan(builder,size,marker,ignoreWidget){var widget=!ignoreWidget&&marker.widgetNode;widget&&builder.map.push(builder.pos,builder.pos+size,widget),!ignoreWidget&&builder.cm.display.input.needsContentAttribute&&(widget||(widget=builder.content.appendChild(document.createElement("span"))),widget.setAttribute("cm-marker",marker.id)),widget&&(builder.cm.display.input.setUneditable(widget),builder.content.appendChild(widget)),builder.pos+=size,builder.trailingSpace=!1}function insertLineContent(line,builder,styles){var spans=line.markedSpans,allText=line.text,at=0;if(spans)for(var style,css,spanStyle,spanEndStyle,spanStartStyle,title,collapsed,len=allText.length,pos=0,i=1,text="",nextChange=0;;){if(nextChange==pos){spanStyle=spanEndStyle=spanStartStyle=title=css="",collapsed=null,nextChange=1/0;for(var foundBookmarks=[],endStyles=void 0,j=0;jpos||m.collapsed&&sp.to==pos&&sp.from==pos)?(null!=sp.to&&sp.to!=pos&&nextChange>sp.to&&(nextChange=sp.to,spanEndStyle=""),m.className&&(spanStyle+=" "+m.className),m.css&&(css=(css?css+";":"")+m.css),m.startStyle&&sp.from==pos&&(spanStartStyle+=" "+m.startStyle),m.endStyle&&sp.to==nextChange&&(endStyles||(endStyles=[])).push(m.endStyle,sp.to),m.title&&!title&&(title=m.title),m.collapsed&&(!collapsed||compareCollapsedMarkers(collapsed.marker,m)<0)&&(collapsed=sp)):sp.from>pos&&nextChange>sp.from&&(nextChange=sp.from)}if(endStyles)for(var j$1=0;j$1=len)break;for(var upto=Math.min(len,nextChange);;){if(text){var end=pos+text.length;if(!collapsed){var tokenText=end>upto?text.slice(0,upto-pos):text;builder.addToken(builder,tokenText,style?style+spanStyle:spanStyle,spanStartStyle,pos+tokenText.length==nextChange?spanEndStyle:"",title,css)}if(end>=upto){text=text.slice(upto-pos),pos=upto;break}pos=end,spanStartStyle=""}text=allText.slice(at,at=styles[i++]),style=interpretTokenStyle(styles[i++],builder.cm.options)}}else for(var i$1=1;i$12&&heights.push((cur.bottom+next.top)/2-rect.top)}}heights.push(rect.bottom-rect.top)}}function mapFromLineView(lineView,line,lineN){if(lineView.line==line)return{map:lineView.measure.map,cache:lineView.measure.cache};for(var i=0;ilineN)return{map:lineView.measure.maps[i$1],cache:lineView.measure.caches[i$1],before:!0}}function updateExternalMeasurement(cm,line){var lineN=lineNo(line=visualLine(line)),view=cm.display.externalMeasured=new LineView(cm.doc,line,lineN);view.lineN=lineN;var built=view.built=buildLineContent(cm,view);return view.text=built.pre,removeChildrenAndAdd(cm.display.lineMeasure,built.pre),view}function measureChar(cm,line,ch,bias){return measureCharPrepared(cm,prepareMeasureForLine(cm,line),ch,bias)}function findViewForLine(cm,lineN){if(lineN>=cm.display.viewFrom&&lineN=ext.lineN&&lineNch)&&(start=(end=mEnd-mStart)-1,ch>=mEnd&&(collapse="right")),null!=start){if(node=map$$1[i+2],mStart==mEnd&&bias==(node.insertLeft?"left":"right")&&(collapse=bias),"left"==bias&&0==start)for(;i&&map$$1[i-2]==map$$1[i-3]&&map$$1[i-1].insertLeft;)node=map$$1[2+(i-=3)],collapse="left";if("right"==bias&&start==mEnd-mStart)for(;i=0&&(rect=rects[i$1]).left==rect.right;i$1--);return rect}function measureCharInner(cm,prepared,ch,bias){var rect,place=nodeAndOffsetInLineMap(prepared.map,ch,bias),node=place.node,start=place.start,end=place.end,collapse=place.collapse;if(3==node.nodeType){for(var i$1=0;i$1<4;i$1++){for(;start&&isExtendingChar(prepared.line.text.charAt(place.coverStart+start));)--start;for(;place.coverStart+end0&&(collapse=bias="right");var rects;rect=cm.options.lineWrapping&&(rects=node.getClientRects()).length>1?rects["right"==bias?rects.length-1:0]:node.getBoundingClientRect()}if(ie&&ie_version<9&&!start&&(!rect||!rect.left&&!rect.right)){var rSpan=node.parentNode.getClientRects()[0];rect=rSpan?{left:rSpan.left,right:rSpan.left+charWidth(cm.display),top:rSpan.top,bottom:rSpan.bottom}:nullRect}for(var rtop=rect.top-prepared.rect.top,rbot=rect.bottom-prepared.rect.top,mid=(rtop+rbot)/2,heights=prepared.view.measure.heights,i=0;i=lineObj.text.length?(ch=lineObj.text.length,sticky="before"):ch<=0&&(ch=0,sticky="after"),!order)return get("before"==sticky?ch-1:ch,"before"==sticky);var partPos=getBidiPartAt(order,ch,sticky),other=bidiOther,val=getBidi(ch,partPos,"before"==sticky);return null!=other&&(val.other=getBidi(ch,other,"before"!=sticky)),val}function estimateCoords(cm,pos){var left=0;pos=clipPos(cm.doc,pos),cm.options.lineWrapping||(left=charWidth(cm.display)*pos.ch);var lineObj=getLine(cm.doc,pos.line),top=heightAtLine(lineObj)+paddingTop(cm.display);return{left:left,right:left,top:top,bottom:top+lineObj.height}}function PosWithInfo(line,ch,sticky,outside,xRel){var pos=Pos(line,ch,sticky);return pos.xRel=xRel,outside&&(pos.outside=!0),pos}function coordsChar(cm,x,y){var doc=cm.doc;if((y+=cm.display.viewOffset)<0)return PosWithInfo(doc.first,0,null,!0,-1);var lineN=lineAtHeight(doc,y),last=doc.first+doc.size-1;if(lineN>last)return PosWithInfo(doc.first+doc.size-1,getLine(doc,last).text.length,null,!0,1);x<0&&(x=0);for(var lineObj=getLine(doc,lineN);;){var found=coordsCharInner(cm,lineObj,lineN,x,y),merged=collapsedSpanAtEnd(lineObj),mergedPos=merged&&merged.find(0,!0);if(!merged||!(found.ch>mergedPos.from.ch||found.ch==mergedPos.from.ch&&found.xRel>0))return found;lineN=lineNo(lineObj=mergedPos.to.line)}}function wrappedLineExtent(cm,lineObj,preparedMeasure,y){var measure=function(ch){return intoCoordSystem(cm,lineObj,measureCharPrepared(cm,preparedMeasure,ch),"line")},end=lineObj.text.length,begin=findFirst(function(ch){return measure(ch-1).bottom<=y},end,0);return end=findFirst(function(ch){return measure(ch).top>y},begin,end),{begin:begin,end:end}}function wrappedLineExtentChar(cm,lineObj,preparedMeasure,target){return wrappedLineExtent(cm,lineObj,preparedMeasure,intoCoordSystem(cm,lineObj,measureCharPrepared(cm,preparedMeasure,target),"line").top)}function coordsCharInner(cm,lineObj,lineNo$$1,x,y){y-=heightAtLine(lineObj);var pos,begin=0,end=lineObj.text.length,preparedMeasure=prepareMeasureForLine(cm,lineObj);if(getOrder(lineObj,cm.doc.direction)){if(cm.options.lineWrapping){var assign;begin=(assign=wrappedLineExtent(cm,lineObj,preparedMeasure,y)).begin,end=assign.end}pos=new Pos(lineNo$$1,Math.floor(begin+(end-begin)/2));var prevDiff,prevPos,beginLeft=cursorCoords(cm,pos,"line",lineObj,preparedMeasure).left,dir=beginLeft1){var diff_change_per_step=Math.abs(diff-prevDiff)/steps;steps=Math.min(steps,Math.ceil(Math.abs(diff)/diff_change_per_step)),dir=diff<0?1:-1}}while(0!=diff&&(steps>1||dir<0!=diff<0&&Math.abs(diff)<=Math.abs(prevDiff)));if(Math.abs(diff)>Math.abs(prevDiff)){if(diff<0==prevDiff<0)throw new Error("Broke out of infinite loop in coordsCharInner");pos=prevPos}}else{var ch=findFirst(function(ch){var box=intoCoordSystem(cm,lineObj,measureCharPrepared(cm,preparedMeasure,ch),"line");return box.top>y?(end=Math.min(ch,end),!0):!(box.bottom<=y)&&(box.left>x||!(box.rightcoords.right?1:0,pos}function textHeight(display){if(null!=display.cachedTextHeight)return display.cachedTextHeight;if(null==measureText){measureText=elt("pre");for(var i=0;i<49;++i)measureText.appendChild(document.createTextNode("x")),measureText.appendChild(elt("br"));measureText.appendChild(document.createTextNode("x"))}removeChildrenAndAdd(display.measure,measureText);var height=measureText.offsetHeight/50;return height>3&&(display.cachedTextHeight=height),removeChildren(display.measure),height||1}function charWidth(display){if(null!=display.cachedCharWidth)return display.cachedCharWidth;var anchor=elt("span","xxxxxxxxxx"),pre=elt("pre",[anchor]);removeChildrenAndAdd(display.measure,pre);var rect=anchor.getBoundingClientRect(),width=(rect.right-rect.left)/10;return width>2&&(display.cachedCharWidth=width),width||10}function getDimensions(cm){for(var d=cm.display,left={},width={},gutterLeft=d.gutters.clientLeft,n=d.gutters.firstChild,i=0;n;n=n.nextSibling,++i)left[cm.options.gutters[i]]=n.offsetLeft+n.clientLeft+gutterLeft,width[cm.options.gutters[i]]=n.clientWidth;return{fixedPos:compensateForHScroll(d),gutterTotalWidth:d.gutters.offsetWidth,gutterLeft:left,gutterWidth:width,wrapperWidth:d.wrapper.clientWidth}}function compensateForHScroll(display){return display.scroller.getBoundingClientRect().left-display.sizer.getBoundingClientRect().left}function estimateHeight(cm){var th=textHeight(cm.display),wrapping=cm.options.lineWrapping,perLine=wrapping&&Math.max(5,cm.display.scroller.clientWidth/charWidth(cm.display)-3);return function(line){if(lineIsHidden(cm.doc,line))return 0;var widgetsHeight=0;if(line.widgets)for(var i=0;i=cm.display.viewTo)return null;if((n-=cm.display.viewFrom)<0)return null;for(var view=cm.display.view,i=0;i=cm.display.viewTo||range$$1.to().line3&&(add(left,leftPos.top,null,leftPos.bottom),left=leftSide,leftPos.bottomend.bottom||rightPos.bottom==end.bottom&&rightPos.right>end.right)&&(end=rightPos),left0?display.blinker=setInterval(function(){return display.cursorDiv.style.visibility=(on=!on)?"":"hidden"},cm.options.cursorBlinkRate):cm.options.cursorBlinkRate<0&&(display.cursorDiv.style.visibility="hidden")}}function ensureFocus(cm){cm.state.focused||(cm.display.input.focus(),onFocus(cm))}function delayBlurEvent(cm){cm.state.delayingBlurEvent=!0,setTimeout(function(){cm.state.delayingBlurEvent&&(cm.state.delayingBlurEvent=!1,onBlur(cm))},100)}function onFocus(cm,e){cm.state.delayingBlurEvent&&(cm.state.delayingBlurEvent=!1),"nocursor"!=cm.options.readOnly&&(cm.state.focused||(signal(cm,"focus",cm,e),cm.state.focused=!0,addClass(cm.display.wrapper,"CodeMirror-focused"),cm.curOp||cm.display.selForContextMenu==cm.doc.sel||(cm.display.input.reset(),webkit&&setTimeout(function(){return cm.display.input.reset(!0)},20)),cm.display.input.receivedFocus()),restartBlink(cm))}function onBlur(cm,e){cm.state.delayingBlurEvent||(cm.state.focused&&(signal(cm,"blur",cm,e),cm.state.focused=!1,rmClass(cm.display.wrapper,"CodeMirror-focused")),clearInterval(cm.display.blinker),setTimeout(function(){cm.state.focused||(cm.display.shift=!1)},150))}function updateHeightsInViewport(cm){for(var display=cm.display,prevBottom=display.lineDiv.offsetTop,i=0;i.001||diff<-.001)&&(updateLineHeight(cur.line,height),updateWidgetHeight(cur.line),cur.rest))for(var j=0;j=to&&(from=lineAtHeight(doc,heightAtLine(getLine(doc,ensureTo))-display.wrapper.clientHeight),to=ensureTo)}return{from:from,to:Math.max(to,from+1)}}function alignHorizontally(cm){var display=cm.display,view=display.view;if(display.alignWidgets||display.gutters.firstChild&&cm.options.fixedGutter){for(var comp=compensateForHScroll(display)-display.scroller.scrollLeft+cm.doc.scrollLeft,gutterW=display.gutters.offsetWidth,left=comp+"px",i=0;i(window.innerHeight||document.documentElement.clientHeight)&&(doScroll=!1),null!=doScroll&&!phantom){var scrollNode=elt("div","​",null,"position: absolute;\n top: "+(rect.top-display.viewOffset-paddingTop(cm.display))+"px;\n height: "+(rect.bottom-rect.top+scrollGap(cm)+display.barHeight)+"px;\n left: "+rect.left+"px; width: "+Math.max(2,rect.right-rect.left)+"px;");cm.display.lineSpace.appendChild(scrollNode),scrollNode.scrollIntoView(doScroll),cm.display.lineSpace.removeChild(scrollNode)}}}function scrollPosIntoView(cm,pos,end,margin){null==margin&&(margin=0);for(var rect,limit=0;limit<5;limit++){var changed=!1,coords=cursorCoords(cm,pos),endCoords=end&&end!=pos?cursorCoords(cm,end):coords,scrollPos=calculateScrollPos(cm,rect={left:Math.min(coords.left,endCoords.left),top:Math.min(coords.top,endCoords.top)-margin,right:Math.max(coords.left,endCoords.left),bottom:Math.max(coords.bottom,endCoords.bottom)+margin}),startTop=cm.doc.scrollTop,startLeft=cm.doc.scrollLeft;if(null!=scrollPos.scrollTop&&(updateScrollTop(cm,scrollPos.scrollTop),Math.abs(cm.doc.scrollTop-startTop)>1&&(changed=!0)),null!=scrollPos.scrollLeft&&(setScrollLeft(cm,scrollPos.scrollLeft),Math.abs(cm.doc.scrollLeft-startLeft)>1&&(changed=!0)),!changed)break}return rect}function scrollIntoView(cm,rect){var scrollPos=calculateScrollPos(cm,rect);null!=scrollPos.scrollTop&&updateScrollTop(cm,scrollPos.scrollTop),null!=scrollPos.scrollLeft&&setScrollLeft(cm,scrollPos.scrollLeft)}function calculateScrollPos(cm,rect){var display=cm.display,snapMargin=textHeight(cm.display);rect.top<0&&(rect.top=0);var screentop=cm.curOp&&null!=cm.curOp.scrollTop?cm.curOp.scrollTop:display.scroller.scrollTop,screen=displayHeight(cm),result={};rect.bottom-rect.top>screen&&(rect.bottom=rect.top+screen);var docBottom=cm.doc.height+paddingVert(display),atTop=rect.topdocBottom-snapMargin;if(rect.topscreentop+screen){var newTop=Math.min(rect.top,(atBottom?docBottom:rect.bottom)-screen);newTop!=screentop&&(result.scrollTop=newTop)}var screenleft=cm.curOp&&null!=cm.curOp.scrollLeft?cm.curOp.scrollLeft:display.scroller.scrollLeft,screenw=displayWidth(cm)-(cm.options.fixedGutter?display.gutters.offsetWidth:0),tooWide=rect.right-rect.left>screenw;return tooWide&&(rect.right=rect.left+screenw),rect.left<10?result.scrollLeft=0:rect.leftscreenw+screenleft-3&&(result.scrollLeft=rect.right+(tooWide?0:10)-screenw),result}function addToScrollTop(cm,top){null!=top&&(resolveScrollToPos(cm),cm.curOp.scrollTop=(null==cm.curOp.scrollTop?cm.doc.scrollTop:cm.curOp.scrollTop)+top)}function ensureCursorVisible(cm){resolveScrollToPos(cm);var cur=cm.getCursor(),from=cur,to=cur;cm.options.lineWrapping||(from=cur.ch?Pos(cur.line,cur.ch-1):cur,to=Pos(cur.line,cur.ch+1)),cm.curOp.scrollToPos={from:from,to:to,margin:cm.options.cursorScrollMargin}}function scrollToCoords(cm,x,y){null==x&&null==y||resolveScrollToPos(cm),null!=x&&(cm.curOp.scrollLeft=x),null!=y&&(cm.curOp.scrollTop=y)}function scrollToRange(cm,range$$1){resolveScrollToPos(cm),cm.curOp.scrollToPos=range$$1}function resolveScrollToPos(cm){var range$$1=cm.curOp.scrollToPos;range$$1&&(cm.curOp.scrollToPos=null,scrollToCoordsRange(cm,estimateCoords(cm,range$$1.from),estimateCoords(cm,range$$1.to),range$$1.margin))}function scrollToCoordsRange(cm,from,to,margin){var sPos=calculateScrollPos(cm,{left:Math.min(from.left,to.left),top:Math.min(from.top,to.top)-margin,right:Math.max(from.right,to.right),bottom:Math.max(from.bottom,to.bottom)+margin});scrollToCoords(cm,sPos.scrollLeft,sPos.scrollTop)}function updateScrollTop(cm,val){Math.abs(cm.doc.scrollTop-val)<2||(gecko||updateDisplaySimple(cm,{top:val}),setScrollTop(cm,val,!0),gecko&&updateDisplaySimple(cm),startWorker(cm,100))}function setScrollTop(cm,val,forceScroll){val=Math.min(cm.display.scroller.scrollHeight-cm.display.scroller.clientHeight,val),(cm.display.scroller.scrollTop!=val||forceScroll)&&(cm.doc.scrollTop=val,cm.display.scrollbars.setScrollTop(val),cm.display.scroller.scrollTop!=val&&(cm.display.scroller.scrollTop=val))}function setScrollLeft(cm,val,isScroller,forceScroll){val=Math.min(val,cm.display.scroller.scrollWidth-cm.display.scroller.clientWidth),(isScroller?val==cm.doc.scrollLeft:Math.abs(cm.doc.scrollLeft-val)<2)&&!forceScroll||(cm.doc.scrollLeft=val,alignHorizontally(cm),cm.display.scroller.scrollLeft!=val&&(cm.display.scroller.scrollLeft=val),cm.display.scrollbars.setScrollLeft(val))}function measureForScrollbars(cm){var d=cm.display,gutterW=d.gutters.offsetWidth,docH=Math.round(cm.doc.height+paddingVert(cm.display));return{clientHeight:d.scroller.clientHeight,viewHeight:d.wrapper.clientHeight,scrollWidth:d.scroller.scrollWidth,clientWidth:d.scroller.clientWidth,viewWidth:d.wrapper.clientWidth,barLeft:cm.options.fixedGutter?gutterW:0,docHeight:docH,scrollHeight:docH+scrollGap(cm)+d.barHeight,nativeBarWidth:d.nativeBarWidth,gutterWidth:gutterW}}function updateScrollbars(cm,measure){measure||(measure=measureForScrollbars(cm));var startWidth=cm.display.barWidth,startHeight=cm.display.barHeight;updateScrollbarsInner(cm,measure);for(var i=0;i<4&&startWidth!=cm.display.barWidth||startHeight!=cm.display.barHeight;i++)startWidth!=cm.display.barWidth&&cm.options.lineWrapping&&updateHeightsInViewport(cm),updateScrollbarsInner(cm,measureForScrollbars(cm)),startWidth=cm.display.barWidth,startHeight=cm.display.barHeight}function updateScrollbarsInner(cm,measure){var d=cm.display,sizes=d.scrollbars.update(measure);d.sizer.style.paddingRight=(d.barWidth=sizes.right)+"px",d.sizer.style.paddingBottom=(d.barHeight=sizes.bottom)+"px",d.heightForcer.style.borderBottom=sizes.bottom+"px solid transparent",sizes.right&&sizes.bottom?(d.scrollbarFiller.style.display="block",d.scrollbarFiller.style.height=sizes.bottom+"px",d.scrollbarFiller.style.width=sizes.right+"px"):d.scrollbarFiller.style.display="",sizes.bottom&&cm.options.coverGutterNextToScrollbar&&cm.options.fixedGutter?(d.gutterFiller.style.display="block",d.gutterFiller.style.height=sizes.bottom+"px",d.gutterFiller.style.width=measure.gutterWidth+"px"):d.gutterFiller.style.display=""}function initScrollbars(cm){cm.display.scrollbars&&(cm.display.scrollbars.clear(),cm.display.scrollbars.addClass&&rmClass(cm.display.wrapper,cm.display.scrollbars.addClass)),cm.display.scrollbars=new scrollbarModel[cm.options.scrollbarStyle](function(node){cm.display.wrapper.insertBefore(node,cm.display.scrollbarFiller),on(node,"mousedown",function(){cm.state.focused&&setTimeout(function(){return cm.display.input.focus()},0)}),node.setAttribute("cm-not-content","true")},function(pos,axis){"horizontal"==axis?setScrollLeft(cm,pos):updateScrollTop(cm,pos)},cm),cm.display.scrollbars.addClass&&addClass(cm.display.wrapper,cm.display.scrollbars.addClass)}function startOperation(cm){cm.curOp={cm:cm,viewChanged:!1,startHeight:cm.doc.height,forceUpdate:!1,updateInput:null,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:!1,id:++nextOpId},pushOperation(cm.curOp)}function endOperation(cm){finishOperation(cm.curOp,function(group){for(var i=0;i=display.viewTo)||display.maxLineChanged&&cm.options.lineWrapping,op.update=op.mustUpdate&&new DisplayUpdate(cm,op.mustUpdate&&{top:op.scrollTop,ensure:op.scrollToPos},op.forceUpdate)}function endOperation_W1(op){op.updatedDisplay=op.mustUpdate&&updateDisplayIfNeeded(op.cm,op.update)}function endOperation_R2(op){var cm=op.cm,display=cm.display;op.updatedDisplay&&updateHeightsInViewport(cm),op.barMeasure=measureForScrollbars(cm),display.maxLineChanged&&!cm.options.lineWrapping&&(op.adjustWidthTo=measureChar(cm,display.maxLine,display.maxLine.text.length).left+3,cm.display.sizerWidth=op.adjustWidthTo,op.barMeasure.scrollWidth=Math.max(display.scroller.clientWidth,display.sizer.offsetLeft+op.adjustWidthTo+scrollGap(cm)+cm.display.barWidth),op.maxScrollLeft=Math.max(0,display.sizer.offsetLeft+op.adjustWidthTo-displayWidth(cm))),(op.updatedDisplay||op.selectionChanged)&&(op.preparedSelection=display.input.prepareSelection(op.focus))}function endOperation_W2(op){var cm=op.cm;null!=op.adjustWidthTo&&(cm.display.sizer.style.minWidth=op.adjustWidthTo+"px",op.maxScrollLeft