diff --git a/ucoinpy/__init__.py b/ucoinpy/__init__.py
index 0eb79a469b6c7977fe9734e20b2a5163b63f9472..354362cd909050638e202e6df9b96384bf8fc7c0 100644
--- a/ucoinpy/__init__.py
+++ b/ucoinpy/__init__.py
@@ -15,3 +15,7 @@
 # Authors:
 # Caner Candan <caner@candan.fr>, http://caner.candan.fr
 #
+
+PROTOCOL_VERSION="1"
+
+MANAGED_API=["BASIC_MERKLED_API"]
diff --git a/ucoinpy/documents/__init__.py b/ucoinpy/documents/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d0057d916ed98558dc74d2c1678b35ffb32895f0 100644
--- a/ucoinpy/documents/__init__.py
+++ b/ucoinpy/documents/__init__.py
@@ -0,0 +1,25 @@
+'''
+Created on 3 déc. 2014
+
+@author: inso
+'''
+import base58
+import time
+from ..key import Base58Encoder
+
+
+class Document:
+    def __init__(self, timestamp):
+        self.timestamp = timestamp
+
+    def ts(self):
+        return "META:TS:{0}".format(self.timestamp)
+
+    def content(self):
+        return ""
+
+    def sign(self, key):
+        return key.sign(self.content(), encoder=Base58Encoder)
+
+    def signed(self, key):
+        return "{0}\n{1}\n".format(self.content(), self.sign(key))
diff --git a/ucoinpy/documents/block.py b/ucoinpy/documents/block.py
index 8b0772d41523b36ce41a5ed456dd414a5af79226..33d1e043f6a247aeab74a169d7921fc88aceddb1 100644
--- a/ucoinpy/documents/block.py
+++ b/ucoinpy/documents/block.py
@@ -4,13 +4,131 @@ Created on 2 déc. 2014
 @author: inso
 '''
 
-class Block(object):
+from .. import PROTOCOL_VERSION
+from . import Document
+
+
+class Block(Document):
     '''
