|
|
|
var http = require('http');
|
|
|
|
var models = require('../models');
|
|
|
|
var rpcConfig = require('../config/config')['rpc'];
|
|
|
|
|
|
|
|
const {username, password, hostname, port} = rpcConfig;
|
|
|
|
|
|
|
|
function MakeRPCRequest(postData) {
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
var post_options = {
|
|
|
|
host: hostname,
|
|
|
|
port: port,
|
|
|
|
auth: `${username}:${password}`,
|
|
|
|
path: '/',
|
|
|
|
method: 'POST',
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
|
|
'Content-Length': Buffer.byteLength(postData)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
var post_req = http.request(post_options, function(res) {
|
|
|
|
res.setEncoding("utf8");
|
|
|
|
let body = "";
|
|
|
|
res.on("data", data => {
|
|
|
|
body += data;
|
|
|
|
});
|
|
|
|
res.on("end", () => {
|
|
|
|
resolve(body);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
post_req.write(postData);
|
|
|
|
post_req.end();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// async function saveTransaction(txid, blockHeight) {
|
|
|
|
async function saveTransaction(txid, blockHeight) {
|
|
|
|
const res_tx = await MakeRPCRequest(JSON.stringify({
|
|
|
|
method: 'getrawtransaction',
|
|
|
|
params: [txid, 1],
|
|
|
|
id: 1
|
|
|
|
}));
|
|
|
|
const tx = JSON.parse(res_tx)['result'];
|
|
|
|
if (tx === null) {
|
|
|
|
await models.Failure.create({
|
|
|
|
msg: `Transaction ${txid} fetching failed`,
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// const transaction = await models.Transaction.create({
|
|
|
|
// txid: tx.txid,
|
|
|
|
// BlockHeight: blockHeight,
|
|
|
|
// });
|
|
|
|
const transaction = {
|
|
|
|
txid: tx.txid,
|
|
|
|
BlockHeight: blockHeight,
|
|
|
|
vouts: [],
|
|
|
|
vins: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
// Loop over vouts
|
|
|
|
for (var i = 0; i < tx.vout.length; i++) {
|
|
|
|
const vout = tx.vout[i];
|
|
|
|
const m_vout = {
|
|
|
|
n: vout.n,
|
|
|
|
value: vout.value,
|
|
|
|
addresses: [],
|
|
|
|
direction: 1,
|
|
|
|
};
|
|
|
|
// Loop over addresses in vout
|
|
|
|
for (var y = 0; y < vout.scriptPubKey.addresses.length; y++) {
|
|
|
|
const address = vout.scriptPubKey.addresses[y];
|
|
|
|
m_vout.addresses.push(address);
|
|
|
|
}
|
|
|
|
transaction.vouts.push(m_vout); // TODO create
|
|
|
|
}
|
|
|
|
for (var i = 0; i < tx.vin.length; i++) {
|
|
|
|
const vin = tx.vin[i];
|
|
|
|
if (vin.txid) {
|
|
|
|
transaction.vins.push(vin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return transaction;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function createBlock(block) {
|
|
|
|
return models.sequelize.transaction().then(async (t) => {
|
|
|
|
try {
|
|
|
|
console.log(1)
|
|
|
|
const m_block = await models.Block.create(Object.assign({}, block, {transaction: t}));
|
|
|
|
for (var i = block.tx.length - 1; i >= 0; i--) {
|
|
|
|
tx = block.tx[i];
|
|
|
|
if (!tx) continue; // for genesis block
|
|
|
|
console.log(2)
|
|
|
|
const m_transaction = await models.Transaction.create({
|
|
|
|
txid: tx.txid,
|
|
|
|
BlockHeight: tx.BlockHeight,
|
|
|
|
transaction: t,
|
|
|
|
});
|
|
|
|
console.log(2.1, m_transaction.id, m_block.height)
|
|
|
|
await m_block.addTransaction(m_transaction, t);
|
|
|
|
console.log(2.2)
|
|
|
|
for (var y = tx.vouts.length - 1; y >= 0; y--) {
|
|
|
|
const vout = tx.vouts[y];
|
|
|
|
console.log(3)
|
|
|
|
const m_vout = await models.Vout.create({
|
|
|
|
n: vout.n,
|
|
|
|
value: vout.value,
|
|
|
|
direction: 1,
|
|
|
|
transaction: t,
|
|
|
|
});
|
|
|
|
console.log(4)
|
|
|
|
await m_transaction.addVouts(m_vout, {
|
|
|
|
through: {
|
|
|
|
direction: 1
|
|
|
|
},
|
|
|
|
transaction: t,
|
|
|
|
});
|
|
|
|
for (var z = vout.addresses.length - 1; z >= 0; z--) {
|
|
|
|
const address = vout.addresses[z];
|
|
|
|
console.log(5)
|
|
|
|
let m_address = await models.Address.findOne({
|
|
|
|
where: {
|
|
|
|
address,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
if (!m_address) {
|
|
|
|
console.log(6)
|
|
|
|
m_address = await models.Address.create({
|
|
|
|
address,
|
|
|
|
transaction: t,
|
|
|
|
}, t);
|
|
|
|
}
|
|
|
|
console.log(7)
|
|
|
|
await m_vout.addAddress(m_address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (var y = tx.vins.length - 1; y >= 0; y--) {
|
|
|
|
const vin = tx.vins[y];
|
|
|
|
if (vin.txid) {
|
|
|
|
console.log(8)
|
|
|
|
const vout = await models.Vout.findAll({
|
|
|
|
include: {
|
|
|
|
model: models.Transaction,
|
|
|
|
where: {
|
|
|
|
txid: vin.txid,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
where: {
|
|
|
|
n: vin.vout,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
if (vout) {
|
|
|
|
console.log(9)
|
|
|
|
await m_transaction.addVouts(vout[0], {
|
|
|
|
through: {
|
|
|
|
direction: 0,
|
|
|
|
},
|
|
|
|
transaction: t,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
throw('Couldnt find vout for VIN');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t.commit();
|
|
|
|
} catch (e) {
|
|
|
|
t.rollback();
|
|
|
|
console.log('===', e, 'error');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function syncNextBlock(syncedHeight) {
|
|
|
|
const height = syncedHeight + 1;
|
|
|
|
const res_hash = await MakeRPCRequest(JSON.stringify({
|
|
|
|
method: 'getblockhash',
|
|
|
|
params: [height],
|
|
|
|
id: 1
|
|
|
|
}));
|
|
|
|
const blockHash = JSON.parse(res_hash)['result'];
|
|
|
|
const res_block = await MakeRPCRequest(JSON.stringify({
|
|
|
|
method: 'getblock',
|
|
|
|
params: [blockHash],
|
|
|
|
id: 1
|
|
|
|
}));
|
|
|
|
const block = JSON.parse(res_block)['result'];
|
|
|
|
block.time = new Date(block.time * 1000);
|
|
|
|
|
|
|
|
const blockToCreate = Object.assign({}, block, {'tx': []});
|
|
|
|
for (var i = 0; i < block.tx.length; i++) {
|
|
|
|
tx = await saveTransaction(block.tx[i], block.height);
|
|
|
|
blockToCreate.tx.push(tx);
|
|
|
|
}
|
|
|
|
if (blockToCreate.height > 1) {
|
|
|
|
await models.Block.update({
|
|
|
|
nextblockhash: block.hash
|
|
|
|
},{
|
|
|
|
where: {
|
|
|
|
hash: block.previousblockhash
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
await createBlock(blockToCreate);
|
|
|
|
return height;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getCurrentHeight() {
|
|
|
|
const result = await MakeRPCRequest(JSON.stringify({
|
|
|
|
method: 'getblockcount',
|
|
|
|
params: [],
|
|
|
|
id: 1
|
|
|
|
}));
|
|
|
|
|
|
|
|
return JSON.parse(result)['result'];
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getSyncedHeight() {
|
|
|
|
const result = await models.Block.findOne({
|
|
|
|
attributes: ['height'],
|
|
|
|
order: [['height', 'DESC']],
|
|
|
|
limit: 1
|
|
|
|
});
|
|
|
|
const height = result ? result.height : -1;
|
|
|
|
return height;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function syncBlockchain() {
|
|
|
|
let syncedHeight = await getSyncedHeight();
|
|
|
|
console.log('\x1b[36m%s\x1b[0m', 'syncedHeight is', syncedHeight);
|
|
|
|
|
|
|
|
let currentHeight = await getCurrentHeight();
|
|
|
|
console.log('\x1b[36m%s\x1b[0m', 'currentHeight is', currentHeight);
|
|
|
|
try {
|
|
|
|
while (syncedHeight < currentHeight) {
|
|
|
|
syncedHeight = await syncNextBlock(syncedHeight);
|
|
|
|
// process.stdout.write(`Synced ${syncedHeight} out of ${currentHeight}\r`);
|
|
|
|
console.log(`Synced ${syncedHeight} out of ${currentHeight}`);
|
|
|
|
if (syncedHeight >= 125)
|
|
|
|
process.exit(0)
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.log('+====', e);
|
|
|
|
process.exit(0);
|
|
|
|
}
|
|
|
|
process.stdout.write('\nDone\n');
|
|
|
|
process.exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
syncBlockchain()
|
|
|
|
.then((res) => process.exit(0));
|