Ejemplo n.º 1
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.º 2
0
def cosign(master_xpriv, recovery_package):
    raw_txs = recovery_package['txs']
    print "Signing %d transactions" % len(raw_txs)
    for tx_index, tx_raw in enumerate(raw_txs):
        print "\nTransaction #", tx_index
        hex_tx = tx_raw['bytes']
        tx = bitcoin.transaction.deserialize(unhexlify(hex_tx))
        keypaths = tx_raw['input_paths']
        keypairs = {}
        for p in keypaths:
            xpriv = seedlib.bip32_child(master_xpriv, p)
            priv = bitcoin.bip32_extract_key(xpriv)
            pub = bitcoin.privtopub(priv)
            keypairs[pub] = priv
        for i, inp in enumerate(tx['ins']):
            (sigs, pubkeys, redeemScript, M,
             N) = multisig.decode_multisig_script(inp['script'])
            for p in pubkeys:
                if p in keypairs:
                    sig_new = bitcoin.multisign(hex_tx, i, redeemScript,
                                                keypairs[p])
                    sigs.append(sig_new)
                    tx_new = bitcoin.apply_multisignatures(
                        unhexlify(hex_tx), i, redeemScript, sigs)
                    print "Signed transaction %d input %d" % (tx_index, i)

                    # replace tx with newly signed transaction
                    hex_tx = hexlify(tx_new)
        recovery_package['txs'][tx_index]['bytes'] = hex_tx
    return recovery_package
Ejemplo n.º 3
0
 def seal(self):
     index = 0
     for p in self.participants:
         for i, o in enumerate(p.tx_inputs):
             if o.signature:
                 signatures = [o.signature]
                 sealing_signature = self.sealer and self.sealer.signatures.get(
                     o.outpoint_hash + ':' + str(o.outpoint_index), None
                 )
                 if not sealing_signature:
                     raise exceptions.MissingSealerSignature(
                         o.outpoint_hash + ':' + str(o.outpoint_index)
                     )
                 sealing_signature and signatures.append(sealing_signature)
                 if o.is_segwit:
                     assert o.script
                     self._raw = bitcoin.apply_segwit_multisignatures(
                         self._raw, index, o.script, signatures, nested=True
                     )
                 else:
                     self._raw = bitcoin.apply_multisignatures(self._raw, index, o.script, signatures)
             else:
                 raise exceptions.MissingClientSignature
             index += 1
     return self
Ejemplo n.º 4
0
	def pushPayment(self):
		#compliantScript = bitcoin.mk_multisig_script([bitcoin.privtopub(self.privS), self.pubKeyClient], 2,2)  # inversion!!!!
		compliantScript = bitcoin.mk_multisig_script([self.pubKeyClient, bitcoin.privtopub(self.privS)], 2,2)
		sigServer = bitcoin.multisign(self.lastpayment, 0, compliantScript, self.privS)
		signedPtx = bitcoin.apply_multisignatures(self.lastpayment, 0, compliantScript, [self.lastsig, sigServer])
		print 'Broadcast the very last payment. Just got richer. Tx hash:', bitcoin.txhash(signedPtx)
		bitcoin.pushtx(signedPtx)
Ejemplo n.º 5
0
def main():
    ''' Our main function. '''

    # Two of the three keys. The missing one:
    # 3347122612f83ad80517dfef41c8b6a6400eef1949df732ce60a4e7c9d00ccb0
    priv_keys = [
        fix_priv('93d716a849c4c215b3049670ef7c1eba9b19c0452caf37b8f3383dcdd5577440'),
        fix_priv('7b389eb21fb17ef293be410009d436a495e0942005b1e6845f64429a5ff13420'),
    ]

    # Same order that Counterparty uses.  Order should not matter.
    pub_0 = priv2pub(fix_priv('3347122612f83ad80517dfef41c8b6a6400eef1949df732ce60a4e7c9d00ccb0'))
    pub_1 = priv2pub(priv_keys[0])
    pub_2 = priv2pub(priv_keys[1])

    script_not_real = bitcoin.mk_multisig_script(pub_0, pub_1, pub_2, 2, 3)

    for priv in priv_keys:
        check_priv_key_looks_good(priv)

    deserialized_txn = deserialize_tx(get_raw_tx())

    print('The raw transaction: {}\n'.format(get_raw_tx()))

    print('The decoded transaction:\n{}\n'.format(pprint.pformat(deserialized_txn)))


    bitcoind_script = '52410427db4059d24bab05df3f6bcc768fb01bd976b973f93e72cce2dfbfbed5a32056c9040a2c2ea4c10c812a54fed7ff2e6a917dbc843362d398f6ace4000fafa5c641043e12a6cb1c7c156f789110abf8397b714047414b5a32c742f17ccf93ff23bdf3128f946207086bcef012558240cd16182c741123e93ed18327c4cd6ebac668a94104e4168c172283c7dfaa85d2004f763a28bf6d0f1602fc1452ccec62a7c8a66e422af1410fbf24a47355ddc43dfe3491cb1b806574ccd1c434680466dcff926f0153ae'

    # You might want to use _ for something.
    signatures_A = []
    signatures_B = []
    for tx_index, tx_input in enumerate(deserialized_txn['ins']):
        assert tx_index == 0 # Only once!
        script_real = tx_input['script']
        assert script_real == bitcoind_script
        assert len(script_real) == len(script_not_real)
        signatures_A.append(bitcoin.multisign(get_raw_tx(), tx_index, script_real, priv_keys[0]))
        signatures_B.append(bitcoin.multisign(get_raw_tx(), tx_index, script_real, priv_keys[1]))
    fully_signed_tx = get_raw_tx()
    fully_signed_tx_before = fully_signed_tx
    for tx_index, tx_input in enumerate(deserialized_txn['ins']):
        assert tx_index == 0 # Only once!
        fully_signed_tx = bitcoin.apply_multisignatures( \
            fully_signed_tx,
            tx_index,
            script_real,
            signatures_A[tx_index],
            signatures_B[tx_index])

    assert fully_signed_tx_before != fully_signed_tx

    # may the bytes be with you
    print('Fully signed: {}'.format(fully_signed_tx))
