212 lines
8.1 KiB
JavaScript
212 lines
8.1 KiB
JavaScript
if (process.argv.length <= 2) {
|
|
console.log("Fetch interactions of address\nUsage node TODO.js [address]");
|
|
process.exit();
|
|
}
|
|
const FloweeServices = require('../');
|
|
var Flowee = new FloweeServices();
|
|
|
|
function printDate(timeInSeconds) {
|
|
let date = new Date(timeInSeconds * 1000); // JS wants milliseconds.
|
|
var answer = date.getUTCDate() + "/" + (date.getUTCMonth() + 1);
|
|
while (answer.length < 5)
|
|
answer = " " + answer;
|
|
|
|
// JS misses 'getUTCYear' :(
|
|
let y = date.getUTCFullYear() - 2000;
|
|
answer += "/" + y;
|
|
|
|
return answer;
|
|
}
|
|
|
|
function listInteractions(address) {
|
|
// search request passes an object literal
|
|
var promise = Flowee.search({
|
|
jobs: [
|
|
{ // Get all transactions based on the following address
|
|
value: address,
|
|
type: Flowee.Job.LookupByAddress,
|
|
}
|
|
],
|
|
|
|
// don't fetch items twice, remember which ones we created jobs for
|
|
requests: {
|
|
headers: {},
|
|
tx: {}
|
|
},
|
|
|
|
// private method to avoid fetching blockheaders multiple times.
|
|
fetchHeader: function(blockHeight) {
|
|
if (typeof this.requests.headers[`${blockHeight}`] === 'undefined') {
|
|
this.requests.headers[`${blockHeight}`] = true;
|
|
this.addJob({
|
|
type: Flowee.Job.FetchBlockHeader,
|
|
value: blockHeight,
|
|
});
|
|
}
|
|
},
|
|
|
|
fetchTxOutputs: function(txid) {
|
|
if (typeof this.requests.tx[`${txid}`] === 'undefined') {
|
|
this.requests.tx[`${txid}`] = true;
|
|
this.addJob({
|
|
type: Flowee.Job.FetchTx,
|
|
value: txid,
|
|
txFilter: [
|
|
Flowee.IncludeOutputAmounts,
|
|
Flowee.IncludeOutputAddresses
|
|
]
|
|
});
|
|
}
|
|
},
|
|
|
|
onAddressUsedInOutput: function(blockHeight, offsetInBlock, outIndex) {
|
|
this.fetchHeader(blockHeight);
|
|
|
|
this.addJob({
|
|
type: Flowee.Job.FetchTx,
|
|
value: blockHeight,
|
|
value2: offsetInBlock,
|
|
txFilter: [
|
|
Flowee.IncludeTxid,
|
|
Flowee.IncludeOutputAmounts,
|
|
Flowee.IncludeOutputAddresses,
|
|
Flowee.IncludeInputs
|
|
],
|
|
|
|
// the next one is for myself, not the framework.
|
|
outIndex: outIndex
|
|
});
|
|
},
|
|
|
|
onTxAdded: function(transaction) {
|
|
let jobId = transaction.jobId;
|
|
let job = this.jobs[jobId];
|
|
// If outIndex was defined this is one of the tx's we got from the
|
|
// address-search because we added it in onAddressUsedInOutput()
|
|
if (typeof job.outIndex !== 'undefined') {
|
|
transaction.isMain = true;
|
|
transaction.outIndex = job.outIndex;
|
|
for (let i = 0; i < transaction.inputs.length; ++i) {
|
|
if (i == 6) break; // no need to show more inputs.
|
|
// input is really just a link to another Tx, fetch its outputs.
|
|
this.fetchTxOutputs(transaction.inputs[i].previousTxid);
|
|
}
|
|
for (let i = 0; i < transaction.outputs.length; ++i) {
|
|
if (i >= 6) {
|
|
if (job.outIndex >= i)
|
|
i = job.outIndex;
|
|
else
|
|
break;
|
|
}
|
|
this.addJob({
|
|
type: Flowee.Job.LookupSpentTx,
|
|
value: transaction.txid,
|
|
value2: i
|
|
});
|
|
}
|
|
} else {
|
|
// anything not from the address search should be made easy to find.
|
|
this[`${transaction.txid}`] = transaction;
|
|
this.fetchHeader(transaction.blockHeight);
|
|
}
|
|
},
|
|
|
|
onSpentOutputResolved: function(jobId, blockHeight, offsetInBlock) {
|
|
if (blockHeight != -1) { // it was spent.
|
|
let job = this.jobs[jobId];
|
|
this[`out${job.value}${job.value2}`] = blockHeight;
|
|
this.fetchHeader(blockHeight);
|
|
}
|
|
},
|
|
|
|
onFinished: function() {
|
|
console.log("Transactions that interacted with address: " + address);
|
|
|
|
for (num in this.transactions) {
|
|
let tx = this.transactions[num];
|
|
if (tx.isMain) {
|
|
let header = this[`block${tx.blockHeight}`];
|
|
console.log();
|
|
console.log(printDate(header.time) + " (block: " + tx.blockHeight
|
|
+ ") txid: " + tx.txid);
|
|
|
|
for (let i = 0; i < tx.inputs.length; ++i) {
|
|
if (i == 6) {
|
|
console.log(" " + (tx.inputs.length - i)
|
|
+ " more inputs not shown");
|
|
break;
|
|
}
|
|
if (tx.isCoinbase) {
|
|
let header = this[`block${tx.blockHeight}`];
|
|
let amount = 0;
|
|
for (let o = 0; o < tx.outputs.length; ++o) {
|
|
amount += tx.outputs[o].amount;
|
|
}
|
|
console.log(printDate(header.time)
|
|
+ " " + amount / 100000000 + " BCH [coinbase]");
|
|
break;
|
|
}
|
|
else {
|
|
// input is really just a link.
|
|
let prev = this[`${tx.inputs[i].previousTxid}`];
|
|
let prevOut = prev.outputs[tx.inputs[i].outputIndex];
|
|
|
|
let header = this[`block${prev.blockHeight}`];
|
|
console.log(printDate(header.time)
|
|
+ " " + prevOut.amount / 100000000 + " BCH => "
|
|
+ prevOut.address);
|
|
}
|
|
}
|
|
for (let i = 0; i < tx.outputs.length; ++i) {
|
|
if (i >= 6) {
|
|
if (tx.outIndex > i) {
|
|
console.log(" skipping " + (tx.outIndex - i) + " outputs");
|
|
i = tx.outIndex;
|
|
}
|
|
else if (tx.outIndex < i) {
|
|
console.log(" " + (tx.outputs.length - i)
|
|
+ " more outputs not shown");
|
|
break;
|
|
}
|
|
}
|
|
let str = tx.outIndex === i ? "\t* " : "\t ";
|
|
let spentHeight = this[`out${tx.txid}${i}`];
|
|
if (spentHeight) {
|
|
let header = this[`block${spentHeight}`];
|
|
var spent = printDate(header.time);
|
|
} else {
|
|
var spent = "unspent";
|
|
}
|
|
console.log(str + tx.outputs[i].amount / 100000000 + " BCH => "
|
|
+ tx.outputs[i].address + " (spent: " + spent +")");
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
console.log("Address balance fetched: " + address);
|
|
if (this.total != 0 || this.outputs != 0) {
|
|
console.log(" UTXOs: " + this.unspent + "/" + this.outputs);
|
|
console.log(" Balance: " + this.total / 100000000);
|
|
} else {
|
|
console.log(" empty");
|
|
}
|
|
*/
|
|
}
|
|
});
|
|
return promise;
|
|
}
|
|
|
|
Flowee.connect().then(async function() {
|
|
try {
|
|
for (arg in process.argv) {
|
|
if (arg > 1) { // nodejs, skip first two args.
|
|
let result = await listInteractions(process.argv[arg])
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.log("Error detected: " + error);
|
|
}
|
|
process.exit();
|
|
})
|
|
|