@bithomp/xrpl-api
Version:
A Bithomp JavaScript/TypeScript library for interacting with the XRP Ledger
285 lines (284 loc) • 11.9 kB
JavaScript
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;
}
;