bitcoin Thin Clients Download an header chain with bitcore-p2p


Example

IMPORTANT This is just an example code, do not use in production.

In order to download an header chain we have to send a getHeaders message.

In this example we will require as much as possible headers after the 40000th one.The peer will respond with batch of 2000 headers so, we have to take the last header hash for be able to require the next 2000 headers.

Consider the fact that bitcore-p2p is an event based library the most robust option could be promisify the network interface in order to use async/await for download the header chain. Here for the sake of simplicity we will use a generator to face the asynchronous nature of the network.

let Messages = require('bitcore-p2p').Messages;
let Pool = require('bitcore-p2p').Pool;
let NetworksData = require('bitcore-lib').Networks;

let network = 'livenet';
let headers = [];          // do not do that in production!
let validHeaders = [];     // do not do that in production!
let firsHeader = '000000000000000004ec466ce4732fe6f1ed1cddc2ed4b328fff5224276e3f6f';

// Isatnciate and connect a node Pool
let pool = new Pool({network: NetworksData[network]});
pool.connect();

// we have to reverese the hash becaouse is in the format xxxx0000 instead of 0000xxxx or vice versa
function reverseHash(hash){
  return hash.match(/.{1,2}/g).reverse().join('');
}

// check if the response is the one associate with the last request becaouse could be a response associate to an old request
function isValidResponse(firstHeaderRecived, headerHashRequested){
  // the header hash of the block before the first block header that we get on the response
  let headerHashBeforeFirstHeaderRecived = reverseHash(firstHeaderRecived.prevHash.toString('hex'));
  if (headerHashBeforeFirstHeaderRecived === headerHashRequested){
    return true;
  }
  else{
    return false;
  }
}

pool.on('peerheaders', function(peer, message) {
  let lastHeaderHashRequested;
  if (validHeaders[validHeaders.length -1]) {
    lastHeaderHashRequested = validHeaders[validHeaders.length -1].hash;
  }
  else {
    lastHeaderHashRequested = firsHeader;
  }
  if (isValidResponse(message.headers[0], lastHeaderHashRequested) && headers.length === 0) {
    headers.push({
      peer: peer,
      message: message,
    });

    console.log('Recived from: ', peer.host, message.headers.length, 'headers');
    console.log('The first block hash is', message.headers[0].hash);
    console.log('The last block hash is', message.headers[message.headers.length - 1].hash);

    syncronize.next();
    //console.log(syncronize)
  }
});

function* sync(lastHash) {
  let startHash = new Buffer(lastHash, 'hex');
  let message = new Messages({network: NetworksData[network]}).GetHeaders();
  // require as much as possible headers after startHash
  message.starts.push(startHash);
  pool.sendMessage(message);
  yield;
  validHeaders.push(...headers[0].message.headers);
  headers = [];
  let lastDownloadedHeader = validHeaders[validHeaders.length - 1];
  if (validHeaders.length % 2000 === 0) {
    yield * sync(reverseHash(lastDownloadedHeader.hash));
  }
}

syncronize = sync(reverseHash(firsHeader));

// Wait for pool to connect
setTimeout(function(){
  console.log(pool);
  syncronize.next();
}, 5000);