diff --git a/docs/index.md b/docs/index.md index 7887eaba..ce398019 100644 --- a/docs/index.md +++ b/docs/index.md @@ -63,6 +63,8 @@ - [combine](#combine) - [submit](#submit) - [generateAddress](#generateaddress) + - [signPaymentChannelClaim](#signpaymentchannelclaim) + - [verifyPaymentChannelClaim](#verifypaymentchannelclaim) - [computeLedgerHash](#computeledgerhash) - [API Events](#api-events) - [ledger](#ledger) @@ -3691,6 +3693,84 @@ return api.generateAddress(); ``` +## signPaymentChannelClaim + +`signPaymentChannelClaim(channel: string, amount: string, privateKey: string): string` + +Sign a payment channel claim. The signature can be submitted in a subsequent [PaymentChannelClaim](#preparePaymmentChannelClaim) transaction. + +### Parameters + +Name | Type | Description +---- | ---- | ----------- +channel | string | 256-bit hexadecimal channel identifier. +amount | [value](#value) | Amount of XRP authorized by the claim. +privateKey | string | The private key to sign the payment channel claim. + +### Return Value + +This method returns a signature string: + +Name | Type | Description +---- | ---- | ----------- + | string | The hexadecimal representation of a signature. + +### Example + +```javascript +const channel = + '3E18C05AD40319B809520F1A136370C4075321B285217323396D6FD9EE1E9037'; +const amount = '.00001'; +const privateKey = + 'ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A'; +return api.signPaymentChannelClaim(channel, amount, privateKey); +``` + + +```json +"3045022100B5C54654221F154347679B97AE7791CBEF5E6772A3F894F9C781B8F1B400F89F022021E466D29DC5AEB5DFAFC76E8A88D2E388EBD25A84143B6AC3B647F479CB89B7" +``` + + +## verifyPaymentChannelClaim + +`verifyPaymentChannelClaim(channel: string, amount: string, signature: string, publicKey: string): boolean` + +Verify a payment channel claim signature. + +### Parameters + +Name | Type | Description +---- | ---- | ----------- +channel | string | 256-bit hexadecimal channel identifier. +amount | [value](#value) | Amount of XRP authorized by the claim. +signature | string | Signature of this claim. +publicKey | string | Public key of the channel's sender + +### Return Value + +This method returns `true` if the claim signature is valid. + +Name | Type | Description +---- | ---- | ----------- + | boolean | + +### Example + +```javascript +const channel = + '3E18C05AD40319B809520F1A136370C4075321B285217323396D6FD9EE1E9037'; +const amount = '.00001'; +const signature = "3045022100B5C54654221F154347679B97AE7791CBEF5E6772A3F894F9C781B8F1B400F89F022021E466D29DC5AEB5DFAFC76E8A88D2E388EBD25A84143B6AC3B647F479CB89B7"; +const publicKey = + '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'; +return api.verifyPaymentChannelClaim(channel, amount, signature, publicKey); +``` + +```json +true +``` + ## computeLedgerHash `computeLedgerHash(ledger: Object): string` diff --git a/docs/src/index.md.ejs b/docs/src/index.md.ejs index a99c6a42..d1e7f194 100644 --- a/docs/src/index.md.ejs +++ b/docs/src/index.md.ejs @@ -37,5 +37,7 @@ <% include combine.md.ejs %> <% include submit.md.ejs %> <% include generateAddress.md.ejs %> +<% include signPaymentChannelClaim.md.ejs %> +<% include verifyPaymentChannelClaim.md.ejs %> <% include computeLedgerHash.md.ejs %> <% include events.md.ejs %> diff --git a/docs/src/signPaymentChannelClaim.md.ejs b/docs/src/signPaymentChannelClaim.md.ejs new file mode 100644 index 00000000..06e4399b --- /dev/null +++ b/docs/src/signPaymentChannelClaim.md.ejs @@ -0,0 +1,28 @@ +## signPaymentChannelClaim + +`signPaymentChannelClaim(channel: string, amount: string, privateKey: string): string` + +Sign a payment channel claim. The signature can be submitted in a subsequent [PaymentChannelClaim](#preparePaymmentChannelClaim) transaction. + +### Parameters + +<%- renderSchema("input/sign-payment-channel-claim.json") %> + +### Return Value + +This method returns a signature string: + +<%- renderSchema("output/sign-payment-channel-claim.json") %> + +### Example + +```javascript +const channel = + '3E18C05AD40319B809520F1A136370C4075321B285217323396D6FD9EE1E9037'; +const amount = '.00001'; +const privateKey = + 'ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A'; +return api.signPaymentChannelClaim(channel, amount, privateKey); +``` + +<%- renderFixture("responses/sign-payment-channel-claim.json") %> diff --git a/docs/src/verifyPaymentChannelClaim.md.ejs b/docs/src/verifyPaymentChannelClaim.md.ejs new file mode 100644 index 00000000..6ca3ed40 --- /dev/null +++ b/docs/src/verifyPaymentChannelClaim.md.ejs @@ -0,0 +1,31 @@ +## verifyPaymentChannelClaim + +`verifyPaymentChannelClaim(channel: string, amount: string, signature: string, publicKey: string): boolean` + +Verify a payment channel claim signature. + +### Parameters + +<%- renderSchema("input/verify-payment-channel-claim.json") %> + +### Return Value + +This method returns `true` if the claim signature is valid. + +<%- renderSchema("output/verify-payment-channel-claim.json") %> + +### Example + +```javascript +const channel = + '3E18C05AD40319B809520F1A136370C4075321B285217323396D6FD9EE1E9037'; +const amount = '.00001'; +const signature = <%- importFile("test/fixtures/responses/sign-payment-channel-claim.json") %>; +const publicKey = + '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'; +return api.verifyPaymentChannelClaim(channel, amount, signature, publicKey); +``` + +```json +true +``` diff --git a/src/api.js b/src/api.js index 0f872be3..2c3cd9ee 100644 --- a/src/api.js +++ b/src/api.js @@ -56,6 +56,10 @@ const errors = require('./common').errors const generateAddress = require('./offline/generate-address').generateAddressAPI const computeLedgerHash = require('./offline/ledgerhash') +const signPaymentChannelClaim = + require('./offline/sign-payment-channel-claim') +const verifyPaymentChannelClaim = + require('./offline/verify-payment-channel-claim') const getLedger = require('./ledger/ledger') type APIOptions = { @@ -146,6 +150,8 @@ _.assign(RippleAPI.prototype, { generateAddress, computeLedgerHash, + signPaymentChannelClaim, + verifyPaymentChannelClaim, errors }) diff --git a/src/common/schema-validator.js b/src/common/schema-validator.js index bd357457..803f88a0 100644 --- a/src/common/schema-validator.js +++ b/src/common/schema-validator.js @@ -76,6 +76,8 @@ function loadSchemas() { require('./schemas/output/get-transaction.json'), require('./schemas/output/get-transactions.json'), require('./schemas/output/get-trustlines.json'), + require('./schemas/output/sign-payment-channel-claim.json'), + require('./schemas/output/verify-payment-channel-claim.json'), require('./schemas/input/get-balances.json'), require('./schemas/input/get-balance-sheet.json'), require('./schemas/input/get-ledger.json'), @@ -103,6 +105,8 @@ function loadSchemas() { require('./schemas/input/sign.json'), require('./schemas/input/submit.json'), require('./schemas/input/generate-address.json'), + require('./schemas/input/sign-payment-channel-claim.json'), + require('./schemas/input/verify-payment-channel-claim.json'), require('./schemas/input/combine.json') ] const titles = _.map(schemas, schema => schema.title) diff --git a/src/common/schemas/input/sign-payment-channel-claim.json b/src/common/schemas/input/sign-payment-channel-claim.json new file mode 100644 index 00000000..5266facf --- /dev/null +++ b/src/common/schemas/input/sign-payment-channel-claim.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "signPaymentChannelClaimParameters", + "type": "object", + "properties": { + "channel": { + "$ref": "hash256", + "description": "256-bit hexadecimal channel identifier." + }, + "amount": { + "$ref": "value", + "description": "Amount of XRP authorized by the claim." + }, + "privateKey": { + "$ref": "publicKey", + "description": "The private key to sign the payment channel claim." + } + }, + "additionalProperties": false, + "required": ["channel", "amount", "privateKey"] +} diff --git a/src/common/schemas/input/verify-payment-channel-claim.json b/src/common/schemas/input/verify-payment-channel-claim.json new file mode 100644 index 00000000..767267a1 --- /dev/null +++ b/src/common/schemas/input/verify-payment-channel-claim.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "verifyPaymentChannelClaimParameters", + "type": "object", + "properties": { + "channel": { + "$ref": "hash256", + "description": "256-bit hexadecimal channel identifier." + }, + "amount": { + "$ref": "value", + "description": "Amount of XRP authorized by the claim." + }, + "signature": { + "$ref": "signature", + "description": "Signature of this claim." + }, + "publicKey": { + "$ref": "publicKey", + "description": "Public key of the channel's sender" + } + }, + "additionalProperties": false, + "required": ["channel", "amount", "signature", "publicKey"] +} diff --git a/src/common/schemas/output/sign-payment-channel-claim.json b/src/common/schemas/output/sign-payment-channel-claim.json new file mode 100644 index 00000000..96129db4 --- /dev/null +++ b/src/common/schemas/output/sign-payment-channel-claim.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "signPaymentChannelClaim", + "type": "string", + "$ref": "signature", + "additionalProperties": false +} diff --git a/src/common/schemas/output/verify-payment-channel-claim.json b/src/common/schemas/output/verify-payment-channel-claim.json new file mode 100644 index 00000000..26309718 --- /dev/null +++ b/src/common/schemas/output/verify-payment-channel-claim.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "verifyPaymentChannelClaim", + "type": "boolean", + "additionalProperties": false +} diff --git a/src/common/validate.js b/src/common/validate.js index 7d91490e..73930b02 100644 --- a/src/common/validate.js +++ b/src/common/validate.js @@ -57,6 +57,10 @@ module.exports = { submit: _.partial(schemaValidate, 'submitParameters'), computeLedgerHash: _.partial(schemaValidate, 'computeLedgerHashParameters'), generateAddress: _.partial(schemaValidate, 'generateAddressParameters'), + signPaymentChannelClaim: _.partial(schemaValidate, + 'signPaymentChannelClaimParameters'), + verifyPaymentChannelClaim: _.partial(schemaValidate, + 'verifyPaymentChannelClaimParameters'), apiOptions: _.partial(schemaValidate, 'api-options'), instructions: _.partial(schemaValidate, 'instructions') } diff --git a/src/offline/sign-payment-channel-claim.js b/src/offline/sign-payment-channel-claim.js new file mode 100644 index 00000000..c9bb70e0 --- /dev/null +++ b/src/offline/sign-payment-channel-claim.js @@ -0,0 +1,20 @@ +/* @flow */ +'use strict' // eslint-disable-line strict +const common = require('../common') +const keypairs = require('ripple-keypairs') +const binary = require('ripple-binary-codec') +const {validate, xrpToDrops} = common + +function signPaymentChannelClaim(channel: string, amount: string, + privateKey: string +): string { + validate.signPaymentChannelClaim({channel, amount, privateKey}) + + const signingData = binary.encodeForSigningClaim({ + channel: channel, + amount: xrpToDrops(amount), + }) + return keypairs.sign(signingData, privateKey) +} + +module.exports = signPaymentChannelClaim diff --git a/src/offline/verify-payment-channel-claim.js b/src/offline/verify-payment-channel-claim.js new file mode 100644 index 00000000..712d5927 --- /dev/null +++ b/src/offline/verify-payment-channel-claim.js @@ -0,0 +1,20 @@ +/* @flow */ +'use strict' // eslint-disable-line strict +const common = require('../common') +const keypairs = require('ripple-keypairs') +const binary = require('ripple-binary-codec') +const {validate, xrpToDrops} = common + +function verifyPaymentChannelClaim(channel: string, amount: string, + signature: string, publicKey: string +): string { + validate.verifyPaymentChannelClaim({channel, amount, signature, publicKey}) + + const signingData = binary.encodeForSigningClaim({ + channel: channel, + amount: xrpToDrops(amount), + }) + return keypairs.verify(signingData, signature, publicKey) +} + +module.exports = verifyPaymentChannelClaim diff --git a/test/api-test.js b/test/api-test.js index 17be3698..fcc85f57 100644 --- a/test/api-test.js +++ b/test/api-test.js @@ -452,6 +452,37 @@ describe('RippleAPI', function() { }); }); + it('signPaymentChannelClaim', function() { + const privateKey = + 'ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A'; + const result = this.api.signPaymentChannelClaim( + requests.signPaymentChannelClaim.channel, + requests.signPaymentChannelClaim.amount, privateKey); + checkResult(responses.signPaymentChannelClaim, + 'signPaymentChannelClaim', result) + }); + + it('verifyPaymentChannelClaim', function() { + const publicKey = + '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'; + const result = this.api.verifyPaymentChannelClaim( + requests.signPaymentChannelClaim.channel, + requests.signPaymentChannelClaim.amount, + responses.signPaymentChannelClaim, publicKey); + checkResult(true, 'verifyPaymentChannelClaim', result) + }); + + it('verifyPaymentChannelClaim - invalid', function() { + const publicKey = + '03A6523FE4281DA48A6FD77FAF3CB77F5C7001ABA0B32BCEDE0369AC009758D7D9'; + const result = this.api.verifyPaymentChannelClaim( + requests.signPaymentChannelClaim.channel, + requests.signPaymentChannelClaim.amount, + responses.signPaymentChannelClaim, publicKey); + checkResult(false, + 'verifyPaymentChannelClaim', result) + }); + it('combine', function() { const combined = this.api.combine(requests.combine.setDomain); checkResult(responses.combine.single, 'sign', combined); diff --git a/test/fixtures/requests/index.js b/test/fixtures/requests/index.js index 89c96c11..ab5b67a6 100644 --- a/test/fixtures/requests/index.js +++ b/test/fixtures/requests/index.js @@ -61,6 +61,7 @@ module.exports = { escrow: require('./sign-escrow.json'), signAs: require('./sign-as') }, + signPaymentChannelClaim: require('./sign-payment-channel-claim'), getPaths: { normal: require('./getpaths/normal'), UsdToUsd: require('./getpaths/usd2usd'), diff --git a/test/fixtures/requests/sign-payment-channel-claim.json b/test/fixtures/requests/sign-payment-channel-claim.json new file mode 100644 index 00000000..7665c928 --- /dev/null +++ b/test/fixtures/requests/sign-payment-channel-claim.json @@ -0,0 +1,4 @@ +{ + "channel": "3E18C05AD40319B809520F1A136370C4075321B285217323396D6FD9EE1E9037", + "amount": ".00001" +} diff --git a/test/fixtures/responses/index.js b/test/fixtures/responses/index.js index 5f1cab0f..e247c514 100644 --- a/test/fixtures/responses/index.js +++ b/test/fixtures/responses/index.js @@ -134,6 +134,7 @@ module.exports = { escrow: require('./sign-escrow.json'), signAs: require('./sign-as') }, + signPaymentChannelClaim: require('./sign-payment-channel-claim'), combine: { single: require('./combine.json') }, diff --git a/test/fixtures/responses/sign-payment-channel-claim.json b/test/fixtures/responses/sign-payment-channel-claim.json new file mode 100644 index 00000000..09b68372 --- /dev/null +++ b/test/fixtures/responses/sign-payment-channel-claim.json @@ -0,0 +1 @@ +"3045022100B5C54654221F154347679B97AE7791CBEF5E6772A3F894F9C781B8F1B400F89F022021E466D29DC5AEB5DFAFC76E8A88D2E388EBD25A84143B6AC3B647F479CB89B7"