Ejemplo n.º 1
1
	def recv_txio(self, nick, utxo_list, cj_pub, change_addr):	
		if nick not in self.nonrespondants:
			debug('nick(' + nick + ') not in nonrespondants ' + str(self.nonrespondants))
			return
		self.utxos[nick] = utxo_list
		order = self.db.execute('SELECT ordertype, txfee, cjfee FROM '
			'orderbook WHERE oid=? AND counterparty=?',
			(self.active_orders[nick], nick)).fetchone()
		utxo_data = common.bc_interface.query_utxo_set(self.utxos[nick])
		if None in utxo_data:
			common.debug('ERROR outputs unconfirmed or already spent. utxo_data='
				+ pprint.pformat(utxo_data))
			raise RuntimeError('killing taker, TODO handle this error')
		total_input = sum([d['value'] for d in utxo_data])
		real_cjfee = calc_cj_fee(order['ordertype'], order['cjfee'], self.cj_amount)
		self.outputs.append({'address': change_addr, 'value':
			total_input - self.cj_amount - order['txfee'] + real_cjfee})
		print 'fee breakdown for %s totalin=%d cjamount=%d txfee=%d realcjfee=%d' % (nick,
			total_input, self.cj_amount, order['txfee'], real_cjfee)
		cj_addr = btc.pubtoaddr(cj_pub, get_addr_vbyte())
		self.outputs.append({'address': cj_addr, 'value': self.cj_amount})
		self.cjfee_total += real_cjfee
		self.nonrespondants.remove(nick)
		if len(self.nonrespondants) > 0:
			debug('nonrespondants = ' + str(self.nonrespondants))
			return
		debug('got all parts, enough to build a tx cjfeetotal=' + str(self.cjfee_total))

		my_total_in = 0
		for u, va in self.input_utxos.iteritems():
			my_total_in += va['value']
		#my_total_in = sum([va['value'] for u, va in self.input_utxos.iteritems()])

		my_change_value = my_total_in - self.cj_amount - self.cjfee_total - self.my_txfee
		print 'fee breakdown for me totalin=%d txfee=%d cjfee_total=%d => changevalue=%d' % (my_total_in, 
			self.my_txfee, self.cjfee_total, my_change_value)
		if self.my_change_addr == None:
			if my_change_value != 0 and abs(my_change_value) != 1:
				#seems you wont always get exactly zero because of integer rounding
				# so 1 satoshi extra or fewer being spent as miner fees is acceptable
				print 'WARNING CHANGE NOT BEING USED\nCHANGEVALUE = ' + str(my_change_value)
		else:
			self.outputs.append({'address': self.my_change_addr, 'value': my_change_value})
		utxo_tx = [dict([('output', u)]) for u in sum(self.utxos.values(), [])]
		random.shuffle(utxo_tx)
		random.shuffle(self.outputs)
		tx = btc.mktx(utxo_tx, self.outputs)	
		debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx)))
		self.msgchan.send_tx(self.active_orders.keys(), tx)

		#now sign it ourselves here
		for index, ins in enumerate(btc.deserialize(tx)['ins']):
			utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
			if utxo not in self.input_utxos.keys():
				continue
			addr = self.input_utxos[utxo]['address']
			tx = btc.sign(tx, index, self.wallet.get_key_from_addr(addr))
		self.latest_tx = btc.deserialize(tx)
Ejemplo n.º 2
0
    def test_native_P2WSH_SIGHASH_SINGLE_ANYONECANPAY(self):
        tx = TEST_CASES[2]
        deserialized = deserialize(tx['unsigned'])
        serialized = serialize(deserialized)
        self.assertEqual(serialized, tx['unsigned'])
        self.assertEqual(deserialized['locktime'], tx['locktime'])
        ins = self.get_pybtc_vins(tx)
        outs = self.get_pybtc_outs(tx)
        generated_tx = mktx(ins, outs)
        stripped_tx = strip_witness_data(generated_tx)
        self.assertEqual(stripped_tx, serialized)
        priv0 = self.append_compressed_flag_to_privkey(tx['ins'][1]['privkey'])
        partially_signed = segwit_sign(generated_tx,
                                       0,
                                       priv0,
                                       int(0.16777215 * 10**8),
                                       hashcode=SIGHASH_SINGLE|SIGHASH_ANYONECANPAY,
                                       script=tx['ins'][0]['txinwitness'][1])

        signed = segwit_sign(partially_signed,
                             1,
                             priv0,
                             int(0.16777215 * 10 ** 8),
                             hashcode=SIGHASH_SINGLE|SIGHASH_ANYONECANPAY,
                             script=tx['ins'][1]['txinwitness'][1],
                             separator_index=tx['ins'][1]['separator'])
        self.assertEqual(signed, tx['signed'])
        print('[Native P2WSH] SIGHASH_SINGLE OK')
Ejemplo n.º 3
0
def test_spend_p2sh_utxos(setup_tx_creation):
    #make a multisig address from 3 privs
    privs = [chr(x) * 32 + '\x01' for x in range(1, 4)]
    pubs = [btc.privkey_to_pubkey(binascii.hexlify(priv)) for priv in privs]
    script = btc.mk_multisig_script(pubs, 2)
    msig_addr = btc.scriptaddr(script, magicbyte=196)
    #pay into it
    wallet = make_wallets(1, [[2, 0, 0, 0, 1]], 3)[0]['wallet']
    jm_single().bc_interface.sync_wallet(wallet)
    amount = 350000000
    ins_full = wallet.select_utxos(0, amount)
    txid = make_sign_and_push(ins_full, wallet, amount, output_addr=msig_addr)
    assert txid
    #wait for mining
    time.sleep(4)
    #spend out; the input can be constructed from the txid of previous
    msig_in = txid + ":0"
    ins = [msig_in]
    #random output address and change addr
    output_addr = wallet.get_new_addr(1, 1)
    amount2 = amount - 50000
    outs = [{'value': amount2, 'address': output_addr}]
    tx = btc.mktx(ins, outs)
    sigs = []
    for priv in privs[:2]:
        sigs.append(btc.multisign(tx, 0, script, binascii.hexlify(priv)))
    tx = btc.apply_multisignatures(tx, 0, script, sigs)
    txid = jm_single().bc_interface.pushtx(tx)
    assert txid
Ejemplo n.º 4
0
def make_sign_and_push(ins_full,
                       wallet,
                       amount,
                       output_addr=None,
                       change_addr=None,
                       hashcode=btc.SIGHASH_ALL):
    total = sum(x['value'] for x in ins_full.values())
    ins = ins_full.keys()
    #random output address and change addr
    output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_addr
    change_addr = wallet.get_new_addr(1, 0) if not change_addr else change_addr
    outs = [{'value': amount,
             'address': output_addr}, {'value': total - amount - 100000,
                                       'address': change_addr}]

    tx = btc.mktx(ins, outs)
    de_tx = btc.deserialize(tx)
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        addr = ins_full[utxo]['address']
        priv = wallet.get_key_from_addr(addr)
        if index % 2:
            priv = binascii.unhexlify(priv)
        tx = btc.sign(tx, index, priv, hashcode=hashcode)
    #pushtx returns False on any error
    print btc.deserialize(tx)
    push_succeed = jm_single().bc_interface.pushtx(tx)
    if push_succeed:
        return btc.txhash(tx)
    else:
        return False
Ejemplo n.º 5
0
def make_tx_add_notify():
    wallet_dict = make_wallets(1, [[1, 0, 0, 0, 0]], mean_amt=4, sdev_amt=0)[0]
    amount = 250000000
    txfee = 10000
    wallet = wallet_dict['wallet']
    sync_wallet(wallet)
    inputs = wallet.select_utxos(0, amount)
    ins = inputs.keys()
    input_value = sum([i['value'] for i in inputs.values()])
    output_addr = wallet.get_new_addr(1, 0)
    change_addr = wallet.get_new_addr(0, 1)
    outs = [{'value': amount, 'address': output_addr},
            {'value': input_value - amount - txfee, 'address': change_addr}]
    tx = btc.mktx(ins, outs)
    de_tx = btc.deserialize(tx)
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        addr = inputs[utxo]['address']
        priv = wallet.get_key_from_addr(addr)
        tx = btc.sign(tx, index, priv)

    unconfirm_called[0] = confirm_called[0] = False
    timeout_unconfirm_called[0] = timeout_confirm_called[0] = False
    jm_single().bc_interface.add_tx_notify(
        btc.deserialize(tx), unconfirm_callback,
        confirm_callback, output_addr, timeout_callback)
    return tx
Ejemplo n.º 6
0
def make_sign_and_push(
    ins_full, wallet, amount, output_addr=None, change_addr=None, hashcode=btc.SIGHASH_ALL, estimate_fee=False
):
    """Utility function for easily building transactions
    from wallets
    """
    total = sum(x["value"] for x in ins_full.values())
    ins = ins_full.keys()
    # random output address and change addr
    output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_addr
    change_addr = wallet.get_new_addr(1, 0) if not change_addr else change_addr
    fee_est = estimate_tx_fee(len(ins), 2) if estimate_fee else 10000
    outs = [{"value": amount, "address": output_addr}, {"value": total - amount - fee_est, "address": change_addr}]

    tx = btc.mktx(ins, outs)
    de_tx = btc.deserialize(tx)
    for index, ins in enumerate(de_tx["ins"]):
        utxo = ins["outpoint"]["hash"] + ":" + str(ins["outpoint"]["index"])
        addr = ins_full[utxo]["address"]
        priv = wallet.get_key_from_addr(addr)
        if index % 2:
            priv = binascii.unhexlify(priv)
        tx = btc.sign(tx, index, priv, hashcode=hashcode)
    # pushtx returns False on any error
    print btc.deserialize(tx)
    push_succeed = jm_single().bc_interface.pushtx(tx)
    if push_succeed:
        return btc.txhash(tx)
    else:
        return False
Ejemplo n.º 7
0
def send_whole_wallet(fromprivkey, toaddr):
	transaction_fee = 20000 # .0002 BTC
	fromaddress = bitcoin.privtoaddr(fromprivkey)
	balance = sum(transaction['value'] for transaction in bitcoin.unspent(fromaddress))
	assert balance >= transaction_fee
	tx = bitcoin.mktx(bitcoin.history(fromaddress), [{'value': balance - transaction_fee, 'address': toaddr}])
	signed_tx = bitcoin.sign(tx, 0, fromprivkey)
	bitcoin.pushtx(signed_tx)
Ejemplo n.º 8
0
 def test_native_P2WSH_SIGHASH_SINGLE(self):
     tx = TEST_CASES[1]
     deserialized = deserialize(tx['unsigned'])
     serialized = serialize(deserialized)
     self.assertEqual(serialized, tx['unsigned'])
     self.assertEqual(deserialized['locktime'], tx['locktime'])
     ins = self.get_pybtc_vins(tx)
     outs = self.get_pybtc_outs(tx)
     generated_tx = mktx(ins, outs)
     stripped_tx = strip_witness_data(generated_tx)
     self.assertEqual(stripped_tx, serialized)
     partially_signed = p2pk_sign(stripped_tx,
                                  0,
                                  self.append_compressed_flag_to_privkey(tx['ins'][0]['privkey']),
                                  hashcode=SIGHASH_ALL)
     priv0 = self.append_compressed_flag_to_privkey(tx['ins'][1]['privkeys'][0])
     priv1 = self.append_compressed_flag_to_privkey(tx['ins'][1]['privkeys'][1])
     pub0 = privtopub(priv0)
     pub1 = privtopub(priv1)
     REDEEM_SCRIPT_STRUCTURE = {
         'keys': [
             pub0,
             pub1
         ],
         'schema': [
             {
                 'reqs': 1,
                 'keys': [0],
             },
             {
                 'reqs': 1,
                 'keys': [1],
             }
         ]
     }
     witness_script = mk_OPCS_multisig_script(REDEEM_SCRIPT_STRUCTURE)
     sign1 = segwit_multisign(partially_signed,
                              1,
                              witness_script,
                              priv0,
                              49 * 10**8,
                              hashcode=SIGHASH_SINGLE)
     sign2 = segwit_multisign(partially_signed,
                              1,
                              witness_script,
                              priv1,
                              49 * 10 ** 8,
                              hashcode=SIGHASH_SINGLE,
                              separator_index=1)
     signed = apply_segwit_multisignatures(partially_signed, 1, witness_script, [sign2, sign1], dummy=False)
     self.assertEqual(signed, tx['signed'])
     print('[Native P2WSH] SIGHASH_SINGLE OK')
Ejemplo n.º 9
0
            def cb(ec, history, order):

                self.log.debug('Callback for history %s', history)

                private_key = self.get_signing_key(seller['seller_contract_id'])

                if ec is not None:
                    self.log.error("Error fetching history: %s", ec)
                    # TODO: Send error message to GUI
                    return

                # Create unsigned transaction
                unspent = [row[:4] for row in history if row[4] is None]

                # Send all unspent outputs (everything in the address) minus
                # the fee
                total_amount = 0
                inputs = []
                for row in unspent:
                    assert len(row) == 4
                    inputs.append(
                        str(row[0].encode('hex')) + ":" + str(row[1])
                    )
                    value = row[3]
                    total_amount += value

                # Constrain fee so we don't get negative amount to send
                fee = min(total_amount, 10000)
                send_amount = total_amount - fee

                payment_output = order['payment_address']
                tx = mktx(
                    inputs, [str(payment_output) + ":" + str(send_amount)]
                )

                # Sign all the inputs
                signatures = []
                for x in range(0, len(inputs)):
                    ms = multisign(tx, x, script, private_key)
                    signatures.append(ms)

                self.log.debug('Merchant TX Signatures: %s', signatures)

                order['merchant_tx'] = tx
                order['merchant_script'] = script
                order['buyer_order_id'] = buyer['buyer_order_id']
                order['merchant_sigs'] = signatures

                self.transport.send(order, bid_data_json['Buyer']['buyer_GUID'])
Ejemplo n.º 10
0
def change_stage():
    global stage, tx, inputs, outputs, confirmed, last_update, stage_time, confirmed

    if stage == "outputs" and len(outputs) > 0:
        stage = "inputs"
    elif stage == "inputs":
        if len(outputs) == len(inputs) and len(outputs) > 0:
            tx = mktx(inputs, outputs)
            print tx
            stage = "signatures"
        else:
            # If a different number of inputs and outputs, on just one of each one, is received, the process is restarted
            reset_arrays()
            stage = "outputs"
    elif stage == "signatures":
        if len(outputs) == len(inputs) == len(signatures):
            tx = insert_signatures(tx)
            print "Final tx: " + tx
            stage = "confirm"
        else:
            # If the three arrays are not of the same size, the process is restarted.
            reset_arrays()
            stage = "outputs"
    elif stage == "confirm":
        if confirmed:
            result = blockr_pushtx(tx, 'testnet')
            #result = local_push(tx)

            # End of the mixing, starting the process again
            reset_arrays()
            confirmed = False
            stage = "outputs"
        else:
            # Wait for the inputs to be confirmed
            # Check if there are utxo unconfirmed yet
            if len(unconfirmed) is not 0:
                for tx in unconfirmed:
                    if get_tx_info(tx)['confirmations'] >= 6:
                        unconfirmed.remove(tx)

            if len(unconfirmed) is 0:
                confirmed = True

    last_update = time()
    t = threading.Timer(stage_time, change_stage)
    t.start()
    print " * Current stage: " + stage
Ejemplo n.º 11
0
    def build_transaction(self, inputs, outputs):
        """
        Thin wrapper around ``bitcoin.mktx(inputs, outputs)``

        Args:
            inputs (dict): inputs in the form of
                ``{'output': 'txid:vout', 'value': amount in satoshi}``
            outputs (dict): outputs in the form of
                ``{'address': to_address, 'value': amount in satoshi}``
        Returns:
            transaction
        """
        # prepare inputs and outputs for bitcoin
        inputs = [{'output': '{}:{}'.format(input['txid'], input['vout']),
                   'value': input['amount']} for input in inputs]
        tx = bitcoin.mktx(inputs, outputs)
        return tx
