Esempio n. 1
0
def test_ibd_integration(caplog):
    # just testing the basics: if we set up 2 threads, one with a coinstate, and one without, will the chain propagate?

    caplog.set_level(logging.INFO)

    coinstate = _read_chain_from_disk(5)

    thread_a = NetworkingThread(coinstate, 12412, FakeDiskInterface())
    thread_a.start()

    _try_to_connect('127.0.0.1', 12412)

    thread_b = NetworkingThread(CoinState.zero(), 12413, FakeDiskInterface())
    thread_b.local_peer.network_manager.disconnected_peers = load_peers_from_list(
        [('127.0.0.1', 12412, "OUTGOING")])
    thread_b.start()

    try:
        start_time = time()
        while True:
            if thread_b.local_peer.chain_manager.coinstate.head().height == 5:
                break

            if time() > start_time + 5:
                print("\n".join(str(r) for r in caplog.records))
                raise Exception("IBD failed")

            sleep(0.01)

    finally:
        thread_a.stop()
        thread_a.join()

        thread_b.stop()
        thread_b.join()
Esempio n. 2
0
def read_chain_from_disk() -> CoinState:
    if os.path.isfile('chain.cache'):
        print("Reading cached chain")
        with open('chain.cache', 'rb') as file:
            coinstate = CoinState.load(lambda: pickle.load(file))
    else:
        coinstate = CoinState.zero()

    rewrite = False

    if os.path.isdir('chain'):
        # the code below is no longer needed by normal users, but some old testcases still rely on it:
        for filename in sorted(os.listdir('chain')):
            (height_str, hash_str) = filename.split("-")
            (height, hash) = (int(height_str), computer(hash_str))

            if hash not in coinstate.block_by_hash:

                if height % 1000 == 0:
                    print(filename)

                if os.path.getsize(f"chain/{filename}") == 0:
                    print("Stopping at empty block file: %s" % filename)
                    break

                with open(Path("chain") / filename, 'rb') as f:
                    try:
                        block = Block.stream_deserialize(f)
                    except Exception as e:
                        raise Exception("Corrupted block on disk: %s" % filename) from e
                    try:
                        coinstate = coinstate.add_block_no_validation(block)
                    except Exception:
                        print("Failed to add block at height=%d, previous_hash=%s"
                              % (block.height, human(block.header.summary.previous_block_hash)))
                        break

                rewrite = True

    if rewrite:
        DiskInterface().write_chain_cache_to_disk(coinstate)

    return coinstate
Esempio n. 3
0
def build_pkb2(coinstate: CoinState) -> immutables.Map[PublicKey, PKBalance2]:
    public_key_balances_2: immutables.Map[PublicKey,
                                          PKBalance2] = immutables.Map()

    for height in range(coinstate.head().height + 1):
        block = coinstate.at_head.block_by_height[height]
        public_key_balances_2 = build_pkb2_block(coinstate, block,
                                                 public_key_balances_2)

    return public_key_balances_2
def test_construct_pow_evidence_genesis_block():
    coinstate = CoinState.empty()

    transactions = [
        construct_coinbase_transaction(0, [], immutables.Map(), b"Political statement goes here", example_public_key),
    ]

    summary = construct_minable_summary_genesis(transactions, 1231006505, 0)

    evidence = construct_pow_evidence(coinstate, summary, 0, transactions)
    evidence.serialize()
Esempio n. 5
0
def _read_chain_from_disk(max_height):
    coinstate = CoinState.zero()

    for file_path in sorted(CHAIN_TESTDATA_PATH.iterdir()):
        height = int(file_path.name.split("-")[0])
        if height > max_height:
            return coinstate

        block = Block.stream_deserialize(open(file_path, 'rb'))
        coinstate = coinstate.add_block_no_validation(block)

    return coinstate
Esempio n. 6
0
def read_chain_from_disk():
    print("Reading chain from disk")
    coinstate = CoinState.zero()
    for filename in sorted(os.listdir('chain')):
        height = int(filename.split("-")[0])
        if height % 1000 == 0:
            print(filename)

        block = Block.stream_deserialize(open(Path('chain') / filename, 'rb'))
        coinstate = coinstate.add_block_no_validation(block)

    return coinstate
Esempio n. 7
0
def _read_chain_from_disk(max_height):
    # the requirement to run these tests from an environment that has access to the real blockchain is hard-coded (for
    # now)

    coinstate = CoinState.zero()

    for filename in sorted(os.listdir('chain')):
        height = int(filename.split("-")[0])
        if height > max_height:
            return coinstate

        block = Block.stream_deserialize(open(Path('chain') / filename, 'rb'))
        coinstate = coinstate.add_block_no_validation(block)

    return coinstate
Esempio n. 8
0
def read_chain_from_disk():
    print("Reading chain from disk")
    coinstate = CoinState.zero()
    for filename in sorted(os.listdir('chain')):
        height = int(filename.split("-")[0])
        if height % 1000 == 0:
            print(filename)

        try:
            block = Block.stream_deserialize(open(Path('chain') / filename, 'rb'))
        except Exception as e:
            raise Exception("Corrupted block on disk: %s" % filename) from e

        coinstate = coinstate.add_block_no_validation(block)

    return coinstate
Esempio n. 9
0
def test_validate_block_by_itself_invalid_coinbase_transaction():
    coinstate = CoinState.empty()

    transactions = [
        Transaction(inputs=[
            Input(
                OutputReference(b'x' * 32, 1),
                SECP256k1Signature(b'y' * 64),
            )
        ],
                    outputs=[Output(1, example_public_key)])
    ]

    summary = construct_minable_summary(coinstate, transactions, 1615209942,
                                        39)
    evidence = construct_pow_evidence(coinstate, summary, 0, transactions)
    block = Block(BlockHeader(summary, evidence), transactions)

    with pytest.raises(ValidateTransactionError, match=".*thin air.*"):
        validate_block_by_itself(block, 1615209942)
Esempio n. 10
0
 def write_chain_cache_to_disk(self, coinstate: CoinState) -> None:
     # Currently this takes about 2 seconds. It could be optimized further
     # if we switch to an appendable file format for the cache.
     with open('chain.cache.tmp', 'wb') as file:
         coinstate.dump(lambda data: pickle.dump(data, file))
     os.replace('chain.cache.tmp', 'chain.cache')
Esempio n. 11
0
def read_chain_from_disk() -> CoinState:
    if os.path.isfile('chain.cache'):
        print("Reading cached chain")
        with open('chain.cache', 'rb') as file:
            coinstate = CoinState.load(lambda: pickle.load(file))
    else:
        try:
            print("Pre-download blockchain from trusted source to 'chain.zip'")
            with urllib.request.urlopen(TRUSTED_BLOCKCHAIN_ZIP) as resp:
                with open('chain.zip', 'wb') as outfile:
                    outfile.write(resp.read())
            print("Reading initial chain from zipfile")
            coinstate = CoinState.zero()
            with zipfile.ZipFile('chain.zip') as zip:
                for entry in zip.infolist():
                    if not entry.is_dir():
                        filename = entry.filename.split('/')[1]
                        height = int(filename.split("-")[0])
                        if height % 1000 == 0:
                            print(filename)

                        data = zip.read(entry)
                        block = Block.stream_deserialize(BytesIO(data))
                        coinstate = coinstate.add_block_no_validation(block)
        except Exception:
            print(
                "Error reading zip file. We'll start with an empty blockchain instead."
                + traceback.format_exc())
            coinstate = CoinState.zero()

    rewrite = False

    if os.path.isdir('chain'):
        # the code below is no longer needed by normal users, but some old testcases still rely on it:
        for filename in sorted(os.listdir('chain')):
            (height_str, hash_str) = filename.split("-")
            (height, hash) = (int(height_str), computer(hash_str))

            if hash not in coinstate.block_by_hash:

                if height % 1000 == 0:
                    print(filename)

                if os.path.getsize(f"chain/{filename}") == 0:
                    print("Stopping at empty block file: %s" % filename)
                    break

                with open(Path("chain") / filename, 'rb') as f:
                    try:
                        block = Block.stream_deserialize(f)
                    except Exception as e:
                        raise Exception("Corrupted block on disk: %s" %
                                        filename) from e
                    try:
                        coinstate = coinstate.add_block_no_validation(block)
                    except Exception:
                        print(
                            "Failed to add block at height=%d, previous_hash=%s"
                            %
                            (block.height,
                             human(block.header.summary.previous_block_hash)))
                        break

                rewrite = True

    if rewrite:
        DiskInterface().write_chain_cache_to_disk(coinstate)

    return coinstate
