Example #1
0
    def release_funds(self, order_id):
        """
        This function should be called to release funds from a disputed contract after
        the moderator has resolved the dispute and provided his signature.
        """
        if os.path.exists(DATA_FOLDER + "purchases/in progress/" + order_id + ".json"):
            file_path = DATA_FOLDER + "purchases/trade receipts/" + order_id + ".json"
            outpoints = pickle.loads(self.db.Purchases().get_outpoint(order_id))
        elif os.path.exists(DATA_FOLDER + "store/contracts/in progress/" + order_id + ".json"):
            file_path = DATA_FOLDER + "store/contracts/in progress/" + order_id + ".json"
            outpoints = pickle.loads(self.db.Sales().get_outpoint(order_id))

        with open(file_path, 'r') as filename:
            contract = json.load(filename, object_pairs_hook=OrderedDict)

        vendor_address = contract["vendor_order_confirmation"]["invoice"]["payout"]["address"]
        buyer_address = contract["buyer_order"]["order"]["refund_address"]

        for moderator in contract["vendor_offer"]["listing"]["moderators"]:
            if moderator["guid"] == contract["buyer_order"]["order"]["moderator"]:
                masterkey_m = moderator["pubkeys"]["bitcoin"]["key"]

        outputs = []

        outputs.append({'value': contract["dispute_resolution"]["resolution"]["moderator_fee"],
                        'address': contract["dispute_resolution"]["resolution"]["moderator_address"]})

        if "buyer_payout" in contract["dispute_resolution"]["resolution"]:
            outputs.append({'value': contract["dispute_resolution"]["resolution"]["buyer_payout"],
                            'address': buyer_address})

        if "vendor_payout" in contract["dispute_resolution"]["resolution"]:
            outputs.append({'value': contract["dispute_resolution"]["resolution"]["vendor_payout"],
                            'address': vendor_address})

        tx = bitcoin.mktx(outpoints, outputs)
        signatures = []
        chaincode = contract["buyer_order"]["order"]["payment"]["chaincode"]
        redeem_script = str(contract["buyer_order"]["order"]["payment"]["redeem_script"])
        masterkey = bitcoin.bip32_extract_key(KeyChain(self.db).bitcoin_master_privkey)
        childkey = derive_childkey(masterkey, chaincode, bitcoin.MAINNET_PRIVATE)

        mod_key = derive_childkey(masterkey_m, chaincode)

        valid_inputs = 0
        for index in range(0, len(outpoints)):
            sig = bitcoin.multisign(tx, index, redeem_script, childkey)
            signatures.append({"input_index": index, "signature": sig})
            for s in contract["dispute_resolution"]["resolution"]["tx_signatures"]:
                if s["input_index"] == index:
                    if bitcoin.verify_tx_input(tx, index, redeem_script, s["signature"], mod_key):
                        tx = bitcoin.apply_multisignatures(tx, index, str(redeem_script),
                                                           sig, str(s["signature"]))
                        valid_inputs += 1

        if valid_inputs == len(outpoints):
            self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx))
            self.protocol.multiplexer.blockchain.broadcast(tx)
        else:
            raise Exception("Failed to reconstruct transaction with moderator signature.")
Example #2
0
 def rpc_order(self, sender, pubkey, encrypted):
     try:
         box = Box(PrivateKey(self.signing_key.encode(nacl.encoding.RawEncoder)), PublicKey(pubkey))
         order = box.decrypt(encrypted)
         c = Contract(self.db, contract=json.loads(order, object_pairs_hook=OrderedDict),
                      testnet=self.multiplexer.testnet)
         if c.verify(sender.signed_pubkey[64:]):
             self.router.addContact(sender)
             self.log.info("received an order from %s, waiting for payment..." % sender)
             payment_address = c.contract["buyer_order"]["order"]["payment"]["address"]
             chaincode = c.contract["buyer_order"]["order"]["payment"]["chaincode"]
             masterkey_b = c.contract["buyer_order"]["order"]["id"]["pubkeys"]["bitcoin"]
             buyer_key = derive_childkey(masterkey_b, chaincode)
             amount = c.contract["buyer_order"]["order"]["payment"]["amount"]
             listing_hash = c.contract["buyer_order"]["order"]["ref_hash"]
             signature = self.signing_key.sign(
                 str(payment_address) + str(amount) + str(listing_hash) + str(buyer_key))[:64]
             c.await_funding(self.multiplexer.ws, self.multiplexer.blockchain, signature, False)
             return [signature]
         else:
             self.log.warning("received invalid order from %s" % sender)
             return ["False"]
     except Exception:
         self.log.error("unable to decrypt order from %s" % sender)
         return ["False"]
