Exemple #1
0
    def get_priv_keychain(self, hex_privkey, chaincode):
        if hex_privkey:
            return PrivateKeychain.from_private_key(hex_privkey,
                                                    chain_path=chaincode)

        log.debug('No privatekey given, starting new wallet')
        return PrivateKeychain()
Exemple #2
0
    def __init__(self, hex_privkey=None, enable_cache=False):
        """
            If @hex_privkey is given, use that to derive keychain
            otherwise, use a new random seed
        """

        if hex_privkey:
            self.priv_keychain = PrivateKeychain.from_private_key(hex_privkey)
        else:
            #log.debug("No privatekey given, starting new wallet")
            self.priv_keychain = PrivateKeychain()

        self.master_address = self.get_master_address()
        self.child_addresses = None

        if enable_cache:
            cache = self.get_cache()

            if cache is not None:

                if cache['master_address'] == self.master_address:
                    self.child_addresses = cache['child_addresses']
                else:
                    log.debug("Wallet already exists with master address: %s" %
                              cache['master_address'])
            else:
                #log.debug("Creating cache of HD wallet addresses ...")
                self.create_addresses_cache()
Exemple #3
0
 def setUp(self):
     self.private_keychains = {
         "root":
         "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
         "0H":
         "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
         "0H/1":
         "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
         "0H/1/2H":
         "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
         "0H/1/2H/2":
         "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
         "0H/1/2H/2/1000000000":
         "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"
     }
     self.public_keychains = {
         "root":
         "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
         "0H":
         "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw",
         "0H/1":
         "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ",
         "0H/1/2H":
         "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
         "0H/1/2H/2":
         "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV",
         "0H/1/2H/2/1000000000":
         "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"
     }
     self.root_private_keychain = PrivateKeychain(
         self.private_keychains["root"])
    def __init__(self, hex_privkey=None, enable_cache=False):

        """
            If @hex_privkey is given, use that to derive keychain
            otherwise, use a new random seed
        """

        if hex_privkey:
            self.priv_keychain = PrivateKeychain.from_private_key(hex_privkey)
        else:
            #log.debug("No privatekey given, starting new wallet")
            self.priv_keychain = PrivateKeychain()

        self.master_address = self.get_master_address()
        self.child_addresses = None

        if enable_cache:
            cache = self.get_cache()

            if cache is not None:

                if cache['master_address'] == self.master_address:
                    self.child_addresses = cache['child_addresses']
                else:
                    log.debug("Wallet already exists with master address: %s" % cache['master_address'])
            else:
                #log.debug("Creating cache of HD wallet addresses ...")
                self.create_addresses_cache()
Exemple #5
0
    def __init__(self, hex_privkey=None):

        """
            If @hex_privkey is given, use that to derive keychain
            otherwise, use a new random seed
        """

        if hex_privkey:
            self.priv_keychain = PrivateKeychain.from_private_key(hex_privkey)
        else:
            self.priv_keychain = PrivateKeychain()
Exemple #6
0
    def __init__(self, hex_privkey=None):
        """
            If @hex_privkey is given, use that to derive keychain
            otherwise, use a new random seed
        """

        if hex_privkey:
            self.priv_keychain = PrivateKeychain.from_private_key(hex_privkey)
        else:
            log.debug("No privatekey given, starting new wallet")
            self.priv_keychain = PrivateKeychain()

        self.master_address = self.get_master_address()
        self.child_addresses = None
Exemple #7
0
 def test_derivation_from_raw_keys(self):
     public_keychain = PublicKeychain.from_public_key(self.public_key_hex)
     private_keychain = PrivateKeychain.from_private_key(
         self.private_key_hex)
     public_keychain_2 = private_keychain.public_keychain()
     self.assertEqual(str(public_keychain), str(public_keychain_2))
     self.assertEqual(str(public_keychain), self.reference_public_keychain)
Exemple #8
0
    def __init__(self, hex_privkey=None, config_path=CONFIG_PATH):

        """
            If @hex_privkey is given, use that to derive keychain
            otherwise, use a new random seed
        """

        if hex_privkey:
            self.priv_keychain = PrivateKeychain.from_private_key(hex_privkey)
        else:
            log.debug("No privatekey given, starting new wallet")
            self.priv_keychain = PrivateKeychain()

        self.master_address = self.get_master_address()
        self.child_addresses = None
        self.config_path = config_path
class BasicKeychainTest(unittest.TestCase):
    def setUp(self):
        self.private_keychains = {
            "root": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
            "0H": "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
            "0H/1": "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
            "0H/1/2H": "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
            "0H/1/2H/2": "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
            "0H/1/2H/2/1000000000": "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"
        }
        self.public_keychains = {
            "root": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
            "0H": "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw",
            "0H/1": "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ",
            "0H/1/2H": "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
            "0H/1/2H/2": "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV",
            "0H/1/2H/2/1000000000": "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"
        }
        self.root_private_keychain = PrivateKeychain(self.private_keychains["root"])

    def tearDown(self):
        pass

    def test_root_private_to_public(self):
        public_keychain = self.root_private_keychain.public_keychain()
        self.assertEqual(str(public_keychain), str(self.public_keychains["root"]))

    def test_hardened_child_0H(self):
        private_keychain = self.root_private_keychain.hardened_child(0)
        self.assertEqual(str(private_keychain), str(self.private_keychains["0H"]))
        self.assertEqual(str(private_keychain.public_keychain()), str(self.public_keychains["0H"]))

    def test_unhardened_child_0H_1(self):
        private_keychain = self.root_private_keychain.hardened_child(0).child(1)
        self.assertEqual(str(private_keychain), str(self.private_keychains["0H/1"]))
        public_keychain = private_keychain.public_keychain()
        self.assertEqual(str(public_keychain), str(self.public_keychains["0H/1"]))
        public_keychain_2 = self.root_private_keychain.hardened_child(0).public_keychain().child(1)
        self.assertEqual(str(public_keychain), str(public_keychain_2))

    def test_5_step_derivation(self):
        private_keychain = self.root_private_keychain.hardened_child(0).child(1).hardened_child(2).child(2).child(1000000000)
        self.assertEqual(str(private_keychain), str(self.private_keychains["0H/1/2H/2/1000000000"]))
        public_keychain = private_keychain.public_keychain()
        self.assertEqual(str(public_keychain), str(self.public_keychains["0H/1/2H/2/1000000000"]))

    def test_private_key(self):
        root_private_key = self.root_private_keychain.private_key()
        self.assertTrue(len(root_private_key) == 66)

    def test_address(self):
        address = self.root_private_keychain.public_keychain().address()
        self.assertTrue(address[0] == '1')
    def test_high_volume_derivation(self):
        number_of_keys = 10
        public_keychain = PublicKeychain.from_public_key(self.public_key_hex)
        private_keychain = PrivateKeychain.from_private_key(self.private_key_hex)
        keypairs = []
        print ""
        for i in range(number_of_keys):
            print "making key %i of %i" % (i+1, number_of_keys)
            public_key = public_keychain.child(i).public_key()
            private_key = private_keychain.child(i).private_key()
            keypairs.append({ 'public': public_key, 'private': private_key })

        for i in range(len(keypairs)):
            keypair = keypairs[i]
            print "checking key %i of %i" % (i+1, number_of_keys)
            self.assertEqual(privkey_to_pubkey(keypair['private']), keypair['public'])
    def test_high_volume_derivation(self):
        number_of_keys = 10
        public_keychain = PublicKeychain.from_public_key(self.public_key_hex)
        private_keychain = PrivateKeychain.from_private_key(self.private_key_hex)
        keypairs = []
        print ""
        for i in range(number_of_keys):
            print "making key %i of %i" % (i+1, number_of_keys)
            public_key = public_keychain.child(i).public_key()
            private_key = private_keychain.child(i).private_key()
            keypairs.append({ 'public': public_key, 'private': private_key })

        for i in range(len(keypairs)):
            keypair = keypairs[i]
            print "checking key %i of %i" % (i+1, number_of_keys)
            # self.assertEqual(privkey_to_pubkey(keypair['private']), keypair['public'])
            self.assertEqual(keylib.ECPrivateKey(keypair['private']).public_key().to_hex(), keylib.ECPublicKey(keypair['public']).to_hex())
Exemple #12
0
class HDWallet(object):

    """
        Initialize a hierarchical deterministic wallet with
        hex_privkey and get child addresses and private keys
    """

    def __init__(self, hex_privkey=None, config_path=CONFIG_PATH):

        """
            If @hex_privkey is given, use that to derive keychain
            otherwise, use a new random seed
        """

        if hex_privkey:
            self.priv_keychain = PrivateKeychain.from_private_key(hex_privkey)
        else:
            log.debug("No privatekey given, starting new wallet")
            self.priv_keychain = PrivateKeychain()

        self.master_address = self.get_master_address()
        self.child_addresses = None
        self.config_path = config_path


    def get_master_privkey(self):

        return self.priv_keychain.private_key()


    def get_child_privkey(self, index=0):
        """
            @index is the child index

            Returns:
            child privkey for given @index
        """

        child = self.priv_keychain.hardened_child(index)
        return child.private_key()


    def get_master_address(self):

        hex_privkey = self.get_master_privkey()
        return get_address_from_privkey(hex_privkey)


    def get_child_address(self, index=0):
        """
            @index is the child index

            Returns:
            child address for given @index
        """

        if self.child_addresses is not None:
            return self.child_addresses[index]

        hex_privkey = self.get_child_privkey(index)
        return get_address_from_privkey(hex_privkey)


    def get_child_keypairs(self, count=1, offset=0, include_privkey=False):
        """
            Returns (privkey, address) keypairs

            Returns:
            returns child keypairs

            @include_privkey: toggles between option to return
                             privkeys along with addresses or not
        """

        keypairs = []

        for index in range(offset, offset+count):
            address = self.get_child_address(index)

            if include_privkey:
                hex_privkey = self.get_child_privkey(index)
                keypairs.append((address, hex_privkey))
            else:
                keypairs.append(address)

        return keypairs


    def get_next_keypair(self, count=1, config_path=None):
        """ Get next payment address that is ready to use

            Returns (payment_address, hex_privkey)
        """

        if config_path is None:
            config_path = self.config_path

        addresses = self.get_child_keypairs(count=count)
        index = 0

        for payment_address in addresses:

            # find an address that can be used for payment
            if not is_address_usable(payment_address, config_path=config_path):
                log.debug("Pending tx on address: %s" % payment_address)

            balance = get_balance( payment_address, config_path=config_path )
            if balance < MINIMUM_BALANCE: 
                log.debug("Underfunded address: %s" % payment_address)

            else:
                return payment_address, self.get_child_privkey(index)

            index += 1

        log.debug("No valid address available.")

        return None, None


    def get_privkey_from_address(self, target_address, count=1):
        """ Given a child address, return priv key of that address
        """

        addresses = self.get_child_keypairs(count=count)

        index = 0

        for address in addresses:

            if address == target_address:

                return self.get_child_privkey(index)

            index += 1

        return None
 def test_derivation_from_raw_keys(self):
     public_keychain = PublicKeychain.from_public_key(self.public_key_hex)
     private_keychain = PrivateKeychain.from_private_key(self.private_key_hex)
     public_keychain_2 = private_keychain.public_keychain()
     self.assertEqual(str(public_keychain), str(public_keychain_2))
     self.assertEqual(str(public_keychain), self.reference_public_keychain)
