pyspool

pyspool is a reference implementation of the Secure Public Online Ownership Ledger SPOOL and part of the development stack of ascribe.io

Installation

pip install pyspool

Contents

Overview

All hashes are in base 58 so that they can be inserted in the bitcoin blockchain.

Refill Wallet

The refill wallet is only used to refill the Federation Wallet with 10000 and 600 satoshi outputs. This way we maintain the Federation Wallet clean which makes it easier to manage.

Federation Wallet

The Federation wallet is the wallet from where all the registers in the blockchain occur. We can check the validity of transactions by looking at the origin of this transactions and see if the originating address belongs to a federation. For instance all pieces registered through ascribe.io will originate from the address 1JttRRdtAi6cDNM23Uq4BEU61R8kJeANJs for the first version of the protocol or 1AScRhqdXMrJyxNmjEapMZi1PLFsqmLquG for protocol version 01.

The Federation wallet should only contain fuel outputs with the amount of 10000 satoshi and token outputs with the amount of 600 satoshi. The fuel is used to pay the fee for the transactions and tokens are used to register address on the blockchain.

Ensuring that the Federation wallet cointains outputs with these amounts makes the transactions simpler since there is no need for change. It is also easier to keep track of the unspents in order to prevent invalid transactions due to double spends (specially important when there is a high throughput of transactions).

Examples

All the addresses and transaction ids output by this examples can be checked in the bitcoin blockchain.

For this example we will be implementing the following actions:

  1. Refill Federation wallet with fuel and tokens
  2. user1 registers master edition
  3. user1 registers number of editions
  4. user1 registers edition number 1
  5. user1 transfers edition number 1 to user2
  6. user2 consigns edition number 1 to user3
  7. user3 unconsigns edition number 1 to user2
  8. user2 loans edition number 1 to user3

This is the information that we will need:

refill_pass
refill wallet password
federation_pass
federation wallet password
user1_pass
user1 password
user2_pass
user2 password
user3_pass
user3 password
refill_root
('', u'mhyCaF2HFk7CVwKmyQ8TahgVdjnHSr1pTv')
federation_root
('', u'mqXz83H4LCxjf2ie8hYNsTRByvtfV43Pa7')
user1_root

root address for user1 HD wallet

('', u'n36EiKuYYXNS9h84CnkLK3sEevZsfksaGN')
user1_leaf

leaf address for user1 HD wallet

('2015/5/28/9/28/44/982190',
 u'mjq4rZZEyJGFfzg59RLFmuNJTo3jakEDrS')
user2_leaf

leaf address for user2 HD wallet

('2015/5/28/9/28/45/184623',
 u'mhRJqLG3BwXXG7YADQD3Ng3c6coZVwoFo6')
user3_leaf

leaf address for user3 HD wallet

('2015/5/28/9/28/45/387493',
 u'mhMXXntQCorduhcygY7gLdmuunVEM59C8q')
file_hash

file_hash, file_hash + metadata

(u'mhXeWEMLnEwVNnyqKrqUDPAYSuwhfyNXA7',
 u'mzQxP43Y4A6PfYeArV2mGBEucJfDtsyCk5')

Federation Wallet Refill

Before we can start with spool transactions we need to have a Federation with all the necessary fuel and tokens.

>>> from spool import Spool

>>> spool = Spool(testnet=True)

>>> # lets refill the federation wallet with necessary fuel*7 and tokens*11 for this example
>>> txid = spool.refill_main_wallet(refill_root, federation_root[1], 7, 11,
                                    refill_pass, min_confirmations=1, sync=True)
>>> print txid
67d22e66ee46a96e94f08bed0c857f23de39aee8b25db5fa0369c495e072e44c

67d22e66ee46a96e94f08bed0c857f23de39aee8b25db5fa0369c495e072e44c

Registrations

user1 registers the master edition

Now that we have enough funds in the Federation wallet user1 can ascribe his master edition. A master edition is a register with edition number 0 that ascribes the piece to user1 making him the original owner/creator of the piece. Master editions are ascribed to the user’s root address of the HD wallet.

