def close_dispute(resolution_json, db, message_listener, notification_listener, testnet): """ This function processes a dispute close message received from the moderator. It will store the resolution in the contract and send a notification to the listeners telling them that the dispute is resolved. Args: resolution_json: The `dispute_resolution` portion of the contract received from the moderator. db: a `Database` object. message_listener: a `MessageListenerImpl` object. notification_listener: a `NotificationListenerImpl` object. testnet: `bool` of whether we're on testnet or not. """ order_id = resolution_json["dispute_resolution"]["resolution"]["order_id"] if os.path.exists(DATA_FOLDER + "purchases/in progress/" + order_id + ".json"): file_path = DATA_FOLDER + "purchases/in progress/" + order_id + ".json" elif os.path.exists(DATA_FOLDER + "store/contracts/in progress/" + order_id + ".json"): file_path = DATA_FOLDER + "store/contracts/in progress/" + order_id + ".json" with open(file_path, 'r') as filename: contract = json.load(filename, object_pairs_hook=OrderedDict) for moderator in contract["vendor_offer"]["listing"]["moderators"]: if moderator["guid"] == contract["buyer_order"]["order"]["moderator"]: moderator_guid = unhexlify(moderator["guid"]) moderator_handle = moderator["blockchain_id"] moderator_pubkey = unhexlify(moderator["pubkeys"]["guid"]) moderator_avatar = unhexlify(moderator["avatar"]) verify_key = nacl.signing.VerifyKey(moderator_pubkey) verify_key.verify(json.dumps(resolution_json["dispute_resolution"]["resolution"], indent=4), base64.b64decode(resolution_json["dispute_resolution"]["signature"])) contract["dispute_resolution"] = resolution_json["dispute_resolution"] if db.purchases.get_purchase(order_id) is not None: db.purchases.update_status(order_id, 5) elif db.sales.get_sale(order_id) is not None: db.sales.update_status(order_id, 5) with open(file_path, 'wb') as outfile: outfile.write(json.dumps(contract, indent=4)) p = PlaintextMessage() p.sender_guid = moderator_guid p.handle = moderator_handle p.pubkey = moderator_pubkey p.subject = str(order_id) p.type = PlaintextMessage.Type.Value("DISPUTE_CLOSE") p.message = str(resolution_json["dispute_resolution"]["resolution"]["decision"]) p.timestamp = int(time.time()) p.avatar_hash = moderator_avatar message_listener.notify(p, "") notification_listener.notify(moderator_guid, moderator_handle, "dispute_close", order_id, contract["vendor_offer"]["listing"]["item"]["title"], contract["vendor_offer"]["listing"]["item"]["image_hashes"][0])
def _create_valid_plaintext_message(handle): p = PlaintextMessage() p.sender_guid = 'test_guid' p.handle = handle p.pubkey = 'test_pubkey' p.subject = 'test_subject' p.type = 1 p.message = 'test_message' p.timestamp = 10 p.avatar_hash = 'test_avatar_hash' return p
def process_dispute(contract, db, message_listener, notification_listener, testnet): """ This function processes a dispute message received from another node. It checks the contract to see if this a dispute for a purchase we made, a dispute for one of our sales, or a new case if we are the moderator. If it's a purchase or sale it will update the order status to disputed and push a notification to the listener. If it's a new case it will validate the contract, create a new case in the db, and push a notification to the listener. Args: contract: a json contract of the current order state. Should have a "dispute" object attached with dispute info. db: a `Database` object. message_listener: a `MessageListenerImpl` object. notification_listener: a `NotificationListenerImpl` object. testnet: `bool` of whether we're on testnet or not. """ tmp_contract = deepcopy(contract) if "vendor_order_confirmation" in tmp_contract: del tmp_contract["vendor_order_confirmation"] if "buyer_receipt" in tmp_contract: del tmp_contract["buyer_receipt"] del tmp_contract["dispute"] order_id = digest(json.dumps(tmp_contract, indent=4)).encode("hex") own_guid = KeyChain(db).guid.encode("hex") if contract["dispute"]["info"]["guid"] == contract["vendor_offer"]["listing"]["id"]["guid"]: guid = unhexlify(contract["vendor_offer"]["listing"]["id"]["guid"]) public_key = unhexlify(contract["vendor_offer"]["listing"]["id"]["pubkeys"]["guid"]) if "blockchain_id" in contract["vendor_offer"]["listing"]["id"]: handle = contract["vendor_offer"]["listing"]["id"]["blockchain_id"] else: handle = "" proof_sig = None elif contract["dispute"]["info"]["guid"] == contract["buyer_order"]["order"]["id"]["guid"]: guid = unhexlify(contract["buyer_order"]["order"]["id"]["guid"]) public_key = unhexlify(contract["buyer_order"]["order"]["id"]["pubkeys"]["guid"]) if "blockchain_id" in contract["buyer_order"]["order"]["id"]: handle = contract["buyer_order"]["order"]["id"]["blockchain_id"] else: handle = "" proof_sig = contract["dispute"]["info"]["proof_sig"] else: raise Exception("Dispute guid not in contract") verify_key = nacl.signing.VerifyKey(public_key) verify_key.verify(json.dumps(contract["dispute"]["info"], indent=4), base64.b64decode(contract["dispute"]["signature"])) p = PlaintextMessage() p.sender_guid = guid p.handle = handle p.pubkey = public_key p.subject = str(order_id) p.type = PlaintextMessage.Type.Value("DISPUTE_OPEN") p.message = str(contract["dispute"]["info"]["claim"]) p.timestamp = int(time.time()) p.avatar_hash = unhexlify(str(contract["dispute"]["info"]["avatar_hash"])) if db.purchases.get_purchase(order_id) is not None: db.purchases.update_status(order_id, 4) elif db.sales.get_sale(order_id) is not None: db.sales.update_status(order_id, 4) elif "moderators" in contract["vendor_offer"]["listing"]: # TODO: make sure a case isn't already open in the db is_selected = False for moderator in contract["vendor_offer"]["listing"]["moderators"]: if moderator["guid"] == own_guid and contract["buyer_order"]["order"]["moderator"] == own_guid: is_selected = True if not is_selected: raise Exception("Not a moderator for this contract") else: if "blockchain_id" in contract["vendor_offer"]["listing"]["id"] and \ contract["vendor_offer"]["listing"]["id"]["blockchain_id"] != "": vendor = contract["vendor_offer"]["listing"]["id"]["blockchain_id"] else: vendor = contract["vendor_offer"]["listing"]["id"]["guid"] if "blockchain_id" in contract["buyer_order"]["order"]["id"] and \ contract["buyer_order"]["order"]["id"]["blockchain_id"] != "": buyer = contract["buyer_order"]["order"]["id"]["blockchain_id"] else: buyer = contract["buyer_order"]["order"]["id"]["guid"] c = Contract(db, contract=contract, testnet=testnet) validation_failures = c.validate_for_moderation(proof_sig) db.cases.new_case(order_id, contract["vendor_offer"]["listing"]["item"]["title"], time.time(), contract["buyer_order"]["order"]["date"], float(contract["buyer_order"]["order"]["payment"]["amount"]), contract["vendor_offer"]["listing"]["item"]["image_hashes"][0], buyer, vendor, json.dumps(validation_failures), contract["dispute"]["info"]["claim"]) with open(os.path.join(DATA_FOLDER, "cases", order_id + ".json"), 'wb') as outfile: outfile.write(json.dumps(contract, indent=4)) else: raise Exception("Order ID for dispute not found") message_listener.notify(p, "") title = contract["vendor_offer"]["listing"]["item"]["title"] notification_listener.notify(guid, handle, "dispute_open", order_id, title, unhexlify(contract["vendor_offer"]["listing"]["item"]["image_hashes"][0])) # Send SMTP notification notification = SMTPNotification(db) guid = guid.encode("hex") notification.send("[OpenBazaar] Dispute Opened", "A dispute has been opened.\n\n" "Order: %s\n" "Opened By: %s\n" "Title: %s" % (order_id, guid, title))