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)
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)
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 __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)
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
'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' }, {
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
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)
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)
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)
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)
def get_utxos(addr): testnet = Bitcoin(testnet=True) utxos = testnet.unspent(addr) return utxos