import { Network } from './network'
import { sign, verify } from './signing'
import * as base58 from './base58'
import * as strkey from './strkey'
import { default as xdr } from './generated/xdr_generated'
import nacl from 'tweetnacl'
export class Keypair {
/**
* `Keypair` represents public (and secret) keys of the account.
*
* Use more convenient methods to create `Keypair` object:
* * `{@link Keypair.fromAccountId}`
* * `{@link Keypair.fromSecret}`
* * `{@link Keypair.random}`
*
* @constructor
* @param {object} keys
* @param {string} keys.publicKey Raw public key
* @param {string} [keys.secretSeed] Raw secret key seed.
*/
constructor (keys) {
this._publicKey = Buffer.from(keys.publicKey)
if (keys.secretSeed) {
this._secretSeed = Buffer.from(keys.secretSeed)
this._secretKey = Buffer.from(keys.secretKey)
}
}
/**
* @param {string} seed Secret key seed
* @deprecated Use {@link Keypair.fromSecret}
* @returns {Keypair}
*/
static fromSeed (seed) {
console.warn('Keypair.fromSeed() is deprecated. Use Keypair.fromSecret().')
return Keypair.fromSecret(seed)
}
/**
* Creates a new `Keypair` instance from secret key.
* @param {string} secretKey Secret key
* @returns {Keypair}
*/
static fromSecret (secretKey) {
let rawSeed = strkey.decodeCheck('seed', secretKey)
return this.fromRawSeed(rawSeed)
}
/**
* Base58 address encoding is **DEPRECATED**! Use this method only for transition to strkey encoding.
* @param {string} seed Base58 secret seed
* @deprecated Use {@link Keypair.fromSecret}
* @returns {Keypair}
*/
static fromBase58Seed (seed) {
let rawSeed = base58.decodeBase58Check('seed', seed)
return this.fromRawSeed(rawSeed)
}
/**
* Creates a new `Keypair` object from secret seed raw bytes.
*
* @param {Buffer} rawSeed Buffer containing secret seed
* @returns {Keypair}
*/
static fromRawSeed (rawSeed) {
rawSeed = Buffer.from(rawSeed)
let rawSeedU8 = new Uint8Array(rawSeed)
let keys = nacl.sign.keyPair.fromSeed(rawSeedU8)
keys.secretSeed = rawSeed
return new this(keys)
}
/**
* Returns `Keypair` object representing network master key.
* @returns {Keypair}
*/
static master () {
return this.fromRawSeed(Network.current().networkId())
}
/**
* Creates a new `Keypair` object from account ID.
* @param {string} accountId account ID (ex. `GB3KJPLFUYN5VL6R3GU3EGCGVCKFDSD7BEDX42HWG5BWFKB3KQGJJRMA`)
* @returns {Keypair}
*/
static fromAccountId (accountId) {
let publicKey = strkey.decodeCheck('accountId', accountId)
if (publicKey.length !== 32) {
throw new Error('Invalid Stellar accountId')
}
return new this({ publicKey })
}
static fromBalanceId (balanceId) {
let publicKey = strkey.decodeCheck('balanceId', balanceId)
if (publicKey.length !== 32) {
throw new Error('Invalid Stellar balanceId')
}
return new this({ publicKey })
}
/**
* Create a random `Keypair` object.
* @returns {Keypair}
*/
static random () {
let seed = nacl.randomBytes(32)
return this.fromRawSeed(seed)
}
/**
* Returns true if the given accountId is a valid Stellar account ID.
* @param {string} accountId account ID to check
* @returns {boolean}
*/
static isValidAccountId (accountId) {
return Keypair.isValidPublicKey(accountId)
}
/**
* Returns true if the given Stellar public key is valid.
* @param {string} publicKey public key to check
* @returns {boolean}
*/
static isValidPublicKey (publicKey) {
if (publicKey && publicKey.length !== 56) {
return false
}
try {
let decoded = strkey.decodeCheck('accountId', publicKey)
if (decoded.length !== 32) {
return false
}
} catch (err) {
return false
}
return true
}
/**
* Returns true if the given Stellar secret key is valid.
* @param {string} secretKey secret key to check
* @returns {boolean}
*/
static isValidSecretKey (secretKey) {
if (secretKey && secretKey.length !== 56) {
return false
}
try {
let decoded = strkey.decodeCheck('seed', secretKey)
if (decoded.length !== 32) {
return false
}
} catch (err) {
return false
}
return true
}
xdrAccountId () {
return new xdr.AccountId.keyTypeEd25519(this._publicKey)
}
xdrPublicKey () {
return new xdr.PublicKey.keyTypeEd25519(this._publicKey)
}
/**
* Returns raw public key
* @returns {Buffer}
*/
rawPublicKey () {
return this._publicKey
}
signatureHint () {
let a = this.xdrAccountId().toXDR()
return a.slice(a.length - 4)
}
/**
* Returns account ID associated with this `Keypair` object.
* @returns {string}
*/
accountId () {
return strkey.encodeCheck('accountId', this._publicKey)
}
/**
* @deprecated Use {@link Keypair.secret}
* @returns {string}
*/
seed () {
console.warn('Keypair.seed() is deprecated. Use Keypair.secret().')
return this.secret()
}
/**
* Returns secret key associated with this `Keypair` object
* @returns {string}
*/
secret () {
return strkey.encodeCheck('seed', this._secretSeed)
}
/**
* Returns raw secret key seed.
* @deprecated
* @returns {Buffer}
*/
rawSeed () {
console.warn('Keypair.rawSeed() is deprecated.')
return this._secretSeed
}
/**
* Returns raw secret key.
* @returns {Buffer}
*/
rawSecretKey () {
return this._secretKey
}
/**
* Returns `true` if this `Keypair` object contains secret key and can sign.
* @returns {boolean}
*/
canSign () {
return !!this._secretKey
}
/**
* Signs data.
* @param {Buffer} data Data to sign
* @returns {Buffer}
*/
sign (data) {
if (!this.canSign()) {
throw new Error('cannot sign: no secret key available')
}
return sign(data, this._secretKey)
}
/**
* Verifies if `signature` for `data` is valid.
* @param {Buffer} data Signed data
* @param {Buffer} signature Signature
* @returns {boolean}
*/
verify (data, signature) {
return verify(data, signature, this._publicKey)
}
signDecorated (data) {
let signature = this.sign(data)
let hint = this.signatureHint()
return new xdr.DecoratedSignature({ hint, signature })
}
}