diff --git a/.github/workflows/get-sha.yml b/.github/workflows/get-sha.yml new file mode 100644 index 000000000..aaea83d5d --- /dev/null +++ b/.github/workflows/get-sha.yml @@ -0,0 +1,25 @@ +name: Set SHAs +on: + workflow_call: {} + +jobs: + get-sha: + runs-on: ubuntu-latest + steps: + - name: Check SHA + uses: nrwl/nx-set-shas@v3 + id: sha + with: + main-branch-name: 'master' + + check_status: + needs: [get-sha] + runs-on: ubuntu-latest + steps: + - name: output SHAs + run: | + if [${{needs.get-sha.result}} == 'success']; then + echo "get sha failed" + exit 1 + fi + echo "All steps passed" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/intigration-test-long-running.yml similarity index 60% rename from .github/workflows/ci.yml rename to .github/workflows/intigration-test-long-running.yml index b00c1c14e..6542e2479 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/intigration-test-long-running.yml @@ -1,44 +1,20 @@ -name: Testing +name: Intigration Testing Datil Long Running on: - push: - branches: - - master - pull_request: - branches: - - master - - staging/** - - feat/** - - feature/** + workflow_call: + outputs: + intigration-tests: + description: 'unit test results' + value: ${{jobs.integration-tests.result}} + inputs: + commit: + required: true + type: string + workflow_dispatch: {} + jobs: - unit-tests: - runs-on: warp-ubuntu-latest-x64-16x - timeout-minutes: 30 - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - uses: nrwl/nx-set-shas@v3 - with: - main-branch-name: 'master' - - uses: actions/checkout@v3 - - name: Use Node.js - uses: buildjet/setup-node@v3 - with: - node-version: 20 - cache: yarn - cache-dependency-path: ${{ github.workspace }}/yarn.lock - - name: Install Dependencies - run: yarn - - name: Build - run: yarn build:dev - - name: Run Unit tests - run: yarn test:ci integration-tests: runs-on: ubuntu-latest - timeout-minutes: 30 - env: - DATIL_COMMIT_HASH: ae3c20e07eb933b61073689b95f56867c03de252 + timeout-minutes: 40 steps: - name: Checkout repo uses: actions/checkout@v2 @@ -50,7 +26,7 @@ jobs: with: fetch-depth: 0 repository: LIT-Protocol/lit-assets - ref: ${{env.DATIL_COMMIT_HASH}} + ref: ${{inputs.commit}} token: ${{secrets.GH_PAT}} path: ${{ github.workspace }}/lit-assets/ submodules: false @@ -76,7 +52,7 @@ jobs: run: docker pull ghcr.io/lit-protocol/shiva:latest - name: Run Shiva Container id: shiva-runner - run: docker run -d -m 32g -p 8000:8000 -p 8545:8545 -p 7470:7470 -p 7471:7471 -p 7472:7472 -p 7473:7473 -p 7474:7474 -p 7475:7475 -v ${{github.workspace}}/lit-assets:/data -e GH_PAT=${{secrets.GH_PAT}} -e HASH=$DATIL_COMMIT_HASH -e IPFS_API_KEY=${{secrets.IPFS_API_KEY}} --name shiva ghcr.io/lit-protocol/shiva:latest + run: docker run -d -m 32g -p 8000:8000 -p 8545:8545 -p 7470:7470 -p 7471:7471 -p 7472:7472 -p 7473:7473 -p 7474:7474 -p 7475:7475 -v ${{github.workspace}}/lit-assets:/data -e GH_PAT=${{secrets.GH_PAT}} -e HASH=${{inputs.commit}} -e IPFS_API_KEY=${{secrets.IPFS_API_KEY}} --name shiva ghcr.io/lit-protocol/shiva:latest - name: Install project dependencies run: yarn - uses: nrwl/nx-set-shas@v3 @@ -87,18 +63,10 @@ jobs: run: yarn build:dev - name: Copy ENV File run: cp .env.ci .env - - name: End to End Tests - Connection - id: connection + - name: End to End Tests + id: e2e-tests if: steps.build.outputs.exit_code == 0 - run: yarn test:e2e -t 'Connections' - - name: End Tests Session Compaitiblity - id: session - if: steps.connection.outputs.exit_code == 0 - run: yarn test:e2e -t 'SessionSigs' - - name: End to End Tests - PKP Ethers - if: steps.session.outputs.exit_code == 0 - id: pkp - run: yarn test:e2e -t 'PKP Ethers' + run: yarn test:e2e:ci:epoch - name: Get Container Logs if: always() run: docker logs shiva @@ -106,4 +74,6 @@ jobs: id: container-stop if: steps.shiva-pull.outputs.exit_code == 0 run: docker stop shiva && docker rm shiva - + - name: Post Pull Shiva Image + if: steps.shiva-pull.outputs.exit_code == 0 + run: docker rmi ghcr.io/lit-protocol/shiva diff --git a/.github/workflows/intigration-test.yml b/.github/workflows/intigration-test.yml new file mode 100644 index 000000000..e6de8966f --- /dev/null +++ b/.github/workflows/intigration-test.yml @@ -0,0 +1,79 @@ +name: Intigration Testing Datil +on: + workflow_call: + outputs: + intigration-tests: + description: 'unit test results' + value: ${{jobs.integration-tests.result}} + inputs: + commit: + required: true + type: string + workflow_dispatch: {} + +jobs: + integration-tests: + runs-on: ubuntu-latest + timeout-minutes: 40 + steps: + - name: Checkout repo + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Checkout Lit Actions + uses: actions/checkout@v4 + id: checkout + with: + fetch-depth: 0 + repository: LIT-Protocol/lit-assets + ref: ${{inputs.commit}} + token: ${{secrets.GH_PAT}} + path: ${{ github.workspace }}/lit-assets/ + submodules: false + sparse-checkout: | + blockchain + rust/lit-node + - name: Check LA dir + run: ls -la ${{github.workspace}}/lit-assets + - name: Setup Lit Bockchain Dependencies + uses: buildjet/setup-node@v3 + with: + node-version: 20 + cache: npm + cache-dependency-path: ${{ github.workspace }}/lit-assets/blockchain/contracts/package-lock.json + - name: Install Blockchain Deps + run: npm i + working-directory: ${{github.workspace}}/lit-assets/blockchain/contracts + - name: Docker login + id: login + run: docker login ghcr.io/ -u ${{secrets.GH_USER}} --password ${{secrets.GH_PAT}} + - name: Pull Shiva Container + id: shiva-pull + run: docker pull ghcr.io/lit-protocol/shiva:latest + - name: Run Shiva Container + id: shiva-runner + run: docker run -d -m 32g -p 8000:8000 -p 8545:8545 -p 7470:7470 -p 7471:7471 -p 7472:7472 -p 7473:7473 -p 7474:7474 -p 7475:7475 -v ${{github.workspace}}/lit-assets:/data -e GH_PAT=${{secrets.GH_PAT}} -e HASH=${{inputs.commit}} -e IPFS_API_KEY=${{secrets.IPFS_API_KEY}} --name shiva ghcr.io/lit-protocol/shiva:latest + - name: Install project dependencies + run: yarn + - uses: nrwl/nx-set-shas@v3 + with: + main-branch-name: 'master' + - name: Build packages + id: build + run: yarn build:dev + - name: Copy ENV File + run: cp .env.ci .env + - name: End to End Tests + id: e2e-tests + if: steps.build.outputs.exit_code == 0 + run: yarn test:e2e:ci + - name: Get Container Logs + if: always() + run: docker logs shiva + - name: Post Pull Shiva Container + id: container-stop + if: steps.shiva-pull.outputs.exit_code == 0 + run: docker stop shiva && docker rm shiva + - name: Post Pull Shiva Image + if: steps.shiva-pull.outputs.exit_code == 0 + run: docker rmi ghcr.io/lit-protocol/shiva \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3b93e3017..09e47eb01 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,9 +1,12 @@ name: lint on: - pull_request: - push: - branches: - - master + workflow_call: + outputs: + lint_result: + description: 'lint result' + value: ${{jobs.linter.result}} + + workflow_dispatch: {} jobs: linter: runs-on: ubuntu-latest @@ -22,3 +25,18 @@ jobs: run: yarn install - name: Lint run: yarn nx format:check --all + check_status: + needs: + [ + linter, + ] + runs-on: ubuntu-latest + steps: + - name: Check Status + working-directory: ${{ github.workspace }} + run: | + if [ ${{ needs.linter.result }} != 'success' ]; then + echo "Linting failed" + exit 1 + fi + echo "All tests passed" diff --git a/.github/workflows/list-changed-files.yml b/.github/workflows/list-changed-files.yml new file mode 100644 index 000000000..12baa50c6 --- /dev/null +++ b/.github/workflows/list-changed-files.yml @@ -0,0 +1,64 @@ +# This workflow is triggered by a PR being created or pushed to. It checks out the develop branch and gets the sha of the develop branch. It then checks for changes in the files and triggers the appropriate workflows based on the changes. It also checks the status of all the triggered workflows and fails the build if any of the workflows fail. It runs the test against a new temporary branch which is created by merging the PR into develop. This means that someone pushing to develop can break your PR even if you haven't changed anything! This is a tradeoff to ensure that the tests are run against the latest code on develop. If you want to run the tests against the code in your PR, you can run the tests locally. +name: master-trigger +on: + workflow_call: + outputs: + lit_core_changed: + description: 'lit_core_changed' + value: ${{ jobs.list_changed_files.outputs.lit_core_changed }} + + workflow_dispatch: {} + + +jobs: + get_base_commit_hash: + runs-on: ubuntu-latest + outputs: + base_sha: ${{ steps.base_sha.outputs.base_sha }} + steps: + - name: Get branch name + id: branch-name + uses: tj-actions/branch-names@v7 + + - name: Make sure we checked out develop, so we can get it's sha + uses: actions/checkout@v4 + with: + ref: ${{ steps.branch-name.outputs.base_ref_branch }} + + - name: Get sha of base branch + id: base_sha + run: | + export BASE_SHA=$(git rev-parse HEAD) + echo "Base sha is $BASE_SHA" + echo "base_sha=$BASE_SHA" >> $GITHUB_OUTPUT + + list_changed_files: + runs-on: ubuntu-latest + needs: [get_base_commit_hash] + outputs: + lit_core_changed: ${{ steps.changed-files-yaml.outputs.lit_core_any_modified }} + + steps: + - name: Checkout js-sdk + uses: actions/checkout@v4 + + - name: Print base sha + run: | + echo "Base sha is ${{ needs.get_base_commit_hash.outputs.base_sha }}" + + - name: Get changed files + id: changed-files-yaml + uses: tj-actions/changed-files@v41 + with: + base_sha: ${{ needs.get_base_commit_hash.outputs.base_sha }} + files_yaml: | + lit_core: + - packages/core/** + any: + - ./** + + - name: List all changed files + run: | + for file in ${{ steps.changed-files-yaml.outputs.any_all_modified_files }}; do + echo "$file was changed" + done diff --git a/.github/workflows/master-trigger.yml b/.github/workflows/master-trigger.yml new file mode 100644 index 000000000..e5af53944 --- /dev/null +++ b/.github/workflows/master-trigger.yml @@ -0,0 +1,50 @@ +name: root-trigger +on: + pull_request: + workflow_dispatch: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true +jobs: + list_changed_files_and_trigger_workflows: + uses: ./.github/workflows/list-changed-files.yml + secrets: inherit + get_shas: + uses: ./.github/workflows/get-sha.yml + secrets: inherit + lint: + needs: [list_changed_files_and_trigger_workflows] + uses: ./.github/workflows/lint.yml + secrets: inherit + unit_tests: + uses: ./.github/workflows/unit-test.yml + secrets: inherit + needs: [lint, get_shas] + intigration_tests: + uses: ./.github/workflows/intigration-test.yml + secrets: inherit + if: ${{needs.list_changed_files_and_trigger_workflows.outputs.lit_core_changed == 'true'}} + needs: [lint, get_shas, list_changed_files_and_trigger_workflows, unit_tests] + with: + commit: ${{vars.DATIL_COMMIT_HASH}} + intigration_tests_longrun_epoch: + uses: ./.github/workflows/intigration-test-long-running.yml + secrets: inherit + needs: [lint, get_shas, list_changed_files_and_trigger_workflows, unit_tests] + with: + commit: ${{vars.DATIL_COMMIT_HASH}} + all_jobs: + if: ${{ always() }} + needs: [ + lint, + unit_tests, + intigration_tests + ] + runs-on: ubuntu-latest + steps: + - name: Check status + run: | + echo "Linter ${{needs.lint.result}}" + echo "Tests Unit ${{needs.unit_tests.result}}" + echo "Tests Intigration" diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml new file mode 100644 index 000000000..ba2819946 --- /dev/null +++ b/.github/workflows/unit-test.yml @@ -0,0 +1,36 @@ +name: Unit Testing +on: + workflow_call: + outputs: + unit-tests: + description: 'unit test results' + value: ${{jobs.unit-tests.result}} + + workflow_dispatch: {} + +jobs: + unit-tests: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repo + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/checkout@v3 + - name: Use Node.js + uses: buildjet/setup-node@v3 + with: + node-version: 20 + cache: yarn + cache-dependency-path: ${{ github.workspace }}/yarn.lock + - name: Check SHA + uses: nrwl/nx-set-shas@v3 + with: + main-branch-name: 'master' + - name: Install Dependencies + run: yarn + - name: Build + run: yarn build:dev + - name: Run Unit tests + run: yarn test:unit \ No newline at end of file diff --git a/package.json b/package.json index 7a0759cce..82ca91236 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "postBuild:mapDepsToDist": "node tools/scripts/map-deps-to-dist.mjs packages dist @lit-protocol", "test:ci": "nx affected --target=test --exclude=e2e-tests", "test:e2e": "nx run e2e-tests:test", + "test:e2e:ci": "nx run e2e-tests:test-ci", + "test:e2e:ci:epoch": "nx run e2e-tests:test ./packages/e2e-tests/src/tests/Epoch/EpochTransition.spec.ts -t 'Epoch Transition'", "test:unit": "nx run-many --target=test --exclude=e2e-tests", "test:unit:watch": "nx run-many --target=test --watch", "test:unit:bun": "bun ./tools/scripts/unit-test-with-bun.mjs", diff --git a/packages/core/src/lib/lit-core.ts b/packages/core/src/lib/lit-core.ts index 31d565c6f..eb0af2fd0 100644 --- a/packages/core/src/lib/lit-core.ts +++ b/packages/core/src/lib/lit-core.ts @@ -312,13 +312,12 @@ export class LitCore { log(`New state detected: "${state}"`); const validatorData = await this._getValidatorData(); - + log('validator info ', validatorData.epochInfo); if (state === StakingStates.Active) { // We always want to track the most recent epoch number on _all_ networks - this._epochState = await this._fetchCurrentEpochState( - validatorData.epochInfo - ); + this._epochState = await this._fetchCurrentEpochState(); + //log("Epoch state: ", this._epochState.currentNumber); if (CENTRALISATION_BY_NETWORK[this.config.litNetwork] !== 'centralised') { // We don't need to handle node urls changing on centralised networks, since their validator sets are static @@ -328,8 +327,8 @@ export class LitCore { ); const existingNodeUrls: string[] = [...this.config.bootstrapUrls]; - const delta: string[] = validatorData.bootstrapUrls.filter((item) => - existingNodeUrls.includes(item) + const delta: string[] = validatorData.bootstrapUrls.filter( + (item) => existingNodeUrls.indexOf(item) < 0 ); // if the sets differ we reconnect. if (delta.length > 1) { @@ -347,9 +346,13 @@ export class LitCore { delta, 'starting node connection' ); - } - await this.connect(); + await this.connect(); + } else { + log( + 'validator sets found to match previous epoch, skipping reconnect' + ); + } } catch (err: unknown) { // FIXME: We should emit an error event so that consumers know that we are de-synced and can connect() again // But for now, our every-30-second network sync will fix things in at most 30s from now. @@ -985,6 +988,7 @@ export class LitCore { Math.floor(EPOCH_PROPAGATION_DELAY / 1000) && this._epochCache.currentNumber >= 3 ) { + log('found not to be time for epoch propigation'); return this._epochCache.currentNumber - 1; } return this._epochCache.currentNumber; diff --git a/packages/e2e-tests/ci/jest.config.ts b/packages/e2e-tests/ci/jest.config.ts new file mode 100644 index 000000000..106af2bbf --- /dev/null +++ b/packages/e2e-tests/ci/jest.config.ts @@ -0,0 +1,24 @@ +/** @type {import('jest').Config} */ +const config = { + displayName: 'e2e-tests', + preset: 'ts-jest', + transform: { + '^.+\\.ts$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/packages/e2e-tests', + setupFilesAfterEnv: ['../../../jest.setup.js'], + testEnvironment: './../setup.config.js', + globalSetup: './../setup.jest.js', + globalTeardown: './../teardown.jest.js', + roots: ['./../src/tests/Core'], + testPathIgnorePatterns: [ + './../src/tests/Epoch', + './../src/tests/WrappedKeys', + './../src/tests/Relayer', + './../src/tests/Delegation', + ], +}; + +//@ts-ignore +module.exports = config; diff --git a/packages/e2e-tests/jest.config.ts b/packages/e2e-tests/jest.config.ts index 23b7c8501..6b43f8d9e 100644 --- a/packages/e2e-tests/jest.config.ts +++ b/packages/e2e-tests/jest.config.ts @@ -1,5 +1,5 @@ -/* eslint-disable */ -export default { +/** @type {import('jest').Config} */ +const config = { displayName: 'e2e-tests', preset: 'ts-jest', transform: { @@ -9,4 +9,8 @@ export default { coverageDirectory: '../../coverage/packages/e2e-tests', setupFilesAfterEnv: ['../../jest.setup.js'], testEnvironment: './setup.config.js', + globalSetup: './setup.jest.js', + globalTeardown: './teardown.jest.js', }; + +module.exports = config; diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index eb8048114..c9b958866 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -17,6 +17,9 @@ "tags": [ "universal" ], + "scripts": { + "test:long-run": "node src/tests/long-running/build.mjs && dotenvx run --env-file=../../.env -- node src/tests/long-running/build/test.mjs" + }, "version": "6.10.0", "private": true, "main": "./dist/src/index.js", diff --git a/packages/e2e-tests/project.json b/packages/e2e-tests/project.json index dad0e4f51..45c6efe03 100644 --- a/packages/e2e-tests/project.json +++ b/packages/e2e-tests/project.json @@ -28,7 +28,24 @@ "options": { "jestConfig": "packages/e2e-tests/jest.config.ts", "passWithNoTests": true, - "runInBand": false + "runInBand": false, + "json": true + } + }, + "test-ci": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/packages/e2e-tests"], + "options": { + "jestConfig": "packages/e2e-tests/ci/jest.config.ts", + "runInBand": true, + "passWithNoTests": true, + "json": true + } + }, + "test-long-run": { + "executor": "nx:run-script", + "options": { + "script": "test:long-run" } }, "testWatch": { diff --git a/packages/e2e-tests/setup.config.js b/packages/e2e-tests/setup.config.js index c5bbf0138..41a4ae0d9 100644 --- a/packages/e2e-tests/setup.config.js +++ b/packages/e2e-tests/setup.config.js @@ -1,30 +1,14 @@ -/** - * Jest Node Env docs: https://jestjs.io/docs/configuration#testenvironment-string - * Global setup file for jest e2e tests - * Loads all global context and provides to each test runner as needed - * env loading has been moved into - */ +const TestEnvironment = require('jest-environment-node').TestEnvironment; -const NodeEnvironment = require('jest-environment-node').TestEnvironment; - -const TinnyEnvironment = require('@lit-protocol/tinny').TinnyEnvironment; - -require('dotenv').config(); -console.log('loaded configuration from .env', __dirname); - -class CustomEnvironment extends NodeEnvironment { - _hasLoadedTinny = false; +class CustomEnvironment extends TestEnvironment { constructor(config) { super(config); } async setup() { await super.setup(); - if (!this._hasLoadedTinny) { - this.global.devEnv = new TinnyEnvironment(); - await this.global.devEnv.init(); - this._hasLoadedTinny = true; - } + require('dotenv').config(); + console.log('loaded configuration from .env', __dirname); } async teardown() { diff --git a/packages/e2e-tests/setup.jest.js b/packages/e2e-tests/setup.jest.js new file mode 100644 index 000000000..6e6f5ef2d --- /dev/null +++ b/packages/e2e-tests/setup.jest.js @@ -0,0 +1,24 @@ +require('dotenv').config(); +console.log('loaded configuration from .env', __dirname); + +module.exports = async function (globalConfig, projectConfig) { + console.log('starting global setup, initalizing testnet'); + + if (process.env['USE_SHIVA'] === true) { + const tinny = require('@lit-protocol/tinny'); + global.__SHIVA__ = new tinny.ShivaClient(); + global.__TESTNET__ = await global.__SHIVA__.startTestnetManager(); + const state = await global.__TESTNET__.pollTestnetForActive(); + console.log('test net state is ', state); + if (state === `UNKNOWN`) { + console.log( + 'Testnet state found to be Unknown meaning there was an error with testnet creation. shutting down' + ); + throw new Error(`Error while creating testnet, aborting test run`); + } + const config = await global.__TESTNET__.getTestnetConfig(); + console.log(config); + } else { + console.log('Skipping network creation, using live testnet'); + } +}; diff --git a/packages/e2e-tests/src/tests/PKPEthers.spec.ts b/packages/e2e-tests/src/tests/Core/PKPEthers.spec.ts similarity index 98% rename from packages/e2e-tests/src/tests/PKPEthers.spec.ts rename to packages/e2e-tests/src/tests/Core/PKPEthers.spec.ts index f3d664cbe..0914f25e8 100644 --- a/packages/e2e-tests/src/tests/PKPEthers.spec.ts +++ b/packages/e2e-tests/src/tests/Core/PKPEthers.spec.ts @@ -39,9 +39,10 @@ try { describe('PKP Ethers', () => { let devEnv: TinnyEnvironment; let alice: TinnyPerson; + beforeAll(async () => { - //@ts-expect-error defined in global - devEnv = global.devEnv; + devEnv = new TinnyEnvironment(); + await devEnv.init(); }); beforeEach(async () => { @@ -53,7 +54,11 @@ describe('PKP Ethers', () => { }); beforeEach(() => { - jest.spyOn(console, 'warn').mockImplementation(jest.fn()); + jest.spyOn(console, 'warn').mockImplementation( + jest.fn(() => { + return void 0; + }) + ); }); afterAll(async () => { @@ -758,13 +763,10 @@ const signWithAuthContext = async ( }); await pkpEthersWallet.init(); + const signature: string = await pkpEthersWallet.signMessage(alice.loveLetter); - expect( - pkpEthersWallet.signMessage(alice.loveLetter).then((signature) => { - expect(signature).toBeDefined(); - expect(signature.length).toEqual(132); - }) - ).resolves.not.toThrowError(); + expect(signature).toBeDefined(); + expect(signature.length).toEqual(132); }; const ethPersonalSign = async ( diff --git a/packages/e2e-tests/src/tests/SOLAuthSig.spec.ts b/packages/e2e-tests/src/tests/Core/SOLAuthSig.spec.ts similarity index 96% rename from packages/e2e-tests/src/tests/SOLAuthSig.spec.ts rename to packages/e2e-tests/src/tests/Core/SOLAuthSig.spec.ts index 0c78e2113..fc4283856 100644 --- a/packages/e2e-tests/src/tests/SOLAuthSig.spec.ts +++ b/packages/e2e-tests/src/tests/Core/SOLAuthSig.spec.ts @@ -16,8 +16,8 @@ describe('Sol AuthSig', () => { let devEnv: TinnyEnvironment; let alice: TinnyPerson; beforeAll(async () => { - //@ts-expect-error defined in global - devEnv = global.devEnv; + devEnv = new TinnyEnvironment(); + await devEnv.init(); }); beforeEach(async () => { diff --git a/packages/e2e-tests/src/tests/SessionSigs.spec.ts b/packages/e2e-tests/src/tests/Core/SessionSigs.spec.ts similarity index 99% rename from packages/e2e-tests/src/tests/SessionSigs.spec.ts rename to packages/e2e-tests/src/tests/Core/SessionSigs.spec.ts index 8943e7620..2b34e2d55 100644 --- a/packages/e2e-tests/src/tests/SessionSigs.spec.ts +++ b/packages/e2e-tests/src/tests/Core/SessionSigs.spec.ts @@ -33,8 +33,8 @@ describe('SessionSigs', () => { let devEnv: TinnyEnvironment; let alice: TinnyPerson; beforeAll(async () => { - //@ts-expect-error defined in global - devEnv = global.devEnv; + devEnv = new TinnyEnvironment(); + await devEnv.init(); }); beforeEach(async () => { diff --git a/packages/e2e-tests/src/tests/Core/connection.spec.ts b/packages/e2e-tests/src/tests/Core/connection.spec.ts new file mode 100644 index 000000000..8a8ae1d15 --- /dev/null +++ b/packages/e2e-tests/src/tests/Core/connection.spec.ts @@ -0,0 +1,63 @@ +import { expect, jest } from '@jest/globals'; + +import { TinnyEnvironment } from '@lit-protocol/tinny'; + +try { + jest.setTimeout(60000); +} catch (e) { + // ... continue execution +} + +describe('Connections', () => { + beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation( + jest.fn(() => { + void 0; + }) + ); + }); + + let devEnv: TinnyEnvironment; + beforeAll(async () => { + devEnv = new TinnyEnvironment(undefined, { useRLI: false }); + await devEnv.init(); + }); + + afterEach(async () => { + await devEnv.litNodeClient?.disconnect(); + }); + + it('Should connect to nodes and signal ready single connect', async () => { + await devEnv.litNodeClient?.disconnect(); + await devEnv.litNodeClient?.connect(); + expect(devEnv.litNodeClient).toBeDefined(); + expect(devEnv.litNodeClient?.ready).toBe(true); + expect(devEnv.litNodeClient?.config.litNetwork).toBe( + devEnv.processEnvs.NETWORK + ); + expect(devEnv.litNodeClient?.networkPubKey).toBeDefined(); + expect(devEnv.litNodeClient?.hdRootPubkeys).toBeDefined(); + expect(devEnv.litNodeClient?.connectedNodes?.size).toBeGreaterThanOrEqual( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain + devEnv.litNodeClient?.config?.minNodeCount! + ); + }); + + it('Should connect to nodes and signal ready after disconnect', async () => { + await devEnv.litNodeClient?.disconnect(); + + await devEnv.litNodeClient?.connect().then(() => { + expect(devEnv.litNodeClient).toBeDefined(); + expect(devEnv.litNodeClient?.ready).toBe(true); + expect(devEnv.litNodeClient?.config.litNetwork).toBe( + devEnv.processEnvs.NETWORK + ); + expect(devEnv.litNodeClient?.networkPubKey).toBeDefined(); + expect(devEnv.litNodeClient?.hdRootPubkeys).toBeDefined(); + expect(devEnv.litNodeClient?.connectedNodes?.size).toBeGreaterThanOrEqual( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain + devEnv.litNodeClient?.config?.minNodeCount! + ); + }); + }); +}); diff --git a/packages/e2e-tests/src/tests/Delegation.spec.ts b/packages/e2e-tests/src/tests/Delegation/Delegation.spec.ts similarity index 99% rename from packages/e2e-tests/src/tests/Delegation.spec.ts rename to packages/e2e-tests/src/tests/Delegation/Delegation.spec.ts index 55710912e..2c31eb02a 100644 --- a/packages/e2e-tests/src/tests/Delegation.spec.ts +++ b/packages/e2e-tests/src/tests/Delegation/Delegation.spec.ts @@ -19,8 +19,9 @@ describe('Delegation', () => { let bob: TinnyPerson; beforeAll(async () => { - //@ts-expect-error is defined - devEnv = global.devEnv; + devEnv = new TinnyEnvironment(); + await devEnv.init(); + alice = await devEnv.createRandomPerson(); bob = await devEnv.createRandomPerson(); }); diff --git a/packages/e2e-tests/src/tests/Epoch/EpochTransition.spec.ts b/packages/e2e-tests/src/tests/Epoch/EpochTransition.spec.ts new file mode 100644 index 000000000..1881d86a9 --- /dev/null +++ b/packages/e2e-tests/src/tests/Epoch/EpochTransition.spec.ts @@ -0,0 +1,130 @@ +import { expect, jest } from '@jest/globals'; + +import { + LitAbility, + LitActionResource, + LitPKPResource, +} from '@lit-protocol/auth-helpers'; +import { LitContracts } from '@lit-protocol/contracts-sdk'; +import { getPkpSessionSigs, TinnyEnvironment } from '@lit-protocol/tinny'; + +describe('Epoch Transition', () => { + beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation( + jest.fn(() => { + void 0; + }) + ); + }); + + let devEnv: TinnyEnvironment; + beforeAll(async () => { + devEnv = new TinnyEnvironment(undefined, { useRLI: false }); + await devEnv.init(); + }, 60 * 60 * 1_000); + + afterAll(async () => { + await devEnv.litNodeClient?.disconnect(); + }, 60 * 60 * 1_000); + + it( + 'Transition should trigger staking event', + async () => { + const connectedNodes: string[] | undefined = + devEnv.litNodeClient?.config.bootstrapUrls; + const currentEpoch = devEnv.litNodeClient?.currentEpochNumber; + await devEnv.testnet?.transitionEpochAndWait(); + + await new Promise((res) => { + setTimeout(res, 45_000); + }); + + expect(devEnv.litNodeClient?.ready).toBe(true); + + expect(connectedNodes?.length).toEqual( + devEnv.litNodeClient?.config.bootstrapUrls.length + ); + + for (const url of connectedNodes!) { + expect( + devEnv.litNodeClient?.config.bootstrapUrls.indexOf(url) + ).toBeGreaterThan(-1); + } + + expect(currentEpoch! + 1).toEqual( + devEnv.litNodeClient?.currentEpochNumber + ); + }, + 60 * 60 * 1_000 + ); + + it( + 'Validate BLS signature valid after epoch transition', + async () => { + const alice = await devEnv.createRandomPerson(); + await devEnv.testnet?.transitionEpochAndWait(); + + const session = await getPkpSessionSigs(devEnv, alice, [ + { + resource: new LitPKPResource('*'), + ability: LitAbility.PKPSigning, + }, + { + resource: new LitActionResource('*'), + ability: LitAbility.LitActionExecution, + }, + ]); + expect(session).toBeDefined(); + + devEnv.releasePrivateKeyFromUser(alice); + }, + 60 * 60 * 1_000 + ); + + it( + 'Validate BLS signature during epoch transition', + async () => { + const alice = await devEnv.createRandomPerson(); + devEnv.testnet?.transitionEpochAndWait(); + // give some time for the transition to start + await new Promise((res) => { + setTimeout(res, 10_000); + }); + const session = await getPkpSessionSigs(devEnv, alice, [ + { + resource: new LitPKPResource('*'), + ability: LitAbility.PKPSigning, + }, + { + resource: new LitActionResource('*'), + ability: LitAbility.LitActionExecution, + }, + ]); + + expect(session).toBeDefined(); + }, + 60 * 60 * 1_000 + ); + + it( + 'should return same epoch id in handshake when connecting during epoch transition', + async () => { + devEnv.testnet?.transitionEpochAndWait(); + // give some time for the transition to start + await new Promise((res) => { + setTimeout(res, 20_000); + }); + await devEnv.litNodeClient?.disconnect(); + await devEnv.litNodeClient?.connect(); + + const connnectionInfo = await LitContracts.getConnectionInfo({ + litNetwork: 'custom', + networkContext: devEnv.litNodeClient?.config.contractContext, + rpcUrl: devEnv.rpc, + nodeProtocol: devEnv.litNodeClient?.config.nodeProtocol, + }); + console.log(connnectionInfo); + }, + 60 * 60 * 1_000 + ); +}); diff --git a/packages/e2e-tests/src/tests/Relayer.spec.ts b/packages/e2e-tests/src/tests/Relayer/Relayer.spec.ts similarity index 94% rename from packages/e2e-tests/src/tests/Relayer.spec.ts rename to packages/e2e-tests/src/tests/Relayer/Relayer.spec.ts index 237a5a12d..aeb006570 100644 --- a/packages/e2e-tests/src/tests/Relayer.spec.ts +++ b/packages/e2e-tests/src/tests/Relayer/Relayer.spec.ts @@ -14,8 +14,8 @@ try { describe('Relayer', () => { let devEnv: TinnyEnvironment; beforeAll(async () => { - //@ts-expect-error global defined - devEnv = global.devEnv; + devEnv = new TinnyEnvironment(); + await devEnv.init(); }); beforeEach(() => { diff --git a/packages/e2e-tests/src/tests/WrappedKeys.spec.ts b/packages/e2e-tests/src/tests/WrappedKeys/WrappedKeys.spec.ts similarity index 99% rename from packages/e2e-tests/src/tests/WrappedKeys.spec.ts rename to packages/e2e-tests/src/tests/WrappedKeys/WrappedKeys.spec.ts index 90f7c258b..35b22fccc 100644 --- a/packages/e2e-tests/src/tests/WrappedKeys.spec.ts +++ b/packages/e2e-tests/src/tests/WrappedKeys/WrappedKeys.spec.ts @@ -73,8 +73,8 @@ describe('Wrapped Keys', () => { let devEnv: TinnyEnvironment; let alice: TinnyPerson; beforeAll(async () => { - //@ts-expect-error defined in global - devEnv = global.devEnv; + devEnv = new TinnyEnvironment(); + await devEnv.init(); }); beforeEach(async () => { diff --git a/packages/e2e-tests/src/tests/connection.spec.ts b/packages/e2e-tests/src/tests/connection.spec.ts deleted file mode 100644 index 0806423f4..000000000 --- a/packages/e2e-tests/src/tests/connection.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { expect, jest } from '@jest/globals'; - -import { TinnyEnvironment } from '@lit-protocol/tinny'; - -try { - jest.setTimeout(60000); -} catch (e) { - // ... continue execution -} - -describe('Connections', () => { - beforeEach(() => { - jest.spyOn(console, 'warn').mockImplementation(jest.fn()); - }); - - let devEnv: TinnyEnvironment; - beforeAll(() => { - //@ts-expect-error defined in global - devEnv = global.devEnv; - }); - - afterAll(async () => { - await devEnv.litNodeClient?.disconnect(); - }); - - it('Testing Network Handshake', async () => { - expect(devEnv.litNodeClient).toBeDefined(); - expect(devEnv.litNodeClient?.ready).toBe(true); - expect(devEnv.litNodeClient?.config.litNetwork).toBe( - devEnv.processEnvs.NETWORK - ); - expect(devEnv.litNodeClient?.networkPubKey).toBeDefined(); - expect(devEnv.litNodeClient?.hdRootPubkeys).toBeDefined(); - expect(devEnv.litNodeClient?.connectedNodes?.size).toBeGreaterThanOrEqual( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain - devEnv.litNodeClient?.config?.minNodeCount! - ); - }); -}); diff --git a/packages/e2e-tests/teardown.jest.js b/packages/e2e-tests/teardown.jest.js new file mode 100644 index 000000000..e3273131a --- /dev/null +++ b/packages/e2e-tests/teardown.jest.js @@ -0,0 +1,12 @@ +module.exports = async function (globalConfig, projectConfig) { + console.log('starting global teardown, stopping testnet'); + console.log(globalConfig); + console.log(projectConfig); + if (process.env['USE_SHIVA'] === true) { + const stopResp = await global.__TESTNET__.getTestnetConfig(); + + console.log('test suite cleanup last testnet config', stopResp); + } else { + console.log('Skipping network teardown, using live network'); + } +}; diff --git a/packages/e2e-tests/tsconfig.spec.json b/packages/e2e-tests/tsconfig.spec.json index 546f12877..79e606be3 100644 --- a/packages/e2e-tests/tsconfig.spec.json +++ b/packages/e2e-tests/tsconfig.spec.json @@ -5,5 +5,11 @@ "module": "commonjs", "types": ["jest", "node"] }, - "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] + "include": [ + "jest.config.ts", + "**/*.test.ts", + "**/*.spec.ts", + "**/*.d.ts", + "src/tests/long-running/tests/EpochTransition.ts" + ] } diff --git a/packages/lit-node-client-nodejs/src/lib/helpers/validate-bls-session-sig.ts b/packages/lit-node-client-nodejs/src/lib/helpers/validate-bls-session-sig.ts index 3e1e01d85..5af52631e 100644 --- a/packages/lit-node-client-nodejs/src/lib/helpers/validate-bls-session-sig.ts +++ b/packages/lit-node-client-nodejs/src/lib/helpers/validate-bls-session-sig.ts @@ -1,3 +1,4 @@ +import { log } from '@lit-protocol/misc'; import { AuthSig } from '@lit-protocol/types'; import { uint8arrayToString } from '@lit-protocol/uint8arrays'; import { ethers } from 'ethers'; @@ -61,7 +62,7 @@ export const blsSessionSigVerify = ( `${checkTime.toISOString()} < ${issuedAt.toISOString()}` ); } - + log('verifiying signature against public bls key'); verifier( networkPubKey, shaHashed, diff --git a/packages/tinny/src/lib/shiva-client.ts b/packages/tinny/src/lib/shiva-client.ts index 941cff7e1..9b100a560 100644 --- a/packages/tinny/src/lib/shiva-client.ts +++ b/packages/tinny/src/lib/shiva-client.ts @@ -274,8 +274,8 @@ export class ShivaClient { ); const body: Partial = createReq ?? { nodeCount: 3, - pollingInterval: '2000', - epochLength: 90_000, + pollingInterval: '20', + epochLength: 200, }; if (this.processEnvs.USE_LIT_BINARIES) { diff --git a/packages/tinny/src/lib/tinny-environment.ts b/packages/tinny/src/lib/tinny-environment.ts index 9385c0d5e..117f76252 100644 --- a/packages/tinny/src/lib/tinny-environment.ts +++ b/packages/tinny/src/lib/tinny-environment.ts @@ -110,10 +110,11 @@ export class TinnyEnvironment { | LitContractResolverContext | undefined; - constructor(network?: LIT_TESTNET) { + private _config: { useRLI: boolean }; + constructor(network?: LIT_TESTNET, config?: { useRLI: boolean }) { // -- setup networkj this.network = network || this.processEnvs.NETWORK; - + this._config = config ?? { useRLI: true }; if (Object.values(LIT_TESTNET).indexOf(this.network) === -1) { throw new Error( `Invalid network environment. Please use one of ${Object.values( @@ -393,7 +394,10 @@ export class TinnyEnvironment { } await this.setupLitNodeClient(); - await this.setupSuperCapacityDelegationAuthSig(); + if (this._config.useRLI) { + await this.setupSuperCapacityDelegationAuthSig(); + } + await this.setupBareEthAuthSig(); } catch (e) { const err = toErrorWithMessage(e); @@ -401,7 +405,7 @@ export class TinnyEnvironment { `[𐬺🧪 Tinny Environment𐬺] Failed to init() tinny ${err.message}` ); console.log(err.stack); - process.exit(1); + throw err; } }