Skip to content

Commit

Permalink
add multisig wallet and prover mode to deployment steps in the deploy…
Browse files Browse the repository at this point in the history
…er utils (#2028)

* set the owner of the fee contract to the multisig_address when it is deployed using the deployer util

* Make multisig addr optional in deploy script

* Set multisig ownership for light client contract

* Set permissioned prover address when deploying light client contract

* Make multisig optional for dev node

---------

Co-authored-by: Jeb Bearer <jeb@espressosys.com>
  • Loading branch information
alysiahuggins and jbearer authored Sep 20, 2024
1 parent 95b3e0b commit 52ad3fa
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 12 deletions.
12 changes: 10 additions & 2 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ ESPRESSO_BUILDER_ETH_ACCOUNT_INDEX=8
ESPRESSO_DEPLOYER_ACCOUNT_INDEX=9

# Contracts
ESPRESSO_SEQUENCER_HOTSHOT_ADDRESS=0xb19b36b1456e65e3a6d514d3f715f204bd59f431
ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS=0x0c8e79f3534b00d9a3d4a856b665bf4ebc22f2ba
ESPRESSO_SEQUENCER_HOTSHOT_ADDRESS=0x8ce361602b935680e8dec218b820ff5056beb7af
ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS=0xed1db453c3156ff3155a97ad217b3087d5dc5f6e
ESPRESSO_SEQUENCER_LIGHTCLIENT_ADDRESS=$ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS
ESPRESSO_SEQUENCER_PERMISSIONED_PROVER=0x14dc79964da2c08b23698b3d3cc7ca32193d9955

# Example sequencer demo private keys
ESPRESSO_DEMO_SEQUENCER_STAKING_PRIVATE_KEY_0=BLS_SIGNING_KEY~lNDh4Pn-pTAyzyprOAFdXHwhrKhEwqwtMtkD3CZF4x3o
Expand Down Expand Up @@ -132,3 +133,10 @@ ESPRESSO_SEQUENCER_FETCH_RATE_LIMIT=25

# Query service stress test
ESPRESSO_NASTY_CLIENT_PORT=24011

# Test setting upgradable contract address after deployment.
#
# Setting this address to a random address effectively relinuquishes ownership of the contracts,
# which ensures that all services work correctly in the demo without admin access. In a real
# deployment, we would set this to the address of a multisig wallet.
ESPRESSO_SEQUENCER_ETH_MULTISIG_ADDRESS=8626f6940e2eb28930efb4cef49b2d1f2c9c1199
2 changes: 2 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ services:
- ESPRESSO_SEQUENCER_L1_PROVIDER
- ESPRESSO_SEQUENCER_URL
- ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY
- ESPRESSO_SEQUENCER_PERMISSIONED_PROVER
- ESPRESSO_DEPLOYER_ACCOUNT_INDEX
- ESPRESSO_SEQUENCER_ETH_MULTISIG_ADDRESS
- RUST_LOG
- RUST_LOG_FORMAT
- ASYNC_STD_THREAD_COUNT
Expand Down
26 changes: 26 additions & 0 deletions sequencer/src/bin/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{fs::File, io::stdout, path::PathBuf};

use clap::Parser;
use ethers::types::Address;
use futures::FutureExt;
use hotshot_stake_table::config::STAKE_TABLE_CAPACITY;
use hotshot_state_prover::service::light_client_genesis;
Expand Down Expand Up @@ -57,6 +58,19 @@ struct Options {
default_value = "test test test test test test test test test test test junk"
)]
mnemonic: String,

/// Address for the multisig wallet that will be the admin
///
/// If provided, this the multisig wallet that will be able to upgrade contracts and execute
/// admin only functions on contracts. If not provided, admin power for all contracts will be
/// held by the account used to deploy the contracts (determined from MNEMONIC, ACCOUNT_INDEX).
#[clap(
long,
name = "MULTISIG_ADDRESS",
env = "ESPRESSO_SEQUENCER_ETH_MULTISIG_ADDRESS"
)]
multisig_address: Option<Address>,

