Skip to content

Commit 74f0e04

Browse files
committed
use a feature to perform submitpackage test
The feature requires bitcoin 28.0 to work
1 parent 6cfd434 commit 74f0e04

3 files changed

Lines changed: 116 additions & 120 deletions

File tree

.github/workflows/rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
curl -sSL https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz | tar -xz
4242
chmod +x bitcoin-28.0/bin/bitcoind
4343
- name: Run tests (Bitcoin 28.0, REST+Electrum)
44-
run: RUST_LOG=debug cargo test
44+
run: RUST_LOG=debug cargo test --features bitcoind_28_0
4545
env:
4646
BITCOIND_EXE: ${{ github.workspace }}/bitcoin-28.0/bin/bitcoind
4747

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ otlp-tracing = [
2727
"opentelemetry-semantic-conventions",
2828
"electrs_macros/otlp-tracing"
2929
]
30+
bitcoind_28_0 = []
3031

3132
[dependencies]
3233
arraydeque = "0.5.1"

tests/rest.rs

Lines changed: 114 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -207,92 +207,88 @@ fn test_rest() -> Result<()> {
207207
let status = empty_package_resp.status();
208208
assert_eq!(status, 400);
209209

210-
// Elements-only tests
211-
#[cfg(not(feature = "liquid"))]
210+
// bitcoin 28.0 only tests - submitpackage
211+
#[cfg(all(not(feature = "liquid"), feature = "bitcoind_28_0"))]
212212
{
213-
let network_info = tester.node_client().call::<Value>("getnetworkinfo", &[])?;
214-
let version = network_info["version"].as_u64().expect("network version");
215-
if version >= 280000 {
216-
// Test with a real transaction package - create parent-child transactions
217-
// submitpackage requires between 2 and 25 transactions with proper dependencies
218-
let package_addr1 = tester.newaddress()?;
219-
let package_addr2 = tester.newaddress()?;
220-
221-
// Create parent transaction
222-
let tx1_result = tester.node_client().call::<Value>(
223-
"createrawtransaction",
224-
&[
225-
serde_json::json!([]),
226-
serde_json::json!({package_addr1.to_string(): 0.5}),
227-
],
228-
)?;
229-
let tx1_unsigned_hex = tx1_result.as_str().expect("raw tx hex").to_string();
230-
231-
let tx1_fund_result = tester
232-
.node_client()
233-
.call::<Value>("fundrawtransaction", &[serde_json::json!(tx1_unsigned_hex)])?;
234-
let tx1_funded_hex = tx1_fund_result["hex"]
235-
.as_str()
236-
.expect("funded tx hex")
237-
.to_string();
238-
239-
let tx1_sign_result = tester.node_client().call::<Value>(
240-
"signrawtransactionwithwallet",
241-
&[serde_json::json!(tx1_funded_hex)],
242-
)?;
243-
let tx1_signed_hex = tx1_sign_result["hex"]
244-
.as_str()
245-
.expect("signed tx hex")
246-
.to_string();
247-
248-
// Decode parent transaction to get its txid and find the output to spend
249-
let tx1_decoded = tester
250-
.node_client()
251-
.call::<Value>("decoderawtransaction", &[serde_json::json!(tx1_signed_hex)])?;
252-
let tx1_txid = tx1_decoded["txid"].as_str().expect("parent txid");
253-
254-
// Find the output going to package_addr1 (the one we want to spend)
255-
let tx1_vouts = tx1_decoded["vout"].as_array().expect("parent vouts");
256-
let mut spend_vout_index = None;
257-
let mut spend_vout_value = 0u64;
258-
259-
for (i, vout) in tx1_vouts.iter().enumerate() {
260-
if let Some(script_pub_key) = vout.get("scriptPubKey") {
261-
if let Some(address) = script_pub_key.get("address") {
262-
if address.as_str() == Some(&package_addr1.to_string()) {
263-
spend_vout_index = Some(i);
264-
// Convert from BTC to satoshis
265-
spend_vout_value = (vout["value"].as_f64().expect("vout value")
266-
* 100_000_000.0)
267-
as u64;
268-
break;
269-
}
213+
// Test with a real transaction package - create parent-child transactions
214+
// submitpackage requires between 2 and 25 transactions with proper dependencies
215+
let package_addr1 = tester.newaddress()?;
216+
let package_addr2 = tester.newaddress()?;
217+
218+
// Create parent transaction
219+
let tx1_result = tester.node_client().call::<Value>(
220+
"createrawtransaction",
221+
&[
222+
serde_json::json!([]),
223+
serde_json::json!({package_addr1.to_string(): 0.5}),
224+
],
225+
)?;
226+
let tx1_unsigned_hex = tx1_result.as_str().expect("raw tx hex").to_string();
227+
228+
let tx1_fund_result = tester
229+
.node_client()
230+
.call::<Value>("fundrawtransaction", &[serde_json::json!(tx1_unsigned_hex)])?;
231+
let tx1_funded_hex = tx1_fund_result["hex"]
232+
.as_str()
233+
.expect("funded tx hex")
234+
.to_string();
235+
236+
let tx1_sign_result = tester.node_client().call::<Value>(
237+
"signrawtransactionwithwallet",
238+
&[serde_json::json!(tx1_funded_hex)],
239+
)?;
240+
let tx1_signed_hex = tx1_sign_result["hex"]
241+
.as_str()
242+
.expect("signed tx hex")
243+
.to_string();
244+
245+
// Decode parent transaction to get its txid and find the output to spend
246+
let tx1_decoded = tester
247+
.node_client()
248+
.call::<Value>("decoderawtransaction", &[serde_json::json!(tx1_signed_hex)])?;
249+
let tx1_txid = tx1_decoded["txid"].as_str().expect("parent txid");
250+
251+
// Find the output going to package_addr1 (the one we want to spend)
252+
let tx1_vouts = tx1_decoded["vout"].as_array().expect("parent vouts");
253+
let mut spend_vout_index = None;
254+
let mut spend_vout_value = 0u64;
255+
256+
for (i, vout) in tx1_vouts.iter().enumerate() {
257+
if let Some(script_pub_key) = vout.get("scriptPubKey") {
258+
if let Some(address) = script_pub_key.get("address") {
259+
if address.as_str() == Some(&package_addr1.to_string()) {
260+
spend_vout_index = Some(i);
261+
// Convert from BTC to satoshis
262+
spend_vout_value =
263+
(vout["value"].as_f64().expect("vout value") * 100_000_000.0) as u64;
264+
break;
270265
}
271266
}
272267
}
268+
}
273269

274-
let spend_vout_index = spend_vout_index.expect("Could not find output to spend");
275-
276-
// Create child transaction that spends from parent
277-
// Leave some satoshis for fee (e.g., 1000 sats)
278-
let child_output_value = spend_vout_value - 1000;
279-
let child_output_btc = child_output_value as f64 / 100_000_000.0;
280-
281-
let tx2_result = tester.node_client().call::<Value>(
282-
"createrawtransaction",
283-
&[
284-
serde_json::json!([{
285-
"txid": tx1_txid,
286-
"vout": spend_vout_index
287-
}]),
288-
serde_json::json!({package_addr2.to_string(): child_output_btc}),
289-
],
290-
)?;
291-
let tx2_unsigned_hex = tx2_result.as_str().expect("raw tx hex").to_string();
292-
293-
// Sign the child transaction
294-
// We need to provide the parent transaction's output details for signing
295-
let tx2_sign_result = tester.node_client().call::<Value>(
270+
let spend_vout_index = spend_vout_index.expect("Could not find output to spend");
271+
272+
// Create child transaction that spends from parent
273+
// Leave some satoshis for fee (e.g., 1000 sats)
274+
let child_output_value = spend_vout_value - 1000;
275+
let child_output_btc = child_output_value as f64 / 100_000_000.0;
276+
277+
let tx2_result = tester.node_client().call::<Value>(
278+
"createrawtransaction",
279+
&[
280+
serde_json::json!([{
281+
"txid": tx1_txid,
282+
"vout": spend_vout_index
283+
}]),
284+
serde_json::json!({package_addr2.to_string(): child_output_btc}),
285+
],
286+
)?;
287+
let tx2_unsigned_hex = tx2_result.as_str().expect("raw tx hex").to_string();
288+
289+
// Sign the child transaction
290+
// We need to provide the parent transaction's output details for signing
291+
let tx2_sign_result = tester.node_client().call::<Value>(
296292
"signrawtransactionwithwallet",
297293
&[
298294
serde_json::json!(tx2_unsigned_hex),
@@ -304,50 +300,49 @@ fn test_rest() -> Result<()> {
304300
}])
305301
],
306302
)?;
307-
let tx2_signed_hex = tx2_sign_result["hex"]
308-
.as_str()
309-
.expect("signed tx hex")
310-
.to_string();
311-
312-
// Debug: try calling submitpackage directly to see the result
313-
eprintln!("Trying submitpackage directly with parent-child transactions...");
314-
let direct_result = tester.node_client().call::<Value>(
315-
"submitpackage",
316-
&[serde_json::json!([
317-
tx1_signed_hex.clone(),
318-
tx2_signed_hex.clone()
319-
])],
320-
);
321-
match direct_result {
322-
Ok(result) => {
323-
eprintln!("Direct submitpackage succeeded: {:#?}", result);
324-
}
325-
Err(e) => {
326-
eprintln!("Direct submitpackage failed: {:?}", e);
327-
}
303+
let tx2_signed_hex = tx2_sign_result["hex"]
304+
.as_str()
305+
.expect("signed tx hex")
306+
.to_string();
307+
308+
// Debug: try calling submitpackage directly to see the result
309+
eprintln!("Trying submitpackage directly with parent-child transactions...");
310+
let direct_result = tester.node_client().call::<Value>(
311+
"submitpackage",
312+
&[serde_json::json!([
313+
tx1_signed_hex.clone(),
314+
tx2_signed_hex.clone()
315+
])],
316+
);
317+
match direct_result {
318+
Ok(result) => {
319+
eprintln!("Direct submitpackage succeeded: {:#?}", result);
320+
}
321+
Err(e) => {
322+
eprintln!("Direct submitpackage failed: {:?}", e);
328323
}
324+
}
329325

330-
// Now submit this transaction package via the package endpoint
331-
let package_json =
332-
serde_json::json!([tx1_signed_hex.clone(), tx2_signed_hex.clone()]).to_string();
333-
let package_result = ureq::post(&format!("http://{}/txs/package", rest_addr))
334-
.set("Content-Type", "application/json")
335-
.send_string(&package_json);
326+
// Now submit this transaction package via the package endpoint
327+
let package_json =
328+
serde_json::json!([tx1_signed_hex.clone(), tx2_signed_hex.clone()]).to_string();
329+
let package_result = ureq::post(&format!("http://{}/txs/package", rest_addr))
330+
.set("Content-Type", "application/json")
331+
.send_string(&package_json);
336332

337-
let package_resp = package_result.unwrap();
338-
assert_eq!(package_resp.status(), 200);
339-
let package_result = package_resp.into_json::<Value>()?;
333+
let package_resp = package_result.unwrap();
334+
assert_eq!(package_resp.status(), 200);
335+
let package_result = package_resp.into_json::<Value>()?;
340336

341-
// Verify the response structure
342-
assert!(package_result["tx-results"].is_object());
343-
assert!(package_result["package_msg"].is_string());
337+
// Verify the response structure
338+
assert!(package_result["tx-results"].is_object());
339+
assert!(package_result["package_msg"].is_string());
344340

345-
let tx_results = package_result["tx-results"].as_object().unwrap();
346-
assert_eq!(tx_results.len(), 2);
341+
let tx_results = package_result["tx-results"].as_object().unwrap();
342+
assert_eq!(tx_results.len(), 2);
347343

348-
// The transactions should be processed (whether accepted or rejected)
349-
assert!(!tx_results.is_empty());
350-
}
344+
// The transactions should be processed (whether accepted or rejected)
345+
assert!(!tx_results.is_empty());
351346
}
352347

353348
// Elements-only tests

0 commit comments

Comments
 (0)