diff --git a/.github/workflows/acceptance-public.yml b/.github/workflows/acceptance-public.yml index 3f82d397dc..879830cf1a 100644 --- a/.github/workflows/acceptance-public.yml +++ b/.github/workflows/acceptance-public.yml @@ -47,15 +47,6 @@ jobs: operator_id: ${{ inputs.operator_id }} operator_key: ${{ inputs.operator_key }} - rpc_api_schema_conformity: - name: API Conformity - uses: ./.github/workflows/acceptance-workflow.yml - with: - testfilter: api_conformity - envfile: ${{ inputs.network }}Acceptance.env - operator_id: ${{ inputs.operator_id }} - operator_key: ${{ inputs.operator_key }} - erc20: name: ERC20 uses: ./.github/workflows/acceptance-workflow.yml @@ -147,7 +138,6 @@ jobs: - api_batch_1 - api_batch_2 - api_batch_3 - - rpc_api_schema_conformity - erc20 - ratelimiter - tokencreate diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index c335f9de84..db62ef8db7 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -30,12 +30,6 @@ jobs: with: testfilter: api_batch3 - rpc_api_schema_conformity: - name: API Conformity - uses: ./.github/workflows/acceptance-workflow.yml - with: - testfilter: rpc_api_schema_conformity - erc20: name: ERC20 uses: ./.github/workflows/acceptance-workflow.yml @@ -119,7 +113,6 @@ jobs: - api_batch_1 - api_batch_2 - api_batch_3 - - rpc_api_schema_conformity - erc20 - ratelimiter - hbarlimiter diff --git a/.github/workflows/conformity-workflow.yml b/.github/workflows/conformity-workflow.yml new file mode 100644 index 0000000000..9c1cc5b5f6 --- /dev/null +++ b/.github/workflows/conformity-workflow.yml @@ -0,0 +1,96 @@ +name: Conformity Test Workflow + +on: + pull_request: + branches: [main, release/**] + push: + branches: [main, release/**] + tags: [v*] + +jobs: + clone-and-build-execution-apis: + runs-on: [self-hosted, Linux, medium, ephemeral] + + steps: + - name: Checkout execution-apis repo + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + with: + ref: 7907424db935b93c2fe6a3c0faab943adebe8557 + repository: 'ethereum/execution-apis' + path: 'execution-apis' + + - name: Use Node.js TLS 18 + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: + node-version: 18 + + - name: Install dependencies + run: npm install + working-directory: ./execution-apis + + - name: Build project + run: npm run build + working-directory: ./execution-apis + + - name: Upload openrpc.json as an artifact + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + with: + name: openrpc + path: ./execution-apis/openrpc.json + + build-and-test: + runs-on: [self-hosted, Linux, medium, ephemeral] + needs: clone-and-build-execution-apis + + steps: + - name: Checkout repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Use Node.js TLS 18 + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: + node-version: 18 + + - name: Install make + run: sudo apt-get update; sudo apt-get install build-essential -y + + - name: Checkout repo + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Install packages + run: npm ci + + - name: Create .env file + run: cp ./packages/server/tests/localAcceptance.env .env + + - name: Install pnpm + run: npm install -g pnpm + + - name: Build Typescript + run: npx lerna run build + + - name: Install hedera local + run: npm install @hashgraph/hedera-local -g + + - name: Set operator id and key env variable if CI is manual + if: ${{ inputs.operator_id }} + run: | + echo "OPERATOR_ID_MAIN=${{ inputs.operator_id }}" >> $GITHUB_ENV + echo "OPERATOR_KEY_MAIN=${{ inputs.operator_key }}" >> $GITHUB_ENV + + - name: Run hedera local + run: npx hedera restart -d --network-tag=${{inputs.networkTag}} --mirror-tag=${{inputs.mirrorTag}} --verbose=trace + + - name: Stop relay + run: docker stop json-rpc-relay json-rpc-relay-ws + + - name: Download openrpc.json artifact + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 + with: + name: openrpc + + - name: Rename file + run: mv openrpc.json openrpc_exec_apis.json + + - name: Run conformity tests + run: npm run acceptancetest:rpc_api_schema_conformity diff --git a/.github/workflows/manual-testing.yml b/.github/workflows/manual-testing.yml index f0802968bd..2cbac6e7d3 100644 --- a/.github/workflows/manual-testing.yml +++ b/.github/workflows/manual-testing.yml @@ -41,15 +41,6 @@ jobs: testfilter: api_batch3 networkTag: ${{inputs.networkNodeTag}} mirrorTag: ${{inputs.mirrorNodeTag}} - - - rpc_api_schema_conformity: - name: API Conformity - uses: ./.github/workflows/acceptance-workflow.yml - with: - testfilter: rpc_api_schema_conformity - networkTag: ${{inputs.networkNodeTag}} - mirrorTag: ${{inputs.mirrorNodeTag}} erc20: name: ERC20 @@ -131,7 +122,6 @@ jobs: - api_batch_1 - api_batch_2 - api_batch_3 - - rpc_api_schema_conformity - erc20 - ratelimiter - hbarlimiter diff --git a/package-lock.json b/package-lock.json index 13cf9a201d..30d5568bfa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,8 @@ "@open-rpc/schema-utils-js": "^1.16.1", "@types/find-config": "^1.0.4", "@types/sinon": "^10.0.20", + "ajv": "^8.16.0", + "ajv-formats": "^3.0.1", "chai-exclude": "^2.1.1", "eslint-config-standard-with-typescript": "^43.0.1", "eslint-plugin-n": "^15.7.0", @@ -898,6 +900,28 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, "node_modules/@eslint/js": { "version": "8.48.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", @@ -4422,6 +4446,28 @@ "isomorphic-fetch": "^3.0.0" } }, + "node_modules/@open-rpc/schema-utils-js/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@open-rpc/schema-utils-js/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -5681,20 +5727,47 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv/node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -8684,6 +8757,22 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -8759,6 +8848,12 @@ "node": ">=8" } }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -9266,8 +9361,8 @@ }, "node_modules/execution-apis": { "version": "0.0.0", - "resolved": "git+ssh://git@github.com/ethereum/execution-apis.git#584905270d8ad665718058060267061ecfd79ca5", - "integrity": "sha512-ly5HxKGZr7G4tfFrbtJ05hdEx23Wt6U09EPzGNNlnmXKenDEiIauvmdpeIsL6V1J/efFPuEqLJ9DAfjK/WQeTw==", + "resolved": "git+ssh://git@github.com/ethereum/execution-apis.git#7907424db935b93c2fe6a3c0faab943adebe8557", + "integrity": "sha512-OWOqlt3EFodZ5u8JGXcON6fWeNs1wNwiReh89xK/AG6GoCO8AcMuU8r7tA+lPSeDG8GLth/UpWN3Qn+dBv5l+A==", "dev": true, "license": "CC0-1.0" }, @@ -9378,7 +9473,8 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -9398,6 +9494,12 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "license": "MIT" + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -10704,6 +10806,30 @@ "node": ">=6" } }, + "node_modules/har-validator/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/har-validator/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -12230,9 +12356,10 @@ "dev": true }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -19759,6 +19886,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -20885,7 +21013,7 @@ "axios-retry": "^3.5.1", "chai": "^4.3.6", "ethers": "^6.7.0", - "execution-apis": "git://github.com/ethereum/execution-apis.git#584905270d8ad665718058060267061ecfd79ca5", + "execution-apis": "git://github.com/ethereum/execution-apis.git#7907424db935b93c2fe6a3c0faab943adebe8557", "shelljs": "^0.8.5", "ts-mocha": "^9.0.2", "ts-node": "^10.8.1", @@ -22622,6 +22750,24 @@ "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + } } }, "@eslint/js": { @@ -24026,7 +24172,7 @@ "co-body": "6.2.0", "dotenv": "^16.0.0", "ethers": "^6.7.0", - "execution-apis": "git://github.com/ethereum/execution-apis.git#584905270d8ad665718058060267061ecfd79ca5", + "execution-apis": "git://github.com/ethereum/execution-apis.git#7907424db935b93c2fe6a3c0faab943adebe8557", "koa": "^2.13.4", "koa-body-parser": "^0.2.1", "koa-cors": "^0.0.16", @@ -26172,6 +26318,24 @@ "fs-extra": "^9.0.0", "is-url": "^1.2.4", "isomorphic-fetch": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + } } }, "@pkgjs/parseargs": { @@ -27234,14 +27398,29 @@ } }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "dependencies": { + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + } + } + }, + "ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "requires": { + "ajv": "^8.0.0" } }, "ansi-colors": { @@ -29251,6 +29430,17 @@ "text-table": "^0.2.0" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -29299,6 +29489,11 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -29877,10 +30072,10 @@ } }, "execution-apis": { - "version": "git+ssh://git@github.com/ethereum/execution-apis.git#584905270d8ad665718058060267061ecfd79ca5", - "integrity": "sha512-ly5HxKGZr7G4tfFrbtJ05hdEx23Wt6U09EPzGNNlnmXKenDEiIauvmdpeIsL6V1J/efFPuEqLJ9DAfjK/WQeTw==", + "version": "git+ssh://git@github.com/ethereum/execution-apis.git#7907424db935b93c2fe6a3c0faab943adebe8557", + "integrity": "sha512-OWOqlt3EFodZ5u8JGXcON6fWeNs1wNwiReh89xK/AG6GoCO8AcMuU8r7tA+lPSeDG8GLth/UpWN3Qn+dBv5l+A==", "dev": true, - "from": "execution-apis@git://github.com/ethereum/execution-apis.git#584905270d8ad665718058060267061ecfd79ca5" + "from": "execution-apis@git://github.com/ethereum/execution-apis.git#7907424db935b93c2fe6a3c0faab943adebe8557" }, "exponential-backoff": { "version": "3.1.1", @@ -29984,6 +30179,11 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -30894,6 +31094,26 @@ "requires": { "ajv": "^6.12.3", "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } } }, "hard-rejection": { @@ -31981,9 +32201,9 @@ "dev": true }, "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", diff --git a/package.json b/package.json index 0259662076..d0de467b72 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,8 @@ "@types/find-config": "^1.0.4", "@types/sinon": "^10.0.20", "chai-exclude": "^2.1.1", + "ajv": "^8.16.0", + "ajv-formats": "^3.0.1", "eslint-config-standard-with-typescript": "^43.0.1", "eslint-plugin-n": "^15.7.0", "keyv-file": "^0.3.1", diff --git a/packages/server/package.json b/packages/server/package.json index 2b7e62d46c..7a88115231 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -37,7 +37,7 @@ "axios-retry": "^3.5.1", "chai": "^4.3.6", "ethers": "^6.7.0", - "execution-apis": "git://github.com/ethereum/execution-apis.git#584905270d8ad665718058060267061ecfd79ca5", + "execution-apis": "git://github.com/ethereum/execution-apis.git#7907424db935b93c2fe6a3c0faab943adebe8557", "shelljs": "^0.8.5", "ts-mocha": "^9.0.2", "ts-node": "^10.8.1", diff --git a/packages/server/src/validator/constants.ts b/packages/server/src/validator/constants.ts index 5ac1a9b1f6..3274cb7d2f 100644 --- a/packages/server/src/validator/constants.ts +++ b/packages/server/src/validator/constants.ts @@ -21,7 +21,7 @@ export const BASE_HEX_REGEX = '^0[xX][a-fA-F0-9]'; export const ERROR_CODE = -32602; export const DEFAULT_HEX_ERROR = 'Expected 0x prefixed hexadecimal value'; -export const HASH_ERROR = '0x prefixed string representing the hash (32 bytes)'; +export const HASH_ERROR = 'Expected 0x prefixed string representing the hash (32 bytes)'; export const ADDRESS_ERROR = 'Expected 0x prefixed string representing the address (20 bytes)'; export const BLOCK_NUMBER_ERROR = 'Expected 0x prefixed hexadecimal block number, or the string "latest", "earliest" or "pending"'; diff --git a/packages/server/src/validator/methods.ts b/packages/server/src/validator/methods.ts index af01fa6529..90858199db 100644 --- a/packages/server/src/validator/methods.ts +++ b/packages/server/src/validator/methods.ts @@ -147,7 +147,7 @@ export const METHODS: { [key: string]: IMethodValidation } = { }, 1: { required: true, - type: 'hex', + type: 'hex64', }, 2: { type: 'blockNumber|blockHash', diff --git a/packages/server/src/validator/types.ts b/packages/server/src/validator/types.ts index fd92b2e690..837afe30dd 100644 --- a/packages/server/src/validator/types.ts +++ b/packages/server/src/validator/types.ts @@ -84,6 +84,10 @@ export const TYPES: { [key: string]: ITypeValidation } = { test: (param: string) => new RegExp(Constants.BASE_HEX_REGEX + '*$').test(param), error: Constants.DEFAULT_HEX_ERROR, }, + hex64: { + test: (param: string) => new RegExp(Constants.BASE_HEX_REGEX + '{1,64}$').test(param), + error: Constants.HASH_ERROR, + }, topicHash: { test: (param: string) => new RegExp(Constants.BASE_HEX_REGEX + '{64}$').test(param) || param === null, error: Constants.TOPIC_HASH_ERROR, diff --git a/packages/server/tests/acceptance/conformityTests.spec.ts b/packages/server/tests/acceptance/conformityTests.spec.ts index afd05920e9..7bc7ed0a1e 100644 --- a/packages/server/tests/acceptance/conformityTests.spec.ts +++ b/packages/server/tests/acceptance/conformityTests.spec.ts @@ -1,12 +1,36 @@ +/* + * + * Hedera JSON RPC Relay + * + * Copyright (C) 2022-2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import fs from 'fs'; -const { bytecode } = require('../contracts/Basic.json'); -const path = require('path'); +import { bytecode } from '../contracts/Basic.json'; +import path from 'path'; const directoryPath = path.resolve(__dirname, '../../../../node_modules/execution-apis/tests'); -const axios = require('axios'); -const openRpcData = require('../../../../docs/openrpc.json'); -require('dotenv').config(); +import axios from 'axios'; +import openRpcData from '../../../../docs/openrpc.json'; +import Ajv from 'ajv'; +import addFormats from 'ajv-formats'; import { signTransaction } from '../../../relay/tests/helpers'; import { expect } from 'chai'; +import { config } from 'dotenv'; +config(); + let currentBlockHash; let legacyTransactionAndBlockHash; let transaction2930AndBlockHash; @@ -28,13 +52,15 @@ const LEGACY_CONTRACT_FILE_NAME = 'get-legacy-contract.io'; const LEGACY_TX_FILE_NAME = 'get-legacy-tx.io'; const LEGACY_RECEIPT_FILE_NAME = 'get-legacy-receipt.io'; const NOT_FOUND_TX_FILE_NAME = 'get-notfound-tx.io'; -const ETHEREUM_NETWORK_BLOCK_HASH = '0x7cb4dd3daba1f739d0c1ec7d998b4a2f6fd83019116455afa54ca4f49dfa0ad4'; +const ETHEREUM_NETWORK_BLOCK_HASH = '0xac5c61edb087a51279674fe01d5c1f65eac3fd8597f9bea215058e745df8088e'; const ETHEREUM_NETWORK_SIGNED_TRANSACTION = '0xf86709843b9aca018261a894aa000000000000000000000000000000000000000a825544820a95a0281582922adf6475f5b2241f0a4f886dafa947ecdc5913703b7840344a566b45a05f685fc099161126637a12308f278a8cd162788a6c6d5aee4d425cde261ba35d'; const ETHEREUM_NETWORK_ACCOUNT_HASH = '0x5C41A21F14cFe9808cBEc1d91b55Ba75ed327Eb6'; const EMPTY_TX_HASH = '0x0000000000000000000000000000000000000000000000000000000000000000'; const NONEXISTENT_TX_HASH = '0x00000000000000000000000000000000000000000000000000000000deadbeef'; - +const ajv = new Ajv({ strict: false }); +addFormats(ajv); +let execApisOpenRpcData; let legacyTransaction = { chainId: 0x12a, to: receiveAccountAddress, @@ -85,7 +111,7 @@ async function getTransactionCount() { params: [sendAccountAddress, 'latest'], }; - const response = await sendRequestToRelay(request); + const response = await sendRequestToRelay(request, false); return response.result; } @@ -98,7 +124,7 @@ async function getLatestBlockHash() { id: 0, }; - const response = await sendRequestToRelay(request); + const response = await sendRequestToRelay(request, false); return response.result.hash; } @@ -111,18 +137,22 @@ function splitReqAndRes(content) { * @returns {{ request: string, response: string }} - An object containing the separated request and response strings. */ const lines = content.split('\n'); - const filteredLines = lines.filter((line) => line != '').map((line) => line.slice(3)); + const filteredLines = lines.filter((line) => line != '' && !line.startsWith('//')).map((line) => line.slice(3)); return { request: filteredLines[0], response: filteredLines[1] }; } -async function sendRequestToRelay(request) { +async function sendRequestToRelay(request, needError) { try { const response = await axios.post(relayUrl, request); return response.data; } catch (error) { console.error(error); - throw error; + if (needError) { + return error; + } else { + throw error; + } } } @@ -136,14 +166,14 @@ async function signAndSendRawTransaction(transaction) { params: [signed], }; - const response = await sendRequestToRelay(request); + const response = await sendRequestToRelay(request, false); const requestTransactionReceipt = { id: 'test_id', jsonrpc: '2.0', method: 'eth_getTransactionReceipt', params: [response.result], }; - const transactionReceipt = await sendRequestToRelay(requestTransactionReceipt); + const transactionReceipt = await sendRequestToRelay(requestTransactionReceipt, false); return { transactionHash: response.result, blockHash: transactionReceipt.result.blockHash, @@ -193,15 +223,30 @@ async function checkRequestBody(fileName, request) { return request; } -function checkResponseFormat(fileName, actualReponse, expectedResponse) { +function checkResponseFormat(actualReponse, expectedResponse) { const actualResponseKeys = extractKeys(actualReponse); const expectedResponseKeys = extractKeys(expectedResponse); const missingKeys = expectedResponseKeys.filter((key) => !actualResponseKeys.includes(key)); - if ((fileName === DYNAMIC_FEE_FILE_NAME || fileName === ACCESS_LIST_FILE_NAME) && missingKeys[0] === 'result.v') { - return []; + if (missingKeys.length > 0) { + return true; + } else { + return false; } +} - return missingKeys; +function findSchema(file) { + const schema = execApisOpenRpcData.methods.find((method) => method.name === file)?.result?.schema; + + return schema; +} + +function isResponseValid(schema, response) { + const validate = ajv.compile(schema); + const valid = validate(response.result); + + expect(validate.errors).to.be.null; + + return valid; } function extractKeys(obj, prefix = '') { @@ -260,7 +305,7 @@ function formatTransactionByHashAndReceiptRequests(fileName, request) { return request; } -async function processFileContent(file, content) { +async function processFileContent(directory, file, content) { /** * Processes a file from the execution apis repo * containing test request and response data. @@ -271,10 +316,13 @@ async function processFileContent(file, content) { */ console.log('Executing for ', file); const modifiedRequest = await checkRequestBody(file, JSON.parse(content.request)); - const response = await sendRequestToRelay(modifiedRequest); - const missingKeys = checkResponseFormat(file, response, JSON.parse(content.response)); - - return missingKeys; + const needError = JSON.parse(content.response).error; + const response = await sendRequestToRelay(modifiedRequest, needError); + const schema = findSchema(directory); + const valid = needError + ? checkResponseFormat(response.response.data, content.response) + : isResponseValid(schema, response); + expect(valid).to.be.true; } describe('@api-conformity Ethereum execution apis tests', function () { @@ -288,23 +336,29 @@ describe('@api-conformity Ethereum execution apis tests', function () { }); //Reading the directories within the ethereum execution api repo let directories = fs.readdirSync(directoryPath); - const relaySupportedMethodNames = openRpcData.methods.map((method) => method.name); //Filtering in order to use only the tests for methods we support in our relay directories = directories.filter((directory) => relaySupportedMethodNames.includes(directory)); - for (const directory of directories) { const filePath = path.join(directoryPath, directory); - if (fs.statSync(filePath).isDirectory()) { const files = fs.readdirSync(path.resolve(directoryPath, directory)); for (const file of files) { - it(`Executing for ${directory}`, async () => { + it(`Executing for ${directory} and ${file}`, async () => { + //We are excluding these directories, since these tests in execution-apis repos + //use set of contracts which are not deployed on our network + if (directory === 'eth_getLogs' || directory === 'eth_call' || directory === 'eth_estimateGas') { + return; + } + execApisOpenRpcData = require('../../../../openrpc_exec_apis.json'); + //Currently, we do not support blobs + if (file.includes('blob')) { + return; + } const data = fs.readFileSync(path.resolve(directoryPath, directory, file)); const content = splitReqAndRes(data.toString('utf-8')); - const missingKeys = await processFileContent(file, content); - expect(missingKeys).to.be.empty; + await processFileContent(directory, file, content); }); } } diff --git a/packages/server/tests/integration/server.spec.ts b/packages/server/tests/integration/server.spec.ts index 05720ec4ca..db4dc47658 100644 --- a/packages/server/tests/integration/server.spec.ts +++ b/packages/server/tests/integration/server.spec.ts @@ -1826,7 +1826,7 @@ describe('RPC Server', function () { BaseTest.invalidParamError( error.response, Validator.ERROR_CODE, - `Invalid parameter 1: ${Validator.DEFAULT_HEX_ERROR}, value: 1234`, + `Invalid parameter 1: ${Validator.HASH_ERROR}, value: 1234`, ); } }); diff --git a/packages/server/tests/integration/validator.spec.ts b/packages/server/tests/integration/validator.spec.ts index f112e4adec..5d1b3a5a3e 100644 --- a/packages/server/tests/integration/validator.spec.ts +++ b/packages/server/tests/integration/validator.spec.ts @@ -20,6 +20,7 @@ import { expect } from 'chai'; import { OBJECTS_VALIDATIONS, TransactionObject, Validator } from '../../src/validator'; +import * as Constants from '../../src/validator/constants'; describe('Validator', async () => { function expectInvalidParam(index: number | string, message: string, paramValue?: string) { @@ -766,7 +767,7 @@ describe('Validator', async () => { }); validatorObject.validate(); }).to.throw( - `Invalid parameter 'topics' for EthSubscribeLogsParamsObject: Expected an array or array of arrays containing 0x prefixed string representing the hash (32 bytes) of a topic, value: NotHEX`, + `Invalid parameter 'topics' for EthSubscribeLogsParamsObject: Expected an array or array of arrays containing ${Constants.HASH_ERROR} of a topic, value: NotHEX`, ); }); @@ -778,7 +779,7 @@ describe('Validator', async () => { }); validatorObject.validate(); }).to.throw( - `Invalid parameter 'topics' for EthSubscribeLogsParamsObject: Expected an array or array of arrays containing 0x prefixed string representing the hash (32 bytes) of a topic, value: null`, + `Invalid parameter 'topics' for EthSubscribeLogsParamsObject: Expected an array or array of arrays containing ${Constants.HASH_ERROR} of a topic, value: null`, ); }); diff --git a/packages/ws-server/tests/acceptance/subscribe.spec.ts b/packages/ws-server/tests/acceptance/subscribe.spec.ts index b840ce913e..4e7f61a2c8 100644 --- a/packages/ws-server/tests/acceptance/subscribe.spec.ts +++ b/packages/ws-server/tests/acceptance/subscribe.spec.ts @@ -878,7 +878,7 @@ describe('@web-socket-batch-3 eth_subscribe', async function () { it('Calling eth_subscribe Logs with an invalid topics should fail', async function () { const expectedError = predefined.INVALID_PARAMETER( `'topics' for EthSubscribeLogsParamsObject`, - `Expected an array or array of arrays containing 0x prefixed string representing the hash (32 bytes) of a topic, value: 0x000`, + `Expected an array or array of arrays containing Expected 0x prefixed string representing the hash (32 bytes) of a topic, value: 0x000`, ); await Assertions.assertPredefinedRpcError(expectedError, wsProvider.send, true, wsProvider, [