Add SuspendedPayment{Create,Finish,Cancel}

* Add SusPay core tests
* Rename SusPay -> SuspendedPayment (code review)
* Rename cancelAfter -> allowCancelAfter
* Rename suspendedPayment{Finish,Execution}
This commit is contained in:
sentientwaffle
2015-08-31 07:24:55 -07:00
parent 6e98629f9b
commit b134081293
29 changed files with 733 additions and 31 deletions

View File

@@ -69,6 +69,9 @@ function loadSchemas() {
require('./schemas/sign.json'),
require('./schemas/signed-value.json'),
require('./schemas/submit.json'),
require('./schemas/suspended-payment-cancellation.json'),
require('./schemas/suspended-payment-execution.json'),
require('./schemas/suspended-payment-creation.json'),
require('./schemas/timestamp.json'),
require('./schemas/transaction-options.json'),
require('./schemas/transactions-options.json'),

View File

@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "suspended-payment-cancellation",
"type": "object",
"properties": {
"memos": {
"type": "array",
"items": {
"$ref": "memo"
}
},
"owner": {"$ref": "address"},
"paymentSequence": {"$ref": "uint32"}
},
"required": ["owner", "paymentSequence"],
"additionalProperties": false
}

View File

@@ -0,0 +1,28 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "suspended-payment-creation",
"type": "object",
"properties": {
"source": {"$ref": "maxAdjustment"},
"destination": {"$ref": "adjustment"},
"memos": {
"type": "array",
"items": {
"$ref": "memo"
}
},
"digest": {"$ref": "hash256"},
"allowCancelAfter": {
"type": "integer",
"minimum": 0,
"description": "milliseconds since unix epoch"
},
"allowExecuteAfter": {
"type": "integer",
"minimum": 0,
"description": "milliseconds since unix epoch"
}
},
"required": ["source", "destination"],
"additionalProperties": false
}

View File

@@ -0,0 +1,20 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "suspended-payment-execution",
"type": "object",
"properties": {
"memos": {
"type": "array",
"items": {
"$ref": "memo"
}
},
"owner": {"$ref": "address"},
"paymentSequence": {"$ref": "uint32"},
"method": {"type": "integer", "minimum": 0, "maximum": 255},
"digest": {"$ref": "hash256"},
"proof": {"type": "string"}
},
"required": ["owner", "paymentSequence"],
"additionalProperties": false
}

View File

