Skip to content

Commit

Permalink
refactored fetchBitcoinTxs && api falllback mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
uwezukwechibuzor committed Sep 27, 2023
1 parent 940e369 commit 85ef752
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 56 deletions.
12 changes: 11 additions & 1 deletion backend/Routes/Chains/bitcoin/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,18 @@ require("dotenv").config();

const BITCOIN_API = process.env.BITCOIN_REST_API;

let RESTAPI = [];

if (BITCOIN_API) {
try {
RESTAPI = JSON.parse(BITCOIN_API);
} catch (error) {
console.error("Error parsing BITCOIN_API :", error);
}
}

//cron task for Bitcoin
createBitcoinCronJob(BITCOIN_API, Model.bitcoinTxsModel);
createBitcoinCronJob(RESTAPI, Model.bitcoinTxsModel);

// Define a helper function to prefix the routes with "/bitcoin"
function bitcoinRoute(routePrefix, path, handler) {
Expand Down
126 changes: 71 additions & 55 deletions backend/data/chainQueries/bitcoin/index.js
Original file line number Diff line number Diff line change
@@ -1,74 +1,90 @@
const endpoints = require("../../endpoints.jsx");
const fetch = require("node-fetch");

const fetchBitcoinTxs = async (api, txModel) => {
const fetchBitcoinTxs = async (apis, txModel) => {
try {
// Get transactions data in each block
const getTxs = await fetch(api + endpoints.bitcoinTxs);

if (!getTxs.ok) {
throw new Error(
`Unexpected response: ${getTxs.status} - ${getTxs.statusText}`
);
// Parameter validation
if (!apis || !Array.isArray(apis) || apis.length === 0 || !txModel) {
throw new Error("Invalid parameters");
}

// Check the Content-Type header
const contentType = getTxs.headers.get("Content-Type");
if (!contentType || !contentType.includes("application/json")) {
throw new Error("Invalid response content type: not JSON");
}
for (const api of apis) {
try {
// Make an HTTP request to the current API endpoint to get Bitcoin transactions
const getTxs = await fetch(api + endpoints.bitcoinTxs);

const responseText = await getTxs.text();
// Check if the response is not OK (e.g., HTTP status code is not in the 200 range)
if (!getTxs.ok) {
throw new Error(
`Unexpected response: ${getTxs.status} - ${getTxs.statusText}`
);
}

// Check if the response body is empty
if (!responseText) {
throw new Error("Empty response body");
}
// Check the Content-Type header to ensure it's JSON
const contentType = getTxs.headers.get("Content-Type");
if (!contentType || !contentType.includes("application/json")) {
throw new Error("Invalid response content type: not JSON");
}

// Attempt to parse the response as JSON
let txData;
try {
txData = JSON.parse(responseText);
} catch (jsonError) {
console.error(`Failed to parse JSON response: ${jsonError}`);
throw new Error("Invalid JSON response");
}
// Read the response text
const responseText = await getTxs.text();

// Check if txData and txData.data.list are not null
if (!txData || !txData.data || !txData.data.list) {
return;
}
// Check if the response body is empty
if (!responseText) {
throw new Error("Empty response body");
}

const mapTxData = txData.data.list.map(async (tx) => {
// Skip saving if a transaction with the same hash already exists
const existingTx = await txModel.findOne({ hash: tx.hash });
if (existingTx) {
return;
}
// Attempt to parse the response as JSON
let txData;
try {
txData = JSON.parse(responseText);
} catch (jsonError) {
console.error(`Failed to parse JSON response: ${jsonError}`);
throw new Error("Invalid JSON response");
}

// Check if txData and txData.data.list are not null
if (!txData || !txData.data || !txData.data.list) {
// Continue to the next API if this one doesn't have valid data
continue;
}

// Map and process each transaction in the response
const mapTxData = txData.data.list.map(async (tx) => {
// Skip saving if a transaction with the same hash already exists
const existingTx = await txModel.findOne({ hash: tx.hash });
if (existingTx) {
return;
}

const transactionsData = new txModel({
block_height: tx.block_height,
hash: tx.hash,
block_time: tx.block_time,
fee: tx.fee,
is_coinbase: tx.is_coinbase,
is_double_spend: tx.is_double_spend,
outputs_count: tx.outputs_count,
outputs_value: tx.outputs_value,
witness_hash: tx.witness_hash,
inputs: tx.inputs,
outputs: tx.outputs,
});
// Create a new transaction data object
const transactionsData = new txModel({
block_height: tx.block_height,
hash: tx.hash,
block_time: tx.block_time,
fee: tx.fee,
is_coinbase: tx.is_coinbase,
is_double_spend: tx.is_double_spend,
outputs_count: tx.outputs_count,
outputs_value: tx.outputs_value,
witness_hash: tx.witness_hash,
inputs: tx.inputs,
outputs: tx.outputs,
});

// Save the data
await transactionsData.save();
});
// Save the transaction data
await transactionsData.save();
});

// To ensure that the function waits for the save operations to complete before returning
await Promise.all(mapTxData);
// Ensure that the function waits for all save operations to complete before continuing
await Promise.all(mapTxData);
} catch (apiError) {
// Continue to the next API if this one fails
continue;
}
}
} catch (err) {
console.error(err);
// Handle errors here, you might want to log them or perform specific error-handling actions
}
};

Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ services:
MONGOOSE_URL: mongodb://admin:12345@db:27017
COSMOSHUB_REST_API: '[{"address": "http://xx.xx.xx.xx:1317"}, {"address": "https://xxxxxxx.com:443"}, {"address": "https://api-xxxxxx.network"}]'
COSMOSHUB_RPC: '[{"address": "http://xx.xx.xx.xx:26657"}, {"address": "https://xxxx.org"}, {"address": "https://rpc.xxxxxxxx.io"}]'
BITCOIN_REST_API: '[{"address": "https://chain.api.btc.com"}]'
PORT: 5001
REDIS_HOST: redis
REDIS_PORT: 6379
Expand Down

0 comments on commit 85ef752

Please sign in to comment.