Ejemplo n.º 12
0
            def cb(ec, history, order):
                settings = self.market.get_settings()
                private_key = settings.get('privkey')

                if ec is not None:
                    self.log.error("Error fetching history: %s", ec)
                    # TODO: Send error message to GUI
                    return

                # Create unsigned transaction
                unspent = [row[:4] for row in history if row[4] is None]

                # Send all unspent outputs (everything in the address) minus
                # the fee
                total_amount = 0
                inputs = []
                for row in unspent:
                    assert len(row) == 4
                    inputs.append(
                        str(row[0].encode('hex')) + ":" + str(row[1])
                    )
                    value = row[3]
                    total_amount += value

                # Constrain fee so we don't get negative amount to send
                fee = min(total_amount, 10000)
                send_amount = total_amount - fee

                payment_output = order['payment_address']
                tx = mktx(
                    inputs, [str(payment_output) + ":" + str(send_amount)]
                )

                signatures = []
                for x in range(0, len(inputs)):
                    ms = multisign(tx, x, script, private_key)
                    signatures.append(ms)

                print signatures

                self.market.release_funds_to_merchant(
                    buyer['buyer_order_id'],
                    tx, script, signatures,
                    order.get('merchant')
                )
Ejemplo n.º 13
0
def donation_address(cjtx):
	reusable_donation_pubkey = '02be838257fbfddabaea03afbb9f16e8529dfe2de921260a5c46036d97b5eacf2a'

	donation_utxo_data = cjtx.input_utxos.iteritems().next()
	global donation_utxo
	donation_utxo = donation_utxo_data[0]
	privkey = cjtx.wallet.get_key_from_addr(donation_utxo_data[1]['address'])

	tx = btc.mktx(cjtx.utxo_tx, cjtx.outputs) #tx without our inputs and outputs
	#address = privtoaddr(privkey)
	#signing_tx = signature_form(tx, 0, mk_pubkey_script(address), SIGHASH_ALL)
	msghash = btc.bin_txhash(tx, btc.SIGHASH_ALL)
	#generate unpredictable k
	global sign_k
	sign_k = btc.deterministic_generate_k(msghash, privkey)
	c = btc.sha256(btc.multiply(reusable_donation_pubkey, sign_k))
	sender_pubkey = btc.add_pubkeys(reusable_donation_pubkey, btc.multiply(btc.G, c))
	sender_address = btc.pubtoaddr(sender_pubkey, get_p2pk_vbyte())
	debug('sending coins to ' + sender_address)
	return sender_address
Ejemplo n.º 14
0
 def test_native_P2WPKH_SIGHASH_ALL(self):
     tx = TEST_CASES[0]
     deserialized = deserialize(tx['unsigned'])
     serialized = serialize(deserialized)
     self.assertEqual(serialized, tx['unsigned'])
     self.assertEqual(deserialized['locktime'], tx['locktime'])
     ins = self.get_pybtc_vins(tx)
     outs = self.get_pybtc_outs(tx)
     generated_tx = mktx(ins, outs, locktime=tx['locktime'])
     stripped_tx = strip_witness_data(generated_tx)
     self.assertEqual(stripped_tx, serialized)
     partially_signed = p2pk_sign(stripped_tx,
                                  0,
                                  self.append_compressed_flag_to_privkey(tx['ins'][0]['privkey']))
     signed = segwit_sign(partially_signed,
                          1,
                          self.append_compressed_flag_to_privkey(tx['ins'][1]['privkey']),
                          tx['ins'][1]['amount'] * 10**8)
     self.assertEqual(signed, tx['signed'])
     print('[Native P2WPKH] SIGHASH_ALL OK')
    def sendCustomTransaction(self, privkeys, inputs, outputs, fee=0):
        success = False
        totalInputValue = 0
        UTXOs = []
        for tx_input in inputs:
            if 'spend' not in tx_input:
                totalInputValue += tx_input['value']
                UTXOs.append(tx_input)

        totalOutputValue = 0
        for tx_output in outputs:
            totalOutputValue += tx_output['value']

        diff = totalInputValue - totalOutputValue
        if fee != diff:
            pprint("Warning: Fee incorrect! aborting transaction")
        else:
            allKeysPresent = True
            allInputsConfirmed = True
            for tx_input in UTXOs:
                if tx_input['address'] not in privkeys:
                    print 'not found:', tx_input['address']
                    allKeysPresent = False

                if tx_input['block_height'] == None:
                    allInputsConfirmed = False

            if allKeysPresent == True and allInputsConfirmed == True:
                tx = bitcoin.mktx(UTXOs, outputs)
                for i in range(0, len(UTXOs)):
                    tx = bitcoin.sign(tx, i, str(privkeys[UTXOs[i]['address']]))

                try:
                    bitcoin.pushtx(tx)
                    success = True
                except:
                    e = sys.exc_info()
                    print e
                    success = False

        return success
Ejemplo n.º 16
0
    def get_hex(self, signed=True):
        """
        Given all the data the user has given so far, make the hex using pybitcointools
        """
        total_ins_satoshi = self.total_input_satoshis()
        if total_ins_satoshi == 0:
            raise ValueError("Can't make transaction, there are zero inputs")

        # Note: there can be zero outs (sweep or coalesc transactions)
        total_outs_satoshi = sum([x['value'] for x in self.outs])

        if not self.fee_satoshi:
            self.fee() # use default of $0.02

        change_satoshi = total_ins_satoshi - (total_outs_satoshi + self.fee_satoshi)

        if change_satoshi < 0:
            raise ValueError(
                "Input amount (%s) must be more than all output amounts (%s) plus fees (%s). You need more %s."
                % (total_ins_satoshi, total_outs_satoshi, self.fee_satoshi, self.crypto.upper())
            )

        ins = [x['input'] for x in self.ins]

        if change_satoshi > 0:
            if self.verbose:
                print("Adding change address of %s satoshis to %s" % (change_satoshi, self.change_address))
            change = [{'value': change_satoshi, 'address': self.change_address}]
        else:
            change = [] # no change ?!
            if self.verbose: print("Inputs == Outputs, no change address needed.")

        tx = mktx(ins, self.outs + change)

        if signed:
            for i, input_data in enumerate(self.ins):
                if not input_data['private_key']:
                    raise Exception("Can't sign transaction, missing private key for input %s" % i)
                tx = sign(tx, i, input_data['private_key'])

        return tx
Ejemplo n.º 17
0
    def sendCustomTransaction(self, privkeys, inputs, outputs, fee=0):

        success = False
        totalInputValue = 0
        UTXOs = []
        for tx_input in inputs:
            if 'spend' not in tx_input:
                totalInputValue += tx_input['value']
                UTXOs.append(tx_input)

        totalOutputValue = 0
        for tx_output in outputs:
            totalOutputValue += tx_output['value']

        diff = totalInputValue - totalOutputValue
        if fee != diff:
            self.response.write(
                "<br>Warning: Fee incorrect! aborting transaction")
            logging.error("Warning: Fee incorrect! aborting transaction")
        else:
            allKeysPresent = True
            allInputsConfirmed = True
            for tx_input in UTXOs:
                if tx_input['address'] not in privkeys:
                    allKeysPresent = False

                if tx_input['block_height'] == None:
                    allInputsConfirmed = False

            if allKeysPresent == True and allInputsConfirmed == True:
                tx = bitcoin.mktx(UTXOs, outputs)
                for i in range(0, len(UTXOs)):
                    tx = bitcoin.sign(tx, i,
                                      str(privkeys[UTXOs[i]['address']]))

                bitcoin.pushtx(tx)
                success = True

        return success
Ejemplo n.º 18
0
def donation_address(cjtx):
    from bitcoin.main import multiply, G, deterministic_generate_k, add_pubkeys
    reusable_donation_pubkey = ('02be838257fbfddabaea03afbb9f16e852'
                                '9dfe2de921260a5c46036d97b5eacf2a')

    donation_utxo_data = cjtx.input_utxos.iteritems().next()
    global donation_utxo
    donation_utxo = donation_utxo_data[0]
    privkey = cjtx.wallet.get_key_from_addr(donation_utxo_data[1]['address'])
    # tx without our inputs and outputs
    tx = btc.mktx(cjtx.utxo_tx, cjtx.outputs)
    msghash = btc.bin_txhash(tx, btc.SIGHASH_ALL)
    # generate unpredictable k
    global sign_k
    sign_k = deterministic_generate_k(msghash, privkey)
    c = btc.sha256(multiply(reusable_donation_pubkey, sign_k))
    sender_pubkey = add_pubkeys(
            reusable_donation_pubkey, multiply(
                    G, c))
    sender_address = btc.pubtoaddr(sender_pubkey, get_p2pk_vbyte())
    log.debug('sending coins to ' + sender_address)
    return sender_address
Ejemplo n.º 19
0
 def test_mktx(self):
     address = 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7'
     inputs = [
         {
             'output': 'ff'*32 + ':0',
             'value': 100,
             'segregated': True
         }
     ]
     outputs = [
         {
             'value': 100,
             'address': address
         }
     ]
     tx = mktx(inputs, outputs)
     print(tx)
     deserialized = deserialize(tx)
     re_encoded_address = bech32encode(deserialized['outs'][0]['script'], prefix=BECH32_BITCOIN_TESTNET_PREFIX)
     assert re_encoded_address == address
     decoded = bech32decode(re_encoded_address)
     self.assertEqual(deserialized['outs'][0]['script'], decoded)
Ejemplo n.º 20
0
def main():
    scriptSig = getScriptPubKey(args.solution)
    addrP2SH = scriptaddr(scriptSig, 50)
    solution = b2a_hex(args.solution.encode()).decode('utf-8')

    ins, balance = getBalance(addrP2SH)
    len_inputs = len(ins)

    if balance > 0:
        # Output
        outs = [{'address': args.address, 'value': int(balance * COIN)}]

        # Make unsigned transaction
        tx = mktx(ins, outs)

        # Calculate fee
        size = len(a2b_hex(tx))
        fee = int((size / 1024) * 0.01 * COIN)

        # MAX FEE = 0.1 CHA
        fee = 1e7 if fee > 1e7 else fee

        # sign inputs + apply fee
        unpacked = deserialize(tx)

        for puzzle_input in unpacked['ins']:
            puzzle_input['script'] = getHexLen(solution) + solution
            puzzle_input['script'] += getHexLen(scriptSig) + scriptSig

        unpacked['outs'][0]['value'] -= fee

        tx = serialize(unpacked)

        if len(tx) > 0:
            txid = broadcast(tx)
            print('> Transaction broadcasted, %s' % tx)
    else:
        print('> No funds :C')
    def sendCustomTransaction(self, privkeys, inputs, outputs, fee=0):

        success = False
        totalInputValue = 0
        UTXOs = []
        for tx_input in inputs:
            if 'spend' not in tx_input:
                totalInputValue += tx_input['value']
                UTXOs.append(tx_input)

        totalOutputValue = 0
        for tx_output in outputs:
            totalOutputValue += tx_output['value']

        diff = totalInputValue - totalOutputValue
        if fee != diff:
            self.response.write("<br>Warning: Fee incorrect! aborting transaction")
            logging.error("Warning: Fee incorrect! aborting transaction")
        else:
            allKeysPresent = True
            allInputsConfirmed = True
            for tx_input in UTXOs:
                if tx_input['address'] not in privkeys:
                    allKeysPresent = False

                if tx_input['block_height'] == None:
                    allInputsConfirmed = False

            if allKeysPresent == True and allInputsConfirmed == True:
                tx = bitcoin.mktx(UTXOs, outputs)
                for i in range(0, len(UTXOs)):
                    tx = bitcoin.sign(tx, i, str(privkeys[UTXOs[i]['address']]))


                bitcoin.pushtx(tx)
                success = True

        return success
