Expose EC point arithmetic and add bip352_encode for Silent Payments - #57
Open
macgyver13 wants to merge 4 commits into
Open
Expose EC point arithmetic and add bip352_encode for Silent Payments#57macgyver13 wants to merge 4 commits into
macgyver13 wants to merge 4 commits into
Conversation
scgbckbone
suggested changes
Jul 30, 2026
scgbckbone
left a comment
Contributor
There was a problem hiding this comment.
- Replace vstr output and mp_obj_new_str_from_vstr(...) with a byte buffer returned via mp_obj_new_bytes()
uint8_t output[33];
size_t outlen = sizeof(output);
secp256k1_ec_pubkey_serialize(..., output, &outlen, ...);
return mp_obj_new_bytes(output, outlen);
- Replace STATIC with standard lowercase static.
we will eventually migrate to newer mpy (v1.17 -> v1.28) and above is deprecated
Add thin wrappers returning unhashed curve points, which BIP-352 needs because keypair.ecdh_multiply() returns sha256(x||y) and the ECDH share has to be re-scaled by input_hash before it is hashed: secp256k1_ec_pubkey_tweak_mul -> ec_pubkey_tweak_mul(pubkey, scalar32) secp256k1_ec_pubkey_combine -> ec_pubkey_combine([pubkey, ...]) Add constants: generator(), curve_order() and curve_order_int()
Fold pairwise instead of allocating parse buffers for the whole list, so memory use is constant regardless of input length. Add test coverage for the new BIP-352 wrappers including the accumulator's point-at-infinity handling.
Encodes scan || spend compressed pubkeys as bech32m per BIP-352, with an optional version (0-31, default 0). A v0 address is ~117 characters, past bech32's 90-char cap. bech32.patch now names that cap BECH32_MAX_LEN (128) and derives BECH32_BUF_SIZE from it. Also accept a 32-byte scan key for the watch-only spscan/tspscan export. That payload is not a BIP-352 address and is only meaningful under those distinct HRPs.
Use lowercase static instead of STATIC. Return byte buffer instead of vstr for s_ec_pubkey_tweak_mul and s_ec_pubkey_combine.
macgyver13
force-pushed
the
add-bip352-support
branch
from
August 5, 2026 14:20
0515853 to
5dd9474
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
BIP-352 (Silent Payments) and BIP-374 (DLEQ proofs) need generic EC arithmetic
that the current API cannot express.
The existing
keypair.ecdh_multiply()returnssha256(x || y)of the sharedpoint. BIP-352 needs the point, not a digest, because the ECDH share is
multiplied again by
input_hashbefore it is hashed.BIP-352 also uses point addition to sum input pubkeys into A_sum, combine per-input
ECDH shares, and add a label tweak to the spend key.
generator()andcurve_order*()are exposed as constants because the Pythonside references them on nearly every operation. Deriving G through
keypair(1).pubkey()per call is wasteful, andcurve_order_int()skips anint.from_bytes()on each scalar reduction.bip352_encodefollows BIP-352's bech32m + version-byte layout forscan_key || spend_key(65 or 66 bytes depending on whether a scan pubkey or a watch-only scan privkey is passed).
Raises BECH32_MAX_LEN from bech32's 90-char cap to 128 to fit the silent payments address's
~117 chars, and re-derives the two pre-existing bech32 encode buffers from that same constant so
they don't drift.
Used by the Coldcard firmware's
shared/silentpayments.py(BIP-352) andshared/dleq.py(BIP-374) PR 587.
Note to reviewers: