text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  const crypto = require("./crypto");
  const bs58check = require('bs58check');
  const ecc = require('tiny-secp256k1');
  const typeforce = require('typeforce');
  const wif = require('wif');
  const UINT256_TYPE = typeforce.BufferN(32);
  const NETWORK_TYPE = typeforce.compile({
      wif: typeforce.UInt8,
      bip32: {
          public: typeforce.UInt32,
          private: typeforce.UInt32,
      },
  });
  const BITCOIN = {
      wif: 0xd4,
      bip32: {
          public:  0x022D2533,
          private: 0x0221312B,
      },
  };
  const HIGHEST_BIT = 0x80000000;
  const UINT31_MAX = Math.pow(2, 31) - 1;
  function BIP32Path(value) {
      return (typeforce.String(value) && value.match(/^(m\/)?(\d+'?\/)*\d+'?$/) !== null);
  }
  function UInt31(value) {
      return typeforce.UInt32(value) && value <= UINT31_MAX;
  }
  class BIP32 {
      constructor(__D, __Q, chainCode, network, __DEPTH = 0, __INDEX = 0, __PARENT_FINGERPRINT = 0x00000000) {
          this.__D = __D;
          this.__Q = __Q;
          this.chainCode = chainCode;
          this.network = network;
          this.__DEPTH = __DEPTH;
          this.__INDEX = __INDEX;
          this.__PARENT_FINGERPRINT = __PARENT_FINGERPRINT;
          typeforce(NETWORK_TYPE, network);
          this.lowR = false;
      }
      get depth() {
          return this.__DEPTH;
      }
      get index() {
          return this.__INDEX;
      }
      get parentFingerprint() {
          return this.__PARENT_FINGERPRINT;
      }
      get publicKey() {
          if (this.__Q === undefined)
              this.__Q = ecc.pointFromScalar(this.__D, true);
          return this.__Q;
      }
      get privateKey() {
          return this.__D;
      }
      get identifier() {
          return crypto.hash160(this.publicKey);
      }
      get fingerprint() {
          return this.identifier.slice(0, 4);
      }
      // Private === not neutered
      // Public === neutered
      isNeutered() {
          return this.__D === undefined;
      }
      neutered() {
          return fromPublicKeyLocal(this.publicKey, this.chainCode, this.network, this.depth, this.index, this.parentFingerprint);
      }
      toBase58() {
          const network = this.network;
          const version = !this.isNeutered()
              ? network.bip32.private
              : network.bip32.public;
          const buffer = Buffer.allocUnsafe(78);
          // 4 bytes: version bytes
          buffer.writeUInt32BE(version, 0);
          // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ....
          buffer.writeUInt8(this.depth, 4);
          // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
          buffer.writeUInt32BE(this.parentFingerprint, 5);
          // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
          // This is encoded in big endian. (0x00000000 if master key)
          buffer.writeUInt32BE(this.index, 9);
          // 32 bytes: the chain code
          this.chainCode.copy(buffer, 13);
          // 33 bytes: the public key or private key data
          if (!this.isNeutered()) {
              // 0x00 + k for private keys
              buffer.writeUInt8(0, 45);
              this.privateKey.copy(buffer, 46);
              // 33 bytes: the public key
          }
          else {
              // X9.62 encoding for public keys
              this.publicKey.copy(buffer, 45);
          }
          return bs58check.encode(buffer);
      }
      toWIF() {
          if (!this.privateKey)
              throw new TypeError('Missing private key');
          return wif.encode(this.network.wif, this.privateKey, true);
      }
      // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions
      derive(index) {
          typeforce(typeforce.UInt32, index);
          const isHardened = index >= HIGHEST_BIT;
          const data = Buffer.allocUnsafe(37);
          // Hardened child
          if (isHardened) {
              if (this.isNeutered())
                  throw new TypeError('Missing private key for hardened child key');
              // data = 0x00 || ser256(kpar) || ser32(index)
              data[0] = 0x00;
              this.privateKey.copy(data, 1);
              data.writeUInt32BE(index, 33);
              // Normal child
          }
          else {
              // data = serP(point(kpar)) || ser32(index)
              //      = serP(Kpar) || ser32(index)
              this.publicKey.copy(data, 0);
              data.writeUInt32BE(index, 33);
          }
          const I = crypto.hmacSHA512(this.chainCode, data);
          const IL = I.slice(0, 32);
          const IR = I.slice(32);
          // if parse256(IL) >= n, proceed with the next value for i
          if (!ecc.isPrivate(IL))
              return this.derive(index + 1);
          // Private parent key -> private child key
          let hd;
          if (!this.isNeutered()) {
              // ki = parse256(IL) + kpar (mod n)
              const ki = ecc.privateAdd(this.privateKey, IL);
              // In case ki == 0, proceed with the next value for i
              if (ki == null)
                  return this.derive(index + 1);
              hd = fromPrivateKeyLocal(ki, IR, this.network, this.depth + 1, index, this.fingerprint.readUInt32BE(0));
              // Public parent key -> public child key
          }
          else {
              // Ki = point(parse256(IL)) + Kpar
              //    = G*IL + Kpar
              const Ki = ecc.pointAddScalar(this.publicKey, IL, true);
              // In case Ki is the point at infinity, proceed with the next value for i
              if (Ki === null)
                  return this.derive(index + 1);
              hd = fromPublicKeyLocal(Ki, IR, this.network, this.depth + 1, index, this.fingerprint.readUInt32BE(0));
          }
          return hd;
      }
      deriveHardened(index) {
          typeforce(UInt31, index);
          // Only derives hardened private keys by default
          return this.derive(index + HIGHEST_BIT);
      }
      derivePath(path) {
          typeforce(BIP32Path, path);
          let splitPath = path.split('/');
          if (splitPath[0] === 'm') {
              if (this.parentFingerprint)
                  throw new TypeError('Expected master, got child');
              splitPath = splitPath.slice(1);
          }
          return splitPath.reduce((prevHd, indexStr) => {
              let index;
              if (indexStr.slice(-1) === `'`) {
                  index = parseInt(indexStr.slice(0, -1), 10);
                  return prevHd.deriveHardened(index);
              }
              else {
                  index = parseInt(indexStr, 10);
                  return prevHd.derive(index);
              }
          }, this);
      }
      sign(hash, lowR) {
          if (!this.privateKey)
              throw new Error('Missing private key');
          if (lowR === undefined)
              lowR = this.lowR;
          if (lowR === false) {
              return ecc.sign(hash, this.privateKey);
          }
          else {
              let sig = ecc.sign(hash, this.privateKey);
              const extraData = Buffer.alloc(32, 0);
              let counter = 0;
              // if first try is lowR, skip the loop
              // for second try and on, add extra entropy counting up
              while (sig[0] > 0x7f) {
                  counter++;
                  extraData.writeUIntLE(counter, 0, 6);
                  sig = ecc.signWithEntropy(hash, this.privateKey, extraData);
              }
              return sig;
          }
      }
      verify(hash, signature) {
          return ecc.verify(hash, this.publicKey, signature);
      }
  }
  function fromBase58(inString, network) {
      const buffer = bs58check.decode(inString);
      if (buffer.length !== 78)
          throw new TypeError('Invalid buffer length');
      network = network || BITCOIN;
      // 4 bytes: version bytes
      const version = buffer.readUInt32BE(0);
      if (version !== network.bip32.private && version !== network.bip32.public)
          throw new TypeError('Invalid network version');
      // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ...
      const depth = buffer[4];
      // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
      const parentFingerprint = buffer.readUInt32BE(5);
      if (depth === 0) {
          if (parentFingerprint !== 0x00000000)
              throw new TypeError('Invalid parent fingerprint');
      }
      // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
      // This is encoded in MSB order. (0x00000000 if master key)
      const index = buffer.readUInt32BE(9);
      if (depth === 0 && index !== 0)
          throw new TypeError('Invalid index');
      // 32 bytes: the chain code
      const chainCode = buffer.slice(13, 45);
      let hd;
      // 33 bytes: private key data (0x00 + k)
      if (version === network.bip32.private) {
          if (buffer.readUInt8(45) !== 0x00)
              throw new TypeError('Invalid private key');
          const k = buffer.slice(46, 78);
          hd = fromPrivateKeyLocal(k, chainCode, network, depth, index, parentFingerprint);
          // 33 bytes: public key data (0x02 + X or 0x03 + X)
      }
      else {
          const X = buffer.slice(45, 78);
          hd = fromPublicKeyLocal(X, chainCode, network, depth, index, parentFingerprint);
      }
      return hd;
  }
  exports.fromBase58 = fromBase58;
  function fromPrivateKey(privateKey, chainCode, network) {
      return fromPrivateKeyLocal(privateKey, chainCode, network);
  }
  exports.fromPrivateKey = fromPrivateKey;
  function fromPrivateKeyLocal(privateKey, chainCode, network, depth, index, parentFingerprint) {
      typeforce({
          privateKey: UINT256_TYPE,
          chainCode: UINT256_TYPE,
      }, { privateKey, chainCode });
      network = network || BITCOIN;
      if (!ecc.isPrivate(privateKey))
          throw new TypeError('Private key not in range [1, n)');
      return new BIP32(privateKey, undefined, chainCode, network, depth, index, parentFingerprint);
  }
  function fromPublicKey(publicKey, chainCode, network) {
      return fromPublicKeyLocal(publicKey, chainCode, network);
  }
  exports.fromPublicKey = fromPublicKey;
  function fromPublicKeyLocal(publicKey, chainCode, network, depth, index, parentFingerprint) {
      typeforce({
          publicKey: typeforce.BufferN(33),
          chainCode: UINT256_TYPE,
      }, { publicKey, chainCode });
      network = network || BITCOIN;
      // verify the X coordinate is a point on the curve
      if (!ecc.isPoint(publicKey))
          throw new TypeError('Point is not on the curve');
      return new BIP32(undefined, publicKey, chainCode, network, depth, index, parentFingerprint);
  }
  function fromSeed(seed, network) {
      typeforce(typeforce.Buffer, seed);
      if (seed.length < 16)
          throw new TypeError('Seed should be at least 128 bits');
      if (seed.length > 64)
          throw new TypeError('Seed should be at most 512 bits');
      network = network || BITCOIN;
      const I = crypto.hmacSHA512(Buffer.from('Bitcoin seed', 'utf8'), seed);
      const IL = I.slice(0, 32);
      const IR = I.slice(32);
      return fromPrivateKey(IL, IR, network);
  }
  exports.fromSeed = fromSeed;
  