def delete_contract(self, request): if "id" in request.args: c = Contract(hash_value=unhexlify(request.args["id"][0])) for keyword in c.contract["vendor_offer"]["listing"]["item"]["keywords"]: self.kserver.delete(keyword.lower(), c.get_contract_id(), KeyChain().signing_key.sign(c.get_contract_id())[:64]) c.delete()
def __init__(self, mserver, kserver, protocol): self.mserver = mserver self.kserver = kserver self.protocol = protocol self.db = mserver.db self.keychain = KeyChain(self.db) APIResource.__init__(self)
def parse_results(values): if values is not None: for v in values: try: val = Value() val.ParseFromString(v) n = objects.Node() n.ParseFromString(val.serializedData) node_to_ask = Node( n.guid, n.nodeAddress.ip, n.nodeAddress.port, n.signedPublicKey, None if not n.HasField("relayAddress") else (n.relayAddress.ip, n.relayAddress.port), n.natType, n.vendor) if n.guid == KeyChain(self.factory.db).guid: proto = self.factory.db.ListingsStore().get_proto() l = Listings() l.ParseFromString(proto) for listing in l.listing: if listing.contract_hash == val.valueKey: respond(listing, node_to_ask) else: self.factory.mserver.get_contract_metadata(node_to_ask, val.valueKey)\ .addCallback(respond, node_to_ask) except Exception: pass
def create(self, name, location, handle=None): self.profile.name = name self.profile.location = location if handle is not None: self.profile.handle = handle self.profile.encryption_key = KeyChain().encryption_pubkey self.db.set_proto(self.profile.SerializeToString())
def release_funds(self, order_id): """ This function should be called to release funds from a disputed contract after the moderator has resolved the dispute and provided his signature. """ if os.path.exists(DATA_FOLDER + "purchases/in progress/" + order_id + ".json"): file_path = DATA_FOLDER + "purchases/trade receipts/" + order_id + ".json" outpoints = pickle.loads(self.db.Purchases().get_outpoint(order_id)) elif os.path.exists(DATA_FOLDER + "store/contracts/in progress/" + order_id + ".json"): file_path = DATA_FOLDER + "store/contracts/in progress/" + order_id + ".json" outpoints = pickle.loads(self.db.Sales().get_outpoint(order_id)) with open(file_path, 'r') as filename: contract = json.load(filename, object_pairs_hook=OrderedDict) vendor_address = contract["vendor_order_confirmation"]["invoice"]["payout"]["address"] buyer_address = contract["buyer_order"]["order"]["refund_address"] for moderator in contract["vendor_offer"]["listing"]["moderators"]: if moderator["guid"] == contract["buyer_order"]["order"]["moderator"]: masterkey_m = moderator["pubkeys"]["bitcoin"]["key"] outputs = [] outputs.append({'value': contract["dispute_resolution"]["resolution"]["moderator_fee"], 'address': contract["dispute_resolution"]["resolution"]["moderator_address"]}) if "buyer_payout" in contract["dispute_resolution"]["resolution"]: outputs.append({'value': contract["dispute_resolution"]["resolution"]["buyer_payout"], 'address': buyer_address}) if "vendor_payout" in contract["dispute_resolution"]["resolution"]: outputs.append({'value': contract["dispute_resolution"]["resolution"]["vendor_payout"], 'address': vendor_address}) tx = bitcoin.mktx(outpoints, outputs) signatures = [] chaincode = contract["buyer_order"]["order"]["payment"]["chaincode"] redeem_script = str(contract["buyer_order"]["order"]["payment"]["redeem_script"]) masterkey = bitcoin.bip32_extract_key(KeyChain(self.db).bitcoin_master_privkey) childkey = derive_childkey(masterkey, chaincode, bitcoin.MAINNET_PRIVATE) mod_key = derive_childkey(masterkey_m, chaincode) valid_inputs = 0 for index in range(0, len(outpoints)): sig = bitcoin.multisign(tx, index, redeem_script, childkey) signatures.append({"input_index": index, "signature": sig}) for s in contract["dispute_resolution"]["resolution"]["tx_signatures"]: if s["input_index"] == index: if bitcoin.verify_tx_input(tx, index, redeem_script, s["signature"], mod_key): tx = bitcoin.apply_multisignatures(tx, index, str(redeem_script), sig, str(s["signature"])) valid_inputs += 1 if valid_inputs == len(outpoints): self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx)) self.protocol.multiplexer.blockchain.broadcast(tx) else: raise Exception("Failed to reconstruct transaction with moderator signature.")
def set_settings(self, request): try: settings = self.db.Settings() settings.update( request.args["refund_address"][0], request.args["currency_code"][0], request.args["country"][0], request.args["language"][0], request.args["time_zone"][0], 1 if bool(request.args["notifications"][0]) else 0, request.args["ship_to_name"][0], request.args["ship_to_street"][0], request.args["ship_to_city"][0], request.args["ship_to_state"][0], request.args["ship_to_postal_code"][0], request.args["ship_to_country"][0], pickle.dumps(request.args["blocked"]), request.args["libbitcoin_server"][0], 1 if bool(request.args["ssl"][0]) else 0, KeyChain(self.db).guid_privkey.encode("hex"), request.args["server_url"][0], request.args["terms_conditions"][0], request.args["refund_policy"][0]) request.write(json.dumps({"success": True}, indent=4)) request.finish() return server.NOT_DONE_YET except Exception, e: request.write( json.dumps({ "success": False, "reason": e.message }, indent=4)) request.finish() return server.NOT_DONE_YET
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)
def parse_response(moderators): if moderators is not None: m.clear_all() def parse_profile(profile, node): if profile is not None: m.save_moderator( node.id.encode("hex"), node.signed_pubkey, profile.encryption_key.public_key, profile.encryption_key.signature, profile.bitcoin_key.public_key, profile.bitcoin_key.signature, profile.name, profile.avatar_hash, profile.moderation_fee, profile.handle, profile.short_description) moderator = { "id": message_id, "moderator": { "guid": node.id.encode("hex"), "name": profile.name, "handle": profile.handle, "short_description": profile.short_description, "avatar_hash": profile.avatar_hash.encode("hex"), "about": profile.about, "fee": profile.moderation_fee } } self.sendMessage(json.dumps(moderator, indent=4), False) else: m.delete_moderator(node.id) for mod in moderators: try: val = objects.Value() val.ParseFromString(mod) n = objects.Node() n.ParseFromString(val.serializedData) node_to_ask = Node( n.guid, n.nodeAddress.ip, n.nodeAddress.port, n.signedPublicKey, None if not n.HasField("relayAddress") else (n.relayAddress.ip, n.relayAddress.port), n.natType, n.vendor) if n.guid == KeyChain(self.factory.db).guid: parse_profile( Profile(self.factory.db).get(), node_to_ask) else: self.factory.mserver.get_profile(node_to_ask)\ .addCallback(parse_profile, node_to_ask) except Exception: pass
def __init__(self, database, contract=None, hash_value=None, testnet=False): """ 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 hash160 (in raw bytes) of a contract testnet: is this contract on the testnet """ self.db = database self.keychain = KeyChain(self.db) if contract is not None: self.contract = contract elif hash_value is not None: try: file_path = self.db.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: try: file_path = DATA_FOLDER + "purchases/in progress/" + hexlify( hash_value) + ".json" with open(file_path, 'r') as filename: self.contract = json.load( filename, object_pairs_hook=OrderedDict) except Exception: self.contract = {} else: self.contract = {} self.log = Logger(system=self) # used when purchasing this contract self.testnet = testnet self.ws = None self.blockchain = None self.amount_funded = 0 self.received_txs = [] self.timeout = None self.is_purchase = False self.outpoints = []
def make_moderator(self): """ Set self as a moderator in the DHT. """ u = objects.Profile() k = u.PublicKey() k.public_key = bitcoin.bip32_deserialize(KeyChain(self.db).bitcoin_master_pubkey)[5] k.signature = self.signing_key.sign(k.public_key)[:64] u.bitcoin_key.MergeFrom(k) u.moderator = True Profile(self.db).update(u) proto = self.kserver.node.getProto().SerializeToString() self.kserver.set(digest("moderators"), digest(proto), proto)
def addpgpkey(): parser = argparse.ArgumentParser( description="Add a pgp key to the profile.", usage='''usage: networkcli.py addpgpkey -k KEY, -s SIGNATURE''') parser.add_argument('-k', '--key', help="path to the key file") parser.add_argument('-s', '--signature', help="path to the signature file") args = parser.parse_args(sys.argv[2:]) with open(args.key, "r") as filename: key = filename.read() with open(args.signature, "r") as filename: sig = filename.read() p = Profile() print p.add_pgp_key(key, sig, KeyChain().guid.encode("hex"))
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)
def parse_profile(profile): if profile is not None: profile_json = { "profile": { "name": profile.name, "location": str(CountryCode.Name(profile.location)), "enryption_key": profile.encryption_key.encode("hex"), "nsfw": profile.nsfw, "vendor": profile.vendor, "moderator": profile.moderator, "handle": profile.handle, "about": profile.about, "website": profile.website, "email": profile.email, "primary_color": profile.primary_color, "secondary_color": profile.secondary_color, "background_color": profile.background_color, "text_color": profile.text_color, "pgp_key": profile.pgp_key.publicKey, "avatar_hash": profile.avatar_hash.encode("hex"), "header_hash": profile.header_hash.encode("hex"), "social_accounts": {} } } if "guid" in request.args: profile_json["profile"]["guid"] = request.args["guid"][0] else: profile_json["profile"]["guid"] = KeyChain().guid.encode( "hex") for account in profile.social: profile_json["profile"]["social_accounts"][str( objects.Profile.SocialAccount.SocialType.Name( account.type)).lower()] = { "username": account.username, "proof_url": account.proof_url } request.setHeader('content-type', "application/json") request.write(json.dumps(profile_json, indent=4)) request.finish() else: request.write(NoResource().render(request)) request.finish()
def run(*args): TESTNET = args[0] # database db = Database(TESTNET) # key generation keys = KeyChain(db) # logging # TODO: prune this log file and prevent it from getting too large? logFile = logfile.LogFile.fromFullPath(DATA_FOLDER + "debug.log") log.addObserver(FileLogObserver(logFile, level=args[1]).emit) log.addObserver(FileLogObserver(level=args[1]).emit) logger = Logger(system="OpenBazaard") # NAT traversal port = args[2] PortMapper().add_port_mapping(port, port, 'UDP') logger.info("Finding NAT Type...") try: response = stun.get_ip_info(source_port=port) except Exception: response = stun.get_ip_info() logger.info("%s on %s:%s" % (response[0], response[1], response[2])) ip_address = response[1] port = response[2] # TODO: use TURN if symmetric NAT def on_bootstrap_complete(resp): logger.info("bootstrap complete, downloading outstanding messages...") mlistener = MessageListenerImpl(ws_factory, db) mserver.get_messages(mlistener) mserver.protocol.add_listener(mlistener) nlistener = NotificationListenerImpl(ws_factory, db) mserver.protocol.add_listener(nlistener) # TODO: ping seed node to establish connection if not full cone NAT # TODO: after bootstrap run through any pending contracts and see if the bitcoin address # has been funded, if not listen on the address and start the 10 minute delete timer. protocol = OpenBazaarProtocol((ip_address, port), testnet=TESTNET) # kademlia node = Node(keys.guid, ip_address, port, signed_pubkey=keys.guid_signed_pubkey, vendor=Profile(db).get().vendor) storage = ForgetfulStorage() if TESTNET else PersistentStorage(db.DATABASE) try: kserver = Server.loadState(DATA_FOLDER + 'cache.pickle', ip_address, port, protocol, db, on_bootstrap_complete, storage=storage) except Exception: kserver = Server(node, db, KSIZE, ALPHA, storage=storage) kserver.protocol.connect_multiplexer(protocol) kserver.bootstrap( kserver.querySeed("seed.openbazaar.org:8080", "5b44be5c18ced1bc9400fe5e79c8ab90204f06bebacc04dd9c70a95eaca6e117"))\ .addCallback(on_bootstrap_complete) # TODO: load seeds from config file kserver.saveStateRegularly(DATA_FOLDER + 'cache.pickle', 10) protocol.register_processor(kserver.protocol) # market mserver = network.Server(kserver, keys.signing_key, db) mserver.protocol.connect_multiplexer(protocol) protocol.register_processor(mserver.protocol) reactor.listenUDP(port, protocol) # websockets api ws_factory = WSFactory("ws://127.0.0.1:18466", mserver, kserver) ws_factory.protocol = WSProtocol ws_factory.setProtocolOptions(allowHixie76=True) listenWS(ws_factory) webdir = File(".") web = Site(webdir) reactor.listenTCP(9000, web, interface=args[4]) # rest api api = OpenBazaarAPI(mserver, kserver, protocol) site = Site(api, timeout=None) reactor.listenTCP(18469, site, interface=args[3]) # TODO: add optional SSL on rest and websocket servers # blockchain # TODO: listen on the libbitcoin heartbeat port instead fetching height def height_fetched(ec, height): # TODO: re-broadcast any unconfirmed txs in the db using height to find confirmation status logger.info("Libbitcoin server online") try: timeout.cancel() except Exception: pass def timeout(client): logger.critical("Libbitcoin server offline") client = None if TESTNET: libbitcoin_client = LibbitcoinClient( "tcp://libbitcoin2.openbazaar.org:9091") else: libbitcoin_client = LibbitcoinClient( "tcp://libbitcoin1.openbazaar.org:9091") # TODO: load libbitcoin server url from config file libbitcoin_client.fetch_last_height(height_fetched) timeout = reactor.callLater(7, timeout, libbitcoin_client) protocol.set_servers(ws_factory, libbitcoin_client) reactor.run()
# stun print "Finding NAT Type.." response = stun.get_ip_info(stun_host='seed.openbazaar.org', stun_port=3478, source_port=0) print "%s on %s:%s" % (response[0], response[1], response[2]) ip_address = response[1] port = response[2] # database if not os.path.isfile(DATABASE): create_database() # key generation keys = KeyChain() def on_bootstrap_complete(resp): mlistener = MessageListenerImpl(ws_factory) mserver.get_messages(mlistener) mserver.protocol.add_listener(mlistener) nlistener = NotificationListenerImpl(ws_factory) mserver.protocol.add_listener(nlistener) protocol = OpenBazaarProtocol((ip_address, port)) # kademlia node = Node(keys.guid, ip_address, port, signed_pubkey=keys.guid_signed_pubkey)
from dht.node import Node from dht.network import Server from dht.crawling import NodeSpiderCrawl from dht.utils import digest, deferredDict from protos import objects from wireprotocol import OpenBazaarProtocol from market import network from keyutils.keys import KeyChain sys.path.append(os.path.dirname(__file__)) application = service.Application("OpenBazaar_seed_server") application.setComponent(ILogObserver, log.FileLogObserver(sys.stdout, log.INFO).emit) # Load the keys keychain = KeyChain() if os.path.isfile('keys.pickle'): keys = pickle.load(open("keys.pickle", "r")) signing_key_hex = keys["signing_privkey"] signing_key = nacl.signing.SigningKey(signing_key_hex, encoder=nacl.encoding.HexEncoder) else: signing_key = nacl.signing.SigningKey.generate() keys = { 'signing_privkey': signing_key.encode(encoder=nacl.encoding.HexEncoder), 'signing_pubkey': signing_key.verify_key.encode(encoder=nacl.encoding.HexEncoder) } pickle.dump(keys, open("keys.pickle", "wb"))
def process_dispute(contract, db, message_listener, notification_listener, testnet): """ This function processes a dispute message received from another node. It checks the contract to see if this a dispute for a purchase we made, a dispute for one of our sales, or a new case if we are the moderator. If it's a purchase or sale it will update the order status to disputed and push a notification to the listener. If it's a new case it will validate the contract, create a new case in the db, and push a notification to the listener. Args: contract: a json contract of the current order state. Should have a "dispute" object attached with dispute info. db: a `Database` object. message_listener: a `MessageListenerImpl` object. notification_listener: a `NotificationListenerImpl` object. testnet: `bool` of whether we're on testnet or not. """ tmp_contract = contract if "vendor_order_confirmation" in tmp_contract: del tmp_contract["vendor_order_confirmation"] if "buyer_receipt" in tmp_contract: del tmp_contract["buyer_receipt"] order_id = digest(json.dumps(tmp_contract, indent=4)).encode("hex") own_guid = KeyChain(db).guid.encode("hex") if contract["dispute"]["info"]["guid"] == contract["vendor_offer"][ "listing"]["id"]["guid"]: guid = unhexlify(contract["vendor_offer"]["listing"]["id"]["guid"]) signing_key = unhexlify( contract["vendor_offer"]["listing"]["id"]["pubkeys"]["guid"]) if "blockchain_id" in contract["vendor_offer"]["listing"]["id"]: handle = contract["vendor_offer"]["listing"]["id"]["blockchain_id"] else: handle = "" encryption_key = unhexlify( contract["vendor_offer"]["listing"]["id"]["pubkeys"]["encryption"]) proof_sig = contract["dispute"]["info"]["proof_sig"] elif contract["dispute"]["info"]["guid"] == contract["buyer_order"][ "order"]["id"]["guid"]: guid = unhexlify(contract["buyer_order"]["order"]["id"]["guid"]) signing_key = unhexlify( contract["buyer_order"]["order"]["id"]["pubkeys"]["guid"]) if "blockchain_id" in contract["buyer_order"]["order"]["id"]: handle = contract["buyer_order"]["order"]["id"]["blockchain_id"] else: handle = "" encryption_key = unhexlify( contract["buyer_order"]["order"]["id"]["pubkeys"]["encryption"]) proof_sig = None else: raise Exception("Dispute guid not in contract") verify_key = nacl.signing.VerifyKey(signing_key) verify_key.verify(json.dumps(contract["dispute"]["info"], indent=4), contract["dispute"]["signature"]) p = PlaintextMessage() p.sender_guid = guid p.handle = handle p.signed_pubkey = signing_key p.encryption_pubkey = encryption_key p.subject = order_id p.type = PlaintextMessage.Type.Value("DISPUTE_OPEN") p.message = contract["dispute"]["info"]["claim"] p.timestamp = time.time() p.avatar_hash = contract["dispute"]["info"]["avatar_hash"] if db.Purchases().get_purchase(order_id) is not None: db.Purchases().update_status(order_id, 4) elif db.Sales().get_sale(order_id) is not None: db.Purchases().update_status(order_id, 4) elif "moderators" in contract["vendor_offer"]["listing"]: # TODO: make sure a case isn't already open in the db is_selected = False for moderator in contract["vendor_offer"]["listing"]["moderators"]: if moderator["guid"] == own_guid and contract["buyer_order"][ "order"]["moderator"] == own_guid: is_selected = True if not is_selected: raise Exception("Not a moderator for this contract") else: if "blockchain_id" in contract["vendor_offer"]["listing"]["id"]: vendor = contract["vendor_offer"]["listing"]["id"][ "blockchain_id"] else: vendor = contract["vendor_offer"]["listing"]["id"]["guid"] if "blockchain_id" in contract["buyer_order"]["order"]["id"]: buyer = contract["buyer_order"]["order"]["id"]["blockchain_id"] else: buyer = contract["buyer_order"]["order"]["id"]["guid"] c = Contract(db, contract=contract, testnet=testnet) validation_failures = c.validate_for_moderation(proof_sig) db.Cases().new_case( order_id, contract["vendor_offer"]["listing"]["item"]["title"], time.time(), contract["buyer_order"]["order"]["date"], contract["buyer_order"]["order"], float(contract["buyer_order"]["order"]["payment"]["amount"]), contract["vendor_offer"]["listing"]["item"]["image_hashes"][0], buyer, vendor, json.dumps(validation_failures), contract["dispute"]["info"]["claim"]) with open(DATA_FOLDER + "cases/" + order_id + ".json", 'wb') as outfile: outfile.write(json.dumps(contract, indent=4)) else: raise Exception("Order ID for dispute not found") message_listener.notify(p, "") notification_listener.notify( guid, handle, "dispute_open", order_id, contract["vendor_offer"]["listing"]["item"]["title"], contract["vendor_offer"]["listing"]["item"]["image_hashes"][0])
def run(*args): TESTNET = args[0] LOGLEVEL = args[1] PORT = args[2] ALLOWIP = args[3] SSL = args[4] RESTPORT = args[5] WSPORT = args[6] # database db = Database(TESTNET) # key generation keys = KeyChain(db) # logging logFile = logfile.LogFile.fromFullPath(DATA_FOLDER + "debug.log", rotateLength=15000000, maxRotatedFiles=1) log.addObserver(FileLogObserver(logFile, level=LOGLEVEL).emit) log.addObserver(FileLogObserver(level=LOGLEVEL).emit) logger = Logger(system="OpenBazaard") # NAT traversal p = PortMapper() threading.Thread(target=p.add_port_mapping, args=(PORT, PORT, "UDP")).start() logger.info("Finding NAT Type...") while True: # sometimes the stun server returns a code the client # doesn't understand so we have to try again try: response = stun.get_ip_info(source_port=PORT) break except Exception: pass logger.info("%s on %s:%s" % (response[0], response[1], response[2])) nat_type = response[0] ip_address = response[1] port = response[2] # TODO: use TURN if symmetric NAT def on_bootstrap_complete(resp): logger.info("bootstrap complete") mserver.get_messages(mlistener) task.LoopingCall(check_unfunded_for_payment, db, libbitcoin_client, nlistener, TESTNET).start(600) protocol = OpenBazaarProtocol((ip_address, port), response[0], testnet=TESTNET) # kademlia node = Node(keys.guid, ip_address, port, signed_pubkey=keys.guid_signed_pubkey, vendor=Profile(db).get().vendor) storage = ForgetfulStorage() if TESTNET else PersistentStorage(db.DATABASE) try: kserver = Server.loadState(DATA_FOLDER + 'cache.pickle', ip_address, port, protocol, db, on_bootstrap_complete, storage=storage) except Exception: kserver = Server(node, db, KSIZE, ALPHA, storage=storage) kserver.protocol.connect_multiplexer(protocol) kserver.bootstrap(kserver.querySeed(SEED)).addCallback(on_bootstrap_complete) kserver.saveStateRegularly(DATA_FOLDER + 'cache.pickle', 10) protocol.register_processor(kserver.protocol) if nat_type != "Full Cone": kserver.protocol.ping(SEED_NODE_TESTNET if TESTNET else SEED_NODE) # market mserver = network.Server(kserver, keys.signing_key, db) mserver.protocol.connect_multiplexer(protocol) protocol.register_processor(mserver.protocol) reactor.listenUDP(port, protocol) class OnlyIP(Site): def __init__(self, resource, ip, timeout=60 * 60 * 1): self.ip = ip Site.__init__(self, resource, timeout=timeout) def buildProtocol(self, addr): if addr.host == self.ip: return Site.buildProtocol(self, addr) return None # websockets api if SSL: ws_factory = WSFactory("wss://127.0.0.1:" + str(WSPORT), mserver, kserver, only_ip=ALLOWIP) contextFactory = ChainedOpenSSLContextFactory(SSL_KEY, SSL_CERT) ws_factory.protocol = WSProtocol listenWS(ws_factory, contextFactory) else: ws_factory = WSFactory("ws://127.0.0.1:" + str(WSPORT), mserver, kserver, only_ip=ALLOWIP) ws_factory.protocol = WSProtocol listenWS(ws_factory) if ALLOWIP != "127.0.0.1" and ALLOWIP != "0.0.0.0": ws_interface = "0.0.0.0" else: ws_interface = ALLOWIP webdir = File(".") web = Site(webdir) reactor.listenTCP(WSPORT - 1, web, interface=ws_interface) # rest api api = OpenBazaarAPI(mserver, kserver, protocol) if ALLOWIP != "127.0.0.1" and ALLOWIP != "0.0.0.0": rest_interface = "0.0.0.0" site = OnlyIP(api, ALLOWIP, timeout=None) else: rest_interface = ALLOWIP site = Site(api, timeout=None) if SSL: reactor.listenSSL(RESTPORT, site, ChainedOpenSSLContextFactory(SSL_KEY, SSL_CERT), interface=rest_interface) else: reactor.listenTCP(RESTPORT, site, interface=rest_interface) # blockchain if TESTNET: libbitcoin_client = LibbitcoinClient(LIBBITCOIN_SERVER_TESTNET, log=Logger(service="LibbitcoinClient")) else: libbitcoin_client = LibbitcoinClient(LIBBITCOIN_SERVER, log=Logger(service="LibbitcoinClient")) # listeners nlistener = NotificationListenerImpl(ws_factory, db) mserver.protocol.add_listener(nlistener) mlistener = MessageListenerImpl(ws_factory, db) mserver.protocol.add_listener(mlistener) blistener = BroadcastListenerImpl(ws_factory, db) mserver.protocol.add_listener(blistener) protocol.set_servers(ws_factory, libbitcoin_client) logger.info("Startup took %s seconds" % str(round(time.time() - args[7], 2))) reactor.run()
def open_dispute(self, order_id, claim): """ Given and order ID we will pull the contract from disk and send it along with the claim to both the moderator and other party to the dispute. If either party isn't online we will stick it in the DHT for them. """ try: file_path = DATA_FOLDER + "purchases/in progress" + order_id + ".json" with open(file_path, 'r') as filename: contract = json.load(filename, object_pairs_hook=OrderedDict) guid = contract["vendor_offer"]["listing"]["id"]["guid"] enc_key = contract["vendor_offer"]["listing"]["id"]["pubkeys"]["encryption"] proof_sig = self.db.Purchases().get_proof_sig(order_id) except Exception: try: file_path = DATA_FOLDER + "sales/in progress/" + order_id + ".json" with open(file_path, 'r') as filename: contract = json.load(filename, object_pairs_hook=OrderedDict) guid = contract["buyer_order"]["order"]["id"]["guid"] enc_key = contract["buyer_order"]["order"]["id"]["pubkeys"]["encryption"] proof_sig = None except Exception: return False keychain = KeyChain(self.db) contract["dispute"] = {} contract["dispute"]["info"] = {} contract["dispute"]["info"]["claim"] = claim contract["dispute"]["info"]["guid"] = keychain.guid.encode("hex") contract["dispute"]["info"]["avatar_hash"] = Profile(self.db).get().avatar_hash.encode("hex") if proof_sig: contract["dispute"]["info"]["proof_sig"] = base64.b64encode(proof_sig) info = json.dumps(contract["dispute"]["info"], indent=4) contract["dispute"]["signature"] = base64.b64encode(keychain.signing_key.sign(info)[:64]) mod_guid = contract["buyer_order"]["order"]["moderator"] for mod in contract["vendor_offer"]["listing"]["moderators"]: if mod["guid"] == mod_guid: mod_enc_key = mod["pubkeys"]["encryption"]["key"] def get_node(node_to_ask, recipient_guid, public_key): def parse_response(response): if not response[0]: self.send_message(Node(unhexlify(recipient_guid)), public_key, objects.PlaintextMessage.Type.Value("DISPUTE_OPEN"), contract, order_id, store_only=True) if node_to_ask: skephem = PrivateKey.generate() pkephem = skephem.public_key.encode(nacl.encoding.RawEncoder) box = Box(skephem, PublicKey(public_key, nacl.encoding.HexEncoder)) nonce = nacl.utils.random(Box.NONCE_SIZE) ciphertext = box.encrypt(json.dumps(contract, indent=4), nonce) d = self.protocol.callDisputeOpen(node_to_ask, pkephem, ciphertext) return d.addCallback(parse_response) else: return parse_response([False]) self.kserver.resolve(unhexlify(guid)).addCallback(get_node, guid, enc_key) self.kserver.resolve(unhexlify(mod_guid)).addCallback(get_node, mod_guid, mod_enc_key)
def history_fetched(ec, history): outpoints = [] satoshis = 0 outputs = [] dispute_json = {"dispute_resolution": {"resolution": {}}} if ec: print ec else: for tx_type, txid, i, height, value in history: # pylint: disable=W0612 if tx_type == obelisk.PointIdent.Output: satoshis += value outpoint = txid.encode("hex") + ":" + str(i) if outpoint not in outpoints: outpoints.append(outpoint) satoshis -= TRANSACTION_FEE moderator_fee = round(float(moderator_percentage * satoshis)) satoshis -= moderator_fee outputs.append({'value': moderator_fee, 'address': moderator_address}) dispute_json["dispute_resolution"]["resolution"]["moderator_address"] = moderator_address dispute_json["dispute_resolution"]["resolution"]["moderator_fee"] = moderator_fee dispute_json["dispute_resolution"]["resolution"]["transaction_fee"] = TRANSACTION_FEE if float(buyer_percentage) > 0: amt = round(float(buyer_percentage * satoshis)) dispute_json["dispute_resolution"]["resolution"]["buyer_payout"] = amt outputs.append({'value': amt, 'address': buyer_address}) if float(vendor_percentage) > 0: amt = round(float(vendor_percentage * satoshis)) dispute_json["dispute_resolution"]["resolution"]["vendor_payout"] = amt outputs.append({'value': amt, 'address': vendor_address}) tx = bitcoin.mktx(outpoints, outputs) chaincode = contract["buyer_order"]["order"]["payment"]["chaincode"] redeem_script = str(contract["buyer_order"]["order"]["payment"]["redeem_script"]) masterkey_m = bitcoin.bip32_extract_key(KeyChain(self.db).bitcoin_master_privkey) moderator_priv = derive_childkey(masterkey_m, chaincode, bitcoin.MAINNET_PRIVATE) signatures = [] for index in range(0, len(outpoints)): sig = bitcoin.multisign(tx, index, redeem_script, moderator_priv) signatures.append({"input_index": index, "signature": sig}) dispute_json["dispute_resolution"]["resolution"]["order_id"] = order_id dispute_json["dispute_resolution"]["resolution"]["tx_signatures"] = signatures dispute_json["dispute_resolution"]["resolution"]["claim"] = self.db.Cases().get_claim(order_id) dispute_json["dispute_resolution"]["resolution"]["decision"] = resolution dispute_json["dispute_resolution"]["signature"] = \ base64.b64encode(KeyChain(self.db).signing_key.sign(json.dumps( dispute_json["dispute_resolution"]["resolution"]))[:64]) def get_node(node_to_ask, recipient_guid, public_key): def parse_response(response): if not response[0]: self.send_message(Node(unhexlify(recipient_guid)), public_key, objects.PlaintextMessage.Type.Value("DISPUTE_CLOSE"), dispute_json, order_id, store_only=True) if node_to_ask: skephem = PrivateKey.generate() pkephem = skephem.public_key.encode(nacl.encoding.RawEncoder) box = Box(skephem, PublicKey(public_key, nacl.encoding.HexEncoder)) nonce = nacl.utils.random(Box.NONCE_SIZE) ciphertext = box.encrypt(json.dumps(dispute_json, indent=4), nonce) d = self.protocol.callDisputeClose(node_to_ask, pkephem, ciphertext) return d.addCallback(parse_response) else: return parse_response([False]) self.kserver.resolve(unhexlify(vendor_guid)).addCallback(get_node, vendor_guid, vendor_enc_key) self.kserver.resolve(unhexlify(buyer_guid)).addCallback(get_node, buyer_guid, buyer_enc_key) self.db.Cases().update_status(order_id, 1)
def create(self, expiration_date, metadata_category, title, description, currency_code, price, process_time, nsfw, shipping_origin, shipping_regions, est_delivery_domestic=None, est_delivery_international=None, keywords=None, category=None, condition=None, sku=None, images=None, free_shipping=None, shipping_currency_code=None, shipping_domestic=None, shipping_international=None): """ All parameters are strings except: :param expiration_date: `string` (must be formatted UTC datetime) :param keywords: `list` :param nsfw: `boolean` :param images: a `list` of image files :param free_shipping: `boolean` :param shipping_origin: a 'string' formatted `CountryCode` :param shipping_regions: a 'list' of 'string' formatted `CountryCode`s """ # TODO: import keys into the contract, import moderator information from db, sign contract. profile = Profile().get() keychain = KeyChain() self.contract = OrderedDict({ "vendor_offer": { "listing": { "metadata": { "version": "0.1", "expiry": expiration_date + " UTC", "category": metadata_category, "category_sub": "fixed price" }, "id": { "guid": keychain.guid.encode("hex"), "pubkeys": { "guid": keychain.guid_signed_pubkey[64:].encode("hex"), "bitcoin": keychain.bitcoin_master_pubkey } }, "item": { "title": title, "description": description, "process_time": process_time, "price_per_unit": {}, "nsfw": nsfw } } } }) if metadata_category == "physical good" and condition is not None: self.contract["vendor_offer"]["listing"]["item"][ "condition"] = condition if currency_code.upper() == "BTC": item = self.contract["vendor_offer"]["listing"]["item"] item["price_per_unit"]["bitcoin"] = price else: item = self.contract["vendor_offer"]["listing"]["item"] item["price_per_unit"]["fiat"] = {} item["price_per_unit"]["fiat"]["price"] = price item["price_per_unit"]["fiat"]["currency_code"] = currency_code if keywords is not None: self.contract["vendor_offer"]["listing"]["item"]["keywords"] = [] self.contract["vendor_offer"]["listing"]["item"][ "keywords"].extend(keywords) if category is not None: self.contract["vendor_offer"]["listing"]["item"][ "category"] = category if sku is not None: self.contract["vendor_offer"]["listing"]["item"]["sku"] = sku if metadata_category == "physical good": self.contract["vendor_offer"]["listing"]["shipping"] = {} shipping = self.contract["vendor_offer"]["listing"]["shipping"] shipping["shipping_origin"] = shipping_origin if free_shipping is False: self.contract["vendor_offer"]["listing"]["shipping"][ "free"] = False self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"] = {} if shipping_currency_code == "BTC": self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"]["bitcoin"] = {} self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"]["bitcoin"]["domestic"] = shipping_domestic self.contract["vendor_offer"]["listing"]["shipping"][ "flat_fee"]["bitcoin"][ "international"] = shipping_international else: shipping = self.contract["vendor_offer"]["listing"][ "shipping"] shipping["flat_fee"]["fiat"] = {} shipping["flat_fee"]["fiat"]["price"] = {} shipping["flat_fee"]["fiat"]["price"][ "domestic"] = shipping_domestic shipping["flat_fee"]["fiat"]["price"][ "international"] = shipping_international shipping["flat_fee"]["fiat"][ "currency_code"] = shipping_currency_code else: self.contract["vendor_offer"]["listing"]["shipping"][ "free"] = True self.contract["vendor_offer"]["listing"]["shipping"][ "shipping_regions"] = [] for region in shipping_regions: shipping = self.contract["vendor_offer"]["listing"]["shipping"] shipping["shipping_regions"].append(region) listing = self.contract["vendor_offer"]["listing"] listing["shipping"]["est_delivery"] = {} listing["shipping"]["est_delivery"][ "domestic"] = est_delivery_domestic listing["shipping"]["est_delivery"][ "international"] = est_delivery_international if profile.HasField("handle"): self.contract["vendor_offer"]["listing"]["id"][ "blockchain_id"] = profile.handle if images is not None: self.contract["vendor_offer"]["listing"]["item"][ "image_hashes"] = [] for image in images: hash_value = digest(image).encode("hex") self.contract["vendor_offer"]["listing"]["item"][ "image_hashes"].append(hash_value) with open(DATA_FOLDER + "store/media/" + hash_value, 'w') as outfile: outfile.write(image) HashMap().insert(digest(image), DATA_FOLDER + "store/media/" + hash_value) listing = json.dumps(self.contract["vendor_offer"]["listing"], indent=4) self.contract["vendor_offer"]["signature"] = \ keychain.signing_key.sign(listing, encoder=nacl.encoding.HexEncoder)[:128] self.save()
def run(*args): TESTNET = args[0] # Create the database db = Database(testnet=TESTNET) # logging logFile = logfile.LogFile.fromFullPath(DATA_FOLDER + "debug.log", rotateLength=15000000, maxRotatedFiles=1) log.addObserver(FileLogObserver(logFile, level="debug").emit) log.addObserver(FileLogObserver(level="debug").emit) logger = Logger(system="Httpseed") # Load the keys keychain = KeyChain(db) if os.path.isfile(DATA_FOLDER + 'keys.pickle'): keys = pickle.load(open(DATA_FOLDER + "keys.pickle", "r")) signing_key_hex = keys["signing_privkey"] signing_key = nacl.signing.SigningKey(signing_key_hex, encoder=nacl.encoding.HexEncoder) else: signing_key = nacl.signing.SigningKey.generate() keys = { 'signing_privkey': signing_key.encode(encoder=nacl.encoding.HexEncoder), 'signing_pubkey': signing_key.verify_key.encode(encoder=nacl.encoding.HexEncoder) } pickle.dump(keys, open(DATA_FOLDER + "keys.pickle", "wb")) # Stun port = 18467 if not TESTNET else 28467 logger.info("Finding NAT Type...") response = stun.get_ip_info(stun_host="stun.l.google.com", source_port=port, stun_port=19302) logger.info("%s on %s:%s" % (response[0], response[1], response[2])) ip_address = response[1] port = response[2] # Start the kademlia server this_node = Node(keychain.guid, ip_address, port, keychain.guid_signed_pubkey, vendor=False) protocol = OpenBazaarProtocol((ip_address, port), response[0], testnet=TESTNET) try: kserver = Server.loadState('cache.pickle', ip_address, port, protocol, db) except Exception: kserver = Server(this_node, db) kserver.protocol.connect_multiplexer(protocol) protocol.register_processor(kserver.protocol) kserver.saveStateRegularly('cache.pickle', 10) # start the market server mserver = network.Server(kserver, keychain.signing_key, db) mserver.protocol.connect_multiplexer(protocol) protocol.register_processor(mserver.protocol) reactor.listenUDP(port, protocol) class WebResource(resource.Resource): def __init__(self, kserver_r): resource.Resource.__init__(self) self.kserver = kserver_r self.nodes = {} for bucket in self.kserver.protocol.router.buckets: for node in bucket.getNodes(): self.nodes[(node.ip, node.port)] = node self.nodes[(this_node.ip, this_node.port)] = this_node loopingCall = task.LoopingCall(self.crawl) loopingCall.start(180, True) def crawl(self): def gather_results(result): for proto in result: n = objects.Node() try: n.ParseFromString(proto) node = Node(n.guid, n.ip, n.port, n.signedPublicKey, n.vendor) self.nodes[(node.ip, node.port)] = node except Exception: pass def start_crawl(results): for node, result in results.items(): if not result[0]: del self.nodes[(node.ip, node.port)] node = Node(digest(random.getrandbits(255))) nearest = self.kserver.protocol.router.findNeighbors(node) spider = NodeSpiderCrawl(self.kserver.protocol, node, nearest, 100, 4) spider.find().addCallback(gather_results) ds = {} for bucket in self.kserver.protocol.router.buckets: for node in bucket.getNodes(): self.nodes[(node.ip, node.port)] = node for node in self.nodes.values(): if node.id != this_node.id: ds[node] = self.kserver.protocol.callPing(node) deferredDict(ds).addCallback(start_crawl) def getChild(self, child, request): return self def render_GET(self, request): nodes = self.nodes.values() shuffle(nodes) logger.info("Received a request for nodes, responding...") if "format" in request.args: if request.args["format"][0] == "json": json_list = [] if "type" in request.args and request.args["type"][ 0] == "vendors": for node in nodes: if node.vendor is True: node_dic = {} node_dic["ip"] = node.ip node_dic["port"] = node.port node_dic["guid"] = node.id.encode("hex") node_dic[ "signed_pubkey"] = node.signed_pubkey.encode( "hex") json_list.append(node_dic) sig = signing_key.sign(str(json_list)) resp = { "peers": json_list, "signature": hexlify(sig[:64]) } request.write(json.dumps(resp, indent=4)) else: for node in nodes[:50]: node_dic = {} node_dic["ip"] = node.ip node_dic["port"] = node.port json_list.append(node_dic) sig = signing_key.sign(str(json_list)) resp = { "peers": json_list, "signature": hexlify(sig[:64]) } request.write(json.dumps(resp, indent=4)) elif request.args["format"][0] == "protobuf": proto = peers.PeerSeeds() for node in nodes[:50]: peer = peers.PeerData() peer.ip_address = node.ip peer.port = node.port peer.vendor = node.vendor proto.peer_data.append(peer.SerializeToString()) sig = signing_key.sign("".join(proto.peer_data))[:64] proto.signature = sig uncompressed_data = proto.SerializeToString() request.write(uncompressed_data.encode("zlib")) else: proto = peers.PeerSeeds() if "type" in request.args and request.args["type"][ 0] == "vendors": for node in nodes: if node.vendor is True: peer = peers.PeerData() peer.ip_address = node.ip peer.port = node.port peer.vendor = node.vendor peer.guid = node.id peer.signedPubkey = node.signed_pubkey proto.peer_data.append(peer.SerializeToString()) sig = signing_key.sign("".join(proto.peer_data))[:64] proto.signature = sig uncompressed_data = proto.SerializeToString() request.write(uncompressed_data.encode("zlib")) else: for node in nodes[:50]: peer = peers.PeerData() peer.ip_address = node.ip peer.port = node.port peer.vendor = node.vendor proto.peer_data.append(peer.SerializeToString()) sig = signing_key.sign("".join(proto.peer_data))[:64] proto.signature = sig uncompressed_data = proto.SerializeToString() request.write(uncompressed_data.encode("zlib")) request.finish() return server.NOT_DONE_YET server_protocol = server.Site(WebResource(kserver)) reactor.listenTCP(8080, server_protocol) reactor.run()
def run(*args): TESTNET = args[0] # database db = Database(TESTNET) # key generation keys = KeyChain(db) # logging # TODO: prune this log file and prevent it from getting too large? logFile = logfile.LogFile.fromFullPath(DATA_FOLDER + "debug.log") log.addObserver(log.FileLogObserver(logFile).emit) log.startLogging(sys.stdout) # stun # TODO: accept port from command line port = 18467 if not TESTNET else 28467 print "Finding NAT Type.." # TODO: maintain a list of backup STUN servers and try them if ours fails response = stun.get_ip_info(stun_host="seed.openbazaar.org", source_port=port) print "%s on %s:%s" % (response[0], response[1], response[2]) ip_address = response[1] port = response[2] # TODO: try UPnP if restricted NAT # TODO: maintain open connection to seed node if STUN/UPnP fail # TODO: use TURN if symmetric NAT def on_bootstrap_complete(resp): mlistener = MessageListenerImpl(ws_factory, db) mserver.get_messages(mlistener) mserver.protocol.add_listener(mlistener) nlistener = NotificationListenerImpl(ws_factory, db) mserver.protocol.add_listener(nlistener) # TODO: after bootstrap run through any pending contracts and see if the bitcoin address # has been funded, if not listen on the address and start the 10 minute delete timer. protocol = OpenBazaarProtocol((ip_address, port), testnet=TESTNET) # kademlia node = Node(keys.guid, ip_address, port, signed_pubkey=keys.guid_signed_pubkey) try: kserver = Server.loadState(DATA_FOLDER + 'cache.pickle', ip_address, port, protocol, db, on_bootstrap_complete, storage=PersistentStorage(db.DATABASE)) except Exception: kserver = Server(node, db, KSIZE, ALPHA, storage=PersistentStorage(db.DATABASE)) kserver.protocol.connect_multiplexer(protocol) kserver.bootstrap( kserver.querySeed("seed.openbazaar.org:8080", "ddd862778e3ed71af06db0e3619a4c6269ec7468c745132dbb73982b319fc572"))\ .addCallback(on_bootstrap_complete) # TODO: load seeds from config file kserver.saveStateRegularly(DATA_FOLDER + 'cache.pickle', 10) protocol.register_processor(kserver.protocol) # market mserver = network.Server(kserver, keys.signing_key, db) mserver.protocol.connect_multiplexer(protocol) protocol.register_processor(mserver.protocol) reactor.listenUDP(port, protocol) # websockets api ws_factory = WSFactory("ws://127.0.0.1:18466", mserver, kserver) ws_factory.protocol = WSProtocol ws_factory.setProtocolOptions(allowHixie76=True) listenWS(ws_factory) webdir = File(".") web = Site(webdir) reactor.listenTCP(9000, web, interface="127.0.0.1") # rest api api = OpenBazaarAPI(mserver, kserver, protocol) site = Site(api, timeout=None) reactor.listenTCP(18469, site, interface="127.0.0.1") # TODO: add optional SSL on rest and websocket servers # blockchain # TODO: listen on the libbitcoin heartbeat port instead fetching height def height_fetched(ec, height): # TODO: re-broadcast any unconfirmed txs in the db using height to find confirmation status print "Libbitcoin server online" try: timeout.cancel() except Exception: pass def timeout(client): print "Libbitcoin server offline" client = None if TESTNET: libbitcoin_client = LibbitcoinClient( "tcp://libbitcoin2.openbazaar.org:9091") else: libbitcoin_client = LibbitcoinClient( "tcp://libbitcoin1.openbazaar.org:9091") # TODO: load libbitcoin server url from config file libbitcoin_client.fetch_last_height(height_fetched) timeout = reactor.callLater(5, timeout, libbitcoin_client) protocol.set_servers(ws_factory, libbitcoin_client) reactor.run()