Esempio n. 1
0
class MarketProtocol(RPCProtocol):
    implements(MessageProcessor)

    def __init__(self, node_proto, router):
        self.router = router
        RPCProtocol.__init__(self, node_proto, router)
        self.log = Logger(system=self)
        self.handled_commands = [GET_CONTRACT, GET_IMAGE]
        self.multiplexer = None
        self.hashmap = HashMap()

    def connect_multiplexer(self, multiplexer):
        self.multiplexer = multiplexer

    def rpc_get_contract(self, sender, contract_hash):
        self.log.info("Looking up contract ID %s" % contract_hash.encode('hex'))
        self.router.addContact(sender)
        try:
            with open(self.hashmap.get_file(contract_hash), "r") as file:
                contract = file.read()
            return [contract]
        except:
            return ["None"]

    def rpc_get_image(self, sender, image_hash):
        self.log.info("Looking up image with hash %s" % image_hash.encode('hex'))
        self.router.addContact(sender)
        try:
            with open(self.hashmap.get_file(image_hash), "r") as file:
                image = file.read()
            return [image]
        except:
            return ["None"]

    def callGetContract(self, nodeToAsk, contract_hash):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_contract(address, contract_hash)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetImage(self, nodeToAsk, image_hash):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_image(address, image_hash)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def handleCallResponse(self, result, node):
        """
        If we get a response, add the node to the routing table.  If
        we get no response, make sure it's removed from the routing table.
        """
        if result[0]:
            self.log.info("got response from %s, adding to router" % node)
            self.router.addContact(node)
        else:
            self.log.debug("no response from %s, removing from router" % node)
            self.router.removeContact(node)
        return result

    def __iter__(self):
        return iter(self.handled_commands)
Esempio n. 2
0
 def setimage():
     parser = argparse.ArgumentParser(
         description="Maps a image hash to a file path in the database",
         usage='''usage:
 networkcli.py setimage [-f FILEPATH]''')
     parser.add_argument('-f', '--filepath', help="a path to the image")
     args = parser.parse_args(sys.argv[2:])
     with open(args.filepath, "r") as f:
         image = f.read()
     d = digest(image)
     h = HashMap()
     h.insert(d, args.filepath)
     print h.get_file(d)
Esempio n. 3
0
 def setimage():
     parser = argparse.ArgumentParser(
         description="Maps a image hash to a file path in the database",
         usage='''usage:
 networkcli.py setimage [-f FILEPATH]''')
     parser.add_argument('-f', '--filepath', help="a path to the image")
     args = parser.parse_args(sys.argv[2:])
     with open(args.filepath, "r") as f:
         image = f.read()
     d = digest(image)
     h = HashMap()
     h.insert(d, args.filepath)
     print h.get_file(d)
Esempio n. 4
0
    def delete(self, delete_images=True):
        """
        Deletes the contract json from the OpenBazaar directory as well as the listing
        metadata from the db and all the related images in the file system.
        """
        # build the file_name from the contract
        file_name = str(self.contract["vendor_offer"]["listing"]["item"]["title"][:100])
        file_name = re.sub(r"[^\w\s]", '', file_name)
        file_name = re.sub(r"\s+", '_', file_name)
        file_path = DATA_FOLDER + "store/listings/contracts/" + file_name + ".json"

        h = HashMap()

        # maybe delete the images from disk
        if "image_hashes" in self.contract["vendor_offer"]["listing"]["item"] and delete_images:
            for image_hash in self.contract["vendor_offer"]["listing"]["item"]["image_hashes"]:
                # delete from disk
                image_path = h.get_file(unhexlify(image_hash))
                if os.path.exists(image_path):
                    os.remove(image_path)
                # remove pointer to the image from the HashMap
                h.delete(unhexlify(image_hash))

        # delete the contract from disk
        if os.path.exists(file_path):
            os.remove(file_path)
        # delete the listing metadata from the db
        contract_hash = digest(json.dumps(self.contract, indent=4))
        ListingsStore().delete_listing(contract_hash)
        # remove the pointer to the contract from the HashMap
        h.delete(contract_hash)
    def delete(self, delete_images=True):
        """
        Deletes the contract json from the OpenBazaar directory as well as the listing
        metadata from the db and all the related images in the file system.
        """
        # build the file_name from the contract
        file_name = str(
            self.contract["vendor_offer"]["listing"]["item"]["title"][:100])
        file_name = re.sub(r"[^\w\s]", '', file_name)
        file_name = re.sub(r"\s+", '_', file_name)
        file_path = DATA_FOLDER + "store/listings/contracts/" + file_name + ".json"

        h = HashMap()

        # maybe delete the images from disk
        if "image_hashes" in self.contract["vendor_offer"]["listing"][
                "item"] and delete_images:
            for image_hash in self.contract["vendor_offer"]["listing"]["item"][
                    "image_hashes"]:
                # delete from disk
                image_path = h.get_file(unhexlify(image_hash))
                if os.path.exists(image_path):
                    os.remove(image_path)
                # remove pointer to the image from the HashMap
                h.delete(unhexlify(image_hash))

        # delete the contract from disk
        if os.path.exists(file_path):
            os.remove(file_path)
        # delete the listing metadata from the db
        contract_hash = digest(json.dumps(self.contract, indent=4))
        ListingsStore().delete_listing(contract_hash)
        # remove the pointer to the contract from the HashMap
        h.delete(contract_hash)
