Maximum fee values (#902)

* Add maxFeeXRP (default 2 XRP) as an optional RippleAPI constructor parameter
  - No calculated or specified fee can exceed this value
  - If the fee exceeds 2 XRP, throw a ValidationError
* sign() - throw ValidationError when Fee exceeds maxFeeXRP
* Document getFee parameters
* Explain new fee limits in HISTORY.md
* Deprecate `maxFee`
This commit is contained in:
Elliot Lee
2018-06-07 23:29:24 -07:00
committed by GitHub
parent 7c92adbf45
commit e07fa11923
13 changed files with 290 additions and 26 deletions

View File

@@ -392,6 +392,37 @@ describe('RippleAPI', function () {
instructions).then(_.partial(checkResult,
responses.preparePayment.minAmount, 'prepare'));
});
it('preparePayment - throws when fee exceeds 2 XRP', function () {
const localInstructions = _.defaults({
fee: '2.1'
}, instructions);
assert.throws(() => {
this.api.preparePayment(
address, requests.preparePayment.normal, localInstructions)
}, /Fee of 2\.1 XRP exceeds max of 2 XRP\. To use this fee, increase `maxFeeXRP` in the RippleAPI constructor\./)
});
it('preparePayment - allows fee exceeding 2 XRP when maxFeeXRP is higher', function () {
this.api._maxFeeXRP = '2.2'
const localInstructions = _.defaults({
fee: '2.1'
}, instructions);
const expectedResponse = {
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"SendMax\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"LastLedgerSequence\":8820051,\"Fee\":\"2100000\",\"Sequence\":23}",
"instructions": {
"fee": "2.1",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}
return this.api.preparePayment(
address, requests.preparePayment.normal, localInstructions).then(
_.partial(checkResult, expectedResponse, 'prepare'));
});
});
it('prepareOrder - buy order', function () {
@@ -823,6 +854,62 @@ describe('RippleAPI', function () {
assert.deepEqual(signature, responses.sign.signAs);
});
it('sign - throws when Fee exceeds maxFeeXRP (in drops)', function () {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
const request = {
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"AccountSet\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Domain\":\"726970706C652E636F6D\",\"LastLedgerSequence\":8820051,\"Fee\":\"2010000\",\"Sequence\":23,\"SigningPubKey\":\"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8\"}",
"instructions": {
"fee": "2.01",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}
assert.throws(() => {
this.api.sign(request.txJSON, secret)
}, /Fee" should not exceed "2000000"\. To use a higher fee, set `maxFeeXRP` in the RippleAPI constructor\./)
});
it('sign - throws when Fee exceeds maxFeeXRP (in drops) - custom maxFeeXRP', function () {
this.api._maxFeeXRP = '1.9'
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
const request = {
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"AccountSet\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Domain\":\"726970706C652E636F6D\",\"LastLedgerSequence\":8820051,\"Fee\":\"2010000\",\"Sequence\":23,\"SigningPubKey\":\"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8\"}",
"instructions": {
"fee": "2.01",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}
assert.throws(() => {
this.api.sign(request.txJSON, secret)
}, /Fee" should not exceed "1900000"\. To use a higher fee, set `maxFeeXRP` in the RippleAPI constructor\./)
});
it('sign - permits fee exceeding 2000000 drops when maxFeeXRP is higher than 2 XRP', function () {
this.api._maxFeeXRP = '2.1'
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
const request = {
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"AccountSet\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Domain\":\"726970706C652E636F6D\",\"LastLedgerSequence\":8820051,\"Fee\":\"2010000\",\"Sequence\":23,\"SigningPubKey\":\"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8\"}",
"instructions": {
"fee": "2.01",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}
const result = this.api.sign(request.txJSON, secret)
const expectedResponse = {
signedTransaction: "12000322800000002400000017201B008695536840000000001EAB90732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8744630440220384FBB48EEE7B0E58BD89294A609F9407C51FBE8FA08A4B305B22E9A7489D66602200152315EFE752DA381E74493419871550D206AC6503841DA5F8C30E35D9E3892770A726970706C652E636F6D81145E7B112523F68D2F5E879DB4EAC51C6698A69304",
id: "A1586D6AF7B0821E7075E12A0132D9EB50BC1874A0749441201497F7561795FB"
}
assert.deepEqual(result, expectedResponse)
schemaValidator.schemaValidate('sign', result)
});
it('submit', function () {
return this.api.submit(responses.sign.normal.signedTransaction).then(
_.partial(checkResult, responses.submit, 'submit'));
@@ -1663,6 +1750,76 @@ describe('RippleAPI', function () {
});
});
it('getFee - high load_factor', function () {
this.api.connection._send(JSON.stringify({
command: 'config',
data: { highLoadFactor: true }
}));
return this.api.getFee().then(fee => {
assert.strictEqual(fee, '2');
});
});
it('fee - default maxFee of 2 XRP', function () {
this.api._feeCushion = 1000000;
const expectedResponse = {
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"SendMax\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"LastLedgerSequence\":8820051,\"Fee\":\"2000000\",\"Sequence\":23}",
"instructions": {
"fee": "2",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}
return this.api.preparePayment(
address, requests.preparePayment.normal, instructions).then(
_.partial(checkResult, expectedResponse, 'prepare'));
});
it('fee - capped to maxFeeXRP when maxFee exceeds maxFeeXRP', function () {
this.api._feeCushion = 1000000
this.api._maxFeeXRP = '3'
const localInstructions = _.defaults({
maxFee: '4'
}, instructions);
const expectedResponse = {
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"SendMax\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"LastLedgerSequence\":8820051,\"Fee\":\"3000000\",\"Sequence\":23}",
"instructions": {
"fee": "3",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}
return this.api.preparePayment(
address, requests.preparePayment.normal, localInstructions).then(
_.partial(checkResult, expectedResponse, 'prepare'));
});
it('fee - capped to maxFee', function () {
this.api._feeCushion = 1000000
this.api._maxFeeXRP = '5'
const localInstructions = _.defaults({
maxFee: '4'
}, instructions);
const expectedResponse = {
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"SendMax\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"LastLedgerSequence\":8820051,\"Fee\":\"4000000\",\"Sequence\":23}",
"instructions": {
"fee": "4",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}
return this.api.preparePayment(
address, requests.preparePayment.normal, localInstructions).then(
_.partial(checkResult, expectedResponse, 'prepare'));
});
it('disconnect & isConnected', function () {
assert.strictEqual(this.api.isConnected(), true);
return this.api.disconnect().then(() => {

View File

@@ -152,7 +152,39 @@ module.exports = function createMockRippled(port) {
mock.on('request_server_info', function (request, conn) {
assert.strictEqual(request.command, 'server_info');
if (conn.config.returnErrorOnServerInfo) {
if (conn.config.highLoadFactor) {
const response = {
"id": 0,
"status": "success",
"type": "response",
"result": {
"info": {
"build_version": "0.24.0-rc1",
"complete_ledgers": "32570-6595042",
"hostid": "ARTS",
"io_latency_ms": 1,
"last_close": {
"converge_time_s": 2.007,
"proposers": 4
},
"load_factor": 4294967296,
"peers": 53,
"pubkey_node": "n94wWvFUmaKGYrKUGgpv1DyYgDeXRGdACkNQaSe7zJiy5Znio7UC",
"server_state": "full",
"validated_ledger": {
"age": 5,
"base_fee_xrp": 0.00001,
"hash": "4482DEE5362332F54A4036ED57EE1767C9F33CF7CE5A6670355C16CECE381D46",
"reserve_base_xrp": 20,
"reserve_inc_xrp": 5,
"seq": 6595042
},
"validation_quorum": 3
}
}
}
conn.send(createResponse(request, response));
} else if (conn.config.returnErrorOnServerInfo) {
conn.send(createResponse(request, fixtures.server_info.error));
} else if (conn.config.disconnectOnServerInfo) {
conn.close();