Beispiel #1
0
    def add_transaction_to_pool(self, transaction: Transaction) -> bool:
        with self.lock:
            self.local_peer.logger.info(
                "%15s ChainManager.add_transaction_to_pool(%s)" %
                ("", human(transaction.hash())))

            try:
                validate_non_coinbase_transaction_by_itself(transaction)

                assert self.coinstate.current_chain_hash

                validate_non_coinbase_transaction_in_coinstate(
                    transaction, self.coinstate.current_chain_hash,
                    self.coinstate)

                # Horribly inefficiently implemented (AKA 'room for improvement)
                validate_no_duplicate_output_references_in_transactions(
                    self.transaction_pool + [transaction])

                #  we don't do validate_no_duplicate_transactions here (assuming it's just been done before
                #  add_transaction_to_pool).

            except ValidateTransactionError as e:
                # TODO: dirty hack at this particular point... to allow for e.g. out-of-order transactions to not take
                # down the whole peer, but this should more specifically match for a short list of OK problems.
                self.local_peer.logger.warning("%15s INVALID transaction %s" %
                                               ("", str(e)))
                self.local_peer.disk_interface.save_transaction_for_debugging(
                    transaction)

                return False  # not successful

            self.transaction_pool.append(transaction)

        return True  # successfully added
Beispiel #2
0
def test_validate_non_coinbase_transaction_by_itself_no_inputs():
    transaction = Transaction(
        inputs=[],
        outputs=[Output(30, example_public_key)],
    )

    with pytest.raises(ValidateTransactionError, match=".*No inputs.*"):
        validate_non_coinbase_transaction_by_itself(transaction)
Beispiel #3
0
def test_validate_non_coinbase_transaction_by_itself():
    transaction = Transaction(
        inputs=[Input(
            OutputReference(b'a' * 32, 1),
            SignableEquivalent(),
        )],
        outputs=[Output(30, example_public_key)])

    with pytest.raises(ValidateTransactionError,
                       match=".*Non-signature Signature class used.*"):
        validate_non_coinbase_transaction_by_itself(transaction)
Beispiel #4
0
def test_validate_non_coinbase_transaction_by_itself_max_size():
    transaction = Transaction(inputs=[
        Input(
            OutputReference(b'a' * 32, 1),
            SECP256k1Signature(b'y' * 64),
        )
    ] * 30_000,
                              outputs=[Output(30, example_public_key)])

    with pytest.raises(ValidateTransactionError, match=".*MAX_BLOCK_SIZE.*"):
        validate_non_coinbase_transaction_by_itself(transaction)
Beispiel #5
0
def test_validate_non_coinbase_transaction_by_itself_is_not_coinbase():
    transaction = Transaction(inputs=[
        Input(
            OutputReference(b'\x00' * 32, 0),
            SECP256k1Signature(b'y' * 64),
        )
    ],
                              outputs=[Output(30, example_public_key)])

    with pytest.raises(ValidateTransactionError,
                       match=".*null-reference in non-coinbase transaction.*"):
        validate_non_coinbase_transaction_by_itself(transaction)
Beispiel #6
0
def test_validate_non_coinbase_transaction_by_itself_max_total_output():
    transaction = Transaction(
        inputs=[
            Input(
                OutputReference(b'a' * 32, 1),
                SECP256k1Signature(b'y' * 64),
            )
        ],
        outputs=[Output(21_000_000 * SASHIMI_PER_COIN, example_public_key)])

    with pytest.raises(ValidationError, match=".out of range.*"):
        validate_non_coinbase_transaction_by_itself(transaction)
Beispiel #7
0
def test_validate_non_coinbase_transaction_by_itself_no_duplicate_output_references(
):
    transaction = Transaction(inputs=[
        Input(
            OutputReference(b'a' * 32, 1),
            SECP256k1Signature(b'y' * 64),
        )
    ] * 2,
                              outputs=[Output(30, example_public_key)])

    with pytest.raises(ValidateTransactionError,
                       match=".*output_reference referenced more than once.*"):
        validate_non_coinbase_transaction_by_itself(transaction)
Beispiel #8
0
def test_validate_non_coinbase_transaction_by_itself_no_outputs():
    transaction = Transaction(
        inputs=[
            Input(
                OutputReference(b'a' * 32, 1),
                SECP256k1Signature(b'y' * 64),
            )
        ],
        outputs=[],
    )

    with pytest.raises(ValidateTransactionError, match=".*No outputs.*"):
        validate_non_coinbase_transaction_by_itself(transaction)
Beispiel #9
0
def main() -> None:
    parser = DefaultArgumentParser()
    parser.add_argument("amount", help="The amount of to send", type=int)
    parser.add_argument("denomination", help="'skepticoin' or 'sashimi'", choices=['skepticoin', 'sashimi'])
    parser.add_argument("address", help="The address to send to")
    args = parser.parse_args()
    configure_logging_from_args(args)

    value = args.amount * (SASHIMI_PER_COIN if args.denomination == 'skepticoin' else 1)

    if not is_valid_address(args.address):
        print("Invalid address")
        return

    check_chain_dir()
    coinstate = read_chain_from_disk()
    wallet = open_or_init_wallet()
    thread = start_networking_peer_in_background(args, coinstate)

    try:
        # we need a fresh chain because our wallet doesn't track spending/receiving, so we need to look at the real
        # blockchain to know what we can spend.
        check_for_fresh_chain(thread)
        print("Chain up to date")

        target_address = SECP256k1PublicKey(parse_address(args.address))
        change_address = SECP256k1PublicKey(wallet.get_annotated_public_key("change"))
        save_wallet(wallet)

        transaction = create_spend_transaction(
            wallet,
            coinstate,
            value,
            0,  # we'll get to paying fees later
            target_address,
            change_address,
        )

        validate_non_coinbase_transaction_by_itself(transaction)
        assert coinstate.current_chain_hash
        validate_non_coinbase_transaction_in_coinstate(transaction, coinstate.current_chain_hash, coinstate)

        print("Broadcasting transaction on the network", transaction)
        thread.local_peer.network_manager.broadcast_transaction(transaction)

        print("Monitoring...")

        while True:
            sleep(5)

            # it's late and I'm too lazy for the efficient & correct implementation.
            coinstate = thread.local_peer.chain_manager.coinstate

            max_height = coinstate.head().height

            for i in range(10):
                block = coinstate.by_height_at_head()[max(max_height - i, 0)]
                if transaction in block.transactions:
                    print("Transaction confirmed at", block.height, "with", i, "confirmation blocks")

                    if i == 6:  # this is the magic number of confirmations according to the "literature" on the subject
                        thread.stop()
                        return

    except KeyboardInterrupt:
        print("KeyboardInterrupt")
    finally:
        print("Stopping networking thread")
        thread.stop()
        print("Waiting for networking thread to stop")
        thread.join()
        print("Done; waiting for Python-exit")