Skip to content

Commit

Permalink
Merge branch 'dev' into naynay/remove-programs
Browse files Browse the repository at this point in the history
  • Loading branch information
rh0delta committed Jul 16, 2024
2 parents 3007519 + 9f946dc commit dba8cc9
Show file tree
Hide file tree
Showing 29 changed files with 428 additions and 117 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,19 @@ 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
- 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
- added test for removing 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
- new: './src/flows/user-program-management/remove.ts' - service file for removing user program pure function
### Fixed

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions src/common/logger.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -122,5 +123,4 @@ export class EntropyLogger {
stack,
});
}

}
}
14 changes: 1 addition & 13 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { decodeAddress, encodeAddress } from "@polkadot/keyring"
import { hexToU8a, isHex } from "@polkadot/util"
import { Buffer } from 'buffer'
import { EntropyAccountConfig } from "../config/types"

Expand All @@ -9,7 +7,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
Expand Down Expand Up @@ -48,16 +46,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) => ({
Expand Down
28 changes: 24 additions & 4 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
}
43 changes: 43 additions & 0 deletions src/config/migrations/02.ts
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 2 additions & 0 deletions src/config/migrations/index.ts
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion src/flows/index.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
8 changes: 2 additions & 6 deletions src/flows/manage-accounts/cli.ts
Original file line number Diff line number Diff line change
@@ -1,12 +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,
verifyingKeys: account?.data?.admin?.verifyingKeys
}))
return listAccounts(storedConfig)
}
25 changes: 25 additions & 0 deletions src/flows/manage-accounts/helpers/create-account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// @ts-ignore
import Keyring from '@entropyxyz/sdk/keys'
import { EntropyLogger } from 'src/common/logger';
import { EntropyAccountConfig } from "src/config/types";

export async function createAccount ({ name, seed, path }: { name: string, seed: string, path?: string }, logger?: EntropyLogger): Promise<EntropyAccountConfig> {
const FLOW_CONTEXT = 'MANAGE_ACCOUNTS::CREATE_ACCOUNT'
const keyring = new Keyring({ seed, path, debug: true })
const fullAccount = keyring.getAccount()
// TO-DO: sdk should create account on constructor
const { admin } = keyring.getAccount()
logger?.debug('fullAccount:', FLOW_CONTEXT)
logger?.debug(fullAccount, FLOW_CONTEXT)

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,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const importQuestions = [
{
type: 'input',
name: 'path',
meesage: 'derivation path:',
message: 'derivation path:',
default: 'none',
when: ({ importKey }) => importKey
},
Expand Down
4 changes: 2 additions & 2 deletions src/flows/manage-accounts/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import inquirer from 'inquirer'
import { print } from '../../common/utils'
import { newKey } from './new-key'
import { newAccount } from './new-account'
import { selectAccount } from './select-account'
import { listAccounts } from './list'
import { EntropyTuiOptions } from 'src/types'
import { EntropyLogger } from 'src/common/logger'

const actions = {
'Create/Import Account': newKey,
'Create/Import Account': newAccount,
'Select Account': selectAccount,
'List Accounts': (config) => {
try {
Expand Down
4 changes: 3 additions & 1 deletion src/flows/manage-accounts/list.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
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 { print } from '../../common/utils'
import { createAccount } from './helpers/create-account'
import { EntropyLogger } from 'src/common/logger'

export async function newKey ({ accounts }, logger: EntropyLogger) {
const FLOW_CONTEXT = 'MANAGE_ACCOUNTS::NEW_KEY'
export async function newAccount ({ accounts }, logger: EntropyLogger) {
accounts = Array.isArray(accounts) ? accounts : []

const questions = [
Expand Down Expand Up @@ -64,23 +62,7 @@ export async function newKey ({ accounts }, logger: EntropyLogger) {
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()
logger.debug('fullAccount:', FLOW_CONTEXT)
logger.debug(fullAccount, FLOW_CONTEXT)

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 }, logger)

print(`New account:\n{\n\tname: ${newAccount.name}\n\taddress: ${newAccount.address}\n}`)

Expand Down
45 changes: 12 additions & 33 deletions src/flows/register/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
}
Loading

0 comments on commit dba8cc9

Please sign in to comment.