defmodule BitcoinLib.Transaction.Signer do @moduledoc """ Signs transactions """ alias BitcoinLib.Crypto alias BitcoinLib.Key.{PrivateKey, PublicKey} alias BitcoinLib.Transaction alias BitcoinLib.Script alias BitcoinLib.Script.Opcodes @sighash_all 1 @byte 8 @four_bytes 32 @doc """ Takes a transaction and signs it with the private key that's in the second parameter ## Examples testnet transaction id: 838935b6bf2a16966fe261f23f28a88482f3b3d24c9847b68a56abe90d41ca97 iex> private_key = BitcoinLib.Key.PrivateKey.from_seed_phrase( ...> "rally celery split order almost twenty ignore record legend learn chaos decade" ...> ) ...> %BitcoinLib.Transaction{ ...> version: 1, ...> inputs: [ ...> %BitcoinLib.Transaction.Input{ ...> txid: "e4c226432a9319d603b2ed1fa609bffe4cd91f89b3176a9e73b19f7891a92bb6", ...> vout: 0, ...> sequence: 0xFFFFFFFF, ...> script_sig: <<0x76A914AFC3E518577316386188AF748A816CD14CE333F288AC::200>> |> BitcoinLib.Script.parse!() ...> } ...> ], ...> outputs: [%BitcoinLib.Transaction.Output{ ...> script_pub_key: <<0x76a9283265393261373463333431393661303236653839653061643561633431386366393430613361663288ac::360>> |> BitcoinLib.Script.parse!(), ...> value: 10_000} ...> ], ...> locktime: 0 ...> } ...> |> BitcoinLib.Transaction.Signer.sign_and_encode(private_key) "0100000001b62ba991789fb1739e6a17b3891fd94cfebf09a61fedb203d619932a4326c2e4000000006a47304402207d2ff650acf4bd2f413dc04ded50fbbfc315bcb0aa97636b3c4caf55333d1c6a02207590f62363b2263b3d9b65dad3cd56e840e0d61dc0feab8f7e7956831c7e5103012102702ded1cca9816fa1a94787ffc6f3ace62cd3b63164f76d227d0935a33ee48c3ffffffff0110270000000000002d76a9283265393261373463333431393661303236653839653061643561633431386366393430613361663288ac00000000" """ @spec sign_and_encode(%Transaction{}, %PrivateKey{}) :: binary() def sign_and_encode(%Transaction{} = transaction, %PrivateKey{} = private_key) do with public_key <- PublicKey.from_private_key(private_key) do transaction |> create_preimage(@sighash_all) |> create_signature(private_key) |> sign_input(transaction, public_key, @sighash_all) |> encode() end end defp create_preimage(transaction, sighash_type) do transaction |> Transaction.encode() |> append_sighash(sighash_type) |> Crypto.double_sha256() end defp create_signature(preimage, private_key), do: PrivateKey.sign_message(preimage, private_key) defp sign_input(signature, transaction, public_key, sighash_type) do script_sig = signature |> create_script_sig(public_key, sighash_type) [old_input] = transaction.inputs new_input = %{old_input | script_sig: script_sig} %{transaction | inputs: [new_input]} end defp encode(transaction) do transaction |> Transaction.encode() |> Binary.to_hex() end defp create_script_sig(signature, public_key, sighash_type) do Script.encode([ %Opcodes.Data{value: <>}, %Opcodes.Data{value: public_key.key} ]) |> elem(1) end defp append_sighash(transaction, sighash) do <> end end