Ejemplo n.º 22
0
def send(args):
    if len(args.outputs) % 2 != 0:
        raise Exception("When sending, there must be an even number of arguments " +
                        "for the outputs (address, price)")
    unspents = bitcoin.BlockchainInfo.unspent_xpub(args.xpub)
    def btctosatoshi(vs):
        return int(float(vs)*100000000.0)
    fee = btctosatoshi(args.fee)
    if fee < 0:
        fee = int(0.0001*100000000) #todo do something to estimated fee...make it negative or something though... DONT
    outaddrval = [(args.outputs[2*i],btctosatoshi(args.outputs[2*i+1])) for i in range(len(args.outputs)//2)]
    outtotalval = sum([o[1] for o in outaddrval])
    changeindex = check_outputs_max_index(unspents,1)
    changeaddress = bitcoin.pubtoaddr(bitcoin.bip32_descend(args.xpub, 1, changeindex))

    unspents = bitcoin.select(unspents, outtotalval+fee)
    #unspents cull using outtotalval
    unspenttotalval = sum([u['value'] for u in unspents])
    changeamount = unspenttotalval - (outtotalval + abs(fee))

    if changeamount < 0:
        raise Exception("There is unlikely to be enough unspent outputs to cover the transaction and fees")
    out = {}
    outs = outaddrval
    if changeamount > 0:
        outs += [[changeaddress, changeamount]]
    #print(unspents)
    #print(outs)
    otx = [{'address':o[0], 'value':o[1]} for o in outs]
    tx = bitcoin.mktx(unspents, otx)

    #compute all the underlying addresses and pubkeys into a string hash
    #estimate the fee
    #build the transaction
    out['tx'] = tx
    out['keys'] = dict([(u['output'], u['xpub']) for u in unspents])
    #print(bitcoin.deserialize(tx))
    json.dump(out, sys.stdout)
Ejemplo n.º 23
0
            def cb(ec, history, order):

                settings = self.market.get_settings()
                private_key = settings.get('privkey')

                if ec is not None:
                    self.log.error("Error fetching history: %s", ec)
                    # TODO: Send error message to GUI
                    return

                # Create unsigned transaction
                unspent = [row[:4] for row in history if row[4] is None]

                # Send all unspent outputs (everything in the address) minus
                # the fee
                total_amount = 0
                inputs = []
                for row in unspent:
                    assert len(row) == 4, 'Obelisk returned a wonky row'
                    inputs.append("%s:%s" % (row[0].encode('hex'), row[1]))
                    value = row[3]
                    total_amount += value

                # Constrain fee so we don't get negative amount to send
                fee = min(total_amount, 10000)
                send_amount = total_amount - fee

                payment_output = order['payment_address']
                tx = mktx(inputs, ["%s:%s" % (payment_output, send_amount)])

                signatures = [
                    multisign(tx, x, script, private_key)
                    for x in range(len(inputs))
                ]

                self.market.release_funds_to_merchant(buyer['buyer_order_id'],
                                                      tx, script, signatures,
                                                      order.get('merchant'))
Ejemplo n.º 24
0
def sign(utxo, priv, destaddrs):
    """Sign a tx sending the amount amt, from utxo utxo,
    equally to each of addresses in list destaddrs,
    after fees; the purpose is to create a large
    number of utxos.
    """
    results = validate_utxo_data([(utxo, priv)], retrieve=True)
    if not results:
        return False
    assert results[0][0] == utxo
    amt = results[0][1]
    ins = [utxo]
    estfee = estimate_tx_fee(1, len(destaddrs))
    outs = []
    share = int((amt - estfee) / len(destaddrs))
    fee = amt - share * len(destaddrs)
    assert fee >= estfee
    log.info("Using fee: " + str(fee))
    for i, addr in enumerate(destaddrs):
        outs.append({'address': addr, 'value': share})
    unsigned_tx = btc.mktx(ins, outs)
    return btc.sign(unsigned_tx, 0,
                    btc.from_wif_privkey(priv, vbyte=get_p2pk_vbyte()))
Ejemplo n.º 25
0
def sign(utxo, priv, destaddrs):
    """Sign a tx sending the amount amt, from utxo utxo,
    equally to each of addresses in list destaddrs,
    after fees; the purpose is to create a large
    number of utxos.
    """
    results = validate_utxo_data([(utxo, priv)], retrieve=True)
    if not results:
        return False
    assert results[0][0] == utxo
    amt = results[0][1]
    ins = [utxo]
    estfee = estimate_tx_fee(1, len(destaddrs))
    outs = []
    share = int((amt - estfee) / len(destaddrs))
    fee = amt - share*len(destaddrs)
    assert fee >= estfee
    log.debug("Using fee: " + str(fee))
    for i, addr in enumerate(destaddrs):
        outs.append({'address': addr, 'value': share})
    unsigned_tx = btc.mktx(ins, outs)
    return btc.sign(unsigned_tx, 0, btc.from_wif_privkey(
        priv, vbyte=get_p2pk_vbyte()))
Ejemplo n.º 26
0
def make_sign_and_push(ins_full,
                       wallet,
                       amount,
                       output_addr=None,
                       change_addr=None,
                       hashcode=btc.SIGHASH_ALL,
                       estimate_fee=False):
    total = sum(x['value'] for x in ins_full.values())
    ins = ins_full.keys()
    #random output address and change addr
    output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_addr
    change_addr = wallet.get_new_addr(1, 0) if not change_addr else change_addr
    fee_est = estimate_tx_fee(len(ins), 2) if estimate_fee else 10000
    outs = [{
        'value': amount,
        'address': output_addr
    }, {
        'value': total - amount - fee_est,
        'address': change_addr
    }]

    tx = btc.mktx(ins, outs)
    de_tx = btc.deserialize(tx)
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        addr = ins_full[utxo]['address']
        priv = wallet.get_key_from_addr(addr)
        if index % 2:
            priv = binascii.unhexlify(priv)
        tx = btc.sign(tx, index, priv, hashcode=hashcode)
    #pushtx returns False on any error
    print btc.deserialize(tx)
    push_succeed = jm_single().bc_interface.pushtx(tx)
    if push_succeed:
        return btc.txhash(tx)
    else:
        return False
Ejemplo n.º 27
0
    def get_hex(self, signed=True):
        """
        Given all the data the user has given so far, make the hex using pybitcointools
        """
        logging.info('2')

        total_ins_satoshi = self.total_input_satoshis()
        logging.info('3')
        if total_ins_satoshi == 0:
            raise ValueError("Can't make transaction, there are zero inputs")
        logging.info('4')

        # Note: there can be zero outs (sweep or coalesc transactions)
        total_outs_satoshi = sum([x['value'] for x in self.outs])
        logging.info('5')

        if not self.fee_satoshi:
            self.fee()  # use default of $0.02
        logging.info('6')

        change_satoshi = total_ins_satoshi - (total_outs_satoshi +
                                              self.fee_satoshi)
        logging.info('7')

        if change_satoshi < 0:
            raise ValueError(
                "Input amount (%s) must be more than all output amounts (%s) plus fees (%s). You need more %s."
                % (total_ins_satoshi, total_outs_satoshi, self.fee_satoshi,
                   self.crypto.upper()))
        logging.info('8')

        ins = [x['input'] for x in self.ins]
        logging.info('9')

        if change_satoshi > 0:

            if self.verbose:
                print("Adding change address of %s satoshis to %s" %
                      (change_satoshi, self.change_address))
            change = [{
                'value': change_satoshi,
                'address': self.change_address
            }]
            logging.info('10')
        else:
            change = []  # no change ?!
            if self.verbose:
                print("Inputs == Outputs, no change address needed.")
            logging.info('11')

        tx = mktx(ins, self.outs + change)
        logging.info('12')

        if signed:
            for i, input_data in enumerate(self.ins):
                logging.info('13')

                if not input_data['private_key']:
                    raise Exception(
                        "Can't sign transaction, missing private key for input %s"
                        % i)
                tx = sign(tx, i, input_data['private_key'])
                logging.info('14')

        return tx
Ejemplo n.º 28
0
    def recv_txio(self, nick, utxo_list, cj_pub, change_addr):
        if nick not in self.nonrespondants:
            debug('nick(' + nick + ') not in nonrespondants ' +
                  str(self.nonrespondants))
            return
        self.utxos[nick] = utxo_list
        order = self.db.execute(
            'SELECT ordertype, txfee, cjfee FROM '
            'orderbook WHERE oid=? AND counterparty=?',
            (self.active_orders[nick], nick)).fetchone()
        utxo_data = common.bc_interface.query_utxo_set(self.utxos[nick])
        if None in utxo_data:
            common.debug(
                'ERROR outputs unconfirmed or already spent. utxo_data=' +
                pprint.pformat(utxo_data))
            raise RuntimeError('killing taker, TODO handle this error')
        total_input = sum([d['value'] for d in utxo_data])
        real_cjfee = calc_cj_fee(order['ordertype'], order['cjfee'],
                                 self.cj_amount)
        self.outputs.append({
            'address':
            change_addr,
            'value':
            total_input - self.cj_amount - order['txfee'] + real_cjfee
        })
        print 'fee breakdown for %s totalin=%d cjamount=%d txfee=%d realcjfee=%d' % (
            nick, total_input, self.cj_amount, order['txfee'], real_cjfee)
        cj_addr = btc.pubtoaddr(cj_pub, get_addr_vbyte())
        self.outputs.append({'address': cj_addr, 'value': self.cj_amount})
        self.cjfee_total += real_cjfee
        self.nonrespondants.remove(nick)
        if len(self.nonrespondants) > 0:
            debug('nonrespondants = ' + str(self.nonrespondants))
            return
        debug('got all parts, enough to build a tx cjfeetotal=' +
              str(self.cjfee_total))

        my_total_in = 0
        for u, va in self.input_utxos.iteritems():
            my_total_in += va['value']
        #my_total_in = sum([va['value'] for u, va in self.input_utxos.iteritems()])

        my_change_value = my_total_in - self.cj_amount - self.cjfee_total - self.my_txfee
        print 'fee breakdown for me totalin=%d txfee=%d cjfee_total=%d => changevalue=%d' % (
            my_total_in, self.my_txfee, self.cjfee_total, my_change_value)
        if self.my_change_addr == None:
            if my_change_value != 0 and abs(my_change_value) != 1:
                #seems you wont always get exactly zero because of integer rounding
                # so 1 satoshi extra or fewer being spent as miner fees is acceptable
                print 'WARNING CHANGE NOT BEING USED\nCHANGEVALUE = ' + str(
                    my_change_value)
        else:
            self.outputs.append({
                'address': self.my_change_addr,
                'value': my_change_value
            })
        utxo_tx = [dict([('output', u)]) for u in sum(self.utxos.values(), [])]
        random.shuffle(self.outputs)
        tx = btc.mktx(utxo_tx, self.outputs)
        debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx)))
        self.msgchan.send_tx(self.active_orders.keys(), tx)

        #now sign it ourselves here
        for index, ins in enumerate(btc.deserialize(tx)['ins']):
            utxo = ins['outpoint']['hash'] + ':' + str(
                ins['outpoint']['index'])
            if utxo not in self.input_utxos.keys():
                continue
            addr = self.input_utxos[utxo]['address']
            tx = btc.sign(tx, index, self.wallet.get_key_from_addr(addr))
        self.latest_tx = btc.deserialize(tx)
Ejemplo n.º 29
0
def test_donation_address(setup_donations, amount):
    wallets = make_wallets(1,
                           wallet_structures=[[1, 1, 1, 0, 0]],
                           mean_amt=0.5)
    wallet = wallets[0]['wallet']
    jm_single().bc_interface.sync_wallet(wallet)
    #make a rdp from a simple privkey
    rdp_priv = "\x01" * 32
    reusable_donation_pubkey = binascii.hexlify(
        secp256k1.PrivateKey(privkey=rdp_priv, raw=True,
                             ctx=btc.ctx).pubkey.serialize())
    dest_addr, sign_k = donation_address(reusable_donation_pubkey)
    print dest_addr
    jm_single().bc_interface.rpc('importaddress', [dest_addr, '', False])
    ins_full = wallet.unspent
    total = sum(x['value'] for x in ins_full.values())
    ins = ins_full.keys()
    output_addr = wallet.get_new_addr(1, 1)
    fee_est = 10000
    outs = [{
        'value': amount,
        'address': dest_addr
    }, {
        'value': total - amount - fee_est,
        'address': output_addr
    }]

    tx = btc.mktx(ins, outs)
    de_tx = btc.deserialize(tx)
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        addr = ins_full[utxo]['address']
        priv = wallet.get_key_from_addr(addr)
        priv = binascii.unhexlify(priv)
        usenonce = binascii.unhexlify(sign_k) if index == 0 else None
        if index == 0:
            log.debug("Applying rdp to input: " + str(ins))
        tx = btc.sign(tx, index, priv, usenonce=usenonce)
    #pushtx returns False on any error
    push_succeed = jm_single().bc_interface.pushtx(tx)
    if push_succeed:
        log.debug(btc.txhash(tx))
    else:
        assert False
    #Role of receiver: regenerate the destination private key,
    #and address, from the nonce of the first input; check it has
    #received the coins.
    detx = btc.deserialize(tx)
    first_utxo_script = detx['ins'][0]['script']
    sig, pub = btc.deserialize_script(first_utxo_script)
    log.debug(sig)
    sig = binascii.unhexlify(sig)
    kGlen = ord(sig[3])
    kG = sig[4:4 + kGlen]
    log.debug(binascii.hexlify(kG))
    if kG[0] == "\x00":
        kG = kG[1:]
    #H(rdp private key * K) + rdp should be ==> dest addr
    #Open issue: re-introduce recovery without ECC shenanigans
    #Just cheat by trying both signs for pubkey
    coerced_kG_1 = "02" + binascii.hexlify(kG)
    coerced_kG_2 = "03" + binascii.hexlify(kG)
    for coerc in [coerced_kG_1, coerced_kG_2]:
        c = btc.sha256(btc.multiply(binascii.hexlify(rdp_priv), coerc, True))
        pub_check = btc.add_pubkeys(
            [reusable_donation_pubkey,
             btc.privtopub(c + '01', True)], True)
        addr_check = btc.pubtoaddr(pub_check, get_p2pk_vbyte())
        log.debug("Found checked address: " + addr_check)
        if addr_check == dest_addr:
            time.sleep(3)
            received = jm_single().bc_interface.get_received_by_addr(
                [dest_addr], None)['data'][0]['balance']
            assert received == amount
            return
    assert False
Ejemplo n.º 30
0
def main():

    # get current fast confirmation bitcoin fee
    best_fee = bitcoin_fee()

    # command line arguments
    parser = argparse.ArgumentParser(
        description='Create a Bitcoin transaction')
    parser.add_argument('-f',
                        '--from',
                        help='one of more from addresses',
                        nargs='+',
                        required=True)
    parser.add_argument('-t',
                        '--to',
                        help='address to send to',
                        nargs=1,
                        required=True)
    parser.add_argument('-m',
                        '--fee',
                        help='miner fee in Satoshis per byte',
                        nargs=1,
                        type=valid_fee,
                        required=False,
                        default=[best_fee])
    parser.add_argument('-b',
                        '--bitcoin',
                        help='amount to transfer in BTC',
                        nargs=1,
                        type=float,
                        required=False)
    parser.add_argument('-u',
                        '--usd',
                        help='amount to transfer in USD',
                        nargs=1,
                        type=float,
                        required=False)
    parser.add_argument('-o',
                        '--override',
                        help='override high fee sanity check',
                        action='store_true',
                        required=False)
    parser.add_argument(
        '-e',
        '--envfriendly',
        help=
        'spend small UXTO amounts first; results in higher fees, but reduces global UTXO DB size',
        action='store_true',
        required=False)
    parser.add_argument('-v',
                        '--verbose',
                        help='show verbose output',
                        action='store_true',
                        required=False)
    args = vars(parser.parse_args())

    if (args['verbose']):
        logger.setLevel(logging.DEBUG)
    else:
        logger.setLevel(logging.INFO)

    # check which currency
    sweep = False
    btc_specified = (args['bitcoin'] is not None)
    usd_specified = (args['usd'] is not None)
    amount_satoshi = None

    if not (btc_specified or usd_specified):
        sweep = True
        logger.debug('SWEEP all funds')
    elif (btc_specified and usd_specified):
        logger.error('Specify the amount in Bitcoin or USD, but not both')
        exit(1)
    elif btc_specified:
        amount_btc = args['bitcoin'][0]
        amount_satoshi = int(round(amount_btc * 1e8))
        logger.info('AMOUNT {:,.8f} BTC = {:,.0f} Satoshi'.format(
            amount_btc, amount_satoshi))
    elif usd_specified:
        amount_usd = args['usd'][0]
        btc_price = get_bitcoin_price()
        amount_btc = amount_usd / btc_price
        amount_satoshi = int(round(amount_btc * 1e8))
        logger.info(
            'AMOUNT {:,.2f} USD = {:,.8f} BTC = {:,.0f} Satoshi'.format(
                amount_usd, amount_btc, amount_satoshi))

    # substring search for destination address in wallet
    dest = None
    item = lookup(args['to'][0])
    if item is not None:
        dest = item['address']
        logger.debug('Found destination address {} {} in wallet'.format(
            item['name'], dest))
    else:
        dest = args['to'][0]
        logger.debug('Using destination address {}'.format(dest))

    if not validate_address(dest):
        logger.warning(
            'Destination address "{}" is not a valid Bitcoin address'.format(
                dest))

    # gather UTXOs from inputs
    utxos = []
    privkeys = {}
    from_addrs = []
    for source in args['from']:

        entry = lookup(source)
        if (entry == None):
            logger.error(
                'No source address found in wallet matching "{}"'.format(
                    source))
            exit(1)

        name = entry['name']
        address = entry['address']
        privkey = entry['privkey']
        logger.debug('Found source address {} {} in wallet'.format(
            name, address))
        from_addrs.append(address)
        privkeys[address] = privkey

        # gather UTXOs
        unspent = get_unspent(address)
        logger.debug('Address {} has {} unspent{}'.format(
            address, len(unspent), pluralize(len(unspent))))
        has_utxos = False
        for tx in unspent:
            utxo = {}
            utxo['output'] = '{}:{}'.format(tx['id'], tx['vout'])
            utxo['address'] = address
            utxo['value'] = tx['amount']
            logger.debug('utxo["value"] = {}'.format(utxo['value']))
            utxos.append(utxo)
            has_utxos = True

        if not has_utxos:
            logger.warning('Address {} has no confirmed UTXOs'.format(address))

    # must have at least one UTXO
    nutxos = len(utxos)
    if nutxos == 0:
        logger.error('No confirmed UTXOs found')
        exit(1)

    # report UTXO summary
    naddr = len(args['from'])
    btc_price = get_bitcoin_price()
    avail_satoshi = sum([tx['value'] for tx in utxos])
    avail_usd = (avail_satoshi / 1e8) * btc_price
    addr_suffix = '' if naddr == 1 else 'es'
    logger.debug(
        'UTXO Summary: {} address{} {} UTXO{} {:,.0f} Satoshi ${:,.2f} USD'.
        format(naddr, addr_suffix, nutxos, pluralize(nutxos), avail_satoshi,
               avail_usd))

    # build tx
    txins = []
    txouts = []
    fee_per_byte = int(args['fee'][0])
    logger.debug('Using fee of {} satoshis per byte'.format(fee_per_byte))

    # sweep all BTC
    if sweep:
        logger.warning('Sweeping entire {:,.0f} satoshi from all UTXOs'.format(
            avail_satoshi))
        est_length = config['len-base'] + (config['len-per-input'] *
                                           nutxos) + config['len-per-output']
        fee = est_length * fee_per_byte

        # inputs
        n = 0
        total = 0
        for utxo in utxos:
            total += utxo['value']
            logger.debug('Input {} UTXO {} Value {:,.0f} Total {:,.0f}'.format(
                n, utxo['output'], utxo['value'], total))
            txins.append(utxo)
            n += 1

        # output
        send_satoshi = avail_satoshi - fee
        txouts = [{'value': send_satoshi, 'address': dest}]
        logger.debug('OUTPUT 0 Address {} Value {:,.0f}'.format(
            dest, send_satoshi))

    # transfer specific amount
    else:
        change_address = None
        send_satoshi = amount_satoshi
        logger.debug('transferring {:,.0f} Satoshi'.format(send_satoshi))

        initial_fee = (config['len-base'] +
                       (config['len-per-output'] * 2)) * fee_per_byte
        remaining = send_satoshi + initial_fee
        total_fees = initial_fee

        # Environmentally-friendly transfer spends smallest UTXOs
        # first. This reduces the size of UTXO database each full-node
        # must store, but results in a higher fee.
        reverse = True
        if args['envfriendly']:
            logger.warning(
                'environmentally friendly mode active, higher fees apply')
            reverse = False
        ordered_utxos = sorted(utxos,
                               key=lambda k: k['value'],
                               reverse=reverse)

        # inputs
        n = 0
        total = 0
        change = 0
        for utxo in ordered_utxos:
            fee_inc = (config['len-per-input'] * fee_per_byte)
            remaining += fee_inc
            total_fees += fee_inc
            remaining -= utxo['value']
            total += utxo['value']
            logger.debug('Input {} UTXO {} Value {:,.0f} Total {:,.0f}'.format(
                n, utxo['output'], utxo['value'], total))
            txins.append(utxo)
            n += 1

            if remaining < 0:
                change = -remaining
                change_address = utxo['address']
                break

        # insufficient funds
        if (remaining > 0):
            note = ''
            if (remaining <= total_fees):
                note = 'after adding miner fees '
                logger.critical(
                    'Insufficient funds {}{:,.0f} > {:,.0f}'.format(
                        note, send_satoshi + total_fees, avail_satoshi))
                exit(1)

        # outputs
        txouts = [{'address': dest, 'value': send_satoshi}]
        logger.debug('OUTPUT 0 Address {} Value {:,.0f}'.format(
            dest, send_satoshi))

        if (change > 0):

            # trivial remainder condition: it costs more in fees to
            # use the UTXO than what actually remains there, so just
            # leave it for the miner and a take speed bonus.

            sweep_utxo_len = config['len-base'] + config[
                'len-per-input'] + config['len-per-output']
            sweep_utxo_fee = sweep_utxo_len * best_fee
            if change < sweep_utxo_fee:
                change_usd = (change / 1e8) * btc_price
                logger.warning(
                    'Trivial UTXO remainder released to miner {:,.0f} Satoshi ${:,.2f} USD'
                    .format(change, change_usd))

            # return change
            else:

                # merge if change going to dest address
                if (change_address == dest):
                    merged_value = send_satoshi + change
                    logger.warning(
                        'Change address same as destination, merging output values {:,.0f}'
                        .format(merged_value))
                    txouts = [{'address': dest, 'value': merged_value}]

                # add extra output
                else:
                    txouts.append({'address': change_address, 'value': change})
                    logger.debug('OUTPUT 1 Address {} Value {:,.0f}'.format(
                        change_address, change))

    # sanity checks
    sum_ins = sum([x['value'] for x in txins])
    logger.debug('SUM(inputs) = {:,.0f}'.format(sum_ins))

    sum_outs = sum([x['value'] for x in txouts])
    logger.debug('SUM(outputs) = {:,.0f}'.format(sum_outs))

    fee_satoshi = (sum_ins - sum_outs)
    fee_btc = (fee_satoshi / 1e8)
    fee_usd = btc_price * fee_btc
    logger.info('Paying miner fee of {:,.0f} Satoshi ${:,.2f} USD'.format(
        fee_satoshi, fee_usd))

    if (fee_usd < 0):
        logger.critical(
            'Sanity check failed: sum of outputs {:,.0f} exceeds sum of inputs {:,.0f}'
            .format(sum_outs, sum_ins))
        exit(1)

    elif (fee_usd < 0.01):
        logger.critical(
            'Bad transaction: miner fee too small: ${:,.6f}'.format(fee_usd))
        exit(1)

    if (fee_usd > config['insane-fee-usd']):
        msg = 'Sanity check failed: miner fee too large ${:,.2f} >= ${:,.2f}'.format(
            fee_usd, config['insane-fee-usd'])
        if args['override']:
            logger.warning(msg)
            logger.warning('Overriding sanity check')
        else:
            logger.error(msg)
            exit(1)

    # sign tx inputs
    tx = mktx(txins, txouts)
    for i in range(len(txins)):
        logger.debug('Signing input {}'.format(i))
        try:
            tx = sign(tx, i, privkeys[txins[i]['address']])
        except:
            logger.critical('Failed to sign UTXO {}'.format(
                txins[i]['output']))
            exit(1)

    # confirm
    send_btc = send_satoshi / 1e8
    confirm = 'Sending {:,.8f} BTC ${:,.2f} USD '.format(
        send_btc, send_btc * btc_price)
    confirm += 'from {} to {} '.format(from_addrs, [dest])
    confirm += 'using fee of {:,.8f} BTC ${:,.2f} USD'.format(fee_btc, fee_usd)
    logger.warning(confirm)
    raw_input('[ Press Enter to confirm ]')

    # submit
    tid = broadcast(tx)
    if tid is None:
        logger.critical('Failed to submit transaction to the network')
    else:
        logger.warning('Broadcasted TXID {}'.format(tid))
