PageRenderTime 1194ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/ts_src/address.ts

http://github.com/bitcoinjs/bitcoinjs-lib
TypeScript | 175 lines | 143 code | 30 blank | 2 comment | 39 complexity | 2ae77baf58f1027cde55ba3628bee7bb MD5 | raw file
  1. import { Network } from './networks';
  2. import * as networks from './networks';
  3. import * as payments from './payments';
  4. import * as bscript from './script';
  5. import * as types from './types';
  6. import { bech32, bech32m } from 'bech32';
  7. import * as bs58check from 'bs58check';
  8. const { typeforce } = types;
  9. export interface Base58CheckResult {
  10. hash: Buffer;
  11. version: number;
  12. }
  13. export interface Bech32Result {
  14. version: number;
  15. prefix: string;
  16. data: Buffer;
  17. }
  18. const FUTURE_SEGWIT_MAX_SIZE: number = 40;
  19. const FUTURE_SEGWIT_MIN_SIZE: number = 2;
  20. const FUTURE_SEGWIT_MAX_VERSION: number = 16;
  21. const FUTURE_SEGWIT_MIN_VERSION: number = 1;
  22. const FUTURE_SEGWIT_VERSION_DIFF: number = 0x50;
  23. function _toFutureSegwitAddress(output: Buffer, network: Network): string {
  24. const data = output.slice(2);
  25. if (
  26. data.length < FUTURE_SEGWIT_MIN_SIZE ||
  27. data.length > FUTURE_SEGWIT_MAX_SIZE
  28. )
  29. throw new TypeError('Invalid program length for segwit address');
  30. const version = output[0] - FUTURE_SEGWIT_VERSION_DIFF;
  31. if (
  32. version < FUTURE_SEGWIT_MIN_VERSION ||
  33. version > FUTURE_SEGWIT_MAX_VERSION
  34. )
  35. throw new TypeError('Invalid version for segwit address');
  36. if (output[1] !== data.length)
  37. throw new TypeError('Invalid script for segwit address');
  38. return toBech32(data, version, network.bech32);
  39. }
  40. export function fromBase58Check(address: string): Base58CheckResult {
  41. const payload = bs58check.decode(address);
  42. // TODO: 4.0.0, move to "toOutputScript"
  43. if (payload.length < 21) throw new TypeError(address + ' is too short');
  44. if (payload.length > 21) throw new TypeError(address + ' is too long');
  45. const version = payload.readUInt8(0);
  46. const hash = payload.slice(1);
  47. return { version, hash };
  48. }
  49. export function fromBech32(address: string): Bech32Result {
  50. let result;
  51. let version;
  52. try {
  53. result = bech32.decode(address);
  54. } catch (e) {}
  55. if (result) {
  56. version = result.words[0];
  57. if (version !== 0) throw new TypeError(address + ' uses wrong encoding');
  58. } else {
  59. result = bech32m.decode(address);
  60. version = result.words[0];
  61. if (version === 0) throw new TypeError(address + ' uses wrong encoding');
  62. }
  63. const data = bech32.fromWords(result.words.slice(1));
  64. return {
  65. version,
  66. prefix: result.prefix,
  67. data: Buffer.from(data),
  68. };
  69. }
  70. export function toBase58Check(hash: Buffer, version: number): string {
  71. typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments);
  72. const payload = Buffer.allocUnsafe(21);
  73. payload.writeUInt8(version, 0);
  74. hash.copy(payload, 1);
  75. return bs58check.encode(payload);
  76. }
  77. export function toBech32(
  78. data: Buffer,
  79. version: number,
  80. prefix: string,
  81. ): string {
  82. const words = bech32.toWords(data);
  83. words.unshift(version);
  84. return version === 0
  85. ? bech32.encode(prefix, words)
  86. : bech32m.encode(prefix, words);
  87. }
  88. export function fromOutputScript(output: Buffer, network?: Network): string {
  89. // TODO: Network
  90. network = network || networks.bitcoin;
  91. try {
  92. return payments.p2pkh({ output, network }).address as string;
  93. } catch (e) {}
  94. try {
  95. return payments.p2sh({ output, network }).address as string;
  96. } catch (e) {}
  97. try {
  98. return payments.p2wpkh({ output, network }).address as string;
  99. } catch (e) {}
  100. try {
  101. return payments.p2wsh({ output, network }).address as string;
  102. } catch (e) {}
  103. try {
  104. return _toFutureSegwitAddress(output, network);
  105. } catch (e) {}
  106. throw new Error(bscript.toASM(output) + ' has no matching Address');
  107. }
  108. export function toOutputScript(address: string, network?: Network): Buffer {
  109. network = network || networks.bitcoin;
  110. let decodeBase58: Base58CheckResult | undefined;
  111. let decodeBech32: Bech32Result | undefined;
  112. try {
  113. decodeBase58 = fromBase58Check(address);
  114. } catch (e) {}
  115. if (decodeBase58) {
  116. if (decodeBase58.version === network.pubKeyHash)
  117. return payments.p2pkh({ hash: decodeBase58.hash }).output as Buffer;
  118. if (decodeBase58.version === network.scriptHash)
  119. return payments.p2sh({ hash: decodeBase58.hash }).output as Buffer;
  120. } else {
  121. try {
  122. decodeBech32 = fromBech32(address);
  123. } catch (e) {}
  124. if (decodeBech32) {
  125. if (decodeBech32.prefix !== network.bech32)
  126. throw new Error(address + ' has an invalid prefix');
  127. if (decodeBech32.version === 0) {
  128. if (decodeBech32.data.length === 20)
  129. return payments.p2wpkh({ hash: decodeBech32.data }).output as Buffer;
  130. if (decodeBech32.data.length === 32)
  131. return payments.p2wsh({ hash: decodeBech32.data }).output as Buffer;
  132. } else if (
  133. decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
  134. decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
  135. decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE &&
  136. decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE
  137. )
  138. return bscript.compile([
  139. decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF,
  140. decodeBech32.data,
  141. ]);
  142. }
  143. }
  144. throw new Error(address + ' has no matching Script');
  145. }