UNPKG

@bithomp/xrpl-api

Version:

A Bithomp JavaScript/TypeScript library for interacting with the XRP Ledger

285 lines (284 loc) 11.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DEFAULT_DEFINITIONS = exports.XrplDefinitions = exports.XrplDefinitionsBase = void 0; exports.isValidSecret = isValidSecret; exports.walletFromSeed = walletFromSeed; exports.generateAddress = generateAddress; exports.isValidClassicAddress = isValidClassicAddress; exports.checksumClassicAddress = checksumClassicAddress; exports.signTransaction = signTransaction; exports.verifyTransaction = verifyTransaction; exports.verifySignature = verifySignature; exports.hashSignedTx = hashSignedTx; exports.getXahauDefinitions = getXahauDefinitions; const bignumber_js_1 = __importDefault(require("bignumber.js")); const omitBy_1 = __importDefault(require("lodash/omitBy")); const Crypto = __importStar(require("crypto")); const xrpl_1 = require("xrpl"); const ripple_address_codec_1 = require("ripple-address-codec"); const ripple_binary_codec_1 = require("ripple-binary-codec"); Object.defineProperty(exports, "XrplDefinitionsBase", { enumerable: true, get: function () { return ripple_binary_codec_1.XrplDefinitionsBase; } }); Object.defineProperty(exports, "XrplDefinitions", { enumerable: true, get: function () { return ripple_binary_codec_1.XrplDefinitions; } }); Object.defineProperty(exports, "DEFAULT_DEFINITIONS", { enumerable: true, get: function () { return ripple_binary_codec_1.DEFAULT_DEFINITIONS; } }); const ripple_keypairs_1 = require("ripple-keypairs"); const Base58 = __importStar(require("./base58")); const common_1 = require("./common"); var HashPrefix; (function (HashPrefix) { HashPrefix[HashPrefix["TRANSACTION_ID"] = 1415073280] = "TRANSACTION_ID"; })(HashPrefix || (HashPrefix = {})); function isValidSecret(secret) { try { (0, ripple_keypairs_1.deriveKeypair)(secret); return true; } catch (_err) { } return false; } function walletFromSeed(seed, options) { options = { ignoreSeedPayload: false, ...options }; let algorithm = options.algorithm; if (!options.ignoreSeedPayload && !options.algorithm) { const decodedSeed = (0, ripple_address_codec_1.decodeSeed)(seed); if (decodedSeed.type === "secp256k1") { if (options.seedAddress) { const { publicKey } = (0, ripple_keypairs_1.deriveKeypair)(seed, { algorithm: xrpl_1.ECDSA.secp256k1 }); const classicAddress = (0, ripple_keypairs_1.deriveAddress)(publicKey); if (classicAddress === options.seedAddress) { algorithm = xrpl_1.ECDSA.secp256k1; } else { algorithm = xrpl_1.ECDSA.ed25519; } } else { algorithm = xrpl_1.ECDSA.secp256k1; } } } const wallet = xrpl_1.Wallet.fromSeed(seed, { algorithm, masterAddress: options.masterAddress }); return wallet; } function generateAddress() { const wallet = xrpl_1.Wallet.generate(); const { publicKey, privateKey, classicAddress, seed } = wallet; return { publicKey, privateKey, address: classicAddress, seed: seed }; } function isValidClassicAddress(address) { if (!address || address.length === 0) { return false; } const buffer = Base58.decode(address); if (buffer === null) { return false; } const checksum = checksumClassicAddress(buffer); if (checksum[0] !== buffer[21] || checksum[1] !== buffer[22] || checksum[2] !== buffer[23] || checksum[3] !== buffer[24]) { return false; } return true; } function checksumClassicAddress(buffer) { const hash = buffer.slice(0, 21); const checksumPrepare = Crypto.createHash("sha256").update(Buffer.from(hash)).digest(); const checksum = Crypto.createHash("sha256").update(checksumPrepare).digest(); return checksum; } function signTransaction(wallet, transaction, multisign, definitions, validateTx) { let multisignAddress = false; if (typeof multisign === "string" && multisign.startsWith("X")) { multisignAddress = multisign; } else if (multisign) { multisignAddress = wallet.classicAddress; } const tx = (0, omitBy_1.default)({ ...transaction }, (value) => value == null); if (tx.TxnSignature || tx.Signers) { throw new xrpl_1.ValidationError('txJSON must not contain "TxnSignature" or "Signers" properties'); } removeTrailingZeros(tx); if (validateTx !== false) { (0, xrpl_1.validate)(tx); } const txToSignAndEncode = { ...tx }; txToSignAndEncode.SigningPubKey = multisignAddress ? "" : wallet.publicKey; if (multisignAddress) { const signer = { Account: multisignAddress, SigningPubKey: wallet.publicKey, TxnSignature: computeSignature(txToSignAndEncode, wallet.privateKey, multisignAddress, definitions), }; txToSignAndEncode.Signers = [{ Signer: signer }]; } else { txToSignAndEncode.TxnSignature = computeSignature(txToSignAndEncode, wallet.privateKey, undefined, definitions); } const serialized = (0, ripple_binary_codec_1.encode)(txToSignAndEncode, definitions); return { tx_blob: serialized, hash: hashSignedTx(serialized, definitions), }; } function verifyTransaction(signedTransaction, definitions) { const tx = typeof signedTransaction === "string" ? (0, ripple_binary_codec_1.decode)(signedTransaction, definitions) : signedTransaction; const messageHex = (0, ripple_binary_codec_1.encodeForSigning)(tx, definitions); const signature = tx.TxnSignature; const publicKey = tx.SigningPubKey; return (0, ripple_keypairs_1.verify)(messageHex, signature, publicKey); } function verifySignature(signedTransaction, explicitMultiSigner, definitions) { let tx; let signedBy = ""; let signatureValid = false; try { if (typeof signedTransaction === "string") { tx = (0, ripple_binary_codec_1.decode)(signedTransaction, definitions); } else if (signedTransaction !== null && typeof signedTransaction === "object") { tx = signedTransaction; } } catch (err) { return { signatureValid: false, error: err.message }; } if (!tx) { return { signatureValid: false, error: "The transaction could not be decoded." }; } if (!tx.TxnSignature && !tx.Signers) { return { signatureValid: false, error: "The transaction must be signed to verify the signature." }; } const signatureMultiSign = typeof tx.Signers !== "undefined" && Array.isArray(tx.Signers) && tx.Signers.length > 0 && typeof tx.SigningPubKey === "string" && tx.SigningPubKey === ""; try { if (signatureMultiSign && explicitMultiSigner && explicitMultiSigner.match(/^r/)) { signedBy = explicitMultiSigner; } else if (signatureMultiSign && explicitMultiSigner) { signedBy = (0, ripple_keypairs_1.deriveAddress)(explicitMultiSigner); } else { let signer = tx.SigningPubKey; if (signatureMultiSign && tx.Signers && tx.Signers.length > 0) { const firstSigner = Object.values(tx.Signers)[0]; signer = firstSigner.Signer.SigningPubKey; } signedBy = (0, ripple_keypairs_1.deriveAddress)(signer); } } catch (err) { return { signatureValid: false, error: err.message }; } try { if (signatureMultiSign && tx.Signers) { const matchingSigners = Object.values(tx.Signers).filter((signer) => { return (0, ripple_keypairs_1.deriveAddress)(signer.Signer.SigningPubKey) === signedBy; }); if (matchingSigners.length > 0) { const multiSigner = matchingSigners[0]; signatureValid = (0, ripple_keypairs_1.verify)((0, ripple_binary_codec_1.encodeForMultisigning)(tx, signedBy, definitions), multiSigner.Signer.TxnSignature, multiSigner.Signer.SigningPubKey); } else { return { signatureValid: false, error: "Explicit MultiSigner not in Signers" }; } } else { signatureValid = (0, ripple_keypairs_1.verify)((0, ripple_binary_codec_1.encodeForSigning)(tx, definitions), tx.TxnSignature, tx.SigningPubKey); } } catch (err) { return { signedBy, signatureValid: false, error: err.message }; } return { signedBy, signatureValid, signatureMultiSign, }; } function computeSignature(tx, privateKey, signAs, definitions) { if (signAs) { const classicAddress = (0, ripple_address_codec_1.isValidXAddress)(signAs) ? (0, ripple_address_codec_1.xAddressToClassicAddress)(signAs).classicAddress : signAs; return (0, ripple_keypairs_1.sign)((0, ripple_binary_codec_1.encodeForMultisigning)(tx, classicAddress, definitions), privateKey); } return (0, ripple_keypairs_1.sign)((0, ripple_binary_codec_1.encodeForSigning)(tx, definitions), privateKey); } function removeTrailingZeros(tx) { if (tx.TransactionType === "Payment" && typeof tx.Amount !== "string" && tx.Amount.value.includes(".") && tx.Amount.value.endsWith("0")) { tx.Amount = { ...tx.Amount }; tx.Amount.value = new bignumber_js_1.default(tx.Amount.value).toString(); } } function hashSignedTx(tx, definitions, validateTx) { let txBlob; let txObject; if (typeof tx === "string") { txBlob = tx; txObject = (0, ripple_binary_codec_1.decode)(tx, definitions); } else { txBlob = (0, ripple_binary_codec_1.encode)(tx, definitions); txObject = tx; } if (validateTx !== false) { if (txObject.TxnSignature === undefined && txObject.Signers === undefined) { throw new xrpl_1.ValidationError("The transaction must be signed to hash it."); } } const prefix = HashPrefix.TRANSACTION_ID.toString(16).toUpperCase(); return (0, common_1.sha512Half)(prefix.concat(txBlob)); } function getXahauDefinitions() { try { const xahauEnums = require("../config/xahau_definitions.json"); const xahauDefinitions = new ripple_binary_codec_1.XrplDefinitions(xahauEnums); return xahauDefinitions; } catch (_err) { } return undefined; }