Example #3
0
 def rpc_order(self, sender, pubkey, encrypted):
     try:
         box = Box(PrivateKey(self.signing_key.encode(nacl.encoding.RawEncoder)), PublicKey(pubkey))
         order = box.decrypt(encrypted)
         c = Contract(self.db, contract=json.loads(order, object_pairs_hook=OrderedDict),
                      testnet=self.multiplexer.testnet)
         if c.verify(sender.signed_pubkey[64:]):
             self.router.addContact(sender)
             self.log.info("received an order from %s, waiting for payment..." % sender)
             payment_address = c.contract["buyer_order"]["order"]["payment"]["address"]
             chaincode = c.contract["buyer_order"]["order"]["payment"]["chaincode"]
             masterkey_b = c.contract["buyer_order"]["order"]["id"]["pubkeys"]["bitcoin"]
             buyer_key = derive_childkey(masterkey_b, chaincode)
             amount = c.contract["buyer_order"]["order"]["payment"]["amount"]
             listing_hash = c.contract["buyer_order"]["order"]["ref_hash"]
             signature = self.signing_key.sign(
                 str(payment_address) + str(amount) + str(listing_hash) + str(buyer_key))[:64]
             c.await_funding(self.get_notification_listener(), self.multiplexer.blockchain, signature, False)
             return [signature]
         else:
             self.log.warning("received invalid order from %s" % sender)
             return ["False"]
     except Exception:
         self.log.error("unable to decrypt order from %s" % sender)
         return ["False"]
Example #4
0
 def parse_response(response):
     try:
         address = contract.contract["buyer_order"]["order"]["payment"]["address"]
         chaincode = contract.contract["buyer_order"]["order"]["payment"]["chaincode"]
         masterkey_b = contract.contract["buyer_order"]["order"]["id"]["pubkeys"]["bitcoin"]
         buyer_key = derive_childkey(masterkey_b, chaincode)
         amount = contract.contract["buyer_order"]["order"]["payment"]["amount"]
         listing_hash = contract.contract["buyer_order"]["order"]["ref_hash"]
         verify_key = nacl.signing.VerifyKey(node_to_ask.signed_pubkey[64:])
         verify_key.verify(str(address) + str(amount) + str(listing_hash) + str(buyer_key), response[1][0])
         return response[1][0]
     except Exception:
         return False
Example #5
0
 def parse_response(response):
     try:
         address = contract.contract["buyer_order"]["order"]["payment"]["address"]
         chaincode = contract.contract["buyer_order"]["order"]["payment"]["chaincode"]
         masterkey_b = contract.contract["buyer_order"]["order"]["id"]["pubkeys"]["bitcoin"]
         buyer_key = derive_childkey(masterkey_b, chaincode)
         amount = contract.contract["buyer_order"]["order"]["payment"]["amount"]
         listing_hash = contract.contract["buyer_order"]["order"]["ref_hash"]
         verify_key = nacl.signing.VerifyKey(node_to_ask.signed_pubkey[64:])
         verify_key.verify(
             str(address) + str(amount) + str(listing_hash) + str(buyer_key), response[1][0])
         return response[1][0]
     except Exception:
         return False