Esempio n. 6
0
    def update(self,
               expiration_date=None,
               metadata_category=None,
               title=None,
               description=None,
               currency_code=None,
               price=None,
               process_time=None,
               nsfw=None,
               est_delivery_domestic=None,
               est_delivery_international=None,
               shipping_origin=None,
               shipping_regions=None,
               keywords=None,
               category=None,
               condition=None,
               sku=None,
               image_hashes=None,  # if intending to delete an image, pass in
                                   # the hashes that are staying.
               images=None,  # to add new images pass in a list of image files.
               free_shipping=None,
               shipping_currency_code=None,
               shipping_domestic=None,
               shipping_international=None):

        self.delete(False)
        vendor_listing = self.contract["vendor_offer"]["listing"]
        if expiration_date is not None:
            vendor_listing["item"]["expiry"] = expiration_date
        if metadata_category is not None:
            vendor_listing["metadata"]["category"] = metadata_category
        if metadata_category != "physical good" and vendor_listing["metadata"][
                "category"] == "physical good":
            del vendor_listing["shipping"]
        elif metadata_category == "physical good" and vendor_listing["metadata"][
                "category"] != "physical good":
            vendor_listing["shipping"] = {}
            vendor_listing["shipping"]["est_delivery"] = {}
            vendor_listing["shipping"]["free"] = False
        if title is not None:
            vendor_listing["item"]["title"] = title
        if description is not None:
            vendor_listing["item"]["description"] = description
        if currency_code is not None:
            if currency_code.upper() != "BTC" and "bitcoin" \
                    in vendor_listing["item"]["price_per_unit"]:
                p = vendor_listing["item"]["price_per_unit"]["bitcoin"]
                del vendor_listing["item"]["price_per_unit"]["bitcoin"]
                vendor_listing["item"]["price_per_unit"]["fiat"] = {}
                vendor_listing["item"]["price_per_unit"]["fiat"][
                    "currency_code"] = currency_code
                vendor_listing["item"]["price_per_unit"]["fiat"]["price"] = p
            elif currency_code.upper() == "BTC" and "fiat" in \
                    vendor_listing["item"]["price_per_unit"]:
                p = vendor_listing["item"]["price_per_unit"]["fiat"]["price"]
                del vendor_listing["item"]["price_per_unit"]["fiat"]
                vendor_listing["item"]["price_per_unit"]["bitcoin"] = p
        if price is not None:
            if "bitcoin" in vendor_listing["item"]["price_per_unit"]:
                vendor_listing["item"]["price_per_unit"]["bitcoin"] = price
            else:
                vendor_listing["item"]["price_per_unit"]["fiat"]["price"] = price
        if process_time is not None:
            vendor_listing["item"]["process_time"] = process_time
        if nsfw is not None:
            vendor_listing["item"]["nsfw"] = nsfw
        if keywords is not None:
            vendor_listing["item"]["keywords"] = []
            vendor_listing["item"]["keywords"].extend(keywords)
        if category is not None:
            vendor_listing["item"]["category"] = category
        if image_hashes is not None:
            to_delete = list(set(vendor_listing["item"]["image_hashes"]) - set(image_hashes))
            for image_hash in to_delete:
                # delete from disk
                h = HashMap()
                image_path = h.get_file(unhexlify(image_hash))
                if os.path.exists(image_path):
                    os.remove(image_path)
                # remove pointer to the image from the HashMap
                h.delete(unhexlify(image_hash))
            vendor_listing["item"]["image_hashes"] = []
            vendor_listing["item"]["image_hashes"].extend(image_hashes)
        if images is not None:
            if "image_hashes" not in vendor_listing["item"]:
                vendor_listing["item"]["image_hashes"] = []
            for image in images:
                hash_value = digest(image).encode("hex")
                vendor_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)
        if vendor_listing["metadata"]["category"] == "physical good" and condition is not None:
            vendor_listing["item"]["condition"] = condition
        if sku is not None:
            vendor_listing["item"]["sku"] = sku
        if vendor_listing["metadata"]["category"] == "physical good":
            if shipping_origin is not None:
                vendor_listing["shipping"]["shipping_origin"] = shipping_origin
            if free_shipping is not None:
                if free_shipping is True and vendor_listing["shipping"]["free"] is False:
                    vendor_listing["shipping"]["free"] = True
                    del vendor_listing["shipping"]["flat_fee"]
                elif free_shipping is False and vendor_listing["shipping"]["free"] is True:
                    vendor_listing["shipping"]["flat_fee"] = {}
                    vendor_listing["shipping"]["flat_fee"]["bitcoin"] = {}
                    vendor_listing["shipping"]["free"] = False
            if shipping_currency_code is not None and vendor_listing["shipping"]["free"] is False:
                if shipping_currency_code == "BTC" and "bitcoin" not in \
                        vendor_listing["shipping"]["flat_fee"]:
                    vendor_listing["shipping"]["flat_fee"]["bitcoin"] = {}
                    d = vendor_listing["shipping"]["flat_fee"]["fiat"]["price"]["domestic"]
                    i = vendor_listing["shipping"]["flat_fee"]["fiat"]["price"][
                        "international"]
                    vendor_listing["shipping"]["flat_fee"]["bitcoin"]["domestic"] = d
                    vendor_listing["shipping"]["flat_fee"]["bitcoin"]["international"] = i
                    del vendor_listing["shipping"]["flat_fee"]["fiat"]
                elif shipping_currency_code != "BTC" and "bitcoin" in \
                        vendor_listing["shipping"]["flat_fee"]:
                    d = vendor_listing["shipping"]["flat_fee"]["bitcoin"]["domestic"]
                    i = vendor_listing["shipping"]["flat_fee"]["bitcoin"]["international"]
                    vendor_listing["shipping"]["flat_fee"]["fiat"] = {}
                    vendor_listing["shipping"]["flat_fee"]["fiat"]["price"] = {}
                    vendor_listing["shipping"]["flat_fee"]["fiat"]["price"]["domestic"] = d
                    vendor_listing["shipping"]["flat_fee"]["fiat"]["price"][
                        "international"] = i
                    vendor_listing["shipping"]["flat_fee"]["fiat"][
                        "currency_code"] = shipping_currency_code
                    del vendor_listing["shipping"]["flat_fee"]["bitcoin"]
            if shipping_domestic is not None and "bitcoin" not in \
                    vendor_listing["shipping"]["flat_fee"]:
                vendor_listing["shipping"]["flat_fee"]["fiat"]["price"][
                    "domestic"] = shipping_domestic
            if shipping_international is not None and "bitcoin" not in \
                    vendor_listing["shipping"]["flat_fee"]:
                vendor_listing["shipping"]["flat_fee"]["fiat"]["price"][
                    "international"] = shipping_international
            if shipping_domestic is not None and "bitcoin" in \
                    vendor_listing["shipping"]["flat_fee"]:
                vendor_listing["shipping"]["flat_fee"]["bitcoin"][
                    "domestic"] = shipping_domestic
            if shipping_international is not None and "bitcoin" in \
                    vendor_listing["shipping"]["flat_fee"]:
                vendor_listing["shipping"]["flat_fee"]["bitcoin"][
                    "international"] = shipping_international
            if shipping_regions is not None:
                vendor_listing["shipping"]["shipping_regions"] = shipping_regions
            if est_delivery_domestic is not None:
                vendor_listing["shipping"]["est_delivery"]["domestic"] = est_delivery_domestic
            if est_delivery_international is not None:
                vendor_listing["shipping"]["est_delivery"][
                    "international"] = est_delivery_international

        self.save()
