Skip to content

Commit 47a8c81

Browse files
committed
Add some examples of spending transaction outputs
1 parent d2d9e18 commit 47a8c81

5 files changed

Lines changed: 181 additions & 3 deletions

File tree

README

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ CBlockHeader, nValue etc. Otherwise Python naming conventions are followed.
5050
Example Code
5151
------------
5252

53-
See examples/ directory.
53+
See examples/ directory. For instance this example creates a transaction
54+
spending a pay-to-script-hash transaction output:
55+
56+
$ PYTHONPATH=. examples/spend-pay-to-script-hash-txout.py
57+
01000000012db211b32295f7ca3e9cdd9f3f0ea12bd938f8fc62c372f1147882dea35a197e000000006b483045022100a4dc2dcebc908e7ea791f69cf99b55b28e709f37d6da1a9da79cff511e47192f02206bdb1f1a67ff0715510717852e452a12bea0076388ca72ae523e9998f053185a01210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71ffffffff01a0860100000000001976a91479fbfc3f34e7745860d76137da68f362380c606c88ac00000000
5458

5559

5660
Selecting the chain to use

bitcoin/tests/test_wallet.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
import unittest
77

8-
from bitcoin.core import b2x
8+
from bitcoin.core import b2x, x
9+
from bitcoin.core.script import CScript
910
from bitcoin.wallet import *
1011

1112
class Test_CBitcoinAddress(unittest.TestCase):
@@ -27,6 +28,15 @@ def test(self):
2728
self.assertEqual(a.nVersion, 196)
2829

2930

31+
def test_from_scriptPubKey(self):
32+
def T(hex_scriptpubkey, expected_str_address):
33+
scriptPubKey = CScript(x(hex_scriptpubkey))
34+
self.assertEqual(str(CBitcoinAddress.from_scriptPubKey(scriptPubKey)),
35+
expected_str_address)
36+
37+
T('a914000000000000000000000000000000000000000087', '31h1vYVSYuKP6AhS86fbRdMw9XHieotbST')
38+
39+
3040
class Test_CBitcoinSecret(unittest.TestCase):
3141
def test(self):
3242
def T(base58_privkey, expected_hex_pubkey, expected_is_compressed_value):

bitcoin/wallet.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
from __future__ import absolute_import, division, print_function, unicode_literals
1616

1717
import sys
18+
bchr = chr
1819
bord = ord
1920
if sys.version > '3':
20-
long = int
21+
bchr = lambda x: bytes([x])
2122
bord = lambda x: x
2223

2324
import bitcoin
@@ -31,6 +32,14 @@ class CBitcoinAddressError(bitcoin.base58.Base58Error):
3132
class CBitcoinAddress(bitcoin.base58.CBase58Data):
3233
"""A Bitcoin address"""
3334

35+
@classmethod
36+
def from_scriptPubKey(cls, scriptPubKey):
37+
if scriptPubKey.is_p2sh():
38+
return cls.from_bytes(scriptPubKey[2:22], bitcoin.params.BASE58_PREFIXES['SCRIPT_ADDR'])
39+
else:
40+
# FIXME: add pay-to-pubkey-hash
41+
raise CBitcoinAddressError('scriptPubKey not recognized')
42+
3443
def to_scriptPubKey(self):
3544
"""Convert an address to a scriptPubKey"""
3645
if self.nVersion == bitcoin.params.BASE58_PREFIXES['PUBKEY_ADDR']:
@@ -86,6 +95,14 @@ class CBitcoinSecretError(bitcoin.base58.Base58Error):
8695
class CBitcoinSecret(bitcoin.base58.CBase58Data, CKey):
8796
"""A base58-encoded secret key"""
8897

