def verify_txi(tx, txi, idx, testnet=False): prev_tx = fetch(txi["txid"], testnet) lock_script = prev_tx["txos"][txi["idx"]]["script"] # check if p2sh-p2wpkh or p2sh-p2wsh, p2sh_pattern = [OP_HASH160(0xa9) <hash:20bytes> OP_EQUAL(0x87)] if len(lock_script) == 3 and lock_script[0] == 0xa9 and \ type(lock_script[1]) == bytes and len(lock_script[1]) == 20 and lock_script[2] == 0x87: # the last cmd has to be the redeem_script raw_redeem_script = txi["script"][-1] # encode_varint로 변환해야 하는것 아닌가? 원소사이즈는 max 520bytes # 확인해보니 (지미송)코드에 문제 발견 # script.parse에서도 length = util.read_varint(s)로 읽고 있음. # 그래서 encode_varint로 변경 # 인코딩: https://github.com/jimmysong/programmingbitcoin/blob/2a6558263923214320bbdeb10826464fe24ef540/code-ch13/tx.py#L313 # 디코딩: https://github.com/jimmysong/programmingbitcoin/blob/2a6558263923214320bbdeb10826464fe24ef540/code-ch13/script.py#L78 # raw_redeem_script = util.int_to_little_endian(len(raw_redeem_script), 1) + raw_redeem_script raw_redeem_script = util.encode_varint( len(raw_redeem_script)) + raw_redeem_script _, redeem_script = script.parse( BytesIO(raw_redeem_script)) # (length, script) if script.is_p2wpkh(redeem_script): z = sig_hash_bip143(prev_tx, tx, txi, redeem_script=redeem_script, witness_script=None, testnet=testnet) witness = txi["wits"] elif script.is_p2wsh(redeem_script): raw_witness_script = txi["wits"][-1] # wits:[,,,witness_script] raw_witness_script = util.encode_varint( len(raw_witness_script)) + raw_witness_script _, witness_script = script.parse(BytesIO(raw_witness_script)) z = sig_hash_bip143(prev_tx, tx, txi, witness_script=witness_script) witness = txi["wits"] else: z = sig_hash(tx, idx, redeem_script) witness = None else: # p2wpkh or p2wsh or ... if script.is_p2wpkh(lock_script): z = sig_hash_bip143(prev_tx, tx, txi) witness = txi["wits"] elif script.is_p2wsh(lock_script): raw_witness_script = txi["wits"][-1] raw_witness_script = util.encode_varint( len(raw_witness_script)) + raw_witness_script _, witness_script = script.parse(BytesIO(raw_witness_script)) z = sig_hash_bip143(prev_tx, tx, txi, witness_script=witness_script) witness = txi["wits"] else: z = sig_hash(tx, idx, lock_script) witness = None commands = txi["script"] + lock_script return script.evaluate(commands, z, witness)
def serialize_legacy(tx): result = util.int_to_little_endian(tx["version"], 4) result += util.encode_varint(tx["txi.count"]) for txi in tx["txis"]: result += serialize_txi(txi) result += util.encode_varint(tx["txo.count"]) for txo in tx["txos"]: result += serialize_txo(txo) result += util.int_to_little_endian(tx["locktime"], 4) return result
def serialize_version(payload): """Serialize this message to send over the network""" # version is 4 bytes little endian result = util.int_to_little_endian(payload['version'], 4) # services is 8 bytes little endian result += util.int_to_little_endian(payload['services'], 8) # timestamp is 8 bytes little endian result += util.int_to_little_endian(payload['timestamp'], 8) # receiver services is 8 bytes little endian result += util.int_to_little_endian(payload['receiver_services'], 8) # IPV4 is 10 00 bytes and 2 ff bytes then receiver ip result += b'\x00' * 10 + b'\xff\xff' + payload['receiver_ip'] # receiver port is 2 bytes, big endian result += payload['receiver_port'].to_bytes(2, 'big') # sender services is 8 bytes little endian result += util.int_to_little_endian(payload['sender_services'], 8) # IPV4 is 10 00 bytes and 2 ff bytes then sender ip result += b'\x00' * 10 + b'\xff\xff' + payload['sender_ip'] # sender port is 2 bytes, big endian result += payload['sender_port'].to_bytes(2, 'big') # nonce should be 8 bytes result += payload['nonce'] # useragent is a variable string, so varint first result += util.encode_varint(len(payload['user_agent'])) result += payload['user_agent'] # latest block is 4 bytes little endian result += util.int_to_little_endian(payload['latest_block'], 4) # relay is 00 if false, 01 if true if payload['relay']: result += b'\x01' else: result += b'\x00' return result
def serialize_segwit(tx): result = util.int_to_little_endian(tx["version"], 4) result += tx["mark"] result += tx["flag"] result += util.encode_varint(tx["txi.count"]) for txi in tx["txis"]: result += serialize_txi(txi) result += util.encode_varint(tx["txo.count"]) for txo in tx["txos"]: result += serialize_txo(txo) for txi in tx["txis"]: result += util.int_to_little_endian(len(txi["wits"]), 1) for item in txi["wits"]: if type(item) == int: result += util.int_to_little_endian(item, 1) else: result += util.encode_varint(len(item)) + item result += util.int_to_little_endian(tx["locktime"], 4) return result
def serialize_getdata(payload): # start with the number of items as a varint result = util.encode_varint(len(payload['data'])) # loop through each tuple (data_type, identifier) in self.data for data_type, identifier in payload['data']: # data type is 4 bytes Little-Endian result += util.int_to_little_endian(data_type, 4) # identifier needs to be in Little-Endian result += identifier[::-1] return result
def serialize_getheaders(payload): """Serialize this message to send over the network""" # protocol version is 4 bytes little-endian result = util.int_to_little_endian(payload['version'], 4) # number of hashes is a varint result += util.encode_varint(payload['num_hashes']) # start block is in little-endian result += payload['start_block'][::-1] # end block is also in little-endian result += payload['end_block'][::-1] return result
def serialize_filterload(payload, flag=1): """Return the filterload message""" # start the payload with the size of the filter in bytes result = util.encode_varint(payload['size']) # next add the bit field using self.filter_bytes() result += bloomfilter.bit_field_to_bytes(payload['bit_field']) # function count is 4 bytes little endian result += util.int_to_little_endian(payload['function_count'], 4) # tweak is 4 bytes little endian result += util.int_to_little_endian(payload['tweak'], 4) # flag is 1 byte little endian result += util.int_to_little_endian(flag, 1) return result
def serialize(script): result = b'' # initialize what we'll send back for token in script: # go through each token if type(token) == int: # if the token is an integer, it's an opcode result += util.int_to_little_endian(token, 1) else: # otherwise, this is an element length = len(token) # get the length in bytes if length < 75: # turn the length into a single byte integer result += util.int_to_little_endian(length, 1) elif 75 < length < 0x100: # 76 is pushdata1 result += util.int_to_little_endian(76, 1) result += util.int_to_little_endian(length, 1) elif 0x100 <= length <= 520: # 77 is pushdata2 result += util.int_to_little_endian(77, 1) result += util.int_to_little_endian(length, 2) else: raise ValueError('too long an token') result += token total = len(result) return util.encode_varint(total) + result
def evaluate(commands, z, witness): cmds = commands[:] # create a copy as we may need to add to this list if we have a redeem_script stack = [] altstack = [] while len(cmds) > 0: cmd = cmds.pop(0) if type(cmd) == int: # do what the opcode says operation = op.OP_CODE_FUNCTIONS[cmd] if cmd in (99, 100): # op_if/op_notif require the cmds array if not operation(stack, cmds): LOGGER.info('bad op: {}'.format(op.OP_CODE_NAMES[cmd])) return False elif cmd in (107, 108): # op_toaltstack/op_fromaltstack require the altstack if not operation(stack, altstack): LOGGER.info('bad op: {}'.format(op.OP_CODE_NAMES[cmd])) return False elif cmd in (172, 173, 174, 175): # these are signing operations, they need a sig_hash # to check against if not operation(stack, z): LOGGER.info('bad op: {}'.format(op.OP_CODE_NAMES[cmd])) return False else: if not operation(stack): LOGGER.info('bad op: {}'.format(op.OP_CODE_NAMES[cmd])) return False else: # add the cmd to the stack stack.append(cmd) # p2sh rule. if the next three cmds are: # OP_HASH160 <20 byte hash> OP_EQUAL this is the RedeemScript # OP_HASH160 == 0xa9 and OP_EQUAL == 0x87 if len(cmds) == 3 and cmds[0] == 0xa9 \ and type(cmds[1]) == bytes and len(cmds[1]) == 20 \ and cmds[2] == 0x87: # we execute the next three opcodes cmds.pop() h160 = cmds.pop() cmds.pop() if not op.op_hash160(stack): return False stack.append(h160) if not op.op_equal(stack): return False # final result should be a 1 if not op.op_verify(stack): LOGGER.info('bad p2sh h160') return False raw_redeem_script = util.encode_varint(len(cmd)) + cmd _, redeem_script = parse(BytesIO(raw_redeem_script)) cmds.extend(redeem_script) # witness program version 0 rule. if stack cmds are [0 <20 byte hash>] this is p2wpkh if len(stack) == 2 and stack[0] == b'' and len(stack[1]) == 20: h160 = stack.pop() stack.pop() cmds.extend(witness) cmds.extend(p2pkh_script(h160)) # witness program version 0 rule. if stack cmds are:[0 <32 byte hash>] this is p2wsh if len(stack) == 2 and stack[0] == b'' and len(stack[1]) == 32: s256 = stack.pop() stack.pop() cmds.extend(witness[:-1]) raw_witness_script = witness[-1] if s256 != util.sha256(raw_witness_script): print('bad sha256 {} vs {}'.format( s256.hex(), util.sha256(raw_witness_script).hex())) return False stream = BytesIO( util.encode_varint(len(raw_witness_script)) + raw_witness_script) cmds.extend(parse(stream)) if len(stack) == 0: return False if stack.pop() == b'': return False return True