From da36457d5c089c30a25bd8f38aa5faaea9437fbf Mon Sep 17 00:00:00 2001 From: wilsonianb Date: Thu, 23 Mar 2017 16:40:33 -0700 Subject: [PATCH] Update condition and fulfillment for escrow Calculate escrowFinish fulfillment fee --- docs/index.md | 12 ++++---- src/common/connection.js | 17 +++++++++++ .../specifications/escrow-creation.json | 5 ++-- .../specifications/escrow-execution.json | 7 +++-- src/ledger/parse/escrow-execution.js | 2 +- src/transaction/escrow-creation.js | 7 +++-- src/transaction/escrow-execution.js | 2 +- src/transaction/utils.js | 29 ++++++++++++------- test/api-test.js | 14 +++++++++ .../requests/prepare-escrow-execution.json | 4 +-- .../get-transaction-escrow-execution.json | 4 +-- .../responses/prepare-escrow-execution.json | 4 +-- .../fixtures/rippled/tx/escrow-execution.json | 4 +-- 13 files changed, 76 insertions(+), 35 deletions(-) diff --git a/docs/index.md b/docs/index.md index 2413ca4c..0bd378a3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -603,8 +603,8 @@ memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the { "owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", "escrowSequence": 1234, - "condition": "712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D", - "fulfillment": "this must have 32 characters...." + "condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", + "fulfillment": "A0028000" } ``` @@ -3298,8 +3298,8 @@ const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59'; const escrowExecution = { "owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", "escrowSequence": 1234, - "condition": "712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D", - "fulfillment": "this must have 32 characters...." + "condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", + "fulfillment": "A0028000" }; return api.prepareEscrowExecution(address, escrowExecution).then(prepared => {/* ... */}); @@ -3308,9 +3308,9 @@ return api.prepareEscrowExecution(address, escrowExecution).then(prepared => ```json { - "txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"EscrowFinish\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"Condition\":\"712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D\",\"Fulfillment\":\"74686973206D757374206861766520333220636861726163746572732E2E2E2E\",\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}", + "txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"EscrowFinish\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"Condition\":\"A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100\",\"Fulfillment\":\"A0028000\",\"LastLedgerSequence\":8820051,\"Fee\":\"396\",\"Sequence\":23}", "instructions": { - "fee": "0.000012", + "fee": "0.000396", "sequence": 23, "maxLedgerVersion": 8820051 } diff --git a/src/common/connection.js b/src/common/connection.js index a7ae49a7..0e962408 100644 --- a/src/common/connection.js +++ b/src/common/connection.js @@ -42,6 +42,8 @@ class Connection extends EventEmitter { this._retryTimer = null this._onOpenErrorBound = null this._onUnexpectedCloseBound = null + this._fee_base = null + this._fee_ref = null } _updateLedgerVersions(data) { @@ -55,6 +57,11 @@ class Connection extends EventEmitter { } } + _updateFees(data) { + this._fee_base = Number(data.fee_base) + this._fee_ref = Number(data.fee_ref) + } + // return value is array of arguments to Connection.emit _parseMessage(message) { const data = JSON.parse(message) @@ -66,6 +73,7 @@ class Connection extends EventEmitter { } else if (isStreamMessageType(data.type)) { if (data.type === 'ledgerClosed') { this._updateLedgerVersions(data) + this._updateFees(data) } return [data.type, data] } else if (data.type === undefined && data.error) { @@ -173,6 +181,7 @@ class Connection extends EventEmitter { } this._updateLedgerVersions(data) + this._updateFees(data) this._rebindOnUnxpectedClose() this._retry = 0 @@ -354,6 +363,14 @@ class Connection extends EventEmitter { return this.hasLedgerVersions(ledgerVersion, ledgerVersion) } + getFeeBase() { + return this._whenReady(Promise.resolve(Number(this._fee_base))) + } + + getFeeRef() { + return this._whenReady(Promise.resolve(Number(this._fee_ref))) + } + _send(message) { if (this._trace) { this._console.log(message) diff --git a/src/common/schemas/specifications/escrow-creation.json b/src/common/schemas/specifications/escrow-creation.json index 0c7b52ad..eb4372d9 100644 --- a/src/common/schemas/specifications/escrow-creation.json +++ b/src/common/schemas/specifications/escrow-creation.json @@ -14,8 +14,9 @@ }, "memos": {"$ref": "memos"}, "condition": { - "$ref": "hash256", - "description": "If present, fulfillment is required upon execution." + "type": "string", + "description": "If present, fulfillment is required upon execution.", + "pattern": "^[A-F0-9]{0,256}$" }, "allowCancelAfter": { "type": "string", diff --git a/src/common/schemas/specifications/escrow-execution.json b/src/common/schemas/specifications/escrow-execution.json index 23fcd3fd..db08248b 100644 --- a/src/common/schemas/specifications/escrow-execution.json +++ b/src/common/schemas/specifications/escrow-execution.json @@ -14,13 +14,14 @@ "description": "The [account sequence number](#account-sequence-number) of the [Escrow Creation](#escrow-creation) transaction for the escrow to execute." }, "condition": { - "$ref": "hash256", - "description": "The original `condition` from the escrow creation transaction. This is sha256 hash of `fulfillment` string. It is replicated here so that the relatively expensive hashing operation can be delegated to a server without ledger history and the server with ledger history only has to do a quick comparison of the old condition with the new condition." + "type": "string", + "description": "The original `condition` from the escrow creation transaction. This is sha256 hash of `fulfillment` string. It is replicated here so that the relatively expensive hashing operation can be delegated to a server without ledger history and the server with ledger history only has to do a quick comparison of the old condition with the new condition.", + "pattern": "^[A-F0-9]{0,256}$" }, "fulfillment": { "type": "string", "description": "A value that produces the condition when hashed. It must be 32 charaters long and contain only 8-bit characters.", - "pattern": "^[\\x00-\\xFF]{32}$" + "pattern": "^[A-F0-9]+$" } }, "required": ["owner", "escrowSequence"], diff --git a/src/ledger/parse/escrow-execution.js b/src/ledger/parse/escrow-execution.js index 7d254ba6..0fd444fc 100644 --- a/src/ledger/parse/escrow-execution.js +++ b/src/ledger/parse/escrow-execution.js @@ -11,7 +11,7 @@ function parseEscrowExecution(tx: Object): Object { owner: tx.Owner, escrowSequence: tx.OfferSequence, condition: tx.Condition, - fulfillment: tx.Fulfillment ? utils.hexToString(tx.Fulfillment) : undefined + fulfillment: tx.Fulfillment }) } diff --git a/src/transaction/escrow-creation.js b/src/transaction/escrow-creation.js index 744b722f..911b524c 100644 --- a/src/transaction/escrow-creation.js +++ b/src/transaction/escrow-creation.js @@ -3,6 +3,7 @@ const _ = require('lodash') const utils = require('./utils') const {validate, iso8601ToRippleTime, toRippledAmount} = utils.common +const ValidationError = utils.common.errors.ValidationError import type {Instructions, Prepare} from './types.js' import type {Adjustment, MaxAdjustment, Memo} from '../common/types.js' @@ -44,9 +45,9 @@ function createEscrowCreationTransaction(account: string, txJSON.Memos = _.map(payment.memos, utils.convertMemo) } if (Boolean(payment.allowCancelAfter) && Boolean(payment.allowExecuteAfter) && - payment.CancelAfter <= payment.FinishAfter) { - throw new ValidationError('"CancelAfter" must be after "FinishAfter" on' - + ' EscrowCreate.') + txJSON.CancelAfter <= txJSON.FinishAfter) { + throw new ValidationError('"CancelAfter" must be after "FinishAfter" for' + + ' EscrowCreate') } return txJSON } diff --git a/src/transaction/escrow-execution.js b/src/transaction/escrow-execution.js index 06646199..848b69b9 100644 --- a/src/transaction/escrow-execution.js +++ b/src/transaction/escrow-execution.js @@ -33,7 +33,7 @@ function createEscrowExecutionTransaction(account: string, txJSON.Condition = payment.condition } if (payment.fulfillment !== undefined) { - txJSON.Fulfillment = utils.convertStringToHex(payment.fulfillment) + txJSON.Fulfillment = payment.fulfillment } if (payment.memos !== undefined) { txJSON.Memos = _.map(payment.memos, utils.convertMemo) diff --git a/src/transaction/utils.js b/src/transaction/utils.js index 66b6c508..f12b8905 100644 --- a/src/transaction/utils.js +++ b/src/transaction/utils.js @@ -27,8 +27,8 @@ function setCanonicalFlag(txJSON) { txJSON.Flags = txJSON.Flags >>> 0 } -function scaleValue(value, multiplier) { - return (new BigNumber(value)).times(multiplier).toString() +function scaleValue(value, multiplier, extra=0) { + return (new BigNumber(value)).times(multiplier).plus(extra).toString() } function prepareTransaction(txJSON: Object, api: Object, @@ -63,15 +63,22 @@ function prepareTransaction(txJSON: Object, api: Object, } const cushion = api._feeCushion return common.serverInfo.getFee(api.connection, cushion).then(fee => { - const feeDrops = common.xrpToDrops(fee) - if (instructions.maxFee !== undefined) { - const maxFeeDrops = common.xrpToDrops(instructions.maxFee) - const normalFee = BigNumber.min(feeDrops, maxFeeDrops).toString() - txJSON.Fee = scaleValue(normalFee, multiplier) - } else { - txJSON.Fee = scaleValue(feeDrops, multiplier) - } - return txJSON + return api.connection.getFeeRef().then(feeRef => { + const extraFee = + (txJSON.TransactionType !== 'EscrowFinish' || + txJSON.Fulfillment === undefined) ? 0 : + (cushion * feeRef * (32 + Math.floor( + new Buffer(txJSON.Fulfillment, 'hex').length / 16))) + const feeDrops = common.xrpToDrops(fee) + if (instructions.maxFee !== undefined) { + const maxFeeDrops = common.xrpToDrops(instructions.maxFee) + const normalFee = scaleValue(feeDrops, multiplier, extraFee) + txJSON.Fee = BigNumber.min(normalFee, maxFeeDrops).toString(); + } else { + txJSON.Fee = scaleValue(feeDrops, multiplier, extraFee) + } + return txJSON + }) }) } diff --git a/test/api-test.js b/test/api-test.js index 46d2389d..97342c70 100644 --- a/test/api-test.js +++ b/test/api-test.js @@ -1136,6 +1136,20 @@ describe('RippleAPI', function() { }, done); }); + it('getFeeBase', function(done) { + this.api.connection.getFeeBase().then(fee => { + assert.strictEqual(fee, 10); + done(); + }, done); + }); + + it('getFeeRef', function(done) { + this.api.connection.getFeeRef().then(fee => { + assert.strictEqual(fee, 10); + done(); + }, done); + }); + it('getLedger', function() { return this.api.getLedger().then( _.partial(checkResult, responses.getLedger.header, 'getLedger')); diff --git a/test/fixtures/requests/prepare-escrow-execution.json b/test/fixtures/requests/prepare-escrow-execution.json index 39abe9a3..b49200c5 100644 --- a/test/fixtures/requests/prepare-escrow-execution.json +++ b/test/fixtures/requests/prepare-escrow-execution.json @@ -1,6 +1,6 @@ { "owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", "escrowSequence": 1234, - "condition": "712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D", - "fulfillment": "this must have 32 characters...." + "condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", + "fulfillment": "A0028000" } diff --git a/test/fixtures/responses/get-transaction-escrow-execution.json b/test/fixtures/responses/get-transaction-escrow-execution.json index dd6e5a0a..10a25f08 100644 --- a/test/fixtures/responses/get-transaction-escrow-execution.json +++ b/test/fixtures/responses/get-transaction-escrow-execution.json @@ -6,8 +6,8 @@ "specification": { "owner": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "escrowSequence": 5, - "condition": "632F2F3E437AE720C397994A985B5D21FE186DE61523A9CA3E8709CC581671A1", - "fulfillment": "whateverthis bemusthave 32 chars" + "condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", + "fulfillment": "A0028000" }, "outcome": { "result": "tesSUCCESS", diff --git a/test/fixtures/responses/prepare-escrow-execution.json b/test/fixtures/responses/prepare-escrow-execution.json index 7125c361..4aa0cc56 100644 --- a/test/fixtures/responses/prepare-escrow-execution.json +++ b/test/fixtures/responses/prepare-escrow-execution.json @@ -1,7 +1,7 @@ { - "txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"EscrowFinish\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"Condition\":\"712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D\",\"Fulfillment\":\"74686973206D757374206861766520333220636861726163746572732E2E2E2E\",\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}", + "txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"EscrowFinish\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"Condition\":\"A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100\",\"Fulfillment\":\"A0028000\",\"LastLedgerSequence\":8820051,\"Fee\":\"396\",\"Sequence\":23}", "instructions": { - "fee": "0.000012", + "fee": "0.000396", "sequence": 23, "maxLedgerVersion": 8820051 } diff --git a/test/fixtures/rippled/tx/escrow-execution.json b/test/fixtures/rippled/tx/escrow-execution.json index 82a6333d..5b6c1055 100644 --- a/test/fixtures/rippled/tx/escrow-execution.json +++ b/test/fixtures/rippled/tx/escrow-execution.json @@ -2,13 +2,13 @@ "id": 0, "result": { "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", - "Condition": "632F2F3E437AE720C397994A985B5D21FE186DE61523A9CA3E8709CC581671A1", + "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", "Fee": "12", "Flags": 2147483648, "LastLedgerSequence": 113, "OfferSequence": 5, "Owner": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", - "Fulfillment": "7768617465766572746869732062656D75737468617665203332206368617273", + "Fulfillment": "A0028000", "Sequence": 6, "SigningPubKey": "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020", "TransactionType": "EscrowFinish",