Exemple #14
0
class HDWallet(object):
    """
        Initialize a hierarchical deterministic wallet with
        hex_privkey and get child addresses and private keys
    """
    def __init__(self, hex_privkey=None):
        """
            If @hex_privkey is given, use that to derive keychain
            otherwise, use a new random seed
        """

        if hex_privkey:
            self.priv_keychain = PrivateKeychain.from_private_key(hex_privkey)
        else:
            log.debug("No privatekey given, starting new wallet")
            self.priv_keychain = PrivateKeychain()

        self.master_address = self.get_master_address()
        self.child_addresses = None

    def get_master_privkey(self):

        return self.priv_keychain.private_key()

    def get_child_privkey(self, index=0):
        """
            @index is the child index

            Returns:
            child privkey for given @index
        """

        child = self.priv_keychain.hardened_child(index)
        return child.private_key()

    def get_master_address(self):

        hex_privkey = self.get_master_privkey()
        return get_address_from_privkey(hex_privkey)

    def get_child_address(self, index=0):
        """
            @index is the child index

            Returns:
            child address for given @index
        """

        if self.child_addresses is not None:
            return self.child_addresses[index]

        hex_privkey = self.get_child_privkey(index)
        return get_address_from_privkey(hex_privkey)

    def get_child_keypairs(self, count=1, offset=0, include_privkey=False):
        """
            Returns (privkey, address) keypairs

            Returns:
            returns child keypairs

            @include_privkey: toggles between option to return
                             privkeys along with addresses or not
        """

        keypairs = []

        for index in range(offset, offset + count):
            address = self.get_child_address(index)

            if include_privkey:
                hex_privkey = self.get_child_privkey(index)
                keypairs.append((address, hex_privkey))
            else:
                keypairs.append(address)

        return keypairs

    def get_privkey_from_address(self, target_address, count=1):
        """ Given a child address, return priv key of that address
        """

        addresses = self.get_child_keypairs(count=count)

        index = 0

        for address in addresses:

            if address == target_address:

                return self.get_child_privkey(index)

            index += 1

        return None
Exemple #15
0
class HDWallet(object):
    """
        Initialize a hierarchical deterministic wallet with
        hex_privkey and get child addresses and private keys
    """
    def __init__(self, hex_privkey=None, enable_cache=False):
        """
            If @hex_privkey is given, use that to derive keychain
            otherwise, use a new random seed
        """

        if hex_privkey:
            self.priv_keychain = PrivateKeychain.from_private_key(hex_privkey)
        else:
            #log.debug("No privatekey given, starting new wallet")
            self.priv_keychain = PrivateKeychain()

        self.master_address = self.get_master_address()
        self.child_addresses = None

        if enable_cache:
            cache = self.get_cache()

            if cache is not None:

                if cache['master_address'] == self.master_address:
                    self.child_addresses = cache['child_addresses']
                else:
                    log.debug("Wallet already exists with master address: %s" %
                              cache['master_address'])
            else:
                #log.debug("Creating cache of HD wallet addresses ...")
                self.create_addresses_cache()

    def create_addresses_cache(self, count=DEFAULT_CHILD_ADDRESSES):

        if self.get_cache() is not None:
            return True

        child_addresses = []

        for index in range(0, count):
            hex_privkey = self.get_child_privkey(index)
            address = self.get_child_address(index)

            child_addresses.append(address)

        if not os.path.exists(LOCAL_DIR):
            os.makedirs(LOCAL_DIR)

        with open(CACHE_FILE_FULLPATH, 'w') as cache_file:
            data = {'child_addresses': child_addresses}
            data['master_address'] = self.get_master_address()
            cache_file.write(json.dumps(data))

        return True

    def get_cache(self):

        if not os.path.isfile(CACHE_FILE_FULLPATH):
            return None

        try:
            with open(CACHE_FILE_FULLPATH, 'r') as cache_file:
                data = json.loads(cache_file.read())

            return data
        except:
            return None

    def get_master_privkey(self):

        return self.priv_keychain.private_key()

    def get_child_privkey(self, index=0):
        """
            @index is the child index

            Returns:
            child privkey for given @index
        """

        child = self.priv_keychain.hardened_child(index)
        return child.private_key()

    def get_master_address(self):

        hex_privkey = self.get_master_privkey()
        return get_address_from_privkey(hex_privkey)

    def get_child_address(self, index=0):
        """
            @index is the child index

            Returns:
            child address for given @index
        """

        if self.child_addresses is not None:
            return self.child_addresses[index]

        hex_privkey = self.get_child_privkey(index)
        return get_address_from_privkey(hex_privkey)

    def get_child_keypairs(self, count=1, offset=0, include_privkey=False):
        """
            Returns (privkey, address) keypairs

            Returns:
            returns child keypairs

            @include_privkey: toggles between option to return
                             privkeys along with addresses or not
        """

        keypairs = []

        for index in range(offset, offset + count):
            address = self.get_child_address(index)

            if include_privkey:
                hex_privkey = self.get_child_privkey(index)
                keypairs.append((address, hex_privkey))
            else:
                keypairs.append(address)

        return keypairs

    def get_next_keypair(self, count=DEFAULT_CHILD_ADDRESSES):
        """ Get next payment address that is ready to use

            Returns (payment_address, hex_privkey)
        """

        addresses = self.get_child_keypairs(count=count)
        index = 0

        for payment_address in addresses:

            # find an address that can be used for payment

            if dontuseAddress(payment_address):
                log.debug("Pending tx on address: %s" % payment_address)

            elif underfundedAddress(payment_address):
                log.debug("Underfunded address: %s" % payment_address)

            else:
                return payment_address, self.get_child_privkey(index)

            index += 1

        log.debug("No valid address available.")

        return None, None

    def get_privkey_from_address(self,
                                 target_address,
                                 count=DEFAULT_CHILD_ADDRESSES):
        """ Given a child address, return priv key of that address
        """

        addresses = self.get_child_keypairs(count=count)

        index = 0

        for address in addresses:

            if address == target_address:

                return self.get_child_privkey(index)

            index += 1

        return None
Exemple #16
0
class HDWallet(object):

    """
        Initialize a hierarchical deterministic wallet with
        hex_privkey and get child addresses and private keys
    """

    def __init__(self, hex_privkey=None):

        """
            If @hex_privkey is given, use that to derive keychain
            otherwise, use a new random seed
        """

        if hex_privkey:
            self.priv_keychain = PrivateKeychain.from_private_key(hex_privkey)
        else:
            self.priv_keychain = PrivateKeychain()

    def get_privkey(self, index=None):
        """
            @index is the child index

            Returns:
            master/root privkey by default
            or child privkey for given @index
        """

        if index is None:
            return self.priv_keychain.private_key()

        child = self.priv_keychain.hardened_child(index)
        return child.private_key()

    def get_address(self, index=None):
        """
            @index is the child index

            Returns:
            master/root address by default
            or child address for given @index
        """

        if index is None:
            hex_privkey = self.get_privkey()
            return get_address_from_privkey(hex_privkey)

        hex_privkey = self.get_privkey(index)
        return get_address_from_privkey(hex_privkey)

    def get_keypairs(self, count=None, include_privkey=False):
        """
            Returns (privkey, address) keypairs

            Returns:
            master/root pair by default
            if count is given, then returns child keypairs

            @include_privkey: toggles between option to return
                             privkeys along with addresses or not
        """

        keypairs = []

        if count is None:

            if include_privkey:
                keypairs.append((self.get_address(), self.get_privkey()))
            else:
                keypairs.append(self.get_address())

            return keypairs

        for index in range(count):
            hex_privkey = self.get_privkey(index)
            address = self.get_address(index)

            if include_privkey:
                keypairs.append((address, hex_privkey))
            else:
                keypairs.append(address)

        return keypairs

    def get_next_keypair(self, count=DEFAULT_CHILD_ADDRESSES):
        """ Get next payment address that is ready to use

            Returns (payment_address, hex_privkey)
        """

        addresses = self.get_keypairs(count=count)
        index = 0

        for payment_address in addresses:

            # find an address that can be used for payment

            if dontuseAddress(payment_address):
                log.debug("Pending tx on address: %s" % payment_address)

            elif underfundedAddress(payment_address):
                log.debug("Underfunded address: %s" % payment_address)

            else:
                return payment_address, self.get_privkey(index)

            index += 1

        log.debug("No valid address available.")

        return None, None
    def get_priv_keychain(self, hex_privkey, chaincode):
        if hex_privkey:
            return PrivateKeychain.from_private_key(hex_privkey, chain_path=chaincode)

        log.debug('No privatekey given, starting new wallet')
        return PrivateKeychain()