Ejemplo n.º 31
0
    def recv_txio(self, nick, utxo_list, cj_pub, change_addr):
        if nick not in self.nonrespondants:
            debug('recv_txio => nick=' + nick + ' not in nonrespondants ' +
                  str(self.nonrespondants))
            return
        self.utxos[nick] = utxo_list
        order = self.db.execute(
            'SELECT ordertype, txfee, cjfee FROM '
            'orderbook WHERE oid=? AND counterparty=?',
            (self.active_orders[nick], nick)).fetchone()
        utxo_data = common.bc_interface.query_utxo_set(self.utxos[nick])
        if None in utxo_data:
            common.debug(
                'ERROR outputs unconfirmed or already spent. utxo_data=' +
                pprint.pformat(utxo_data))
            #when internal reviewing of makers is created, add it here to immediately quit
            return  #ignore this message, eventually the timeout thread will recover
        total_input = sum([d['value'] for d in utxo_data])
        real_cjfee = calc_cj_fee(order['ordertype'], order['cjfee'],
                                 self.cj_amount)
        self.outputs.append({
            'address':
            change_addr,
            'value':
            total_input - self.cj_amount - order['txfee'] + real_cjfee
        })
        debug(
            'fee breakdown for %s totalin=%d cjamount=%d txfee=%d realcjfee=%d'
            % (nick, total_input, self.cj_amount, order['txfee'], real_cjfee))
        cj_addr = btc.pubtoaddr(cj_pub, get_p2pk_vbyte())
        self.outputs.append({'address': cj_addr, 'value': self.cj_amount})
        self.cjfee_total += real_cjfee
        self.maker_txfee_contributions += order['txfee']
        self.nonrespondants.remove(nick)
        if len(self.nonrespondants) > 0:
            debug('nonrespondants = ' + str(self.nonrespondants))
            return
        self.all_responded = True
        with self.timeout_lock:
            self.timeout_lock.notify()
        debug('got all parts, enough to build a tx')
        self.nonrespondants = list(self.active_orders.keys())

        my_total_in = sum(
            [va['value'] for u, va in self.input_utxos.iteritems()])
        my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0)
        my_change_value = (my_total_in - self.cj_amount - self.cjfee_total -
                           my_txfee)
        debug(
            'fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d'
            % (my_total_in, my_txfee, self.maker_txfee_contributions,
               self.cjfee_total, my_change_value))
        if self.my_change_addr == None:
            if my_change_value != 0 and abs(my_change_value) != 1:
                #seems you wont always get exactly zero because of integer rounding
                # so 1 satoshi extra or fewer being spent as miner fees is acceptable
                debug('WARNING CHANGE NOT BEING USED\nCHANGEVALUE = ' +
                      str(my_change_value))
        else:
            self.outputs.append({
                'address': self.my_change_addr,
                'value': my_change_value
            })
        self.utxo_tx = [
            dict([('output', u)]) for u in sum(self.utxos.values(), [])
        ]
        self.outputs.append({
            'address': self.coinjoin_address(),
            'value': self.cj_amount
        })
        random.shuffle(self.utxo_tx)
        random.shuffle(self.outputs)
        tx = btc.mktx(self.utxo_tx, self.outputs)
        debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx)))
        self.msgchan.send_tx(self.active_orders.keys(), tx)

        self.latest_tx = btc.deserialize(tx)
        for index, ins in enumerate(self.latest_tx['ins']):
            utxo = ins['outpoint']['hash'] + ':' + str(
                ins['outpoint']['index'])
            if utxo not in self.input_utxos.keys():
                continue
            #placeholders required
            ins['script'] = 'deadbeef'
Ejemplo n.º 32
0
 def test_P2SH_P2WSH_ALL_SIGHASH(self):
     tx = TEST_CASES[3]
     VIN_AMOUNT = int(9.87654321 * 10**8)
     deserialized = deserialize(tx['unsigned'])
     serialized = serialize(deserialized)
     self.assertEqual(serialized, tx['unsigned'])
     self.assertEqual(deserialized['locktime'], tx['locktime'])
     ins = self.get_pybtc_vins(tx)
     outs = self.get_pybtc_outs(tx)
     generated_tx = mktx(ins, outs)
     stripped_tx = strip_witness_data(generated_tx)
     self.assertEqual(stripped_tx, serialized)
     priv0 = self.append_compressed_flag_to_privkey(
         tx['ins'][0]['privkeys'][0])
     priv1 = self.append_compressed_flag_to_privkey(
         tx['ins'][0]['privkeys'][1])
     priv2 = self.append_compressed_flag_to_privkey(
         tx['ins'][0]['privkeys'][2])
     priv3 = self.append_compressed_flag_to_privkey(
         tx['ins'][0]['privkeys'][3])
     priv4 = self.append_compressed_flag_to_privkey(
         tx['ins'][0]['privkeys'][4])
     priv5 = self.append_compressed_flag_to_privkey(
         tx['ins'][0]['privkeys'][5])
     witness_script = mk_multisig_script(privtopub(priv0), privtopub(priv1),
                                         privtopub(priv2), privtopub(priv3),
                                         privtopub(priv4), privtopub(priv5),
                                         6)
     self.assertEqual(witness_script, tx['ins'][0]['witness_script'])
     sign0 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv0,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_ALL)
     sign1 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv1,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_NONE)
     sign2 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv2,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_SINGLE)
     sign3 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv3,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_ALL | SIGHASH_ANYONECANPAY)
     sign4 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv4,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_NONE | SIGHASH_ANYONECANPAY)
     sign5 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv5,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_SINGLE
                              | SIGHASH_ANYONECANPAY)
     signed = apply_segwit_multisignatures(
         stripped_tx,
         0,
         witness_script, [sign0, sign1, sign2, sign3, sign4, sign5],
         nested=True)
     self.assertEqual(signed, tx['signed'])
     print(
         '[P2WSH 6-of-6 multisig NESTED in P2SH] SIGHASH_SINGLE\SIGHASH_ALL\SIGHASH_NONE & ANYONECANPAY'
     )
Ejemplo n.º 33
0
def send(privkey, pubkey, sendto, message="", force=False):

    op_return = message

    addr = pubkey

    confirmed_balance, inputs, unconfirmed = insight.getunspentbalance(addr)

    if not force:
        if not crypto.pubkey_is_valid(addr):
            # Invalid Address
            # print("Invalid Address")
            return False

        elif constants.FEE_MINIMUM > confirmed_balance:
            # Not enough chauchas
            # print("No minimum fee")
            return False

        elif len(op_return) > MAX:
            # Message too long
            # print("Message to long")
            return False

    # Transformar valores a Chatoshis
    used_amount = int(constants.FEE_MINIMUM * constants.COIN)

    # Utilizar solo las unspent que se necesiten
    used_balance = 0
    used_inputs = []

    for i in inputs:
        used_balance += i["value"]
        used_inputs.append(i)
        if used_balance > used_amount:
            break

    # Output
    outputs = [{"address": sendto, "value": used_amount}]

    # OP_RETURN
    if len(op_return) > 0 and len(op_return) <= MAX:
        payload = __payload(op_return)
        script = constants.OP_RETURN + b2a_hex(payload).decode("utf-8",
                                                               errors="ignore")

        outputs.append({"value": 0, "script": script})

    # Transaction
    template_tx = mktx(used_inputs, outputs)
    size = len(a2b_hex(template_tx))

    # FEE = 0.01 CHA/kb
    # MAX FEE = 0.1 CHA

    fee = int((size / constants.KYLOBYTE) * constants.FEE_RECOMMENDED *
              constants.COIN)
    fee = constants.FEE_MAX if fee > constants.FEE_MAX else fee

    if used_balance == confirmed_balance:
        outputs[0] = {"address": sendto, "value": used_amount - fee}
        tx = mktx(used_inputs, outputs)
    else:
        tx = mksend(used_inputs, outputs, addr, fee)

    for i in range(len(used_inputs)):
        tx = sign(tx, i, privkey)

    broadcasting = insight.broadcast(tx)

    try:
        msg = insight.endpoint() + "/tx/%s" % broadcasting.json()["txid"]
    except Exception:
        msg = broadcasting.text

    return msg
Ejemplo n.º 34
0
    def recv_txio(self, nick, utxo_list, cj_pub, change_addr):
        if nick not in self.nonrespondants:
            log.debug(('recv_txio => nick={} not in '
                       'nonrespondants {}').format(nick, self.nonrespondants))
            return
        self.utxos[nick] = utxo_list
        order = self.db.execute(
            'SELECT ordertype, txfee, cjfee FROM '
            'orderbook WHERE oid=? AND counterparty=?',
            (self.active_orders[nick], nick)).fetchone()
        utxo_data = jm_single().bc_interface.query_utxo_set(self.utxos[nick])
        if None in utxo_data:
            log.debug(('ERROR outputs unconfirmed or already spent. '
                       'utxo_data={}').format(pprint.pformat(utxo_data)))
            # when internal reviewing of makers is created, add it here to
            # immediately quit
            return

        # ignore this message, eventually the timeout thread will recover
        total_input = sum([d['value'] for d in utxo_data])
        real_cjfee = calc_cj_fee(order['ordertype'], order['cjfee'],
                                 self.cj_amount)
        self.outputs.append({
            'address':
            change_addr,
            'value':
            total_input - self.cj_amount - order['txfee'] + real_cjfee
        })
        fmt = ('fee breakdown for {} totalin={:d} '
               'cjamount={:d} txfee={:d} realcjfee={:d}').format
        log.debug(
            fmt(nick, total_input, self.cj_amount, order['txfee'], real_cjfee))
        cj_addr = btc.pubtoaddr(cj_pub, get_p2pk_vbyte())
        self.outputs.append({'address': cj_addr, 'value': self.cj_amount})
        self.cjfee_total += real_cjfee
        self.maker_txfee_contributions += order['txfee']
        self.nonrespondants.remove(nick)
        if len(self.nonrespondants) > 0:
            log.debug('nonrespondants = ' + str(self.nonrespondants))
            return
        self.all_responded = True
        with self.timeout_lock:
            self.timeout_lock.notify()
        log.debug('got all parts, enough to build a tx')
        self.nonrespondants = list(self.active_orders.keys())

        my_total_in = sum(
            [va['value'] for u, va in self.input_utxos.iteritems()])
        if self.my_change_addr:
            #Estimate fee per choice of next/3/6 blocks targetting.
            estimated_fee = estimate_tx_fee(len(sum(self.utxos.values(), [])),
                                            len(self.outputs) + 2)
            log.debug("Based on initial guess: " + str(self.total_txfee) +
                      ", we estimated a fee of: " + str(estimated_fee))
            #reset total
            self.total_txfee = estimated_fee
        my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0)
        my_change_value = (my_total_in - self.cj_amount - self.cjfee_total -
                           my_txfee)
        #Since we could not predict the maker's inputs, we may end up needing
        #too much such that the change value is negative or small. Note that
        #we have tried to avoid this based on over-estimating the needed amount
        #in SendPayment.create_tx(), but it is still a possibility if one maker
        #uses a *lot* of inputs.
        if self.my_change_addr and my_change_value <= 0:
            raise ValueError("Calculated transaction fee of: " +
                             str(self.total_txfee) +
                             " is too large for our inputs;Please try again.")
        elif self.my_change_addr and my_change_value <= jm_single(
        ).DUST_THRESHOLD:
            log.debug("Dynamically calculated change lower than dust: " +
                      str(my_change_value) + "; dropping.")
            self.my_change_addr = None
            my_change_value = 0
        log.debug(
            'fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d'
            % (my_total_in, my_txfee, self.maker_txfee_contributions,
               self.cjfee_total, my_change_value))
        if self.my_change_addr is None:
            if my_change_value != 0 and abs(my_change_value) != 1:
                # seems you wont always get exactly zero because of integer
                # rounding so 1 satoshi extra or fewer being spent as miner
                # fees is acceptable
                log.debug(('WARNING CHANGE NOT BEING '
                           'USED\nCHANGEVALUE = {}').format(my_change_value))
        else:
            self.outputs.append({
                'address': self.my_change_addr,
                'value': my_change_value
            })
        self.utxo_tx = [
            dict([('output', u)]) for u in sum(self.utxos.values(), [])
        ]
        self.outputs.append({
            'address': self.coinjoin_address(),
            'value': self.cj_amount
        })
        random.shuffle(self.utxo_tx)
        random.shuffle(self.outputs)
        tx = btc.mktx(self.utxo_tx, self.outputs)
        log.debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx)))
        self.msgchan.send_tx(self.active_orders.keys(), tx)

        self.latest_tx = btc.deserialize(tx)
        for index, ins in enumerate(self.latest_tx['ins']):
            utxo = ins['outpoint']['hash'] + ':' + str(
                ins['outpoint']['index'])
            if utxo not in self.input_utxos.keys():
                continue
            # placeholders required
            ins['script'] = 'deadbeef'
