Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(spammer): add blob txs #140

Merged
merged 9 commits into from
Jul 17, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
chore: rename to contract
namn-grg committed Jul 17, 2024
commit c9016aecca17cfa781910c1a01c5196c8f79352c
308 changes: 210 additions & 98 deletions bolt-spammer-helder/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,98 +1,210 @@
// use ethers::{
// abi::{Abi, Token},
// contract::{Contract, ContractError},
// providers::{Http, Provider},
// types::Address,
// };
// use eyre::{eyre, Result};
// use std::{path::PathBuf, sync::Arc};

// use beacon_api_client::ProposerDuty;

// /// Returns the next pre-confirmation slot and proposer RPC URL from the registry contract
// ///
// /// Fails if no registered validators are found in the lookahead
// pub async fn next_preconfer_from_registry(
// proposer_duties: Vec<ProposerDuty>,
// abi_path: PathBuf,
// registry_address: Address,
// eth_provider: Arc<Provider<Http>>,
// ) -> Result<(String, u64)> {
// let contract_abi: Abi = serde_json::from_str(&std::fs::read_to_string(abi_path)?)?;
// let registry_contract = Contract::new(registry_address, contract_abi, eth_provider);
// let mut next_preconfer_slot = 0;
// let mut proposer_rpc = String::new();
// for duty in proposer_duties {
// let res = registry_contract
// .method::<u64, Token>("getOperatorForValidator", duty.validator_index as u64)?
// .call()
// .await;
// match res {
// Ok(token_raw) => {
// next_preconfer_slot = duty.slot;
// proposer_rpc = try_parse_url_from_token(token_raw)?;
// tracing::info!(
// "pre-confirmation will be sent for slot {} to validator with index {} at url {}",
// duty.slot,
// duty.validator_index,
// proposer_rpc,
// );
// break;
// }
// // Such validator index is not registered, continue
// Err(ContractError::Revert(_)) => {
// tracing::warn!(
// "validator index {} not registered, skipping",
// duty.validator_index
// );
// continue;
// }
// Err(e) => {
// return Err(eyre!(
// "unexpected error while calling registry contract: {:?}",
// e
// ));
// }
// }
// }

// if next_preconfer_slot == 0 {
// return Err(eyre!("no registered validators found in the lookahead"));
// };

// Ok((proposer_rpc, next_preconfer_slot))
// }

// /// Tries to parse the registered validator's sidecars URL from the token returned
// /// by the view call to the registry smart contract
// ///
// /// Reference: https://github.com/chainbound/bolt/blob/e71c61aa97dcd7b08fad23067caf18bc90a582cd/bolt-contracts/src/interfaces/IBoltRegistry.sol#L6-L16
// pub fn try_parse_url_from_token(token: Token) -> Result<String> {
// let Token::Tuple(registrant_struct_fields) = token else {
// return Err(eyre!("register call result is not a struct"));
// };

// let Some(metadata_token) = registrant_struct_fields.last() else {
// return Err(eyre!("register call result is a struct with no fields"));
// };

// let Token::Tuple(metadata_fields) = metadata_token else {
// return Err(eyre!(
// "register call result is a struct without the `metadata` field"
// ));
// };

// let Some(rpc_token) = metadata_fields.first() else {
// return Err(eyre!(
// "register call result has a `metadata` field, but the struct is empty"
// ));
// };

// let Token::String(rpc) = rpc_token else {
// return Err(eyre!(
// "register call result has a `metadata` field, but its `rpc` property is not a string"
// ));
// };

// Ok(rpc.clone())
// }
use std::str::FromStr;

use alloy::{
contract::{Error as ContractError, Result as ContractResult},
primitives::{Address, Bytes},
providers::{ProviderBuilder, RootProvider},
sol,
sol_types::{Error as SolError, SolInterface},
transports::{http::Http, TransportError},
};
use reqwest::Client;
use url::Url;

use beacon_api_client::ProposerDuty;
use BoltRegistryContract::{BoltRegistryContractErrors, BoltRegistryContractInstance, Registrant};

#[derive(Debug, Clone)]
pub struct BoltRegistry(BoltRegistryContractInstance<Http<Client>, RootProvider<Http<Client>>>);

