Skip to content

Commit

Permalink
Replace p2as with 2-of-2 p2ms
Browse files Browse the repository at this point in the history
  • Loading branch information
mohanson authored May 16, 2024
1 parent 6c5b5fa commit e83068c
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 35 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ $ python example/addr.py --net mainnet --prikey 1

**example/taproot.py**

This example demonstrates how to create a P2TR script with two script spending paths: p2pk and p2as.
This example demonstrates how to create a P2TR script with two script spending paths: p2pk and p2ms(2-of-2 multisig).

```sh
$ python example/taproot.py
Expand Down
1 change: 1 addition & 0 deletions btc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
from . import rpc
from . import schnorr
from . import secp256k1
from . import taproot
from . import wallet
22 changes: 22 additions & 0 deletions btc/taproot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import btc
import typing


class Leaf:
def __init__(self, script: bytearray):
data = bytearray()
data.append(0xc0)
data.extend(btc.core.compact_size_encode(len(script)))
data.extend(script)
self.hash = btc.core.hashtag('TapLeaf', data)
self.script = script


class Node:
def __init__(self, l: typing.Self | Leaf, r: typing.Self | Leaf):
if l.hash < r.hash:
self.hash = btc.core.hashtag('TapBranch', l.hash + r.hash)
else:
self.hash = btc.core.hashtag('TapBranch', r.hash + l.hash)
self.l = l
self.r = r
58 changes: 24 additions & 34 deletions example/taproot.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,22 @@
import btc
import typing
Self = typing.Self

# This example shows how to create a P2TR script with two unlock conditions: p2pk and p2as.


class TapLeaf:
def __init__(self, script: bytearray):
data = bytearray()
data.append(0xc0)
data.extend(btc.core.compact_size_encode(len(script)))
data.extend(script)
self.hash = btc.core.hashtag('TapLeaf', data)
self.script = script


class TapNode:
def __init__(self, l: Self | TapLeaf, r: Self | TapLeaf):
if l.hash < r.hash:
self.hash = btc.core.hashtag('TapBranch', l.hash + r.hash)
else:
self.hash = btc.core.hashtag('TapBranch', r.hash + l.hash)
self.l = l
self.r = r
# This example shows how to create a P2TR script with two unlock conditions: p2pk and p2ms.


# Here created two scripts, one of which is a p2pk script, which requires that it can only be unlocked by private key 2,
# and the other is an p2as(always success) script, which means that anyone can spend the utxo.
mast = TapNode(
TapLeaf(btc.core.script([
# and the other is an 2-of-2 multisig script.
mast = btc.taproot.Node(
btc.taproot.Leaf(btc.core.script([
btc.opcode.op_pushdata(btc.core.PriKey(2).pubkey().sec()[1:]),
btc.opcode.op_checksig,
])),
TapLeaf(btc.core.script([
btc.opcode.op_1
btc.taproot.Leaf(btc.core.script([
btc.opcode.op_pushdata(btc.core.PriKey(3).pubkey().sec()[1:]),
btc.opcode.op_checksig,
btc.opcode.op_pushdata(btc.core.PriKey(4).pubkey().sec()[1:]),
btc.opcode.op_checksigadd,
btc.opcode.op_n(2),
btc.opcode.op_equal,
]))
)

Expand Down Expand Up @@ -64,7 +47,7 @@ def txin(self, op: btc.core.OutPoint):
])


class Tp2trp2as:
class Tp2trp2ms:
def __init__(self, pubkey: btc.core.PubKey):
self.pubkey = pubkey
self.addr = btc.core.address_p2tr(pubkey, mast.hash)
Expand All @@ -77,10 +60,17 @@ def __init__(self, pubkey: btc.core.PubKey):
self.prefix = 0xc0

def sign(self, tx: btc.core.Transaction):
return tx
for i, e in enumerate(tx.vin):
m = btc.secp256k1.Fr(int.from_bytes(tx.digest_segwit_v1(i, btc.core.sighash_all, mast.r.script)))
r, s = btc.schnorr.sign(btc.secp256k1.Fr(4), m)
e.witness[0] = bytearray(r.x.x.to_bytes(32) + s.x.to_bytes(32)) + bytearray([btc.core.sighash_all])
r, s = btc.schnorr.sign(btc.secp256k1.Fr(3), m)
e.witness[1] = bytearray(r.x.x.to_bytes(32) + s.x.to_bytes(32)) + bytearray([btc.core.sighash_all])

def txin(self, op: btc.core.OutPoint):
return btc.core.TxIn(op, bytearray(), 0xffffffff, [
bytearray(65),
bytearray(65),
mast.r.script,
bytearray([self.prefix]) + self.pubkey.sec()[1:] + mast.l.hash,
])
Expand Down Expand Up @@ -115,8 +105,8 @@ def txin(self, op: btc.core.OutPoint):
# Spending by script path: always success(op_1).
mate.transfer(user_p2tr.script, 1 * btc.denomination.bitcoin)
assert user_p2tr.balance() == btc.denomination.bitcoin
user_p2as = btc.wallet.Wallet(Tp2trp2as(user_p2tr.signer.pubkey))
print('main: spending by script path p2as')
user_p2as.transfer_all(mate.script)
user_p2ms = btc.wallet.Wallet(Tp2trp2ms(user_p2tr.signer.pubkey))
print('main: spending by script path p2ms')
user_p2ms.transfer_all(mate.script)
assert user_p2tr.balance() == 0
print('main: spending by script path p2as done')
print('main: spending by script path p2ms done')

0 comments on commit e83068c

Please sign in to comment.