@@ -61,6 +61,12 @@ module.exports = {
order: _.partial(schemaValidate, 'order'),
orderbook: _.partial(schemaValidate, 'orderbook'),
payment: _.partial(schemaValidate, 'payment'),
suspendedPaymentCreation:
_.partial(schemaValidate, 'suspended-payment-creation'),
suspendedPaymentExecution:
_.partial(schemaValidate, 'suspended-payment-execution'),
suspendedPaymentCancellation:
_.partial(schemaValidate, 'suspended-payment-cancellation'),
pathfind: _.partial(schemaValidate, 'pathfind'),
settings: _.partial(schemaValidate, 'settings'),
trustline: _.partial(schemaValidate, 'trustline'),

View File

@@ -23,6 +23,12 @@ const preparePayment = require('./transaction/payment');
const prepareTrustline = require('./transaction/trustline');
const prepareOrder = require('./transaction/order');
const prepareOrderCancellation = require('./transaction/ordercancellation');
const prepareSuspendedPaymentCreation =
require('./transaction/suspended-payment-creation');
const prepareSuspendedPaymentExecution =
require('./transaction/suspended-payment-execution');
const prepareSuspendedPaymentCancellation =
require('./transaction/suspended-payment-cancellation');
const prepareSettings = require('./transaction/settings');
const sign = require('./transaction/sign');
const submit = require('./transaction/submit');
@@ -61,6 +67,9 @@ RippleAPI.prototype = {
prepareTrustline,
prepareOrder,
prepareOrderCancellation,
prepareSuspendedPaymentCreation,
prepareSuspendedPaymentExecution,
prepareSuspendedPaymentCancellation,
prepareSettings,
sign,
submit,

View File

@@ -18,19 +18,6 @@ function isQualityLimited(tx) {
return (tx.Flags & Transaction.flags.Payment.LimitQuality) !== 0;
}
function parsePaymentMemos(tx) {
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
return undefined;
}
return tx.Memos.map((m) => {
return utils.removeUndefined({
type: m.Memo.parsed_memo_type,
format: m.Memo.parsed_memo_format,
data: m.Memo.parsed_memo_data
});
});
}
function removeGenericCounterparty(amount, address) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount;
@@ -55,7 +42,7 @@ function parsePayment(tx: Object): Object {
return utils.removeUndefined({
source: utils.removeUndefined(source),
destination: utils.removeUndefined(destination),
memos: parsePaymentMemos(tx),
memos: utils.parseMemos(tx),
invoiceID: tx.InvoiceID,
paths: tx.Paths ? JSON.stringify(tx.Paths) : undefined,
allowPartialPayment: isPartialPayment(tx) || undefined,

View File

@@ -0,0 +1,16 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
function parseSuspendedPaymentCancellation(tx: Object): Object {
assert(tx.TransactionType === 'SuspendedPaymentCancel');
return utils.removeUndefined({
memos: utils.parseMemos(tx),
owner: tx.Owner,
paymentSequence: tx.OfferSequence
});
}
module.exports = parseSuspendedPaymentCancellation;

View File

@@ -0,0 +1,39 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
function removeGenericCounterparty(amount, address) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount;
}
function parseSuspendedPaymentCreation(tx: Object): Object {
assert(tx.TransactionType === 'SuspendedPaymentCreate');
const source = {
address: tx.Account,
maxAmount: removeGenericCounterparty(
parseAmount(tx.SendMax || tx.Amount), tx.Account),
tag: tx.SourceTag
};
const destination = {
address: tx.Destination,
amount: removeGenericCounterparty(parseAmount(tx.Amount), tx.Destination),
tag: tx.DestinationTag
};
return utils.removeUndefined({
source: utils.removeUndefined(source),
destination: utils.removeUndefined(destination),
memos: utils.parseMemos(tx),
digest: tx.Digest,
allowCancelAfter: tx.CancelAfter,
allowExecuteAfter: tx.FinishAfter
});
}
module.exports = parseSuspendedPaymentCreation;

View File

@@ -0,0 +1,25 @@
/* @flow */
'use strict';
const assert = require('assert');
const sjclcodec = require('sjcl-codec');
const utils = require('./utils');
function convertHexToString(hexString) {
const bits = sjclcodec.hex.toBits(hexString);
return sjclcodec.utf8String.fromBits(bits);
}
function parseSuspendedPaymentExecution(tx: Object): Object {
assert(tx.TransactionType === 'SuspendedPaymentFinish');
return utils.removeUndefined({
memos: utils.parseMemos(tx),
owner: tx.Owner,
paymentSequence: tx.OfferSequence,
method: tx.Method,
digest: tx.Digest,
proof: tx.Proof ? convertHexToString(tx.Proof) : undefined
});
}
module.exports = parseSuspendedPaymentExecution;

View File

@@ -7,6 +7,10 @@ const parseTrustline = require('./trustline');
const parseOrder = require('./order');
const parseOrderCancellation = require('./cancellation');
const parseSettings = require('./settings');
const parseSuspendedPaymentCreation = require('./suspended-payment-creation');
const parseSuspendedPaymentExecution = require('./suspended-payment-execution');
const parseSuspendedPaymentCancellation =
require('./suspended-payment-cancellation');
function parseTransactionType(type) {
const mapping = {
@@ -15,7 +19,10 @@ function parseTransactionType(type) {
OfferCreate: 'order',
OfferCancel: 'orderCancellation',
AccountSet: 'settings',
SetRegularKey: 'settings'
SetRegularKey: 'settings',
SuspendedPaymentCreate: 'suspendedPaymentCreation',
SuspendedPaymentFinish: 'suspendedPaymentExecution',
SuspendedPaymentCancel: 'suspendedPaymentCancellation'
};
return mapping[type] || null;
}
@@ -27,7 +34,10 @@ function parseTransaction(tx: Object): Object {
'trustline': parseTrustline,
'order': parseOrder,
'orderCancellation': parseOrderCancellation,
'settings': parseSettings
'settings': parseSettings,
'suspendedPaymentCreation': parseSuspendedPaymentCreation,
'suspendedPaymentExecution': parseSuspendedPaymentExecution,
'suspendedPaymentCancellation': parseSuspendedPaymentCancellation
};
const parser = mapping[type];
assert(parser !== undefined, 'Unrecognized transaction type');

View File

@@ -67,8 +67,22 @@ function parseOutcome(tx: Object): ?Object {
};
}
function parseMemos(tx: Object): ?Array<Object> {
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
return undefined;
}
return tx.Memos.map((m) => {
return removeUndefined({
type: m.Memo.parsed_memo_type,
format: m.Memo.parsed_memo_format,
data: m.Memo.parsed_memo_data
});
});
}
module.exports = {
parseOutcome,
parseMemos,
removeUndefined,
adjustQualityForXRP,
dropsToXrp: utils.common.dropsToXrp,

View File

@@ -0,0 +1,40 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
function createSuspendedPaymentCancellationTransaction(account, payment) {
validate.address(account);
validate.suspendedPaymentCancellation(payment);
const transaction = new Transaction();
transaction.suspendedPaymentCancel({
account: account,
owner: payment.owner,
paymentSequence: payment.paymentSequence
});
if (payment.memos) {
_.forEach(payment.memos, memo =>
transaction.addMemo(memo.type, memo.format, memo.data)
);
}
return transaction;
}
function prepareSuspendedPaymentCancellationAsync(account, payment,
instructions, callback) {
const transaction =
createSuspendedPaymentCancellationTransaction(account, payment);
utils.prepareTransaction(transaction, this.remote, instructions, callback);
}
function prepareSuspendedPaymentCancellation(account: string, payment: Object,
instructions = {}) {
return utils.promisify(prepareSuspendedPaymentCancellationAsync)
.call(this, account, payment, instructions);
}
module.exports = prepareSuspendedPaymentCancellation;

View File

@@ -0,0 +1,57 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const toRippledAmount = utils.common.toRippledAmount;
const Transaction = utils.common.core.Transaction;
function createSuspendedPaymentCreationTransaction(account, payment) {
validate.address(account);
validate.suspendedPaymentCreation(payment);
const transaction = new Transaction();
transaction.suspendedPaymentCreate({
account: account,
destination: payment.destination.address,
amount: toRippledAmount(payment.destination.amount)
});
if (payment.digest) {
transaction.setDigest(payment.digest);
}
if (payment.allowCancelAfter) {
transaction.setAllowCancelAfter(payment.allowCancelAfter);
}
if (payment.allowExecuteAfter) {
transaction.setAllowExecuteAfter(payment.allowExecuteAfter);
}
if (payment.source.tag) {
transaction.sourceTag(payment.source.tag);
}
if (payment.destination.tag) {
transaction.destinationTag(payment.destination.tag);
}
if (payment.memos) {
_.forEach(payment.memos, memo =>
transaction.addMemo(memo.type, memo.format, memo.data)
);
}
return transaction;
}
function prepareSuspendedPaymentCreationAsync(account, payment, instructions,
callback) {
const transaction =
createSuspendedPaymentCreationTransaction(account, payment);
utils.prepareTransaction(transaction, this.remote, instructions, callback);
}
function prepareSuspendedPaymentCreation(account: string, payment: Object,
instructions = {}) {
return utils.promisify(prepareSuspendedPaymentCreationAsync)
.call(this, account, payment, instructions);
}
module.exports = prepareSuspendedPaymentCreation;

View File

@@ -0,0 +1,50 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
function createSuspendedPaymentExecutionTransaction(account, payment) {
validate.address(account);
validate.suspendedPaymentExecution(payment);
const transaction = new Transaction();
transaction.suspendedPaymentFinish({
account: account,
owner: payment.owner,
paymentSequence: payment.paymentSequence
});
if (payment.method) {
transaction.setMethod(payment.method);
}
if (payment.digest) {
transaction.setDigest(payment.digest);
}
if (payment.proof) {
transaction.setProof(payment.proof);
}
if (payment.memos) {
_.forEach(payment.memos, memo =>
transaction.addMemo(memo.type, memo.format, memo.data)
);
}
return transaction;
}
function prepareSuspendedPaymentExecutionAsync(account, payment, instructions,
callback) {
const transaction =
createSuspendedPaymentExecutionTransaction(account, payment);
utils.prepareTransaction(transaction, this.remote, instructions, callback);
}
function prepareSuspendedPaymentExecution(account: string, payment: Object,
instructions = {}) {
return utils.promisify(prepareSuspendedPaymentExecutionAsync)
.call(this, account, payment, instructions);
}
module.exports = prepareSuspendedPaymentExecution;

View File

@@ -92,6 +92,8 @@ const FIELDS_MAP = exports.fields = {
33: 'SetFlag',
34: 'ClearFlag',
35: 'SignerQuorum',
36: 'CancelAfter',
37: 'FinishAfter',
38: 'SignerListID'
},
3: { // Int64
@@ -121,7 +123,8 @@ const FIELDS_MAP = exports.fields = {
17: 'InvoiceID',
18: 'Nickname',
19: 'Amendment',
20: 'TicketID'
20: 'TicketID',
21: 'Digest'
},
6: { // Amount
1: 'Amount',
@@ -151,7 +154,8 @@ const FIELDS_MAP = exports.fields = {
11: 'CreateCode',
12: 'MemoType',
13: 'MemoData',
14: 'MemoFormat'
14: 'MemoFormat',
17: 'Proof'
},
8: { // Account
1: 'Account',
@@ -190,7 +194,7 @@ const FIELDS_MAP = exports.fields = {
// Uncommon types
16: { // Int8
1: 'CloseResolution',
2: 'TemplateEntryType',
2: 'Method',
3: 'TransactionResult'
},
17: { // Hash160
@@ -217,9 +221,9 @@ Object.keys(FIELDS_MAP).forEach(function(k1) {
});
});
const REQUIRED = exports.REQUIRED = 0,
OPTIONAL = exports.OPTIONAL = 1,
DEFAULT = exports.DEFAULT = 2;
const REQUIRED = exports.REQUIRED = 0;
const OPTIONAL = exports.OPTIONAL = 1;
const DEFAULT = exports.DEFAULT = 2;
const base = [
[ 'TransactionType' , REQUIRED ],
@@ -308,6 +312,25 @@ exports.tx = {
SignerListSet: [12].concat(base, [
['SignerQuorum', REQUIRED],
['SignerEntries', OPTIONAL]
]),
SuspendedPaymentCreate: [1].concat(base, [
[ 'Destination' , REQUIRED ],
[ 'Amount' , REQUIRED ],
[ 'Digest' , OPTIONAL ],
[ 'CancelAfter' , OPTIONAL ],
[ 'FinishAfter' , OPTIONAL ],
[ 'DestinationTag' , OPTIONAL ]
]),
SuspendedPaymentFinish: [2].concat(base, [
[ 'Owner' , REQUIRED ],
[ 'OfferSequence' , REQUIRED ],
[ 'Method' , OPTIONAL ],
[ 'Digest' , OPTIONAL ],
[ 'Proof' , OPTIONAL ]
]),
SuspendedPaymentCancel: [4].concat(base, [
[ 'Owner' , REQUIRED ],
[ 'OfferSequence' , REQUIRED ]
])
};

View File

@@ -2276,7 +2276,10 @@ Remote.prototype.createTransaction = function(type, options = {}) {
OfferCreate: transaction.offerCreate,
OfferCancel: transaction.offerCancel,
SetRegularKey: transaction.setRegularKey,
SignerListSet: transaction.setSignerList
SignerListSet: transaction.setSignerList,
SuspendedPaymentCreate: transaction.suspendedPaymentCreate,
SuspendedPaymentFinish: transaction.suspendedPaymentFinish,
SuspendedPaymentCancel: transaction.suspendedPaymentCancel
};
const transactionConstructor = constructorMap[type];

View File

@@ -808,6 +808,11 @@ Transaction.prototype.setFlags = function(flags) {
return this;
};
function convertStringToHex(string) {
const utf8String = sjclcodec.utf8String.toBits(string);
return sjclcodec.hex.fromBits(utf8String).toUpperCase();
}
/**
* Add a Memo to transaction.
*
@@ -834,11 +839,6 @@ Transaction.prototype.addMemo = function(options_) {
};
}
function convertStringToHex(string) {
const utf8String = sjclcodec.utf8String.toBits(string);
return sjclcodec.hex.fromBits(utf8String).toUpperCase();
}
const memo = {};
const memoRegex = Transaction.MEMO_REGEX;
let memoType = options.memoType;
@@ -1501,6 +1501,110 @@ Transaction.prototype.summary = function() {
return txSummary;
};
/**
* Construct a 'SuspendedPaymentCreate' transaction
*
* Relevant setters:
* - setSourceTag()
* - setFlags()
* - setDigest()
* - setAllowCancelAfter()
* - setAllowExecuteAfter()
*
* @param {String} options.account source account
* @param {String} options.destination account
* @param {Amount} options.amount payment amount
*/
Transaction.prototype.suspendedPaymentCreate = function(options) {
this.setType('SuspendedPaymentCreate');
this.setAccount(options.account);
this.setDestination(options.destination);
this.setAmount(options.amount);
return this;
};
/**
* Construct a 'SuspendedPaymentFinish' transaction
*
* Relevant setters:
* - setSourceTag()
* - setFlags()
* - setOwner()
* - setOfferSequence()
* - setMethod()
* - setDigest()
* - setProof()
*
* @param {String} options.account source account
* @param {String} options.owner SuspendedPaymentCreate's Account
* @param {Integer} options.paymentSequence SuspendedPaymentCreate's Sequence
*/
Transaction.prototype.suspendedPaymentFinish = function(options) {
this.setType('SuspendedPaymentFinish');
this.setAccount(options.account);
this.setOwner(options.owner);
this.setOfferSequence(options.paymentSequence);
return this;
};
/**
* Construct a 'SuspendedPaymentCancel' transaction
*
* Relevant setters:
* - setSourceTag()
* - setFlags()
* - setOwner()
* - setOfferSequence()
*
* @param {String} options.account source account
* @param {String} options.owner SuspendedPaymentCreate's Account
* @param {Integer} options.paymentSequence SuspendedPaymentCreate's Sequence
*/
Transaction.prototype.suspendedPaymentCancel = function(options) {
this.setType('SuspendedPaymentCancel');
this.setAccount(options.account);
this.setOwner(options.owner);
this.setOfferSequence(options.paymentSequence);
return this;
};
Transaction.prototype.setDigest = function(digest) {
return this._setHash256('Digest', digest);
};
Transaction.prototype.setAllowCancelAfter = function(after) {
return this._setUInt32('CancelAfter', utils.time.toRipple(after));
};
Transaction.prototype.setAllowExecuteAfter = function(after) {
return this._setUInt32('FinishAfter', utils.time.toRipple(after));
};
Transaction.prototype.setOwner = function(owner) {
return this._setAccount('Owner', owner);
};
Transaction.prototype.setMethod = function(method) {
return this._setUInt8('Method', method);
};
Transaction.prototype.setProof = function(proof) {
this.tx_json.Proof = convertStringToHex(proof);
return this;
};
Transaction.prototype._setUInt8 = function(name, value) {
const isValidUInt8 = typeof value === 'number' && value >= 0 && value < 256;
if (!isValidUInt8) {
throw new Error(name + ' must be a valid UInt8');
}
this.tx_json[name] = value;
return this;
};
Transaction.prototype.setSigners = function(signers) {
if (_.isArray(signers)) {
this.tx_json.Signers = signers;

View File

@@ -143,6 +143,31 @@ describe('RippleAPI', function() {
'prepare'));
});
it('prepareSuspendedPaymentCreation', function() {
const localInstructions = _.defaults({
maxFee: '0.000012'
}, instructions);
return this.api.prepareSuspendedPaymentCreation(
address, requests.prepareSuspendedPaymentCreation,
localInstructions).then(
_.partial(checkResult, responses.prepareSuspendedPaymentCreation,
'prepare'));
});
it('prepareSuspendedPaymentExecution', function() {
return this.api.prepareSuspendedPaymentExecution(
address, requests.prepareSuspendedPaymentExecution, instructions).then(
_.partial(checkResult, responses.prepareSuspendedPaymentExecution,
'prepare'));
});
it('prepareSuspendedPaymentCancellation', function() {
return this.api.prepareSuspendedPaymentCancellation(
address, requests.prepareSuspendedPaymentCancellation, instructions).then(
_.partial(checkResult, responses.prepareSuspendedPaymentCancellation,
'prepare'));
});
it('sign', function() {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
const result = this.api.sign(requests.sign.txJSON, secret);

View File

@@ -7,6 +7,12 @@ module.exports = {
preparePaymentAllOptions: require('./prepare-payment-all-options'),
preparePaymentNoCounterparty: require('./prepare-payment-no-counterparty'),
prepareSettings: require('./prepare-settings'),
prepareSuspendedPaymentCreation:
require('./prepare-suspended-payment-creation'),
prepareSuspendedPaymentExecution:
require('./prepare-suspended-payment-execution'),
prepareSuspendedPaymentCancellation:
require('./prepare-suspended-payment-cancellation'),
prepareTrustline: {
simple: require('./prepare-trustline-simple'),
complex: require('./prepare-trustline')

View File

@@ -0,0 +1,4 @@
{
"owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"paymentSequence": 1234
}

View File

@@ -0,0 +1,19 @@
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"maxAmount": {
"value": "0.01",
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
}
},
"destination": {
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"amount": {
"value": "0.01",
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
}
},
"allowCancelAfter": 1440694329002
}

View File

@@ -0,0 +1,7 @@
{
"owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"paymentSequence": 1234,
"method": 1,
"digest": "8F434346648F6B96DF89DDA901C5176B10A6D83961DD3C1AC88B59B2DC327AA4",
"proof": "whatever"
}

View File

@@ -47,6 +47,12 @@ module.exports = {
setTransferRate: require('./prepare-settings-set-transfer-rate.json'),
fieldClear: require('./prepare-settings-field-clear.json')
},
prepareSuspendedPaymentCreation:
require('./prepare-suspended-payment-creation'),
prepareSuspendedPaymentExecution:
require('./prepare-suspended-payment-execution'),
prepareSuspendedPaymentCancellation:
require('./prepare-suspended-payment-cancellation'),
prepareTrustline: {
simple: require('./prepare-trustline-simple.json'),
complex: require('./prepare-trustline.json')

View File

@@ -0,0 +1,8 @@
{
"txJSON": "{\"Flags\":0,\"TransactionType\":\"SuspendedPaymentCancel\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "12",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}

View File

@@ -0,0 +1,8 @@
{
"txJSON": "{\"Flags\":0,\"TransactionType\":\"SuspendedPaymentCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"CancelAfter\":494009529,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "12",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}

View File

@@ -0,0 +1,8 @@
{
"txJSON": "{\"Flags\":0,\"TransactionType\":\"SuspendedPaymentFinish\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"Method\":1,\"Digest\":\"8F434346648F6B96DF89DDA901C5176B10A6D83961DD3C1AC88B59B2DC327AA4\",\"Proof\":\"7768617465766572\",\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "12",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}

View File

@@ -1,4 +1,4 @@
/* eslint-disable no-new, max-len, no-comma-dangle, indent */
/* eslint-disable no-new, max-len, no-comma-dangle, indent, max-nested-callbacks */
'use strict';
@@ -14,7 +14,9 @@ const Amount = require('ripple-lib').Amount;
const PathFind = require('ripple-lib')._test.PathFind;
const Log = require('ripple-lib')._test.Log;
let options, remote, callback;
let options;
let remote;
let callback;
const ADDRESS = 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS';
const LEDGER_INDEX = 9592219;
@@ -2095,4 +2097,50 @@ describe('Remote', function() {
]
});
});
it('Construct SuspendedPaymentCreate transaction', function() {
const tx = remote.createTransaction('SuspendedPaymentCreate', {
account: TX_JSON.Account,
destination: TX_JSON.Destination,
amount: TX_JSON.Amount
});
assert.deepEqual(tx.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentCreate',
Account: TX_JSON.Account,
Destination: TX_JSON.Destination,
Amount: TX_JSON.Amount
});
});
it('Construct SuspendedPaymentFinish transaction', function() {
const tx = remote.createTransaction('SuspendedPaymentFinish', {
account: TX_JSON.Account,
owner: TX_JSON.Account,
paymentSequence: 1234
});
assert.deepEqual(tx.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentFinish',
Account: TX_JSON.Account,
Owner: TX_JSON.Account,
OfferSequence: 1234
});
});
it('Construct SuspendedPaymentCancel transaction', function() {
const tx = remote.createTransaction('SuspendedPaymentCancel', {
account: TX_JSON.Account,
owner: TX_JSON.Account,
paymentSequence: 1234
});
assert.deepEqual(tx.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentCancel',
Account: TX_JSON.Account,
Owner: TX_JSON.Account,
OfferSequence: 1234
});
});
});

View File

@@ -1,4 +1,4 @@
/* eslint-disable max-len */
/* eslint-disable max-len, max-nested-callbacks */
'use strict';
@@ -2182,4 +2182,116 @@ describe('Transaction', function() {
{Signer: s1}
]);
});
it('Construct SuspendedPaymentCreate transaction', function() {
const transaction = new Transaction().suspendedPaymentCreate({
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
destination: 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe',
amount: '1/USD/r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe'
});
assert(transaction instanceof Transaction);
assert.deepEqual(transaction.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentCreate',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
Destination: 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe',
Amount: {
value: '1',
currency: 'USD',
issuer: 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe'
}
});
});
it('Set Digest', function() {
const transaction = new Transaction();
assert.strictEqual(transaction.tx_json.Digest, undefined);
transaction.setType('SuspendedPaymentCreate');
assert.throws(function() {
transaction.setDigest('foo');
}, /Error: Digest must be a valid Hash256/);
const hash = '8F434346648F6B96DF89DDA901C5176B10A6D83961DD3C1AC88B59B2DC327AA4';
transaction.setDigest(hash);
assert.strictEqual(transaction.tx_json.Digest, hash);
});
it('Set CancelAfter', function() {
const transaction = new Transaction();
assert.strictEqual(transaction.tx_json.CancelAfter, undefined);
transaction.setType('SuspendedPaymentCreate');
assert.throws(function() {
transaction.setAllowCancelAfter('foo');
}, /Error: CancelAfter must be a valid UInt32/);
transaction.setAllowCancelAfter(1441043377523);
assert.strictEqual(transaction.tx_json.CancelAfter, 494358578);
});
it('Set FinishAfter', function() {
const transaction = new Transaction();
assert.strictEqual(transaction.tx_json.FinishAfter, undefined);
transaction.setType('SuspendedPaymentCreate');
assert.throws(function() {
transaction.setAllowExecuteAfter('foo');
}, /Error: FinishAfter must be a valid UInt32/);
transaction.setAllowExecuteAfter(1441043377523);
assert.strictEqual(transaction.tx_json.FinishAfter, 494358578);
});
it('Construct SuspendedPaymentFinish transaction', function() {
const transaction = new Transaction().suspendedPaymentFinish({
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
owner: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
paymentSequence: 1234
});
assert(transaction instanceof Transaction);
assert.deepEqual(transaction.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentFinish',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
Owner: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
OfferSequence: 1234
});
});
it('Set Method', function() {
const transaction = new Transaction();
assert.strictEqual(transaction.tx_json.Method, undefined);
transaction.setType('SuspendedPaymentFinish');
assert.throws(function() {
transaction.setMethod('foo');
}, /Error: Method must be a valid UInt8/);
transaction.setMethod(1);
assert.strictEqual(transaction.tx_json.Method, 1);
});
it('Set Proof', function() {
const transaction = new Transaction();
assert.strictEqual(transaction.tx_json.Proof, undefined);
transaction.setType('SuspendedPaymentFinish');
transaction.setProof('foo');
assert.strictEqual(transaction.tx_json.Proof, '666F6F');
});
it('Construct SuspendedPaymentCancel transaction', function() {
const transaction = new Transaction().suspendedPaymentCancel({
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
owner: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
paymentSequence: 1234
});
assert(transaction instanceof Transaction);
assert.deepEqual(transaction.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentCancel',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
Owner: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
OfferSequence: 1234
});
});
});