Ejemplo n.º 35
0
    def accept_receipt(self, ws, blockchain, receipt_json=None):
        """
        Process the final receipt sent over by the buyer. If valid, broadcast the transaction
        to the bitcoin network.
        """
        self.ws = ws
        self.blockchain = blockchain
        try:
            if receipt_json:
                self.contract["buyer_receipt"] = json.loads(receipt_json,
                                                            object_pairs_hook=OrderedDict)
            contract_dict = json.loads(json.dumps(self.contract, indent=4), object_pairs_hook=OrderedDict)
            del contract_dict["buyer_receipt"]
            contract_hash = digest(json.dumps(contract_dict, indent=4)).encode("hex")
            ref_hash = self.contract["buyer_receipt"]["receipt"]["ref_hash"]
            if ref_hash != contract_hash:
                raise Exception("Order number doesn't match")

            # The buyer may have sent over this whole contract, make sure the data we added wasn't manipulated.
            verify_key = self.keychain.signing_key.verify_key
            verify_key.verify(json.dumps(self.contract["vendor_order_confirmation"]["invoice"], indent=4),
                              unhexlify(self.contract["vendor_order_confirmation"]["signature"]))

            order_id = self.contract["vendor_order_confirmation"]["invoice"]["ref_hash"]
            outpoints = pickle.loads(self.db.Sales().get_outpoint(order_id))
            payout_address = self.contract["vendor_order_confirmation"]["invoice"]["payout"]["address"]
            redeem_script = str(self.contract["buyer_order"]["order"]["payment"]["redeem_script"])
            for output in outpoints:
                del output["value"]
            value = self.contract["vendor_order_confirmation"]["invoice"]["payout"]["value"]
            outs = [{'value': value, 'address': payout_address}]
            tx = bitcoin.mktx(outpoints, outs)

            chaincode = self.contract["buyer_order"]["order"]["payment"]["chaincode"]
            masterkey_b = self.contract["buyer_order"]["order"]["id"]["pubkeys"]["bitcoin"]
            buyer_key = derive_childkey(masterkey_b, chaincode)

            vendor_sigs = self.contract["vendor_order_confirmation"]["invoice"]["payout"]["signature(s)"]
            buyer_sigs = self.contract["buyer_receipt"]["receipt"]["payout"]["signature(s)"]
            for index in range(0, len(outpoints)):
                for s in vendor_sigs:
                    if s["input_index"] == index:
                        sig1 = str(s["signature"])
                for s in buyer_sigs:
                    if s["input_index"] == index:
                        sig2 = str(s["signature"])

                if bitcoin.verify_tx_input(tx, index, redeem_script, sig2, buyer_key):
                    tx = bitcoin.apply_multisignatures(tx, index, str(redeem_script), sig1, sig2)
                else:
                    raise Exception("Buyer sent invalid signature")

            d = defer.Deferred()

            def on_broadcast_complete(success):
                if success:
                    d.callback(order_id)
                else:
                    d.callback(False)

            def on_validate(success):
                def on_fetch(ec, result):
                    if ec:
                        # if it's not in the blockchain, let's try broadcasting it.
                        self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx))
                        self.blockchain.broadcast(tx, cb=on_broadcast_complete)
                    else:
                        d.callback(order_id)

                if success:
                    # broadcast anyway but don't wait for callback
                    self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx))
                    self.blockchain.broadcast(tx)
                    d.callback(order_id)
                else:
                    # check to see if the tx is already in the blockchain
                    self.blockchain.fetch_transaction(unhexlify(bitcoin.txhash(tx)), on_fetch)

            if "txid" in self.contract["buyer_receipt"]["receipt"]["payout"] \
                    and bitcoin.txhash(tx) == self.contract["buyer_receipt"]["receipt"]["payout"]["txid"]:
                # check mempool and blockchain for tx
                self.blockchain.validate(tx, cb=on_validate)
            else:
                # try broadcasting
                self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx))
                self.blockchain.broadcast(tx, cb=on_broadcast_complete)

            # TODO: update db and file system if successful
            # TODO: broadcast over websocket
            return d

        except Exception:
            return defer.succeed(False)
Ejemplo n.º 36
0
    def accept_receipt(self, ws, blockchain, receipt_json=None):
        """
        Process the final receipt sent over by the buyer. If valid, broadcast the transaction
        to the bitcoin network.
        """
        self.ws = ws
        self.blockchain = blockchain
        try:
            if receipt_json:
                self.contract["buyer_receipt"] = json.loads(
                    receipt_json, object_pairs_hook=OrderedDict)
            contract_dict = json.loads(json.dumps(self.contract, indent=4),
                                       object_pairs_hook=OrderedDict)
            del contract_dict["buyer_receipt"]
            contract_hash = digest(json.dumps(contract_dict,
                                              indent=4)).encode("hex")
            ref_hash = self.contract["buyer_receipt"]["receipt"]["ref_hash"]
            if ref_hash != contract_hash:
                raise Exception("Order number doesn't match")

            # The buyer may have sent over this whole contract, make sure the data we added wasn't manipulated.
            verify_key = self.keychain.signing_key.verify_key
            verify_key.verify(
                json.dumps(
                    self.contract["vendor_order_confirmation"]["invoice"],
                    indent=4),
                unhexlify(
                    self.contract["vendor_order_confirmation"]["signature"]))

            order_id = self.contract["vendor_order_confirmation"]["invoice"][
                "ref_hash"]
            outpoints = pickle.loads(self.db.Sales().get_outpoint(order_id))
            payout_address = self.contract["vendor_order_confirmation"][
                "invoice"]["payout"]["address"]
            redeem_script = str(self.contract["buyer_order"]["order"]
                                ["payment"]["redeem_script"])
            for output in outpoints:
                del output["value"]
            value = self.contract["vendor_order_confirmation"]["invoice"][
                "payout"]["value"]
            outs = [{'value': value, 'address': payout_address}]
            tx = bitcoin.mktx(outpoints, outs)

            chaincode = self.contract["buyer_order"]["order"]["payment"][
                "chaincode"]
            masterkey_b = self.contract["buyer_order"]["order"]["id"][
                "pubkeys"]["bitcoin"]
            buyer_key = derive_childkey(masterkey_b, chaincode)

            vendor_sigs = self.contract["vendor_order_confirmation"][
                "invoice"]["payout"]["signature(s)"]
            buyer_sigs = self.contract["buyer_receipt"]["receipt"]["payout"][
                "signature(s)"]
            for index in range(0, len(outpoints)):
                for s in vendor_sigs:
                    if s["input_index"] == index:
                        sig1 = str(s["signature"])
                for s in buyer_sigs:
                    if s["input_index"] == index:
                        sig2 = str(s["signature"])

                if bitcoin.verify_tx_input(tx, index, redeem_script, sig2,
                                           buyer_key):
                    tx = bitcoin.apply_multisignatures(tx, index,
                                                       str(redeem_script),
                                                       sig1, sig2)
                else:
                    raise Exception("Buyer sent invalid signature")

            d = defer.Deferred()

            def on_broadcast_complete(success):
                if success:
                    d.callback(order_id)
                else:
                    d.callback(False)

            def on_validate(success):
                def on_fetch(ec, result):
                    if ec:
                        # if it's not in the blockchain, let's try broadcasting it.
                        self.log.info("Broadcasting payout tx %s to network" %
                                      bitcoin.txhash(tx))
                        self.blockchain.broadcast(tx, cb=on_broadcast_complete)
                    else:
                        d.callback(order_id)

                if success:
                    # broadcast anyway but don't wait for callback
                    self.log.info("Broadcasting payout tx %s to network" %
                                  bitcoin.txhash(tx))
                    self.blockchain.broadcast(tx)
                    d.callback(order_id)
                else:
                    # check to see if the tx is already in the blockchain
                    self.blockchain.fetch_transaction(
                        unhexlify(bitcoin.txhash(tx)), on_fetch)

            if "txid" in self.contract["buyer_receipt"]["receipt"]["payout"] \
                    and bitcoin.txhash(tx) == self.contract["buyer_receipt"]["receipt"]["payout"]["txid"]:
                # check mempool and blockchain for tx
                self.blockchain.validate(tx, cb=on_validate)
            else:
                # try broadcasting
                self.log.info("Broadcasting payout tx %s to network" %
                              bitcoin.txhash(tx))
                self.blockchain.broadcast(tx, cb=on_broadcast_complete)

            # TODO: update db and file system if successful
            # TODO: broadcast over websocket
            return d

        except Exception:
            return defer.succeed(False)
Ejemplo n.º 37
0
	def recv_txio(self, nick, utxo_list, cj_pub, change_addr):	
		if nick not in self.nonrespondants:
			debug('recv_txio => nick=' + nick + ' not in nonrespondants ' + str(self.nonrespondants))
			return
		self.utxos[nick] = utxo_list
		order = self.db.execute('SELECT ordertype, txfee, cjfee FROM '
			'orderbook WHERE oid=? AND counterparty=?',
			(self.active_orders[nick], nick)).fetchone()
		utxo_data = common.bc_interface.query_utxo_set(self.utxos[nick])
		if None in utxo_data:
			common.debug('ERROR outputs unconfirmed or already spent. utxo_data='
				+ pprint.pformat(utxo_data))
			#when internal reviewing of makers is created, add it here to immediately quit
			return #ignore this message, eventually the timeout thread will recover
		total_input = sum([d['value'] for d in utxo_data])
		real_cjfee = calc_cj_fee(order['ordertype'], order['cjfee'], self.cj_amount)
		self.outputs.append({'address': change_addr, 'value':
			total_input - self.cj_amount - order['txfee'] + real_cjfee})
		debug('fee breakdown for %s totalin=%d cjamount=%d txfee=%d realcjfee=%d' % (nick,
			total_input, self.cj_amount, order['txfee'], real_cjfee))
		cj_addr = btc.pubtoaddr(cj_pub, get_p2pk_vbyte())
		self.outputs.append({'address': cj_addr, 'value': self.cj_amount})
		self.cjfee_total += real_cjfee
		self.maker_txfee_contributions += order['txfee']
		self.nonrespondants.remove(nick)
		if len(self.nonrespondants) > 0:
			debug('nonrespondants = ' + str(self.nonrespondants))
			return
		self.all_responded = True
		with self.timeout_lock:
			self.timeout_lock.notify()
		debug('got all parts, enough to build a tx')
		self.nonrespondants = list(self.active_orders.keys())

		my_total_in = sum([va['value'] for u, va in self.input_utxos.iteritems()])
		my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0)
		my_change_value = (my_total_in - self.cj_amount - self.cjfee_total - my_txfee)
		debug('fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d' % (my_total_in, 
			my_txfee, self.maker_txfee_contributions, self.cjfee_total, my_change_value))
		if self.my_change_addr == None:
			if my_change_value != 0 and abs(my_change_value) != 1:
				#seems you wont always get exactly zero because of integer rounding
				# so 1 satoshi extra or fewer being spent as miner fees is acceptable
				debug('WARNING CHANGE NOT BEING USED\nCHANGEVALUE = ' + str(my_change_value))
		else:
			self.outputs.append({'address': self.my_change_addr, 'value': my_change_value})
		self.utxo_tx = [dict([('output', u)]) for u in sum(self.utxos.values(), [])]
		self.outputs.append({'address': self.coinjoin_address(), 'value': self.cj_amount})
		random.shuffle(self.utxo_tx)
		random.shuffle(self.outputs)
		tx = btc.mktx(self.utxo_tx, self.outputs)	
		debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx)))
		self.msgchan.send_tx(self.active_orders.keys(), tx)

		self.latest_tx = btc.deserialize(tx)
		for index, ins in enumerate(self.latest_tx['ins']):
			utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
			if utxo not in self.input_utxos.keys():
				continue
			#placeholders required
			ins['script'] = 'deadbeef'
    bitprint('\nPublic keys\n', pubkeys)
    bitprint('\nAddresses\n', addresses)
    # Total: 0.06
    funding_txs = [
        bitcoin_client.sendtoaddress(address, 0.01 * (i + 1))
        for i, address in enumerate(addresses)
    ]
    bitprint('\nFunding txs')
    bitprint(funding_txs)
    outpoints = list(_get_outpoints(bitcoin_client, addresses, funding_txs))

    bitprint(outpoints)
    tx = bitcoin.mktx(
        outpoints,
        [
            {'value': int(0.055 * 10**8), 'address': an_external_address},
            {'value': int(0.004 * 10 ** 8), 'address': addresses[0]}
        ]
    )
    bitprint('\nUnsigned tx')
    bitprint(bitcoin.deserialize(tx))
    bitprint('\nUnsigned raw tx\n', tx)
    for i, key in enumerate(keys):
        tx = bitcoin.sign(tx, i, key)

    bitprint('\nSigned tx')
    bitprint(bitcoin.deserialize(tx))
    bitprint('\nSigned raw tx\n', tx)
    bitprint('\nHash of submitted transaction:', bitcoin_client.sendrawtransaction(tx))
    bitprint('\nMempool:', bitcoin_client.getrawmempool())
    block_hash = bitcoin_client.generate(1)[0]
Ejemplo n.º 39
0
import binascii
import bitcoin
address = '3BSBPJyP7r6W7WBz74ghL6W3A1aB7F6SoF'
pk = 'b3a6b8c655d471f003be2d0ccf5766c48cc36cdf02110cfcf3920971571c02b5'
msg = 'Grazie mille per il bellissimo ed originale regalo di matrimonio!'

OP_RETURN = 106
OP_PUSHDATA1 = 76
inputs = [{
    'output':
    '5377427396f9ba7ca186bd7c82e42a599e19115e7b8818d1051fa153aab5f0f6:3',
    'value': 10000000
}]

outputs = [{
    'address': address,
    'value': 10000000 - 29800
}, {
    'script': '6a4c41' + binascii.hexlify(msg.encode()).decode(),
    'value': 0
}]
tx = bitcoin.mktx(inputs, outputs)
tx = bitcoin.sign(tx, 0, pk)
print(tx)
print(len(tx) / 2000)
Ejemplo n.º 40
0
    def add_receipt(self,
                    received,
                    libbitcoin_client,
                    feedback=None,
                    quality=None,
                    description=None,
                    delivery_time=None,
                    customer_service=None,
                    review="",
                    dispute=False,
                    claim=None,
                    payout=True):

        """
        Add the final piece of the contract that appends the review and payout transaction.
        """
        self.blockchain = libbitcoin_client
        receipt_json = {
            "buyer_receipt": {
                "receipt": {
                    "ref_hash": digest(json.dumps(self.contract, indent=4)).encode("hex"),
                    "listing": {
                        "received": received,
                        "listing_hash": self.contract["buyer_order"]["order"]["ref_hash"]
                    },
                    "dispute": {
                        "dispute": dispute
                    }
                }
            }
        }
        if None not in (feedback, quality, description, delivery_time, customer_service):
            receipt_json["buyer_receipt"]["receipt"]["rating"] = {}
            receipt_json["buyer_receipt"]["receipt"]["rating"]["feedback"] = feedback
            receipt_json["buyer_receipt"]["receipt"]["rating"]["quality"] = quality
            receipt_json["buyer_receipt"]["receipt"]["rating"]["description"] = description
            receipt_json["buyer_receipt"]["receipt"]["rating"]["delivery_time"] = delivery_time
            receipt_json["buyer_receipt"]["receipt"]["rating"]["customer_service"] = customer_service
            receipt_json["buyer_receipt"]["receipt"]["rating"]["review"] = review
        if payout:
            order_id = self.contract["vendor_order_confirmation"]["invoice"]["ref_hash"]
            outpoints = pickle.loads(self.db.Purchases().get_outpoint(order_id))
            payout_address = self.contract["vendor_order_confirmation"]["invoice"]["payout"]["address"]
            redeem_script = str(self.contract["buyer_order"]["order"]["payment"]["redeem_script"])
            for output in outpoints:
                del output["value"]
            value = self.contract["vendor_order_confirmation"]["invoice"]["payout"]["value"]
            outs = [{'value': value, 'address': payout_address}]
            tx = bitcoin.mktx(outpoints, outs)
            signatures = []
            chaincode = self.contract["buyer_order"]["order"]["payment"]["chaincode"]
            masterkey_b = bitcoin.bip32_extract_key(self.keychain.bitcoin_master_privkey)
            buyer_priv = derive_childkey(masterkey_b, chaincode, bitcoin.MAINNET_PRIVATE)
            masterkey_v = self.contract["vendor_offer"]["listing"]["id"]["pubkeys"]["bitcoin"]
            vendor_key = derive_childkey(masterkey_v, chaincode)
            valid_inputs = 0
            for index in range(0, len(outpoints)):
                sig = bitcoin.multisign(tx, index, redeem_script, buyer_priv)
                signatures.append({"input_index": index, "signature": sig})
                for s in self.contract["vendor_order_confirmation"]["invoice"]["payout"]["signature(s)"]:
                    if s["input_index"] == index:
                        if bitcoin.verify_tx_input(tx, index, redeem_script, s["signature"], vendor_key):
                            tx = bitcoin.apply_multisignatures(tx, index, str(redeem_script),
                                                               sig, str(s["signature"]))
                            valid_inputs += 1
            receipt_json["buyer_receipt"]["receipt"]["payout"] = {}
            if valid_inputs == len(outpoints):
                self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx))
                self.blockchain.broadcast(tx)
                receipt_json["buyer_receipt"]["receipt"]["payout"]["txid"] = bitcoin.txhash(tx)
            receipt_json["buyer_receipt"]["receipt"]["payout"]["signature(s)"] = signatures
            receipt_json["buyer_receipt"]["receipt"]["payout"]["value"] = value
        if claim:
            receipt_json["buyer_receipt"]["receipt"]["dispute"]["claim"] = claim
        receipt = json.dumps(receipt_json["buyer_receipt"]["receipt"], indent=4)
        receipt_json["buyer_receipt"]["signature"] = \
            self.keychain.signing_key.sign(receipt, encoder=nacl.encoding.HexEncoder)[:128]
        self.contract["buyer_receipt"] = receipt_json["buyer_receipt"]