Example #6
0
            def history_fetched(ec, history):
                outpoints = []
                satoshis = 0
                outputs = []
                dispute_json = {"dispute_resolution": {"resolution": {}}}
                if ec:
                    print ec
                else:
                    for tx_type, txid, i, height, value in history:  # pylint: disable=W0612
                        if tx_type == obelisk.PointIdent.Output:
                            satoshis += value
                            outpoint = txid.encode("hex") + ":" + str(i)
                            if outpoint not in outpoints:
                                outpoints.append(outpoint)

                    satoshis -= TRANSACTION_FEE
                    moderator_fee = round(float(moderator_percentage * satoshis))
                    satoshis -= moderator_fee

                    outputs.append({'value': moderator_fee, 'address': moderator_address})
                    dispute_json["dispute_resolution"]["resolution"]["moderator_address"] = moderator_address
                    dispute_json["dispute_resolution"]["resolution"]["moderator_fee"] = moderator_fee
                    dispute_json["dispute_resolution"]["resolution"]["transaction_fee"] = TRANSACTION_FEE
                    if float(buyer_percentage) > 0:
                        amt = round(float(buyer_percentage * satoshis))
                        dispute_json["dispute_resolution"]["resolution"]["buyer_payout"] = amt
                        outputs.append({'value': amt,
                                        'address': buyer_address})
                    if float(vendor_percentage) > 0:
                        amt = round(float(vendor_percentage * satoshis))
                        dispute_json["dispute_resolution"]["resolution"]["vendor_payout"] = amt
                        outputs.append({'value': amt,
                                        'address': vendor_address})
                    tx = bitcoin.mktx(outpoints, outputs)
                    chaincode = contract["buyer_order"]["order"]["payment"]["chaincode"]
                    redeem_script = str(contract["buyer_order"]["order"]["payment"]["redeem_script"])
                    masterkey_m = bitcoin.bip32_extract_key(KeyChain(self.db).bitcoin_master_privkey)
                    moderator_priv = derive_childkey(masterkey_m, chaincode, bitcoin.MAINNET_PRIVATE)
                    signatures = []
                    for index in range(0, len(outpoints)):
                        sig = bitcoin.multisign(tx, index, redeem_script, moderator_priv)
                        signatures.append({"input_index": index, "signature": sig})
                    dispute_json["dispute_resolution"]["resolution"]["order_id"] = order_id
                    dispute_json["dispute_resolution"]["resolution"]["tx_signatures"] = signatures
                    dispute_json["dispute_resolution"]["resolution"]["claim"] = self.db.Cases().get_claim(order_id)
                    dispute_json["dispute_resolution"]["resolution"]["decision"] = resolution
                    dispute_json["dispute_resolution"]["signature"] = \
                        base64.b64encode(KeyChain(self.db).signing_key.sign(json.dumps(
                            dispute_json["dispute_resolution"]["resolution"]))[:64])

                    def get_node(node_to_ask, recipient_guid, public_key):
                        def parse_response(response):
                            if not response[0]:
                                self.send_message(Node(unhexlify(recipient_guid)),
                                                  public_key,
                                                  objects.PlaintextMessage.Type.Value("DISPUTE_CLOSE"),
                                                  dispute_json,
                                                  order_id,
                                                  store_only=True)

                        if node_to_ask:
                            skephem = PrivateKey.generate()
                            pkephem = skephem.public_key.encode(nacl.encoding.RawEncoder)
                            box = Box(skephem, PublicKey(public_key, nacl.encoding.HexEncoder))
                            nonce = nacl.utils.random(Box.NONCE_SIZE)
                            ciphertext = box.encrypt(json.dumps(dispute_json, indent=4), nonce)
                            d = self.protocol.callDisputeClose(node_to_ask, pkephem, ciphertext)
                            return d.addCallback(parse_response)
                        else:
                            return parse_response([False])

                    self.kserver.resolve(unhexlify(vendor_guid)).addCallback(get_node, vendor_guid, vendor_enc_key)
                    self.kserver.resolve(unhexlify(buyer_guid)).addCallback(get_node, buyer_guid, buyer_enc_key)
                    self.db.Cases().update_status(order_id, 1)
