-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #101 from nerolation/master
Add taproot support
- Loading branch information
Showing
6 changed files
with
231 additions
and
6 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers | ||
# | ||
# This file is part of bitcoin-blockchain-parser. | ||
# | ||
# It is subject to the license terms in the LICENSE file found in the top-level | ||
# directory of this distribution. | ||
# | ||
# No part of bitcoin-blockchain-parser, including this file, may be copied, | ||
# modified, propagated, or distributed except according to the terms contained | ||
# in the LICENSE file. | ||
# | ||
# The transactions were taken from | ||
# https://bitcoin.stackexchange.com/questions/110995/how | ||
# -can-i-find-samples-for-p2tr-transactions-on-mainnet | ||
# | ||
# 33e7…9036, the first P2TR transaction | ||
# 3777…35c8, the first transaction with both a P2TR scriptpath and a P2TR keypath input | ||
# 83c8…7d82, with multiple P2TR keypath inputs | ||
# 905e…d530, the first scriptpath 2-of-2 multisig spend | ||
# 2eb8…b272, the first use of the new Tapscript opcode OP_CHECKSIGADD | ||
# | ||
# THESE TRANSACTIONS ARE INCLUDED IN BLK02804.DAT | ||
|
||
import os | ||
import sys | ||
sys.path.append('../..') | ||
from blockchain_parser.blockchain import Blockchain | ||
|
||
|
||
FIRST_TAPROOT = "33e794d097969002ee05d336686fc03c9e15a597c1b9827669460fac98799036" | ||
FIRST_TAPROOT_2X_P2TR = "37777defed8717c581b4c0509329550e344bdc14ac38f71fc050096887e535c8" | ||
MULTIPLE_P2TR_INPUTS = "83c8e0289fecf93b5a284705396f5a652d9886cbd26236b0d647655ad8a37d82" | ||
FIRST_2_OF_2_SPEND = "905ecdf95a84804b192f4dc221cfed4d77959b81ed66013a7e41a6e61e7ed530" | ||
USING_OP_CHECKSIGADD = "2eb8dbaa346d4be4e82fe444c2f0be00654d8cfd8c4a9a61b11aeaab8c00b272" | ||
|
||
|
||
TAPROOTS = [FIRST_TAPROOT, | ||
FIRST_TAPROOT_2X_P2TR, | ||
MULTIPLE_P2TR_INPUTS, | ||
FIRST_2_OF_2_SPEND, | ||
USING_OP_CHECKSIGADD] | ||
|
||
|
||
blockchain = Blockchain(os.path.expanduser('../../blocks')) | ||
for block in blockchain.get_unordered_blocks(): | ||
for tx in block.transactions: | ||
if tx.txid in TAPROOTS: | ||
print("{:<15}{}".format("Tx ID: ", tx.txid)) | ||
for tx_input in tx.inputs: | ||
print("{:<15}{}".format("Input Tx ID: ",tx_input.transaction_hash)) | ||
for tx_output in tx.outputs: | ||
for addr in tx_output.addresses: | ||
print("{:<15}{}".format("Address: ", addr.address)) | ||
print("{:<15}{:,.0f} s".format("Value: ", tx_output.value)) | ||
print("----------------------------------------------------------------") |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
# Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers | ||
# | ||
# This file is part of bitcoin-blockchain-parser. | ||
# | ||
# It is subject to the license terms in the LICENSE file found in the top-level | ||
# directory of this distribution. | ||
# | ||
# No part of bitcoin-blockchain-parser, including this file, may be copied, | ||
# modified, propagated, or distributed except according to the terms contained | ||
# in the LICENSE file. | ||
# | ||
# Encoding/Decoding written by Pieter Wuille (2017) | ||
# and adapted by Anton Wahrstätter (2022) | ||
# https://github.com/Bytom/python-bytomlib/blob/master/pybtmsdk/segwit_addr.py | ||
|
||
from enum import Enum | ||
|
||
|
||
class Encoding(Enum): | ||
"""Enumeration type to list the various supported encodings.""" | ||
BECH32 = 1 | ||
BECH32M = 2 | ||
|
||
|
||
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" | ||
BECH32M_CONST = 0x2bc830a3 | ||
|
||
|
||
def bech32_polymod(values): | ||
"""Internal function that computes the Bech32 checksum.""" | ||
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] | ||
chk = 1 | ||
for value in values: | ||
top = chk >> 25 | ||
chk = (chk & 0x1ffffff) << 5 ^ value | ||
for i in range(5): | ||
chk ^= generator[i] if ((top >> i) & 1) else 0 | ||
return chk | ||
|
||
|
||
def bech32_hrp_expand(hrp): | ||
"""Expand the HRP into values for checksum computation.""" | ||
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] | ||
|
||
|
||
def bech32_verify_checksum(hrp, data): | ||
"""Verify a checksum given HRP and converted data characters.""" | ||
const = bech32_polymod(bech32_hrp_expand(hrp) + data) | ||
if const == 1: | ||
return Encoding.BECH32 | ||
if const == BECH32M_CONST: | ||
return Encoding.BECH32M | ||
return None | ||
|
||
|
||
def bech32_create_checksum(hrp, data, spec): | ||
"""Compute the checksum values given HRP and data.""" | ||
values = bech32_hrp_expand(hrp) + data | ||
const = BECH32M_CONST if spec == Encoding.BECH32M else 1 | ||
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ const | ||
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] | ||
|
||
|
||
def bech32_encode(hrp, data, spec): | ||
"""Compute a Bech32 string given HRP and data values.""" | ||
combined = data + bech32_create_checksum(hrp, data, spec) | ||
return hrp + '1' + ''.join([CHARSET[d] for d in combined]) | ||
|
||
|
||
def bech32_decode(bech): | ||
"""Validate a Bech32/Bech32m string, and determine HRP and data.""" | ||
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or | ||
(bech.lower() != bech and bech.upper() != bech)): | ||
return (None, None, None) | ||
bech = bech.lower() | ||
pos = bech.rfind('1') | ||
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: | ||
return (None, None, None) | ||
if not all(x in CHARSET for x in bech[pos+1:]): | ||
return (None, None, None) | ||
hrp = bech[:pos] | ||
data = [CHARSET.find(x) for x in bech[pos+1:]] | ||
spec = bech32_verify_checksum(hrp, data) | ||
if spec is None: | ||
return (None, None, None) | ||
return (hrp, data[:-6], spec) | ||
|
||
|
||
def convertbits(data, frombits, tobits, pad=True): | ||
"""General power-of-2 base conversion.""" | ||
acc = 0 | ||
bits = 0 | ||
ret = [] | ||
maxv = (1 << tobits) - 1 | ||
max_acc = (1 << (frombits + tobits - 1)) - 1 | ||
for value in data: | ||
if value < 0 or (value >> frombits): | ||
return None | ||
acc = ((acc << frombits) | value) & max_acc | ||
bits += frombits | ||
while bits >= tobits: | ||
bits -= tobits | ||
ret.append((acc >> bits) & maxv) | ||
if pad: | ||
if bits: | ||
ret.append((acc << (tobits - bits)) & maxv) | ||
elif bits >= frombits or ((acc << (tobits - bits)) & maxv): | ||
return None | ||
return ret | ||
|
||
|
||
def decode(hrp, addr): | ||
"""Decode a segwit address.""" | ||
hrpgot, data, spec = bech32_decode(addr) | ||
if hrpgot != hrp: | ||
return (None, None) | ||
decoded = convertbits(data[1:], 5, 8, False) | ||
if decoded is None or len(decoded) < 2 or len(decoded) > 40: | ||
return (None, None) | ||
if data[0] > 16: | ||
return (None, None) | ||
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: | ||
return (None, None) | ||
if data[0] == 0 and spec != Encoding.BECH32 \ | ||
or data[0] != 0 and spec != Encoding.BECH32M: | ||
return (None, None) | ||
return (data[0], decoded) | ||
|
||
|
||
def encode(witprog): | ||
hrp, witver = "bc", 1 | ||
"""Encode a segwit address.""" | ||
spec = Encoding.BECH32M | ||
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5), spec) | ||
if decode(hrp, ret) == (None, None): | ||
return None | ||
return ret | ||
|
||
|
||
def from_taproot(tpk): | ||
"""Input Tweaked Public Key.""" | ||
tpk = [int(tpk[i:i+2], 16) for i in range(0, len(tpk), 2)] | ||
return encode(tpk) |