Skip to content

update documentation for multisig quorum constraints #535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

cc-unchained
Copy link

Motivation

Consensus declares 520 as the maximum number of bytes pushable to the stack, but this alone does not define the multisig quorum constraints.
https://github.com/bitcoin/bitcoin/blob/ae024137bda9fe189f4e7ccf26dbaffd44cbbeb6/src/script/script.h#L28

Consider the following constant in standard node policy
https://github.com/bitcoin/bitcoin/blob/3eaa0a3b663782bb1bd874ea881b21649f1db767/src/policy/policy.h#L54-L56

static constexpr unsigned int MAX_STANDARD_SCRIPTSIG_SIZE{1650};

For 2-of-3 the spending scripts are structured as follows, and placed in the scriptSig for P2SH and in the witness items for P2WSH.

OP_0
OP_PUSH72 <ecdsa_signature> OP_PUSH72 <ecdsa_signature>
OP_PUSHDATA1 105
<OP_2 OP_PUSH33 <pubkey> OP_PUSH33 <pubkey> OP_PUSH33 <pubkey> OP_3 OP_CHECKMULTISIG>

We can generalize this to M of N

OP_0
(OP_PUSH72 * <ecdsa_signature>)*M
OP_PUSHDATA2 <size>
OP_M (OP_PUSH33 <pubkey>)*N OP_N OP_CHECKMULTISIG

Let's assume for a moment that we let M=N, so we can solve for the maximum number of keys we can have in a multisig script. (Let's use M=N=K)

NOTE: When N > 8 we have 1+(34*8) + 2 = 276 which is more than 256 bytes we can specify in OP_PUSHDATA1, so we need to use OP_PUSHDATA2.

Operation Byte Count
OP_0 1
OP_PUSH72 * <ecdsa_signature>)*K 73*K
OP_PUSHDATA2 <size> 3
OP_M (OP_PUSH33 <pubkey>)*N OP_N OP_CHECKMULTISIG 3 + 34*K

With a max of 1650 bytes for P2SH

$$1 + 73 \cdot K + 3 + 3 + 34 \cdot K = 7 + 107 \cdot K$$ $$\implies K = floor\>\frac{1650-7}{107} = 15$$

∴ The theoretical maximum number of keys in a P2SH quorum with M=N is 15. Derived not from script element size, but from the maximum size of the scriptSig in policy.

Copy link
Collaborator

@scgbckbone scgbckbone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

imo 520 byte redeem script limit should still be at least mentioned - not just removed. Also the parentheses content does not make much sense. Same limit cannot apply to segwit when native segwit v0 multisig allows N to be up to 20 signers (OP_CHECKMULTISIG limit)

@cc-unchained
Copy link
Author

cc-unchained commented Jun 9, 2025

Thanks for the quick feedback @scgbckbone! I appreciate you entertaining the amendment to the doc and would like to refine it for accuracy as you suggest.

The optech calculator has been an excellent reference for my own understanding in transaction construction especially around the consensus limitations for script. https://bitcoinops.org/en/tools/calc-size/#output

It helps distinguish between the hashed redeem script that's placed in the scriptPubKey of outputs, which is generally fixed at 23 bytes (160 bits from HASH160), and the scriptSig of inputs. I'm suggesting the latter is the true limiting factor for the number of signers in P2SH.

It's not relevant for native segwit since the spending scripts are serialized in the witness data. For P2SH the entire redeem script is exposed in the scriptSig, and each script element does not exceed 520 bytes. The largest is the 72 byte ECDSA signatures. If using bare multisig in the scriptPubKey, the 520 byte limit is also not exceeded.

With respect to the parenthetical comment, it appears the MAX_SIGNERS constant is indeed enforced for both P2SH and P2WSH? I'm looking at shared/multisig.py which imports the constant from external/ckcc-protocol/ckcc/constants.py. I might need to dive deeper into the implementation to understand.

I've included a note about the 520 byte limit rather than just deleting it entirely, let me know if I've misunderstood anything about the segwit limits or should rephrase. Thank you again for the time!

EDIT: here is an additional reference comment in core (which ironically may be inaccurate in its reference to the MAX_SCRIPT_ELEMENT_SIZE)
https://github.com/bitcoin/bitcoin/blob/ae024137bda9fe189f4e7ccf26dbaffd44cbbeb6/src/policy/policy.cpp#L122-L129

EDIT 2: if accepted I can create a complimentary pull request to ckcc-protocol to update this comment
https://github.com/Coldcard/ckcc-protocol/blob/master/ckcc/constants.py#L47, along the lines of

-# - 520 byte redeem script limit <= 15*34 bytes per pubkey == 510 bytes
+# - 1650 byte scriptSig limit >= 1+73*M+3+3+34*M=1612, where M is 15 pubkeys

@scgbckbone
Copy link
Collaborator

With respect to the parenthetical comment, it appears the MAX_SIGNERS constant is indeed enforced for both P2SH and P2WSH? I'm looking at shared/multisig.py which imports the constant from external/ckcc-protocol/ckcc/constants.py. I might need to dive deeper into the implementation to understand.

that is correct - we enforce max N=15 limit for segwit too (for no good reason but nobody asked for N>15 yet)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants