Btcpy
A Python3 SegWit-compliant library which provides tools to handle Bitcoin data structures in a simple fashion.
Install / Use
/learn @chainside/BtcpyREADME
btcpy
btcpy is a Python>=3.3 SegWit-compliant library which provides tools to handle
Bitcoin data structures in a simple fashion. In particular, the main goal of
this library is to provide a simple interface to parse and create complex
Bitcoin scripts.
N.B.: this library is a work in progress so it is highly discouraged to use it in a production environment. Also, as long as the version is 0.*, API breaking changes should be expected
Table of Contents
- btcpy
- Table of Contents
- Requirements
- Installation
- What it does
- What it does not do
- Structure
- Usage examples
- Contributing and running tests
- Roadmap to v1
- TODO
- Acknowledgements
Requirements
The strict requirements of this library are:
pip install ecdsa
pip install base58
as an additional requirement, only used for integration testing purposes, this library uses:
pip install python-bitcoinlib==0.7.0
this is used to communicate with the Bitcoin node in order to test transactions validation.
Installation
To install this library and its dependencies one can just run
pip install chainside-btcpy
What it does
The main functionalities provided by this project are the following.
-
Parsing of blocks
-
Parsing, creation and signing of transactions
-
Parsing and creation of scripts. This also includes many nonstandard script types such as:
- Hashlocked scripts
- Timelocked scripts, with both absolute and relative times
- Arbitrarily nested if-else clauses
all scripts are easily embeddable in P2SH and P2WSH format, also supporting SegWit-over-P2SH formats. This library also offers functions to spend such complex scripts by only providing the necessary data.
What it does not do
This library does not implement the following functionalities:
- Validation: when blocks, transactions and scripts are parsed, only format errors are reported. No proof-of-work validation, script execution, transaction validation and signature verification is performed. For these consensus-critical functionalities, users of this library should rely on Bitcoin Core or other libraries that perform validation.
- Communication with the Bitcoin nodes. This is not provided neither on an RPC nor a networking level. For this purpose we highly recommed python-bitcoinlib.
Structure
All important data structures can be found in btcpy.structs, helper modules
are located in btcpy.lib. Objects in btcpy.structs are meant as a public
interface, while objects located in btcpy.lib are used internally.
Usage examples
Setup
The first thing to do the first time this package is imported is to set a global state which indicates on which network you are working and wether you want strict mode enabled. These two settings are further explained in the following sections.
To setup btcpy, you can use the following function
from btcpy.setup import setup
setup('regtest', strict=True)
Network
You can setup the network you will work on by calling:
from btcpy.setup import setup
setup('regtest')
supported network types are:
regtest
testnet
mainnet
The btcpy.setup module also provides the following network-related functions:
is_mainnet() - returns True if 'mainnet' was selected, False otherwise
net_name() - returns the value that was selected when calling setup()
Strictness
btcpy never performs validation. However, we don't want you to inadvertently lose your funds
for a mistake, so, in strict mode, when you do something that looks dangerous, the library
always makes sure that you know exactly what you are doing.
To setup the library in strict mode, you can run the setup as follows:
setup(my_network, strict=True) # True is actually the default for strict mode, the only other option is False
Additionally, you can force (non-)strictness on specific functions that have a strict=None
as keyword argument. If the strict keyword argument is left to None, then the strictness
specified in the setup will be followed, otherwise the param you pass to strict will be used.
The following additional checks are done when in strict mode:
- Do not allow to create
P2pkScripts with public keys that have an invalid format (please note that during parsing such scripts will not even be recognised as scripts of type'p2pk'when strict mode is enabled, they will instead be recognised as of type'nonstandard') - Do not allow to create m-of-n
MultisigScripts with less thanmpublic keys that have a valid format (please note that during parsing such scripts will not even be recognised as scripts of type'multisig'when strict mode is enabled, they will instead be recognised as of type'nonstandard') - Do not allow to decode
ExtendedPublicKeysorExtendedPrivateKeysthat don't match the network you set insetup - Do not allow to decode
Addresses that don't match the network you set insetup
Parsing and serialization
Transaction, PublicKey, PrivateKey and Block can be extracted
from a hex string by doing:
from btcpy.structs.transaction import Transaction
from btcpy.structs.block import Block
from btcpy.structs.crypto import PublicKey, PrivateKey
tx = Transaction.unhexlify(hex_tx)
block = Block.unhexlify(hex_block)
pubk = PublicKey.unhexlify(pubk_hex)
privk = PrivateKey.unhexlify(privk_hex)
PublicKey and PrivateKey can also be extracted from their BIP32 formats using the
hd module:
>>> from btcpy.structs.hd import ExtendedPrivateKey, ExtendedPublicKey
>>> priv = ExtendedPrivateKey.decode('tprv8kxXxKwakWDtXvKBjjR5oHDFS7Z21HCVLMVUqEFCSVChUZ26BMDDH1JmaGUTEYGMUyQQBSfTgEK76QBvLephodJid5GTEiGFVGJdEBYptd7')
# priv.key holds a `PrivateKey`
>>> priv.key.hexlify()
'a12618ff6540dcd79bf68fda2faf0589b672e18b99a1ebcc32a40a67acdab608'
>>> pub = ExtendedPublicKey.decode('tpubDHea6jyptsuZRPLydP5gCgsN194xAcPPuf6G7kHVrm16K3Grok2oTVvdkNvPM465uuKAShgba7A2hHYeGGuS9B8AQGABfc6hp7mpcLLJUsk')
# pub.key holds a `PublicKey`
>>> pub.key.hexlify()
'025f628d7a11ace2a6379119a778240cb70d6e720750416bb36f824514fbe88260'
PrivateKey can also be extracted from a Wallet Import Format by doing:
>>> privk = PrivateKey.form_wif(wif_key)
All these structures can be converted back to hex by using their hexlify() method.
In the same way, these structures can be serialized and deserialized by using their
serialize() and deserialize() methods. These methods respectively return and
expect a bytearray type.
Keys
The PublicKey class can handle both compressed and uncompressed public
keys. In any case both the compressed and uncompressed version can be extracted.
However, the structure will remember how it was initialised, so the hexlify(),
hash() and to_address() methods will produce different
results depending whether the PublicKey was initialised with a compressed or
uncompressed public key. The to_segwit_address() method will always consider
the key as compressed (P2WPKH addresses are only allowed with compressed keys).
An example of this behaviour follows:
>>> uncomp = PublicKey.unhexlify('04ea4e183e8c751a4cc72abb7088cea79351dbfb7981ceb48f286ccfdade4d42c877d334c1a8b34072400f71b2a900a305ffae8963075fe94ea439b4b57978e9e8')
>>> compr = PublicKey(uncomp.compressed)
>>> uncomp.hexlify()
'04ea4e183e8c751a4cc72abb7088cea79351dbfb7981ceb48f286ccfdade4d42c877d334c1a8b34072400f71b2a900a305ffae8963075fe94ea439b4b57978e9e8'
>>> compr.hexlify()
'02ea4e183e8c751a4cc72abb7088cea79351dbfb7981ceb48f286ccfdade4d42c8'
>>> str(uncomp.to_address())
'mtDD9VFhPaRi6C6McMSnhb7nUZceSh4MnK'
>>> str(uncomp.to_segwit_address())
'tb1qxs0gs9dzukv863jud3wpldtrjh9edeqqqzahcz' # this actually refers to the compressed version!
>>> str(compr.to_address())
'mkGY1QBotzNCrpJaEsje3BpYJsktksi3gJ'
>>> str(compr.to_segwit_address())
'tb1qxs0gs9dzukv863jud3wpldtrjh9edeqqqzahcz'
Please note that by default the to_address() and to_segwit_address()
methods will return an address in the format of the network type
specified in setup (regtest in the case of this example) but a flag
can be passed to it to return an address for another network:
>>> str(uncomp.to_address(mainnet=True))
'1DhFrSAiaYzTK5cjtnUQsfuTca1wXvXfVY'
>>> str(compr.to_address(mainnet=True))
'15kaiM6q5xvx5hpxXJmGDGcDStABoGTzSX'
The PublicKey derived from a PrivateKey can be obtained by doing:
pubk = PrivateKey.unhexlify(privk_hex).pub()
the pub() method will return by default the compressed public key.
The uncompressed version can be obtained by adding the flag compressed=False.
Additionall
Related Skills
node-connect
338.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
338.7kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.6kCommit, push, and open a PR
