From dfd9405498e87ec5c353201daf2cc8c6dc9417e7 Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Mon, 8 Jul 2024 18:56:44 -0400 Subject: [PATCH 01/10] [NayNay] Create Account Refactor + Testing - converted creating an account to pure function - added tests for creating an account - renamed some files for organization and consistency --- src/flows/manage-accounts/cli.ts | 7 ++--- .../manage-accounts/helpers/create-account.ts | 23 ++++++++++++++++ .../{import-key.ts => import-account.ts} | 0 src/flows/manage-accounts/index.ts | 4 +-- src/flows/manage-accounts/list.ts | 4 ++- .../{new-key.ts => new-account.ts} | 26 ++++--------------- 6 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 src/flows/manage-accounts/helpers/create-account.ts rename src/flows/manage-accounts/helpers/{import-key.ts => import-account.ts} (100%) rename src/flows/manage-accounts/{new-key.ts => new-account.ts} (72%) diff --git a/src/flows/manage-accounts/cli.ts b/src/flows/manage-accounts/cli.ts index c2712e66..390f6fe1 100644 --- a/src/flows/manage-accounts/cli.ts +++ b/src/flows/manage-accounts/cli.ts @@ -1,11 +1,8 @@ import * as config from '../../config' +import { listAccounts } from './list' export async function cliListAccounts () { const storedConfig = await config.get() - return storedConfig.accounts - .map(account => ({ - name: account.name, - address: account.address - })) + return listAccounts(storedConfig) } diff --git a/src/flows/manage-accounts/helpers/create-account.ts b/src/flows/manage-accounts/helpers/create-account.ts new file mode 100644 index 00000000..497618a6 --- /dev/null +++ b/src/flows/manage-accounts/helpers/create-account.ts @@ -0,0 +1,23 @@ +// @ts-ignore +import Keyring from '@entropyxyz/sdk/keys' +import { debug } from 'src/common/utils'; +import { EntropyAccountConfig } from "src/config/types"; + +export async function createAccount ({ name, seed, path }): Promise { + const keyring = new Keyring({ seed, path, debug: true }) + const fullAccount = keyring.getAccount() + // TO-DO: sdk should create account on constructor + const { admin } = keyring.getAccount() + debug('fullAccount:', fullAccount) + + const data = fullAccount + delete admin.pair + // const encryptedData = password ? passwordFlow.encrypt(data, password) : data + + return { + name: name, + address: admin.address, + // TODO: replace with data: encryptedData once pasword input is added back + data, + } +} \ No newline at end of file diff --git a/src/flows/manage-accounts/helpers/import-key.ts b/src/flows/manage-accounts/helpers/import-account.ts similarity index 100% rename from src/flows/manage-accounts/helpers/import-key.ts rename to src/flows/manage-accounts/helpers/import-account.ts diff --git a/src/flows/manage-accounts/index.ts b/src/flows/manage-accounts/index.ts index b911afa2..b4c82ff3 100644 --- a/src/flows/manage-accounts/index.ts +++ b/src/flows/manage-accounts/index.ts @@ -1,11 +1,11 @@ import inquirer from 'inquirer' import { debug, print } from '../../common/utils' -import { newKey } from './new-key' +import { newAccount } from './new-account' import { selectAccount } from './select-account' import { listAccounts } from './list' const actions = { - 'Create/Import Account': newKey, + 'Create/Import Account': newAccount, 'Select Account': selectAccount, 'List Accounts': (config) => { try { diff --git a/src/flows/manage-accounts/list.ts b/src/flows/manage-accounts/list.ts index 63ae5995..74b43db8 100644 --- a/src/flows/manage-accounts/list.ts +++ b/src/flows/manage-accounts/list.ts @@ -1,10 +1,12 @@ +import { EntropyAccountConfig } from "src/config/types" + export function listAccounts (config) { const accountsArray = Array.isArray(config.accounts) ? config.accounts : [config.accounts] if (!accountsArray.length) throw new Error( 'There are currently no accounts available, please create or import your new account using the Manage Accounts feature' ) - return accountsArray.map((account) => ({ + return accountsArray.map((account: EntropyAccountConfig) => ({ name: account.name, address: account.address, verifyingKeys: account?.data?.admin?.verifyingKeys diff --git a/src/flows/manage-accounts/new-key.ts b/src/flows/manage-accounts/new-account.ts similarity index 72% rename from src/flows/manage-accounts/new-key.ts rename to src/flows/manage-accounts/new-account.ts index 39161c19..63f6344b 100644 --- a/src/flows/manage-accounts/new-key.ts +++ b/src/flows/manage-accounts/new-account.ts @@ -1,12 +1,11 @@ import inquirer from 'inquirer' import { randomAsHex } from '@polkadot/util-crypto' -// @ts-ignore -import Keyring from '@entropyxyz/sdk/keys' -import { importQuestions } from './helpers/import-key' +import { importQuestions } from './helpers/import-account' // import * as passwordFlow from '../password' -import { debug, print } from '../../common/utils' +import { print } from '../../common/utils' +import { createAccount } from './helpers/create-account' -export async function newKey ({ accounts }) { +export async function newAccount ({ accounts }) { accounts = Array.isArray(accounts) ? accounts : [] const questions = [ @@ -62,22 +61,7 @@ export async function newKey ({ accounts }) { seed = importKey ? secret : randomAsHex(32) } - const keyring = new Keyring({ seed, path, debug: true }) - const fullAccount = keyring.getAccount() - // TO-DO: sdk should create account on constructor - const { admin } = keyring.getAccount() - debug('fullAccount:', fullAccount) - - const data = fullAccount - delete admin.pair - // const encryptedData = password ? passwordFlow.encrypt(data, password) : data - - const newAccount = { - name: name, - address: admin.address, - // TODO: replace with data: encryptedData once pasword input is added back - data, - } + const newAccount = await createAccount({ name, seed, path }) print(`New account:\n{\n\tname: ${newAccount.name}\n\taddress: ${newAccount.address}\n}`) From a647d7ba48c277551868858cb6b40031b1a8173b Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Tue, 9 Jul 2024 14:00:44 -0400 Subject: [PATCH 02/10] updated changelog; completed pure function; general cleanup; added test --- CHANGELOG.md | 3 ++ src/common/utils.ts | 12 ----- .../manage-accounts/helpers/create-account.ts | 2 +- .../manage-accounts/helpers/import-account.ts | 2 +- tests/manage-accounts.test.ts | 46 ++++++++++++++++++- 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43910832..395cd956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Version header format: `[version] Name - year-month-day (entropy-core compatibil ### Added - new: './src/flows/balance/balance.ts' - service file separated out of main flow containing the pure functions to perform balance requests for one or multiple addresses - new: './tests/balance.test.ts' - new unit tests file for balance pure functions +- new: './src/flows/manage-accounts/helpers/create-account.ts' - new helper file to house the pure function used to create a new entropy account +- update: './tests/manage-accounts.test.ts' - added test for create account pure function +- update: './src/common/utils.ts' - removed isValidSubstrateAddress and imported the method in from the sdk ### Fixed - keyring retrieval method was incorrectly returning the default keyring when no keyring was found, which is not the intended flow ### Changed diff --git a/src/common/utils.ts b/src/common/utils.ts index ece9bdcb..975409a2 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -1,5 +1,3 @@ -import { decodeAddress, encodeAddress } from "@polkadot/keyring" -import { hexToU8a, isHex } from "@polkadot/util" import { Buffer } from 'buffer' import Debug from 'debug' import { EntropyAccountConfig } from "../config/types" @@ -58,16 +56,6 @@ export function buf2hex (buffer: ArrayBuffer): string { return Buffer.from(buffer).toString("hex") } -export function isValidSubstrateAddress (address: any) { - try { - encodeAddress(isHex(address) ? hexToU8a(address) : decodeAddress(address)) - - return true - } catch (error) { - return false - } -} - export function accountChoices (accounts: EntropyAccountConfig[]) { return accounts .map((account) => ({ diff --git a/src/flows/manage-accounts/helpers/create-account.ts b/src/flows/manage-accounts/helpers/create-account.ts index 497618a6..b0ebfd65 100644 --- a/src/flows/manage-accounts/helpers/create-account.ts +++ b/src/flows/manage-accounts/helpers/create-account.ts @@ -3,7 +3,7 @@ import Keyring from '@entropyxyz/sdk/keys' import { debug } from 'src/common/utils'; import { EntropyAccountConfig } from "src/config/types"; -export async function createAccount ({ name, seed, path }): Promise { +export async function createAccount ({ name, seed, path }: { name: string, seed: string, path?: string }): Promise { const keyring = new Keyring({ seed, path, debug: true }) const fullAccount = keyring.getAccount() // TO-DO: sdk should create account on constructor diff --git a/src/flows/manage-accounts/helpers/import-account.ts b/src/flows/manage-accounts/helpers/import-account.ts index 17e9f848..9e4e4768 100644 --- a/src/flows/manage-accounts/helpers/import-account.ts +++ b/src/flows/manage-accounts/helpers/import-account.ts @@ -35,7 +35,7 @@ export const importQuestions = [ { type: 'input', name: 'path', - meesage: 'derivation path:', + message: 'derivation path:', default: 'none', when: ({ importKey }) => importKey }, diff --git a/tests/manage-accounts.test.ts b/tests/manage-accounts.test.ts index b2931e8d..2bef1b31 100644 --- a/tests/manage-accounts.test.ts +++ b/tests/manage-accounts.test.ts @@ -1,7 +1,18 @@ -import { EntropyAccountConfig, EntropyConfig } from 'src/config/types' +import { wasmGlobalsReady } from '@entropyxyz/sdk' +// @ts-ignore +import { isValidSubstrateAddress } from '@entropyxyz/sdk/utils' +// @ts-ignore +import Keyring from '@entropyxyz/sdk/keys' +import { randomAsHex } from '@polkadot/util-crypto' import test from 'tape' -import { charlieStashAddress, charlieStashSeed } from './testing-utils/constants' +// @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 { promiseRunner, sleep } from './testing-utils' +import { charlieStashAddress, charlieStashSeed } from './testing-utils/constants' test('List Accounts', async t => { const account: EntropyAccountConfig = { @@ -43,5 +54,36 @@ test('List Accounts', async t => { t.equal(msg, 'There are currently no accounts available, please create or import your new account using the Manage Accounts feature') } + t.end() +}) + +const networkType = 'two-nodes' + +let counter = 0 +test('Create Account', async t => { + const configPath = `/tmp/entropy-cli-${Date.now()}_${counter++}.json` + /* Setup */ + const run = promiseRunner(t) + await run('wasm', wasmGlobalsReady()) + await run('network up', spinNetworkUp(networkType)) + // this gets called after all tests are run + t.teardown(async () => { + await spinNetworkDown(networkType).catch((error) => + console.error('Error while spinning network down', error.message) + ) + }) + await sleep(process.env.GITHUB_WORKSPACE ? 30_000 : 5_000) + await run('config.init', config.init(configPath)) + const testAccountSeed = randomAsHex(32) + const testAccountName = 'Test Account' + const newAccount = await createAccount({ name: testAccountName, seed: testAccountSeed }) + + const testKeyring = new Keyring({ seed: testAccountSeed, path: 'none', debug: true }) + const { admin } = testKeyring.getAccount() + + const isValidAddress = isValidSubstrateAddress(newAccount.address) + + 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 From 7cc85fe824359eb851f48a2f63ff6f20adb3b878 Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Mon, 15 Jul 2024 23:25:29 -0400 Subject: [PATCH 03/10] [NayNay] User Program Management::View Programs + Testing - added new service file for view program pure function - added tests --- CHANGELOG.md | 8 ++++++ .../helpers/questions.ts | 12 ++++++++- .../user-program-management/helpers/utils.ts | 11 ++++++++ src/flows/user-program-management/index.ts | 26 +++++-------------- src/flows/user-program-management/types.ts | 3 +++ src/flows/user-program-management/view.ts | 6 +++++ tests/user-program-management.test.ts | 12 +++++++++ 7 files changed, 58 insertions(+), 20 deletions(-) create mode 100644 src/flows/user-program-management/helpers/utils.ts create mode 100644 src/flows/user-program-management/view.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e364de36..8486704b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,11 +15,19 @@ Version header format: `[version] Name - year-month-day (entropy-core compatibil ### Added - new: './tests/user-program-management.test.ts' - unit tests file for user program management flows - added test for adding a user program + - added test for viewing a user program - new: './src/flows/user-program-management/add.ts' - service file for adding user program pure function +- new: 'src/flows/user-program-management/helpers/questions.ts' - utility helper file for all the different inquirer questions used +- new: 'src/flows/user-program-management/types.ts' - user program management method types +- new: 'src/flows/user-program-management/view.ts' - service file for pure functions of viewing user programs +- new: 'src/flows/user-program-management/helpers/utils.ts' - utility helper file for user program management specific methods ### Fixed ### Changed - folder name for user programs to match the kebab-case style for folder namespace + +### Fixed + ### Broke ### Meta/Dev diff --git a/src/flows/user-program-management/helpers/questions.ts b/src/flows/user-program-management/helpers/questions.ts index c787a72e..794a4543 100644 --- a/src/flows/user-program-management/helpers/questions.ts +++ b/src/flows/user-program-management/helpers/questions.ts @@ -1,3 +1,5 @@ +import Entropy from "@entropyxyz/sdk"; + export const addQuestions = [ { type: "input", @@ -19,4 +21,12 @@ export const addQuestions = [ } }, }, -] \ No newline at end of file +] + +export const verifyingKeyQuestion = (entropy: Entropy) => [{ + type: 'list', + name: 'verifyingKey', + message: 'Select the key to proceeed', + choices: entropy.keyring.accounts.registration.verifyingKeys, + default: entropy.keyring.accounts.registration.verifyingKeys[0] +}] diff --git a/src/flows/user-program-management/helpers/utils.ts b/src/flows/user-program-management/helpers/utils.ts new file mode 100644 index 00000000..ff0a3fe6 --- /dev/null +++ b/src/flows/user-program-management/helpers/utils.ts @@ -0,0 +1,11 @@ +import { print } from "src/common/utils" + +export function displayPrograms (programs): void { + programs.forEach((program, index) => { + print( + `${index + 1}. Pointer: ${ + program.program_pointer + }, Config: ${JSON.stringify(program.program_config)}` + ) + }) +} \ No newline at end of file diff --git a/src/flows/user-program-management/index.ts b/src/flows/user-program-management/index.ts index 53351004..5936712f 100644 --- a/src/flows/user-program-management/index.ts +++ b/src/flows/user-program-management/index.ts @@ -4,7 +4,9 @@ import { initializeEntropy } from "../../common/initializeEntropy" import { getSelectedAccount, print } from "../../common/utils" import { EntropyLogger } from "src/common/logger"; import { addProgram } from "./add"; -import { addQuestions } from "./helpers/questions"; +import { viewPrograms } from "./view"; +import { addQuestions, verifyingKeyQuestion } from "./helpers/questions"; +import { displayPrograms } from "./helpers/utils"; let verifyingKey: string; @@ -37,35 +39,21 @@ export async function userPrograms ({ accounts, selectedAccount: selectedAccount throw new Error("Keys are undefined") } - const verifyingKeyQuestion = [{ - type: 'list', - name: 'verifyingKey', - message: 'Select the key to proceeed', - choices: entropy.keyring.accounts.registration.verifyingKeys, - default: entropy.keyring.accounts.registration.verifyingKeys[0] - }] - switch (actionChoice.action) { case "View My Programs": { try { if (!verifyingKey && entropy.keyring.accounts.registration.verifyingKeys.length) { - ({ verifyingKey } = await inquirer.prompt(verifyingKeyQuestion)) + ({ verifyingKey } = await inquirer.prompt(verifyingKeyQuestion(entropy))) } else { print('You currently have no verifying keys, please register this account to generate the keys') break } - const programs = await entropy.programs.get(verifyingKey) + const programs = await viewPrograms(entropy, { verifyingKey }) if (programs.length === 0) { print("You currently have no programs set.") } else { print("Your Programs:") - programs.forEach((program, index) => { - print( - `${index + 1}. Pointer: ${ - program.program_pointer - }, Config: ${JSON.stringify(program.program_config)}` - ) - }) + displayPrograms(programs) } } catch (error) { console.error(error.message) @@ -108,7 +96,7 @@ export async function userPrograms ({ accounts, selectedAccount: selectedAccount case "Remove a Program from My List": { try { if (!verifyingKey) { - ({ verifyingKey } = await inquirer.prompt(verifyingKeyQuestion)) + ({ verifyingKey } = await inquirer.prompt(verifyingKeyQuestion(entropy))) } const { programPointerToRemove } = await inquirer.prompt([ { diff --git a/src/flows/user-program-management/types.ts b/src/flows/user-program-management/types.ts index fbcd65ee..81d85a04 100644 --- a/src/flows/user-program-management/types.ts +++ b/src/flows/user-program-management/types.ts @@ -2,4 +2,7 @@ export interface AddProgramParams { programPointer: string programConfig: string verifyingKey?: string +} +export interface ViewProgramsParams { + verifyingKey: string } \ No newline at end of file diff --git a/src/flows/user-program-management/view.ts b/src/flows/user-program-management/view.ts new file mode 100644 index 00000000..6cff1e9f --- /dev/null +++ b/src/flows/user-program-management/view.ts @@ -0,0 +1,6 @@ +import Entropy from "@entropyxyz/sdk"; +import { ViewProgramsParams } from "./types"; + +export async function viewPrograms (entropy: Entropy, { verifyingKey }: ViewProgramsParams): Promise { + return entropy.programs.get(verifyingKey) +} \ No newline at end of file diff --git a/tests/user-program-management.test.ts b/tests/user-program-management.test.ts index 71be4d73..85b7c88c 100644 --- a/tests/user-program-management.test.ts +++ b/tests/user-program-management.test.ts @@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs' import { 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' const networkType = 'two-nodes' @@ -27,5 +28,16 @@ test('User Program Management::Add Programs', async t => { await run('adding program', addProgram(entropy, noopProgramInstance)) const programsAfterAdd = await run('get programs after add', entropy.programs.get(entropy.programs.verifyingKey)) t.equal(programsBeforeAdd.length, 1, 'charlie has 2 programs') + + t.end() +}) + +test('User Program Management::View Programs', async t => { + const { run, entropy } = await setupTest(t, { seed: charlieStashSeed, networkType }) + + await run('charlie register', entropy.register()) + const programs = await run('get charlie programs', viewPrograms(entropy, { verifyingKey: entropy.programs.verifyingKey })) + + t.equal(programs.length, 1, 'charlie has 1 program') t.end() }) \ No newline at end of file From 740c3acc1e6427103d5407b95b914861a9e62ec8 Mon Sep 17 00:00:00 2001 From: mix irving Date: Tue, 16 Jul 2024 16:40:24 +1200 Subject: [PATCH 04/10] Mixmix/config encoding (#165) * write tests for migrating config encoding * complete migration 02 test * add better config encode/decode * fix migration --- package.json | 1 + src/common/utils.ts | 2 +- src/config/index.ts | 28 ++++++-- src/config/migrations/02.ts | 43 +++++++++++ src/config/migrations/index.ts | 2 + tests/config.test.ts | 126 +++++++++++++++++++++++++++++++-- yarn.lock | 5 ++ 7 files changed, 198 insertions(+), 9 deletions(-) create mode 100644 src/config/migrations/02.ts diff --git a/package.json b/package.json index 37e6dd40..c7ac98c4 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "commander": "^12.0.0", "env-paths": "^3.0.0", "inquirer": "8.0.0", + "is-base64": "^1.1.0", "lodash.clonedeep": "^4.5.0", "mkdirp": "^3.0.1", "typescript": "^4.8.4", diff --git a/src/common/utils.ts b/src/common/utils.ts index b7e03451..dbe583e2 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -9,7 +9,7 @@ export function stripHexPrefix (str: string): string { } export function replacer (key, value) { - if(value instanceof Uint8Array ){ + if (value instanceof Uint8Array) { return Buffer.from(value).toString('base64') } else return value diff --git a/src/config/index.ts b/src/config/index.ts index e502b780..4ea72eec 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -3,7 +3,7 @@ import { readFileSync } from 'node:fs' import { mkdirp } from 'mkdirp' import { join, dirname } from 'path' import envPaths from 'env-paths' - +import isBase64 from 'is-base64' import allMigrations from './migrations' import { replacer } from 'src/common/utils' @@ -58,15 +58,35 @@ function noop () {} export async function get (configPath = CONFIG_PATH) { const configBuffer = await readFile(configPath) - return JSON.parse(configBuffer.toString()) + return deserialize(configBuffer.toString()) } export function getSync (configPath = CONFIG_PATH) { const configBuffer = readFileSync(configPath, 'utf8') - return JSON.parse(configBuffer) + return deserialize(configBuffer) } export async function set (config = {}, configPath = CONFIG_PATH) { await mkdirp(dirname(configPath)) - await writeFile(configPath, JSON.stringify(config, replacer)) + await writeFile(configPath, serialize(config)) +} + +function serialize (config) { + return JSON.stringify(config, replacer, 2) +} + +function deserialize (config) { + function reviver (key, value) { + if ( + isBase64(value, { allowEmpty: false }) && + value.length >= 32 + // NOTE: we have to check length so we don't accidentally transform + // user simple string that are valid base64 like "registration" + ) { + return Uint8Array.from(Buffer.from(value, 'base64')) + } + else return value + } + + return JSON.parse(config, reviver) } diff --git a/src/config/migrations/02.ts b/src/config/migrations/02.ts new file mode 100644 index 00000000..c84b7c78 --- /dev/null +++ b/src/config/migrations/02.ts @@ -0,0 +1,43 @@ +export const version = 2 + +const targetKeys = new Set(['secretKey', 'publicKey', 'addressRaw']) + +export function migrate (data = {}) { + if (!isObject(data)) return data + if (isUI8A(data)) return data + + const initial = isArray(data) ? [] : {} + + return Object.entries(data).reduce((acc, [key, value]) => { + if (targetKeys.has(key) && !isUI8A(value)) { + acc[key] = objToUI8A(value) + } + else { + acc[key] = migrate(value) + } + + return acc + }, initial) +} + + +function isObject (thing) { + return typeof thing === 'object' +} + +function isArray (thing) { + return Array.isArray(thing) +} + +function isUI8A (thing) { + return thing instanceof Uint8Array +} + + +function objToUI8A (obj) { + const bytes = Object.keys(obj) + .sort((a, b) => Number(a) > Number(b) ? 1 : -1) + .map(arrayIndex => obj[arrayIndex]) + + return new Uint8Array(bytes) +} diff --git a/src/config/migrations/index.ts b/src/config/migrations/index.ts index 5d1f18b7..541608cd 100644 --- a/src/config/migrations/index.ts +++ b/src/config/migrations/index.ts @@ -1,9 +1,11 @@ import * as migration00 from './00' import * as migration01 from './01' +import * as migration02 from './02' const migrations = [ migration00, migration01, + migration02, ] export default migrations diff --git a/tests/config.test.ts b/tests/config.test.ts index 767f214c..0c835a36 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -2,6 +2,7 @@ import test from 'tape' import { writeFile } from 'node:fs/promises' import migrations from '../src/config/migrations' import { migrateData, init, get, set } from '../src/config' +import { replacer } from '../src/common/utils' // used to ensure unique test ids let id = Date.now() @@ -65,10 +66,17 @@ test('config - migrateData', async t => { t.end() }) +const makeKey = () => new Uint8Array( + Array(32).fill(0).map((_, i) => i * 2 + 1) +) + test('config - get', async t => { const configPath = makeTmpPath() - const config = { boop: 'doop' } - await writeFile(configPath, JSON.stringify(config)) + const config = { + boop: 'doop', + secretKey: makeKey() + } + await writeFile(configPath, JSON.stringify(config, replacer)) const result = await get(configPath) t.deepEqual(result, config, 'get works') @@ -83,11 +91,14 @@ test('config - get', async t => { test('config - set', async t => { const configPath = makeTmpPath() - const config = { dog: true } + const config = { + dog: true, + secretKey: makeKey() + } await set(config, configPath) const actual = await get(configPath) - t.deepEqual(actual, config, 'set works') + t.deepEqual(config, actual, 'set works') t.end() }) @@ -146,3 +157,110 @@ test('config - init (migration)', async t => { t.end() }) + + +test('config/migrattions/02', t => { + const initial = JSON.parse( + '{"accounts":[{"name":"Mix","address":"5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8","data":{"debug":true,"seed":"0xc4c466182b86ff1f4a16548df79c5808ab9bcde87c22c27938ac9aabc4300840","admin":{"address":"5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8","type":"registration","verifyingKeys":["0x03b225d2032e1dbff26316cc8b7d695b3386400d30ce004c1b42e2c28bcd834039"],"userContext":"ADMIN_KEY","seed":"0xc4c466182b86ff1f4a16548df79c5808ab9bcde87c22c27938ac9aabc4300840","path":"","pair":{"address":"5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8","addressRaw":{"0":182,"1":241,"2":171,"3":246,"4":239,"5":100,"6":192,"7":41,"8":49,"9":32,"10":10,"11":84,"12":241,"13":225,"14":183,"15":152,"16":164,"17":182,"18":176,"19":244,"20":39,"21":237,"22":74,"23":225,"24":250,"25":244,"26":187,"27":129,"28":97,"29":222,"30":33,"31":116},"isLocked":false,"meta":{},"publicKey":{"0":182,"1":241,"2":171,"3":246,"4":239,"5":100,"6":192,"7":41,"8":49,"9":32,"10":10,"11":84,"12":241,"13":225,"14":183,"15":152,"16":164,"17":182,"18":176,"19":244,"20":39,"21":237,"22":74,"23":225,"24":250,"25":244,"26":187,"27":129,"28":97,"29":222,"30":33,"31":116},"type":"sr25519","secretKey":{"0":120,"1":247,"2":1,"3":38,"4":246,"5":195,"6":0,"7":49,"8":84,"9":240,"10":226,"11":144,"12":66,"13":172,"14":130,"15":168,"16":237,"17":74,"18":121,"19":243,"20":49,"21":217,"22":208,"23":70,"24":160,"25":220,"26":125,"27":114,"28":230,"29":17,"30":254,"31":71,"32":158,"33":68,"34":133,"35":24,"36":119,"37":34,"38":46,"39":154,"40":85,"41":62,"42":178,"43":69,"44":206,"45":217,"46":132,"47":184,"48":8,"49":219,"50":89,"51":165,"52":189,"53":106,"54":6,"55":51,"56":112,"57":76,"58":42,"59":157,"60":146,"61":130,"62":203,"63":241}},"used":true},"registration":{"address":"5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8","type":"registration","verifyingKeys":["0x03b225d2032e1dbff26316cc8b7d695b3386400d30ce004c1b42e2c28bcd834039"],"userContext":"ADMIN_KEY","seed":"0xc4c466182b86ff1f4a16548df79c5808ab9bcde87c22c27938ac9aabc4300840","path":"","pair":{"address":"5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8","addressRaw":{"0":182,"1":241,"2":171,"3":246,"4":239,"5":100,"6":192,"7":41,"8":49,"9":32,"10":10,"11":84,"12":241,"13":225,"14":183,"15":152,"16":164,"17":182,"18":176,"19":244,"20":39,"21":237,"22":74,"23":225,"24":250,"25":244,"26":187,"27":129,"28":97,"29":222,"30":33,"31":116},"isLocked":false,"meta":{},"publicKey":{"0":182,"1":241,"2":171,"3":246,"4":239,"5":100,"6":192,"7":41,"8":49,"9":32,"10":10,"11":84,"12":241,"13":225,"14":183,"15":152,"16":164,"17":182,"18":176,"19":244,"20":39,"21":237,"22":74,"23":225,"24":250,"25":244,"26":187,"27":129,"28":97,"29":222,"30":33,"31":116},"type":"sr25519","secretKey":{"0":120,"1":247,"2":1,"3":38,"4":246,"5":195,"6":0,"7":49,"8":84,"9":240,"10":226,"11":144,"12":66,"13":172,"14":130,"15":168,"16":237,"17":74,"18":121,"19":243,"20":49,"21":217,"22":208,"23":70,"24":160,"25":220,"26":125,"27":114,"28":230,"29":17,"30":254,"31":71,"32":158,"33":68,"34":133,"35":24,"36":119,"37":34,"38":46,"39":154,"40":85,"41":62,"42":178,"43":69,"44":206,"45":217,"46":132,"47":184,"48":8,"49":219,"50":89,"51":165,"52":189,"53":106,"54":6,"55":51,"56":112,"57":76,"58":42,"59":157,"60":146,"61":130,"62":203,"63":241}},"used":true},"deviceKey":{"address":"5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8","type":"deviceKey","verifyingKeys":["0x03b225d2032e1dbff26316cc8b7d695b3386400d30ce004c1b42e2c28bcd834039"],"userContext":"CONSUMER_KEY","seed":"0xc4c466182b86ff1f4a16548df79c5808ab9bcde87c22c27938ac9aabc4300840","path":"","pair":{"address":"5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8","addressRaw":{"0":182,"1":241,"2":171,"3":246,"4":239,"5":100,"6":192,"7":41,"8":49,"9":32,"10":10,"11":84,"12":241,"13":225,"14":183,"15":152,"16":164,"17":182,"18":176,"19":244,"20":39,"21":237,"22":74,"23":225,"24":250,"25":244,"26":187,"27":129,"28":97,"29":222,"30":33,"31":116},"isLocked":false,"meta":{},"publicKey":{"0":182,"1":241,"2":171,"3":246,"4":239,"5":100,"6":192,"7":41,"8":49,"9":32,"10":10,"11":84,"12":241,"13":225,"14":183,"15":152,"16":164,"17":182,"18":176,"19":244,"20":39,"21":237,"22":74,"23":225,"24":250,"25":244,"26":187,"27":129,"28":97,"29":222,"30":33,"31":116},"type":"sr25519","secretKey":{"0":120,"1":247,"2":1,"3":38,"4":246,"5":195,"6":0,"7":49,"8":84,"9":240,"10":226,"11":144,"12":66,"13":172,"14":130,"15":168,"16":237,"17":74,"18":121,"19":243,"20":49,"21":217,"22":208,"23":70,"24":160,"25":220,"26":125,"27":114,"28":230,"29":17,"30":254,"31":71,"32":158,"33":68,"34":133,"35":24,"36":119,"37":34,"38":46,"39":154,"40":85,"41":62,"42":178,"43":69,"44":206,"45":217,"46":132,"47":184,"48":8,"49":219,"50":89,"51":165,"52":189,"53":106,"54":6,"55":51,"56":112,"57":76,"58":42,"59":157,"60":146,"61":130,"62":203,"63":241}},"used":true}}}],"selectedAccount":"5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8","endpoints":{"dev":"ws://127.0.0.1:9944","test-net":"wss://testnet.entropy.xyz"},"migration-version":1}' + ) + + const migrated = migrations[2].migrate(initial) + + // console.log(JSON.stringify(migrated, replacer, 2)) + // => { + // "accounts": [ + // { + // "name": "Mix", + // "address": "5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8", + // "data": { + // "debug": true, + // "seed": "0xc4c466182b86ff1f4a16548df79c5808ab9bcde87c22c27938ac9aabc4300840", + // "admin": { + // "address": "5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8", + // "type": "registration", + // "verifyingKeys": [ + // "0x03b225d2032e1dbff26316cc8b7d695b3386400d30ce004c1b42e2c28bcd834039" + // ], + // "userContext": "ADMIN_KEY", + // "seed": "0xc4c466182b86ff1f4a16548df79c5808ab9bcde87c22c27938ac9aabc4300840", + // "path": "", + // "pair": { + // "address": "5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8", + // "addressRaw": "tvGr9u9kwCkxIApU8eG3mKS2sPQn7Urh+vS7gWHeIXQ=", + // "isLocked": false, + // "meta": {}, + // "publicKey": "tvGr9u9kwCkxIApU8eG3mKS2sPQn7Urh+vS7gWHeIXQ=", + // "type": "sr25519", + // "secretKey": "ePcBJvbDADFU8OKQQqyCqO1KefMx2dBGoNx9cuYR/keeRIUYdyIumlU+skXO2YS4CNtZpb1qBjNwTCqdkoLL8Q==" + // }, + // "used": true + // }, + // "registration": { + // "address": "5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8", + // "type": "registration", + // "verifyingKeys": [ + // "0x03b225d2032e1dbff26316cc8b7d695b3386400d30ce004c1b42e2c28bcd834039" + // ], + // "userContext": "ADMIN_KEY", + // "seed": "0xc4c466182b86ff1f4a16548df79c5808ab9bcde87c22c27938ac9aabc4300840", + // "path": "", + // "pair": { + // "address": "5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8", + // "addressRaw": "tvGr9u9kwCkxIApU8eG3mKS2sPQn7Urh+vS7gWHeIXQ=", + // "isLocked": false, + // "meta": {}, + // "publicKey": "tvGr9u9kwCkxIApU8eG3mKS2sPQn7Urh+vS7gWHeIXQ=", + // "type": "sr25519", + // "secretKey": "ePcBJvbDADFU8OKQQqyCqO1KefMx2dBGoNx9cuYR/keeRIUYdyIumlU+skXO2YS4CNtZpb1qBjNwTCqdkoLL8Q==" + // }, + // "used": true + // }, + // "deviceKey": { + // "address": "5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8", + // "type": "deviceKey", + // "verifyingKeys": [ + // "0x03b225d2032e1dbff26316cc8b7d695b3386400d30ce004c1b42e2c28bcd834039" + // ], + // "userContext": "CONSUMER_KEY", + // "seed": "0xc4c466182b86ff1f4a16548df79c5808ab9bcde87c22c27938ac9aabc4300840", + // "path": "", + // "pair": { + // "address": "5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8", + // "addressRaw": "tvGr9u9kwCkxIApU8eG3mKS2sPQn7Urh+vS7gWHeIXQ=", + // "isLocked": false, + // "meta": {}, + // "publicKey": "tvGr9u9kwCkxIApU8eG3mKS2sPQn7Urh+vS7gWHeIXQ=", + // "type": "sr25519", + // "secretKey": "ePcBJvbDADFU8OKQQqyCqO1KefMx2dBGoNx9cuYR/keeRIUYdyIumlU+skXO2YS4CNtZpb1qBjNwTCqdkoLL8Q==" + // }, + // "used": true + // } + // } + // } + // ], + // "selectedAccount": "5GCaN3fcL6vAQQKamHzVSorwv2XqtM3WcxosCLd9JqGVrtS8", + // "endpoints": { + // "dev": "ws://127.0.0.1:9944", + // "test-net": "wss://testnet.entropy.xyz" + // }, + // "migration-version": 1 + // } + + const targetKeys = ['addressRaw', 'publicKey', 'secretKey'] + + // @ts-ignore + migrated.accounts.forEach(account => { + return Object.keys(account.data).forEach(subAccount => { + if (typeof account.data[subAccount] !== 'object') return + + t.true( + targetKeys.every(targetKey => { + return account.data[subAccount].pair[targetKey] instanceof Uint8Array + }), + `migrated: ${subAccount}` + ) + }) + }) + + t.end() +}) diff --git a/yarn.lock b/yarn.lock index 7a9863ff..d18f41bc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2378,6 +2378,11 @@ is-arrayish@^0.3.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-base64@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-base64/-/is-base64-1.1.0.tgz#8ce1d719895030a457c59a7dcaf39b66d99d56b4" + integrity sha512-Nlhg7Z2dVC4/PTvIFkgVVNvPHSO2eR/Yd0XzhGiXCXEvWnptXlXa/clQ8aePPiMuxEGcWfzWbGw2Fe3d+Y3v1g== + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" From 8dabd114bba2f6665b351a054ff3a6c968ff4064 Mon Sep 17 00:00:00 2001 From: mix irving Date: Tue, 16 Jul 2024 16:56:21 +1200 Subject: [PATCH 05/10] chill the logs out! (#161) * [NayNay] Debug Log - install and integrated winston logging library into cli - allowing us to be able to log any and everything to the respective files saved in the ~/path/to/syslogs/entropy-cryptography/entropy-cli.*.log - included example use of new logger in the tui flow for balance * chill the logs out! * Update src/common/logger.ts Co-authored-by: Nayyir Jutha * fixups --------- Co-authored-by: Nayyir Jutha Co-authored-by: Nayyir Jutha --- src/common/logger.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/logger.ts b/src/common/logger.ts index 436f3b4b..7f112143 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -1,6 +1,7 @@ import envPaths from 'env-paths' import { join } from 'path' import * as winston from 'winston' +import { replacer } from './utils' import { maskPayload } from './masking' import { EntropyLoggerOptions } from 'src/types' @@ -38,7 +39,7 @@ export class EntropyLogger { winston.format.splat(), // Uses safe-stable-stringify to finalize full object message as string // (prevents circular references from crashing) - winston.format.json(), + winston.format.json({ replacer }), ); if (isTesting) { @@ -122,5 +123,4 @@ export class EntropyLogger { stack, }); } - -} \ No newline at end of file +} From ff30a2cb5e78c7344ce27b0638bf7e45b8e87cc5 Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Tue, 16 Jul 2024 12:30:47 -0400 Subject: [PATCH 06/10] [NayNay] User Programs: Add Program + testing (#173) * [NayNay] User Programs: Add Program + testing - updated folder structure for consistency - added new method for adding programs as a pure function - added tests * updated changelog * updated chagelog; --------- Co-authored-by: Nayyir Jutha --- CHANGELOG.md | 14 ++++++++ src/flows/index.ts | 2 +- src/flows/user-program-management/add.ts | 12 +++++++ .../helpers/questions.ts | 22 +++++++++++++ .../index.ts | 32 +++---------------- src/flows/user-program-management/types.ts | 5 +++ tests/user-program-management.test.ts | 31 ++++++++++++++++++ 7 files changed, 89 insertions(+), 29 deletions(-) create mode 100644 src/flows/user-program-management/add.ts create mode 100644 src/flows/user-program-management/helpers/questions.ts rename src/flows/{UserPrograms => user-program-management}/index.ts (81%) create mode 100644 src/flows/user-program-management/types.ts create mode 100644 tests/user-program-management.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 20f9d898..e364de36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,20 @@ The format extends [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Version header format: `[version] Name - year-month-day (entropy-core compatibility: version [range])` +## [UNRELEASED] + +### Added +- new: './tests/user-program-management.test.ts' - unit tests file for user program management flows + - added test for adding a user program +- new: './src/flows/user-program-management/add.ts' - service file for adding user program pure function +### Fixed + +### Changed +- folder name for user programs to match the kebab-case style for folder namespace +### Broke + +### Meta/Dev + ## [0.0.2] AntMan - 2024-07-12 (entropy-core compatibility: 0.2.0) ### Added diff --git a/src/flows/index.ts b/src/flows/index.ts index af41e7bf..d86504ca 100644 --- a/src/flows/index.ts +++ b/src/flows/index.ts @@ -1,7 +1,7 @@ export { entropyFaucet } from './entropyFaucet' export { checkBalance } from './balance' export { register } from './register' -export { userPrograms } from './UserPrograms' +export { userPrograms } from './user-program-management' export { devPrograms } from './DeployPrograms' export { sign } from './sign' export { entropyTransfer } from './entropyTransfer' diff --git a/src/flows/user-program-management/add.ts b/src/flows/user-program-management/add.ts new file mode 100644 index 00000000..c7780f13 --- /dev/null +++ b/src/flows/user-program-management/add.ts @@ -0,0 +1,12 @@ +import Entropy from "@entropyxyz/sdk"; +import { AddProgramParams } from "./types"; + +export async function addProgram (entropy: Entropy, { programPointer, programConfig, verifyingKey }: AddProgramParams): Promise { + return entropy.programs.add( + { + program_pointer: programPointer, + program_config: programConfig, + }, + verifyingKey + ) +} \ No newline at end of file diff --git a/src/flows/user-program-management/helpers/questions.ts b/src/flows/user-program-management/helpers/questions.ts new file mode 100644 index 00000000..c787a72e --- /dev/null +++ b/src/flows/user-program-management/helpers/questions.ts @@ -0,0 +1,22 @@ +export const addQuestions = [ + { + type: "input", + name: "programPointerToAdd", + message: "Enter the program pointer you wish to add:", + validate: (input) => (input ? true : "Program pointer is required!"), + }, + { + type: "editor", + name: "programConfigJson", + message: + "Enter the program configuration as a JSON string (this will open your default editor):", + validate: (input) => { + try { + JSON.parse(input) + return true + } catch (e) { + return "Please enter a valid JSON string for the configuration." + } + }, + }, +] \ No newline at end of file diff --git a/src/flows/UserPrograms/index.ts b/src/flows/user-program-management/index.ts similarity index 81% rename from src/flows/UserPrograms/index.ts rename to src/flows/user-program-management/index.ts index da4e7c4f..53351004 100644 --- a/src/flows/UserPrograms/index.ts +++ b/src/flows/user-program-management/index.ts @@ -3,6 +3,8 @@ 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 { addQuestions } from "./helpers/questions"; let verifyingKey: string; @@ -89,39 +91,13 @@ export async function userPrograms ({ accounts, selectedAccount: selectedAccount case "Add a Program to My List": { try { - const { programPointerToAdd, programConfigJson } = await inquirer.prompt([ - { - type: "input", - name: "programPointerToAdd", - message: "Enter the program pointer you wish to add:", - validate: (input) => (input ? true : "Program pointer is required!"), - }, - { - type: "editor", - name: "programConfigJson", - message: - "Enter the program configuration as a JSON string (this will open your default editor):", - validate: (input) => { - try { - JSON.parse(input) - return true - } catch (e) { - return "Please enter a valid JSON string for the configuration." - } - }, - }, - ]) + const { programPointerToAdd, programConfigJson } = await inquirer.prompt(addQuestions) const encoder = new TextEncoder() const byteArray = encoder.encode(programConfigJson) const programConfigHex = util.u8aToHex(byteArray) - await entropy.programs.add( - { - program_pointer: programPointerToAdd, - program_config: programConfigHex, - } - ) + await addProgram(entropy, { programPointer: programPointerToAdd, programConfig: programConfigHex }) print("Program added successfully.") } catch (error) { diff --git a/src/flows/user-program-management/types.ts b/src/flows/user-program-management/types.ts new file mode 100644 index 00000000..fbcd65ee --- /dev/null +++ b/src/flows/user-program-management/types.ts @@ -0,0 +1,5 @@ +export interface AddProgramParams { + programPointer: string + programConfig: string + verifyingKey?: string +} \ No newline at end of file diff --git a/tests/user-program-management.test.ts b/tests/user-program-management.test.ts new file mode 100644 index 00000000..71be4d73 --- /dev/null +++ b/tests/user-program-management.test.ts @@ -0,0 +1,31 @@ +import test from 'tape' +import { readFileSync } from 'node:fs' +import { charlieStashSeed, setupTest } from './testing-utils' +import { AddProgramParams } from 'src/flows/user-program-management/types' +import { addProgram } from 'src/flows/user-program-management/add' + +const networkType = 'two-nodes' + +test('User Program Management::Add 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' + ) + const newPointer = await run( + 'deploy', + entropy.programs.dev.deploy(noopProgram) + ) + + const noopProgramInstance: AddProgramParams = { + programPointer: newPointer, + programConfig: '', + } + + const programsBeforeAdd = await run('get programs initial', entropy.programs.get(entropy.programs.verifyingKey)) + t.equal(programsBeforeAdd.length, 1, 'charlie has 1 program') + await run('adding program', addProgram(entropy, noopProgramInstance)) + const programsAfterAdd = await run('get programs after add', entropy.programs.get(entropy.programs.verifyingKey)) + t.equal(programsBeforeAdd.length, 1, 'charlie has 2 programs') + t.end() +}) \ No newline at end of file From 59a9ab1eae3bec151444f9e5bce39cd9e11c8bfc Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Tue, 16 Jul 2024 12:54:08 -0400 Subject: [PATCH 07/10] updated tests --- tests/testing-utils/constants.ts | 8 +++++--- tests/user-program-management.test.ts | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/testing-utils/constants.ts b/tests/testing-utils/constants.ts index 0593c40a..14f7374d 100644 --- a/tests/testing-utils/constants.ts +++ b/tests/testing-utils/constants.ts @@ -1,5 +1,7 @@ -export const charlieStashAddress = - '5Ck5SLSHYac6WFt5UZRSsdJjwmpSZq85fd5TRNAdZQVzEAPT' - +export const charlieStashAddress = '5Ck5SLSHYac6WFt5UZRSsdJjwmpSZq85fd5TRNAdZQVzEAPT' export const charlieStashSeed = '0x66256c4e2f90e273bf387923a9a7860f2e9f47a1848d6263de512f7fb110fc08' + +export const charlieSeed = + '0xbc1ede780f784bb6991a585e4f6e61522c14e1cae6ad0895fb57b9a205a8f938' +export const charlieAddress = '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y' \ No newline at end of file diff --git a/tests/user-program-management.test.ts b/tests/user-program-management.test.ts index 85b7c88c..23768e98 100644 --- a/tests/user-program-management.test.ts +++ b/tests/user-program-management.test.ts @@ -1,6 +1,6 @@ import test from 'tape' import { readFileSync } from 'node:fs' -import { charlieStashSeed, setupTest } from './testing-utils' +import { charlieSeed, 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' @@ -27,13 +27,13 @@ test('User Program Management::Add Programs', async t => { t.equal(programsBeforeAdd.length, 1, 'charlie has 1 program') await run('adding program', addProgram(entropy, noopProgramInstance)) const programsAfterAdd = await run('get programs after add', entropy.programs.get(entropy.programs.verifyingKey)) - t.equal(programsBeforeAdd.length, 1, 'charlie has 2 programs') + t.equal(programsAfterAdd.length, 2, 'charlie has 2 programs') t.end() }) test('User Program Management::View Programs', async t => { - const { run, entropy } = await setupTest(t, { seed: charlieStashSeed, networkType }) + const { run, entropy } = await setupTest(t, { seed: charlieSeed, networkType }) await run('charlie register', entropy.register()) const programs = await run('get charlie programs', viewPrograms(entropy, { verifyingKey: entropy.programs.verifyingKey })) From 109fde1f1f9194ecaf992e685b2302fc25bb05fa Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Tue, 16 Jul 2024 12:58:41 -0400 Subject: [PATCH 08/10] Update tests/manage-accounts.test.ts Co-authored-by: mix irving --- tests/manage-accounts.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/manage-accounts.test.ts b/tests/manage-accounts.test.ts index 2bef1b31..f537cf23 100644 --- a/tests/manage-accounts.test.ts +++ b/tests/manage-accounts.test.ts @@ -72,7 +72,6 @@ test('Create Account', async t => { console.error('Error while spinning network down', error.message) ) }) - await sleep(process.env.GITHUB_WORKSPACE ? 30_000 : 5_000) await run('config.init', config.init(configPath)) const testAccountSeed = randomAsHex(32) const testAccountName = 'Test Account' From 11dd078814c71d839c0eb6a054140cae03b5fb15 Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Tue, 16 Jul 2024 12:59:01 -0400 Subject: [PATCH 09/10] Update tests/manage-accounts.test.ts Co-authored-by: mix irving --- tests/manage-accounts.test.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/manage-accounts.test.ts b/tests/manage-accounts.test.ts index f537cf23..76a8db2e 100644 --- a/tests/manage-accounts.test.ts +++ b/tests/manage-accounts.test.ts @@ -65,13 +65,6 @@ test('Create Account', async t => { /* Setup */ const run = promiseRunner(t) await run('wasm', wasmGlobalsReady()) - await run('network up', spinNetworkUp(networkType)) - // this gets called after all tests are run - t.teardown(async () => { - await spinNetworkDown(networkType).catch((error) => - console.error('Error while spinning network down', error.message) - ) - }) await run('config.init', config.init(configPath)) const testAccountSeed = randomAsHex(32) const testAccountName = 'Test Account' From 9f946dc1a75748d7b803999862352129dad1a1bd Mon Sep 17 00:00:00 2001 From: Nayyir Jutha Date: Tue, 16 Jul 2024 14:47:13 -0400 Subject: [PATCH 10/10] [NayNay] Entropy Register + Unit Tests (#167) * [NayNay] Entropy Register + Unit Tests - added new register pure function - imported new types from sdk - added unit test for default register * fixed a false wpositive with register test * added test for full use of register with new program * updated changelog * Update src/common/masking.ts Co-authored-by: Frankie --------- Co-authored-by: Nayyir Jutha Co-authored-by: Frankie --- CHANGELOG.md | 1 + src/flows/index.ts | 2 +- src/flows/register/index.ts | 45 +++++++++------------------------- src/flows/register/register.ts | 28 +++++++++++++++++++++ src/flows/register/types.ts | 5 ++++ src/tui.ts | 2 +- tests/register.test.ts | 44 +++++++++++++++++++++++++++++++++ 7 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 src/flows/register/register.ts create mode 100644 src/flows/register/types.ts create mode 100644 tests/register.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d402c27..6911c81b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Version header format: `[version] Name - year-month-day (entropy-core compatibil ## [UNRELEASED] ### Added +- new: 'src/flows/register/register.ts' - service file for register pure function - new: './src/flows/manage-accounts/helpers/create-account.ts' - new helper file to house the pure function used to create a new entropy account - update: './tests/manage-accounts.test.ts' - added test for create account pure function - update: './src/common/utils.ts' - removed isValidSubstrateAddress and imported the method in from the sdk diff --git a/src/flows/index.ts b/src/flows/index.ts index d86504ca..2187a5eb 100644 --- a/src/flows/index.ts +++ b/src/flows/index.ts @@ -1,6 +1,6 @@ export { entropyFaucet } from './entropyFaucet' export { checkBalance } from './balance' -export { register } from './register' +export { entropyRegister } from './register' export { userPrograms } from './user-program-management' export { devPrograms } from './DeployPrograms' export { sign } from './sign' diff --git a/src/flows/register/index.ts b/src/flows/register/index.ts index d15d6f88..05247578 100644 --- a/src/flows/register/index.ts +++ b/src/flows/register/index.ts @@ -2,8 +2,9 @@ import { getSelectedAccount, print, /*accountChoices*/ } from "../../common/utils" import { initializeEntropy } from "../../common/initializeEntropy" import { EntropyLogger } from "src/common/logger"; +import { register } from "./register"; -export async function register (storedConfig, options, logger: EntropyLogger) { +export async function entropyRegister (storedConfig, options, logger: EntropyLogger) { const FLOW_CONTEXT = 'REGISTER' const { accounts, selectedAccount: selectedFromConfig } = storedConfig; const { endpoint } = options @@ -22,41 +23,19 @@ export async function register (storedConfig, options, logger: EntropyLogger) { // // Setting default to default key proxy program // default: '0x0000000000000000000000000000000000000000000000000000000000000000' // }]) - //@ts-ignore: + // @ts-expect-error: Expecting error here as method expects typeof ChildKey enum from sdk + // export from sdk is not working as intended currently logger.debug('about to register selectedAccount.address' + selectedAccount.address + 'keyring:' + entropy.keyring.getLazyLoadAccountProxy('registration').pair.address, FLOW_CONTEXT) print("Attempting to register the address:", selectedAccount.address, ) - let verifyingKey: string + try { - // For now we are forcing users to only register with the default info before having to format the config for them - // verifyingKey = await entropy.register({ - // programDeployer: entropy.keyring.accounts.registration.address, - // programData: [{ - // program_pointer: programPointer, - // program_config: '0x', - // }] - // }) - verifyingKey = await entropy.register() - if (verifyingKey) { - print("Your address", selectedAccount.address, "has been successfully registered.") - selectedAccount?.data?.registration?.verifyingKeys?.push(verifyingKey) - const arrIdx = accounts.indexOf(selectedAccount) - accounts.splice(arrIdx, 1, selectedAccount) - return { accounts, selectedAccount: selectedAccount.address } - } + const verifyingKey = await register(entropy) + print("Your address", selectedAccount.address, "has been successfully registered.") + selectedAccount?.data?.registration?.verifyingKeys?.push(verifyingKey) + const arrIdx = accounts.indexOf(selectedAccount) + accounts.splice(arrIdx, 1, selectedAccount) + return { accounts, selectedAccount: selectedAccount.address } } catch (error) { - console.error('error', error); - if (!verifyingKey) { - logger.debug('Pruning Registration', FLOW_CONTEXT) - try { - const tx = await entropy.substrate.tx.registry.pruneRegistration() - await tx.signAndSend(entropy.keyring.accounts.registration.pair, ({ status }) => { - if (status.isFinalized) { - print('Successfully pruned registration'); - } - }) - } catch (error) { - console.error('Unable to prune registration due to:', error.message); - } - } + logger.error('There was a problem registering', error) } } diff --git a/src/flows/register/register.ts b/src/flows/register/register.ts new file mode 100644 index 00000000..8b2c3941 --- /dev/null +++ b/src/flows/register/register.ts @@ -0,0 +1,28 @@ +import Entropy from "@entropyxyz/sdk"; +import { RegsiterParams } from "./types"; +import { print } from "src/common/utils"; + +export async function register (entropy: Entropy, params?: RegsiterParams): Promise { + let verifyingKey: string + try { + const registerParams = params?.programModAddress && params?.programData ? { programDeployer: params.programModAddress, programData: params.programData } : undefined + + verifyingKey = await entropy.register(registerParams) + return verifyingKey + } catch (error) { + if (!verifyingKey) { + try { + const tx = entropy.substrate.tx.registry.pruneRegistration() + await tx.signAndSend(entropy.keyring.accounts.registration.pair, ({ status }) => { + if (status.isFinalized) { + print('Successfully pruned registration'); + } + }) + } catch (error) { + console.error('Unable to prune registration due to:', error.message); + throw error + } + } + throw error + } +} \ No newline at end of file diff --git a/src/flows/register/types.ts b/src/flows/register/types.ts new file mode 100644 index 00000000..df77d563 --- /dev/null +++ b/src/flows/register/types.ts @@ -0,0 +1,5 @@ +export interface RegsiterParams { + programModAddress?: string + // TODO: Export ProgramInstance type from sdk + programData?: any +} \ No newline at end of file diff --git a/src/tui.ts b/src/tui.ts index 872fd5ee..90553819 100644 --- a/src/tui.ts +++ b/src/tui.ts @@ -19,7 +19,7 @@ export default function tui (options: EntropyTuiOptions) { const choices = { 'Manage Accounts': flows.manageAccounts, 'Balance': flows.checkBalance, - 'Register': flows.register, + 'Register': flows.entropyRegister, 'Sign': flows.sign, 'Transfer': flows.entropyTransfer, 'Deploy Program': flows.devPrograms, diff --git a/tests/register.test.ts b/tests/register.test.ts new file mode 100644 index 00000000..bbd21618 --- /dev/null +++ b/tests/register.test.ts @@ -0,0 +1,44 @@ +import test from 'tape' + +import { charlieStashSeed, setupTest } from './testing-utils' +import { register } from 'src/flows/register/register' +import { readFileSync } from 'node:fs' + +const networkType = 'two-nodes' + +test('Regsiter - Default Program', async (t) => { + const { run, entropy } = await setupTest(t, { networkType, seed: charlieStashSeed }) + + const verifyingKey = await run('register account', register(entropy)) + + const fullAccount = entropy.keyring.getAccount() + + t.equal(verifyingKey, fullAccount.registration.verifyingKeys[0], 'verifying key matches key added to regsitration account') + + t.end() +}) + +test('Register - Barebones Program', async t => { + const { run, entropy } = await setupTest(t, { networkType, seed: charlieStashSeed }) + const dummyProgram: any = readFileSync( + 'src/programs/template_barebones.wasm' + ) + const pointer = await run( + 'deploy program', + entropy.programs.dev.deploy(dummyProgram) + ) + + const verifyingKey = await run( + 'register', + entropy.register({ + programDeployer: entropy.keyring.accounts.registration.address, + programData: [{ program_pointer: pointer, program_config: '0x' }], + }) + ) + + const fullAccount = entropy.keyring.getAccount() + + t.equal(verifyingKey, fullAccount.registration.verifyingKeys[1], 'verifying key matches key added to regsitration account') + + t.end() +})