bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
311 lines • 14.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bson_1 = require("bson");
const chai_1 = require("chai");
const sinon = __importStar(require("sinon"));
const request_1 = __importDefault(require("request"));
const walletAddress_1 = require("../../../src/models/walletAddress");
const csp_1 = require("../../../src/modules/ripple/api/csp");
const block_1 = require("../../../src/modules/ripple/models/block");
const transaction_1 = require("../../../src/modules/ripple/models/transaction");
const rippletxs_fixture_1 = require("../../fixtures/rippletxs.fixture");
const helpers_1 = require("../../helpers");
const integration_1 = require("../../helpers/integration");
describe('Ripple Api', function () {
const suite = this;
const network = 'testnet';
this.timeout(30000);
before(integration_1.intBeforeHelper);
after(async () => {
await (0, integration_1.intAfterHelper)(suite);
const client = await csp_1.XRP.getClient(network);
client.rpc.disconnect();
});
beforeEach(async () => {
await (0, helpers_1.resetDatabase)();
});
it('should be able to get the ledger', async () => {
const client = await csp_1.XRP.getClient(network);
const { ledger } = await client.getBlock();
(0, chai_1.expect)(ledger).to.exist;
(0, chai_1.expect)(ledger.ledger_hash).to.exist;
});
it('should be able to get local tip', async () => {
const chain = 'XRP';
await block_1.XrpBlockStorage.collection.insertOne({
chain,
network,
height: 5,
hash: '528f01c17829622ed6a4af51b3b3f6c062f304fa60e66499c9cbb8622c8407f7',
time: new Date(1526326784),
timeNormalized: new Date(1526326784),
transactionCount: 1,
reward: 50,
previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
nextBlockHash: '',
size: 264,
processed: true
});
const tip = await csp_1.XRP.getLocalTip({ chain, network });
(0, chai_1.expect)(tip).to.exist;
(0, chai_1.expect)(tip.hash).to.exist;
(0, chai_1.expect)(tip.hash).to.eq('528f01c17829622ed6a4af51b3b3f6c062f304fa60e66499c9cbb8622c8407f7');
});
for (const tx of rippletxs_fixture_1.RippleTxs) {
it('should transform a ripple rpc response into a bitcore transaction: ' + tx.hash, async () => {
const bitcoreTx = await csp_1.XRP.transform(tx, 'testnet');
(0, chai_1.expect)(bitcoreTx).to.have.property('chain');
(0, chai_1.expect)(tx.Account).to.eq(bitcoreTx.from);
(0, chai_1.expect)(tx.ledger_index).to.eq(bitcoreTx.blockHeight);
(0, chai_1.expect)(tx.Fee).to.eq((bitcoreTx.fee).toString());
const nodes = tx.meta.AffectedNodes.filter(node => 'ModifiedNode' in node && node.ModifiedNode.FinalFields?.Account == tx.Account);
const sentVal = nodes.reduce((acc, node) => acc += 'ModifiedNode' in node ? Number(node.ModifiedNode.FinalFields?.Balance) - Number(node.ModifiedNode.PreviousFields?.Balance) : 0, 0);
(0, chai_1.expect)(sentVal).to.be.lt(0);
if (tx.meta.delivered_amount) {
const modNodes = tx.meta.AffectedNodes.filter(n => 'ModifiedNode' in n && n.ModifiedNode.FinalFields?.Account === bitcoreTx.to);
const createNodes = tx.meta.AffectedNodes.filter(n => 'CreatedNode' in n && n.CreatedNode.NewFields.Account === bitcoreTx.to);
(0, chai_1.expect)(modNodes.length + createNodes.length > 0).to.equal(true);
(0, chai_1.expect)(tx.meta.delivered_amount).to.eq(bitcoreTx.value.toString());
let receivedVal = modNodes.reduce((acc, node) => acc += 'ModifiedNode' in node ? Number(node.ModifiedNode.FinalFields?.Balance) - Number(node.ModifiedNode.PreviousFields?.Balance) : 0, 0);
receivedVal += createNodes.reduce((acc, node) => acc += 'CreatedNode' in node ? Number(node.CreatedNode.NewFields.Balance) : 0, 0);
(0, chai_1.expect)(receivedVal).to.be.gt(0);
}
});
}
it('should tag txs from a wallet', async () => {
const chain = 'XRP';
const network = 'testnet';
const wallet = new bson_1.ObjectId();
const address = 'rN33DVnneYUUgTmcxXnXvgAL1BECuLZ8pm';
await walletAddress_1.WalletAddressStorage.collection.insertOne({
chain,
network,
wallet,
address,
processed: true
});
for (const tx of rippletxs_fixture_1.RippleTxs) {
const bitcoreTx = (await csp_1.XRP.transform(tx, network));
const bitcoreCoins = csp_1.XRP.transformToCoins(tx, network);
const { transaction, coins } = await csp_1.XRP.tag(chain, network, bitcoreTx, bitcoreCoins);
(0, chai_1.expect)(transaction.wallets.length).eq(1);
(0, chai_1.expect)(transaction.wallets[0].equals(wallet));
let hasACoin = false;
for (const coin of coins) {
if (coin.address == address) {
hasACoin = true;
(0, chai_1.expect)(coin.wallets.length).eq(1);
(0, chai_1.expect)(coin.wallets[0].equals(wallet));
}
}
(0, chai_1.expect)(hasACoin).eq(true);
}
});
it('should get sequence', async () => {
const sequence = await csp_1.XRP.getAccountNonce(network, 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
(0, chai_1.expect)(sequence).to.exist;
(0, chai_1.expect)(sequence).to.be.a('number');
});
it('should get flags', async () => {
const flags = await csp_1.XRP.getAccountFlags(network, 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
(0, chai_1.expect)(flags).to.exist;
(0, chai_1.expect)(flags).to.haveOwnProperty('requireDestinationTag');
});
it('should save tagged transactions to the database', async () => {
const chain = 'XRP';
const network = 'testnet';
const wallet = new bson_1.ObjectId();
const address = 'rN33DVnneYUUgTmcxXnXvgAL1BECuLZ8pm';
await walletAddress_1.WalletAddressStorage.collection.insertOne({
chain,
network,
wallet,
address,
processed: true
});
const blockTxs = new Array();
const blockCoins = new Array();
for (const tx of rippletxs_fixture_1.RippleTxs) {
const bitcoreTx = csp_1.XRP.transform(tx, network);
const bitcoreCoins = csp_1.XRP.transformToCoins(tx, network);
const { transaction, coins } = await csp_1.XRP.tag(chain, network, bitcoreTx, bitcoreCoins);
blockTxs.push(transaction);
blockCoins.push(...coins);
}
await transaction_1.XrpTransactionStorage.batchImport({
txs: blockTxs,
coins: blockCoins,
chain,
network,
initialSyncComplete: false
});
const walletTxs = await transaction_1.XrpTransactionStorage.collection.find({ chain, network, wallets: wallet }).toArray();
(0, chai_1.expect)(walletTxs.length).eq(rippletxs_fixture_1.RippleTxs.length);
});
describe('getBlockBeforeTime', () => {
// For these tests, we simulate ledgers with a 1 second interval, so the close_time is xrpEpoch + (ledger_index * 1000)
const xrpEpoch = new Date('2000-01-01T00:00:00.000Z');
const getCloseTime = (ledgerIndex) => new Date(xrpEpoch.getTime() + (ledgerIndex * 1000));
const sandbox = sinon.createSandbox();
let requestStub;
let validBody = {
result: {
status: 'success',
ledger: {
ledger_hash: 'abc123',
ledger_index: 12,
parent_hash: 'abc122',
close_time: getCloseTime(12).getTime() / 1000,
close_time_human: getCloseTime(12).toUTCString(),
closed: true
}
}
};
let invalidBody = { result: { status: 'error' } };
let time;
const _configBak = csp_1.XRP.config;
before(async () => {
await block_1.XrpBlockStorage.collection.deleteMany({});
});
beforeEach(async () => {
requestStub = sandbox.stub(request_1.default, 'post');
await block_1.XrpBlockStorage.collection.insertMany([{
chain: 'XRP',
network: 'testnet',
height: 12,
timeNormalized: getCloseTime(12),
hash: 'abc123',
time: getCloseTime(12),
transactionCount: 1,
reward: 50,
previousBlockHash: 'abc122',
nextBlockHash: 'abc124',
size: 264,
processed: true
}, {
chain: 'XRP',
network: 'testnet',
height: 13,
timeNormalized: getCloseTime(13),
hash: 'abc124',
time: getCloseTime(13),
transactionCount: 1,
reward: 50,
previousBlockHash: 'abc123',
nextBlockHash: '',
size: 264,
processed: true
}]);
time = getCloseTime(12).toISOString();
});
afterEach(async () => {
sandbox.restore();
await block_1.XrpBlockStorage.collection.deleteMany({});
csp_1.XRP.config = _configBak;
});
it('should return block', async () => {
requestStub.callsFake(function (req, cb) {
validBody.result.ledger.ledger_index = req.body.params[0].ledger_index;
validBody.result.ledger.close_time = getCloseTime(req.body.params[0].ledger_index).getTime() / 1000;
validBody.result.ledger.close_time_human = new Date(validBody.result.ledger.close_time * 1000).toUTCString();
return cb(null, { body: validBody });
});
const res = await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time });
(0, chai_1.expect)(res).to.deep.equal({
chain: 'XRP',
network: 'testnet',
hash: 'abc123',
height: 12,
previousBlockHash: 'abc122',
processed: true,
time: getCloseTime(12),
timeNormalized: getCloseTime(12),
reward: 0,
size: 0,
transactionCount: 0,
nextBlockHash: ''
});
});
it('should respond null if date is too early', async () => {
const res = await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time: getCloseTime(-1).toISOString() });
(0, chai_1.expect)(res).to.be.null;
});
it('should resolve on empty response', async () => {
requestStub.callsArgWith(1, null, null);
const res = await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time });
(0, chai_1.expect)(res).to.be.null;
});
it('should throw on invalid time', async () => {
try {
await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time: 'not-a-time' });
throw new Error('should have thrown');
}
catch (err) {
(0, chai_1.expect)(err.message).to.equal('Invalid time value');
}
});
it('should throw on response error', async () => {
requestStub.callsArgWith(1, 'Unresponsive server', validBody);
try {
await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time });
throw new Error('should have thrown');
}
catch (err) {
(0, chai_1.expect)(err).to.equal('Unresponsive server');
}
});
it('should return null on error response', async () => {
requestStub.callsArgWith(1, null, invalidBody);
const res = await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time });
(0, chai_1.expect)(res).to.be.null;
});
it('should throw on mis-configuration', async () => {
requestStub.callsArgWith(1, null, validBody);
csp_1.XRP.config = {};
try {
await csp_1.XRP.getBlockBeforeTime({ chain: 'XRP', network: 'testnet', time });
throw new Error('should have thrown');
}
catch (err) {
(0, chai_1.expect)(err.message).to.equal('Cannot read properties of undefined (reading \'provider\')');
}
});
});
});
//# sourceMappingURL=csp.spec.js.map