mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-26 23:25:49 +00:00
Add signWithKeypair (#769)
This commit is contained in:
@@ -3847,7 +3847,10 @@ return api.prepareCheckCash(address, checkCash).then(prepared =>
|
||||
|
||||
## sign
|
||||
|
||||
`sign(txJSON: string, secret: string, options: Object): {signedTransaction: string, id: string}`
|
||||
```
|
||||
sign(txJSON: string, secret: string, options: Object): {signedTransaction: string, id: string}
|
||||
sign(txJSON: string, keypair: Object, options: Object): {signedTransaction: string, id: string}
|
||||
```
|
||||
|
||||
Sign a prepared transaction. The signed transaction must subsequently be [submitted](#submit).
|
||||
|
||||
@@ -3856,9 +3859,12 @@ Sign a prepared transaction. The signed transaction must subsequently be [submit
|
||||
Name | Type | Description
|
||||
---- | ---- | -----------
|
||||
txJSON | string | Transaction represented as a JSON string in rippled format.
|
||||
secret | secret string | The secret of the account that is initiating the transaction.
|
||||
keypair | object | *Optional* The private and public key of the account that is initiating the transaction. (This field is exclusive with secret).
|
||||
*keypair.* privateKey | privateKey | The uppercase hexadecimal representation of the secp256k1 or Ed25519 private key.
|
||||
*keypair.* publicKey | publicKey | The uppercase hexadecimal representation of the secp256k1 or Ed25519 public key.
|
||||
options | object | *Optional* Options that control the type of signature that will be generated.
|
||||
*options.* signAs | [address](#address) | *Optional* The account that the signature should count for in multisigning.
|
||||
secret | secret string | *Optional* The secret of the account that is initiating the transaction. (This field is exclusive with keypair).
|
||||
|
||||
### Return Value
|
||||
|
||||
@@ -3874,7 +3880,8 @@ id | [id](#transaction-id) | The [Transaction ID](#transaction-id) of the signed
|
||||
```javascript
|
||||
const txJSON = '{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"726970706C652E636F6D","LastLedgerSequence":8820051,"Fee":"12","Sequence":23}';
|
||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
||||
return api.sign(txJSON, secret);
|
||||
const keypair = { privateKey: '00ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A', publicKey: '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8' };
|
||||
return api.sign(txJSON, secret); // or: api.sign(txJSON, keypair);
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
## sign
|
||||
|
||||
`sign(txJSON: string, secret: string, options: Object): {signedTransaction: string, id: string}`
|
||||
```
|
||||
sign(txJSON: string, secret: string, options: Object): {signedTransaction: string, id: string}
|
||||
sign(txJSON: string, keypair: Object, options: Object): {signedTransaction: string, id: string}
|
||||
```
|
||||
|
||||
Sign a prepared transaction. The signed transaction must subsequently be [submitted](#submit).
|
||||
|
||||
@@ -19,7 +22,8 @@ This method returns an object with the following structure:
|
||||
```javascript
|
||||
const txJSON = '{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"726970706C652E636F6D","LastLedgerSequence":8820051,"Fee":"12","Sequence":23}';
|
||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
||||
return api.sign(txJSON, secret);
|
||||
const keypair = { privateKey: '00ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A', publicKey: '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8' };
|
||||
return api.sign(txJSON, secret); // or: api.sign(txJSON, keypair);
|
||||
```
|
||||
|
||||
<%- renderFixture("responses/sign.json") %>
|
||||
|
||||
@@ -20,6 +20,7 @@ function loadSchemas() {
|
||||
require('./schemas/objects/memo.json'),
|
||||
require('./schemas/objects/memos.json'),
|
||||
require('./schemas/objects/public-key.json'),
|
||||
require('./schemas/objects/private-key.json'),
|
||||
require('./schemas/objects/uint32.json'),
|
||||
require('./schemas/objects/value.json'),
|
||||
require('./schemas/objects/source-adjustment.json'),
|
||||
|
||||
@@ -10,7 +10,23 @@
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"format": "secret",
|
||||
"description": "The secret of the account that is initiating the transaction."
|
||||
"description": "The secret of the account that is initiating the transaction. (This field is exclusive with keypair)."
|
||||
},
|
||||
"keypair": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"privateKey": {
|
||||
"type": "privateKey",
|
||||
"description": "The uppercase hexadecimal representation of the secp256k1 or Ed25519 private key."
|
||||
},
|
||||
"publicKey": {
|
||||
"type": "publicKey",
|
||||
"description": "The uppercase hexadecimal representation of the secp256k1 or Ed25519 public key."
|
||||
}
|
||||
},
|
||||
"description": "The private and public key of the account that is initiating the transaction. (This field is exclusive with secret).",
|
||||
"required": ["privateKey", "publicKey"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
@@ -25,5 +41,15 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["txJSON", "secret"]
|
||||
"required": ["txJSON"],
|
||||
"oneOf": [
|
||||
{
|
||||
"required": ["secret"],
|
||||
"not": {"required": ["keypair"]}
|
||||
},
|
||||
{
|
||||
"required": ["keypair"],
|
||||
"not": {"required": ["secret"]}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
7
src/common/schemas/objects/private-key.json
Normal file
7
src/common/schemas/objects/private-key.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "privateKey",
|
||||
"description": "The hexadecimal representation of a secp256k1 or Ed25519 private key.",
|
||||
"type": "string",
|
||||
"pattern": "^[A-F0-9]+$"
|
||||
}
|
||||
@@ -2,27 +2,32 @@ import * as utils from './utils'
|
||||
import keypairs = require('ripple-keypairs')
|
||||
import binary = require('ripple-binary-codec')
|
||||
import {computeBinaryTransactionHash} from 'ripple-hashes'
|
||||
import {SignOptions, KeyPair} from './types'
|
||||
const validate = utils.common.validate
|
||||
|
||||
function computeSignature(tx: Object, privateKey: string, signAs?: string) {
|
||||
const signingData = signAs ?
|
||||
binary.encodeForMultisigning(tx, signAs) : binary.encodeForSigning(tx)
|
||||
const signingData = signAs
|
||||
? binary.encodeForMultisigning(tx, signAs)
|
||||
: binary.encodeForSigning(tx)
|
||||
return keypairs.sign(signingData, privateKey)
|
||||
}
|
||||
|
||||
function sign(txJSON: string, secret: string, options: {signAs?: string} = {}
|
||||
): {signedTransaction: string; id: string} {
|
||||
validate.sign({txJSON, secret})
|
||||
// we can't validate that the secret matches the account because
|
||||
// the secret could correspond to the regular key
|
||||
function signWithKeypair(
|
||||
txJSON: string,
|
||||
keypair: KeyPair,
|
||||
options: SignOptions = {
|
||||
signAs: ''
|
||||
}
|
||||
): { signedTransaction: string; id: string } {
|
||||
validate.sign({txJSON, keypair})
|
||||
|
||||
const tx = JSON.parse(txJSON)
|
||||
if (tx.TxnSignature || tx.Signers) {
|
||||
throw new utils.common.errors.ValidationError(
|
||||
'txJSON must not contain "TxnSignature" or "Signers" properties')
|
||||
'txJSON must not contain "TxnSignature" or "Signers" properties'
|
||||
)
|
||||
}
|
||||
|
||||
const keypair = keypairs.deriveKeypair(secret)
|
||||
tx.SigningPubKey = options.signAs ? '' : keypair.publicKey
|
||||
|
||||
if (options.signAs) {
|
||||
@@ -43,4 +48,20 @@ function sign(txJSON: string, secret: string, options: {signAs?: string} = {}
|
||||
}
|
||||
}
|
||||
|
||||
function sign(
|
||||
txJSON: string,
|
||||
secret?: any,
|
||||
options?: SignOptions,
|
||||
keypair?: KeyPair
|
||||
): { signedTransaction: string; id: string } {
|
||||
if (typeof secret === 'string') {
|
||||
// we can't validate that the secret matches the account because
|
||||
// the secret could correspond to the regular key
|
||||
validate.sign({txJSON, secret})
|
||||
return signWithKeypair(txJSON, keypairs.deriveKeypair(secret), options)
|
||||
} else {
|
||||
return signWithKeypair(txJSON, keypair ? keypair : secret, options)
|
||||
}
|
||||
}
|
||||
|
||||
export default sign
|
||||
|
||||
@@ -50,6 +50,15 @@ export interface OfferCreateTransaction {
|
||||
Memos: {Memo: ApiMemo}[]
|
||||
}
|
||||
|
||||
export type KeyPair = {
|
||||
publicKey: string,
|
||||
privateKey: string
|
||||
}
|
||||
|
||||
export type SignOptions = {
|
||||
signAs: string
|
||||
}
|
||||
|
||||
export type Outcome = {
|
||||
result: string,
|
||||
ledgerVersion: number,
|
||||
|
||||
@@ -503,6 +503,58 @@ describe('RippleAPI', function () {
|
||||
assert.deepEqual(signature, responses.sign.signAs);
|
||||
});
|
||||
|
||||
it('sign - withKeypair', function () {
|
||||
const keypair = {
|
||||
privateKey:
|
||||
'00ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A',
|
||||
publicKey:
|
||||
'02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'
|
||||
};
|
||||
const result = this.api.sign(requests.sign.normal.txJSON, keypair);
|
||||
assert.deepEqual(result, responses.sign.normal);
|
||||
schemaValidator.schemaValidate('sign', result);
|
||||
});
|
||||
|
||||
it('sign - withKeypair already signed', function () {
|
||||
const keypair = {
|
||||
privateKey:
|
||||
'00ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A',
|
||||
publicKey:
|
||||
'02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'
|
||||
};
|
||||
const result = this.api.sign(requests.sign.normal.txJSON, keypair);
|
||||
assert.throws(() => {
|
||||
const tx = JSON.stringify(binary.decode(result.signedTransaction));
|
||||
this.api.sign(tx, keypair);
|
||||
}, /txJSON must not contain "TxnSignature" or "Signers" properties/);
|
||||
});
|
||||
|
||||
it('sign - withKeypair EscrowExecution', function () {
|
||||
const keypair = {
|
||||
privateKey:
|
||||
'001ACAAEDECE405B2A958212629E16F2EB46B153EEE94CDD350FDEFF52795525B7',
|
||||
publicKey:
|
||||
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020'
|
||||
};
|
||||
const result = this.api.sign(requests.sign.escrow.txJSON, keypair);
|
||||
assert.deepEqual(result, responses.sign.escrow);
|
||||
schemaValidator.schemaValidate('sign', result);
|
||||
});
|
||||
|
||||
it('sign - withKeypair signAs', function () {
|
||||
const txJSON = requests.sign.signAs;
|
||||
const keypair = {
|
||||
privateKey:
|
||||
'001ACAAEDECE405B2A958212629E16F2EB46B153EEE94CDD350FDEFF52795525B7',
|
||||
publicKey:
|
||||
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020'
|
||||
};
|
||||
const signature = this.api.sign(JSON.stringify(txJSON), keypair, {
|
||||
signAs: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'
|
||||
});
|
||||
assert.deepEqual(signature, responses.sign.signAs);
|
||||
});
|
||||
|
||||
it('submit', function () {
|
||||
return this.api.submit(responses.sign.normal.signedTransaction).then(
|
||||
_.partial(checkResult, responses.submit, 'submit'));
|
||||
|
||||
@@ -167,6 +167,15 @@ describe('http server integration tests', function() {
|
||||
result => assert.deepEqual(result.result, apiResponses.sign.normal)
|
||||
);
|
||||
|
||||
createTest(
|
||||
'sign',
|
||||
[{txJSON: apiRequests.sign.normal.txJSON},
|
||||
{keypair: {
|
||||
privateKey: '00ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A',
|
||||
publicKey: '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8' }}],
|
||||
result => assert.deepEqual(result.result, apiResponses.sign.normal)
|
||||
);
|
||||
|
||||
createTest(
|
||||
'generateAddress',
|
||||
[{options: {entropy: random()}}],
|
||||
|
||||
Reference in New Issue
Block a user