C 303: Double-Spend (51% Attack) on Bitcoin (20 pts.)

What You Need

Purpose

To prepare a Bitcoin local network, and perform a 51% attack. I am following this guide.

Background

Distributed Ledger

Bitcoin is a distributed ledger. So two miners, such as Alice and Bob, both have complete blockchain data, synchronized over the Internet as shown below.

Normal Synchronization

In the normal case, if Alice sends a Bitcoin to Bob, the transaction reaches many miners, who then compete to mine it. One miner will win, and then all the other miners will update their blockchains with that mined block.

Vector image by VectorStock / vectorstock

Double-Spending

Suppose Alice submits a transaction to Bob, and a different transaction spending the same Bitcoin to Cara, as shown below.

If the network connecting the miners is slow, both Alice's and Bob's miners can mine that block and proceed to later blocks, forking the blockchain into two branches.

Eventually the two blockchains will connect, the longer one will be retained, and the shorter one discarded.

But before that happens, Alice can run away with both the items she purchased from Bob and Cara, so one of them becomes a victim of fraud.

The 51% Attack

In the description above, this could be a random error due to network congestion, but if one miner is more powerful, that miner can cause this problem maliciously.

In order to get away with it consistently, one miner needs more than half the total mining power on the network, hence the name "51%". Then the fast miner can choose which transaction to keep, and race ahead, producing the longer blockchain, so the other one will be discarded later.

Task 1: Preparing a Bitcoin Regtest Network

This network runs on one Linux server, with two mining nodes. It's sufficient to perform the 51% attack.

Downloading Bitcoin Core

On your Linux machine, in a Terminal window, execute these commands, one at a time.
wget https://bitcoin.org/bin/bitcoin-core-0.21.1/bitcoin-0.21.1-x86_64-linux-gnu.tar.gz
tar xvf bitcoin-0.21.1-x86_64-linux-gnu.tar.gz 
mv bitcoin-0.21.1 bitcoin

