Ejemplo n.º 1
0
    def test_parse_output(self):
        """ continuation of test_parse_transaction to increase
        readability and lessen test noise """
        output_hex = (
            "f0ca052a01000000"  # Satoshis (49.99990000 BTC)
            "19"  # Bytes in pubkey script: 25
            "76"  # OP_DUP
            "a9"  # OP_HASH160
            "14"  # Push 20 bytes as data
            "cbc20a7664f2f69e5355aa427045bc15"
            "e7c6c772"  # PubKey hash
            "88"  # OP_EQUALVERIFY
            "ac"  # OP_CHECKSIG
        )
        output_bytes = streamify_if_bytes(bytes.fromhex(output_hex))
        tx_output = parse_output(output_bytes, 0)

        self.assertEqual(4999990000, tx_output.value)
        self.assertEqual(
            (
                "76"  # OP_DUP
                "a9"  # OP_HASH160
                "14"  # Push 20 bytes as data
                "cbc20a7664f2f69e5355aa427045bc15"
                "e7c6c772"  # PubKey hash
                "88"  # OP_EQUALVERIFY
                "ac"  # OP_CHECKSIG
            ),
            tx_output.script_pubkey,
        )
        self.assertEqual(tx_output.n, 0)
Ejemplo n.º 2
0
def parse_transaction(byte_stream):
    byte_stream = streamify_if_bytes(byte_stream)

    version_bytes = byte_stream.read(4)
    version = int.from_bytes(version_bytes, "little")

    num_inputs = decode_varint(byte_stream)
    # a 0x00 here is the Segwit marker
    is_segwit = num_inputs == 0
    if is_segwit:
        # next byte is flag
        _ = byte_stream.read(1)[0]  # should be nonzero (0x01 currently)
        num_inputs = decode_varint(byte_stream)

    inputs = [parse_input(byte_stream) for _ in range(num_inputs)]

    num_outputs = decode_varint(byte_stream)
    outputs = [parse_output(byte_stream, n) for n in range(num_outputs)]

    if is_segwit:
        witnesses = [parse_witness(byte_stream) for _ in range(num_inputs)]
        for input_, witness in zip(inputs, witnesses):
            input_.witness = witness

    locktime_bytes = byte_stream.read(4)
    locktime = int.from_bytes(locktime_bytes, "little")

    transaction = Transaction(version=version, locktime=locktime)

    return transaction, inputs, outputs
Ejemplo n.º 3
0
    def test_parse_input(self):
        """ continuation of test_parse_transaction to increase
        readability and lessen test noise """
        input_hex = (
            "7b1eabe0209b1fe794124575ef807057"
            "c77ada2138ae4fa8d6c4de0398a14f3f"  # Outpoint TXID
            "00000000"  # Outpoint index number
            "49"  # Bytes in sig. script: 73
            "48"  # Push 72 bytes as data
            "30450221008949f0cb400094ad2b5eb3"
            "99d59d01c14d73d8fe6e96df1a7150de"
            "b388ab8935022079656090d7f6bac4c9"
            "a94e0aad311a4268e082a725f8aeae05"
            "73fb12ff866a5f01"  # Secp256k1 signature
            "ffffffff"  # Sequence number: UINT32_MAX
        )
        input_bytes = streamify_if_bytes(bytes.fromhex(input_hex))
        tx_input = parse_input(input_bytes)

        self.assertEqual(
            bytes.fromhex("7b1eabe0209b1fe794124575ef807057"
                          "c77ada2138ae4fa8d6c4de0398a14f3f"  # Outpoint TXID
                          )[::-1].hex(),
            tx_input.txid,
            "outpoint txid is incorrect",
        )

        self.assertEqual(tx_input.vout, 0, "outpoint index is incorrect")

        self.assertEqual(
            (
                "48"  # Push 72 bytes as data
                "30450221008949f0cb400094ad2b5eb3"
                "99d59d01c14d73d8fe6e96df1a7150de"
                "b388ab8935022079656090d7f6bac4c9"
                "a94e0aad311a4268e082a725f8aeae05"
                "73fb12ff866a5f01"  # Secp256k1 signature
            ),
            tx_input.script_sig,
        )

        self.assertEqual(int.from_bytes(bytes.fromhex("ffffffff"), "little"),
                         tx_input.sequence)
