Skip to content

Commit 141c08f

Browse files
committed
feat(rpc,node): submitblock pushes accepted blocks into BlockSync apply channel
Op: extend
1 parent 4dfa88e commit 141c08f

4 files changed

Lines changed: 42 additions & 9 deletions

File tree

crates/node/src/run.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ pub fn run(mut config: Config) -> Result<()> {
157157
state.peers(),
158158
state.block_tree(),
159159
state.config().network,
160+
Some(state.inbound_blocks_sender()),
160161
);
161162
let rpc_handler = Arc::new(bitcoin_rs_rpc::Handler::new(Arc::new(rpc_context)));
162163
let rpc_server = bitcoin_rs_rpc::RpcServer::bind(

crates/node/tests/rpc_wiring.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ fn rpc_context_shares_arc_identity_with_node_state() -> Result<()> {
3535
let mining_template_id = state.mining_template_id();
3636
let peers = state.peers();
3737
let block_tree = state.block_tree();
38+
let inbound_blocks_sender = state.inbound_blocks_sender();
3839

3940
let ctx = Context::from_handles(
4041
Arc::clone(&chain_tip),
@@ -50,6 +51,7 @@ fn rpc_context_shares_arc_identity_with_node_state() -> Result<()> {
5051
Arc::clone(&peers),
5152
Arc::clone(&block_tree),
5253
chain_network,
54+
Some(inbound_blocks_sender),
5355
);
5456

5557
assert!(
@@ -99,6 +101,10 @@ fn rpc_context_shares_arc_identity_with_node_state() -> Result<()> {
99101
Arc::ptr_eq(&ctx.block_tree, &block_tree),
100102
"block_tree must share identity"
101103
);
104+
assert!(
105+
ctx.inbound_blocks_sender.is_some(),
106+
"inbound_blocks_sender must be Some"
107+
);
102108

103109
Ok(())
104110
}

crates/rpc/src/context.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ pub struct Context {
136136
pub mining_template_id: Arc<ArcSwap<CompactString>>,
137137
/// Receiver notified when mining template inputs change.
138138
pub mining_notifications: Receiver<()>,
139+
/// Optional outbound channel that submits decoded blocks back to the node's
140+
/// `BlockSync::tick` for the canonical apply path. `None` when no node is
141+
/// wired (tests, embedded callers).
142+
pub inbound_blocks_sender: Option<crossbeam_channel::Sender<bitcoin::Block>>,
139143
mining_sender: Sender<()>,
140144
}
141145
// SAFETY: `Context` is shared by RPC worker threads. Each mutable subsystem
@@ -184,6 +188,7 @@ impl Context {
184188
block_tree: Arc::new(parking_lot::RwLock::new(bitcoin_rs_chain::BlockTree::new())),
185189
mining_template_id: Arc::new(ArcSwap::from_pointee(CompactString::new("0"))),
186190
mining_notifications,
191+
inbound_blocks_sender: None,
187192
mining_sender,
188193
}
189194
}
@@ -209,6 +214,7 @@ impl Context {
209214
peers: Arc<RwLock<Vec<bitcoin_rs_p2p::PeerInfo>>>,
210215
block_tree: Arc<parking_lot::RwLock<bitcoin_rs_chain::BlockTree>>,
211216
chain_network: Network,
217+
inbound_blocks_sender: Option<crossbeam_channel::Sender<bitcoin::Block>>,
212218
) -> Self {
213219
let (mining_sender, mining_notifications) = unbounded();
214220
Self {
@@ -226,6 +232,7 @@ impl Context {
226232
block_tree,
227233
mining_template_id,
228234
mining_notifications,
235+
inbound_blocks_sender,
229236
mining_sender,
230237
}
231238
}
@@ -440,6 +447,7 @@ mod tests {
440447
Arc::new(RwLock::new(Vec::new())),
441448
Arc::clone(&block_tree),
442449
Network::Mainnet,
450+
None,
443451
);
444452
assert!(
445453
Arc::ptr_eq(&ctx.chain_tip, &chain_tip),

crates/rpc/src/handlers/mining.rs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,26 +144,27 @@ fn chainwork_to_f64(work: bitcoin_rs_chain::ChainWork) -> f64 {
144144
.fold(0.0_f64, |acc, &byte| acc.mul_add(256.0, f64::from(byte)))
145145
}
146146

147-
pub(crate) fn submitblock(_ctx: &Arc<Context>, params: &Value) -> Result<Value, RpcError> {
147+
pub(crate) fn submitblock(ctx: &Arc<Context>, params: &Value) -> Result<Value, RpcError> {
148148
use bitcoin::consensus::encode::deserialize;
149-
use bitcoin::hex::FromHex as _;
149+
use bitcoin::hex::FromHex;
150150

151151
let hex = required_str(params, 0, "block hex is required")?;
152-
let bytes = Vec::<u8>::from_hex(hex)
152+
let bytes = <Vec<u8> as FromHex>::from_hex(hex)
153153
.map_err(|_| RpcError::InvalidParams("block hex is not valid hexadecimal"))?;
154154
let block: bitcoin::Block = match deserialize(&bytes) {
155-
Ok(block) => block,
155+
Ok(b) => b,
156156
Err(_) => return Ok(json!("bad-block-encoding")),
157157
};
158158
let target = block.header.target();
159159
if block.header.validate_pow(target).is_err() {
160160
return Ok(json!("high-hash"));
161161
}
162-
163-
// TODO(node-channel): push block bytes to BlockSync via a Sender<Vec<u8>>;
164-
// until then, accept the block as parseable + PoW-self-consistent and
165-
// return null per Bitcoin Core's accept signal. Real apply will happen
166-
// when the node-side channel is wired.
162+
if let Some(sender) = &ctx.inbound_blocks_sender {
163+
if sender.send(block).is_err() {
164+
return Ok(json!("channel-closed"));
165+
}
166+
}
167+
// Successful enqueue (or no-sender accept path) returns null.
167168
Ok(Value::new_null())
168169
}
169170

@@ -205,6 +206,23 @@ mod submitblock_tests {
205206
);
206207
}
207208

209+
#[test]
210+
fn submitblock_pushes_to_channel_when_present() {
211+
let (tx, rx) = crossbeam_channel::unbounded::<bitcoin::Block>();
212+
let mut ctx = Context::new();
213+
ctx.inbound_blocks_sender = Some(tx);
214+
let ctx = Arc::new(ctx);
215+
let genesis = bitcoin::blockdata::constants::genesis_block(bitcoin::Network::Regtest);
216+
let hex = serialize(&genesis).to_lower_hex_string();
217+
let result = submitblock(&ctx, &json!([hex]))
218+
.unwrap_or_else(|err| panic!("submitblock failed: {err}"));
219+
assert!(result.is_null());
220+
let received = rx
221+
.try_recv()
222+
.unwrap_or_else(|err| panic!("channel did not receive block: {err}"));
223+
assert_eq!(received.block_hash(), genesis.block_hash());
224+
}
225+
208226
#[test]
209227
fn submitblock_rejects_garbage() {
210228
let ctx = Arc::new(Context::new());

0 commit comments

Comments
 (0)