def unwrap_network_message(data): datacpy = data if len(data) < 24: return None, None, None, data magic, data = read_bytes(data, 4, int, 'little') assert magic == Constants.NETWORK_MAGIC cmd, data = read_bytes(data, 12, bytes, 'big') i = 0 while cmd[i] != 0 and i < 12: i += 1 try: command = cmd[:i].decode('ascii') except UnicodeDecodeError: raise NetworkError("Invalid command encoding") except Exception as e: print(e) length, data = read_bytes(data, 4, int, 'little') if (len(data) - 4) < length: return command, None, length, datacpy checksum, data = read_bytes(data, 4, bytes, 'big') payload, data = read_bytes(data, length, bytes, 'big') assert checksum == dsha256(payload)[:4] return command, payload, length, data
def parse_unlocking_script(script): # Returns type, signatures, public keys and address of the input if len(script) in [71, 72, 73]: # Pay-to-Public-Key: the unlocking script is the signature sig, script = read_data(script) assert script == bytes() return "p2pk", [sig], None, None, None elif len(script) in [105, 106, 107, 137, 138, 139]: #P2PKH # Pay-to-Public-Key-Hash: signature and public key sig, script = read_data(script) pubkey, script = read_data(script) assert script == bytes() return "p2pkh", [sig], [PublicKey.from_ser(pubkey) ], Address.from_pubkey(pubkey), None elif script[0] == OP_0: # P2SH multisig zero, script = read_bytes(script, 1, int, 'little') data = [] while script != bytes(): d, script = read_data(script) data.append(d) signatures, redeemScript = data[:-1], data[-1] # Address address = Address.from_script(redeemScript) rs = redeemScript # Parsing of redeem script m, rs = read_bytes(rs, 1, int, 'little') assert len(signatures) == (m - OP_1 + 1), "m = {:d}, len sigs = {:d}".format( m, len(signatures)) pubkeys = [] while 0 < rs[0] <= OP_PUSHDATA4: pubkey, rs = read_data(rs) pubkeys.append(PublicKey.from_ser(pubkey)) n, rs = read_bytes(rs, 1, int, 'little') assert len(pubkeys) == (n - OP_1 + 1) assert rs[0] == OP_CHECKMULTISIG return "p2sh", signatures, pubkeys, address, redeemScript else: raise ScriptError("cannot parse unlocking script")
def recv_inv(self, payload): count, payload = read_var_int(payload) for i in range(count): data, payload = read_bytes(payload, 36, bytes, 'big') inv = InventoryVector.from_serialized(data) # TODO get data if we need it # invs.append( inv ) # self.send_getdata( invs ) print(inv)
def read_nulldata_script(script): ''' Read nulldata script content. ''' assert len(script) <= 223 ret, script = read_bytes(script, 1, int, 'big') assert ret == OP_RETURN data = [] while script != bytes(): d, script = read_data(script) data.append(d) return data
def recv_reject(self, payload): length_msg, payload = read_var_int(payload) raw_msg, payload = read_bytes(payload, length_msg, bytes, 'big') msg = raw_msg.decode('ascii') ccode, payload = read_bytes(payload, 1, int, 'little') length_reason, payload = read_var_int(payload) raw_reason, payload = read_bytes(payload, length_reason, bytes, 'big') reason = raw_reason.decode('utf-8') data = payload.hex() print("{} reject {}: {}, {} {}".format( self.address['host'], msg, { Constants.REJECT_MALFORMED: "malformed", Constants.REJECT_INVALID: "invalid", Constants.REJECT_OBSOLETE: "obsolete", Constants.REJECT_DUPLICATE: "duplicate", Constants.REJECT_NONSTANDARD: "non standard", Constants.REJECT_DUST: "dust", Constants.REJECT_INSUFFICIENTFEE: "insufficient fee", Constants.REJECT_CHECKPOINT: "checkpoint" }[ccode], reason, data))
def recv_headers(self, payload): count, payload = read_var_int(payload) assert count <= 2000 headers = [] for i in range(count): hdr, payload = read_bytes(payload, Constants.BLOCKHEADER_SIZE, bytes, 'big') headers.append(hdr) tx_count, payload = read_var_int(payload) assert tx_count == 0 self.network.received_headers(self, headers)
def recv_getdata(self, payload): count, payload = read_var_int(payload) for i in range(count): data, payload = read_bytes(payload, 36, bytes, 'big') inv = InventoryVector.from_serialized(data) if inv in self.inventory: if inv.is_tx(): try: tx = self.txdb[inv.get_id()] except: raise NetworkError("cannot retrieve transaction") else: self.send_tx(bytes.fromhex(tx))
def deserialize_network_address(data, with_timestamp=True): if with_timestamp and len(data) != 30: print("Network address should be 30-byte long") raise NetworkError("Network address should be 30-byte long") elif not with_timestamp and len(data) != 26: print("Network address should be 26-byte long") raise NetworkError("Network address should be 26-byte long") netaddr = {} if with_timestamp: netaddr['time'], data = read_bytes(data, 4, int, 'little') netaddr['services'], data = read_bytes(data, 8, int, 'little') address, data = read_bytes(data, 16, bytes, 'big') if address[0:-4] == bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff]): # IPv4 netaddr['host'] = socket.inet_ntop(socket.AF_INET, address[-4:]) else: # IPv6 netaddr['host'] = socket.inet_ntop(socket.AF_INET6, address) netaddr['port'], data = read_bytes(data, 2, int, 'big') return netaddr
def recv_version(self, payload): if len(payload) < 20: self.state = 'dead' return try: self.version, payload = read_bytes(payload, 4, int, 'little') self.services, payload = read_bytes(payload, 8, int, 'little') self.time, payload = read_bytes(payload, 8, int, 'little') addr_recv, payload = read_bytes(payload, 26, bytes, 'big') my_network_address = deserialize_network_address( addr_recv, with_timestamp=False) addr_trans, payload = read_bytes(payload, 26, bytes, 'big') peer_network_address = deserialize_network_address( addr_trans, with_timestamp=False) nonce, payload = read_bytes(payload, 8, int, 'little') peer_user_agent_size, payload = read_var_int(payload) peer_user_agent, payload = read_bytes(payload, peer_user_agent_size, bytes, 'big') self.block_height, payload = read_bytes(payload, 4, int, 'little') relay, payload = read_bytes(payload, 1, int, 'little') assert payload == bytes() except: self.state = 'dead' return print(" Peer address: {}\n Services: {:d}\n User Agent: {}".format( peer_network_address['host'], peer_network_address['services'], peer_user_agent.decode('ascii'))) self.send_verack() self.peer_verack += 1 if not self.sent_version: self.send_version() if self.peer_verack == 2: self.handshake_time = time.time() print("{} handshake done".format(self.address['host']))
def recv_addr(self, payload): count, payload = read_var_int(payload) for i in range(count): data, payload = read_bytes(payload, 30, bytes, 'big') address = deserialize_network_address(data, with_timestamp=True) if not any([ address['host'] == na['host'] for na in self.network.peer_list ]) & (address['host'] != "") & (address['port'] == Constants.DEFAULT_PORT) & ( len(self.network.peer_list) < self.network.MAX_PEER_ADDRESSES): self.network.peer_list.append(address) print("New peer address: {}".format(address['host'])) assert payload == bytes()
def decode_xkey(xkey): payload = Base58.decode_check(xkey) assert len(payload) == 78 header, payload = read_bytes(payload, 4, int, 'big') depth, payload = read_bytes(payload, 1, int, 'big') fingerprint, payload = read_bytes(payload, 4, bytes, 'big') child_number, payload = read_bytes(payload, 4, bytes, 'big') chain_code, payload = read_bytes(payload, 32, bytes, 'big') if header == Constants.XPRV_HEADER: dummy, payload = read_bytes(payload, 1, int, 'big') assert dummy == 0 key, payload = read_bytes(payload, 32, bytes, 'big') elif header == Constants.XPUB_HEADER: key, payload = read_bytes(payload, 33, bytes, 'big') else: raise KeyDerivationError('Invalid extended key format') assert payload == bytes() return key, chain_code, depth, fingerprint, child_number
def from_serialized(self, raw): assert isinstance(raw, bytes) self.raw = raw version, raw = read_bytes(raw, 4, int, 'little') input_count, raw = read_var_int(raw) txins = [] for i in range(input_count): txin = {} txin['txid'], raw = read_bytes(raw, 32, hex, 'little') txin['index'], raw = read_bytes(raw, 4, int, 'little') scriptsize, raw = read_var_int(raw) unlockingScript, raw = read_bytes(raw, scriptsize, bytes, 'big') txin['unlocking_script'] = unlockingScript.hex() if (txin['txid'] == "00" * 32) & (txin['index'] == 0xffffffff): # Coinbase input txin['type'] = "coinbase" else: t, signatures, pubkeys, address, redeem_script = parse_unlocking_script( unlockingScript) txin['type'] = t if t == "p2pk": txin['signatures'] = [sig.hex() for sig in signatures] elif t == "p2pkh": txin['signatures'] = [sig.hex() for sig in signatures] txin['pubkeys'] = pubkeys txin['address'] = address elif t == "p2sh": # only p2sh-ms for the moment txin['signatures'] = [sig.hex() for sig in signatures] txin['pubkeys'] = pubkeys txin['address'] = address txin['redeem_script'] = redeem_script elif t == "p2ms": raise TransactionError("we do not parse p2ms outputs") else: raise TransactionError("cannot parse unlocking script") txin['nsigs'] = len(signatures) txin['sequence'], raw = read_bytes(raw, 4, int, 'little') txins.append(txin) output_count, raw = read_var_int(raw) txouts = [] for i in range(output_count): txout = {} txout['value'], raw = read_bytes(raw, 8, int, 'little') scriptsize, raw = read_var_int(raw) lockingScript, raw = read_bytes(raw, scriptsize, bytes, 'big') txout['locking_script'] = lockingScript.hex() t, address, data = parse_locking_script(lockingScript) txout['type'] = t if t in ("p2pkh", "p2sh"): txout['address'] = address elif t == "p2pk": txout['address'] = address elif t == "nulldata": txout['data'] = data elif t == "p2ms": raise TransactionError("we do not parse p2pk and p2ms outputs") else: raise TransactionError("cannot parse unlocking script") txouts.append(txout) locktime, raw = read_bytes(raw, 4, int, 'little') return self(version, txins, txouts, locktime)
def recv_feefilter(self, payload): self.peer_feerate, payload = read_bytes(payload, 8, int, 'little') print("feerate (sat/kB)", self.peer_feerate)