-    classdocs
+Version: VERSION
+Type: Block
+Currency: CURRENCY
+Nonce: NONCE
+Number: BLOCK_NUMBER
+PoWMin: NUMBER_OF_ZEROS
+Time: GENERATED_ON
+MedianTime: MEDIAN_DATE
+UniversalDividend: DIVIDEND_AMOUNT
+Issuer: ISSUER_KEY
+PreviousHash: PREVIOUS_HASH
+PreviousIssuer: PREVIOUS_ISSUER_KEY
+Parameters: PARAMETERS
+MembersCount: WOT_MEM_COUNT
+Identities:
+PUBLIC_KEY:SIGNATURE:TIMESTAMP:USER_ID
+...
+Joiners:
+PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID
+...
+Actives:
+PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID
+...
+Leavers:
+PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID
+...
+Excluded:
+PUBLIC_KEY
+...
+Certifications:
+PUBKEY_FROM:PUBKEY_TO:BLOCK_NUMBER:SIGNATURE
+...
+Transactions:
+COMPACT_TRANSACTION
+...
+BOTTOM_SIGNATURE
     '''
 
-
-    def __init__(self, params):
+    def __init__(self, currency, noonce, number, powmin, time,
+                 mediantime, ud, issuer, prev_hash, prev_issuer,
+                 parameters, members_count, identities, joiners,
+                 actives, leavers, excluded, certifications,
+                 transactions):
         '''
         Constructor
         '''
+        self.currency = currency
+        self.noonce = noonce
+        self.number = number
+        self.powmin = powmin
+        self.time = time
+        self.mediantime = mediantime
+        self.ud = ud
+        self.issuer = issuer
+        self.prev_hash = prev_hash
+        self.prev_issuer = prev_issuer
+        self.parameters = parameters
+        self.members_count = members_count
+        self.identities = identities
+        self.joiners = joiners
+        self.actives = actives
+        self.leavers = leavers
+        self.excluded = excluded
+        self.certifications = certifications
+        self.transactions = transactions
+
+    def content(self):
+        doc = """
+Version: {0}
+Type: Block
+Currency: {1}
+Nonce: {2}
+Number: {3}
+PoWMin: {4}
+Time: {5}
+MedianTime: {6}
+UniversalDividend: {7}
+Issuer: {8}
+PreviousHash: {9}
+PreviousIssuer: {10}
+Parameters: {11}
+MembersCount: {12}
+Identities:""".format(PROTOCOL_VERSION,
+                      self.currency,
+                      self.noonce,
+                      self.number,
+                      self.powmin,
+                      self.time,
+                      self.mediantime,
+                      self.ud,
+                      self.issuer,
+                      self.prev_hash,
+                      self.prev_issuer,
+                      self.parameters,
+                      self.members_count)
+
+        for identity in self.identities:
+            doc += "{0}\n".format(identity.inline())
+
+        doc += "Joiners:\n"
+        for joiner in self.joiners:
+            doc += "{0}\n".format(joiner.inline())
+
+        doc += "Actives:\n"
+        for active in self.actives:
+            doc += "{0}\n".format(active.inline())
+
+        doc += "Leavers:\n"
+        for leaver in self.leavers:
+            doc += "{0]\n".format(leaver.inline())
+
+        doc += "Excluded:\n"
+        for exclude in self.exclude:
+            doc += "{0}\n".format(exclude.inline())
+
+        doc += "Certifications:\n"
+        for cert in self.certifications:
+            doc += "{0}\n".format(cert.inline())
+
+        doc += "Transactions:\n"
+        for transaction in self.transactions:
+            doc += "{0}\n".format(transaction.inline())
diff --git a/ucoinpy/documents/certification.py b/ucoinpy/documents/certification.py
index 54104aad68cecae986f7692925a7c0e652b259ee..c5f0f09c028a7a85624fb661ec2f0daed6ba6c72 100644
--- a/ucoinpy/documents/certification.py
+++ b/ucoinpy/documents/certification.py
@@ -3,14 +3,38 @@ Created on 2 déc. 2014
 
 @author: inso
 '''
+from . import Document
 
-class Certification(object):
+
+class SelfCertification(Document):
     '''
-    classdocs
+    A document discribing a self certification.
     '''
 
+    def __init__(self, identifier):
+        self.identifier = identifier
+
+    def uid(self):
+        return "UID:{0}".format(self.identifier)
 
-    def __init__(self, params):
+    def content(self):
+        return "{0}\n{1}".format(self.uid(), self.timestamp())
+
+
+class Certification(Document):
+    '''
+    classdocs
+    '''
+
+    def __init__(self, selfcert, blockid):
         '''
         Constructor
         '''
+        self.selfcert = selfcert
+        self.blockid = blockid
+
+    def timestamp(self):
+        return "META:TS:{0}".format(self.blockid)
+
+    def content(self):
+        return "{0}\n{1}".format(self.selfcert.content(), self.timestamp())
diff --git a/ucoinpy/documents/membership.py b/ucoinpy/documents/membership.py
index 9133840a78f2819bb02dc5b9901b080b8ba2c8ce..ae174521bd1bafa9efb0679104f2f28c64745c3c 100644
--- a/ucoinpy/documents/membership.py
+++ b/ucoinpy/documents/membership.py
@@ -3,14 +3,55 @@ Created on 2 déc. 2014
 
 @author: inso
 '''
+from .. import PROTOCOL_VERSION
+
 
 class Membership(object):
     '''