>>> # user1 registers the master edition of piece with file_hash
>>> txid = spool.register(federation_root, user1_root[1], file_hash,
                          federation_pass, 0, min_confirmations=1, sync=True)
>>> print txid
f67aa26b5f47e83124040970246c969d04ec9adecc5a97d60754a0f54355ee81

f67aa26b5f47e83124040970246c969d04ec9adecc5a97d60754a0f54355ee81

user1 registers the number of editions

Now that user1 has registered the master edition he can now specify how many editions of the piece will exist. user1 can only do this once and this cannot be changed in the future. This creates digital scarcity for this particular piece

>>> # user1 specifies that there will be 10 editions of the piece with hash file_hash
>>> txid = spool.editions(federation_root, user1_root, file_hash,
                          federation_pass, 10, min_confirmations=1, sync=True)
>>> print txid
f1f2cdf6ef2ee2d8af13f9d45a1fd7700f9f281078c71939f78326a4b6b957dc

f1f2cdf6ef2ee2d8af13f9d45a1fd7700f9f281078c71939f78326a4b6b957dc

user1 registers edition number 1

Once the number of editions is registered user1 can now start registering editions so that he can transfer ownership to other users. Each edition is registered to a different leaf address of user1 HD wallet

>>> # user1 registers edition number 1 of piece with file_hash
>>> txid = spool.register(federation_root, user1_leaf[1], file_hash,
                          federation_pass, 1, min_confirmations=1, sync=True)
>>> print txid
2376a200a326ee7cf87b7fee7ea0f9a80c8b23cc3a0d72732b9a75517e664f23

2376a200a326ee7cf87b7fee7ea0f9a80c8b23cc3a0d72732b9a75517e664f23

Transfers

user1 transfers edition number 1 to user2

Now that an edition is registered the user can transfer ownership to another user. Transfering ownserhip implies a transaction originating from user1 wallet address holding the edition. This means that we need to fuel user1 wallet with the necessary funds before performing a spool transaction

>>> # refill user1 wallet before transfer
>>> txid = spool.refill(federation_root, user1_leaf[1], 1, 1,
                federation_pass, min_confirmations=1, sync=True)
>>> print txid
45bc2a3eecac9b5538a3b5bc325e94fcffee47c0025e78ece426aeebfac59c24

>>> # now we can transfer ownserhip of edition 1 from user1 to user2
>>> txid = spool.transfer(user1_leaf, user2_leaf[1], file_hash,
                          user1_pass, 1, min_confirmations=1, sync=True)
>>> print txid
38509a49b00f3c3c3fadedd2c5ce35ffcc05a9737a36dd1b7ff00ed1ffe5fd80

45bc2a3eecac9b5538a3b5bc325e94fcffee47c0025e78ece426aeebfac59c24 38509a49b00f3c3c3fadedd2c5ce35ffcc05a9737a36dd1b7ff00ed1ffe5fd80

Consignments

user2 consigns edition number 1 to user3

user2 now owns edition 1 of piece with hash file_hash and he can transfer ownership of the piece. Lets consign the piece to user3

>>> # refill user2 wallet before consign
>>> txid = spool.refill(federation_root, user2_leaf[1], 1, 1,
                        federation_pass, min_confirmations=1, sync=True)
>>> print txid
e07732c8af3557277f68871babc874766c511fdf898449cc9be9e505f8325f10

>>> # now we can consign edition 1 from user2 to user3
>>> txid = spool.consign(user2_leaf, user3_leaf[1], file_hash,
                         user2_pass, 1, min_confirmations=1, sync=True)
>>> print txid
3b30cea26d49eb023ccd62fb78ddd9308c9505fe0796abc0fe60989980fc5eb8

e07732c8af3557277f68871babc874766c511fdf898449cc9be9e505f8325f10 3b30cea26d49eb023ccd62fb78ddd9308c9505fe0796abc0fe60989980fc5eb8

Unconsignments

user3 unconsigns edition number 1 to user2

Now lets unconsign the piece with hash file_hash back to user2

>>> # refill user3 wallet before unconsign
>>> txid = spool.refill(federation_root, user3_leaf[1], 1, 1,
                        federation_pass, min_confirmations=1, sync=True)
>>> print txid
f0c9cf0832e7ca14012e7379da35dd2d50bd66df45c2eb089a23b10db4047dcc

>>> # user3 unconsigns edition number 1 back to user2
>>> txid = spool.unconsign(user3_leaf, user2_leaf[1], file_hash,
                           user3_pass, 1, min_confirmations=1, sync=True)
>>> print txid
11dcb46061526790e0e7cf0a83e9163d35b75461cd203858c1fd7bdb2149db0c

f0c9cf0832e7ca14012e7379da35dd2d50bd66df45c2eb089a23b10db4047dcc 11dcb46061526790e0e7cf0a83e9163d35b75461cd203858c1fd7bdb2149db0c

Loans

user2 loans edition number 1 to user3

Now that user2 owns the edition again lets loan it to user3 from 15-05-22 to 15-05-23

>>> # refill user2 wallet before loan
>>> txid = spool.refill(federation_root, user2_leaf[1], 1, 1,
                        federation_pass, min_confirmations=1, sync=True)
>>> print txid
087d85fd421db42c3efac5e6aa499edfe7386101b85314b44c86681a98c27832

>>> # user2 loans edition number 1 to user3
>>> txid = spool.loan(user2_leaf, user3_leaf[1], file_hash,
                      user2_pass, 1, '150522', '150523', min_confirmations=1, sync=True)
>>> print txid
6cc0066ee737a7104859328729cd10f8c5a5b64be3f4f8bfcab04f8a6aca4c56

087d85fd421db42c3efac5e6aa499edfe7386101b85314b44c86681a98c27832 6cc0066ee737a7104859328729cd10f8c5a5b64be3f4f8bfcab04f8a6aca4c56

Library Reference

Spool

class spool.Spool(testnet=False, service=u'blockr', username=u'', password=u'', host=u'', port=u'')

Class that contains all Spool methods.

In the SPOOL implementation there is no notion of users only addresses. All addresses come from BIP32 HD wallets. This makes it easier to manage all the keys since we can retrieve everything we need from a master secret (namely the private key to sign the transactions).

Since we are dealing with HD wallets we expect all from_address to be a tuple of (path, address) so that we can retrieve the private key for that particular leaf address. If we want to use the root address we can just pass an empty string to the first element of the tuple e.g. ('', address). For instance when using the federation wallet address we have no need to create leaf addresses.

A file is represented by two hashes:
  • file_hash: is the hash of the digital file
  • file_hash_metadata: is the hash of the digital file + metadata

The hash is passed to the methods has a tuple: (file_hash, file_hash_metadata)

FEE

int

transaction fee

TOKEN

int

token

SPENTS_QUEUE_MAXSIZE

int

spent outputs queue maximum size

__init__(testnet=False, service=u'blockr', username=u'', password=u'', host=u'', port=u'')
Parameters:
  • testnet (bool) – Whether to use the mainnet or testnet. Defaults to the mainnet (False).
  • service (str) – Bitcoin communication interface: 'blockr', 'daemon', or 'regtest'. 'blockr' refers to the public api, whereas 'daemon' and 'regtest' refer to the jsonrpc inteface. Defaults to 'blockr'.
  • username (str) – username for jsonrpc communications
  • password (str) – password for jsonrpc communications
  • hostname (str) – hostname of the bitcoin node when using jsonrpc
  • port (str) – port number of the bitcoin node when using jsonrpc
consign(*args, **kwargs)

Consign a piece to an address

Parameters:
  • from_address (Tuple[str]) – Address currently owning the edition
  • to_address (str) – Address to where the piece will be consigned to
  • hash (Tuple[str]) – Hash of the piece. Tuple (file_hash, file_hash_metadata)
  • password (str) – Password for the wallet currently owning the edition. For signing the transaction
  • edition_num (int) – the number of the edition to consign
  • min_confirmations (int) – Number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
  • ownership (bool) – Check ownsership in the blockchain before pushing the transaction. Defaults to True