class HDWallet(object):

    """
        Initialize a hierarchical deterministic wallet with
        hex_privkey and get child addresses and private keys
    """

    def __init__(self, hex_privkey=None, enable_cache=False):

        """
            If @hex_privkey is given, use that to derive keychain
            otherwise, use a new random seed
        """

        if hex_privkey:
            self.priv_keychain = PrivateKeychain.from_private_key(hex_privkey)
        else:
            #log.debug("No privatekey given, starting new wallet")
            self.priv_keychain = PrivateKeychain()

        self.master_address = self.get_master_address()
        self.child_addresses = None

        if enable_cache:
            cache = self.get_cache()

            if cache is not None:

                if cache['master_address'] == self.master_address:
                    self.child_addresses = cache['child_addresses']
                else:
                    log.debug("Wallet already exists with master address: %s" % cache['master_address'])
            else:
                #log.debug("Creating cache of HD wallet addresses ...")
                self.create_addresses_cache()

    def create_addresses_cache(self, count=DEFAULT_CHILD_ADDRESSES):

        if self.get_cache() is not None:
            return True

        child_addresses = []

        for index in range(0, count):
            hex_privkey = self.get_child_privkey(index)
            address = self.get_child_address(index)

            child_addresses.append(address)

        if not os.path.exists(LOCAL_DIR):
            os.makedirs(LOCAL_DIR)

        with open(CACHE_FILE_FULLPATH, 'w') as cache_file:
            data = {'child_addresses': child_addresses}
            data['master_address'] = self.get_master_address()
            cache_file.write(json.dumps(data))

        return True

    def get_cache(self):

        if not os.path.isfile(CACHE_FILE_FULLPATH):
            return None

        try:
            with open(CACHE_FILE_FULLPATH, 'r') as cache_file:
                data = json.loads(cache_file.read())

            return data
        except:
            return None

    def get_master_privkey(self):

        return self.priv_keychain.private_key()

    def get_child_privkey(self, index=0):
        """
            @index is the child index

            Returns:
            child privkey for given @index
        """

        child = self.priv_keychain.hardened_child(index)
        return child.private_key()

    def get_master_address(self):

        hex_privkey = self.get_master_privkey()
        return get_address_from_privkey(hex_privkey)

    def get_child_address(self, index=0):
        """
            @index is the child index

            Returns:
            child address for given @index
        """

        if self.child_addresses is not None:
            return self.child_addresses[index]

        hex_privkey = self.get_child_privkey(index)
        return get_address_from_privkey(hex_privkey)

    def get_child_keypairs(self, count=1, offset=0, include_privkey=False):
        """
            Returns (privkey, address) keypairs

            Returns:
            returns child keypairs

            @include_privkey: toggles between option to return
                             privkeys along with addresses or not
        """

        keypairs = []

        for index in range(offset, offset+count):
            address = self.get_child_address(index)

            if include_privkey:
                hex_privkey = self.get_child_privkey(index)
                keypairs.append((address, hex_privkey))
            else:
                keypairs.append(address)

        return keypairs

    def get_next_keypair(self, count=DEFAULT_CHILD_ADDRESSES):
        """ Get next payment address that is ready to use

            Returns (payment_address, hex_privkey)
        """

        addresses = self.get_child_keypairs(count=count)
        index = 0

        for payment_address in addresses:

            # find an address that can be used for payment

            if dontuseAddress(payment_address):
                log.debug("Pending tx on address: %s" % payment_address)

            elif underfundedAddress(payment_address):
                log.debug("Underfunded address: %s" % payment_address)

            else:
                return payment_address, self.get_child_privkey(index)

            index += 1

        log.debug("No valid address available.")

        return None, None

    def get_privkey_from_address(self, target_address,
                                 count=DEFAULT_CHILD_ADDRESSES):
        """ Given a child address, return priv key of that address
        """

        addresses = self.get_child_keypairs(count=count)

        index = 0

        for address in addresses:

            if address == target_address:

                return self.get_child_privkey(index)

            index += 1

        return None