Ejemplo n.º 4
0
 def test_parse_witness(self):
     """ continuation of 'test_parse_segwit_transaction' but
     separated out for readability and to lessen test noise """
     witness_hex = (
         "04"  # number of stack items
         "00"  # item size
         "47"  # item size
         "30440220132c0a8e96742ad8fe23bd77fd5646c8f227baaaa43f0bc84cc610f56"
         "2bffaa2022071100f27280c36e3421d50d9be5e87e12cc0f742c2e9a4957ad977"
         "3cc776d7d001"
         "47"  # item size
         "3044022054e768d8bbd05ed384a0b362a7bcacd7a4e1c51ce0c758da8d54ef730"
         "8af1ac402205d645e8c96d63819c2759f6de95f6372d8f5d9168a92ea512a304f"
         "aa28837a1f01"
         "69"  # item size
         "52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd929"
         "76b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff0187"
         "4496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c01"
         "1a32cf9f88053ae")
     witness_bytes = bytes.fromhex(witness_hex)
     witness_bytes = streamify_if_bytes(witness_bytes)
     witness_stack_items = parse_witness(witness_bytes)
     self.assertListEqual(
         witness_stack_items,
         [
             bytes.fromhex(""),
             bytes.fromhex(
                 "30440220132c0a8e96742ad8fe23bd77fd5646c8f227baaaa43f0bc8"
                 "4cc610f562bffaa2022071100f27280c36e3421d50d9be5e87e12cc0"
                 "f742c2e9a4957ad9773cc776d7d001"),
             bytes.fromhex(
                 "3044022054e768d8bbd05ed384a0b362a7bcacd7a4e1c51ce0c758da"
                 "8d54ef7308af1ac402205d645e8c96d63819c2759f6de95f6372d8f5"
                 "d9168a92ea512a304faa28837a1f01"),
             bytes.fromhex(
                 "52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e"
                 "0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac"
                 "749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fe"
                 "e45e84a8a48ad05bd8dbb395c011a32cf9f88053ae"),
         ],
     )
Ejemplo n.º 5
0
    def test_parse_transaction(self):
        tx_hex = (
            "01000000"  # Version
            "01"  # Number of inputs
            "7b1eabe0209b1fe794124575ef807057"
            "c77ada2138ae4fa8d6c4de0398a14f3f"  # Outpoint TXID
            "00000000"  # Outpoint index number
            "49"  # Bytes in sig. script: 73
            "48"  # Push 72 bytes as data
            "30450221008949f0cb400094ad2b5eb3"
            "99d59d01c14d73d8fe6e96df1a7150de"
            "b388ab8935022079656090d7f6bac4c9"
            "a94e0aad311a4268e082a725f8aeae05"
            "73fb12ff866a5f01"  # Secp256k1 signature
            "ffffffff"  # Sequence number: UINT32_MAX
            "01"  # Number of outputs
            "f0ca052a01000000"  # Satoshis (49.99990000 BTC)
            "19"  # Bytes in pubkey script: 25
            "76"  # OP_DUP
            "a9"  # OP_HASH160
            "14"  # Push 20 bytes as data
            "cbc20a7664f2f69e5355aa427045bc15"
            "e7c6c772"  # PubKey hash
            "88"  # OP_EQUALVERIFY
            "ac"  # OP_CHECKSIG
            "00000000"  # locktime: 0 (a block height)
        )
        tx_bytes = bytes.fromhex(tx_hex)
        tx_bytes = streamify_if_bytes(tx_bytes)
        transaction, inputs, outputs = parse_transaction(tx_bytes)

        self.assertEqual(transaction.version, 1)
        self.assertEqual(transaction.locktime, 0)

        self.assertEqual(len(inputs), 1)
        self.assertEqual(len(outputs), 1)

        for input_ in inputs:
            self.assertIsNone(input_.witness, "no witness for legacy tx")
