class TestSignData(unittest.TestCase): def setUp(self): self.api = BtcTxStore(dryrun=True, testnet=True) def test_sign_a(self): wif = fixtures["wallet"]["wif"] data = binascii.hexlify(b"testmessage") address = self.api.get_address(wif) sig = self.api.sign_data(wif, data) valid = self.api.verify_signature(address, sig, data) self.assertEqual(valid, True) def test_sign_b(self): wif = "cSuT2J14dYbe1zvB5z5WTXeRcMbj4tnoKssAK1ZQbnX5HtHfW3bi" data = binascii.hexlify(b"testmessage") address = self.api.get_address(wif) sig = self.api.sign_data(wif, data) valid = self.api.verify_signature(address, sig, data) self.assertEqual(valid, True)
def sign(dict_obj, wif): # FIXME use create instead assert(isinstance(dict_obj, OrderedDict)) if "signature" in dict_obj: del dict_obj["signature"] if sys.version_info >= (3, 0, 0): msg = str(dict_obj).encode("ascii") else: msg = str(dict_obj) # assert("signature" not in msg) # must be unsigned # todo: fix this api = BtcTxStore(testnet=False, dryrun=True) msg = binascii.hexlify(msg).decode("utf-8") sig = api.sign_data(wif, msg) if sys.version_info >= (3, 0, 0): dict_obj[u"signature"] = sig.decode("utf-8") else: dict_obj[u"signature"] = unicode(sig) return dict_obj
class FileTransfer: def __init__(self, net, wif=None, store_config=None, handlers=None): # Accept direct connections. self.net = net # Returned by callbacks. self.success_value = ("127.0.0.1", 7777) # Used for signing messages. self.wallet = BtcTxStore(testnet=False, dryrun=True) self.wif = wif or self.wallet.create_key() # Where will the data be stored? self.store_config = store_config assert(len(list(store_config))) # Handlers for certain events. self.handlers = handlers if self.handlers is None: self.handlers = {} if "complete" not in self.handlers: self.handlers["complete"] = [] if "accept" not in self.handlers: self.handlers["accept"] = [] # Start networking. if not self.net.is_net_started: self.net.start() # Dict of data requests: [contract_id] > contract self.contracts = {} # List of Sock objects returned from UNL.connect. self.cons = [] # Dict of defers for contracts: [contract_id] > defer self.defers = {} # Three-way handshake status for contracts: [contract_id] > state self.handshake = {} # All contracts associated with this connection. # [con] > [contract_id] > con_info self.con_info = {} # File transfer currently active on connection. # [con] > contract_id self.con_transfer = {} # List of active downloads. # (Never try to download multiple copies of the same thing at once.) self.downloading = {} # Lock threads. self.mutex = Lock() def get_their_unl(self, contract): if self.net.unl == pyp2p.unl.UNL(value=contract["dest_unl"]): their_unl = contract["src_unl"] else: their_unl = contract["dest_unl"] return their_unl def get_node_id_from_unl(self, unl): unl = pyp2p.unl.UNL(value=unl).deconstruct() return unl["node_id"] def is_queued(self, con=None): if con is not None: if con not in self.con_info: return 0 if con is None: con_list = list(self.con_info) else: con_list = [con] for con in con_list: for contract_id in list(self.con_info[con]): con_info = self.con_info[con][contract_id] if con_info["remaining"]: return 1 return 0 def cleanup_transfers(self, con, contract_id): # Cleanup downloading. contract = self.contracts[contract_id] if contract["data_id"] in self.downloading: if contract["direction"] == "receive": del self.downloading[contract["data_id"]] # Cleanup handshakes. if contract_id in self.handshake: del self.handshake[contract_id] # Cleanup defers. if contract_id in self.defers: del self.defers[contract_id] # Cleanup con transfers. if con in self.con_transfer: del self.con_transfer[con] # Cleanup con_info. if con in self.con_info: del self.con_info[con] # Cleanup contracts. if contract_id in self.contracts: del self.contracts[contract_id] def queue_next_transfer(self, con): _log.debug("Queing next transfer") for contract_id in list(self.con_info[con]): con_info = self.con_info[con][contract_id] if con_info["remaining"]: self.con_transfer[con] = contract_id con.send(contract_id, send_all=1) return # Mark end of transfers. self.con_transfer[con] = u"0" * 64 def save_contract(self, contract): # Record contract details. contract_id = self.contract_id(contract) self.contracts[contract_id] = contract return contract_id def send_msg(self, dict_obj, unl): node_id = self.net.unl.deconstruct(unl)["node_id"] msg = json.dumps(dict_obj, ensure_ascii=True) self.net.dht_node.relay_message( node_id, msg ) def contract_id(self, contract): if sys.version_info >= (3, 0, 0): contract = str(contract).encode("ascii") else: contract = str(contract) return hashlib.sha256(contract).hexdigest() def sign_contract(self, contract): if sys.version_info >= (3, 0, 0): msg = str(contract).encode("ascii") else: msg = str(contract) msg = binascii.hexlify(msg).decode("utf-8") sig = self.wallet.sign_data(self.wif, msg) if sys.version_info >= (3, 0, 0): contract[u"signature"] = sig.decode("utf-8") else: contract[u"signature"] = unicode(sig) return contract def is_valid_contract_sig(self, contract, node_id=None): sig = contract[u"signature"][:] del contract[u"signature"] if sys.version_info >= (3, 0, 0): msg = str(contract).encode("ascii") else: msg = str(contract) # Use our address. msg = binascii.hexlify(msg).decode("utf-8") if node_id is None: address = self.wallet.get_address(self.wif) ret = self.wallet.verify_signature(address, sig, msg) else: # Use their node ID: try testnet. address = b2a_hashed_base58(b'o' + node_id) ret = self.wallet.verify_signature(address, sig, msg) if not ret: # Use their node ID: try mainnet. address = b2a_hashed_base58(b'\0' + node_id) ret = self.wallet.verify_signature(address, sig, msg) # Move sig back. contract[u"signature"] = sig[:] return ret def simple_data_request(self, data_id, node_unl, direction): file_size = 0 if direction == u"send": action = u"upload" else: action = u"download" return self.data_request(action, data_id, file_size, node_unl) def data_request(self, action, data_id, file_size, node_unl): """ Action = put (upload), get (download.) """ _log.debug("In data request function") # Who is hosting this data? if action == "upload": # We store this data. direction = u"send" host_unl = self.net.unl.value assert(storage.manager.find(self.store_config, data_id) is not None) else: # They store the data. direction = u"receive" host_unl = node_unl if data_id in self.downloading: raise Exception("Already trying to download this.") # Encoding. if sys.version_info >= (3, 0, 0): if type(data_id) == bytes: data_id = data_id.decode("utf-8") if type(host_unl) == bytes: host_unl = host_unl.decode("utf-8") if type(node_unl) == bytes: node_unl = node_unl.decode("utf-8") else: if type(data_id) == str: data_id = unicode(data_id) if type(host_unl) == str: host_unl = unicode(host_unl) if type(node_unl) == str: node_unl = unicode(node_unl) # Create contract. contract = OrderedDict({ u"status": u"SYN", u"direction": direction, u"data_id": data_id, u"file_size": file_size, u"host_unl": host_unl, u"dest_unl": node_unl, u"src_unl": self.net.unl.value, }) # Sign contract. contract = self.sign_contract(contract) # Route contract. contract_id = self.save_contract(contract) self.send_msg(contract, node_unl) _log.debug("Sending data request") # Update handshake. self.handshake[contract_id] = { u"state": u"SYN", u"timestamp": time.time() } # For async code. d = defer.Deferred() self.defers[contract_id] = d # Return defer for async code. return d def get_con_by_contract_id(self, needle): for con in list(self.con_info): for contract_id in list(self.con_info[con]): if contract_id == needle: return con return None def remove_file_from_storage(self, data_id): storage.manager.remove(self.store_config, data_id) def move_file_to_storage(self, path): with open(path, "rb") as shard: storage.manager.add(self.store_config, shard) return { "file_size": storage.shard.get_size(shard), "data_id": storage.shard.get_id(shard) } def get_data_chunk(self, data_id, position, chunk_size=1048576): path = storage.manager.find(self.store_config, data_id) buf = b"" with open(path, "rb") as fp: fp.seek(position, 0) buf = fp.read(chunk_size) return buf def save_data_chunk(self, data_id, chunk): _log.debug("Saving data chunk for " + str(data_id)) _log.debug("of size + " + str(len(chunk))) assert(data_id in self.downloading) # Find temp file path. path = self.downloading[data_id] _log.debug(path) with open(path, "ab") as fp: fp.write(chunk)
class FileTransfer: def __init__(self, net, wif=None, store_config=None, handlers=None): # Accept direct connections. self.net = net # Returned by callbacks. self.success_value = ("127.0.0.1", 7777) # Used for signing messages. self.wallet = BtcTxStore(testnet=True, dryrun=True) self.wif = wif or self.wallet.create_key() # Where will the data be stored? self.store_config = store_config assert (len(list(store_config))) # Handlers for certain events. self.handlers = handlers # Start networking. if not self.net.is_net_started: self.net.start() # Dict of data requests. self.contracts = {} # Dict of defers for contracts. self.defers = {} # Three-way handshake status for contracts. self.handshake = {} # All contracts associated with this connection. self.con_info = {} # File transfer currently active on connection. self.con_transfer = {} # List of active downloads. # (Never try to download multiple copies of the same thing at once.) self.downloading = {} def get_their_unl(self, contract): if self.net.unl == pyp2p.unl.UNL(value=contract["dest_unl"]): their_unl = contract["src_unl"] else: their_unl = contract["dest_unl"] return their_unl def is_queued(self, con=None): if con is not None: if con not in self.con_info: return 0 if con is None: con_list = list(self.con_info) else: con_list = [con] for con in con_list: for contract_id in list(self.con_info[con]): con_info = self.con_info[con][contract_id] if con_info["remaining"]: return 1 return 0 def cleanup_transfers(self, con): # Close con - there's nothing left to download. if not self.is_queued(con): # Cleanup con transfers. if con in self.con_transfer: del self.con_transfer[con] # Cleanup con_info. if con in self.con_info: del self.con_info[con] # Todo: cleanup contract + handshake state. def queue_next_transfer(self, con): _log.debug("Queing next transfer") for contract_id in list(self.con_info[con]): con_info = self.con_info[con][contract_id] if con_info["remaining"]: self.con_transfer[con] = contract_id con.send(contract_id, send_all=1) return # Mark end of transfers. self.con_transfer[con] = u"0" * 64 def is_valid_syn(self, msg): # List of expected fields. syn_schema = (u"status", u"direction", u"data_id", u"file_size", u"host_unl", u"dest_unl", u"src_unl", u"signature") # Check all fields exist. if not all(key in msg for key in syn_schema): _log.debug("Missing required key.") return 0 # Check SYN size. if len(msg) > 5242880: # 5 MB. _log.debug("SYN is too big") return 0 # Check direction is valid. direction_tuple = (u"send", u"receive") if msg[u"direction"] not in direction_tuple: _log.debug("Missing required direction tuple.") return 0 # Check the UNLs are valid. unl_tuple = (u"host_unl", u"dest_unl", u"src_unl") for unl_key in unl_tuple: if not pyp2p.unl.is_valid_unl(msg[unl_key]): _log.debug("Invalid UNL for " + unl_key) _log.debug(msg[unl_key]) return 0 # Check file size. file_size_type = type(msg[u"file_size"]) if sys.version_info >= (3, 0, 0): expr = file_size_type != int else: expr = file_size_type != int and file_size_type != long if expr: _log.debug("File size validation failed") _log.debug(type(msg[u"file_size"])) return 0 # Are we the host? if self.net.unl == pyp2p.unl.UNL(value=msg[u"host_unl"]): # Then check we have this file. path = storage.manager.find(self.store_config, msg[u"data_id"]) if path is None: _log.debug("Failed to find file we're uploading") return 0 else: # Do we already have this file? path = storage.manager.find(self.store_config, msg[u"data_id"]) if path is not None: _log.debug("Attempting to download file we already have") return 0 # Are we already trying to download this? if msg[u"data_id"] in self.downloading: _log.debug("We're already trying to download this") return 0 return 1 def protocol(self, msg): msg = json.loads(msg, object_pairs_hook=OrderedDict) # Associate TCP con with contract. def success_wrapper(self, contract_id, host_unl): def success(con): with mutex: _log.debug("IN SUCCESS CALLBACK") _log.debug("Success() contract_id = " + str(contract_id)) # Associate TCP con with contract. contract = self.contracts[contract_id] file_size = contract["file_size"] # Store con association. if con not in self.con_info: self.con_info[con] = {} # Associate contract with con. if contract_id not in self.con_info[con]: self.con_info[con][contract_id] = { "contract_id": contract_id, "remaining": 350, # Tree fiddy. "file_size": file_size, "file_size_buf": b"" } # Record download state. data_id = contract["data_id"] if self.net.unl != pyp2p.unl.UNL(value=host_unl): _log.debug("Success: download") fp, self.downloading[data_id] = tempfile.mkstemp() else: # Set initial upload for this con. _log.debug("Success: upload") # Queue first transfer. their_unl = self.get_their_unl(contract) is_master = self.net.unl.is_master(their_unl) _log.debug("Is master = " + str(is_master)) if con not in self.con_transfer: if is_master: # A transfer to queue processing. self.queue_next_transfer(con) else: # A transfer to receive (unknown.) self.con_transfer[con] = u"" else: if self.con_transfer[con] == u"0" * 64: if is_master: self.queue_next_transfer(con) else: self.con_transfer[con] = u"" return success # Sanity checking. if u"status" not in msg: return # Accept data request. if msg[u"status"] == u"SYN": # Check syn is valid. if not self.is_valid_syn(msg): _log.debug("SYN: invalid syn.") return # Save contract. contract_id = self.contract_id(msg) self.save_contract(msg) self.handshake[contract_id] = { "state": u"SYN-ACK", "timestamp": time.time() } # Create reply. reply = OrderedDict({ u"status": u"SYN-ACK", u"syn": msg, }) # Sign reply. reply = self.sign_contract(reply) # Save reply. self.send_msg(reply, msg[u"src_unl"]) _log.debug("SYN") # Confirm accept and make connection if needed. if msg[u"status"] == u"SYN-ACK": # Valid syn-ack? if u"syn" not in msg: _log.debug("SYN-ACK: syn not in msg.") return # Is this a reply to our SYN? contract_id = self.contract_id(msg[u"syn"]) if contract_id not in self.contracts: _log.debug("--------------") _log.debug(msg) _log.debug("--------------") _log.debug(self.contracts) _log.debug("--------------") _log.debug("SYN-ACK: contract not found.") return # Check syn is valid. if not self.is_valid_syn(msg[u"syn"]): _log.debug("SYN-ACK: invalid syn.") return # Did I sign this? if not self.is_valid_contract_sig(msg[u"syn"]): _log.debug("SYN-ACK: sig is invalid.") return # Update handshake. contract = self.contracts[contract_id] self.handshake[contract_id] = { "state": u"ACK", "timestamp": time.time() } # Create reply contract. reply = OrderedDict({u"status": u"ACK", u"syn_ack": msg}) # Sign reply. reply = self.sign_contract(reply) # Try make TCP con. self.net.unl.connect(contract["dest_unl"], { "success": success_wrapper(self, contract_id, contract["host_unl"]) }, force_master=0, nonce=contract_id) # Send reply. self.send_msg(reply, msg[u"syn"][u"dest_unl"]) _log.debug("SYN-ACK") if msg[u"status"] == u"ACK": # Valid ack. if u"syn_ack" not in msg: _log.debug("ACK: syn_ack not in msg.") return if u"syn" not in msg[u"syn_ack"]: _log.debug("ACK: syn not in msg.") return # Is this a reply to our SYN-ACK? contract_id = self.contract_id(msg[u"syn_ack"][u"syn"]) if contract_id not in self.contracts: _log.debug("ACK: contract not found.") return # Did I sign this? if not self.is_valid_contract_sig(msg[u"syn_ack"]): _log.debug("--------------") _log.debug(msg) _log.debug("--------------") _log.debug(self.contracts) _log.debug("--------------") _log.debug("ACK: sig is invalid.") return # Is the syn valid? if not self.is_valid_syn(msg[u"syn_ack"][u"syn"]): _log.debug("ACK: syn is invalid.") return # Update handshake. contract = self.contracts[contract_id] self.handshake[contract_id] = { "state": u"ACK", "timestamp": time.time() } # Try make TCP con. self.net.unl.connect(contract["src_unl"], { "success": success_wrapper(self, contract_id, contract["host_unl"]) }, force_master=0, nonce=contract_id) _log.debug("ACK") def save_contract(self, contract): # Record contract details. contract_id = self.contract_id(contract) self.contracts[contract_id] = contract return contract_id def send_msg(self, dict_obj, unl): node_id = self.net.unl.deconstruct(unl)["node_id"] msg = json.dumps(dict_obj, ensure_ascii=True) self.net.dht_node.direct_message(node_id, msg) def contract_id(self, contract): if sys.version_info >= (3, 0, 0): contract = str(contract).encode("ascii") else: contract = str(contract) return hashlib.sha256(contract).hexdigest() def sign_contract(self, contract): if sys.version_info >= (3, 0, 0): msg = str(contract).encode("ascii") else: msg = str(contract) msg = binascii.hexlify(msg).decode("utf-8") sig = self.wallet.sign_data(self.wif, msg) if sys.version_info >= (3, 0, 0): contract[u"signature"] = sig.decode("utf-8") else: contract[u"signature"] = unicode(sig) return contract def is_valid_contract_sig(self, contract): sig = contract[u"signature"][:] del contract[u"signature"] if sys.version_info >= (3, 0, 0): msg = str(contract).encode("ascii") else: msg = str(contract) msg = binascii.hexlify(msg).decode("utf-8") address = self.wallet.get_address(self.wif) ret = self.wallet.verify_signature(address, sig, msg) contract[u"signature"] = sig[:] return ret def simple_data_request(self, data_id, node_unl, direction): file_size = 0 if direction == u"send": action = u"upload" else: action = u"download" return self.data_request(action, data_id, file_size, node_unl) def data_request(self, action, data_id, file_size, node_unl): """ Action = put (upload), get (download.) """ _log.debug("In data request function") # Who is hosting this data? if action == "upload": # We store this data. direction = u"send" host_unl = self.net.unl.value assert (storage.manager.find(self.store_config, data_id) is not None) else: # They store the data. direction = u"receive" host_unl = node_unl if data_id in self.downloading: raise Exception("Already trying to download this.") # Encoding. if sys.version_info >= (3, 0, 0): if type(data_id) == bytes: data_id = data_id.decode("utf-8") if type(host_unl) == bytes: host_unl = host_unl.decode("utf-8") if type(node_unl) == bytes: node_unl = node_unl.decode("utf-8") else: if type(data_id) == str: data_id = unicode(data_id) if type(host_unl) == str: host_unl = unicode(host_unl) if type(node_unl) == str: node_unl = unicode(node_unl) # Create contract. contract = OrderedDict({ u"status": u"SYN", u"direction": direction, u"data_id": data_id, u"file_size": file_size, u"host_unl": host_unl, u"dest_unl": node_unl, u"src_unl": self.net.unl.value }) # Sign contract. contract = self.sign_contract(contract) # Route contract. contract_id = self.save_contract(contract) self.send_msg(contract, node_unl) _log.debug("Sending data request") # Update handshake. self.handshake[contract_id] = { "state": "SYN", "timestamp": time.time() } # For async code. d = defer.Deferred() self.defers[contract_id] = d # Return defer for async code. return d def remove_file_from_storage(self, data_id): storage.manager.remove(self.store_config, data_id) def move_file_to_storage(self, path): with open(path, "rb") as shard: storage.manager.add(self.store_config, shard) return { "file_size": storage.shard.get_size(shard), "data_id": storage.shard.get_id(shard) } def get_data_chunk(self, data_id, position, chunk_size=1048576): path = storage.manager.find(self.store_config, data_id) buf = b"" with open(path, "rb") as fp: fp.seek(position, 0) buf = fp.read(chunk_size) return buf def save_data_chunk(self, data_id, chunk): _log.debug("Saving data chunk for " + str(data_id)) _log.debug("of size + " + str(len(chunk))) assert (data_id in self.downloading) # Find temp file path. path = self.downloading[data_id] _log.debug(path) with open(path, "ab") as fp: fp.write(chunk)
class FileTransfer: def __init__(self, net, wif=None, store_config=None, handlers=None): # Accept direct connections. self.net = net # Returned by callbacks. self.success_value = ("127.0.0.1", 7777) # Used for signing messages. self.wallet = BtcTxStore(testnet=True, dryrun=True) self.wif = wif or self.wallet.create_key() # Where will the data be stored? self.store_config = store_config assert(len(list(store_config))) # Handlers for certain events. self.handlers = handlers # Start networking. if not self.net.is_net_started: self.net.start() # Dict of data requests. self.contracts = {} # Dict of defers for contracts. self.defers = {} # Three-way handshake status for contracts. self.handshake = {} # All contracts associated with this connection. self.con_info = {} # File transfer currently active on connection. self.con_transfer = {} # List of active downloads. # (Never try to download multiple copies of the same thing at once.) self.downloading = {} def get_their_unl(self, contract): if self.net.unl == pyp2p.unl.UNL(value=contract["dest_unl"]): their_unl = contract["src_unl"] else: their_unl = contract["dest_unl"] return their_unl def is_queued(self, con=None): if con is not None: if con not in self.con_info: return 0 if con is None: con_list = list(self.con_info) else: con_list = [con] for con in con_list: for contract_id in list(self.con_info[con]): con_info = self.con_info[con][contract_id] if con_info["remaining"]: return 1 return 0 def cleanup_transfers(self, con): # Close con - there's nothing left to download. if not self.is_queued(con): # Cleanup con transfers. if con in self.con_transfer: del self.con_transfer[con] # Cleanup con_info. if con in self.con_info: del self.con_info[con] # Todo: cleanup contract + handshake state. def queue_next_transfer(self, con): _log.debug("Queing next transfer") for contract_id in list(self.con_info[con]): con_info = self.con_info[con][contract_id] if con_info["remaining"]: self.con_transfer[con] = contract_id con.send(contract_id, send_all=1) return # Mark end of transfers. self.con_transfer[con] = u"0" * 64 def is_valid_syn(self, msg): # List of expected fields. syn_schema = ( u"status", u"direction", u"data_id", u"file_size", u"host_unl", u"dest_unl", u"src_unl", u"signature" ) # Check all fields exist. if not all(key in msg for key in syn_schema): _log.debug("Missing required key.") return 0 # Check SYN size. if len(msg) > 5242880: # 5 MB. _log.debug("SYN is too big") return 0 # Check direction is valid. direction_tuple = (u"send", u"receive") if msg[u"direction"] not in direction_tuple: _log.debug("Missing required direction tuple.") return 0 # Check the UNLs are valid. unl_tuple = (u"host_unl", u"dest_unl", u"src_unl") for unl_key in unl_tuple: if not pyp2p.unl.is_valid_unl(msg[unl_key]): _log.debug("Invalid UNL for " + unl_key) _log.debug(msg[unl_key]) return 0 # Check file size. file_size_type = type(msg[u"file_size"]) if sys.version_info >= (3, 0, 0): expr = file_size_type != int else: expr = file_size_type != int and file_size_type != long if expr: _log.debug("File size validation failed") _log.debug(type(msg[u"file_size"])) return 0 # Are we the host? if self.net.unl == pyp2p.unl.UNL(value=msg[u"host_unl"]): # Then check we have this file. path = storage.manager.find(self.store_config, msg[u"data_id"]) if path is None: _log.debug("Failed to find file we're uploading") return 0 else: # Do we already have this file? path = storage.manager.find(self.store_config, msg[u"data_id"]) if path is not None: _log.debug("Attempting to download file we already have") return 0 # Are we already trying to download this? if msg[u"data_id"] in self.downloading: _log.debug("We're already trying to download this") return 0 return 1 def protocol(self, msg): msg = json.loads(msg, object_pairs_hook=OrderedDict) # Associate TCP con with contract. def success_wrapper(self, contract_id, host_unl): def success(con): with mutex: _log.debug("IN SUCCESS CALLBACK") _log.debug("Success() contract_id = " + str(contract_id)) # Associate TCP con with contract. contract = self.contracts[contract_id] file_size = contract["file_size"] # Store con association. if con not in self.con_info: self.con_info[con] = {} # Associate contract with con. if contract_id not in self.con_info[con]: self.con_info[con][contract_id] = { "contract_id": contract_id, "remaining": 350, # Tree fiddy. "file_size": file_size, "file_size_buf": b"" } # Record download state. data_id = contract["data_id"] if self.net.unl != pyp2p.unl.UNL(value=host_unl): _log.debug("Success: download") fp, self.downloading[data_id] = tempfile.mkstemp() else: # Set initial upload for this con. _log.debug("Success: upload") # Queue first transfer. their_unl = self.get_their_unl(contract) is_master = self.net.unl.is_master(their_unl) _log.debug("Is master = " + str(is_master)) if con not in self.con_transfer: if is_master: # A transfer to queue processing. self.queue_next_transfer(con) else: # A transfer to receive (unknown.) self.con_transfer[con] = u"" else: if self.con_transfer[con] == u"0" * 64: if is_master: self.queue_next_transfer(con) else: self.con_transfer[con] = u"" return success # Sanity checking. if u"status" not in msg: return # Accept data request. if msg[u"status"] == u"SYN": # Check syn is valid. if not self.is_valid_syn(msg): _log.debug("SYN: invalid syn.") return # Save contract. contract_id = self.contract_id(msg) self.save_contract(msg) self.handshake[contract_id] = { "state": u"SYN-ACK", "timestamp": time.time() } # Create reply. reply = OrderedDict({ u"status": u"SYN-ACK", u"syn": msg, }) # Sign reply. reply = self.sign_contract(reply) # Save reply. self.send_msg(reply, msg[u"src_unl"]) _log.debug("SYN") # Confirm accept and make connection if needed. if msg[u"status"] == u"SYN-ACK": # Valid syn-ack? if u"syn" not in msg: _log.debug("SYN-ACK: syn not in msg.") return # Is this a reply to our SYN? contract_id = self.contract_id(msg[u"syn"]) if contract_id not in self.contracts: _log.debug("--------------") _log.debug(msg) _log.debug("--------------") _log.debug(self.contracts) _log.debug("--------------") _log.debug("SYN-ACK: contract not found.") return # Check syn is valid. if not self.is_valid_syn(msg[u"syn"]): _log.debug("SYN-ACK: invalid syn.") return # Did I sign this? if not self.is_valid_contract_sig(msg[u"syn"]): _log.debug("SYN-ACK: sig is invalid.") return # Update handshake. contract = self.contracts[contract_id] self.handshake[contract_id] = { "state": u"ACK", "timestamp": time.time() } # Create reply contract. reply = OrderedDict({ u"status": u"ACK", u"syn_ack": msg }) # Sign reply. reply = self.sign_contract(reply) # Try make TCP con. self.net.unl.connect( contract["dest_unl"], { "success": success_wrapper( self, contract_id, contract["host_unl"] ) }, force_master=0, nonce=contract_id ) # Send reply. self.send_msg(reply, msg[u"syn"][u"dest_unl"]) _log.debug("SYN-ACK") if msg[u"status"] == u"ACK": # Valid ack. if u"syn_ack" not in msg: _log.debug("ACK: syn_ack not in msg.") return if u"syn" not in msg[u"syn_ack"]: _log.debug("ACK: syn not in msg.") return # Is this a reply to our SYN-ACK? contract_id = self.contract_id(msg[u"syn_ack"][u"syn"]) if contract_id not in self.contracts: _log.debug("ACK: contract not found.") return # Did I sign this? if not self.is_valid_contract_sig(msg[u"syn_ack"]): _log.debug("--------------") _log.debug(msg) _log.debug("--------------") _log.debug(self.contracts) _log.debug("--------------") _log.debug("ACK: sig is invalid.") return # Is the syn valid? if not self.is_valid_syn(msg[u"syn_ack"][u"syn"]): _log.debug("ACK: syn is invalid.") return # Update handshake. contract = self.contracts[contract_id] self.handshake[contract_id] = { "state": u"ACK", "timestamp": time.time() } # Try make TCP con. self.net.unl.connect( contract["src_unl"], { "success": success_wrapper( self, contract_id, contract["host_unl"] ) }, force_master=0, nonce=contract_id ) _log.debug("ACK") def save_contract(self, contract): # Record contract details. contract_id = self.contract_id(contract) self.contracts[contract_id] = contract return contract_id def send_msg(self, dict_obj, unl): node_id = self.net.unl.deconstruct(unl)["node_id"] msg = json.dumps(dict_obj, ensure_ascii=True) self.net.dht_node.direct_message( node_id, msg ) def contract_id(self, contract): if sys.version_info >= (3, 0, 0): contract = str(contract).encode("ascii") else: contract = str(contract) return hashlib.sha256(contract).hexdigest() def sign_contract(self, contract): if sys.version_info >= (3, 0, 0): msg = str(contract).encode("ascii") else: msg = str(contract) msg = binascii.hexlify(msg).decode("utf-8") sig = self.wallet.sign_data(self.wif, msg) if sys.version_info >= (3, 0, 0): contract[u"signature"] = sig.decode("utf-8") else: contract[u"signature"] = unicode(sig) return contract def is_valid_contract_sig(self, contract): sig = contract[u"signature"][:] del contract[u"signature"] if sys.version_info >= (3, 0, 0): msg = str(contract).encode("ascii") else: msg = str(contract) msg = binascii.hexlify(msg).decode("utf-8") address = self.wallet.get_address(self.wif) ret = self.wallet.verify_signature(address, sig, msg) contract[u"signature"] = sig[:] return ret def simple_data_request(self, data_id, node_unl, direction): file_size = 0 if direction == u"send": action = u"upload" else: action = u"download" return self.data_request(action, data_id, file_size, node_unl) def data_request(self, action, data_id, file_size, node_unl): """ Action = put (upload), get (download.) """ _log.debug("In data request function") # Who is hosting this data? if action == "upload": # We store this data. direction = u"send" host_unl = self.net.unl.value assert(storage.manager.find(self.store_config, data_id) is not None) else: # They store the data. direction = u"receive" host_unl = node_unl if data_id in self.downloading: raise Exception("Already trying to download this.") # Encoding. if sys.version_info >= (3, 0, 0): if type(data_id) == bytes: data_id = data_id.decode("utf-8") if type(host_unl) == bytes: host_unl = host_unl.decode("utf-8") if type(node_unl) == bytes: node_unl = node_unl.decode("utf-8") else: if type(data_id) == str: data_id = unicode(data_id) if type(host_unl) == str: host_unl = unicode(host_unl) if type(node_unl) == str: node_unl = unicode(node_unl) # Create contract. contract = OrderedDict({ u"status": u"SYN", u"direction": direction, u"data_id": data_id, u"file_size": file_size, u"host_unl": host_unl, u"dest_unl": node_unl, u"src_unl": self.net.unl.value }) # Sign contract. contract = self.sign_contract(contract) # Route contract. contract_id = self.save_contract(contract) self.send_msg(contract, node_unl) _log.debug("Sending data request") # Update handshake. self.handshake[contract_id] = { "state": "SYN", "timestamp": time.time() } # For async code. d = defer.Deferred() self.defers[contract_id] = d # Return defer for async code. return d def remove_file_from_storage(self, data_id): storage.manager.remove(self.store_config, data_id) def move_file_to_storage(self, path): with open(path, "rb") as shard: storage.manager.add(self.store_config, shard) return { "file_size": storage.shard.get_size(shard), "data_id": storage.shard.get_id(shard) } def get_data_chunk(self, data_id, position, chunk_size=1048576): path = storage.manager.find(self.store_config, data_id) buf = b"" with open(path, "rb") as fp: fp.seek(position, 0) buf = fp.read(chunk_size) return buf def save_data_chunk(self, data_id, chunk): _log.debug("Saving data chunk for " + str(data_id)) _log.debug("of size + " + str(len(chunk))) assert(data_id in self.downloading) # Find temp file path. path = self.downloading[data_id] _log.debug(path) with open(path, "ab") as fp: fp.write(chunk)
#!/usr/bin/env python # coding: utf-8 # Copyright (c) 2015 Fabian Barkhau <*****@*****.**> # License: MIT (see LICENSE file) from __future__ import print_function from __future__ import unicode_literals import binascii from btctxstore import BtcTxStore api = BtcTxStore(testnet=True, dryrun=True) # use testing setup for example wif = api.create_key() # create new private key address = api.get_address(wif) # get private key address data = binascii.hexlify(b"messagetext") # hexlify messagetext # sign data with private key signature = api.sign_data(wif, data) print("signature:", signature) # verify signature (no public or private key needed) isvalid = api.verify_signature(address, signature, data) print("valid signature" if isvalid else "invalid signature")
# store data in blockchain as nulldata output (max 40bytes) data = binascii.hexlify(b"example_data") txid = api.store_nulldata(data, wifs) # Show current transaction id print("Current Transaction ID: {}".format(txid)) # Now, retrieve data based on transaction id hexnulldata = api.retrieve_nulldata(txid) print("Retrieved Data: {}".format(hexnulldata)) # create new private key wif = api.create_key() # get private key address address = api.get_address(wif) # hexlify messagetext data = binascii.hexlify(b"messagetext") # sign data with private key signature = api.sign_data(wif, data) print("signature:", signature) # verify signature (no public or private key needed) isvalid = api.verify_signature(address, signature, data) print("valid signature" if isvalid else "invalid signature")