transactions¶
transactions is a small python library to easily create and push transactions to the bitcoin network.
Installation¶
$ pip install transactions
Contents¶
Examples¶
Let’s assume the following cast of characters:
- Alice with the bitcoin address
'mhyCaF2HFk7CVwKmyQ8TahgVdjnHSr1pTv'
- Bob with the bitcoin address
'mqXz83H4LCxjf2ie8hYNsTRByvtfV43Pa7'
- Carol with the bitcoin address
'mtWg6ccLiZWw2Et7E5UqmHsYgrAi5wqiov'
Also consider that one bitcoin is made up of satoshi, such that hundred million satoshi is one bitcoin.
Note
With transactions
all amounts are in satoshi and we currently only
support BIP32 wallets (hierarchical deterministic wallets, aka
“HD Wallets”).
Alice sends 10000 satoshi to Bob¶
from transactions import Transactions
transactions = Transactions(testnet=True)
tx = transactions.simple_transaction(
'mhyCaF2HFk7CVwKmyQ8TahgVdjnHSr1pTv',
('mqXz83H4LCxjf2ie8hYNsTRByvtfV43Pa7', 10000),
)
tx_signed = transactions.sign_transaction(tx, 'master secret')
txid = transactions.push(tx_signed)
print txid
Bob sends 600 satoshi to Carol with a custom op_return¶
from transactions import Transactions
transactions = Transactions(testnet=True)
tx = transactions.simple_transaction(
'mqXz83H4LCxjf2ie8hYNsTRByvtfV43Pa7',
('mtWg6ccLiZWw2Et7E5UqmHsYgrAi5wqiov', 600),
op_return='HELLOFROMASCRIBE',
)
tx_signed = transactions.sign_transaction(tx, 'master secret')
txid = transactions.push(tx_signed)
print txid
Check it out fbbd6407b8fc73169918b2fce7f07aff6a486a241c253f0f8eeb942937fbb970
Get transactions of Alice¶
from transactions import Transactions
transactions = Transactions(testnet=True)
transactions.get('mhyCaF2HFk7CVwKmyQ8TahgVdjnHSr1pTv')
{'transactions': [{'amount': -20000,
'confirmations': 5,
'time': 1431333905,
'txid': u'7f4902599ac9e5c9db347228b489c25fe5095f812c979dd84cc4e88f6812db9e'},
{'amount': -40000,
'confirmations': 11,
'time': 1431329129,
'txid': u'382639448115e859b0dc4092892bc0921edc8851a2b7adbd7b5ab39ccefb73ee'},
...
'unspents': [{'amount': 809760000,
'confirmations': 5,
'txid': u'7f4902599ac9e5c9db347228b489c25fe5095f812c979dd84cc4e88f6812db9e',
'vout': 1}]}
Get details of a transaction between Alice and Bob¶
from transactions import Transactions
transactions = Transactions(testnet=True)
transactions.get('382639448115e859b0dc4092892bc0921edc8851a2b7adbd7b5ab39ccefb73ee')
{u'block': 395966,
u'confirmations': 11,
u'days_destroyed': u'0.00',
u'extras': None,
u'fee': u'0.00010000',
u'is_coinbased': 0,
u'is_unconfirmed': False,
u'time_utc': u'2015-05-11T09:25:29Z',
u'trade': {u'vins': [{u'address': u'mhyCaF2HFk7CVwKmyQ8TahgVdjnHSr1pTv',
u'amount': -0.0004,
u'is_nonstandard': False,
u'n': 3,
u'type': 0,
u'vout_tx': u'dece4f3d0de255bb53c20e89271d1236929d72e426e6e7860d97564c6b9e26ab'}],
u'vouts': [{u'address': u'mqXz83H4LCxjf2ie8hYNsTRByvtfV43Pa7',
u'amount': 0.0001,
u'is_nonstandard': False,
u'is_spent': 0,
u'n': 0,
u'type': 1},
...
u'type': 1}]}
Code¶
transactions
module¶
-
class
transactions.
Transactions
(service=u'blockr', testnet=False, username=u'', password=u'', host=u'', port=u'')¶ Transactions: Bitcoin for Humans
All amounts are in satoshi
-
__init__
(service=u'blockr', testnet=False, username=u'', password=u'', host=u'', port=u'')¶ Parameters: - service (str) – currently supports _blockr_ for blockr.io and and _daemon_ for bitcoin daemon. Defaults to _blockr_
- testnet (bool) – use True if you want to use tesnet. Defaults to False
- username (str) – username to connect to the bitcoin daemon
- password (str) – password to connect to the bitcoin daemon
- hosti (str) – host of the bitcoin daemon
- port (str) – port of the bitcoin daemon
-
build_transaction
(inputs, outputs)¶ Thin wrapper around
bitcoin.mktx(inputs, outputs)
Parameters: Returns: transaction
-
get
(hash, account=u'*', max_transactions=100, min_confirmations=6, raw=False)¶ Parameters: - hash – can be a bitcoin address or a transaction id. If it’s a
bitcoin address it will return a list of transactions up to
max_transactions
a list of unspents with confirmed transactions greater or equal tomin_confirmantions
- account (Optional[str]) – used when using the bitcoind. bitcoind does not provide an easy way to retrieve transactions for a single address. By using account we can retrieve transactions for addresses in a specific account
Returns: transaction
- hash – can be a bitcoin address or a transaction id. If it’s a
bitcoin address it will return a list of transactions up to
-
push
(tx)¶ Parameters: tx – hex of signed transaction Returns: pushed transaction
-
sign_transaction
(tx, master_password, path=u'')¶ Parameters: - tx – hex transaction to sign
- master_password – master password for BIP32 wallets. Can be either a master_secret or a wif
- path (Optional[str]) – optional path to the leaf address of the BIP32 wallet. This allows us to retrieve private key for the leaf address if one was used to construct the transaction.
Returns: signed transaction
Note
Only BIP32 hierarchical deterministic wallets are currently supported.
-
simple_transaction
(from_address, to, op_return=None, min_confirmations=6)¶ Parameters: - from_address (str) – bitcoin address originating the transaction
- to – tuple of
(to_address, amount)
or list of tuples[(to_addr1, amount1), (to_addr2, amount2)]
. Amounts are in satoshi - op_return (str) – ability to set custom
op_return
- min_confirmations (int) – minimal number of required confirmations
Returns: transaction
-
service
module¶
Defines the main BitcoinService
class which other services should subclass.
Theory¶
The intent here is to provide some kind of fundamental knowledge with respect to bitcoin.
As a starting point the material here is currently heavily inspired by the draft version of the book Bitcoin and Cryptocurrency Technologies by
- Arvind Narayanan, Princeton University
- Joseph Bonneau, Princeton University
- Edward Felten, Princeton University
- Andrew Miller, University of Maryland
- Steven Goldfeder, Princeton University
- Jeremy Clark, Concordia University
The long term intention is to extend the material as much as it makes sense meanwhile weaving a connection to the engineering side of bitcoin.
Computer Science of Bitcoin¶
The goal of this section is to dwell on the fundamentals of bitcoin from the point of view of data structures and algorithms.
Some of the key concepts are:
- secure hash functions
- hash pointers and pointer-based acyclic data structures
- digital signatures
- cryptocurrencies
Cryptographic Hash Functions¶
Very briefly, a basic hash function has three main characteristics:
- input value is a string of any size
- output value is of fixed size (i.e.: 256 bits)
- for a string of n bits, the hash function has a running time of O(n)
This is more or less good enough to implement a hash table.
In order to make the basic hash function cryptographically secure, three additional characteristics are required:
- collision‐resistance
- hiding
- puzzle‐friendliness
A hash collition means that for two different input strings the hash function returns the same hash.
Hash functions have collisions since the number of possible inputs is infinite whereas the number of possible outputs is finite.
- collision‐resistance
- A hash function is collision-resistant if it is computationally hard to find its collisions.
- hiding
- Reverse engineering a hash function is computationally hard. That is, given the output of a hash function, the input string cannot be found.
- puzzle‐friendliness
- Very roughly this means that one can pick a puzzle id, k, and bind it to a target result y, such that it is difficult to find a value x, which when fed to the hash function in combination with k, will yield y. By difficult, is meant that there are no better approaches than random trials, and that finding x requires substantial time, more than 2^n for if y has n bits.
Hash function in use in Bitcoin¶
Several cryptocurrencies like Bitcoin use a hash function named SHA-256 for verifying transactions and calculating proof-of-work or proof-of-stake. [1]
For a more in-depth study of the SHA-256 hash function one may consult Descriptions of SHA-256, SHA-384, and SHA-512 by NIST.
Hash Pointer -based Data Structures¶
A hash pointer points to a location where data is stored along with the hash of that data at a given point in time.
Using a hash pointer one can retrieve the data, and verify that the data hasn’t changed.
Using hash pointers, one can build various pointer-based acyclic data structures such as linked lists, trees, and more generally directed acyclic graphs.
The bitcoin blockchain can be viewed as a linked list of binary trees, relying on hash pointers. The hash pointer -based linked list is more precisely called a hash chain, whereas the hash pointer -based binary tree is called a hash tree, or Merkle tree, named after its inventor Ralph Merkle.
Transactions are assembled into a hash tree to form a “block.” Those blocks are then linked to form a hash chain (block chain).
Note
Binary hash trees make it relatively efficient to show the chain of transactions a transaction is linked to within a tree. For a tree with n transactions, only about log(n) transactions are necessary.
Digital Signatures¶
A digital signature requires three steps:
- private / public key pair generation
- signature
- verification
Expressed in code:
private_key, public_key = generate_key_pair(key_size, passphrase=None)
signature = sign(private_key, message)
is_valid = verify(public_key, message, signature)
There are two important requirements, one somewhat obvious, and the other more complex.
- Valid signatures must verify. That is:
verify(public_key, message, sign(private_key, message)) is True
- Reverse engineering the digital signature scheme, aka forging signatures is computationally impossible. That is, for any given message for which the the signature, and public key are known, it is not possible to find the private key, or to figure out how to create new valid signatures for different messages.
ECDSA: digital signature used in Bitcoin¶
For its digital signatures Bitcoin uses the Elliptic Curve Digital Signature Algorithm (ECDSA) [3] with a specific curve that is fine-tuned via the domain parameters known as secp256k1.
Sizes of keys, message, and signature when using ECDSA [2]
- Private key: 256 bits
- Public key, uncompressed: 512 bits
- Public key, compressed: 257 bits
- Message to be signed: 256 bits
- Signature: 512 bits
Public Keys as Identities & Bitcoin Addresses¶
Using a digital signature scheme, public keys can be used as identities. In Bitcoin, public keys are used to identify the sender and receiver in a transaction. Bitcoin refers to these public keys as “addresses”. The sender can sign the transaction with their private key, meanwhile the receiver can verify the signature of the transaction using the public key of the sender.
Two Simple Cryptocurrency Models¶
To make it easier to understand how bitcoin works, Narayanan et al. [2] present two simplified cryptocurrency models, which they call “GoofyCoin”, and “ScroogeCoin”. The first model (GoofyCoin) is somewhat naive, especially with respect to double-spending attacks, and is therefore insecure. The second model (ScroogeCoin) resolves the double-spending attack problem, but depends on the honesty of Scrooge, and is therefore centralized. This section briefly reviews these two models, which are somewhat useful to build upon to understand how bitcoin works.
Note
Double-spending attacks
“Double-spending is the result of successfully spending some money more than once. Bitcoin protects against double spending by verifying each transaction added to the block chain to ensure that the inputs for the transaction had not previously already been spent.” [4]
GoofyCoin: a ledger-less cryptocurrency¶
The GoofyCoin cryptocurrency model is based on two main principles:
- One authority (Goofy) can create coins at will, and assign these newly created coins to themself.
- The owner of a coin can transfer their coin to whomever they wish.
Coin Creation¶
The creation of a goofycoin
works like so:
coin_id = generate_unique_coin_id()
coin_creation_msg = 'create_coin [coin_id]'
coin_creation_signature = sign(goofy_private_key, coin_creation_msg)
The coin_creation_msg
and coin_creation_signature
, taken together, form
a coin. For this example, let’s say that a coin is a tuple:
goofycoin = (coin_creation_msg, coin_creation_signature)
In a more explicit manner:
goofycoin = (
'create_coin [coin_id]',
sign(goofy_private_key, 'create_coin [coin_id]'),
)
Using the public key of Goofy, anyone can verify whether a goofycoin
is
valid:
is_valid = verify(goofy_public_key, goofy_coin[0], goofy_coin[1])
or more explicitly:
is_valid = verify(
goofy_public_key,
'create_coin [coin_id]',
coin_creation_signature,
)
Lastly, in order to be able to refrence the coin, in future transactions, we can hash the information of the coin, such that referencing the coin will be done via the hash. So let’s assume the following dictionary, for the coin creation transaction:
transaction = {
coin_hash: 'a9f268dbfda',
coin : (
'create_coin [coin_id]',
sign(goofy_private_key, 'create_coin [coin_id]'),
)
}
Coin Transfer¶
To transfer the above coin (a9f268dbfda
) to Alice, Goofy would create the
following transaction:
transaction = {
coin_hash: 'b3a364d1a1z',
coin : (
'pay_to alice_pubkey: a9f268dbfda',
sign(goofy_private_key, 'pay_to alice_pubkey: a9f268dbfda'),
)
}
If Alice wanted to transfer her new coin (b3a364d1a1z
) to Bob, she would
then create the following transaction:
transaction = {
coin_hash: '86b9dd63864',
coin : (
'pay_to bob_pubkey: b3a364d1a1z',
sign(goofy_private_key, 'pay_to bob_pubkey: b3a364d1a1z'),
)
}
Double-spending¶
The GoofyCoin model does not prevent Alice from transferring the same coin to multiple recipients. Hence, in addition to the previous transfer she made to Bob, Alice could transfer the same coin to Carol:
transaction = {
coin_hash: 'a1z2g5pw34',
coin : (
'pay_to carol_pubkey: b3a364d1a1z',
sign(goofy_private_key, 'pay_to carol_pubkey: b3a364d1a1z'),
)
}
The two transactions (86b9dd63864
, a1z2g5pw34
) are conflicting, because
two people can’t own the same coin at the same time.
Next section will show how double-spending attacks can be prevented via a centralized ledger, which keeps track of past transactions.
ScroogeCoin – a ledger-based cryptocurrency¶
The ScroogeCoin model relies on an append-only public ledger in which transactions are permanently recorded.
The ledger is maintained by a trusted authority, Scrooge, who can also issue new coins.
A rough sketch of the data structure of the ledger is as follwos;
{'prev': 0,
'tx_id': 0,
'tx': {...}},
{'prev': hash_function(tx_0),
'tx_id': 1,
'tx': {...}},
{'prev': hash_function(tx_1),
'tx_id': 2,
'tx': {...}},
...
The chain of transactions cannot be tempered with because of the use of hash pointers. For example, if the content of transaction 1 was changed, the pointer in transaction 2 would no longer point to transaction 1, and the chain would be broken.
The final hash pointer of the chain is signed by the trusted authority, Scrooge, who then publishes the chain. In this model, a transaction that is not in the signed chain is ignored. Ii is the responsibility of the trusted authority to verify that a transaction is not a double spend.
The ScroogeCoin model supports two types of transactions;
create_coins
- Creates new coins, and is valid if signed by the trusted authority, Scrooge.
pay_coins
- Consumes coins, and produces new coins of the same value, that may belong to new people. The transaction must be signed by each owner of the consumed coins. Moreover, each input coin must not have been already spent.
A new transaction must be validated by the trusted authority, and once validated will be signed and added to the chain of transactions, at which point, and only then, the new transaction will be considered to have occurred.
This model works reasonably well, except for the dependence on a trusted authority. In brief:
- The very existence of the chain relies on one central power.
- The central power can create unlimited amount of coins for itself.
- The central power can deny service to whomever it wishes by simply ignoring transactions.
- The central power can require users of the system to pay fees in order for their transactions to be considered.
The above problems seem sufficient to motivate efforts to decentralize the ScroogeCoin model. This brings the next topic of study: how can such a system be efficiently decentralized?
Practice¶
Running a bitcoin node in regtest mode¶
bitcoind -regtest
Bitcoin clients: bitcoin-cli & json rpc¶
json rpc ref: https://en.bitcoin.it/wiki/API_reference_%28JSON-RPC%29
- via curl
- via python with python-bitcoinrpc
- via python with requests
- via transactions
curl¶
$ curl --user user --data-binary \
'{"jsonrpc": "1.0", "id":"dummy", "method": "getinfo", "params": [] }' \
-H 'content-type: text/plain;' http://127.0.0.1:18332/
bitcoin json rpc revisited with docker¶
We can repeat the same as in the previous section, but this time with some parts, and everything dockerized.
We add the twist that:
- the bitcoin server is running in a container, meanwhile client calls are made from the docker host
- the bitcoin server and client are running in separate containers
For 1. and 2. we connect to the bitcoin node:
- via curl
- via python with python-bitcoinrpc
- via python with requests
- via transactions
host - container¶
Running bitcoind in container and making rpc calls to it from the host machine, (sender_ip)
given the following bitcoin.conf
:
dnsseed=0
rpcuser=a
rpcallowip=<sender_ip>
docker run --rm --name btc -v ~/.bitcoin-docker:/root/.bitcoin -p <sender_ip>:58332:18332 btc5 bitcoind -regtest -printtoconsole
curl --user a:b --data-binary '{"jsonrpc": "1.0", "id":"", "method": "getinfo", "params": [] }' -H 'content-type: text/plain;' http://<sender_ip>:58332
container-container¶
We can use docker-compose.
In one shell:
$ docker-compose run --rm bitcoin
In another shell:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
94787e1325a3 sbellem/bitcoin "bitcoind -regtest -p" 5 seconds ago Up 5 seconds 8332-8333/tcp, 18332-18333/tcp transactions_bitcoin_run_1
Using the CONTAINER ID
or NAME
:
$ docker exec -it transactions_bitcoin_run_1 bash
# bitcoin-cli -regtest getinfo
root@94787e1325a3:/# curl --user a:b --data-binary \
'{"jsonrpc": "1.0", "id":"", "method": "getinfo", "params": [] }' \
-H 'content-type: text/plain;' http://localhost:18332 \
| python -m json.tool
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 386 100 323 100 63 57483 11211 --:--:-- --:--:-- --:--:-- 64600
{
"error": null,
"id": "",
"result": {
"balance": 0.0,
"blocks": 0,
"connections": 0,
"difficulty": 4.656542373906925e-10,
"errors": "",
"keypoololdest": 1459269071,
"keypoolsize": 101,
"paytxfee": 0.0,
"protocolversion": 70012,
"proxy": "",
"relayfee": 1e-05,
"testnet": false,
"timeoffset": 0,
"version": 120000,
"walletversion": 60000
}
}
Under the Hood¶
The intent of this section is to document what goes on under the hood of
transactions
.
We’ll use three main “pillars” to organize and present the information:
One additional section will be used to present some key aspects of the
libraries that transactions
rely on, especially the two bitcoin libraries:
pycoin
and pybitcointools
.
Bitcoin Network¶
There are multiple ways that one may connect to the bitcoin network. For the sake of simplicity, let’s say that they are two main ways:
- via a daemon
- that is communicating directly with a node located at a specific host and port
- via a blockchain explorer
- that is communicating with the bitcoin network via the public API of the blockchain explorer service such as blockr.io
Different Modes of the Bitcoin Network¶
The bitcoin daemon and other bitcoin core programs can be run in three different “network modes”:
- mainnet
- The original and main network for Bitcoin transactions, where satoshis have real economic value. [1]
- testnet
- A global testing environment in which developers can obtain and spend satoshis that have no real-world value on a network that is very similar to the Bitcoin. [2]
- regtest
- A local testing environment in which developers can almost instantly generate blocks on demand for testing events, and can create private satoshis with no real-world value. [3]
Running a bitcoin node in regtest mode¶
bitcoin json rpc¶
ref: https://en.bitcoin.it/wiki/API_reference_%28JSON-RPC%29
- via curl
- via python with python-bitcoinrpc
- via python with requests
- via transactions
curl¶
$ curl --user user --data-binary \
'{"jsonrpc": "1.0", "id":"dummy", "method": "getinfo", "params": [] }' \
-H 'content-type: text/plain;' http://127.0.0.1:18332/
docker¶
host - container¶
Runnign bitcoind in container and making rpc calls to it from the host machine, (sender_ip)
given the following bitcoin.conf
:
dnsseed=0
rpcuser=a
rpcallowip=<sender_ip>
docker run --rm --name btc -v ~/.bitcoin-docker:/root/.bitcoin -p <sender_ip>:58332:18332 btc5 bitcoind -regtest -printtoconsole
curl --user a:b --data-binary '{"jsonrpc": "1.0", "id":"", "method": "getinfo", "params": [] }' -H 'content-type: text/plain;' http://<sender_ip>:58332
container-container¶
Making rpc calls from a container to the bitcoind running in another container.
Connecting to the Bitcoin Network with transactions
¶
When using transactions
, one can interact with the bitcoin network
via a daemon or via a blockchain explorer. When connecting via a daemon it is
possible to connect to the three networks: mainnet, testnet, or regtest,
whereas when connecting via a blockchain explorer one may connect to the
mainnet or testnet.
The supported blockchain explorer is blockr.io
Todo
show code examples
Bitcoin Addresses¶
Todo
Show how a bitcoin address is created.
Bitcoin Transactions¶
Todo
Show the different steps required to publish a transaction in the bitcoin network.
Lifecycle of a transaction: creation, signing, publishing, confirmation
- Using
create
to fetch a transaction - Using
sign
to fetch a transaction - Using
push
to publish a transaction - Using
get
to fetch a transaction
Elements of the payload of a transaction
Blocks¶
Transactions are assembled into blocks.
credits:
Example:
$ curl https://blockexplorer.com/api/block/0000000000000000e067a478024addfecdc93628978aa52d91fabd4292982a50 | python -m json.tool
Or in python:
import json
import requests
BLOCKEXPLORER_API_URL = 'https://blockexplorer.com/api'
BLOCKHASH = '0000000000000000e067a478024addfecdc93628978aa52d91fabd4292982a50q'
url = '{}/block/{}'.format(BLOCKEXPLORER_API_URL, blockhash)
response = requests.get(url)
block = json.loads(response.content)
block
{u'bits': u'19015f53',
u'chainwork': u'000000000000000000000000000000000000000000001a6eca45b2459ce9eed8',
u'confirmations': 120187,
u'difficulty': 3129573174.5222874,
u'hash': u'0000000000000000e067a478024addfecdc93628978aa52d91fabd4292982a50',
u'height': 286819,
u'isMainChain': True,
u'merkleroot': u'871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a',
u'nextblockhash': u'0000000000000000b0f08ec6a3d1e84994498ecf993a9981f57982cfdb66c443',
u'nonce': 856192328,
u'poolInfo': {u'poolName': u'ghash.io', u'url': u'https://ghash.io/'},
u'previousblockhash': u'000000000000000117c80378b8da0e33559b5997f2ad55e2f7d18ec1975b9717',
u'reward': 25,
u'size': 152509,
u'time': 1392872245,
u'tx': [u'00baf6626abc2df808da36a518c69f09b0d2ed0a79421ccfde4f559d2e42128b',
u'91c5e9f288437262f218c60f986e8bc10fb35ab3b9f6de477ff0eb554da89dea',
u'46685c94b82b84fa05b6a0f36de6ff46475520113d5cb8c6fb060e043a0dbc5c',
u'ba7ed2544c78ad793ef5bb0ebe0b1c62e8eb9404691165ffcb08662d1733d7a8',
u'b8dc1b7b7ed847c3595e7b02dbd7372aa221756b718c5f2943c75654faf48589',
...]
u'version': 2}
'merkleroot': u'871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a',
The merkle root corresponds to the cummulative hashing of the transactions hashes.
That is, each transaction is hashed. Each hash is a leaf of a binary tree.
A binary tree is built by pairing leaves, concatenating the pair, and computing the hash of the concatenated pair. The same process is repeated for the parent, recursively all the way to the root, resulting in the merkle root.
At each level of the tree, if the number of hashes is odd, then the last hash is included twice.
Progammatically, this means:
def merkleroot(hashes);
if len(hashes) == 1:
return hashes[0]
if len(hashes) % 2 == 1;
hashes.append(hashes[-1])
parent_hashes = []
for i in range(0, len(hashes), 2);
h = sec_hash_algo(hashes[i] + hashes[i+1])
parent_hashes.append(h)
return merkle_root(parent_hashes)
Libraries used by transactions
¶
Todo
Present libraries used; requests
, pycoin
, pybitcointools
Dive into the details of how pycoin and pybitcointools are used and work under the hood.
Contributing¶
Pull requests, feedback, and suggestions are welcome. The github repository is at https://github.com/ascribe/transactions
You can also send inquiries directly to devel@ascribe.io
Background¶
This was developed by ascribe GmbH as part of the overall ascribe technology stack. http://www.ascribe.io
License¶
Licensed under the Apache License, Version 2.0.
\ Sort by:\ best rated\ newest\ oldest\
\\
Add a comment\ (markup):
\``code``
, \ code blocks:::
and an indented block after blank line