Skip to content

Commit

Permalink
refactor: move relay logic to bitcoin crate so it can be shared with
Browse files Browse the repository at this point in the history
parachain
  • Loading branch information
sander2 committed Sep 7, 2023
1 parent 79e1235 commit 2f8630e
Show file tree
Hide file tree
Showing 15 changed files with 325 additions and 286 deletions.
2 changes: 2 additions & 0 deletions bitcoin/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ pub enum Error {
AddressError(#[from] AddressError),
#[error("Failed to fetch coinbase tx")]
CoinbaseFetchingFailure,
#[error("Expected a value in a rpc result that is missing")]
MissingValue,
}

impl Error {
Expand Down
6 changes: 2 additions & 4 deletions bitcoin/src/iter.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use crate::{BitcoinCoreApi, BitcoinRpcError, Error};
use crate::{BitcoinRpcError, DynBitcoinCoreApi, Error};
use bitcoincore_rpc::{
bitcoin::{Block, BlockHash, Transaction},
jsonrpc::Error as JsonRpcError,
Error as BitcoinError,
};
use futures::{prelude::*, stream::StreamExt};
use log::trace;
use std::{iter, sync::Arc};

type DynBitcoinCoreApi = Arc<dyn BitcoinCoreApi + Send + Sync>;
use std::iter;

/// Stream over transactions, starting with this in the mempool and continuing with
/// transactions from previous in-chain block. The stream ends after the block at
Expand Down
4 changes: 4 additions & 0 deletions bitcoin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod addr;
mod electrs;
mod error;
mod iter;
pub mod relay;

use async_trait::async_trait;
use backoff::{backoff::Backoff, future::retry, ExponentialBackoff};
Expand Down Expand Up @@ -48,6 +49,7 @@ pub use electrs::{ElectrsClient, Error as ElectrsError};
pub use error::{BitcoinRpcError, ConversionError, Error};
pub use iter::{reverse_stream_transactions, stream_blocks, stream_in_chain_transactions};
use log::{info, trace, warn};
pub use relay::*;
use serde_json::error::Category as SerdeJsonCategory;
pub use sp_core::H256;
use std::{
Expand Down Expand Up @@ -101,6 +103,8 @@ const RANDOMIZATION_FACTOR: f64 = 0.25;
const DERIVATION_KEY_LABEL: &str = "derivation-key";
const DEPOSIT_LABEL: &str = "deposit";

pub type DynBitcoinCoreApi = Arc<dyn BitcoinCoreApi + Send + Sync>;

fn get_exponential_backoff() -> ExponentialBackoff {
ExponentialBackoff {
current_interval: INITIAL_INTERVAL,
Expand Down
18 changes: 8 additions & 10 deletions vault/src/relay/backing.rs → bitcoin/src/relay/backing.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,46 @@
use super::Error;
use crate::service::DynBitcoinCoreApi;
use crate::{serialize, BitcoinCoreApi, DynBitcoinCoreApi, Error as BitcoinError};
use async_trait::async_trait;
use bitcoin::{serialize, BitcoinCoreApi, Error as BitcoinError};

#[async_trait]
pub trait Backing {
/// Returns the height of the longest chain
async fn get_block_count(&self) -> Result<u32, Error>;
async fn get_block_count(&self) -> Result<u32, BitcoinError>;

/// Returns the raw header of a block in storage
///
/// # Arguments
///
/// * `height` - The height of the block to fetch
async fn get_block_header(&self, height: u32) -> Result<Option<Vec<u8>>, Error>;
async fn get_block_header(&self, height: u32) -> Result<Option<Vec<u8>>, BitcoinError>;

/// Returns the (little endian) hash of a block
///
/// # Arguments
///
/// * `height` - The height of the block to fetch
async fn get_block_hash(&self, height: u32) -> Result<Vec<u8>, Error>;
async fn get_block_hash(&self, height: u32) -> Result<Vec<u8>, BitcoinError>;
}

#[async_trait]
impl Backing for DynBitcoinCoreApi {
async fn get_block_count(&self) -> Result<u32, Error> {
async fn get_block_count(&self) -> Result<u32, BitcoinError> {
let count = BitcoinCoreApi::get_block_count(&**self).await?;
return Ok(count as u32);
}

async fn get_block_header(&self, height: u32) -> Result<Option<Vec<u8>>, Error> {
async fn get_block_header(&self, height: u32) -> Result<Option<Vec<u8>>, BitcoinError> {
let block_hash = match BitcoinCoreApi::get_block_hash(&**self, height).await {
Ok(h) => h,
Err(BitcoinError::InvalidBitcoinHeight) => {
return Ok(None);
}
Err(err) => return Err(err.into()),
Err(err) => return Err(err),
};
let block_header = BitcoinCoreApi::get_block_header(&**self, &block_hash).await?;
Ok(Some(serialize(&block_header)))
}

async fn get_block_hash(&self, height: u32) -> Result<Vec<u8>, Error> {
async fn get_block_hash(&self, height: u32) -> Result<Vec<u8>, BitcoinError> {
let block_hash = BitcoinCoreApi::get_block_hash(&**self, height)
.await
.map(|hash| serialize(&hash))?;
Expand Down
15 changes: 4 additions & 11 deletions vault/src/relay/error.rs → bitcoin/src/relay/error.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#![allow(clippy::enum_variant_names)]

use bitcoin::Error as BitcoinError;
use runtime::Error as RuntimeError;
use crate::Error as BitcoinError;
use thiserror::Error;

#[cfg(test)]
use std::mem::discriminant;

#[derive(Error, Debug)]
pub enum Error {
pub enum Error<RuntimeError> {
#[error("Client already initialized")]
AlreadyInitialized,
#[error("Client has not been initialized")]
Expand All @@ -28,13 +27,7 @@ pub enum Error {

#[error("BitcoinError: {0}")]
BitcoinError(#[from] BitcoinError),
// note: we can't have two #[from]s when one is generic. We'll use map_err for the runtime error
#[error("RuntimeError: {0}")]
RuntimeError(#[from] RuntimeError),
}

#[cfg(test)]
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
discriminant(self) == discriminant(other)
}
RuntimeError(RuntimeError),
}
61 changes: 61 additions & 0 deletions bitcoin/src/relay/issuing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use async_trait::async_trait;
use std::{fmt, sync::Arc};

#[async_trait]
pub trait RandomDelay: fmt::Debug {
type Error;
async fn delay(&self, seed_data: &[u8; 32]) -> Result<(), Self::Error>;
}

#[async_trait]
pub trait Issuing {
type Error;

/// Returns true if the light client is initialized
async fn is_initialized(&self) -> Result<bool, Self::Error>;

/// Initialize the light client
///
/// # Arguments
///
/// * `header` - Raw block header
/// * `height` - Starting height
async fn initialize(&self, header: Vec<u8>, height: u32) -> Result<(), Self::Error>;

/// Submit a block header and wait for inclusion
///
/// # Arguments
///
/// * `header` - Raw block header
async fn submit_block_header(
&self,
header: Vec<u8>,
random_delay: Arc<Box<dyn RandomDelay<Error = Self::Error> + Send + Sync>>,
) -> Result<(), Self::Error>;

/// Submit a batch of block headers and wait for inclusion
///
/// # Arguments
///
/// * `headers` - Raw block headers (multiple of 80 bytes)
async fn submit_block_header_batch(&self, headers: Vec<Vec<u8>>) -> Result<(), Self::Error>;

/// Returns the light client's chain tip
async fn get_best_height(&self) -> Result<u32, Self::Error>;

/// Returns the block hash stored at a given height,
/// this is assumed to be in little-endian format
///
/// # Arguments
///
/// * `height` - Height of the block to fetch
async fn get_block_hash(&self, height: u32) -> Result<Vec<u8>, Self::Error>;

/// Returns true if the block described by the hash
/// has been stored in the light client
///
/// # Arguments
///
/// * `hash_le` - Hash (little-endian) of the block
async fn is_block_stored(&self, hash_le: Vec<u8>) -> Result<bool, Self::Error>;
}
Loading

0 comments on commit 2f8630e

Please sign in to comment.