Example #7
0
    def verify(self, sender_key):
        """
        Validate that an order sent over by a buyer is filled out correctly.
        """

        try:
            contract_dict = json.loads(json.dumps(self.contract, indent=4), object_pairs_hook=OrderedDict)
            del contract_dict["buyer_order"]
            contract_hash = digest(json.dumps(contract_dict, indent=4))

            ref_hash = unhexlify(self.contract["buyer_order"]["order"]["ref_hash"])

            # verify that the reference hash matches the contract and that the contract actually exists
            if contract_hash != ref_hash or not self.db.HashMap().get_file(ref_hash):
                raise Exception("Order for contract that doesn't exist")

            # verify the signature on the order
            verify_key = nacl.signing.VerifyKey(sender_key)
            verify_key.verify(
                json.dumps(self.contract["buyer_order"]["order"], indent=4),
                unhexlify(self.contract["buyer_order"]["signature"]),
            )

            # verify buyer included the correct bitcoin amount for payment
            price_json = self.contract["vendor_offer"]["listing"]["item"]["price_per_unit"]
            if "bitcoin" in price_json:
                asking_price = price_json["bitcoin"]
            else:
                currency_code = price_json["fiat"]["currency_code"]
                fiat_price = price_json["fiat"]["price"]
                request = Request("https://api.bitcoinaverage.com/ticker/" + currency_code.upper() + "/last")
                response = urlopen(request)
                conversion_rate = response.read()
                asking_price = float("{0:.8f}".format(float(fiat_price) / float(conversion_rate)))
            if asking_price > self.contract["buyer_order"]["order"]["payment"]["amount"]:
                raise Exception("Insuffient Payment")

            # verify a valid moderator was selected
            # TODO: handle direct payments
            valid_mod = False
            for mod in self.contract["vendor_offer"]["listing"]["moderators"]:
                if mod["guid"] == self.contract["buyer_order"]["order"]["moderator"]:
                    valid_mod = True
            if not valid_mod:
                raise Exception("Invalid moderator")

            # verify all the shipping fields exist
            if self.contract["vendor_offer"]["listing"]["metadata"]["category"] == "physical good":
                shipping = self.contract["buyer_order"]["order"]["shipping"]
                keys = ["ship_to", "address", "postal_code", "city", "state", "country"]
                for value in map(shipping.get, keys):
                    if value is None:
                        raise Exception("Missing shipping field")

            # verify buyer ID
            pubkeys = self.contract["buyer_order"]["order"]["id"]["pubkeys"]
            keys = ["guid", "bitcoin", "encryption"]
            for value in map(pubkeys.get, keys):
                if value is None:
                    raise Exception("Missing pubkey field")

            # verify redeem script
            chaincode = self.contract["buyer_order"]["order"]["payment"]["chaincode"]
            for mod in self.contract["vendor_offer"]["listing"]["moderators"]:
                if mod["guid"] == self.contract["buyer_order"]["order"]["moderator"]:
                    masterkey_m = mod["pubkeys"]["bitcoin"]["key"]

            masterkey_v = bitcoin.bip32_extract_key(self.keychain.bitcoin_master_pubkey)
            masterkey_b = self.contract["buyer_order"]["order"]["id"]["pubkeys"]["bitcoin"]
            buyer_key = derive_childkey(masterkey_b, chaincode)
            vendor_key = derive_childkey(masterkey_v, chaincode)
            moderator_key = derive_childkey(masterkey_m, chaincode)

            redeem_script = "75" + bitcoin.mk_multisig_script([buyer_key, vendor_key, moderator_key], 2)
            if redeem_script != self.contract["buyer_order"]["order"]["payment"]["redeem_script"]:
                raise Exception("Invalid redeem script")

            # verify the payment address
            if self.testnet:
                payment_address = bitcoin.p2sh_scriptaddr(redeem_script, 196)
            else:
                payment_address = bitcoin.p2sh_scriptaddr(redeem_script)
            if payment_address != self.contract["buyer_order"]["order"]["payment"]["address"]:
                raise Exception("Incorrect payment address")

            return True

        except Exception:
            return False
