您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

xrp-codec.js 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. "use strict";
  2. /**
  3. * Codec class
  4. */
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. 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;
  7. const baseCodec = require("base-x");
  8. const createHash = require("create-hash");
  9. const utils_1 = require("./utils");
  10. class Codec {
  11. constructor(options) {
  12. this._sha256 = options.sha256;
  13. this._alphabet = options.alphabet;
  14. this._codec = baseCodec(this._alphabet);
  15. }
  16. /**
  17. * Encoder.
  18. *
  19. * @param bytes - Buffer of data to encode.
  20. * @param opts - Options object including the version bytes and the expected length of the data to encode.
  21. */
  22. encode(bytes, opts) {
  23. const versions = opts.versions;
  24. return this._encodeVersioned(bytes, versions, opts.expectedLength);
  25. }
  26. /**
  27. * Decoder.
  28. *
  29. * @param base58string - Base58Check-encoded string to decode.
  30. * @param opts - Options object including the version byte(s) and the expected length of the data after decoding.
  31. */
  32. /* eslint-disable max-lines-per-function --
  33. * TODO refactor */
  34. decode(base58string, opts) {
  35. var _a;
  36. const versions = opts.versions;
  37. const types = opts.versionTypes;
  38. const withoutSum = this.decodeChecked(base58string);
  39. if (versions.length > 1 && !opts.expectedLength) {
  40. throw new Error('expectedLength is required because there are >= 2 possible versions');
  41. }
  42. const versionLengthGuess = typeof versions[0] === 'number' ? 1 : versions[0].length;
  43. const payloadLength = (_a = opts.expectedLength) !== null && _a !== void 0 ? _a : withoutSum.length - versionLengthGuess;
  44. const versionBytes = withoutSum.slice(0, -payloadLength);
  45. const payload = withoutSum.slice(-payloadLength);
  46. for (let i = 0; i < versions.length; i++) {
  47. /* eslint-disable @typescript-eslint/consistent-type-assertions --
  48. * TODO refactor */
  49. const version = Array.isArray(versions[i])
  50. ? versions[i]
  51. : [versions[i]];
  52. if ((0, utils_1.seqEqual)(versionBytes, version)) {
  53. return {
  54. version,
  55. bytes: payload,
  56. type: types ? types[i] : null,
  57. };
  58. }
  59. /* eslint-enable @typescript-eslint/consistent-type-assertions */
  60. }
  61. throw new Error('version_invalid: version bytes do not match any of the provided version(s)');
  62. }
  63. encodeChecked(buffer) {
  64. const check = this._sha256(this._sha256(buffer)).slice(0, 4);
  65. return this._encodeRaw(Buffer.from((0, utils_1.concatArgs)(buffer, check)));
  66. }
  67. decodeChecked(base58string) {
  68. const buffer = this._decodeRaw(base58string);
  69. if (buffer.length < 5) {
  70. throw new Error('invalid_input_size: decoded data must have length >= 5');
  71. }
  72. if (!this._verifyCheckSum(buffer)) {
  73. throw new Error('checksum_invalid');
  74. }
  75. return buffer.slice(0, -4);
  76. }
  77. _encodeVersioned(bytes, versions, expectedLength) {
  78. if (expectedLength && bytes.length !== expectedLength) {
  79. throw new Error('unexpected_payload_length: bytes.length does not match expectedLength.' +
  80. ' Ensure that the bytes are a Buffer.');
  81. }
  82. return this.encodeChecked(Buffer.from((0, utils_1.concatArgs)(versions, bytes)));
  83. }
  84. _encodeRaw(bytes) {
  85. return this._codec.encode(bytes);
  86. }
  87. /* eslint-enable max-lines-per-function */
  88. _decodeRaw(base58string) {
  89. return this._codec.decode(base58string);
  90. }
  91. _verifyCheckSum(bytes) {
  92. const computed = this._sha256(this._sha256(bytes.slice(0, -4))).slice(0, 4);
  93. const checksum = bytes.slice(-4);
  94. return (0, utils_1.seqEqual)(computed, checksum);
  95. }
  96. }
  97. /**
  98. * XRP codec
  99. */
  100. // base58 encodings: https://xrpl.org/base58-encodings.html
  101. // Account address (20 bytes)
  102. const ACCOUNT_ID = 0;
  103. // Account public key (33 bytes)
  104. const ACCOUNT_PUBLIC_KEY = 0x23;
  105. // 33; Seed value (for secret keys) (16 bytes)
  106. const FAMILY_SEED = 0x21;
  107. // 28; Validation public key (33 bytes)
  108. const NODE_PUBLIC = 0x1c;
  109. // [1, 225, 75]
  110. const ED25519_SEED = [0x01, 0xe1, 0x4b];
  111. const codecOptions = {
  112. sha256(bytes) {
  113. return createHash('sha256').update(Buffer.from(bytes)).digest();
  114. },
  115. alphabet: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
  116. };
  117. const codecWithXrpAlphabet = new Codec(codecOptions);
  118. exports.codec = codecWithXrpAlphabet;
  119. // entropy is a Buffer of size 16
  120. // type is 'ed25519' or 'secp256k1'
  121. function encodeSeed(entropy, type) {
  122. if (entropy.length !== 16) {
  123. throw new Error('entropy must have length 16');
  124. }
  125. const opts = {
  126. expectedLength: 16,
  127. // for secp256k1, use `FAMILY_SEED`
  128. versions: type === 'ed25519' ? ED25519_SEED : [FAMILY_SEED],
  129. };
  130. // prefixes entropy with version bytes
  131. return codecWithXrpAlphabet.encode(entropy, opts);
  132. }
  133. exports.encodeSeed = encodeSeed;
  134. function decodeSeed(seed, opts = {
  135. versionTypes: ['ed25519', 'secp256k1'],
  136. versions: [ED25519_SEED, FAMILY_SEED],
  137. expectedLength: 16,
  138. }) {
  139. return codecWithXrpAlphabet.decode(seed, opts);
  140. }
  141. exports.decodeSeed = decodeSeed;
  142. function encodeAccountID(bytes) {
  143. const opts = { versions: [ACCOUNT_ID], expectedLength: 20 };
  144. return codecWithXrpAlphabet.encode(bytes, opts);
  145. }
  146. exports.encodeAccountID = encodeAccountID;
  147. /* eslint-disable import/no-unused-modules ---
  148. * unclear why this is aliased but we should keep it in case someone else is
  149. * importing it with the aliased name */
  150. exports.encodeAddress = encodeAccountID;
  151. /* eslint-enable import/no-unused-modules */
  152. function decodeAccountID(accountId) {
  153. const opts = { versions: [ACCOUNT_ID], expectedLength: 20 };
  154. return codecWithXrpAlphabet.decode(accountId, opts).bytes;
  155. }
  156. exports.decodeAccountID = decodeAccountID;
  157. /* eslint-disable import/no-unused-modules ---
  158. * unclear why this is aliased but we should keep it in case someone else is
  159. * importing it with the aliased name */
  160. exports.decodeAddress = decodeAccountID;
  161. /* eslint-enable import/no-unused-modules */
  162. function decodeNodePublic(base58string) {
  163. const opts = { versions: [NODE_PUBLIC], expectedLength: 33 };
  164. return codecWithXrpAlphabet.decode(base58string, opts).bytes;
  165. }
  166. exports.decodeNodePublic = decodeNodePublic;
  167. function encodeNodePublic(bytes) {
  168. const opts = { versions: [NODE_PUBLIC], expectedLength: 33 };
  169. return codecWithXrpAlphabet.encode(bytes, opts);
  170. }
  171. exports.encodeNodePublic = encodeNodePublic;
  172. function encodeAccountPublic(bytes) {
  173. const opts = { versions: [ACCOUNT_PUBLIC_KEY], expectedLength: 33 };
  174. return codecWithXrpAlphabet.encode(bytes, opts);
  175. }
  176. exports.encodeAccountPublic = encodeAccountPublic;
  177. function decodeAccountPublic(base58string) {
  178. const opts = { versions: [ACCOUNT_PUBLIC_KEY], expectedLength: 33 };
  179. return codecWithXrpAlphabet.decode(base58string, opts).bytes;
  180. }
  181. exports.decodeAccountPublic = decodeAccountPublic;
  182. function isValidClassicAddress(address) {
  183. try {
  184. decodeAccountID(address);
  185. }
  186. catch (_error) {
  187. return false;
  188. }
  189. return true;
  190. }
  191. exports.isValidClassicAddress = isValidClassicAddress;
  192. //# sourceMappingURL=xrp-codec.js.map