Skip to content

getblockmeta: allow request by height #73

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

Merged
merged 2 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 3 additions & 3 deletions client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,21 @@ pub struct Client {

/// A block structure containing validated transaction metadata
/// relevant to the Spaces protocol
#[derive(Clone, Serialize, Deserialize, Encode, Decode)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
pub struct BlockMeta {
pub height: u32,
pub tx_meta: Vec<TxEntry>,
}

#[derive(Clone, Serialize, Deserialize, Encode, Decode)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
pub struct TxEntry {
#[serde(flatten)]
pub changeset: TxChangeSet,
#[serde(skip_serializing_if = "Option::is_none", flatten)]
pub tx: Option<TxData>,
}

#[derive(Clone, Serialize, Deserialize, Encode, Decode)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
pub struct TxData {
pub position: u32,
pub raw: Bytes,
Expand Down
126 changes: 81 additions & 45 deletions client/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ pub struct RootAnchor {
pub block: ChainAnchor,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum HeightOrHash {
Hash(BlockHash),
Height(u32),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockMetaWithHash {
pub hash: BlockHash,
#[serde(flatten)]
pub block_meta: BlockMeta,
}

pub enum ChainStateCommand {
CheckPackage {
txs: Vec<String>,
Expand All @@ -105,8 +119,8 @@ pub enum ChainStateCommand {
resp: Responder<anyhow::Result<Option<TxEntry>>>,
},
GetBlockMeta {
block_hash: BlockHash,
resp: Responder<anyhow::Result<Option<BlockMeta>>>,
height_or_hash: HeightOrHash,
resp: Responder<anyhow::Result<BlockMetaWithHash>>,
},
EstimateBid {
target: usize,
Expand Down Expand Up @@ -179,8 +193,8 @@ pub trait Rpc {
#[method(name = "getblockmeta")]
async fn get_block_meta(
&self,
block_hash: BlockHash,
) -> Result<Option<BlockMeta>, ErrorObjectOwned>;
height_or_hash: HeightOrHash,
) -> Result<BlockMetaWithHash, ErrorObjectOwned>;

#[method(name = "gettxmeta")]
async fn get_tx_meta(&self, txid: Txid) -> Result<Option<TxEntry>, ErrorObjectOwned>;
Expand Down Expand Up @@ -599,19 +613,6 @@ impl WalletManager {
Ok(())
}

pub async fn get_block_hash(
&self,
client: &reqwest::Client,
height: u32,
) -> anyhow::Result<BlockId> {
let hash = self
.rpc
.send_json(&client, &self.rpc.get_block_hash(height))
.await?;

Ok(BlockId { height, hash })
}

async fn get_wallet_start_block(&self, client: &reqwest::Client) -> anyhow::Result<BlockId> {
let count: i32 = self
.rpc
Expand Down Expand Up @@ -797,11 +798,11 @@ impl RpcServer for RpcServerImpl {

async fn get_block_meta(
&self,
block_hash: BlockHash,
) -> Result<Option<BlockMeta>, ErrorObjectOwned> {
height_or_hash: HeightOrHash,
) -> Result<BlockMetaWithHash, ErrorObjectOwned> {
let data = self
.store
.get_block_meta(block_hash)
.get_block_meta(height_or_hash)
.await
.map_err(|error| ErrorObjectOwned::owned(-1, error.to_string(), None::<String>))?;

Expand Down Expand Up @@ -1072,55 +1073,76 @@ impl AsyncChainState {
BlockHash::from_str(info.get("blockhash").and_then(|t| t.as_str()).ok_or_else(
|| anyhow!("Could not retrieve block hash for tx (is it in the mempool?)"),
)?)?;
let block = Self::get_indexed_block(index, &block_hash, client, rpc, chain_state).await?;
let block = Self::get_indexed_block(
index,
HeightOrHash::Hash(block_hash),
client,
rpc,
chain_state,
)
.await?;

if let Some(block) = block {
return Ok(block
.tx_meta
.into_iter()
.find(|tx| &tx.changeset.txid == txid));
}
Ok(None)
Ok(block
.block_meta
.tx_meta
.into_iter()
.find(|tx| &tx.changeset.txid == txid))
}

async fn get_indexed_block(
index: &mut Option<LiveSnapshot>,
block_hash: &BlockHash,
height_or_hash: HeightOrHash,
client: &reqwest::Client,
rpc: &BitcoinRpc,
chain_state: &mut LiveSnapshot,
) -> Result<Option<BlockMeta>, anyhow::Error> {
) -> Result<BlockMetaWithHash, anyhow::Error> {
let index = index
.as_mut()
.ok_or_else(|| anyhow!("block index must be enabled"))?;
let hash = BaseHash::from_slice(block_hash.as_ref());
let block: Option<BlockMeta> = index
.get(hash)
.context("Could not fetch block from index")?;
let hash = match height_or_hash {
HeightOrHash::Hash(hash) => hash,
HeightOrHash::Height(height) => rpc
.send_json(client, &rpc.get_block_hash(height))
.await
.map_err(|e| anyhow!("Could not retrieve block hash ({})", e))?,
};

if let Some(block_set) = block {
return Ok(Some(block_set));
if let Some(block_meta) = index
.get(BaseHash::from_slice(hash.as_ref()))
.context("Could not fetch block from index")?
{
return Ok(BlockMetaWithHash {
hash,
block_meta,
});
}

let info: serde_json::Value = rpc
.send_json(client, &rpc.get_block_header(block_hash))
.send_json(client, &rpc.get_block_header(&hash))
.await
.map_err(|e| anyhow!("Could not retrieve block ({})", e))?;

let height = info
.get("height")
.and_then(|t| t.as_u64())
.and_then(|h| u32::try_from(h).ok())
.ok_or_else(|| anyhow!("Could not retrieve block height"))?;

let tip = chain_state.tip.read().expect("read meta").clone();
if height > tip.height as u64 {
if height > tip.height {
return Err(anyhow!(
"Spaces is syncing at height {}, requested block height {}",
tip.height,
height
));
}
Ok(None)
Ok(BlockMetaWithHash {
hash,
block_meta: BlockMeta {
height,
tx_meta: Vec::new(),
},
})
}

pub async fn handle_command(
Expand Down Expand Up @@ -1168,10 +1190,18 @@ impl AsyncChainState {
.context("could not fetch spaceout");
let _ = resp.send(result);
}
ChainStateCommand::GetBlockMeta { block_hash, resp } => {
let res =
Self::get_indexed_block(block_index, &block_hash, client, rpc, chain_state)
.await;
ChainStateCommand::GetBlockMeta {
height_or_hash,
resp,
} => {
let res = Self::get_indexed_block(
block_index,
height_or_hash,
client,
rpc,
chain_state,
)
.await;
let _ = resp.send(res);
}
ChainStateCommand::GetTxMeta { txid, resp } => {
Expand Down Expand Up @@ -1479,10 +1509,16 @@ impl AsyncChainState {
resp_rx.await?
}

pub async fn get_block_meta(&self, block_hash: BlockHash) -> anyhow::Result<Option<BlockMeta>> {
pub async fn get_block_meta(
&self,
height_or_hash: HeightOrHash,
) -> anyhow::Result<BlockMetaWithHash> {
let (resp, resp_rx) = oneshot::channel();
self.sender
.send(ChainStateCommand::GetBlockMeta { block_hash, resp })
.send(ChainStateCommand::GetBlockMeta {
height_or_hash,
resp,
})
.await?;
resp_rx.await?
}
Expand Down
2 changes: 1 addition & 1 deletion protocol/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
#[derive(Debug, Clone)]
pub struct Validator {}

#[derive(Clone)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "bincode", derive(Encode, Decode))]
/// A `TxChangeSet` captures all resulting state changes.
Expand Down
Loading