Starting the First Node (Alice's)

Execute this command:
bitcoin/bin/bitcoind -regtest -fallbackfee='0.00000001' \
  -rpcport=8332 -rpcuser=alice -rpcpassword=alicepass 
The output is long. The bottom portion is shown below.

Leave this window running.

Generating a Wallet

Open a second Terminal window and execute these commands:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass createwallet "alicewallet"
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass getnewaddress
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass getbalance
A wallet is created, and a new address (make a note of it somewhere).

Right now Alice's balance is zero, as shown below.

Starting a Second Node

We can run a second node on the same computer, by using different ports and a different data directory.

Open a third Terminal window and execute these commands:

mkdir .bitcoin2
bitcoin/bin/bitcoind -regtest \
  -rpcuser=bob -rpcpassword=bobpass \
  -port=28445 -rpcport=28333 \
  -datadir=.bitcoin2
Leave this window running.

Mining 50 Blocks

In the unused Terminal window, execute these commands, replacing the address with Alice's address:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  generatetoaddress 50 bcrt1q7emxccs2dqypd0n6pwwu70rkt5dc6w0el0xst8

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass getbalance
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass getblockcount
You see a long list of blockhashes as the blocks are mined.

However, your balance is still zero, as shown below, even though the block count is now 50.

Connecting to the Second Node

In the unused Terminal window, execute these commands:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
getaddednodeinfo

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  addnode "127.0.0.1:28445" add

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
getaddednodeinfo
The first command shows that Alice's node has no peers.

The second command runs without errors.

Now the "getpeerinfo" command shows information about Bob's node, as shown below.

The window running your second node shows a long list of "UpdateTip" messages, as shown below.

These show Bob's node catching up with the blocks that were mined on Alice's node.

Mining 51 More Blocks

Bitcoin mining awards aren't useable on the regtest network until 100 more blocks are mined on top of them.

Execute these commands, replacing the address with Alice's address:

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  generatetoaddress 51 bcrt1q7emxccs2dqypd0n6pwwu70rkt5dc6w0el0xst8

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass getblockcount
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass getbalance
Now Alice has some coins, as shown below.

Generating a Wallet on the Second Node (Bob's)

In the unused Terminal window, execute these commands:
bitcoin/bin/bitcoin-cli -rpcuser=bob -rpcpassword=bobpass -rpcport=28333 \
  createwallet "bobwallet"
  
bitcoin/bin/bitcoin-cli -rpcuser=bob -rpcpassword=bobpass -rpcport=28333 \
  getnewaddress
  
bitcoin/bin/bitcoin-cli -rpcuser=bob -rpcpassword=bobpass -rpcport=28333 \
  getbalance
Bob's wallet is created, with a new address (make a note of it somewhere).

Bob's balance is zero, as shown below.

Sending 2 Bitcoins from Alice to Bob

Execute these commands, replacing the address with Bob's address. The "sendtoaddress" command has a lot of unusual fields at the end to define the transaction fee limits.
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  sendtoaddress bcrt1qce08rq7wg2c5q8yrmryq9kyl8pfnewysy7pm8l 2 
  
bitcoin/bin/bitcoin-cli -rpcuser=bob -rpcpassword=bobpass -rpcport=28333 \
  getbalance
The transaction succeeded, returning a Transaction ID, but Bob still has a balance of zero, as shown below.

This is because the transaction has been submitted, but not yet added to a block and mined.

Make a note of this Transaction ID--you'll need it below.

Mining 1 Block

Execute these commands, replacing the address with Alice's address:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  generatetoaddress 1 bcrt1q7emxccs2dqypd0n6pwwu70rkt5dc6w0el0xst8

bitcoin/bin/bitcoin-cli -rpcuser=bob -rpcpassword=bobpass -rpcport=28333 \
  getbalance
Now Bob has 2 Bitcoins, as shown below.

C 303.1: gettransaction (10 pts)

Execute this command, replacing the Transaction ID with the value you noted previously:

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  gettransaction 6d4e2677ec7a0b90c02af359508e08ccd6902d0ddb5e8c8ed4d47526ac8b0cf4
The flag is covered by a green rectangle in the image below.

Task 2: Performing a Double-Spend Attack

Generating a "Cara" Account

Execute this command:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass getnewaddress
A new address appears, which we'll call Cara's. (make a note of it somewhere).

Finding an Unspent Balance

We'll find some of Alice's Bitcoins to spend twice.

Execute this command:

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass listunspent
There are two unspent balances in Alice's wallet. We'll use the first one shown below, with a balance of 50 Bitcoins.

Find the txid. In the image below, it's

101e8a913a60e2df55727cdc86ac360e00d2d4b3835dad796ec9b6fef3858f28

Creating the Alice -> Bob Transaction

We'll prepare a transaction sending 10 Bitcoins from Alice to Bob, with change of 39.9999 Bitcoins. The small amount left over is a transaction fee.

Execute this command, replacing the three values below with:

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass createrawtransaction \
    '[{"txid" : "101e8a913a60e2df55727cdc86ac360e00d2d4b3835dad796ec9b6fef3858f28", "vout" : 0}]' \
    '{"bcrt1qce08rq7wg2c5q8yrmryq9kyl8pfnewysy7pm8l": 10.0, "bcrt1q7emxccs2dqypd0n6pwwu70rkt5dc6w0el0xst8": 39.9999}'
The output is a long hexadecimal blob, which is the raw transaction, highlighted in the image below.

Finally, we need to sign that transaction with Alice's private key.

Execute this command, replacing the argument with your raw transaction data. (I omitted the center of the blob to fit the command on the page).

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass signrawtransactionwithwallet \
  "0200000...9636a5d00000000"
You get an even longer blob of hex data, highlighted in the image below.

This is the signed raw transaction for Alice -> Bob. Save it--you'll need it later.

Creating the Alice -> Cara Transaction

Execute this command, replacing the three values below with:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass createrawtransaction \
    '[{"txid" : "101e8a913a60e2df55727cdc86ac360e00d2d4b3835dad796ec9b6fef3858f28", "vout" : 0}]' \
    '{"bcrt1q5t4gsvdkl7cgumqmp6akq6kk3nf7rnuqn5fdqa": 10.0, "bcrt1q7emxccs2dqypd0n6pwwu70rkt5dc6w0el0xst8": 39.9999}'
The output is a long hexadecimal blob, which is the raw transaction, highlighted in the image below.

Finally, we need to sign that transaction with Alice's private key.

Execute this command, replacing the argument with your raw transaction data.

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass signrawtransactionwithwallet \
  "0200000...9636a5d00000000"
You get an even longer blob of hex data, highlighted in the image below.

This is the signed raw transaction for Alice -> Cara. Save it--you'll need it later.

Disconnecting the Nodes

Execute these commands:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  addnode "127.0.0.1:28445" remove

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  disconnectnode "127.0.0.1:28445"
The commands complete without errors, as shown below.

Submitting Alice -> Bob to Alice's Miner

Execute this command, replacing the hexadecimal value with your signed Alice -> Bob raw transaction.
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  sendrawtransaction "020000...000000"
The transaction completes, returning a transaction ID, as shown in the center Terminal window in the image below.

Record this Transaction ID--you'll need it later.

The transaction appears in Alice's node (the top Terminal window), but not in Bob's node, because the two nodes are disconnected.

Submitting Alice -> Cara to Bob's Miner

Execute this command, replacing the hexadecimal value with your signed Alice -> Cara raw transaction.
bitcoin/bin/bitcoin-cli -rpcuser=bob -rpcpassword=bobpass -rpcport=28333 \
  sendrawtransaction "020000...000000"
The transaction completes, returning a transaction ID, as shown in the image below.

Record this Transaction ID--you'll need it later.

Mining 10 Blocks on Alice's Node

Execute this command, replacing the address with Alice's address:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  generatetoaddress 10 bcrt1q7emxccs2dqypd0n6pwwu70rkt5dc6w0el0xst8
The blocks are mined, as shown below.

Verifying the Alice -> Bob Transaction

Execute this command, replacing the transaction ID with the value you noted earlier for the Alice -> Bob Transaction:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  gettransaction b44790e2fd44a0b2a4ab23c0f774eefb6b127e733d4ed65a858110700009b70d
The transaction is found, as shown below, showing amounts of 10.0 and 39.9999 bitcoins being sent.

(You will have a smaller number of confirmations--don't worry about that.)

Mining 1 Block on Bob's Node

Execute this command, replacing the address with Bob's address:
bitcoin/bin/bitcoin-cli -rpcuser=bob -rpcpassword=bobpass -rpcport=28333 \
  generatetoaddress 1 bcrt1qce08rq7wg2c5q8yrmryq9kyl8pfnewysy7pm8l
The block is mined, as shown below.

We can't verify this transaction, because it doesn't involve Bob's wallet, but it's on the blockchain. If you try to perform the sendrawtransaction action again, you'll see an error telling you that.

Checking Block Count on the Nodes

Execute these commands, replacing the address with Bob's address:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  getblockcount

bitcoin/bin/bitcoin-cli -rpcuser=bob -rpcpassword=bobpass -rpcport=28333 \
  getblockcount
Alice's blockchain has more blocks than Bob's, as shown below, although the different will be smaller than shown in the image below.

Connecting the Nodes

In the unused Terminal window, execute these commands:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  addnode "127.0.0.1:28445" add

bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  getblockcount

bitcoin/bin/bitcoin-cli -rpcuser=bob -rpcpassword=bobpass -rpcport=28333 \
  getblockcount
The two nodes synchronize, and are now the same height, as shown below.

Verifying the Alice -> Bob Transaction

Execute this command, replacing the transaction ID with the value you noted earlier for the Alice -> Bob Transaction:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  gettransaction b44790e2fd44a0b2a4ab23c0f774eefb6b127e733d4ed65a858110700009b70d
The transaction is still on the blockchain, as shown below.

C 303.2: Verifying the Alice -> Cara Transaction (10 pts)

Execute this command, replacing the transaction ID with the value you noted earlier for the Alice -> Cara Transaction:
bitcoin/bin/bitcoin-cli -rpcuser=alice -rpcpassword=alicepass \
  gettransaction 6cbfe92849d913fda936d8524d7be66c2e6f7c846ef9dce19c4270cdcce462d9
The flag is covered by a green rectangle in the image below.

Sources

How to Bitcoin regtest

Posted 5-16-2021 by Sam Bowne