BSV.Transaction.P2MPKH (bsv_sdk v2.0.0)

Copy Markdown View Source

Pay-to-Multiple-Public-Key-Hash (P2MPKH) signing template — STAS 3.0 v0.1 §10.2.

Module purpose

This module implements the wire-format helpers needed to construct, parse, hash, lock, and spend P2MPKH redeem-buffers as specified in the STAS 3.0 protocol specification v0.1 (section 10.2). It is used at issuance and redemption boundaries of a STAS token's lifecycle, and to derive the MPKH (HASH160 of the redeem buffer) used as the owner / arbitrator anchor inside STAS 3 in-life UTXOs.

Wire format (per STAS 3.0 v0.1 §10.2)

The "redeem script" (a.k.a. redeem buffer / multisig script) is exactly:

[m: 1B raw 0x01..0x05]
  [0x21][pk1: 33B compressed SEC1]
  [0x21][pk2: 33B compressed SEC1]
  ...
  [0x21][pkN: 33B compressed SEC1]
[n: 1B raw 0x01..0x05]

Total length = 2 + 34*N bytes. Constraints: 1 <= m <= n <= 5.

Note: m and n are raw threshold bytes (not OP_m / OP_n opcodes), and there is no trailing OP_CHECKMULTISIG. The template / engine re-builds the multisig script when verifying signatures.

MPKH

MPKH = HASH160(redeem_buffer). The 20-byte MPKH is what appears in the fixed 70-byte P2MPKH locking script and inside STAS 3 token UTXOs.

Unlocking stack (P2MPKH spend)

Per spec §10.2 line 414/434:

OP_0 <sig_1> <sig_2> ... <sig_m> <redeem_buffer>

The leading OP_0 is the OP_CHECKMULTISIG dummy.

Functions

  • new_multisig/2 — validate and wrap a (threshold, public keys) tuple.
  • to_script_bytes/1 — emit the spec wire format described above.
  • from_script_bytes/1 — parse the spec wire format. Rejects any other form.
  • mpkh/1 — HASH160 of the redeem buffer.
  • lock/1 — wrap the redeem buffer as a BSV.Script.t() (used as the "redemption script" data push, NOT a P2MPKH locking script — for the 70-byte fixed locking script see BSV.Tokens.Script.Templates).
  • unlock/3 + sign/3 — produce the P2MPKH unlocking script OP_0 <sigs...> <redeem_buffer>.
  • estimate_length/3 — size estimate of the unlocking script bytes.

Summary

Functions

Estimate the unlocking script length in bytes.

Parse a STAS 3.0 v0.1 §10.2 redeem-buffer back into a multisig script map.

Build a BSV.Script.t() whose only chunk is the redeem-buffer push.

Compute the MPKH — 20-byte HASH160 of the serialized redeem buffer.

Create a new multisig script map.

Sign a transaction input producing a P2MPKH unlocking script.

Serialize a multisig script to the STAS 3.0 v0.1 §10.2 redeem-buffer bytes.

Create a P2MPKH unlocker.

Types

multisig_script()

@type multisig_script() :: %{threshold: pos_integer(), public_keys: [binary()]}

t()

@type t() :: %BSV.Transaction.P2MPKH{
  multisig: multisig_script(),
  private_keys: [BSV.PrivateKey.t()],
  sighash_flag: non_neg_integer()
}

Functions

estimate_length(p2_mpkh, _, _)

Estimate the unlocking script length in bytes.

Layout: OP_0 (1B) + m * 73B (sig pushes) + push prefix for redeem (2B PUSHDATA1) + redeem buffer (2 + 34*n) = m*73 + 34*n + 5.

from_script_bytes(arg1)

@spec from_script_bytes(binary()) :: {:ok, multisig_script()} | {:error, term()}

Parse a STAS 3.0 v0.1 §10.2 redeem-buffer back into a multisig script map.

Input

  • Binary produced by to_script_bytes/1. Length must equal 2 + 34*N for some N in 1..5, and the leading byte m and trailing byte n must satisfy 1 <= m <= n <= 5.

Output

{:ok, multisig_script()} on success, {:error, reason} on any deviation from the wire format. The function deliberately rejects any other shape (no support for the legacy OP_m / OP_CHECKMULTISIG form).

lock(ms)

@spec lock(multisig_script()) :: {:ok, BSV.Script.t()}

Build a BSV.Script.t() whose only chunk is the redeem-buffer push.

This is convenient when the SDK needs to embed the redeem buffer as a data push (e.g. as the last item on the unlocking stack). It is not the fixed 70-byte P2MPKH locking script — for that, see BSV.Tokens.Script.Templates.p2mpkh_locking_script/1.

Input

  • multisig_script() map.

Output

{:ok, %BSV.Script{chunks: [{:data, redeem_buffer}]}}.

mpkh(ms)

@spec mpkh(multisig_script()) :: <<_::160>>

Compute the MPKH — 20-byte HASH160 of the serialized redeem buffer.

Input

  • multisig_script() map.

Output

20-byte binary (<<_::160>>).

new_multisig(threshold, public_keys)

@spec new_multisig(pos_integer(), [binary()]) ::
  {:ok, multisig_script()} | {:error, term()}

Create a new multisig script map.

Arguments

  • threshold — minimum signatures required (m), 1..5
  • public_keys — list of 33-byte compressed SEC1 public keys, length 1..5

Returns

{:ok, multisig_script()} or {:error, reason}

sign(p2_mpkh, tx, input_index)

Sign a transaction input producing a P2MPKH unlocking script.

Output

{:ok, %BSV.Script{}} whose chunks (in order) are:

OP_0 (as {:data, <<>>}),
<sig_1>, <sig_2>, ..., <sig_m>,
<redeem_buffer>

per STAS 3.0 v0.1 §10.2 line 414/434.

to_script_bytes(map)

@spec to_script_bytes(multisig_script()) :: binary()

Serialize a multisig script to the STAS 3.0 v0.1 §10.2 redeem-buffer bytes.

Input

  • multisig_script() map with :threshold (1..5) and :public_keys (1..5 compressed 33-byte keys).

Output

Binary of length 2 + 34*N shaped as:

<m::8> (<<0x21, pk_i::binary-size(33)>> for each pk_i) <n::8>

where both m and n are raw bytes (NOT OP_m / OP_n).

unlock(private_keys, multisig, opts \\ [])

@spec unlock([BSV.PrivateKey.t()], multisig_script(), keyword()) ::
  {:ok, t()} | {:error, term()}

Create a P2MPKH unlocker.

Arguments

  • private_keys — exactly m private keys satisfying the threshold
  • multisig — the multisig script the UTXO commits to
  • opts:sighash_flag (default 0x41 = SIGHASH_ALL | SIGHASH_FORKID)

Returns

{:ok, t()} on success, {:error, term()} if the key count does not match the threshold.