Skip to content

Commit

Permalink
Added new executor logic
Browse files Browse the repository at this point in the history
  • Loading branch information
RPate97 committed Nov 9, 2022
1 parent 6eba17e commit 071d867
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 241 deletions.
6 changes: 6 additions & 0 deletions .changeset/kind-crews-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@chugsplash/executor': patch
'@chugsplash/plugins': patch
---

Implemented minimal standalone executor
157 changes: 44 additions & 113 deletions packages/executor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,16 @@ import hre from 'hardhat'
import { BaseServiceV2, validators } from '@eth-optimism/common-ts'
import { ethers } from 'ethers'
import {
ChugSplashRegistryABI,
ChugSplashManagerABI,
ChugSplashRegistryABI,
CHUGSPLASH_REGISTRY_PROXY_ADDRESS,
} from '@chugsplash/contracts'
import { ChugSplashBundleState } from '@chugsplash/core'

import {
parseStrategyString,
ExecutorSelectionStrategy,
compileRemoteBundle,
} from './utils'
import { compileRemoteBundle } from './utils'

type Options = {
registry: string
rpc: ethers.providers.StaticJsonRpcProvider
key: string
ess: string
eps: string
network: string
}

type Metrics = {}
Expand All @@ -39,90 +33,59 @@ export class ChugSplashExecutor extends BaseServiceV2<Options, Metrics, State> {
loopIntervalMs: 1000,
options,
optionsSpec: {
registry: {
desc: 'address of the ChugSplashRegistry contract',
validator: validators.str,
},
rpc: {
desc: 'rpc for the chain to run the executor on',
validator: validators.staticJsonRpcProvider,
},
key: {
desc: 'private key to use for signing transactions',
validator: validators.str,
},
ess: {
desc: 'comma separated list of ESS contracts to accept',
validator: validators.str,
},
eps: {
desc: 'comma separated list of EPS contracts to accept',
network: {
desc: 'network for the chain to run the executor on',
validator: validators.str,
default: 'http://localhost:8545',
},
// key: {
// desc: 'private key to use for signing transactions',
// validator: validators.str,
// },
},
metricsSpec: {},
})
}

async init() {
const reg = CHUGSPLASH_REGISTRY_PROXY_ADDRESS
const provider = ethers.getDefaultProvider(this.options.network)
this.state.registry = new ethers.Contract(
this.options.registry,
reg,
ChugSplashRegistryABI,
this.options.rpc
provider
)
this.state.wallet = new ethers.Wallet(this.options.key, this.options.rpc)
this.state.ess = parseStrategyString(this.options.ess)
this.state.eps = parseStrategyString(this.options.eps)

this.state.wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider)
}