Returns:

transaction id

Return type:

str

consigned_registration(*args, **kwargs)

Register an edition or master edition of a piece consigned to from_address

Parameters:
  • from_address (Tuple[str]) – Federation address. All register transactions originate from the the Federation wallet
  • to_address (str) – Address registering the edition
  • hash (Tuple[str]) – Hash of the piece. Tuple (file_hash, file_hash_metadata)
  • password (str) – Federation wallet password. For signing the transaction
  • min_confirmations (int) – Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
  • ownership (bool) – Check ownsership in the blockchain before pushing the transaction. Defaults to True
Returns:

transaction id

Return type:

str

editions(*args, **kwargs)

Register the number of editions of a piece

Parameters:
  • from_address (Tuple[str]) – Federation address. All register transactions originate from the the Federation wallet
  • to_address (str) – Address registering the number of editions
  • hash (Tuple[str]) – Hash of the piece. Tuple (file_hash, file_hash_metadata)
  • password (str) – Federation wallet password. For signing the transaction
  • num_editions (int) – Number of editions of the piece
  • min_confirmations (int) – Number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
  • ownership (bool) – Check ownsership in the blockchain before pushing the transaction. Defaults to True
Returns:

transaction id

Return type:

str

loan(*args, **kwargs)

Loan the edition

Parameters:
  • from_address (Tuple[str]) – Address currently holding the edition
  • to_address (str) – Address to loan the edition to
  • hash (Tuple[str]) – Hash of the piece. Tuple (file_hash, file_hash_metadata)
  • password (str) – Password for the wallet currently holding the edition. For signing the transaction
  • edition_num (int) – the number of the edition to loan
  • loan_start (str) – Start date for the loan. In the form YYMMDD
  • loan_end (str) – End date for the loan. In the form YYMMDD
  • min_confirmations (int) – Number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
  • ownership (bool) – Check ownsership in the blockchain before pushing the transaction. Defaults to True
Returns:

transaction id

Return type:

str

migrate(*args, **kwargs)

Migrate an edition

Parameters:
  • from_address (Tuple[str]) – Federation address. All register transactions originate from the the Federation wallet
  • to_address (str) – Address registering the edition
  • hash (Tuple[str]) – Hash of the piece. Tuple (file_hash, file_hash_metadata)
  • password (str) – Federation wallet password. For signing the transaction
  • edition_num (int) – The number of the edition to register. User edition_num=0 to register the master edition
  • min_confirmations (int) – Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
  • ownership (bool) – Check ownsership in the blockchain before pushing the transaction. Defaults to True
Returns:

transaction id

Return type:

str

refill(*args, **kwargs)

Refill wallets with the necessary fuel to perform spool transactions

Parameters:
  • from_address (Tuple[str]) – Federation wallet address. Fuels the wallets with tokens and fees. All transactions to wallets holding a particular piece should come from the Federation wallet
  • to_address (str) – Wallet address that needs to perform a spool transaction
  • nfees (int) – Number of fees to transfer. Each fee is 10000 satoshi. Used to pay for the transactions
  • ntokens (int) – Number of tokens to transfer. Each token is 600 satoshi. Used to register hashes in the blockchain
  • password (str) – Password for the Federation wallet. Used to sign the transaction
  • min_confirmations (int) – Number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
Returns:

transaction id

Return type:

str

refill_main_wallet(*args, **kwargs)

Refill the Federation wallet with tokens and fees. This keeps the federation wallet clean. Dealing with exact values simplifies the transactions. No need to calculate change. Easier to keep track of the unspents and prevent double spends that would result in transactions being rejected by the bitcoin network.

Parameters:
  • from_address (Tuple[str]) – Refill wallet address. Refills the federation wallet with tokens and fees
  • to_address (str) – Federation wallet address
  • nfees (int) – Number of fees to transfer. Each fee is 10000 satoshi. Used to pay for the transactions
  • ntokens (int) – Number of tokens to transfer. Each token is 600 satoshi. Used to register hashes in the blockchain
  • password (str) – Password for the Refill wallet. Used to sign the transaction
  • min_confirmations (int) – Number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
