-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: start running with only blockfrost
- Loading branch information
1 parent
3789908
commit 3e5bc5c
Showing
6 changed files
with
189 additions
and
65 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
use anyhow::{bail, Context as _, Result}; | ||
use async_trait::async_trait; | ||
use blockfrost::{BlockFrostSettings, BlockfrostAPI, BlockfrostError, Pagination}; | ||
use blockfrost_openapi::models::BlockContent; | ||
use futures::future::try_join_all; | ||
use pallas_primitives::conway::Tx; | ||
|
||
use crate::streams::{BlockInfo, BlockReference}; | ||
|
||
use super::{ChainSyncClient, RequestNextResponse}; | ||
|
||
pub struct Blockfrost { | ||
api: BlockfrostAPI, | ||
genesis_hash: String, | ||
} | ||
|
||
impl Blockfrost { | ||
pub fn new(blockfrost_key: &str, genesis_hash: &str) -> Self { | ||
Self { | ||
api: BlockfrostAPI::new(blockfrost_key, BlockFrostSettings::new()), | ||
genesis_hash: genesis_hash.to_string(), | ||
} | ||
} | ||
|
||
pub async fn submit(&self, transaction: Tx) -> Result<String> { | ||
let transaction_data = { | ||
let mut bytes = vec![]; | ||
minicbor::encode(transaction, &mut bytes).expect("infallible"); | ||
bytes | ||
}; | ||
Ok(self.api.transactions_submit(transaction_data).await?) | ||
} | ||
|
||
pub async fn open_chainsync(&self) -> Result<BlockfrostChainSync> { | ||
BlockfrostChainSync::new(self.api.clone(), self.genesis_hash.clone()).await | ||
} | ||
} | ||
|
||
pub struct BlockfrostChainSync { | ||
api: BlockfrostAPI, | ||
tip: BlockInfo, | ||
genesis_hash: String, | ||
} | ||
|
||
impl BlockfrostChainSync { | ||
async fn new(api: BlockfrostAPI, genesis_hash: String) -> Result<Self> { | ||
let tip = api.blocks_latest().await?; | ||
let tip = parse_block(&api, tip).await?; | ||
Ok(Self { | ||
api, | ||
tip, | ||
genesis_hash, | ||
}) | ||
} | ||
|
||
async fn request_ref(&self, block_ref: &BlockReference) -> Result<Option<BlockReference>> { | ||
let (requested_slot, requested_hash) = match block_ref { | ||
BlockReference::Origin => (None, &self.genesis_hash), | ||
BlockReference::Point(number, hash) => (*number, hash), | ||
}; | ||
let block = match self.api.blocks_by_id(requested_hash).await { | ||
Err(BlockfrostError::Response { reason, .. }) if reason.status_code == 404 => { | ||
return Ok(None) | ||
} | ||
Err(error) => return Err(error.into()), | ||
Ok(block) => block, | ||
}; | ||
|
||
if requested_slot.is_some_and(|s| block.slot.is_some_and(|b| b as u64 != s)) { | ||
bail!("requested_block returned a block in the wrong slot"); | ||
} | ||
|
||
let block_ref = BlockReference::Point(block.slot.map(|s| s as u64), block.hash); | ||
Ok(Some(block_ref)) | ||
} | ||
} | ||
|
||
#[async_trait] | ||
impl ChainSyncClient for BlockfrostChainSync { | ||
async fn request_next(&mut self) -> Result<RequestNextResponse> { | ||
bail!("not done yet") | ||
} | ||
async fn find_intersect( | ||
&mut self, | ||
points: &[BlockReference], | ||
) -> Result<(Option<BlockReference>, BlockReference)> { | ||
for point in points { | ||
let Some(block) = self.request_ref(point).await? else { | ||
continue; | ||
}; | ||
return Ok((Some(block), self.tip.as_reference())); | ||
} | ||
Ok((None, self.tip.as_reference())) | ||
} | ||
async fn request_block(&mut self, block_ref: &BlockReference) -> Result<Option<BlockInfo>> { | ||
let (requested_slot, requested_hash) = match block_ref { | ||
BlockReference::Origin => (None, &self.genesis_hash), | ||
BlockReference::Point(number, hash) => (*number, hash), | ||
}; | ||
request_block(&self.api, requested_hash, requested_slot).await | ||
} | ||
} | ||
|
||
pub async fn request_block( | ||
api: &BlockfrostAPI, | ||
hash: &str, | ||
slot: Option<u64>, | ||
) -> Result<Option<BlockInfo>> { | ||
let block = match api.blocks_by_id(hash).await { | ||
Err(BlockfrostError::Response { reason, .. }) if reason.status_code == 404 => { | ||
return Ok(None) | ||
} | ||
Err(error) => return Err(error.into()), | ||
Ok(block) => block, | ||
}; | ||
|
||
if slot.is_some_and(|s| block.slot.is_some_and(|b| b as u64 != s)) { | ||
bail!("requested_block returned a block in the wrong slot"); | ||
} | ||
|
||
Ok(Some(parse_block(api, block).await?)) | ||
} | ||
|
||
async fn parse_block(api: &BlockfrostAPI, block: BlockContent) -> Result<BlockInfo> { | ||
let block_hash = block.hash; | ||
let block_height = block.height.map(|h| h as u64); | ||
let block_slot = block.slot.map(|s| s as u64); | ||
|
||
let transaction_hashes = api.blocks_txs(&block_hash, Pagination::all()).await?; | ||
|
||
let tx_fetch_jobs = transaction_hashes | ||
.iter() | ||
.map(|tx_hash| fetch_tx(api, tx_hash)); | ||
let transactions = try_join_all(tx_fetch_jobs).await?; | ||
|
||
let info = BlockInfo { | ||
block_hash, | ||
block_height, | ||
block_slot, | ||
parent_hash: block.previous_block, | ||
transaction_hashes, | ||
transactions, | ||
}; | ||
Ok(info) | ||
} | ||
|
||
async fn fetch_tx(blockfrost: &BlockfrostAPI, hash: &str) -> Result<Vec<u8>> { | ||
let tx_body = blockfrost | ||
.transactions_cbor(hash) | ||
.await | ||
.context("could not fetch tx body")?; | ||
let bytes = hex::decode(&tx_body.cbor)?; | ||
Ok(bytes) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters