Skip to content

Commit 9a60f35

Browse files
committed
Return an error for missing or not yet indexed blocks. See #18
1 parent 159df16 commit 9a60f35

File tree

4 files changed

+69
-29
lines changed

4 files changed

+69
-29
lines changed

node/src/bin/spaced.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use tokio::{
1616
sync::{broadcast, mpsc},
1717
task::{JoinHandle, JoinSet},
1818
};
19+
use spaced::source::BitcoinRpc;
1920

2021
#[tokio::main]
2122
async fn main() {
@@ -82,6 +83,7 @@ impl Composer {
8283
};
8384

8485
let (async_chain_state, async_chain_state_handle) = create_async_store(
86+
spaced.rpc.clone(),
8587
spaced.chain.state.clone(),
8688
spaced.block_index.as_ref().map(|index| index.state.clone()),
8789
self.shutdown.subscribe(),
@@ -140,15 +142,16 @@ impl Composer {
140142
}
141143

142144
async fn create_async_store(
145+
rpc: BitcoinRpc,
143146
chain_state: LiveSnapshot,
144147
block_index: Option<LiveSnapshot>,
145148
shutdown: broadcast::Receiver<()>,
146149
) -> (AsyncChainState, JoinHandle<()>) {
147150
let (tx, rx) = mpsc::channel(32);
148151
let async_store = AsyncChainState::new(tx);
149-
152+
let client = reqwest::Client::new();
150153
let handle = tokio::spawn(async move {
151-
AsyncChainState::handler(chain_state, block_index, rx, shutdown).await
154+
AsyncChainState::handler(&client, rpc, chain_state, block_index, rx, shutdown).await
152155
});
153156
(async_store, handle)
154157
}

node/src/rpc.rs

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub trait Rpc {
100100

101101
#[method(name = "getspaceowner")]
102102
async fn get_space_owner(&self, space_hash: &str)
103-
-> Result<Option<OutPoint>, ErrorObjectOwned>;
103+
-> Result<Option<OutPoint>, ErrorObjectOwned>;
104104

105105
#[method(name = "getspaceout")]
106106
async fn get_spaceout(&self, outpoint: OutPoint) -> Result<Option<SpaceOut>, ErrorObjectOwned>;
@@ -111,8 +111,8 @@ pub trait Rpc {
111111
#[method(name = "getrollout")]
112112
async fn get_rollout(&self, target: usize) -> Result<Vec<(u32, SpaceKey)>, ErrorObjectOwned>;
113113

114-
#[method(name = "getblockmeta")]
115-
async fn get_block_meta(
114+
#[method(name = "getblock")]
115+
async fn get_block(
116116
&self,
117117
block_hash: BlockHash,
118118
) -> Result<Option<BlockMeta>, ErrorObjectOwned>;
@@ -156,11 +156,11 @@ pub trait Rpc {
156156

157157
#[method(name = "walletlistspaces")]
158158
async fn wallet_list_spaces(&self, wallet: &str)
159-
-> Result<Vec<FullSpaceOut>, ErrorObjectOwned>;
159+
-> Result<Vec<FullSpaceOut>, ErrorObjectOwned>;
160160

161161
#[method(name = "walletlistunspent")]
162162
async fn wallet_list_unspent(&self, wallet: &str)
163-
-> Result<Vec<LocalOutput>, ErrorObjectOwned>;
163+
-> Result<Vec<LocalOutput>, ErrorObjectOwned>;
164164

165165
#[method(name = "walletlistauctionoutputs")]
166166
async fn wallet_list_auction_outputs(
@@ -654,13 +654,13 @@ impl RpcServer for RpcServerImpl {
654654
Ok(rollouts)
655655
}
656656

657-
async fn get_block_meta(
657+
async fn get_block(
658658
&self,
659659
block_hash: BlockHash,
660660
) -> Result<Option<BlockMeta>, ErrorObjectOwned> {
661661
let data = self
662662
.store
663-
.get_block_meta(block_hash)
663+
.get_block(block_hash)
664664
.await
665665
.map_err(|error| ErrorObjectOwned::owned(-1, error.to_string(), None::<String>))?;
666666

@@ -796,7 +796,44 @@ impl AsyncChainState {
796796
Self { sender }
797797
}
798798

799+
async fn get_indexed_block(
800+
index: &mut Option<LiveSnapshot>,
801+
block_hash: &BlockHash,
802+
client: &reqwest::Client,
803+
rpc: &BitcoinRpc,
804+
chain_state: &mut LiveSnapshot,
805+
) -> Result<Option<BlockMeta>, anyhow::Error> {
806+
let index = index.as_mut()
807+
.ok_or_else(|| anyhow!("block index must be enabled"))?;
808+
let hash = BaseHash::from_slice(block_hash.as_ref());
809+
let block: Option<BlockMeta> = index.get(hash)
810+
.context("Could not fetch block from index")?;
811+
812+
if let Some(block_set) = block {
813+
return Ok(Some(block_set));
814+
}
815+
816+
let info: serde_json::Value = rpc.send_json(client, &rpc.
817+
get_block_header(block_hash)).await
818+
.map_err(|e| anyhow!("Could not retrieve block ({})", e))?;
819+
820+
let height = info.get("height").and_then(|t| t.as_u64())
821+
.ok_or_else(|| anyhow!("Could not retrieve block height"))?;
822+
823+
let tip = chain_state.tip.read().expect("read meta").clone();
824+
if height > tip.height as u64 {
825+
return Err(anyhow!(
826+
"Spaces is syncing at height {}, requested block height {}",
827+
tip.height,
828+
height
829+
));
830+
}
831+
Err(anyhow!("Could not retrieve block"))
832+
}
833+
799834
pub async fn handle_command(
835+
client: &reqwest::Client,
836+
rpc: &BitcoinRpc,
800837
chain_state: &mut LiveSnapshot,
801838
block_index: &mut Option<LiveSnapshot>,
802839
cmd: ChainStateCommand,
@@ -822,19 +859,12 @@ impl AsyncChainState {
822859
.context("could not fetch spaceout");
823860
let _ = resp.send(result);
824861
}
825-
ChainStateCommand::GetBlockMeta { block_hash, resp } => match block_index {
826-
None => {
827-
let _ = resp.send(Err(anyhow!("block index must be enabled")));
828-
}
829-
Some(index) => {
830-
let hash = BaseHash::from_slice(block_hash.as_ref());
831-
let _ = resp.send(
832-
index
833-
.get(hash)
834-
.context("Could not fetch blockdata from index"),
835-
);
836-
}
837-
},
862+
ChainStateCommand::GetBlockMeta { block_hash, resp } => {
863+
let res = Self::get_indexed_block(
864+
block_index, &block_hash, client, rpc, chain_state
865+
).await;
866+
let _ = resp.send(res);
867+
}
838868
ChainStateCommand::EstimateBid { target, resp } => {
839869
let estimate = chain_state.estimate_bid(target);
840870
_ = resp.send(estimate);
@@ -847,6 +877,8 @@ impl AsyncChainState {
847877
}
848878

849879
pub async fn handler(
880+
client: &reqwest::Client,
881+
rpc: BitcoinRpc,
850882
mut chain_state: LiveSnapshot,
851883
mut block_index: Option<LiveSnapshot>,
852884
mut rx: mpsc::Receiver<ChainStateCommand>,
@@ -858,7 +890,7 @@ impl AsyncChainState {
858890
break;
859891
}
860892
Some(cmd) = rx.recv() => {
861-
Self::handle_command(&mut chain_state, &mut block_index, cmd).await;
893+
Self::handle_command(client, &rpc, &mut chain_state, &mut block_index, cmd).await;
862894
}
863895
}
864896
}
@@ -912,7 +944,7 @@ impl AsyncChainState {
912944
resp_rx.await?
913945
}
914946

915-
pub async fn get_block_meta(&self, block_hash: BlockHash) -> anyhow::Result<Option<BlockMeta>> {
947+
pub async fn get_block(&self, block_hash: BlockHash) -> anyhow::Result<Option<BlockMeta>> {
916948
let (resp, resp_rx) = oneshot::channel();
917949
self.sender
918950
.send(ChainStateCommand::GetBlockMeta { block_hash, resp })

node/src/source.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ impl BitcoinRpc {
141141
self.make_request("getblockcount", params)
142142
}
143143

144+
pub fn get_block_header(&self, hash : &BlockHash) -> BitcoinRpcRequest {
145+
let params = serde_json::json!([hash]);
146+
self.make_request("getblockheader", params)
147+
}
148+
144149
pub fn get_block_hash(&self, height: u32) -> BitcoinRpcRequest {
145150
let params = serde_json::json!([height]);
146151

protocol/src/validate.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,11 @@ impl Validator {
9898
Self {}
9999
}
100100

101-
pub fn process(&self, height: u32, tx: &Transaction, mut meta: TxContext) -> TxChangeSet {
101+
pub fn process(&self, height: u32, tx: &Transaction, mut ctx: TxContext) -> TxChangeSet {
102102
// Auctioned outputs could technically be spent in the same transaction
103103
// making the bid psbt unusable. We need to clear any spent ones
104104
// before proceeding with further validation
105-
Self::clear_auctioned_spent(tx, &mut meta);
105+
Self::clear_auctioned_spent(tx, &mut ctx);
106106

107107
let mut changeset = TxChangeSet {
108108
txid: tx.compute_txid(),
@@ -115,7 +115,7 @@ impl Validator {
115115
let mut space_data = BTreeMap::new();
116116
let mut reserve = false;
117117

118-
for fullspacein in meta.inputs.into_iter() {
118+
for fullspacein in ctx.inputs.into_iter() {
119119
changeset.spends.push(SpaceIn {
120120
n: fullspacein.n,
121121
script_error: None,
@@ -125,7 +125,7 @@ impl Validator {
125125
self.process_spend(
126126
height,
127127
tx,
128-
&mut meta.auctioned_output,
128+
&mut ctx.auctioned_output,
129129
fullspacein.n,
130130
fullspacein.sstxo,
131131
&mut changeset,
@@ -143,7 +143,7 @@ impl Validator {
143143
self.process_open(
144144
height,
145145
open,
146-
&mut meta.auctioned_output,
146+
&mut ctx.auctioned_output,
147147
&mut changeset,
148148
);
149149
}

0 commit comments

Comments
 (0)