Ejemplo n.º 41
0
    def recv_txio(self, nick, utxo_list, cj_pub, change_addr):
        if nick not in self.nonrespondants:
            log.debug(('recv_txio => nick={} not in '
                       'nonrespondants {}').format(nick, self.nonrespondants))
            return
        self.utxos[nick] = utxo_list
        utxo_data = jm_single().bc_interface.query_utxo_set(self.utxos[nick])
        if None in utxo_data:
            log.debug(('ERROR outputs unconfirmed or already spent. '
                       'utxo_data={}').format(pprint.pformat(utxo_data)))
            # when internal reviewing of makers is created, add it here to
            # immediately quit
            return

        # ignore this message, eventually the timeout thread will recover
        total_input = sum([d['value'] for d in utxo_data])
        real_cjfee = calc_cj_fee(self.active_orders[nick]['ordertype'],
                       self.active_orders[nick]['cjfee'], self.cj_amount)
        self.outputs.append({'address': change_addr,
              'value': total_input - self.cj_amount -
               self.active_orders[nick]['txfee'] + real_cjfee})
        fmt = ('fee breakdown for {} totalin={:d} '
               'cjamount={:d} txfee={:d} realcjfee={:d}').format
        log.debug(fmt(nick, total_input, self.cj_amount,
            self.active_orders[nick]['txfee'], real_cjfee))
        cj_addr = btc.pubtoaddr(cj_pub, get_p2pk_vbyte())
        self.outputs.append({'address': cj_addr, 'value': self.cj_amount})
        self.cjfee_total += real_cjfee
        self.maker_txfee_contributions += self.active_orders[nick]['txfee']
        self.nonrespondants.remove(nick)
        if len(self.nonrespondants) > 0:
            log.debug('nonrespondants = ' + str(self.nonrespondants))
            return
        self.all_responded = True
        with self.timeout_lock:
            self.timeout_lock.notify()
        log.debug('got all parts, enough to build a tx')
        self.nonrespondants = list(self.active_orders.keys())

        my_total_in = sum([va['value'] for u, va in
                           self.input_utxos.iteritems()])
        if self.my_change_addr:
            #Estimate fee per choice of next/3/6 blocks targetting.
            estimated_fee = estimate_tx_fee(len(sum(
                self.utxos.values(),[])), len(self.outputs)+2)
            log.debug("Based on initial guess: "+str(
                self.total_txfee)+", we estimated a fee of: "+str(estimated_fee))
            #reset total
            self.total_txfee = estimated_fee
        my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0)
        my_change_value = (
            my_total_in - self.cj_amount - self.cjfee_total - my_txfee)
        #Since we could not predict the maker's inputs, we may end up needing
        #too much such that the change value is negative or small. Note that 
        #we have tried to avoid this based on over-estimating the needed amount
        #in SendPayment.create_tx(), but it is still a possibility if one maker
        #uses a *lot* of inputs.
        if self.my_change_addr and my_change_value <= 0:
            raise ValueError("Calculated transaction fee of: "+str(
                self.total_txfee)+" is too large for our inputs;Please try again.")
        elif self.my_change_addr and my_change_value <= jm_single().DUST_THRESHOLD:
            log.debug("Dynamically calculated change lower than dust: "+str(
                my_change_value)+"; dropping.")
            self.my_change_addr = None
            my_change_value = 0
        log.debug('fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d'
                  % (my_total_in, my_txfee, self.maker_txfee_contributions,            
                  self.cjfee_total, my_change_value))
        if self.my_change_addr is None:
            if my_change_value != 0 and abs(my_change_value) != 1:
                # seems you wont always get exactly zero because of integer
                # rounding so 1 satoshi extra or fewer being spent as miner
                # fees is acceptable
                log.debug(('WARNING CHANGE NOT BEING '
                           'USED\nCHANGEVALUE = {}').format(my_change_value))
        else:
            self.outputs.append({'address': self.my_change_addr,
                                 'value': my_change_value})
        self.utxo_tx = [dict([('output', u)])
                        for u in sum(self.utxos.values(), [])]
        self.outputs.append({'address': self.coinjoin_address(),
                             'value': self.cj_amount})
        random.shuffle(self.utxo_tx)
        random.shuffle(self.outputs)
        tx = btc.mktx(self.utxo_tx, self.outputs)
        log.debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx)))
        self.msgchan.send_tx(self.active_orders.keys(), tx)

        self.latest_tx = btc.deserialize(tx)
        for index, ins in enumerate(self.latest_tx['ins']):
            utxo = ins['outpoint']['hash'] + ':' + str(
                    ins['outpoint']['index'])
            if utxo not in self.input_utxos.keys():
                continue
            # placeholders required
            ins['script'] = 'deadbeef'
Ejemplo n.º 42
0
print 'addr', addr

#check if you have any token left
h = b.blockr_unspent(addr)
print 'history', h

balance = sum([i['value'] for i in h])
print 'balance', balance

fee = 3000

#build a get change back out
outs = [{'value': balance - fee, 'address': addr}]  #

#%create a transaction
tx_hex = b.mktx(h, outs)

#create a op_return hexcode
tx_opr = b.mk_opreturn("hello world! by mepppp", tx_hex)

assert (tx_opr != tx_hex)

#%%
#generate the tx dict to append the op_ret script
#tx=b.deserialize(tx_hex)

#tx['outs'].append({'value':0,'script':op_ret})

print "====== tx_opr ======"
print tx_opr
Ejemplo n.º 43
0
 def add_receipt(self,
                 received,
                 libbitcoin_client,
                 feedback=None,
                 quality=None,
                 description=None,
                 delivery_time=None,
                 customer_service=None,
                 review="",
                 dispute=False,
                 claim=None,
                 payout=True):
     """
     Add the final piece of the contract that appends the review and payout transaction.
     """
     self.blockchain = libbitcoin_client
     receipt_json = {
         "buyer_receipt": {
             "receipt": {
                 "ref_hash":
                 digest(json.dumps(self.contract, indent=4)).encode("hex"),
                 "listing": {
                     "received":
                     received,
                     "listing_hash":
                     self.contract["buyer_order"]["order"]["ref_hash"]
                 },
                 "dispute": {
                     "dispute": dispute
                 }
             }
         }
     }
     if None not in (feedback, quality, description, delivery_time,
                     customer_service):
         receipt_json["buyer_receipt"]["receipt"]["rating"] = {}
         receipt_json["buyer_receipt"]["receipt"]["rating"][
             "feedback"] = feedback
         receipt_json["buyer_receipt"]["receipt"]["rating"][
             "quality"] = quality
         receipt_json["buyer_receipt"]["receipt"]["rating"][
             "description"] = description
         receipt_json["buyer_receipt"]["receipt"]["rating"][
             "delivery_time"] = delivery_time
         receipt_json["buyer_receipt"]["receipt"]["rating"][
             "customer_service"] = customer_service
         receipt_json["buyer_receipt"]["receipt"]["rating"][
             "review"] = review
     if payout:
         order_id = self.contract["vendor_order_confirmation"]["invoice"][
             "ref_hash"]
         outpoints = pickle.loads(
             self.db.Purchases().get_outpoint(order_id))
         payout_address = self.contract["vendor_order_confirmation"][
             "invoice"]["payout"]["address"]
         redeem_script = str(self.contract["buyer_order"]["order"]
                             ["payment"]["redeem_script"])
         for output in outpoints:
             del output["value"]
         value = self.contract["vendor_order_confirmation"]["invoice"][
             "payout"]["value"]
         outs = [{'value': value, 'address': payout_address}]
         tx = bitcoin.mktx(outpoints, outs)
         signatures = []
         chaincode = self.contract["buyer_order"]["order"]["payment"][
             "chaincode"]
         masterkey_b = bitcoin.bip32_extract_key(
             self.keychain.bitcoin_master_privkey)
         buyer_priv = derive_childkey(masterkey_b, chaincode,
                                      bitcoin.MAINNET_PRIVATE)
         masterkey_v = self.contract["vendor_offer"]["listing"]["id"][
             "pubkeys"]["bitcoin"]
         vendor_key = derive_childkey(masterkey_v, chaincode)
         valid_inputs = 0
         for index in range(0, len(outpoints)):
             sig = bitcoin.multisign(tx, index, redeem_script, buyer_priv)
             signatures.append({"input_index": index, "signature": sig})
             for s in self.contract["vendor_order_confirmation"]["invoice"][
                     "payout"]["signature(s)"]:
                 if s["input_index"] == index:
                     if bitcoin.verify_tx_input(tx, index, redeem_script,
                                                s["signature"], vendor_key):
                         tx = bitcoin.apply_multisignatures(
                             tx, index, str(redeem_script), sig,
                             str(s["signature"]))
                         valid_inputs += 1
         receipt_json["buyer_receipt"]["receipt"]["payout"] = {}
         if valid_inputs == len(outpoints):
             self.log.info("Broadcasting payout tx %s to network" %
                           bitcoin.txhash(tx))
             self.blockchain.broadcast(tx)
             receipt_json["buyer_receipt"]["receipt"]["payout"][
                 "txid"] = bitcoin.txhash(tx)
         receipt_json["buyer_receipt"]["receipt"]["payout"][
             "signature(s)"] = signatures
         receipt_json["buyer_receipt"]["receipt"]["payout"]["value"] = value
     if claim:
         receipt_json["buyer_receipt"]["receipt"]["dispute"][
             "claim"] = claim
     receipt = json.dumps(receipt_json["buyer_receipt"]["receipt"],
                          indent=4)
     receipt_json["buyer_receipt"]["signature"] = \
         self.keychain.signing_key.sign(receipt, encoder=nacl.encoding.HexEncoder)[:128]
     self.contract["buyer_receipt"] = receipt_json["buyer_receipt"]
Ejemplo n.º 44
0
    def add_order_confirmation(self,
                               payout_address,
                               comments=None,
                               shipper=None,
                               tracking_number=None,
                               est_delivery=None,
                               url=None,
                               password=None):
        """
        Add the vendor's order confirmation to the contract.
        """

        if not self.testnet and not (payout_address[:1] == "1" or payout_address[:1] == "3"):
            raise Exception("Bitcoin address is not a mainnet address")
        elif self.testnet and not \
                (payout_address[:1] == "n" or payout_address[:1] == "m" or payout_address[:1] == "2"):
            raise Exception("Bitcoin address is not a testnet address")
        try:
            bitcoin.b58check_to_hex(payout_address)
        except AssertionError:
            raise Exception("Invalid Bitcoin address")
        conf_json = {
            "vendor_order_confirmation": {
                "invoice": {
                    "ref_hash": digest(json.dumps(self.contract, indent=4)).encode("hex")
                }
            }
        }
        if self.contract["vendor_offer"]["listing"]["metadata"]["category"] == "physical good":
            shipping = {"shipper": shipper, "tracking_number": tracking_number, "est_delivery": est_delivery}
            conf_json["vendor_order_confirmation"]["invoice"]["shipping"] = shipping
        elif self.contract["vendor_offer"]["listing"]["metadata"]["category"] == "digital good":
            content_source = {"url": url, "password": password}
            conf_json["vendor_order_confirmation"]["invoice"]["content_source"] = content_source
        if comments:
            conf_json["vendor_order_confirmation"]["invoice"]["comments"] = comments
        confirmation = json.dumps(conf_json["vendor_order_confirmation"]["invoice"], indent=4)
        conf_json["vendor_order_confirmation"]["signature"] = \
            self.keychain.signing_key.sign(confirmation, encoder=nacl.encoding.HexEncoder)[:128]
        order_id = digest(json.dumps(self.contract, indent=4)).encode("hex")
        # apply signatures
        outpoints = pickle.loads(self.db.Sales().get_outpoint(order_id))
        redeem_script = self.contract["buyer_order"]["order"]["payment"]["redeem_script"]
        value = 0
        for output in outpoints:
            value += output["value"]
            del output["value"]
        value -= TRANSACTION_FEE
        outs = [{'value': value, 'address': payout_address}]
        tx = bitcoin.mktx(outpoints, outs)
        signatures = []
        chaincode = self.contract["buyer_order"]["order"]["payment"]["chaincode"]
        masterkey_v = bitcoin.bip32_extract_key(self.keychain.bitcoin_master_privkey)
        vendor_priv = derive_childkey(masterkey_v, chaincode, bitcoin.MAINNET_PRIVATE)
        for index in range(0, len(outpoints)):
            sig = bitcoin.multisign(tx, index, redeem_script, vendor_priv)
            signatures.append({"input_index": index, "signature": sig})
        conf_json["vendor_order_confirmation"]["invoice"]["payout"] = {}
        conf_json["vendor_order_confirmation"]["invoice"]["payout"]["address"] = payout_address
        conf_json["vendor_order_confirmation"]["invoice"]["payout"]["value"] = value
        conf_json["vendor_order_confirmation"]["invoice"]["payout"]["signature(s)"] = signatures

        self.contract["vendor_order_confirmation"] = conf_json["vendor_order_confirmation"]
        self.db.Sales().update_status(order_id, 2)
        file_path = DATA_FOLDER + "store/listings/in progress/" + order_id + ".json"
        with open(file_path, 'w') as outfile:
            outfile.write(json.dumps(self.contract, indent=4))
