How to get bitcoin block headers in regtest?

how do I get the block headers in regtest?
for mainnet I see this canister: https://dashboard.internetcomputer.org/canister/ghsi2-tqaaa-aaaan-aaaca-cai

I tried calling management canister for regtest, found out that it has no bitcoin_get_block_headers method.

Hi @Buriburizaemon,
Here is an example of usage of bitcoin_get_block_headers method chore: Add example of using bitcoin_get_block_headers to basic_bitcoin by dragoljub-duric · Pull Request #960 · dfinity/examples · GitHub . As you can see, it is not necessary to call the Bitcoin canister directly, but rather you can call the management canister which will route the message to the respective Bitcoin canister based on the network argument.

The SDK team plans to add CDK support soon, which will further simplify the usage of the endpoint.

2 Likes

hello @dragoljub_duric ,
I updated the code, it works now. I’m able to fetch the bitcoin headers.

but I still get this error some times.
(CanisterError, \\"IC0536: Management canister has no method 'bitcoin_get_block_headers'\\")
do you think, this error is due to calling management canister frequently in a short period of time?

here is the log:

If you are using Regtest the call should fail, because we do not have a Regtest Bitcoin canister (we only have Mainnet and Testnet). But since your request is passing, I guess you do not call with network = Regtest.

In that case, I will need more code in order to try to find the cause of this behavior.

here is my code:

use bitcoin::{block::Header, BlockHash};
use candid::{self, CandidType, Deserialize, Principal};
use core2::io::Cursor;
use rune_indexer_interface::OrdError;

lazy_static::lazy_static! {
  pub static ref BTC: Principal = Principal::management_canister();
}

type BlockHeight = u32;

#[derive(CandidType, Deserialize)]
pub enum Network {
  #[serde(rename = "mainnet")]
  Mainnet,
  #[serde(rename = "regtest")]
  Regtest,
  #[serde(rename = "testnet")]
  Testnet,
}

#[derive(CandidType, Deserialize)]
pub struct GetBlockHeadersRequest {
  pub start_height: BlockHeight,
  pub end_height: Option<BlockHeight>,
  pub network: Network,
}

#[derive(CandidType, Deserialize)]
pub struct GetBlockHeadersResponse {
  pub tip_height: BlockHeight,
  pub block_headers: Vec<Vec<u8>>,
}

pub async fn get_block_hash(height: u32) -> crate::Result<BlockHash> {
  let req = GetBlockHeadersRequest {
    start_height: height,
    end_height: None,
    network: Network::Regtest,
  };
  ic_cdk::println!("calling bitcoin header");
  let res: (GetBlockHeadersResponse,) = ic_cdk::call(*BTC, "bitcoin_get_block_headers", (req,))
    .await
    .map_err(|e| {
      OrdError::Params(format!(
        "failed to retrieve header from btc_canister, error: {:?}",
        e
      ))
    })?;
  ic_cdk::println!("successfully called bitcoin header");
  let header = res
    .0
    .block_headers
    .first()
    .map(|b| {
      let mut buffer = Cursor::new(b);
      <Header as bitcoin::consensus::Decodable>::consensus_decode(&mut buffer)
    })
    .ok_or_else(|| OrdError::Params("block not ready".to_string()))?;
  Ok(
    header
      .map_err(|_| {
        OrdError::Params(
          "invalid block header from canister because we can't decode it".to_string(),
        )
      })?
      .block_hash(),
  )
}

here I’m setting up that:

let req = GetBlockHeadersRequest {
    start_height: height,
    end_height: None,
    network: Network::Regtest,
  };

Please use ic_cdk::api::management_canister::bitcoin::BitcoinNetwork instead of this as done in the example above.

@dragoljub_duric

I updated the code:

still the same behaviour

Where this log is coming from?

here is the code:

pub(crate) async fn get_best_from_rpc() -> Result<(u32, BlockHash)> {
  let url = get_url();
  let hash = rpc::get_best_block_hash(&url).await?;
  let header = rpc::get_block_header(&url, hash).await?;
  log!(
    INFO,
    "printing after the line header, here is header: {:?}",
    header
  );
  ic_cdk::println!("Am I getting printed?");
  ic_cdk::println!("Header: {:?}", header);
  Ok((header.height.try_into().expect("usize to u32"), hash))
}

@dragoljub_duric
This code if from ord_canister by omnity network. I’m modifying the code for running in my localhost with regtest mode for testing the application.

1 Like

@dragoljub_duric
getting headers on regtest isn’t possible?

No, it is not possible.
If you are using ic_cdk::api::management_canister::bitcoin::BitcoinNetwork as I pointed out above, you should choose between Testnet and Mainnet.

hello @dragoljub_duric

As I’m running a bitcoin node locally, DFX already fetch the blocks from that node.
I’m running a server that uploads bitcoin blocks to the canister for filtering runes.

Skipping the block header check, might not cause that much issue, right?