Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contrib/make_libsecp256k1.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ info "Building $pkgname..."
--enable-module-recovery \
--enable-module-extrakeys \
--enable-module-schnorrsig \
--enable-module-musig \
--enable-experimental \
--enable-module-ecdh \
--disable-benchmark \
Expand Down
2 changes: 1 addition & 1 deletion contrib/requirements/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ aiohttp>=3.11.0,<4.0.0
aiohttp_socks>=0.9.2
certifi
jsonpatch
electrum_ecc>=0.0.4,<0.1
electrum_ecc>=0.0.8,<0.1
electrum_aionostr>=0.1.0,<0.2

# - upper limit to avoid needing hatchling at build-time :/
Expand Down
28 changes: 23 additions & 5 deletions electrum/bitcoin.py
Original file line number Diff line number Diff line change
Expand Up @@ -793,13 +793,30 @@ def is_dummy_address(cls, addr: str) -> bool:
class DummyAddressUsedInTxException(Exception): pass


def taproot_tweak_pubkey(pubkey32: bytes, h: bytes) -> Tuple[int, bytes]:
LEAF_VERSION_TAPSCRIPT = 0xC0


def tapleaf_hash(*, leaf_version: int, script: bytes) -> bytes:
if not isinstance(script, bytes):
raise TypeError("tapleaf script must be bytes")
if type(leaf_version) is not int:
raise TypeError("tapleaf version must be an integer")
if not 0 <= leaf_version <= 0xFE or leaf_version & 1 or leaf_version == 0x50:
raise ValueError("invalid tapleaf version")
return bip340_tagged_hash(b"TapLeaf", bytes([leaf_version]) + witness_push(script))


def taproot_tweak_hash(pubkey32: bytes, h: bytes) -> bytes:
assert isinstance(pubkey32, bytes), type(pubkey32)
assert isinstance(h, bytes), type(h)
assert len(pubkey32) == 32, len(pubkey32)
return bip340_tagged_hash(b"TapTweak", pubkey32 + h)


def taproot_tweak_pubkey(pubkey32: bytes, h: bytes) -> Tuple[int, bytes]:
int_from_bytes = lambda x: int.from_bytes(x, byteorder="big", signed=False)

tweak = int_from_bytes(bip340_tagged_hash(b"TapTweak", pubkey32 + h))
tweak = int_from_bytes(taproot_tweak_hash(pubkey32, h))
if tweak >= ecc.CURVE_ORDER:
raise ValueError
P = ecc.ECPubkey(b"\x02" + pubkey32)
Expand All @@ -816,7 +833,7 @@ def taproot_tweak_seckey(seckey0: bytes, h: bytes) -> bytes:
P = ecc.ECPrivkey(seckey0)
seckey = P.secret_scalar if P.has_even_y() else ecc.CURVE_ORDER - P.secret_scalar
pubkey32 = P.get_public_key_bytes(compressed=True)[1:]
tweak = int_from_bytes(bip340_tagged_hash(b"TapTweak", pubkey32 + h))
tweak = int_from_bytes(taproot_tweak_hash(pubkey32, h))
if tweak >= ecc.CURVE_ORDER:
raise ValueError
return int.to_bytes((seckey + tweak) % ecc.CURVE_ORDER, length=32, byteorder="big", signed=False)
Expand All @@ -832,8 +849,9 @@ def taproot_tweak_seckey(seckey0: bytes, h: bytes) -> bytes:
def taproot_tree_helper(script_tree: TapTree):
if isinstance(script_tree, tuple):
leaf_version, script = script_tree
h = bip340_tagged_hash(b"TapLeaf", bytes([leaf_version]) + witness_push(script))
return [((leaf_version, script), bytes())], h
return [((leaf_version, script), bytes())], tapleaf_hash(
leaf_version=leaf_version, script=script
)
left, left_h = taproot_tree_helper(script_tree[0])
right, right_h = taproot_tree_helper(script_tree[1])
ret = [(l, c + right_h) for l, c in left] + [(l, c + left_h) for l, c in right]
Expand Down
7 changes: 4 additions & 3 deletions electrum/descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@

from .bip32 import convert_bip32_strpath_to_intpath, BIP32Node, KeyOriginInfo, BIP32_PRIME
from . import bitcoin
from .bitcoin import construct_script, opcodes, construct_witness, taproot_output_script
from .bitcoin import (
LEAF_VERSION_TAPSCRIPT, construct_script, opcodes, construct_witness, taproot_output_script,
)
from . import constants
from .crypto import hash_160, sha256
from . import segwit_addr
Expand Down Expand Up @@ -808,9 +810,8 @@ def expand(self, *, pos: Optional[int] = None) -> "ExpandedScripts":
if self.desc_tree:
def transform(tree_node):
if isinstance(tree_node, Descriptor):
leaf_version = 0xc0
leaf_script = tree_node.expand(pos=pos).scriptcode_for_sighash # FIXME maybe rename scriptcode_for_sighash
return (leaf_version, leaf_script)
return (LEAF_VERSION_TAPSCRIPT, leaf_script)
assert len(tree_node) == 2, len(tree_node)
return [transform(tree_node[0]), transform(tree_node[1])]
script_tree = transform(self.desc_tree)
Expand Down
14 changes: 14 additions & 0 deletions electrum/plugins/swapserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

from electrum.util import log_exceptions, ignore_exceptions
from electrum.logging import Logger
from electrum.submarine_swaps import TAPROOT_COOPERATIVE_CAPABILITY, TAPROOT_SWAP_PROTOCOL
from electrum.util import EventListener

if TYPE_CHECKING:
Expand Down Expand Up @@ -71,6 +72,8 @@ async def run(self):
app.add_routes([web.post('/createswap', self.create_swap)])
app.add_routes([web.post('/createnormalswap', self.create_normal_swap)])
app.add_routes([web.post('/addswapinvoice', self.add_swap_invoice)])
app.add_routes([web.post('/claimtaprootswap', self.claim_taproot_swap)])
app.add_routes([web.post('/refundtaprootswap', self.refund_taproot_swap)])

runner = web.AppRunner(app)
await runner.setup()
Expand All @@ -85,6 +88,7 @@ async def get_pairs(self, r):
"info": [],
"warnings": [],
"htlcFirst": True,
"protocols": [TAPROOT_SWAP_PROTOCOL, TAPROOT_COOPERATIVE_CAPABILITY],
"pairs": {
"BTC/BTC": {
"rate": 1,
Expand Down Expand Up @@ -134,3 +138,13 @@ async def create_swap(self, r):
request = await r.json()
response = self.sm.server_create_swap(request)
return web.json_response(response)

async def claim_taproot_swap(self, r):
request = await r.json()
response = self.sm.server_claim_taproot_swap(request)
return web.json_response(response)

async def refund_taproot_swap(self, r):
request = await r.json()
response = self.sm.server_refund_taproot_swap(request)
return web.json_response(response)
Loading
Loading