@ -1,17 +1,16 @@
# note, only used for non-cryptographic randomness:
import random
import json
from typing import List , Union , Tuple
# needed for single sha256 evaluation, which is used
# needed for single sha256 evaluation, which is used
# in bitcoin (p2wsh) but not exposed in python-bitcointx:
# in bitcoin (p2wsh) but not exposed in python-bitcointx:
import hashlib
import hashlib
import json
# note, only used for non-cryptographic randomness:
import random
from math import ceil
from math import ceil
from typing import List , Optional , Tuple , Union
from jmbitcoin . secp256k1_main import *
from jmbase import bintohex , utxo_to_utxostr
from bitcointx . core import ( CMutableTransaction , CTxInWitness ,
from bitcointx . core import ( CMutableTransaction , CTxInWitness ,
CMutableOutPoint , CMutableTxIn , CTransaction ,
CMutableOutPoint , CMutableTxIn , CTransaction ,
CMutableTxOut , CTxIn , CTxOut , ValidationError )
CMutableTxOut , CTxIn , CTxOut , ValidationError ,
CBitcoinTransaction )
from bitcointx . core . script import *
from bitcointx . core . script import *
from bitcointx . wallet import ( P2WPKHCoinAddress , CCoinAddress , P2PKHCoinAddress ,
from bitcointx . wallet import ( P2WPKHCoinAddress , CCoinAddress , P2PKHCoinAddress ,
CCoinAddressError )
CCoinAddressError )
@ -20,7 +19,11 @@ from bitcointx.core.scripteval import (VerifyScript, SCRIPT_VERIFY_WITNESS,
SCRIPT_VERIFY_STRICTENC ,
SCRIPT_VERIFY_STRICTENC ,
SIGVERSION_WITNESS_V0 )
SIGVERSION_WITNESS_V0 )
def human_readable_transaction ( tx , jsonified = True ) :
from jmbase import bintohex , utxo_to_utxostr
from jmbitcoin . secp256k1_main import *
def human_readable_transaction ( tx : CTransaction , jsonified : bool = True ) - > str :
""" Given a CTransaction object, output a human
""" Given a CTransaction object, output a human
readable json - formatted string ( suitable for terminal
readable json - formatted string ( suitable for terminal
output or large GUI textbox display ) containing
output or large GUI textbox display ) containing
@ -49,7 +52,8 @@ def human_readable_transaction(tx, jsonified=True):
return outdict
return outdict
return json . dumps ( outdict , indent = 4 )
return json . dumps ( outdict , indent = 4 )
def human_readable_input ( txinput , txinput_witness ) :
def human_readable_input ( txinput : CTxIn ,
txinput_witness : Optional [ CTxInWitness ] ) - > dict :
""" Pass objects of type CTxIn and CTxInWitness (or None)
""" Pass objects of type CTxIn and CTxInWitness (or None)
and a dict of human - readable entries for this input
and a dict of human - readable entries for this input
is returned .
is returned .
@ -68,7 +72,7 @@ def human_readable_input(txinput, txinput_witness):
txinput_witness . scriptWitness . serialize ( ) )
txinput_witness . scriptWitness . serialize ( ) )
return outdict
return outdict
def human_readable_output ( txoutput ) :
def human_readable_output ( txoutput : CTxOut ) - > dict :
""" Returns a dict of human-readable entries
""" Returns a dict of human-readable entries
for this output .
for this output .
"""
"""
@ -175,7 +179,7 @@ def estimate_tx_size(ins: List[str], outs: List[str]) -> Union[int, Tuple[int]]:
return nwsize
return nwsize
return ( wsize , nwsize )
return ( wsize , nwsize )
def tx_vsize ( tx ) :
def tx_vsize ( tx : CTransaction ) - > int :
"""
"""
Computes the virtual size ( in vbytes ) of a transaction
Computes the virtual size ( in vbytes ) of a transaction
"""
"""
@ -184,7 +188,8 @@ def tx_vsize(tx):
non_witness_size = raw_tx_size - witness_size
non_witness_size = raw_tx_size - witness_size
return ceil ( non_witness_size + .25 * witness_size )
return ceil ( non_witness_size + .25 * witness_size )
def pubkey_to_p2pkh_script ( pub , require_compressed = False ) :
def pubkey_to_p2pkh_script ( pub : bytes ,
require_compressed : bool = False ) - > CScript :
"""
"""
Given a pubkey in bytes , return a CScript
Given a pubkey in bytes , return a CScript
representing the corresponding pay - to - pubkey - hash
representing the corresponding pay - to - pubkey - hash
@ -192,7 +197,7 @@ def pubkey_to_p2pkh_script(pub, require_compressed=False):
"""
"""
return P2PKHCoinAddress . from_pubkey ( pub ) . to_scriptPubKey ( )
return P2PKHCoinAddress . from_pubkey ( pub ) . to_scriptPubKey ( )
def pubkey_to_p2wpkh_script ( pub ) :
def pubkey_to_p2wpkh_script ( pub : bytes ) - > CScript :
"""
"""
Given a pubkey in bytes ( compressed ) , return a CScript
Given a pubkey in bytes ( compressed ) , return a CScript
representing the corresponding pay - to - witness - pubkey - hash
representing the corresponding pay - to - witness - pubkey - hash
@ -200,7 +205,7 @@ def pubkey_to_p2wpkh_script(pub):
"""
"""
return P2WPKHCoinAddress . from_pubkey ( pub ) . to_scriptPubKey ( )
return P2WPKHCoinAddress . from_pubkey ( pub ) . to_scriptPubKey ( )
def pubkey_to_p2sh_p2wpkh_script ( pub ) :
def pubkey_to_p2sh_p2wpkh_script ( pub : bytes ) - > CScript :
"""
"""
Given a pubkey in bytes , return a CScript representing
Given a pubkey in bytes , return a CScript representing
the corresponding nested pay to witness keyhash
the corresponding nested pay to witness keyhash
@ -210,7 +215,7 @@ def pubkey_to_p2sh_p2wpkh_script(pub):
raise Exception ( " Invalid pubkey " )
raise Exception ( " Invalid pubkey " )
return pubkey_to_p2wpkh_script ( pub ) . to_p2sh_scriptPubKey ( )
return pubkey_to_p2wpkh_script ( pub ) . to_p2sh_scriptPubKey ( )
def redeem_script_to_p2wsh_script ( redeem_script ) :
def redeem_script_to_p2wsh_script ( redeem_script : Union [ bytes , CScript ] ) - > CScript :
""" Given redeem script of type CScript (or bytes)
""" Given redeem script of type CScript (or bytes)
returns the corresponding segwit v0 scriptPubKey as
returns the corresponding segwit v0 scriptPubKey as
for the case pay - to - witness - scripthash .
for the case pay - to - witness - scripthash .
@ -218,7 +223,7 @@ def redeem_script_to_p2wsh_script(redeem_script):
return standard_witness_v0_scriptpubkey (
return standard_witness_v0_scriptpubkey (
hashlib . sha256 ( redeem_script ) . digest ( ) )
hashlib . sha256 ( redeem_script ) . digest ( ) )
def mk_freeze_script ( pub , locktime ) :
def mk_freeze_script ( pub : bytes , locktime : int ) - > CScript :
"""
"""
Given a pubkey and locktime , create a script which can only be spent
Given a pubkey and locktime , create a script which can only be spent
after the locktime has passed using OP_CHECKLOCKTIMEVERIFY
after the locktime has passed using OP_CHECKLOCKTIMEVERIFY
@ -232,7 +237,7 @@ def mk_freeze_script(pub, locktime):
return CScript ( [ locktime , OP_CHECKLOCKTIMEVERIFY , OP_DROP , pub ,
return CScript ( [ locktime , OP_CHECKLOCKTIMEVERIFY , OP_DROP , pub ,
OP_CHECKSIG ] )
OP_CHECKSIG ] )
def mk_burn_script ( data ) :
def mk_burn_script ( data : bytes ) - > CScript :
""" For a given bytestring (data),
""" For a given bytestring (data),
returns a scriptPubKey which is an OP_RETURN
returns a scriptPubKey which is an OP_RETURN
of that data .
of that data .
@ -241,7 +246,12 @@ def mk_burn_script(data):
raise TypeError ( " data must be in bytes " )
raise TypeError ( " data must be in bytes " )
return CScript ( [ OP_RETURN , data ] )
return CScript ( [ OP_RETURN , data ] )
def sign ( tx , i , priv , hashcode = SIGHASH_ALL , amount = None , native = False ) :
def sign ( tx : CMutableTransaction ,
i : int ,
priv : bytes ,
hashcode : SIGHASH_Type = SIGHASH_ALL ,
amount : Optional [ int ] = None ,
native : bool = False ) - > Tuple [ Optional [ bytes ] , str ] :
"""
"""
Given a transaction tx of type CMutableTransaction , an input index i ,
Given a transaction tx of type CMutableTransaction , an input index i ,
and a raw privkey in bytes , updates the CMutableTransaction to contain
and a raw privkey in bytes , updates the CMutableTransaction to contain
@ -325,7 +335,10 @@ def sign(tx, i, priv, hashcode=SIGHASH_ALL, amount=None, native=False):
return sig , " signing succeeded "
return sig , " signing succeeded "
def mktx ( ins , outs , version = 1 , locktime = 0 ) :
def mktx ( ins : List [ Tuple [ bytes , int ] ] ,
outs : List [ dict ] ,
version : int = 1 ,
locktime : int = 0 ) - > CMutableTransaction :
""" Given a list of input tuples (txid(bytes), n(int)),
""" Given a list of input tuples (txid(bytes), n(int)),
and a list of outputs which are dicts with
and a list of outputs which are dicts with
keys " address " ( value should be * str * not CCoinAddress ) (
keys " address " ( value should be * str * not CCoinAddress ) (
@ -361,7 +374,10 @@ def mktx(ins, outs, version=1, locktime=0):
vout . append ( out )
vout . append ( out )
return CMutableTransaction ( vin , vout , nLockTime = locktime , nVersion = version )
return CMutableTransaction ( vin , vout , nLockTime = locktime , nVersion = version )
def make_shuffled_tx ( ins , outs , version = 1 , locktime = 0 ) :
def make_shuffled_tx ( ins : List [ Tuple [ bytes , int ] ] ,
outs : List [ dict ] ,
version : int = 1 ,
locktime : int = 0 ) - > CMutableTransaction :
""" Simple wrapper to ensure transaction
""" Simple wrapper to ensure transaction
inputs and outputs are randomly ordered .
inputs and outputs are randomly ordered .
NB : This mutates ordering of ` ins ` and ` outs ` .
NB : This mutates ordering of ` ins ` and ` outs ` .
@ -370,7 +386,12 @@ def make_shuffled_tx(ins, outs, version=1, locktime=0):
random . shuffle ( outs )
random . shuffle ( outs )
return mktx ( ins , outs , version = version , locktime = locktime )
return mktx ( ins , outs , version = version , locktime = locktime )
def verify_tx_input ( tx , i , scriptSig , scriptPubKey , amount = None , witness = None ) :
def verify_tx_input ( tx : CTransaction ,
i : int ,
scriptSig : CScript ,
scriptPubKey : CScript ,
amount : Optional [ int ] = None ,
witness : Optional [ CScriptWitness ] = None ) - > bool :
flags = set ( [ SCRIPT_VERIFY_STRICTENC ] )
flags = set ( [ SCRIPT_VERIFY_STRICTENC ] )
if witness :
if witness :
# https://github.com/Simplexum/python-bitcointx/blob/648ad8f45ff853bf9923c6498bfa0648b3d7bcbd/bitcointx/core/scripteval.py#L1250-L1252
# https://github.com/Simplexum/python-bitcointx/blob/648ad8f45ff853bf9923c6498bfa0648b3d7bcbd/bitcointx/core/scripteval.py#L1250-L1252
@ -383,7 +404,8 @@ def verify_tx_input(tx, i, scriptSig, scriptPubKey, amount=None, witness=None):
return False
return False
return True
return True
def extract_witness ( tx , i ) :
def extract_witness ( tx : CTransaction ,
i : int ) - > Tuple [ Optional [ Tuple [ CTxInWitness , . . . ] ] , str ] :
""" Given `tx` of type CTransaction, extract,
""" Given `tx` of type CTransaction, extract,
as a list of objects of type CScript , which constitute the
as a list of objects of type CScript , which constitute the
witness at the index i , followed by " success " .
witness at the index i , followed by " success " .
@ -401,7 +423,8 @@ def extract_witness(tx, i):
witness = tx . wit . vtxinwit [ i ]
witness = tx . wit . vtxinwit [ i ]
return ( witness , " success " )
return ( witness , " success " )
def extract_pubkey_from_witness ( tx , i ) :
def extract_pubkey_from_witness ( tx : CTransaction ,
i : int ) - > Tuple [ Optional [ CScriptWitness ] , str ] :
""" Extract the pubkey used to sign at index i,
""" Extract the pubkey used to sign at index i,
in CTransaction tx , assuming it is of type p2wpkh
in CTransaction tx , assuming it is of type p2wpkh
( including wrapped segwit version ) .
( including wrapped segwit version ) .
@ -418,7 +441,7 @@ def extract_pubkey_from_witness(tx, i):
return None , " invalid pubkey in witness "
return None , " invalid pubkey in witness "
return sWitness [ 1 ] , " success "
return sWitness [ 1 ] , " success "
def get_equal_outs ( tx ) :
def get_equal_outs ( tx : CTransaction ) - > Optional [ List [ CTxOut ] ] :
""" If 2 or more transaction outputs have the same
""" If 2 or more transaction outputs have the same
bitcoin value , return then as a list of CTxOuts .
bitcoin value , return then as a list of CTxOuts .
If there is not exactly one equal output size , return False .
If there is not exactly one equal output size , return False .
@ -429,14 +452,16 @@ def get_equal_outs(tx):
if len ( eos ) > 0 :
if len ( eos ) > 0 :
eos = set ( eos )
eos = set ( eos )
if len ( eos ) > 1 :
if len ( eos ) > 1 :
return Fals e
return Non e
for i , vout in enumerate ( tx . vout ) :
for i , vout in enumerate ( tx . vout ) :
if vout . nValue == list ( eos ) [ 0 ] :
if vout . nValue == list ( eos ) [ 0 ] :
retval . append ( ( i , vout ) )
retval . append ( ( i , vout ) )
assert len ( retval ) > 1
assert len ( retval ) > 1
return retval
return retval
def is_jm_tx ( tx , min_cj_amount = 75000 , min_participants = 3 ) :
def is_jm_tx ( tx : CBitcoinTransaction ,
min_cj_amount : int = 75000 ,
min_participants : int = 3 ) - > Union [ Tuple [ bool , None ] , Tuple [ int , int ] ] :
""" Identify Joinmarket-patterned transactions.
""" Identify Joinmarket-patterned transactions.
TODO : this should be in another module .
TODO : this should be in another module .
Given a CBitcoinTransaction tx , check :
Given a CBitcoinTransaction tx , check :