Ejemplo n.º 1
0
    def parse(cls, stream, testnet=False):
        """
        직렬화된 정보를 파싱하는 클래스 메서드
        :param stream: 직렬화 정보에 대한 스트림
            - 직렬화 정보는 네트워크 통신이나 파일 입출력을 통해 받기 때문에 시간이 걸림
            - 받아야 하는 트랜잭션 크기(직렬화 크기)가 커지면 받을 때까지 파싱 불가능
            - 직렬화 정보의 스트림을 인수로 받아 파싱하면 효율적
              i.e 받은 데이터까지 파싱 진행 가능
        :param testnet : 테스트넷 여부. 직렬화 정보에는 없는 정보
        :return: 역직렬화된 객체
        """

        # 버전 : 4바이트 리틀엔디언
        version_info = stream.read(4)
        version = little_endian_to_int(version_info)

        # 사용할 비트코인 : 이전 트랜잭션 정보
        num_inputs = read_varint(stream)
        inputs = []
        for _ in range(num_inputs):
            inputs.append(TxIn.parse(stream))

        # 비트코인을 보내는 종착지 : 트랜잭션 출력 정보
        num_outputs = read_varint(stream)
        outputs = []
        for _ in range(num_outputs):
            outputs.append(TxOut.parse(stream))

        # 록타임 : 4바이트의 리틀엔디언
        locktime = little_endian_to_int(stream.read(4))

        return cls(version, inputs, outputs, locktime, testnet=testnet)
    def parse(cls, stream):
        # 버전 : 4바이트 리틀엔디언
        version = little_endian_to_int(stream.read(4))

        # 이전 블록 해시값 : 32바이트 리틀엔디언
        prev_block = stream.read(32)[::-1]

        # 머클루트 : 32 바이트 리틀엔디언
        merkle_root = stream.read(32)[::-1]

        # 타임스탬프 : 4 바이트 리틀엔디언
        timestamp = little_endian_to_int(stream.read(4))

        # 비트값 : 4바이트
        bits = stream.read(4)

        # 논스값 : 4바이트
        nounce = stream.read(4)

        # 전체 트랜잭션 갯수
        total = little_endian_to_int(stream.read(4))

        # 해시값 : 가변 정수 및 32바이트 리틀엔디언
        num_hashes = read_varint(stream)
        hashes = []
        for _ in range(num_hashes):
            hashes.append(stream.read(32)[::-1])

        # 플래그 비트 : 가변 정수 및 1바이트
        num_flags = read_varint(stream)
        flag_bits = stream.read(num_flags)

        return cls(version, prev_block, merkle_root, timestamp, bits, nounce,
                   total, hashes, flag_bits)
    def parse(cls, stream):
        """
        블록 헤더의 필드는 고정 길이
        - 정확히 80바이트
        - 버전과 타임스탬프만 정수이고, 나머지는 바이트형
        - 매우 작은 헤더의 크기는 단순 지급 검증(SPV, 11장) 기능에서 매우 중요
        """
        # 버전 : 4바이트 리틀엔디언
        version = little_endian_to_int(stream.read(4))

        # 이전 블록 해시값 : 32바이트 리틀엔디언
        prev_block = stream.read(32)[::-1]

        # 머클루트 : 32 바이트 리틀엔디언
        merkle_root = stream.read(32)[::-1]

        # 타임스탬프 : 4 바이트 리틀엔디언
        timestamp = little_endian_to_int(stream.read(4))

        # 비트값 : 4바이트
        bits = stream.read(4)

        # 논스값 : 4바이트
        nounce = stream.read(4)

        return cls(version, prev_block, merkle_root, timestamp, bits, nounce)