-    classdocs
+    This is a utility class to generate membership documents :
+    Version: VERSION
+    Type: Membership
+    Currency: CURRENCY_NAME
+    Issuer: ISSUER
+    Block: NUMBER-HASH
+    Membership: MEMBERSHIP_TYPE
+    UserID: USER_ID
+    CertTS: CERTIFICATION_TS
     '''
 
-
-    def __init__(self, params):
+    def __init__(self, currency, issuer, block_number, block_hash,
+                 membership_type, userid, cert_ts):
         '''
         Constructor
         '''
+        self.currency = currency
+        self.issuer = issuer
+        self.block_number = block_number
+        self.block_hash = block_hash
+        self.membership_type = membership_type
+        self.userid = userid
+        self.cert_ts = cert_ts
+
+    def content(self):
+        return """
+Version: {0}
+Type: Membership
+Currency: {1}
+Issuer: {2}
+Block: {3}-{4}
+Membership: {5}
+UserID: {6}
+CertTS: {7}""".format(PROTOCOL_VERSION,
+                      self.currency,
+                      self.issuer,
+                      self.block_number, self.block_hash,
+                      self.membership_type,
+                      self.userid,
+                      self.cert_ts)
+
+    def inline(self):
+        return "{0}:{1}:{2}:{3}".format(self.issuer,
+                                        self.sign(),
+                                        self.block_number,
+                                        self.block_hash,
+                                        self.cert_ts)
diff --git a/ucoinpy/documents/peer.py b/ucoinpy/documents/peer.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..082c06ef5d2e7655872c8a75167f9051e8256fbb 100644
--- a/ucoinpy/documents/peer.py
+++ b/ucoinpy/documents/peer.py
@@ -0,0 +1,84 @@
+'''
+Created on 2 déc. 2014
+
+@author: inso
+'''
+
+import re
+
+from . import Document
+from .. import PROTOCOL_VERSION, MANAGED_API
+
+
+class Peer(Document):
+    """
+    Version: VERSION
+    Type: Peer
+    Currency: CURRENCY_NAME
+    PublicKey: NODE_PUBLICKEY
+    Block: BLOCK
+    Endpoints:
+    END_POINT_1
+    END_POINT_2
+    END_POINT_3
+    [...]
+    """
+
+    def __init__(self, currency, pubkey, blockid, endpoints):
+        self.currency = currency
+        self.pubkey = pubkey
+        self.blockid = blockid
+        self.endpoints = endpoints
+
+    def content(self):
+        doc = """
+Version: {0}
+Type: Peer
+Currency: {1}
+PublicKey: {2}
+Block: {3}
+Endpoints:
+""".format(PROTOCOL_VERSION, self.currency, self.pubkey, self.blockid)
+
+        for endpoint in self.endpoints:
+            doc += "{0}\n".format(endpoint.inline())
+        return doc
+
+
+class Endpoint():
+    """
+    Describing endpoints
+    """
+
+    @staticmethod
+    def from_inline(inline):
+        for api in MANAGED_API:
+            if (inline.startswith(api)):
+                return Endpoint.parse_line(inline, api)
+
+    @staticmethod
+    def parse_line(self, inline, api):
+        if (api == "BASIC_MERKLED_API"):
+            bma_endpoints = re.compile('^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]+))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$')
+            m = bma_endpoints.match(inline)
+            server = m.group(2)
+            ipv4 = m.group(4)
+            ipv6 = m.group(6)
+            port = int(m.group(8))
+            return BMAEndpoint(server, ipv4, ipv6, port)
+        return None
+
+
+class BMAEndpoint(Endpoint):
+    def __init__(self, server, ipv4, ipv6, port):
+        self.server = server
+        self.ipv4 = ipv4
+        self.ipv6 = ipv6
+        self.port = port
+
+    def inline(self):
+        return "BASIC_MERKLED_API {DNS} {IPv4} {IPv6} {PORT}" \
+                    .format(DNS=self.server,
+                            IPv4=self.ipv4,
+                            IPv6=self.ipv6,
+                            PORT=self.port)
diff --git a/ucoinpy/documents/pubkey.py b/ucoinpy/documents/pubkey.py
deleted file mode 100644
index 4a3a4796299102e7007ebf8eb1d4c42950a6df34..0000000000000000000000000000000000000000
--- a/ucoinpy/documents/pubkey.py
+++ /dev/null
@@ -1,16 +0,0 @@
-'''
-Created on 2 déc. 2014
-
-@author: inso
-'''
-
-class Pubkey(object):
-    '''
-    classdocs
-    '''
-
-
-    def __init__(self, params):
-        '''
-        Constructor
-        '''
diff --git a/ucoinpy/documents/status.py b/ucoinpy/documents/status.py
index adf7bf5afa7b92dd07ceb8b2091b58c554b59c7b..dd11ce1af153ef774793acf3ef9b5676f807a6ae 100644
--- a/ucoinpy/documents/status.py
+++ b/ucoinpy/documents/status.py
@@ -4,13 +4,39 @@ Created on 2 déc. 2014
 @author: inso
 '''
 
