Skip to content

Commit 82a0f3a

Browse files
committed
refactor: simplified the wallet commands
fix: got tests passing
1 parent e6bbd6e commit 82a0f3a

6 files changed

Lines changed: 310 additions & 142 deletions

File tree

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
"homepage": "https://github.com/wharfkit/cli#readme",
66
"description": "Command line utilities for Wharf",
77
"scripts": {
8-
"prepare": "make"
8+
"prepare": "make",
9+
"check": "tsc --noEmit",
10+
"test": "make test",
11+
"lint": "make check"
912
},
1013
"engines": {
1114
"node": ">=18.0.0"

src/commands/chain/local.ts

Lines changed: 215 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable no-console */
2-
import {PrivateKey} from '@wharfkit/antelope'
2+
import {PrivateKey, PublicKey} from '@wharfkit/antelope'
3+
import {Session} from '@wharfkit/session'
34
import {WalletPluginPrivateKey} from '@wharfkit/wallet-plugin-privatekey'
45
import {spawn} from 'child_process'
56
import * as fs from 'fs'
@@ -23,7 +24,7 @@ import {
2324
waitForChain,
2425
} from './utils'
2526
import {ensureLeapInstalled} from './install'
26-
import {addKeyToWallet, listWalletKeys} from '../wallet/utils'
27+
import {addKeyToWallet, DEFAULT_KEY_NAME, listWalletKeys} from '../wallet/utils'
2728
import {NonInteractiveConsoleUI} from '../../utils/wharfkit-ui'
2829

2930
export interface LocalStartOptions {
@@ -158,6 +159,8 @@ export async function startLocalChain(options: LocalStartOptions): Promise<void>
158159

159160
console.log('Chain is ready!')
160161

162+
await ensureEosioPermissionsMatchChainKey(options.port, chainPrivateKey)
163+
161164
// Setup dev wallet (import the key used for the chain)
162165
await setupDevWallet(chainPrivateKey.toString())
163166

@@ -358,62 +361,15 @@ function isPrivateKeyString(str: string): boolean {
358361
async function setupDevWallet(customKey?: string): Promise<void> {
359362
console.log('Setting up development wallet...')
360363

361-
const walletName = 'dev'
364+
const walletName = DEFAULT_KEY_NAME
362365
const devKeys = getDevKeys()
363366
const devPrivateKey = PrivateKey.from(devKeys.privateKey)
364367

365368
try {
366-
// First, import the default dev key
367-
const existingKeys = listWalletKeys()
368-
const existingEntry = existingKeys.find(
369-
(key) => key.name === walletName || key.publicKey === devKeys.publicKey
370-
)
369+
ensureDevelopmentKeyStored(devPrivateKey, walletName, devKeys.publicKey)
371370

372-
if (!existingEntry) {
373-
addKeyToWallet(devPrivateKey, walletName)
374-
console.log(`Stored development key in WharfKit wallet as "${walletName}"`)
375-
} else if (existingEntry.name !== walletName) {
376-
console.log(
377-
`Development key already stored as "${existingEntry.name}", keeping existing entry`
378-
)
379-
} else {
380-
console.log('Development key already stored')
381-
}
382-
383-
// If a custom key is provided, import it automatically
384-
// Store genesis key as 'eosio' for predictable account creation
385371
if (customKey) {
386-
try {
387-
if (isPrivateKeyString(customKey)) {
388-
const customPrivateKey = PrivateKey.from(customKey)
389-
const customPublicKey = customPrivateKey.toPublic().toString()
390-
391-
// Check if 'default' key already exists
392-
const existingDefaultKey = existingKeys.find((k) => k.name === 'default')
393-
394-
if (existingDefaultKey) {
395-
// 'default' key already exists - reuse it
396-
console.log(
397-
'Genesis key already exists in wallet as "default" - reusing it'
398-
)
399-
} else {
400-
// 'default' doesn't exist - create it
401-
addKeyToWallet(customPrivateKey, 'default')
402-
console.log(
403-
'✅ Automatically imported genesis key into wallet as "default"'
404-
)
405-
console.log(` Public Key: ${customPublicKey}`)
406-
}
407-
} else {
408-
console.log(
409-
`Warning: Provided key "${customKey}" does not appear to be a valid private key format`
410-
)
411-
}
412-
} catch (error: any) {
413-
console.log(`Warning: Could not import chain key: ${error.message}`)
414-
console.log('You can manually import it with:')
415-
console.log(` wharfkit wallet keys add "${customKey}"`)
416-
}
372+
ensureChainKeyStored(customKey)
417373
}
418374

419375
const walletPlugin = new WalletPluginPrivateKey(devPrivateKey)
@@ -428,3 +384,210 @@ async function setupDevWallet(customKey?: string): Promise<void> {
428384
console.log(` wharfkit wallet keys add --name ${walletName} ${devKeys.privateKey}`)
429385
}
430386
}
387+
388+
function ensureDevelopmentKeyStored(
389+
devPrivateKey: PrivateKey,
390+
walletName: string,
391+
devPublicKey: string
392+
): void {
393+
const walletKeys = listWalletKeys()
394+
const existingByPublic = walletKeys.find((key) => key.publicKey === devPublicKey)
395+
396+
if (existingByPublic) {
397+
if (existingByPublic.name === walletName) {
398+
console.log('Development key already stored')
399+
} else {
400+
console.log(
401+
`Development key already stored as "${existingByPublic.name}", keeping existing entry`
402+
)
403+
}
404+
return
405+
}
406+
407+
const conflictingByName = walletKeys.find((key) => key.name === walletName)
408+
if (conflictingByName) {
409+
console.log(
410+
`Warning: Wallet already contains a key named "${walletName}" with a different public key.`
411+
)
412+
console.log(' Skipping automatic import of the development key to avoid conflicts.')
413+
console.log(
414+
` You can remove or rename the existing entry and rerun "wharfkit chain local start".`
415+
)
416+
return
417+
}
418+
419+
addKeyToWallet(devPrivateKey, walletName)
420+
console.log(`Stored development key in WharfKit wallet as "${walletName}"`)
421+
}
422+
423+
function ensureChainKeyStored(customKey: string): void {
424+
if (!isPrivateKeyString(customKey)) {
425+
console.log(
426+
`Warning: Provided key "${customKey}" does not appear to be a valid private key format`
427+
)
428+
return
429+
}
430+
431+
try {
432+
const customPrivateKey = PrivateKey.from(customKey)
433+
const customPublicKey = customPrivateKey.toPublic().toString()
434+
const walletKeys = listWalletKeys()
435+
const existingEntry = walletKeys.find((key) => key.publicKey === customPublicKey)
436+
437+
if (existingEntry) {
438+
console.log(
439+
`Genesis key already stored in wallet as "${existingEntry.name}" - reusing it`
440+
)
441+
return
442+
}
443+
444+
// Try to use default name
445+
const defaultKey = walletKeys.find((key) => key.name === DEFAULT_KEY_NAME)
446+
if (!defaultKey) {
447+
addKeyToWallet(customPrivateKey, DEFAULT_KEY_NAME)
448+
console.log(
449+
`✅ Automatically imported genesis key into wallet as "${DEFAULT_KEY_NAME}"`
450+
)
451+
console.log(` Public Key: ${customPublicKey}`)
452+
return
453+
}
454+
455+
// If default already exists, warn user
456+
console.log(
457+
`Warning: Key "${DEFAULT_KEY_NAME}" already exists. Skipping automatic import of genesis key.`
458+
)
459+
console.log('You can manually import it with:')
460+
console.log(` wharfkit wallet keys add "${customKey}"`)
461+
} catch (error: any) {
462+
console.log(`Warning: Could not import chain key: ${error.message}`)
463+
console.log('You can manually import it with:')
464+
console.log(` wharfkit wallet keys add "${customKey}"`)
465+
}
466+
}
467+
468+
async function ensureEosioPermissionsMatchChainKey(
469+
port: number,
470+
chainPrivateKey: PrivateKey
471+
): Promise<void> {
472+
try {
473+
const client = createApiClientForPort(port)
474+
const [account, info] = await Promise.all([
475+
client.v1.chain.get_account('eosio'),
476+
client.v1.chain.get_info(),
477+
])
478+
const targetPublicKey = chainPrivateKey.toPublic()
479+
const permissions: any[] = account.permissions ?? []
480+
const ownerPermission = permissions.find((perm) => perm.perm_name === 'owner')
481+
const activePermission = permissions.find((perm) => perm.perm_name === 'active')
482+
483+
const ownerMatches = permissionIncludesKey(ownerPermission, targetPublicKey)
484+
const activeMatches = permissionIncludesKey(activePermission, targetPublicKey)
485+
486+
if (ownerMatches && activeMatches) {
487+
console.log('eosio account permissions already match the configured chain key')
488+
return
489+
}
490+
491+
console.log('Aligning eosio account permissions with the configured chain key...')
492+
493+
const walletPlugin = new WalletPluginPrivateKey(chainPrivateKey)
494+
walletPlugin.config.requiresChainSelect = false
495+
walletPlugin.config.requiresPermissionSelect = false
496+
walletPlugin.config.requiresPermissionEntry = false
497+
498+
const session = new Session({
499+
chain: {
500+
id: String(info.chain_id),
501+
url: `http://127.0.0.1:${port}`,
502+
},
503+
actor: 'eosio',
504+
permission: 'owner',
505+
walletPlugin,
506+
ui: new NonInteractiveConsoleUI(),
507+
})
508+
509+
const actions: any[] = []
510+
if (!ownerMatches) {
511+
actions.push(buildUpdateAuthAction('owner', targetPublicKey))
512+
}
513+
if (!activeMatches) {
514+
actions.push(buildUpdateAuthAction('active', targetPublicKey))
515+
}
516+
517+
if (actions.length === 0) {
518+
return
519+
}
520+
521+
await session.transact(
522+
{
523+
actions,
524+
},
525+
{
526+
broadcast: true,
527+
}
528+
)
529+
530+
console.log('Updated eosio account permissions to use the configured chain key')
531+
} catch (error: any) {
532+
console.log(
533+
`Warning: Could not verify or update eosio permissions: ${error?.message ?? error}`
534+
)
535+
console.log('You can manually verify with:')
536+
console.log(
537+
' curl -s http://127.0.0.1:8888/v1/chain/get_account -X POST -d \'{"account_name":"eosio"}\''
538+
)
539+
}
540+
}
541+
542+
function permissionIncludesKey(permission: any, targetPublicKey: PublicKey): boolean {
543+
if (!permission || !permission.required_auth) {
544+
return false
545+
}
546+
547+
const targetVariants = new Set<string>([
548+
targetPublicKey.toString(),
549+
targetPublicKey.toLegacyString(),
550+
])
551+
552+
return (permission.required_auth.keys ?? []).some((entry: any) => {
553+
if (!entry?.key) {
554+
return false
555+
}
556+
557+
try {
558+
const parsedKey = PublicKey.from(entry.key)
559+
return parsedKey.equals(targetPublicKey)
560+
} catch {
561+
return targetVariants.has(entry.key)
562+
}
563+
})
564+
}
565+
566+
function buildUpdateAuthAction(permission: 'owner' | 'active', publicKey: PublicKey): any {
567+
return {
568+
account: 'eosio',
569+
name: 'updateauth',
570+
authorization: [
571+
{
572+
actor: 'eosio',
573+
permission: 'owner',
574+
},
575+
],
576+
data: {
577+
account: 'eosio',
578+
permission,
579+
parent: permission === 'owner' ? '' : 'owner',
580+
auth: {
581+
threshold: 1,
582+
keys: [
583+
{
584+
key: publicKey.toString(),
585+
weight: 1,
586+
},
587+
],
588+
accounts: [],
589+
waits: [],
590+
},
591+
},
592+
}
593+
}

0 commit comments

Comments
 (0)