import { newKitFromWeb3 } from '@celo/contractkit' ; import { hotfixToHash , ProposalBuilder , proposalToJSON } from '@celo/governance' ; import { privateKeyToAddress } from '@celo/utils/lib/address' ; import { concurrentMap } from '@celo/utils/lib/async' ; import { randomBytes } from 'crypto' ; import { getFornoUrl } from 'src/lib/endpoints' ; import { envVar , fetchEnv } from 'src/lib/env-utils' ; import { AccountType , getPrivateKeysFor } from 'src/lib/generate_utils' ; import { exitIfCelotoolHelmDryRun } from 'src/lib/helm_deploy' ; import Web3 from 'web3' ; import yargs from 'yargs' ; import { UpgradeArgv } from '../../deploy/upgrade' ; export const command = 'hotfix' ; export const describe = 'runs a hotfix' ; type EthstatsArgv = UpgradeArgv & { } ; export const builder = ( argv ) => { return argv ; } ; export const handler = async ( argv ) => { exitIfCelotoolHelmDryRun ( ) ; try { const kit = newKitFromWeb3 ( new Web3 ( getFornoUrl ( argv . celoEnv ) ) ) ; const governance = await kit . contracts . getGovernance ( ) ; const keys = getPrivateKeysFor ( AccountType . VALIDATOR , fetchEnv ( envVar . MNEMONIC ) , parseInt ( fetchEnv ( envVar . VALIDATORS ) , 10 ) ) ; const addresses = keys . map ( privateKeyToAddress ) ; console . info ( 'Add keys to ContractKit' ) ; for ( const key of keys ) { kit . connection . addAccount ( key ) ; } const proposalBuilder = new ProposalBuilder ( kit ) ; const proposal = await proposalBuilder . build ( ) ; if ( proposal . length === 0 ) { console . error ( '\nPlease see examples in hotfix.ts and add transactions' ) ; process . exit ( 1 ) ; } console . info ( 'Proposal: ' , await proposalToJSON ( kit , proposal ) ) ; const salt = randomBytes ( 32 ) ; console . info ( `Salt: ${ salt . toString ( 'hex' ) }` ) ; const proposalHash = hotfixToHash ( kit , proposal , salt ) ; console . info ( `Proposal Hash: ${ proposalHash . toString ( 'hex' ) }` ) ; console . info ( '\nWhitelist the hotfix' ) ; await concurrentMap ( 25 , addresses , async ( address , index ) => { try { await governance . whitelistHotfix ( proposalHash ) . sendAndWaitForReceipt ( { from : address } ) ; } catch ( error ) { console . error ( `Error whitelisting for validator ${ index } (${ address }): ${ error }` ) ; } } ) ; let hotfixRecord = await governance . getHotfixRecord ( proposalHash ) ; console . info ( 'Hotfix Record: ' , hotfixRecord ) ; console . info ( '\nApprove the hotfix' ) ; await governance . approveHotfix ( proposalHash ) . sendAndWaitForReceipt ( { from : addresses [ 0 ] } ) ; hotfixRecord = await governance . getHotfixRecord ( proposalHash ) ; console . info ( 'Hotfix Record: ' , hotfixRecord ) ; const canPass = await governance . isHotfixPassing ( proposalHash ) ; const tally = await governance . hotfixWhitelistValidatorTally ( proposalHash ) ; if ( ! canPass ) { throw new Error ( `Hotfix cannot pass. Currently tally is ${ tally }` ) ; } console . info ( '\nPrepare the hotfix' ) ; await governance . prepareHotfix ( proposalHash ) . sendAndWaitForReceipt ( { from : addresses [ 0 ] } ) ; hotfixRecord = await governance . getHotfixRecord ( proposalHash ) ; console . info ( '\nHotfix Record: ' , hotfixRecord ) ; if ( hotfixRecord . preparedEpoch . toNumber ( ) === 0 ) { console . error ( 'Hotfix could not be prepared' ) ; throw new Error ( ) ; } console . info ( '\nExecute the hotfix' ) ; await governance . executeHotfix ( proposal , salt ) . sendAndWaitForReceipt ( { from : addresses [ 0 ] } ) ; hotfixRecord = await governance . getHotfixRecord ( proposalHash ) ; console . info ( '\nHotfix Record: ' , hotfixRecord ) ; if ( ! hotfixRecord . executed ) { console . error ( 'Hotfix could somehow not be executed' ) ; throw new Error ( ) ; } console . info ( 'Hotfix successfully executed!' ) ; process . exit ( 0 ) ; } catch ( error ) { console . error ( error ) ; process . exit ( 1 ) ; } } ;