async main() {
// TODO: Recover if we crashed and are in the middle of executing an upgrade

// Find all active upgrades that have not yet been started
const approvalAnnouncementEvents = await this.state.registry.queryFilter(
this.state.registry.filters.EventAnnounced('ChugSplashBundleApproved')
)

// TODO: Cache events that we've already seen so we don't do a bunch of work on the same events
// more than once.
// TODO: When we spin up, should we look for previous events, or should we only look at new
// events? If we look at previous events, we need to figure out how to quickly filter out
// upgrades that have already been completed.

for (const approvalAnnouncementEvent of approvalAnnouncementEvents) {
const signer = this.state.wallet
const manager = new ethers.Contract(
approvalAnnouncementEvent.args.manager,
ChugSplashManagerABI,
this.state.wallet
signer
)

// TODO: Add this to the ChugSplashManager contract
const ess = await manager.getExecutorSelectionStrategy()
if (!this.state.ess.includes(ess)) {
// We don't like this strategy, skip the upgrade.
continue
}

// TODO: Add this to the ChugSplashManager contract
const eps = await manager.getExecutorPaymentStrategy()
if (!this.state.eps.includes(eps)) {
// We don't like this strategy, skip the upgrade.
const activeBundleId = await manager.activeBundleId()
if (
activeBundleId ===
'0x0000000000000000000000000000000000000000000000000000000000000000'
) {
console.log('no active bundle')
continue
}

const receipt = await approvalAnnouncementEvent.getTransactionReceipt()
const approvalEvent = manager.parseLog(
receipt.logs.find((log) => {
return log.logIndex === approvalAnnouncementEvent.logIndex - 1
})
const bundleState: ChugSplashBundleState = await manager.bundles(
activeBundleId
)

const activeBundleId = await manager.activeBundleId()
if (activeBundleId !== approvalEvent.args.bundleId) {
// This is not the active bundle, so we can skip it.
continue
}

// TODO: Add this to the ChugSplashManager contract
const selectedExecutor = await manager.getSelectedExecutor(activeBundleId)
if (selectedExecutor !== ethers.constants.AddressZero) {
Expand All @@ -131,67 +94,35 @@ export class ChugSplashExecutor extends BaseServiceV2<Options, Metrics, State> {
}

const proposalEvents = await manager.queryFilter(
manager.filters.EventProposed(activeBundleId)
manager.filters.ChugSplashBundleProposed(activeBundleId)
)

if (proposalEvents.length !== 1) {
// TODO: throw an error here or skip
}

const proposalEvent = proposalEvents[0]
const bundle = await compileRemoteBundle(
const { bundle, canonicalConfig } = await compileRemoteBundle(
hre,
proposalEvent.args.configUri
)
if (bundle.root !== proposalEvent.args.bundleRoot) {
// TODO: throw an error here or skip
}

// TODO: Perform a quick upper-bound estimation of the amount of gas required to execute this
// upgrade. We can do this without simulating anything because ChugSplash upgrades are
// fully deterministic. If account's balance is above the upper-bound estimation, then
// we're ok with claiming the upgrade.

// Try to become the selected executor.
// TODO: Use an adapter system to make this easier.
if (ess === ExecutorSelectionStrategy.SIMPLE_LOCK) {
try {
const strategy = new ethers.Contract(
ess,
// TODO: Use the right ABI here
ChugSplashManagerABI,
this.state.wallet
)

const tx = await strategy.claim(activeBundleId)
await tx.wait()
} catch (err) {
// Unable to claim the lock, so skip this upgrade.
continue
}
} else {
throw new Error(`unsupported strategy: ${ess}`)
}

// TODO: Handle cancellation cleanly
for (const action of bundle.actions) {
// TODO: Handle errors cleanly
const tx = await manager.executeChugSplashAction(
action.action,
action.proof.actionIndex,
action.proof.siblings
)
await tx.wait()
}

const completedEvents = await manager.queryFilter(
manager.filters.ChugSplashBundleCompleted(activeBundleId)
)
if (completedEvents.length !== 1) {
// TODO: throw an error here
}

// TODO: Check that we got paid appropriately.
// TODO: Get our bond back.
// todo call chugsplash-execute if deploying locally
await hre.run('chugsplash-execute', {
chugSplashManager: manager,
bundleState,
bundle,
deployerAddress: await signer.getAddress(),
parsedConfig: canonicalConfig,
deployer: signer,
hide: false,
})
}
}
}

const executor = new ChugSplashExecutor()
executor.run()
12 changes: 8 additions & 4 deletions packages/executor/src/utils/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { add0x } from '@eth-optimism/core-utils'
import {
CanonicalChugSplashConfig,
ChugSplashActionBundle,
makeActionBundleFromConfig,
} from '@chugsplash/core'
import { create } from 'ipfs-http-client'
import { ContractArtifact } from '@chugsplash/plugins'
Expand All @@ -27,10 +26,15 @@ dotenv.config()
export const compileRemoteBundle = async (
hre: any,
configUri: string
): Promise<ChugSplashActionBundle> => {
): Promise<{
bundle: ChugSplashActionBundle
canonicalConfig: CanonicalChugSplashConfig
}> => {
const canonicalConfig = await fetchChugSplashConfig(configUri)
const artifacts = await getArtifactsFromCanonicalConfig(hre, canonicalConfig)
return makeActionBundleFromConfig(canonicalConfig, artifacts, {})
const bundle = await hre.run('chugsplash-bundle-remote', {
deployConfig: canonicalConfig,
})
return { bundle, canonicalConfig }
}

export const getArtifactsFromCanonicalConfig = async (
Expand Down
30 changes: 17 additions & 13 deletions packages/plugins/src/hardhat/deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,21 @@ import { writeHardhatSnapshotId } from './utils'
export const deployContracts = async (
hre: any,
verbose: boolean,
hide: boolean
hide: boolean,
local: boolean
) => {
const fileNames = fs.readdirSync(hre.config.paths.chugsplash)
for (const fileName of fileNames) {
await deployChugSplashConfig(hre, fileName, verbose, hide)
await deployChugSplashConfig(hre, fileName, verbose, hide, local)
}
}

export const deployChugSplashConfig = async (
hre: any,
fileName: string,
verbose: boolean,
hide: boolean
hide: boolean,
local: boolean
) => {
const configRelativePath = path.format({
dir: path.basename(hre.config.paths.chugsplash),
Expand Down Expand Up @@ -111,7 +113,7 @@ export const deployChugSplashConfig = async (

const { bundle } = await hre.run('chugsplash-propose', {
deployConfig: configRelativePath,
local: true,
local: false,
log: verbose,
})

Expand Down Expand Up @@ -141,15 +143,17 @@ export const deployChugSplashConfig = async (
})

// todo call chugsplash-execute if deploying locally
await hre.run('chugsplash-execute', {
chugSplashManager: ChugSplashManager,
bundleState,
bundle,
deployerAddress,
parsedConfig,
deployer,
hide,
})
if (local) {
await hre.run('chugsplash-execute', {
chugSplashManager: ChugSplashManager,
bundleState,
bundle,
deployerAddress,
parsedConfig,
deployer,
hide,
})
}
}

export const getContract = async (
Expand Down
Loading

0 comments on commit 071d867

Please sign in to comment.