Example #8
0
    def add_purchase_info(
        self,
        quantity,
        ship_to=None,
        shipping_address=None,
        city=None,
        state=None,
        postal_code=None,
        country=None,
        moderator=None,
        options=None,
    ):
        """
        Update the contract with the buyer's purchase information.
        """

        profile = Profile(self.db).get()
        order_json = {
            "buyer_order": {
                "order": {
                    "ref_hash": digest(json.dumps(self.contract, indent=4)).encode("hex"),
                    "quantity": quantity,
                    "id": {
                        "guid": self.keychain.guid.encode("hex"),
                        "pubkeys": {
                            "guid": self.keychain.guid_signed_pubkey[64:].encode("hex"),
                            "bitcoin": bitcoin.bip32_extract_key(self.keychain.bitcoin_master_pubkey),
                            "encryption": self.keychain.encryption_pubkey.encode("hex"),
                        },
                    },
                    "payment": {},
                }
            }
        }
        if profile.HasField("handle"):
            order_json["buyer_order"]["order"]["id"]["blockchain_id"] = profile.handle
        if self.contract["vendor_offer"]["listing"]["metadata"]["category"] == "physical good":
            order_json["buyer_order"]["order"]["shipping"] = {}
            order_json["buyer_order"]["order"]["shipping"]["ship_to"] = ship_to
            order_json["buyer_order"]["order"]["shipping"]["address"] = shipping_address
            order_json["buyer_order"]["order"]["shipping"]["city"] = city
            order_json["buyer_order"]["order"]["shipping"]["state"] = state
            order_json["buyer_order"]["order"]["shipping"]["postal_code"] = postal_code
            order_json["buyer_order"]["order"]["shipping"]["country"] = country
        if options is not None:
            order_json["buyer_order"]["order"]["options"] = options
        if moderator:  # TODO: Handle direct payments
            chaincode = sha256(str(random.getrandbits(256))).digest().encode("hex")
            order_json["buyer_order"]["order"]["payment"]["chaincode"] = chaincode
            valid_mod = False
            for mod in self.contract["vendor_offer"]["listing"]["moderators"]:
                if mod["guid"] == moderator:
                    order_json["buyer_order"]["order"]["moderator"] = moderator
                    masterkey_m = mod["pubkeys"]["bitcoin"]["key"]
                    valid_mod = True
            if not valid_mod:
                return False
            masterkey_b = bitcoin.bip32_extract_key(self.keychain.bitcoin_master_pubkey)
            masterkey_v = self.contract["vendor_offer"]["listing"]["id"]["pubkeys"]["bitcoin"]
            buyer_key = derive_childkey(masterkey_b, chaincode)
            vendor_key = derive_childkey(masterkey_v, chaincode)
            moderator_key = derive_childkey(masterkey_m, chaincode)

            redeem_script = "75" + bitcoin.mk_multisig_script([buyer_key, vendor_key, moderator_key], 2)
            order_json["buyer_order"]["order"]["payment"]["redeem_script"] = redeem_script
            if self.testnet:
                payment_address = bitcoin.p2sh_scriptaddr(redeem_script, 196)
            else:
                payment_address = bitcoin.p2sh_scriptaddr(redeem_script)
            order_json["buyer_order"]["order"]["payment"]["address"] = payment_address

        price_json = self.contract["vendor_offer"]["listing"]["item"]["price_per_unit"]
        if "bitcoin" in price_json:
            order_json["buyer_order"]["order"]["payment"]["amount"] = price_json["bitcoin"]
        else:
            currency_code = price_json["fiat"]["currency_code"]
            fiat_price = price_json["fiat"]["price"]
            try:
                request = Request("https://api.bitcoinaverage.com/ticker/" + currency_code.upper() + "/last")
                response = urlopen(request)
                conversion_rate = response.read()
            except URLError:
                return False
            order_json["buyer_order"]["order"]["payment"]["amount"] = float(
                "{0:.8f}".format(float(fiat_price) / float(conversion_rate))
            )

        self.contract["buyer_order"] = order_json["buyer_order"]
        order = json.dumps(self.contract["buyer_order"]["order"], indent=4)
        self.contract["buyer_order"]["signature"] = self.keychain.signing_key.sign(
            order, encoder=nacl.encoding.HexEncoder
        )[:128]

        return self.contract["buyer_order"]["order"]["payment"]["address"]