Returns:

transaction id

Return type:

str

register(*args, **kwargs)

Register an edition or master edition of a piece

Parameters:
  • from_address (Tuple[str]) – Federation address. All register transactions originate from the the Federation wallet
  • to_address (str) – Address registering the edition
  • hash (Tuple[str]) – Hash of the piece. Tuple (file_hash, file_hash_metadata)
  • password (str) – Federation wallet password. For signing the transaction
  • edition_num (int) – The number of the edition to register. User edition_num=0 to register the master edition
  • min_confirmations (int) – Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
  • ownership (bool) – Check ownsership in the blockchain before pushing the transaction. Defaults to True
Returns:

transaction id

Return type:

str

register_piece(*args, **kwargs)

Register a piece

Parameters:
  • from_address (Tuple[str]) – Federation address. All register transactions originate from the the Federation wallet
  • to_address (str) – Address registering the edition
  • hash (Tuple[str]) – Hash of the piece. (file_hash, file_hash_metadata)
  • password (str) – Federation wallet password. For signing the transaction
  • edition_num (int) – The number of the edition to register. User edition_num=0 to register the master edition
  • min_confirmations (int) – Override the number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
  • ownership (bool) – Check ownsership in the blockchain before pushing the transaction. Defaults to True
Returns:

transaction id

Return type:

str

select_inputs(address, nfees, ntokens, min_confirmations=6)

Selects the inputs for the spool transaction.

Parameters:
  • address (str) – bitcoin address to select inputs for
  • nfees (int) – number of fees
  • ntokens (int) – number of tokens
  • min_confirmations (Optional[int]) – minimum number of required confirmations; defaults to 6
simple_spool_transaction(from_address, to, op_return, min_confirmations=6)

Utililty function to create the spool transactions. Selects the inputs, encodes the op_return and constructs the transaction.

Parameters:
  • from_address (str) – Address originating the transaction
  • to (str) – list of addresses to receive tokens (file_hash, file_hash_metadata, ...)
  • op_return (str) – String representation of the spoolverb, as returned by the properties of Spoolverb
  • min_confirmations (int) – Number of confirmations when chosing the inputs of the transaction. Defaults to 6
Returns:

unsigned transaction

Return type:

str

transfer(*args, **kwargs)

Transfer a piece between addresses

Parameters:
  • from_address (Tuple[str]) – Address currently owning the edition
  • to_address – Address to receive the edition
  • hash (Tuple[str]) – Hash of the piece. Tuple (file_hash, file_hash_metadata)
  • password (str) – Password for the wallet currently owning the edition. For signing the transaction
  • edition_num (int) – the number of the edition to transfer
  • min_confirmations (int) – Number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
  • ownership (bool) – Check ownsership in the blockchain before pushing the transaction. Defaults to True
Returns:

transaction id

Return type:

str

unconsign(*args, **kwargs)

Unconsign the edition

Parameters:
  • from_address (Tuple[str]) – Address where the edition is currently consigned
  • to_address (str) – Address that consigned the piece to from_address
  • hash (Tuple[str]) – Hash of the piece. Tuple (file_hash, file_hash_metadata)
  • password (str) – Password for the wallet currently holding the edition. For signing the transaction
  • edition_num (int) – the number of the edition to unconsign
  • min_confirmations (int) – Number of confirmations when chosing the inputs of the transaction. Defaults to 6
  • sync (bool) – Perform the transaction in synchronous mode, the call to the function will block until there is at least on confirmation on the blockchain. Defaults to False
  • ownership (bool) – Check ownsership in the blockchain before pushing the transaction. Defaults to True
Returns:

transaction id

Return type:

str

File

class spool.File(filename, testnet=False, **kwargs)

Class used to calculate the hash of a file and the hash of the file + metadata to be included on the blockchain

__init__(filename, testnet=False, **kwargs)
Parameters:
  • filename (str) – Name of the file
  • testnet (bool) – testnet flag. Defaults to False
  • **kwargs

    Additional metadata to be encoded with the file. Only the values are used to compute the hash. Values are ordered using their keys, so that the computation of the hash is consistent. As an example, given:

    File('filename', title='piece title', artist='artist')
    

    the values ('artist', 'piece title') would be used in that order for the computation of the hash.