Ejemplo n.º 4
0
 def parse(cls, stream):
     # 이전 트랜잭션 해시값 : 32바이트 리틀엔디언
     prev_tx = stream.read(32)[::-1]
     # 이전 트랜잭션 번호 : 4바이트 리틀엔디언
     prev_index = little_endian_to_int(stream.read(4))
     # 해제 스크립트
     script_sig = Script.parse(stream)
     # 시퀀스 필드 : 4바이트 리틀엔디언
     seqeunce = little_endian_to_int(stream.read(4))
     return cls(prev_tx, prev_index, script_sig, seqeunce)
    def parse(cls, stream):
        """
        스크립트 파싱
        - 첫 1바이트를 읽어 n이라 한다
        - 0x01 ~ 0x4b(1~75) 사이의 값인 경우
            : 이어서 n바이트 길이를 읽어 해당 숫자를 한 원소로 간주
        - 0x4c 인 경우
            : OP_PUSHDATA1 에 해당
              => 바로 이후의 1 바이트 값이 그 다음 읽을 원소의 길이 정보를 표현
                 i.e OP_PUSHDATA1 <1바이트 리틀엔디언으로 표현한 원소 길이> <원소> 형태
        - 0x4d 인 경우
            : OP_PUSHDATA2 에 해당
              => 바로 이후의 2 바이트 값이 그 다음 읽을 원소의 길이 정보를 표현
                 i.e OP_PUSHDATA1 <2바이트 리틀엔디언으로 표현한 원소 길이> <원소> 형태
        -  78 이상인 경우
            : 해당 바이트(n)은 오피코드(연산자)
        :param stream: 직렬화한 스크립트의 스트림
        :return: 역질력화된 스크립트 객체
        """
        # 스크립트는 가변 길이 필드로 시작
        length = read_varint(stream)

        commands = []
        count = 0
        while count < length:
            current = stream.read(1)
            count += 1
            current_bytes = current[0]

            # 다음 current_bytes가 한 데이터 원소
            if 1 <= current_bytes <= 75:
                n = current_bytes
                commands.append(stream.read(n))
                count += n
            # 다음 한 바이트가 파싱할 데이터 원소의 길이
            elif current_bytes == 76:
                data_length = little_endian_to_int(stream.read(1))
                commands.append(stream.read(data_length))
                count += (data_length + 1)
            # 다음 두 바이트가 파싱할 데이터 원소의 길이
            elif current_bytes == 77:
                data_length = little_endian_to_int(stream.read(2))
                commands.append(stream.read(data_length))
                count += (data_length + 2)
            # 해당 바이트 자체가 오피코드
            else:
                op_code = current_bytes
                commands.append(op_code)

        # 스크립트 파싱이 정확하게 되었는지 확인
        if count != length:
            raise SyntaxError("Parsing srcipt failed")

        return cls(commands)
Ejemplo n.º 6
0
    def parse_segwit(cls, stream, testnet=False):
        """
        세그윗 트랜잭션의 직렬화된 정보를 파싱하는 클래스 메서드
        - 세그윗 마커
        - 입력마다 있는 증인 필드
        :param stream: 직렬화 정보에 대한 스트림
        :param testnet: 테스트넷 여부. 직렬화 정보에는 없는 정보
        :return: 역직렬화된 객체
        """
        # 버전 : 4바이트 리틀엔디언
        version_info = stream.read(4)
        version = little_endian_to_int(version_info)

        # 세그윗 마커 정보 : 2바이트
        marker = stream.read(2)
        if marker != b"\x00\x01":
            raise RuntimeError("Not a segwit transaction : {}".format(marker))

        # 사용할 비트코인 : 이전 트랜잭션 정보
        num_inputs = read_varint(stream)
        inputs = []
        for _ in range(num_inputs):
            inputs.append(TxIn.parse(stream))

        # 비트코인을 보내는 종착지 : 트랜잭션 출력 정보
        num_outputs = read_varint(stream)
        outputs = []
        for _ in range(num_outputs):
            outputs.append(TxOut.parse(stream))

        # 입력마다 주어지는 증인(witness) 필드
        for tx_in in inputs:
            num_items = read_varint(stream)
            items = []
            for _ in range(num_items):
                item_len = read_varint(stream)
                if item_len == 0:
                    items.append(0)
                else:
                    items.append(stream.read(item_len))
            tx_in.witness = items

        # 록타임 : 4바이트의 리틀엔디언
        locktime = little_endian_to_int(stream.read(4))

        return cls(version,
                   inputs,
                   outputs,
                   locktime,
                   testnet=testnet,
                   segwit=True)