impl BoltRegistry {
pub fn new<U: Into<Url>>(execution_client_url: U, registry_address: Address) -> Self {
let provider = ProviderBuilder::new().on_http(execution_client_url.into());
let registry = BoltRegistryContract::new(registry_address, provider);

Self(registry)
}

/// Gets the sidecar RPC URL for a given validator index.
///
/// Returns Ok(None) if the operator is not found in the registry.
pub async fn get_sidecar_rpc_url_for_validator(
&self,
validator_index: u64,
) -> ContractResult<Option<String>> {
let registrant = self.get_registrant_for_validator(validator_index).await?;
Ok(registrant.map(|r| r.metadata.rpc))
}

/// Gets the operator for a given validator index.
///
/// Returns Ok(None) if the operator is not found in the registry.
pub async fn get_registrant_for_validator(
&self,
validator_index: u64,
) -> ContractResult<Option<Registrant>> {
let returndata = self.0.getOperatorForValidator(validator_index).call().await;

// TODO: clean this after https://github.com/alloy-rs/alloy/issues/787 is merged
let error = match returndata.map(|data| data._0) {
Ok(registrant) => return Ok(Some(registrant)),
Err(error) => match error {
ContractError::TransportError(TransportError::ErrorResp(err)) => {
let data = err.data.unwrap_or_default();
let data = data.get().trim_matches('"');
let data = Bytes::from_str(data).unwrap_or_default();

BoltRegistryContractErrors::abi_decode(&data, true)?
}
e => return Err(e),
},
};

if matches!(error, BoltRegistryContractErrors::NotFound(_)) {
Ok(None)
} else {
Err(SolError::custom(format!(
"unexpected Solidity error selector: {:?}",
error.selector()
))
.into())
}
}
merklefruit marked this conversation as resolved.
Show resolved Hide resolved

/// Gets the next pre-confirmation slot and proposer RPC URL from the registry contract
///
/// Returns Ok(None) if no registered validators are found in the lookahead
pub async fn next_preconfer_from_registry(
&self,
proposer_duties: Vec<ProposerDuty>,
) -> ContractResult<Option<(String, u64)>> {
let mut next_preconfer_slot = 0;
let mut proposer_rpc = String::new();

for duty in proposer_duties {
let res = self.get_registrant_for_validator(duty.validator_index as u64).await;
match res {
Ok(Some(token_raw)) => {
next_preconfer_slot = duty.slot;
proposer_rpc = token_raw.metadata.rpc;
tracing::info!(
"pre-confirmation will be sent for slot {} to validator with index {} at url {}",
duty.slot,
duty.validator_index,
proposer_rpc,
);
break;
}
Ok(None) => {
// Handle the case where the result is Ok but contains None.
// You might want to continue to the next iteration, log something, or handle it
// in another way.
tracing::info!(
"No registrant found for validator index {}",
duty.validator_index
);
continue;
}
Err(e) => {
return Err(e);
}
}
}

if next_preconfer_slot == 0 {
return Ok(None);
};

Ok(Some((proposer_rpc, next_preconfer_slot)))
}
}

sol! {
#[sol(rpc)]
interface BoltRegistryContract {
#[derive(Debug, Default)]
struct Registrant {
address operator;
uint64[] validatorIndexes;
uint256 enteredAt;
uint256 exitInitiatedAt;
uint256 balance;
Status status;
MetaData metadata;
}

#[derive(Debug, Default)]
enum Status {
#[default]
INACTIVE,
ACTIVE,
FROZEN,
EXITING
}

#[derive(Debug, Default)]
struct MetaData {
string rpc;
bytes extra;
}

function getOperatorForValidator(uint64 _validatorIndex) external view returns (Registrant memory);

error NotFound();
error Unauthorized();
}
}

#[cfg(test)]
mod tests {
use std::str::FromStr;

use alloy::{primitives::U256, sol_types::SolCall};
use BoltRegistryContract::{MetaData, Status};

use super::*;

#[test]
fn test_abigen() {
assert_eq!(BoltRegistryContract::getOperatorForValidatorCall::SELECTOR, [238, 124, 139, 77])
}

#[tokio::test]
async fn test_get_operators_helder() -> eyre::Result<()> {
let registry = BoltRegistry::new(
Url::parse("http://remotebeast:4485")?,
Address::from_str("0xdF11D829eeC4C192774F3Ec171D822f6Cb4C14d9")?,
namn-grg marked this conversation as resolved.
Show resolved Hide resolved
);

let registrant = registry.get_registrant_for_validator(0).await;
assert!(matches!(registrant, Ok(None)));

let registrant = match registry.get_registrant_for_validator(2150).await {
Ok(Some(registrant)) => registrant,
e => panic!("unexpected error reading from registry: {:?}", e),
};

let expected = Registrant {
operator: Address::from_str("0xad3cd1b81c80f4a495d6552ae6423508492a27f8")?,
validatorIndexes: (2145..2245).collect(),
enteredAt: U256::from(1720183620),
exitInitiatedAt: U256::from(0),
balance: U256::from(10000000000000000000u128),
status: Status::ACTIVE,
metadata: MetaData {
rpc: "http://135.181.191.125:8000".to_string(),
extra: Bytes::from_str("0x")?,
},
};

assert_eq!(registrant.metadata.rpc, expected.metadata.rpc);
assert_eq!(registrant.metadata.extra, expected.metadata.extra);
assert_eq!(registrant.operator, expected.operator);
assert_eq!(registrant.validatorIndexes, expected.validatorIndexes);
assert_eq!(registrant.enteredAt, expected.enteredAt);
assert_eq!(registrant.exitInitiatedAt, expected.exitInitiatedAt);
assert_eq!(registrant.balance, expected.balance);

Ok(())
}
}
4 changes: 2 additions & 2 deletions bolt-spammer-helder/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub mod constants;
/// The on-chain registry view for querying active Bolt sidecars
pub mod onchain_registry;
use onchain_registry::BoltRegistry;
pub mod contract;
use contract::BoltRegistry;

pub mod utils;
4 changes: 2 additions & 2 deletions bolt-spammer-helder/src/main.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ use alloy::{
use beacon_api_client::mainnet::Client as BeaconApiClient;
use bolt_spammer_helder::{
constants::SLOTS_PER_EPOCH,
onchain_registry::BoltRegistry,
contract::BoltRegistry,
utils::{
current_slot, generate_random_blob_tx, generate_random_tx, prepare_rpc_request,
sign_transaction,
@@ -98,7 +98,7 @@ async fn main() -> Result<()> {
B256::from(keccak256(data))
merklefruit marked this conversation as resolved.
Show resolved Hide resolved
};

let signature = wallet.sign_message(&message_digest).unwrap();
let signature = wallet.sign_message(&message_digest).await?;

let request = prepare_rpc_request(
"bolt_inclusionPreconfirmation",
210 changes: 0 additions & 210 deletions bolt-spammer-helder/src/onchain_registry.rs

This file was deleted.