From 688e8de8d42cd70a6e1b5f0ca841379eefc71246 Mon Sep 17 00:00:00 2001 From: mixmix Date: Wed, 31 Jul 2024 14:27:04 +1200 Subject: [PATCH 1/7] merge user/dev programs flows --- src/flows/DeployPrograms/index.ts | 101 ---------------- src/flows/index.ts | 3 +- .../add.ts | 0 .../helpers/questions.ts | 0 .../helpers/utils.ts | 0 .../index.ts | 111 +++++++++++++++++- .../remove.ts | 0 .../types.ts | 0 .../view.ts | 0 src/tui.ts | 2 +- ...am-management.test.ts => programs.test.ts} | 14 +-- {src => tests}/programs/program_noop.wasm | Bin .../programs/template_barebones.wasm | Bin tests/register.test.ts | 2 +- 14 files changed, 115 insertions(+), 118 deletions(-) delete mode 100644 src/flows/DeployPrograms/index.ts rename src/flows/{user-program-management => programs}/add.ts (100%) rename src/flows/{user-program-management => programs}/helpers/questions.ts (100%) rename src/flows/{user-program-management => programs}/helpers/utils.ts (100%) rename src/flows/{user-program-management => programs}/index.ts (55%) rename src/flows/{user-program-management => programs}/remove.ts (100%) rename src/flows/{user-program-management => programs}/types.ts (100%) rename src/flows/{user-program-management => programs}/view.ts (100%) rename tests/{user-program-management.test.ts => programs.test.ts} (84%) rename {src => tests}/programs/program_noop.wasm (100%) rename {src => tests}/programs/template_barebones.wasm (100%) diff --git a/src/flows/DeployPrograms/index.ts b/src/flows/DeployPrograms/index.ts deleted file mode 100644 index 479add7..0000000 --- a/src/flows/DeployPrograms/index.ts +++ /dev/null @@ -1,101 +0,0 @@ -import Entropy from "@entropyxyz/sdk" -import * as util from "@polkadot/util" -import inquirer from "inquirer" -import { readFileSync } from "fs" -import { initializeEntropy } from "../../common/initializeEntropy" -import { print, getSelectedAccount } from "../../common/utils" -import { EntropyTuiOptions } from "src/types" - -export async function devPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options: EntropyTuiOptions) { - const { endpoint } = options - const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress) - - const choices = { - "Deploy": deployProgram, - "Get Owned Programs": getOwnedPrograms, - "Exit to Main Menu": () => 'exit' - } - - const actionChoice = await inquirer.prompt([ - { - type: "list", - name: "action", - message: "Select your action:", - choices: Object.keys(choices) - }, - ]) - - const entropy = await initializeEntropy({ - keyMaterial: selectedAccount.data, - endpoint - }) - - const flow = choices[actionChoice.action] - await flow(entropy, selectedAccount) -} - -async function deployProgram (entropy: Entropy, account: any) { - const deployQuestions = [ - { - type: "input", - name: "programPath", - message: "Please provide the path to your program:", - }, - { - type: "confirm", - name: "hasConfig", - message: "Does your program have a configuration file?", - default: false, - }, - ] - - const deployAnswers = await inquirer.prompt(deployQuestions) - const userProgram = readFileSync(deployAnswers.programPath) - - let programConfig = "" - - if (deployAnswers.hasConfig) { - const configAnswers = await inquirer.prompt([ - { - type: "input", - name: "config", - message: "Please provide your program configuration as a JSON string:", - }, - ]) - - // Convert JSON string to bytes and then to hex - const encoder = new TextEncoder() - const byteArray = encoder.encode(configAnswers.config) - programConfig = util.u8aToHex(new Uint8Array(byteArray)) - } - - try { - // Deploy the program with config - const pointer = await entropy.programs.dev.deploy( - userProgram, - programConfig - ) - print("Program deployed successfully with pointer:", pointer) - } catch (deployError) { - console.error("Deployment failed:", deployError) - } - - print("Deploying from account:", account.address) -} - -async function getOwnedPrograms (entropy: Entropy, account: any) { - const userAddress = account.address - if (!userAddress) return - - try { - const fetchedPrograms = await entropy.programs.dev.get(userAddress) - if (fetchedPrograms.length) { - print("Retrieved program pointers:") - print(fetchedPrograms) - } else { - print("There are no programs to show") - } - } catch (error) { - console.error("Failed to retrieve program pointers:", error) - } -} diff --git a/src/flows/index.ts b/src/flows/index.ts index 2187a5e..82c5a11 100644 --- a/src/flows/index.ts +++ b/src/flows/index.ts @@ -1,8 +1,7 @@ export { entropyFaucet } from './entropyFaucet' export { checkBalance } from './balance' export { entropyRegister } from './register' -export { userPrograms } from './user-program-management' -export { devPrograms } from './DeployPrograms' +export { userPrograms, devPrograms } from './programs' export { sign } from './sign' export { entropyTransfer } from './entropyTransfer' export { manageAccounts } from './manage-accounts' diff --git a/src/flows/user-program-management/add.ts b/src/flows/programs/add.ts similarity index 100% rename from src/flows/user-program-management/add.ts rename to src/flows/programs/add.ts diff --git a/src/flows/user-program-management/helpers/questions.ts b/src/flows/programs/helpers/questions.ts similarity index 100% rename from src/flows/user-program-management/helpers/questions.ts rename to src/flows/programs/helpers/questions.ts diff --git a/src/flows/user-program-management/helpers/utils.ts b/src/flows/programs/helpers/utils.ts similarity index 100% rename from src/flows/user-program-management/helpers/utils.ts rename to src/flows/programs/helpers/utils.ts diff --git a/src/flows/user-program-management/index.ts b/src/flows/programs/index.ts similarity index 55% rename from src/flows/user-program-management/index.ts rename to src/flows/programs/index.ts index 0765bf2..02b1048 100644 --- a/src/flows/user-program-management/index.ts +++ b/src/flows/programs/index.ts @@ -1,18 +1,22 @@ +import Entropy from "@entropyxyz/sdk" +import { readFileSync } from "fs" import inquirer from "inquirer" import * as util from "@polkadot/util" -import { initializeEntropy } from "../../common/initializeEntropy" -import { getSelectedAccount, print } from "../../common/utils" -import { EntropyLogger } from "src/common/logger"; + import { addProgram } from "./add"; import { viewPrograms } from "./view"; +import { removeProgram } from "./remove"; import { addQuestions, getProgramPointerInput, verifyingKeyQuestion } from "./helpers/questions"; import { displayPrograms } from "./helpers/utils"; -import { removeProgram } from "./remove"; +import { initializeEntropy } from "../../common/initializeEntropy" +import { getSelectedAccount, print } from "../../common/utils" +import { EntropyLogger } from "../../common/logger"; +import { EntropyTuiOptions } from "../../types" let verifyingKey: string; -export async function userPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options, logger: EntropyLogger) { - const FLOW_CONTEXT = 'USER_PROGRAMS' +export async function userPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options: EntropyTuiOptions, logger: EntropyLogger) { + const FLOW_CONTEXT = 'PROGRAMS' const { endpoint } = options const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress) @@ -112,3 +116,98 @@ export async function userPrograms ({ accounts, selectedAccount: selectedAccount } } +// eslint-disable-next-line +export async function devPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options: EntropyTuiOptions, logger: EntropyLogger) { + // const FLOW_CONTEXT = 'PROGRAMS' + const { endpoint } = options + const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress) + + const choices = { + "Deploy": deployProgram, + "Get Owned Programs": getOwnedPrograms, + "Exit to Main Menu": () => 'exit' + } + + const actionChoice = await inquirer.prompt([ + { + type: "list", + name: "action", + message: "Select your action:", + choices: Object.keys(choices) + }, + ]) + + const entropy = await initializeEntropy({ + keyMaterial: selectedAccount.data, + endpoint + }) + + const flow = choices[actionChoice.action] + await flow(entropy, selectedAccount) +} + +async function deployProgram (entropy: Entropy, account: any) { + const deployQuestions = [ + { + type: "input", + name: "programPath", + message: "Please provide the path to your program:", + }, + { + type: "confirm", + name: "hasConfig", + message: "Does your program have a configuration file?", + default: false, + }, + ] + + const deployAnswers = await inquirer.prompt(deployQuestions) + const userProgram = readFileSync(deployAnswers.programPath) + + let programConfig = "" + + if (deployAnswers.hasConfig) { + const configAnswers = await inquirer.prompt([ + { + type: "input", + name: "config", + message: "Please provide your program configuration as a JSON string:", + }, + ]) + + // Convert JSON string to bytes and then to hex + const encoder = new TextEncoder() + const byteArray = encoder.encode(configAnswers.config) + programConfig = util.u8aToHex(new Uint8Array(byteArray)) + } + + try { + // Deploy the program with config + const pointer = await entropy.programs.dev.deploy( + userProgram, + programConfig + ) + print("Program deployed successfully with pointer:", pointer) + } catch (deployError) { + console.error("Deployment failed:", deployError) + } + + print("Deploying from account:", account.address) +} + +async function getOwnedPrograms (entropy: Entropy, account: any) { + const userAddress = account.address + if (!userAddress) return + + try { + const fetchedPrograms = await entropy.programs.dev.get(userAddress) + if (fetchedPrograms.length) { + print("Retrieved program pointers:") + print(fetchedPrograms) + } else { + print("There are no programs to show") + } + } catch (error) { + console.error("Failed to retrieve program pointers:", error) + } +} diff --git a/src/flows/user-program-management/remove.ts b/src/flows/programs/remove.ts similarity index 100% rename from src/flows/user-program-management/remove.ts rename to src/flows/programs/remove.ts diff --git a/src/flows/user-program-management/types.ts b/src/flows/programs/types.ts similarity index 100% rename from src/flows/user-program-management/types.ts rename to src/flows/programs/types.ts diff --git a/src/flows/user-program-management/view.ts b/src/flows/programs/view.ts similarity index 100% rename from src/flows/user-program-management/view.ts rename to src/flows/programs/view.ts diff --git a/src/tui.ts b/src/tui.ts index 9055381..746640f 100644 --- a/src/tui.ts +++ b/src/tui.ts @@ -6,7 +6,6 @@ import { logo } from './common/ascii' import { print } from './common/utils' import { EntropyLogger } from './common/logger' - let shouldInit = true // tui = text user interface @@ -22,6 +21,7 @@ export default function tui (options: EntropyTuiOptions) { 'Register': flows.entropyRegister, 'Sign': flows.sign, 'Transfer': flows.entropyTransfer, + // TODO: design programs in TUI (merge deploy+user programs) 'Deploy Program': flows.devPrograms, 'User Programs': flows.userPrograms, // 'Construct an Ethereum Tx': flows.ethTransaction, diff --git a/tests/user-program-management.test.ts b/tests/programs.test.ts similarity index 84% rename from tests/user-program-management.test.ts rename to tests/programs.test.ts index 1fe1733..6958c18 100644 --- a/tests/user-program-management.test.ts +++ b/tests/programs.test.ts @@ -1,18 +1,18 @@ import test from 'tape' import { readFileSync } from 'node:fs' import { promiseRunner, charlieStashSeed, setupTest } from './testing-utils' -import { AddProgramParams } from 'src/flows/user-program-management/types' -import { addProgram } from 'src/flows/user-program-management/add' -import { viewPrograms } from 'src/flows/user-program-management/view' -import { removeProgram } from 'src/flows/user-program-management/remove' +import { AddProgramParams } from 'src/flows/programs/types' +import { addProgram } from 'src/flows/programs/add' +import { viewPrograms } from 'src/flows/programs/view' +import { removeProgram } from 'src/flows/programs/remove' const networkType = 'two-nodes' -test('User Program Management', async t => { +test('programs', async t => { const { run, entropy } = await setupTest(t, { seed: charlieStashSeed, networkType }) await run('charlie stash register', entropy.register()) const noopProgram: any = readFileSync( - 'src/programs//program_noop.wasm' + './programs/program_noop.wasm' ) const newPointer = await run( 'deploy', @@ -53,4 +53,4 @@ test('User Program Management', async t => { vp.equal(programs.length, 1, 'charlie has 1 program') vp.end() }) -}) \ No newline at end of file +}) diff --git a/src/programs/program_noop.wasm b/tests/programs/program_noop.wasm similarity index 100% rename from src/programs/program_noop.wasm rename to tests/programs/program_noop.wasm diff --git a/src/programs/template_barebones.wasm b/tests/programs/template_barebones.wasm similarity index 100% rename from src/programs/template_barebones.wasm rename to tests/programs/template_barebones.wasm diff --git a/tests/register.test.ts b/tests/register.test.ts index 0f16622..7fe6cfb 100644 --- a/tests/register.test.ts +++ b/tests/register.test.ts @@ -21,7 +21,7 @@ test('Regsiter - Default Program', async (t) => { test('Register - Barebones Program', async t => { const { run, entropy } = await setupTest(t, { networkType, seed: charlieStashSeed }) const dummyProgram: any = readFileSync( - 'src/programs/template_barebones.wasm' + './programs/template_barebones.wasm' ) const pointer = await run( 'deploy program', From 93e0ae2b9fc148f36a6d5885fe0e5f019e7f8476 Mon Sep 17 00:00:00 2001 From: mixmix Date: Wed, 31 Jul 2024 15:46:52 +1200 Subject: [PATCH 2/7] ts fixups --- tests/programs.test.ts | 11 ++++++----- tsconfig.json | 3 +-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/programs.test.ts b/tests/programs.test.ts index 6958c18..e3700bb 100644 --- a/tests/programs.test.ts +++ b/tests/programs.test.ts @@ -1,10 +1,11 @@ import test from 'tape' import { readFileSync } from 'node:fs' + import { promiseRunner, charlieStashSeed, setupTest } from './testing-utils' -import { AddProgramParams } from 'src/flows/programs/types' -import { addProgram } from 'src/flows/programs/add' -import { viewPrograms } from 'src/flows/programs/view' -import { removeProgram } from 'src/flows/programs/remove' +import { addProgram } from '../src/flows/programs/add' +import { viewPrograms } from '../src/flows/programs/view' +import { removeProgram } from '../src/flows/programs/remove' +import { AddProgramParams } from '../src/flows/programs/types' const networkType = 'two-nodes' @@ -12,7 +13,7 @@ test('programs', async t => { const { run, entropy } = await setupTest(t, { seed: charlieStashSeed, networkType }) await run('charlie stash register', entropy.register()) const noopProgram: any = readFileSync( - './programs/program_noop.wasm' + new URL('./programs/program_noop.wasm', import.meta.url) ) const newPointer = await run( 'deploy', diff --git a/tsconfig.json b/tsconfig.json index a0d3887..b35dedd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,6 @@ "exclude": [ "node_modules", "dist", - "build", - "examples" + "tests" ] } From 56260a0d9dbe822592046c3c1b95777f61d05d98 Mon Sep 17 00:00:00 2001 From: mixmix Date: Wed, 31 Jul 2024 15:59:15 +1200 Subject: [PATCH 3/7] import fixups in test (remove baseUrl dependence) --- tests/manage-accounts.test.ts | 10 +++++----- tests/register.test.ts | 4 ++-- tests/transfer.test.ts | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/manage-accounts.test.ts b/tests/manage-accounts.test.ts index 76a8db2..21475f8 100644 --- a/tests/manage-accounts.test.ts +++ b/tests/manage-accounts.test.ts @@ -7,10 +7,10 @@ import { randomAsHex } from '@polkadot/util-crypto' import test from 'tape' // @ts-ignore import { spinNetworkUp, spinNetworkDown, } from "@entropyxyz/sdk/testing" -import { EntropyAccountConfig, EntropyConfig } from 'src/config/types' -import { listAccounts } from 'src/flows/manage-accounts/list' -import { createAccount } from 'src/flows/manage-accounts/helpers/create-account' -import * as config from 'src/config' +import { EntropyAccountConfig, EntropyConfig } from '../src/config/types' +import { listAccounts } from '../src/flows/manage-accounts/list' +import { createAccount } from '../src/flows/manage-accounts/helpers/create-account' +import * as config from '../src/config' import { promiseRunner, sleep } from './testing-utils' import { charlieStashAddress, charlieStashSeed } from './testing-utils/constants' @@ -78,4 +78,4 @@ test('Create Account', async t => { t.ok(isValidAddress, 'Valid address created') t.equal(newAccount.address, admin.address, 'Generated Account matches Account created by Keyring') t.end() -}) \ No newline at end of file +}) diff --git a/tests/register.test.ts b/tests/register.test.ts index 7fe6cfb..c1f4b80 100644 --- a/tests/register.test.ts +++ b/tests/register.test.ts @@ -1,7 +1,7 @@ import test from 'tape' import { charlieStashSeed, setupTest } from './testing-utils' -import { register } from 'src/flows/register/register' +import { register } from '../src/flows/register/register' import { readFileSync } from 'node:fs' const networkType = 'two-nodes' @@ -21,7 +21,7 @@ test('Regsiter - Default Program', async (t) => { test('Register - Barebones Program', async t => { const { run, entropy } = await setupTest(t, { networkType, seed: charlieStashSeed }) const dummyProgram: any = readFileSync( - './programs/template_barebones.wasm' + new URL('./programs/template_barebones.wasm', import.meta.url) ) const pointer = await run( 'deploy program', diff --git a/tests/transfer.test.ts b/tests/transfer.test.ts index 83379bc..4b99d5c 100644 --- a/tests/transfer.test.ts +++ b/tests/transfer.test.ts @@ -12,9 +12,9 @@ import { } from './testing-utils' import { getBalance } from '../src/flows/balance/balance' -import { initializeEntropy } from 'src/common/initializeEntropy' +import { initializeEntropy } from '../src/common/initializeEntropy' +import { transfer } from '../src/flows/entropyTransfer/transfer' import { charlieStashAddress, charlieStashSeed } from './testing-utils/constants' -import { transfer } from 'src/flows/entropyTransfer/transfer' const networkType = 'two-nodes' @@ -79,4 +79,4 @@ test('Transfer', async (t) => { t.equal(naynayBalance, 1000 * 10e10, 'naynay is rolling in it!') t.end() -}) \ No newline at end of file +}) From 21cb32c4625a49f870ac9c127b82c81f7f4521f2 Mon Sep 17 00:00:00 2001 From: mixmix Date: Wed, 31 Jul 2024 17:21:06 +1200 Subject: [PATCH 4/7] test programs deploy --- package.json | 2 +- src/flows/programs/deploy.ts | 24 +++++++++ tests/programs.test.ts | 98 ++++++++++++++++++++++-------------- yarn.lock | 8 +++ 4 files changed, 92 insertions(+), 40 deletions(-) create mode 100644 src/flows/programs/deploy.ts diff --git a/package.json b/package.json index 1838e80..d86077f 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "homepage": "https://github.com/entropyxyz/cli#readme", "dependencies": { "@entropyxyz/sdk": "^0.2.3", - "@types/node": "^20.12.12", "ansi-colors": "^4.1.3", "cli-progress": "^3.12.0", "commander": "^12.0.0", @@ -63,6 +62,7 @@ "@types/cli-progress": "^3", "@types/inquirer": "^9.0.2", "@types/node": "^20.12.12", + "@types/tape": "^5.6.4", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "eslint": "^8.56.0", diff --git a/src/flows/programs/deploy.ts b/src/flows/programs/deploy.ts new file mode 100644 index 0000000..2772b32 --- /dev/null +++ b/src/flows/programs/deploy.ts @@ -0,0 +1,24 @@ +import Entropy from "@entropyxyz/sdk"; +import fs from "node:fs/promises" +import { isAbsolute, join } from "node:path" + +// TODO: change to this when SDK ready +// export async function deploy (entropy: Entropy, { bytecode, configurationSchema, auxillaryDataSchema }) { +// return entropy.programs.dev.deploy({ +// bytecode, +// configurationSchema, +// auxillaryDataSchema +// }) +// } + +export async function deployProgram (entropy: Entropy, { bytecodePath }) { + const fullBytecodePath = isAbsolute(bytecodePath) + ? bytecodePath + : join(process.cwd(), bytecodePath) + + const bytecode = await fs.readFile(fullBytecodePath) + + return entropy.programs.dev.deploy( + bytecode + ) +} diff --git a/tests/programs.test.ts b/tests/programs.test.ts index e3700bb..1fb4c3e 100644 --- a/tests/programs.test.ts +++ b/tests/programs.test.ts @@ -1,57 +1,77 @@ import test from 'tape' -import { readFileSync } from 'node:fs' import { promiseRunner, charlieStashSeed, setupTest } from './testing-utils' import { addProgram } from '../src/flows/programs/add' import { viewPrograms } from '../src/flows/programs/view' import { removeProgram } from '../src/flows/programs/remove' -import { AddProgramParams } from '../src/flows/programs/types' +import { deployProgram } from '../src/flows/programs/deploy' const networkType = 'two-nodes' test('programs', async t => { const { run, entropy } = await setupTest(t, { seed: charlieStashSeed, networkType }) - await run('charlie stash register', entropy.register()) - const noopProgram: any = readFileSync( - new URL('./programs/program_noop.wasm', import.meta.url) - ) - const newPointer = await run( - 'deploy', - entropy.programs.dev.deploy(noopProgram) - ) - - const noopProgramInstance: AddProgramParams = { - programPointer: newPointer, - programConfig: '', - } - - t.test('Add Program', async ap => { - const runAp = promiseRunner(ap) - - const programsBeforeAdd = await runAp('get programs initial', entropy.programs.get(entropy.programs.verifyingKey)) - ap.equal(programsBeforeAdd.length, 1, 'charlie has 1 program') - await runAp('adding program', addProgram(entropy, noopProgramInstance)) - const programsAfterAdd = await runAp('get programs after add', entropy.programs.get(entropy.programs.verifyingKey)) - ap.equal(programsAfterAdd.length, 2, 'charlie has 2 programs') - ap.end() + await run('register', entropy.register()) // TODO: consider removing this in favour of just testing add + + let programPointer1 + + t.test('programs - deploy', async t => { + const run = promiseRunner(t) + + programPointer1 = await run ( + 'deploy!', + deployProgram(entropy, { + bytecodePath: './tests/programs/program_noop.wasm' + }) + ) + + t.end() }) - t.test('Remove Program', async rp => { - const runRp = promiseRunner(rp) - const programsBeforeRemove = await runRp('get programs initial', entropy.programs.get(entropy.programs.verifyingKey)) - - rp.equal(programsBeforeRemove.length, 2, 'charlie has 2 programs') - await runRp('removing noop program', removeProgram(entropy, { programPointer: newPointer, verifyingKey: entropy.programs.verifyingKey })) - const programsAfterRemove = await runRp('get programs initial', entropy.programs.get(entropy.programs.verifyingKey)) - rp.equal(programsAfterRemove.length, 1, 'charlie has 1 less program') - rp.end() + const getPrograms = () => entropy.programs.get(entropy.programs.verifyingKey) + const verifyingKey = entropy.programs.verifyingKey + + t.test('programs - add', async t => { + const run = promiseRunner(t) + + const programsBeforeAdd = await run('get programs initial', getPrograms()) + t.equal(programsBeforeAdd.length, 1, 'charlie has 1 program') + + await run( + 'adding program', + addProgram(entropy, { programPointer: programPointer1, programConfig: '' }) + ) + const programsAfterAdd = await run('get programs after add', getPrograms()) + t.equal(programsAfterAdd.length, 2, 'charlie has 2 programs') + + t.end() }) - t.test('View Program', async vp => { - const runVp = promiseRunner(vp) - const programs = await runVp('get charlie programs', viewPrograms(entropy, { verifyingKey: entropy.programs.verifyingKey })) + t.test('programs - remove', async t => { + const run = promiseRunner(t) + + const programsBeforeRemove = await run('get programs initial', getPrograms()) + t.equal(programsBeforeRemove.length, 2, 'charlie has 2 programs') + + await run( + 'removing noop program', + removeProgram(entropy, { programPointer: programPointer1, verifyingKey }) + ) + const programsAfterRemove = await run('get programs initial', getPrograms()) + t.equal(programsAfterRemove.length, 1, 'charlie has 1 less program') + + t.end() + }) + + t.test('programs - view', async t => { + const run = promiseRunner(t) + + const programs = await run( + 'get charlie programs', + viewPrograms(entropy, { verifyingKey }) + ) + + t.equal(programs.length, 1, 'charlie has 1 program') - vp.equal(programs.length, 1, 'charlie has 1 program') - vp.end() + t.end() }) }) diff --git a/yarn.lock b/yarn.lock index dfd1e8a..7488d0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1007,6 +1007,14 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== +"@types/tape@^5.6.4": + version "5.6.4" + resolved "https://registry.yarnpkg.com/@types/tape/-/tape-5.6.4.tgz#efae4202493043457b1900dceb4808c8f04c7d8f" + integrity sha512-EmL4fJpZyByNCkupLLcJhneqcnT+rQUG5fWKNCsZyBK1x7nUuDTwwEerc4biEMZgvSK2+FXr775aLeXhKXK4Yw== + dependencies: + "@types/node" "*" + "@types/through" "*" + "@types/through@*": version "0.0.33" resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.33.tgz#14ebf599320e1c7851e7d598149af183c6b9ea56" From f4e6814204d71a21853e28913e7921ef09eda85c Mon Sep 17 00:00:00 2001 From: mixmix Date: Wed, 31 Jul 2024 20:32:46 +1200 Subject: [PATCH 5/7] plug it innnn --- src/flows/programs/deploy.ts | 55 ++++++++++++++++------- src/flows/programs/index.ts | 86 +++++++++++++++++------------------- src/flows/programs/types.ts | 13 ++++-- tests/register.test.ts | 6 +-- 4 files changed, 94 insertions(+), 66 deletions(-) diff --git a/src/flows/programs/deploy.ts b/src/flows/programs/deploy.ts index 2772b32..e71b8ff 100644 --- a/src/flows/programs/deploy.ts +++ b/src/flows/programs/deploy.ts @@ -1,24 +1,49 @@ import Entropy from "@entropyxyz/sdk"; import fs from "node:fs/promises" import { isAbsolute, join } from "node:path" +import { u8aToHex } from "@polkadot/util" -// TODO: change to this when SDK ready -// export async function deploy (entropy: Entropy, { bytecode, configurationSchema, auxillaryDataSchema }) { -// return entropy.programs.dev.deploy({ -// bytecode, -// configurationSchema, -// auxillaryDataSchema -// }) -// } +import { DeployProgramParams } from "./types" -export async function deployProgram (entropy: Entropy, { bytecodePath }) { - const fullBytecodePath = isAbsolute(bytecodePath) - ? bytecodePath - : join(process.cwd(), bytecodePath) - - const bytecode = await fs.readFile(fullBytecodePath) +export async function deployProgram (entropy: Entropy, params: DeployProgramParams) { + const bytecode = await loadFile(params.bytecodePath) + const configurationSchema = await loadFile(params.configurationSchemaPath, 'json') + const auxillaryDataSchema = await loadFile(params.auxillaryDataSchemaPath, 'json') + // QUESTION: where / how are schema validated? return entropy.programs.dev.deploy( - bytecode + bytecode, + jsonToHex(configurationSchema), + jsonToHex(auxillaryDataSchema) ) } + +function loadFile (path?: string, encoding?: string) { + if (path === undefined) return + + const absolutePath = isAbsolute(path) + ? path + : join(process.cwd(), path) + + switch (encoding) { + case undefined: + return fs.readFile(absolutePath) + + case 'json': + return fs.readFile(absolutePath, 'utf-8') + .then(string => JSON.parse(string)) + + default: + throw Error('unknown encoding: ' + encoding) + // return fs.readFile(absolutePath, encoding) + } +} + +function jsonToHex (obj?: object) { + if (obj === undefined) return + + const encoder = new TextEncoder() + const byteArray = encoder.encode(JSON.stringify(obj)) + + return u8aToHex(new Uint8Array(byteArray)) +} diff --git a/src/flows/programs/index.ts b/src/flows/programs/index.ts index 02b1048..db8eade 100644 --- a/src/flows/programs/index.ts +++ b/src/flows/programs/index.ts @@ -1,8 +1,8 @@ import Entropy from "@entropyxyz/sdk" -import { readFileSync } from "fs" import inquirer from "inquirer" -import * as util from "@polkadot/util" +import { u8aToHex } from "@polkadot/util" +import { deployProgram } from "./deploy"; import { addProgram } from "./add"; import { viewPrograms } from "./view"; import { removeProgram } from "./remove"; @@ -35,11 +35,11 @@ export async function userPrograms ({ accounts, selectedAccount: selectedAccount }, ]) - const entropy = await initializeEntropy({ + const entropy = await initializeEntropy({ keyMaterial: selectedAccount.data, endpoint }) - + if (!entropy.registrationManager?.signer?.pair) { throw new Error("Keys are undefined") } @@ -85,13 +85,13 @@ export async function userPrograms ({ accounts, selectedAccount: selectedAccount case "Add a Program to My List": { try { const { programPointerToAdd, programConfigJson } = await inquirer.prompt(addQuestions) - + const encoder = new TextEncoder() const byteArray = encoder.encode(programConfigJson) - const programConfigHex = util.u8aToHex(byteArray) - + const programConfigHex = u8aToHex(byteArray) + await addProgram(entropy, { programPointer: programPointerToAdd, programConfig: programConfigHex }) - + print("Program added successfully.") } catch (error) { console.error(error.message) @@ -123,8 +123,8 @@ export async function devPrograms ({ accounts, selectedAccount: selectedAccountA const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress) const choices = { - "Deploy": deployProgram, - "Get Owned Programs": getOwnedPrograms, + "Deploy": deployProgramTUI, + "Get Owned Programs": getOwnedProgramsTUI, "Exit to Main Menu": () => 'exit' } @@ -146,47 +146,43 @@ export async function devPrograms ({ accounts, selectedAccount: selectedAccountA await flow(entropy, selectedAccount) } -async function deployProgram (entropy: Entropy, account: any) { - const deployQuestions = [ +async function deployProgramTUI (entropy: Entropy, account: any) { + const answers = await inquirer.prompt([ { type: "input", - name: "programPath", - message: "Please provide the path to your program:", + name: "bytecodePath", + message: "Please provide the path to your program binary:", + validate (input: string) { + return input.endsWith('.wasm') + ? true + : 'program binary must be .wasm file' + } }, { - type: "confirm", - name: "hasConfig", - message: "Does your program have a configuration file?", - default: false, + type: "input", + name: "configurationSchemaPath", + message: "Please provide the path to your configuration schema:", + validate (input: string) { + return input.endsWith('.json') + ? true + : 'configuration schema must be a .json file' + } }, - ] - - const deployAnswers = await inquirer.prompt(deployQuestions) - const userProgram = readFileSync(deployAnswers.programPath) - - let programConfig = "" - - if (deployAnswers.hasConfig) { - const configAnswers = await inquirer.prompt([ - { - type: "input", - name: "config", - message: "Please provide your program configuration as a JSON string:", - }, - ]) - - // Convert JSON string to bytes and then to hex - const encoder = new TextEncoder() - const byteArray = encoder.encode(configAnswers.config) - programConfig = util.u8aToHex(new Uint8Array(byteArray)) - } + { + type: "input", + name: "auxillaryDataSchemaPath", + message: "Please provide the path to your auxillary data schema:", + validate (input: string) { + return input.endsWith('.json') + ? true + : 'configuration schema must be a .json file' + } + }, + ]) try { - // Deploy the program with config - const pointer = await entropy.programs.dev.deploy( - userProgram, - programConfig - ) + const pointer = await deployProgram(entropy, answers) + print("Program deployed successfully with pointer:", pointer) } catch (deployError) { console.error("Deployment failed:", deployError) @@ -195,7 +191,7 @@ async function deployProgram (entropy: Entropy, account: any) { print("Deploying from account:", account.address) } -async function getOwnedPrograms (entropy: Entropy, account: any) { +async function getOwnedProgramsTUI (entropy: Entropy, account: any) { const userAddress = account.address if (!userAddress) return diff --git a/src/flows/programs/types.ts b/src/flows/programs/types.ts index c4274bf..fa3bc46 100644 --- a/src/flows/programs/types.ts +++ b/src/flows/programs/types.ts @@ -1,7 +1,7 @@ -export interface AddProgramParams { +export interface AddProgramParams { programPointer: string programConfig: string - verifyingKey?: string + verifyingKey?: string } export interface ViewProgramsParams { @@ -11,4 +11,11 @@ export interface ViewProgramsParams { export interface RemoveProgramParams { programPointer: string verifyingKey: string -} \ No newline at end of file +} + +export interface DeployProgramParams { + bytecodePath: string, + configurationSchemaPath?: string + auxillaryDataSchemaPath?: string + // TODO: confirm which of these are optional +} diff --git a/tests/register.test.ts b/tests/register.test.ts index c1f4b80..f093df6 100644 --- a/tests/register.test.ts +++ b/tests/register.test.ts @@ -13,7 +13,7 @@ test('Regsiter - Default Program', async (t) => { const fullAccount = entropy.keyring.getAccount() - t.equal(verifyingKey, fullAccount.registration.verifyingKeys[0], 'verifying key matches key added to registration account') + t.equal(verifyingKey, fullAccount?.registration?.verifyingKeys?.[0], 'verifying key matches key added to registration account') t.end() }) @@ -37,8 +37,8 @@ test('Register - Barebones Program', async t => { ) const fullAccount = entropy.keyring.getAccount() - - t.equal(verifyingKey, fullAccount.registration.verifyingKeys[1], 'verifying key matches key added to registration account') + + t.equal(verifyingKey, fullAccount?.registration?.verifyingKeys?.[1], 'verifying key matches key added to registration account') t.end() }) From 21c142d57c4c8f079648e941032ba86e9304fbcd Mon Sep 17 00:00:00 2001 From: mix irving Date: Thu, 1 Aug 2024 13:27:29 +1200 Subject: [PATCH 6/7] Update tests/programs.test.ts Co-authored-by: Nayyir Jutha --- tests/programs.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/programs.test.ts b/tests/programs.test.ts index 1fb4c3e..978c67b 100644 --- a/tests/programs.test.ts +++ b/tests/programs.test.ts @@ -27,7 +27,7 @@ test('programs', async t => { t.end() }) - const getPrograms = () => entropy.programs.get(entropy.programs.verifyingKey) + const getPrograms = () => viewPrograms(entropy, { verifyingKey: entropy.programs.verifyingKey }) const verifyingKey = entropy.programs.verifyingKey t.test('programs - add', async t => { From 222071e7c14505e11eeefb02ba8e96675495ec01 Mon Sep 17 00:00:00 2001 From: mixmix Date: Thu, 1 Aug 2024 13:37:53 +1200 Subject: [PATCH 7/7] CHANGELOG --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 248b0cb..a6e9a9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,8 +29,19 @@ Version header format: `[version] Name - year-month-day (entropy-core compatibil - new: './src/flows/user-program-management/remove.ts' - service file for removing user program pure function ### Changed + - folder name for user programs to match the kebab-case style for folder namespace - updated SDK version to v0.2.3 +- merged user + dev program folders + tests + + +### Broke + +- deploying programs with TUI + - now requires a `*.wasm` file for `bytecode` + - now requires a `*.json` file path for `configurationSchema` + - now requires a `*.json` file path for `auxillaryDataSchema` + ## [0.0.3] Blade - 2024-07-17 (entropy-core compatibility: 0.2.0)