diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index 6aa7ef489a..e6bdfbf72a 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -224,6 +224,15 @@ jobs: working-directory: client-sdk/ts-web/core run: npm run-script test-e2e-cy + - name: Set up Node.js 18 + uses: actions/setup-node@v3 + with: + node-version: "18.x" + + - name: Node.js 18 e2e run + working-directory: client-sdk/ts-web/core + run: npm run-script test-e2e-node + - name: Upload screenshot if: failure() uses: actions/upload-artifact@v3 diff --git a/client-sdk/ts-web/core/docs/getting-started.md b/client-sdk/ts-web/core/docs/getting-started.md index fa09e78ef0..5162cb15a6 100644 --- a/client-sdk/ts-web/core/docs/getting-started.md +++ b/client-sdk/ts-web/core/docs/getting-started.md @@ -183,5 +183,5 @@ When you create an `oasis.client.NodeInternal`, pass the HTTP endpoint of your Envoy proxy: ```js -const client = new oasis.client.NodeInternal('http://localhost:42280'); +const client = new oasis.client.NodeInternal('http://127.0.0.1:42280'); ``` diff --git a/client-sdk/ts-web/core/package.json b/client-sdk/ts-web/core/package.json index 2455e20014..47f172d530 100644 --- a/client-sdk/ts-web/core/package.json +++ b/client-sdk/ts-web/core/package.json @@ -21,7 +21,8 @@ "lint": "prettier --check playground/src src test", "playground": "cd playground && webpack s -c webpack.config.js", "test": "jest", - "test-e2e-cy": "cypress run" + "test-e2e-cy": "cypress run", + "test-e2e-node": "node --experimental-global-webcrypto playground/e2e-test-nodejs.js" }, "dependencies": { "bech32": "^2.0.0", @@ -45,6 +46,7 @@ "typescript": "^5.0.4", "webpack": "^5.79.0", "webpack-cli": "^5.0.1", - "webpack-dev-server": "^4.13.3" + "webpack-dev-server": "^4.13.3", + "xhr2": "^0.2.1" } } diff --git a/client-sdk/ts-web/core/playground/e2e-test-nodejs.js b/client-sdk/ts-web/core/playground/e2e-test-nodejs.js new file mode 100644 index 0000000000..bc181ed2e5 --- /dev/null +++ b/client-sdk/ts-web/core/playground/e2e-test-nodejs.js @@ -0,0 +1,9 @@ +// @ts-check +global.XMLHttpRequest = require('xhr2'); +if (typeof crypto === 'undefined') { + throw 'Upgrade to Node.js@>=19 or Node.js@>=16 with --experimental-global-webcrypto.' +} + +import('./src/startPlayground.mjs').then(async ({startPlayground}) => { + await startPlayground(); +}); diff --git a/client-sdk/ts-web/core/playground/src/index.js b/client-sdk/ts-web/core/playground/src/index.js index 6dd2e91c6f..1407da10a7 100644 --- a/client-sdk/ts-web/core/playground/src/index.js +++ b/client-sdk/ts-web/core/playground/src/index.js @@ -1,162 +1,8 @@ // @ts-check -import * as oasis from './../..'; +import {startPlayground} from './startPlayground.mjs'; -const nic = new oasis.client.NodeInternal('http://localhost:42280'); - -export const playground = (async function () { - // Wait for ready. - { - console.log('waiting for node to be ready'); - const waitStart = Date.now(); - await nic.nodeControllerWaitReady(); - const waitEnd = Date.now(); - console.log(`ready ${waitEnd - waitStart} ms`); - } - - // Get something with addresses. - { - console.log('nodes', await nic.registryGetNodes(oasis.consensus.HEIGHT_LATEST)); - } - - // Try map with non-string keys. - { - const toAddr = 'oasis1qpl5634wyu6larn047he9af7a3qyhzx59u0mquw7'; - console.log('delegations to', toAddr); - const response = await nic.stakingDelegationsTo({ - height: oasis.consensus.HEIGHT_LATEST, - owner: oasis.staking.addressFromBech32(toAddr), - }); - for (const [fromAddr, delegation] of response) { - console.log({ - from: oasis.staking.addressToBech32(fromAddr), - shares: oasis.quantity.toBigInt(delegation.shares), - }); - } - } - - // Try sending a transaction. - { - const src = oasis.signature.NaclSigner.fromRandom('this key is not important'); - const dst = oasis.signature.NaclSigner.fromRandom('this key is not important'); - console.log('src', src, 'dst', dst); - - const chainContext = await nic.consensusGetChainContext(); - console.log('chain context', chainContext); - - const genesis = await nic.consensusGetGenesisDocument(); - const ourChainContext = await oasis.genesis.chainContext(genesis); - console.log('computed from genesis', ourChainContext); - if (ourChainContext !== chainContext) throw new Error('computed chain context mismatch'); - - const nonce = await nic.consensusGetSignerNonce({ - account_address: await oasis.staking.addressFromPublicKey(src.public()), - height: oasis.consensus.HEIGHT_LATEST, - }); - console.log('nonce', nonce); - - const account = await nic.stakingAccount({ - height: oasis.consensus.HEIGHT_LATEST, - owner: await oasis.staking.addressFromPublicKey(src.public()), - }); - console.log('account', account); - if ((account.general?.nonce ?? 0) !== nonce) throw new Error('nonce mismatch'); - - const tw = oasis.staking.transferWrapper(); - tw.setNonce(account.general?.nonce ?? 0); - tw.setFeeAmount(oasis.quantity.fromBigInt(0n)); - tw.setBody({ - to: await oasis.staking.addressFromPublicKey(dst.public()), - amount: oasis.quantity.fromBigInt(0n), - }); - - const gas = await tw.estimateGas(nic, src.public()); - console.log('gas', gas); - tw.setFeeGas(gas); - console.log('transaction', tw.transaction); - - await tw.sign(new oasis.signature.BlindContextSigner(src), chainContext); - console.log('singed transaction', tw.signedTransaction); - console.log('hash', await tw.hash()); - - await tw.submit(nic); - console.log('sent'); - } - - // Try verifying transaction signatures. - { - // TODO: Make sure this is the block with the transaction we sent above. - const chainContext = await nic.consensusGetChainContext(); - console.log('chain context', chainContext); - const response = await nic.consensusGetTransactionsWithResults( - oasis.consensus.HEIGHT_LATEST, - ); - const transactions = response.transactions || []; - const results = response.results || []; - for (let i = 0; i < transactions.length; i++) { - const signedTransaction = /** @type {oasis.types.SignatureSigned} */ ( - oasis.misc.fromCBOR(transactions[i]) - ); - const transaction = await oasis.consensus.openSignedTransaction( - chainContext, - signedTransaction, - ); - console.log({ - hash: await oasis.consensus.hashSignedTransaction(signedTransaction), - from: oasis.staking.addressToBech32( - await oasis.staking.addressFromPublicKey( - signedTransaction.signature.public_key, - ), - ), - transaction: transaction, - feeAmount: transaction.fee ? oasis.quantity.toBigInt(transaction.fee.amount) : 0n, - result: results[i], - }); - } - } - - // Block and events have a variety of different types. - { - // TODO: Make sure this is the block with the transaction we sent above. - const block = await nic.consensusGetBlock(oasis.consensus.HEIGHT_LATEST); - console.log('block', block); - const stakingEvents = await nic.stakingGetEvents(oasis.consensus.HEIGHT_LATEST); - console.log('staking events', stakingEvents); - } - - // Try server streaming. - { - console.log('watching consensus blocks for 5s'); - await /** @type {Promise} */ ( - new Promise((resolve, reject) => { - const blocks = nic.consensusWatchBlocks(); - const cancel = setTimeout(() => { - console.log("time's up, cancelling"); - blocks.cancel(); - resolve(); - }, 5_000); - blocks.on('error', (e) => { - clearTimeout(cancel); - reject(e); - }); - blocks.on('status', (status) => { - console.log('status', status); - }); - blocks.on('metadata', (metadata) => { - console.log('metadata', metadata); - }); - blocks.on('data', (block) => { - console.log('block', block); - }); - blocks.on('end', () => { - clearTimeout(cancel); - resolve(); - }); - }) - ); - console.log('done watching'); - } -})(); +export const playground = startPlayground(); playground.catch((e) => { console.error(e); diff --git a/client-sdk/ts-web/core/playground/src/startPlayground.mjs b/client-sdk/ts-web/core/playground/src/startPlayground.mjs new file mode 100644 index 0000000000..3402b995a4 --- /dev/null +++ b/client-sdk/ts-web/core/playground/src/startPlayground.mjs @@ -0,0 +1,157 @@ +// @ts-check +import * as oasis from '@oasisprotocol/client'; + +export async function startPlayground() { + const nic = new oasis.client.NodeInternal('http://127.0.0.1:42280'); + // Wait for ready. + { + console.log('waiting for node to be ready'); + const waitStart = Date.now(); + await nic.nodeControllerWaitReady(); + const waitEnd = Date.now(); + console.log(`ready ${waitEnd - waitStart} ms`); + } + + // Get something with addresses. + { + console.log('nodes', await nic.registryGetNodes(oasis.consensus.HEIGHT_LATEST)); + } + + // Try map with non-string keys. + { + const toAddr = 'oasis1qpl5634wyu6larn047he9af7a3qyhzx59u0mquw7'; + console.log('delegations to', toAddr); + const response = await nic.stakingDelegationsTo({ + height: oasis.consensus.HEIGHT_LATEST, + owner: oasis.staking.addressFromBech32(toAddr), + }); + for (const [fromAddr, delegation] of response) { + console.log({ + from: oasis.staking.addressToBech32(fromAddr), + shares: oasis.quantity.toBigInt(delegation.shares), + }); + } + } + + // Try sending a transaction. + { + const src = oasis.signature.NaclSigner.fromRandom('this key is not important'); + const dst = oasis.signature.NaclSigner.fromRandom('this key is not important'); + console.log('src', src, 'dst', dst); + + const chainContext = await nic.consensusGetChainContext(); + console.log('chain context', chainContext); + + const genesis = await nic.consensusGetGenesisDocument(); + const ourChainContext = await oasis.genesis.chainContext(genesis); + console.log('computed from genesis', ourChainContext); + if (ourChainContext !== chainContext) throw new Error('computed chain context mismatch'); + + const nonce = await nic.consensusGetSignerNonce({ + account_address: await oasis.staking.addressFromPublicKey(src.public()), + height: oasis.consensus.HEIGHT_LATEST, + }); + console.log('nonce', nonce); + + const account = await nic.stakingAccount({ + height: oasis.consensus.HEIGHT_LATEST, + owner: await oasis.staking.addressFromPublicKey(src.public()), + }); + console.log('account', account); + if ((account.general?.nonce ?? 0) !== nonce) throw new Error('nonce mismatch'); + + const tw = oasis.staking.transferWrapper(); + tw.setNonce(account.general?.nonce ?? 0); + tw.setFeeAmount(oasis.quantity.fromBigInt(0n)); + tw.setBody({ + to: await oasis.staking.addressFromPublicKey(dst.public()), + amount: oasis.quantity.fromBigInt(0n), + }); + + const gas = await tw.estimateGas(nic, src.public()); + console.log('gas', gas); + tw.setFeeGas(gas); + console.log('transaction', tw.transaction); + + await tw.sign(new oasis.signature.BlindContextSigner(src), chainContext); + console.log('singed transaction', tw.signedTransaction); + console.log('hash', await tw.hash()); + + await tw.submit(nic); + console.log('sent'); + } + + // Try verifying transaction signatures. + { + // TODO: Make sure this is the block with the transaction we sent above. + const chainContext = await nic.consensusGetChainContext(); + console.log('chain context', chainContext); + const response = await nic.consensusGetTransactionsWithResults( + oasis.consensus.HEIGHT_LATEST, + ); + const transactions = response.transactions || []; + const results = response.results || []; + for (let i = 0; i < transactions.length; i++) { + const signedTransaction = /** @type {oasis.types.SignatureSigned} */ ( + oasis.misc.fromCBOR(transactions[i]) + ); + const transaction = await oasis.consensus.openSignedTransaction( + chainContext, + signedTransaction, + ); + console.log({ + hash: await oasis.consensus.hashSignedTransaction(signedTransaction), + from: oasis.staking.addressToBech32( + await oasis.staking.addressFromPublicKey( + signedTransaction.signature.public_key, + ), + ), + transaction: transaction, + feeAmount: transaction.fee ? oasis.quantity.toBigInt(transaction.fee.amount) : 0n, + result: results[i], + }); + } + } + + // Block and events have a variety of different types. + { + // TODO: Make sure this is the block with the transaction we sent above. + const block = await nic.consensusGetBlock(oasis.consensus.HEIGHT_LATEST); + console.log('block', block); + const stakingEvents = await nic.stakingGetEvents(oasis.consensus.HEIGHT_LATEST); + console.log('staking events', stakingEvents); + } + + // Try server streaming. + { + console.log('watching consensus blocks for 5s'); + await /** @type {Promise} */ ( + new Promise((resolve, reject) => { + const blocks = nic.consensusWatchBlocks(); + const cancel = setTimeout(() => { + console.log("time's up, cancelling"); + blocks.cancel(); + resolve(); + }, 5_000); + blocks.on('error', (e) => { + clearTimeout(cancel); + reject(e); + }); + blocks.on('status', (status) => { + console.log('status', status); + }); + blocks.on('metadata', (metadata) => { + console.log('metadata', metadata); + }); + blocks.on('data', (block) => { + console.log('block', block); + }); + blocks.on('end', () => { + clearTimeout(cancel); + resolve(); + }); + }) + ); + console.log('done watching'); + } +} diff --git a/client-sdk/ts-web/ext-utils/sample-ext/src/index.js b/client-sdk/ts-web/ext-utils/sample-ext/src/index.js index 4aff64cd7e..a847031e80 100644 --- a/client-sdk/ts-web/ext-utils/sample-ext/src/index.js +++ b/client-sdk/ts-web/ext-utils/sample-ext/src/index.js @@ -2,8 +2,7 @@ import * as oasis from '@oasisprotocol/client'; import * as oasisRT from '@oasisprotocol/client-rt'; - -import * as oasisExt from './../..'; +import * as oasisExt from '@oasisprotocol/client-ext-utils'; const testNoninteractive = new URL(window.location.href).searchParams.has('test_noninteractive'); diff --git a/client-sdk/ts-web/ext-utils/sample-page/src/index.js b/client-sdk/ts-web/ext-utils/sample-page/src/index.js index e061b4d7ae..0d9d0d52c1 100644 --- a/client-sdk/ts-web/ext-utils/sample-page/src/index.js +++ b/client-sdk/ts-web/ext-utils/sample-page/src/index.js @@ -2,8 +2,7 @@ import * as oasis from '@oasisprotocol/client'; import * as oasisRT from '@oasisprotocol/client-rt'; - -import * as oasisExt from '../..'; +import * as oasisExt from '@oasisprotocol/client-ext-utils'; const options = new URL(window.location.href).searchParams; const extOrigin = options.get('ext'); diff --git a/client-sdk/ts-web/package-lock.json b/client-sdk/ts-web/package-lock.json index 915c6a4747..4179f4c844 100644 --- a/client-sdk/ts-web/package-lock.json +++ b/client-sdk/ts-web/package-lock.json @@ -37,7 +37,8 @@ "typescript": "^5.0.4", "webpack": "^5.79.0", "webpack-cli": "^5.0.1", - "webpack-dev-server": "^4.13.3" + "webpack-dev-server": "^4.13.3", + "xhr2": "^0.2.1" } }, "ext-utils": { @@ -8607,6 +8608,15 @@ } } }, + "node_modules/xhr2": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz", + "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/xmlcreate": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", @@ -9701,7 +9711,8 @@ "typescript": "^5.0.4", "webpack": "^5.79.0", "webpack-cli": "^5.0.1", - "webpack-dev-server": "^4.13.3" + "webpack-dev-server": "^4.13.3", + "xhr2": "^0.2.1" } }, "@oasisprotocol/client-ext-utils": { @@ -15418,6 +15429,12 @@ "dev": true, "requires": {} }, + "xhr2": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz", + "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==", + "dev": true + }, "xmlcreate": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", diff --git a/client-sdk/ts-web/rt/playground/src/consensus.js b/client-sdk/ts-web/rt/playground/src/consensus.js index bc88337165..8ceb514df8 100644 --- a/client-sdk/ts-web/rt/playground/src/consensus.js +++ b/client-sdk/ts-web/rt/playground/src/consensus.js @@ -1,8 +1,7 @@ // @ts-check import * as oasis from '@oasisprotocol/client'; - -import * as oasisRT from './../..'; +import * as oasisRT from '@oasisprotocol/client-rt'; const CONSENSUS_RT_ID = oasis.misc.fromHex( '8000000000000000000000000000000000000000000000000000000000000001', @@ -21,7 +20,7 @@ function delay(duration) { }); } -const nic = new oasis.client.NodeInternal('http://localhost:42280'); +const nic = new oasis.client.NodeInternal('http://127.0.0.1:42280'); const accountsWrapper = new oasisRT.accounts.Wrapper(CONSENSUS_RT_ID); const consensusWrapper = new oasisRT.consensusAccounts.Wrapper(CONSENSUS_RT_ID); diff --git a/client-sdk/ts-web/rt/playground/src/index.js b/client-sdk/ts-web/rt/playground/src/index.js index 9cba5c4f0c..322e7f9a31 100644 --- a/client-sdk/ts-web/rt/playground/src/index.js +++ b/client-sdk/ts-web/rt/playground/src/index.js @@ -1,8 +1,7 @@ // @ts-check import * as oasis from '@oasisprotocol/client'; - -import * as oasisRT from './../..'; +import * as oasisRT from '@oasisprotocol/client-rt'; const KEYVALUE_RUNTIME_ID = oasis.misc.fromHex( '8000000000000000000000000000000000000000000000000000000000000000', @@ -88,7 +87,7 @@ function moduleEventHandler( return /** @type {oasisRT.event.ModuleHandler} */ ([MODULE_NAME, codes]); } -const nic = new oasis.client.NodeInternal('http://localhost:42280'); +const nic = new oasis.client.NodeInternal('http://127.0.0.1:42280'); const accountsWrapper = new oasisRT.accounts.Wrapper(KEYVALUE_RUNTIME_ID); const rewardsWrapper = new oasisRT.rewards.Wrapper(KEYVALUE_RUNTIME_ID); const coreWrapper = new oasisRT.core.Wrapper(KEYVALUE_RUNTIME_ID); diff --git a/client-sdk/ts-web/signer-ledger/playground/src/index.js b/client-sdk/ts-web/signer-ledger/playground/src/index.js index 8364339a2a..e1fbb929c2 100644 --- a/client-sdk/ts-web/signer-ledger/playground/src/index.js +++ b/client-sdk/ts-web/signer-ledger/playground/src/index.js @@ -1,7 +1,7 @@ // @ts-check import * as oasis from '@oasisprotocol/client'; -import * as oasisLedger from './../..'; +import * as oasisLedger from '@oasisprotocol/client-signer-ledger'; async function play() { try {