Ejemplo n.º 6
0
    def test_parse_coinbase_transaction(self):
        coinbase_tx_hex = (
            "01000000"  # Version
            "01"  # Number of inputs
            "00000000000000000000000000000000"
            "00000000000000000000000000000000"  # Previous outpoint TXID
            "ffffffff"  # Previous outpoint index
            "29"  # Bytes in coinbase
            "03"  # Bytes in height
            "4e0105"  # Height: 328014
            "062f503253482f0472d35454085fffed"
            "f2400000f90f54696d65202620486561"
            "6c74682021"  # Arbitrary data
            "00000000"  # Sequence
            "01"  # Output count
            "2c37449500000000"  # Satoshis (25.04275756 BTC)
            "1976a914a09be8040cbf399926aeb1f4"
            "70c37d1341f3b46588ac"  # P2PKH script
            "00000000"  # Locktime
        )
        coinbase_tx_bytes = bytes.fromhex(coinbase_tx_hex)
        coinbase_tx_bytes = streamify_if_bytes(coinbase_tx_bytes)
        transaction, inputs, outputs = parse_transaction(coinbase_tx_bytes)

        # --------- coinbase-specific asserts ---------- #
        self.assertEqual(len(inputs), 1, "coinbase tx has one input")
        self.assertIsNone(inputs[0].witness, "no witness for coinbase tx")

        coinbase_input = inputs[0]
        self.assertEqual(coinbase_input.txid, "00" * 32,
                         "outpoint txid must be null")
        self.assertEqual(coinbase_input.vout, 2**32 - 1,
                         "outpoint index must be max")
        # ---------------------------------------------- #

        self.assertEqual(len(outputs), 1)

        self.assertEqual(transaction.version, 1)
        self.assertEqual(transaction.locktime, 0)
Ejemplo n.º 7
0
def parse_block(raw_block, height):
    """
    :param raw_block: string
        hex string for serialized block
    :param height: integer
        height on block chain (genesis block is 0)
    """
    block_bytes = bytes.fromhex(raw_block)
    byte_stream = streamify_if_bytes(block_bytes)

    version = int.from_bytes(byte_stream.read(4), "little")
    prev_hash = byte_stream.read(32)[::-1].hex()
    merkle_root = byte_stream.read(32)[::-1].hex()
    timestamp = int.from_bytes(byte_stream.read(4), "little")
    bits = byte_stream.read(4).hex()
    nonce = byte_stream.read(4).hex()

    num_transactions = decode_varint(byte_stream)

    block, created = Block.objects.get_or_create(
        height=height,
        version=version,
        prev_hash=prev_hash,
        merkle_root=merkle_root,
        timestamp=timestamp,
        bits=bits,
        nonce=nonce,
        num_transactions=num_transactions,
    )
    created_or_skipped = "created" if created else "skipped"

    if created:
        logger.debug(f"No block in DB for height {height}, creating...")

        tx_parts = [parse_transaction(byte_stream) for _ in range(num_transactions)]
        transactions = [t[0] for t in tx_parts]

        for transaction in transactions:
            transaction.block = block

        Transaction.objects.bulk_create(transactions)

        for transaction, inputs, outputs in tx_parts:
            for in_or_out in inputs + outputs:
                in_or_out.transaction = transaction

            TransactionInput.objects.bulk_create(inputs)
            TransactionOutput.objects.bulk_create(outputs)

        for transaction in transactions:
            # create txid (transaction hash) after the
            # related names, vin and vout, are attached
            transaction.txid = transaction._txid()

        Transaction.objects.bulk_update(transactions, ["txid"])

        logger.debug("Done creating transactions.")

    logger.info(f"block {created_or_skipped} at height {height}")

    return block