Exemplo n.º 1
0
def txn_pool_runner(txn_list, registration_list, bc):
    """Iterates through transactions received from peers. If the transaction
    is a property genesis transaction, send it to all peers. If it's a
    transaction with an associated btc payment, check if it's in a block on
    Testnet. If it is, send it to all peers."""
    headers = {'content-type': 'application/json'}
    testnet = Bitcoin(testnet=True)
    while True:
        for txn in txn_list:
            if txn.txn_type == 'propTx':
                # first, make sure transaction is still valid
                txn_valid = True
                for input_ in txn.list_of_inputs:
                    status = bc.get_output_status(input_)
                    if status == 'Invalid':
                        txn_valid = False

                if not txn_valid:
                    print(
                        'Transaction removed: no longer valid because input was spent. Hash:'
                    )
                    print(txn.transaction_hash)
                    txn_list.remove(txn)

                if not verify_signature(txn):
                    print('The signature for transaction was not valid')
                    print(txn.transaction_hash)
                    txn_list.remove(txn)

                btc_txn_hash = txn.btc_txn_hash
                try:
                    testnet.block_height(btc_txn_hash)
                except KeyError:
                    pass
                else:
                    txn_list.remove(txn)
                    print('Testnet transaction validated.')
                    print('Testnet hash: {}'.format(btc_txn_hash))
                    print('Own chain hash: {}'.format(txn.transaction_hash))
                    payload = txn.to_dict()
                    for registration in registration_list:
                        ip_addr = registration.addrMe
                        request_url = 'http://{}:58336/jsonrpc'.format(ip_addr)
                        requests.post(request_url,
                                      data=json.dumps(payload),
                                      headers=headers)
            elif txn.txn_type == 'propGen':
                # for now, propGen always valid
                txn_list.remove(txn)
                payload = txn.to_dict()
                for registration in registration_list:
                    ip_addr = registration.addrMe
                    request_url = 'http://{}:58336/jsonrpc'.format(ip_addr)
                    requests.post(request_url,
                                  data=json.dumps(payload),
                                  headers=headers)
Exemplo n.º 2
0
def create_unsigned_redeem_tx(hash_tx):
    inputs = [{'output': hash_tx + ':0', 'value': utils.amount}]
    outs = [{
        'value': utils.amount - utils.fee,
        'address': utils.addr_redeemer
    }]
    return Bitcoin(testnet=True).mktx(inputs, outs)
Exemplo n.º 3
0
    def __init__(self, mnemonic=""):
        '''
        create master bip32 key object either with seed or generate new mnemonics
        if existing seed, do account/address search (bip44)
        set up branch records according to BIP 44 for Bitcoin
        '''
        self.master = Key.usingMnemonic(mnemonic=mnemonic)

        # key = account_num, value = {address_num : address}  -- only issued addresses and accounts are saved
        self.hierarchy = {}
        self.free_account = 0
        self.change_hierarchy = {}
        self.btc = Bitcoin()

        if mnemonic != "":
            self.initialize_wallet()
Exemplo n.º 4
0
    def __init__(self, network="testnet"):

        # Bitcoin network.
        self.network = network
        if self.network == "mainnet":
            self.mainnet = True
        elif self.network == "testnet":
            self.mainnet = False
        else:
            raise ValueError("invalid network, only mainnet or testnet")
        # Bitcoin wallet initialization.
        self.bitcoin = Bitcoin(testnet=(not self.mainnet))

        # Is compressed
        self.is_compressed = None
        # Main wallet init.
        self._private_key, self._public_key, self._address = None, None, None
        # Compressed and Uncompressed public key.
        self._compressed, self._uncompressed = None, None
def create_unsigned_2_of_3_tx(add):
    inputs = get_utxos(add)
    balance = sum([utxo['value'] for utxo in inputs])
    outs = [
        #{'value': utils.amount, 'script': utils.mk_2_of_3_script()},
        {
            'value':
            utils.amount,
            'script':
            '76a9147e67314504e2d48225e6bc9214a9be1bea95330c8763ac676d00687b76a914baa42949adda8ed2ad3354332be5e4a3d44902768763ac676d00687276a914e39299dc381b4428032bf4cf51403c209b2751038763ac676d0068939352a1'
        },
        {
            'value': balance - utils.amount - utils.fee,
            'script': mk_pubkey_script(add)
        }
    ]
    return Bitcoin(testnet=True).mktx(inputs, outs)
