"use strict"; /** * Codec class */ Object.defineProperty(exports, "__esModule", { value: true }); exports.isValidClassicAddress = exports.decodeAccountPublic = exports.encodeAccountPublic = exports.encodeNodePublic = exports.decodeNodePublic = exports.decodeAddress = exports.decodeAccountID = exports.encodeAddress = exports.encodeAccountID = exports.decodeSeed = exports.encodeSeed = exports.codec = void 0; const baseCodec = require("base-x"); const createHash = require("create-hash"); const utils_1 = require("./utils"); class Codec { constructor(options) { this.sha256 = options.sha256; this.alphabet = options.alphabet; this.codec = baseCodec(this.alphabet); this.base = this.alphabet.length; } /** * Encoder. * * @param bytes - Buffer of data to encode. * @param opts - Options object including the version bytes and the expected length of the data to encode. */ encode(bytes, opts) { const versions = opts.versions; return this.encodeVersioned(bytes, versions, opts.expectedLength); } encodeVersioned(bytes, versions, expectedLength) { if (expectedLength && bytes.length !== expectedLength) { throw new Error('unexpected_payload_length: bytes.length does not match expectedLength.' + ' Ensure that the bytes are a Buffer.'); } return this.encodeChecked(Buffer.from((0, utils_1.concatArgs)(versions, bytes))); } encodeChecked(buffer) { const check = this.sha256(this.sha256(buffer)).slice(0, 4); return this.encodeRaw(Buffer.from((0, utils_1.concatArgs)(buffer, check))); } encodeRaw(bytes) { return this.codec.encode(bytes); } /** * Decoder. * * @param base58string - Base58Check-encoded string to decode. * @param opts - Options object including the version byte(s) and the expected length of the data after decoding. */ /* eslint-disable max-lines-per-function -- * TODO refactor */ decode(base58string, opts) { const versions = opts.versions; const types = opts.versionTypes; const withoutSum = this.decodeChecked(base58string); if (versions.length > 1 && !opts.expectedLength) { throw new Error('expectedLength is required because there are >= 2 possible versions'); } const versionLengthGuess = typeof versions[0] === 'number' ? 1 : versions[0].length; const payloadLength = opts.expectedLength || withoutSum.length - versionLengthGuess; const versionBytes = withoutSum.slice(0, -payloadLength); const payload = withoutSum.slice(-payloadLength); for (let i = 0; i < versions.length; i++) { const version = Array.isArray(versions[i]) ? versions[i] : [versions[i]]; if ((0, utils_1.seqEqual)(versionBytes, version)) { return { version, bytes: payload, type: types ? types[i] : null, }; } } throw new Error('version_invalid: version bytes do not match any of the provided version(s)'); } /* eslint-enable max-lines-per-function */ decodeChecked(base58string) { const buffer = this.decodeRaw(base58string); if (buffer.length < 5) { throw new Error('invalid_input_size: decoded data must have length >= 5'); } if (!this.verifyCheckSum(buffer)) { throw new Error('checksum_invalid'); } return buffer.slice(0, -4); } decodeRaw(base58string) { return this.codec.decode(base58string); } verifyCheckSum(bytes) { const computed = this.sha256(this.sha256(bytes.slice(0, -4))).slice(0, 4); const checksum = bytes.slice(-4); return (0, utils_1.seqEqual)(computed, checksum); } } /** * XRP codec */ // base58 encodings: https://xrpl.org/base58-encodings.html // Account address (20 bytes) const ACCOUNT_ID = 0; // Account public key (33 bytes) const ACCOUNT_PUBLIC_KEY = 0x23; // 33; Seed value (for secret keys) (16 bytes) const FAMILY_SEED = 0x21; // 28; Validation public key (33 bytes) const NODE_PUBLIC = 0x1c; // [1, 225, 75] const ED25519_SEED = [0x01, 0xe1, 0x4b]; const codecOptions = { sha256(bytes) { return createHash('sha256').update(Buffer.from(bytes)).digest(); }, alphabet: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz', }; const codecWithXrpAlphabet = new Codec(codecOptions); exports.codec = codecWithXrpAlphabet; // entropy is a Buffer of size 16 // type is 'ed25519' or 'secp256k1' function encodeSeed(entropy, type) { if (entropy.length !== 16) { throw new Error('entropy must have length 16'); } const opts = { expectedLength: 16, // for secp256k1, use `FAMILY_SEED` versions: type === 'ed25519' ? ED25519_SEED : [FAMILY_SEED], }; // prefixes entropy with version bytes return codecWithXrpAlphabet.encode(entropy, opts); } exports.encodeSeed = encodeSeed; function decodeSeed(seed, opts = { versionTypes: ['ed25519', 'secp256k1'], versions: [ED25519_SEED, FAMILY_SEED], expectedLength: 16, }) { return codecWithXrpAlphabet.decode(seed, opts); } exports.decodeSeed = decodeSeed; function encodeAccountID(bytes) { const opts = { versions: [ACCOUNT_ID], expectedLength: 20 }; return codecWithXrpAlphabet.encode(bytes, opts); } exports.encodeAccountID = encodeAccountID; /* eslint-disable import/no-unused-modules --- * unclear why this is aliased but we should keep it in case someone else is * importing it with the aliased name */ exports.encodeAddress = encodeAccountID; /* eslint-enable import/no-unused-modules */ function decodeAccountID(accountId) { const opts = { versions: [ACCOUNT_ID], expectedLength: 20 }; return codecWithXrpAlphabet.decode(accountId, opts).bytes; } exports.decodeAccountID = decodeAccountID; /* eslint-disable import/no-unused-modules --- * unclear why this is aliased but we should keep it in case someone else is * importing it with the aliased name */ exports.decodeAddress = decodeAccountID; /* eslint-enable import/no-unused-modules */ function decodeNodePublic(base58string) { const opts = { versions: [NODE_PUBLIC], expectedLength: 33 }; return codecWithXrpAlphabet.decode(base58string, opts).bytes; } exports.decodeNodePublic = decodeNodePublic; function encodeNodePublic(bytes) { const opts = { versions: [NODE_PUBLIC], expectedLength: 33 }; return codecWithXrpAlphabet.encode(bytes, opts); } exports.encodeNodePublic = encodeNodePublic; function encodeAccountPublic(bytes) { const opts = { versions: [ACCOUNT_PUBLIC_KEY], expectedLength: 33 }; return codecWithXrpAlphabet.encode(bytes, opts); } exports.encodeAccountPublic = encodeAccountPublic; function decodeAccountPublic(base58string) { const opts = { versions: [ACCOUNT_PUBLIC_KEY], expectedLength: 33 }; return codecWithXrpAlphabet.decode(base58string, opts).bytes; } exports.decodeAccountPublic = decodeAccountPublic; function isValidClassicAddress(address) { try { decodeAccountID(address); } catch (_error) { return false; } return true; } exports.isValidClassicAddress = isValidClassicAddress; //# sourceMappingURL=xrp-codec.js.map