use bitcoin_capnp_types::mining_capnp;
#[path = "util/bitcoin_core.rs"]
mod bitcoin_core_util;
#[path = "util/bitcoin_core_wallet.rs"]
mod bitcoin_core_wallet_util;
use bitcoin_core_util::{
destroy_template, make_block_template, mempool_tx_count, with_init_client, with_mining_client,
};
use bitcoin_core_wallet_util::{
bitcoin_test_wallet, create_mempool_self_transfer, ensure_wallet_loaded_and_funded,
};
#[tokio::test]
#[serial_test::parallel]
async fn integration() {
with_init_client(|client, thread| async move {
let mut echo = client.make_echo_request();
echo.get().get_context().unwrap().set_thread(thread.clone());
let echo_client_request = echo.send().promise.await.unwrap();
let echo_client = echo_client_request.get().unwrap().get_result().unwrap();
let mut echo_conf = echo_client.echo_request();
echo_conf
.get()
.get_context()
.unwrap()
.set_thread(thread.clone());
echo_conf.get().set_echo("Hello world");
let echo_response = echo_conf.send().promise.await.unwrap();
let text = echo_response
.get()
.unwrap()
.get_result()
.unwrap()
.to_string()
.unwrap();
assert_eq!("Hello world", text);
})
.await;
}
#[tokio::test]
#[serial_test::parallel]
async fn make_mining_old2_rejected() {
with_init_client(|client, _thread| async move {
let result = client.make_mining_old2_request().send().promise.await;
assert!(
result.is_err(),
"makeMiningOld2 should be rejected by the server"
);
})
.await;
}
#[test]
#[serial_test::parallel]
fn mining_constants() {
assert_eq!(mining_capnp::MAX_MONEY, 2_100_000_000_000_000i64);
const { assert!(mining_capnp::MAX_DOUBLE > 1e300) };
assert_eq!(mining_capnp::DEFAULT_BLOCK_RESERVED_WEIGHT, 8_000u32);
assert_eq!(
mining_capnp::DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS,
400u32
);
}
#[tokio::test]
#[serial_test::parallel]
async fn mining_basic_queries() {
with_mining_client(|_client, thread, mining| async move {
let mut req = mining.is_test_chain_request();
req.get().get_context().unwrap().set_thread(thread.clone());
let resp = req.send().promise.await.unwrap();
assert!(resp.get().unwrap().get_result(), "regtest is a test chain");
let mut req = mining.is_initial_block_download_request();
req.get().get_context().unwrap().set_thread(thread.clone());
let resp = req.send().promise.await.unwrap();
let _ibd: bool = resp.get().unwrap().get_result();
let mut req = mining.get_tip_request();
req.get().get_context().unwrap().set_thread(thread.clone());
let resp = req.send().promise.await.unwrap();
let results = resp.get().unwrap();
assert!(results.get_has_result(), "node should have a tip");
let tip = results.get_result().unwrap();
let tip_hash = tip.get_hash().unwrap();
assert_eq!(tip_hash.len(), 32, "block hash must be 32 bytes");
assert!(tip.get_height() >= 0, "height must be non-negative");
})
.await;
}
#[tokio::test]
#[serial_test::serial]
async fn mining_wait_tip_changed() {
with_mining_client(|_client, thread, mining| async move {
let mut req = mining.get_tip_request();
req.get().get_context().unwrap().set_thread(thread.clone());
let resp = req.send().promise.await.unwrap();
let results = resp.get().unwrap();
let tip = results.get_result().unwrap();
let tip_hash: Vec<u8> = tip.get_hash().unwrap().to_vec();
let tip_height: i32 = tip.get_height();
let mut req = mining.wait_tip_changed_request();
req.get().get_context().unwrap().set_thread(thread.clone());
req.get().set_current_tip(&tip_hash);
req.get().set_timeout(500.0); let resp = req.send().promise.await.unwrap();
let wait_result = resp.get().unwrap().get_result().unwrap();
assert_eq!(wait_result.get_hash().unwrap().len(), 32);
assert_eq!(wait_result.get_height(), tip_height);
})
.await;
}
#[tokio::test]
#[serial_test::parallel]
async fn mining_block_template_inspection() {
with_mining_client(|_client, thread, mining| async move {
let template = make_block_template(&mining, &thread).await;
let mut req = template.get_block_header_request();
req.get().get_context().unwrap().set_thread(thread.clone());
let resp = req.send().promise.await.unwrap();
let header = resp.get().unwrap().get_result().unwrap();
assert_eq!(header.len(), 80, "block header must be 80 bytes");
let mut req = template.get_block_request();
req.get().get_context().unwrap().set_thread(thread.clone());
let resp = req.send().promise.await.unwrap();
let block = resp.get().unwrap().get_result().unwrap();
assert!(block.len() > 80, "serialized block must be > 80 bytes");
let mut req = template.get_tx_fees_request();
req.get().get_context().unwrap().set_thread(thread.clone());
let resp = req.send().promise.await.unwrap();
let _fees = resp.get().unwrap().get_result().unwrap();
let mut req = template.get_tx_sigops_request();
req.get().get_context().unwrap().set_thread(thread.clone());
let resp = req.send().promise.await.unwrap();
let _sigops = resp.get().unwrap().get_result().unwrap();
let mut req = template.get_coinbase_tx_request();
req.get().get_context().unwrap().set_thread(thread.clone());
let resp = req.send().promise.await.unwrap();
let coinbase = resp.get().unwrap().get_result().unwrap();
let _version: u32 = coinbase.get_version();
let _sequence: u32 = coinbase.get_sequence();
let script_sig_prefix = coinbase.get_script_sig_prefix().unwrap();
assert!(
!script_sig_prefix.is_empty(),
"scriptSigPrefix must contain at least the block height"
);
let _witness = coinbase.get_witness().unwrap();
let reward: i64 = coinbase.get_block_reward_remaining();
assert!(reward > 0 && reward <= mining_capnp::MAX_MONEY);
let _required_outputs = coinbase.get_required_outputs().unwrap();
let _lock_time: u32 = coinbase.get_lock_time();
let mut req = template.get_coinbase_merkle_path_request();
req.get().get_context().unwrap().set_thread(thread.clone());
let resp = req.send().promise.await.unwrap();
let _merkle_path = resp.get().unwrap().get_result().unwrap();
destroy_template(&template, &thread).await;
})
.await;
}
#[tokio::test]
#[serial_test::serial]
async fn mining_block_template_lifecycle() {
with_mining_client(|_client, thread, mining| async move {
let template = make_block_template(&mining, &thread).await;
let mut req = template.wait_next_request();
req.get().get_context().unwrap().set_thread(thread.clone());
{
let mut opts = req.get().init_options();
opts.set_timeout(100.0); opts.set_fee_threshold(mining_capnp::MAX_MONEY);
}
let resp = req.send().promise.await.unwrap();
let _has_next = resp.get().unwrap().has_result();
template
.interrupt_wait_request()
.send()
.promise
.await
.unwrap();
let mut req = template.submit_solution_request();
req.get().get_context().unwrap().set_thread(thread.clone());
req.get().set_version(1);
req.get().set_timestamp(0);
req.get().set_nonce(0);
req.get().set_coinbase(&[0u8; 64]);
let resp = req.send().promise.await.unwrap();
let submitted = resp.get().unwrap().get_result();
assert!(!submitted, "garbage solution must not be accepted");
destroy_template(&template, &thread).await;
})
.await;
}
#[tokio::test]
#[serial_test::serial]
async fn mining_check_block_and_interrupt() {
with_mining_client(|_client, thread, mining| async move {
let template = make_block_template(&mining, &thread).await;
let mut get_block_req = template.get_block_request();
get_block_req
.get()
.get_context()
.unwrap()
.set_thread(thread.clone());
let get_block_resp = get_block_req.send().promise.await.unwrap();
let block = get_block_resp.get().unwrap().get_result().unwrap().to_vec();
let mut req = mining.check_block_request();
req.get().get_context().unwrap().set_thread(thread.clone());
req.get().set_block(&block);
{
let mut opts = req.get().init_options();
opts.set_check_merkle_root(true);
opts.set_check_pow(false);
}
let result = req.send().promise.await;
match result {
Ok(resp) => {
let results = resp.get().unwrap();
let _valid: bool = results.get_result();
let _reason = results.get_reason().unwrap();
let _debug = results.get_debug().unwrap();
}
Err(_) => {
}
}
destroy_template(&template, &thread).await;
mining.interrupt_request().send().promise.await.unwrap();
})
.await;
}
#[tokio::test]
#[serial_test::serial]
async fn wallet_helpers_create_mempool_transaction() {
let wallet = bitcoin_test_wallet();
assert!(!wallet.is_empty(), "test wallet name must not be empty");
ensure_wallet_loaded_and_funded(&wallet);
let before = mempool_tx_count();
let _tx = create_mempool_self_transfer(&wallet);
let after = mempool_tx_count();
assert_eq!(
after,
before + 1,
"self-transfer should add one mempool transaction"
);
}