Ejemplo n.º 7
0
    def coinbase_height(self):
        """
        트랜잭션이 소속한 블록의 높이
        - 해제 스크립트의 첫 원소 규정
            - 채굴하고 있는 블록의 높이를 코인베이스 해제 스크립트의 첫 원소로 한다는 소프트포크 규정
            - 코인베이스 해제 스크립트를 통해 소속된 블록이 몇 번째 블록인지 확인 가능
            - 높이는 리틀엔디언 정수로 표현
        - 다른 블록에서 동일한 트랜잭션 ID를 갖는 문제 해결
            - 서로 다른 코인베이스 트랜잭션은 소속된 블록마다 높이가 다름
            - 해당 트랙잭션들의 직렬화 정보가 바이트 단위로 똑같을 수 없음
        - 소프트포크
        - 포크
            - 비트코인 네트워크를 구성하는 채굴 노드의 소프트웨어의 버전을 업데이트하는 것
                - 탈중앙화된 비트코인 네트워크는 동시 업데이트가 불가능
                - 각 채굴 노드는 점진적으로 버전업이 이루어짐
            - 소프트포크
                - 예전 버전 노드와 최신 버전 노드가 혼재되어 있어도 네트워크가 멈추지 않고 돌아가도록 갱신하는 것
       :return: 코인베이스 트랜잭션의 블록 높이
        """

        if not self.is_coinbase():
            raise ValueError("the transaction is not coinbase")

        tx_input = self.tx_inputs[0]
        script_sig = tx_input.script_sig
        return little_endian_to_int(script_sig.commands[0])
Ejemplo n.º 8
0
def my_address():
    from ecc.secp256k1 import PrivateKey
    my_message = b'YJ Choi secret'
    secret_key = little_endian_to_int(hash256(my_message))
    private_key = PrivateKey(secret_key)
    address = private_key.public_key.address(testnet=True)
    print(address)
Ejemplo n.º 9
0
    def parse(cls, stream):
        # 금액 : 8바이트 리틀엔디언
        amount = little_endian_to_int(stream.read(8))
        # 해제 스크립트
        script_pubkey = Script.parse(stream)

        return cls(amount, script_pubkey)
Ejemplo n.º 10
0
    def fetch(cls, tx_id, testnet=False, fresh=False):
        """
        찾고자 하는 트랜잭션 id롤 가지고 트랜잭션을 찾아 반환
            - 전체 트랜잭션을 반환
            - 해당 트랜잭션의 해시값(id)를 이용하여 검증을 하기 위해서
        :param tx_id: 찾고자 하는 트랜잭션 id
        :param testnet: 테스트넷 여부
        :param fresh: 캐시 이용 여부
        :return: 트랜잭션 id에 대응하는 트랜잭션 반환
        """
        if fresh or (tx_id not in cls.cache):
            url = "{}/tx/{}/hex".format(cls.get_url(testnet), tx_id)
            response = requests.get(url)
            try:
                raw = bytes.fromhex(response.text.strip())
            except ValueError:
                raise ValueError('unexpected response: {}'.format(response))

            if raw[4] == 0:
                raw = raw[:4] + raw[6:]
                tx = Tx.parse(BytesIO(raw), testnet=testnet)
                tx.locktime = little_endian_to_int(raw[-4:])
            else:
                tx = Tx.parse(BytesIO(raw), testnet=testnet)

            if tx.id() != tx_id:
                raise ValueError("Not the same id : {} vs {}".format(
                    tx.id(), tx_id))

            cls.cache[tx_id] = tx

        cls.cache[tx_id].testnet = testnet
        return cls.cache[tx_id]
