Files

294 lines
8.8 KiB
JavaScript
Raw Permalink Normal View History

2020-07-27 11:44:26 +02:00
/*
* This file is part of the Flowee project
2021-02-17 16:51:06 +01:00
* Copyright (C) 2019-2021 Tom Zander <tom@flowee.org>
2020-07-27 11:44:26 +02:00
* Copyright (C) 2020 James Zuccon <zuccon@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2020-07-16 23:23:57 +02:00
const Message = require('./message');
/**
* Blockchain information service.
2020-07-18 14:36:37 +02:00
* @param {Flowee} instance Instance of Flowee
2020-07-16 23:23:57 +02:00
*/
class BlockchainService {
constructor(network) {
this.network = network;
}
2020-07-18 14:36:37 +02:00
/**
* Get Blockchain Information
* @example
* let blockChainInfo = await flowee.getBlockChainInfo();
*/
async getBlockChainInfo() {
let reply = await this.network.sendMessage(new Message(1, 0));
return {
difficulty: reply[64],
medianTime: reply[65],
chainWork: reply["66str"],
chain: reply[67],
blocks: reply[68],
headers: reply[69],
bestBlockHash: reply[70],
bestBlockId: reply["70str"],
};
}
/**
* Get the best block hash available
* @returns The an object with the block hash as string and buffer.
* @example
* let bestBlockHash = await flowee.getBestBlockHash();
*/
async getBestBlockHash() {
let reply = await this.network.sendMessage(new Message(1, 2));
return {
blockId: reply["1str"],
blockHash: reply[1],
}
}
2020-07-16 23:23:57 +02:00
/**
* Get Block height
* @returns {number} The block height
* @example
2020-07-18 14:36:37 +02:00
* let blockCount = await flowee.getBlockCount();
2020-07-16 23:23:57 +02:00
*/
async getBlockCount() {
let reply = await this.network.sendMessage(new Message(1, 10));
2021-02-15 12:24:26 +01:00
return reply[7]; // Block Height (is equal to count since genesis is height=0)
2020-07-18 14:36:37 +02:00
}
/**
* Get Block
* @param param (Number or Hash) The block height
* @returns {number} Object containing details about block
* @example
* let blockHeader = await flowee.getBlock(500000);
*/
2021-04-19 18:46:53 +02:00
async getBlock(param, filters) {
2020-07-18 14:36:37 +02:00
let message = new Message(1, 4);
message.body[44] = true; // Include_OffsetInBlock
message.body[45] = false; // Include_FullTxData
if (typeof param === 'number') {
message.body[7] = param;
} else if (param * 1 == parseInt(param)) { // is really a number
message.body[7] = parseInt(param);
} else if (typeof param === 'string') {
message.body[5] = Buffer.from(param, 'hex').reverse();
if (message.body[5].length != 32)
throw "Invalid argument, not blockId";
} else if (typeof param == 'undefined') {
throw "Missing argument";
} else {
message.body[5] = param;
}
2021-04-19 18:46:53 +02:00
applyFilters(message, filers);
2020-07-18 14:36:37 +02:00
let reply = await this.network.sendMessage(message);
let answer = {
blockId: reply["5str"],
blockHash: reply[5],
blockHeight: reply[7],
transactions: []
}
let tx = null;
for (let prop of reply.body) {
if (prop.key == 0) {
if (tx != null)
answer.transactions.push(tx);
tx = null;
}
else if (prop.key == 8) {
tx = {};
tx.offset = prop.value;
}
}
return answer;
2020-07-16 23:23:57 +02:00
}
2020-07-27 11:44:26 +02:00
2021-02-17 16:51:06 +01:00
async getTransaction(blockHeight, offsetInBlock, filters) {
if (blockHeight * 1 == parseInt(blockHeight)) { // is really a number
blockHeight = blockHeight * 1;
}
if (typeof blockHeight === 'string') { // assume this is a txid
if (typeof offsetInBlock === 'object') { // move arg
filters = offsetInBlock;
}
var search = await this.network.search({
jobs: [{
value: blockHeight,
type: this.network.Job.FetchTx,
txFilter: filters
}]});
return search.transactions;
}
if (offsetInBlock * 1 == parseInt(offsetInBlock)) { // is really a number
offsetInBlock = offsetInBlock * 1;
}
if (typeof offsetInBlock !== 'number')
throw "OffsetInBlock should be a number";
if (typeof filters[0] !== 'number') {
// set some defaults
filters[0] = this.network.IncludeOffsetInBlock;
filters[1] = this.network.IncludeOutputs;
filters[2] = this.network.IncludeInputs;
}
let message = new Message(1, 12);
message.body[7] = blockHeight;
message.body[8] = offsetInBlock;
2021-04-19 18:46:53 +02:00
applyFilters(message, filers);
2021-02-17 16:51:06 +01:00
let reply = await this.network.sendMessage(message);
let outputs = [];
let inputs = [];
let currentInput = -1;
let currentOutput = 0;
for (let row of reply.body) {
switch (row.key) {
case '6': // amount
outputs[currentOutput].value = row.value;
break;
case '2': // p2pkh address
outputs[currentOutput].p2pkhAddress = row.value;
break;
case '9': // BitcoinScriptHashed
outputs[currentOutput].scriptHashed = row.value;
break;
case '20': // Tx_IN_TXID
inputs[++currentInput] = { prevTxid: row.value };
break;
case '21': // Tx_IN_OutIndex
inputs[currentInput].outIndex = row.value;
break;
case '22': // Tx_InputScript
inputs[currentInput].script = row.value;
break;
case '23': // Tx_OutputScript
outputs[currentOutput].script = row.value;
break;
case '24': // Tx_Out_Index
currentOutput = row.value;
outputs[currentOutput] = {}
break;
}
}
return {
raw: reply[1],
txid: reply[2],
offsetInBlock: reply[8],
inputs: inputs,
outputs: outputs,
};
}
2021-04-19 18:46:53 +02:00
/// For block and transaction fetching messages, apply the filters.
applyFilters(message, filters) {
for (let filter of filters) {
switch (filter) {
case this.network.IncludeTxid:
message.body[43] = true; break;
case this.network.IncludeOffsetInBlock:
message.body[44] = true; break;
case this.network.IncludeFullTxData:
message.body[45] = true; break;
case this.network.IncludeInputs:
message.body[46] = true; break;
case this.network.IncludeOutputAmounts:
message.body[47] = true; break;
case this.network.IncludeOutputScripts:
message.body[48] = true; break;
case this.network.IncludeOutputs:
message.body[49] = true; break;
case this.network.IncludeOutputAddresses:
message.body[50] = true; break;
case this.network.IncludeOutputScriptHash:
message.body[51] = true; break;
}
}
}
2020-07-27 11:44:26 +02:00
async getRawTransaction(blockHeight, offsetInBlock) {
if (blockHeight * 1 == parseInt(blockHeight)) { // is really a number
blockHeight = blockHeight * 1;
}
if (typeof blockHeight === 'string') {
// assume this is a txid
var search = await this.network.search({
jobs: [{
value: blockHeight,
type: this.network.Job.FetchTx,
txFilter: [ this.network.IncludeFullTxData ]
}]});
return search.transactions[0].fullTxData;
}
if (typeof blockHeight === 'number' && typeof offsetInBlock === 'number') {
let message = new Message(1, 12);
message.body[7] = blockHeight;
message.body[8] = offsetInBlock;
message.body[45] = true; // Include_FullTxData
let reply = await this.network.sendMessage(message);
return reply["1"];
}
else
throw "Missing arguments";
}
2020-07-27 12:00:42 +02:00
async getBlockHeader(blockHeight) {
if (blockHeight * 1 == parseInt(blockHeight)) { // is really a number
blockHeight = blockHeight * 1;
}
let message = new Message(1, 8);
if (typeof blockHeight === 'number') {
message.body[7] = blockHeight;
} else if (typeof blockHeight === 'string') {
message.body[5] = Buffer.from(param, 'hex').reverse();
if (message.body[5].length != 32)
throw "Invalid argument, not blockId";
}
else
throw "Missing arguments";
let reply = await this.network.sendMessage(message);
return {
blockId: reply["5str"],
blockHash: reply[5],
confirmations: reply[72],
blockHeight: reply[7],
version: reply[62],
merkleRoot: reply[73],
merkleRootHex: reply["73str"],
time: reply[63],
medianTime: reply[65],
nonce: reply[74],
bits: reply[75],
difficulty: reply[64],
prevBlockHash: reply["76str"],
nextBlockHash: reply["77str"]
};
}
2020-07-16 23:23:57 +02:00
}
module.exports = BlockchainService;