From 5cb63a258c477980ae82bcc7cc6123d2d591168c Mon Sep 17 00:00:00 2001 From: Ivan Tivonenko Date: Thu, 6 Aug 2015 02:23:05 +0300 Subject: [PATCH] annotate api functions with types --- bin/ci.sh | 2 +- package.json | 2 +- src/api/ledger/accountinfo.js | 45 +++++++- src/api/ledger/transaction-types.js | 147 ++++++++++++++++++++++++ src/api/ledger/transaction.js | 31 +++-- src/api/ledger/utils.js | 27 +++-- src/api/server/server.js | 48 ++++++-- test/fixtures/api/rippled/account-tx.js | 2 +- 8 files changed, 272 insertions(+), 32 deletions(-) create mode 100644 src/api/ledger/transaction-types.js diff --git a/bin/ci.sh b/bin/ci.sh index a0bd0140..f9489697 100755 --- a/bin/ci.sh +++ b/bin/ci.sh @@ -11,7 +11,7 @@ typecheck() { lint() { REPO_URL="https://raw.githubusercontent.com/ripple/javascript-style-guide" curl "$REPO_URL/es6/eslintrc" > ./eslintrc - echo "plugins: [flowtype]" >> ./eslintrc + echo "parser: babel-eslint" >> ./eslintrc node_modules/.bin/eslint -c ./eslintrc $(git --no-pager diff --name-only -M100% --diff-filter=AM --relative $(git merge-base FETCH_HEAD origin/HEAD) FETCH_HEAD | grep "\.js$") } diff --git a/package.json b/package.json index 9fd02862..4f910cbd 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "prepublish": "npm run clean && npm run compile", "test": "istanbul test _mocha", "coveralls": "cat ./coverage/lcov.info | coveralls", - "lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'plugins:\n - flowtype' >> eslintrc; fi; eslint -c eslintrc src/", + "lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'parser: babel-eslint' >> eslintrc; fi; eslint -c eslintrc src/", "perf": "./scripts/perf_test.sh" }, "repository": { diff --git a/src/api/ledger/accountinfo.js b/src/api/ledger/accountinfo.js index 72854abb..902e1e00 100644 --- a/src/api/ledger/accountinfo.js +++ b/src/api/ledger/accountinfo.js @@ -6,7 +6,43 @@ const removeUndefined = require('./parse/utils').removeUndefined; const validate = utils.common.validate; const composeAsync = utils.common.composeAsync; -function formatAccountInfo(response) { +type AccountData = { + Sequence: number, + Account: string, + Balance: string, + Flags: number, + LedgerEntryType: string, + OwnerCount: number, + PreviousTxnID: string, + AccountTxnID?: string, + PreviousTxnLgrSeq: number, + index: string +} + +type AccountDataResponse = { + account_data: AccountData, + ledger_current_index?: number, + ledger_hash?: string, + ledger_index: number, + validated: boolean +} + +type AccountInfoOptions = { + ledgerVersion?: number +} + +type AccountInfoCallback = (err: any, data: AccountInfoResponse) => void + +type AccountInfoResponse = { + sequence: number, + xrpBalance: string, + ownerCount: number, + previousInitiatedTransactionID: string, + previousAffectingTransactionID: string, + previousAffectingTransactionLedgerVersion: number +} + +function formatAccountInfo(response: AccountDataResponse) { const data = response.account_data; return removeUndefined({ sequence: data.Sequence, @@ -18,7 +54,9 @@ function formatAccountInfo(response) { }); } -function getAccountInfoAsync(account, options, callback) { +function getAccountInfoAsync(account: string, options: AccountInfoOptions, + callback: AccountInfoCallback +) { validate.address(account); validate.getAccountInfoOptions(options); @@ -31,7 +69,8 @@ function getAccountInfoAsync(account, options, callback) { composeAsync(formatAccountInfo, callback)); } -function getAccountInfo(account: string, options={}) { +function getAccountInfo(account: string, options: AccountInfoOptions={} +): Promise { return utils.promisify(getAccountInfoAsync.bind(this))(account, options); } diff --git a/src/api/ledger/transaction-types.js b/src/api/ledger/transaction-types.js new file mode 100644 index 00000000..9a7f3d18 --- /dev/null +++ b/src/api/ledger/transaction-types.js @@ -0,0 +1,147 @@ +/* @flow */ +'use strict'; + +type Outcome = { + result: string, + timestamp?: string, + fee: string, + balanceChanges: Object, + orderbookChanges: Object, + ledgerVersion: number, + indexInLedger: number +} + +type Adjustment = { + address: string, + amount: { + currency: string, + counterparty?: string, + value: string + }, + tag?: number +} + +type Trustline = { + currency: string, + counterparty: string, + limit: string, + qualityIn?: number, + qualityOut?: number, + ripplingDisabled?: boolean, + authorized?: boolean, + frozen?: boolean +} + +type Settings = { + passwordSpent?: boolean, + requireDestinationTag?: boolean, + requireAuthorization?: boolean, + disallowIncomingXRP?: boolean, + disableMasterKey?: boolean, + enableTransactionIDTracking?: boolean, + noFreeze?: boolean, + globalFreeze?: boolean, + defaultRipple?: boolean, + emailHash?: string, + walletLocator?: string, + walletSize?: number, + messageKey?: string, + domain?: string, + transferRate?: number, + signers?: string, + regularKey?: string +} + +type OrderCancellation = { + orderSequence: number +} + +type Memo = { + type?: string, + format?: string, + data?: string +} + +type Amount = { + value: string, + currency: string, + counterparty?: string +} + +type Payment = { + source: Adjustment, + destination: Adjustment, + paths?: string, + memos?: Array, + invoiceID?: string, + allowPartialPayment?: boolean, + noDirectRipple?: boolean, + limitQuality?: boolean +} + +type PaymentTransaction = { + type: string, + specification: Payment, + outcome: Outcome, + id: string, + address: string, + sequence: number +} + +type Order = { + direction: string, + quantity: Amount, + totalPrice: Amount, + immediateOrCancel?: boolean, + fillOrKill?: boolean, + passive?: boolean +} + +type OrderTransaction = { + type: string, + specification: Order, + outcome: Outcome, + id: string, + address: string, + sequence: number +} + +type OrderCancellationTransaction = { + type: string, + specification: OrderCancellation, + outcome: Outcome, + id: string, + address: string, + sequence: number +} + +type TrustlineTransaction = { + type: string, + specification: Trustline, + outcome: Outcome, + id: string, + address: string, + sequence: number +} + +type SettingsTransaction = { + type: string, + specification: Settings, + outcome: Outcome, + id: string, + address: string, + sequence: number +} + +export type TransactionOptions = { + minLedgerVersion?: number, + maxLedgerVersion?: number +} + +export type GetTransactionResponse = PaymentTransaction | OrderTransaction | + OrderCancellationTransaction | TrustlineTransaction | SettingsTransaction + +export type GetTransactionResponseCallback = + (err?: ?Error, data?: GetTransactionResponse) => void + +export type CallbackType = (err?: ?Error, data?: Object) => void diff --git a/src/api/ledger/transaction.js b/src/api/ledger/transaction.js index 92a2e297..0a0216ac 100644 --- a/src/api/ledger/transaction.js +++ b/src/api/ledger/transaction.js @@ -8,7 +8,15 @@ const validate = utils.common.validate; const errors = utils.common.errors; const RippleError = require('../../core/rippleerror').RippleError; -function attachTransactionDate(remote, tx, callback) { +import type {Remote} from '../../core/remote'; + +import type {CallbackType, GetTransactionResponse, + GetTransactionResponseCallback, TransactionOptions} + from './transaction-types'; + +function attachTransactionDate(remote: Remote, tx: Object, + callback: CallbackType +) { if (tx.date) { callback(null, tx); return; @@ -29,22 +37,24 @@ function attachTransactionDate(remote, tx, callback) { }); } -function isTransactionInRange(tx, options) { +function isTransactionInRange(tx: Object, options: TransactionOptions) { return (!options.minLedgerVersion || tx.ledger_index >= options.minLedgerVersion) && (!options.maxLedgerVersion || tx.ledger_index <= options.maxLedgerVersion); } -function getTransactionAsync(identifier, options, callback) { +function getTransactionAsync(identifier: string, options: TransactionOptions, + callback: GetTransactionResponseCallback +) { validate.identifier(identifier); validate.getTransactionOptions(options); const remote = this.remote; - const maxLedgerVersion = Math.min(options.maxLedgerVersion, + const maxLedgerVersion = Math.min(options.maxLedgerVersion || Infinity, remote.getLedgerSequence()); - function callbackWrapper(error_, tx) { + function callbackWrapper(error_?: Error, tx?: Object) { let error = error_; if (error instanceof RippleError && error.remote && error.remote.error === 'txnNotFound') { @@ -56,22 +66,27 @@ function getTransactionAsync(identifier, options, callback) { options.minLedgerVersion, maxLedgerVersion)) { callback(new errors.MissingLedgerHistoryError('Transaction not found,' + ' but the server\'s ledger history is incomplete')); - } else if (!error && !isTransactionInRange(tx, options)) { + } else if (!error && tx && !isTransactionInRange(tx, options)) { callback(new errors.NotFoundError('Transaction not found')); } else if (error) { callback(error); + } else if (!tx) { + callback(new Error('Internal error')); } else { callback(error, parseTransaction(tx)); } } async.waterfall([ - _.partial(remote.requestTx.bind(remote), {hash: identifier, binary: false}), + _.partial(remote.requestTx.bind(remote), + {hash: identifier, binary: false}), _.partial(attachTransactionDate, remote) ], callbackWrapper); } -function getTransaction(identifier: string, options={}) { +function getTransaction(identifier: string, + options: TransactionOptions={} +): Promise { return utils.promisify(getTransactionAsync.bind(this))(identifier, options); } diff --git a/src/api/ledger/utils.js b/src/api/ledger/utils.js index 3ccb135d..d0fc866f 100644 --- a/src/api/ledger/utils.js +++ b/src/api/ledger/utils.js @@ -5,6 +5,7 @@ const assert = require('assert'); const common = require('../common'); const dropsToXrp = common.dropsToXrp; const composeAsync = common.composeAsync; +import type {Remote} from '../../core/remote'; type Callback = (err: any, data: any) => void @@ -13,8 +14,9 @@ function clamp(value: number, min: number, max: number): number { return Math.min(Math.max(value, min), max); } -function getXRPBalance(remote: any, address: string, ledgerVersion?: number, - callback: Callback): void { +function getXRPBalance(remote: Remote, address: string, ledgerVersion?: number, + callback: Callback +): void { remote.requestAccountInfo({account: address, ledger: ledgerVersion}, composeAsync((data) => dropsToXrp(data.account_data.Balance), callback)); } @@ -24,7 +26,8 @@ type Getter = (marker: ?string, limit: number, callback: Callback) => void // If the marker is omitted from a response, you have reached the end // getter(marker, limit, callback), callback(error, {marker, results}) function getRecursiveRecur(getter: Getter, marker?: string, limit: number, - callback: Callback): void { + callback: Callback +): void { getter(marker, limit, (error, data) => { if (error) { return callback(error); @@ -81,19 +84,21 @@ function signum(num) { * @returns {Number} [-1, 0, 1] */ -type Outcome = {outcome: {ledgerVersion: string, indexInLedger: string}}; +type Outcome = {outcome: {ledgerVersion: number, indexInLedger: number}}; function compareTransactions(first: Outcome, second: Outcome): number { - if (first.outcome.ledgerVersion === second.outcome.ledgerVersion) { - return signum(Number(first.outcome.indexInLedger) - - Number(second.outcome.indexInLedger)); + if (!first.outcome || !second.outcome) { + return 0; } - return Number(first.outcome.ledgerVersion) < - Number(second.outcome.ledgerVersion) ? -1 : 1; + if (first.outcome.ledgerVersion === second.outcome.ledgerVersion) { + return signum(first.outcome.indexInLedger - second.outcome.indexInLedger); + } + return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1; } -function hasCompleteLedgerRange(remote: any, minLedgerVersion: number, - maxLedgerVersion: number): boolean { +function hasCompleteLedgerRange(remote: Remote, minLedgerVersion?: number, + maxLedgerVersion?: number +): boolean { const firstLedgerVersion = 32570; // earlier versions have been lost return remote.getServer().hasLedgerRange( minLedgerVersion || firstLedgerVersion, diff --git a/src/api/server/server.js b/src/api/server/server.js index b2048bab..ede7f104 100644 --- a/src/api/server/server.js +++ b/src/api/server/server.js @@ -4,11 +4,41 @@ const _ = require('lodash'); const common = require('../common'); +import type {Remote} from '../../core/remote'; // If a ledger is not received in this time, consider the connection offline const CONNECTION_TIMEOUT = 1000 * 30; -function isUpToDate(remote): boolean { +type GetServerInfoResponse = { + buildVersion: string, + completeLedgers: string, + hostid: string, + ioLatencyMs: number, + load?: { + jobTypes: Array, + threads: number + }, + lastClose: { + convergeTimeS: number, + proposers: number + }, + loadFactor: number, + peers: number, + pubkeyNode: string, + pubkeyValidator?: string, + serverState: string, + validatedLedger: { + age: number, + baseFeeXrp: number, + hash: string, + reserveBaseXrp: number, + reserveIncXrp: number, + seq: number + }, + validationQuorum: number +} + +function isUpToDate(remote: Remote): boolean { const server = remote.getServer(); return Boolean(server) && (remote._stand_alone || (Date.now() - server._lastLedgerClose) <= CONNECTION_TIMEOUT); @@ -18,13 +48,17 @@ function isConnected(): boolean { return Boolean(this.remote._ledger_current_index) && isUpToDate(this.remote); } -function getServerInfoAsync(callback: (err: any, data: any) => void): void { +function getServerInfoAsync( + callback: (err: any, data?: GetServerInfoResponse) => void +): void { this.remote.requestServerInfo((error, response) => { if (error) { - const message = _.get(error, ['remote', 'error_message'], error.message); + const message = + _.get(error, ['remote', 'error_message'], error.message); callback(new common.errors.RippledNetworkError(message)); } else { - callback(null, common.convertKeysFromSnakeCaseToCamelCase(response.info)); + callback(null, + common.convertKeysFromSnakeCaseToCamelCase(response.info)); } }); } @@ -37,19 +71,19 @@ function getLedgerVersion(): number { return this.remote.getLedgerSequence(); } -function connect() { +function connect(): Promise { return common.promisify(callback => { this.remote.connect(() => callback(null)); })(); } -function disconnect() { +function disconnect(): Promise { return common.promisify(callback => { this.remote.disconnect(() => callback(null)); })(); } -function getServerInfo() { +function getServerInfo(): Promise { return common.promisify(getServerInfoAsync.bind(this))(); } diff --git a/test/fixtures/api/rippled/account-tx.js b/test/fixtures/api/rippled/account-tx.js index ac7d31e2..da5addbb 100644 --- a/test/fixtures/api/rippled/account-tx.js +++ b/test/fixtures/api/rippled/account-tx.js @@ -228,7 +228,7 @@ module.exports = function(request, options={}) { marker: marker === undefined ? undefined : String(marker), transactions: [ { - ledger_index: 348860 - Number(marker), + ledger_index: 348860 - Number(marker || 100), tx_blob: SerializedObject.from_json(tx).to_hex(), meta: SerializedObject.from_json(meta).to_hex(), validated: options.validated