Ejemplo n.º 6
0
 def pushPayment(self):
     #compliantScript = bitcoin.mk_multisig_script([bitcoin.privtopub(self.privS), self.pubKeyClient], 2,2)  # inversion!!!!
     compliantScript = bitcoin.mk_multisig_script(
         [self.pubKeyClient,
          bitcoin.privtopub(self.privS)], 2, 2)
     sigServer = bitcoin.multisign(self.lastpayment, 0, compliantScript,
                                   self.privS)
     signedPtx = bitcoin.apply_multisignatures(self.lastpayment, 0,
                                               compliantScript,
                                               [self.lastsig, sigServer])
     print 'Broadcast the very last payment. Just got richer. Tx hash:', bitcoin.txhash(
         signedPtx)
     bitcoin.pushtx(signedPtx)
Ejemplo n.º 7
0
def tx_sign_multisig(tx,
                     idx,
                     redeem_script,
                     private_keys,
                     hashcode=bitcoin.SIGHASH_ALL):
    """
    Sign a p2sh multisig input.
    Return the signed transaction

    TODO: move to virtualchain
    """
    # sign in the right order.  map all possible public keys to their private key
    privs = {}
    for pk in private_keys:
        pubk = keylib.ECPrivateKey(pk).public_key().to_hex()

        compressed_pubkey = keylib.key_formatting.compress(pubk)
        uncompressed_pubkey = keylib.key_formatting.decompress(pubk)

        privs[compressed_pubkey] = pk
        privs[uncompressed_pubkey] = pk

    m, public_keys = virtualchain.parse_multisig_redeemscript(
        str(redeem_script))

    used_keys, sigs = [], []
    for public_key in public_keys:
        if public_key not in privs:
            continue

        if len(used_keys) == m:
            break

        assert public_key not in used_keys, 'Tried to reuse key {}'.format(
            public_key)

        pk_str = privs[public_key]
        used_keys.append(public_key)

        sig = tx_make_input_signature(tx, idx, redeem_script, pk_str, hashcode)
        sigs.append(sig)

    assert len(
        used_keys) == m, 'Missing private keys (used {}, required {})'.format(
            len(used_keys), m)
    return bitcoin.apply_multisignatures(tx, idx, str(redeem_script), sigs)
Ejemplo n.º 8
0
def tx_sign_multisig(blockstack_tx,
                     idx,
                     redeem_script,
                     private_keys,
                     hashcode=bitcoin.SIGHASH_ALL):
    """
    Sign a p2sh multisig input.
    Return the signed transaction
    """

    # sign in the right order
    privs = dict([
        (virtualchain.BitcoinPrivateKey(str(pk_str)).public_key().to_hex(),
         str(pk_str)) for pk_str in private_keys
    ])
    m, public_keys = virtualchain.parse_multisig_redeemscript(
        str(redeem_script))

    used_keys = []
    sigs = []

    for ki in xrange(0, len(public_keys)):
        if not privs.has_key(public_keys[ki]):
            continue

        if len(used_keys) == m:
            break

        assert public_keys[
            ki] not in used_keys, "Tried to reuse key %s" % public_keys[ki]

        pk_str = privs[public_keys[ki]]
        used_keys.append(public_keys[ki])

        pk_hex = virtualchain.BitcoinPrivateKey(str(pk_str)).to_hex()
        sig = bitcoin.multisign(blockstack_tx,
                                idx,
                                str(redeem_script),
                                pk_hex,
                                hashcode=hashcode)
        sigs.append(sig)

    assert len(used_keys) == m, "Missing private keys"

    return bitcoin.apply_multisignatures(blockstack_tx, idx,
                                         str(redeem_script), sigs)
