Skip to content

Commit f613a38

Browse files
authored
Fireblocks support (#53)
* wip fireblocks filler logic * add fireblocks feature through an alloy provider * license
1 parent 048dcfe commit f613a38

File tree

9 files changed

+154
-52
lines changed

9 files changed

+154
-52
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
.DS_Store
22
target/
3-
.env
3+
.env
4+
5+
fireblocks*.csr
6+
fireblocks*.key

Cargo.lock

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7-
alloy = { version = "0.3.1", features = ["node-bindings", "network", "providers", "transports", "signer-local"] }
7+
alloy = { version = "0.3.1", features = ["node-bindings", "network", "providers", "transports", "signer-local", "rpc-types"] }
88
alloy-contract = "0.3.1"
99
alloy-sol-types = { version = "0.8.2", features = ["json"] }
1010
anyhow = "1.0"
@@ -27,3 +27,4 @@ serde_with = { version = "3.8", features = ["base64"] }
2727

2828
[features]
2929
prebuilt-docker = ["blobstream0-core/prebuilt-docker"]
30+
fireblocks = []

cli/src/fireblocks.rs

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2024 RISC Zero, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use alloy::{
16+
network::{Network, TransactionBuilder},
17+
primitives::Address,
18+
providers::{
19+
fillers::{FillerControlFlow, TxFiller},
20+
Provider, SendableTx,
21+
},
22+
transports::{Transport, TransportResult},
23+
};
24+
25+
#[derive(Clone, Copy, Debug, Default)]
26+
pub(crate) struct FireblocksFiller {
27+
pub sender: Address,
28+
}
29+
30+
impl<N: Network> TxFiller<N> for FireblocksFiller {
31+
type Fillable = ();
32+
33+
fn status(&self, _tx: &<N as Network>::TransactionRequest) -> FillerControlFlow {
34+
FillerControlFlow::Finished
35+
}
36+
37+
fn fill_sync(&self, tx: &mut SendableTx<N>) {
38+
if let Some(builder) = tx.as_mut_builder() {
39+
// 1 Ether max tx fee
40+
if let Some(fee) = builder.max_fee_per_gas() {
41+
builder.set_max_fee_per_gas(core::cmp::min(0x0de0b6b3a7640000, fee));
42+
}
43+
44+
builder.set_from(self.sender.clone());
45+
}
46+
}
47+
48+
async fn prepare<P, T>(
49+
&self,
50+
_provider: &P,
51+
_tx: &<N as Network>::TransactionRequest,
52+
) -> TransportResult<Self::Fillable>
53+
where
54+
P: Provider<T, N>,
55+
T: Transport + Clone,
56+
{
57+
Ok(())
58+
}
59+
60+
async fn fill(
61+
&self,
62+
_fillable: Self::Fillable,
63+
tx: SendableTx<N>,
64+
) -> TransportResult<SendableTx<N>> {
65+
Ok(tx)
66+
}
67+
}

cli/src/main.rs

+47-18
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@
1414

1515
use alloy::{
1616
hex::FromHex,
17-
network::EthereumWallet,
1817
primitives::{hex, Address, FixedBytes},
1918
providers::ProviderBuilder,
20-
signers::local::PrivateKeySigner,
2119
};
2220
use alloy_sol_types::{sol, SolCall};
2321
use blobstream0_core::prove_block_range;
@@ -30,6 +28,8 @@ use tokio::fs;
3028
use tracing_subscriber::fmt::format;
3129
use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter};
3230

31+
#[cfg(feature = "fireblocks")]
32+
pub(crate) mod fireblocks;
3333
mod service;
3434

