Source: rvnaddr.js

/***
 * @license
 * https://github.com/raven-community/rvnaddr
 * Copyright (c) 2018 MSFTserver
 * Copyright (c) 2018 Emilio Almansi
 * Distributed under the MIT software license, see the accompanying
 * file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 */

var bs58check = require('bs58check')

/**
 * General purpose Ravencoin address detection and translation.<br />
 * Supports all major Ravencoin address formats.<br />
 * Currently:
 * <ul>
 *    <li> Legacy format </li>
 *    <li> Rvn2addr format </li>
 * </ul>
 * @module rvnaddr
 */

/**
 * @static
 * Supported Ravencoin address formats.
 */
var Format = {}
Format.Legacy = 'legacy'
//Format.Rvn2addr = 'rvn2addr'

/**
 * @static
 * Supported networks.
 */
var Network = {}
Network.Mainnet = 'mainnet'
Network.Testnet = 'testnet'

/**
 * @static
 * Supported address types.
 */
var Type = {}
Type.P2PKH = 'p2pkh'
Type.P2SH = 'p2sh'

/**
 * Detects what is the given address' format.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @return {string}
 * @throws {InvalidAddressError}
 */
function detectAddressFormat (address) {
  return decodeAddress(address).format
}

/**
 * Detects what is the given address' network.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @return {string}
 * @throws {InvalidAddressError}
 */
function detectAddressNetwork (address) {
  return decodeAddress(address).network
}

/**
 * Detects what is the given address' type.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @return {string}
 * @throws {InvalidAddressError}
 */
function detectAddressType (address) {
  return decodeAddress(address).type
}

/**
 * Translates the given address into legacy format.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @return {string}
 * @throws {InvalidAddressError}
 */
function toLegacyAddress (address) {
  var decoded = decodeAddress(address)
  if (decoded.format === Format.Legacy) {
    return address
  }
  return encodeAsLegacy(decoded)
}

/**
 * Translates the given address into rvn2addr format.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @return {string}
 * @throws {InvalidAddressError}
 */
//function toRvn2Address (address) {
//  var decoded = decodeAddress(address)
//  if (decoded.format === Format.Rvn2addr) {
//    return address
//  }
//  return encodeAsRvn2addr(decoded)
//}

/**
 * Version byte table for base58 formats.
 * @private
 */
var VERSION_BYTE = {}
VERSION_BYTE[Format.Legacy] = {}
VERSION_BYTE[Format.Legacy][Network.Mainnet] = {}
VERSION_BYTE[Format.Legacy][Network.Mainnet][Type.P2PKH] = 0
VERSION_BYTE[Format.Legacy][Network.Mainnet][Type.P2SH] = 5
VERSION_BYTE[Format.Legacy][Network.Testnet] = {}
VERSION_BYTE[Format.Legacy][Network.Testnet][Type.P2PKH] = 111
VERSION_BYTE[Format.Legacy][Network.Testnet][Type.P2SH] = 196

/**
 * Decodes the given address into its constituting hash, format, network and type.
 * @private
 * @param {string} address - A valid Ravencoin address in any format.
 * @return {object}
 * @throws {InvalidAddressError}
 */
function decodeAddress (address) {
  try {
    return decodeBase58Address(address)
  } catch (error) {
  }
  //try {
  //  return decodeRvn2Address(address)
  //} catch (error) {
  //}
  throw new InvalidAddressError()
}

/**
 * Attempts to decode the given address assuming it is a base58 address.
 * @private
 * @param {string} address - A valid Ravencoin address in any format.
 * @return {object}
 * @throws {InvalidAddressError}
 */
function decodeBase58Address (address) {
  try {
    var payload = bs58check.decode(address)
    var versionByte = payload[0]
    var hash = Array.prototype.slice.call(payload, 1)
    switch (versionByte) {
      case VERSION_BYTE[Format.Legacy][Network.Mainnet][Type.P2PKH]:
        return {
          hash: hash,
          format: Format.Legacy,
          network: Network.Mainnet,
          type: Type.P2PKH
        }
      case VERSION_BYTE[Format.Legacy][Network.Mainnet][Type.P2SH]:
        return {
          hash: hash,
          format: Format.Legacy,
          network: Network.Mainnet,
          type: Type.P2SH
        }
      case VERSION_BYTE[Format.Legacy][Network.Testnet][Type.P2PKH]:
        return {
          hash: hash,
          format: Format.Legacy,
          network: Network.Testnet,
          type: Type.P2PKH
        }
      case VERSION_BYTE[Format.Legacy][Network.Testnet][Type.P2SH]:
        return {
          hash: hash,
          format: Format.Legacy,
          network: Network.Testnet,
          type: Type.P2SH
        }
    }
  } catch (error) {
  }
  throw new InvalidAddressError()
}

/**
 * Attempts to decode the given address assuming it is a rvn2addr address.
 * @private
 * @param {string} address - A valid Ravencoin address in any format.
 * @return {object}
 * @throws {InvalidAddressError}
 */