Esempio n. 7
0
class MarketProtocol(RPCProtocol):
    implements(MessageProcessor)

    def __init__(self, node_proto, router, signing_key):
        self.router = router
        RPCProtocol.__init__(self, node_proto, router)
        self.log = Logger(system=self)
        self.multiplexer = None
        self.hashmap = HashMap()
        self.signing_key = signing_key
        self.listeners = []
        self.handled_commands = [GET_CONTRACT, GET_IMAGE, GET_PROFILE, GET_LISTINGS, GET_USER_METADATA,
                                 GET_CONTRACT_METADATA, FOLLOW, UNFOLLOW, GET_FOLLOWERS, GET_FOLLOWING,
                                 NOTIFY, MESSAGE]

    def connect_multiplexer(self, multiplexer):
        self.multiplexer = multiplexer

    def add_listener(self, listener):
        self.listeners.append(listener)

    def rpc_get_contract(self, sender, contract_hash):
        self.log.info("Looking up contract ID %s" % contract_hash.encode('hex'))
        self.router.addContact(sender)
        try:
            with open(self.hashmap.get_file(contract_hash), "r") as filename:
                contract = filename.read()
            return [contract]
        except Exception:
            self.log.warning("Could not find contract %s" % contract_hash.encode('hex'))
            return ["None"]

    def rpc_get_image(self, sender, image_hash):
        self.log.info("Looking up image with hash %s" % image_hash.encode('hex'))
        self.router.addContact(sender)
        try:
            with open(self.hashmap.get_file(image_hash), "r") as filename:
                image = filename.read()
            return [image]
        except Exception:
            self.log.warning("Could not find image %s" % image_hash.encode('hex'))
            return ["None"]

    def rpc_get_profile(self, sender):
        self.log.info("Fetching profile")
        self.router.addContact(sender)
        try:
            proto = Profile().get(True)
            return [proto, self.signing_key.sign(proto)[:64]]
        except Exception:
            self.log.error("Unable to load the profile")
            return ["None"]

    def rpc_get_user_metadata(self, sender):
        self.log.info("Fetching metadata")
        self.router.addContact(sender)
        try:
            proto = Profile().get(False)
            m = Metadata()
            m.name = proto.name
            m.handle = proto.handle
            m.short_description = proto.short_description
            m.avatar_hash = proto.avatar_hash
            m.nsfw = proto.nsfw
            return [m.SerializeToString(), self.signing_key.sign(m.SerializeToString())[:64]]
        except Exception:
            self.log.error("Unable to get the profile metadata")
            return ["None"]

    def rpc_get_listings(self, sender):
        self.log.info("Fetching listings")
        self.router.addContact(sender)
        try:
            p = Profile().get()
            l = Listings()
            l.ParseFromString(ListingsStore().get_proto())
            l.handle = p.handle
            l.avatar_hash = p.avatar_hash
            return [l.SerializeToString(), self.signing_key.sign(l.SerializeToString())[:64]]
        except Exception:
            self.log.warning("Could not find any listings in the database")
            return ["None"]

    def rpc_get_contract_metadata(self, sender, contract_hash):
        self.log.info("Fetching metadata for contract %s" % hexlify(contract_hash))
        self.router.addContact(sender)
        try:
            proto = ListingsStore().get_proto()
            l = Listings()
            l.ParseFromString(proto)
            for listing in l.listing:
                if listing.contract_hash == contract_hash:
                    ser = listing.SerializeToString()
            return [ser, self.signing_key.sign(ser)[:64]]
        except Exception:
            self.log.warning("Could not find metadata for contract %s" % hexlify(contract_hash))
            return ["None"]

    def rpc_follow(self, sender, proto, signature):
        self.log.info("Follow request from %s" % sender.id.encode("hex"))
        self.router.addContact(sender)
        try:
            verify_key = nacl.signing.VerifyKey(sender.signed_pubkey[64:])
            verify_key.verify(proto, signature)
            f = Followers.Follower()
            f.ParseFromString(proto)
            if f.guid != sender.id:
                raise Exception('GUID does not match sending node')
            if f.following != self.proto.guid:
                raise Exception('Following wrong node')
            f.signature = signature
            FollowData().set_follower(f)
            proto = Profile().get(False)
            m = Metadata()
            m.name = proto.name
            m.handle = proto.handle
            m.avatar_hash = proto.avatar_hash
            m.nsfw = proto.nsfw
            return ["True", m.SerializeToString(), self.signing_key.sign(m.SerializeToString())[:64]]
        except Exception:
            self.log.warning("Failed to validate follower")
            return ["False"]

    def rpc_unfollow(self, sender, signature):
        self.log.info("Unfollow request from %s" % sender.id.encode("hex"))
        self.router.addContact(sender)
        try:
            verify_key = nacl.signing.VerifyKey(sender.signed_pubkey[64:])
            verify_key.verify("unfollow:" + self.proto.guid, signature)
            f = FollowData()
            f.delete_follower(sender.id)
            return ["True"]
        except Exception:
            self.log.warning("Failed to validate follower signature")
            return ["False"]

    def rpc_get_followers(self, sender):
        self.log.info("Fetching followers list from db")
        self.router.addContact(sender)
        ser = FollowData().get_followers()
        if ser is None:
            return ["None"]
        else:
            return [ser, self.signing_key.sign(ser)[:64]]

    def rpc_get_following(self, sender):
        self.log.info("Fetching following list from db")
        self.router.addContact(sender)
        ser = FollowData().get_following()
        if ser is None:
            return ["None"]
        else:
            return [ser, self.signing_key.sign(ser)[:64]]

    def rpc_notify(self, sender, message, signature):
        if len(message) <= 140 and FollowData().is_following(sender.id):
            try:
                verify_key = nacl.signing.VerifyKey(sender.signed_pubkey[64:])
                verify_key.verify(message, signature)
            except Exception:
                return ["False"]
            self.log.info("Received a notification from %s" % sender)
            self.router.addContact(sender)
            for listener in self.listeners:
                try:
                    verifyObject(NotificationListener, listener)
                    listener.notify(sender.id, message)
                except DoesNotImplement:
                    pass
            return ["True"]
        else:
            return ["False"]

    def rpc_message(self, sender, pubkey, encrypted):
        try:
            box = Box(PrivateKey(self.signing_key.encode(nacl.encoding.RawEncoder)), PublicKey(pubkey))
            plaintext = box.decrypt(encrypted)
            p = Plaintext_Message()
            p.ParseFromString(plaintext)
            signature = p.signature
            p.ClearField("signature")
            verify_key = nacl.signing.VerifyKey(p.signed_pubkey[64:])
            verify_key.verify(p.SerializeToString(), signature)
            h = nacl.hash.sha512(p.signed_pubkey)
            pow_hash = h[64:128]
            if int(pow_hash[:6], 16) >= 50 or hexlify(p.sender_guid) != h[:40] or p.sender_guid != sender.id:
                raise Exception('Invalid guid')
            self.log.info("Received a message from %s" % sender)
            self.router.addContact(sender)
            for listener in self.listeners:
                try:
                    verifyObject(MessageListener, listener)
                    listener.notify(p, signature)
                except DoesNotImplement:
                    pass
            return ["True"]
        except Exception:
            self.log.error("Received invalid message from %s" % sender)
            return ["False"]

    def callGetContract(self, nodeToAsk, contract_hash):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_contract(address, contract_hash)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetImage(self, nodeToAsk, image_hash):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_image(address, image_hash)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetProfile(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_profile(address)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetUserMetadata(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_user_metadata(address)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetListings(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_listings(address)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetContractMetadata(self, nodeToAsk, contract_hash):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_contract_metadata(address, contract_hash)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callFollow(self, nodeToAsk, proto, signature):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.follow(address, proto, signature)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callUnfollow(self, nodeToAsk, signature):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.unfollow(address, signature)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetFollowers(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_followers(address)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetFollowing(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_following(address)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callNotify(self, nodeToAsk, message, signature):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.notify(address, message, signature)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callMessage(self, nodeToAsk, ehemeral_pubkey, ciphertext):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.message(address, ehemeral_pubkey, ciphertext)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def handleCallResponse(self, result, node):
        """
        If we get a response, add the node to the routing table.  If
        we get no response, make sure it's removed from the routing table.
        """
        if result[0]:
            self.log.info("got response from %s, adding to router" % node)
            self.router.addContact(node)
        else:
            self.log.debug("no response from %s, removing from router" % node)
            self.router.removeContact(node)
        return result

    def __iter__(self):
        return iter(self.handled_commands)
Esempio n. 8
0
class MarketProtocol(RPCProtocol):
    implements(MessageProcessor)

    def __init__(self, node_proto, router, signing_key):
        self.router = router
        RPCProtocol.__init__(self, node_proto, router)
        self.log = Logger(system=self)
        self.multiplexer = None
        self.hashmap = HashMap()
        self.signing_key = signing_key
        self.listeners = []
        self.handled_commands = [
            GET_CONTRACT, GET_IMAGE, GET_PROFILE, GET_LISTINGS,
            GET_USER_METADATA, GET_CONTRACT_METADATA, FOLLOW, UNFOLLOW,
            GET_FOLLOWERS, GET_FOLLOWING, NOTIFY, MESSAGE
        ]

    def connect_multiplexer(self, multiplexer):
        self.multiplexer = multiplexer

    def add_listener(self, listener):
        self.listeners.append(listener)

    def rpc_get_contract(self, sender, contract_hash):
        self.log.info("Looking up contract ID %s" %
                      contract_hash.encode('hex'))
        self.router.addContact(sender)
        try:
            with open(self.hashmap.get_file(contract_hash), "r") as filename:
                contract = filename.read()
            return [contract]
        except Exception:
            self.log.warning("Could not find contract %s" %
                             contract_hash.encode('hex'))
            return ["None"]

    def rpc_get_image(self, sender, image_hash):
        self.log.info("Looking up image with hash %s" %
                      image_hash.encode('hex'))
        self.router.addContact(sender)
        try:
            with open(self.hashmap.get_file(image_hash), "r") as filename:
                image = filename.read()
            return [image]
        except Exception:
            self.log.warning("Could not find image %s" %
                             image_hash.encode('hex'))
            return ["None"]

    def rpc_get_profile(self, sender):
        self.log.info("Fetching profile")
        self.router.addContact(sender)
        try:
            proto = Profile().get(True)
            return [proto, self.signing_key.sign(proto)[:64]]
        except Exception:
            self.log.error("Unable to load the profile")
            return ["None"]

    def rpc_get_user_metadata(self, sender):
        self.log.info("Fetching metadata")
        self.router.addContact(sender)
        try:
            proto = Profile().get(False)
            m = Metadata()
            m.name = proto.name
            m.handle = proto.handle
            m.avatar_hash = proto.avatar_hash
            m.nsfw = proto.nsfw
            return [
                m.SerializeToString(),
                self.signing_key.sign(m.SerializeToString())[:64]
            ]
        except Exception:
            self.log.error("Unable to get the profile metadata")
            return ["None"]

    def rpc_get_listings(self, sender):
        self.log.info("Fetching listings")
        self.router.addContact(sender)
        try:
            proto = ListingsStore().get_proto()
            return [proto, self.signing_key.sign(proto)[:64]]
        except Exception:
            self.log.warning("Could not find any listings in the database")
            return ["None"]

    def rpc_get_contract_metadata(self, sender, contract_hash):
        self.log.info("Fetching metadata for contract %s" %
                      hexlify(contract_hash))
        self.router.addContact(sender)
        try:
            proto = ListingsStore().get_proto()
            l = Listings()
            l.ParseFromString(proto)
            for listing in l.listing:
                if listing.contract_hash == contract_hash:
                    country_code = Profile().get().country_code
                    listing.country_code = country_code
                    ser = listing.SerializeToString()
            return [ser, self.signing_key.sign(ser)[:64]]
        except Exception:
            self.log.warning("Could not find metadata for contract %s" %
                             hexlify(contract_hash))
            return ["None"]

    def rpc_follow(self, sender, proto, signature):
        self.log.info("Follow request from %s" % sender.id.encode("hex"))
        self.router.addContact(sender)
        try:
            verify_key = nacl.signing.VerifyKey(sender.signed_pubkey[64:])
            verify_key.verify(proto, signature)
            f = Followers.Follower()
            f.ParseFromString(proto)
            if f.guid != sender.id:
                raise Exception('GUID does not match sending node')
            if f.following != self.proto.guid:
                raise Exception('Following wrong node')
            f.signature = signature
            FollowData().set_follower(f)
            proto = Profile().get(False)
            m = Metadata()
            m.name = proto.name
            m.handle = proto.handle
            m.avatar_hash = proto.avatar_hash
            m.nsfw = proto.nsfw
            return [
                "True",
                m.SerializeToString(),
                self.signing_key.sign(m.SerializeToString())[:64]
            ]
        except Exception:
            self.log.warning("Failed to validate follower")
            return ["False"]

    def rpc_unfollow(self, sender, signature):
        self.log.info("Unfollow request from %s" % sender.id.encode("hex"))
        self.router.addContact(sender)
        try:
            verify_key = nacl.signing.VerifyKey(sender.signed_pubkey[64:])
            verify_key.verify("unfollow:" + self.proto.guid, signature)
            f = FollowData()
            f.delete_follower(sender.id)
            return ["True"]
        except Exception:
            self.log.warning("Failed to validate follower signature")
            return ["False"]

    def rpc_get_followers(self, sender):
        self.log.info("Fetching followers list from db")
        self.router.addContact(sender)
        ser = FollowData().get_followers()
        if ser is None:
            return ["None"]
        else:
            return [ser, self.signing_key.sign(ser)[:64]]

    def rpc_get_following(self, sender):
        self.log.info("Fetching following list from db")
        self.router.addContact(sender)
        ser = FollowData().get_following()
        if ser is None:
            return ["None"]
        else:
            return [ser, self.signing_key.sign(ser)[:64]]

    def rpc_notify(self, sender, message, signature):
        if len(message) <= 140 and FollowData().is_following(sender.id):
            try:
                verify_key = nacl.signing.VerifyKey(sender.signed_pubkey[64:])
                verify_key.verify(message, signature)
            except Exception:
                return ["False"]
            self.log.info("Received a notification from %s" % sender)
            self.router.addContact(sender)
            for listener in self.listeners:
                try:
                    verifyObject(NotificationListener, listener)
                    listener.notify(sender.id, message)
                except DoesNotImplement:
                    pass
            return ["True"]
        else:
            return ["False"]

    def rpc_message(self, sender, pubkey, encrypted):
        try:
            box = Box(
                PrivateKey(self.signing_key.encode(nacl.encoding.RawEncoder)),
                PublicKey(pubkey))
            plaintext = box.decrypt(encrypted)
            p = Plaintext_Message()
            p.ParseFromString(plaintext)
            signature = p.signature
            p.ClearField("signature")
            verify_key = nacl.signing.VerifyKey(p.signed_pubkey[64:])
            verify_key.verify(p.SerializeToString(), signature)
            h = nacl.hash.sha512(p.signed_pubkey)
            pow_hash = h[64:128]
            if int(pow_hash[:6], 16) >= 50 or hexlify(
                    p.sender_guid) != h[:40] or p.sender_guid != sender.id:
                raise Exception('Invalid guid')
            self.log.info("Received a message from %s" % sender)
            self.router.addContact(sender)
            for listener in self.listeners:
                try:
                    verifyObject(MessageListener, listener)
                    listener.notify(p, signature)
                except DoesNotImplement:
                    pass
            return ["True"]
        except Exception:
            self.log.error("Received invalid message from %s" % sender)
            return ["False"]

    def callGetContract(self, nodeToAsk, contract_hash):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_contract(address, contract_hash)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetImage(self, nodeToAsk, image_hash):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_image(address, image_hash)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetProfile(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_profile(address)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetUserMetadata(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_user_metadata(address)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetListings(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_listings(address)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetContractMetadata(self, nodeToAsk, contract_hash):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_contract_metadata(address, contract_hash)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callFollow(self, nodeToAsk, proto, signature):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.follow(address, proto, signature)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callUnfollow(self, nodeToAsk, signature):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.unfollow(address, signature)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetFollowers(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_followers(address)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callGetFollowing(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.get_following(address)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callNotify(self, nodeToAsk, message, signature):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.notify(address, message, signature)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callMessage(self, nodeToAsk, ehemeral_pubkey, ciphertext):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.message(address, ehemeral_pubkey, ciphertext)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def handleCallResponse(self, result, node):
        """
        If we get a response, add the node to the routing table.  If
        we get no response, make sure it's removed from the routing table.
        """
        if result[0]:
            self.log.info("got response from %s, adding to router" % node)
            self.router.addContact(node)
        else:
            self.log.debug("no response from %s, removing from router" % node)
            self.router.removeContact(node)
        return result

    def __iter__(self):
        return iter(self.handled_commands)
    def update(
            self,
            expiration_date=None,
            metadata_category=None,
            title=None,
            description=None,
            currency_code=None,
            price=None,
            process_time=None,
            nsfw=None,
            est_delivery_domestic=None,
            est_delivery_international=None,
            shipping_origin=None,
            shipping_regions=None,
            keywords=None,
            category=None,
            condition=None,
            sku=None,
            image_hashes=None,  # if intending to delete an image, pass in
            # the hashes that are staying.
        images=None,  # to add new images pass in a list of image files.
            free_shipping=None,
            shipping_currency_code=None,
            shipping_domestic=None,
            shipping_international=None):

        self.delete(False)
        vendor_listing = self.contract["vendor_offer"]["listing"]
        if expiration_date is not None:
            vendor_listing["item"]["expiry"] = expiration_date
        if metadata_category is not None:
            vendor_listing["metadata"]["category"] = metadata_category
        if metadata_category != "physical good" and vendor_listing["metadata"][
                "category"] == "physical good":
            del vendor_listing["shipping"]
        elif metadata_category == "physical good" and vendor_listing[
                "metadata"]["category"] != "physical good":
            vendor_listing["shipping"] = {}
            vendor_listing["shipping"]["est_delivery"] = {}
            vendor_listing["shipping"]["free"] = False
        if title is not None:
            vendor_listing["item"]["title"] = title
        if description is not None:
            vendor_listing["item"]["description"] = description
        if currency_code is not None:
            if currency_code.upper() != "BTC" and "bitcoin" \
                    in vendor_listing["item"]["price_per_unit"]:
                p = vendor_listing["item"]["price_per_unit"]["bitcoin"]
                del vendor_listing["item"]["price_per_unit"]["bitcoin"]
                vendor_listing["item"]["price_per_unit"]["fiat"] = {}
                vendor_listing["item"]["price_per_unit"]["fiat"][
                    "currency_code"] = currency_code
                vendor_listing["item"]["price_per_unit"]["fiat"]["price"] = p
            elif currency_code.upper() == "BTC" and "fiat" in \
                    vendor_listing["item"]["price_per_unit"]:
                p = vendor_listing["item"]["price_per_unit"]["fiat"]["price"]
                del vendor_listing["item"]["price_per_unit"]["fiat"]
                vendor_listing["item"]["price_per_unit"]["bitcoin"] = p
        if price is not None:
            if "bitcoin" in vendor_listing["item"]["price_per_unit"]:
                vendor_listing["item"]["price_per_unit"]["bitcoin"] = price
            else:
                vendor_listing["item"]["price_per_unit"]["fiat"][
                    "price"] = price
        if process_time is not None:
            vendor_listing["item"]["process_time"] = process_time
        if nsfw is not None:
            vendor_listing["item"]["nsfw"] = nsfw
        if keywords is not None:
            vendor_listing["item"]["keywords"] = []
            vendor_listing["item"]["keywords"].extend(keywords)
        if category is not None:
            vendor_listing["item"]["category"] = category
        if image_hashes is not None:
            to_delete = list(
                set(vendor_listing["item"]["image_hashes"]) -
                set(image_hashes))
            for image_hash in to_delete:
                # delete from disk
                h = HashMap()
                image_path = h.get_file(unhexlify(image_hash))
                if os.path.exists(image_path):
                    os.remove(image_path)
                # remove pointer to the image from the HashMap
                h.delete(unhexlify(image_hash))
            vendor_listing["item"]["image_hashes"] = []
            vendor_listing["item"]["image_hashes"].extend(image_hashes)
        if images is not None:
            if "image_hashes" not in vendor_listing["item"]:
                vendor_listing["item"]["image_hashes"] = []
            for image in images:
                hash_value = digest(image).encode("hex")
                vendor_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)
        if vendor_listing["metadata"][
                "category"] == "physical good" and condition is not None:
            vendor_listing["item"]["condition"] = condition
        if sku is not None:
            vendor_listing["item"]["sku"] = sku
        if vendor_listing["metadata"]["category"] == "physical good":
            if shipping_origin is not None:
                vendor_listing["shipping"]["shipping_origin"] = shipping_origin
            if free_shipping is not None:
                if free_shipping is True and vendor_listing["shipping"][
                        "free"] is False:
                    vendor_listing["shipping"]["free"] = True
                    del vendor_listing["shipping"]["flat_fee"]
                elif free_shipping is False and vendor_listing["shipping"][
                        "free"] is True:
                    vendor_listing["shipping"]["flat_fee"] = {}
                    vendor_listing["shipping"]["flat_fee"]["bitcoin"] = {}
                    vendor_listing["shipping"]["free"] = False
            if shipping_currency_code is not None and vendor_listing[
                    "shipping"]["free"] is False:
                if shipping_currency_code == "BTC" and "bitcoin" not in \
                        vendor_listing["shipping"]["flat_fee"]:
                    vendor_listing["shipping"]["flat_fee"]["bitcoin"] = {}
                    d = vendor_listing["shipping"]["flat_fee"]["fiat"][
                        "price"]["domestic"]
                    i = vendor_listing["shipping"]["flat_fee"]["fiat"][
                        "price"]["international"]
                    vendor_listing["shipping"]["flat_fee"]["bitcoin"][
                        "domestic"] = d
                    vendor_listing["shipping"]["flat_fee"]["bitcoin"][
                        "international"] = i
                    del vendor_listing["shipping"]["flat_fee"]["fiat"]
                elif shipping_currency_code != "BTC" and "bitcoin" in \
                        vendor_listing["shipping"]["flat_fee"]:
                    d = vendor_listing["shipping"]["flat_fee"]["bitcoin"][
                        "domestic"]
                    i = vendor_listing["shipping"]["flat_fee"]["bitcoin"][
                        "international"]
                    vendor_listing["shipping"]["flat_fee"]["fiat"] = {}
                    vendor_listing["shipping"]["flat_fee"]["fiat"][
                        "price"] = {}
                    vendor_listing["shipping"]["flat_fee"]["fiat"]["price"][
                        "domestic"] = d
                    vendor_listing["shipping"]["flat_fee"]["fiat"]["price"][
                        "international"] = i
                    vendor_listing["shipping"]["flat_fee"]["fiat"][
                        "currency_code"] = shipping_currency_code
                    del vendor_listing["shipping"]["flat_fee"]["bitcoin"]
            if shipping_domestic is not None and "bitcoin" not in \
                    vendor_listing["shipping"]["flat_fee"]:
                vendor_listing["shipping"]["flat_fee"]["fiat"]["price"][
                    "domestic"] = shipping_domestic
            if shipping_international is not None and "bitcoin" not in \
                    vendor_listing["shipping"]["flat_fee"]:
                vendor_listing["shipping"]["flat_fee"]["fiat"]["price"][
                    "international"] = shipping_international
            if shipping_domestic is not None and "bitcoin" in \
                    vendor_listing["shipping"]["flat_fee"]:
                vendor_listing["shipping"]["flat_fee"]["bitcoin"][
                    "domestic"] = shipping_domestic
            if shipping_international is not None and "bitcoin" in \
                    vendor_listing["shipping"]["flat_fee"]:
                vendor_listing["shipping"]["flat_fee"]["bitcoin"][
                    "international"] = shipping_international
            if shipping_regions is not None:
                vendor_listing["shipping"][
                    "shipping_regions"] = shipping_regions
            if est_delivery_domestic is not None:
                vendor_listing["shipping"]["est_delivery"][
                    "domestic"] = est_delivery_domestic
            if est_delivery_international is not None:
                vendor_listing["shipping"]["est_delivery"][
                    "international"] = est_delivery_international

        self.save()