Introducing Blockchain With Java
Introducing Blockchain With Java
Introduction
to Blockchain
The entirety of this chapter is comprised of an excerpt from
Introducing Blockchain with Lisp: Implement and Extend
Blockchains with the Racket Language by Boro Sitnikovski,
and it is reused here with the author’s permission.
1
We will use this definition throughout the book, but note that there are many
different definitions on the Internet. By the end of this book, you should be able to
distinguish the slight nuances and similarities in each definition.
2
Chapter 1 Introduction to Blockchain
Further, at the end of every day, you all sit together and refer to the
ledger to do the calculations to settle up. Let’s imagine that there is a pot
that is the place where all of the money is kept. If you spent more than
you received, you put that money in the pot; otherwise, you take that
money out.
We want to design the system such that it functions similarly to a
regular bank account. A holder of a wallet (bank account) should be
able to only send money from their wallet to other wallets. Thus, every
person in the system will have a wallet of a kind, which can also be used
to determine the balance for them. Note that with the current setup using
a ledger, we have to go through all the existing records to determine the
balance of a specific wallet.
If we want to avoid going through all the existing records, there is a way
we can optimize this with unspent transaction outputs (UTXOs), as we will
see later in Chapter 3.
3
Chapter 1 Introduction to Blockchain
4
Chapter 1 Introduction to Blockchain
However, let’s assume that Bob is keeping the ledger to himself, and
everybody agrees to this. The ledger is now stored in what is a centralized
place. But in this case, if Bob is unavailable at the end of the day when
everybody gathers to settle up, nobody will be able to refer to the ledger.
We need a way to decentralize the ledger, such that at any given time
any of the people can do a transaction. For this, every person involved will
keep a copy of the ledger to themselves, and when they meet at the end of
the day, they will sync their ledgers.
You are connected to your friends, and so are they to you. Informally,
this makes a peer-to-peer network.
For example, when you are accessing a web page on the Internet using
a browser, your browser is the client, and the web page you’re accessing is
hosted by a server. This represents a centralized system since every user is
getting the information from a single place—the server.
In contrast, in a peer-to-peer network, which represents a
decentralized system, the distinction between a client and a server is
blurred. Every peer is both a client and a server at the same time.
With the system (Figure 1-3), as the list of peers (people) grows, we
might run into a problem of trust. When everybody meets at the end of
the day to sync their ledgers, how can they believe the others that the
transactions listed in their ledgers are true? Even if everybody trusts
everybody else for their ledger, what if a new person wants to join this
network? It’s natural for existing users to ask this newcomer to prove that
they can be trusted. We need to modify our system to support this kind of
trust. One way to achieve that is through so-called proof of work, which we
introduce next.
5
Chapter 1 Introduction to Blockchain
For each record we will also include a special number (or a hash)
that will represent proof of work, in that it will provide proof that the
transaction is valid. We will cover the technical details in the “Hashing”
section.
At the end of the day, we agree that we will trust the ledger of the
person who has put most of the work in it. If Bob has some errands to
run, he can catch up the next day by trusting the rest of the peers in the
network.
In addition to all this, we want the transactions to have an order, so
every record will also contain a link to the previous record. This represents
the actual blockchain, depicted in Figure 1-4.
6
Chapter 1 Introduction to Blockchain
1.2 Encryption
We will start with the following definition.
7
Chapter 1 Introduction to Blockchain
Note that in this section we will mostly talk about numbers, but
characters and letters can also be encrypted/decrypted with the same
methods, by using the ASCII2 values for the characters.
Before we talk about encryption, we first have to recall what functions are,
since the encoding/decoding values are achieved with the usage of them.
1.2.1 Functions
For example, you might have a function that accepts as input a person and
as output returns the person’s age or name. Another example is the function
f (x) = x + 1. There are many inputs this function can accept: 1, 2, 3.14. For
example, when we input 2, it gives us an output of 3, since f (2) = 2 + 1 = 3.
2
An ASCII table is a table that assigns a unique number to each character
(such as !, @, a, Z, etc.).
8
Chapter 1 Introduction to Blockchain
9
Chapter 1 Introduction to Blockchain
This algorithm scheme has a neat property where only the private key
can decode a message, and the public key can encode a message.
10
Chapter 1 Introduction to Blockchain
We have two functions that should have the same properties as those
for the symmetric-key algorithm.
11
Chapter 1 Introduction to Blockchain
As we said earlier, each record will also include a special number (or a
hash). This hash will be what is produced by S(x, s) (encryption). A hash
can be verified by using the verify function to confirm a record’s ownership
(decryption).
The wallet will contain a pair of public and a private key. These keys
will be used to receive or send money. With the private key, it is possible
to write new blocks (or transactions) to the blockchain, effectively
spending money. With the public key, others can send currency and verify
signatures.
EXERCISE 1-1
EXERCISE 1-2
Check the three properties for a symmetric-key algorithm to ensure the Caesar
cipher is compatible with them.
12
Chapter 1 Introduction to Blockchain
EXERCISE 1-3
EXERCISE 1-4
Use the asymmetric-key algorithm we defined to sign a message and verify it.
1.3 Hashing
Definition 1-8 Hashing is a one-way function in that it encodes
text without a way to retrieve the original value.
13
Chapter 1 Introduction to Blockchain
EXERCISE 1-5
EXERCISE 1-6
In which way can the linked list depicted in Figure 1-4 be traversed? What are
the implications of this property?
3
Hashcash was initially targeted for limiting email spam and other attacks.
However, recently it’s also become known for its usage in blockchains as part of
the mining process. Hashcash was proposed in 1997 by Adam Backa.
14
Chapter 1 Introduction to Blockchain
1.5 Bitcoin
Bitcoin was the world’s first implementation of a blockchain. In November
2008, a link to a paper authored by Satoshi Nakamoto titled “Bitcoin:
A Peer-to-Peer Electronic Cash System” was published on a cryptography
mailing list. Bitcoin’s white paper consists of nine pages; however, it is
a mostly theoretical explanation of the design and as such may be a bit
overwhelming to newcomers.
The Bitcoin software is open source code and was released in January
2009 on SourceForge. The design of a Bitcoin includes a decentralized
network (peer-to-peer network), block (mining), blockchain, transactions,
and wallets, each of which we will look at in detail in this book.
15
Chapter 1 Introduction to Blockchain
Although there are many blockchain models and each one of them
differs in implementation details, the blockchain we will be building upon
in this book will look pretty similar to Bitcoin, with some parts simplified.
1.7 Summary
The point of this chapter is to get a vague idea of how the system that we
will implement looks. Things will become much clearer in the next chapter
where we will be explicit about the definitions of every entity.
16
Chapter 1 Introduction to Blockchain
17
CHAPTER 2
Model: Blockchain
Core
2.1 Block.java
We will start first by listing the imports in the following code snippet:
1 package com.company.Model;
2
3 import sun.security.provider.DSAPublicKeyImpl;
4
5 import java.io.Serializable;
6 import java.security.InvalidKeyException;
7 import java.security.Signature;
8 import java.security.SignatureException;
9 import java.util.ArrayList;
10 import java.util.Arrays;
11 import java.util.LinkedList;
12
20
Chapter 2 Model: Blockchain Core
21
Chapter 2 Model: Blockchain Core
We will touch on this topic a bit later in this section when we explain the
isVerified method of this class. Next is our ledgerId field. Since we
intend to implement a database with separate Block and Transaction
tables, this field will help us retrieve the correct corresponding ledger for
this block. You can also look at this field as the block number. Our next
fields, miningPoints and luck, will be used to form the network consensus
in regard to choosing this block’s miner.
We will get into the details of how these fields are used in Chapter 6.
The field transactionLedger is simply an arraylist of all the transactions
contained in this block. We will explain the Transaction class in the
section “Transaction.java.”
In the following snippet, we can see the three constructors starting on
line 26, line 38, and line 45:
22
Chapter 2 Model: Blockchain Core
48
49 public Boolean isVerified(Signature signing)
50 throws InvalidKeyException, SignatureException {
51 signing.initVerify(new DSAPublicKeyImpl(this.minedBy));
52 signing.update(this.toString().getBytes());
53 return signing.verify(this.currHash);
54 }
55
23
Chapter 2 Model: Blockchain Core
55
56 @Override
57 public boolean equals(Object o) {
58 if (this == o) return true;
59 if (!(o instanceof Block)) return false;
60 Block block = (Block) o;
61 return Arrays.equals(getPrevHash(),
block.getPrevHash());
62 }
63
64 @Override
65 public int hashCode() {
66 return Arrays.hashCode(getPrevHash());
67 }
68
69 public byte[] getPrevHash() { return prevHash; }
70 public byte[] getCurrHash() { return currHash; }
71
24
Chapter 2 Model: Blockchain Core
25
Chapter 2 Model: Blockchain Core
95 @Override
96 public String toString() {
97 return "Block{" +
98 "prevHash=" + Arrays.toString(prevHash) +
99 ", timeStamp='" + timeStamp + '\'' +
100 ", minedBy=" + Arrays.toString(minedBy) +
101 ", ledgerId=" + ledgerId +
102 ", miningPoints=" + miningPoints +
103 ", luck=" + luck +
104 '}';
105 }
106}
107
The first thing to note here is that the equals method compares the
previous hash of the block class. We’ll use this later in Chapter 6 when we
explain the consensus algorithm further. The other thing of note is the
fields contained in the toString method. We include everything that goes
into verifying the block against the current hash.
Important!
• Remember that a wallet’s public key is also the wallet’s
public address/account number.
• The current hash/signature of the block is just the encrypted
version of the data contained in the block.
• The miner’s private key is used to encrypt the block’s data,
which creates the signature.
26
Chapter 2 Model: Blockchain Core
• The miner’s public key is used for other peers to verify the
block by comparing the signature’s hash against the hash of
the block’s data.
• Note how we use the toString() method throughout this
class to prep our data conveniently for comparison.
• Note how all of the essential fields that make certain the
block is unique are included in the toString() method.
2.2 Transaction.java
We briefly mentioned that we keep an array list of transactions in our
Block class in the previous section, and now it’s time to explain in detail
what our Transaction.java class contains. First we’ll start with the
imports found in the following code snippet:
1 package com.company.Model;
2
3 import sun.security.provider.DSAPublicKeyImpl;
4
5 import java.io.Serializable;
6 import java.security.InvalidKeyException;
7 import java.security.Signature;
8 import java.security.SignatureException;
9 import java.time.LocalDateTime;
10 import java.util.Arrays;
11 import java.util.Base64;
12
27
Chapter 2 Model: Blockchain Core
Next let’s go over the class declaration and its fields, as shown in the
next code snippet:
Since this class also creates objects from which we are building
our blockchain, it will implement the interface serializable so that it’s
shareable through the network.
The fields from and to will contain the public keys/addresses of the
account that sends and the account that receives the coins, respectively.
The value is the amount of coins that will be sent, and timeStamp is the
time at which the transaction has occurred. Signature will contain the
encrypted information of all the fields, and it will be used to verify the
validity of the transaction (it will be used the same way the field currHash
was used in the previous class).The ledgerId serves the same purpose as
in the previous class. The fields with the FX suffix are simple duplicates
formatted to String instead of byte[]. We do this so that we can easily
display them on our front end.
In this class we also have two constructors; the first one is used when
we retrieve a transaction from the database, and the second one is used
when we want to create a new transaction within our application. Let’s
observe them in the following code snippet:
28
Chapter 2 Model: Blockchain Core
29
Chapter 2 Model: Blockchain Core
52 String sr = this.toString();
53 signing.update(sr.getBytes());
54 this.signature = signing.sign();
55 this.signatureFX = encoder.encodeToString(this
.signature);
56 }
57
The first constructor simply sets the class fields according to the
retrieved data from the database and uses the Base64.Encoder class to
convert the byte[] fields safely into String.
The second constructor is a bit more complex, so we will explain it in
more detail piece by piece. First let’s look at the constructor parameters
that are different: Wallet fromWallet and Signature signing. We will
explain the Wallet class in more detail in the next section, for now we
should just note that the fromWallet parameter contains the public and
private keys of the sender/maker of the transaction. We use the same
Signature class as in our Block isVerified method mentioned in the
previous section.
Next let’s explain the body of the constructor so we understand how
encrypting data works in our case. The signature creation phase shown in
Figure 2-1 offers an overview of what we are trying to accomplish.
30
Chapter 2 Model: Blockchain Core
To achieve this, first we set up our data by initiating our class fields
from the parameters and add a timestamp as shown on lines 44 to 50. Now
once we have our data that we would like to encrypt, we set our private key
to the signing object with the statement on line 51. This tells the signing
object, when encrypting, to use the private key we provided. On line 52
we are putting all the data we want to encrypt in a single String object
by using the toString() method. On line 53 we are feeding all the data
we want to encrypt to the signing object, and on line 54 we are actually
encrypting the data and assigning it to our signature field.
Next is our method for the verification of transactions, as shown in the
following snippet:
This method will be used by the other peers to verify that each
transaction is valid. Before explaining the code, let’s look at Figure 2-2 and
see what our method tries to accomplish.
31
Chapter 2 Model: Blockchain Core
32
Chapter 2 Model: Blockchain Core
We finish up the rest of the class with generic getters, setters, and our
toString, equals, and hash methods, as shown in the following snippet:
65 @Override
66 public String toString() {
67 return "Transaction{" +
68 "from=" + Arrays.toString(from) +
69 ", to=" + Arrays.toString(to) +
70 ", value=" + value +
71 ", timeStamp= " + timestamp +
72 ", ledgerId=" + ledgerId +
73 '}';
74 }
75
76 public byte[] getFrom() { return from; }
77 public void setFrom(byte[] from) { this.from = from; }
78
79 public byte[] getTo() { return to; }
80 public void setTo(byte[] to) { this.to = to; }
81
82 public Integer getValue() { return value; }
83 public void setValue(Integer value) { this.value = value; }
84 public byte[] getSignature() { return signature; }
85
86 public Integer getLedgerId() { return ledgerId; }
87 public void setLedgerId(Integer ledgerId) {
this.ledgerId = ledgerId; }
88
89 public String getTimestamp() { return timestamp; }
90
91 public String getFromFX() { return fromFX; }
92 public String getToFX() { return toFX; }
33
Chapter 2 Model: Blockchain Core
Important!
• Note how we use the toString() method throughout this
class to prep our data conveniently for comparison.
• Note how all the essential fields that make certain the
transaction is unique are included in the toString()
method.
34
Chapter 2 Model: Blockchain Core
EXERCISE 2-1
2.3 Wallet.java
Let’s start as always with the imports for this class located in the following
snippet:
1 package com.company.Model;
2
3 import java.io.Serializable;
4 import java.security.*;
5
35
Chapter 2 Model: Blockchain Core
Important!
• Note how our blockchain wallet doesn’t require any
information regarding the wallet holder. This is the basis of
the wallet holder’s anonymity.
• Our blockchain wallet also won’t contain any field containing
the current balance of the wallet. We will explain how we
obtain our wallet balance in Chapter 6.
Let’s look at our first two constructors on our next snippet, which
will be used when we want to create a new wallet and assign a new key
pair to it:
36
Chapter 2 Model: Blockchain Core
Our third constructor will be used to create our wallet once we have
imported an already existing key pair from our database. We can observe it
in the following snippet:
18
19 //Constructor for importing Keys only
public Wallet(PublicKey publicKey, PrivateKey privateKey) {
20
21 this.keyPair = new KeyPair(publicKey,privateKey);
22 }
The code here simply receives public and private key objects and
creates a new KeyPair object with them.
Finally, we finish up this class and chapter by including the generic
getters and setters in the following snippet:
23
24 public KeyPair getKeyPair() { return keyPair; }
25
26 public PublicKey getPublicKey() { return
keyPair.getPublic(); }
27 public PrivateKey getPrivateKey() {
return keyPair.getPrivate(); }
28}
2.4 Summary
In this chapter, we covered the creation of our model layer of the
application. These classes and their methods will be used as basic building
blocks throughout the rest of the application. Therefore, having nice grasp
of them now will greatly benefit you in understanding the more complex
logic in the upcoming chapters. This is a small recap of what concepts we
covered so far:
37
Chapter 2 Model: Blockchain Core
38
CHAPTER 3
Database Setup
40
Chapter 3 Database Setup
This means that you have installed the database browser successfully,
and we can move on to setting up our actual databases.
3.2 Blockchain.db
Our first database that we will implement is the one for storing our
blockchain data, and we will name it appropriately as blockchain.db. In
this chapter, we will learn to set up our database step-by-step by using the
SQLite browser. However, in the section “Writing your App init() Method,”
we will learn to set up our database programmatically by using SQL and
SQL JDBC driver. Ideally this chapter will go a long way in visualizing the
data contained in the blockchain through the database browser and help
bridge the gap for people less familiar with SQL. The “Writing your App
init() Method” section will give you the SQL code and teach you how to
programmatically implement your database setup in the future.
We start by clicking the New Database button or go to File
and then click the New Database command in case the button tray doesn’t
show. This should open a screen similar to Figure 3-2, asking us to name
our database and choose a filepath for saving it.
41
Chapter 3 Database Setup
42
Chapter 3 Database Setup
We will name our table BLOCKCHAIN, and we want it to store all of our
blocks and the data they contain. Now in the upper window on the tab
named Fields, we will click the Add button to add a field row. These field
rows will represent the table columns, so from now on we will refer to
them as such. When you start creating your columns in the upper window,
you will notice that the SQL code in the lower window will modify itself to
correspond to your choices. For now let’s look at Figure 3-4 and set up our
table columns accordingly.
43
Chapter 3 Database Setup
If you noticed that the names of the columns are similar to the names
of the fields of the Block.java class, then great job; having a different
column for each class field is exactly how we want to store our data.
Each row in our BLOCKCHAIN table will represent a different block, so we
also include an ID column here, which will number our blocks. The type
44
Chapter 3 Database Setup
chosen for our column corresponds closely to the types of the fields in
the Block.java class in order to maintain the integrity of the data. This
is especially important for all our class fields of the byte array type to be
stored as BLOB; otherwise, the conversion to another type will change the
original information and render the public keys and signatures stored in
them invalid. You can read up more on SQLite types if you are interested
at https://www.w3resource.com/sqlite/sqlite-data-types.php. Now
once we click the OK button, we will have our first table in our database
created, and our database browser should look like Figure 3-5.
45
Chapter 3 Database Setup
Our next step is to create a table that will contain the transactions
included in each block. For that purpose we click the Create Table button
and get another window looking exactly like the window we used to create
our BLOCKCHAIN table.
EXERCISE 3-1
Try creating the next table named TRANSACTIONS on your own before
learning how we did it.
46
Chapter 3 Database Setup
47
Chapter 3 Database Setup
Now obviously if you click the Browse Data tab, the tables will be
empty, but let’s conclude this section by looking at Figure 3-8 and
Figure 3-9, which contain some dummy data so we can visualize how
everything will fall into place once we complete the rest of our application.
48
Chapter 3 Database Setup
49
Chapter 3 Database Setup
3.3 Wallet.db
Before we go through basically the same steps for creating Wallet.db, let’s
explain our design decision as to why we create the wallet in a separate
database instead of creating just another table in our blockchain.db
database. It’s because this way we get a clear separation of concerns.
All the data in the blockchain database is supposed to get shared over
a peer-to-peer network, and all the data in your Wallet.db database is
supposed to be kept away from others; therefore, having your application
create separate connections to both also increases security. Another
benefit is that portability of either your wallet or your blockchain is just
a simple copy/paste of either blockchain.db or wallet.db from your
current machine to another without the need of any extra code. Of course,
this is not to say that the application can’t be made to work with only a
single database or that this is all you need to keep everything secure.
EXERCISE 3-2
Try creating the next table named WALLET on your own before looking how
we did it.
50
Chapter 3 Database Setup
51
Chapter 3 Database Setup
Our complete database schema for our wallet.db database will look
like Figure 3-11.
52
Chapter 3 Database Setup
You can create a libs folder inside your project folder and save it
there just like we have in our repository. Next Select File and then Project
Structure; then go to the Libraries tab in Project Settings, click the plus
sign to add new project library, choose Java, and select the JAR file we just
downloaded then click OK. Look at Figures 3-13 and 3-14 for visual help.
53
Chapter 3 Database Setup
With the library in place, we can start writing our ECoin class that will
contain our main and init() methods.
54
Chapter 3 Database Setup
class; in other words, our application starts from here. Our next snippet
shows the class imports, class declaration, the main method, and the
start method:
1 package com.company;
2
3 import com.company.Model.Block;
4 import com.company.Model.Transaction;
5 import com.company.Model.Wallet;
6 import com.company.ServiceData.BlockchainData;
7 import com.company.ServiceData.WalletData;
8 import com.company.Threads.MiningThread;
9 import com.company.Threads.PeerClient;
10 import com.company.Threads.PeerServer;
11 import com.company.Threads.UI;
12 import javafx.application.Application;
13 import javafx.stage.Stage;
14
15 import java.security.*;
16 import java.sql.*;
17 import java.time.LocalDateTime;
18
19 public class ECoin extends Application {
20
21 public static void main(String[] args) {
22 launch(args);
23 }
24
25 @Override
26 public void start(Stage primaryStage) throws Exception {
27 new UI().start(primaryStage);
28 new PeerClient().start();
55
Chapter 3 Database Setup
29 new PeerServer(6000).start();
30 new MiningThread().start();
31 }
32
On line 19 we can see that our class extends the Application class.
Application is part of the jaxafx package, and by extending it we show
that we will be running our application as a javafx application. Since the
Application class is actually an abstract class, by extending it we need
to implement the start(Stage s) method before we can run our app.
On lines 26 to 31 you can see our implementation of the start(Stage s)
method. Each line of the method body (lines 27–30) represents a different
thread that will run in parallel in our application. The thread on line 27
will display our UI and execute commands associated with it. The thread
on line 28 will act as a client and query other peers. The thread on line
29 will act as a server and respond to incoming queries from other peers.
The thread on line 30 will run the blockchain verification and consensus
tasks continuously. We will talk about each thread in our future sections
and explain how each thread works in detail. For now the explanation of
what these threads are trying to accomplish is enough and should give you
a nice overview of how our application is structured when it comes to the
topic of what it tries to accomplish.
It’s finally time to talk about our init() method and what we will
accomplish with it. The init() method runs before our application
start(Stage s) method, and its purpose is to check and set up the
necessary prerequisites so that our application can run. In our case, those
prerequisites include the existence of our wallet.db and blockchain.db,
their schemas, and their contents. Let’s look at the following snippet and
start explaining the code:
32
33 @Override
34 public void init() {
56
Chapter 3 Database Setup
35 try {
36 //This creates your wallet if there is none and gives you a
KeyPair.
37 //We will create it in separate db for better security and
ease of portability.
38 Connection walletConnection = DriverManager
39 .getConnection("jdbc:sqlite:C:\\Users\\
spiro\\IdeaProjects\\e-coin\\db\\
wallet.db");
40 Statement walletStatment =
walletConnection.createStatement();
41 walletStatment.executeUpdate("CREATE TABLE IF
NOT EXISTS WALLET ( " +
42 " PRIVATE_KEY BLOB NOT NULL UNIQUE, " +
43 " PUBLIC_KEY BLOB NOT NULL UNIQUE, " +
44 " PRIMARY KEY (PRIVATE_KEY, PUBLIC_KEY)" +
45 ") "
46 );
47 ResultSet resultSet = walletStatment.execute
Query(" SELECT * FROM WALLET ");
48 if (!resultSet.next()) {
49 Wallet newWallet = new Wallet();
50 byte[] pubBlob =
newWallet.getPublicKey().getEncoded();
51 byte[] prvBlob =
newWallet.getPrivateKey().getEncoded();
52 PreparedStatement pstmt = walletConnection
53 .prepareStatement("INSERT INTO
WALLET(PRIVATE_KEY,
PUBLIC_KEY) " +
54 " VALUES (?,?) ");
57
Chapter 3 Database Setup
55 pstmt.setBytes(1, prvBlob);
56 pstmt.setBytes(2, pubBlob);
57 pstmt.executeUpdate();
58 }
59 resultSet.close();
60 walletStatment.close();
61 walletConnection.close();
62 WalletData.getInstance().loadWallet();
63
58
Chapter 3 Database Setup
object is empty, and if it is, then lines 49–57 create a new wallet and
export its keys to our Wallet table. Let’s see what this set of functionalities
allows us to do. We can run the app without any wallet.db, and it will
automatically set up the database for it and create a new wallet for us. We
can port our wallet to another machine by simply copying our wallet.db
and replacing the wallet.db file on that machine. Our init() method will
recognize that a keypair is present and won’t try to overwrite it. The last
thing to note is line 65. Here we will load the contents from our wallet.db
into our WalletData singleton class. This will store our wallet data into the
app memory and make it more readily accessible throughout the rest of
our app. We will talk more about the WalletData class in our next chapter.
Now let’s look at the next part of our init() method in the following
snippet:
59
Chapter 3 Database Setup
60
Chapter 3 Database Setup
61
Chapter 3 Database Setup
it to the TRANSACTIONS table since we still are not sure if it exists in the
database. Let’s look at the following code snippet where we handle the
TRANSACTIONS table:
107
108 blockchainStmt.executeUpdate("CREATE
TABLE IF NOT
EXISTS TRANSACTIONS ( " +
109 " ID INTEGER NOT NULL UNIQUE, " +
110 " \"FROM\" BLOB, " +
111 " \"TO\" BLOB, " +
112 " LEDGER_ID INTEGER, " +
113 " VALUE INTEGER, " +
114 " SIGNATURE BLOB UNIQUE, " +
115 " CREATED_ON TEXT, " +
116 " PRIMARY KEY(ID AUTOINCREMENT) " +
117 ")"
118 );
119 if (initBlockRewardTransaction != null) {
120 BlockchainData.getInstance()
.addTransaction(initBlockRewardTransaction,
true);
121 BlockchainData.getInstance()
.addTransactionState(initBlockReward
Transaction);
122 }
123 blockchainStmt.close();
124 blockchainConnection.close();
125 } catch (SQLException | NoSuchAlgorithmException |
InvalidKeyException | SignatureException e) {
126 System.out.println("db failed: " +
e.getMessage());
62
Chapter 3 Database Setup
3.6 Summary
In this chapter, we covered the creation of two different databases that
will support our application. We also created our init() method with
functionality that allows easy portability and creation of wallets. Also, we
made sure that our application is capable of creating the first block in our
blockchain when no blockchain is present. This is a small recap of what
concepts we covered so far:
• Creation of databases and table schemas using the
database browser program
• Creation of our application’s main class
• Creation of our start(Stage s) method
• Setting up the SQLite driver in Java and using it to
create and query databases using Java and SQL
• Creating business logic for our app’s init() method by
using the SQLite driver and Java
63
CHAPTER 4
Building the UI
This chapter will cover how to create our UI. Using the UI we will be
able to check our coin balance, display our public address so that we can
share it with others, look at recent transactions, and send coins to others.
For this purpose, we will use Scene Builder, a GUI tool that will help us
create our front end. For our controller classes, we will be using Java and
JavaFX. Finally, we will explain how to create our separate UI thread that
will listen for our input.
66
Chapter 4 Building the UI
4.2.1 MainWindow.fxml
Let’s create our MainWindow.fxml file. First start Scene Builder and choose
an empty scene, as shown in Figure 4-3.
67
Chapter 4 Building the UI
Once you do this, your next screen should look like Figure 4-4.
69
Chapter 4 Building the UI
70
Chapter 4 Building the UI
Notice the BorderPane element in the Hierarchy tab together with its
subelements. Next let’s drag a MenuBar from the Controls tab and drop it
into the TOP subelement of our BorderPane, as shown in Figure 4-7.
71
Chapter 4 Building the UI
If you click the + next to the MenuBar element, you will notice that it
comes with three menu subelements named File, Edit, and Help. Since
we are only going to use the File subelement, let’s delete the Edit and Help
subelements by right-clicking them and selecting Delete from the drop-
down menu, as shown in Figure 4-8.
72
Chapter 4 Building the UI
Now let’s expand the File submenu. Here you will notice that we have
another nested element called MenuItem named Close. Let’s add another
MenuItem element and name it Make Transaction. To do this, first let’s
open the Menu tab and drag another MenuItem element and drop it right
above the Close MenuItem, as shown on Figure 4-9.
73
Chapter 4 Building the UI
As you are dragging the MenuItem element, notice the orange line
that will help you visualize where the item will be placed once you drop it.
You will notice that this MenuItem gets named by default as Unspecified
Action. Let’s select this element by clicking it in the Hierarchy tab and then
expand the Properties tab. You will notice a property called text that has
a text value named Unspecified Action. Let’s change this value to Make
Transaction, which effectively renames our element. The final result
should look like Figure 4-10.
74
Chapter 4 Building the UI
75
Chapter 4 Building the UI
EXERCISE 4-1
Rename the Menu element named File to Menu and the MenuItem Close
to Exit.
We are finished with our MenuBar and its subelements for now, so
let’s move on. Next add a TableView element from the Controls tab onto
the BorderPane’s CENTER subelement. Once you expand the TableView
element, you should find two nested TableColumn elements named C1
and C2. Drag TableColumn C2 into TableColumn C1. This will make
TableColumn C2 a nested element of TableColumn C1. If you followed
the instructions correctly, your hierarchy structure should look like
Figure 4-11.
76
Chapter 4 Building the UI
Let’s add four more TableColumn elements from the Controls tab as
nested elements of TableColumn C1. In this table view that we are creating
we would like to be able to display our current block transactions with the
associated details, so we should rename our table columns accordingly
such as C1 to Current Block Transactions and then all the nested table
columns in order like this: From, To, Value, Signature, and Created On. If
you have done things correctly, your screen should look like Figure 4-12.
77
Chapter 4 Building the UI
You have most likely noticed that the width of our table columns
doesn’t cover the whole area of our screen. To fix the width of our table
columns, select the TableView element, go into its Properties tab to find
the Column Resize Policy attribute, and select constrained-resize, as
shown in Figure 4-13.
Our next task is to add some elements that will allow us to display
our coins balance, our public key/wallet address, and a button that will
refresh our UI and retrieve the latest information regarding transactions
and our balance. We will do this by dragging another BorderPane from the
Containers tab inside the BOTTOM section of our existing BorderPane.
This new BorderPane will come with its own existing TOP, LEFT, CENTER,
RIGHT, and BOTTOM sections. In other words, this new BorderPane
subdivides the bottom section of the first BorderPane into five new
sections, as shown in Figure 4-14.
78
Chapter 4 Building the UI
79
Chapter 4 Building the UI
EXERCISE 4-2
Rename the Label element named Label to Your Balance: and reposition it
and the TextField element so they look better visually.
80
Chapter 4 Building the UI
Next let’s drop a Label and TextArea element from the Controls tab
into our CENTER section AnchorPane and a Button from the same tab into
our RIGHT section AnchorPane. Your hierarchy structure should look like
Figure 4-17.
EXERCISE 4-3
Rename the Label element named Label to Your Address / Public Key:,
rename the Button from Button to Refresh, and then reposition them to look
better visually.
In Figure 4-18 you can see the complete UX/UI design of our scene
with its expanded structure hierarchy. This is a good figure to review and
check if you have missed something in your own scene.
81
Chapter 4 Building the UI
If you open the MainWindow.fxml file from IntelliJ now, you will notice
that it has been filled up with the appropriate FXML code by Scene Builder
automatically. Keep in mind that you need to save your changes in Scene
Builder before the code in the FXML file gets generated. Also make sure to
save the latest changes in Scene Builder and exit if you intend to change
the FXML file manually; otherwise, you might desynchronize the work
from Scene Builder and your local file and lose some of the changes.
Until this point we have worked only on the visual design of our scene.
We have all our elements positioned the way we want them, but what we
are missing now are references to our back-end methods and fields so that
we can use them. First let’s add the file path of our future controller class
for this scene. This will create a link in the application between our view
and its controller. In other words, the application will try to find and run/
display any methods or fields that we will reference in the view from its
corresponding controller class. To add the file path of our controller class,
open the Controller tab in the bottom-left side of your screen and enter it
in the Controller class text field, as shown in Figure 4-19.
82
Chapter 4 Building the UI
Now let’s add a reference to the method that our refresh button should
call once we click it. To do that, select our refresh button, expand its Code
tab from the bottom-right side of the screen, and enter the method name
in the On Action text field, which will be refresh, as shown in Figure 4-20.
83
Chapter 4 Building the UI
Next let’s add reference to our public key field so that we can display it
in our TextArea. Let’s select the TextArea and in the Code tab find the fx:id
field; name it publicKey, as shown on Figure 4-21.
We’ll name our TextField for our coins balance eCoins, as shown in
Figure 4-22.
84
Chapter 4 Building the UI
Now that we’ve shown you how to add controller references, you
should be able to finish adding the rest on your own.
EXERCISE 4-4
Add references to the fx:id field for our first BorderPane as borderPane, for
our TableView as tableview, for our From TableColum as from, for our To
TableColumn as to, for our Value TableColumn as value, for our Signature
TableColumn as signature, for our Created On TableColumn as timestamp.
85
Chapter 4 Building the UI
EXERCISE 4-5
Add references to the On Action field for our Make Transaction MenuItem as
toNewTransactionController, and for the Exit MenuItem as handleExit.
86
Chapter 4 Building the UI
16 prefHeight="500.0" prefWidth="800.0"
xmlns="http://javafx.com/javafx/15.0.1"
xmlns:fx="http://javafx.com/fxml/1"
17 fx:controller="com.company.Controller.MainWindow
Controller">
18 <top>
19 <MenuBar prefHeight="25.0" prefWidth="800.0"
BorderPane.alignment="CENTER">
20 <menus>
21 <Menu mnemonicParsing="false" text="Menu">
22 <items>
23 <MenuItem mnemonicParsing="false"
onAction="#toNewTransactionController"
text="Make Transaction" />
24 <MenuItem mnemonicParsing="false"
onAction="#handleExit" text="Exit" />
25 </items>
26 </Menu>
27 </menus>
28 </MenuBar>
29 </top>
30 <center>
31 <TableView fx:id="tableview" prefHeight="406.0"
prefWidth="800.0" BorderPane.alignment="CENTER">
32 <columns>
33 <TableColumn prefWidth="75.0" text="Current Block
Transactions">
34 <columns>
35 <TableColumn fx:id="from" prefWidth="160"
text="From" />
87
Chapter 4 Building the UI
88
Chapter 4 Building the UI
56 </AnchorPane>
57 </center>
58 <left>
59 <AnchorPane prefHeight="56.0" prefWidth="136.0"
BorderPane.alignment="CENTER">
60 <children>
61 <Label layoutX="6.0" layoutY="6.0"
prefHeight="17.0" prefWidth="84.0"
text="Your Balance:" />
62 <TextField fx:id="eCoins" editable="false"
layoutX="6.0" layoutY="23.0"
prefHeight="25.0" prefWidth="125.0" />
63 </children>
64 </AnchorPane>
65 </left>
66 <right>
67 <AnchorPane prefHeight="200.0" prefWidth="200.0"
BorderPane.alignment="CENTER">
68 <children>
69 <Button layoutX="113.0" layoutY="20.0"
mnemonicParsing="false"
nodeOrientation="LEFT_TO_RIGHT"
onAction="#refresh" prefHeight="30.0"
70 prefWidth="67.0" text="Refresh"
textAlignment="CENTER" />
71 </children>
72 </AnchorPane>
73 </right>
74 </BorderPane>
75 </bottom>
76 </BorderPane>
77
89
Chapter 4 Building the UI
With the exercises complete, we now have our main window scene
completely finished. If you open the file in IntelliJ, it will show you some
errors since the references we added don’t exist yet, but we are about to fix
that in our following sections.
4.2.2 AddNewTransactionWindow.fxml
Since by now we have explained everything you need to know to create
your own view in Scene Builder, it’s time for you to create the scene from
this section by performing the following exercises.
EXERCISE 4-6
Open a new file in Scene Builder and save it in the same folder structure as
our previous scene. Name the file AddNewTransactionWindow.fxml.
EXERCISE 4-7
Create your scene with the hierarchy structure and appearance shown in
Figure 4-23. Add the controller class path as com.company.Controller.
AddNewTransactionController.
90
Chapter 4 Building the UI
EXERCISE 4-8
EXERCISE 4-9
Add references to the fx:id field for our To Address TextField as toAddress and
for our Amount TextField as value.
EXERCISE 4-10
Add references to the On Action field for our Send Transaction Button as
createNewTransaction.
91
Chapter 4 Building the UI
92
Chapter 4 Building the UI
19 <font>
20 <Font size="18.0" />
21 </font>
22 </Label>
23 <Button layoutX="447.0" layoutY="189.0"
mnemonicParsing="false"
onAction="#createNewTransaction"
text="Send Transaction" />
24 </children>
25 </AnchorPane>
26 </content>
27 </DialogPane>
28
93
Chapter 4 Building the UI
4.3.1 MainWindowController
Let’s observe our imports for the MainWindowController class in the
following code snippet:
1 package com.company.Controller;
2
3 import com.company.Model.Transaction;
4 import com.company.ServiceData.BlockchainData;
5 import com.company.ServiceData.WalletData;
6 import javafx.application.Platform;
7 import javafx.fxml.FXML;
8 import javafx.fxml.FXMLLoader;
9 import javafx.scene.control.*;
94
Chapter 4 Building the UI
10 import javafx.scene.control.cell.PropertyValueFactory;
11 import javafx.scene.layout.BorderPane;
12
13 import java.io.IOException;
14 import java.util.Base64;
15 import java.util.Optional;
16
Next let’s move on to the class fields as shown in the following snippet
and explain them:
95
Chapter 4 Building the UI
96
Chapter 4 Building the UI
52 tableview.setItems(BlockchainData
.getInstance().getTransactionLedgerFX());
53 tableview.getSelectionModel().select(0);
54 }
55
56 @FXML
57 public void toNewTransactionController() {
58 Dialog<ButtonType> newTransactionController =
new Dialog<>();
59 newTransactionController.initOwner(borderPane
.getScene().getWindow());
60 FXMLLoader fxmlLoader = new FXMLLoader();
61 fxmlLoader.setLocation(getClass().getResource(
"../View/AddNewTransactionWindow.fxml"));
97
Chapter 4 Building the UI
62 try {
63 newTransactionController.getDialogPane()
.setContent(fxmlLoader.load());
64 } catch (IOException e) {
65 System.out.println("Cant load dialog");
66 e.printStackTrace();
67 return;
68 }
69 newTransactionController.getDialogPane()
.getButtonTypes().add(ButtonType.FINISH);
70 Optional<ButtonType> result =
newTransactionController.showAndWait();
71 if (result.isPresent() ) {
72 tableview.setItems(BlockchainData.getInstance()
.getTransactionLedgerFX());
73 eCoins.setText(BlockchainData.getInstance()
.getWalletBallanceFX());
74 }
75 }
76
77 @FXML
78 public void refresh() {
79 tableview.setItems(BlockchainData.getInstance()
.getTransactionLedgerFX());
80 tableview.getSelectionModel().select(0);
81 eCoins.setText(BlockchainData.getInstance()
.getWalletBallanceFX());
82 }
83
84 @FXML
85 public void handleExit() {
98
Chapter 4 Building the UI
86 BlockchainData.getInstance().setExit(true);
87 Platform.exit();
88 }
89 }
90
You will notice that, just like our fields, all of our methods are methods
we referenced in our associated FXML file, which means we need to
annotate them with the @FXML tag also.
99
Chapter 4 Building the UI
4.3.2 AddNewTransactionController
Let’s look at our second controller’s code in
AddNewTransactionController.fxml, as shown in the following snippet:
1 package com.company.Controller;
2
3 import com.company.Model.Transaction;
4 import com.company.ServiceData.BlockchainData;
5 import com.company.ServiceData.WalletData;
6 import javafx.fxml.FXML;
7 import javafx.scene.control.TextField;
8
9 import java.security.GeneralSecurityException;
10 import java.security.Signature;
11 import java.util.Base64;
12
13 public class AddNewTransactionController {
14
15 @FXML
16 private TextField toAddress;
17 @FXML
18 private TextField value;
19
20 @FXML
21 public void createNewTransaction()
throws GeneralSecurityException {
22 Base64.Decoder decoder = Base64.getDecoder();
23 Signature signing =
Signature.getInstance("SHA256withDSA");
24 Integer ledgerId = BlockchainData.getInstance()
.getTransactionLedgerFX().get(0).getLedgerId();
100
Chapter 4 Building the UI
101
Chapter 4 Building the UI
4.4 Summary
In this chapter, we covered how to create our front-end UI with its
corresponding controllers. We learned how to use Scene Builder to create
quick mock-ups of our scenes and create references for the back end. We
learned how to create controller classes and how to design them properly.
This is a small recap of what concepts we covered so far:
102
CHAPTER 5
This chapter will cover creating our network layer; we will explain the
creation and functionality of each of our multiple threads and how they
fit into our application. Here we will explain the need for each thread
and how some of them are used to create a peer-to-peer network where
we transfer blockchain data and reach block consensus. In this chapter,
we will be using the standard Java and Javafx libraries, which will help
you learn every detail of the implementation. Before starting with this
chapter, if you are unfamiliar with the topic of Java threads, you can find a
quick tutorial that will help you grasp the basic concepts at https://www.
javatpoint.com/thread-concept-in-java.
1 package com.company.Threads;
2
3 import javafx.application.Application;
4 import javafx.fxml.FXMLLoader;
5 import javafx.scene.Parent;
6 import javafx.scene.Scene;
7 import javafx.stage.Stage;
8
104
Chapter 5 Setting Up the Network and Multithreading
9 import java.io.IOException;
10
11 public class UI extends Application {
12
13 @Override
14 public void start(Stage stage) {
15 Parent root = null;
16 try {
17 root = FXMLLoader.load(getClass()
.getResource("../View/MainWindow.fxml"));
18 } catch (IOException e) {
19 e.printStackTrace();
20 }
21 stage.setTitle("E-Coin");
22 stage.setScene(new Scene(root, 900, 700));
23 stage.show();
24 }
25 }
105
Chapter 5 Setting Up the Network and Multithreading
106
Chapter 5 Setting Up the Network and Multithreading
1 package com.company.Threads;
2
3 import com.company.ServiceData.BlockchainData;
4
5 import java.time.LocalDateTime;
6 import java.time.ZoneOffset;
7
8 public class MiningThread extends Thread {
9
10 @Override
11 public void run() {
12 while (true) {
13 long lastMinedBlock = LocalDateTime.parse(
BlockchainData.getInstance()
14 .getCurrentBlockChain().getLast()
.getTimeStamp())
.toEpochSecond(ZoneOffset.UTC);
107
Chapter 5 Setting Up the Network and Multithreading
108
Chapter 5 Setting Up the Network and Multithreading
30 BlockchainData.getInstance().setMiningPoints(
BlockchainData.getInstance()
.getMiningPoints() + 2);
31 } catch (InterruptedException e) {
32 e.printStackTrace();
33 }
34 }
35 }
36 }
Our MiningThread class extends the Thread class from the java.
lang package. We are overriding the run() method to set the logic that
this thread will run on. On line 12 we are setting a while loop with a true
parameter, which means we will be repeating the actions of this method
as long as our application is running. On lines 13 and 14 we are setting a
field with the value of the date when we mined our last block in seconds.
For this purpose we are using the BlockchainData class, which is part of
our service layer that we explain how to create in our next chapter. In order
not to burden the code, our application won’t handle different time zones;
therefore, we will be using the standard ZoneOffset.UTC constant. Our first
if statement on line 15 checks to see if more than 65 seconds (the timeout
interval we have set up in our BlockchainData class that we will cover in
Chapter 6) have passed since our last mined block. This will mean that our
blockchain is too old, so there is no point in trying to mine it until we get
an up-to-date version. Therefore, in this case, we print the message on line
16 in the console. If our first if statement is false, then on line 17 our else
if statement checks to see if less than 60 seconds (the mining interval
also covered in Chapter 6) have passed since the last mined block. If this
is true, then we will print to the console the time remaining until we get to
60 seconds. If we reach our else statement on line 20, that means we are
in that time window between 60 and 65 seconds since our last mined block
and it’s time to mine (create) another. Line 22 uses the service layer again
109
Chapter 5 Setting Up the Network and Multithreading
to call a method to mine another block, and line 23 is used to print our new
wallet balance to the console by using the service layer again. On lines 25
and 26, regardless of the outcome of the previous control flow statements,
we print out the time of our last mined block to the console just for clarity.
Since there is no point in this thread to loop as fast as it can on line 28, we
call the thread to sleep for two seconds, which effectively makes this thread
repeat every two seconds. We will set the rate of acquiring mining points
to be one point per second; therefore, on line 29 we will add two mining
points for each two seconds we wait.
EXERCISE 5-1
Try to come up with an even more efficient way of looping this thread.
110
Chapter 5 Setting Up the Network and Multithreading
For the purposes of our book, we won’t be exposing our network to the
Internet due to security concerns; instead, we will set different local ports
to act as if they are different peers on our network.
1 package com.company.Threads;
2
3 import com.company.Model.Block;
4 import com.company.ServiceData.BlockchainData;
5
6 import java.io.IOException;
7 import java.io.ObjectInputStream;
8 import java.io.ObjectOutputStream;
9 import java.net.Socket;
10 import java.net.SocketTimeoutException;
11 import java.util.LinkedList;
12 import java.util.Queue;
13 import java.util.concurrent.ConcurrentLinkedQueue;
14
15 public class PeerClient extends Thread {
16
private Queue<Integer> queue = new ConcurrentLinkedQueue<>();
17
18
19 public PeerClient() {
20 this.queue.add(6001);
21 this.queue.add(6002);
22 }
23
111
Chapter 5 Setting Up the Network and Multithreading
Before we override the run() method, we have a queue field that will
be populated with the port numbers of the peers we want to connect to. As
you can see on lines 19–21, we use the constructor to populate our queue
with a few predetermined port numbers. We mentioned before that we
will be using our local ports as peers. This means that instead of changing
the IP address to connect to different peers, we will be using our local IP
address 127.0.0.1 and instead change the local ports to which we connect.
EXERCISE 5-2
Try to use what we have learned so far to create a database table to store the
list of ports instead of having them hard-coded here.
For extra points: Try using the init() method from our ECoin class to set
everything up programmatically instead of manually doing it with the SQLite
browser.
Let’s move on to the next code snippet where we explore the run()
method of this thread:
24 @Override
25 public void run() {
26 while (true) {
27 try (Socket socket = new Socket("127.0.0.1",
queue.peek())) {
28 System.out.println("Sending blockchain object on
port: " + queue.peek());
29 queue.add(queue.poll());
30 socket.setSoTimeout(5000);
31
112
Chapter 5 Setting Up the Network and Multithreading
32 ObjectOutputStream objectOutput =
new ObjectOutputStream(
socket.getOutputStream());
33 ObjectInputStream objectInput =
new ObjectInputStream(
socket.getInputStream());
34
35 LinkedList<Block> blockChain = BlockchainData
.getInstance().getCurrentBlockChain();
36 objectOutput.writeObject(blockChain);
37
38 LinkedList<Block> returnedBlockchain =
(LinkedList<Block>) objectInput
.readObject();
39 System.out.println(" RETURNED BC LedgerId = " +
returnedBlockchain.getLast()
.getLedgerId() +
40 " Size= " + returnedBlockchain.getLast()
.getTransactionLedger().size());
41 BlockchainData.getInstance()
.getBlockchainConsensus(
returnedBlockchain);
42 Thread.sleep(2000);
43
44 } catch (SocketTimeoutException e) {
45 System.out.println("The socket timed out");
46 queue.add(queue.poll());
47 } catch (IOException e) {
48 System.out.println("Client Error: " +
e.getMessage() + " -- Error on port: " +
queue.peek());
49 queue.add(queue.poll());
113
Chapter 5 Setting Up the Network and Multithreading
} catch (InterruptedException |
50
ClassNotFoundException e) {
51 e.printStackTrace();
52 queue.add(queue.poll());
53 }
54 }
55 }
56 }
114
Chapter 5 Setting Up the Network and Multithreading
We will go into detail and explain our consensus algorithm and the
steps we undertake in validating blockchains in the next chapter when
we talk about the service layer. On line 40 we make our thread sleep for
two seconds before looping again and attempting to connect to the next
peer. Lastly in this method it’s also important to look at our catch blocks.
So far we send the port number to the back of the queue only after we
successfully connect to the peer. However, in case we fail to connect to this
peer, we also include statements in our catch blocks that send the current
port number to the back of the queue. This makes sure we don’t get stuck
trying to connect to a single peer.
1 package com.company.Threads;
2
3
4 import java.io.IOException;
5 import java.net.ServerSocket;
6
7
115
Chapter 5 Setting Up the Network and Multithreading
5.4 PeerRequestThread
The PeerRequestThread handles each specific request that arrives at our
peer. Let’s look at the following code snippet and explain how it works:
116
Chapter 5 Setting Up the Network and Multithreading
1 package com.company.Threads;
2
3
4 import com.company.Model.Block;
5 import com.company.ServiceData.BlockchainData;
6
7 import java.io.IOException;
8 import java.io.ObjectInputStream;
9 import java.io.ObjectOutputStream;
10 import java.net.Socket;
11 import java.util.LinkedList;
12
13
14 public class PeerRequestThread extends Thread {
15
16 private Socket socket;
17
18 public PeerRequestThread(Socket socket) {
19 this.socket = socket;
20 }
21
22 @Override
23 public void run() {
24 try {
25
26 ObjectOutputStream objectOutput = new
ObjectOutputStream(socket
.getOutputStream());
27 ObjectInputStream objectInput = new
ObjectInputStream(socket
.getInputStream());
28
117
Chapter 5 Setting Up the Network and Multithreading
118
Chapter 5 Setting Up the Network and Multithreading
5.5 Summary
In this chapter, we covered the creation of multiple threads that will allow
our application to run responsively and uninterrupted. We learned how to
use sockets to send and receive requests to other peers. We learned what
elements are required to form a peer-to-peer network. We learned how
to run the UI of our application, and we learned how to set up a mining
subroutine. This is a small recap of what concepts we covered so far:
• Creating threads
119
CHAPTER 6
Service Layer
This chapter will cover creating our service layer that will provide
the functionality to everything covered in the previous chapters. In
this chapter, we will finally implement all the methods that we briefly
mentioned in our previous chapters. This chapter will most likely be the
most complex one to grasp so far since it will try to tie together all the
separate elements of our app. We recommend a solid familiarity with the
previous chapters before continuing with this one.
6.1 WalletData
In this section, we will explain our WalletData class. It is used to store our
wallet while our application is running and to make it available to every
other part of our application. Any additional functionality of the wallet
should be implemented through this class.
For now let’s explain the following code:
1 package com.company.ServiceData;
2
3 import com.company.Model.Wallet;
4
5 import java.security.KeyFactory;
6 import java.security.NoSuchAlgorithmException;
7 import java.security.PrivateKey;
8 import java.security.PublicKey;
9 import java.security.spec.InvalidKeySpecException;
10 import java.security.spec.PKCS8EncodedKeySpec;
11 import java.security.spec.X509EncodedKeySpec;
12 import java.sql.*;
13
14 public class WalletData {
15
122
Chapter 6 Service Layer
First we start with the regular class imports. Then, since we will be
storing our wallet in this class, we have a field of the Wallet class on line 16.
Now because we want the same wallet to be available throughout the
application we will create this class as a singleton class. Singleton is a basic
design pattern, and it means that this class will be present only as a single
object in memory and that we won’t be able to instantiate any additional
objects. This means there will be no chance of wallet duplicates all over our
application. We make our class a singleton class by having a static field of
our WalletData class, as shown on line 18, and then initiating it statically,
as shown on lines 20–22. The last thing to mention is the getInstance()
method. Instead of trying to instantiate new objects, we use this method to
gain access to the class. Let’s look at our next code snippet and explain the
rest of the code contained in this class:
27
28 //This will load your wallet from the database.
29 public void loadWallet() throws SQLException,
NoSuchAlgorithmException, InvalidKeySpecException {
123
Chapter 6 Service Layer
124
Chapter 6 Service Layer
Lines 30–34 are regular JDBC code for establishing a connection to the
database where we keep our wallet keys and executing a select query to
retrieve them. On line 35, we initiate our KeyFactory helper class. It’s a class
from the java.security package, and it will help us create the PublicKey
and PrivateKey objects. On lines 36 and 37 we declare the PublicKey and
PrivateKey objects. On lines 39 and 40 we use the KeyFactory helper class
to generate a public key by inserting the byte array that we retrieve from
the database. However, since the generatePublic(KeySpec ks) accepts
a KeySpec object instead of byte array, we have to create a KeySpec object
by constructing it from the X509EncodedKeySpec constructor. This class’s
parent class implements the KeySpec interface, which means we can use it
to generate our public key, but also this class’s constructor accepts our byte
array, as shown in Figure 6-1.
Lines 41 and 42 generate the private key for us using a similar method
and the PKCS8EncodedKeySpec class. It also accepts a byte array and builds
an object that implements the KeySpec interface, as shown in Figure 6-2.
125
Chapter 6 Service Layer
6.2 BlockchainData
This is the biggest and most complex class in our application. Here we can
find all the functionality regarding our blockchain. Let’s start by showing
all the imports in the next code snippet for reference:
1 package com.company.ServiceData;
2
3 import com.company.Model.Block;
4 import com.company.Model.Transaction;
5 import com.company.Model.allet;
6 import javafx.collections.FXCollections;
7 import javafx.collections.ObservableList;
8 import sun.security.provider.DSAPublicKeyImpl;
9
10 import java.security.*;
11 import java.sql.*;
12 import java.time.LocalDateTime;
13 import java.time.ZoneOffset;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Comparator;
17 import java.util.LinkedList;
18
Next let’s go over the fields used in this class, as shown in our next code
snippet:
126
Chapter 6 Service Layer
18
19 public class BlockchainData {
20
21 private ObservableList<Transaction> newBlockTransactionsFX;
22 private ObservableList<Transaction> newBlockTransactions;
23 private LinkedList<Block> currentBlockChain =
new LinkedList<>();
24 private Block latestBlock;
25 private boolean exit = false;
26 private int miningPoints;
27 private static final int TIMEOUT_INTERVAL = 65;
28 private static final int MINING_INTERVAL = 60;
29 //helper class.
30 private Signature signing = Signature
.getInstance("SHA256withDSA");
31
32 //singleton class
33 private static BlockchainData instance;
34
127
Chapter 6 Service Layer
34
35 static {
36 try {
37 instance = new BlockchainData();
38 } catch (NoSuchAlgorithmException e) {
39 e.printStackTrace();
40 }
41 }
42
43 public BlockchainData() throws NoSuchAlgorithmException {
44 newBlockTransactions = FXCollections
.observableArrayList();
45 newBlockTransactionsFX = FXCollections
.observableArrayList();
46 }
47
48 public static BlockchainData getInstance() {
49 return instance;
50 }
51
128
Chapter 6 Service Layer
51
52 Comparator<Transaction> transactionComparator = Comparator
.comparing(Transaction::getTimestamp);
53 public ObservableList<Transaction>
getTransactionLedgerFX() {
54 newBlockTransactionsFX.clear();
55 newBlockTransactions.sort(transactionComparator);
56 newBlockTransactionsFX.addAll(newBlockTransactions);
57 return FXCollections.observableArrayList(
newBlockTransactionsFX);
58 }
59
129
Chapter 6 Service Layer
Let’s start with the getBalance method on line 65 since it’s used in the
method on line 60. This method goes through a given blockchain and a
current ledger with potential transactions for the new block and finds the
balance for the given public address. This method is useful not only for
displaying the current balance but also for verifying if a certain address has
130
Chapter 6 Service Layer
enough coins before sending them. On lines 65 and 66 we see the input
parameters we just mentioned. On lines 68 to 77 we go through each block
of the blockchain, and for each transaction we check whether the given
address is sending or receiving funds. We also adjust the price accordingly.
To prevent double spending we also need to subtract any funds we
are already trying to send that exist in the current transaction ledger.
Remember that double spending is a situation where the sender tries to
send his total funds multiple times, effectively multiplying his funds out
of thin air. This happens on lines 78–82. If you are wondering why we
don’t count incoming funds until they are put on the blockchain, it’s to
prevent any bugs where an invalid incoming transaction is created just so
that we can send the incoming funds before the consensus discovers and
invalidates the incoming transaction. Finally, on line 83, we return the
balance that we calculated.
Now if we go back to our first method on lines 60–63 we can see that
it simply calls our getBalance method and inputs the local blockchain,
the current ledger, and our wallet’s public address. This method is used
to retrieve our own current balance when we need it throughout the
application.
Let’s look at our next method displayed in the next code snippet:
131
Chapter 6 Service Layer
This is the method that actually checks the validity of each transaction
and each block. In this method we can finally see the isVerified methods
from the Block and Transaction classes in action and how they fit in our
application. If you need, feel free to go back to Chapter 2 and recall how
these methods worked. The method itself is simple: it goes through all
of the blocks and all of the transactions within each block and checks if
each one is valid. You can see further in our book that this method is used
to validate our own blockchain as well as validate a blockchain we have
received from other peers.
Now let’s move on to the next code snippet:
132
Chapter 6 Service Layer
133
Chapter 6 Service Layer
Let’s go back to our method and look at lines 106–109. You can notice
that we perform a check calling the getBalance method and check if
the sender has more coins than the transaction’s amount. This of course
makes sure we reject any transaction that tries to cheat and send more
money than the sender actually has. However, also notice the caveat at
the end where we use the reward transaction flag to skip this check if this
is a reward transaction. If we don’t do this, the reward transactions will
otherwise fail this check because the getBalance method will discover
that these coins appear out of thin air. Since this feature is intended in
order to reward miners, the need to skip the getBalance method when it
134
Chapter 6 Service Layer
EXERCISE 6-1
If you are familiar with any ORM solutions like Hibernate, you can try to
implement it here to automatically add the transaction in the database instead
of doing it manually like we do in our addTransaction method.
171
172 private ArrayList<Transaction> loadTransactionLedger(
Integer ledgerID) throws SQLException {
173 ArrayList<Transaction> transactions = new ArrayList<>();
174 try {
175 Connection connection = DriverManager.getConnection
176 ("jdbc:sqlite:C:\\Users\\
spiro\\IdeaProjects\\e-coin\\db\\
blockchain.db");
177 PreparedStatement stmt = connection.prepareStatement
178 (" SELECT * FROM TRANSACTIONS WHERE
LEDGER_ID = ?");
135
Chapter 6 Service Layer
179 stmt.setInt(1, ledgerID);
180 ResultSet resultSet = stmt.executeQuery();
181 while (resultSet.next()) {
182 transactions.add(new Transaction(
183 resultSet.getBytes("FROM"),
184 resultSet.getBytes("TO"),
185 resultSet.getInt("VALUE"),
186 resultSet.getBytes("SIGNATURE"),
187 resultSet.getInt("LEDGER_ID"),
188 resultSet.getString("CREATED_ON")
189 ));
190 }
191 resultSet.close();
192 stmt.close();
193 connection.close();
194 } catch (SQLException e) {
195 e.printStackTrace();
196 }
197 return transactions;
198 }
199
136
Chapter 6 Service Layer
137
Chapter 6 Service Layer
Lines 137–140 are pretty standard JDBC code. We set up the connection
to our database and create and execute our query to select everything from
the BLOCKCHAIN table. Next we cycle through the result set creating a new
Block object and adding it to the currentBlockChan field from this class.
One thing to note is line 150 where we use the loadTransactionLedger
method with the current ledger ID from the result set to retrieve the list of
transactions for this block and include it in the Block object’s constructor.
Let’s move on to the second part of this method:
153
154 latestBlock = currentBlockChain.getLast();
155 Transaction transaction = new Transaction(
new Wallet(),
156 WalletData.getInstance().getWallet()
.getPublicKey().getEncoded(),
157 100, latestBlock.getLedgerId() + 1,
signing);
158 newBlockTransactions.clear();
159 newBlockTransactions.add(transaction);
160 verifyBlockChain(currentBlockChain);
161 resultSet.close();
162 stmt.close();
163 connection.close();
} catch (SQLException | NoSuchAlgorithmException e) {
164
165 System.out.println("Problem with DB: " +
e.getMessage());
166 e.printStackTrace();
167 } catch (GeneralSecurityException e) {
168 e.printStackTrace();
169 }
170 }
171
138
Chapter 6 Service Layer
In this part of the method, we will update the state of the application
so that it works properly from this point onward. To achieve this, we will
need to change our latestBlock and newBlockTransactions fields. On
line 154 we set up the lastestBlock field by getting the last block from
our currentBlockChain list, which got filled up in the first part of this
method. Next, to set up our newBlocksTransactions list first, we create a
new reward transaction object on lines 155–157. Then we clear the list and
add the new transaction we just created. This is the reward transaction
for our future block. The last important line is line 160, where we can see
the verifyBlockChain method on the currentBlockChain we just loaded
from our database. This is an important step since it covers cases when we
import someone else’s database and we need to check its validity.
It’s time to go over the methods that constitute the mining
functionality. In our next code snippet, let’s look briefly at the
mineBlock() method.
Let’s first recall that this is the method we call in the MiningThread
class when it’s time to mine new block. This method simply calls the
methods finalizeBlock and addBlock. The finalizeBlock method
139
Chapter 6 Service Layer
performs the necessary steps to finish up the latest block and adds it to
our currentBlockChain list. Different miners can have different latest
(nonfinal) blocks with different transactions in them. The addBlock
method simply adds the block to the database.
Let’s start explaining the finalizeBlock method first, as shown in the
following code snippet:
209
210 private void finalizeBlock(Wallet minersWallet) throws
GeneralSecurityException, SQLException {
211 latestBlock = new Block(BlockchainData
.getInstance().currentBlockChain);
212 latestBlock.setTransactionLedger(new ArrayList<>(
newBlockTransactions));
213 latestBlock.setTimeStamp(LocalDateTime.now()
.toString());
214 latestBlock.setMinedBy(minersWallet.getPublicKey()
.getEncoded());
215 latestBlock.setMiningPoints(miningPoints);
216 signing.initSign(minersWallet.getPrivateKey());
217 signing.update(latestBlock.toString().getBytes());
218 latestBlock.setCurrHash(signing.sign());
219 currentBlockChain.add(latestBlock);
220 miningPoints = 0;
221 //Reward transaction
222 latestBlock.getTransactionLedger()
.sort(transactionComparator);
223 addTransaction(latestBlock.getTransactionLedger()
.get(0), true);
Transaction transaction = new Transaction(new Wallet(),
224
minersWallet.getPublicKey().getEncoded(),
225 100, latestBlock.getLedgerId() + 1, signing);
140
Chapter 6 Service Layer
226 newBlockTransactions.clear();
227 newBlockTransactions.add(transaction);
228 }
229
141
Chapter 6 Service Layer
EXERCISE 6-2
Hint We all know that the block’s creator is the one getting
rewarded. Try tweaking the getBalance method to add an extra 100
coins for each block that the address in question has created. This
will remove the need for reward transaction to be added explicitly.
Now once we have finalized/mined our block, it’s time to add it to the
database as well. For this purpose, we mentioned that we use the addBlock
method. Let’s look at the following code snippet and explain it:
229
230 private void addBlock(Block block) {
231 try {
232 Connection connection = DriverManager.getConnection
233 ("jdbc:sqlite:C:\\Users\\spiro\\
IdeaProjects\\e-coin\\db\\blockchain.db");
234 PreparedStatement pstmt;
142
Chapter 6 Service Layer
235 pstmt = connection.prepareStatement
236 ("INSERT INTO BLOCKCHAIN(PREVIOUS_HASH,
CURRENT_HASH, LEDGER_ID, CREATED_ON," +
237 " CREATED_BY, MINING_POINTS, LUCK)
VALUES (?,?,?,?,?,?,?) ");
238 pstmt.setBytes(1, block.getPrevHash());
239 pstmt.setBytes(2, block.getCurrHash());
240 pstmt.setInt(3, block.getLedgerId());
241 pstmt.setString(4, block.getTimeStamp());
242 pstmt.setBytes(5, block.getMinedBy());
243 pstmt.setInt(6, block.getMiningPoints());
244 pstmt.setDouble(7, block.getLuck());
245 pstmt.executeUpdate();
246 pstmt.close();
247 connection.close();
248 } catch (SQLException e) {
249 System.out.println("Problem with DB: " +
e.getMessage());
250 e.printStackTrace();
251 }
252 }
253
143
Chapter 6 Service Layer
The need for this method arises when we need to replace our own
blockchain with a blockchain we have received from another peer. We will
cover these reasons when we go over the consensus algorithm right after
we explain this method first. Let’s look at the following code snippet:
254
private void replaceBlockchainInDatabase(LinkedList<Block>
receivedBC) {
255 try {
Connection connection = DriverManager.getConnection
256
257 ("jdbc:sqlite:C:\\Users\\spiro\\
IdeaProjects\\e-coin\\db\\blockchain.db");
258 Statement clearDBStatement = connection
.createStatement();
259 clearDBStatement.executeUpdate(" DELETE FROM
BLOCKCHAIN ");
260 clearDBStatement.executeUpdate(" DELETE FROM
TRANSACTIONS ");
261 clearDBStatement.close();
262 connection.close();
263 for (Block block : receivedBC) {
264 addBlock(block);
265 boolean rewardTransaction = true;
266 block.getTransactionLedger()
.sort(transactionComparator);
267 for (Transaction transaction : block
.getTransactionLedger()) {
268
addTransaction(transaction,
rewardTransaction);
269 rewardTransaction = false;
270 }
271 }
144
Chapter 6 Service Layer
EXERCISE 6-3
145
Chapter 6 Service Layer
146
Chapter 6 Service Layer
277
278 public LinkedList<Block> getBlockchainConsensus(
LinkedList<Block> receivedBC) {
279 try {
280 //Verify the validity of the received blockchain.
281 verifyBlockChain(receivedBC);
282 //Check if we have received an identical blockchain.
283 if (!Arrays.equals(receivedBC.getLast()
.getCurrHash(), getCurrentBlockChain().getLast()
.getCurrHash())) {
284 if (checkIfOutdated(receivedBC) != null) {
285 return getCurrentBlockChain();
286 } else {
287 if (checkWhichIsCreatedFirst(receivedBC)
!= null) {
288 return getCurrentBlockChain();
289 } else {
290 if (compareMiningPointsAndLuck(
receivedBC) != null) {
291 return getCurrentBlockChain();
292 }
293 }
294 }
295 // if only the transaction ledgers are different
then combine them.
296 } else if (!receivedBC.getLast()
.getTransactionLedger().equals(
getCurrentBlockChain()
297 .getLast().getTransactionLedger())) {
298 updateTransactionLedgers(receivedBC);
299 System.out.println("Transaction ledgers
updated");
147
Chapter 6 Service Layer
300 return receivedBC;
301 } else {
302
System.out.println("blockchains are identical");
303 }
304 } catch (GeneralSecurityException e) {
305 e.printStackTrace();
306 }
307 return receivedBC;
308 }
309
148
Chapter 6 Service Layer
149
Chapter 6 Service Layer
we expect his blockchain to win the consensus checks with peer 2 and
peer 3. However, all the peers start with only the transactions they have
recorded on their blocks. First peer 1 shares his blockchain with peer
2, and peer 2 accepts peer 1’s last block but also adds his transactions
to the ones peer 2 already has. Next, peer 1 shares his blockchain with
peer 3 and the same thing happens. At this point, both peer 2 and peer 3
contain peer 1’s last block and his transactions. At this point, when peer
2 tries to share its blockchain with peer 3, they will both have the same
current hashes since they both have peer 1’s last block; however, peer 2
is missing peer 3’s transactions and vice versa. This is the point at which
the consensus algorithm reaches line 296 and checks their transaction
ledgers. Since their transaction ledgers won’t be identical, the method
updateTransactionLedgers on line 298 will be called.
QUESTION 6-1
In the previous scenario, what will happen next if peer 3 tries to share his
blockchain with peer 1?
QUESTION 6-2
In the previous scenario, what will happen next if peer 2 tries to share his
blockchain with peer 3 again?
150
Chapter 6 Service Layer
Now let’s circle back and go over the methods we mentioned. First
we start with the checkIfOutdated method, as shown in the following
snippet:
326
327 private LinkedList<Block> checkIfOutdated(
LinkedList<Block> receivedBC) {
328 //Check how old the blockchains are.
329 long lastMinedLocalBlock = LocalDateTime.parse
330 (getCurrentBlockChain().getLast()
.getTimeStamp())
.toEpochSecond(ZoneOffset.UTC);
331 long lastMinedRcvdBlock = LocalDateTime.parse
332 (receivedBC.getLast().getTimeStamp())
.toEpochSecond(ZoneOffset.UTC);
333 //if both are old just do nothing
334 if ((lastMinedLocalBlock + TIMEOUT_INTERVAL) <
LocalDateTime.now().toEpochSecond(
ZoneOffset.UTC) &&
335 (lastMinedRcvdBlock + TIMEOUT_INTERVAL) <
LocalDateTime.now().toEpochSecond(
ZoneOffset.UTC)) {
336 System.out.println("both are old check other
peers");
337 //If your blockchain is old but the received one is
new use the received one
338 } else if ((lastMinedLocalBlock + TIMEOUT_INTERVAL) <
LocalDateTime.now().toEpochSecond(
ZoneOffset.UTC) &&
339 (lastMinedRcvdBlock + TIMEOUT_INTERVAL) >=
LocalDateTime.now().toEpochSecond(
ZoneOffset.UTC)) {
151
Chapter 6 Service Layer
152
Chapter 6 Service Layer
but the one you received is up-to-date. If this is the case, then the lines
341–345 run. This means that the mining points get reset since we were
spending time with an outdated blockchain until now. Then we add the
received blockchain to our database and set the application state to match.
The method returns null in this case as well. Our last case happens when
the check on lines 347 and 348 are true, which means that the receiving
blockchain is old but our local is up-to-date. In this case, we return our
local blockchain.
Our next method is the checkWhichIsCreatedFirst method.
153
Chapter 6 Service Layer
In this method, first we retrieve the creation times of the initial blocks
in seconds, as shown on lines 357–360. On line 361 we check if the received
blockchain’s creation time in seconds is smaller than the one from our
local blockchain. This means that the receiving blockchain got created
first. If this is the case, then lines 363–367 execute, and the null on line 371
gets returned. If the case is the opposite, which means our local blockchain
got created first, then we simply return our local blockchain.
Let’s look at the compareMiningPointsAndLuck method in the
following snippet:
154
Chapter 6 Service Layer
155
Chapter 6 Service Layer
397 setMiningPoints(BlockchainData
.getInstance().getMiningPoints() +
398 getCurrentBlockChain().getLast()
.getMiningPoints());
399 replaceBlockchainInDatabase(receivedBC);
400 setCurrentBlockChain(new LinkedList<>());
401 loadBlockChain();
402 System.out.println("Received
blockchain won!");
403 } else {
404 // remove the reward transaction from their
losing block and transfer
405 // the transactions to our winning block
406 receivedBC.getLast().getTransactionLedger()
.remove(0);
407 for (Transaction transaction :
receivedBC.getLast()
.getTransactionLedger()) {
408 if (!getCurrentBlockChain().getLast()
.getTransactionLedger()
.contains(transaction)) {
409 getCurrentBlockChain().getLast()
.getTransactionLedger()
.add(transaction);
410 addTransaction(transaction, false);
411 }
412 }
413 getCurrentBlockChain().getLast()
.getTransactionLedger()
.sort(transactionComparator);
156
Chapter 6 Service Layer
414 return getCurrentBlockChain();
415 }
416 }
417 return null;
418 }
419
As the comments in the code point out, this method starts by checking
if both blockchains contain the same previous hashes. This means that
they share the same blocks up until the last block. If this check fails, the
null on line 417 is returned. Lines 383–386 check if the winning conditions
for the received blockchain are met. If that is the case, then lines 389–402
get executed. What these lines do is as follows: we remove the reward
transaction from our ledger, we combine the ledgers, we refund our own
mining points, and then we add the winning blockchain with updated
transactons in our database and set up the application state accordingly.
The else on line 403 means that the winning conditions for the received
blockchain were not met, which means our local blockchain won. Lines
406–414 remove the reward transaction from the received blockchain, add
its transactions to our own ledger, and then return our blockchain.
The only method that remains to be explained from the getConsensus
method is the updateTransactionLedgers method. Let’s look at the
following snippet:
157
Chapter 6 Service Layer
158
Chapter 6 Service Layer
we loop through our local blockchain’s last block transactions. On line 320
we check if each transaction is contained in the received blockchain. If it
isn’t, then we add it to the received blockchain.
Lastly in this class we have some simple getters and setters, as shown
in the following code snippet:
419
420 public LinkedList<Block> getCurrentBlockChain() {
421 return currentBlockChain;
422 }
423
424 public void setCurrentBlockChain(LinkedList<Block>
currentBlockChain) {
425 this.currentBlockChain = currentBlockChain;
426 }
427
428 public static int getTimeoutInterval() { return
TIMEOUT_INTERVAL; }
429
430 public static int getMiningInterval() { return
MINING_INTERVAL; }
431
432 public int getMiningPoints() {
433 return miningPoints;
434 }
435
436 public void setMiningPoints(int miningPoints) {
437 this.miningPoints = miningPoints;
438 }
439
159
Chapter 6 Service Layer
6.3 Summary
In this chapter, we covered the complete service layer of our application.
We talked about singleton class design and how it fits our application.
Next we covered methods that allow the functionality of our wallet and
our blockchain. We learned how the functionality of our previous chapters
is achieved by going over the implementation of the methods they use.
One of the most important things we covered is the implementation of our
blockchain consensus.
Here is a small recap of the topics in this chapter:
• Wallet functionality
• Blockchain functionality
160
Chapter 6 Service Layer
161
CHAPTER 7
Extras
This chapter will cover how to set up and run our blockchain application.
We will also talk about topics and ideas for expanding the functionality of
our application and conclude the book.
Once this is done and you have all your copies open in IntelliJ, it’s time
to change some of the hard-coded values to make sure each copy is its own
original peer. First let’s change the PeerServer port, because each peer
needs to have its own port number. See Figure 7-4.
164
Chapter 7 Extras
Next we’ll need to add the socket port numbers of the other peers to
each other’s PeerClient queue inside the PeerClient class constructor, as
shown in Figure 7-5.
Lastly, we’ll need to go over the jdbc connections and change the
file path to correspond to the file path of our copies. Figure 7-6 shows an
example of one.
The rest of these connection file paths can be found in the init()
method of our ECoin class and in several methods in the service layer
classes BlockchainData and WalletData. This last change will make sure
that each peer will have its own databases for storing the blockchain and
its wallet. If you want to export your wallet to another peer, you can simply
copy the wallet.db database from one peer’s folder path to another. Also
165
Chapter 7 Extras
keep in mind that if you delete a wallet.db database and you don’t have a
backup copy, that wallet’s access and coins will forever be lost to you since
you will be losing your key pair. Beyond this you just need to run each
copy from within IntelliJ and you will have yourself a running peer-to-peer
network.
Before running the application, make sure that you have Java SE 8
installed before running the application. If you are not sure what version
you have, you can check it by typing java -version in the console of your
IntelliJ.
Once you have mined some coins, you can actually send them to
another peer by copying the other peer’s address from their UI and using it
in the Add New Transaction window, as represented in Figure 7-7. Observe
that until consensus is reached for the current block, the coins won’t
appear in the receiving account. This prevents double spending because
the consensus algorithm will check for attempts at double spending before
assigning the coins to the other peer’s account.
166
Chapter 7 Extras
Other functionality would be to stop one of the peers, let its blockchain
get old (older than one to two minutes), and then restart it and observe
how everything syncs up. Lastly, you can stop everything and check the
databases with SQLite Developer. This should provide you with additional
insight and confirmation of how the data is stored, and you’ll have a great
overview of the whole blockchain you have just created. Things should
look similar to Figure 7-8 and Figure 7-9.
167
Chapter 7 Extras
168
Chapter 7 Extras
7.3 Conclusion
First I want to thank you for reaching the end of my book. What was
covered in this book by no means exhausts everything that can be said
regarding blockchain technology, but it is my sincere hope that it has
provided you with solid fundamentals and a better theoretical and practical
understanding of blockchains while using basic Java. The application as
you can see is by no means a finished product, but it is functional enough
to be a nice learning tool that you can use to experiment on and further
your learning on your own. If by now you have started getting ideas of
how to improve and rewrite pieces of the code by using your favorite
frameworks and libraries, I’ll consider that this book has served its purpose.
169