Skip to content

Commit

Permalink
Merge pull request #520 from chainbound/jonas/feat/cli-errors
Browse files Browse the repository at this point in the history
feat(cli): contract error handling
  • Loading branch information
Jonas Bostoen authored Dec 6, 2024
2 parents 941dd23 + 6a49c24 commit 8d2a5f1
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 92 deletions.
2 changes: 1 addition & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ Github release assets.
These will then be automatically consumed by `boltup` consumers when selecting the
tag they want to use for their Bolt cli installation.

e.g. `boltup --tag v0.3.0-alpha` will pick one of the tarballs in the `v0.3.0-alpha` release.
e.g. `boltup --tag v0.1.0` will pick one of the tarballs in the `cli-v0.1.0` release.
2 changes: 1 addition & 1 deletion bolt-cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions bolt-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bolt"
version = "0.3.0-alpha"
version = "0.1.0"
edition = "2021"

[dependencies]
Expand All @@ -26,9 +26,9 @@ bls12_381 = "0.8.0"
ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "cf3c404" }
lighthouse_eth2_keystore = { package = "eth2_keystore", git = "https://github.com/sigp/lighthouse", rev = "a87f19d" }
alloy = { version = "0.7.0", features = [
"full",
"provider-anvil-api",
"provider-anvil-node",
"full",
"provider-anvil-api",
"provider-anvil-node",
] }

# utils
Expand Down
28 changes: 27 additions & 1 deletion bolt-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ git clone [email protected]:chainbound/bolt.git
cd bolt-cli

# build and install the binary on your machine
cargo install --path . --force
cargo install --path . --force --locked

