Getting Started¶
In this section you’ll find a tutorial to learn more about Protocoin.
Installation¶
To install Protocoin, use pip (recommended method) or easy_install:
pip install protocoin
Architecture¶
Protocoin uses a simple architecture of classes representing the data to be serialized and also classes representing the types of the fields to be serialized.
Protocoin is organized in four main submodules:
Each module structure is described in the next sections.
Protocoin Fields¶
The protocoin.fields
module contains all field types suported by the
serializers. All field classes inherit from the base protocoin.fields.Field
class,
so if you want to create a new field type, you should inherit from this class too. There
are some composite field types to help in common uses like the protocoin.fields.VariableStringField
for instance, representing a string with variable length.
There are a lot of different fields you can use to extend the protocol, examples are:
protocoin.fields.Int32LEField
(a 32-bit integer little-endian),
protocoin.fields.UInt32LEField
(a 32-bit unsigned int little-endian),
protocoin.fields.Int64LEField
(a 64-bit integer little-endian),
protocoin.fields.UInt64LEField
(a 64-bit unsigned integer little-endiang), etc. For
more information about the fields avaiable please see the module documentation.
Example of code for the unsigned 32-bit integer field:
class UInt32LEField(Field):
datatype = "<I"
def parse(self, value):
self.value = value
def deserialize(self, stream):
data_size = struct.calcsize(self.datatype)
data = stream.read(data_size)
return struct.unpack(self.datatype, data)[0]
def serialize(self):
data = struct.pack(self.datatype, self.value)
return data
Protocoin Serializers¶
Serializers are classes that describe the field types (in the correct order) that
will be used to serializer or deserialize the message or a part of a message, for
instance, see this example of a protocoin.serializers.IPv4Address
object
class and then its serializer class implementation:
class IPv4Address(object):
def __init__(self):
self.services = fields.SERVICES["NODE_NETWORK"]
self.ip_address = "0.0.0.0"
self.port = 8333
class IPv4AddressSerializer(Serializer):
model_class = IPv4Address
services = fields.UInt64LEField()
ip_address = fields.IPv4AddressField()
port = fields.UInt16BEField()
To serialize a message, you simple do:
address = IPv4Address()
serializer = IPv4AddressSerializer()
binary_data = serializer.serialize(address)
and to deserialize:
address = serializer.deserialize(binary_data)
Warning
It is important to subclass the protocoin.serializers.Serializer
class in order for the serializer to work, Serializers uses Python
metaclasses magic to deserialize the fields using the correct types
and also the correct order.
Note that we have a special attribute on the serializer that is defining the model_class for the serializer, this class is used to instantiate the correct object class in the deserialization of the data.
There are some useful fields you can use to nest another serializer or
a list of serializers inside a serializer, see in this example of the
implementation of the Version (protocoin.serializers.Version
) command:
class VersionSerializer(Serializer):
model_class = Version
version = fields.Int32LEField()
services = fields.UInt64LEField()
timestamp = fields.Int64LEField()
addr_recv = fields.NestedField(IPv4AddressSerializer)
addr_from = fields.NestedField(IPv4AddressSerializer)
nonce = fields.UInt64LEField()
user_agent = fields.VariableStringField()
Note that the fields addr_recv and addr_from are using the special
field called protocoin.fields.NestedField
.
Note
There are other special fields like the protocoin.fields.ListField
,
that will create a vector of objects using the correct Bitcoin format to serialize
vectors of data.
Network Clients¶
Protocoin also have useful classes to implement a network client for the Bitcoin P2P network.
A basic network client¶
The most basic class available to implement a client is the
protocoin.clients.BitcoinBasicClient
, which is a simple client
of the Bitcoin network that accepts a socket in the constructor and then
will handle and route the messages received to the correct methods of the class,
see this example of a basic client:
import socket
from protocoin.clients import BitcoinBasicClient
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("bitcoin.sipa.be", 8333))
client = BitcoinBasicClient(sock)
client.loop()
Note that this client is very basic, in the example above, the client
will connect into the node bitcoin.sipa.be (a seed node) in the port 8333
and then will wait for messages. The protocoin.clients.BitcoinBasicClient
class doesn’t implement the handshake of the protocol and also doesn’t
answer the pings of the nodes, so you may be disconnected from the node and
it is your reponsability to implement the handshake and the Pong response message
to the Ping message. To implement answer according to the received messages
from the network node, you can implement methods with the name handle_[name of the
command], to implement the handling method to show a message every time
that a Version message arrives, you can do like in the example below:
class MyBitcoinClient(BitcoinBasicClient):
def handle_version(self, message_header, message):
print "A version was received !"
If you want to answer the version command message with a VerAck message, you
just need to create the message, the serializer and then call the
protocoin.clients.BitcoinBasicClient.send_message()
method of the
Bitcoin class, like in the example below:
class MyBitcoinClient(BitcoinBasicClient):
def handle_version(self, message_header, message):
verack = VerAck()
verack_serial = VerAckSerializer()
self.send_message(verack, verack_serial)
Since these problems are very common, there are another class which implements
a node that will stay up in the Bitcoin network. To use this class, just
subclass the protocoin.clients.BitcoinClient
class, for more information
read the next section.
A more complete client implementation¶
The protocoin.clients.BitcoinClient
class implements the minimum
required protocol rules to a client stay online on the Bitcoin network. This
class will answer to Ping message commands with Pong messages and also have
a handshake method that will send the Version command and answer the Version
with the VerAck command message too. See an example of the use:
import socket
from protocoin.clients import BitcoinClient
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("bitcoin.sipa.be", 8333))
client = BitcoinClient(sock)
client.handshake()
client.loop()
In the example above, the handshake is done before entering the message loop.
Bitcoin Keys – Creating, exporting/importing and conversions¶
The protocoin.keys
module contains classes to represent and
handle Bitcoin Private Keys as well Public Keys. The two main classes
in this module are protocoin.keys.BitcoinPublicKey
and
protocoin.keys.BitcoinPrivateKey
. These classes contain
methods to generate new key pairs (Private and Public), to convert
the keys into Bitcoin addresses or Bitcoin WIF (Wallet Import Format)
and to import keys from different formats.
Creating Private Keys and Public Keys¶
In order to create a new Private Key, you only need to instantiate the
protocoin.keys.BitcoinPrivateKey
class without any parameter:
from protocoin import keys
priv_key = keys.BitcoinPrivateKey()
print priv_key
The example above, will create a new Private Key called priv_key and will output the string representation of the Private Key in hex:
<BitcoinPrivateKey hexkey=[E005459416BE7FDC13FA24BA2F2C0DE289F47495D6E94CF2DFBC9FB941CBB565]>
You can now use this generated Private Key to create your Public Key like in the example below:
from protocoin import keys
priv_key = keys.BitcoinPrivateKey()
pub_key = priv_key.generate_public_key()
print pub_key
This example will output:
<BitcoinPublicKey address=[19eQMjBSeeo8fhCRPEVCfnauhsCFVGgV6H]>
Which is the Bitcoin address for the Public Key. You can also convert
the Public Key to hext format using the method
protocoin.keys.BitcoinPublicKey.to_hex()
.
Importing and Exporting Keys¶
You can also export a Private Key into the WIF (Wallet Import Format, used by wallets to import Private Keys):
from protocoin import keys
priv_key = keys.BitcoinPrivateKey()
print priv_key.to_wif()
In this case, the output will be:
5KWwtPkCodUs9WfbrSjzjLqnfbohABUAuLs3NpdxLqi4U6MjuKC
Which is the Private Key in the WIF format. You can also create a new Private Key or a new Public Key using the hex representation in the construction:
from protocoin import keys
hex_key = "E005459416BE7FDC13FA24BA2F2C0DE289F47495D6E94CF2DFBC9FB941CBB565"
priv_key = keys.BitcoinPrivateKey(hex_key)
If you have only the WIF format and you need to use it to create a new
Private Key, you can use the protocoin.keys.BitcoinPrivateKey.from_wif()
method to import it and then create a new Private Key object like in
the example below:
priv_key_wif = "5KWwtPkCodUs9WfbrSjzjLqnfbohABUAuLs3NpdxLqi4U6MjuKC"
priv_key = BitcoinPrivateKey.from_wif(priv_key_wif)