/// Account index in the L1 wallet generated by MNEMONIC to use when deploying the contracts.
#[clap(
long,
Expand Down Expand Up @@ -87,6 +101,16 @@ struct Options {
#[clap(short, long, env = "ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY", default_value_t = STAKE_TABLE_CAPACITY)]
pub stake_table_capacity: usize,

/// Permissioned prover address for light client contract.
///
/// If the light client contract is being deployed and this is set, the prover will be
/// permissioned so that only this address can update the light client state. Otherwise, proving
/// will be permissionless.
///
/// If the light client contract is not being deployed, this option is ignored.
#[clap(long, env = "ESPRESSO_SEQUENCER_PERMISSIONED_PROVER")]
permissioned_prover: Option<Address>,

#[clap(flatten)]
logging: logging::Config,
}
Expand All @@ -106,9 +130,11 @@ async fn main() -> anyhow::Result<()> {
opt.rpc_url,
opt.mnemonic,
opt.account_index,
opt.multisig_address,
opt.use_mock_contract,
opt.only,
genesis,
opt.permissioned_prover,
contracts,
)
.await?;
Expand Down
34 changes: 31 additions & 3 deletions sequencer/src/bin/espresso-dev-node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use ethers::{
middleware::{MiddlewareBuilder, SignerMiddleware},
providers::{Http, Middleware, Provider},
signers::{coins_bip39::English, MnemonicBuilder, Signer},
types::{Address, U256},
types::{Address, H160, U256},
};
use futures::{future::BoxFuture, stream::FuturesUnordered, FutureExt, StreamExt};
use hotshot_state_prover::service::{
Expand Down Expand Up @@ -62,6 +62,16 @@ struct Args {
)]
account_index: u32,

/// Address for the multisig wallet that will be the admin
///
/// This the multisig wallet that will be upgrade contracts and execute admin only functions on contracts
#[clap(
long,
name = "MULTISIG_ADDRESS",
env = "ESPRESSO_SEQUENCER_ETH_MULTISIG_ADDRESS"
)]
multisig_address: Option<H160>,

