defmodule BitcoinLib.Address.P2SH do @moduledoc """ Implementation of P2SH-P2WPKH addresses BIP13: Address Format for pay-to-script-hash https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki BIP16: Pay to Script Hash https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki Source: https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki """ require Logger alias BitcoinLib.Crypto alias BitcoinLib.Key.PublicKey @doc """ Creates a P2SH-P2WPKH address, which is starting by 3, out of an Extended Public Key ## Examples iex> %BitcoinLib.Key.PublicKey{ ...> key: <<0x02D0DE0AAEAEFAD02B8BDC8A01A1B8B11C696BD3D66A2C5F10780D95B7DF42645C::264>>, ...> chain_code: <<0::256>> ...> } ...> |> BitcoinLib.Address.P2SH.from_public_key() "3D9iyFHi1Zs9KoyynUfrL82rGhJfYTfSG4" """ @spec from_public_key(%PublicKey{}, :mainnet | :testnet) :: binary() def from_public_key(%PublicKey{key: key}, network \\ :mainnet) do key |> hash160 |> create_redeem_script |> hash160 |> prepend_version_bytes(network) |> append_checksum |> base58_encode end @doc """ Creates a P2SH-P2WPKH address, which is starting by 3, out of a script hash ## Examples iex> <<0x11c371a2b2d22c7b8b1b51d9fde0e44a9dfdc7bb::160>> ...> |> BitcoinLib.Address.P2SH.from_script_hash(:testnet) "2Mts9cDyaoGfxPseMzxab2bAKHLW4o4SzAK" """ @spec from_script_hash(bitstring(), :mainnet | :testnet) :: binary() def from_script_hash(script_hash, network \\ :mainnet) do script_hash |> prepend_version_bytes(network) |> append_checksum() |> base58_encode end @doc """ Applies the address's checksum to make sure it's valid ## Examples iex> "2N4GxhpXrgdWJf5CaEoGWkoWCsW8NhygsKg" ...> |> BitcoinLib.Address.P2SH.valid?() true """ @spec valid?(binary()) :: boolean() def valid?("2" <> _ = address) do address |> base58_decode |> validate_checksum() end def valid?("3" <> _ = address) do address |> base58_decode |> validate_checksum() end def valid?(address) do {:error, "#{address} is not a valid P2SH address"} end defp hash160(value) do value |> Crypto.hash160() end defp create_redeem_script(public_key_hash) do <<0x14::size(16), public_key_hash::bitstring>> end defp prepend_version_bytes(redeem_script, :mainnet) do <<5::8, redeem_script::bitstring>> end defp prepend_version_bytes(redeem_script, :testnet) do <<0xC4::8, redeem_script::bitstring>> end defp append_checksum(public_key_hash) do checksum = public_key_hash |> Crypto.checksum() <> end defp validate_checksum(<>) do Crypto.checksum(data) == address_checksum end defp base58_encode(value) do value |> Base58.encode() end defp base58_decode(value) do value |> Base58.decode() end end