98+
@classmethod
99+
def from_secret_bytes(cls, secret, compressed=True):
100+
"""Create a secret key from a 32-byte secret"""
101+
self = cls.from_bytes(secret + b'\x01' if compressed else b'',
102+
bitcoin.params.BASE58_PREFIXES['SECRET_KEY'])
103+
self.__init__(None)
104+
return self
105+
89106
def __init__(self, s):
90107
if self.nVersion != bitcoin.params.BASE58_PREFIXES['SECRET_KEY']:
91108
raise CBitcoinSecretError('Not a base58-encoded secret key: got nVersion=%d; expected nVersion=%d' % \

examples/spend-p2sh-txout.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/python3
2+
3+
# Distributed under the MIT/X11 software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
"""Low-level example of how to spend a P2SH/BIP16 txout"""
7+
8+
import hashlib
9+
10+
from bitcoin import SelectParams
11+
from bitcoin.core import b2x, lx, COIN, COutPoint, CTxOut, CTxIn, CTransaction, Hash160
12+
from bitcoin.core.script import CScript, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL
13+
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
14+
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
15+
16+
SelectParams('mainnet')
17+
18+
# Create the (in)famous correct brainwallet secret key.
19+
h = hashlib.sha256(b'correct horse battery staple').digest()
20+
seckey = CBitcoinSecret.from_secret_bytes(h)
21+
22+
# Create a redeemScript. Similar to a scriptPubKey the redeemScript must be
23+
# satisfied for the funds to be spent.
24+
txin_redeemScript = CScript([seckey.pub, OP_CHECKSIG])
25+
print(b2x(txin_redeemScript))
26+
27+
# Create the magic P2SH scriptPubKey format from that redeemScript. You should
28+
# look at the CScript.to_p2sh_scriptPubKey() function in bitcoin.core.script to
29+
# understand what's happening, as well as read BIP16:
30+
# https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
31+
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
32+
33+
# Convert the P2SH scriptPubKey to a base58 Bitcoin address and print it.
34+
# You'll need to send some funds to it to create a txout to spend.
35+
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
36+
print('Pay to:',str(txin_p2sh_address))
37+
38+
# Same as the txid:vout the createrawtransaction RPC call requires
39+
#
40+
# lx() takes *little-endian* hex and converts it to bytes; in Bitcoin
41+
# transaction hashes are shown little-endian rather than the usual big-endian.
42+
# There's also a corresponding x() convenience function that takes big-endian
43+
# hex and converts it to bytes.
44+
txid = lx('bff785da9f8169f49be92fa95e31f0890c385bfb1bd24d6b94d7900057c617ae')
45+
vout = 0
46+
47+
# Create the txin structure, which includes the outpoint. The scriptSig
48+
# defaults to being empty.
49+
txin = CTxIn(COutPoint(txid, vout))
50+
51+
# Create the txout. This time we create the scriptPubKey from a Bitcoin
52+
# address.
53+
txout = CTxOut(0.0005*COIN, CBitcoinAddress('323uf9MgLaSn9T7vDaK1cGAZ2qpvYUuqSp').to_scriptPubKey())
54+
55+
# Create the unsigned transaction.
56+
tx = CTransaction([txin],[txout])
57+
58+
# Calculate the signature hash for that transaction. Note how the script we use
59+
# is the redeemScript, not the scriptPubKey. That's because when the CHECKSIG
60+
# operation happens EvalScript() will be evaluating the redeemScript, so the
61+
# corresponding SignatureHash() function will use that same script when it
62+
# replaces the scriptSig in the transaction being hashed with the script being
63+
# executed.
64+
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
65+
66+
# Now sign it. We have to append the type of signature we want to the end, in
67+
# this case the usual SIGHASH_ALL.
68+
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
69+
70+
# Set the scriptSig of our transaction input appropriately.
71+
txin.scriptSig = CScript([sig, txin_redeemScript])
72+
73+
# Verify the signature worked. This calls EvalScript() and actually executes
74+
# the opcodes in the scripts to see if everything worked out. If it doesn't an
75+
# exception will be raised.
76+
print(b2x(txin_scriptPubKey))
77+
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
78+
79+
# Done! Print the transaction to standard output with the bytes-to-hex
80+
# function.
81+
print(b2x(tx.serialize()))
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/python3
2+
3+
# Distributed under the MIT/X11 software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
"""Low-level example of how to spend a standard pay-to-pubkey-hash txout"""
7+
8+
import hashlib
9+
10+
from bitcoin import SelectParams
11+
from bitcoin.core import b2x, lx, COIN, COutPoint, CTxOut, CTxIn, CTransaction, Hash160
12+
from bitcoin.core.script import CScript, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL
13+
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
14+
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
15+
16+
SelectParams('mainnet')
17+
18+
# Create the (in)famous correct brainwallet secret key.
19+
h = hashlib.sha256(b'correct horse battery staple').digest()
20+
seckey = CBitcoinSecret.from_secret_bytes(h)
21+
22+
# Same as the txid:vout the createrawtransaction RPC call requires
23+
#
24+
# lx() takes *little-endian* hex and converts it to bytes; in Bitcoin
25+
# transaction hashes are shown little-endian rather than the usual big-endian.
26+
# There's also a corresponding x() convenience function that takes big-endian
27+
# hex and converts it to bytes.
28+
txid = lx('7e195aa3de827814f172c362fcf838d92ba10e3f9fdd9c3ecaf79522b311b22d')
29+
vout = 0
30+
31+
# Create the txin structure, which includes the outpoint. The scriptSig
32+
# defaults to being empty.
33+
txin = CTxIn(COutPoint(txid, vout))
34+
35+
# We also need the scriptPubKey of the output we're spending because
36+
# SignatureHash() replaces the transaction scriptSig's with it.
37+
#
38+
# Here we'll create that scriptPubKey from scratch using the pubkey that
39+
# corresponds to the secret key we generated above.
40+
txin_scriptPubKey = CScript([OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG])
41+
42+
# Create the txout. This time we create the scriptPubKey from a Bitcoin
43+
# address.
44+
txout = CTxOut(0.001*COIN, CBitcoinAddress('1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8').to_scriptPubKey())
45+
46+
# Create the unsigned transaction.
47+
tx = CTransaction([txin],[txout])
48+
49+
# Calculate the signature hash for that transaction.
50+
sighash = SignatureHash(txin_scriptPubKey, tx, 0, SIGHASH_ALL)
51+
52+
# Now sign it. We have to append the type of signature we want to the end, in
53+
# this case the usual SIGHASH_ALL.
54+
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
55+
56+
# Set the scriptSig of our transaction input appropriately.
57+
txin.scriptSig = CScript([sig, seckey.pub])
58+
59+
# Verify the signature worked. This calls EvalScript() and actually executes
60+
# the opcodes in the scripts to see if everything worked out. If it doesn't an
61+
# exception will be raised.
62+
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
63+
64+
# Done! Print the transaction to standard output with the bytes-to-hex
65+
# function.
66+
print(b2x(tx.serialize()))

0 commit comments

Comments
 (0)