turtlecoin-wallet-backend
Version:
[](https://travis-ci.org/turtlecoin/turtlecoin-wallet-backend-js)
266 lines (265 loc) • 10.5 kB
JavaScript
"use strict";
// Copyright (c) 2018, Zpalmtree
//
// Please see the included LICENSE file for more information.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const _ = require("lodash");
const events_1 = require("events");
const Config_1 = require("./Config");
const Logger_1 = require("./Logger");
const Types_1 = require("./Types");
const ValidateParameters_1 = require("./ValidateParameters");
const WalletError_1 = require("./WalletError");
const Assert_1 = require("./Assert");
/* REEEEE ADD TYPES */
const TurtleCoind = require('turtlecoin-rpc').TurtleCoind;
/**
* @deprecated This Class will be removed in v4.0.0. Please update your code
* to instead use the [[Daemon]] class. This supports both ConventionalDaemon's,
* BlockchainCacheApi's, and http/https, all automatically.
*/
class ConventionalDaemon extends events_1.EventEmitter {
constructor(daemonHost, daemonPort) {
super();
/**
* The address node fees will go to
*/
this.feeAddress = '';
/**
* The amount of the node fee in atomic units
*/
this.feeAmount = 0;
/**
* The amount of blocks the daemon we're connected to has
*/
this.localDaemonBlockCount = 0;
/**
* The amount of blocks the network has
*/
this.networkBlockCount = 0;
/**
* The amount of peers we have, incoming+outgoing
*/
this.peerCount = 0;
/**
* The hashrate of the last known local block
*/
this.lastKnownHashrate = 0;
this.config = new Config_1.Config();
console.warn('This Class will be removed in v4.0.0. Please update your code ' +
'to instead use the Daemon class. This supports both ' +
'ConventionalDaemon\'s, BlockchainCacheApi\'s, and http/https, ' +
'all automatically.');
Assert_1.assertString(daemonHost, 'daemonHost');
Assert_1.assertNumber(daemonPort, 'daemonPort');
this.daemonHost = daemonHost;
this.daemonPort = daemonPort;
this.daemon = new TurtleCoind({
host: daemonHost,
port: daemonPort,
ssl: false,
timeout: this.config.requestTimeout,
});
}
updateConfig(config) {
this.config = Config_1.MergeConfig(config);
}
/**
* Get the amount of blocks the network has
*/
getNetworkBlockCount() {
return this.networkBlockCount;
}
/**
* Get the amount of blocks the daemon we're connected to has
*/
getLocalDaemonBlockCount() {
return this.localDaemonBlockCount;
}
/**
* Initialize the daemon and the fee info
*/
init() {
return __awaiter(this, void 0, void 0, function* () {
/* Note - if one promise throws, the other will be cancelled */
yield Promise.all([this.updateDaemonInfo(), this.updateFeeInfo()]);
});
}
/**
* Update the daemon info
*/
updateDaemonInfo() {
return __awaiter(this, void 0, void 0, function* () {
let info;
try {
info = yield this.daemon.info();
}
catch (err) {
Logger_1.logger.log('Failed to update daemon info: ' + err.toString(), Logger_1.LogLevel.INFO, [Logger_1.LogCategory.DAEMON]);
return;
}
this.localDaemonBlockCount = info.height;
this.networkBlockCount = info.network_height;
/* Height returned is one more than the current height - but we
don't want to overflow is the height returned is zero */
if (this.networkBlockCount !== 0) {
this.networkBlockCount--;
}
this.peerCount = info.incoming_connections_count + info.outgoing_connections_count;
this.lastKnownHashrate = info.difficulty / this.config.blockTargetTime;
});
}
/**
* Get the node fee and address
*/
nodeFee() {
return [this.feeAddress, this.feeAmount];
}
/**
* @param blockHashCheckpoints Hashes of the last known blocks. Later
* blocks (higher block height) should be
* ordered at the front of the array.
*
* @param startHeight Height to start taking blocks from
* @param startTimestamp Block timestamp to start taking blocks from
*
* Gets blocks from the daemon. Blocks are returned starting from the last
* known block hash (if higher than the startHeight/startTimestamp)
*/
getWalletSyncData(blockHashCheckpoints, startHeight, startTimestamp, blockCount) {
return __awaiter(this, void 0, void 0, function* () {
const { items, topBlock } = yield this.daemon.getWalletSyncData({
blockCount,
blockHashCheckpoints,
skipCoinbaseTransactions: !this.config.scanCoinbaseTransactions,
startHeight,
startTimestamp,
});
return [items.map(Types_1.Block.fromJSON), topBlock];
});
}
/**
* @returns Returns a mapping of transaction hashes to global indexes
*
* Get global indexes for the transactions in the range
* [startHeight, endHeight]
*/
getGlobalIndexesForRange(startHeight, endHeight) {
return __awaiter(this, void 0, void 0, function* () {
const data = yield this.daemon.getGlobalIndexesForRange({
endHeight,
startHeight,
});
const indexes = new Map();
for (const index of data) {
indexes.set(index.key, index.value);
}
return indexes;
});
}
getCancelledTransactions(transactionHashes) {
return __awaiter(this, void 0, void 0, function* () {
const data = yield this.daemon.getTransactionsStatus({
transactionHashes,
});
return data.transactionsUnknown || [];
});
}
/**
* Gets random outputs for the given amounts. requestedOuts per. Usually mixin+1.
*
* @returns Returns an array of amounts to global indexes and keys. There
* should be requestedOuts indexes if the daemon fully fulfilled
* our request.
*/
getRandomOutputsByAmount(amounts, requestedOuts) {
return __awaiter(this, void 0, void 0, function* () {
let data;
try {
data = yield this.daemon.getRandomOutputs({
amounts: amounts,
mixin: requestedOuts,
});
}
catch (err) {
Logger_1.logger.log('Failed to get random outs: ' + err.toString(), Logger_1.LogLevel.ERROR, [Logger_1.LogCategory.TRANSACTIONS, Logger_1.LogCategory.DAEMON]);
return [];
}
/* Most likely daemon is busy */
if (data.status !== 'OK') {
Logger_1.logger.log('Failed to get random outputs, got status ' + data.status + ' from daemon.', Logger_1.LogLevel.ERROR, [Logger_1.LogCategory.TRANSACTIONS, Logger_1.LogCategory.DAEMON]);
return [];
}
const outputs = [];
for (const output of data.outs) {
const indexes = [];
for (const outs of output.outs) {
indexes.push([outs.global_amount_index, outs.out_key]);
}
/* Sort by output index to make it hard to determine real one */
outputs.push([output.amount, _.sortBy(indexes, ([index, key]) => index)]);
}
return outputs;
});
}
sendTransaction(rawTransaction) {
return __awaiter(this, void 0, void 0, function* () {
const result = yield this.daemon.sendRawTransaction({
tx: rawTransaction,
});
return result.status === 'OK';
});
}
getConnectionInfo() {
return {
host: this.daemonHost,
port: this.daemonPort,
daemonType: Types_1.DaemonType.ConventionalDaemon,
daemonTypeDetermined: true,
ssl: false,
sslDetermined: true,
};
}
/**
* Update the fee address and amount
*/
updateFeeInfo() {
return __awaiter(this, void 0, void 0, function* () {
let feeInfo;
try {
feeInfo = yield this.daemon.fee();
}
catch (err) {
Logger_1.logger.log('Failed to update fee info: ' + err.toString(), Logger_1.LogLevel.INFO, [Logger_1.LogCategory.DAEMON]);
return;
}
/* Most likely daemon is busy */
if (feeInfo.status !== 'OK') {
Logger_1.logger.log('Failed to update fee info, got status ' + feeInfo.status + ' from daemon.', Logger_1.LogLevel.INFO, [Logger_1.LogCategory.DAEMON]);
return;
}
if (feeInfo.address === '') {
return;
}
const integratedAddressesAllowed = false;
const err = ValidateParameters_1.validateAddresses(new Array(feeInfo.address), integratedAddressesAllowed, this.config).errorCode;
if (err !== WalletError_1.WalletErrorCode.SUCCESS) {
Logger_1.logger.log('Failed to validate address from daemon fee info: ' + err.toString(), Logger_1.LogLevel.WARNING, [Logger_1.LogCategory.DAEMON]);
return;
}
if (feeInfo.amount > 0) {
this.feeAddress = feeInfo.address;
this.feeAmount = feeInfo.amount;
}
});
}
}
exports.ConventionalDaemon = ConventionalDaemon;