//function decodeRvn2Address (address) {
//  if (address.indexOf(':') !== -1) {
//    try {
//      return decodeRvn2AddressWithPrefix(address)
//    } catch (error) {
//    }
//  } else {
//    var prefixes = ['ravencoin', 'rvntest', 'regtest']
//    for (var i = 0; i < prefixes.length; ++i) {
//      try {
//        var prefix = prefixes[i]
//        return decodeRvn2AddressWithPrefix(prefix + ':' + address)
//      } catch (error) {
//      }
//    }
//  }
//  throw new InvalidAddressError()
//}

/**
 * Attempts to decode the given address assuming it is a rvn2addr address with explicit prefix.
 * @private
 * @param {string} address - A valid Ravencoin address in any format.
 * @return {object}
 * @throws {InvalidAddressError}
 */
//function decodeRvn2AddressWithPrefix (address) {
//  try {
//    var decoded = rvn2addr.decode(address)
//    var hash = Array.prototype.slice.call(decoded.hash, 0)
//    var type = decoded.type === 'P2PKH' ? Type.P2PKH : Type.P2SH
//    switch (decoded.prefix) {
//      case 'ravencoin':
//        return {
//          hash: hash,
//          format: Format.Rvn2addr,
//          network: Network.Mainnet,
//          type: type
//        }
//      case 'rvntest':
//      case 'regtest':
//        return {
//          hash: hash,
//          format: Format.Rvn2addr,
//          network: Network.Testnet,
//          type: type
//        }
//    }
//  } catch (error) {
//  }
//  throw new InvalidAddressError()
//}

/**//
 * Encodes the given decoded address into legacy format.
 * @private
 * @param {object} decoded
 * @returns {string}
 */
function encodeAsLegacy (decoded) {
  var versionByte = VERSION_BYTE[Format.Legacy][decoded.network][decoded.type]
  var buffer = Buffer.alloc(1 + decoded.hash.length)
  buffer[0] = versionByte
  buffer.set(decoded.hash, 1)
  return bs58check.encode(buffer)
}

/**
 * Encodes the given decoded address into rvn2addr format.
 * @private
 * @param {object} decoded
 * @returns {string}
 */
//function encodeAsRvn2addr (decoded) {
//  var prefix = decoded.network === Network.Mainnet ? 'ravencoin' : 'rvntest'
//  var type = decoded.type === Type.P2PKH ? 'P2PKH' : 'P2SH'
//  var hash = Uint8Array.from(decoded.hash)
//  return rvn2addr.encode(prefix, type, hash)
//}

/**
 * Returns a boolean indicating whether the address is in legacy format.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @returns {boolean}
 * @throws {InvalidAddressError}
 */
function isLegacyAddress (address) {
  return detectAddressFormat(address) === Format.Legacy
}

/**
 * Returns a boolean indicating whether the address is in rvn2addr format.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @returns {boolean}
 * @throws {InvalidAddressError}
 */
//function isRvn2Address (address) {
//  return detectAddressFormat(address) === Format.Rvn2addr
//}

/**
 * Returns a boolean indicating whether the address is a mainnet address.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @returns {boolean}
 * @throws {InvalidAddressError}
 */
function isMainnetAddress (address) {
  return detectAddressNetwork(address) === Network.Mainnet
}

/**
 * Returns a boolean indicating whether the address is a testnet address.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @returns {boolean}
 * @throws {InvalidAddressError}
 */
function isTestnetAddress (address) {
  return detectAddressNetwork(address) === Network.Testnet
}

/**
 * Returns a boolean indicating whether the address is a p2pkh address.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @returns {boolean}
 * @throws {InvalidAddressError}
 */
function isP2PKHAddress (address) {
  return detectAddressType(address) === Type.P2PKH
}

/**
 * Returns a boolean indicating whether the address is a p2sh address.
 * @static
 * @param {string} address - A valid Ravencoin address in any format.
 * @returns {boolean}
 * @throws {InvalidAddressError}
 */
function isP2SHAddress (address) {
  return detectAddressType(address) === Type.P2SH
}

/**
 * Error thrown when the address given as input is not a valid Ravencoin address.
 * @constructor
 * InvalidAddressError
 */
function InvalidAddressError () {
  var error = new Error()
  this.name = error.name = 'InvalidAddressError'
  this.message = error.message = 'Received an invalid Ravencoin address as input.'
  this.stack = error.stack
}

InvalidAddressError.prototype = Object.create(Error.prototype)

module.exports = {
  Format: Format,
  Network: Network,
  Type: Type,
  detectAddressFormat: detectAddressFormat,
  detectAddressNetwork: detectAddressNetwork,
  detectAddressType: detectAddressType,
  toLegacyAddress: toLegacyAddress,
  //toRvn2Address: toRvn2Address,
  isLegacyAddress: isLegacyAddress,
  //isRvn2Address: isRvn2Address,
  isMainnetAddress: isMainnetAddress,
  isTestnetAddress: isTestnetAddress,
  isP2PKHAddress: isP2PKHAddress,
  isP2SHAddress: isP2SHAddress,
  InvalidAddressError: InvalidAddressError
}