def test_sweep(self): cmds = MocCommands() cmds.wallet.add_address_transaction(120000) cmds.wallet.add_address_transaction(120000) unspent = [] for tx in cmds.wallet.transactions.values(): for output in tx._outputs: unspent.append((tx.hash(), output)) cmds.network = MocNetwork({ 'blockchain.address.listunspent': lambda _: [{ 'tx_hash': tx_hash, 'tx_pos': 1, 'address': address, 'coinbase': False, 'height': 2, 'is_claim': False, 'is_support': False, 'is_update': False, 'prevout_hash': 'df303881e9014cce89c7acf55b124372e22979284baa99bb9fa178a9d35c97cb', 'prevout_n': 0, 'value': amount } for (tx_hash, (type, address, amount)) in unspent] }) destination = cmds.wallet.create_new_address() result = cmds.sweep(self.private_key, destination) self.assertEqual(True, result['success']) sent = Transaction(result['tx']) sent.deserialize() self.assertEqual(len(sent._outputs), 1) self.assertEqual(sent._outputs[0][2], 230000)
def _get_decoded_tx(self, raw_tx): tx = Transaction(raw_tx) decoded_tx = {} decoded_tx['vout'] = [] for output in tx.outputs(): out = {} out['value'] = Decimal(output[2]) / Decimal(COIN) decoded_tx['vout'].append(out) return decoded_tx
def make_tx(self, coins, outputs, change_addrs, fee_estimator, dust_threshold, abandon_txid=None): '''Select unspent coins to spend to pay outputs. If the change is greater than dust_threshold (after adding the change output to the transaction) it is kept, otherwise none is sent and it is added to the transaction fee.''' # Deterministic randomness from coins utxos = [c['prevout_hash'] + str(c['prevout_n']) for c in coins] self.p = PRNG(''.join(sorted(utxos))) # Copy the ouputs so when adding change we don't modify "outputs" tx = Transaction.from_io([], outputs[:]) # Size of the transaction with no inputs and no change base_size = tx.estimated_size() spent_amount = tx.output_value() claim_coin = None if abandon_txid is not None: claim_coins = [coin for coin in coins if coin['is_claim']] assert len(claim_coins) >= 1 claim_coin = claim_coins[0] spent_amount -= claim_coin['value'] coins = [coin for coin in coins if not coin['is_claim']] def sufficient_funds(buckets): '''Given a list of buckets, return True if it has enough value to pay for the transaction''' total_input = sum(bucket.value for bucket in buckets) total_size = sum(bucket.size for bucket in buckets) + base_size return total_input >= spent_amount + fee_estimator(total_size) # Collect the coins into buckets, choose a subset of the buckets buckets = self.bucketize_coins(coins) buckets = self.choose_buckets(buckets, sufficient_funds, self.penalty_func(tx)) if claim_coin is not None: tx.add_inputs([claim_coin]) tx.add_inputs([coin for b in buckets for coin in b.coins]) tx_size = base_size + sum(bucket.size for bucket in buckets) # This takes a count of change outputs and returns a tx fee; # each pay-to-bitcoin-address output serializes as 34 bytes def fee(count): return fee_estimator(tx_size + count * 34) change = self.change_outputs(tx, change_addrs, fee, dust_threshold) tx.add_outputs(change) log.debug("using %i inputs", len(tx.inputs())) log.info("using buckets: %s", [bucket.desc for bucket in buckets]) return tx
def _decode(raw_tx): tx = Transaction(raw_tx).deserialize() decoded_tx = {} for txkey in tx.keys(): if isinstance(tx[txkey], list): decoded_tx[txkey] = [] for i in tx[txkey]: tmp = {} for k in i.keys(): if isinstance(i[k], Decimal): tmp[k] = float(i[k] / 1e8) else: tmp[k] = i[k] decoded_tx[txkey].append(tmp) else: decoded_tx[txkey] = tx[txkey] return decoded_tx
def get_input_tx(self, tx_hash): # First look up an input transaction in the wallet where it # will likely be. If co-signing a transaction it may not have # all the input txs, in which case we ask the network. tx = self.transactions.get(tx_hash) if not tx: request = ('blockchain.transaction.get', [tx_hash]) # FIXME: what if offline? tx = Transaction(self.network.synchronous_get(request)) return tx
def tx_response(self, response): params, result = self.parse_response(response) if not params: return tx_hash, tx_height = params assert tx_hash == hash_encode(Hash(result.decode('hex'))) tx = Transaction(result) try: tx.deserialize() except Exception: log.info("cannot deserialize transaction, skipping: %s", tx_hash) return self.wallet.receive_tx_callback(tx_hash, tx, tx_height) self.requested_tx.remove((tx_hash, tx_height)) log.info("received tx %s height: %d bytes: %d", tx_hash, tx_height, len(tx.raw)) # callbacks self.network.trigger_callback('new_transaction', tx) if not self.requested_tx: self.network.trigger_callback('updated')
def tx_response(response): params, result = self.parse_response(response) if not params: log.warning("failed to get %s", txid) self.requested_tx.remove((txid, height)) return tx_hash, tx_height = params assert tx_hash == hash_encode(Hash(result.decode('hex'))) tx = Transaction(result) try: tx.deserialize() except Exception: log.info("cannot deserialize transaction, skipping: %s", tx_hash) return self.wallet.receive_tx_callback(tx_hash, tx, tx_height) self.requested_tx.remove((tx_hash, tx_height)) log.info("received tx %s height: %d bytes: %d", tx_hash, tx_height, len(tx.raw)) # callbacks self.network.trigger_callback('new_transaction', tx) if not self.requested_tx: self.network.trigger_callback('updated')
def make_Bucket(desc, coins): size = sum(Transaction.estimated_input_size(coin) for coin in coins) value = sum(coin['value'] for coin in coins) return Bucket(desc, size, value, coins)
def pubkeys_to_address(self, pubkeys): redeem_script = Transaction.multisig_script(sorted(pubkeys), self.m) address = hash_160_bytes_to_address(hash_160(redeem_script.decode('hex')), 5) return address
def redeem_script(self, for_change, n): pubkeys = self.get_pubkeys(for_change, n) return Transaction.multisig_script(sorted(pubkeys), self.m)
def make_Bucket(desc, coins): size = sum( Transaction.estimated_input_size(coin) for coin in coins) value = sum(coin['value'] for coin in coins) return Bucket(desc, size, value, coins)
def main(self): add_menu() welcome = 'Use the menu to scan a transaction.' droid.fullShow(qr_layout(welcome)) while True: event = droid.eventWait().result if not event: continue elif event["name"] == "key": if event["data"]["key"] == '4': if self.qr_data: self.show_qr(None) self.show_title(welcome) else: break elif event["name"] == "seed": password = self.get_password() if password is False: modal_dialog('Error','incorrect password') continue seed = wallet.get_mnemonic(password) modal_dialog('Your seed is', seed) elif event["name"] == "password": self.change_password_dialog() elif event["name"] == "xpub": mpk = wallet.get_master_public_key() self.show_qr(mpk) self.show_title('master public key') droid.setClipboard(mpk) droid.makeToast("Master public key copied to clipboard") elif event["name"] == "scan": r = droid.scanBarcode() r = r.result if not r: continue data = r['extras']['SCAN_RESULT'] data = base_decode(data.encode('utf8'), None, base=43) data = ''.join(chr(ord(b)) for b in data).encode('hex') tx = Transaction(data) #except: # modal_dialog('Error', 'Cannot parse transaction') # continue if not wallet.can_sign(tx): modal_dialog('Error', 'Cannot sign this transaction') continue lines = map(lambda x: x[0] + u'\t\t' + format_satoshis(x[1]) if x[1] else x[0], tx.get_outputs()) if not modal_question('Sign?', '\n'.join(lines)): continue password = self.get_password() if password is False: modal_dialog('Error','incorrect password') continue droid.dialogCreateSpinnerProgress("Signing") droid.dialogShow() wallet.sign_transaction(tx, password) droid.dialogDismiss() data = base_encode(str(tx).decode('hex'), base=43) self.show_qr(data) self.show_title('Signed Transaction') droid.makeToast("Bye!")
def pubkeys_to_address(self, pubkeys): redeem_script = Transaction.multisig_script(sorted(pubkeys), self.m) address = hash_160_to_bc_address(hash_160(redeem_script.decode('hex')), 5) return address