# test the installation
bolt --version
Expand All @@ -34,6 +34,7 @@ Available commands:
- [`send`](#send) - Send a preconfirmation request to a Bolt sidecar.
- [`validators`](#validators) - Subcommand for bolt validators.
- [`operators`](#operators) - Subcommand for bolt operators.
- [`generate`](#generate) - Subcommand for generating bolt related data.

---

Expand Down Expand Up @@ -365,6 +366,31 @@ Options:

---

### `generate`

The `generate` subcommand contains functionality for generating bolt related data like BLS keypairs.

<details>
<summary>Usage</summary>

```text
❯ bolt generate --help
Useful data generation commands
Usage: bolt generate <COMMAND>
Commands:
bls Generate a BLS keypair
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
```

</details>

---

## Security

The Bolt CLI is designed to be used offline. It does not require any network connections
Expand Down
165 changes: 112 additions & 53 deletions bolt-cli/src/commands/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use alloy::{
primitives::{utils::format_ether, Bytes},
providers::{Provider, ProviderBuilder, WalletProvider},
signers::{local::PrivateKeySigner, SignerSync},
sol_types::SolInterface,
};
use eyre::Context;
use tracing::{info, warn};
Expand All @@ -12,11 +13,11 @@ use crate::{
cli::{
Chain, EigenLayerSubcommand, OperatorsCommand, OperatorsSubcommand, SymbioticSubcommand,
},
common::{bolt_manager::BoltManagerContract, request_confirmation},
common::{bolt_manager::BoltManagerContract, request_confirmation, try_parse_contract_error},
contracts::{
bolt::{
BoltEigenLayerMiddleware,
BoltSymbioticMiddleware::{self},
BoltEigenLayerMiddleware::{self, BoltEigenLayerMiddlewareErrors},
BoltSymbioticMiddleware::{self, BoltSymbioticMiddlewareErrors},
SignatureWithSaltAndExpiry,
},
deployments_for_chain,
Expand Down Expand Up @@ -149,23 +150,40 @@ impl OperatorsCommand {
Bytes::from(signer.sign_hash_sync(&signature_digest_hash)?.as_bytes());
let signature = SignatureWithSaltAndExpiry { signature, expiry, salt };

let result = bolt_eigenlayer_middleware
match bolt_eigenlayer_middleware
.registerOperator(operator_rpc.to_string(), signature)
.send()
.await?;

info!(
hash = ?result.tx_hash(),
"registerOperator transaction sent, awaiting receipt..."
);

let receipt = result.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
.await
{
Ok(pending) => {
info!(
hash = ?pending.tx_hash(),
"registerOperator transaction sent, awaiting receipt..."
);

let receipt = pending.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
}

info!("Succesfully registered EigenLayer operator");
}
Err(e) => {
match try_parse_contract_error::<BoltEigenLayerMiddlewareErrors>(e)? {
BoltEigenLayerMiddlewareErrors::AlreadyRegistered(_) => {
eyre::bail!("Operator already registered in bolt")
}
BoltEigenLayerMiddlewareErrors::NotOperator(_) => {
eyre::bail!("Operator not registered in EigenLayer")
}
other => unreachable!(
"Unexpected error with selector {:?}",
other.selector()
),
}
}
}

info!("Succesfully registered EigenLayer operator");

Ok(())
}
EigenLayerSubcommand::Deregister { rpc_url, operator_private_key } => {
Expand All @@ -192,20 +210,33 @@ impl OperatorsCommand {
let bolt_eigenlayer_middleware =
BoltEigenLayerMiddleware::new(bolt_avs_address, provider);

let result = bolt_eigenlayer_middleware.deregisterOperator().send().await?;

info!(
hash = ?result.tx_hash(),
"deregisterOperator transaction sent, awaiting receipt..."
);

let receipt = result.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
match bolt_eigenlayer_middleware.deregisterOperator().send().await {
Ok(pending) => {
info!(
hash = ?pending.tx_hash(),
"deregisterOperator transaction sent, awaiting receipt..."
);

let receipt = pending.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
}

info!("Succesfully deregistered EigenLayer operator");
}
Err(e) => {
match try_parse_contract_error::<BoltEigenLayerMiddlewareErrors>(e)? {
BoltEigenLayerMiddlewareErrors::NotRegistered(_) => {
eyre::bail!("Operator not registered in bolt")
}
other => unreachable!(
"Unexpected error with selector {:?}",
other.selector()
),
}
}
}

info!("Succesfully deregistered EigenLayer operator");

Ok(())
}
EigenLayerSubcommand::Status { rpc_url: rpc, address } => {
Expand Down Expand Up @@ -268,21 +299,36 @@ impl OperatorsCommand {
provider.clone(),
);

let pending =
middleware.registerOperator(operator_rpc.to_string()).send().await?;

info!(
hash = ?pending.tx_hash(),
"registerOperator transaction sent, awaiting receipt..."
);

let receipt = pending.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
match middleware.registerOperator(operator_rpc.to_string()).send().await {
Ok(pending) => {
info!(
hash = ?pending.tx_hash(),
"registerOperator transaction sent, awaiting receipt..."
);

let receipt = pending.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
}

info!("Succesfully registered Symbiotic operator");
}
Err(e) => {
match try_parse_contract_error::<BoltSymbioticMiddlewareErrors>(e)? {
BoltSymbioticMiddlewareErrors::AlreadyRegistered(_) => {
eyre::bail!("Operator already registered in bolt")
}
BoltSymbioticMiddlewareErrors::NotOperator(_) => {
eyre::bail!("Operator not registered in Symbiotic")
}
other => unreachable!(
"Unexpected error with selector {:?}",
other.selector()
),
}
}
}

info!("Succesfully registered Symbiotic operator");

Ok(())
}
SymbioticSubcommand::Deregister { rpc_url, operator_private_key } => {
Expand Down Expand Up @@ -310,20 +356,33 @@ impl OperatorsCommand {
provider,
);

let pending = middleware.deregisterOperator().send().await?;

info!(
hash = ?pending.tx_hash(),
"deregisterOperator transaction sent, awaiting receipt..."
);

let receipt = pending.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
match middleware.deregisterOperator().send().await {
Ok(pending) => {
info!(
hash = ?pending.tx_hash(),
"deregisterOperator transaction sent, awaiting receipt..."
);

let receipt = pending.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
}

info!("Succesfully deregistered Symbiotic operator");
}
Err(e) => {
match try_parse_contract_error::<BoltSymbioticMiddlewareErrors>(e)? {
BoltSymbioticMiddlewareErrors::NotRegistered(_) => {
eyre::bail!("Operator not registered in bolt")
}
other => unreachable!(
"Unexpected error with selector {:?}",
other.selector()
),
}
}
}

info!("Succesfully deregistered Symbiotic operator");

Ok(())
}
SymbioticSubcommand::Status { rpc_url, address } => {
Expand Down
52 changes: 39 additions & 13 deletions bolt-cli/src/commands/validators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ use alloy::{
network::EthereumWallet,
providers::{Provider, ProviderBuilder},
signers::local::PrivateKeySigner,
sol_types::SolInterface,
};
use ethereum_consensus::crypto::PublicKey as BlsPublicKey;
use eyre::Context;
use tracing::{info, warn};

use crate::{
cli::{Chain, ValidatorsCommand, ValidatorsSubcommand},
common::{hash::compress_bls_pubkey, request_confirmation},
contracts::{bolt::BoltValidators, deployments_for_chain},
common::{hash::compress_bls_pubkey, request_confirmation, try_parse_contract_error},
contracts::{
bolt::BoltValidators::{self, BoltValidatorsErrors},
deployments_for_chain,
},
};

impl ValidatorsCommand {
Expand Down Expand Up @@ -54,26 +58,48 @@ impl ValidatorsCommand {

request_confirmation();

let pending = bolt_validators
match bolt_validators
.batchRegisterValidatorsUnsafe(
pubkey_hashes,
max_committed_gas_limit,
authorized_operator,
)
.send()
.await?;
.await
{
Ok(pending) => {
info!(
hash = ?pending.tx_hash(),
"batchRegisterValidatorsUnsafe transaction sent, awaiting receipt..."
);
let receipt = pending.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
}

info!(
hash = ?pending.tx_hash(),
"batchRegisterValidatorsUnsafe transaction sent, awaiting receipt..."
);
let receipt = pending.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
info!("Successfully registered validators into bolt");
}
Err(e) => {
let decoded = try_parse_contract_error::<BoltValidatorsErrors>(e)?;

match decoded {
BoltValidatorsErrors::ValidatorAlreadyExists(b) => {
eyre::bail!(format!(
"Validator already exists (pubkeyHash: {:?})",
b.pubkeyHash
))
}
BoltValidatorsErrors::InvalidAuthorizedOperator(_) => {
eyre::bail!("Invalid authorized operator")
}
other => unreachable!(
"Unexpected error with selector {:?}",
other.selector()
),
}
}
}

info!("Successfully registered validators into bolt");

Ok(())
}
ValidatorsSubcommand::Status { rpc_url, pubkeys_path, pubkeys } => {
Expand Down
Loading

0 comments on commit 8d2a5f1

Please sign in to comment.