-class Status(object):
+from . import Document
+from .. import PROTOCOL_VERSION
+
+
+class Status(Document):
     '''
-    classdocs
+    Version: VERSION
+    Type: Status
+    Currency: CURRENCY_NAME
+    Status: STATUS
+    Block: BLOCK
+    From: SENDER
+    To: RECIPIENT
     '''
 
-
-    def __init__(self, params):
+    def __init__(self, currency, status, blockid, sender, recipient):
         '''
         Constructor
         '''
+        self.currency = currency
+        self.status = status
+        self.blockid = blockid
+        self.sender = sender
+        self.recipient = recipient
+
+    def content(self):
+        return '''
+Version: {0}
+Type: Status
+Currency: {1}
+Status: {2}
+Block: {3}
+From: {4}
+To: {5}
+'''.format(PROTOCOL_VERSION, self.currency, self.status,
+           self.blockid, self.sender, self.recipient)
diff --git a/ucoinpy/documents/transaction.py b/ucoinpy/documents/transaction.py
index f9ae19c41e7dfdc688e610f7985006ae8d4c06a8..10fa5d690c7f3f647792fd95f10ab620ea43cfe9 100644
--- a/ucoinpy/documents/transaction.py
+++ b/ucoinpy/documents/transaction.py
@@ -4,13 +4,148 @@ Created on 2 déc. 2014
 @author: inso
 '''
 
-class Transaction(object):
+from . import Document
+from .. import PROTOCOL_VERSION
+
+
+class Transaction(Document):
     '''
