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 __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 get_image(self, request):
        @defer.inlineCallbacks
        def _showImage(resp=None):
            @defer.inlineCallbacks
            def _setContentDispositionAndSend(file_path, extension,
                                              content_type):
                request.setHeader('content-disposition',
                                  'filename="%s.%s"' % (file_path, extension))
                request.setHeader('content-type', content_type)

                f = open(file_path, "rb")
                yield FileSender().beginFileTransfer(f, request)
                f.close()
                defer.returnValue(0)

            if os.path.exists(image_path):
                yield _setContentDispositionAndSend(image_path, ".jpg",
                                                    "image/jpeg")
            else:
                request.setResponseCode(http.NOT_FOUND)
                request.write("No such image '%s'" % request.path)
            request.finish()

        if "hash" in request.args:
            if HashMap().get_file(unhexlify(
                    request.args["hash"][0])) is not None:
                image_path = HashMap().get_file(
                    unhexlify(request.args["hash"][0]))
            else:
                image_path = DATA_FOLDER + "cache/" + request.args["hash"][0]
            if not os.path.exists(image_path) and "guid" in request.args:

                def get_node(node):
                    if node is not None:
                        self.mserver.get_image(
                            node, unhexlify(request.args["hash"]
                                            [0])).addCallback(_showImage)
                    else:
                        _showImage()

                self.kserver.resolve(unhexlify(
                    request.args["guid"][0])).addCallback(get_node)
            else:
                _showImage()
        else:
            request.write(NoResource().render(request))
            request.finish()

        return server.NOT_DONE_YET
    def __init__(self, contract=None, hash_value=None):
        """
        This class can be instantiated with either an `OrderedDict` or a hash
        of a contract. If a hash is used, we will load the contract from either
        the file system or cache.

        Alternatively, pass in no parameters if the intent is to create a new
        contract.

        Args:
            contract: an `OrderedDict` containing a filled out json contract
            hash: a hash (in raw bytes) of a contract
        """
        if contract is not None:
            self.contract = contract
        elif hash_value is not None:
            try:
                file_path = HashMap().get_file(hash_value)
                if file_path is None:
                    file_path = DATA_FOLDER + "cache/" + hexlify(hash_value)
                with open(file_path, 'r') as filename:
                    self.contract = json.load(filename,
                                              object_pairs_hook=OrderedDict)
            except Exception:
                self.contract = {}
        else:
            self.contract = {}
Esempio n. 6
0
 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 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 update_profile(self, request):
     p = Profile()
     if not p.get().encryption_key \
             and "name" not in request.args \
             and "location" not in request.args:
         return "False"
     u = objects.Profile()
     if "name" in request.args:
         u.name = request.args["name"][0]
     if "location" in request.args:
         # This needs to be formatted. Either here or from the UI.
         u.location = CountryCode.Value(request.args["location"][0].upper())
     if "handle" in request.args:
         u.handle = request.args["handle"][0]
     if "about" in request.args:
         u.about = request.args["about"][0]
     if "nsfw" in request.args:
         u.nsfw = True
     if "vendor" in request.args:
         u.vendor = True
     if "moderator" in request.args:
         u.moderator = True
     if "website" in request.args:
         u.website = request.args["website"][0]
     if "email" in request.args:
         u.email = request.args["email"][0]
     if "avatar" in request.args:
         with open(DATA_FOLDER + "store/avatar", 'wb') as outfile:
             outfile.write(request.args["avatar"][0])
         avatar_hash = digest(request.args["avatar"][0])
         HashMap().insert(avatar_hash, DATA_FOLDER + "store/avatar")
         u.avatar_hash = avatar_hash
     if "header" in request.args:
         with open(DATA_FOLDER + "store/header", 'wb') as outfile:
             outfile.write(request.args["header"][0])
         header_hash = digest(request.args["header"][0])
         HashMap().insert(header_hash, DATA_FOLDER + "store/header")
         u.header_hash = header_hash
     if "pgp_key" in request.args and "signature" in request.args:
         p.add_pgp_key(request.args["pgp_key"][0],
                       request.args["signature"][0],
                       KeyChain().guid.encode("hex"))
     u.encryption_key = KeyChain().encryption_pubkey
     p.update(u)
Esempio n. 9
0
 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 save(self):
        """
        Saves the json contract into the OpenBazaar/store/listings/contracts/ directory.
        It uses the title as the file name so it's easy on human eyes. A mapping of the
        hash of the contract and file path is stored in the database so we can retrieve
        the contract with only its hash.

        Additionally, the contract metadata (sent in response to the GET_LISTINGS query)
        is saved in the db for fast access.
        """
        # get the contract title to use as the file name and format it
        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)

        # save the json contract to the file system
        file_path = DATA_FOLDER + "store/listings/contracts/" + file_name + ".json"
        with open(file_path, 'w') as outfile:
            outfile.write(json.dumps(self.contract, indent=4))

        # Create a `ListingMetadata` protobuf object using data from the full contract
        listings = Listings()
        data = listings.ListingMetadata()
        data.contract_hash = digest(json.dumps(self.contract, indent=4))
        vendor_item = self.contract["vendor_offer"]["listing"]["item"]
        data.title = vendor_item["title"]
        if "image_hashes" in vendor_item:
            data.thumbnail_hash = unhexlify(vendor_item["image_hashes"][0])
        data.category = vendor_item["category"]
        if "bitcoin" not in vendor_item["price_per_unit"]:
            data.price = float(vendor_item["price_per_unit"]["fiat"]["price"])
            data.currency_code = vendor_item["price_per_unit"]["fiat"][
                "currency_code"]
        else:
            data.price = float(vendor_item["price_per_unit"]["bitcoin"])
            data.currency_code = "BTC"
        data.nsfw = vendor_item["nsfw"]
        if "shipping" not in self.contract["vendor_offer"]["listing"]:
            data.origin = CountryCode.Value("NA")
        else:
            data.origin = CountryCode.Value(
                self.contract["vendor_offer"]["listing"]["shipping"]
                ["shipping_origin"].upper())
            for region in self.contract["vendor_offer"]["listing"]["shipping"][
                    "shipping_regions"]:
                data.ships_to.append(CountryCode.Value(region.upper()))

        # save the mapping of the contract file path and contract hash in the database
        HashMap().insert(data.contract_hash, file_path)

        # save the `ListingMetadata` protobuf to the database as well
        ListingsStore().add_listing(data)