Ejemplo n.º 9
0
def tx_sign_multisig(tx,
                     idx,
                     redeem_script,
                     private_keys,
                     hashcode=bitcoin.SIGHASH_ALL):
    """
    Sign a p2sh multisig input.
    Return the signed transaction

    TODO: move to virtualchain
    """
    # sign in the right order
    privs = {
        virtualchain.BitcoinPrivateKey(str(pk)).public_key().to_hex(): str(pk)
        for pk in private_keys
    }
    m, public_keys = virtualchain.parse_multisig_redeemscript(
        str(redeem_script))

    used_keys, sigs = [], []
    for public_key in public_keys:
        if public_key not in privs:
            continue

        if len(used_keys) == m:
            break

        assert public_key not in used_keys, 'Tried to reuse key {}'.format(
            public_key)

        pk_str = privs[public_key]
        used_keys.append(public_key)

        pk_hex = virtualchain.BitcoinPrivateKey(str(pk_str)).to_hex()

        sig = tx_make_input_signature(tx, idx, redeem_script, pk_str, hashcode)
        # sig = bitcoin.multisign(tx, idx, str(redeem_script), pk_hex, hashcode=hashcode)
        sigs.append(sig)

    assert len(used_keys) == m, 'Missing private keys'
    return bitcoin.apply_multisignatures(tx, idx, str(redeem_script), sigs)
Ejemplo n.º 10
0
    def sign( self, privkey ):
	if( self.tx is None or self.redeemScript is None ):
            raise RuntimeError( "You have not entered in a tx to sign" )

        try:
            pub = bitcoin.privtopub( privkey )
            if pub not in self.pubs:
                pub = bitcoin.encode_pubkey( pub, 'hex_compressed' )
                if pub not in self.pubs: 
                    raise ValueError( "Key doesn't match any public keys in redeemscript" )
            
            stx = bitcoin.serialize( self.tx )
            sigs = self.extract_sigs( self.tx ) 
            sigs.append( bitcoin.multisign( stx, 0, self.redeemScript, privkey ) )
            sigs = self.reorder_sigs( sigs )
            stx = bitcoin.apply_multisignatures( stx, 0, self.redeemScript, sigs )
            return stx, ( len(sigs) >= self.neededSigs )
        except ValueError as e:
            raise RuntimeError( "Key Error", str( e ) )
        except Exception as e: 
            raise RuntimeError( "Unexpected Error", "Unexpected error formating key: " + str( e ) )
Ejemplo n.º 11
0
            def cb(ec, history, order):
                if ec is not None:
                    self.log.error("Error fetching history: %s", ec)
                    # TODO: Send error message to GUI
                    return

                unspent = [row[:4] for row in history if row[4] is None]

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

                seller_signatures = []
                print 'private key ', self.transport.settings['privkey']
                for x in range(0, len(inputs)):
                    ms = multisign(
                        tx, x, script, self.transport.settings['privkey']
                    )
                    print 'seller sig', ms
                    seller_signatures.append(ms)

                tx2 = apply_multisignatures(
                    tx, 0, script, seller_signatures[0], msg['signatures'][0]
                )

                print 'FINAL SCRIPT: %s' % tx2
                print 'Sent', eligius_pushtx(tx2)

                self.send_to_client(
                    None,
                    {
                        "type": "order_notify",
                        "msg": "Funds were released for your sale."
                    }
                )
Ejemplo n.º 12
0
            def cb(ec, history, order):
                if ec is not None:
                    self.log.error("Error fetching history: %s", ec)
                    # TODO: Send error message to GUI
                    return

                unspent = [row[:4] for row in history if row[4] is None]

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

                seller_signatures = []
                print 'private key ', self.transport.settings['privkey']
                for x in range(0, len(inputs)):
                    ms = multisign(tx, x, script,
                                   self.transport.settings['privkey'])
                    print 'seller sig', ms
                    seller_signatures.append(ms)

                tx2 = apply_multisignatures(tx, 0, script,
                                            seller_signatures[0],
                                            msg['signatures'][0])

                print 'FINAL SCRIPT: %s' % tx2
                print 'Sent', eligius_pushtx(tx2)

                self.send_to_client(
                    None, {
                        "type": "order_notify",
                        "msg": "Funds were released for your sale."
                    })
Ejemplo n.º 13
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.º 14
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.º 15
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.º 16
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"]