Returns:

File instance

classmethod from_hash(hash)
Parameters:hash (str) – hash of the file
Returns:File instance

Wallet

class spool.Wallet(password, testnet=False)

Represents a BIP32 wallet.

wallet

BIP32Node

BIP32NOde instance.

root_address

Tuple[str]

Root address of the HD Wallet.

__init__(password, testnet=False)

Initializes a BIP32 wallet.

Addresses returned by the wallet are of the form (path, address).

Parameters:
  • password (bytes) – Master secret for the wallet. The password can also be passed as a string (str).
  • testnet (bool) – Wwether to use the bitcoin testnet or mainnet. Defaults to False.
address_from_path(path=None)
Parameters:path (str) – Path for the HD wallet. If path is None it will generate a unique path based on time.
Returns:A tuple with the path and leaf address.

Spoolverb

class spool.Spoolverb(num_editions=None, edition_num=None, loan_start=u'', loan_end=u'', meta=u'ASCRIBESPOOL', version=u'01', action=None)

Allows for easy creation of the verb to be encoded on the op_return of all SPOOL transactions.

supported_actions

List[str]

Actions supported by the SPOOL protocol.

__init__(num_editions=None, edition_num=None, loan_start=u'', loan_end=u'', meta=u'ASCRIBESPOOL', version=u'01', action=None)

Initializer for the Spoolverb class.

Parameters:
  • num_editions (int) – Number of editions to register.
  • edition_num (str) – Number of the edition to use.
  • loan_start (str) – Start of the loan in the format YYMMDD.
  • loan_end (str) – End of the loan in the format YYMMDD.
  • meta (str) – Header for the spool protocol. Defaults to 'ASCRIBESPOOL'.
  • version (str) – Version of the protocol. Defaults to '01'.
  • action (str) – One of the actions in supported_actions.
Returns:

Spoolverb instance.

consign
str: representation of the CONSIGN spoolverb. E.g.:
'ASCRIBESPOOL01CONSIGN1'.
consigned_registration
str: representation of the CONSIGNEDREGISTRATION spoolverb. E.g.:
'ASCRIBESPOOL01CONSIGNEDREGISTRATION'.
editions
str: representation of the EDITIONS spoolverb. E.g.:
'ASCRIBESPOOL01EDITIONS10'.
classmethod from_verb(verb)

Constructs a Spoolverb instance from the string representation of the given verb.