3535
sol!(
@@ -54,6 +54,35 @@ const CONTROL_ID: [u8; 32] =
5454
const BN254_CONTROL_ID: [u8; 32] =
5555
hex!("05a022e1db38457fb510bc347b30eb8f8cf3eda95587653d0eac19e1f10d164e");
5656

57+
#[cfg(feature = "fireblocks")]
58+
macro_rules! setup_provider {
59+
($cli:ident) => {{
60+
let fireblocks_address: Address = $cli.fireblocks_address.parse()?;
61+
let provider = ProviderBuilder::new()
62+
.fetch_chain_id()
63+
.filler(crate::fireblocks::FireblocksFiller {
64+
sender: fireblocks_address,
65+
})
66+
.on_http($cli.eth_rpc.parse()?);
67+
(provider, fireblocks_address)
68+
}};
69+
}
70+
71+
#[cfg(not(feature = "fireblocks"))]
72+
macro_rules! setup_provider {
73+
($cli:ident) => {{
74+
let signer: alloy::signers::local::PrivateKeySigner = $cli.private_key_hex.parse()?;
75+
let signer_address = signer.address();
76+
let provider = ProviderBuilder::new()
77+
.with_recommended_fillers()
78+
.wallet(alloy::network::EthereumWallet::from(signer))
79+
.on_http($cli.eth_rpc.parse()?);
80+
(provider, signer_address)
81+
}};
82+
}
83+
84+
pub(crate) use setup_provider;
85+
5786
#[derive(Parser, Debug)]
5887
#[command(name = "blobstream0-cli")]
5988
#[command(bin_name = "blobstream0-cli")]
@@ -91,6 +120,12 @@ struct DeployArgs {
91120
#[clap(long, env)]
92121
eth_rpc: String,
93122

123+
#[cfg(feature = "fireblocks")]
124+
/// Fireblocks signer address.
125+
#[clap(long, env)]
126+
fireblocks_address: String,
127+
128+
#[cfg(not(feature = "fireblocks"))]
94129
/// Hex encoded private key to use for deploying.
95130
#[clap(long, env)]
96131
private_key_hex: String,
@@ -127,7 +162,13 @@ struct UpgradeArgs {
127162
#[clap(long, env)]
128163
eth_rpc: String,
129164

130-
/// Hex encoded private key to use for upgrading the contract. Must be the owner.
165+
#[cfg(feature = "fireblocks")]
166+
/// Fireblocks signer address.
167+
#[clap(long, env)]
168+
fireblocks_address: String,
169+
170+
#[cfg(not(feature = "fireblocks"))]
171+
/// Hex encoded private key to use for deploying.
131172
#[clap(long, env)]
132173
private_key_hex: String,
133174

@@ -162,19 +203,13 @@ async fn main() -> anyhow::Result<()> {
162203
fs::write(out, bincode::serialize(&receipt)?).await?;
163204
}
164205
BlobstreamCli::Deploy(deploy) => {
165-
let signer: PrivateKeySigner = deploy.private_key_hex.parse()?;
166-
206+
let (provider, signer_address) = setup_provider!(deploy);
167207
let admin_address: Address = if let Some(address) = deploy.admin_address {
168208
address.parse()?
169209
} else {
170-
signer.address()
210+
signer_address
171211
};
172212

173-
let wallet = EthereumWallet::from(signer);
174-
let provider = ProviderBuilder::new()
175-
.with_recommended_fillers()
176-
.wallet(wallet)
177-
.on_http(deploy.eth_rpc.parse()?);
178213
let verifier_address: Address = if let Some(address) = deploy.verifier_address {
179214
address.parse()?
180215
} else {
@@ -222,13 +257,7 @@ async fn main() -> anyhow::Result<()> {
222257
println!("deployed contract to address: {}", proxy.address());
223258
}
224259
BlobstreamCli::Upgrade(upgrade) => {
225-
let signer: PrivateKeySigner = upgrade.private_key_hex.parse()?;
226-
227-
let wallet = EthereumWallet::from(signer);
228-
let provider = ProviderBuilder::new()
229-
.with_recommended_fillers()
230-
.wallet(wallet)
231-
.on_http(upgrade.eth_rpc.parse()?);
260+
let (provider, _) = setup_provider!(upgrade);
232261

233262
let proxy_address: Address = upgrade.proxy_address.parse()?;
234263
println!("proxy address: {}", proxy_address);

cli/src/service/mod.rs

+13-24
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@
1313
// limitations under the License.
1414

1515
use self::blobstream::BlobstreamService;
16-
use alloy::{
17-
network::EthereumWallet, primitives::Address, providers::ProviderBuilder,
18-
signers::local::PrivateKeySigner,
19-
};
16+
use super::setup_provider;
17+
use alloy::{primitives::Address, providers::ProviderBuilder};
2018
use blobstream0_primitives::IBlobstream;
2119
use clap::Parser;
2220
use tendermint_rpc::HttpClient;
@@ -38,7 +36,13 @@ pub(crate) struct ServiceArgs {
3836
#[clap(long, env)]
3937
eth_address: Address,
4038

41-
/// Hex encoded private key to use for submitting proofs to Ethereum
39+
#[cfg(feature = "fireblocks")]
40+
/// Fireblocks signer address.
41+
#[clap(long, env)]
42+
fireblocks_address: String,
43+
44+
#[cfg(not(feature = "fireblocks"))]
45+
/// Hex encoded private key to use for deploying.
4246
#[clap(long, env)]
4347
private_key_hex: String,
4448

@@ -49,29 +53,14 @@ pub(crate) struct ServiceArgs {
4953

5054
impl ServiceArgs {
5155
pub(crate) async fn start(self) -> anyhow::Result<()> {
52-
let ServiceArgs {
53-
tendermint_rpc,
54-
eth_rpc,
55-
eth_address,
56-
private_key_hex,
57-
batch_size,
58-
} = self;
59-
60-
let tm_client = HttpClient::new(tendermint_rpc.as_str())?;
61-
62-
let signer: PrivateKeySigner = private_key_hex.parse().expect("should parse private key");
63-
let wallet = EthereumWallet::from(signer);
56+
let tm_client = HttpClient::new(self.tendermint_rpc.as_str())?;
6457

65-
// Create a provider with the wallet.
66-
let provider = ProviderBuilder::new()
67-
.with_recommended_fillers()
68-
.wallet(wallet)
69-
.on_http(eth_rpc.parse()?);
58+
let (provider, _) = setup_provider!(self);
7059

71-
let contract = IBlobstream::new(eth_address, provider);
60+
let contract = IBlobstream::new(self.eth_address, provider);
7261

7362
tracing::info!(target: "blobstream0::service", "Starting service");
74-
BlobstreamService::new(contract, tm_client, batch_size)
63+
BlobstreamService::new(contract, tm_client, self.batch_size)
7564
.spawn()
7665
.await?;
7766

0 commit comments

Comments
 (0)