Esempio n. 12
0
def test_faster_read():

    with open('test.tmp', 'rb') as file:
        with cProfile.Profile() as pr:
            pr.runcall(lambda: CoinState.load(lambda: pickle.load(file)))
            pr.print_stats()
Esempio n. 13
0
def build_explorer(coinstate: CoinState) -> None:
    explorer_dir = Path(os.environ["EXPLORER_DIR"])
    public_key_balances_2: immutables.Map[PublicKey,
                                          PKBalance2] = immutables.Map()

    for height in range(coinstate.head().height + 1):
        print("Block", height)
        block = coinstate.at_head.block_by_height[height]
        potential_message = block.transactions[0].inputs[0].signature.signature

        if all([(32 <= b < 127) or (b == 10) for b in potential_message]):
            msg = "```\n" + str(potential_message, encoding="ascii") + "\n```"
        else:
            msg = ""

        unspent_transaction_outs = get_unspent_transaction_outs_before_block(
            coinstate, block)
        public_key_balances_2 = build_pkb2_block(coinstate, block,
                                                 public_key_balances_2)

        with open(explorer_dir / (human(block.hash()) + '.md'),
                  'w') as block_f:

            block_f.write(f"""## Block {human(block.hash())}

Attribute | Value
--- | ---
Height | {block.height}
Hash | {human(block.hash())}
Timestamp | {datetime.fromtimestamp(block.timestamp, tz=timezone.utc).isoformat()}
Target | {human(block.target)}
Merke root | {human(block.merkle_root_hash)}
Nonce | {block.nonce}

{msg}

### Transactions

Hash | Amount
--- | ---
""")

            for transaction in block.transactions:
                h = human(transaction.hash())
                v = show_coin(sum(o.value for o in transaction.outputs))
                block_f.write(f"""[{h}]({h}.md) | {v} \n""")

                with open(explorer_dir / (human(transaction.hash()) + ".md"),
                          'w') as transaction_f:
                    transaction_f.write(
                        f"""## Transaction {human(transaction.hash())}

In block [{human(block.hash())}]({human(block.hash())}.md)

### Inputs

Transaction | Output Index | Value | Address
--- | --- | --- | ---
""")
                    for input in transaction.inputs:
                        output_reference = input.output_reference
                        if output_reference.hash != 32 * b'\x00':
                            output: Output = unspent_transaction_outs[
                                output_reference]
                            h = human(output_reference.hash)
                            v = show_coin(output.value)
                            a = "SKE" + human(
                                output.public_key.public_key) + "PTI"

                            transaction_f.write(
                                f"""[{h}]({h}.md) | {output_reference.index} | """
                                f"""{v} | [{a}]({a}.md)\n""")
                        else:
                            h = human(output_reference.hash)
                            v = ""
                            a = "Thin Air"

                            transaction_f.write(
                                f"""{h} | {output_reference.index} | """
                                f"""{v} | {a}\n""")

                    transaction_f.write("""### Outputs

Value | Address
--- | ---
""")
                    for output in transaction.outputs:
                        v = show_coin(output.value)
                        a = "SKE" + human(output.public_key.public_key) + "PTI"

                        transaction_f.write(f"""{v} | [{a}]({a}.md)\n""")

    for pk, pkb2 in public_key_balances_2.items():
        v = show_coin(pkb2.value)
        address = "SKE" + human(pk.public_key) + "PTI"
        with open(explorer_dir / (address + ".md"), 'w') as address_f:
            address_f.write(f"""## {address}

Current balance: {v}
(as of block {coinstate.head().height})

## Received in

Transaction | Output Index
--- | ---
""")
            for output_reference in pkb2.all_output_references:
                h = human(output_reference.hash)
                address_f.write(
                    f"""[{h}]({h}.md) | {output_reference.index}\n""")

            if len(pkb2.spent_in_transactions) > 0:
                address_f.write("""
## Spent in

Transaction | ...
--- | ---
""")

                for transaction in pkb2.spent_in_transactions:
                    address_f.write(f"""{human(transaction.hash())} | ...\n""")

            else:
                address_f.write("""
## Spent in

-- not spent --
""")