Example #9
0
    def verify(self, sender_key):
        """
        Validate that an order sent over by a buyer is filled out correctly.
        """

        try:
            contract_dict = json.loads(json.dumps(self.contract, indent=4),
                                       object_pairs_hook=OrderedDict)
            del contract_dict["buyer_order"]
            contract_hash = digest(json.dumps(contract_dict, indent=4))

            ref_hash = unhexlify(
                self.contract["buyer_order"]["order"]["ref_hash"])

            # verify that the reference hash matches the contract and that the contract actually exists
            if contract_hash != ref_hash or not self.db.HashMap().get_file(
                    ref_hash):
                raise Exception("Order for contract that doesn't exist")

            # verify the signature on the order
            verify_key = nacl.signing.VerifyKey(sender_key)
            verify_key.verify(
                json.dumps(self.contract["buyer_order"]["order"], indent=4),
                unhexlify(self.contract["buyer_order"]["signature"]))

            # TODO: verify the bitcoin signature after we add it

            # verify buyer included the correct bitcoin amount for payment
            price_json = self.contract["vendor_offer"]["listing"]["item"][
                "price_per_unit"]
            if "bitcoin" in price_json:
                asking_price = price_json["bitcoin"]
            else:
                currency_code = price_json["fiat"]["currency_code"]
                fiat_price = price_json["fiat"]["price"]
                request = Request('https://api.bitcoinaverage.com/ticker/' +
                                  currency_code.upper() + '/last')
                response = urlopen(request)
                conversion_rate = response.read()
                asking_price = float("{0:.8f}".format(
                    float(fiat_price) / float(conversion_rate)))
            if asking_price > self.contract["buyer_order"]["order"]["payment"][
                    "amount"]:
                raise Exception("Insuffient Payment")

            # verify a valid moderator was selected
            # TODO: handle direct payments
            valid_mod = False
            for mod in self.contract["vendor_offer"]["listing"]["moderators"]:
                if mod["guid"] == self.contract["buyer_order"]["order"][
                        "moderator"]:
                    valid_mod = True
            if not valid_mod:
                raise Exception("Invalid moderator")

            # verify all the shipping fields exist
            if self.contract["vendor_offer"]["listing"]["metadata"][
                    "category"] == "physical good":
                shipping = self.contract["buyer_order"]["order"]["shipping"]
                keys = [
                    "ship_to", "address", "postal_code", "city", "state",
                    "country"
                ]
                for value in map(shipping.get, keys):
                    if value is None:
                        raise Exception("Missing shipping field")

            # verify buyer ID
            pubkeys = self.contract["buyer_order"]["order"]["id"]["pubkeys"]
            keys = ["guid", "bitcoin", "encryption"]
            for value in map(pubkeys.get, keys):
                if value is None:
                    raise Exception("Missing pubkey field")

            # verify redeem script
            chaincode = self.contract["buyer_order"]["order"]["payment"][
                "chaincode"]
            for mod in self.contract["vendor_offer"]["listing"]["moderators"]:
                if mod["guid"] == self.contract["buyer_order"]["order"][
                        "moderator"]:
                    masterkey_m = mod["pubkeys"]["bitcoin"]["key"]

            masterkey_b = self.contract["buyer_order"]["order"]["id"][
                "pubkeys"]["bitcoin"]
            masterkey_v = bitcoin.bip32_extract_key(
                self.keychain.bitcoin_master_pubkey)
            buyer_key = derive_childkey(masterkey_b, chaincode)
            vendor_key = derive_childkey(masterkey_v, chaincode)
            moderator_key = derive_childkey(masterkey_m, chaincode)

            redeem_script = bitcoin.mk_multisig_script(
                [buyer_key, vendor_key, moderator_key], 2)
            if redeem_script != self.contract["buyer_order"]["order"][
                    "payment"]["redeem_script"]:
                raise Exception("Invalid redeem script")

            # verify the payment address
            if self.testnet:
                payment_address = bitcoin.p2sh_scriptaddr(redeem_script, 196)
            else:
                payment_address = bitcoin.p2sh_scriptaddr(redeem_script)
            if payment_address != self.contract["buyer_order"]["order"][
                    "payment"]["address"]:
                raise Exception("Incorrect payment address")

            return True

        except Exception:
            return False
