def __init__(self, node, router, signing_key, database, audit=True): self.router = router self.node = node RPCProtocol.__init__(self, node, router) self.log = Logger(system=self) self.audit = Audit(db=database, enabled=audit) self.multiplexer = None self.db = database 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, BROADCAST, MESSAGE, ORDER, ORDER_CONFIRMATION, COMPLETE_ORDER, DISPUTE_OPEN, DISPUTE_CLOSE, GET_RATINGS, REFUND, ]
def __init__(self, node, router, signing_key, database): self.router = router self.node = node RPCProtocol.__init__(self, node, router) self.log = Logger(system=self) self.audit = Audit(db=database) self.multiplexer = None self.db = database 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, BROADCAST, MESSAGE, ORDER, ORDER_CONFIRMATION, COMPLETE_ORDER, DISPUTE_OPEN, DISPUTE_CLOSE, GET_RATINGS, REFUND ]
class MarketProtocol(RPCProtocol): implements(MessageProcessor) def __init__(self, node, router, signing_key, database): self.router = router self.node = node RPCProtocol.__init__(self, node, router) self.log = Logger(system=self) self.audit = Audit(db=database) self.multiplexer = None self.db = database 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, BROADCAST, MESSAGE, ORDER, ORDER_CONFIRMATION, COMPLETE_ORDER, DISPUTE_OPEN, DISPUTE_CLOSE, GET_RATINGS, REFUND] 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("serving contract %s to %s" % (contract_hash.encode('hex'), sender)) self.audit.record(sender.id.encode("hex"), "GET_CONTRACT", contract_hash.encode('hex')) self.router.addContact(sender) try: with open(self.db.filemap.get_file(contract_hash.encode("hex")), "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.router.addContact(sender) try: if len(image_hash) != 20: self.log.warning("Image hash is not 20 characters %s" % image_hash) raise Exception("Invalid image hash") self.log.info("serving image %s to %s" % (image_hash.encode('hex'), sender)) with open(self.db.filemap.get_file(image_hash.encode("hex")), "rb") as filename: image = filename.read() return [image] except Exception: self.log.warning("could not find image %s" % image_hash[:20].encode('hex')) return None def rpc_get_profile(self, sender): self.log.info("serving profile to %s" % sender) self.audit.record(sender.id.encode("hex"), "GET_PROFILE") self.router.addContact(sender) try: proto = Profile(self.db).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("serving user metadata to %s" % sender) self.router.addContact(sender) try: proto = Profile(self.db).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 load profile metadata") return None def rpc_get_listings(self, sender): self.log.info("serving store listings to %s" % sender) self.audit.record(sender.id.encode("hex"), "GET_LISTINGS") self.router.addContact(sender) try: p = Profile(self.db).get() l = Listings() l.ParseFromString(self.db.listings.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("serving metadata for contract %s to %s" % (contract_hash.encode("hex"), sender)) self.router.addContact(sender) try: proto = self.db.listings.get_proto() p = Profile(self.db).get() l = Listings() l.ParseFromString(proto) for listing in l.listing: if listing.contract_hash == contract_hash: listing.avatar_hash = p.avatar_hash listing.handle = p.handle ser = listing.SerializeToString() return [ser, self.signing_key.sign(ser)[:64]] except Exception: self.log.warning("could not find metadata for contract %s" % contract_hash.encode("hex")) return None def rpc_follow(self, sender, proto, signature): self.log.info("received follow request from %s" % sender) self.router.addContact(sender) try: verify_key = nacl.signing.VerifyKey(sender.pubkey) 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.node.id: raise Exception('Following wrong node') f.signature = signature self.db.follow.set_follower(f.SerializeToString()) proto = Profile(self.db).get(False) m = Metadata() m.name = proto.name m.handle = proto.handle m.avatar_hash = proto.avatar_hash m.short_description = proto.short_description m.nsfw = proto.nsfw for listener in self.listeners: try: verifyObject(NotificationListener, listener) listener.notify(sender.id, f.metadata.handle, "follow", "", "", f.metadata.avatar_hash) except DoesNotImplement: pass # Send SMTP notification notification = SMTPNotification(self.db) notification.send("[OpenBazaar] %s is now following you!" % f.metadata.name, "You have a new follower:<br><br>Name: %s<br>GUID: <a href=\"ob://%s\">%s</a><br>" "Handle: %s" % (f.metadata.name, f.guid.encode('hex'), f.guid.encode('hex'), f.metadata.handle)) 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("received unfollow request from %s" % sender) self.router.addContact(sender) try: verify_key = nacl.signing.VerifyKey(sender.pubkey) verify_key.verify("unfollow:" + self.node.id, signature) f = self.db.follow f.delete_follower(sender.id) return ["True"] except Exception: self.log.warning("failed to validate signature on unfollow request") return ["False"] def rpc_get_followers(self, sender, start=None): self.log.info("serving followers list to %s" % sender) self.audit.record(sender.id.encode("hex"), "GET_FOLLOWERS") self.router.addContact(sender) if start is not None: ser = self.db.follow.get_followers(int(start)) else: ser = self.db.follow.get_followers() return [ser[0], self.signing_key.sign(ser[0])[:64], ser[1]] def rpc_get_following(self, sender): self.log.info("serving following list to %s" % sender) self.audit.record(sender.id.encode("hex"), "GET_FOLLOWING") self.router.addContact(sender) ser = self.db.follow.get_following() if ser is None: return None else: return [ser, self.signing_key.sign(ser)[:64]] def rpc_broadcast(self, sender, message, signature): if len(message) <= 140 and self.db.follow.is_following(sender.id): try: verify_key = nacl.signing.VerifyKey(sender.pubkey) verify_key.verify(message, signature) except Exception: self.log.warning("received invalid broadcast from %s" % sender) return ["False"] self.log.info("received a broadcast from %s" % sender) self.router.addContact(sender) for listener in self.listeners: try: verifyObject(BroadcastListener, listener) listener.notify(sender.id, message) except DoesNotImplement: pass return ["True"] else: return ["False"] def rpc_message(self, sender, pubkey, encrypted): try: box = Box(self.signing_key.to_curve25519_private_key(), PublicKey(pubkey)) plaintext = box.decrypt(encrypted) p = PlaintextMessage() p.ParseFromString(plaintext) signature = p.signature p.ClearField("signature") verify_key = nacl.signing.VerifyKey(p.pubkey) verify_key.verify(p.SerializeToString(), signature) h = nacl.hash.sha512(p.pubkey) pow_hash = h[40:] if int(pow_hash[:6], 16) >= 50 or p.sender_guid.encode("hex") != 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.warning("received invalid message from %s" % sender) return ["False"] def rpc_order(self, sender, pubkey, encrypted): try: box = Box(self.signing_key.to_curve25519_private_key(), PublicKey(pubkey)) order = box.decrypt(encrypted) c = Contract(self.db, contract=json.loads(order, object_pairs_hook=OrderedDict), testnet=self.multiplexer.testnet) v = c.verify(sender.pubkey) if v is True: self.router.addContact(sender) self.log.info("received an order from %s, waiting for payment..." % sender) payment_address = c.contract["buyer_order"]["order"]["payment"]["address"] chaincode = c.contract["buyer_order"]["order"]["payment"]["chaincode"] masterkey_b = c.contract["buyer_order"]["order"]["id"]["pubkeys"]["bitcoin"] buyer_key = derive_childkey(masterkey_b, chaincode) amount = c.contract["buyer_order"]["order"]["payment"]["amount"] listing_hash = c.contract["vendor_offer"]["listing"]["contract_id"] signature = self.signing_key.sign( str(payment_address) + str(amount) + str(listing_hash) + str(buyer_key))[:64] c.await_funding(self.get_notification_listener(), self.multiplexer.blockchain, signature, False) return [signature] else: self.log.warning("received invalid order from %s reason %s" % (sender, v)) return ["False"] except Exception, e: self.log.error("Exception (%s) occurred processing order from %s" % (e.message, sender)) return ["False"]
class MarketProtocol(RPCProtocol): implements(MessageProcessor) def __init__(self, node, router, signing_key, database): self.router = router self.node = node RPCProtocol.__init__(self, node, router) self.log = Logger(system=self) self.audit = Audit(db=database) self.multiplexer = None self.db = database 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, BROADCAST, MESSAGE, ORDER, ORDER_CONFIRMATION, COMPLETE_ORDER, DISPUTE_OPEN, DISPUTE_CLOSE, GET_RATINGS, REFUND ] 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("serving contract %s to %s" % (contract_hash.encode('hex'), sender)) self.audit.record(sender.id.encode("hex"), "GET_CONTRACT", contract_hash.encode('hex')) self.router.addContact(sender) try: with open(self.db.filemap.get_file(contract_hash.encode("hex")), "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.router.addContact(sender) try: if len(image_hash) != 20: self.log.warning("Image hash is not 20 characters %s" % image_hash) raise Exception("Invalid image hash") self.log.info("serving image %s to %s" % (image_hash.encode('hex'), sender)) with open(self.db.filemap.get_file(image_hash.encode("hex")), "rb") as filename: image = filename.read() return [image] except Exception: self.log.warning("could not find image %s" % image_hash[:20].encode('hex')) return None def rpc_get_profile(self, sender): self.log.info("serving profile to %s" % sender) self.audit.record(sender.id.encode("hex"), "GET_PROFILE") self.router.addContact(sender) try: proto = Profile(self.db).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("serving user metadata to %s" % sender) self.router.addContact(sender) try: proto = Profile(self.db).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 load profile metadata") return None def rpc_get_listings(self, sender): self.log.info("serving store listings to %s" % sender) self.audit.record(sender.id.encode("hex"), "GET_LISTINGS") self.router.addContact(sender) try: p = Profile(self.db).get() l = Listings() l.ParseFromString(self.db.listings.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("serving metadata for contract %s to %s" % (contract_hash.encode("hex"), sender)) self.router.addContact(sender) try: proto = self.db.listings.get_proto() p = Profile(self.db).get() l = Listings() l.ParseFromString(proto) for listing in l.listing: if listing.contract_hash == contract_hash: listing.avatar_hash = p.avatar_hash listing.handle = p.handle ser = listing.SerializeToString() return [ser, self.signing_key.sign(ser)[:64]] except Exception: self.log.warning("could not find metadata for contract %s" % contract_hash.encode("hex")) return None def rpc_follow(self, sender, proto, signature): self.log.info("received follow request from %s" % sender) self.router.addContact(sender) try: verify_key = nacl.signing.VerifyKey(sender.pubkey) 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.node.id: raise Exception('Following wrong node') f.signature = signature self.db.follow.set_follower(f.SerializeToString()) proto = Profile(self.db).get(False) m = Metadata() m.name = proto.name m.handle = proto.handle m.avatar_hash = proto.avatar_hash m.short_description = proto.short_description m.nsfw = proto.nsfw for listener in self.listeners: try: verifyObject(NotificationListener, listener) listener.notify(sender.id, f.metadata.handle, "follow", "", "", f.metadata.avatar_hash) except DoesNotImplement: pass # Send SMTP notification notification = SMTPNotification(self.db) notification.send( "[OpenBazaar] %s is now following you!" % f.metadata.name, "You have a new follower:<br><br>Name: %s<br>GUID: <a href=\"ob://%s\">%s</a><br>" "Handle: %s" % (f.metadata.name, f.guid.encode('hex'), f.guid.encode('hex'), f.metadata.handle)) 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("received unfollow request from %s" % sender) self.router.addContact(sender) try: verify_key = nacl.signing.VerifyKey(sender.pubkey) verify_key.verify("unfollow:" + self.node.id, signature) f = self.db.follow f.delete_follower(sender.id) return ["True"] except Exception: self.log.warning( "failed to validate signature on unfollow request") return ["False"] def rpc_get_followers(self, sender, start=None): self.log.info("serving followers list to %s" % sender) self.audit.record(sender.id.encode("hex"), "GET_FOLLOWERS") self.router.addContact(sender) if start is not None: ser = self.db.follow.get_followers(int(start)) else: ser = self.db.follow.get_followers() return [ser[0], self.signing_key.sign(ser[0])[:64], ser[1]] def rpc_get_following(self, sender): self.log.info("serving following list to %s" % sender) self.audit.record(sender.id.encode("hex"), "GET_FOLLOWING") self.router.addContact(sender) ser = self.db.follow.get_following() if ser is None: return None else: return [ser, self.signing_key.sign(ser)[:64]] def rpc_broadcast(self, sender, message, signature): if len(message) <= 140 and self.db.follow.is_following(sender.id): try: verify_key = nacl.signing.VerifyKey(sender.pubkey) verify_key.verify(message, signature) except Exception: self.log.warning("received invalid broadcast from %s" % sender) return ["False"] self.log.info("received a broadcast from %s" % sender) self.router.addContact(sender) for listener in self.listeners: try: verifyObject(BroadcastListener, listener) listener.notify(sender.id, message) except DoesNotImplement: pass return ["True"] else: return ["False"] def rpc_message(self, sender, pubkey, encrypted): try: box = Box(self.signing_key.to_curve25519_private_key(), PublicKey(pubkey)) plaintext = box.decrypt(encrypted) p = PlaintextMessage() p.ParseFromString(plaintext) signature = p.signature p.ClearField("signature") verify_key = nacl.signing.VerifyKey(p.pubkey) verify_key.verify(p.SerializeToString(), signature) h = nacl.hash.sha512(p.pubkey) pow_hash = h[40:] if int(pow_hash[:6], 16) >= 50 or p.sender_guid.encode( "hex") != 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.warning("received invalid message from %s" % sender) return ["False"] def rpc_order(self, sender, pubkey, encrypted): try: box = Box(self.signing_key.to_curve25519_private_key(), PublicKey(pubkey)) order = box.decrypt(encrypted) c = Contract(self.db, contract=json.loads(order, object_pairs_hook=OrderedDict), testnet=self.multiplexer.testnet) v = c.verify(sender.pubkey) if v is True: self.router.addContact(sender) self.log.info( "received an order from %s, waiting for payment..." % sender) payment_address = c.contract["buyer_order"]["order"][ "payment"]["address"] chaincode = c.contract["buyer_order"]["order"]["payment"][ "chaincode"] masterkey_b = c.contract["buyer_order"]["order"]["id"][ "pubkeys"]["bitcoin"] buyer_key = derive_childkey(masterkey_b, chaincode) amount = c.contract["buyer_order"]["order"]["payment"][ "amount"] listing_hash = c.contract["vendor_offer"]["listing"][ "contract_id"] signature = self.signing_key.sign( str(payment_address) + str(amount) + str(listing_hash) + str(buyer_key))[:64] c.await_funding(self.get_notification_listener(), self.multiplexer.blockchain, signature, False) return [signature] else: self.log.warning("received invalid order from %s reason %s" % (sender, v)) return ["False"] except Exception, e: self.log.error("Exception (%s) occurred processing order from %s" % (e.message, sender)) return ["False"]