Ejemplo n.º 11
0
    def parse(cls, stream, testnet=False):
        # 매직 넘버 : 4바이트
        magic = stream.read(4)

        # 올바른 스트림인지 확인
        if magic == b"":
            raise RuntimeError("Connection Reset")

        if testnet:
            expected_magic = TESTNET_NETWORK_MAGIC
        else:
            expected_magic = NETWORK_MAGIC
        if magic != expected_magic:
            raise RuntimeError("magic is not right {} (expected : {}".format(
                magic, expected_magic))

        # 커맨드 필드 : 12바이트(빈공간 0x00포함)
        command = stream.read(12)
        command = command.strip(b"\x00")

        # 페이로드 길이 : 4바이트 리틀엔디언
        payload_length = little_endian_to_int(stream.read(4))

        # 체크섬 필드 : 4바이트
        checksum = stream.read(4)

        # 페이로드 필드
        payload = stream.read(payload_length)

        # 체크섬을 통한 에러 확인
        payload_checksum = hash256(payload)[:4]
        if payload_checksum != checksum:
            raise RuntimeError("checksum error")

        return cls(command, payload, testnet)
Ejemplo n.º 12
0
def example2():
    print("Example : Height of Transaction in the block")
    stream = BytesIO(
        bytes.fromhex(
            '5e03d71b07254d696e656420627920416e74506f6f6c20626a31312f4542312f4144362f43205914293101fabe6d6d678e2c8c34afc36896e7d9402824ed38e856676ee94bfdb0c6c4bcd8b2e5666a0400000000000000c7270000a5e00e00'
        ))
    script_sig = Script.parse(stream)
    print("Height : {}".format(little_endian_to_int(script_sig.commands[0])))
Ejemplo n.º 13
0
def exercise6():
    def assertEqual(title, my_sol, ans):
        print("{} : {}".format(title, my_sol == ans))

    hex_merkle_block = '00000020df3b053dc46f162a9b00c7f0d5124e2676d47bbe7c5d0793a500000000000000ef445fef2ed495c275892206ca533e7411907971013ab83e3b47bd0d692d14d4dc7c835b67d8001ac157e670bf0d00000aba412a0d1480e370173072c9562becffe87aa661c1e4a6dbc305d38ec5dc088a7cf92e6458aca7b32edae818f9c2c98c37e06bf72ae0ce80649a38655ee1e27d34d9421d940b16732f24b94023e9d572a7f9ab8023434a4feb532d2adfc8c2c2158785d1bd04eb99df2e86c54bc13e139862897217400def5d72c280222c4cbaee7261831e1550dbb8fa82853e9fe506fc5fda3f7b919d8fe74b6282f92763cef8e625f977af7c8619c32a369b832bc2d051ecd9c73c51e76370ceabd4f25097c256597fa898d404ed53425de608ac6bfe426f6e2bb457f1c554866eb69dcb8d6bf6f880e9a59b3cd053e6c7060eeacaacf4dac6697dac20e4bd3f38a2ea2543d1ab7953e3430790a9f81e1c67f5b58c825acf46bd02848384eebe9af917274cdfbb1a28a5d58a23a17977def0de10d644258d9c54f886d47d293a411cb6226103b55635'
    mb = MerkleBlock.parse(BytesIO(bytes.fromhex(hex_merkle_block)))

    merkle_root_hex = 'ef445fef2ed495c275892206ca533e7411907971013ab83e3b47bd0d692d14d4'
    merkle_root = bytes.fromhex(merkle_root_hex)[::-1]
    assertEqual("merkle root", mb.merkle_root, merkle_root)

    prev_block_hex = 'df3b053dc46f162a9b00c7f0d5124e2676d47bbe7c5d0793a500000000000000'
    prev_block = bytes.fromhex(prev_block_hex)[::-1]
    assertEqual("previous block", mb.prev_block, prev_block)

    timestamp = little_endian_to_int(bytes.fromhex('dc7c835b'))
    assertEqual("time stamp", mb.timestamp, timestamp)

    bits = bytes.fromhex('67d8001a')
    assertEqual("bits", mb.bits, bits)

    nonce = bytes.fromhex('c157e670')
    assertEqual("nonce", mb.nonce, nonce)

    total = little_endian_to_int(bytes.fromhex('bf0d0000'))
    assertEqual("total transactions", mb.total, total)

    hex_hashes = [
        'ba412a0d1480e370173072c9562becffe87aa661c1e4a6dbc305d38ec5dc088a',
        '7cf92e6458aca7b32edae818f9c2c98c37e06bf72ae0ce80649a38655ee1e27d',
        '34d9421d940b16732f24b94023e9d572a7f9ab8023434a4feb532d2adfc8c2c2',
        '158785d1bd04eb99df2e86c54bc13e139862897217400def5d72c280222c4cba',
        'ee7261831e1550dbb8fa82853e9fe506fc5fda3f7b919d8fe74b6282f92763ce',
        'f8e625f977af7c8619c32a369b832bc2d051ecd9c73c51e76370ceabd4f25097',
        'c256597fa898d404ed53425de608ac6bfe426f6e2bb457f1c554866eb69dcb8d',
        '6bf6f880e9a59b3cd053e6c7060eeacaacf4dac6697dac20e4bd3f38a2ea2543',
        'd1ab7953e3430790a9f81e1c67f5b58c825acf46bd02848384eebe9af917274c',
        'dfbb1a28a5d58a23a17977def0de10d644258d9c54f886d47d293a411cb62261',
    ]
    hashes = [bytes.fromhex(h)[::-1] for h in hex_hashes]
    assertEqual("hashes", mb.hashes, hashes)

    flags = bytes.fromhex('b55635')
    assertEqual("bit flags", mb.flags, flags)
