Ejemplo n.º 1
0
def start_mining(block_queue, transaction_queue, public_key, private_key):
    blockchain = BlockChain([args.ip_other])
    miner = Miner(blockchain, public_key)
    miner_status = False  # Checks if miner is ready to send

    # Variables for double-spending attack:
    start_attack = False
    announced_attack = False
    cease_attacks = False
    unsent_bad_tx = False
    sent_tx = False
    ignore_transactions = []
    private_fork = []
    skip_mine_count = 0
    trigger_block = 3
    trigger_block_hash = ""
    mine_private_blocks = False
    private_last_hash = ""
    original_blocks = []

    while True:
        blockchain.resolve()
        if args.attacker and start_attack:
            # Make sure new blocks don't include the bad_tx from attacker
            merkletree, ledger = miner.create_merkle(
                transaction_queue, tx_to_ignore=ignore_transactions)
        else:  # Not attacker or is an attacker and not attacking
            merkletree, ledger = miner.create_merkle(transaction_queue)

        if not cease_attacks:
            if args.attacker:
                # 'and sent_tx' ensures attacker only attacks at least one block after bad_tx is sent
                start_attack = len(
                    blockchain.cleaned_keys) > trigger_block and sent_tx
            else:
                start_attack = len(blockchain.cleaned_keys) > trigger_block

            # Send a bad transaction after trigger_block number of blocks in chain
            unsent_bad_tx = len(blockchain.cleaned_keys) == trigger_block

            # This if statement should only be True once
            # Send transaction with intent to double spend
            if args.attacker and unsent_bad_tx:
                bad_tx = Transaction(public_key, SigningKey.generate().get_verifying_key(), 50,
                                     "give me the goods", sender_pk=private_key).to_json().encode()
                print("Sending transaction with intent to double-spend...")
                print("Transaction sent.")
                ignore_transactions.append(bad_tx)
                unsent_bad_tx = False
                sent_tx = True

        while True:
            miner_status = False
            # This if statement should only be True once
            # Start of attack
            if not announced_attack and args.attacker and start_attack:
                # Take the hash of the block before the bad_tx
                trigger_block_hash = blockchain.cleaned_keys[trigger_block-1]
                private_last_hash = trigger_block_hash

                # Used to track which blocks to ignore in trying to build new longest chain
                original_blocks = copy.deepcopy(
                    blockchain.cleaned_keys[trigger_block:])
                print("=============\nSTART ATTACK!\n============")
                announced_attack = True

                # Generate new public key and empty out balance from old public key
                new_private_key = create_key()
                new_public_key = new_private_key.get_verifying_key()
                try:
                    print(
                        "Double-spending: Making transaction to empty out old account...")
                    empty_old_account = Transaction(public_key, new_public_key, amount=ledger.get_balance(
                        public_key), comment="transferring all money out", sender_pk=private_key)
                    print("sent transaction")
                except AssertionError:
                    # Old account already empty
                    pass
                public_key = new_public_key
                private_key = new_private_key

                # Take on a new identity
                miner = Miner(blockchain, new_public_key)
                # Need to create merkle again so coinbase goes to new_public_key
                merkletree, ledger = miner.create_merkle(
                    transaction_queue, tx_to_ignore=ignore_transactions)

            # If attack starts, slow down honest miner
            if not args.attacker and start_attack:
                if skip_mine_count % 500 == 0:
                    miner_status = miner.mine(merkletree, ledger)
                    # Keep range of skip_mine_count within (0,500]
                    skip_mine_count = 0
                skip_mine_count += 1
            elif args.attacker and start_attack:
                # Start mining from block before bad_tx
                miner_status = miner.mine_from_old_block(
                    merkletree, ledger, private_last_hash)
            else:
                # Mine normally if no attack
                miner_status = miner.mine(merkletree, ledger)

            mine_or_recv = ""
            # Check if mine is successful
            if miner_status:
                mine_or_recv = "Block MINED "

                if args.attacker and start_attack:
                    # sending_block might not be last block of blockchain
                    sending_block = find_private_block(
                        blockchain.chain, private_last_hash, original_blocks)
                    sending_block_header_hash = binascii.hexlify(
                        sending_block.header_hash()).decode()
                    mine_or_recv += sending_block_header_hash
                    private_fork.append(sending_block)
                    # Update private_last_hash
                    private_last_hash = sending_block_header_hash
                    if len(private_fork) == 3:
                        for block in private_fork:
                            block_data = pickle.dumps(block, protocol=2)
                            send_failed = True
                            while send_failed:
                                try:
                                    requests.post("http://"+args.ip_other +
                                                  "/block", data=block_data)
                                    send_failed = False
                                except:
                                    time.sleep(0.1)
                        private_fork = []
                        if not check_block_in_chain(blockchain, original_blocks[0]):
                            print("=============\nATTACK ENDED\n============")
                            # stop attack
                            start_attack = False
                            cease_attacks = True
                        else:
                            print(
                                "Block to void:", original_blocks[0], "Chain:", blockchain.cleaned_keys)
                            print(
                                "Block we want to void is still in chain! Continuing attack...")

                else:  # args.attacker and not start_attack or not args.attacker
                    sending_block = blockchain.last_block()
                    mine_or_recv += binascii.hexlify(
                        sending_block.header_hash()).decode()

                    data = pickle.dumps(sending_block, protocol=2)
                    send_failed = True
                    while send_failed:
                        try:
                            requests.post("http://" + args.ip_other +
                                          "/block", data=data)
                            send_failed = False
                        except:
                            time.sleep(0.2)
                break

            if miner.nonce % 1000 == 0:
                # Check if new blocks have been detected
                block_queue_status_initial = block_queue.empty()
                while not block_queue.empty():
                    mine_or_recv += "Block RECEIVED "
                    # If detected, add new block to blockchain
                    new_block = block_queue.get()
                    miner.network_block(new_block)
                    mine_or_recv += binascii.hexlify(
                        new_block.header_hash()).decode() + " "
                if not block_queue_status_initial:
                    break
                block_queue_status_initial = True
        print(color(args.color) + "PORT: {}\n".format(args.port) + mine_or_recv +
              str(miner.blockchain).split("~~~")[1])
        if start_attack and args.attacker:
            print(color(args.color) + "private fork:"+" "*14*(trigger_block-2), trigger_block_hash[:10]+" -> ",
                  [binascii.hexlify(private_block.header_hash()).decode()[:10] for private_block in private_fork])