Example #10
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)
Example #11
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"]
Example #12
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))
Example #13
0
    def add_purchase_info(self,
                          quantity,
                          ship_to=None,
                          shipping_address=None,
                          city=None,
                          state=None,
                          postal_code=None,
                          country=None,
                          moderator=None,
                          options=None):
        """
        Update the contract with the buyer's purchase information.
        """

        profile = Profile(self.db).get()
        order_json = {
            "buyer_order": {
                "order": {
                    "ref_hash":
                    digest(json.dumps(self.contract, indent=4)).encode("hex"),
                    "quantity":
                    quantity,
                    "id": {
                        "guid": self.keychain.guid.encode("hex"),
                        "pubkeys": {
                            "guid":
                            self.keychain.guid_signed_pubkey[64:].encode(
                                "hex"),
                            "bitcoin":
                            bitcoin.bip32_extract_key(
                                self.keychain.bitcoin_master_pubkey),
                            "encryption":
                            self.keychain.encryption_pubkey.encode("hex")
                        }
                    },
                    "payment": {}
                }
            }
        }
        if profile.HasField("handle"):
            order_json["buyer_order"]["order"]["id"][
                "blockchain_id"] = profile.handle
        if self.contract["vendor_offer"]["listing"]["metadata"][
                "category"] == "physical good":
            order_json["buyer_order"]["order"]["shipping"] = {}
            order_json["buyer_order"]["order"]["shipping"]["ship_to"] = ship_to
            order_json["buyer_order"]["order"]["shipping"][
                "address"] = shipping_address
            order_json["buyer_order"]["order"]["shipping"]["city"] = city
            order_json["buyer_order"]["order"]["shipping"]["state"] = state
            order_json["buyer_order"]["order"]["shipping"][
                "postal_code"] = postal_code
            order_json["buyer_order"]["order"]["shipping"]["country"] = country
        if options is not None:
            order_json["buyer_order"]["order"]["options"] = options
        if moderator:  # TODO: Handle direct payments
            chaincode = sha256(str(
                random.getrandbits(256))).digest().encode("hex")
            order_json["buyer_order"]["order"]["payment"][
                "chaincode"] = chaincode
            valid_mod = False
            for mod in self.contract["vendor_offer"]["listing"]["moderators"]:
                if mod["guid"] == moderator:
                    order_json["buyer_order"]["order"]["moderator"] = moderator
                    masterkey_m = mod["pubkeys"]["bitcoin"]["key"]
                    valid_mod = True
            if not valid_mod:
                return False
            masterkey_b = bitcoin.bip32_extract_key(
                self.keychain.bitcoin_master_pubkey)
            masterkey_v = self.contract["vendor_offer"]["listing"]["id"][
                "pubkeys"]["bitcoin"]
            buyer_key = derive_childkey(masterkey_b, chaincode)
            vendor_key = derive_childkey(masterkey_v, chaincode)
            moderator_key = derive_childkey(masterkey_m, chaincode)

            redeem_script = bitcoin.mk_multisig_script(
                [buyer_key, vendor_key, moderator_key], 2)
            order_json["buyer_order"]["order"]["payment"][
                "redeem_script"] = redeem_script
            if self.testnet:
                payment_address = bitcoin.p2sh_scriptaddr(redeem_script, 196)
            else:
                payment_address = bitcoin.p2sh_scriptaddr(redeem_script)
            order_json["buyer_order"]["order"]["payment"][
                "address"] = payment_address

        price_json = self.contract["vendor_offer"]["listing"]["item"][
            "price_per_unit"]
        if "bitcoin" in price_json:
            order_json["buyer_order"]["order"]["payment"][
                "amount"] = price_json["bitcoin"]
        else:
            currency_code = price_json["fiat"]["currency_code"]
            fiat_price = price_json["fiat"]["price"]
            try:
                request = Request('https://api.bitcoinaverage.com/ticker/' +
                                  currency_code.upper() + '/last')
                response = urlopen(request)
                conversion_rate = response.read()
            except URLError:
                return False
            order_json["buyer_order"]["order"]["payment"]["amount"] = float(
                "{0:.8f}".format(float(fiat_price) / float(conversion_rate)))

        self.contract["buyer_order"] = order_json["buyer_order"]
        order = json.dumps(self.contract["buyer_order"]["order"], indent=4)
        # TODO: This should also be signed with the bitcoin key. It's the only way a moderator
        # will have to link this contract to a bitcoin transaction.
        self.contract["buyer_order"]["signature"] = \
            self.keychain.signing_key.sign(order, encoder=nacl.encoding.HexEncoder)[:128]
        return (self.contract["buyer_order"]["order"]["payment"]["address"],
                order_json["buyer_order"]["order"]["payment"]["amount"])
Example #14
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)
Example #15
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"]
Example #16
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))