Decouple core.Transaction from API

This commit is contained in:
Chris Clark
2015-10-20 16:13:34 -07:00
parent 98422e4153
commit c324682ca3
20 changed files with 224 additions and 186 deletions

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const core = require('./utils').core; const core = require('./utils').core;
const flagIndices = core.Transaction.set_clear_flags.AccountSet; const flagIndices = require('./txflags').txFlagIndices.AccountSet;
const flags = core.Remote.flags.account_root; const flags = core.Remote.flags.account_root;
const AccountFlags = { const AccountFlags = {

View File

@@ -7,6 +7,7 @@ module.exports = {
constants: require('./constants'), constants: require('./constants'),
errors: require('./errors'), errors: require('./errors'),
validate: require('./validate'), validate: require('./validate'),
txFlags: require('./txflags').txFlags,
dropsToXrp: utils.dropsToXrp, dropsToXrp: utils.dropsToXrp,
xrpToDrops: utils.xrpToDrops, xrpToDrops: utils.xrpToDrops,
toRippledAmount: utils.toRippledAmount, toRippledAmount: utils.toRippledAmount,

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const transactionFlags = { const txFlags = {
// Universal flags can apply to any transaction type // Universal flags can apply to any transaction type
Universal: { Universal: {
FullyCanonicalSig: 0x80000000 FullyCanonicalSig: 0x80000000
@@ -41,7 +41,7 @@ const transactionFlags = {
// The following are integer (as opposed to bit) flags // The following are integer (as opposed to bit) flags
// that can be set for particular transactions in the // that can be set for particular transactions in the
// SetFlag or ClearFlag field // SetFlag or ClearFlag field
const transactionFlagIndices = { const txFlagIndices = {
AccountSet: { AccountSet: {
asfRequireDest: 1, asfRequireDest: 1,
asfRequireAuth: 2, asfRequireAuth: 2,
@@ -55,6 +55,6 @@ const transactionFlagIndices = {
}; };
module.exports = { module.exports = {
transactionFlags, txFlags,
transactionFlagIndices txFlagIndices
}; };

View File

@@ -3,7 +3,7 @@
const assert = require('assert'); const assert = require('assert');
const utils = require('./utils'); const utils = require('./utils');
const parseAmount = require('./amount'); const parseAmount = require('./amount');
const flags = utils.core.Transaction.flags.OfferCreate; const flags = utils.txFlags.OfferCreate;
function parseOrder(tx: Object): Object { function parseOrder(tx: Object): Object {
assert(tx.TransactionType === 'OfferCreate'); assert(tx.TransactionType === 'OfferCreate');

View File

@@ -4,18 +4,18 @@ const _ = require('lodash');
const assert = require('assert'); const assert = require('assert');
const utils = require('./utils'); const utils = require('./utils');
const parseAmount = require('./amount'); const parseAmount = require('./amount');
const Transaction = utils.core.Transaction; const txFlags = utils.txFlags;
function isPartialPayment(tx) { function isPartialPayment(tx) {
return (tx.Flags & Transaction.flags.Payment.PartialPayment) !== 0; return (tx.Flags & txFlags.Payment.PartialPayment) !== 0;
} }
function isNoDirectRipple(tx) { function isNoDirectRipple(tx) {
return (tx.Flags & Transaction.flags.Payment.NoRippleDirect) !== 0; return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0;
} }
function isQualityLimited(tx) { function isQualityLimited(tx) {
return (tx.Flags & Transaction.flags.Payment.LimitQuality) !== 0; return (tx.Flags & txFlags.Payment.LimitQuality) !== 0;
} }
function removeGenericCounterparty(amount, address) { function removeGenericCounterparty(amount, address) {

View File

@@ -2,7 +2,7 @@
'use strict'; 'use strict';
const assert = require('assert'); const assert = require('assert');
const utils = require('./utils'); const utils = require('./utils');
const flags = utils.core.Transaction.flags.TrustSet; const flags = utils.txFlags.TrustSet;
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
function parseFlag(flagsValue, trueValue, falseValue) { function parseFlag(flagsValue, trueValue, falseValue) {

View File

@@ -91,5 +91,6 @@ module.exports = {
adjustQualityForXRP, adjustQualityForXRP,
dropsToXrp: utils.common.dropsToXrp, dropsToXrp: utils.common.dropsToXrp,
constants: utils.common.constants, constants: utils.common.constants,
txFlags: utils.common.txFlags,
core: utils.common.core core: utils.common.core
}; };

View File

@@ -2,40 +2,45 @@
'use strict'; 'use strict';
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction; const offerFlags = utils.common.txFlags.OfferCreate;
import type {Instructions, Prepare} from './types.js'; import type {Instructions, Prepare} from './types.js';
import type {Order} from '../ledger/transaction-types.js'; import type {Order} from '../ledger/transaction-types.js';
const OfferCreateFlags = { function createOrderTransaction(account: string, order: Order): Object {
passive: {set: 'Passive'},
immediateOrCancel: {set: 'ImmediateOrCancel'},
fillOrKill: {set: 'FillOrKill'}
};
function createOrderTransaction(account: string, order: Order): Transaction {
validate.address(account); validate.address(account);
validate.order(order); validate.order(order);
const transaction = new Transaction();
const takerPays = utils.common.toRippledAmount(order.direction === 'buy' ? const takerPays = utils.common.toRippledAmount(order.direction === 'buy' ?
order.quantity : order.totalPrice); order.quantity : order.totalPrice);
const takerGets = utils.common.toRippledAmount(order.direction === 'buy' ? const takerGets = utils.common.toRippledAmount(order.direction === 'buy' ?
order.totalPrice : order.quantity); order.totalPrice : order.quantity);
transaction.offerCreate(account, takerPays, takerGets); const txJSON: Object = {
TransactionType: 'OfferCreate',
utils.setTransactionBitFlags(transaction, order, OfferCreateFlags); Account: account,
TakerGets: takerGets,
TakerPays: takerPays,
Flags: 0
};
if (order.direction === 'sell') { if (order.direction === 'sell') {
transaction.setFlags('Sell'); txJSON.Flags |= offerFlags.Sell;
} }
if (order.passive === true) {
return transaction; txJSON.Flags |= offerFlags.Passive;
}
if (order.immediateOrCancel === true) {
txJSON.Flags |= offerFlags.ImmediateOrCancel;
}
if (order.fillOrKill === true) {
txJSON.Flags |= offerFlags.FillOrKill;
}
return txJSON;
} }
function prepareOrderAsync(account: string, order: Order, function prepareOrderAsync(account: string, order: Order,
instructions: Instructions, callback instructions: Instructions, callback
) { ) {
const txJSON = createOrderTransaction(account, order).tx_json; const txJSON = createOrderTransaction(account, order);
utils.prepareTransaction(txJSON, this.remote, instructions, callback); utils.prepareTransaction(txJSON, this.remote, instructions, callback);
} }

View File

@@ -2,24 +2,25 @@
'use strict'; 'use strict';
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
import type {Instructions, Prepare} from './types.js'; import type {Instructions, Prepare} from './types.js';
function createOrderCancellationTransaction(account: string, function createOrderCancellationTransaction(account: string,
sequence: number sequence: number
): Transaction { ): Object {
validate.address(account); validate.address(account);
validate.sequence(sequence); validate.sequence(sequence);
const transaction = new Transaction(); return {
transaction.offerCancel(account, sequence); TransactionType: 'OfferCancel',
return transaction; Account: account,
OfferSequence: sequence
};
} }
function prepareOrderCancellationAsync(account: string, sequence: number, function prepareOrderCancellationAsync(account: string, sequence: number,
instructions: Instructions, callback instructions: Instructions, callback
) { ) {
const txJSON = createOrderCancellationTransaction(account, sequence).tx_json; const txJSON = createOrderCancellationTransaction(account, sequence);
utils.prepareTransaction(txJSON, this.remote, instructions, callback); utils.prepareTransaction(txJSON, this.remote, instructions, callback);
} }

View File

@@ -4,7 +4,7 @@ const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const toRippledAmount = utils.common.toRippledAmount; const toRippledAmount = utils.common.toRippledAmount;
const Transaction = utils.common.core.Transaction; const paymentFlags = utils.common.txFlags.Payment;
const ValidationError = utils.common.errors.ValidationError; const ValidationError = utils.common.errors.ValidationError;
import type {Instructions, Prepare} from './types.js'; import type {Instructions, Prepare} from './types.js';
import type {Amount, Adjustment, MaxAdjustment, import type {Amount, Adjustment, MaxAdjustment,
@@ -66,7 +66,7 @@ function createMaximalAmount(amount: Amount): Amount {
} }
function createPaymentTransaction(account: string, paymentArgument: Payment function createPaymentTransaction(account: string, paymentArgument: Payment
): Transaction { ): Object {
const payment = _.cloneDeep(paymentArgument); const payment = _.cloneDeep(paymentArgument);
applyAnyCounterpartyEncoding(payment); applyAnyCounterpartyEncoding(payment);
validate.address(account); validate.address(account);
@@ -88,63 +88,63 @@ function createPaymentTransaction(account: string, paymentArgument: Payment
createMaximalAmount(payment.destination.minAmount) : createMaximalAmount(payment.destination.minAmount) :
(payment.destination.amount || payment.destination.minAmount); (payment.destination.amount || payment.destination.minAmount);
const transaction = new Transaction(); const txJSON: Object = {
transaction.payment({ TransactionType: 'Payment',
from: payment.source.address, Account: payment.source.address,
to: payment.destination.address, Destination: payment.destination.address,
amount: toRippledAmount(amount) Amount: toRippledAmount(amount),
}); Flags: 0
};
if (payment.invoiceID) { if (payment.invoiceID !== undefined) {
transaction.invoiceID(payment.invoiceID); txJSON.InvoiceID = payment.invoiceID;
} }
if (payment.source.tag) { if (payment.source.tag !== undefined) {
transaction.sourceTag(payment.source.tag); txJSON.SourceTag = payment.source.tag;
} }
if (payment.destination.tag) { if (payment.destination.tag !== undefined) {
transaction.destinationTag(payment.destination.tag); txJSON.DestinationTag = payment.destination.tag;
} }
if (payment.memos) { if (payment.memos !== undefined) {
_.forEach(payment.memos, memo => txJSON.Memos = _.map(payment.memos, utils.convertMemo);
transaction.addMemo(memo.type, memo.format, memo.data)
);
} }
if (payment.noDirectRipple) { if (payment.noDirectRipple === true) {
transaction.setFlags(['NoRippleDirect']); txJSON.Flags |= paymentFlags.NoRippleDirect;
} }
if (payment.limitQuality) { if (payment.limitQuality === true) {
transaction.setFlags(['LimitQuality']); txJSON.Flags |= paymentFlags.LimitQuality;
} }
if (!isXRPToXRPPayment(payment)) { if (!isXRPToXRPPayment(payment)) {
// Don't set SendMax for XRP->XRP payment // Don't set SendMax for XRP->XRP payment
// temREDUNDANT_SEND_MAX removed in: // temREDUNDANT_SEND_MAX removed in:
// https://github.com/ripple/rippled/commit/ // https://github.com/ripple/rippled/commit/
// c522ffa6db2648f1d8a987843e7feabf1a0b7de8/ // c522ffa6db2648f1d8a987843e7feabf1a0b7de8/
if (payment.allowPartialPayment || payment.destination.minAmount) { if (payment.allowPartialPayment === true
transaction.setFlags(['PartialPayment']); || payment.destination.minAmount !== undefined) {
txJSON.Flags |= paymentFlags.PartialPayment;
} }
transaction.setSendMax(toRippledAmount( txJSON.SendMax = toRippledAmount(
payment.source.maxAmount || payment.source.amount)); payment.source.maxAmount || payment.source.amount);
if (payment.destination.minAmount) { if (payment.destination.minAmount !== undefined) {
transaction.setDeliverMin(toRippledAmount(payment.destination.minAmount)); txJSON.DeliverMin = toRippledAmount(payment.destination.minAmount);
} }
if (payment.paths) { if (payment.paths !== undefined) {
transaction.paths(JSON.parse(payment.paths)); txJSON.Paths = JSON.parse(payment.paths);
} }
} else if (payment.allowPartialPayment) { } else if (payment.allowPartialPayment === true) {
throw new ValidationError('XRP to XRP payments cannot be partial payments'); throw new ValidationError('XRP to XRP payments cannot be partial payments');
} }
return transaction; return txJSON;
} }
function preparePaymentAsync(account: string, payment: Payment, function preparePaymentAsync(account: string, payment: Payment,
instructions: Instructions, callback instructions: Instructions, callback
) { ) {
const txJSON = createPaymentTransaction(account, payment).tx_json; const txJSON = createPaymentTransaction(account, payment);
utils.prepareTransaction(txJSON, this.remote, instructions, callback); utils.prepareTransaction(txJSON, this.remote, instructions, callback);
} }

View File

@@ -6,7 +6,6 @@ const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const AccountFlagIndices = utils.common.constants.AccountFlagIndices; const AccountFlagIndices = utils.common.constants.AccountFlagIndices;
const AccountFields = utils.common.constants.AccountFields; const AccountFields = utils.common.constants.AccountFields;
const Transaction = utils.common.core.Transaction;
import type {Instructions, Prepare} from './types.js'; import type {Instructions, Prepare} from './types.js';
import type {Settings} from './settings-types.js'; import type {Settings} from './settings-types.js';
@@ -14,7 +13,7 @@ import type {Settings} from './settings-types.js';
const CLEAR_SETTING = null; const CLEAR_SETTING = null;
function setTransactionFlags(transaction: Transaction, values: Settings) { function setTransactionFlags(txJSON: Object, values: Settings) {
const keys = Object.keys(values); const keys = Object.keys(values);
assert(keys.length === 1, 'ERROR: can only set one setting per transaction'); assert(keys.length === 1, 'ERROR: can only set one setting per transaction');
const flagName = keys[0]; const flagName = keys[0];
@@ -22,14 +21,14 @@ function setTransactionFlags(transaction: Transaction, values: Settings) {
const index = AccountFlagIndices[flagName]; const index = AccountFlagIndices[flagName];
if (index !== undefined) { if (index !== undefined) {
if (value) { if (value) {
transaction.tx_json.SetFlag = index; txJSON.SetFlag = index;
} else { } else {
transaction.tx_json.ClearFlag = index; txJSON.ClearFlag = index;
} }
} }
} }
function setTransactionFields(transaction: Transaction, input: Settings) { function setTransactionFields(txJSON: Object, input: Settings) {
const fieldSchema = AccountFields; const fieldSchema = AccountFields;
for (const fieldName in fieldSchema) { for (const fieldName in fieldSchema) {
const field = fieldSchema[fieldName]; const field = fieldSchema[fieldName];
@@ -49,7 +48,7 @@ function setTransactionFields(transaction: Transaction, input: Settings) {
value = new Buffer(value, 'ascii').toString('hex').toUpperCase(); value = new Buffer(value, 'ascii').toString('hex').toUpperCase();
} }
transaction.tx_json[fieldName] = value; txJSON[fieldName] = value;
} }
} }
@@ -71,33 +70,35 @@ function convertTransferRate(transferRate: number | string): number | string {
} }
function createSettingsTransaction(account: string, settings: Settings function createSettingsTransaction(account: string, settings: Settings
): Transaction { ): Object {
validate.address(account); validate.address(account);
validate.settings(settings); validate.settings(settings);
const transaction = new Transaction();
if (settings.regularKey) { if (settings.regularKey) {
return transaction.setRegularKey({ return {
account: account, TransactionType: 'SetRegularKey',
regular_key: settings.regularKey Account: account,
}); RegularKey: settings.regularKey
};
} }
transaction.accountSet(account); const txJSON: Object = {
setTransactionFlags(transaction, settings); TransactionType: 'AccountSet',
setTransactionFields(transaction, settings); Account: account
};
setTransactionFlags(txJSON, settings);
setTransactionFields(txJSON, settings);
if (transaction.tx_json.TransferRate !== undefined) { if (txJSON.TransferRate !== undefined) {
transaction.tx_json.TransferRate = convertTransferRate( txJSON.TransferRate = convertTransferRate(txJSON.TransferRate);
transaction.tx_json.TransferRate);
} }
return transaction; return txJSON;
} }
function prepareSettingsAsync(account: string, settings: Settings, function prepareSettingsAsync(account: string, settings: Settings,
instructions: Instructions, callback instructions: Instructions, callback
) { ) {
const txJSON = createSettingsTransaction(account, settings).tx_json; const txJSON = createSettingsTransaction(account, settings);
utils.prepareTransaction(txJSON, this.remote, instructions, callback); utils.prepareTransaction(txJSON, this.remote, instructions, callback);
} }

View File

@@ -3,7 +3,6 @@
const _ = require('lodash'); const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
import type {Instructions, Prepare} from './types.js'; import type {Instructions, Prepare} from './types.js';
import type {Memo} from '../common/types.js'; import type {Memo} from '../common/types.js';
@@ -15,30 +14,27 @@ type SuspendedPaymentCancellation = {
function createSuspendedPaymentCancellationTransaction(account: string, function createSuspendedPaymentCancellationTransaction(account: string,
payment: SuspendedPaymentCancellation payment: SuspendedPaymentCancellation
): Transaction { ): Object {
validate.address(account); validate.address(account);
validate.suspendedPaymentCancellation(payment); validate.suspendedPaymentCancellation(payment);
const transaction = new Transaction(); const txJSON: Object = {
transaction.suspendedPaymentCancel({ TransactionType: 'SuspendedPaymentCancel',
account: account, Account: account,
owner: payment.owner, Owner: payment.owner,
paymentSequence: payment.paymentSequence OfferSequence: payment.paymentSequence
}); };
if (payment.memos !== undefined) {
if (payment.memos) { txJSON.Memos = _.map(payment.memos, utils.convertMemo);
_.forEach(payment.memos, memo =>
transaction.addMemo(memo.type, memo.format, memo.data)
);
} }
return transaction; return txJSON;
} }
function prepareSuspendedPaymentCancellationAsync(account: string, function prepareSuspendedPaymentCancellationAsync(account: string,
payment: SuspendedPaymentCancellation, instructions: Instructions, callback payment: SuspendedPaymentCancellation, instructions: Instructions, callback
) { ) {
const txJSON = const txJSON =
createSuspendedPaymentCancellationTransaction(account, payment).tx_json; createSuspendedPaymentCancellationTransaction(account, payment);
utils.prepareTransaction(txJSON, this.remote, instructions, callback); utils.prepareTransaction(txJSON, this.remote, instructions, callback);
} }

View File

@@ -4,7 +4,6 @@ const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const toRippledAmount = utils.common.toRippledAmount; const toRippledAmount = utils.common.toRippledAmount;
const Transaction = utils.common.core.Transaction;
import type {Instructions, Prepare} from './types.js'; import type {Instructions, Prepare} from './types.js';
import type {Adjustment, MaxAdjustment, Memo} from '../common/types.js'; import type {Adjustment, MaxAdjustment, Memo} from '../common/types.js';
@@ -19,46 +18,42 @@ type SuspendedPaymentCreation = {
function createSuspendedPaymentCreationTransaction(account: string, function createSuspendedPaymentCreationTransaction(account: string,
payment: SuspendedPaymentCreation payment: SuspendedPaymentCreation
): Transaction { ): Object {
validate.address(account); validate.address(account);
validate.suspendedPaymentCreation(payment); validate.suspendedPaymentCreation(payment);
const transaction = new Transaction(); const txJSON: Object = {
transaction.suspendedPaymentCreate({ TransactionType: 'SuspendedPaymentCreate',
account: account, Account: account,
destination: payment.destination.address, Destination: payment.destination.address,
amount: toRippledAmount(payment.destination.amount) Amount: toRippledAmount(payment.destination.amount)
}); };
if (payment.digest) { if (payment.digest !== undefined) {
transaction.setDigest(payment.digest); txJSON.Digest = payment.digest;
} }
if (payment.allowCancelAfter) { if (payment.allowCancelAfter !== undefined) {
transaction.setAllowCancelAfter(payment.allowCancelAfter); txJSON.CancelAfter = utils.fromTimestamp(payment.allowCancelAfter);
} }
if (payment.allowExecuteAfter) { if (payment.allowExecuteAfter !== undefined) {
transaction.setAllowExecuteAfter(payment.allowExecuteAfter); txJSON.FinishAfter = utils.fromTimestamp(payment.allowExecuteAfter);
} }
if (payment.source.tag !== undefined) {
if (payment.source.tag) { txJSON.SourceTag = payment.source.tag;
transaction.sourceTag(payment.source.tag);
} }
if (payment.destination.tag) { if (payment.destination.tag !== undefined) {
transaction.destinationTag(payment.destination.tag); txJSON.DestinationTag = payment.destination.tag;
} }
if (payment.memos) { if (payment.memos !== undefined) {
_.forEach(payment.memos, memo => txJSON.Memos = _.map(payment.memos, utils.convertMemo);
transaction.addMemo(memo.type, memo.format, memo.data)
);
} }
return transaction; return txJSON;
} }
function prepareSuspendedPaymentCreationAsync(account: string, function prepareSuspendedPaymentCreationAsync(account: string,
payment: SuspendedPaymentCreation, instructions: Instructions, callback payment: SuspendedPaymentCreation, instructions: Instructions, callback
) { ) {
const txJSON = const txJSON = createSuspendedPaymentCreationTransaction(account, payment);
createSuspendedPaymentCreationTransaction(account, payment).tx_json;
utils.prepareTransaction(txJSON, this.remote, instructions, callback); utils.prepareTransaction(txJSON, this.remote, instructions, callback);
} }

View File

@@ -3,7 +3,6 @@
const _ = require('lodash'); const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
import type {Instructions, Prepare} from './types.js'; import type {Instructions, Prepare} from './types.js';
import type {Memo} from '../common/types.js'; import type {Memo} from '../common/types.js';
@@ -18,40 +17,36 @@ type SuspendedPaymentExecution = {
function createSuspendedPaymentExecutionTransaction(account: string, function createSuspendedPaymentExecutionTransaction(account: string,
payment: SuspendedPaymentExecution payment: SuspendedPaymentExecution
): Transaction { ): Object {
validate.address(account); validate.address(account);
validate.suspendedPaymentExecution(payment); validate.suspendedPaymentExecution(payment);
const transaction = new Transaction(); const txJSON: Object = {
transaction.suspendedPaymentFinish({ TransactionType: 'SuspendedPaymentFinish',
account: account, Account: account,
owner: payment.owner, Owner: payment.owner,
paymentSequence: payment.paymentSequence OfferSequence: payment.paymentSequence
}); };
if (payment.method) { if (payment.method !== undefined) {
transaction.setMethod(payment.method); txJSON.Method = payment.method;
} }
if (payment.digest) { if (payment.digest !== undefined) {
transaction.setDigest(payment.digest); txJSON.Digest = payment.digest;
} }
if (payment.proof) { if (payment.proof !== undefined) {
transaction.setProof(payment.proof); txJSON.Proof = utils.convertStringToHex(payment.proof);
} }
if (payment.memos !== undefined) {
if (payment.memos) { txJSON.Memos = _.map(payment.memos, utils.convertMemo);
_.forEach(payment.memos, memo =>
transaction.addMemo(memo.type, memo.format, memo.data)
);
} }
return transaction; return txJSON;
} }
function prepareSuspendedPaymentExecutionAsync(account: string, function prepareSuspendedPaymentExecutionAsync(account: string,
payment: SuspendedPaymentExecution, instructions: Instructions, callback payment: SuspendedPaymentExecution, instructions: Instructions, callback
) { ) {
const txJSON = const txJSON = createSuspendedPaymentExecutionTransaction(account, payment);
createSuspendedPaymentExecutionTransaction(account, payment).tx_json;
utils.prepareTransaction(txJSON, this.remote, instructions, callback); utils.prepareTransaction(txJSON, this.remote, instructions, callback);
} }

View File

@@ -2,17 +2,11 @@
'use strict'; 'use strict';
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction; const trustlineFlags = utils.common.txFlags.TrustSet;
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
import type {Instructions, Prepare} from './types.js'; import type {Instructions, Prepare} from './types.js';
import type {TrustLineSpecification} from '../ledger/trustlines-types.js'; import type {TrustLineSpecification} from '../ledger/trustlines-types.js';
const TrustSetFlags = {
authorized: {set: 'SetAuth'},
ripplingDisabled: {set: 'NoRipple', unset: 'ClearNoRipple'},
frozen: {set: 'SetFreeze', unset: 'ClearFreeze'}
};
function convertQuality(quality) { function convertQuality(quality) {
return quality === undefined ? undefined : return quality === undefined ? undefined :
(new BigNumber(quality)).shift(9).truncated().toNumber(); (new BigNumber(quality)).shift(9).truncated().toNumber();
@@ -20,7 +14,7 @@ function convertQuality(quality) {
function createTrustlineTransaction(account: string, function createTrustlineTransaction(account: string,
trustline: TrustLineSpecification trustline: TrustLineSpecification
): Transaction { ): Object {
validate.address(account); validate.address(account);
validate.trustline(trustline); validate.trustline(trustline);
@@ -30,17 +24,36 @@ function createTrustlineTransaction(account: string,
value: trustline.limit value: trustline.limit
}; };
const transaction = new Transaction(); const txJSON: Object = {
transaction.trustSet(account, limit, convertQuality(trustline.qualityIn), TransactionType: 'TrustSet',
convertQuality(trustline.qualityOut)); Account: account,
utils.setTransactionBitFlags(transaction, trustline, TrustSetFlags); LimitAmount: limit,
return transaction; Flags: 0
};
if (trustline.qualityIn !== undefined) {
txJSON.QualityIn = convertQuality(trustline.qualityIn);
}
if (trustline.qualityOut !== undefined) {
txJSON.QualityOut = convertQuality(trustline.qualityOut);
}
if (trustline.authorized === true) {
txJSON.Flags |= trustlineFlags.SetAuth;
}
if (trustline.ripplingDisabled !== undefined) {
txJSON.Flags |= trustline.ripplingDisabled ?
trustlineFlags.NoRipple : trustlineFlags.ClearNoRipple;
}
if (trustline.frozen !== undefined) {
txJSON.Flags |= trustline.frozen ?
trustlineFlags.SetFreeze : trustlineFlags.ClearFreeze;
}
return txJSON;
} }
function prepareTrustlineAsync(account: string, function prepareTrustlineAsync(account: string,
trustline: TrustLineSpecification, instructions: Instructions, callback trustline: TrustLineSpecification, instructions: Instructions, callback
) { ) {
const txJSON = createTrustlineTransaction(account, trustline).tx_json; const txJSON = createTrustlineTransaction(account, trustline);
utils.prepareTransaction(txJSON, this.remote, instructions, callback); utils.prepareTransaction(txJSON, this.remote, instructions, callback);
} }

View File

@@ -5,25 +5,12 @@ const async = require('async');
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
const common = require('../common'); const common = require('../common');
const composeAsync = common.composeAsync; const composeAsync = common.composeAsync;
const txFlags = require('./txflags'); const txFlags = common.txFlags;
import type {Remote} from '../../core/remote'; import type {Remote} from '../../core/remote';
import type {Transaction} from '../../core/transaction';
import type {Instructions} from './types.js'; import type {Instructions} from './types.js';
function setTransactionBitFlags(transaction: Transaction, values: any, function removeUndefined(obj: Object): Object {
flags: any return _.omit(obj, _.isUndefined);
): void {
for (const flagName in flags) {
const flagValue = values[flagName];
const flagConversions = flags[flagName];
if (flagValue === true && flagConversions.set !== undefined) {
transaction.setFlags(flagConversions.set);
}
if (flagValue === false && flagConversions.unset !== undefined) {
transaction.setFlags(flagConversions.unset);
}
}
} }
function getFeeDrops(remote: Remote, callback) { function getFeeDrops(remote: Remote, callback) {
@@ -46,7 +33,7 @@ function formatPrepareResponse(txJSON: Object): Object {
} }
function setCanonicalFlag(txJSON) { function setCanonicalFlag(txJSON) {
txJSON.Flags |= txFlags.transactionFlags.Universal.FullyCanonicalSig; txJSON.Flags |= txFlags.Universal.FullyCanonicalSig;
// JavaScript converts operands to 32-bit signed ints before doing bitwise // JavaScript converts operands to 32-bit signed ints before doing bitwise
// operations. We need to convert it back to an unsigned int. // operations. We need to convert it back to an unsigned int.
@@ -114,8 +101,47 @@ function prepareTransaction(txJSON: Object, remote: Remote,
})); }));
} }
function convertStringToHex(string: string) {
return string ? (new Buffer(string, 'utf8')).toString('hex').toUpperCase() :
undefined;
}
function convertMemo(memo: Object): Object {
return {
Memo: removeUndefined({
MemoData: convertStringToHex(memo.data),
MemoType: convertStringToHex(memo.type),
MemoFormat: convertStringToHex(memo.format)
})
};
}
/**
* @param {Number} rpepoch (seconds since 1/1/2000 GMT)
* @return {Number} ms since unix epoch
*
*/
function toTimestamp(rpepoch: number): number {
return (rpepoch + 0x386D4380) * 1000;
}
/**
* @param {Number|Date} timestamp (ms since unix epoch)
* @return {Number} seconds since ripple epoch ( 1/1/2000 GMT)
*/
function fromTimestamp(timestamp: number | Date): number {
const timestamp_ = timestamp instanceof Date ?
timestamp.getTime() :
timestamp;
return Math.round(timestamp_ / 1000) - 0x386D4380;
}
module.exports = { module.exports = {
setTransactionBitFlags, removeUndefined,
convertStringToHex,
fromTimestamp,
toTimestamp,
convertMemo,
prepareTransaction, prepareTransaction,
common, common,
promisify: common.promisify promisify: common.promisify

View File

@@ -29,7 +29,11 @@ const orderbook = {
}; };
function checkResult(expected, schemaName, response) { function checkResult(expected, schemaName, response) {
assert.deepEqual(response, expected); if (expected.txJSON) {
assert(response.txJSON);
assert.deepEqual(JSON.parse(response.txJSON), JSON.parse(expected.txJSON));
}
assert.deepEqual(_.omit(response, 'txJSON'), _.omit(expected, 'txJSON'));
if (schemaName) { if (schemaName) {
schemaValidator.schemaValidate(schemaName, response); schemaValidator.schemaValidate(schemaName, response);
} }
@@ -908,7 +912,7 @@ describe('RippleAPI - offline', function() {
fee: '0.000012' fee: '0.000012'
}; };
return api.prepareSettings(address, settings, instructions).then(data => { return api.prepareSettings(address, settings, instructions).then(data => {
assert.deepEqual(data, responses.prepareSettings.flags); checkResult(responses.prepareSettings.flags, 'prepare', data);
assert.deepEqual(api.sign(data.txJSON, secret), responses.sign); assert.deepEqual(api.sign(data.txJSON, secret), responses.sign);
}); });
}); });

View File

@@ -26,5 +26,5 @@
"allowPartialPayment": true, "allowPartialPayment": true,
"noDirectRipple": true, "noDirectRipple": true,
"limitQuality": true, "limitQuality": true,
"paths": "[[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"type\":49,\"type_hex\":\"0000000000000031\"},{\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"type\":48,\"type_hex\":\"0000000000000030\"},{\"account\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"type\":49,\"type_hex\":\"0000000000000031\"}]]" "paths": "[[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\"},{\"account\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\"}]]"
} }

View File

@@ -1,5 +1,5 @@
{ {
"currency": "BTC", "currency": "BTC",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", "counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM",
"limit": "0.10" "limit": "0.1"
} }

View File

@@ -1,5 +1,5 @@
{ {
"txJSON": "{\"Flags\":2147942400,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":{\"value\":\"0.01\",\"currency\":\"LTC\",\"issuer\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\"},\"InvoiceID\":\"A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A\",\"SourceTag\":14,\"DestinationTag\":58,\"Memos\":[{\"Memo\":{\"MemoType\":\"74657374\",\"MemoFormat\":\"706C61696E2F74657874\",\"MemoData\":\"7465787465642064617461\"}}],\"SendMax\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\"},\"Paths\":[[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"currency\":\"USD\",\"type_hex\":\"0000000000000031\"},{\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"currency\":\"LTC\",\"type_hex\":\"0000000000000030\"},{\"account\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"currency\":\"LTC\",\"type_hex\":\"0000000000000031\"}]],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}", "txJSON": "{\"Flags\":2147942400,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":{\"value\":\"0.01\",\"currency\":\"LTC\",\"issuer\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\"},\"InvoiceID\":\"A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A\",\"SourceTag\":14,\"DestinationTag\":58,\"Memos\":[{\"Memo\":{\"MemoType\":\"74657374\",\"MemoFormat\":\"706C61696E2F74657874\",\"MemoData\":\"7465787465642064617461\"}}],\"SendMax\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\"},\"Paths\":[[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"currency\":\"USD\"},{\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"currency\":\"LTC\"},{\"account\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"currency\":\"LTC\"}]],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": { "instructions": {
"fee": "12", "fee": "12",
"sequence": 23, "sequence": 23,