Esempio n. 11
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)
Esempio n. 12
0
 def setprofile():
     parser = argparse.ArgumentParser(
         description="Sets a profile in the database.",
         usage='''usage:
 networkcli.py setprofile [options]''')
     parser.add_argument('-n', '--name', help="the name of the user/store")
     parser.add_argument('-o', '--onename', help="the onename id")
     parser.add_argument('-a', '--avatar', help="the file path to the avatar image")
     parser.add_argument('-hd', '--header', help="the file path to the header image")
     parser.add_argument('-c', '--country',
                         help="a string consisting of country from protos.countries.CountryCode")
     # we could add all the fields here but this is good enough to test.
     args = parser.parse_args(sys.argv[2:])
     p = Profile()
     u = objects.Profile()
     h = HashMap()
     if args.name is not None:
         u.name = args.name
     if args.country is not None:
         u.location = countries.CountryCode.Value(args.country.upper())
     if args.onename is not None:
         u.handle = args.onename
     if args.avatar is not None:
         with open(args.avatar, "r") as filename:
             image = filename.read()
         hash_value = digest(image)
         u.avatar_hash = hash_value
         h.insert(hash_value, args.avatar)
     if args.header is not None:
         with open(args.header, "r") as filename:
             image = filename.read()
         hash_value = digest(image)
         u.header_hash = hash_value
         h.insert(hash_value, args.header)
     u.encryption_key = KeyChain().encryption_pubkey
     p.update(u)
Esempio n. 13
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. 14
0
    def get_contract(self, request):
        def parse_contract(contract):
            if contract is not None:
                request.setHeader('content-type', "application/json")
                request.write(json.dumps(contract, indent=4))
                request.finish()
            else:
                request.write(NoResource().render(request))
                request.finish()

        if "id" in request.args:
            if "guid" in request.args:

                def get_node(node):
                    if node is not None:
                        self.mserver.get_contract(node, unhexlify(request.args["id"][0]))\
                            .addCallback(parse_contract)
                    else:
                        request.write(NoResource().render(request))
                        request.finish()

                try:
                    with open(DATA_FOLDER + "cache/" + request.args["id"][0],
                              "r") as filename:
                        contract = json.loads(filename.read(),
                                              object_pairs_hook=OrderedDict)
                    parse_contract(contract)
                except Exception:
                    self.kserver.resolve(unhexlify(
                        request.args["guid"][0])).addCallback(get_node)
            else:
                try:
                    with open(
                            HashMap().get_file(unhexlify(
                                request.args["id"][0])), "r") as filename:
                        contract = json.loads(filename.read(),
                                              object_pairs_hook=OrderedDict)
                    parse_contract(contract)
                except Exception:
                    parse_contract(None)
        else:
            request.write(NoResource().render(request))
            request.finish()
        return server.NOT_DONE_YET
Esempio n. 15
0
 def setprofile():
     parser = argparse.ArgumentParser(
         description="Sets a profile in the database.",
         usage='''usage:
 networkcli.py setprofile [options]''')
     parser.add_argument('-n', '--name', help="the name of the user/store")
     parser.add_argument('-o', '--onename', help="the onename id")
     parser.add_argument('-a',
                         '--avatar',
                         help="the file path to the avatar image")
     parser.add_argument('-hd',
                         '--header',
                         help="the file path to the header image")
     parser.add_argument(
         '-c',
         '--country',
         help=
         "a string consisting of country from protos.countries.CountryCode")
     # we could add all the fields here but this is good enough to test.
     args = parser.parse_args(sys.argv[2:])
     p = Profile()
     u = objects.Profile()
     h = HashMap()
     if args.name is not None:
         u.name = args.name
     if args.country is not None:
         u.location = countries.CountryCode.Value(args.country.upper())
     if args.onename is not None:
         u.handle = args.onename
     if args.avatar is not None:
         with open(args.avatar, "r") as filename:
             image = filename.read()
         hash_value = digest(image)
         u.avatar_hash = hash_value
         h.insert(hash_value, args.avatar)
     if args.header is not None:
         with open(args.header, "r") as filename:
             image = filename.read()
         hash_value = digest(image)
         u.header_hash = hash_value
         h.insert(hash_value, args.header)
     u.encryption_key = KeyChain().encryption_pubkey
     p.update(u)
Esempio n. 16
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()
Esempio n. 18
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()
    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()
Esempio n. 20
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)