/// The frequency of updating the light client state, expressed in update interval
#[clap( long, value_parser = parse_duration, default_value = "20s", env = "ESPRESSO_STATE_PROVER_UPDATE_INTERVAL")]
update_interval: Duration,
Expand All @@ -87,6 +97,11 @@ struct Args {
#[clap(long, env = "ESPRESSO_SEQUENCER_DEPLOYER_ALT_INDICES")]
alt_account_indices: Vec<u32>,

/// Optional list of multisig addresses for the alternate chains.
/// If there are fewer multisig addresses provided than chains, the base MULTISIG_ADDRESS will be used.
#[arg(long, env = "ESPRESSO_DEPLOYER_ALT_MULTISIG_ADDRESSES", num_args = 1.., value_delimiter = ',')]
alt_multisig_addresses: Vec<H160>,

/// The frequency of updating the light client state for alt chains.
/// If there are fewer intervals provided than chains, the base update interval will be used.
#[clap(long, value_parser = parse_duration, env = "ESPRESSO_STATE_PROVER_ALT_UPDATE_INTERVALS", num_args = 1.., value_delimiter = ',')]
Expand Down Expand Up @@ -134,9 +149,11 @@ async fn main() -> anyhow::Result<()> {
rpc_url,
mnemonic,
account_index,
multisig_address,
alt_chain_providers,
alt_mnemonics,
alt_account_indices,
alt_multisig_addresses,
sequencer_api_port,
sequencer_api_max_connections,
builder_port,
Expand Down Expand Up @@ -204,10 +221,11 @@ async fn main() -> anyhow::Result<()> {
let mut mock_contracts = BTreeMap::new();
let mut handles = FuturesUnordered::new();
// deploy contract for L1 and each alt chain
for (url, mnemonic, account_index, update_interval, retry_interval) in once((
for (url, mnemonic, account_index, multisig_address, update_interval, retry_interval) in once((
l1_url.clone(),
mnemonic.clone(),
account_index,
multisig_address,
update_interval,
retry_interval,
))
Expand All @@ -220,6 +238,12 @@ async fn main() -> anyhow::Result<()> {
.into_iter()
.chain(std::iter::repeat(account_index)),
)
.zip(
alt_multisig_addresses
.into_iter()
.map(Some)
.chain(std::iter::repeat(multisig_address)),
)
.zip(
alt_prover_update_intervals
.into_iter()
Expand All @@ -230,17 +254,21 @@ async fn main() -> anyhow::Result<()> {
.into_iter()
.chain(std::iter::repeat(retry_interval)),
)
.map(|((((url, mnc), idx), update), retry)| (url.clone(), mnc, idx, update, retry)),
.map(|(((((url, mnc), idx), mlts), update), retry)| {
(url.clone(), mnc, idx, mlts, update, retry)
}),
) {
tracing::info!("deploying the contract for provider: {url:?}");

let contracts = deploy(
url.clone(),
mnemonic.clone(),
account_index,
multisig_address,
true,
None,
async { Ok(lc_genesis.clone()) }.boxed(),
None,
contracts.clone(),
)
.await?;
Expand Down
48 changes: 41 additions & 7 deletions utils/src/deployer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,11 @@ pub async fn deploy(
l1url: Url,
mnemonic: String,
account_index: u32,
multisig_address: Option<H160>,
use_mock_contract: bool,
only: Option<Vec<ContractGroup>>,
genesis: BoxFuture<'_, anyhow::Result<(ParsedLightClientState, ParsedStakeTableState)>>,
permissioned_prover: Option<Address>,
mut contracts: Contracts,
) -> anyhow::Result<Contracts> {
let provider = Provider::<Http>::try_from(l1url.to_string())?;
Expand All @@ -323,17 +325,17 @@ pub async fn deploy(
.index(account_index)?
.build()?
.with_chain_id(chain_id);
let owner = wallet.address();
let deployer = wallet.address();
let l1 = Arc::new(SignerMiddleware::new(provider.clone(), wallet));

// As a sanity check, check that the deployer address has some balance of ETH it can use to pay
// gas.
let balance = l1.get_balance(owner, None).await?;
let balance = l1.get_balance(deployer, None).await?;
ensure!(
balance > 0.into(),
"deployer account {owner:#x} is not funded!"
"deployer account {deployer:#x} is not funded!"
);
tracing::info!(%balance, "deploying from address {owner:#x}");
tracing::info!(%balance, "deploying from address {deployer:#x}");

// `HotShot.sol`
if should_deploy(ContractGroup::HotShot, &only) {
Expand Down Expand Up @@ -363,7 +365,7 @@ pub async fn deploy(
let (genesis_lc, genesis_stake) = genesis.await?.clone();

let data = light_client
.initialize(genesis_lc.into(), genesis_stake.into(), 864000, owner)
.initialize(genesis_lc.into(), genesis_stake.into(), 864000, deployer)
.calldata()
.context("calldata for initialize transaction not available")?;
let light_client_proxy_address = contracts
Expand All @@ -380,6 +382,25 @@ pub async fn deploy(
{
panic!("Light Client contract's address is not a proxy");
}

// Instantiate a wrapper with the proxy address and light client ABI.
let proxy = LightClient::new(light_client_proxy_address, l1.clone());

// Perission the light client prover.
if let Some(prover) = permissioned_prover {
tracing::info!(%light_client_proxy_address, %prover, "setting permissioned prover");
proxy.set_permissioned_prover(prover).send().await?.await?;
}

// Transfer ownership to the multisig wallet if provided.
if let Some(owner) = multisig_address {
tracing::info!(
%light_client_proxy_address,
%owner,
"transferring light client proxy ownership to multisig",
);
proxy.transfer_ownership(owner).send().await?.await?;
}
}

// `FeeContract.sol`
Expand All @@ -389,7 +410,7 @@ pub async fn deploy(
.await?;
let fee_contract = FeeContract::new(fee_contract_address, l1.clone());
let data = fee_contract
.initialize(owner)
.initialize(deployer)
.calldata()
.context("calldata for initialize transaction not available")?;
let fee_contract_proxy_address = contracts
Expand All @@ -406,6 +427,19 @@ pub async fn deploy(
{
panic!("Fee contract's address is not a proxy");
}

// Instantiate a wrapper with the proxy address and fee contract ABI.
let proxy = FeeContract::new(fee_contract_proxy_address, l1.clone());

// Transfer ownership to the multisig wallet if provided.
if let Some(owner) = multisig_address {
tracing::info!(
%fee_contract_proxy_address,
%owner,
"transferring fee contract proxy ownership to multisig",
);
proxy.transfer_ownership(owner).send().await?.await?;
}
}

Ok(contracts)
Expand Down Expand Up @@ -440,7 +474,7 @@ pub async fn is_proxy_contract(
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub enum ContractGroup {
#[clap(name = "hotshot")]
HotShot,
HotShot, // TODO: confirm whether we keep HotShot in the contract group
FeeContract,
LightClient,
}

0 comments on commit 52ad3fa

Please sign in to comment.