Parameters:verb (str) – representation of the verb e.g.: 'ASCRIBESPOOL01LOAN12/150526150528'. Can also be in binary format (bytes): b'ASCRIBESPOOL01PIECE'.
Returns:Spoolverb instance.
fuel
str: representation of the FUEL spoolverb. E.g.:
'ASCRIBESPOOL01FUEL'.
loan
str: representation of the LOAN spoolverb. E.g.:
'ASCRIBESPOOL01LOAN1/150526150528'.
migrate
str: representation of the MIGRATE spoolverb. E.g.:
'ASCRIBESPOOL01MIGRATE1'.
piece
str: representation of the PIECE spoolverb. E.g.:
'ASCRIBESPOOL01PIECE'.
register
str: representation of the REGISTER spoolverb. E.g.:
'ASCRIBESPOOL01REGISTER1'`.
transfer
str: representation of the TRANSFER spoolverb. E.g.:
'ASCRIBESPOOL01TRANSFER1'.
unconsign
str: representation of the UNCONSIGN spoolverb. E.g.:
'ASCRIBESPOOL01UNCONSIGN1'.

BlockchainSpider

class spool.BlockchainSpider(testnet=False, service=u'blockr', username=u'', password=u'', host=u'', port=u'')

Spool blockchain explorer. Retrieves from the blockchain the chain of ownership of a hash created with the SPOOL protocol.

__init__(testnet=False, service=u'blockr', username=u'', password=u'', host=u'', port=u'')
Parameters:
  • testnet (bool) – Whether to use the mainnet or testnet. Defaults to the mainnet (False).
  • service (str) – Bitcoin communication interface: 'blockr', 'daemon', or 'regtest'. 'blockr' refers to the public api, whereas 'daemon' and 'regtest' refer to the jsonrpc inteface. Defaults to 'blockr'.
  • username (str) – username for jsonrpc communications
  • password (str) – password for jsonrpc communications
  • hostname (str) – hostname of the bitcoin node when using jsonrpc
  • port (str) – port number of the bitcoin node when using jsonrpc
static chain(tree, edition_number)
Parameters:
  • tree (dict) – Tree history of all editions of a piece.
  • edition_number (int) – The edition number to check for. In the case of a piece (master edition), an empty string ('') or zero (0) can be passed.
Returns:

The chain of ownsership of a particular edition of the piece ordered by time.

Return type:

list

static check_script(vouts)

Looks into the vouts list of a transaction and returns the op_return if one exists.

Args;
vouts (list): List of outputs of a transaction.
Returns:String representation of the op_return.
Return type:str
Raises:Exception – If no vout having a supported verb (supported_actions) is found.
static decode_op_return(op_return_hex)

Decodes the given op_return hexadecimal string representation into a string (str).

Parameters:op_return_hex (str) – Hexadecimal string representation of the op_return.
Returns:String representation of the op_return.
Return type:str
history(hash)

Retrieve the ownership tree of all editions of a piece given the hash.

Parameters:hash (str) – Hash of the file to check. Can be created with the File class
Returns:Ownsership tree of all editions of a piece.
Return type:dict

Note

For now we only support searching the blockchain by the piece hash.

static pprint(tree)

Utility function to pretty print the history tree of a piece.

Parameters:tree (dict) – History tree of a piece.
static strip_loan(chain)

Returns the chain without loan. This way we can look at the last transaction to establish ownership.

Parameters:chain (list) – Chain for a particular edition.
Returns:Chain with loan transactions striped from the end of the chain.
Return type:list

Ownership

class spool.Ownership(address, piece_address, edition_number, testnet=False, service=u'blockr', username=u'', password=u'', host=u'', port=u'')

Checks the actions that an address can make on a piece.

address

str

Bitcoin address to check ownership over piece_address.

piece_address

str

Bitcoin address of the piece to check.

edition_number

int

The edition number of the piece.

testnet

bool

Bitcoin network. True for testnet or False for mainnet.

reason

str

Message indicating the reason for the failure of an ownership property.

__init__(address, piece_address, edition_number, testnet=False, service=u'blockr', username=u'', password=u'', host=u'', port=u'')
Parameters:
  • address (str) – Bitcoin address to check ownership over piece_address.
  • piece_address (str) – Bitcoin address of the piece to check.
  • edition_number (int) – The edition number of the piece.
  • testnet (Optional[boo]l) – Whether to use the testnet (True) or the mainnet (False). Defaults to False.
  • service (Optional[str]) – Name of service to use to connect to the bitcoin network. Possible names are ('blockr', 'daemon', 'regtest'). Defaults to 'blockr'.
  • username (Optional[str]) – Username to connect to a bitcoin node via json-rpc based services: ('daemon', 'regtest').
  • password (Optional[str]) – Password to connect to a bitcoin node via json-rpc based services: ('daemon', 'regtest').
  • host (Optional[str]) – Host of the bitcoin node to connect to via json-rpc based services: ('daemon', 'regtest').
  • port (Optional[str]) – Port of the bitcoin node to connect to via json-rpc based services: ('daemon', 'regtest').
can_consign

bool: True if address can consign the edition edition_number of piece_address else False.

can_editions

bool: True if address can register the number of editions of piece_address else False.

In order to register the number of editions:

1. There needs to a least one transaction for the piece_address (the registration of the master edition).

2. A piece with address piece_address needs to be registered with 'ASCRIBESPOOL01PIECE' (master edition).

3. The number of editions should have not been set yet (no tx with verb 'ASCRIBESPOOLEDITIONS').

can_loan

bool: True if address can loan the edition edition_number of piece_address else False.

can_register

bool: True if address can register the edition edition_number of piece_address else False.

In order to register an edition:

  1. The master piece needs to be registered.
  2. The number of editions needs to be registered.
  3. The edition_number should not have been registered yet.

Todo

Also check that the root address owns the piece. Right now we cannot do this because we only receive the leaf address when registering an edition.

can_register_master

bool: True if address can register the master edition of piece_address else False.

To register a master edition the piece address cannot exist in the bitcoin network.

can_transfer

bool: True if address can transfer the edition edition_number of piece_address else False.

can_unconsign

bool: True if address can unconsign the edition edition_number of piece_address else False.

If the last transaction is a consignment of the edition to the user.

Exceptions

class spool.ownership.OwnershipError(message)

To be raised when an address does not have ownership of a hash

message

str

Message of the exception.

class spool.spool.SpoolFundsError(message)

To be raised when an address does not have ownership of a hash.

message

str

Message of the exception.

class spool.spoolex.InvalidTransactionError(message)

To be raised when a malformed/invalid transaction is found.

message

str

Message of the exception.

class spool.spoolverb.SpoolverbError(message)

To be raised when an address does not have ownership of a hash.

message

str

Message of the exception.

Testing

At the moment most tests rely on a bitcoin regtest node running. Depending on how the bitcoin node is run some environment variables may need to be set.

The simplest is to use the provided docker-compose.yml under the pyspool github repository.

First run the bitcoin regtest daemon in background mode:

$ docker-compose up -d bitcoin

Then run the tests:

$ docker-compose run --rm spool py.test -v

To run the tests against python 2:

$ docker-compose run --rm spool-py2 py.test -v

Note

You may need to build the image for the services spool and spool-py2. E.g.:

$ docker-compose build spool

Without Docker

The tests rely on four environment variables specific to bitcoin:

BITCOIN_HOST

The host of the bitcoin regtest node. Defaults to 'localhost'.

BITCOIN_PORT

The port of the bitcoin regtest node. Defaults to 18332.

BITCOIN_RPCUSER

The rpc user used to connect to bitcoin regtest node. Defaults to 'merlin'.

BITCOIN_RPCPASSWORD

The password of the user, used to connect to the bitcoin regtest node. Defaults to 'secret'.

Assuming the above default environment variables, a bitcoin regtest node can be run as follows:

$ bitcoind -daemon -regtest -rpcuser=merlin -rpcpassword=secret -txindex=1

Important

Please note the -txindex=1 option. This ensures that all transactions are indexed and retrievable by the RPC getrawtransaction. Without this option some tests will fail.

Using a bitcoin.conf

A bitcoin.conf file can also be used. E.g.:

# $HOME/.bitcoin/bitcoin.conf (under linux)
rpcuser=merlin
rpcpassword=secret
txindex=1

Tip

The .travis.yml, docker-compose.yml, and bitcoin_regtest.conf files, under the pyspool github repository may be helpful to look at.

Contributing

Pull requests, feedback, and suggestions are welcome. Github repository is at https://github.com/ascribe/pyspool

You can also send inquiries directly to devel@ascribe.io

About this Documentation

This section contains instructions to build and view the documentation locally, using the docker-compose.yml file of the pyspool repository: https://github.com/ascribe/pyspool.

If you do not have a clone of the repo, you need to get one.

Building the documentation

To build the docs, simply run

$ docker-compose up bdocs

Or if you prefer, start a bash session,

$ docker-compose run --rm bdocs bash

and build the docs:

root@a651959a1f2d:/usr/src/app/docs# make html

Viewing the documentation

You can start a little web server to view the docs at http://localhost:40084/

$ docker-compose up -d vdocs

Note

If you are using docker-machine you need to replace localhost with the ip of the machine (e.g.: docker-machine ip tm if your machine is named tm).

Making changes

The necessary source code is mounted, which allows you to make modifications, and view the changes by simply re-building the docs, and refreshing the browser.

Background

pyspool 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.

Indices and tables