Exemplo n.º 6
0
def main():
    wallet_privkeys = list()
    wallet_privkeys.append(
        'cPZa7oEsR6GBuyrUH1s64meMZB31nxxPbhCVc4HZot55kgFGhWUg')
    wallet_privkeys.append(
        'cQ8RV14JvX8X9sNg2VAp98tGT1kG4BaG1NBy75T3jNeX8YYGAWUf')
    wallet_privkeys.append(
        'cQ6PpZDZQmffjo9X1PAriUCX7asMog8kM8t8tBf1WoqLwomkvkh9')
    wallet_privkeys.append(
        'cV88fZveSBQjcGZkfnDbY8akcBPEM3L1CCLehb8D4Kc8dHNLmzTG')
    testnet = Bitcoin(testnet=True)
    # tx_info = testnet.fetchtx('3c5d680ee98f8f2c375f5850896b134fc86e32adc00dcafc5debcfa1074a3bc5')
    # print(tx_info)
    # wallet_1_priv = wallet_privkeys[0]
    # wallet_1_pub = testnet.privtopub(wallet_1_priv)
    # wallet_1_addr = testnet.pubtoaddr(wallet_1_pub)
    # wallet_2_priv = wallet_privkeys[1]
    # wallet_2_pub = testnet.privtopub(wallet_2_priv)
    # wallet_2_addr = testnet.pubtoaddr(wallet_2_pub)
    # wallet_4_priv = wallet_privkeys[3]
    # wallet_4_pub = testnet.privtopub(wallet_4_priv)
    # wallet_4_addr = testnet.pubtoaddr(wallet_4_pub)
    # tx_dump = testnet.send(wallet_4_priv, wallet_1_addr, 1)
    # print(tx_dump)
    # if tx_dump['status'] == 'success':
    #     tx_data = tx_dump['data']
    #     tx_hash = tx_data['txid']
    #     print(tx_hash)
    #     tx_info = testnet.fetchtx(tx_hash)
    #     print(tx_info)
    #     raises key error
    #     block_height = testnet.block_height(tx_hash)
    #     print(block_height)
    # print(testnet.block_height('ae227de42633d413e83935cc99ad18c9db99a728fa689ae57c161c191f1c3e86'))
    # tx_hist = testnet.history(wallet_1_addr)
    # print(tx_hist)
    # for wallet in wallet_privkeys:
    #     pub = testnet.privtopub(wallet)
    #     print('Public key: {}'.format(pub))
    #     addr = testnet.pubtoaddr(pub)
    #     print('Address: {}'.format(addr))
    #     inputs = testnet.unspent(addr)
    #     print(inputs)
    pass