Ejemplo n.º 45
0
    def commitTo(self, testnet, commit_address, private_key):
        """
        Commit to some address on the blockchain
        """
        # Check if we have the keys for the BM address
        public_key = highlevelcrypto.privToPub(
            private_key.encode('hex')).decode('hex')
        fromAddress = self.getAddress(testnet, public_key)

        result = self.getUnspentTransactions(testnet, [fromAddress])
        if not 'unspent' in result or len(result['unspent']) == 0:
            log_debug("commitTo: No unspent TXs (%s)" % (fromAddress))
            return False

        unspent_txs = result['unspent']
        # filter for those with an amount >= minimum amount
        unspent_txs = filter(
            lambda tx: float(tx['amount']) > BitcoinThread.
            BTC_UNSPENT_MIN_AVAILABLE, unspent_txs)
        if len(unspent_txs) == 0:
            log_debug("commitTo: No unspent TXs >= %d (%s, %s)" %
                      (BitcoinThread.BTC_UNSPENT_MIN_AVAILABLE, fromAddress,
                       result['unspent']))
            return False

        # Find random unspent with an amount >= 0.00010 mBTC
        random.shuffle(unspent_txs)

        while len(unspent_txs) > 0:
            tx = unspent_txs.pop(0)
            log_debug("Trying unspent tx: %s" % tx)

            amount = float(tx['amount'])

            amount_satoshis = int(amount * 100000000)
            change_satoshis = amount_satoshis - BitcoinThread.SATOSHI_COMMITMENT_AMOUNT - BitcoinThread.SATOSHI_TRANSACTION_FEE

            # Code in bitcoin.mktx separates the input string into tx=input[:64] and n=input[65:]
            input_tx = "%s %d" % (tx['tx'], tx['n'])

            commit_payable = {
                "address": commit_address,
                "value": BitcoinThread.SATOSHI_COMMITMENT_AMOUNT
            }
            change_payable = {"address": fromAddress, "value": change_satoshis}

            tx = bitcoin.mktx([input_tx], [commit_payable, change_payable])

            signed_tx = bitcoin.sign(tx, 0, private_key)

            log_debug("Pushing tx: %s" % bitcoin.deserialize(tx))

            if testnet:
                try:
                    result = json.loads(
                        bitcoin.blockr_pushtx(signed_tx, 'testnet'))
                except Exception, e:

                    # If we get {"status":"fail","data":"Could not push your transaction!","code":500,"message":"Did you sign your transaction?"}
                    # in an exception here, it probably means that the referenced inputs in our transaction have been spent in the meantime.
                    try:
                        e_obj = json.loads(e.message)
                        if e_obj["data"] == "Could not push your transaction!":
                            from debug import logger
                            log_warn(
                                "Couldn't push transaction. Sometimes this is because the referenced inputs have been spent in the meantime, %s"
                                % e_obj)
                            # Continue to try the next unspent tx

                            continue
                        else:
                            log_warn(e)
                    except:
                        log_warn(e)

                return 'status' in result and result['status'] == "success"

            else:  # if not testnet
                # I had problems pushing non-testnet transactions to blockr.io,
                # so we're using blockchain.info for this, and that works fine.
                try:
                    result = bitcoin.pushtx(signed_tx)
                    if result.lower() == "transaction submitted":
                        log_debug("Committed to %s" % commit_address)
                        return True
                    else:
                        log_warn("Transaction push fail: %s" % (result, ))
                except Exception, e:
                    log_warn("Transaction push exception: %s" % (e, ))
                continue
Ejemplo n.º 46
0
def test_donation_address(setup_donations, amount):
    wallets = make_wallets(1, wallet_structures=[[1,1,1,0,0]],
                               mean_amt=0.5)
    wallet = wallets[0]['wallet']
    jm_single().bc_interface.sync_wallet(wallet)
    #make a rdp from a simple privkey
    rdp_priv = "\x01"*32
    reusable_donation_pubkey = binascii.hexlify(secp256k1.PrivateKey(
        privkey=rdp_priv, raw=True, ctx=btc.ctx).pubkey.serialize())    
    dest_addr, sign_k = donation_address(reusable_donation_pubkey)
    print dest_addr
    jm_single().bc_interface.rpc('importaddress',
                                [dest_addr, '', False])    
    ins_full = wallet.unspent
    total = sum(x['value'] for x in ins_full.values())
    ins = ins_full.keys()
    output_addr = wallet.get_new_addr(1, 1)
    fee_est = 10000
    outs = [{'value': amount,
             'address': dest_addr}, {'value': total - amount - fee_est,
                                       'address': output_addr}]

    tx = btc.mktx(ins, outs)
    de_tx = btc.deserialize(tx)
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        addr = ins_full[utxo]['address']
        priv = wallet.get_key_from_addr(addr)
        priv = binascii.unhexlify(priv)
        usenonce = binascii.unhexlify(sign_k) if index == 0 else None
        if index == 0:
            log.debug("Applying rdp to input: " + str(ins))
        tx = btc.sign(tx, index, priv, usenonce=usenonce)
    #pushtx returns False on any error
    push_succeed = jm_single().bc_interface.pushtx(tx)
    if push_succeed:
        log.debug(btc.txhash(tx))
    else:
        assert False
    #Role of receiver: regenerate the destination private key,
    #and address, from the nonce of the first input; check it has
    #received the coins.
    detx = btc.deserialize(tx)
    first_utxo_script = detx['ins'][0]['script']
    sig, pub = btc.deserialize_script(first_utxo_script)
    log.debug(sig)
    sig = binascii.unhexlify(sig)
    kGlen = ord(sig[3])
    kG = sig[4:4+kGlen]
    log.debug(binascii.hexlify(kG))
    if kG[0] == "\x00":
        kG = kG[1:]
    #H(rdp private key * K) + rdp should be ==> dest addr
    #Open issue: re-introduce recovery without ECC shenanigans
    #Just cheat by trying both signs for pubkey
    coerced_kG_1 = "02" + binascii.hexlify(kG)
    coerced_kG_2 = "03" + binascii.hexlify(kG)
    for coerc in [coerced_kG_1, coerced_kG_2]:
        c = btc.sha256(btc.multiply(binascii.hexlify(rdp_priv), coerc, True))
        pub_check = btc.add_pubkeys([reusable_donation_pubkey,
                                     btc.privtopub(c+'01', True)], True)
        addr_check = btc.pubtoaddr(pub_check, get_p2pk_vbyte())
        log.debug("Found checked address: " + addr_check)
        if addr_check == dest_addr:
            time.sleep(3)
            received = jm_single().bc_interface.get_received_by_addr(
                    [dest_addr], None)['data'][0]['balance']
            assert received == amount
            return
    assert False
Ejemplo n.º 47
0
    def recv_txio(self, nick, utxo_list, cj_pub, change_addr):
        if nick not in self.nonrespondants:
            log.debug(('recv_txio => nick={} not in '
                       'nonrespondants {}').format(nick, self.nonrespondants))
            return
        self.utxos[nick] = utxo_list
        utxo_data = jm_single().bc_interface.query_utxo_set(self.utxos[nick])
        if None in utxo_data:
            log.debug(('ERROR outputs unconfirmed or already spent. '
                       'utxo_data={}').format(pprint.pformat(utxo_data)))
            # when internal reviewing of makers is created, add it here to
            # immediately quit; currently, the timeout thread suffices.
            return

        total_input = sum([d['value'] for d in utxo_data])
        real_cjfee = calc_cj_fee(self.active_orders[nick]['ordertype'],
                       self.active_orders[nick]['cjfee'], self.cj_amount)
        change_amount = (total_input - self.cj_amount -
            self.active_orders[nick]['txfee'] + real_cjfee)

        # certain malicious and/or incompetent liquidity providers send
        # inputs totalling less than the coinjoin amount! this leads to
        # a change output of zero satoshis, so the invalid transaction
        # fails harmlessly; let's fail earlier, with a clear message.
        if change_amount < jm_single().DUST_THRESHOLD:
            fmt = ('ERROR counterparty requires sub-dust change. nick={}'
                   'totalin={:d} cjamount={:d} change={:d}').format
            log.debug(fmt(nick, total_input, self.cj_amount, change_amount))
            return              # timeout marks this maker as nonresponsive

        self.outputs.append({'address': change_addr, 'value': change_amount})
        fmt = ('fee breakdown for {} totalin={:d} '
               'cjamount={:d} txfee={:d} realcjfee={:d}').format
        log.debug(fmt(nick, total_input, self.cj_amount,
            self.active_orders[nick]['txfee'], real_cjfee))
        cj_addr = btc.pubtoaddr(cj_pub, get_p2pk_vbyte())
        self.outputs.append({'address': cj_addr, 'value': self.cj_amount})
        self.cjfee_total += real_cjfee
        self.maker_txfee_contributions += self.active_orders[nick]['txfee']
        self.nonrespondants.remove(nick)
        if len(self.nonrespondants) > 0:
            log.debug('nonrespondants = ' + str(self.nonrespondants))
            return
        log.debug('got all parts, enough to build a tx')
        self.nonrespondants = list(self.active_orders.keys())

        my_total_in = sum([va['value'] for u, va in
                           self.input_utxos.iteritems()])
        if self.my_change_addr:
            #Estimate fee per choice of next/3/6 blocks targetting.
            estimated_fee = estimate_tx_fee(len(sum(
                self.utxos.values(),[])), len(self.outputs)+2)
            log.debug("Based on initial guess: "+str(
                self.total_txfee)+", we estimated a fee of: "+str(estimated_fee))
            #reset total
            self.total_txfee = estimated_fee
        my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0)
        my_change_value = (
            my_total_in - self.cj_amount - self.cjfee_total - my_txfee)
        #Since we could not predict the maker's inputs, we may end up needing
        #too much such that the change value is negative or small. Note that 
        #we have tried to avoid this based on over-estimating the needed amount
        #in SendPayment.create_tx(), but it is still a possibility if one maker
        #uses a *lot* of inputs.
        if self.my_change_addr and my_change_value <= 0:
            raise ValueError("Calculated transaction fee of: "+str(
                self.total_txfee)+" is too large for our inputs;Please try again.")
        elif self.my_change_addr and my_change_value <= jm_single().DUST_THRESHOLD:
            log.debug("Dynamically calculated change lower than dust: "+str(
                my_change_value)+"; dropping.")
            self.my_change_addr = None
            my_change_value = 0
        log.debug('fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d'
                  % (my_total_in, my_txfee, self.maker_txfee_contributions,            
                  self.cjfee_total, my_change_value))
        if self.my_change_addr is None:
            if my_change_value != 0 and abs(my_change_value) != 1:
                # seems you wont always get exactly zero because of integer
                # rounding so 1 satoshi extra or fewer being spent as miner
                # fees is acceptable
                log.debug(('WARNING CHANGE NOT BEING '
                           'USED\nCHANGEVALUE = {}').format(my_change_value))
        else:
            self.outputs.append({'address': self.my_change_addr,
                                 'value': my_change_value})
        self.utxo_tx = [dict([('output', u)])
                        for u in sum(self.utxos.values(), [])]
        self.outputs.append({'address': self.coinjoin_address(),
                             'value': self.cj_amount})
        random.shuffle(self.utxo_tx)
        random.shuffle(self.outputs)
        tx = btc.mktx(self.utxo_tx, self.outputs)
        log.debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx)))
        #Re-calculate a sensible timeout wait based on the throttling
        #settings and the tx size.
        #Calculation: Let tx size be S; tx undergoes two b64 expansions, 1.8*S
        #So we're sending N*1.8*S over the wire, and the
        #maximum bytes/sec = B, means we need (1.8*N*S/B) seconds,
        #and need to add some leeway for network delays, we just add the
        #contents of jm_single().maker_timeout_sec (the user configured value)
        self.maker_timeout_sec = (len(tx) * 1.8 * len(
            self.active_orders.keys()))/(B_PER_SEC) + jm_single().maker_timeout_sec
        log.debug("Based on transaction size: " + str(
            len(tx)) + ", calculated time to wait for replies: " + str(
            self.maker_timeout_sec))
        self.all_responded = True
        with self.timeout_lock:
            self.timeout_lock.notify()
        self.msgchan.send_tx(self.active_orders.keys(), tx)

        self.latest_tx = btc.deserialize(tx)
        for index, ins in enumerate(self.latest_tx['ins']):
            utxo = ins['outpoint']['hash'] + ':' + str(
                    ins['outpoint']['index'])
            if utxo not in self.input_utxos.keys():
                continue
            # placeholders required
            ins['script'] = 'deadbeef'
Ejemplo n.º 48
0
 def test_P2SH_P2WSH_ALL_SIGHASH(self):
     tx = TEST_CASES[3]
     VIN_AMOUNT = int(9.87654321 * 10**8)
     deserialized = deserialize(tx['unsigned'])
     serialized = serialize(deserialized)
     self.assertEqual(serialized, tx['unsigned'])
     self.assertEqual(deserialized['locktime'], tx['locktime'])
     ins = self.get_pybtc_vins(tx)
     outs = self.get_pybtc_outs(tx)
     generated_tx = mktx(ins, outs)
     stripped_tx = strip_witness_data(generated_tx)
     self.assertEqual(stripped_tx, serialized)
     priv0 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][0])
     priv1 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][1])
     priv2 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][2])
     priv3 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][3])
     priv4 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][4])
     priv5 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][5])
     witness_script = mk_multisig_script(privtopub(priv0),
                                         privtopub(priv1),
                                         privtopub(priv2),
                                         privtopub(priv3),
                                         privtopub(priv4),
                                         privtopub(priv5),
                                         6)
     self.assertEqual(witness_script, tx['ins'][0]['witness_script'])
     sign0 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv0,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_ALL)
     sign1 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv1,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_NONE)
     sign2 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv2,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_SINGLE)
     sign3 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv3,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_ALL|SIGHASH_ANYONECANPAY)
     sign4 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv4,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_NONE|SIGHASH_ANYONECANPAY)
     sign5 = segwit_multisign(generated_tx,
                              0,
                              witness_script,
                              priv5,
                              VIN_AMOUNT,
                              hashcode=SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)
     signed = apply_segwit_multisignatures(stripped_tx, 0, witness_script,
                                           [sign0, sign1, sign2, sign3, sign4, sign5],
                                           nested=True)
     self.assertEqual(signed, tx['signed'])
     print('[P2WSH 6-of-6 multisig NESTED in P2SH] SIGHASH_SINGLE\SIGHASH_ALL\SIGHASH_NONE & ANYONECANPAY')
Ejemplo n.º 49
0
    def add_order_confirmation(self,
                               payout_address,
                               comments=None,
                               shipper=None,
                               tracking_number=None,
                               est_delivery=None,
                               url=None,
                               password=None):
        """
        Add the vendor's order confirmation to the contract.
        """

        if not self.testnet and not (payout_address[:1] == "1"
                                     or payout_address[:1] == "3"):
            raise Exception("Bitcoin address is not a mainnet address")
        elif self.testnet and not \
                (payout_address[:1] == "n" or payout_address[:1] == "m" or payout_address[:1] == "2"):
            raise Exception("Bitcoin address is not a testnet address")
        try:
            bitcoin.b58check_to_hex(payout_address)
        except AssertionError:
            raise Exception("Invalid Bitcoin address")
        conf_json = {
            "vendor_order_confirmation": {
                "invoice": {
                    "ref_hash":
                    digest(json.dumps(self.contract, indent=4)).encode("hex")
                }
            }
        }
        if self.contract["vendor_offer"]["listing"]["metadata"][
                "category"] == "physical good":
            shipping = {
                "shipper": shipper,
                "tracking_number": tracking_number,
                "est_delivery": est_delivery
            }
            conf_json["vendor_order_confirmation"]["invoice"][
                "shipping"] = shipping
        elif self.contract["vendor_offer"]["listing"]["metadata"][
                "category"] == "digital good":
            content_source = {"url": url, "password": password}
            conf_json["vendor_order_confirmation"]["invoice"][
                "content_source"] = content_source
        if comments:
            conf_json["vendor_order_confirmation"]["invoice"][
                "comments"] = comments
        confirmation = json.dumps(
            conf_json["vendor_order_confirmation"]["invoice"], indent=4)
        conf_json["vendor_order_confirmation"]["signature"] = \
            self.keychain.signing_key.sign(confirmation, encoder=nacl.encoding.HexEncoder)[:128]
        order_id = digest(json.dumps(self.contract, indent=4)).encode("hex")
        # apply signatures
        outpoints = pickle.loads(self.db.Sales().get_outpoint(order_id))
        redeem_script = self.contract["buyer_order"]["order"]["payment"][
            "redeem_script"]
        value = 0
        for output in outpoints:
            value += output["value"]
            del output["value"]
        value -= TRANSACTION_FEE
        outs = [{'value': value, 'address': payout_address}]
        tx = bitcoin.mktx(outpoints, outs)
        signatures = []
        chaincode = self.contract["buyer_order"]["order"]["payment"][
            "chaincode"]
        masterkey_v = bitcoin.bip32_extract_key(
            self.keychain.bitcoin_master_privkey)
        vendor_priv = derive_childkey(masterkey_v, chaincode,
                                      bitcoin.MAINNET_PRIVATE)
        for index in range(0, len(outpoints)):
            sig = bitcoin.multisign(tx, index, redeem_script, vendor_priv)
            signatures.append({"input_index": index, "signature": sig})
        conf_json["vendor_order_confirmation"]["invoice"]["payout"] = {}
        conf_json["vendor_order_confirmation"]["invoice"]["payout"][
            "address"] = payout_address
        conf_json["vendor_order_confirmation"]["invoice"]["payout"][
            "value"] = value
        conf_json["vendor_order_confirmation"]["invoice"]["payout"][
            "signature(s)"] = signatures

        self.contract["vendor_order_confirmation"] = conf_json[
            "vendor_order_confirmation"]
        self.db.Sales().update_status(order_id, 2)
        file_path = DATA_FOLDER + "store/listings/in progress/" + order_id + ".json"
        with open(file_path, 'w') as outfile:
            outfile.write(json.dumps(self.contract, indent=4))
    def commitTo(self, testnet, commit_address, private_key):
        """
        Commit to some address on the blockchain
        """
        # Check if we have the keys for the BM address
        public_key = highlevelcrypto.privToPub( private_key.encode('hex') ).decode('hex')
        fromAddress = self.getAddress( testnet, public_key )
        
        result = self.getUnspentTransactions( testnet, [fromAddress] )
        if not 'unspent' in result or len( result['unspent'] ) == 0:
            log_debug( "commitTo: No unspent TXs (%s)" % ( fromAddress ) )
            return False   
        
        unspent_txs = result['unspent']
        # filter for those with an amount >= minimum amount
        unspent_txs = filter( lambda tx: float( tx['amount'] ) > BitcoinThread.BTC_UNSPENT_MIN_AVAILABLE, unspent_txs )
        if len( unspent_txs ) == 0:
            log_debug( "commitTo: No unspent TXs >= %d (%s, %s)" % ( BitcoinThread.BTC_UNSPENT_MIN_AVAILABLE, fromAddress, result['unspent'] ) )
            return False
        
        # Find random unspent with an amount >= 0.00010 mBTC
        random.shuffle( unspent_txs )
        
        while len( unspent_txs ) > 0:
            tx = unspent_txs.pop( 0 )
            log_debug( "Trying unspent tx: %s" % tx )
            
            amount = float( tx['amount'] )

            amount_satoshis = int( amount * 100000000 )
            change_satoshis = amount_satoshis - BitcoinThread.SATOSHI_COMMITMENT_AMOUNT - BitcoinThread.SATOSHI_TRANSACTION_FEE
            
            # Code in bitcoin.mktx separates the input string into tx=input[:64] and n=input[65:]
            input_tx = "%s %d" % ( tx['tx'], tx['n'] )
            
            commit_payable = { "address": commit_address, "value": BitcoinThread.SATOSHI_COMMITMENT_AMOUNT }
            change_payable = { "address": fromAddress, "value": change_satoshis }
            
            tx = bitcoin.mktx( [input_tx], [ commit_payable, change_payable ] )
            
            signed_tx = bitcoin.sign(tx, 0, private_key )
            
            log_debug( "Pushing tx: %s" % bitcoin.deserialize( tx ) )
            
            if testnet:
                try:
                    result = json.loads( bitcoin.blockr_pushtx(signed_tx, 'testnet') )
                except Exception, e:
                    
                    # If we get {"status":"fail","data":"Could not push your transaction!","code":500,"message":"Did you sign your transaction?"}
                    # in an exception here, it probably means that the referenced inputs in our transaction have been spent in the meantime.
                    try:
                        e_obj = json.loads( e.message )
                        if e_obj["data"] == "Could not push your transaction!":
                            from debug import logger
                            log_warn( "Couldn't push transaction. Sometimes this is because the referenced inputs have been spent in the meantime, %s" % e_obj )
                            # Continue to try the next unspent tx
                            
                            continue
                        else:
                            log_warn( e )
                    except:
                        log_warn( e )
                        
                return 'status' in result and result['status'] == "success"
            
            else: # if not testnet
                # I had problems pushing non-testnet transactions to blockr.io,
                # so we're using blockchain.info for this, and that works fine.
                try:
                    result = bitcoin.pushtx( signed_tx )
                    if result.lower() == "transaction submitted":
                        log_debug( "Committed to %s" % commit_address )
                        return True
                    else:
                        log_warn( "Transaction push fail: %s" % ( result, ) )
                except Exception, e:
                    log_warn( "Transaction push exception: %s" % ( e, ) )
                continue