-    classdocs
+Version: VERSION
+Type: Transaction
+Currency: CURRENCY_NAME
+Issuers:
+PUBLIC_KEY
+...
+Inputs:
+INDEX:SOURCE:NUMBER:FINGERPRINT:AMOUNT
+...
+Outputs:
+PUBLIC_KEY:AMOUNT
+...
+Comment: COMMENT
+SIGNATURES
+...
     '''
 
+    def __init__(self, currency, pubkeys, inputs, outputs, comment=None):
+        '''
+        Constructor
+        '''
+        self.currency = currency
+        self.pubkeys = pubkeys
+        self.inputs = inputs
+        self.outputs = outputs
+        self.comment = comment
+
+    def content(self):
+        doc = """
+Version: {0}
+Type: Transaction
+Currency: {1}
+Issuers:""".format(PROTOCOL_VERSION,
+                   self.currency)
+
+        for p in self.pubkeys:
+            doc += "{0}\n".format(p)
+
+        doc += "Inputs:\n"
+        for i in self.inputs:
+            doc += "{0}\n".format(i.inline())
 
-    def __init__(self, params):
+        doc += "Outputs:\n"
+        for o in self.outputs:
+            doc += "{0}\n".format(o.inline())
+
+        doc += """
+COMMENT:
+{0}
+""".format(self.comment)
+
+        return doc
+
+    def compact(self):
+        '''
+        Return a transaction in its compact format.
+        '''
+        """TX:VERSION:NB_ISSUERS:NB_INPUTS:NB_OUTPUTS:HAS_COMMENT
+PUBLIC_KEY:INDEX
+...
+INDEX:SOURCE:FINGERPRINT:AMOUNT
+...
+PUBLIC_KEY:AMOUNT
+...
+COMMENT
+"""
+        doc = "TX:{0}:{1}:{2}:{3}:{4}".format(PROTOCOL_VERSION,
+                                              self.pubkeys.len,
+                                              self.inputs.len,
+                                              self.outputs.len,
+                                              '1' if self.Comment else '0')
+        for pubkey in self.pubkeys:
+            doc += "{0}\n".format(pubkey)
+        for i in self.inputs:
+            doc += "{0}\n".format(i.compact())
+        for o in self.outputs:
+            doc += "{0}\n".format(o.inline())
+        if self.comment:
+            doc += "{0}\n".format(self.comment)
+        return doc
+
+    def sign(self, keys):
+        signatures = ""
+        for k in keys:
+            signatures += "{0}\n".format(super().sign(k))
+        return signatures
+
+
+class SimpleTransaction(Transaction):
+    '''
+As transaction class, but for only one issuer.
+...
+    '''
+    def __init__(self, currency, pubkey, single_input, outputs, comment):
         '''
         Constructor
         '''
+        self.currency = currency
+        self.pubkeys = [pubkey]
+        self.inputs = [single_input]
+        self.outputs = outputs
+        self.comment = comment
+
+
+class InputSource():
+    '''
+    A Transaction INPUT
+    '''
+    def __init__(self, index, source, number, fingerprint, amount):
+        self.index = index
+        self.source = source
+        self.number = number
+        self.fingerprint = fingerprint
+        self.amount = amount
+
+    def inline(self):
+        return "{0}:{1}:{2}:{3}:{4}".format(self.index,
+                                            self.source,
+                                            self.number,
+                                            self.fingerprint,
+                                            self.amount)
+
+    def compact(self):
+        return "{0}:{1}:{2}:{3}".format(self.index,
+                                        self.source,
+                                        self.fingerprint,
+                                        self.amount)
+
+
+class OutputSource():
+    '''
+    A Transaction OUTPUT
+    '''
+    def __init__(self, pubkey, amount):
+        self.pubkey = pubkey
+        self.amount = amount
+
+    def inline(self):
+        return "{0}:{1}".format(self.pubkey, self.amount)
diff --git a/ucoinpy/key/__init__.py b/ucoinpy/key/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a5fec73d28ef80aef5b07e2663c970c0fd80d4
--- /dev/null
+++ b/ucoinpy/key/__init__.py
@@ -0,0 +1,17 @@
+'''
+Ucoin public and private keys
+
+@author: inso
+'''
+
+import base58
+
+
+class Base58Encoder(object):
+    @staticmethod
+    def encode(data):
+        return base58.b58encode(data)
+
+    @staticmethod
+    def decode(data):
+        return base58.b58decode(data)
diff --git a/ucoinpy/key/hdwallet.py b/ucoinpy/key/hdwallet.py
new file mode 100644
index 0000000000000000000000000000000000000000..dffb5fffa8515b8d8433e9af2b965bf16ae3b459
--- /dev/null
+++ b/ucoinpy/key/hdwallet.py
@@ -0,0 +1,378 @@
+'''
+HD Wallet inspired from Bip32 wallets.
+
+@author: inso
+'''
+'''
+import os
+import hmac
+import hashlib
+import ed25519
+import struct
+import base58
+import base64
+
+from hashlib import sha256
+from ecdsa.curves import SECP256k1
+from ecdsa.ecdsa import int_to_string, string_to_int
+from ecdsa.numbertheory import square_root_mod_prime as sqrt_mod
+
+MIN_ENTROPY_LEN = 128        # bits
+HDWALLET_HARDENED    = 0x80000000 # choose from hardened set of child keys
+CURVE_GEN       = ecdsa.ecdsa.generator_secp256k1
+CURVE_ORDER     = CURVE_GEN.order()
+FIELD_ORDER     = SECP256k1.curve.p()
+INFINITY        = ecdsa.ellipticcurve.INFINITY
+
+
+class HDWalletKey(object):
+
+    # Static initializers to create from entropy or external formats
+    #
+    @staticmethod
+    def fromEntropy(entropy, public=False):
+        "Create a HDWallet using supplied entropy >= MIN_ENTROPY_LEN"
+        if entropy == None:
+            entropy = os.urandom(MIN_ENTROPY_LEN/8) # Python doesn't have os.random()
+        if not len(entropy) >= MIN_ENTROPY_LEN/8:
+            raise ValueError("Initial entropy %i must be at least %i bits" %
+                                (len(entropy), MIN_ENTROPY_LEN))
+        I = hmac.new("UCoin seed", entropy, hashlib.sha512).digest()
+
+        Il, Ir = I[:32], I[32:]
+        # FIXME test Il for 0 or less than SECP256k1 prime field order
+        key = HDWalletKey(secret=Il, chain=Ir, depth=0, index=0, fpr='\0\0\0\0', public=False)
+        if public:
+            key.SetPublic()
+        return key
+
+    @staticmethod
+    def fromExtendedKey(xkey, public=False):
+        """
+        Create a HDWallet by importing from extended private or public key string
+
+        If public is True, return a public-only key regardless of input type.
+        """
+        # Sanity checks
+        raw = base58.b58decode_check(xkey)
+        # To fix
+        #if len(raw) != 78:
+        #    raise ValueError("extended key format wrong length")
+
+        # Verify address version/type
+        #version = raw[:4]
+        #if version == EX_MAIN_PRIVATE:
+        #    raise ValueError("unknown extended key version")
+
+        # Extract remaining fields
+        depth = ord(raw[4])
+        fpr = raw[5:9]
+        child = struct.unpack(">L", raw[9:13])[0]
+        chain = raw[13:45]
+        secret = raw[45:78]
+
+        # Extract private key or public key point
+        if keytype == 'xprv':
+            secret = secret[1:]
+        else:
+            # Recover public curve point from compressed key
+            lsb = ord(secret[0]) & 1
+            x = string_to_int(secret[1:])
+            ys = (x**3+7) % FIELD_ORDER # y^2 = x^3 + 7 mod p
+            y = sqrt_mod(ys, FIELD_ORDER)
+            if y & 1 != lsb:
+                y = FIELD_ORDER-y
+            point = ecdsa.ellipticcurve.Point(SECP256k1.curve, x, y)
+            secret = ecdsa.VerifyingKey.from_public_point(point, curve=SECP256k1)
+
+        is_pubkey = (keytype == 'xpub')
+        key = HDWalletKey(secret=secret, chain=chain, depth=depth, index=child,
+                          fpr=fpr, public=is_pubkey)
+        if not is_pubkey and public:
+            key = key.SetPublic()
+        return key
+
+
+    # Normal class initializer
+    def __init__(self, secret, chain, depth, index, fpr, public=False):
+        """
+        Create a public or private BIP32Key using key material and chain code.
+
+        secret   This is the source material to generate the keypair, either a
+                 32-byte string representation of a private key, or the ECDSA
+                 library object representing a public key.
+
+        chain    This is a 32-byte string representation of the chain code
+
+        depth    Child depth; parent increments its own by one when assigning this
+
+        index    Child index
+
+        fpr      Parent fingerprint
+
+        public   If true, this keypair will only contain a public key and can only create
+                 a public key chain.
+        """
+
+        self.public = public
+        if public is False:
+            self.k = ed25519.SigningKey(base58.b58decode(secret))
+            self.K = self.k.get_verifying_key()
+        else:
+            self.k = None
+            self.K = secret
+
+        self.C = chain
+        self.depth = depth
+        self.index = index
+        self.parent_fpr = fpr
+
+    # Internal methods not intended to be called externally
+    def _hmac(self, data):
+        """
+        Calculate the HMAC-SHA512 of input data using the chain code as key.
+
+        Returns a tuple of the left and right halves of the HMAC
+        """
+        I = hmac.new(self.C, data, hashlib.sha512).digest()
+        return (I[:32], I[32:])
+
+    def _CKDpriv(self, i):
+        """
+        Create a child key of index 'i'.
+
+        If the most significant bit of 'i' is set, then select from the
+        hardened key set, otherwise, select a regular child key.
+
+        Returns a BIP32Key constructed with the child key parameters,
+        or None if i index would result in an invalid key.
+        """
+        # Index as bytes, BE
+        i_str = struct.pack(">L", i)
+
+        # Data to HMAC
+        if i & HDWALLET_HARDENED:
+            data = b'\0' + self.k.to_string() + i_str
+        else:
+            data = self.PublicKey() + i_str
+        # Get HMAC of data
+        (Il, Ir) = self._hmac(data)
+
+        # Construct new key material from Il and current private key
+        Il_int = string_to_int(Il)
+        if Il_int > CURVE_ORDER:
+            return None
+        pvt_int = string_to_int(self.k.to_string())
+        k_int = (Il_int + pvt_int) % CURVE_ORDER
+        if (k_int == 0):
+            return None
+        secret = (b'\0'*32 + int_to_string(k_int))[-32:]
+
+        # Construct and return a new BIP32Key
+        return HDWalletKey(secret=secret, chain=Ir, depth=self.depth+1,
+                           index=i, fpr=self.Fingerprint(), public=False)
+
+    def _CKDpub(self, i):
+        """
+        Create a publicly derived child key of index 'i'.
+
+        If the most significant bit of 'i' is set, this is
+        an error.
+
+        Returns a HDWalletKey constructed with the child key parameters,
+        or None if index would result in invalid key.
+        """
+
+        if i & HDWALLET_HARDENED:
+            raise Exception("Cannot create a hardened child key using public child derivation")
+
+        # Data to HMAC.  Same as CKDpriv() for public child key.
+        data = self.PublicKey() + struct.pack(">L", i)
+
+        # Get HMAC of data
+        (Il, Ir) = self.hmac(data)
+
+        # Construct curve point Il*G+K
+        Il_int = string_to_int(Il)
+        if Il_int >= CURVE_ORDER:
+            return None
+        point = Il_int*CURVE_GEN + self.K.pubkey.point
+        if point == INFINITY:
+            return None
+
+        # Retrieve public key based on curve point
+        K_i = ed25519.VerifyingKey.from_public_point(point, curve=SECP256k1)
+
+        # Construct and return a new BIP32Key
+        return HDWalletKey(secret=K_i, chain=Ir, depth=self.depth, index=i, fpr=self.Fingerprint(), public=True)
+
+
+    # Public methods
+    #
+    def ChildKey(self, i):
+        """
+        Create and return a child key of this one at index 'i'.
+
+        The index 'i' should be summed with BIP32_HARDEN to indicate
+        to use the private derivation algorithm.
+        """
+        if self.public is False:
+            return self.CKDpriv(i)
+        else:
+            return self.CKDpub(i)
+
+
+    def SetPublic(self):
+        "Convert a private BIP32Key into a public one"
+        self.k = None
+        self.public = True
+
+
+    def PrivateKey(self):
+        "Return private key as string"
+        if self.public:
+            raise Exception("Publicly derived deterministic keys have no private half")
+        else:
+            return self.k.to_string()
+
+
+    def PublicKey(self):
+        "Return compressed public key encoding"
+        if self.K.pubkey.point.y() & 1:
+            ck = b'\3'+int_to_string(self.K.pubkey.point.x())
+        else:
+            ck = b'\2'+int_to_string(self.K.pubkey.point.x())
+        return ck
+
+
+    def ChainCode(self):
+        "Return chain code as string"
+        return self.C
+
+
+    def Identifier(self):
+        "Return key identifier as string"
+        cK = self.PublicKey()
+        return hashlib.new('ripemd160', sha256(cK).digest()).digest()
+
+
+    def Fingerprint(self):
+        "Return key fingerprint as string"
+        return self.Identifier()[:4]
+
+
+    def Address(self):
+        "Return compressed public key address"
+        vh160 = '\x00'+self.Identifier()
+        return Base58.check_encode(vh160)
+
+
+    def WalletImportFormat(self):
+        "Returns private key encoded for wallet import"
+        if self.public:
+            raise Exception("Publicly derived deterministic keys have no private half")
+        raw = '\x80' + self.k.to_string() + '\x01' # Always compressed
+        return Base58.check_encode(raw)
+
+
+    def ExtendedKey(self, private=True, encoded=True):
+        "Return extended private or public key as string, optionally Base58 encoded"
+        if self.public is True and private is True:
+            raise Exception("Cannot export an extended private key from a public-only deterministic key")
+        version = EX_MAIN_PRIVATE if private else EX_MAIN_PUBLIC
+        depth = chr(self.depth)
+        fpr = self.parent_fpr
+        child = struct.pack('>L', self.index)
+        chain = self.C
+        if self.public is True or private is False:
+            data = self.PublicKey()
+        else:
+            data = '\x00' + self.PrivateKey()
+        raw = version+depth+fpr+child+chain+data
+        if not encoded:
+            return raw
+        else:
+            return Base58.check_encode(raw)
+
+    # Debugging methods
+    #
+    def dump(self):
+        "Dump key fields mimicking the BIP0032 test vector format"
+        print "   * Identifier"
+        print "     * (hex):      ", self.Identifier().encode('hex')
+        print "     * (fpr):      ", self.Fingerprint().encode('hex')
+        print "     * (main addr):", self.Address()
+        if self.public is False:
+            print "   * Secret key"
+            print "     * (hex):      ", self.PrivateKey().encode('hex')
+            print "     * (wif):      ", self.WalletImportFormat()
+        print "   * Public key"
+        print "     * (hex):      ", self.PublicKey().encode('hex')
+        print "   * Chain code"
+        print "     * (hex):      ", self.C.encode('hex')
+        print "   * Serialized"
+        print "     * (pub hex):  ", self.ExtendedKey(private=False, encoded=False).encode('hex')
+        print "     * (prv hex):  ", self.ExtendedKey(private=True, encoded=False).encode('hex')
+        print "     * (pub b58):  ", self.ExtendedKey(private=False, encoded=True)
+        print "     * (prv b58):  ", self.ExtendedKey(private=True, encoded=True)
+
+
+if __name__ == "__main__":
+    import sys
+
+    # BIP0032 Test vector 1
+    entropy='000102030405060708090A0B0C0D0E0F'.decode('hex')
+    m = BIP32Key.fromEntropy(entropy)
+    print "Test vector 1:"
+    print "Master (hex):", entropy.encode('hex')
+    print "* [Chain m]"
+    m.dump()
+
+    print "* [Chain m/0h]"
+    m = m.ChildKey(0+BIP32_HARDEN)
+    m.dump()
+
+    print "* [Chain m/0h/1]"
+    m = m.ChildKey(1)
+    m.dump()
+
+    print "* [Chain m/0h/1/2h]"
+    m = m.ChildKey(2+BIP32_HARDEN)
+    m.dump()
+
+    print "* [Chain m/0h/1/2h/2]"
+    m = m.ChildKey(2)
+    m.dump()
+
+    print "* [Chain m/0h/1/2h/2/1000000000]"
+    m = m.ChildKey(1000000000)
+    m.dump()
+
+    # BIP0032 Test vector 2
+    entropy = 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542'.decode('hex')
+    m = BIP32Key.fromEntropy(entropy)
+    print "Test vector 2:"
+    print "Master (hex):", entropy.encode('hex')
+    print "* [Chain m]"
+    m.dump()
+
+    print "* [Chain m/0]"
+    m = m.ChildKey(0)
+    m.dump()
+
+    print "* [Chain m/0/2147483647h]"
+    m = m.ChildKey(2147483647+BIP32_HARDEN)
+    m.dump()
+
+    print "* [Chain m/0/2147483647h/1]"
+    m = m.ChildKey(1)
+    m.dump()
+
+    print "* [Chain m/0/2147483647h/1/2147483646h]"
+    m = m.ChildKey(2147483646+BIP32_HARDEN)
+    m.dump()
+
+    print "* [Chain m/0/2147483647h/1/2147483646h/2]"
+    m = m.ChildKey(2)
+    m.dump()
+'''
\ No newline at end of file