Exemplo n.º 7
0
        '5afb578976d3bbfd470f906586cb33abf64830563e0c43255b34a421665e04f7',
        '050d94209dfbc9150fad697c37defc83089b138105d23d32e3e6c32eb78f4429',
        '946f050043939a8e3d4ccf3ef393d0d8832141dcbdcb9c2363ac78cc3649d080',
        'f8f3ff7bb10bc246a0c657c8f79c47db7588b086072e11d821489f461b41b2f5',
        '18401302386e56dd7309aa10fb89a7db1bbe77bc6bff5fda8619a9b11a809497',
        '81dcf5b60b87b03c66ab530fed899c656c6a3b03f45352ad5f949bd9bfc328e7'
    ], 5, 2086000000001)]))

    x12 = dumps((0, 1, 'TXID->[MNODE]', [
        (12,
         '54c693db802d83596e3a0cdec1f99dc01af246ca51b82adaad2f41e0a8fb2131')
    ]))

    x13 = dumps((0, 1, 'NAME->[OUTPOINT]', [(13, '[h', True)]))

    c = Bitcoin()
    priv = sha256('allegory allpay test dummy seed')
    pub = c.privtopub(priv)
    addr = c.pubtoaddr(pub)

    ###############
    inputsD = \
        [( (0, 'dee533d8d0ac0b1f0ccb49b80f075fee63802a9fab9114a90e9c5ae866695731', 0), 12345678)]

    inputs = \
        [{'output': 'e4714990d74d9636c2efdb98a5e7dc7c1c516a43572638641eb67dda9df43015:0', 'value': 1000000},
         {'output': '51ce9804e1a4fd3067416eb5052b9930fed7fdd9857067b47d935d69f41faa38:0', 'value': 1000000}]
    outs = [{
        'value': 1000000,
        'address': '18TLpiL4UFwmQY8nnnjmh2um11dFzZnBd9'
    }, {
Exemplo n.º 8
0
class Wallet:
    """
    Bitcoin Wallet class.

    :param network: Bitcoin network, defaults to testnet.
    :type network: str
    :returns:  Wallet -- Bitcoin wallet instance.

    .. note::
        Bitcoin has only two networks, ``mainnet`` and ``testnet``.
    """

    # PyShuttle Bitcoin (BTC) wallet init.
    def __init__(self, network="testnet"):

        # Bitcoin network.
        self.network = network
        if self.network == "mainnet":
            self.mainnet = True
        elif self.network == "testnet":
            self.mainnet = False
        else:
            raise ValueError("invalid network, only mainnet or testnet")
        # Bitcoin wallet initialization.
        self.bitcoin = Bitcoin(testnet=(not self.mainnet))

        # Is compressed
        self.is_compressed = None
        # Main wallet init.
        self._private_key, self._public_key, self._address = None, None, None
        # Compressed and Uncompressed public key.
        self._compressed, self._uncompressed = None, None

    def from_private_key(self, private_key, compressed=COMPRESSED):
        """
        Initiate Bitcoin wallet from private key.

        :param private_key: Bitcoin wallet private key.
        :type private_key: str.
        :param compressed: Bitcoin public key compressed, default is True.
        :type compressed: bool
        :returns:  Wallet -- Bitcoin wallet instance.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="mainnet")
        >>> wallet.from_private_key("92cbbc5990cb5090326a76feeb321cad01048635afe5756523bbf9f7a75bf38b")
        <shuttle.providers.bitcoin.wallet.Wallet object at 0x040DA268>
        """
        self.is_compressed = compressed
        self._private_key = PrivateKey.unhexlify(private_key)
        public_key = self.bitcoin.privtopub(self._private_key.hexlify())
        self._compressed = PublicKey.unhexlify(public_key).compressed.hex()
        self._uncompressed = PublicKey.unhexlify(public_key).uncompressed.hex()
        self._public_key = PublicKey.unhexlify(self._compressed) if self.is_compressed \
            else PublicKey.unhexlify(self._uncompressed)
        self._address = self._public_key.to_address(mainnet=self.mainnet)
        return self

    def from_passphrase(self, passphrase, compressed=COMPRESSED):
        """
        Initiate Bitcoin wallet from passphrase.

        :param passphrase: Bitcoin wallet passphrase.
        :type passphrase: str.
        :param compressed: Bitcoin public key compressed, default is True.
        :type compressed: bool
        :returns:  Wallet -- Bitcoin wallet instance.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="mainnet")
        >>> wallet.from_passphrase("meherett")
        """
        self.is_compressed = compressed
        private_key = hashlib.sha256(passphrase.encode()).hexdigest()
        self._private_key = PrivateKey.unhexlify(private_key)
        public_key = self.bitcoin.privtopub(self._private_key.hexlify())
        self._compressed = PublicKey.unhexlify(public_key).compressed.hex()
        self._uncompressed = PublicKey.unhexlify(public_key).uncompressed.hex()
        self._public_key = PublicKey.unhexlify(self._compressed) if self.is_compressed \
            else PublicKey.unhexlify(self._uncompressed)
        self._address = self._public_key.to_address(mainnet=self.mainnet)
        return self

    def from_address(self, address):
        """
        Initiate Bitcoin wallet from address.

        :param address: Bitcoin wallet private key.
        :type address: str.
        :returns:  Wallet -- Bitcoin wallet instance.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_address("mqLyrNDjpENRMZAoDpspH7kR9RtgvhWzYE")
        <shuttle.providers.bitcoin.wallet.Wallet object at 0x040DA268>
        """
        if not is_address(address=address, network=self.network):
            raise AddressError("invalid %s %s address" %
                               (self.network, address))
        self._address = Address.from_string(address)
        return self

    # Bitcoin main private key.
    def private_key(self):
        """
        Get Bitcoin wallet private key.

        :returns: str -- Bitcoin private key.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett")
        >>> wallet.private_key()
        "d4f5c55a45c004660b95ec833bb24569eba1559f214e90efa6e8d0b3afa14394"
        """
        return self._private_key.hexlify()

    # Bitcoin main public key.
    def public_key(self, private_key=None, compressed=COMPRESSED):
        """
        Get Bitcoin wallet public key.

        :param private_key: Bitcoin private key, default is None.
        :type private_key: str
        :param compressed: Bitcoin public key compressed, default is True.
        :type compressed: bool
        :return: str -- Bitcoin public key.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett", compressed=False)
        >>> wallet.public_key()
        "04afa8301b068c2c184e0a3e77183dc95ec1130371c02ed172bec8f3bfbad6b17334244f64fe877d5e4839690c62b9d1f4095608f2ac29235e4b0299b6b96f6f35"
        """
        if private_key is None:
            return self._public_key.hexlify()
        public_key = self.bitcoin.privtopub(private_key)
        return PublicKey.unhexlify(public_key).compressed.hex() if compressed else \
            PublicKey.unhexlify(public_key).uncompressed.hex()

    # Compressed public key.
    def compressed(self, public_key=None):
        """
        Get Bitcoin wallet compressed public key.

        :param public_key: Bitcoin public key, default is None.
        :type public_key: str
        :return: str -- Bitcoin compressed public key.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett")
        >>> wallet.compressed()
        "03afa8301b068c2c184e0a3e77183dc95ec1130371c02ed172bec8f3bfbad6b173"
        """
        if public_key is None:
            return self._compressed
        return PublicKey.unhexlify(public_key).compressed.hex()

    # Uncompressed public key.
    def uncompressed(self, public_key=None):
        """
        Get Bitcoin wallet uncompressed public key.

        :param public_key: Bitcoin public key, default is None.
        :type public_key: str
        :return: str -- Bitcoin uncompressed public key.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett")
        >>> wallet.uncompressed()
        "04afa8301b068c2c184e0a3e77183dc95ec1130371c02ed172bec8f3bfbad6b17334244f64fe877d5e4839690c62b9d1f4095608f2ac29235e4b0299b6b96f6f35"
        """
        if public_key is None:
            return self._uncompressed
        return PublicKey.unhexlify(public_key).uncompressed.hex()

    # Bitcoin main _address.
    def address(self, public_key=None):
        """
        Get Bitcoin wallet address.

        :param public_key: Bitcoin address, default is None.
        :type public_key: str
        :return: str -- Bitcoin address.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett")
        >>> wallet.address()
        "mm357rHaKqVmhEhFFwUhz6mRVAHkJaDTKt"
        """
        if public_key is None:
            return str(self._address)
        return str(
            PublicKey.unhexlify(public_key).to_address(mainnet=self.mainnet))

    # Bitcoin main _address hash.
    def hash(self, public_key=None):
        """
        Get Bitcoin wallet hash.

        :param public_key: Bitcoin hash, default is None.
        :type public_key: str
        :return: str -- Bitcoin hash.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett")
        >>> wallet.hash()
        "3c8acde1c7cf370d970725f13eff03bf74b3fc61"
        """
        if public_key is None:
            return self._address.hash.hex()
        return PublicKey.unhexlify(public_key)\
            .to_address(mainnet=self.mainnet).hash.hex()

    # Bitcoin public to public key hash script.
    def p2pkh(self, address=None):
        """
        Get Bitcoin wallet p2pkh.

        :param address: Bitcoin p2pkh, default is None.
        :type address: str
        :return: str -- Bitcoin p2pkh.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett")
        >>> wallet.p2pkh()
        "76a9143c8acde1c7cf370d970725f13eff03bf74b3fc6188ac"
        """
        if address is None:
            return P2pkhScript(self._address).hexlify()
        if not is_address(address=address, network=self.network):
            raise AddressError("invalid %s %s address" %
                               (self.network, address))
        address = Address.from_string(address)
        return P2pkhScript(address).hexlify()

    # Bitcoin public to script hash script.
    def p2sh(self, address=None):
        """
        Get Bitcoin wallet p2sh.

        :param address: Bitcoin p2sh, default is None.
        :type address: str
        :return: str -- Bitcoin p2sh.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett")
        >>> wallet.p2sh()
        "a914a3c4995d9cd0303e5f89ee1433212c797d04ee5d87"
        """
        if address is None:
            return P2shScript(P2pkhScript(self._address)).hexlify()
        if not is_address(address=address, network=self.network):
            raise AddressError("invalid %s %s address" %
                               (self.network, address))
        address = Address.from_string(address)
        return P2shScript(P2pkhScript(address)).hexlify()

    # Bitcoin balance
    def balance(self, address=None, network="testnet"):
        """
        Get Bitcoin wallet balance.

        :param address: Bitcoin balance, default is None.
        :type address: str
        :param network: Bitcoin balance, default is testnet.
        :type network: str
        :return: int -- Bitcoin balance.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett")
        >>> wallet.balance()
        1000000
        """
        if address is None:
            return get_balance(str(self._address), self.network)
        return get_balance(address, network)

    def unspent(self, address=None, network="testnet", limit=15):
        """
        Get Bitcoin wallet unspent transaction output.

        :param address: Bitcoin balance, default is None.
        :type address: str
        :param network: Bitcoin balance, default is testnet.
        :type network: str
        :param limit: Bitcoin balance, default is 15.
        :type limit: int
        :return: list -- Bitcoin unspent transaction outputs.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett")
        >>> wallet.unspent()
        [{'index': 0, 'hash': 'be346626628199608926792d775381e54d8632c14b3ce702f90639481722392c', 'output_index': 1, 'amount': 12340, 'script': '76a9146bce65e58a50b97989930e9a4ff1ac1a77515ef188ac'}]
        """
        if address is None:
            address = str(self._address)
            network = str(self.network)
        unspent = list()
        if not is_address(address=address, network=network):
            raise AddressError("invalid %s %s address" % (network, address))
        unspent_transactions = get_unspent_transactions(address,
                                                        network,
                                                        limit=limit)
        for index, unspent_transaction in enumerate(unspent_transactions):
            unspent.append(
                dict(index=index,
                     hash=unspent_transaction["tx_hash"],
                     output_index=unspent_transaction["tx_output_n"],
                     amount=unspent_transaction["value"],
                     script=unspent_transaction["script"]))
        return unspent
Exemplo n.º 9
0
def txn_pool_runner(txn_list, registration_list, bc):
    """Iterates through transactions received from peers. If the transaction
    is a property genesis transaction, send it to all peers. If it's a
    transaction with an associated btc payment, check if it's in a block on
    Testnet. If it is, send it to all peers."""
    headers = {'content-type': 'application/json'}
    testnet = Bitcoin(testnet=True)
    # cache registration list so we can pause when a new peer is found
    local_reg_list = registration_list[:]
    while True:
        for registration in registration_list:
            if registration not in local_reg_list:
                time.sleep(2)
                local_reg_list.append(registration)
        for txn in txn_list:
            if not verify_signature(txn):
                print('Transaction was not signed correctly')
                print(txn.transaction_hash)
                txn_list.remove(txn)
                continue
            if txn.txn_type == 'propTx':
                # first, make sure transaction is still valid
                txn_valid = True
                for input_ in txn.list_of_inputs:
                    status = bc.get_output_status(input_)
                    if status == 'Invalid':
                        txn_valid = False

                if not txn_valid:
                    print(
                        'Transaction removed: no longer valid because input was spent. Hash:'
                    )
                    print(txn.transaction_hash)
                    txn_list.remove(txn)

                btc_txn_hash = txn.btc_txn_hash
                try:
                    testnet.block_height(btc_txn_hash)
                except KeyError:
                    pass
                else:
                    txn_list.remove(txn)
                    print('Testnet transaction validated.')
                    print('Testnet hash: {}'.format(btc_txn_hash))
                    print('Own chain hash: {}'.format(txn.transaction_hash))
                    payload = txn.to_dict()
                    for registration in registration_list:
                        ip_addr = registration.addrMe
                        request_url = 'http://{}:58336/jsonrpc'.format(ip_addr)
                        try:
                            requests.post(request_url,
                                          data=json.dumps(payload),
                                          headers=headers)
                        except requests.exceptions.RequestException:
                            registration_list.remove(registration)
            elif txn.txn_type == 'propGen':
                # for now, propGen always valid
                txn_list.remove(txn)
                payload = txn.to_dict()
                for registration in registration_list:
                    ip_addr = registration.addrMe
                    request_url = 'http://{}:58336/jsonrpc'.format(ip_addr)
                    try:
                        requests.post(request_url,
                                      data=json.dumps(payload),
                                      headers=headers)
                    except requests.exceptions.RequestException:
                        registration_list.remove(registration)
            try:
                with open('txns.pickle', mode='wb') as txn_file:
                    txns_dict = txn_list.save_txns_to_dict()
                    pickle.dump(txns_dict, txn_file)
            except OSError:
                pass
            time.sleep(2)
Exemplo n.º 10
0
def transact_property(bc, host='registrar'):
    """Transacts property on demand"""
    headers = {'content-type': 'application/json'}
    registrar_input_url = 'http://{}:58335/jsonrpc'.format(host)
    registrar_txn_url = 'http://{}:58336/jsonrpc'.format(host)
    wallet_privkeys = [
        'cPZa7oEsR6GBuyrUH1s64meMZB31nxxPbhCVc4HZot55kgFGhWUg',
        'cQ8RV14JvX8X9sNg2VAp98tGT1kG4BaG1NBy75T3jNeX8YYGAWUf',
        'cQ6PpZDZQmffjo9X1PAriUCX7asMog8kM8t8tBf1WoqLwomkvkh9',
        'cV88fZveSBQjcGZkfnDbY8akcBPEM3L1CCLehb8D4Kc8dHNLmzTG'
    ]

    input_hash = input('Hash of input to spend?')
    candidate_input = bc.unspent_output_dict[input_hash][0]

    payload = candidate_input.to_dict()
    return_data = requests.post(registrar_input_url,
                                data=json.dumps(payload),
                                headers=headers).json()
    if return_data['Status'] != 'Unspent':
        print('Attempted to create propTx with invalid input')
        raise ValueError('Attempted to create propTx with invalid input')
    input_list = [candidate_input]

    # generate random number of outputs
    property_addr = candidate_input.property_addr
    input_value = candidate_input.value
    print('Input value to spend: ' + str(input_value))

    num_outputs = int(input('Number of new owners?'))

    output_list = list()
    total_value = 0
    for idx in range(num_outputs):
        wallet_addr = input('Wallet address of owner #' + str(idx + 1) + '?')
        value_i = int(input('Value to owner #' + str(idx + 1) + '?'))
        script = make_random_string()

        if value_i <= 0:
            print('Non-positive value invalid')
            raise ValueError('Non-positive value invalid')
        total_value += value_i

        output = Output(wallet_addr, property_addr, value_i, idx, script)
        output_list.append(output)

    if total_value != input_value:
        print('Total output value not equal intput value, try again')
        raise ValueError('Total output value not equal input value, try again')

    testnet = Bitcoin(testnet=True)
    print('Testnet privkey options:')
    for wallet_privkey in wallet_privkeys:
        print(wallet_privkey)
    payor_privkey = input('Payor privkey?')
    recip_privkey = input('Recipient privkey?')

    if payor_privkey not in wallet_privkeys or recip_privkey not in wallet_privkeys:
        print('testnet privkeys invalid')
        raise ValueError('testnet privkeys invalid')

    satoshis = int(input('How many satoshis to send?'))

    recip_address = testnet.privtoaddr(recip_privkey)

    tx_dump = testnet.send(payor_privkey, recip_address, satoshis, fee=400000)

    try:
        if tx_dump['status'] == 'success':
            tx_data = tx_dump['data']
            btc_txn_hash = tx_data['txid']
        else:
            print('Testnet transaction failed')
            raise ValueError('Testnet transaction failed')
    except TypeError:
        raise ValueError('TypeError')

    new_transaction = Transaction(input_list, output_list, 'propTx',
                                  btc_txn_hash)

    print('Submitted transaction to Testnet. Hash:')
    print(btc_txn_hash)
    print('Transaction hash (own chain):')
    print(new_transaction.transaction_hash)

    # send transaction to registrar
    payload = new_transaction.to_dict()
    requests.post(registrar_txn_url, data=json.dumps(payload), headers=headers)

    print('Property transaction request sent to registrar: ' + property_addr)
Exemplo n.º 11
0
class Wallet:
    def __init__(self, mnemonic=""):
        '''
        create master bip32 key object either with seed or generate new mnemonics
        if existing seed, do account/address search (bip44)
        set up branch records according to BIP 44 for Bitcoin
        '''
        self.master = Key.usingMnemonic(mnemonic=mnemonic)

        # key = account_num, value = {address_num : address}  -- only issued addresses and accounts are saved
        self.hierarchy = {}
        self.free_account = 0
        self.change_hierarchy = {}
        self.btc = Bitcoin()

        if mnemonic != "":
            self.initialize_wallet()

    def create_tx(self, from_account, to_addr, amount, fee=0):
        '''
        Create a serialized transaction from funds under 'from_account'

        returns signed serialized tx
        '''
        spendables = self.get_utxo(from_account, amount + fee)
        if not spendables:
            raise Exception("Account is empty or amount to send is 0")
        assert (fee >= 0 and amount > 0)

        keys = []
        total = 0
        for sp in spendables:
            keys.append(
                self.get_key(sp['key_info'][0], sp['key_info'][1],
                             sp['key_info'][2]))
            total += sp['value']
            del sp['key_info']

        change_addr = self.new_address(from_account, change=1)
        payables = [{
            'value': amount,
            'address': to_addr
        }, {
            'value': total - fee - amount,
            'address': change_addr
        }]

        tx = mktx(spendables, payables)
        for i, key in enumerate(keys):
            priv = key.get_private_key().hex()
            tx = sign(tx, i, priv)

        return tx

    def send(self, from_account, to_addr, amount, fee=0):
        ''' This method sends the transaction from create_tx to the mainnet'''
        tx = self.create_tx(from_account, to_addr, amount, fee)
        res = pushtx(binascii.unhexlify(tx))
        return res

    def balance(self, verbose=True):
        '''
        prints out all accounts' balances as a dictionary

        returns all accounts balances summed together
        '''
        res = {}
        total = 0
        for i in self.hierarchy:
            balance = self.chain_balance(i, False)
            total += balance
            res[i] = balance

        for i in self.change_hierarchy:
            balance = self.chain_balance(i, True)
            total += balance
            if res.get(i):
                res[i] += balance
            else:
                res[i] = balance

        if verbose:
            print("Wallet balance...")
            pprint(res)

        return total

    def account_balance(self, acc_num):
        '''
        returns balance on account
        '''

        if self.hierarchy.get(acc_num) is None and self.change_hierarchy.get(
                acc_num) is None:
            raise ValueError("Account %i hasn't been created" % acc_num)
        elif self.hierarchy.get(acc_num) is None:
            raise Exception(
                "account in internal chain cannot exist if it doesn't exist on external chain"
            )
        elif self.change_hierarchy.get(acc_num) is None:
            total = self.chain_balance(acc_num, False, verbose=False)
        else:
            total = self.chain_balance(acc_num, False,
                                       verbose=False) + self.chain_balance(
                                           acc_num, True, verbose=False)
        return total

    def chain_balance(self, acc_num, change, verbose=False):
        '''
        inner method
        params
            acc_num = account number
            change = 1 or 0
            verbose = prints out balances under each address on hierarchy

        returns : balance on internal or external chain
        '''
        if change:
            if verbose:
                print("Scanning internal chain : account %i ..." % acc_num)
            if self.change_hierarchy.get(acc_num) is None:
                raise Exception("Account number %i hasn't been created yet." %
                                acc_num)
            int_entry = self.change_hierarchy[acc_num]
            balance, entry = self.balance_helper(int_entry)
            if verbose:
                print("\nInternal chain balance ...\n")
                pprint(entry)
        else:
            if verbose:
                print("Scanning external chain : account %i ..." % acc_num)
            if self.hierarchy.get(acc_num) is None:
                raise Exception("Account number %i hasn't been created yet." %
                                acc_num)
            ext_entry = self.hierarchy[acc_num]
            balance, entry = self.balance_helper(ext_entry)
            if verbose:
                print("\nExternal chain balance ...\n")
                pprint(entry)

        return balance

    def balance_helper(self, entry):
        '''inner method'''
        total = 0
        d = {}
        for i in entry:
            value = self.btc.history(entry[i])['final_balance']
            d[i] = value
            total += value
        return total, d

    def get_key(self, account, change, addr):
        path = self.build_addr_path(account, change, addr)
        return self.from_path(path)

    def get_address(self, account, change, addr):
        return self.get_key(account, change, addr).address().decode()

    def get_utxo(self, account_num, amount):
        '''
        Get all available UTXO from 'account_num' that exceeds 'amount'

        returns = list of spendable outputs
        '''

        if self.hierarchy.get(account_num) is None:
            raise ValueError("Account : %i hasn't been created yet" %
                             account_num)
        if amount <= 0:
            raise ValueError("'amount' has negative or zero value")

        # getting change first
        int_utxo, int_val = self.get_utxo_helper(account_num, 1, amount)
        if int_val >= amount:
            return int_utxo

        ext_utxo, ext_val = self.get_utxo_helper(account_num, 0, amount)

        sum = (ext_val + int_val)
        if sum < amount:
            raise ValueError(
                "Account : %i doesn't have enough money. Balance = %i required = %i"
                % (account_num, ext_val + int_val, amount))

        return ext_utxo + int_utxo

    def get_utxo_helper(self, account_num, change, value):
        '''
        inner method
        param:
            change = either 0 or 1 depending whether browsing internal or external chain (BIP-44)
            value = amount of utxo to find
            account_num = account number

        get spendables for sending money
        '''
        # check if account exists
        collected = 0  # amount of utxo saved in spendables in satoshi
        spendables = []

        if change == 0:
            account_entry = self.hierarchy.get(account_num)
            if account_entry is None:
                return spendables, collected
        elif change == 1:
            account_entry = self.change_hierarchy.get(account_num)
            if account_entry is None:
                return spendables, collected
        else:
            raise Exception(
                "Wrong 'change' parameter in get_spendables_helper().")

        for addr_num in account_entry:
            address = account_entry[addr_num]
            addr_utxo = self.btc.unspent(address)
            for output in addr_utxo:
                if collected >= value:
                    return spendables, collected
                collected += output["value"]
                output["key_info"] = (account_num, change, addr_num)
                spendables.append(output)

        return spendables, collected

    def new_account(self):
        '''
        create a new account
        returns the number of the account
        '''

        account = self.free_account
        self.hierarchy[account] = {}
        self.change_hierarchy[account] = {}
        self.free_account += 1
        return account

    def new_address(self, account_num, change=0):
        '''
        create an account address for receiving money
        '''
        if self.hierarchy.get(account_num) is None and change == 0:
            raise Exception("Account number %i does not exist in the record" %
                            account_num)
        if self.change_hierarchy.get(account_num) is None and change == 1:
            raise Exception(
                "Account number %i does not exist in the change_record" %
                account_num)

        if change == 1:
            account = self.change_hierarchy[account_num]
            new_addr_num = len(self.change_hierarchy[account_num].keys()) + 1
            new_addr = self.get_address(account_num, change, new_addr_num)
            self.change_hierarchy[account_num][new_addr_num] = new_addr
        elif change == 0:
            account = self.hierarchy[account_num]
            new_addr_num = len(self.hierarchy[account_num].keys()) + 1
            new_addr = self.get_address(account_num, change, new_addr_num)
            self.hierarchy[account_num][new_addr_num] = new_addr
        else:
            raise Exception("Wrong value for 'change' parameter")

        return new_addr

    def build_addr_path(self, account, change, addr):
        return "m/44'/0'" + "/" + str(account) + "'/" + str(
            change) + "/" + str(addr)

    def from_path(self, path):
        '''
        inner method
        Create a bip32 key from path e.g "m/0'/23/3/2"

        returns bip32 key object from key.py
        '''
        path_list = path.split('/')

        if path_list[0] == 'm':
            parent = self.master
        elif path_list[0] == 'M':
            parent = self.master_pub
        else:
            raise ValueError("Error in the path - must start with M or m: %s",
                             path)

        for i, index in enumerate(path_list[1:]):

            if "'" in index:
                if path_list[0] == 'M':
                    raise Exception(
                        "Cannot derive a hardened key from a public master key"
                    )
                index = index[:-1]
                index = int(index) + 2147483648
            else:
                index = int(index)
            parent = parent.get_child(index)

        return parent

    def initialize_wallet(self):
        '''
        initialize self.hierarchy
        it is called when user creates the object with a mnemonic
        '''
        account = 0
        while True:
            free_address = self.init_addresses(account)
            if free_address == 0:
                self.free_account = account
                print("\nWallet Initialized")
                self.info()
                return
            account += 1

    def init_addresses(self, account_num):
        '''
        find first address under account that has no transaction history
        iterate through 20 addresses from last one with tx history (BIP 44)
        this constant is set to 3 in this instance to save time

        returns free address under this account number
        '''

        print("Initializing addresses for account : %i ..." % account_num)
        addr_count = 0
        last_free = 0
        internal = {}
        external = {}
        flag = True

        while True:
            external[addr_count] = self.get_address(account_num, 0, addr_count)
            internal[addr_count] = self.get_address(account_num, 1, addr_count)
            tx_history = history(external[addr_count])

            if len(tx_history) == 0 and flag:
                last_free = addr_count
                flag = False

            if len(tx_history) != 0:
                flag = True

            if (addr_count - last_free) == ADDRESS_CAP and not flag:
                if last_free != 0:
                    self.hierarchy[account_num] = {
                        k: external[k]
                        for k in range(0, last_free)
                    }
                    self.change_hierarchy[account_num] = {
                        k: internal[k]
                        for k in range(0, last_free)
                    }
                return last_free

            addr_count += 1

    def info(self):
        pprint(self.hierarchy)
Exemplo n.º 12
0
def txn_generator(bc, host='registrar'):
    """Creates transactions and broadcasts them."""
    headers = {'content-type': 'application/json'}
    registrar_input_url = 'http://{}:58335/jsonrpc'.format(host)
    registrar_txn_url = 'http://{}:58336/jsonrpc'.format(host)
    wallet_privkeys = [
        'cPZa7oEsR6GBuyrUH1s64meMZB31nxxPbhCVc4HZot55kgFGhWUg',
        'cQ8RV14JvX8X9sNg2VAp98tGT1kG4BaG1NBy75T3jNeX8YYGAWUf',
        'cQ6PpZDZQmffjo9X1PAriUCX7asMog8kM8t8tBf1WoqLwomkvkh9',
        'cV88fZveSBQjcGZkfnDbY8akcBPEM3L1CCLehb8D4Kc8dHNLmzTG'
    ]

    testnet = Bitcoin(testnet=True)
    prop_tx_generated = False
    while True:

        # generate 20 property genesis transactions first, then once that's done
        # randomly choose between genesis or existing property transaction
        if bc.chain_length < 10 or randint(0, 100) < 80:
            txn_type = 'propGen'
            btc_txn_hash = None

            input_list = [choice(list(DATABASE_WALLETS.keys()))]

            # generate list of random number of outputs
            output_list = generate_random_outputs()

        else:
            txn_type = 'propTx'
            candidate_input = bc.get_random_unspent()
            payload = candidate_input.to_dict()
            return_data = requests.post(registrar_input_url,
                                        data=json.dumps(payload),
                                        headers=headers).json()
            if return_data['Status'] != 'Unspent':
                print('Attempted to create propTx with invalid input.')
                continue
            input_list = [candidate_input]

            # generate random number of outputs
            property_addr = candidate_input.property_addr
            input_value = candidate_input.value
            output_list = generate_random_outputs(property_addr, input_value)

            payor_privkey = choice(wallet_privkeys)
            recip_privkey = payor_privkey
            while payor_privkey == recip_privkey:
                recip_privkey = choice(wallet_privkeys)

            recip_address = testnet.privtoaddr(recip_privkey)
            tx_dump = testnet.send(payor_privkey, recip_address, 1, fee=400000)
            try:
                if tx_dump['status'] == 'success':
                    tx_data = tx_dump['data']
                    btc_txn_hash = tx_data['txid']
                else:
                    continue
            except TypeError:
                continue

        # finish creating transaction
        new_transaction = Transaction(input_list, output_list, txn_type,
                                      btc_txn_hash)

        if txn_type == 'propTx':
            print('Submitted transaction to Testnet. Hash:')
            print(btc_txn_hash)
            print('Transaction hash (own chain):')
            print(new_transaction.transaction_hash)
            prop_tx_generated = True

        # send transaction to registrar
        payload = new_transaction.to_dict()
        requests.post(registrar_txn_url,
                      data=json.dumps(payload),
                      headers=headers)

        # pause to restrict growth of transaction pool
        if prop_tx_generated:
            delay = randint(240, 360)
        else:
            if bc.chain_length < 10:
                delay = randint(3, 5)
            else:
                delay = randint(20, 40)
        time.sleep(delay)
Exemplo n.º 13
0
def get_utxos(addr):
    testnet = Bitcoin(testnet=True)
    utxos = testnet.unspent(addr)
    return utxos