Ejemplo n.º 51
0
    def recv_txio(self, nick, utxo_list, auth_pub, cj_addr, change_addr):
        if nick not in self.nonrespondants:
            log.debug(('recv_txio => nick={} not in '
                       'nonrespondants {}').format(nick, self.nonrespondants))
            return
        self.utxos[nick] = utxo_list
        utxo_data = jm_single().bc_interface.query_utxo_set(self.utxos[nick])
        if None in utxo_data:
            log.debug(('ERROR outputs unconfirmed or already spent. '
                       'utxo_data={}').format(pprint.pformat(utxo_data)))
            # when internal reviewing of makers is created, add it here to
            # immediately quit; currently, the timeout thread suffices.
            return
        #Complete maker authorization:
        #Extract the address fields from the utxos
        #Construct the Bitcoin address for the auth_pub field
        #Ensure that at least one address from utxos corresponds.
        input_addresses = [d['address'] for d in utxo_data]
        auth_address = btc.pubkey_to_address(auth_pub, get_p2pk_vbyte())
        if not auth_address in input_addresses:
            log.debug("ERROR maker's authorising pubkey is not included "
                      "in the transaction: " + str(auth_address))
            return

        total_input = sum([d['value'] for d in utxo_data])
        real_cjfee = calc_cj_fee(self.active_orders[nick]['ordertype'],
                                 self.active_orders[nick]['cjfee'],
                                 self.cj_amount)
        change_amount = (total_input - self.cj_amount -
                         self.active_orders[nick]['txfee'] + real_cjfee)

        # certain malicious and/or incompetent liquidity providers send
        # inputs totalling less than the coinjoin amount! this leads to
        # a change output of zero satoshis, so the invalid transaction
        # fails harmlessly; let's fail earlier, with a clear message.
        if change_amount < jm_single().DUST_THRESHOLD:
            fmt = ('ERROR counterparty requires sub-dust change. nick={}'
                   'totalin={:d} cjamount={:d} change={:d}').format
            log.debug(fmt(nick, total_input, self.cj_amount, change_amount))
            return  # timeout marks this maker as nonresponsive

        self.outputs.append({'address': change_addr, 'value': change_amount})
        fmt = ('fee breakdown for {} totalin={:d} '
               'cjamount={:d} txfee={:d} realcjfee={:d}').format
        log.debug(
            fmt(nick, total_input, self.cj_amount,
                self.active_orders[nick]['txfee'], real_cjfee))
        self.outputs.append({'address': cj_addr, 'value': self.cj_amount})
        self.cjfee_total += real_cjfee
        self.maker_txfee_contributions += self.active_orders[nick]['txfee']
        self.nonrespondants.remove(nick)
        if len(self.nonrespondants) > 0:
            log.debug('nonrespondants = ' + str(self.nonrespondants))
            return
        log.debug('got all parts, enough to build a tx')
        self.nonrespondants = list(self.active_orders.keys())

        my_total_in = sum(
            [va['value'] for u, va in self.input_utxos.iteritems()])
        if self.my_change_addr:
            #Estimate fee per choice of next/3/6 blocks targetting.
            estimated_fee = estimate_tx_fee(len(sum(self.utxos.values(), [])),
                                            len(self.outputs) + 2)
            log.debug("Based on initial guess: " + str(self.total_txfee) +
                      ", we estimated a fee of: " + str(estimated_fee))
            #reset total
            self.total_txfee = estimated_fee
        my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0)
        my_change_value = (my_total_in - self.cj_amount - self.cjfee_total -
                           my_txfee)
        #Since we could not predict the maker's inputs, we may end up needing
        #too much such that the change value is negative or small. Note that
        #we have tried to avoid this based on over-estimating the needed amount
        #in SendPayment.create_tx(), but it is still a possibility if one maker
        #uses a *lot* of inputs.
        if self.my_change_addr and my_change_value <= 0:
            raise ValueError("Calculated transaction fee of: " +
                             str(self.total_txfee) +
                             " is too large for our inputs;Please try again.")
        elif self.my_change_addr and my_change_value <= jm_single(
        ).DUST_THRESHOLD:
            log.debug("Dynamically calculated change lower than dust: " +
                      str(my_change_value) + "; dropping.")
            self.my_change_addr = None
            my_change_value = 0
        log.debug(
            'fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d'
            % (my_total_in, my_txfee, self.maker_txfee_contributions,
               self.cjfee_total, my_change_value))
        if self.my_change_addr is None:
            if my_change_value != 0 and abs(my_change_value) != 1:
                # seems you wont always get exactly zero because of integer
                # rounding so 1 satoshi extra or fewer being spent as miner
                # fees is acceptable
                log.debug(('WARNING CHANGE NOT BEING '
                           'USED\nCHANGEVALUE = {}').format(my_change_value))
        else:
            self.outputs.append({
                'address': self.my_change_addr,
                'value': my_change_value
            })
        self.utxo_tx = [
            dict([('output', u)]) for u in sum(self.utxos.values(), [])
        ]
        self.outputs.append({
            'address': self.coinjoin_address(),
            'value': self.cj_amount
        })
        random.shuffle(self.utxo_tx)
        random.shuffle(self.outputs)
        tx = btc.mktx(self.utxo_tx, self.outputs)
        log.debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx)))
        #Re-calculate a sensible timeout wait based on the throttling
        #settings and the tx size.
        #Calculation: Let tx size be S; tx undergoes two b64 expansions, 1.8*S
        #So we're sending N*1.8*S over the wire, and the
        #maximum bytes/sec = B, means we need (1.8*N*S/B) seconds,
        #and need to add some leeway for network delays, we just add the
        #contents of jm_single().maker_timeout_sec (the user configured value)
        self.maker_timeout_sec = (len(tx) * 1.8 * len(self.active_orders.keys(
        ))) / (B_PER_SEC) + jm_single().maker_timeout_sec
        log.debug("Based on transaction size: " + str(len(tx)) +
                  ", calculated time to wait for replies: " +
                  str(self.maker_timeout_sec))
        self.all_responded = True
        with self.timeout_lock:
            self.timeout_lock.notify()
        self.msgchan.send_tx(self.active_orders.keys(), tx)

        self.latest_tx = btc.deserialize(tx)
        for index, ins in enumerate(self.latest_tx['ins']):
            utxo = ins['outpoint']['hash'] + ':' + str(
                ins['outpoint']['index'])
            if utxo not in self.input_utxos.keys():
                continue
            # placeholders required
            ins['script'] = 'deadbeef'
Ejemplo n.º 52
0
from bitcoin_101 import bitprint

if __name__ == '__main__':
    key = bitcoin.random_key()

    outpoints = {
        '97f7c7d8ac85e40c255f8a763b6cd9a68f3a94d2e93e8bfa08f977b92e55465e:0':
        50000,
        '4cc806bb04f730c445c60b3e0f4f44b54769a1c196ca37d8d4002135e4abd171:1':
        50000
    }

    tx = bitcoin.mktx([{
        'output': k,
        'value': v
    } for k, v in outpoints.items()],
                      [{
                          'value': 90000,
                          'address': '16iw1MQ1sy1DtRPYw3ao1bCamoyBJtRB4t'
                      }])
    bitprint('Raw transaction:', tx)
    bitprint('Deserialized transaction')
    bitprint(bitcoin.deserialize(tx))

    one_signed_tx = bitcoin.sign(tx, 0, key)
    bitprint(one_signed_tx)
    bitprint('ScriptSig for the first input')
    bitprint(
        bitcoin.deserialize_script(
            bitcoin.deserialize(one_signed_tx)['ins'][0]['script']))
    bitprint('ScriptPubKey for the first output')
    bitprint(
Ejemplo n.º 53
0
def moveTokens(tokenid, outputs, change):
    """ move given amount of tokens to given outputs.
    tokenid: name of the token to deal with
    outputs is a list of (output, amount) pairs.
    change is the receiving address for change.
    Returns a pair (TokenTransaction, OnchainTransaction) or None if an error occured.
    Does not change the DB.

    Autocreates a mint transaction if the outputs exceed the available funds and there is a blank output plus the corresponding private key available.
    """
    log.info("Trying to move %s tokens to %s outputs plus one change address",
             tokenid, len(outputs))
    token_addr = tokenval.tok2addr(tokenid)

    for x in outputs:
        if x[1] < 0:
            log.error("Negative outputs not allowed.")
            return None, None

    # total needed amount of tokens
    needed = sum(x[1] for x in outputs)
    log.info("Total needed amount of tokens: %d", needed)

    # sum of incoming BCH satoshis
    bch_insum = 0

    # sum on incoming tokens
    token_insum = 0

    utxo_avail = tokenval.unspentOutputsForToken(token_addr)
    log.info("Known UTXOs for token %s: %d.", tokenid, len(utxo_avail))
    # collect enough inputs until we've got everything we need
    privs = {}

    mints = []
    bch_inputs = []
    bch_outputs = []

    for utxo in utxo_avail:
        value, bch_value = takeUtxo(utxo,
                                    privs,
                                    bch_inputs,
                                    skip_if_blank=True)
        if value is None or bch_value is None:
            log.info("Skipping UTXO %s.", utxo)
            continue
        token_insum += value
        bch_insum += bch_value
        if token_insum >= needed:
            break

    log.info("Collected %d inputs for a total of %d %s and %d satoshis.",
             len(bch_inputs), token_insum, tokenid, bch_insum)

    if token_insum < needed:
        # FIXME: code dup
        log.info("Less tokens %d available than needed %d.", token_insum,
                 needed)
        blank = tokenval.unspentBlank(token_addr)
        if blank is None:
            log.error(
                "And no blank outputs are available to create new tokens.")
            return None, None
        value, bch_value = takeUtxo(blank, privs, bch_inputs)

        assert (value is None)
        if bch_value is None:
            log.info("Blank could not be taken.")
            return None, None
        mints.append((blank.txhash, blank.outnum, needed - token_insum))
        log.info("Taking blank unspent output %s and minting %d %s.", blank,
                 needed - token_insum, tokenid)

        token_insum += needed - token_insum
        bch_insum += bch_value

    l = len(outputs) + 1  # +1 for change output

    miner_fee = 200 * l  # total miner fee for transaction

    min_funds = 1000

    log.info("Total miner fee: %d", miner_fee)

    if bch_insum < l * min_funds + miner_fee:  # 1 satoshi per token output plus miner fee available
        log.info(
            "Needing extra inputs for miner fees, having %d satoshis and needing %d satoshis.",
            bch_insum, l * min_funds + miner_fee)
        for utxo in tokenval.allUnspentOutputs():
            if ("TOKEN_" + utxo.token_addr
                    in tokenval.db) or utxo.value is not None:
                log.info("Skipping UTXO %s as it is a token output.", utxo)
                continue
            value, bch_value = takeUtxo(utxo, privs, bch_inputs)
            assert (value is None)
            if bch_value is None:
                continue
            bch_insum += bch_value
            if bch_insum >= l * min_funds + miner_fee:
                break
    if bch_insum < l * min_funds + miner_fee:  # 1 satoshi per token output plus miner fee available
        log.info(
            "Not enough extra inputs available, having %d satoshis and needing %d satoshis",
            bch_insum, l * min_funds + miner_fee)
        return None, None

    tok_outputs = []

    for out in outputs:
        addr, val = out
        bch_outputs.append({"value": min_funds, "address": addr})
        log.info("Adding output %s with %d tokens.", addr, val)
        tok_outputs.append(val)

    bch_outputs.append({
        "value": min_funds + bch_insum - miner_fee - l * min_funds,
        "address": change
    })
    log.info("Adding output %s with %d tokens.", change, token_insum - needed)
    tok_outputs.append(token_insum - needed)

    log.info("Token mints: %d", len(mints))
    log.info("Token outputs: %d", len(outputs))
    toktxn = tokenval.TokenTransaction(token_addr, mints, tok_outputs,
                                       list(privs.values()))

    log.info("BCH transaction inputs: %s", bch_inputs)
    log.info("BCH transaction outputs: %s", bch_outputs)

    bchtxn = bitcoin.mktx(bch_inputs, bch_outputs)
    log.info("Created unsigned BCH transaction.")
    bchtxn = bitcoin.transaction.mk_opreturn(codecs.decode(toktxn.hsh, "hex"),
                                             bchtxn)
    log.info("Added OP_RETURN pointer.")

    for i, bch_in in enumerate(bch_inputs):
        prevoutstr = bch_in["output"]
        # bch_inputs.append(
        #     { "output" : prevoutstr,
        #       "amount" : utxo.bch_value})

        log.info("Signing BCH input %d: %s", i, bch_in)
        bchtxn = bitcoin.segwit_sign(bchtxn,
                                     i,
                                     privs[prevoutstr],
                                     bch_in["amount"],
                                     hashcode=bitcoin.SIGHASH_ALL
                                     | bitcoin.SIGHASH_FORKID,
                                     separator_index=None)

    octxn = tokenval.OnchainTransaction(bchtxn)

    return toktxn, octxn