11/* eslint-disable no-console */
2- import { PrivateKey } from '@wharfkit/antelope'
2+ import { PrivateKey , PublicKey } from '@wharfkit/antelope'
3+ import { Session } from '@wharfkit/session'
34import { WalletPluginPrivateKey } from '@wharfkit/wallet-plugin-privatekey'
45import { spawn } from 'child_process'
56import * as fs from 'fs'
@@ -23,7 +24,7 @@ import {
2324 waitForChain ,
2425} from './utils'
2526import { ensureLeapInstalled } from './install'
26- import { addKeyToWallet , listWalletKeys } from '../wallet/utils'
27+ import { addKeyToWallet , DEFAULT_KEY_NAME , listWalletKeys } from '../wallet/utils'
2728import { NonInteractiveConsoleUI } from '../../utils/wharfkit-ui'
2829
2930export 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 {
358361async 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