def create(self, expiration_date, metadata_category, title, description, currency_code, price, process_time, nsfw, shipping_origin=None, shipping_regions=None, est_delivery_domestic=None, est_delivery_international=None, terms_conditions=None, returns=None, keywords=None, category=None, condition=None, sku=None, images=None, free_shipping=None, shipping_currency_code=None, shipping_domestic=None, shipping_international=None, options=None, moderators=None): """ All parameters are strings except: :param expiration_date: `string` (must be formatted UTC datetime) :param keywords: `list` :param nsfw: `boolean` :param images: a `list` of image files :param free_shipping: `boolean` :param shipping_origin: a 'string' formatted `CountryCode` :param shipping_regions: a 'list' of 'string' formatted `CountryCode`s :param options: a 'dict' containing options as keys and 'list' as option values. :param moderators: a 'list' of 'string' guids (hex encoded). """ profile = Profile(self.db).get() self.contract = OrderedDict({ "vendor_offer": { "listing": { "metadata": { "version": "0.1", "category": metadata_category.lower(), "category_sub": "fixed price" }, "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") } }, "item": { "title": title, "description": description, "process_time": process_time, "price_per_unit": {}, "nsfw": nsfw } } } }) if expiration_date == "": self.contract["vendor_offer"]["listing"]["metadata"][ "expiry"] = "never" else: self.contract["vendor_offer"]["listing"]["metadata"][ "expiry"] = expiration_date + " UTC" if metadata_category == "physical good" and condition is not None: self.contract["vendor_offer"]["listing"]["item"][ "condition"] = condition if currency_code.upper() == "BTC": item = self.contract["vendor_offer"]["listing"]["item"] item["price_per_unit"]["bitcoin"] = price else: item = self.contract["vendor_offer"]["listing"]["item"] item["price_per_unit"]["fiat"] = {} item["price_per_unit"]["fiat"]["price"] = price item["price_per_unit"]["fiat"]["currency_code"] = currency_code if keywords is not None: self.contract["vendor_offer"]["listing"]["item"]["keywords"] = [] self.contract["vendor_offer"]["listing"]["item"][ "keywords"].extend(keywords) if category is not None: self.contract["vendor_offer"]["listing"]["item"][ "category"] = category if sku is not None: self.contract["vendor_offer"]["listing"]["item"]["sku"] = sku if options is not None: self.contract["vendor_offer"]["listing"]["item"][ "options"] = options if metadata_category == "physical good": self.contract["vendor_offer"]["listing"]["shipping"] = {} shipping = self.contract["vendor_offer"]["listing"]["shipping"] shipping["shipping_origin"] = shipping_origin if free_shipping is False: self.contract["vendor_offer"]["listing"]["shipping"][ "free"] = False self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"] = {} if shipping_currency_code == "BTC": self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"]["bitcoin"] = {} self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"]["bitcoin"]["domestic"] = shipping_domestic self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"]["bitcoin"][ "international"] = shipping_international else: shipping = self.contract["vendor_offer"]["listing"][ "shipping"] shipping["flat_fee"]["fiat"] = {} shipping["flat_fee"]["fiat"]["price"] = {} shipping["flat_fee"]["fiat"]["price"][ "domestic"] = shipping_domestic shipping["flat_fee"]["fiat"]["price"][ "international"] = shipping_international shipping["flat_fee"]["fiat"][ "currency_code"] = shipping_currency_code else: self.contract["vendor_offer"]["listing"]["shipping"][ "free"] = True self.contract["vendor_offer"]["listing"]["shipping"][ "shipping_regions"] = [] for region in shipping_regions: shipping = self.contract["vendor_offer"]["listing"]["shipping"] shipping["shipping_regions"].append(region) listing = self.contract["vendor_offer"]["listing"] listing["shipping"]["est_delivery"] = {} listing["shipping"]["est_delivery"][ "domestic"] = est_delivery_domestic listing["shipping"]["est_delivery"][ "international"] = est_delivery_international if profile.HasField("handle"): self.contract["vendor_offer"]["listing"]["id"][ "blockchain_id"] = profile.handle if images is not None: self.contract["vendor_offer"]["listing"]["item"][ "image_hashes"] = [] for image_hash in images: if len(image_hash) != 40: raise Exception("Invalid image hash") self.contract["vendor_offer"]["listing"]["item"][ "image_hashes"].append(image_hash) if terms_conditions is not None or returns is not None: self.contract["vendor_offer"]["listing"]["policy"] = {} if terms_conditions is not None: self.contract["vendor_offer"]["listing"]["policy"][ "terms_conditions"] = terms_conditions if returns is not None: self.contract["vendor_offer"]["listing"]["policy"][ "returns"] = returns if moderators is not None: self.contract["vendor_offer"]["listing"]["moderators"] = [] for mod in moderators: mod_info = self.db.ModeratorStore().get_moderator( unhexlify(mod)) print mod_info if mod_info is not None: moderator = { "guid": mod, "blockchain_id": mod_info[6], "pubkeys": { "signing": { "key": mod_info[1][64:].encode("hex"), "signature": mod_info[1][:64].encode("hex") }, "encryption": { "key": mod_info[2].encode("hex"), "signature": mod_info[3].encode("hex") }, "bitcoin": { "key": mod_info[4].encode("hex"), "signature": mod_info[5].encode("hex") } } } self.contract["vendor_offer"]["listing"][ "moderators"].append(moderator) listing = json.dumps(self.contract["vendor_offer"]["listing"], indent=4) self.contract["vendor_offer"]["signature"] = \ self.keychain.signing_key.sign(listing, encoder=nacl.encoding.HexEncoder)[:128] self.save()
def create(self, expiration_date, metadata_category, title, description, currency_code, price, process_time, nsfw, shipping_origin, shipping_regions, est_delivery_domestic=None, est_delivery_international=None, keywords=None, category=None, condition=None, sku=None, images=None, free_shipping=None, shipping_currency_code=None, shipping_domestic=None, shipping_international=None): """ All parameters are strings except: :param expiration_date: `string` (must be formatted UTC datetime) :param keywords: `list` :param nsfw: `boolean` :param images: a `list` of image files :param free_shipping: `boolean` :param shipping_origin: a 'string' formatted `CountryCode` :param shipping_regions: a 'list' of 'string' formatted `CountryCode`s """ # TODO: import keys into the contract, import moderator information from db, sign contract. profile = Profile().get() keychain = KeyChain() self.contract = OrderedDict({ "vendor_offer": { "listing": { "metadata": { "version": "0.1", "expiry": expiration_date + " UTC", "category": metadata_category, "category_sub": "fixed price" }, "id": { "guid": keychain.guid.encode("hex"), "pubkeys": { "guid": keychain.guid_signed_pubkey[64:].encode("hex"), "bitcoin": keychain.bitcoin_master_pubkey } }, "item": { "title": title, "description": description, "process_time": process_time, "price_per_unit": {}, "nsfw": nsfw } } } }) if metadata_category == "physical good" and condition is not None: self.contract["vendor_offer"]["listing"]["item"][ "condition"] = condition if currency_code.upper() == "BTC": item = self.contract["vendor_offer"]["listing"]["item"] item["price_per_unit"]["bitcoin"] = price else: item = self.contract["vendor_offer"]["listing"]["item"] item["price_per_unit"]["fiat"] = {} item["price_per_unit"]["fiat"]["price"] = price item["price_per_unit"]["fiat"]["currency_code"] = currency_code if keywords is not None: self.contract["vendor_offer"]["listing"]["item"]["keywords"] = [] self.contract["vendor_offer"]["listing"]["item"][ "keywords"].extend(keywords) if category is not None: self.contract["vendor_offer"]["listing"]["item"][ "category"] = category if sku is not None: self.contract["vendor_offer"]["listing"]["item"]["sku"] = sku if metadata_category == "physical good": self.contract["vendor_offer"]["listing"]["shipping"] = {} shipping = self.contract["vendor_offer"]["listing"]["shipping"] shipping["shipping_origin"] = shipping_origin if free_shipping is False: self.contract["vendor_offer"]["listing"]["shipping"][ "free"] = False self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"] = {} if shipping_currency_code == "BTC": self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"]["bitcoin"] = {} self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"]["bitcoin"]["domestic"] = shipping_domestic self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"]["bitcoin"][ "international"] = shipping_international else: shipping = self.contract["vendor_offer"]["listing"][ "shipping"] shipping["flat_fee"]["fiat"] = {} shipping["flat_fee"]["fiat"]["price"] = {} shipping["flat_fee"]["fiat"]["price"][ "domestic"] = shipping_domestic shipping["flat_fee"]["fiat"]["price"][ "international"] = shipping_international shipping["flat_fee"]["fiat"][ "currency_code"] = shipping_currency_code else: self.contract["vendor_offer"]["listing"]["shipping"][ "free"] = True self.contract["vendor_offer"]["listing"]["shipping"][ "shipping_regions"] = [] for region in shipping_regions: shipping = self.contract["vendor_offer"]["listing"]["shipping"] shipping["shipping_regions"].append(region) listing = self.contract["vendor_offer"]["listing"] listing["shipping"]["est_delivery"] = {} listing["shipping"]["est_delivery"][ "domestic"] = est_delivery_domestic listing["shipping"]["est_delivery"][ "international"] = est_delivery_international if profile.HasField("handle"): self.contract["vendor_offer"]["listing"]["id"][ "blockchain_id"] = profile.handle if images is not None: self.contract["vendor_offer"]["listing"]["item"][ "image_hashes"] = [] for image in images: hash_value = digest(image).encode("hex") self.contract["vendor_offer"]["listing"]["item"][ "image_hashes"].append(hash_value) with open(DATA_FOLDER + "store/media/" + hash_value, 'w') as outfile: outfile.write(image) HashMap().insert(digest(image), DATA_FOLDER + "store/media/" + hash_value) listing = json.dumps(self.contract["vendor_offer"]["listing"], indent=4) self.contract["vendor_offer"]["signature"] = \ keychain.signing_key.sign(listing, encoder=nacl.encoding.HexEncoder)[:128] self.save()
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"])