Ejemplo n.º 14
0
def example4():
    print("목표값 설정 : 블록헤더의 해시값이 목표값보다 작을 때 작업 증명 유효")

    # 목표값 계산
    bits = bytes.fromhex("e93c0118")
    exponent = bits[-1]
    coef = little_endian_to_int(bits[:-1])
    target = coef * 256**(exponent - 3)
    print("exponent: {}, coef: {}".format(exponent, coef))
    print("target: ")
    print("{:x}".format(target).zfill(64))

    # 작업 증명 : 블록 헤더의 해시값을 리틀엔디언으로 표현
    block_hash = hash256(
        bytes.fromhex(
            '020000208ec39428b17323fa0ddec8e887b4a7c53b8c0a0a220cfd0000000000000000005b0750fce0a889502d40508d39576821155e9c9e3f5c3157f961db38fd8b25be1e77a759e93c0118a4ffd71d'
        ))
    block_hash_int = little_endian_to_int(block_hash)
    print("block_hash:")
    print("{:x}".format(block_hash_int).zfill(64))
    print("proof of work : {}".format(block_hash_int < target))
Ejemplo n.º 15
0
def bits_to_target(bits):
    """
    4바이트 비트를 작업 증명에 사용할 목표값으로 변환하는 함수
    - 지수 : 마지막 바이트
    - 계수 : 앞 세 바이트를 리틀엔디언으로 표현
    => target = 계수 * pow(256, 지수-3)
    :param bits: 4바이트 비트
    :return: 목표값
    """
    exponent = bits[-1]
    coef = little_endian_to_int(bits[:-1])
    target = coef * 256**(exponent - 3)
    return target
Ejemplo n.º 16
0
 def check_pow(self):
     """
     작업 증명
     - 작업 증명으로 탈중앙화 방식의 비트코인 채굴이 가능
       => 전체 네트워크 수준에서 비트코인 보안이 유지
     - 특정 조건을 만족하는 작은 값을 찾는 것
         - 특정 조건 : 목표값(target)
         - 작은 값 : 블록 헤더의 해시값
         - 블록의 해시값을 리틀엔디언 정수로 표현하여 비교
     - 블록 헤더의 해시 변경 방법
         - 채굴자가 논스값을 변경
         - 코인베이스 트랜잭션 변경
             => 머클루트가 변경되어 새로운 해시
         - 버전 필드 변경
     :return: 자격 증명 결과
     """
     block_hash = hash256(self.serialize())
     block_hash_int = little_endian_to_int(block_hash)
     return block_hash_int < self.target()