Пример #1
0
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)
Пример #2
0
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
Пример #3
0
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
Пример #4
0
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
Пример #5
0
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
Пример #6
0
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
Пример #7
0
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
Пример #8
0
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
Пример #9
0
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