Transcripts
1. Introduction: Hi, Welcome to the
Bitcoin developer course. My name is Alberto, and I'm happy to
present you with the first and only course where you'll learn Bitcoin by building a desktop
Bitcoin wallet. So let's start by explaining what you'll
learn in this course. In short, you will learn how Bitcoin works from a
technical point of view. More specifically,
you'll learn how a Bitcoin wallet
works in detail. Also, you'll learn how a Bitcoin node works
and how to run it. We'll see how it interacts with our wallet and other nodes. Will also learn how
the Bitcoin network works and how it
achieves consensus. Concepts we'll learn include
transactions, addresses, seeds, keys, balances, the blockchain,
mining, and wallet. Since we'll build a desktop
application along the course, the student will have the
opportunity to practice the following software
engineering skills which are in high demand
in the software industry. The object oriented
programming paradigm on which our wallet is based on. Most of the features will build, will follow the test-driven development way of doing things. As the name of this
approach suggests, for each feature will build their tests first and
the tests will guide the feature further
development also will worry a lot about application security
best practices, and treat the application as
a mission critical system. I personally have skin in the
game in this application, and I put a lot of effort into minimizing the chance of
someone losing funds with it. Also, this course has plenty of best
programming practices, examples, solid principles and clean code permeate the
culture of our code. Now, let's see how you'll
learn in this course. We'll follow the methodology
of learning by doing. About 17% of this course
is composed of theory, the rest is composed
of practical classes. So the course has
heavily code focused, intercalated with some
presentations when needed. Below every video, you can
find additional links and study material in the
Projects and Resources page. You'll build a desktop
Bitcoin wallet, learning important Bitcoin
concepts while doing so. Now, let's list the
recommended requirements for taking this course. It's recommended
that the student has a good knowledge of object
oriented programming. The student must know
Java or similar languages before taking this course. Although this course presents different software
engineering concepts, our focus is on
understanding Bitcoin. If the student wants to
know in detail some of the software
engineering concepts touched on in this course. It will have to
use other sources along the course to
understand them better. You'll need a computer
with Windows, macOS, or Linux with an
Internet connection. We'll present this
course using Windows. But since Java is
cross-platform compatible, every code in this course
will work the same way on these three
operating systems. The only difference
will be installing the necessary software
on each platform, but you can easily
find guides to install them on these
operating systems. You'll need at least
30 gb of free-space to run the Bitcoin node on
the test net environment. Optionally, if you intend to run the application on the
main net environment, you'll need at least 430 gb of disk space to download
the main net blockchain. But don't worry, we'll use the main net environment only in the last
video of the course. So you'll have plenty of time to download it before needing it. Now, let's talk a little
more about the wallet you'll build along the course. I'll refer to this
wallet as BYOD W, an acronym for build
your own wallet, will start developing this
application with Java 16, but we'll update it
later in the course. Diversion 19 will use the Spring Boot
framework version 2.5, 0.4, which will provide a dependency injection
container and many facilities to help
us build our application. Later in the course, we'll update it to
version 2.7, 0.5. We'll use Java FX version
16 in the project. We'll update it
later to version 19. This library will provide
classes that will help us build the applications
graphical user interface, also known as gooey. For testing, we'll use Groovy, the Spark framework and test ethics to create wallets,
seeds addresses, transactions, and other
Bitcoin operations will use the open source
Bitcoin Java library, which I built especially
for this course. We'll use the Bitcoin
Core Node version 22 for communication
with the blockchain. Later in the course, we'll update it to version 23. Although the Bitcoin
Core node can generate wallets and addresses, will use it only
to retrieve data from and send data
to the blockchain. Operations such as
generating seeds, addresses, building and signing
transactions will be done by our application. The exception will
be for the tests. In these cases, we'll use the Bitcoin Core Node RPC API to generate addresses,
send transactions, and mine blocks on the test
environments, therefore, will get familiar with
the most important Bitcoin Core Note
RPC API methods and build a client to communicate with it
from our application. As the database will use the SQL Java
implementation H SQL DB will use it smooth
integration with the Spring Boot Data JPA library to communicate
with the database. Finally, we'll use IntelliJ idea as the IDE to build
this project. All the code presented
in this course is open source and is available
on our GitHub page. For each practical class, the Projects and Resources
page will contain a link with the code difference between the last two practical classes for the students convenience. Now, let's present who I am. My name is Alberto Salazar, and I'll be your
instructor in this course. I have more than five years of experience as a professional
software developer, working especially in
the e-commerce industry. I've been a Bitcoin
enthusiasts since 2016. I'd been heavily studying
Bitcoins since then. I develop the open-source
Bitcoin Java library, which we'll use in this course. I've built the Bitcoin education initiative to concentrate my efforts on providing Bitcoin education for
developers around the world. Follow us on Twitter to
receive our newest updates. I concentrated all the
code for this course, including the Bitcoin Java
library on our GitHub page. We also have a website
whose address is www dot bitcoin
education dot site. We have some technical
articles there that can serve as additional
study material. Check it out as a final word. I'd like to say
that a lot of work was put into making this course. It took over two years
in the making and almost all my free time
during this period. So please, if you
have any difficulty or encounter any problem
with this course, contact me before giving it a low rating so that we can
help you with your issue. I'll gladly do
everything possible for you to have a great
learning experience. Thank you very much for your attention and see
you in the next video.
2. Downloading, Configuring and Running the Bitcoin Core Node and IntelliJ IDEA Download: In this video, we'll present the necessary software
you'll have to download and install before proceeding to the next
videos of the course. So let's begin with
the Bitcoin Core Node. The Bitcoin Core Node is the original
implementation of Bitcoin. It's responsible for
communicating with the blockchain,
creating, receiving, sending, and validating
Bitcoin transactions, receiving blocks and more. As we've said in
the previous video, the main functions will rely
on the Bitcoin Core node are the ones related to communication with
the blockchain. So let's go to the
Bitcoin core.org website. Will install Bitcoin
Core version 22. You may ask why not
installing version 23? When I began making this course, the latest version
available was 22. There's a small difference
in the gRPC API in version 23 that will make a method of our application not
work correctly. In one of the latest
videos of the course, we'll update the Bitcoin
node two version 23 and make our application
compatible with it. But for now, let's
install version 22. To do it, click on the release button at
the top of the screen, then click on the
Bitcoin Core 22 link. Next, click on this
link on the page. This page contains
installation files for each operating system. Since I'm using Windows, I'll download the one
ending with dot EXE. If you're using macOS, you'll download the one
ending with dot DNG. For Linux distributions, choose the suitable dot
GZ file according to the distro you'll use on the Projects and
Resources page, there are links with
installation instructions for each operating system. For Windows, after
downloading the file, open it and follow the installation instructions
on the screen. I won't do that because I've already installed
it on my machine. But it's a
straightforward process. In the last phase of
the installation, it will ask if you want
to open the software. Don't open it yet. Will interact with
the Bitcoin node through the command
line interface. If you open the node by
clicking on its icon, it will open a graphical
user interface and start downloading the blockchain
on the main net environment, which we don't want to do yet. Now, let's configure
our Bitcoin node. To do that, we'll have
to create a file named bitcoin.com and the
appropriate folder of the Bitcoin application. This folder location depends on which operating
system you're using. This website on the screen shows the default location where you have to create this file
for each operating system. To create it, open a text
file and save it with the name bitcoin.com from the appropriate
folder for your OS. Now, write the
same configuration you see on the
screen in your file, or copy and paste
the content from the Projects and
Resources page into it. Don't worry, we'll explain what each line of this file
means in a later video. For now, you'll have to
decide where you want to store the blockchain
data on your computer, you'll have to choose
a path with access to at least 30 gb for the
test net blockchain, or an additional 430 gb for
the main net blockchain. Optionally set the
data DIR configuration to the chosen path in this file. In my case, I decided to store the blockchain files in the E drive in the
Bitcoin data folder, as you see on the screen. Now, if we run the Bitcoin
node with this configuration, it will run on the main net and start downloading the
main net blockchain. As its name suggests, the main net environment is the main bitcoin
environment where the blockchain is big and
the bitcoins have value, will only run the
main net environment in the last video of the course. Before that will run it on
the test net environment, which is used for tests. To run our note on the
test net environment, let's add a line
written test net equal one in the
bitcoin.com file. Now using the terminal, let's go to the folder where you installed the Bitcoin Core Node. Inside the demon folder, run the Bitcoin D
application like this. In the console logs. Notice that we're
running indeed in the test net environment and all its data will be stored
in the indicated folder. Good, that means our
node is working with the configuration we've set
in the bitcoin.com file. Now, let's wait for
our node to connect to other nodes and
start downloading and validating the blockchain. Okay, the lines starting
with the text update tip indicates that it began
downloading the blockchain. Each one of these
lines indicates a block of Bitcoin
transactions downloaded. The progress in each line indicates how much the
blockchain is sink. In my case, it indicates that about 98% of the test net
blockchain was downloaded. When this value is equal to one, it indicates that our
copy of the blockchain is completely sync with the
ones from other nodes. For the test net environment, it can take some hours
for it to happen. You can leave it sinking while proceeding to watch the
rest of the course, since we will use it
only after some videos. To exit the application, you can press Control plus C or Command plus C if
you're using Mac OS, if you start the note again, it will start downloading the blockchain where it stopped. After finishing downloading
the test net environment, you can optionally change the bitcoin dot conf file
and start downloading the main net blockchain by running the note on the
main net environment, will only use the
main net environment in the last video of the course, so you'll have plenty
of time to download it. The other initial
requirement of this course is the IntelliJ idea Java IDE. It's the software
will use to write the code and run
our application. If you don't have it, go to the website on the
screen to download it. Download and install the
community edition for your operating system by following the instructions
on the website.
3. What is Bitcoin?: Hi, welcome back to the
Bitcoin developer course. In this course section will give you an overview of Bitcoin. The concepts presented
in this section, we will provide the basic
knowledge necessary to understand what is coming
in the rest of the course. So I will start with the
question, what is Bitcoin? Bitcoin with an uppercase B
is a communication protocol. This protocol is a set of rules that allows
the exchange of messages between the entities participating in the
Bitcoin network. Bitcoin with a lowercase
b is a cryptocurrency. A cryptocurrency is a form of digital currency that we can exchange for goods
or other assets. The crypto part of the name
comes from cryptography, which protects transactions
and other Bitcoin operations. Bitcoin is also a
payment system. We can send and receive
Bitcoin payments to and from other participants
of the Bitcoin network. Speaking of networks, Bitcoin is also a
peer to peer network, which means that
it is composed of entities or nodes
which communicate directly between them without the participation of
centralized servers. The next question is, when was bitcoin invented? Bitcoin was invented in 2008 by Satoshi Nakamoto with the
publication of the white paper, Bitcoin, uh, peer-to-peer
electronic cash system. In this paper, which I
highly recommend reading, Satoshi describes the
basic parts of Bitcoin. Nobody knows who's Hitoshi
Nakamoto really is, or if it is even a real
name or multiple persons. Whoever Satoshi is, in 2009, he published the first
Bitcoin block called the genesis block and put the
Bitcoin network to work. If you do not know what a
Bitcoin block is, do not worry. I will explain in short. So what is so great
about Bitcoin? What is the innovation
of Bitcoin? Bitcoin was the first, well succeeded digital currency. Before Bitcoin, there were some attempts to create
digital currencies, but they all failed due to problems that
Bitcoin solve later. One of these problems is the
double-spending problem, which bitcoin was the first to solve in a decentralized way. It happens when a person spends two times a currency unit. So one of the transactions
is invalidated later, making the recipient of the invalidated
transaction lose money. Bitcoin was also responsible for inventing the blockchain, which was used to solve the
double-spending problem. The blockchain is
a data structure responsible for storing
all Bitcoin transactions. It allows the validation of all its transactions to
anyone with access to it, but more on that later. Bitcoin also introduced the
concept of digital scarcity. It means that unlike
files on your computer that can be copied and
distributed indefinitely, Bitcoin can only be
transferred by its owner and cannot be copied
and distributed freely.
4. Bitcoin Transactions and Private Keys: Now we'll dive a
little bit deeper into the inner
details of Bitcoin. I will explain Bitcoin
from micro to macro. That is, I will talk about Bitcoin transactions
and private keys, then a little about
blockchain and mining. Then I will finish
the overview by explaining the
Bitcoin ecosystem. So let us start
with the question, where's the cryptocurrency
and the system? Or in other words, where is the Bitcoin with a
lowercase b in the system? The answer is, it is
in a transaction. This figure shows a common
Bitcoin transaction with one input and two outputs. A Bitcoin transaction can
have many inputs and outputs, but in many cases, they have only one
input and two outputs. In a Bitcoin transaction, we have the information from
where the Bitcoin came in the input and where the Bitcoin
is going in the output. In this example,
input zero contains an instruction that says that
this input came from John, was signed by John and
contained one Bitcoin. The first output called
the output zero, says to Mary contain
0.9 Bitcoin. In output one, it says to
John contains 0.099 Bitcoin. This output is the change
of the transaction. In most cases, we
need to include a change because when you spend Bitcoin from
a transaction, you have to spend all the
units from the input. So this generates a necessity to create an additional output, which is the change. So if your input
contains more bitcoins, then you want to send
to another person, then you must create
an additional output, the change to yourself. We have the concept of UTXO, which means unspent
transaction output. A bitcoin is more
specifically inside a UTXO. A transaction input must
refer to a UTXO from a previous transaction
to be able to spend that Bitcoin in this transaction. If you notice the
sum of amounts of outputs is less than the
amount in the input. Why is that? The difference is due
to the minor fee. Every bitcoin transaction
must separate an amount for the minor fee so that
your transaction can be included in the
blockchain by a minor. So the transaction fee amount is the difference
between the sum of Bitcoins present
in the outputs and the sum of Bitcoins
present in the inputs. Another important piece
of info is that Bitcoin can be divided into
100 million Satoshi's. Therefore, in this
example transaction, we could have said that
the inputs contained 100 million Satoshi's
in the output zero contains 90 million
Satoshi's and the output one contains
9,900,000 Satoshi's. The minor fee was
100,000 Satoshi's. These are other examples
of Bitcoin transactions. I put them here just to
show that you can have transactions with more than
one input and one output. E.g. you can have a transaction with two
inputs and one output. This transaction does
not have a change. You can have a transaction with two inputs and two outputs. In this case, the
transaction has inputs from two different persons and outputs for two
different persons. This specific type of transaction is called a
Coinjoin transaction, which is a type of transaction
that increases privacy because you cannot link specific inputs to
specific outputs. You can have a transaction with three inputs and two outputs. There are other types
of transactions, such as multi-signature
transactions that we won't talk about now, but we may include them later in an extra course section if
there is enough demand. Let us see a little more about the details of a transaction. In this image, there
are two transactions, transaction one and
transaction two. Both transactions have
an input and an output. The input has three fields, the previous transaction
ID and output number from a previous transaction
and a valid signature. The output has two
fields, destiny, address and quantity,
which refers to the Bitcoin amount
inside the output. In this case,
Transaction two input is spending output zero
from transaction one. To do this, input one
from transaction to must refer to the transaction
ID from transaction one. It also must refer to the output number
from transaction one, which in this case is zero. Finally, it must contain a
valid signature produced with the private key used to derive the destiny address
from output zero. We will see more details about this operation in
the next slide. As you may notice, private keys are a fundamental
part of Bitcoin. Private keys are
used to receive and send bitcoins to receive. Private keys are
transformed into public keys through elliptic
curve multiplication. Then after a hash function, public keys are transformed
into Bitcoin addresses. These are the Bitcoin
addresses that you can show to other people
to receive Bitcoins. Private keys are also used to assign Bitcoin transactions. Signatures are
necessary to transfer Bitcoins from one
address to another. In this image, we have
an unsigned transaction. When we combine the
private key with an insider transaction using the Elliptic Curve digital
Signature Algorithm, ECDSA, we produce a signature that we include in
that transaction. Thus, it becomes a
signed transaction. The same private
key that produced the address referred
to an input zero is also used to unlock
these funds and the current transaction by
producing the signature. Thus, a private key is used
indirectly to lock funds in an address and to
unlock funds from the same address by
producing a signature. Important thing about
private keys and their transformations
is that you cannot produce a private key
from a public key, and you cannot
produce a public key from a Bitcoin address. You also cannot produce a
private key from a signature. These operations are
only in one direction, just as these arrows show. Another important feature of
Bitcoin transactions is that anyone with access to a transaction can
validated signatures. This is possible because
if you have a public key, the signature and the message, which in this case
is the transaction. You can verify if a signature is valid for that public key. And a signature is valid for
a public key only if it was produced with a private key that produced the same public key.
5. Bitcoin Wallets, the Blockchain and Mining: Now that you have a basic
knowledge about private keys, we can start talking
about wallets. A Bitcoin wallet is, in short, a collection
of private keys. There are many types and
standards of wallets. Wallets have independent
development regarding other Bitcoin software
such as nodes. A basic rule for Bitcoin
owning isn't the mantra. Your keys, your bitcoins, not your keys, not
your Bitcoins. This is because the owner of
a private key can transfer Bitcoins locked by his private key and
other transactions. What things do Bitcoin
wallets have in common? They have a safe way to generate
and store private keys. They can communicate
with Bitcoin nodes. They can create, sign and
send Bitcoin transactions. They can generate
Bitcoin addresses. Notice that most of
these abilities are possible thanks to private
keys transformations. Every operation that uses private keys can
be made offline. You only need to be online when communicating with
Bitcoin nodes to fetch information from
the blockchain and to send information
to the blockchain. Speaking of blockchain, let us finally see what
this buzzword means. The blockchain was used to solve the double-spending problem
in a decentralized way. It does that by organizing the transactions in a
chain of blocks so that valid blocks cannot
contain transactions with a UTXO that appeared in the same block or
in previous blocks. To understand what
the blockchain is, let us look at this figure. These blocks represent
blocks from the blockchain. Some of their data is represented in these
smaller rectangles. Inside each block. A block is composed
of many transactions, a field called previous hash, which as the name suggests, is the hash of the
previous block. A field called timestamp, which stores the date where
the block was created, and a field called the nonce, which we will talk about
in the next slide. Each block produces a hash that is inserted
in the next block. The blocks are
created and added to the blockchain in a
process called mining. Mining is the process where blocks are included
in the blockchain. The nodes that do this
process are called miners. To include blocks
in the blockchain, miners must collect transactions and organize them in a block. Then minors must solve
the Proof of Work, which is an algorithm necessary
to produce valid blocks. Proof of work is composed
of the following steps. First, a block is hashed, then the number of
initial zeros of the resulting hash is verified. If this number is
greater or equal to the necessary number of zeros to include the block
in the blockchain. The block is included
in the blockchain. If not, then the minor modifies the nonce and
repeats the process. So this is the
function of the nonce. Its only purpose is to provide variability for a block to
produce different hashes. Proof-of-work has an
interesting characteristic. The more zeros required
for a valid proof of work, the more difficult
is to find it. This makes mining difficulty
easily adjustable. In Bitcoin, the
mining difficulty is automatically adjusted, so the average time to
mine a block is 10 min. When a minor minds a block, he earns a block reward and
all block transaction fees. The block reward is the only way that new
bitcoins are created. It decreases by one-half every four years in an
event called having. So every four years
the supply of new Bitcoins issued
gets scarcer. And it is calculated that the last bitcoin will
be mined in about 21:40 when the total
Bitcoin supply will be equal to 21 million.
6. The Bitcoin Network, Ecosystem and Principles: Now let us talk about
the Bitcoin network. Let us see the basic steps of how the Bitcoin network works. First, new transactions
are sent to the nodes. Each miner gathers the
transactions in a block. Each miner tries to find a block proof of work
as fast as possible. When a miner finds
proof-of-work, the minor broadcasts its
mind block to the network. Other nodes verify if
the block is valid. For a block to be valid, it must follow all
the consensus rules, such as all the transactions
and it must be valid and the previous block
hash must be equal to the block cache of
the last valid block. If the block is valid, nodes add it to
their Blockchain. Copy. Minor start to
work on new blocks using the hash field of the recently mind block
as the previous hash. You may be wondering
what happens if two miners mine a valid
block at the same time. Well, when this happens, it causes a split
in the network. Part of the nodes will
contain one version of the blockchain
with one block from a minor and the other part of the network will contain a
block from the other minor. But this split is
only temporary. When the next block is mined, the two groups of nodes will consider only the
blockchain with more blocks as the
real blockchain and the other blockchain
is discarded, the probability that the network split persists
decreases with time. So it is safe to assume
that after six blocks a transaction is settled
forever in the blockchain. The immutability and security of Bitcoin transactions rest
on the fact that it is almost impossible to produce a valid blockchain with more blocks than
the original one. Such a feat would require
an immense amount of computational power needed to surpass the speed of
block generation of the original network
for a sufficient time. Now that we learned about
various parts of Bitcoin, let us see how they interact inside the Bitcoin ecosystem. Bitcoin users control wallets. Wallets, as you learned, are collections of private keys. And these private keys
can be used to sign transactions and to produce addresses to receive
transactions. Exchanges can trade bitcoins for other cryptocurrencies and fiat currencies,
such as the dollar. You can also trade bitcoins
for goods from merchants. Whoever you transact with, all your transactions are
sent to Bitcoin nodes. When a minor sees
your transaction, he can include it in a block. The miner creates a block with many transactions and
proceeds to mined the block. That is, he starts to calculate the block proof-of-work
using his hardware. When a minor finally finds
the block proof of work, he broadcasts the block
to other Bitcoin nodes. The other nodes receive the recently mind
block and validated. If the block is valid, the nodes add it to their
copy of the blockchain. This way, the network achieves consensus on the current
state of the blockchain. Let us talk about the
Bitcoin principles. The Bitcoin principles are
important to understand the technical
decisions that were made during the
Bitcoin development. One of these principles is the Bitcoin maximum supply
of 21 million Bitcoins. This limit was set at the
beginning of the project. Changing it would cause this arrangements in
the Bitcoin economy. So it is important
to keep it that way. Censorship resistance is another important
Bitcoin principle. The fact that many people can
run Bitcoin nodes makes it almost impossible
for an authority to shut down the network. Bitcoin developers value a
high censorship resistance. Being open source is
another Bitcoin principle. You can find the
Bitcoin Core software for free on the GitHub website. Like many other developers, you can verify the code
and check it for bugs. You can also participate
in its development. Bitcoin has a strong
community of developers responsible for its
security and new features. And being open source
is what allows it. Bitcoin is permissionless,
which means that anyone is allowed
to run a Bitcoin node, mine and transact Bitcoins. That is one of the greatest
strengths of Bitcoin and what makes it so popular
today around the world. Bitcoin transactions
are pseudonymous. That means no
information present in a Bitcoin transaction can
alone connected with people. Unlike the example from the beginning of the overview
might have suggested Bitcoin transactions
do not contain the names and IDs of
recipients and senders. A Bitcoin transaction shows only transfers between
bitcoin addresses, which looked like random
collections of characters. If someone discovers that an address belongs to
a specific person, they can track the
amount of Bitcoin and other transactions
of that person. But that's another story. Bitcoin aims for
high fungibility. Fungibility is a characteristic
that makes a unit of a currency be valued equally as any other unit of
the same currency. That means that ideally every Bitcoin is valued
and treated equally. It is often argued that Bitcoin
is not fungible because Bitcoins from
different transactions can be traced and
treated differently. I would say that although
it is true in practice, most bitcoins are treated
equally and you have ways to make transactions
more private and fungible. Irreversibility of transactions is another
Bitcoin principle. Once a transaction is
settled in the blockchain, it cannot be reversed. This is an important
security feature because it makes Bitcoin
hard to confiscate. But it also shifts
the responsibility of a transaction to
the Bitcoin center. Hence, the need to keep
your private keys secure.
7. Starting the Project: In this video, we'll set up a new Spring Boot project that will be our Bitcoin wallet. To do that, we'll use the Spring initializer tool from the website start
dot spring dot io. So enter the website. Choose Maven project. The language is Java, will use this version
of Spring Boot, but if it's unavailable for you, you can choose the default
one as the group name. You can set whatever
name you like. In my case, I'll set it
as BYOD w dot wallet. I'll use BYOD W as the artifact
name, as the description. I'll use Bitcoin wallet. We'll use the latest
version of Java. If you only know previous
versions of Java, don't worry, older Java features
still work on the latest version as Java
is backward compatible. Finally, click on Generate
to download the project, open it and extract its contents to the
folder you want to. Now, we'll open the project
using intelligent idea. After starting the program, click on Open, then choose the
folder you just extracted. Click on Trust Project. Wait until IntelliJ idea
indexes all project files. After that, you can
explore the files that spring initializer
created automatically. To verify if the project
was set up correctly. Click on Maven on the
right side of the screen. Then inside the life cycle
folder, Double-click on Test. Oops, we got a maven error. To solve that error, Let's click on
configuration dialog. Let's change Maven home path
to bundled Maven three. Click on Apply then. Okay. Before clicking on test again, I'll check if my project
structure was set up correctly. So I'll click on File. Then project structure
makes sure the project SDK has the Java version you chosen the
spring initializer. If it's not the case, click on Add SDK
and download SDK, choose the correct Java
version and one of its implementations
click on download. In my case, I won't do that because I already have
the right version. So I'll click on Cancel. Make sure that the
project language level is also set to the
correct Java version. Now, let's run the test again. Wait until the test passes. Great. The console indicates
that the test has passed. We're ready to begin
working on our wallet.
8. Configuring the GUI: In this video, we'll start building the first
window of our wallet. To do that, we'll first add some dependencies
to our project. Open the palm dot xml file
at the root of the project. The palm dot xml file contains all external dependencies
of the project. We'll add some Java
FX dependencies. Java FX is a framework responsible for graphical
user interface features. It's very well known
in the Java community. The version is 16. The next
dependency is JavaFX, FXML. The version is also 16. We'll also add a plugin. It's called Java
FX Maven plugin. It's version is 0.0, 0.6. To load these dependencies
in your project, click on this icon. Project has loaded
all dependencies. Now let's make the code necessary for booting
our application. This process requires
some coordination between the ways that Spring Boot
and Java effects start. To do that, we'll first make a new Java class called
GUI application. This class must extend the application class
from Java effects. And we must implement
the start method from the same class. We'll add a private
property with the type configurable
application context. We'll use next. We'll add the init method here, which is executed before
the start method, during the application
initialization. The code I just type
is responsible for initializing our Spring
Boot application injected dependencies. Now let's go to the BYOD
W application class. Let's modify the main method. The main method of this class is the first method called when
we run our application. When the code I just
type is executed, it will first call
the init method of the GUI application class than the start method
of the same class. Let's get back to the
GUI application class. Let's continue implementing
the start method. Now, we'll call the
publish event method on the context property, passing a new GUI
started event object, which will create
next as its argument. Let's create the gooey
started event class. We'll create it in a
new events folder. Due to how we
created this class, the ID already
knew what class we had to extend and
it automatically created the
boilerplate code will need very handy, isn't it? Now we'll continue
implementing our constructor. We'll call the super
constructor passing a GUI application
object as its argument. And we'll assign the argument
primary stage to the stage. Private property will also make a listener for
this event class inside a package
called listeners. We'll add the component
annotation to this class. You'll see this annotation
a lot along the course. It's from Spring
Boot and its purpose is to make an object
of a class available for injection and
other objects of the project will also implement the
application listener interface for this class, passing the gooey started
event as a type parameter. This is necessary for
Spring Boot to interpret this class as an event listener for a gooey started event. Now we'll implement the required
method of the interface. Will also need a constructor. This constructor will take
a resource object with a value annotation pointing to a file called main
window dot FXML, which will add later
to the project. The constructor will also take an application context argument. Then we'll initialize to private properties
with the same name as the constructor arguments. Will make both properties Final. Alright, now that we have our dependencies injected
into this class, let's implement the code of
the application event method. This code will be
responsible for showing the main window of our wallet
when we start the program. So we'll first instantiate
an FXML loader object. Then we'll get the stage object from the GUI started event. To do that, we'll
add the method gets stage to the gooey
started event class. This method will return
the stage object. We'll add a title
to the wallet using the set title method
of the stage object. It can be any string you want. I'll set it as BYOD W wallet. This text will be shown at the top bar of the
programs window. Will also call the
method set scene, passing a new scene object as its parameter instantiated with an object returned
by a new method that will create called
initialize FXML. This method will start declaring a variable of the type
parent called root. Then inside a try catch block will use the set
location method from the FXML loader variable passing the URL location of
our FXML property. We'll set the controller
factory of the FXML loader to a reference of
the good bean method of the context property. This is necessary for
the JavaFX framework to recognize controller
classes annotated with the Component annotation. We'll also set the root variable to the result of calling
the load method of the FXML loader will catch an IOException and throw it wrapped in a
runtime exception. We'll return the root variable. Finally, in the
non-application method will call the show
method of the stage, which will cause the window
to show up on the screen. Now we'll create the FXML file, but we referred on the
value annotation of the FXML property of the
listener constructor. And FXML file is a
type of file used to encode GUI elements of
a JavaFX application. Like an HTML file,
encodes webpages, FXML files and code
Java effects windows will create this file inside the new FXML path
that will create. This directory will be located
inside the resources path. We'll call it main
underscore window dot FXML. Let's delete the
auto-generated boilerplate. Let's add a border pane tag. We'll add some attributes to it. Will add an Fx ID that
will allow referring to the border pane tag inside
its controller class, which will create later. Let's add some attributes
to set the window size. Finally, we'll add
some metal attributes to set the file schema. That's it. Let's run the application
to see if all went well. Inside the Maven tab, Let's click on plug-ins, Spring Boot and Spring Boot run. Great. The first window of our wallet has shown up on the screen. Good job.
9. Creating our First Test: In this video, we'll
start building a test for the first
feature of our wallet. The first feature
of our wallet will be the option to
create a wallet. More specifically, we'll create a menu with a button
that when clicked, will make a dialog box
pop up on the screen. That dialog box will initially
contain a TextField where the user will define
the wallet name and a Create button
that when clicked, will generate a
random mnemonic seed. Will learn what is
the mnemonic seed. In the next video, we'll create every feature
of our Wallet using the test-driven development
or TDD approach. This approach consists of first creating an
integration test, which is a test describing what we want to achieve
for that feature. Then we'll implement the feature and run the test
until it passes. During the implementation, we may encounter situations where we need to make new classes or methods to help us
achieve our goals. If that's the case, we'll
create unit tests for these classes were methods
and implement them using TDD. At the end of each TDD cycle, we'll refactor our
code if needed. Let's see how this
works in practice. But first, let's add some
dependencies to our project. We'll add some dependencies related to the Spark framework. The Spark framework is a
testing framework in groovy, a programming language
compatible with Java. It contains features that
facilitate building tests, will also use a version of
test FX compatible with Spock. Test fx is a framework
that provides features for testing
JavaFX applications. Now, let's add
groovy version 3.0, 0.6 to our POM file. And let's add spock
spring to it. The version will be
2.0, groovy 3.0. Its scope will be test. The next dependency
will add is Spark Core. The version will be the
same as Spock spring. The scope will be test. The next dependency
will be test FX Spock. Its version will be 4.0, 0.16 alpha will also
add a G Maven plugin. Okay, I forgot to put the right scope and the
test FX dependency. We'll also add some goals
in the GI Maven plugin. There'll be necessary for
groovy to work correctly. Now, we'll make a change in the application dot
properties file. Spring, by default
creates a web server, but we won't need one
in our application. So we must set this
property to none. So spring won't create a
web server when it boots. Now we'll create our first test. But first, let's delete this file BYOD W
application test, which was auto-generated
by the spring initializer, will create a package in
this folder called gooey that will contain all our
graphical user interface tests. Inside this package,
Let's create the test. But first on the
palm dot xml file, click on the load
Maven Changes button. Now the IDE will allow us
to create groovy classes. So let's create one called
Create wallet test. We'll add to this class a Spring Boot test annotation will extend this class with
the application spec class. This is necessary for
both Spring Boot and test fx to consider this
class as a test class. Every integration test must contain this annotation
and extension. We must implement
the start method from the application spec. We will also implement the
unit and stop methods. Inside the init method will add this code which is necessary
for test initialization. Inside the stop
method will include a code necessary for
stopping the test. Inside the stark method, we must include the
code necessary for showing the first screen
of our application. This code will be
similar to the code present in the GUI
started listener class. So we'll instantiate
an FXML loader passing the FXML URL
as its argument, will then create the FXML
private property with the same value
annotation present in the GUI started
listener class. Groovy transforms every method with getter set in
an object property, we can use this URL here. Instead of get URL will set the controller
factory property to the context get bean
method reference. Let's add the context
field to the class. Above. It will put the auto
wired annotation. This annotation is necessary
for Spring Boot to inject the application
context object from its dependency injection
container to this variable. Next, we'll create
the root variable using the load method
from the FXML loader. Then we'll set the stage title, the scene and call
the stage show method just as the code inside the
gooey started listener. Okay, we're ready to work on
the code of our first test. The name of the test will
be should create wallet. The test will contain
the requirements of the feature we
want to implement, will use the keyword when to describe the actions the
test user will make. So let's implement it. The user will click on a
button with the text knew. Then you'll click on a
button with the text wallet. Then on a text field
with an FX id of name. When we want to
refer to an FX id, we put the name of the ID
after a pound sign like this. Then the user will write
the words myText wallet. Then he'll click on a button
with the create text. Next, the test will
look up the contents of a text area field with an
ethics ID of mnemonic seed. It will assign the contents of this field to the
mnemonic seed variable. Finally, we'll use the keyword then to define what we expect to happen after the user makes all actions inside
the wind block. We expect that the
mnemonic seed variable contains a non-null value. To do that, we just put the mnemonic seed variable
inside the then block. If the mnemonic seed is set, the block will evaluate to
true and the test will pass. Otherwise, it'll evaluate to false and the test won't pass. Let's run this test
to see what happens. First, we'll click on Build
than on rebuild project to make sure the IDE loads all the changes we've
made to the code. Then we'll click on Test
inside the Maven Config tab. *****. The IDE complained
about Maven again, let's fix that quickly. Now. Let's run the test again. The test has failed as expected. Did you notice that
a window popped up quickly on the screen
during the test? It shows our window
opened, at least. Let's check the results of
our tests on the console. It says that the query
new return no nodes. The test opened our application, then tried to find an
element with the text knew as it didn't find it, it quickly failed and
close the application. In the next video, we'll learn what a
mnemonic seed is. Then in the video
after the next, we'll create our first
wallet and make this test pass. See you.
10. Mnemonic Seeds: In this video, we'll learn
more about mnemonic seeds. So what's the mnemonic seed? A mnemonic seed is a
sentence containing 121-51-8201 or 24 words. It's used to derive all wallets, private keys, and addresses. The rules of mnemonic
seed generation that we'll use are described in the Bitcoin Improvement
Proposal 39 or BIP 39. Many wallets used
the BIP 39 rules to generate mnemonic seeds. Mnemonic seeds are combined
with the string mnemonic plus an optional password to produce a root seed represented
here in hexadecimal format. The optional password is commonly referred
to as passphrase, but we'll use the word
password for simplicity. Then, using other algorithms, the roots seed
generates private keys, public keys and addresses, will learn the details of these operations
in a later video. Now, let's focus on how
mnemonic seeds are generated. To generate a
mnemonic seed, first, we must generate a random
sequence of bits called entropy of length
equal to 128 bits. If we want a mnemonic seed
of 12 words, 160 bits, if we want one with 15 words, 192 bits, if we want
one with 18 words, 224 bits if we want one
with 21 words or 256 bits, if we want one with 24 words, then we calculate the checksum. To do this, we calculate
the entropy length divided by 32 and obtain
the checksum size. Then we get the first
checksum size bits of the SHA-256 hash of the entropy. The result is the checksum. The checksum is used to check if a given mnemonic seed is valid. We append the checksum to the
end of the initial entropy. The result is split
into groups of 11 bits. Each group of 11 bits is
converted to a number 0-2047. These numbers are used as
indexes in a word list. The corresponding words
form the mnemonic seed.
11. Create Wallet Dialog Window: In this video, we'll
continue to implement the create wallet feature
of our application. The first step is to add a button with the text
new written on it. Let's do this. Open the
main window dot FXML file. From now on, we'll use the IDE Scene Builder tool to create the windows
of our project. Scene Builder
allows us to create the graphical user interface of our application
interactively and easily. Click on the Scene
Builder button at the bottom of the screen. In my case, this
window appeared. But if you don't have
Java FX or Scene Builder installed a button to download them will appear on the
top of your screen. After you download
and install them, this window will show up. Now we'll add a top menu
bar to the main window. To do this, we'll
click on controls, then drag and drop the
menu bar component to the top part of the
border pane menu below. Now we'll have a menu bar
with three menu items. Let's delete the
edit and help ones. Let's change the file
menu labeled two new. Great. Now we have
our new button. Let's check our test to
see what to do next. The next action our user
will do is to click on the wallet button.
Let's create it. Back to the main window. We'll add a menu item
inside the new menu. In this case, we already
have a menu item. So let's change its
label to wallet. When the user clicks
on the wallet button, a new dialog window must appear. Let's implement it in
the Scene Builder. Click on the wallet menu item. We'll add an action
to this button. Click on Code, type open
create wallet dialogue. If we click on the Text
button at the bottom of the screen and
check the FXML file. We'll notice that the IDE
has added the on action attribute in the menu item tag with the value we just typed. Now, to create an action for this button will need
to create a controller. But first, let's do some
refactoring in our code. We'll create a new package
inside the BYOD W package. Let's call it gooey will transfer the events and
listeners packages to it, will add all the
classes related to the applications graphical user interface to this package. Back to the main window, will add an FX
controller attribute to the border pane
tag and set it to the new controller we'll create will create it inside a
new controllers package. Inside the gooey package. Its name will be main
window controller. Every FXML file can have a controller which is used
to define the actions that happen in response to the user's interactions with the window encoded by that FXML. In our case, the main window
controller will contain code that will define actions in response to events
on the main window. Back to the main
window dot FXML file. When we click on the Open create wallet dialogue attribute
and press Alt plus enter, the IDE will show
the option to create a method inside the
main window controller. Click on this option. The IDE auto-generated
the method in the main window controller. Let's delete the
action event argument because we won't need it. In this method will
include the code responsible for opening the
create wallet dialogue. When we click on the
wallet menu item, will first instantiate
a dialogue object of type button type. Then we'll set the
dialogues in it owner to the border
pane window to indicate that the
dialogue belongs to the border pane
of the main window. Back to the main
window dot FXML file. We can click on the ethics ID attribute of the
border pane tag, press Alt plus Enter and create the border pane property inside the main
window controller. The IDE auto-generated
the property. Let's set it to private and
include the FXML annotation above it to indicate that it
refers to an FXML component. Then we'll set the dialogue
title to create new wallet. Now, inside a try
catch block will instantiate an FXML
loader object. Passing these arguments. We'll then create the create
wallet dialogue property and the context property. Then we'll create a constructor to initialize both properties. Both properties will be
automatically injected by the Spring dependency
injection container will add a value annotation to the create wallet dialogue
constructor argument passing the path of an FXML
file that will create next. Then we'll set the content of the dialogue pain to the result of calling
the method load of the FXML loader will then catch an IOException and throw it as a runtime exception. Finally, we'll call
the dialogue show and wait method to
open the dialogue. Don't forget to
add the component annotation to this class. Now, let's run our application to see what we have so far. In the Maven tab inside
plugins and Spring Boot, click on Spring Boot run. We have our top menu bar with the New button and
the wallet button. When we click on
the wallet button, the application
throws an exception. That is because it
didn't find the create wallet dialog dot FXML file. Let's create it.
After creating it, click on Scene Builder. Let's delete this anchor pane. Drag the dialogue pain,
and drop it here. Good. Let's adjust the size of it. Click on Layout. Then in the pref
height field type 500, in the pref width
field type for 50. Then set use prep size on the fields min-width
and min-height. Now let's add a header
text to the window. In the header text field type. Give your wallet a name
and click on Create. Let's add some
padding around it. Good. Now let's add a grid pane. Drag and drop this component
to the dialogue pain below. Inside the first cell of the grid pane will add
a label like this. And this labelled will
contain the text wallet name. We'll also add a text
field to the grid pane. Let's change its coordinates, set its column index to one. Now let's add a button
to the grid pane. Change its row index to one
and its column index to one. Let's change its text to create. Let's get back to our test to check what else it requires. It'll try to click on a component with an
ethics id of name. So let's add an Fx id of
name to the text field. After that, it will try to write my text wallet in
the text field. Then it will click
on a component with the text Create on it, create with an uppercase
C. When it clicks on it, the test will look
up the contents of a text area with an FX
idea of mnemonic seed. So let's create this text area. Drag it to the grid pane below. Then change its row index to
two and its column span to two so that the
mnemonic seed field takes up the space
of two columns. Good. We'll also add a label for
the mnemonic seed field. But first, let's add another
row to the grid pane. Good. Let's increase the mnemonic seed field row index to three. Then we'll drag a label
to the grid pane. Adjust its row index to two, change its text
to mnemonic seed. Now let's add an Fx id of
mnemonic seed to the text area. Now, let's run our application
and see what happens. Rebuild the project first and
click on Spring Boot run. Great. But when we click on
Create, nothing happens. We also must handle the
closing of our dialogue. For now, click on stop. The positioning of these
fields is bothering me a bit. Let's fix that. Click on the Text button. We've included the
grid pane inside the header tag of
the dialogue pain. The correct place
to include it is in the content tag.
Let's change it. Let's check its appearance
now on the Scene Builder. Great, now it looks way better. In the next video, we'll continue to implement
the create wallet feature. See you.
12. Creating the Mnemonic Seed: In this video, we'll continue to implement the create wallet
feature and finally, make our first GUI test pass. To do this, Let's add a
dependency to the project. We'll add Bitcoin, Java, and open-source library made
exclusively for this course. This library contains many
Bitcoin related utilities, such as methods to generate
random mnemonic seeds, build transactions,
and many more. For the correct functioning
of this library, we must add a plugin
goal that will copy the file word
list dot TXT from Bitcoin Java to
the project during the compile phase of the
applications building process. Wordless dot TXT contains all valid words for
mnemonic seed generation. Now open the create
wallet dialog dot FXML. Change to the text view. We'll add a controller to this FXML in order to add an
action to the Create button. So let's add the FX
controller attribute to the dialogue pain tag. The controller's name will be created wallet
dialogue controller, and we'll add it to the
controllers package. As with every controller, will add the component
annotation to it. Let's create the mnemonic seed property in this controller. In the FXML, we'll add an
action to the Create button. Let's add it here. We'll name the action as
create mnemonic seed. And we'll create it in the
corresponding controller. So when the user clicks
on the Create button, this method will be called and the text area will show
a random mnemonic seed. So we'll set the mnemonic
seed texts to the result of the create method
of the mnemonic seed service will create next. Let's inject the mnemonic
seed service into this class. Now, let's create the mnemonic
seed service class inside the new package called api dot services inside the
BYOD W package. Basically, every business
class not related to the graphical user
interface will be placed inside the API package. Let's add the create method
to the mnemonic seed service. Add the service
annotation to this class. It's purpose is identical to
the Component annotation, but it is more suited
to service classes. Now, let's initialize the
mnemonic seed service property in the create
wallet constructor. Now, let's implement this method because we're using TDD to
create our application. We'll first create a unit
test for this class. Let's create an API package
inside the test folder. Inside it, create a groovy class called mnemonic
seed service test. As a unit test, this
class only needs to extend the specification
class from Spock. Now, we'll create a test called should return random
mnemonic seed. And we'll expect that the
mnemonic seed service returns a non null value. Let's instantiate the
mnemonic seed service here. Let's run this test to
check what happens. It's failed as expected. Let's implement this method. In order to do that, we'll use the generate
random method of the mnemonic seed generator
class from Bitcoin Java. We must pass the
entropies number of bits that we want
the mnemonic seed to have as an argument to
this method, we'll use to 56. Remember that an entropy of 256 bits generates a
mnemonic seed of 24 words. Finally, we'll call the
get sentenced method on the return of this method
and return the result. As with every job, a
checked exception, we also need to add
the sentence throws FileNotFoundException to the
create method signature. We have to do the same to the create mnemonic
seed method in the create wallet
dialogue controller. Let's run this test again. Great, The test has passed. Now let's check what happens
when we run our application. Whoops, and error occurred. Oh, it's overwrite with an
uppercase W in this tag. This group ID tag is also wrong. Actually, it's just
Bitcoin hyphen education. Let's run our application again. Great. We've successfully
generated a mnemonic seed, but I want its text
wrapped. Let's fix it. Let's go to the create
wallet dialog dot FXML. In the Scene Builder, click on the text area and click on
The Wrap Text checkbox. Let's run the project again. Okay, way better. Now, let's run all our tests. Great. They all have passed.
13. Create Wallet Dialog Window Buttons and Validations: In this video, we
will further develop our applications
create wallet feature. Will add an OK button and a cancel button to the
create wallet dialogue. When the user clicks
on the OK button, his wallet will be created and the wallets name will appear
on the top of the screen. When he clicks on
the Cancel button, the dialogue will just close. We'll also add a restriction to the Okay button so that
it will be disabled by default and only be enabled when the name and the mnemonic
seed fields are fulfilled. Finally, we'll add an
optional password field so the user can only access its wallet
and transact with it after providing the
correct password. Let's add these new features first in our create wallet test. First, we will add a click
on OK button action. Then we'll expect the
stage title to be equal to the name of our application
dash the name of our wallet, which is my test wallet. Now let's create
the stage property. We'll initialize it
in the start method. Now, let's create another
test to verify what happens when the user tries to click on the Cancel button. For this, we'll
create a test with the name should cancel
wallet creation. So we expect that when the user clicks on new wallet and cancel, no exception will be thrown. Let's run these tests
to see what happens. The tests have
failed as expected. Let's implement these features. Go to the create wallet
dialog dot FXML file. Click on the dialog
pane on the left side. Then add an OK button and
this button types field. Click on plus, then
add a cancel button. Now we'll add the
password field. But first, let's add another
row in the grid pane. Now let's move the name, label and field
to the first row. Then we'll add a password label and a password field
in the next row. Let's also add an Fx idea of password to the
password field. Now, let's change the dialogue header
text to the following. While we're at it, let's make the mnemonic seed
field not editable. This is a security measure
because we don't want the user to change his
mnemonic seed accidentally. Change to the text view. We'll add some changes
to these buttons, tags in the Okay button, we'll add an Fx idea of okay. The Cancel button will erase its content and add
an Fx idea of cancel. We'll also add the
value of cancelled close to the Attribute
button data. We'll also add the cancel
string to the text attribute. This is needed because when we use the default cancel button, it's text gets translated
to the computer's language, and we don't want that. Now, let's make some changes to the create
dialogue controller. Will add a method called
initialize to it. But first, let's get
back to its FXML and add an Fx id of dialogue pain
to the dialogue pain tag. Now, let's add the properties with an FX ID to the controller. Will do that with the
properties password named dialog pain,
OK, and Cancel. Now back to the
initialized method. This method will
be executed after the initialization
of the dialogue. Will include code there to define the actions
of each button. First, we'll define the
action of the Cancel button. When a user clicks on it, the dialogue will hide. So we'll call the
Lookup Button Method of the dialogue pain passing the cancel button
as its argument. Then we'll add an
event handler to it, passing the action argument and a callback that will call the hide method of the
dialogue pains window. We'll do the same
to the Okay button. But we'll call the method create wallet in the callback instead. Let's include the same code to hide the window
when the create wallet method for now and run the application to
see what happens. When we click on the Cancel
button, the dialogue hides. The same happens when we
click on the Okay button. Good. But when we click on the X
on the side of the dialogue, nothing happens. Let's fix that. Go to the main
window controller. Will need to include the
following code to close the dialog window when we
click on the X button. Now, let's run the
application and try again. Good, that fixed it. Let's also change
the show and wait method called on the dialogue to simply show that
simplifies things a bit. Now, run the application again. Everything continues
to work as expected. But there's another
issue with the dialogue. It allows the
creation of wallets without a name and
a mnemonic seed. We want every wallet to have
a name and a mnemonic seed. So we need to disable the
Okay button if one is not fulfilled. Let's fix this. Go to the create wallet
dialogue controller. Now, we'll create a
private property of type Boolean binding called
all required inputs are full. We'll set this property in the initialized method
with the following code. Here we'll use a
Java anonymous class that extends Boolean binding. Then we'll initialize the
class with the bind method, passing the name
and mnemonic seed text properties
as its arguments. Then on the compute value method will include a code that will return true only if the name and mnemonic seed fields
aren't empty. Now, we'll create a getter
method for this property. Finally, we'll call the disabled property
method on the Okay button. Then we'll call bind on the
result and pass the get all required inputs are full method calling
the method on it. Let's make a little fix
in this method first, it must return a
Boolean binding. And let's remove this, get after it. Good. Now let's run our
application again. Okay, the Okay button
is disabled by default. If we type something in the wallet name field,
it stays disabled. But when we click on the Create
button, it gets enabled. Great. Let's run our tests again. That should create wallet test has failed because we hadn't implemented the code to change
the application title yet. Let's do that in the next video.
14. The Current Wallet Observable: In this video, we will implement
a feature that will make the Application
Load the wallet and show its name on the
application's title. After the user
creates, the wallet. Will create code that will be useful later when
we want to show other wallets features such
as addresses on the screen. After we implement this feature, this test will pass because the stage title will be equal
to BYOD W wallet space, dash space, the wallets name. So let's implement it. First. We'll go to the create
wallet controller. Then in this method, will create a Wallet using
a new service called create wallet service and
a method called create. We'll pass the wallets name, password, and mnemonic
seed to the method. Now we'll create
the wallet class. We'll add a new package inside
BYOD W, call it domains. Inside it will add a new
Java record called wallet. For now, we'll just add a string attribute called
name to this record. A Java record is a recent
feature of the language. Every record attribute has a built-in getter
and a constructor, so we don't need to add them. Now, import the wallet record to the create wallet
dialogue controller. Now, we'll inject the create wallet
service into this class. And we'll create
this class inside the API services package. Will add the component
annotation to this class. Let's inject this service into
this class's constructor. Now, we'll create
the create method inside the create
wallet service. For now, we will only return a new wallet passing the name as its parameter back to the Create wallet
dialogue controller. Now that we've created
the wallet will publish an event that will take the created wallet
as a parameter. This event will be
listened to by a class so that when the user
creates a wallet, the listener will update the current wallet loaded
by the application. Let's inject the application
context into this class. Now, let's create the
created wallet event inside the events package. Will then call the
super constructor passing the create wallet
dialogue controller. And we'll set a
new wallet field, assigning the wallet
constructor argument to it will also include a
getter for this field. Now, let's create a listener for this event called
created wallet listener. And we'll create it inside
the listeners package. It will implement the
application listener class with the created wallet
event as its parameter. Will then implement the
method on application event. It will call a method
from a new service called update current
wallet service. This method called update takes the events
wallet as a parameter. Let's inject this
new service into this class and
create the service. We'll add it inside a
new services package. Inside the gooey package. Will then create the
update method in it. We'll also add a Service
annotation to it. For now, the update method
will only set the name of the current wallet to the
newly created wallet name. Now, let's create a new package inside BYOD W
called observables. Inside this package will create
the current wallet class. Back to the Update Current
wallet service will instantiate the current
wallet in its constructor. In the current wallet class will include the
Component annotation. This class will represent the current wallet loaded
by the application. Every change the application
makes to this class instance will appear in the applications
graphical user interface. We included it in a
package called observable, because every property of this class will be unobservable. And observable is
a type of object in which every state
change can be listened to, enacted upon by other objects. The first observable property we'll add to it is the name. It'll have the simple
string property type. This type implements the
observable interface and triggers a change event when
it's string value changes. We'll instantiate it here. And we'll create a
getter for this field. Intellij idea identified that it is an observable and
created two getters. One for the property
called name property, and the other for the
property value called getName will also create a setter for this field to set the
name property value. Let's make this field final. Will also make a test for this create wallet
service class. We should have created the test before
implementing this method, since we're using TDD. But considering we'll increment this methods functionality
where forgiven. Let's create the test. Now. We'll first create a property for the create
wallet service and instantiated inside
the setup method that runs before each test. Then we'll create a test
called should create wallet. Given we have a name, a password, and a mnemonic seed. When we call the
method create from the create wallet service
with these parameters. Then we will expect
the wallet name to be equal to the
name variable. Let's run the test. The test has passed. Now, let's recapitulate
what we've done so far. When the user clicks
on the OK button, the create wallet method from the create wallet
dialogue controller will be called and
the wallet will be created by the wallet service. To do that, it uses
the method create, taking the name, the password, and the mnemonic seed
the user has chosen. For now, this method only returns a new wallet
with its name. Then we pass the
created wallet to a new created wallet event
and publish this event. When this happens,
it will trigger the created wallet listener
method on application event. It will receive the
created wallet event as its parameter. We must add the component
annotation to this class and inject the update
current wallet service in its constructor. So the application
event method will call the update method from the
update current wallet service, passing the wallet from the
event as its parameter. Finally, the update method. We'll set the name of
the current wallet to the name of the newly
created wallet. Now, let's go back to the
main window controller. Here will create an initialized
method which will be executed before the main
application window shows up. Inside it, will add a listener to the
current wallet property. Let's inject the current
wallet into this class first. Inside the ad listener
method will pass a callback that will take three parameters and observable and old value, and a new value in
the callback body will get the Windows stage and assign it to the stage variable. We must cast its type to stage. Then we'll set its title
to BYOD W wallet space, dash space, new value. The new value variable contains the new value of the name
of the current wallet. Whenever the current
wallet name changes, this change will be listened
to by this callback and the stage title will also change to the sentence
we've just said. Let's run the application
to check if this works. We'll create a new wallet
with the name test, then a mnemonic seed and click, Okay, great, it worked. The window title change
to BYU OWL a dash test. Let's create another wallet with a different name to
see what happens. As expected. It's changed to match the
newly created wallet. Now, let's run all our tests. Great. All tests have passed. Good job.
15. The Receive Bitcoin Tab: In this video, we'll start to implement a feature
that will allow us to create addresses that will be used later to receive Bitcoins. The first thing we'll do is
to create the Receive tab. This tab will contain the derived addresses
of our wallet. So go to the main
window dot FXML file. In the Scene Builder
will include a V box in the center
of the border pane. Inside the V box will
include a tab pane. Will delete the second tab that was created, automatically. Change the remaining
tabs text to receive. Inside the VBox
configuration menu will set the pref height to 300. Inside the Receive tab
will include a grid pane. For some reason,
we can't include a grid pane in a tab
using the Scene Builder. No problem. We'll do it
using the text editor. Instead. We'll include a label. It's text will be equal
to receiving address. Inside it will add some
tags to set its margins. Now, we'll include a text field. It won't be editable. It'll have 350 as
it's pref width. And its column index
will be equal to one, will include the same
grid pane margin tags that we use previously. The scene Builder is
not working anymore due to the grid pane we've
included in the tab. No problem. We'll see how the application
looks by running it. Great, our wallet is
now ready to receive an address in this new
field we've just created. Inside the create
wallet test will include a code to check
if an address was created and inserted in the receiving address field
after the wallet is created. So after the test clicks
on the OK button, it will click on
the Receive tab. And the test will look
up the content of the receiving address field and store it in the
address variable. Then the test will assert, if the address is not know, let's run the test
to see what happens. The test has failed as expected. Now, let's do some
refactoring and our main window FXML file. We see now that our main
FXML file is getting bigger, it's better to create
a new FXML file only for the receiving tab, which are main window
will refer to later. So let's create it. We'll name it Receive tab dot FXML will copy all the tab content to
the Receive tab dot FXML. The only change we will
make is to the tab tag. We'll change it to
f x colon root. Then we'll use the tab class
as the type attribute. We also need to include the Meta attribute XML and S colon FX. Now we'll import the other tags. Back to the main window dot FXML will delete the tab tag
and all its content. Will include the tag
Receive tab controller, which will create now. We'll create it inside
the controllers package. And let's import it inside
the main window dot FXML. Back to the Receive
tab controller will add the component
annotation to this class. We'll create a constructor
passing a resource with a value annotation pointing
to the Receive tab FXML file will also pass the
application context. Here we will configure a new
FXML loader instance with parameters similar to those we've used for the
other controllers. Then we'll set its
controller to this. We'll also set its
route to this. Then we'll call the
load method on it. Okay, we need to do one
more thing to make it work. Inside the gooey
started listener class, add the following code here. This code is necessary to tell Java FX how to build
custom components, such as the Receive
tab controller when it sees it inside the FXML files. This code will make Java FX use the context to
get bean method to build a component
when it sees a Receive tab controller tag. So whenever Java FX needs to build a Receive
tab controller tag, it'll use the Receive tab
controller class to build it. For other tags, it'll use
the default Java FX builder. That's it. Now Java FX knows how to build our
new customized FXML tag. Let's run our application
to check if all goes well. ***** and error occurred. It's saying that
the root controller isn't a tab instance. I know how to fix it. We must make the
Receive tab controller extend the TAP class. Now, let's see if it works. Okay, it's working
just as before. So when we create a new wallet, we expect the receiving
address field to be filled with the
new Bitcoin address. In the next couple of videos, we'll understand the
process of deriving a Bitcoin address and implement this feature
in our wallet. See, yeah.
16. Bitcoin Addresses and HD Wallets: In this video, we'll learn about HD wallets and
Bitcoin addresses. So what is a Bitcoin address? A bitcoin address is a word
encoding a public key, the hash of a public
key or a script hash. It is used to receive
bitcoins in a transaction. To receive Bitcoins through
a Bitcoin transaction, you show your address
to another person so that person can send
bitcoins to your address. There are several types
of Bitcoin addresses and the rules to generate
them are in many VIPs, such as VIP, VIP 3d2 and BIP 44. Let's now talk about HD wallets. Hd wallets stand for hierarchical
deterministic wallets. They're hierarchical
because they're composed of many levels of keys, and they're deterministic
because a single seed will always generate the same
addresses and private keys. And HD Wallet is a type
of wallet where from a single seed derivation pads are used to derive private keys, public keys and addresses. Hd wallets specifications
were originally defined in BI P32 with other BIPs extending
on that specification. Now let's recap a slide from
our last ppt presentation. In this presentation, we learned how to generate a mnemonic seed. And we mentioned
that by combining a mnemonic seed with an
optional passphrase, we could generate
a root seed using the algorithm P vk df two. From that root seed, we
could generate private keys, public keys, and addresses. In this presentation, we'll dive into the details
of that last part on how private and
public keys and addresses are generated
from a root seed. So beginning with a root seed, which has 512 bits in size, applying the HMAC SHA-512
hash algorithm to it, we obtain the master key, which also has 512 bits
from the master key. By applying the function CKD, which stands for
child key derivation, we can obtain different
extended keys by using different
indexes as parameters. If n is the index
for that function, than n can vary 0-2 to the
power of 32 minus one. Each extended key can generate more extended keys by using the CKD function and thus be the parent of many
children keys. This derivation schema allows
the formation of a tree of keys with an indefinite
number of generations. Let's see more details
about CKD functions. There are two types
of extended keys. They can be extended
private keys or extended public keys. And extended private key whose serialized version
begins with x PRV, or another letter plus PRV is generated using the
CKD private function. If the index pass
to that function is equal to or greater than
two to the power of 31, then it's a hardened derivation. And it said that the generated
extended private key is a hardened child. Hardened keys have
additional security features that we'll explain in short, it's possible to generate an extended public key by
using the CKD public function. Attended public keys
when serialized, begin with x pub, or another letter plus pub. It's also possible to generate an extended
public key from another extended public key by using the CKD public function, two derivations aren't possible. Generating an
extended private key from an extended public key and a hardened
extended public key from an extended public key. So how do we get from
extended keys to addresses? Starting with an
extended private key, we extract a private
key from it. A private key is part of
an extended private key. So this extraction is
a simple operation. Then by an operation called elliptic curve
multiplication, we generate a public
key from a private key. Finally, by hashing and
encoding the public key, we obtain a Bitcoin address. There are different types
of hashing and encoding of public keys to produce
different types of addresses. We'll learn more about these operations in
upcoming videos. We also use private keys to produce transaction
signatures, to transfer Bitcoins
to other addresses. Transaction signatures
are produced by using the Elliptic Curve digital
signature algorithm or ECDSA. You can also generate addresses with an
extended public key. You do that by extracting
the public key from it. Then a bitcoin address
is generated from the public key
through hashing and encoding, as explained before. Remember that a
public key cannot be converted to a private key. Thus, it cannot be used to sign transactions and
transfer Bitcoins. Now, let's understand the
concept of derivation paths, starting with the master key, which has the depth of zero
in a tree of extended keys. By deriving an
extended key passing zero as the argument
to a CKD function, we obtain an extended
key with a depth of one. Repeating the process with the newly generated extended key, we obtain an extended
key with a depth of two. Then we can repeat the process, passing one as the
index and obtaining an extended key with
a depth of three. From this last extended key, we can extract a public
key and an address. It is said that this address has a derivation path of
zero slash zero slash one because these were
the index is used to derive the extended key that
generated that address. Applying the same rationale. In this other example, we get an address
with a derivation path of one slash
zero slash two. This other example
shows that you can use other numbers as indexes to CKD functions at any
extended key in a tree of keys, allowing the generation of an almost infinite number of derivation paths and addresses. This other example
shows the generation of a hardened child by using a
single quote after the index, we can represent
indexes that generate hardened children in
a derivation path. Zero followed by a single quote, represents the first
hardened index, which is two to
the power of 3011, followed by a single quote is the second harden
index and so on. This notation is used to
facilitate the description of a derivation path so that we can use small numbers
to describe them. It's important to add that. To spend Bitcoins
from an address, it's necessary to produce
a private key with the same derivation path
used to derive that address. So you can use CKD
public functions to produce extended public
keys and addresses. But to spend funds
from those addresses, you must use CKD
private functions to produce extended private
keys and private keys. Another characteristic of
HD wallets and they're derivation paths is their
death function relation. Each level in a tree of keys
is assigned a function. Bi P32 defines the depth, one represents accounts depth two different chains and depth
three different addresses. Bip 44 came with different definitions
which are more used nowadays in
most HD wallets. According to be at 44 depth one hardened extended keys
represent different purposes. Depth to harden keys
different coin types, depth three hardened
keys, different accounts. Depth for defines if descendant addresses are
to be used for change are not in a Boolean manner and death five represents
different addresses. To better understand how
that works in practice, Let's see some examples of derivation paths and
their functions. And addressed with a derivation
path of 84 quotes slash slash zero slash zero slash zero has a purpose to
find and BIP A14. Vip 84 defines that
addresses with a purpose of 84 must be P2 WP k h addresses, which stands for pay to witness public key hash addresses. We'll talk about
different types of addresses in detail
in upcoming videos. But in short, P2, WP k, h, aka segue, native addresses are
the most common type of addresses in most
updated modern wallets. This address also has a
coin type of zero quote, which means that it's
a Bitcoin address. It also has an account of zero, meaning that it's part of
the first user's account. It's changed. Index is set to zero, meaning that it isn't
a change address. Finally, it's address
index is zero, meaning that it's the first
users receiving address. The next example has a derivation path similar
to the previous example, except that it's
address index is one. So it's the second Bitcoin segue native receiving address of
the first user's account, applying the same rationale. The third example is the first Bitcoin segue
native change address of the first user's account. The index one in depth four indicates that it's
a change address. The last example is
similar to the first one, except it's purpose
is 49 unquote. Vip 49 defines that addresses
with that purpose must be P2 WP k-th nested
in P2 SSH addresses. If that sounds
complicated, don't worry, we will learn details about
it in upcoming videos. Now, let's do an overview
of HD Wallet features. With HD wallets, we
can easily generate an almost infinite number of addresses from a single seed. This is great since we can avoid address reuse and
loss of privacy. Hd wallets also allow multiple
account structure where the parent extended private key has access to their
children's key funds, but the children can
transfer the parents funds. This schema can be useful for cases such as company owners having access to children's keys from their company's
departments. Another useful feature
that HD wallets allow is the creation
of watch only wallets whose addresses can
be generated and their funds audited using
only extended public keys, but where no funds can be moved since no transactions
can be signed. Another HD Wallet feature
is the possibility of generating other
cryptocurrency addresses from the same mnemonic seed. A list of different
coin type indexes is described in slip 44, a BIP like proposal
system from Satoshi labs, the company responsible
for the truss or wallet. Another feature that HD wallets have is that their addresses and private keys can
be reconstructed with their seeds and
derivation paths. This allows you to import a mnemonic seed and
passphrase into a compatible wallet to retrieve all your addresses
and private keys. Security compartmentalization is another nice feature
of HD wallets. If an extended private
key leaks by accident, that would affect only
the funds locked in addresses descending
from the leaked key. Funds locked in a parent or
siblings keys would be safe. Finally, HD wallets facilitate generating an organizing
different types of Bitcoin addresses. Now let's see why hardened
keys are necessary. During HD Wallet development, it was discovered that if an extended public key and a non-Hodgkin child private key from the next level leak, this would be
equivalent to leaking apparent extended private key. This could lead to fund
loss for every address descending from the leaked
extended public key. Hence, pardon derivation
was created to prevent that exploit from happening
at account and lower depths. Finally, let's discuss
how we'll implement the address derivation
schema in our wallet. Will use the derivation
path 84 quotes slash slash zero slash zero slash zero to generate our first
receiving address. After receiving Bitcoins,
the next receiving address will be changed by increasing
the address index by one. So the next receiving
addresses will have address indexes
of 123 and so on. Doing so prevents us from using the same address to receive
Bitcoins more than once. Will use the derivation
path 84 quotes slash, slash, slash one slash zero to generate our first
change address. After sending Bitcoins
and receiving a change, the next change
address is changed by increasing the
address index by one. The same way we'll do
for receiving addresses. You may ask why avoiding
address reuse is important for two main reasons. First, by doing that, we increase our privacy. By doing so, if a third party discovers that an
address belongs to us, that information won't replicate to other addresses we use. The other reason is security. If for some reason
the private key for an address leaks that will
affect only that address. The proverb that says, don't put all your eggs in
one basket applies here. Having said that,
we'll implement the derivation schema by first generating the extended
public key with a path equal to 84 quotes slash zero slash zero slash zero
for receiving addresses. And the extended public key with a path equal to 84 quotes slash slash zero slash
one for change addresses. Then we'll derive their children extended public
keys and addresses. By doing so, will
generate all addresses we need without exposing any
private keys to memory. So our main security
policy is to avoid exposing seeds and private keys to memory as much as possible. Our private keys will be derived only when signing
transactions using the same derivation
pads used to produce the addresses from which such transactions are
spending Bitcoins.
17. Creating our wallet’s extended public keys: In this video, we'll create the extended public
keys that are wallet will need to
create addresses. As we saw in the latest video, our wallets will initially
have to extended keys, which will be the extended key for segment receiving addresses, and the extended key for
segue change addresses. First, let's fix some
issues with our GUI tests. If you try to run the
create wallet test, now, you'll see
that it will work. This is partly due to a premature instantiation of our new Receive tab
controller class. To fix that, create
a new package called Config inside the
BYOD W test package. Then create a new
class called test lazy being an IP
configuration in it. This class will implement the bean factory post-processor will implement this required
method with this code. This code will make every class during tests be
initialized lazily. There's another piece
of code that we must add in our create wallet
test for it to work. We'll add to the FXML
loader in the start method, the same builder
factory we've set in our GUI started
listener class. Now let's run our GUI tests. Great. Now the tests are working. We see that only they
should cancel tests have passed and the other we
still have to make it pass. Now, let's proceed to add the extended public
keys to our wallet. The first thing we'll
do is to adjust our create wallet service test will pass a valid mnemonic
seed to this variable. You can obtain it on the
Project and Resources page. Then we'll verify if the created wallet has
extended public keys. Will assert if it has two
extended public keys, since every wallet will have one extended public
key for receiving addresses and one extended public key for change addresses. Now, we'll add a list of extended public keys
to the wallet record. Let's create our
extended pub key class. It will have a key and a type. Let's add them to a constructor. Both properties
will have a getter. Now we'll create an address
configuration class. First, let's create
a new config package inside the API package. Then we'll create the address
configuration class in it. Add the Configuration
annotation to it. This annotation is necessary for Spring Boot to initialize all objects defined in this class during
application startup. Now we'll create a domain
class called address config. Actually, it'll be a record. It'll have an address type
and the derivation path. Then create an enum called address type in
the same package. It'll have two types, segment and segue with
underscore change. Now back to the address
configuration class. We'll create a beam for
our segment address. In Spring Boot, a bean
is just an object which can be injected into
other project classes. Will instantiate this bean
with these parameters. And add the Bean
annotation to this method. Passing the segment string as its parameter will duplicate this method and modify it to be our change address config, change the Bean annotation
parameter to segue change. The method name will be
segue change config. The address type will
be seg would change. And the derivation path will
have a change index of one. Now, we'll use these
configurations in our create wallet service. So let's inject a list of address configs into this class. By doing so, Spring
Boot automatically identifies that there are two beans with the
address config type, and it will inject
both into this list. Now, in the create method will create a mnemonic
seed variable. First, we'll change the
mnemonic seed method parameter to mnemonic seeds string will then instantiate a mnemonic seed object passing the mnemonic seed string
as its parameter. Then we'll create a master key, which is a type of
extended private key. To do that, we'll call the mnemonic seed method
to master key and pass the password and this
constant as its parameters. But first, we have to update our Bitcoin Java version to 0.3, 0.0 in the POM file. Let's do it. Now. Click on Load Maven changes
back to the create method. It now allows us to import
the main net prefix constant, will pass its private
prefix as a parameter. Now let's add this code, then I'll explain
what it does next. This code starts converting the address config
list into a stream. In Java eight or above, streams are used to apply functional operations to
a collection of data. In this case, each
address config from the list is being passed
to this lambda function, which will use the extended
pub key service will create next to return an
extended pub key object. Each returned extended pub
key will be collected in a list which will be stored in the extended pub keys variable, will then pass the
extended pub keys list to the returned wallet. Now, let's inject the extended pub key
service into this class. We'll have to create it. Now we can add it
to the constructor. Now will create
its create method. It'll return an
extended pub key. Following the TDD. It's time to create a
test for this method. Let's do it. Will create it inside
the test API package. It must extend the
specification class. Let's instantiate the extended
pub key service here. We also need to add this piece of code to the setup method. And we'll create a test called should create extended key. When given a mnemonic seed and a master key
derived from it. When we call the
create method from the extended pub key service. Then we expect that the
created extended pub key key is equal to the
expected extended pub key. Okay, so how will we create
these variables in gray? We'll pass them using the
where clause like this. You may ask, where did
I get these values? I got them on the EN Coleman io slash pip threonine website. This website is very
useful for generating many types of extended
keys and addresses. I'll copy and paste
this mnemonic seed to the BIP 39 field on the website to
demonstrate how it works. Immediately after we
pasted the mnemonic seed, the tool calculated the
root seed master key and extended keys to show the derived BIP AT
for extended keys, click on this tab. We see that the
expected pub key and the test is equal to this
value on the website. By applying the CKD function to this extended public key with different numbers as
indexes will later obtain extended
public keys whose extracted public keys
will be used to generate receiving addresses
using the same website I created for more test cases. I'll paste them here and
adjust the formatting. The last two test
cases will generate extended pub keys that will be used to generate
change addresses. You can copy them from this
video's resources or make your own test cases using
the Ian Coleman website. Using the where clause, the same test will run for each parameterization
we created. Let's run the test. The test failed as expected. Let's now implement the method. First. We'll create a map here
which will not address types to extended
pub keys prefixes. Both address types will map to the same main net
segment prefix. By doing so, we make
it easy to extend this functionality if we need to add more addressed
types later. Now in the create method will return a new
extended pub key. Passing these parameters will pass the return of the CKD
method from the master key, which will take a
derivation path. False since it's not an extended private key and the prefix of
the address type. Then we'll serialize the result. And the second extended
pub key argument will be the address
type and string form. Finally, let's not forget to add the service
annotation here. Now, let's run the extended
pub key service test again. Great, they've all passed. Now let's run our create
wallet service tests. But first, let's make some
changes to this test. We must add the
security provider, which is necessary for the Bitcoin Java cryptography
functions to work. We also have to set the correct arguments
for the service. Let's build the address configs and the extended
pub key service. Now let's run it. Great, the tests have passed. That means our wallet
was created with a list of extended
public keys of size two. Oops, the change
index here is one. It won't affect this
test since we're just asserting that the wallet will
have to extended pub keys. But let's change it to one
for the sake of correctness.
18. Generating Segwit Addresses: In this video, we'll
create a dresses for each extended pub key
that the Wallet makes. To do this, each
extended pub key will be passed to the
generated dresses method, which will create now. This method will set a list of addresses to the
extended pub key record. Let's create this center. This list of addresses
will be created by a new address sequential generator service
that will create. It'll use a generate
method which will take the extended pub key key and
the extended pub key type. The address sequential
generator service will have this name because it will generate a dresses with an increasing address
derivation index. Let's inject the address
sequential generator into this class and create it. Now, let's create
its generate method which will return a
list of addresses. Let's create the address record. This record will have
an address field and an index field. Let's adjust the set
addresses method from the extended pub key. It'll take a list of
addresses and set the extended pub key
addresses field. Let's create a test for the address sequential
generator class. For now, it's method
will return null. The test class will extend
the specification class. We'll create a test called
should generate 20 addresses, will use the address sequential generator in the wind block. So let's add it as a
field to this class. When the generate method is
called with these parameters, that is an extended public
key in the segment type. Then we expect that
the return that dresses list has a size of 20. You can copy the
extended public key of this test from this
lesson's resources. Let's instantiate the
address sequential generator in the setup method
and run the test. It failed as expected. Now let's implement the service will declare an
extended pub key. Then inside a try
catch block will and serialize the
received key parameter. The ANS serialized
method transforms the extended pub key key in the extended pub key object from Bitcoin Java will then wrap the IOException in a runtime exception
in the catch block. Now we'll use the range method from the lungs stream
class to loop from zero to an initial number of generated addresses variable
that will set later. We'll inject this
variable into this class. In the constructor will set a qualifier annotation
for this variable, which will take the same
variable name as a parameter, will change its type to hint, will map over each index and call the generate
address method. So for each index, we'll call the generate
address method, passing the return of the address generator
factory Get method, the extended pub key and the
index as its parameters. Finally, we will convert
the result into a list. Let's create the generate
addressed method. It'll take an address
generator and extended pub key and an index. The address generator
will be passed from an address
generator factory, which will inject into
this class and create now. Let's change the type
parameter to address type. Now we'll create the address generator factory Get method. It'll return an address generator interface
that will create now back to the
generate address method from the address
sequential generator. We'll first derive an
extended child key from the extended pub key
using the CKD method. This method will apply the child key
derivation function we learned about two videos ago. It'll take an index which will define the child's
derivation index. Now will return a
new address record. It'll take the result of the generate method from
the address generator. This method will take
the extended child key and derive an address from it. The second address parameter
will be the index. Now we'll add the
generate method to the address
generator interface. Now we'll implement
the get method from the address
generator factory. It'll return the result of the getMethod from an
address generator map. This variable will be
a field in this class and will be initialized in
this class constructor. It'll be a map of strings as keys and address
generators as values. In the constructor will pass a segment address generator
service, which will create. Then we'll map each address type to the segment
address generator. In the address generator map. The segment address
generator will implement the address
generator interface. Let's create the required
interface method. Before implementing it,
Let's create its test. It'll extend the
specification class. Will instantiate the segment
address generator here. In the setup method will add the security provider just like our other
tests that need it. Then we'll create that should generate segment address test. In the given block will
create an extended pub key using the extended pub key
class on serialized method. The serialized method will take an extended pub key string, which will pass in
the where block. Then we'll create an
extended childcare calling the CKD method on the
extended pub key variable. To this method will pass an index parameter which will
pass through the web block. When the segment address
generator generate method is called with the
extended child key. Then we expect that
the return address will be equal to the
expected address. Now, let's use the
where block to initialize the variables in gray will use test data they've taken from the E and Coleman
website for it. You can copy these parameters from this lesson's resources. Let's run the test. It's failed as expected. In the next video, we'll learn more about
sig what addresses. Then in the video
after the next, we'll implement the segment
address generator and the sequential address generator and make their tests pass.
19. Understanding Segwit Addresses and Introduction to Bitcoin Network Environments: In this presentation,
we will learn more about Bitcoin
segment addresses, which will be the
default address type that our wallet will provide. So what is a Bitcoin
segue, what address? Segment addresses are
also known as segue native addresses
back 32 addresses, segment version zero addresses
or P2 WP k-th addresses, where P2 WP k h means pay
to witness public key hash. Each of these terms is used
to refer to the same thing. It was the most up-to-date
type of Bitcoin address until November 2021. After November 2021 when the taproot upgrade was activated
on the Bitcoin network, a newer type of Bitcoin address
called taproot address, aka segue version one
address became available. For now, let's focus on
the segment address, which most modern wallet support segmented addresses
became available in 2017 on the Bitcoin network after the segregated
witness upgrade. This upgrade optionally allowed smaller transactions
since the part of these newer transactions
called witness is sent separately and is not
included in the blockchain. The rules to generate segment addresses are
described in VIP one-seventh three and are also
implemented in methods of the
Bitcoin Java library. Segment addresses allow
Bitcoin senders to pay the cheapest fees
for common transactions. The reason for that is because transactions that sends
bitcoins to segment addresses are effective smaller
in comparison to transactions that send to
other types of addresses. But we'll learn
more details about Bitcoin transactions
in future videos. Because segment addresses
are well-supported by most modern wallets
and because they allow us to send
cheaper transactions, we'll set them as the default
address type of our wallet. Now, let's recapitulate
a slide from the previous presentation
about extended keys. In that video, we learned that we could
obtain a public key from an extended private key
or an extended public key. Then by hashing and
encoding the public key, we could obtain an address. Now, let's see
more details about this hashing and
encoding process. Starting with a public key obtained as
previously explained, by applying the SHA-256 and the right-hand 160 cryptographic hash functions to
the public key, we obtain a public key hash
with a size of 20 bytes. The SHA-256, combined
with the right-hand 160 is called the
hash 160 algorithm. Finally, the public key hash, combined with a prefix
and a numeric version, generates a segment address
through Beck 32 encoding. Back 32 encoding is reversible by the process called
Beck 32 decoding. When building transactions,
we must decode back 32 addresses to
public key hashes before including them
in transaction outputs. That's why this address
type is also called pay to witness public key
hash will revisit this topic in greater
detail when talking about transactions for
segment addresses. When Beck 32 encoding
public key hashes, the version is set to zero when the prefix is set
to BC unnamed net. Speaking about main net, let's quickly overview
Bitcoin network environments. Each of these environments
is a separate network with different blockchains and
purposes, but similar rules. The Bitcoin may net is how it's called the main
Bitcoin network. It's where the
bitcoins have value. The blockchain is immutable and the mining difficulty
is the highest. The test net environment is similar to the main net but has a lower mining difficulty and where bitcoins have no value, it is used to make tests
in an environment where nodes interact while maintaining
a shared blockchain. Test net has had
different versions since it was created with
different blockchains. Finally, the reg
test environment is an individual
network that can be run by any Bitcoin node user with a blockchain not
shared with others. The blocks can be
generated instantly, effortlessly and
freely by the user. It is used to make faster
and more automated tests in a controlled environment. Addresses on each network have different prefixes and are valid only in their
environments. For segment
addresses, these C is the prefix used for red
dresses on the main net. Tb is used for addresses
on the test net. And BCRP is the prefix used for red dresses
on the reg test. As said before, version zero is used for
segment addresses. Version one is used
for taproot addresses.
20. Generating Segwit Addresses – Part II: In this video, we will make
all tests we've created paths to do it will first implement the segment
address generator method. To generate a
segment address will convert an extended
key to a public key. Let's change this parameter
name to extended key. Then the public key will be converted to a segment address using this method as its
parameter will pass a prefix. Let's pass this
constant for now, which will make the
address valid in the main net Bitcoin
environment. Let's also add the service
annotation to this class. Now, let's run the segment
address generator test. It's failed because
we have to bump the Bitcoin Java
version to 0.4, 0.0, which contains the
right CKD method for this operation to
work. Let's do it. Click on Load Maven changes. Let's run the test again. Great, all tests have passed. Now let's adjust the address
sequential generator test. We have two new
constructor parameters that we have to pass
to the service. First, let's instantiate the
address generator factory. Passing a new seg would
address generator. Then we'll pass 20 is the initial number of
generated addresses. Now let's run this test again. Okay, we forgot to add
the security provider in the setup method.
Let's fix it. Run it again. Okay,
The test has passed. Some services are missing
the service annotation. Let's fix that. Okay, now
let's add a new Bean, which will be the
initial number of generated addresses
for each extended key, will arbitrarily set it to 20. Now let's run our application
to see how it's working. We got an error in the
console indicating that our application is missing
the security provider. Let's add it to
the main method in the BYOD W application class. Let's run our application again. Okay, the error didn't occur, but the receiving address field is still missing an address. Let's change this. First. Let's run all our tests to check what other tests
need to be fixed. The create wallet service tests are broken. Let's fix them. First, we have to
include a valid address sequential generator object
as its third parameter. Let's instantiate
it passing 20 as the first parameter and an address generator
factory as the second. Then we'll instantiate
the factory just like the previous test. Now will ensure that the
create wallet service is creating a wallet with
two extended pub keys, each with 20 addresses. Will assert that the first extended pub key addresses field has a size of 20. Let's create a getter
for this field. Will assert the same for the
second extended pub key. Now let's run this test. Great, it's passed. Now let's make the
create wallet test pass. Remember that we expect the receiving
address TextField to contain an address after
creating a wallet. So let's go to the
receiving tab controller. Will add a method
called initialize, which will run after
the application initializes this controller. Then we'll inject the current
wallet into this class. In the current wallet
class will include a new simple string
property field called receiving address, which will initialize
right here, will create setters
and getters for it. Now, let's go to
the Receive tab. Fxml will add an Fx
ID to the TextField. This ethics ID will be
called receiving address, and we'll also add it to the
receiving tab controller. Now, in the initialized method will bind the current
wallet receiving address property to a
listener that will take these three parameters
in a Lambda function. In the Lambda body will set the receiving address
field to the new value, which will be an address. Now in the update
current wallet service will set the current wallet receiving address to
the first address of the first wallet
extended pub key. Now, when the receiving
address is changed this way, it will trigger this
listener that we've set here in the Receive
tab controller. And hopefully it will add the address to the
receiving address field. Now, let's run
these tests to see if they work. Groups. It's failed. Let's find out why. Okay, the error happened because this method must be
initialized, not initialized. Let's fix it and run
the application. Great. The wallet did what we
expected and fulfilled the receiving address field
after we've created a wallet. Now, if we present this
address to someone, they can send bitcoins to it. Now, let's run all
application tests. Great, they've all passed.
21. Adding Support for Main Net, Test Net, and Reg Test Environments: In this video, we'll make a quick refactor in the
segment address generator. Instead of hard-coding the
main net segment prefix as the parameter for the
segment address from compressed public key method will create a service
that will allow us to create addresses for the test net and reg
test environments. We'll call this service
address prefix factory. It'll have a get method
where we'll pass an address type and it'll
return an address prefix. This returned value will
be used as a parameter for the segment address from
compressed public key method. Now we'll inject this service into this class and create it. Now we'll create
It's GET method. To implement this method
will need an auxiliary map, which will map address
types to another map, mapping the three
environments to their corresponding
address prefixes. Let's build it and it'll
become clear what I've meant. Now, let's change this
parameter name to address type. In the getMethod will return an address prefix by using the address type
as the first key. And a variable called
Bitcoin environment as the second key of the
map we've just created. The Bitcoin environment
variable will be defined later in our
application properties file, and we'll inject it
into this class. Now. In the constructor, we'll add
a value annotation before the variable to inject its
value from the property file. Now, let's properly type
the address type map field. Now let's add the Bitcoin
environment configuration to the application
dot properties file. We'll set it to reg test. Now let's fix some test files to handle the changes
we've made so far. Beginning with the segment
address generator test, will delete this
part and instantiate the segment address generator
in the setup method, passing in the address prefix
factory as its parameter. Now we'll instantiate the
address prefix factory, passing the main net
constant as a parameter. Now let's run this test. Great, it continues working. Now let's fix the create
wallet service test. We'll do the same
as the last test. Just inject the
address prefix factory into the segment
address generator. Let's run the test. Great, It has passed. Now let's fix the address
sequential generator test. We'll do the same as before. Now, let's run the test. Great, it's passed two. Now let's run all our tests. Some tests failed because
the address prefix factory is missing an annotation.
Let's fix it. Run the tests again. Great. All tests have passed. Now let's make this
little refactor of adding static imports
for these constants. Now, let's run the application to see what we've
accomplished so far. Remember that now we expect
that our wallet generates reg test compatible
addresses since our Bitcoin environment
property is set to reg test. Okay, as expected, our wallet generated addresses
beginning with BCR t, since it's the prefix of segment addresses in the
reg test environment. Now let's change the Bitcoin
environment property two main net and run
the application again. Now we have addresses
beginning with BC, which is the segment address
prefix for the main net. Finally, let's change the
Bitcoin environment property to test net and try the same. Okay, Now we can generate
addresses beginning with TB, which is the prefix for test
net compatible addresses. Great, Now we have an
easy way to change the network environment
compatible with our wallet.
22. Preparing our Bitcoin Core Node for Communicating With Our Wallet: We now have a Bitcoin wallet capable of generating addresses compatible with
different Bitcoin network environments
which are reg, test, test net and main net. It's already possible to use these addresses to
receive Bitcoins. To do that, you just need to show them to other
bitcoin owners, which can use their wallets
to send you Bitcoins, or most likely Satoshi, which are Bitcoin fractions. But for now, our wallet is enabled to detect
these payments. So nothing will happen in our wallets user-interface after a transaction to one
of our addresses. From now on, we aim
to change that. We'll start to build a feature that will allow us to listen for transactions that arrive
in our Bitcoin core node. Then we'll filter the
transactions with outputs that match our
current wallet addresses. Finally, we'll modify
our wallet to show information about the amount
of Bitcoin our wallet holds. Now let's make sure that our Bitcoin node is
correctly configured and prepared to send and receive transaction information
to our wallet. To do that, open the
bitcoin.com file. The location of this file will depend on which OS you're using. Since I'm using Windows ten, my bitcoin.com file is located
at the path on the screen. Open the file and make
sure it looks like this. Let's review the meaning of
each of these configurations. The first parameter,
reg test is set to one to make our node run on
the reg test environments. Since we'll use the
reg test environment to help us develop
our application. The second parameter, data there is set to the folder that you want the node to record blockchain and
wallet information. I set mine to the colon
backslash Bitcoin data, but you can choose
any path you prefer. The server parameter
is set to one to allow our node to receive API
calls from our application. The RPC user and RPC password parameters can be
set to whatever you like. For now, I'll choose
BYUI W for both. But when using our
application for real, It's highly advisable to choose safer values for
these parameters. The TX index is set to one. This is necessary to enable
our node to retrieve any transaction information
from the blockchain. The fallback fee parameter
can be set to 0.00 001. This value will be used
for transaction fees that could not be calculated
appropriately by the node. Finally, we'll set the z MQ pub Ratti x
property to this value. This property sets
the TCP address where the transaction information that arrives at the node goes. Our application, we'll listen
to the same TCP address and capture new transactions sent to one of our Bitcoin
addresses this way. Now, let's run our
Bitcoin node with these configurations
using the terminal, go to the folder where your Bitcoin application
is installed. This depends on
where you installed it and which OS you're using. Since I'm using Windows ten, I already opened my terminal and went to this
folder on the screen. Now run the Bitcoin
node by typing dot slash Bitcoin D
and pressing Enter. Now your Bitcoin core node is running with the
configurations. We said previously, we are now ready for
the next classes.
23. Creating the Node Client for Creating and Listing Wallets: In this video, we'll create an HTTP client that will make some calls to our
Bitcoin core node. This client will be
necessary later when we'll use it to send and
receive transactions. First, we will import a
dependency in the POM file. This dependency is the
Spring Boot starter web, which is necessary for the
HTTP client implementation, will exclude a transitive
dependency from this library by adding
an exclusion tag. Excluded dependency will be the Spring Boot starter Tomcat. We'll do it to avoid starting a web server in our application. Now, click on the load
Maven Changes button. Now let's make a configuration
class for the node client. Create a class
called Node client configuration inside
the api dot config package at a configuration
annotation to it. Now, we'll create some private
properties in this class, all preceded by a
value annotation. The first one we'll have
this value annotation. And it'll be called
node RPC URI. The next will have
this value annotation. And it'll be the
node RPC username will have another property with the following value annotation with the name equal
node RPC password. All these properties will be injected from values
will add later in the application dot properties file will also create a beam with the rest
template class. It'll take a rest Template
Builder as a parameter. We'll use this builder to return arrest
template object with a root URI equal to the
note RPC URI property. It will have a basic
authentication with the note RPC username and the note RPC
password properties. Then we'll call
Build and return. Now let's assign the properties that will be injected
into this class. Let's go to the application
dot properties file. Let's assign the
node dot RPC dot URI property the
following address. This URI with port
18443 is the default URI for the Bitcoin
Core Node RPC API for the reg test environment. As the node dot RPC
dot username property, you have to choose the
same value you've chosen for the RPC user
configuration in your bitcoin.com file as the node dot RPC
dot password property, choose the same value
that you've set for the RPC password configuration
in your bitcoin.com. Now inside the api
dot services package, create the node package, incited, create the
client package. Now let's create the node
client class inside it. Let's add the service
annotation to it. Now, let's inject the rest
template bean into this class. We'll add the following
method to make requests to the Bitcoin RPC API. As for its parameters, it'll take a string method, a parameterized type reference of type node client response, which will create next a string URL and zero
or more objects. Let's now create the
node pliant response inside the package
domains dot node. Back to the node
client method will instantiate a new node
client request object. It'll take the method and params arguments in
its constructor. Let's create this new class inside the domains,
that node package. Let's create its constructor. Now, we'll wrap the request
inside an HTTP entity object. Then we'll return the
result of this method call, which will finally
make a post request to the Bitcoin Core Node, will adjust the node
client response class, will transform it into a record and add a result property to it will make a similar adjustment
to the node client request, will transform it into a record and add
these fields to it. We've forgotten to
put a T right here. Now, we'll see how to make an important call to the
Bitcoin Core Node API. Go to this website
on the screen. It's a reference to
the create wallet Bitcoin core RPC call. We see in the last line
that this called takes a method key with the value
create wallet in its body. Every Bitcoin API call has this format with the method key containing the
name of the method. Bitcoin API calls also have a params key containing
various types of data. In this case, it's value is an array with
only one element. The wallet name, the
create wallet called creates a wallet inside the
Bitcoin Core and loads it. Many API calls we'll
use later required. But our Bitcoin core node
has a loaded wallet. Back to the node client. We can now understand why the make request method
builds a request object which takes a string argument
called method and a variable type
argument called params. Now let's create a class called node create
wallet client. Let's add a Service
annotation to it. Will inject the node
client into it. Now we'll create a
method called create, which will take a
string parameter. Now to make a call to the create wallet method
of the Bitcoin Core API. We'll call them make request method of the node
client like this, following the Bitcoin
Core website reference. We'll now test this
method for that. Let's run our Bitcoin Core
Node in the terminal. Instead of using our traditional
TDD way of doing things, we'll test this
method differently. Back to the node create
wallet client will create a method preceded by a
post construct annotation. The Post construct
annotation is useful for executing code when a spring
application is starting. It's also useful for fast testing and
Spring applications, almost like creating a main static method in a Java class, but with all the benefits of the beans managed
by the application. Inside this method will call the create method
passing any wallet name. Let's run the application. Okay, By watching the Bitcoin Core Node
logs in the terminal, we see that our node
created and loaded a wallet with the name we've
said in the method call. With that, we know
that our method is working and can remove this
post construct method. Now, let's go to the
Bitcoin Core website again and search for the
list wallet DIR method. This method takes
no parameters and returns all node
wallets created so far. It returns a JSON structure
with a wallets field. This field contains
an array of objects, each one representing an existing wallet
with a name field. Let's create a client
for this method. We'll call it node list
while it's client. Again, let's inject the node
client into this class. Then we'll create
the list all method, which will return a
list of wallet names. Inside this method will call them make
request method from the node client passing list wallet DIR as
the method name. A new parameterized type
reference and an empty URL. We won't pass any
additional parameters. The returned object will
be a node wallets record, which will create now, we'll create it inside the
domains dot node package. This record will
have only one field. It'll be a list of node wallets. Its name will be wallets. Let's create the node wallet
record in the same package. It's only field will
be a string name. Remember that we're modeling
these objects based on this reference from
the Bitcoin Core website. But wallets field on the node wallets record
matches the name of this field on the JSON
response of the API call. The same happens with the name field of the
node wallet record. The wallets field from
the response will have a list of objects
with the name field. Each of these objects will be stored as NerdWallet records on the application back to the
node list while it's client. From the list, all method will return a list of wallet names. To do that, we'll get the
wallets from the node wallets. Then we'll call stream, then map passing a reference to the node wallet name field. Then we'll convert the result
to a list and return it. Now, let's test the list all method using a method
annotated with Post construct will
print each element returned by the list
all method call. We see by this line on
the console that the application log the name of the node wallet we've created. Great. Now we'll implement
another client to call the Node API method
called list wallets. The difference
between this method and the list wallet DIR is that the list wallets will return only the currently loaded
wallets by the node. It also returns a list of wallet names without
wrapping them in objects. Now let's create the
list loaded method. It'll return a list
of wallet names. To do that, we just have to
return this method call. Now let's test it the same
way we've tested the list. I'll call. We see from the console, but our application log two times the wallet
name we've created. The first was due to the
list all method call, and the second was due to
the list loaded method call. Now, to see the difference
between the two method calls, let's do the following. Terminate the Bitcoin
Core Node application by pressing Control
plus C on the terminal. Now start the Bitcoin
Core Note again. Now let's do the same test. But first, add
these lines before the method calls to differentiate
between each method. From the console, we
see that only the list all method call returned
the created wallet. This demonstrates that after we start the Bitcoin Core
Node application, we have to load the
wallet will use, so we can make other API calls that need a node wallet loaded. But we'll see how that works in practice in the next videos.
24. Node Clients for Loading a Wallet, Get a New Address, Checking our Balance and Mining Bitcoins: In this video, we'll continue to implement more
methods that will allow us to interact with our Bitcoin node from
our application. The first one is a method
that will load a node wallet. When you run your node, you need to load a
wallet to be able to call many other node methods. So let's create the node
load wallet client. As always, at a Service
annotation to it. Now, we'll inject the
node client into it. Then we'll create
the load method, which will take the wallets
name as its parameter. Then we'll simply call
them make request method from the node
client like this. Now, let's test this method
using a post construct. First, let's remove this post construct method
we've created before. Now we'll call the load method passing the name of
the wallet we created. Before running it, let's run our Bitcoin node
in the terminal. Okay, from the terminal logs, we see that our node
correctly loaded our wallet. Now we'll create a
useful service to load a node wallet if it already
exists and is not loaded. If a wallet with that name
already exists and is already loaded by the node
than the method won't do anything
and we'll return. Finally, if a wallet with
that name doesn't exist, the method will create
it and the node will automatically loaded since it loads a wallet
after its creation. So in the services
dot node package, Let's create a class
name node load or create wallet service. Let's inject the following
clients into it. The node create wallet client, the node load wallet client, and the node list
while its client. Now let's create a method
named load or create wallet. It'll take a name
as its parameter. Inside it will call the
list all method from the node list
wallets client will store the result into a
variable called all wallets. Then we'll call the
list loaded method from the same client. And we'll store the result into the loaded wallets variable. Now, we'll add an if statement checking for the
following condition. If this condition
evaluates to true, it means that the
wallet with that name already exists and it's
loaded by the node. If that's the case, we don't need to do
anything and can return. Now, we'll add the
following if statement. If this statement returns true, it means that a wallet with
that name already exists, but it's not loaded by the node. If that's the case, we'll call the load method from the node load wallet
client passing the name of the wallet and return. Finally, if the code execution evaluates to false the
previous IF statements, it means that a wallet with
that name doesn't exist. In that case, we simply
create the wallet, calling the create method
from the node create wallet client and passing
the wallets name. Okay, The method is finished. We'll use it in the next videos. Now let's create
another useful client. It'll be called Node
get new address client. As its name suggests, it'll be used to generate a new address from our
loaded node wallet. So let's inject the
node client into it. Now, we'll create a method
named get new address. It will return the
address in a string. And it'll take the wallet
name as its parameter. Then we'll return to
make request method call from the node client
with these parameters. Notice that we've set
the URL parameter to the string Wallet slash
the name of the wallet. This is how we tell
our node that we want an address from
this specific wallet. Let's test this method. For that, we'll create the
following Post construct annotated method will call the get new address method
passing the name of a wallet we've loaded
before as the parameter. If we call this method without
loading this wallet First, it won't work and the node
will return an error. We'll store the result
of the method call in the address variable
and we'll print it. Let's test it. The console log, show
that it generated and printed a Bitcoin segment
reg test address. Great. Take note of your
generated address. We'll use it to test the
method will implement next. Now, we'll create a method
that will be used to mind some blocks producing
Bitcoins to an address. This method only works on
the reg test environment, and it'll be useful for
us in some tests that need an initial number
of coins to play with. Let's create a class called no degenerate to address client. As usual, we'll inject
the node client into it. Then we'll create a method
called generate to address. It'll take as parameters,
a wallet name, a number of blocks,
and an address. Then we'll call them
make request method from the node client with the
following parameters. Again, we'll pass
the string Wallet slash the name variable. Then we'll pass the
blocks parameter, which indicates the number of blocks you want to generate. Finally, we'll pass
the address variable, which is the address that will receive the generated Bitcoins. Now, let's test this method. First. Let's remove the previous post construct annotated method. Now, we'll make this
post construct method will call the generate
to address method, passing the name of
a loaded wallet. Again, if you pass
the name of a wallet that doesn't exist
or is not loaded, the method will work. We'll create 1,000
blocks and we'll pass the previously
generated address. Let's run it. If we inspect our node logs, we see that it shows some lines indicating that our
method called worked. The last client will create for this video will be called
Node get balanced client. It will be used to check
the balance of our wallets. Let's inject the
node client into it. Then we'll create
the get method, which will return the number of bitcoins our wallet holds. It'll take the wallet
name as an argument. Then we'll return
this method call. Like the previous methods. This method will send the
Wallet slash the wallet name as the URL parameter to define the return of the balance
of this specific wallet. Let's test this method
using a post construct. Will call the get method passing the name of a previously
loaded wallet. Again, this method won't work. If the past wallet isn't
loaded by the node, then we'll store the result of the call in a variable and print it. Let's test it. Great. From the logs, we see that we now have thousands of Bitcoins. Of course, these
bitcoins are only valid in our reg
test environment. In the next videos, we'll use them for testing.
25. Sending Bitcoins From the Node to the Application: In this video, we'll build a client that will
allow us to send Bitcoin from our node wallet to any other red dress we want. Then we'll use all the clients we've built so far to create a test to receive bitcoins to an address of our
application wallet. So after removing
any previous post construct methods from
previous lessons, Let's create a class called
nodes sent to address client. Now let's inject the
node client into it. Then we'll create a
method named send to address as parameters or wallet name and
address and an amount. Then we'll call them
make request method from the node client passing
the following parameters. Again, we'll pass Wallet slash
the wallet name variable. It'll be the wallet from where
the bitcoins will be sent. It will also take
an address to where the bitcoins will be
sent and an amount, which is the amount being
transferred in Bitcoins. Let's test this method. First, make sure that
your node is running. In my case, I'll
have to start it. Now, let's make sure that our node wallet is funded
with Bitcoins so that we can send bitcoins from it will call the get balanced
method to check that. But this time we'll call
it from the terminal. Every API method that we can call from our application can be called from the
terminal using the Bitcoin CLI application. Open another terminal window, go to the same folder where
your node is installed. Then type the following
command to load a previously created
wallet and press Enter. In my case, after load wallet, I have to type
testing wallet name, which is the name of the wallet I created between double-quotes. Okay, Now that our
wallet is loaded, we can enter other
commands that require it. So let's enter this command
to check our wallet balance. Well, it's balanced, is zero. It looks like every
time we restart the note and the reg
test environment, our funds vanish. No problem. We'll fund our node wallet. Now using the command
line interface. To do that, first, we get a new address from
our node while it like this. Then we call the generate to address command
to generate 1,000 blocks and destination
the block rewards to the address we've
just generated. Now, let's call the Get
balanced command again. Great. We see that our wallet now contains thousands
of Bitcoins. Again. Now back to the node
send to address client. Let's make a post
construct method to test the send
to address method. Will call the send
to address method, passing the loaded wallet name. The second parameter can
be any reg test address. We'll use the Bitcoin CLI
to generate a new address. Then we'll pass it as
the second parameter to the send to address method. Then we'll choose
one as the amount. Let's run it. Okay, if we call the Get balanced
method from the Bitcoin CLI, we'll see that our
balanced barely changed. There's a small
difference between the current balance and
the previous balance. This difference is due to
the transaction fee since we transferred a Bitcoin to an address from the
same node wallet, if we call the get received by address method from the CLI. Passing the address,
we've sent a Bitcoin to the number zero as
the second parameter, which indicates that
we want the balance, including unconfirmed
transactions, we obtain the value one, which is the amount we
transfer it to this address. Okay, we've finished our
CLI playing for now. Let's remove the Post
construct method. Now inside the BYOD
w dot gooey package will create the
receive Bitcoin test. This test will need
a lot of things in common with the
create wallet test. So we'll make it extend an abstract class we'll
create called GUI test. This class will have all methods common to any GUI test we need. So let's create it. In the create wallet test
will cut all properties and methods except the last and transfer them to
the GUI test class. Then we'll add the Spring
Boot test annotation to it, and it'll extend the
application spec class. We'll remove the
same annotation from the create wallet test and make it extend the
GUI test class. Let's remove all unused imports. Now will make all private
properties in the GUI test protected and will include this security dot add provider call into
it's start method. Finally, we'll make
this class abstract. Now, let's test the
create wallet test to see if everything continues
to work as before on error occurred. My intuition says that
it's an IntelliJ idea bug. To solve it, Let's call them maven clean lifecycle method. Let's also rebuild the project and run all application tests. All tests passed. So it was only an IDE bug. Now, let's continue building our receive Bitcoin test will inject the
following beans into it. The node send to address client, the node load or
create wallet service. The node get balanced, client. And the node get
new address client. *****. It looks like
we forgot to add the word client and the
node get new address. Let's rename it. Now, let's create a setup method which will run before
this class is tests. Inside it will call the load or create wallet method
from the node load or create wallet service as its parameter will
pass the string, test wallet and store
it in the constant. Then we'll call the method
create balance if necessary, that will create next. This method, we'll first
check the balance of the test Wallet using the
node get balanced client. If the balance is less
than 100 Bitcoin, then we'll get a new address
from the test wallet. Using the node generate
to address client, which we've forgotten to
inject into this class, and we'll do it now. We'll generate 1,000 blocks, thus funding our wallet. And we'll make this
method private. With this method
running before tests, we guarantee that
we'll always have Bitcoins that we can
use in our tests. Now, we'll create a test
called should receive Bitcoin. It's when block
will be similar to the wind block in the
create wallet test, we'll call the following
methods to create a new wallet. We'll click on the Receive tab. Then we'll look up the value in the receiving address input, which at this point will contain the first
address of our wallet. And we'll store it in
the address variable. Now, we'll call the
send to address method from the node
sent to address client. As its parameters will
pass the test wallet name, the address we've generated
with our application wallet, and the amount one. After receiving
Bitcoins, we expect our application wallet to
detect the transaction, but this process
won't be immediate. It will take some seconds
for that to happen. For this reason,
we'll use a method called wait-for from test fx. As its parameters will pass ten, which will be the timeout
value for the weight. The timeout in this context is the maximum time the
application will wait before throwing
a time-out exception. The second parameter indicates the time unit of the
previous parameter. So we'll choose seconds. The last parameter will
be a callback method. Inside it will look up a GUI component with
an ID of addresses. Table, will query it has a TableView and store its result in a variable
of the same type. Then we'll return the comparison between the TableView
item size and one. This TableView with an ID
of addresses table will be a component that will store
a table containing rows, each with a funded
address of our wallet. The callback method will run in a loop and will only stop if the comparison returns true or if the timeout is achieved. In summary, this weight for method call will
do the following. It'll check if there is a table with one row filled
in the screen. This will happen when our wallet detects that one of
its addresses received a transaction and it filled one table row with
that information. It will wait 10 s for
that to happen or less. If the table gets filled
with a row before, then we'll copy the table view lookup line
and paste it here. Then block will check again if the TableView item
size is equal to one. Let's store this timeout
value in a constant. Now, let's run the test. As expected. The test failed because it didn't find the addresses table. In the next video, we'll start to implement
this feature in our wallet.
26. The Addresses Table: In this video, we'll
create a table showing information
about our addresses. That table will contain
each funded address, their amount in Bitcoin
and the minimum number of confirmations that transactions
to those addresses have. The idea is to fill
that table just after our wallets detect transactions
sent to our addresses. So in the main window dot FXML, if we click on the
Scene Builder tab, we'll see that it's
showing a message to download Java FX, even if we downloaded it before. This is happening because this IDE feature doesn't handle
external components well, such as the Receive
tab controller. So to be able to use
the Scene Builder will create a new FXML file
called playground. In this file, we'll paste all the contents from the
main window dot FXML. But instead of using
external components such as the Receive
tab controller, will inline them in the file. So inside the resources
dot FXML package, create the playground
dot FXML file. Delete all its content. Then copy all the content from the main window dot FXML
and paste it there. Now we'll substitute the
content from the tag Receive tab controller with the content from the Receive tab dot FXML. And we'll modify the FX
root tag with the tag tag. And let's delete
its type attribute. Let's import the insets tag. Now, when we click on
the Scene Builder, we can use it normally. Now, let's add a new tab
pane to the VBox component. Now, let's delete one
of the created tabs. Let's rename the remaining
tab two addresses. Let's give it an FX
id of addresses tab. Now, inside the Content tab, we'll add a table
view component. Unfortunately, for
buggy reasons, the Scene Builder
does not allow us to add a table view to a tab. No problem. Let's add it
using the text editor. Let's also add an Fx idea
of addresses table here. Let's go back to
the Scene Builder. It started to work again. Now we have an empty table
inside the addresses tab. Now we'll add three
columns to the table. We'll rename the first
column two addresses, the second to balance, and the third two confirmations. Then we'll click on
the table and change this configuration to
constrained resize. Now, every column is the same size and they occupy
the entire table area. Now let's click on each
table column and de-select. They're editable and
sortable checkboxes. Now, for each table column will set their max width to 100. This is just a necessary trick so we can adjust their size. Now, let's adjust their size
using the mouse like this. The addresses column
must be bigger since addresses will occupy
a larger space, balance and confirmation
columns can have the same size. Back to the text editor. Let's change the pref height attribute of the
tab pane to 355. Returning to the Scene Builder, we can see that it looks better. Let's also change the
placeholder message, which shows when
the table is empty. To do that, we just need to add an empty label tag inside
this placeholder tag. Now that our table component
is ready in the playground, Let's create an FXML for it. Let's name it addresses table. Let's delete all it's
boilerplate code. And let's copy all
the content between the TableView tag
and paste it there. Now, instead of using
the table view tag, Let's use the FX root tag referencing the table view
plants and the type attribute. Let's import the remaining tags. And let's add this XML
and S attribute here. Now back to the playground FXML. The addresses tab is missing
an FX ID. Let's add to it. This idea was already used in the VBox component by mistake. So let's remove it from there. Now, let's copy these four lines and paste them into
the main window. Now, let's add the closing
tags and the tags we copied. Now we'll delete
the table view tag and add a content
tag in its place. Inside it will add an addresses
table controller tag. Now let's create
this controller. Let's add a component
annotation to it. It must extend the
table view class with the address class
as its type parameter. Now back to the main window. Let's import the recently
created controller. In the addresses
table controller, let's create a constructor. It will take an FXML parameter
with a value annotation. The value annotation will reference the
addresses table FXML. The second constructor
parameter will be an application
context object. In the constructor's body will create a new FXML loader object, initializing it with
these parameters. Then we'll set its FXML
controller to this. And we'll set its route to this. Then we'll call load on it. And we'll add a throws
statement to the constructor. We need to do one more
thing for it to work. Inside this if statement in
the GUI started listener add the following code. This is needed for Java FX to build the addresses
controller tag correctly. Now, let's run our application
to see how it's working. Great, alright, dresses
table is looking nice. In the next videos, we'll make it work
for our wallets.
27. Importing our Application Addresses to Our Bitcoin Node: In this video, we
will make our wallets start to interact with
our Bitcoin nodes so that we can receive
information about transactions sent
to our addresses. Remember that when
we create a wallet, we generate the first
20 addresses for it. But our Bitcoin
node doesn't know yet about are
generated addresses. Are Bitcoin node must know about our addresses to actively send information about their transactions
to our application. Therefore, we need to import our wallet addresses
to our node. For that, we'll create a new node client that will
be responsible for it. So inside the node dot
client package will create a class called node
multi import address client. Like other clients, it will have a service annotation and we'll inject the node
client service into it. Before we create its method, Let's check the Bitcoin Core
website documentation for its API will use the import multi-method
from the Bitcoin node API. We see here that it takes an array of objects
as a parameter. Each object can have a
lot of different keys, but we'll use just
three of them. The script pub key, which can take as a value
and object with an address which the node will
import a timestamp, which is the date when the
address was generated. This parameter is important to determine how far in the past the node will search
for transactions with that address
in the blockchain, will also set the watch only parameter to true
since our node will only use our addresses to monitor transactions
in the blockchain, but won't be able to sign transactions with
those addresses. Only our application
wallet will be able to sign outgoing
transactions, but that's a topic
for future videos. Back to the IDE, let's create a method
called import addresses. It'll take as parameters
a wallet name, a list of addresses, and a wallet creation date. Now we'll build a params
object that will model the import multi-parameter we saw on the Bitcoin
Core documentation. This object will be a list
of a new object we'll create called node multi
reimported dress params. Let's create it now. It'll be a record and we'll create it in the
domains dot node package. It will have a new
node multi reimported, respire him script pub
key object will create. It will also have a
long timestamp field and a Boolean watch only field. Let's create this
class as a record. It will have only
an address field. Back to the node multi
import address client. Since the import
addresses method receives a list of addresses, we must use the following
code to convert it into a list of node multi
reimported dress per ohms. Then we'll use the
make request method from the node client like this. Now, let's test this code
using a post construct method. We'll import addresses to the node wallet with the
name testing wallet name. And we'll use the current date as the wallet creation date. As the addresses. Will use these three reg test addresses, which you can find in
this class as resources. Before running this code, let's run our Bitcoin
Core Node and load the wallet with the
same name we'll use. To load it will use
the Bitcoin CLI, just as we did in
previous videos. Okay, By looking at
the Bitcoin node logs, it seems like it worked. The last line of the log shows that the rescan
was completed. The rescan happens by default after we import
addresses into the node. The rescan search
for transactions in the blockchain for the
recently imported addresses. In this process, the node searches for
transactions and blocks created up to 2 h
before the date we've passed as the
timestamp parameter. Let's remove the Post
construct method. Now inside the gooey
dot listeners package, Let's create a class called created wallet
important listener. It'll have a
component annotation, and it will implement the
application listener class with a parameter of
created wallet event. Let's implement its method. Just like the created
wallet listener, this class is method will be called after a wallet creation. Let's inject the node load or create wallet service into it. Now, in the non-application
event method will call the load or create wallet method from
the injected service passing the name of the event
wallet as the parameter. Let's also inject the node multi import address client into it. Let's make both injected
services final. Now we'll call the
import addresses method from the last
injected service. But first, let's
create a variable for the event wallet and use it in this method. Now in the import
addresses call, Let's pass the wallet name. The wallet get addresses method, which will create the
wallet created at property, which we will also create
in the wallet record. Let's create the
createdAt date property, will create it using the
change signature IDE feature. Let's use a default
value of new date. By doing so, the IDE
will automatically add this third parameter in every instantiation of a wallet
it finds in the project. Let's click on Refactor
and import the date class. If we go to the create
wallet service, we see that it
added a new date as the third parameter in
the wallet instantiation. This will make the wallet have the current date as
its creation date. The create wallet dialogue
controller will use the created wallet in the
public event method call. This will trigger the on
application event method call. Now let's create the
get addresses method in the wallet record. It'll return a list of addresses by taking
the addresses from its extended
pub keys, like this. By calling the flatMap
method on the stream of extended pub
keys and returning the extended pub key
addresses stream on this call will obtain a stream of all Address Objects
from the wallet. Then we'll call the
map method to convert the stream of address objects into a stream of
address strings. Finally, we'll call the two
list method on the result and return a list of all wallet
addresses in string format. Okay, now after
creating our wallet, we expect that our
application loads were creates a wallet with the
same name in our node. After that, our
application will import all generated addresses
into our node. Let's run our
application to test it. Let's create a new wallet. I clicked on the Okay button, but it seems that
nothing had happened. Let's check the node logs. Okay, after some time, it says the rescan was completed and the wallet
finished its creation. In the next video, we will make this process asynchronous by running the calls to the
node in another thread. By doing so, the gooey won't be stuck and the wallet
creation will be smoother. Cia.
28. Calling our Bitcoin Node Asynchronously: In the last video, we made
our Bitcoin node create and load our application wallet
and importance addresses. But this process was
taking some seconds and it was blocking the
graphical user interface. In this video, we'll fix that by making the communication
with the node run in another thread asynchronously
inside the BYU OWL package. Let's create a new
package called Config. Inside this package, Let's create a class
called async config. This class will contain
every configuration related to async code
execution in the project. Let's add a configuration
annotation to it. And an enabled async
annotation to. This annotation is important to enable asynchronous
features in the project. Now, let's create a method annotated with the
Bean annotation. It'll return an ExecutorService. Let's call it default
executor service. It'll return a new
single thread executor from the executors class. This bean will provide a thread whenever we want to
execute code in it. It'll be a single thread, which means that if two
or more code sections are trying to run in this
thread simultaneously, one section we'll have to wait for the other to finish running. We choose to use a single thread because it makes
processes easier to control in our
application and allows us to avoid race
conditions more easily. Now, let's do a little bit of code refactoring inside the
gooey dot services package. Let's create a class
called import wallet service and add the
service annotation to it. Let's create a method
called import wallet. Will transfer the code from the created wallet
import listener to the import wallet service. Let's also inject the required
services into this class. And let's add the wallet as a parameter to the
import wallet method. In the created wallet
import listener. Let's remove all this code. Let's inject the import
wallet service into it. Now in the non-application
event method, let's just call the
import wallet method from the injected service passing the event wallet
as the parameter. The import wallet
method contains the code that we want
to run asynchronously. To make it asynchronous, we just have to annotate this method with the
async annotation, passing the name of the bean of the executor service
we've created. We also must return a
value from this method, which will allow us to manage this async call from
the method color. The returned value
will be a future. A parameter type of void will return a new
async result object passing null to its constructor. Now, let's test
creating a wallet. But first, let's run
our Bitcoin node. If it's not already running. We see that our wallet
was immediately created, but when we check the node logs, it is still loading and
importing the wallet addresses. Great. The node has
finished the rescan. So our wallet load and import
processes are asynchronous. Now let's do some fine tuning. Let's go to the created
wallet import listener. In the non-application
event method will record the return of the import wallet method and store it in the result variable. We'll make this variable a
private field of this class. Now, we will add the
following if statement. Before calling import wallet. If the result
variable is not null, then we'll call the
cancel method on it, passing true as its argument. This is necessary because
if we try to create another wallet when one hasn't
finished importing first, we'll need to cancel
the import process of the first created wallet. Let's do another fine tuning in the GUI application class will
override the stop method. Inside it, will add
the following code. This code will ensure the
thread created by our default ExecutorService
gets closed after we close our application. Now, there's another issue
with our application. When we create a wallet without
our Bitcoin node running, the calls to create or load a wallet and importance
addresses will fail. Let's see how that works
in practice by closing our Bitcoin node and
running our application. After creating a wallet, it seems our application
is running normally, but that's because
the exceptions and other threads aren't
being reported. Our wallet couldn't
have communicated with our node while
our node was off. When it tried to do that, an exception was thrown by the thread and a thread
finished running. Let's fix this. What we
want our application to do when it creates a wallet is to indefinitely try to
communicate with our note. If for some reason it couldn't
do it after the first try, do it, we'll use a Spring Boot
annotation called retrial. This annotation makes the
annotated method execute again, if it throws an exception. To use it, we must add the following dependencies
to our POM file. The first is spring retry, the second is spring aspects. Now in the import wallet method, let's add the retrial annotation will also pass some
parameters to it. The first will be the
exception expression, which will set as at
sign-in port wallet service dot should retry open
and close parenthesis. This will make the application called a should
retry method before each retry and only retry if this method
call returns true. We will also add the
max attempts parameter, setting it to the max
value integer constant. This setting will
make this method to be retried indefinitely. Finally, let's add the
backoff parameter, setting it to the
backoff annotation, passing the delay
parameter of 1,000. This will make the application
waited 1,000 milliseconds or 1 s before executing
the method at each try. Now let's create this
should retry method. Its return type
will be a Boolean. The method will return the
negation of the result of that is interrupted method call
from the current thread. This call will return true if the current thread is not
interrupted or false otherwise. Therefore, if the
application closes or if another wallet is created
between each retry, this thread will
be interrupted and the import wallet method
won't be retried anymore. One more thing for the
retrial annotation to work, we must add the enabled
retry annotation in the async config
class. Let's do it. Now. Let's run our application with our node closed
and do some testing. Let's create a new wallet. Now, let's start our node. From the node logs, we see that the
created wallet was loaded and the rescan
has been completed. Now. Now let's create another wallet. We see from the
node logs that it also has been
successfully created, loaded, and imported. Great.
29. Listening for Transactions from the Node with ZeroMQ: In this video, we'll continue to integrate our wallet
with our node. The next step in the integration is to listen to the
messages are node sends when it receives a transaction to an
imported address. For that, we'll create a
class called node task, which will have a method that will run in an infinite loop in another thread after
the application starts. This method will continuously listen for transactions that are node receives to any of our applications current
loaded addresses. When it finds one, it will make our application
handle it accordingly. So let's create a
new package called node in the BYOD W package. Inside it will create a class called gooey
started node listener. Let's add a component
annotation to it. This class will be a listener
triggered by the gooey started event published when
the application starts. So it will implement the application listener with that event as its
type parameter. Let's implement its method. Let's add the new node
task service will create as a private
field in this class. Let's create the new service. Now, let's inject it into the
GUI started node listener. In the non-application
event method will call the node
task run method. Let's create this method
in the node task. Let's add the service
annotation to this class. Now, we'll add one
dependency that the node task will
need in the POM file. It'll be the hero MQ library. Let's click on the load
Maven Changes button. This library manages
the communication between the application
and zero MQ. Zero MQ is a message broker that receives messages from
the Bitcoin node. These messages will be sent to a local URL and collected by our application through zero
MQ back to the node task. Let's add the async
annotation to the run method. Let's pass the note executor
service as its parameter. This will make
this method to run asynchronously in another
thread managed by the node executor
service we're going to create in the async
config class, let's create the node
executor service. Like the default
executor service, it will be managed by a new
single thread executor. Now, let's inject
the socket object from zero MQ into this class. This field will be called
subscribers since it'll handle a subscription to
the zero MQ message broker. In the run method, Let's set the subscriber receive
timeout to 1,000. This will add a one-second
delay each time our application queries zero and Q to search for messages. Let's call this subscribers
subscribe method, passing the bytes of the string
rod TX as the parameter. This will make our application
only listen to messages related to rob
Bitcoin transactions that are node sends to zero. Mq will also call the
subscribers connect method, passing the variable
z MQ URL to it. Let's inject this
variable into this class. We'll add a value annotation
to its constructor argument, passing this string
as the parameter. Therefore, this variable value will be injected into
this class through an application property named z and q dot URL will set now, the value of this
variable has to match the same value we've set
in our bitcoin.com file. So let's copy the value
set for the z MQ pub rock TX bitcoin.com configuration
and paste it here. Now, back to the
node task run method will create our
non stopping loop. Let's create a while loop. It will run while the current
thread is not interrupted. Inside the loop, we'll
call the subscribers wreck the STR method and store the result in the
topic variable. The wreck of the STR
method query zero MQ for any message and returns
a string if it finds one. In our case, that string will contain the
name of the topic, which will be equal to ra
TX for raw transactions. Then we'll add an if loop
checking the topic variable. If its content is
different than Ratti x, then we will call
continue making the loop run again
from the beginning. If not, then we'll call
the subscribers wreck V method and store the content
in the contents variable. The wreck of the method is
similar to the rec VNTR, but returns a byte array
instead of a string. At this point, that
byte array will contain the raw transaction or
node sent to zero MQ. Now, we'll convert
the byte array in the Bitcoin Java
transaction object. To do that, we'll use
the following code. We must add a throws IOException statement to
this method signature. In the GUI started
node listener. We also need to
wrap the run call in a try-catch block like this. Back to the run method. Now we'll use an application
event publishers service to publish an event. Let's inject this
service into this class. First. We'll pass a new transaction received event to the
publish event method. To the transaction received event instantiation
will pass this and the transaction object before creating the new event class, let's add a missing piece
for this method to work. Let's create a new class
called node configuration. Let's move it to a new
node dot config package. Let's add a configuration
annotation to it. In this class, we'll
add some beans for the node tasks injected
Socket object to work. The first bean will be
the Z context being. The second beam will
be the zero MQ socket. It'll be named subscriber. And it'll take in its method
the Z context created above. It will return the Z
contexts to create socket method call passing the socket type sub
constant as its parameter. Now back to the node task will create the transaction
received event, will create it in the
node dot events package. Will pass the node task to the super constructor and we'll inject the transaction into
a private transaction field. Let's also create a getter
method for the transaction. Now, let's create a listeners package
in the node package. Inside it. Let's create a transaction received
listener for this event. Let's add a component
annotation to it. This listener will
be responsible for filtering receive
transactions by identifying if they
have addresses belonging to a currently
loaded wallet. It will implement an
application listener with the transaction received
event as its type parameter. Let's implement its method. In this method, let's get the event transaction and store it in the
transaction variable. Now, the first thing
we'll do is to identify the output addresses from
the Received Transaction. To do it will create
the addresses variable, which will be a list of strings. Then we'll assign the result of the following expression to it. Will map the transaction outputs passing an address
parser parse method. Now, let's inject and create
the address parser service. Let's create it in the
node dot services package. Let's add the service
annotation to it. Now, let's create
the parse method. It will return a string and take a transaction output
as its parameter. This method will be responsible for extracting the address from a transaction output back to the transaction
received listener will convert the
result into a list. Then for test purposes will print all the
parsed addresses. Now let's implement
the parse method. The first thing
we'll do is to get the transaction script pub key and assign it to this variable. Don't worry, we'll
explain what a script pub key is and other
transaction details. In the next video, we'll return the result of the
following switch statement. If the type of the
script pub key is equal to the constant P2 WP k, h, which is the type of our
current wallet addresses. Then we'll return the
script pub key P2, WP k h address method call. Passing the address prefix
factory Get method call as the parameter passing
the Segway constant to it. The default case statement
will be an empty string. Let's inject the address prefix
factory into this class. Now, let's do some testing. My Bitcoin node is
already running. Make sure yours is too. Let's go to the receive
Bitcoin test class and run it. The test failed because
we haven't modified our GUI test yet to consider the addresses table controller. Remember that our
GUI started listener handles the issue of
using this if condition. We could use the same if
condition in our fixed, but let's do it in a neater way. Let's go to the
GUI started event. Let's change the GUI
application type to object. Will simply publish the gooey started event in
our GUI test class, the same way we did it here
in the GUI application class. In the GUI test class, let's remove all this code. Now, let's copy this line of the GUI application
and paste it here. Let's remove all
these unused imports and this unused private field. Now, let's run the receive
Bitcoin test again. Our test failed,
but we see here on the test logs that are application
printed two addresses. This happened because we
printed the addresses of the transaction our test made to one of our wallet addresses. One of these addresses is ours, the other is a change
address of the sender. Great. We see that what we've
done so far has worked. Let's do some refactoring
in the GUI test class. Let's remove these
unused imports. In the GUI application class. Let's include this code
line which will make our note executor
service thread stop running after we close
our application. In the next video,
we'll learn more about Bitcoin transactions to prepare
us for what comes next.
30. Bitcoin Transactions: Inputs, Outputs, Scripts, and the UTXO Set: In this presentation,
we will learn some concepts about
Bitcoin transactions. There'll be useful
to understand better the implementation of receiving Bitcoins in our
application wallet. Let's recap what we've
learned so far about Bitcoin transactions
and gradually add complexity to our
understanding of them. So a Bitcoin transaction
is commonly used to transfer Bitcoins from
person a to person B. A Bitcoin transaction
can be composed of many inputs and many outputs. A transaction input indicates the previous Bitcoin owner or the sender of
the transaction. A transaction output indicates
the receiver of the coins or the owner of the coins after the transaction is published. In this image, we have a simple transaction with
one input and two outputs. It says that one Bitcoin
was transferred from John, who signed the input
of this transaction. Output zero indicates that 0.9 Bitcoins were sent to marry. I'll put one indicates that 0.099 Bitcoins were sent
back to John as the change. Bitcoin amounts cannot
be divided in inputs. Since John wanted to send
0.9 Bitcoins to marry, but he only had one Bitcoin. He needed to create a change
output back to Himself. But its amount is
0.099 Bitcoins, not 0.1 Bitcoins, as
you could expect. The difference, which
is 0.001 Bitcoins, is the transaction
fee needed for the transaction to be
included in the blockchain. So from this image, we take three lessons. The sum of bitcoins
in inputs cannot be less than the sum of
Bitcoins and outputs. The Bitcoin amount in inputs is fixed while it can be chosen at will and outputs given that it satisfies the previous rule. Finally, the transaction fee is the difference
between the sum of Bitcoin amounts in inputs and the sum of Bitcoin
amounts and outputs. Increasing the complexity. We also learned about the
fields that are part of a transaction input and
a transaction output. And input always refers to an output from a
previous transaction. It does that by using the previous transaction ID and the output index of the
previous transaction. It would be redundant for the input to have
an amount field. These two fields are
enough to identify any transaction output
in the blockchain. Uh, transaction input also must contain a valid
signature produced using the same private
key used to derive the address from the output
to which the input refers. Notice that a transaction
input doesn't have a field indicating
it's Bitcoin amount. That's because it's amount is automatically equal to the
output amount to which it refers a transaction
output has two fields. One is the address which is
used to receive bitcoins. And address is an
encoded string and by itself doesn't reveal
the owner of the coins. That's why we consider
Bitcoin pseudonymous. The other field is
the quantity which indicates the Bitcoin amount
locked by the address. Now, let's increase the
complexity one more time explaining what exactly transaction inputs
and outputs contain. The transaction input has
the following fields. The previous transaction ID, which is a unique
hexadecimal string, is obtained by hashing the previous serialized
transaction. The previous transaction
output index is an integer number. Since a transaction
can have many outputs, a specific output
can be unequivocally identified by its transaction
ID and output index. The scriptSig or witness field. The input signature is added to the scriptSig field in
non Segway transactions. In Segway transactions, the signature is added
to the witness field, which does not count to
the transaction size. The transaction output
has the following fields. The script pub key. Script pub key contains
the decoded address and the quantity field
which determines the Bitcoin amount
locked by the address. Now, let's see how to build a Bitcoin transaction
through an example. Our objective in this
example is to send 1.5 bitcoins to this address. Before starting, we have
a confirmed transaction, which means that it's already
stored in the blockchain. It has an output
with two bitcoins, which I'm able to
spend since I have a private key that was used
to create its address, and that can be used to sign a transaction input for
it in a new transaction. So let's build a
new transaction, will add an input to it. The input we'll refer
to our transaction through the previous
transaction ID field. The previous transaction
output index is zero, since it's the output
index of our output. The scriptSig field is composed
of a signature produced using the same private key used to produce the address
of our output. And a public key, which is the same public key derived using our private key. The transaction will
have two outputs. The outputs zero will contain a script pub key
field comprised, among other things, of the hashed public
key of the address. We want to send our bitcoins to. This hash is obtained by Beck
32 decoding the address. Back 32 is the algorithm used to encode the
hash of a public key, producing a sec what address? Base 58 is another algorithm used to do the same for
non segment addresses. Since we're sending bitcoins
to a segment address, we must use Beck 32. The quantity field of this
output will be equal to 1.5, which is the Bitcoin
amount we want to send, will need a second
output for the change. Its script pub key will
contain a hashed public key obtained by Beck 32 decoding and address
that belongs to us, that is derived from a private
key we have control over. It's quantity will
be 0.49 Bitcoins, which added to 1.5 of output
zero is equal to 1.99. So we have a difference of 0.01 between the sum of Bitcoin
amounts in inputs and outputs. That difference will be the
transaction fee needed for the transaction to be included in the blockchain by a minor. We talked about scriptSig
and script pub key, but didn't explain what
those things really are yet. Bitcoin has a smart contract
language called script. A smart contract is a fancy
way to refer to a program written on a transaction and executed by the Bitcoin network. The scriptSig and the
script pub key fields are made with script
language code. This language is
interpreted by combining the scriptSig of an input
and the script pub key of an output and checking
if the scriptSig satisfies the conditions
encoded by the script pub key. You can consider this mechanism like a key and lock mechanism, where the scriptSig
is the key to unlock the script pub key,
which is the lock. If a scriptSig is valid for
unlocking a script pub key, then you can spend the
output funds locked by it. A scriptSig has this name
because it usually contains a signature that satisfies a specific script
pub key condition. Likewise, the script pub key
has this name because it contained a public key in the
first Bitcoin transactions. But in theory, any
arbitrary script code can be included in both fields. The unique constraint for a
script program to be valid is that it returns true at
the end of its execution. There are many types
of script programs, also known as script types. One of these is the
pay to public key hash used in simple non
sequitur transactions. Let's see how it's
executed in this example, all Bitcoin scripts are
executed similarly, what changes between them
or the code being executed? So beginning with our
confirmed transaction from the previous slide, here, we show only
its output zero with its script pub key
content in the gray box. In pay to public key
hash transactions, the script pub key
field is composed of opcodes and the
public key hash. And opcode is a script
language instruction. They're identified by the
letters OP at their beginnings. Up underscore equal, e.g. compares two elements and
returns true if they're equal. Every script pub key and pay to public key hash scripts has this code sequence only differing in their
pub key hashes. Now we have our new transaction, which is the same as
the previous slide. This time we show
only its input here. It's scriptSig content
is in the gray box. In pay to public
key hash scripts. The scriptSig has
only a signature followed by a public key. Now let's see how both fields
are combined and executed. First, the scriptSig content of the input is concatenated with the script pub key content of the output referred by
the input in this order. Then each element from
the combined script is pushed into an execution
stack from left to right. If the element is an opcode, it may act on other
elements in the stack, processing them and adding
other elements to the stack. So let's see how this
script is executed. The signature is the first
element from left to right. Therefore, it's pushed
into the stack first. The next element
is the public key, and it's also pushed
into the stack. The next element is the
up underscored up opcode. Up, underscored up encodes
an instruction to duplicate the last element added to the stack and add the
copy to the stack. The next element is the op
underscore hash 160 opcode. It removes the last element
added to the stack, applies the hash 160 function to it and adds the hashed
result back to the stack. So we end up with the public key hash at the
top of the execution stack. The next element to be added to the stack is the
public key hash. Next, we have the op underscore
equal verify opcode. This opcode removes two elements from the top of the
execution stack and compares them if they are equal than the script
continues executing. If not, it's execution fails and the transaction
is invalidated. This step is
important because it validates that the public
key of the scriptSig is equal to the
public key used to generate the address of the
output it's spending from. The last element
from the script is the op underscore
check sig opcode. It removes two elements
from the top of the execution stack
and validates if the first element is
a valid signature for the second element for
this specific transaction, if it's a valid signature, a Boolean true is
added to the stack. If it's invalid than a Boolean, false is added to the stack. Finally, after all
the script elements were added to the
execution stack, the transaction input
would be considered valid if the last
added element is true. Otherwise, the transaction
input is invalid. This process must be repeated for every transaction input. For a transaction to
be considered valid, all its inputs must be valid. There are many script types. The pay to public key script was the first Bitcoin
script created, but today it's deprecated
and rarely used. The first Bitcoin transactions
contain the script type. Instead of using the
hash of the public key, It's script pub key, use
the public key directly. The pay to public
key hash script was exemplified in
the last slide. It was the most commonly used
script type since the rise in relevance of Segway
transactions in the last years. Today, it's still commonly used, so it's good to know about it. The pay to witness public
key hash script is the currently most
used script type in Bitcoin transactions. The reason for this is that it's advantageous to use it since transactions with it are smaller and thus have a smaller
transaction fee. This script type has a
smaller script pub key and it's scriptSig content is
moved to the witness field, which does not contribute
to the transaction size. For these reasons,
we will implement this script type in
our wallets first, the pay-to-script hash
is another script used mostly for
multi-signature transactions. Multi-signature transactions require more than one signature to spend an output. The pay to witness
script hash is the witness version of the
pay-to-script hash script. Its signature is moved
to the witness field. So it has the same
advantages that the pay to witness public
key hash script has. The pay to witness public
key hash wrapped in a pay-to-script
hash script is also known as nested segment
or rap segment. It's also commonly used and
its main function is to make old wallets that are not
native segment compatible, compatible with
Segway transactions. We'll learn more
about the script type later in the course. Finally, another script type is the pay to tap root script. It's the most recently
created script type, adding more capabilities and privacy to Bitcoin transactions. Now, let's see a
summary of how to build transaction inputs and outputs from the viewpoint of a wallet, starting with a mnemonic
seed and a passphrase. Through a cryptographic
algorithm, we combine both and
obtain a root seed. Then through a series
of transformations were master keys and
extended keys are produced. We obtain many private keys. Here we show only one
of these private keys. The same transformations can
also produce public keys. A public key can also be produced from a
specific private key, which we show here
in our example. From a public key, we obtain an address using the hash 160 function and encoding the
result through Beck 32 encoding in the case of
segment addresses or base 58 encoding in the case
of non segment addresses. To receive Bitcoins, you
show your address to someone so that someone
can send bitcoins to it. When a sender puts
your address in his wallet and sends a
Bitcoin amount to it, his wallet will
decode the address back to a public key hash. Then it'll build a
transaction with an output containing a script pub key
equivalent to that address. The script pub key type will depend on the type
of the address. In this case, the
wallet identified that the address type
is compatible with a pay to public key hash script. After receiving Bitcoins
to that address, the receiver can spend them
through a new transaction. The new transaction input
fields contents will depend on the script pub key
type that will be spent. In this case, the
wallet will build a transaction with
an input containing a scriptSig with a signature generated using the same
private key that had originated the public
key that originated the address that received
Bitcoins previously. Details of signing a transaction we'll see in a future video. The public key cited
previously will be added after the signature
in the scriptSig field. Finally, the input will also reference the unspent
transaction output using the previous transaction ID and the previous transaction
output index fields as explained before. Now let's learn the important
concept of the UTXO set. Remember that UTXO means
unspent transaction output. Bitcoin has the
important characteristic of not allowing double-spending. In other words,
you can only spend Bitcoins from unspent
transaction outputs. The UTXO set is the set of
all currently existing UTXOs. This set is stored by nodes in a separate database which allows for fast
retrieval by wallets. The UTXO set is being
constantly updated with each new transaction adding and removing outputs to
and from the set. Utxos are outputs that were never referenced
in any input yet. Therefore, the UTXO set contains all coins
available to be spent. The number of bitcoins
in an address is equal to the sum
of all Bitcoins from all UTXOs whose script pub keys referenced the decoded address. So the Bitcoin
Core Node provides an API method called
list unspent that returns all UTXOs that reference the addresses that you
passed as parameters to it. By using the list
unspent method, you can easily calculate the balance of an
address and discovered transaction information such as the UTXO transaction
ID and index, as well as its script pub key. So you can build
transaction inputs to spend your Bitcoins
from the UTXO. After you spend the UTXO, it's removed from the UTXO set. Now let's see a diagram explaining how our
application will detect incoming transactions
and will let us know about the
bitcoins will receive. That will be the last slide
of this presentation, which has introduced a lot
of complex information. Don't worry if you didn't
absorb it all at first. Along the course,
we'll be reusing the information learned
in dive deeper into it, so we'll have many opportunities
to grasp it better. Now, back to the diagram. Here we have rectangles
representing the application are Bitcoin
node and the Bitcoin network. Transactions are created and broadcasted by nodes from
the Bitcoin network. These transactions,
when confirmed, are already stored
in the blockchain. When not confirmed, they
stay in the temple, a database containing all
unconfirmed transactions. Every node has a copy of the mental transactions
are eventually detected by our nodes
which had already imported our application
wallets and addresses. Through zero MQ notifications, the transactions are
sent to our application. When our application
has a wallet loaded, it will filter out all receive transactions not containing
any of its addresses. Then it will send a list
unspent call to the node, passing as parameters
all addresses from the receive transactions that remained after the filtering. The node will return all UTXOs related to those addresses. Our wallet will then
parse these UTXOs to calculate the Bitcoin balance
for each of its addresses. Then we'll show the
obtained information in the addresses table.
31. Obtaining UTXOs from the Node and Filtering Them: In this video, we will implement the list unspent client
that will enable us to collect information about UTXOs after we
receive transactions. So inside the node
dot client package, Let's create the node
list unspent client. Let's add a Service
annotation to it. Let's also inject the
node client into it. Now let's create the
list unspent method. It'll return a list of UTXOs, which is a record we'll create. It'll take as parameters a list of addresses,
and a wallet name. Let's create the UTXO record in the BYOD w dot domains package. The UTXO will contain
the following fields. The TX ID, which is an
acronym for transaction ID. The vout field, which
represents the output index, the address, the label, which we won't use right now. The script pub key. The amount which represents
the Bitcoin amount, the conformations which
will contain the number of conformations that the
UTXOs transaction has. The number of conformations of a transaction is the
number of blocks that were added to the blockchain
after the transaction was inserted in a block and this block was added
to the blockchain. This number includes the block containing that transaction. So if a transaction
has two confirmations, it means that it was already
included in a block. That block was added
to the blockchain and another block was
added after that. Let's add two more fields. These fields won't
be used for now, but they can be useful
in other types of transactions that we'll
see later in the course. They are the redeem script
and the witness script. Now back to the node
list, unspent client will call the node client make request method and
store the result in the UTXOs array variable will pass the following
parameters to the method. The last three parameters
will be part of the poems list of the
Bitcoin node API. The first is the
minimum number of conformations that the
return UTXOs must have. The second is the maximum. Since we want all
UTXOs will set zero as the minimum and the max
integer value as the maximum. The third parameter is
the list of addresses. Now, we'll convert
the resulting array into a list using this
code and return it. Now back to the transaction
received listener. Let's remove this line of code because it was
just for testing. We'll test the node
list unspent client. Let's get the UTXOs
for these addresses. Let's inject the client
into this class first, the list unspent method will
pass the addresses variable. The second argument will be the wallet name from the
receiving Bitcoin test. Let's copy it and use it
as the second parameter. Now, let's add
this line to print every UTXO obtained
on the screen. Now, let's run our Bitcoin node if it's not already running. Now, let's go to the receive
Bitcoin test and run it. In the test logs, we see that it printed a
UTXO with all its data. Notice that its
address is the same as the receiving
address of our wallet. Rewind the video to check it. The amount is one, which is expected because it's the same Bitcoin amount
sent and the test, it has zero
conformations because no blocks remind after the
node since the transaction. Now let's delete these lines because they were
only for testing. Let's also remove the injected
client from this class. Now, we'll add a
filter to this stream. We want to process
only transactions from the currently loaded
wallet by the application. This filter will be
responsible for that. The filter will check if
the address is not empty. If the current wallet
contains that address. So let's inject the current
wallet into this class. Then we'll call the get
addresses as strings method. Then we'll check if it
contains the address. Oh, a letter S was missing here. Now let's create the get
addresses as strings method. It'll return a list of strings. The returned value will
result from calling the get addresses as strings method on the
addresses field we'll create. So let's create this field. It'll have a new addresses
class as its type. And we'll initialize
it right here. Let's create it in the
observables package. Let's also create its get
addresses as strings method. First, we'll add a
private addresses field. It'll be a map where
the key type will be an address type and the type of the values
will be another map. The second map will be
a linked HashMap with strings as keys and address
as the type of its values. We'll use this data structure
to store the addresses. The reason to use
this data structure is that it enables us to separate and query the wallet addresses
by address type. The second map is a
linked HashMap because this type of map maintains
the insertion order. Finally, the second
map allows us to query the list of address objects by their address string sequences. Now in the get addresses as strings method will return
the following code. The flatMap function will be
used to get all addresses in the second map key set and return a stream
containing all of them. Then we'll convert the
resulting string into a list. Ok, Now we can use
the get addresses as strings method to get all addresses created
for the current wallet. But one thing is missing. We also have to populate the addresses field from
the addresses class. To do that, we'll add the
following code in the update. Current wallet service will set the current
wallet addresses, passing the wallet extended
pub keys as the parameter. Now let's create the current
wallet set addresses method. It'll call the set addresses method of the addresses field, passing the extended
keys as the parameter. Then we'll create this method
in the addresses class. Let's add the
following code to it. We'll use the collect
function to obtain a map from the
extended pub keys. Inside it will add a
to map method call. Its first argument will build an address type using the
extended pub key type field. This argument defines the
key of the resulting map. The second argument will
define the value of the map and it'll contain
the following code. Again, by combining
the collect and to map functions will set it
as a linked Hashmap. The keys will be the
extended pub key addresses string sequences, and the values will be there corresponding address objects. Finally, the filter part of
this stream is completed. In the next video, we'll use what we've built
to continue building the receiving Bitcoins
feature C, yeah.
32. Updating the Current Wallet Addresses: In this video, we'll
get in process the UTXOs from transactions
coming to our wallet. So in the non-application
event method, in the transaction
received listener, Let's add the following
if statement. If the addresses
list is not empty, then we'll call the
update method in the update UTXOs
service we'll create. This method will
take the addresses, the current wallet
name as parameters. Let's inject this
service into this class. Now, let's create this service in the GUI dot services package. Let's add the service
annotation to it. Back to the transaction
received listener. Let's finish injecting
the service. Now, let's create
the update method in the update UTXOs service. We'll add an async
annotation to this method and run it in the default
executor service thread. The reason for
running this method and another thread is that it'll call the list unspent node API
method, which may be slow. So we'll run it asynchronously
to avoid blocking the UI. In the update method will call the list unspent method from the node list unspent client. Let's inject the client
into this class. We'll pass the addresses and the wallet name as
parameters to the list. Unspent method will store the result of the method
call in the UTXOs variable. Now, we'll inject a new update, current wallet addresses
service into this class. Let's create it in the GUI
dots services package. Let's finish injecting
the service. Then we'll call
the update method passing UTXOs as the
parameter to it. Let's create this method. Let's add the services
annotation to it. Now, we'll add the
following code to create a map
where the keys are addresses and the
values are lists of UTXOs containing
those addresses. To do that, we'll call the collect method
on the UTXO stream, will pass the collectors
grouping myMethod call to it. Passing the UTXO address
method as its parameter. We'll store the
returned value in the group UTXOs variable. Then for each group UTXO key value pair will set the corresponding
address balance. It's confirmations
and market is used. Let's implement the
set balance method. Will return the sum of all
UTXOs amounts using this code. Using the map to double method
on the UTXO list stream, will obtain a stream with all UTXO amounts
from the UTXO list. To obtain their sum will simply call the sum
method on the result. Now, let's inject the current
wallet into this class. Then we'll call
the current wallet set address balanced method, which will create passing the address and the
sum as its parameters. Let's create it. Inside it will call the addresses set
address balanced method, passing the same
parameters to it. Then we'll create this method. Inside it will call the get
addresses as map method, which will create.
Let's create it. It'll return a map where
the keys are addresses, strings, and the values
are addressed as objects. By using the following code will return a map containing
all addresses. By calling the string method
on the addresses values will obtain a stream containing all maps of the
addresses private field. Then with this flatMap
method call will convert the stream of maps
into a stream of map entries. Finally, using the
collect method on the resulting stream and the two map method
with these parameters will obtain the desired map. On the result of the get
addresses as map call will call the get method to get
an address object by its address string. Then we'll set the address
balance to the sum variable. Now, let's create the
set balance method in the address record. Whoops, we can't add
setters in a record, so we'll have to convert the
address record into a class. Let's do it with a little
help from our IDE. Let's remove these methods
because we won't need them. Let's create the
balance center now. And remove the
final keyword here. In the address
sequential generator. Let's add zero as the third parameter in the
address instantiation here. Let's also change
the getters name, making them start with get. Now in the update current
wallet addresses service, let's create the set
confirmations method. Confirmation. In this case, we'll refer to the
address balance and it'll be defined as being
equal to the number of conformations of the UTXO with fewer conformations
for that address. E.g. let's say that an address has a balance
of two Bitcoins, with each Bitcoin coming from
a different transaction. One of these transactions
has two confirmations, the other has just one. In this case, we'll
consider that the addresses balance
has one conformation. To accomplish that, we'll
use the following code. We'll obtain a stream containing all confirmations of
the UTXOs list passed. To obtain this stream, we'll call the map
too long method on the UTXO list stream, passing the UTXO confirmations method reference
as its parameter. Then by using the Min method and the good is long
method will return the minimum number
of confirmations in the stream and store it in
the confirmations variable. Then we'll pass the
confirmations and the address variables to the current wallet set address
confirmations method call. Now let's create this method
in the current wallet class. It'll call the addresses set address
confirmations method, passing the same
parameters that received. Let's create this method
in the addresses class. We'll use the get
addresses as map method to get the address and
set its conformations. Now, let's create the
set confirmations method in the address class. We'll use it to set the
confirmations field we'll create. Let's create this field and
add it to the constructor. Then let's add this parameter to the address sequential
generator class in the address instantiation. Finally, let's create
the mark is used method in the update current
wallet addresses service. One of the objectives of
our wallet and many others is to avoid reusing addresses
when receiving Bitcoins. For privacy reasons. To do that, we must first market dresses that had
transactions to it as used. The mark as used method will
be used for that insight. It will call the current
wallet market dress. As used method will create passing the address
as its parameter. Let's create it. Inside it will call the
mark is used method from the addresses class
passing the address to it. Then we'll create this method. Inside it will use
the get addresses as math method to get the
address and market is used. Finally, let's create the mark is used method in
the address class. Here will simply set
the used fuel is true. Let's create this
field in this class.
33. Showing Address Information in the Addresses Table: In this video, we'll finally
connect everything we did in the last lessons to display our addresses information
on the screen. So in the observables package will create the
address row class. As the name suggests, this class will model a
row in the address table. Let's create the following
fields in this class, each for a column in
the address table. The first field will be a
string property named balance, will instantiate it here with a new simple string property. The next field will be a long property
named confirmations, and it'll be instantiated with a new simple long property. The last field will be a
string property named address. It'll be instantiated with a
new simple string property. Now, with the help of the IDE, let's create getters
for each property. Notice that when making getters for Java FX property fields, the IDE creates two getters, one for the property and the other for the content
of the property. Now, let's create the following constructor
for this class. Okay, Now that we've
modeled the address row, will create code to populate it when updating our addresses. To do that, let's go to the update current wallet
addresses service. In the update method, after marking the
address as used, let's call the set address row method on the
current wallet, passing the address
as the parameter. Then let's create the
set address row method. To implement this
method will add the address rows
field to this class. Let's create the
address rows class in the observables package. Let's instantiate it here. Now we'll call the set
address row method on the address rows property will pass an address
object to it. This object will be obtained from the addresses field using the get addressed method we'll create passing the
address string to it. To create this method
will simply select this code snippet and extract a method from it using the ID. Let's substitute all code
occurrences with this method, and we'll make the
generated method public. Now back to the
current wallet class. Let's create the set
address row method in the address rows class. To do that, we'll
create an address row variable calling the method
on the address row class, passing the address
object as its parameter. Let's create the form method
in the address row class. It'll return a new address row, instantiating it with
these parameters. Let's create the good
confirmations method in the address class. And now we can use it here. Notice that here we're simply creating an address
row object by converting the
address field values to the address row field values. Back to the address rows class. We'll create the address road
set field in this class. It'll be an observable set with the address row as
its type parameter. Will instantiate it here with a new observable
set wrapper, passing a new linked hash
set to its constructor. This object will
publish an event for each new address inserted
or removed and will be necessary to update
the addresses table as the current wallet
receives new transactions. Now we'll call the address
row set remove method, passing the address
row as its parameter. Then we'll call the
add method on it, passing the same address
row as its parameter. You may wonder why
we are removing and adding the same address
row in the set. Well, this is a bit of a hack. If we don't remove the
address row first and insert an address row that
is already in the set. The observable set
won't publish an event indicating that an address
row was added to it. There's another thing we
have to do for this to work. When we remove an address row from the set using
the remove method, this method will use
the equals method from the address row to define which address row it will
remove from the set. We'll consider the
two I dress rows are equal if they have
the same address. Therefore, we must implement
the equals method in the address row
class. Let's do it. We'll add the equals and hash code methods using
this IDE feature. Make sure this
checkbox is checked to generate methods
using getters. Now, we'll choose to only use the address field to
generate the equals method. And the same field to generate the hashCode method and select the address field here to
define it as non null. Okay, the IDE has generated the equals
and hash code methods. Now let's go to the
addresses table. Fxml will add FX IDs to each table column
tag. The addresses. Table controller will need these FX IDs to bind and
manipulate these columns later. First the column address, then the column balance, and finally the
column conformations. Now let's go to the addresses table controller will add the following fields, each corresponding
to one tag with an ethics ID in the
addresses table, FXML. The names of these
fields must be equal to the FX IDs of the tags.
They're bound to. First the addresses table, then the column address, the column balance, and the column confirmations. Now we'll add the initialized
method to this class. Remember that
initialized methods in Java FX controllers
are called When the framework is initializing
these controllers. In this method, we'll
add code that will bind the addresses table and their columns with our
address role observables. To do that, we'll call
the set items method on the addresses table as its argument will pass
a new filtered list. This is necessary
because we want to show only addresses with a balanced greater than zero in
the addresses table. As the first filtered
list parameter will pass the current wallet
observable address rows. So let's inject the current
wallet into this class. Then we'll call the get observable address
rows method on it. Let's create this method. It'll return an observable list with address row as
its type parameter. Then we'll return the good
observable address rows method call on the
address rows field. Let's create this method
in the address rows class. Inside it will return the address row list
field we'll create. Let's create it. This field will be instantiated here with a new observable list wrapper passing a linked list
as its parameter. Now, we have to make the
observable list respond to insertions and removals of address rows in the
address row set. We'll do that in the
class's constructor. First, we'll create a set
change listener variable. We'll set it as this
lambda function. The change parameter
here represents an insertion or removal of
an address row in the set. If the change was a removal, then we'll remove
the removed element from the address row list. If the change was an insertion. Then we'll add the added element to the address role list. Finally, we'll add
this listener to the address row set
using this method. Now back to the addresses
table controller. The second parameter of
the filtered list is used as a filter condition
to display rows. It will be a lambda function with an address rho parameter. The function body will
be this code which will check if the address row
balance is greater than zero. That way, we achieve
our objective of only displaying addresses with a
balanced greater than zero. Now, for each column field, we have to set a cell
value factory that will be used to populate each cell
of the addresses table. For that, we will use
the following code. The property value factory will be used to get each address real property and bind its value to its
corresponding cell. It knows what address
real property we want to bind to the column by matching the name
we're passing as its parameter to the
name of the property. There's one more
thing we must do for our receive Bitcoin
feature to work properly. Let's go to the
current wallet class in the set addresses method, which is called when a
new wallet is created, we have to include this code
to ensure that the address rose from a previously
created wallet gets cleared. We'll call the address rows clear method, which will create. To do that, let's
create it. Here. We'll simply call
the clear method on the address row set and
the address row list. Now let's test our feature. Make sure your Bitcoin
core node is running. Let's go to the receive
Bitcoin test and run it. Great, it worked. Notice that the first row of the addresses table was filled correctly with a balance of
one Bitcoin as expected. Good job. In the next videos, we'll add more test
cases to ensure our receive Bitcoin feature is working correctly in
different scenarios.
34. More Test Scenarios For Receiving Bitcoins and Avoiding Address Reuse: In this video, we'll add
more test cases to ensure our receive Bitcoin
feature works correctly in
different scenarios. The first additional
scenario we'll test if the same address can receive Bitcoins from two transactions, one Bitcoin from each. We'll check address balance
gets updated to two bitcoins. So let's copy the content of this method and
paste it below. Let's modify the test
name to should receive Bitcoins in two transactions
to the same address. Now, let's select this code and extract a method from it
with the help of the id. Let's name it, send
Bitcoin and weight. We'll call this method two
times since we want to send to transactions
to this address. Let's modify the send Bitcoin in weight method including
the following code. We'll check if the address in the first row of the table has a balanced equal to the expected
total amount parameter. Let's add this parameter
to the method signature. It'll have a default
value of 1.0. In this method call, let's add 2.0 as the
second parameter, since we expect that
the address balance will be equal to two. Let's add two to
the wallet name. There is a problem with
our test environment. In reg test, it's
only possible to mine 15,000 blocks with
block rewards. Therefore, in this method, it's too much to
mine 1,000 blocks when calling the generate
to address method. Let's only call it if the test wallet
balance is less than 50 and will generate only
150 blocks for each call. That way, we avoid the need to reset our reg test
environment too often. Let's also substitute this code with the send Bitcoin
and wait method. Now, let's make our tests a little more resilient
to race conditions. I've noticed that
sometimes these tests send bitcoins to our
application wallets before they were fully loaded. Therefore, these transactions
weren't captured by the application and the
tests fail for that reason. So after creating a wallet and before sending
Bitcoins to them, Let's call the sleep method
with these parameters. The sleep method call will make the application wait
for 10 s before sending Bitcoins enough time for the new wallet to get
loaded completely. Depending on your computer, you may need to increase
the timeout constant. Now, before running the tests, I'll reset my reg
test environment. I need to do that because
it already generated 15,000 blocks and the block generation isn't producing
Bitcoins anymore. So I'll go to the folder
where the blockchain is stored and remove the reg
test folder inside it. In your case, you can find
what folder it is located by checking the data DIR value
in your bitcoin.com file. Now, let's run our Bitcoin node and run our receive
Bitcoin tests. Great, the tests have passed. When we receive
Bitcoins in our wallet, we want that the next receiving
address gets changed. That way we avoid
reusing addresses. It's important to
use only one address for receiving
transaction so that a third party can easily track how many Bitcoins you have and what addresses you
transact with. To cover this scenario, Let's create the following test. Copy the last test
and paste it below. Let's rename it to
should receive Bitcoins in two transactions, two
different addresses. Let's change the
created wallet name to my test wallet three. Now before the second send
Bitcoin and wait method call, let's add this code to query
the receiving address field again and store its content
in the next address variable. Now, change the
first parameter of this method to the
next address variable. And we expect that the
total Bitcoin amount for this address will
be equal to 1.0. Also, we expect that
the table items size will be equal to 21
row for each address. So let's modify this
method to accept the expected table
size as a parameter. Let's add this parameter to it with a default value of one. Will substitute this
value here to wait for the table number of
rows to be equal to it. And we'll substitute the
table dot items index for the expected
total size minus one to wait for the last row
in the table balance to be equal to the expected
total amount for that row. Finally, let's parameterize
the third argument of this method call to two. Let's run our tests. As expected. The third test failed because
we haven't yet implemented the feature of changing the receiving address
after using it. Let's do it now. So let's go to the update current wallet
addresses service. Inside this for each block, let's call the update receiving address method we'll create. It'll take the address
as its parameter. Let's create it. The first thing
we'll do is to call the get addressed type method
on the current wallet. Passing the address
as its parameter will store the result
of the method call in the address type variable. It's important to
know the address type of the substituted address, since it has to be substituted by an address of the same type. So let's create this method
on the current wallet class. It will simply
call the addresses get addressed type method. We'll create passing the
address as its parameter. Let's create it. Now. We'll call the get addressed method and
return its address type. Let's create the get
address type method in the address class. Inside it will return the address type
field we'll Create. Let's create this field and
add it to the constructor. Let's also fix the address
sequential generator to add the last argument to
the address instantiation. We'll add this address type as a parameter to this method. And we'll include the address
type in this method call by calling the value of method
on the address type class. Now back to the
Update Current wallet addresses service will calculate the value of the next
address index by calling the fine next address index
method on the current wallet, passing the address
type as its parameter will store the result of the method call in the next
address index variable. Let's create this method. Here. We'll call the fine next address index on the addresses field, passing the address
type as the parameter. Let's create this
method in the addresses class with the following code, will get the last address used in store it in the
address variable. Will get all that
dress objects with the given address type
and get a stream from it. Then we'll sort the
obtains stream by using the sorted method with
the following parameters. Will use the comparator
dot comparing method, passing the address
get indexed method as the first parameter and the reverse order
comparator as the second. That way we'll obtain an address stream reverse
ordered by their indexes. Now, we'll filter
the stream to obtain a string containing
only used addresses. To do that, let's
create a getter for the used field in
the address class. They used getter is called is used because that's
how the IDE names Boolean getters back to
the addresses class will pass the addresses used method as the prompt to the
filter method call. Now we'll call the fine
first method on the result. To obtain the first used address and the reversed ordered stream, which is equal to the
last used to address. What if no used addresses found. In this case, we'll use the or else method to return null. We'll add the following
if statement. If the obtained address is null, then we'll return zero
as the next index. If not, we'll return the
last address index plus one. Now back to the Update
Current wallet addresses service will get
the next address by calling the get
address ID method on the current wallet. We'll pass the
next address index and the address
type as parameters. Let's create this method
in the current wallet. Will return, they get
address ID method call on the addresses field, passing the same received
arguments as its parameters. Let's create this method
in the addresses class. Will get all that dress objects with the
given address type. Then we'll convert it to a list. Then we'll get the
address by index. To do that, we need to change the address index type
to a long object, not alone primitive type. And then we'll call
the int value, passing it as the
getMethod parameter. Now we'll return
the address string from the obtained address. Now back to the Update Current
wallet addresses service will simply set
the current wallet receiving address as the
obtained next address. Let's do one more thing
before testing our code. Remember that the codon, this class will run
in a new thread. In Java FX, it's
good practice to run code that modifies the UI
only in the main thread. To do that, Let's call
the platform dot run later method passing a
Lambda function to it. Let's move these two
lines to the body of the lambda function since they cause changes that
will affect the UI. Now, let's run our receiving
Bitcoin tests again. Great. The receiving address
field was updated after receiving funds and
the tests have passed. In the next video, we'll
continue improving our tests and add one
more test scenario. C, yeah.
35. Improving Receiving Bitcoin Tests. Receiving More Transactions Than Addresses: In this video, we'll continue
improving our tests and add one more important test scenario for the receiving
Bitcoin feature. One thing is missing
in our tests. We don't know yet if
the right addresses are being generated and if they are respecting the derivation path
index sequence. To check that, let's create
the address is valid method. Then block of the
first test called the address is valid method
passing the address, the mnemonic seed and the
expected address index, which in this case is
zero as its parameters. Let's create this method. It'll return a Boolean. Let's set the correct
parameter names. Here. We'll create a new
mnemonic seed object and store it in the
mnemonic seed variable. Let's rename the
mnemonic seed parameter to the mnemonic seed string. Now we'll instantiate
the mnemonic seed object passing the mnemonic seed string as the constructor parameter. Now, let's create the master
key with the following code. Will call the two
master key method on the mnemonic seed object, passing an empty string as the first parameter and the main net private
prefix as the second. Now let's create
the extended pub key string with the
following code. We'll use the extended
pub key service. So let's inject it
into this class. Then we will call the
create method on it, passing the master key
as its first parameter. The second parameter will be a concatenation between part of the segment address
derivation path and the index parameter. Let's change the
index parameter type to the integer class, so we can call the
toString method on it. The third parameter will be the address type
Segway constant. Then we'll call the get
key method on the result. Now, we'll obtain the
extended pub key object from the Bitcoin Java
library by using the extended pub key
on serialized method, passing the extended
pub key string as its parameter will obtain the expected address using the
segment address generator. Let's inject it into this class. We'll then call the
generate method on it, passing the extended pub
key as its parameter. Finally, we'll return the
result of comparing if the expected address is equal
to the address parameter. Now let's add the address
is valid method call to check the addresses in the
other methods of this class. In this method, Let's
add it two times. The second time we'll check
if the next address is equal to the expected
address with index one. Let's run our tests. Great, the tests have passed. Now we know that the addresses are being generated correctly. Now let's create another test. The next test will
try to transfer more transactions than the
number of generated addresses. Remember that we've set the initial number of
generated addresses to 20. So we must check
what happens when all initially generated
addresses are used. But running a test with 20 transactions would
take a lot of time. So we'll change the
initial number of generated addresses in
the test environment to three later to
speed things up. So let's copy this method
and paste it below. Change its name
to should receive Bitcoin in seven transactions to different addresses
with three addresses generated in our wallet and seven transactions in this test, our wallet would need to
generate four more addresses than the initial number
for this test to pass. Let's change the created
wallet name to my test wallet. For now, we'll create
a properties file for the test environment to change the initial number of generated
addresses for our tests. So let's create a resources
path inside the test package. Select the Resources
option below. Inside it will create a file called application
test dot properties. We'll copy the content of our
application dot properties file and paste it here. Now let's set the
initial number of generated addresses
property to three. Now let's go to the address
configuration class. The initial number of generated addresses method
is currently returning 20 will inject this value
into this class from the properties file using
this value annotation and this private field. Now in this method will just
return the created field. Now to use properties from the application test dot
properties files in our tests. Let's go to the GUI test class. Add this annotation to it. Okay, Now every test file that
extends the GUI test will get its properties from the application test
dot properties file. Now back to our last test. Let's change the name of this
variable to first address. This variable's name
to second address. Now let's copy these
lines and generate four more transactions to form more newly generated addresses, modifying their
code accordingly. Let's change the expected
table row size to seven. Let's copy these lines also to validate all addresses
generated in the test. Let's change the methods
parameters as required. Now, let's run our tests. As expected. The
last test has failed after the third transaction that happened because the
wallet couldn't find the fourth address since it generated only the
first three addresses. In the next video,
we'll fix that. Before finishing the video, let's add the initial number
of generated addresses property to the application
dot properties file. Let's set it to 20 so
that the wallet will work as before in the
non-test environment.
36. Generating More Addresses: In this video, we'll make
our last created test pass. To do that, let's go to the update current wallet
addresses service. In this method, let's add
the following if statement. After calculating the
next address index. Inside the if statement, we'll call them must
import addresses method passing the next address
index and the address type. Let's create this method. Will only import addresses if the next address
index is equal to the total number of addresses in the current wallet with
a given address type. If that's the case,
it means that we ran out of addresses and thus cannot populate the
next address for our wallet without
creating more addresses. Inside this method
will return the result of checking if the next
address index is equal to the current wallet
get addresses count method call will pass the
address type to this method. And let's create this method. This method will return an int and it will
return the addresses, get adresses count method call, passing the address
type as its parameter. Let's create this method. Here we will simply
get all addresses with the given address type
in a list and return the list size back to the Update Current
wallet addresses service. In the if block will first get the current wallets
extended pub keys, and store them in a variable. Let's create this method. Here. We'll return the
extended pub keys field. Let's create it. Let's also add a
setter for this field. We'll call this set or in the update current wallet
service update method. Passing the wallets
extended pub keys to it. Now back to the Update Current
wallet addresses service. We'll call the add
addresses method on the ADD address service. Passing the previously
obtained extended pub keys as its first parameter and the next address index
as its second parameter. Let's inject this
service into this class. Let's create this service and
the API services package. Let's add the service
annotation to it. Now, let's finish injecting
this service into this class. And let's create this method
in the ad addresses service. Let's change this parameter
name to from index. What we plan to do now is
to generate addresses for the extended pub keys that
were passed as parameters. The number of generated
addresses will be equal to the initial number of generated
addresses injected field. And the first address generated
derivation index will be determined by the firm
index variable, e.g. suppose the initial number of generated addresses is equal to 20 and the from index
variable is equal to 20. In that case, this
method will generate a dresses with a derivation
index of 20 to 39. To do that, for each
extended pub key. We'll call the add
addresses method, passing the key and the
from index parameter. Let's remove this S letter. Now, let's create this
private add addresses method. It will call the add
addresses method on the extended pub key
as its parameter. We will use the address
sequential generator. So let's inject it
into this class. We'll call the
generate method on the address
sequential generator, passing the extended
pub key key, the extended pub key type, and the from index variable
as its parameters. Let's go to the generate
method implementation. Will change it to
generate addresses with derivation indexes using
the from index parameter. So let's add from index
as its third parameter. Here in the range method
call will add the from index variable as
its first parameter. And the sum of the from
index parameter and the initial number of generated
addresses as the second. Now back to the ADD
address service. Let's create the ad
addresses method on the extended pub key class. Let's fix this parameter name. Here. We'll call the addresses add all method passing the
addresses parameter to it. There's a problem
with this code. The addresses field is of type
list, which is immutable. That means that
when we try to add items to it through
the add all method, it'll throw an error. So let's change it to the ArrayList type,
which is mutable. Let's also make it final
and instantiated here. Let's also change the
addresses getter and the addresses method to
get and set an ArrayList. Let's also remove
the set addresses method since we won't
use it anymore. Back to the add
addresses service. Let's wrap the generate method call result in a new ArrayList, since the ad addresses method now accepts only an ArrayList. Now let's run factors that
create wallet service. Since we've made
changes that affected. First, let's remove the address sequential generator
service from this class. Let's also remove this method. Let's remove this code line. We'll use the add
address service to add addresses to the
extended pub keys. So let's inject it
into this class. And let's call the add
addresses method on it, passing the extended pub keys
and zero as its parameters. Let's finish injecting
it into this class. Now back to the Update Current
wallet addresses service. We'll call the set addresses method on the current wallet, passing the extended pub
keys as its parameter. Let's remove the
address rows clear method call in the set
addresses methods since we don't want to remove
any addresses table rows after setting these addresses. Instead, let's create
a method called clear address rows,
which will do that. Then in the update method of the update current
wallet service, we'll call this method to clear the address rows only
after adding a new wallet. Now back to the
Update Current wallet addresses service will get all the newly
generated addresses in the current wallet and store
them in this variable. To get the addresses will call the get addresses as strings method on
the current wallet. Passing the next address
index as its first parameter. And the sum of the
next address index and the initial number of generated addresses field
as the second parameter. Let's inject this
field into this class. Let's add the
qualifier annotation to use the bean defined in the address
configuration class to inject the desired value
into this parameter. Now, let's create this method. Let's change these
parameter names to from index two index. This method will
return the addresses, get addresses as strings method call passing the same
parameters that received. Let's create this method
in the addresses class. This method will return
all its addresses with indexes between the parameters
from index two index, but not including the ladder. To do that, it will return
the following code. We'll use the flatMap
method to convert all addresses into a
stream of address objects. Then we'll filter
the stream to only return addresses with
index is greater than or equal to the
firm index variable and smaller than the
two index variable. Then we'll call a map on the result to return a
stream of address strings. Finally, we'll
convert the result to a list and return it. Now back to the
Update Current wallet addresses service will use the node multi import
address clients to import the newly generated addresses
in our Bitcoin node. Remember that this
is needed because it's required for the
node to know about our addresses so it can send related transactions to
our application wallet. So let's inject it
into this class. Now we'll call the input
addresses method on it, passing the current wallet name, the address strings, and a
new date as its parameters. Great, our address rotation
feature should work now, even after all initially
generated addresses are used. But let's handle
another issue here. We're setting the
next address as the receiving address
in our wallet. Even if it's addressed
type is a change address. Let's fix that. Since we want only addresses
with the Segway type, not the Segway change type being said as receiving
addresses in our wallet. So we'll add this if statement. If the address type is equal to the main
address constant, which will set it to segue. Now. Then we'll set the
next address as the next receiving address
in the current wallet. One more thing, let's remove
this platform run later call will call the run later method only wrapping the code that will really change the UI. So let's use it to wrap only the current wallet set
address roll-call. Let's also use it to wrap the current wallet set
receiving address method call. That way, we avoid
blocking the UI while communicating with
our Bitcoin node in the import
addresses method call. Now let's go to the
addresses table controller. I've noticed that the
TableView is sometimes not updated to reflect the
actual state of its rows. To fix that, Let's add
this code to this method. Will add a listener to the current wallet
observable address rows. In the body of this
lambda will simply call the refresh method
on the addresses table. That way, for every update
in our wallet addresses, we guaranteed that
the table view, we'll refresh it
state and we'll show the current information of our wallet addresses
on the screen. Now, let's go to the address sequential generator
test and add zero as the generate method third parameter so that this
test continues working. Let's also fix the create
wallet service test. In the setup method, Let's instantiate the
ADD address service, passing the address
sequential generator as its parameter. And let's substitute
the third parameter of the create wallet service
for the ADD address service. Now, in the last
receiving Bitcoin test, Let's make a little change. After sending Bitcoin
to the third address. Let's add asleep
method call passing timeout and seconds
as its arguments. Let's also add the
same call after sending Bitcoins to
the sixth address. The reason for these
calls is that before sending Bitcoins to the
fourth and seventh addresses, we have to generate
them and import them to our nodes before accepting
transactions to them. And the importing
process takes some time. Let's finally run all
our application tests. Great, all tests have passed. We finally finished
the basics of the receiving Bitcoin
feature of our wallet. Now, we'll start to implement the observation of new
blocks that are added to the blockchain so we can
update the number of conformations that are
addresses have C, yeah.
37. Testing Confirmations: In this video, we
will make tests to ensure that when a block is
mined in the Blockchain, our wallet receives
updated transactions with increasing conformations. So let's move the
auxiliary methods in the receipt
Bitcoin test class to the GUI test so that we can use these methods in other
classes that extend it. Now, let's move all
these properties to the GUI class also
for the same reason. Now, Let's cut these two lines and paste them to the
load wallet and add balanced method in
the GUI test class. In the setup method in
the received test class, we'll just call this method. Let's delete all these unused
imports in this class. In the GUI test class, Let's make these methods
and properties protected. Now in the GUI test package, Let's create the receiving
block test class. It'll extend the GUI test class. Let's copy the setup method and the receive Bitcoin
test and paste it here. Let's also copy
the first test in the receive Bitcoin
test and paste it into the receiving
block test class. It'll be used as a blueprint for the next test we'll create. Let's change the
created wallet name to my test wallet five. Let's change the name
of the test tube should receive Bitcoin and add
hashtag confirmations, confirmations to
receive that dress. The hashtag conformations
will be interpolated by the confirmations test
variable we'll create. Now, after sending Bitcoins will call the mine
blocks method, passing the confirmations
variable as its parameter. Let's create this method. It'll take an int
as its parameter. This method we'll use
the node generate to address client
to mine blocks. So let's paste
some code into it. In the GUI test class, copy this code line
and paste it here. Modify the address variable
name to note address. Do the same with
this other line. Change the generate method
second parameter to blocks. So it will use this variable to define the number
of blocks it'll mine and change this
parameter to note address. Now, let's copy this weight for a method call and pasted here. Delete these code parts. This method will wait until the first address table row
confirmation cell value is equal to the blocks variable. In the Venn block will check if the first table row
confirmation cell value is equal to the
confirmations variable. Let's include a where clause. Here. We'll set the
confirmation is variable for some test values. We'll add a placeholder for the second column
in the whereClause, because it apparently only works when it has
two or more columns. Let's set the confirmations
values to 12.3. Now, let's run our Bitcoin node, and let's run our new test. Great, the tests have passed. Now, let's make another test
for the following scenario. We want to know
what happens when an address receives
two transactions. And the second transaction has fewer conformations
than the first. In this case, we expect that the address number of
conformations is equal to the number of conformations of the transaction with fewer
conformations to that address. So let's duplicate
the first test. Let's change its name to should receive Bitcoin and consider the transaction with
fewer conformations as the address confirmations. Let's change the
create wallet name to my test wallet six. Now, let's duplicate
these code lines to send bitcoins and
mine blocks again. In the second mine blocks
call add minus one in its parameter to mine fewer blocks after the
second transaction. In the block, we expect
confirmations minus one as the number of confirmations in the
first table row. Let's delete this
line since we don't want this test case
because it would try to mind zero blocks in the second mine blocks
call, which would fail. Let's run our tests again. Great, the tests have passed. So we conclude that
our wallet is already prepared to update the number of conformations of our addresses. That's because after
each mine block, the node sends a message
through zero MQ to our application containing the previously
imported transactions, but with an updated
number of confirmations.
38. The Transactions Table: In this video, we'll start to make the transactions table. This table will be similar
to the addresses table, but instead of rows containing information about addresses, this table will contain
information about every transaction our
wallet sends or receives. So let's start creating a
GUI test for this table. In the GUI package, create a class called
receiving transaction test. Copy these methods from the receiving block test
and paste them here. This class will extend
the GUI test class. Change this test name to have
should receive transaction. Let's format this method. Remove it's where block, change the create wallet name to my test while it's seven. Delete this line. Now, after calling
the method sleep, let's make the test
click on a component with an ethics ID of
transactions tab. Now, let's add another
optional parameter to the send Bitcoin
and wait method. This parameter will be a string and it'll be called
lookup component. Its default value will be
hashtag addresses table. Let's substitute
this parameter in the lookup method call for the
lookup component variable. Now in the test, let's add these parameters to the send Bitcoin
and wait method. The fourth parameter will be
hashtag transactions table. That way after sending
a transaction, this method call will wait for the transaction table to be populated with one
row containing a balance of 1.0 Bitcoin. Now, let's change
the lookup method parameter to hashtag
transactions table, also. In the then block, Let's
just leave the assertion of the table row size being
equal to one for now. Now, let's design our table. Let's go to the
playground dot FXML. Let's copy all this
content between the tab tag and paste it below. Now change its text property to transactions and it's ethics
ID to transactions tab. Also change the table
view tag ethics ID to transactions table. Now let's change the text field of the first column
to transaction ID. The table will also have a balanced column and a
confirmations column. So we'll maintain these
tags as they are. Let's duplicate the last tag, change its text to date. This column will contain the creation date of
these transactions. Now let's click on
the Scene Builder to see how our new
tab and table looks. Okay, It's looking nice. Now, let's create a new
FXML for this component. Let's call it transactions
underscore table. Let's delete the
boilerplate code. Let's copy this content from the playground dot FXML
and paste it here. Let's import these tags. Change this tag to
fx colon route. Delete this content as its type. Let's set the table
view component path and set this URL as the XML NS field as the ethics ID property
set transactions table. Now delete these tags, which we didn't need to copy. Now let's add these ethics
IDs to these column tags. They will be useful
later when we make the transactions
table controller. Now in the main window dot FXML, let's duplicate the content
between these tags. Let's change the tab text to transactions and it's FX
ID to transactions tab. Now, change this tag to
transactions table controller. Let's create this controller in the GUI dot controllers package. Let's imported into the
main window dot FXML. Now let's add the component annotation to the transactions
table controller. It'll extend the
table view class parameterized with the transaction row
class we'll create. Let's create the
transaction real class in the observables package. Now, let's go to the Receive
tab controller and copy its constructor
and paste it here, making the appropriate
modifications to it. Let's change its value
annotation to point to the transactions
underscore table dot FXML. Let's also inject the current
wallet into this class. It will be useful later. Now, we need to include the transactions table
controller in the list of custom components in
the GUI started listener, we could simply add
another or clause in the if statement in the
initialize FXML method. But let's refactor this class
by doing the following. Let's create a
private static set of class objects called
custom components here. Let's instantiate it here
with the set class of method. Let's include these controller
classes in the set. Now in the if statement
will just check if the custom components set
contains the given type. Now let's go to the
receiving transaction test. Now, let's run the clean and compile lifecycle Maven goals. Sometimes this is
necessary to include new FXML files in the
compiled project. Now, let's run our
Bitcoin core node if it's not already running. Finally, let's run the test. The test has failed because
it haven't find a row containing the
transaction information in the transactions table, will continue to implement this feature in the next video. See you.
39. Populating the Transactions Table: In this video, we'll finish
the implementation of the transactions table
and make this test pass. So let's go to the
update UTXO service. If you remember, this
update method is called after we receive a
transaction from our node, the node task class monitors our node for
transaction messages. Then our application
publishes a transaction received event that is listened to by our
transaction listener. The transaction listener
parses and filters the message and calls the update UTXO service update method. So after retrieving UTXOs and updating the current
wallet addresses, let's update the current
wallet transactions using this service. Let's inject it into this class. Let's create it in the
GUI dot services package. Let's add the service
annotation to it. Now, let's finish injecting
it into this class. Now, let's call the update
method on this service, passing the UTXOs variable
as its parameter. Let's create this method. This method will
run asynchronously. So let's add the async
annotation to it using the default executor
service as its parameter. Let's do the same with
the update method in the update current wallet
addresses service. Now let's implement
the update method and the update current Wallet
transaction service will convert the list
of UTXOs into a list of transaction
rows and store the result in the transaction
rows variable using the following code will filter the stream of UTXOs by
maintaining in the stream only the UTXOs with addresses
in the current wallet. So let's inject the current
wallet into this class. Now, in the lambda body
will get all addresses as strings and check if it
contains the UTXO address. Now, we'll call the map method
on the resulting stream, passing the transaction row
from method as its parameter. Let's create this method. It'll be a static
method and it will return a transaction row object. Similar to the
address row class. The transaction row class will represent a row and
the transaction table. So let's add the following
properties to this class. Each property will represent a column in the
transaction table. Let's delete the
final keyword from each property because the IDE does it let automatically create setters for these
properties if their final. Now using this ID feature, add getters and setters
for each property. Now we can add the final keyword back
to these properties. Now, let's create a constructor. The constructor will take each class property
as its parameters. And we'll use the
property setters to set the properties in
the constructor body. Now, we can continue
implementing the method. It'll return a new
transaction row with the following parameters. Back to the Update Current
Wallet transaction service. Now we'll convert the
resulting string into a list. Now we'll add the obtained
transaction rose to the current wallet using the
add transaction rows method. Let's create this method. It'll use the
transaction rows field. So let's create it now. It's type will be the
transactions Rose Class. Let's create it in the
observables package. Back to the current wallet. Let's make it final and
instantiate it here. Let's also make the
address rows field final. In the ED transaction
rows method, let's call the add
transaction rows method on the transaction rows field. Passing the given transaction
rows as its parameter. Let's create this method. Just like the address rows
class is responsible for managing the list of address rows shown in the
addresses table. This class will do the same to the transaction rows
and transaction table. So for each transaction row, we'll check if the
transaction roadmap field already contains it. So let's create this field. It'll be an observable
map where the key is a string and the value
is a transaction row. Let's instantiate it here with
an observable map wrapper, passing a new linked
HashMap as its constructor. So if the transaction roadmap contains the transaction
row ID in its keys, then we'll set the transaction rotate to the date of the
transaction present in the transaction roadmap will do that to preserve the
transaction creation date. Then after the if statement will remove the transaction
row from the map by its ID and put the given transaction
row again in the transaction roadmap
using its ID as the key. Again, we're removing and adding the same transaction
row to force the observable map to publish change events that will
be listened to later. Now, let's create the observable list field
that will be used to bind changes in the
transaction roadmap to the table controller. Let's instantiate it here using an observable list wrapper instantiated with
a new linked list. Now let's create the method get observable transaction row list. It will return the
transaction role list. Now will bind the
transaction roadmap with the transaction role
list in the constructor, just like we did for
the address row set and the address row list in
the address rows class. So to speed things up, let's copy the address
rows constructor and paste it here. Let's fix its name. Now, instead of calling methods
on the address role list, we'll do it on the
transaction role list. Instead of using a set
change listener will use a map change listener parameterized with a string
and a transaction row. Also, instead of using the
get element removed here, we'll use the getValue removed. Here. We'll use the
get value-added. And here we'll add a listener
to the transaction roadmap. One more thing for this to work, we have to implement
the equals method in the transaction row class. This is because the
transaction row list, we'll use the method to decide what transaction row
to remove from itself. So let's implement it
using this IDE feature. Make sure to check the
use getters checkbox in this window will use only the ID property to define
equality in this class. Also, click on the ID checkbox here so that it can't be null. Okay, the IDE generated
these methods as expected. Back to the transaction
rows class. Let's create the clear method, which will be called when
a new wallet is created. Inside it will call
the clear method on the transaction roadmap and
on the transaction role list. In the current wallet class, let's create the good observable
transaction rows method. It'll return they get observable transaction row list method call on the transaction rows field. Let's also create the clear
transactions method here. It'll call the transaction rows clear method we've just created. Now let's go to the update
current wallet service. Here we'll call
the current wallet clear transactions method. By doing so, we
make sure we clear the transaction rows
after creating a wallet. Now, let's go to the
transactions table controller. Let's add the following fields. There'll be used in the
initialized method later to bind the table and its columns to the transaction rows observable. Now let's create the
initialized method. Here, we'll first set the transactions table items to the current wallet
observable transaction rows. Now we'll add a listener to the current wallet
observable transaction rows. We'll call the
transactions table refresh method in
the body so that every change that happens in the observable will trigger a
redrawing of the table. Also will bind every
column field to the transaction row field using the set cell
value factory, just like we did in the
addresses table controller. As the property value
factory parameter, we have to use the same property
names that we've used in the transaction real class
to do the binding correctly. Let's duplicate this line three times and change the
columns we're binding. Now let's go to the receiving
transaction test class. Make sure you're running the Bitcoin Core Node
and run the test. Boops, the test has
failed. Let's see why. There's a problem with the column transaction
balanced field in the transactions
table controller. To fix that, Let's go to the transactions
underscore table dot FXML. The problem is that we
misnamed the balance, confirmations and date FX IDs. All three are missing the word
transaction after column. So let's fix them and
run the test again. Great, The test has passed. In the next video, we'll add more
tests to make sure the transactions table
is working as intended. C, yeah.
40. Improving Tests for the Transactions Table and Running Our Application on the Test Net: In this video, we'll add
more tests to check if our transactions table
works for other scenarios. So let's go to the
receiving block test class. In each method will add code to test if the transactions table contains the
expected information about the receiving
transactions. So let's rename the table view in this test to address
his table view, to clarify its meaning. Now, we'll call the
click on method passing as its parameter,
the string transactions. Now let's copy the line
with the addresses table view variable
and paste it below. Let's change this variable name to transactions table view. In the lookup method, let's substitute this parameter for hashtag transactions table. Now let's copy these two
lines and paste them below and change them to call these methods on the
transactions table view. Let's do the same to
the other test method. But in this case, we expect the transactions table
to contain two lines, one for each transaction. And for the second row, we expect the number
of confirmations to be equal to the total
confirmations variable, which will create the total confirmations value will be equal to the number
of blocks mine so far, this number will be equal to confirmations plus
confirmations minus one. Since we expect
the second row to contain information about
the first transaction, it'll be confirmed
this number of times. Let's make a little fix in
the mine blocks method. Sometimes this code line raises a NullPointerException due to the TableView having a
null items property. To avoid that, Let's use the null safe operator before each property in the table view. Now, let's run our tests. First, make sure to
run your Bitcoin node. Let's run the tests. The last two tests have failed. That happened because the
transactions in the table, we're unordered, their
order is seemingly random. Sometimes these tests may pass because they can be
correctly ordered by chance. So let's fix that. We aim to sort the
transactions by date with the newest transactions
in the first rows. So let's go to the
transaction row class. In the from method.
Let's change how we pass the data to the transaction
row instantiation. Instead of using a new date, let's use the
instant now method. We're doing that because
the instant class is more precise and
easier to Parson sort. Now let's go to the
transactions table controller. In the transactions table
set items method call. Let's pass a new sorted
list as its parameter. The current wallet observable
transaction rows will be the first parameter
instantiating the sorted list. The second parameter will be the comparator
comparing method call. This method will
pass a lambda as the first parameter or
a transaction row will be the parameter in
the lambda body. Let's call the
instant parse method, passing the transaction
rotate as its parameter. The parse method will generate an instance object
from the date string. The second comparing
method parameter will be the comparator reverse
order method call. This way, we expect the
transaction table to be sorted by the transaction date with the
latest transactions first. Now let's run our tests again. This time only the
ones that have failed. We'll do that by
clicking on this button. Great, the tests have passed. Now let's add transactions table tests to the receive
Bitcoin test class. We'll do the same as we did in the receiving block test class. For each method, we'll
refactor the table view name, make the test click on the transaction tab and
make assertions about the expected content in the transactions
table. Let's do it. Okay, now, let's
run these tests. Great, the tests have passed. Now we'll run our wallet on the test net environment
for the first time. The test net environment is
similar to the main net, but their coins are worthless. It's much easier to mind and
their block time is shorter. It requires us to run and sink our Bitcoin node on
this environment. We covered how to do that
in the course setup guide. So let's modify our bitcoin.com to run our node in the
test net environment. Just change this key from
reg test to test net. Let's stop running our
node on the reg test. And let's run our note again to start sinking our
note on the test net. My note is almost sink, so it'll take a few
minutes to catch up. But depending on the
last time you ran it, it may take longer for
us to sync because it'll take some time to
download the latest blocks. Anyways, while it's sinking, Let's modify our projects
so that we can run our application on the
test net successfully. In the resources path, Let's create the file application test
net dot properties. This file will contain
the properties used in our project when we run the application on the
test net environment. Let's copy the content from the application dot properties file and paste it here. Now, change the Bitcoin
environment to test net. And the no dot RPC
dot URI port 218332. This is the default
port of our node when it runs on the
test net environment. Now, let's make a
little improvement. In our wallet, It would be nice to be able to copy
the transaction ID is shown on the
transactions table by simply right-clicking on
them and clicking on Copy. Let's implement this feature. To do that, let's go to the transactions
underscore table dot FXML. Now let's add the
following code here. First, let's add these
context menu tags inside it. Let's add the items tag. Then the menu items tag. In this tags text property add the string copy and hashtag coffee transaction ID
as it's on action property. This tells the
application to call the copy transaction ID method in the transactions
table controller. After we click on the Copy
Menu item in the context menu. The context menu is
a menu that appears after we right-click on
something on the screen. Now, let's go to the
transactions table controller. Let's create the copy
transaction ID method here. First, we'll get
the transaction row that was right-clicked
using this code. Now we'll simply call
the copy method. We'll create passing the transaction row
ID as its parameter. To create this method
will create first a utils package inside
the BYOD W package. Inside it, Let's create
a class called Copy. Now, let's create the
copy method here. As a static method. Will instantiate a
clipboard content object, storing it in the
content variable. Then we'll call the put
string method on it, passing the variable
text as its parameter. Let's add this variable as
the copy method parameter. Finally, we'll call the get
system clipboard method and set its content to the variable content in the transactions
table controller. Let's import this
method. One more thing. When running the Bitcoin node on the test net environment, our node may receive different
types of transactions that the Bitcoin Java
library cannot parse yet and they raise errors
when trying to do it. Although not parsing
these transactions will affect our wallets, the error is raised well. To avoid that, in
the node task class, wrap the transaction method from byte stream in a
try-catch block. In the catch block, let's use a logger to
warn us about the error. In the one method call
will pass a phrase indicating the error and a pair of curly braces at the end. These curly braces will be interpolated by the
second argument, which will be the hex
encoded content variable. The third parameter will
be the exception object. So we can access the
entire stack trace of the error in the logs. After logging, the error
will go directly to the next iteration of the while loop by using
the continue keyword. Let's create the logger field. It'll be a static property. Let's instantiate it here
using the logger factory. Let's fix the logger
class import. Actually, we have
to import it from the 4D dot SELF for J package. Now let's run our application on the test net environment. First, let's check if our node is linked on the test
net environment. Okay, it's already sink because it's progress
is equal to 1.00. Now let's go to the BYOD
W application class. Right-click on this
symbol and click on modify Run Configurations. Now, click on modify options and make sure the ad VM
options is selected. Now set it to minus d spring dot profiles dot
active equals test net. Click on Okay, and click
on the Run button. Now let's create a wallet. Notice that the receiving
address starts with TB, indicating that it's
a test net address. Now, let's copy this address. Will use a test net faucet
to obtain test net Bitcoins. A Bitcoin faucet is a website or API that offers
Bitcoins for free. They were very popular in
the first years of Bitcoin, when one Bitcoin was very cheap. Nowadays, there are fast way
to obtain test net coins. Let's go to the
website on the screen. It's a test net faucet. Let's paste the copied
address into this field. Choose an amount, and click on this button
to send us Bitcoin. Let's wait a little. Okay, the alert says that it sends some Satoshi's
to our wallet. Let's check if
we've received it. Great. Our wallet identified our transaction and
our transaction and address already
have one conformation. There's one weird thing though. The balance is formatted
in scientific notation. We'll fix that later. For now, let's copy
our transaction ID by right-clicking on it
and clicking on Copy. We'll check the transaction in a third-party
blockchain explorer. A blockchain explorer
is a website that shows information about
Bitcoin addresses, blocks, and transactions in
a nice and organized way. If your privacy concerned, searching for your red
dresses and transactions on third-party blockchain
explorer is not advisable since it allows them to link you with
your wallet data. But since we're
just using it for test purposes, that's okay. With that said, let's go to
the website on the screen, which is a Bitcoin test
net blockchain explorer, paste the copied
transaction ID on the search field and click
on the Search button. Here, we will obtain some information about
our transaction. Notice that it already
has two confirmations, but our wallet indicates that it has only one conformation. It turns out that on the
test net environment, our Bitcoin node only sends transaction
notifications through zero MQ when the
transactions are sent and when they
get one confirmation. After that, no matter how many more
confirmations that gets, the node doesn't notify
our application about it. We have to make our application
start to listen for block notifications so that
we can fix this problem. We'll start doing this
in the next video. See you.
41. Listening for Blocks and Formatting Balances: In this video, we will
make our application starts listening to block
messages from the Bitcoin node. That way, we'll be able to
update our transactions confirmations correctly in
the test net environment. Also will modify the address and transaction
balances formatting, since now they're not being displayed nicely on the tables. So let's go to the
bitcoin.com file. Let's change this
key to reg test. To start listening
for block messages, let's add the Z MQ pub
hash block property here. Its value will be the same
URL in the property above. So let's paste it here. This configuration will make the nodes send the hash block to our application after it detects a new block, extending
the blockchain. After that, the
code will create, will send a list
unspent request to the node containing all
current wallet addresses. In response will receive
a list of updated UTXOs that will parse
and use to update our red dresses and
transaction tables. Let's save the bitcoin.com file. Now, let's go to the
node task class. Let's duplicate this line. Let's change the
string to hash block. That way, we'll start to receive hash block messages
from the node. Now, in this if statement, Let's add the following code. If the topic is equal null, or the list with
these topics does not contain the received topic. Then we'll go to the next
iteration of the while loop. Now, let's add a
switch statement here. In case the topic variable
is equal to ra t x. Then we execute
the code to parse and process the
received transaction. Let's move the line
that publishes the transaction received
event to this location. That way we can remove this redundant
continue statement in case the topic variable
is equal to hash block. Then we'll use the
application event publisher to publish a new
block received event. Let's create this event in the BYU w dot no
doubt events package. Now let's create a
listener for this event in the BYOD w dot, dot
listeners package. Let's add a component
annotation to it. Let's make it implement the application
listener interface, passing the block received
event as its type parameter. Let's implement its method. First. We'll get all current
wallet addresses using this method and store them
in the addresses variable. Let's inject the current
wallet into this class. Then using an if statement will check if the addresses
variable is empty. If it's not, then we'll use the update UTXOs service to update our addresses
and transactions. Let's inject it into this class. We'll call the
update method on it, passing the address is variable and the current wallet
name as its parameters. Now let's go to the
transaction received listener will optimize our code. Remember that after receiving
one confirmation in the test net environment and all confirmations in the
reg test environment, the node sends a
transaction message to our application
through zero MQ. Since from now on our block
received listener will update all current wallet addresses when it
receives confirmations, it doesn't make sense to update the addresses and
transactions table in the transaction received
listener class after we receive repeated transaction
messages anymore. Therefore, we will modify
this class to only process transactions if the current wallet
doesn't contain them. So let's add this if statement. If the current Wallet
transaction IDs contain the Received
Transaction ID, then we simply return. Let's create this method. It'll return a list of strings, and it will return
the transaction rows, get transaction IDs method. Let's create this method. Here. We'll return the following code from a stream of the
transaction role list. We'll call the map
method passing a reference to the
transaction row getID method. Then we'll convert the
result to a list and return it back to the transaction
received listener. We have to wrap this code in
a try catch block because the transaction ID method throws a checked exception
in the catch block, will use a logger to
print a warning message. Let's inject the logger
into this class, just like we did in
the node task class. Let's also add a return
statement to the catch block. Now, let's fix the address row balanced
formatting problem. Instead of using the double
class two string method, we'll use the Bitcoin
format or format method passing the address
balance as its parameter. Let's create this class
in the utils package. Now, let's create
the format method. Here, we'll instantiate
a new decimal format symbols object passing the locale route to
its constructor. Then we'll set the dot as its decimal separator and the comma as it's
grouping separator. Then we will instantiate a new decimal format object as its constructor parameter
will pass this pattern. And the symbols variable. Then we'll set the format
or grouping used as false. Then we'll set its minimum
fraction digits to eight. And it's maximum
fraction digits 282. Finally, we'll call the format
method on the formatter, passing the received number as its parameter and return it. Using this method will format
any number of pass to it with a decimal digits and
without grouping separators. This type of formatting
is useful since one bitcoin can have at
most eight fraction digits. This formatting also facilitates the visualization of how many Satoshi is a balanced contains. Now let's go to the
transaction row class. Let's use the Bitcoin
formatter here to, to format the UTXO balance
in the frame method. Now, let's adjust our tests to cover this new
balance formatting. Let's go to the GUI test class in the send Bitcoin
in weight method, let's change the second
parameter type to double and adjust its
default value accordingly. Let's also add this
optional amount parameter with a default value of 1.0. Now, let's substitute
this parameter for the amount variable. And here let's wrap the expected total
amount variable in the Bitcoin format
or format method. Now, let's go to the
receive Bitcoin test. Let's modify this and Bitcoin
and wait method call to send 0.00 001 Bitcoin. So we can test the
new formatting when our wallet receives
Bitcoin fractions. Now, let's check every usage
of this method and modify their parametrization of the expected total
amount parameter to pass a double
instead of a string. Now, let's add these
null checks to the table view properties in this method to avoid having
null pointer exceptions. Now, let's run our node, and let's run all project tests. Great, the tests have passed. Now, let's run our application on the test net environment. To do that, let's adjust
our bitcoin.com file. Now, let's restart our node. After some minutes, my
note is fully sink. Now, let's run our
application in the test net environment using our previously modified
run configuration. Let's create a new wallet. Now, let's copy our
address and use a test net faucet to
receive Satoshi's, just like we did in
a previous video. Okay, our wallets
successfully identified the transaction and it's
balanced is just as we expected. Now, let's eat a sandwich and leave our wallet
running for awhile. After some minutes
are addressed and transaction already got
four confirmations. Great.
42. The Total Wallet’s Balance: In this video, we'll start to develop the total
balance feature. Basically, it'll be a text above the transactions and
addresses tab indicating the balance of the
currently loaded wallet will show the confirmed balance, the unconfirmed balance,
and the total balance. The confirmed balance will show the amount sum of
all confirmed UTXOs. The unconfirmed balance is the sum of all
unconfirmed UTXOs. The total balance
will be the sum of unconfirmed and
confirmed balances. So let's go to the playground dot FXML to sketch something. After the first
closing tab pane. Let's add this label tag. Now, let's add this
VBox dot margin tag. Inside it. Add this insets tag. We'll add some margin
properties here. Let's set the bottom
margin to 10.0, the left margin to 10.0, and the top margin to zero. Now, for testing purposes, let's set the text property of this label to this as a test. Let's see how it looks
in the Scene Builder. Okay, we see that the balanced texts
will be located here. Let's remove this text property. Let's create a new FXML file called total underscore balance. Let's remove this boilerplate. Now, let's copy all
the content and the label tag we've just
created in the playground. And paste it here. Let's also copy these
import lines two and paste them into the total
underscore balanced dot FXML. Now let's substitute the label tags for the FX colon root tag. It will have an FX id of total balance and a
type of Java FX dot, dot control dot label. Let's also include this
XML and S property. Now in the main window, let's add the total
balance controller tag after the first
closing tab pane. Now let's create this controller in the controllers package. And let's import it here
back to the controller. Let's make it extend the label class and add the
component annotation to it. Now, let's copy this constructor and paste it here,
modifying it accordingly. The FXML resource value will be the location of the total
underscore balanced dot FXML. Let's also inject the current
wallet into this class. Now in the GUI started listener, Let's add the total
balance controller class to the custom components set. In the total balance controller. Let's add the
initialized method. We'll use the inherited
setText method to set the label
text dynamically. Let's set it to test for
testing purposes for now. Now, let's create the
total balance test in the GUI package. It'll extend the GUI test class. Let's copy these two methods and paste them
into the new test. Let's rename this method two should calculate total balance. Now, let's make some
adjustments to the body. The name of the created wallet
will be my test validate. Let's remove this line
since we won't need it. Let's also remove these lines. With this code, will
get the text present in the total balance label and store it in the
label text variable. Then block will check if the label text variable is
equal to the following phrase. After receiving the transaction, we expect the label
content to show that our total and unconfirmed
balances are one Bitcoin. We expect the confirmed
balance to be zero since our transaction
won't be included in a block and mind
during the test. Before running our node, let's make sure that our
Bitcoin dot conf environment is set to reg test. Now, let's run our node. And let's run this test. It has failed as
expected because the total balance label contains the string test instead
of what we wanted. In the next video, we'll implement this feature
and make this test pass. See you.
43. Calculating and Showing the Wallet’s Total Balance: In this video, we'll finish implementing the total
balance feature. So let's go to the
update UTXOs service. In the update method will add a new method call to
update the wallet balance. For that, we'll call
the update method on the update current wallet
balance service will create. Let's inject this
service into this class. Let's create this class in
the GUI dot services package. And let's finish
injecting it here. Now, let's create
its update method. Here, we'll first calculate the unconfirmed
balance and store it in the unconfirmed
balance variable. For that, we'll need to inject the current wallet
into this class. Then we'll get its
observable transaction rows and convert them into a stream. Then we'll use the filter
method on the result to exclude the transaction rows with confirmations
different than zero. Then we'll use the
map method to convert the obtained stream
into a stream of transaction row balances. We'll use the parse
double method to convert each balanced
string into a double. Now we'll use the
reduced method, passing the double
sum method to it, to some every obtained
balance in the stream. Finally, we'll use the or else
method passing 0.0 to it. Therefore, in case the
obtained stream is empty, the unconfirmed balanced
variable will be set to zero. Now, let's duplicate
this code block. Let's modify it to calculate the confirmed balance and store it in the confirmed
balanced variable. We just have to change
the equals sign and the filter method to
this greater than sign. With this simple modification, we will obtain the sum
of the balances of all transactions with at
least one conformation. Finally, we'll call the set balances method on
the current wallet, passing the unconfirmed
balance and the confirmed balance
as its parameters. Since this code
will modify the UI, Let's wrap it in a platform
run later method call. And let's create the
set balances method in the current wallet class. To do that, let's add the
balances field to this class. It's type will be the
balances class we'll create. Let's create it in the
observables package. And let's instantiate it here. Here we'll call this set balances method on
the balances field, passing the unconfirmed, unconfirmed balances
as its parameters. Let's create this method. First. Let's add some private
fields to this class. We'll add the unconfirmed
balanced field of type string property. Let's instantiate it here with a new simple
string property. Let's duplicate this
line two times. Let's change these
field names to confirm the balance and total balance. Now in the set balances
method will call the set method on the
injected properties to set their values. Will use the Bitcoin
format or format method to format the balances
to the desired format. To the total balance
will pass the sum of the unconfirmed and
confirmed balances. Now, let's create getters
for these fields. Let's also create
the clear method. This method will set each
property balance to zero. We'll call this method
after we create a wallet to clear the values of the
previously loaded wallet. Now in the current wallet, let's create the good
balances method. It'll return the balances field. And let's create the
clear balances method. It'll call the
balances clear method. In the update current
wallet service, which is called after
a wallet is created, let's call the current wallet
clear balances method here. Now, let's go to the
total balance controller. Let's remove this line. We'll get the unconfirmed
balanced property from the current
wallet balances. We'll call the ad
listener method on it. We'll pass a lambda
as its parameter, where in the body we'll call
the update text method. Let's create this method. Will call the setText
method in it, and we'll pass a string
format method call as its parameter. The first parameter of the format method
will be this phrase. Each percent sign followed
by the letter S will be interpolated by the
following parameters of this method call. The next parameters will be the current wallet
total balance. The current wallet
confirmed balance. And finally, the current
wallet unconfirmed balance. Now in the initialized method, Let's duplicate this
line two times. We'll add the same listener to the confirmed balanced property and the total balance property. So let's modify these
lines accordingly. Let's refactor this string, including it in a
constant in this class. Let's call it balanced
text and make it private. Now, let's make a
little modification in the update UTXOs service. Notice that we already have an async annotation
on the update method. So adding the same annotation in the method's called
an it is redundant. Therefore, let's remove them. First in the update current
wallet addresses service. Then in the update current
Wallet transaction service. Another thing I've
noticed is that we need to wrap the ad
transaction rows method call and a platform learn later method call since it
modifies elements in the UI. Now let's go to the
total balance test. Let's make sure that our
Bitcoin node is running. And let's run our test groups. The test has failed.
Let's see why. Turns out that I forgot to add
the services annotation to the update current
wallet balance service. Let's fix that. And let's run our test again. Great, The test has passed. In the next video, we'll add more balanced tests in our other tests to make sure
it's working as intended.
44. Adding More Tests for the Total Balance Feature: In this video, we'll
implement more tests to ensure the balance feature
is working properly. So let's copy this line and
the total balance test, we'll first add some
test assertions about the balance and the
receiving block test. So let's paste the line here. Let's also copy this line and paste it into the
then block of this test. In this test, we expect that the total balance will
be equal to one Bitcoin. So let's adjust this
phrase accordingly. We also expect that all the
balance will be confirmed. Let's do the same
for this other test. In this case, we expect
that the total balance will be equal to two
bitcoins, also confirmed. Now let's check if our node
is running and run the test. The tests have passed. Now let's add some
balance assertions to the receiving
Bitcoin test class. In these tests will expect
only unconfirmed balances. Let's run these tests. Great, the tests have passed.
45. The Send Bitcoin Tab: Okay, so far with our wallet, we can receive Bitcoins
using generated addresses. We can also inspect the
balance received by each address and check information about
our transactions. Now, we'll start to implement
the ability to spend those bitcoins by sending transactions to other
addresses we want to. So let's start
creating a test called send Bitcoin test in
the GUI test package. It'll extend the GUI test class. We'll add the same setup method. We added two other tests, calling the load wallet
and add balanced method. Let's create a test called
should send Bitcoin. Now in the wind block, will create a new
wallet and send one Bitcoin to its
first address. The following code will be very similar to the code
of the other tests. After sending one Bitcoin
to our wallet address, we'll click on the Send tab. Then we'll click on the amount field to define
how much we want to send. Let's send 0.5 Bitcoin, which is half of our
balance at this point. Now, we'll click on the
address to send field. In this test will send
bitcoins to another address. So let's use the Node get new address client to
get a new node address. Then we'll write the
obtained address on the clicked input. Now, we'll click on
the Send button. After that, we expect that a modal opens with
information about the transaction
and an Okay button to confirm and send
the transaction. So let's call the click on method passing okay,
as its parameter. Now we'll click on
the transactions tab. And with the following code, we'll check if the transaction
table has two rows, one for the receiving
transaction and one for the transaction
we've just sent. Now, let's go to the
playground dot FXML to design our Send tab. Let's duplicate the code
for the Receive tab. And let's change
its text to send. Let's delete these lines so we begin with a fresh
new tab to work on. Now, let's go to
the Scene Builder. Let's click on the Send tab. Let's click on the
body of the tab to select the grid pane
component inside it. Let's set its pref
height to 130, and it's pref width to 600. Now let's add a label control to the grid pane inside
the Send tab. Let's set ten to its top, right and left margins. Let's change its name
to address this end. Now, let's add a text
field to this grid pane. Let's change its grid
pane column index to one. It's not allowing us to do that. First, we have to add a
column to the grid pane. Let's right-click here and add two rows and one column
to the grid pane. Now we can change the
TextField column index to one. Let's set ten to its top, right and left margins. Let's change its
pref width to 350. Let's also change this
Collins pref width to 600. Let's add another label
to the grid pane. Let's change its
row index to one, and let's set ten to its top, right and left margins. Let's also change its
text to amount to send. Let's add another text
field to the grid pane. And let's set its row index to one and its column index to one. Let's set its pref height to 26. And it's pref width to 100. Let's set ten to its top, right and left margins. Now we'll wrap the last inserted
text field in an H box. So let's add an H box
to the grid pane. And its row and column
indexes to one. Let's move this text
field to the H box. Let's also set ten to
the eight bucks top, right and left margins. Now let's add a
label to the H box. Let's also set these margins to ten and change its text to BTC. Actually, let's set these
margins back to zero. We'll set five to these
paddings instead. Now, let's add an OK
button to the grid pane. And let's set its
row index to two. Let's also set these
margins to ten. And let's set its text to send. Now, let's copy all the Send tab content we've created in
the playground dot FXML. Let's create the send
underscore tab dot FXML in the FXML package. And let's paste the copied
content into this file. Let's change the tab tag to
the ethics colon root tag. Let's set its type
to Java effects, dots seen, dot control, dot tab. Now let's import all
the remaining tags. Whoops, we wrongly set
the type property to label. Let's fix it. Now, let's set the
FX id of these tags. First, let's set this
fx ID to send tab. This fx ID to address, to send this fx ID
to amount to send, this fx ID to send. Now let's go to the main
underscore window dot FXML. Let's duplicate this line and let's change this tag
to send tab controller. Let's create this controller
in the controllers package. And let's import it here. It'll extend the tab class and let's add the component
annotation to it. Let's copy the Receive
tab constructor and let's paste it here,
adjusting it accordingly. Let's inject the current
wallet into this class. Now let's add the Send
tab controller class to the set of custom components
in the GUI started listener. And let's change the value of this parameter to send
underscore tab dot FXML. Now let's make sure our
Bitcoin node is running. And let's run our
send Bitcoin test. As expected, the
test has failed. In the next videos, we'll continue
implementing this feature. C, yeah.
46. Transaction Fees and Sizes: In this video, we will learn more about Bitcoin transactions. More specifically,
we'll learn more about transaction size and
transaction fees. This knowledge will be necessary later when we start building transactions to
transfer Bitcoins from our wallets to
other addresses. From the previous presentation, we learned that the
transaction fee is equal to the
sum of Bitcoin in inputs minus the sum of Bitcoins and outputs
of a transaction. Remember that the
transaction fee is the price you
pay for a minor to pick your transaction from the mental included in a
block and mine it. The higher the transaction fee, the faster the transaction
will be included in a block. With a small transaction fee, your transaction will stay
in the mental for more time. So it's important to choose
the right fee to ensure that your transaction
gets confirmed in a reasonable time
without overpaying it. The transaction fee
varies a lot with time. It's value follows the
logic of supply and demand. The greater the number of
transactions being sent, the greater tends to be
the transaction fee. And the greater the
number of miners, the smaller tends to be
the transaction fee. This graphic shows the
average transaction fee in dollars in the
last two years. Notice that in this period, the transaction fee
varied from a couple of dollars to more than
$60 per transaction. So how do we calculate
the transaction fee? Considering the
transaction blocks have limited space but
unlimited demand, it makes sense that fees are calculated by transaction size. There are different
ways to obtain the recommended
transaction fee rate. E.g. you can use websites such as Bitcoin
Fees, earned.com. Here we can see how many
transactions have per fee rate in the last 24 h. In
the right column, we see an estimate of how
long it would take to confirm a transaction with these
fees in blocks and minutes. If you scroll down, it gives a recommended fee rate to use. That almost guarantees that your transaction will be
confirmed in the next block. Men pull dot space is another site that along
with cool graphics, showing the average fee
rate for each block, also shows the recommended
fee rate to use for low, medium, and high
priority transactions. The higher their priority, the faster the transaction
is expected to be confirmed. We can also use our
Bitcoin Core Note RPC estimate smart fee method. According to its documentation, it takes as a parameter,
a confirmation target, which is the number of
blocks that we expect to transaction with
the return fee rate will take to be confirmed. It also takes an
optional parameter to define the estimate mode, which can be unset,
economical, or conservative. The fee rate is
returned in Bitcoin per kilo virtual byte or KV byte. These examples show
that recommended fee rates can vary a lot
between different tools. For simplicity, in
our wallet will use the Bitcoin Core estimates
smartphone RPC call, since it won't
require us to query external tools with a fee rate. And hence, we can calculate the transaction
fee by multiplying the transaction
size in v bytes by the fee rate in
Satoshi is privy bite. But wait, what is
a virtual byte? To calculate the
transaction fees, there are two important
concepts to understand first, virtual size and virtual byte. Virtual size, or V size, is the transaction size in
virtual bites or v bytes. They are concepts
invented to account for the smaller size of
Segway transactions. The V size is calculated in a backward compatible
way so that preset what transaction
sizes are equal to preset what transaction V sizes. For Segway transactions,
the V size is equal to the non witness part size of a transaction plus its
witness size divided by four. The witness is a field present only in Segway transactions. It contains the fields that in pre segment transactions were located in the scriptSig
field of each input. This different way
of calculating transaction sizes results in smaller transaction V sizes for Segway transactions compared
to legacy transactions. E.g. for transactions with
one input and one output, Segway transactions are
110 v bytes in size, while legacy transactions
are 192 v bytes in size. For transactions with one
input and two outputs, Segway transactions are
141 v bytes in size, while legacy transactions
are 226 v bytes in size. Finally, for transactions
with two inputs into outputs, Segway transactions are
209 v bytes in size, while legacy transactions
are 374 bytes in size. So let's say we want to
calculate the fee for a segue transaction
with one input and two outputs with
a fee rate of 19. Satoshi is privy byte. To do that, we would multiply
its transaction v size, which is 141 v bytes, by the fee rate, which is
19 Satoshi's per v bytes. That would give 2,679 Satoshi's in fees for
that transaction. So in summary, to
construct a transaction, we would need to build
the following items. For one output, we would need an address and an amount
to send to that address. If there's a need for a change
and most transactions do, then we would need to
build a second output containing a change
address and an amount. We would also need a
variable number of inputs whose Bitcoin amount sum
must be greater than the amount to send plus
the transaction fee. Remember that an input will
refer to an unspent output of a previous transaction sent
to one of your addresses. A change will be needed if
the sum of input amounts is greater than the amount to
send plus the transaction fee. If by luck or by design, the sum of input amounts is equal to the amount to send plus the transaction fee than the transaction won't
have a change output. And the sender will benefit
from paying less in transaction fee for a transaction
with only one output. Again, to calculate
the transaction fee will need the fee rate in
Satoshi's per v bytes. We'll get this value
by using the estimate smartphone gRPC API
from Bitcoin Core and convert the result in Satoshi's per V bite will also need the
transaction size in v bytes. In the next video, we'll implement the note
estimates smart Fee, client and a service to
calculate the transaction size. Later, we'll combine both to
calculate transaction fees. See, yeah.
47. Creating the Transaction Size Calculator and The Node Estimate Smart Fee Client: In this video, we'll create a transaction size
calculator and build the node client for
the estimate smart feed Bitcoin node RPC method. In the api dot services package, Let's create the transaction
size calculator class. This class will be
responsible for calculating transaction sizes according to their types and numbers
of inputs and outputs. These sizes will be
used later to calculate the fee amount we have to
include in our transactions. Let's add a services
annotation to it. First, we'll define some
constants in this class. These constants will contain the sizes of each part
of a transaction. Since we're working only with Segway transactions for now, these constants will refer only to parts of Segway transactions. So let's begin defining the size of the
transaction overhead. The overhead is the
part of the transaction whose size doesn't change with the number of inputs or outputs. The first constant will
be called inversion, which is part of the overhead. It encodes which version
the transaction has. It has four bytes in size. Then we have the input count, which as the name suggests, specifies the number of
inputs in the transaction. It has a variable
number of bytes, but for up to 252 inputs, it's size is one byte. Then we have the
output count field, which has the same size and
rules as the input count. As its name suggests, it specifies the number of
outputs in the transaction. Then we have the
N lot time field, which has four bytes. This field is sometimes
used to encode the time after which the
transaction can be spent. Then we have the Segway
marker and flag, which is present only
for Segway transactions. It has two bytes in size. Now let's set the
input field sizes, starting with the outpoint, which has 36 bytes in size. The point is the combination of the previous transaction
ID and the output index. It indicates the UTXO
used for that input. Then we have the
scriptSig length field, which we're up to 252
bytes, has one byte. It indicates the scriptSig
size for the input. Then we have the
scriptSig field. For Segway transactions. It's size is zero. Remember that for
segment inputs, the scriptSig content is
moved to the witness field. Now let's add the
end sequence field. It has four bytes in size
and it's used to set if the transaction
is replaceable in case the sender wants
to change its fee. Next, let's add the
witness count field for up to 250 to witness items. It has one byte in size. Then we have the
witness items field, which has a size of
107 bytes for P2, WP k, h inputs. This field is comprised
of the signature, which can have 71 or
72 bytes in size, and the public key, which is 33 bytes
in size, 72 bytes, is chosen as the size
for the signatures, since we prefer to overestimate a bit the transaction size. So the calculated F0 is guaranteed to be enough
for the transaction. The two remaining
bytes which encode the size of the signature
and the public key. Explain the witness items
constant value of 107. Now let's set the constants
with the output sizes. The n value is the amount of bitcoin being sent
in that output. It has eight bytes in size. Then we have the
script pub key length, which has one byte in size for up to 252 bytes in
script pub key length. Next, we have the
script pub key, which has 22 bytes in size
for a sec, what addresses? Now, before continuing
implementing the transaction
size calculation, let's create the transaction
size calculator test in the API test package. It'll extend the
specification class. Let's add a transaction size calculator field in this class. And let's use a setup
method to instantiate it. Now, let's create
a test name should calculate transaction
size for P2, WP k, h transaction
outputs and inputs. In the one-block, we'll call
the calculate method on the transaction size
calculator service and store the return in
the result variable. As parameters will pass
the inputs and outputs, which we'll define
in the where block. In the block will check if the result is equal to the
expected size variable. In the where block will add
the following test cases. For the inputs and
outputs variables will set lists with a variable
number of addresses. Since for now, we don't care
about the type of addresses, will simply add a letter
representing each address. The expected size values here were previously calculated for each combination of
inputs and outputs and validated using
the Bitcoin Core CLI. Now, let's create the
calculate method in the transaction size
calculator class. It'll return a big integer
and it will receive as parameters a list of input addresses and a
list of output addresses. Now, let's first calculate
the overhead size. It's equal to the sum
of these constants. Remember that the
value we want to calculate here is the V size. So the elements from
Segway transactions, such as the Segway,
marker and flag, must be divided by four. As such, Let's divide
this constant by four. Now, let's calculate
the input size. It'll be the sum of
these constants. Now, let's calculate the
size of all inputs by multiplying the input variable by the number of
input addresses. Now, let's calculate
the witness size by adding the witness count and
witness items constants. As segue what elements? Let's divide their sum by four. The total witness
size is equal to the witness variable multiplied by the input addresses size. Now, let's calculate
the output size. It will be equal to the
sum of these constants. The all outputs variable
will be equal to the output size times
the number of outputs. Finally, the transaction
size result will be equal to the BigDecimal some of the
previously calculated values. Let's also set the
scale of the result to zero with the rounding
mode half up. This will make the result have zero decimal places rounding
to the nearest integer. If the result decimal
value is five, then it'll round to
the integer above. Now, we return the result
converted to big integer. Now, let's run the transaction
size calculator test. Great, The test has passed. Now let's create the
node estimate smart Fee client in the node
dot client package. Let's inject the
node client into it. And let's create the
estimates smart fee method. It'll return a new node
FI object will create, and it'll take an integer
as its parameter. Now let's return to make request method call
on the node client. The method name will be
estimates smart fee. It's second parameter will be a new parameterized
type reference object. It's third will be
an empty string and its last parameter,
the blocks variable. Now let's create
the node V record in the domains dot node package. It's fields will be
a double fee rate and a list of errors. Now in the api dot
services package, Let's create an interface
called estimate fee service. Let's add the estimate
method to it. It'll return a big decimal. Now, let's create an
implementation of this interface. Names note estimate fee
service in the same package. Let's implement its method. This class will be
used later to estimate the fee using the node
estimates smart Fee client. If we want to change the
fee estimation method or add more options
to do this later, we just create another
implementation of the estimate fee
service interface. First, we'll call the estimate
smartphone method from the previously created client and stored in the
node V variable. Let's inject this client into this class will pass to this method so that we try
to get a fee rate that will make our transaction to be
confirmed in the next block. If for some reason
the returned fee rate happens to be equal to null, we'll return the
fallback fee rate. Let's inject this
variable into this class. Will inject it from
the properties files. So let's add this value
annotation before it. After the if
statement will return the big decimal value
of the fee rate. Now let's add the bitcoin dot fallback fee rate property to all property files
of the project. Let's make it equal to 0.0 002.
48. Coin Selection and Dust: In this presentation, we'll continue explaining
important concepts to understand Bitcoin
transactions and to continue building our
sin transaction feature. So let's talk about
coin selection. Coin selection is the
process of selecting UTXOs to build a transaction. The general rule for
this selection is that the UTXOs Bitcoin
amounts sum must be greater than the
amount of sand plus the transaction fee plus
the change if needed. There are many different
coins selection methods. Each one has advantages
and disadvantages. The algorithm we'll choose for our wallet is called
single random draw. It's probably the simplest
coin selection algorithm and good enough
for our purposes. This algorithm was first presented in a
master thesis titled and evaluation of coin selection strategies by Mark Earhart. I recommend reading this
thesis if you want to know more about coin
selection strategies. This coin selection method
was added to Bitcoin Core in September 2021 as a fallback
coin selection strategy. It consists of shuffling
the spend or UTXOs, then selecting UTXOs one-by-one until the sum of the
selected UTXOs amounts is greater than or equal
to the amount to send plus the transaction fee plus
the change if needed. If the change amount is
smaller than the dust limit, the change output
is removed from the transaction and its amount is added to the transaction fee. But wait a moment, what is dust? A dust output is an output whose Bitcoin amount is smaller than the
cost to spend it. The following example shows how dust outputs are
uneconomical to spend. Let's say you have a
UTXO containing 50 Satoshi's and you want to send these 50 Satoshi's to a friend. When building the transaction, you discover that
the transaction fee required to send it as
equal to 100 Satoshi's. Therefore, it does not make sense for you to
spend the UTXO with 50 Satoshi's since you will pay more in fees than
the amount sent. Also, your friend will not
benefit either from receiving a UTXO with 50 Satoshi's since he would have the same problem as you're spending it. This example shows
that it is detrimental to the Bitcoin network
to create dust outputs, since no one in the
network benefits from it. Therefore, most
Bitcoin nodes don't relate transactions
containing dust. This helps to protect the
network from dust attacks. Dust attack is the process
of maliciously sending dust to wallet addresses to
track their transactions. This attack is shown
in this example. Suppose the government
agency sends dust to address X that
belongs to person. Why? When e-wallet naively
builds a transaction, it includes the dust output
and five others as inputs. Now, the government agency knows why five previous addresses and all their previous
transactions. Also, the government now
knows the change address of that transaction and contract further transactions with it. But what is the criteria to
consider an output as dust? The dust limit is used for that. Every output whose
Bitcoin amount is lower than the dust limit
is considered dust. Bitcoin core uses the
following formula to calculate the dust limit. The dust limit in Satoshi's
of a transaction output is equal to the
transaction output size plus the input size necessary to spend it both
in v bytes multiplied by the industry leafy
in Satoshi's per KV bytes divided by 1,000. The result represents the
transaction output cost that a transaction output
Bitcoin amount must be equal to or surpass to
not be considered dust. The dust relay fee is a configuration property
from the Bitcoin node. It's the transaction fee used when calculating the dust limit. The default dust relay
fee is set to 3,000 Satoshi's per KV bytes or
three Satoshi's per V bite. This makes the default
dust limit for segue outputs equal to 294. Satoshi's. The default dust limit for
non segue what outputs to be equal to 546 Satoshi's. For more details about
calculating the dust limit, this link from the
Bitcoin Core code has a comment explaining how the
dust limit is calculated. Now let's summarize
the general steps of the single random
draw algorithm. It's first step is to shuffle
all wallet UTXOs in a list. Then for each available UTXO
from the shuffled list, add that UTXO to a list
of selected UTXOs. Calculate the adjusted target, which is equal to the
amount of sand plus the transaction fee plus
the change if needed. If the total Bitcoin amount in the selected UTXOs list is greater than or equal
to the adjusted target. Stop. Finally, use the selected UTXOs as inputs of the
desired transaction. Some points of attention
about this algorithm, adjusted targets
with and without the change output must
be calculated for better precision since
transaction fees differ between transactions
with 1.2 outputs, if the generated change is between zero and the dust limit, we stop the iteration since
we won't include a change to the transaction and thus
do not need more UTXOs. This is necessary
to not generate a dust output for the
change by accident. The drawback of single random
draw algorithm appears in some pathological cases, e.g. when you have one
single big UTXO and a lot of small UTXOs. A transaction with a lot of inputs and a high
transaction fee may be created instead of using the big UTXO to avoid
needing many inputs. When that is the case, a different approach is recommended to construct
a transaction. E.g. manually choose the UTXOs that will be part of some
specific transactions. In the next video, we'll implement a
dust calculator and the single random draw
algorithm, C, yeah.
49. Implementing the Dust Calculator and Other Utilities: In this video, we'll
start to implement a dust calculator and the
single random draw algorithm. But first, let's refactor
the UTXO record. Since we'll do a
lot of calculations with the UTXO amount field. And calculations are better
done with big decimals instead of doubles will change the amount type two big decimal. Now, we have to refactor all usages of the amount
field in the project. So let's do it. Let's change the Bitcoin
format or format parameter to BigDecimal and adjust this
parameter accordingly. Let's do the same
to all methods that the IDE flagged as
errors in the project. Here we'll add big decimals
using the add method. And let's pass the big
decimal zero value instead of the zero primitive
to these method calls. Now, we have to
change the type of this balanced field
and all its usages. Let's refactor this
method to make the sum using big
decimal values. Let's also adjust this
code to do the same thing, but with big decimal values. Okay, the refactoring is done. Now in the api dot
services package. Let's create the dust
calculator class. Let's add the service
annotation to it. Let's create these
dust method in it. As its name suggests, it will return true if an amount is
considered dust false, otherwise, it will return
a Boolean and take a big integer as its parameter representing the
amount in Satoshi's. This method will evaluate
if the amount in Satoshi's of an output is smaller than the
cost to spend it. The cost of spending is equal
to the V size of the output plus the v size of the input
necessary to spend it, which will store
in this constant times the dust relay feet in Satoshi's per kilo v
bytes divided by 1,000. Since we will work only with
sigmoid outputs for now, we'll worry only about the
dust limit for segue outputs. So let's create this constant
which will be equal to 98. This value is taken
directly from the Bitcoin Core
policy dot cpp file mentioned in the
last presentation. Let's also inject the dust
relay fee into this class. It will be along and its value will be injected
from the properties file. Let's add the following
value annotation before it. Now let's add this value
to all properties files. Let's set it to 3,000, the same default value used
in the Bitcoin Core Node. Now in the api dot
services package, Let's create the coin
selector interface. Will create this interface so that it enables
more flexibility. If we want to create more coin selector
implementations later. Let's add the select
method to it. It'll return a list
of selected UTXOs. And it'll take us parameters, a list of UTXOs, a big integer for
the amount to send, a big decimal for the fee rate, and address to send, and a change address. Now in the same package, Let's create the single
random draw coin selector, which will implement
this interface. Let's implement its method and add a Service
annotation to it. Let's inject the transaction size calculator into this class. And let's inject the
dust calculator into it. Before continuing to
implement the select method, let's create the
single random draw a coin selector test class. In the API test class. It'll extend the
specification class. Let's inject the single
random draw into this class. And let's use the setup
method to instantiate it. Let's pass the transaction
size calculator and the dust calculator with a dust relay fee of
3,000 as its parameters. Now, let's create a test Tidal should select expected n inputs, coins for transaction with
expected and outputs outputs. Now let's create a utility
class that will help us to test and implement the single
random draw coin selector. Let's call it Satoshi and
created in the utils package. Now, let's create the
two Satoshi's method. It'll take a big decimal as a parameter and
returns a big integer. This method will
convert an amount in Bitcoin to the same
amount in Satoshi's. To do that, we simply return
the amount in Bitcoin multiplied by 100 million and convert the result
to a big integer. Let's also create a method
to do the inverse operation, which is to convert
Satoshi's to Bitcoin. For that, we'll just return
the amount wrapped in a new big decimal
divided by 100 million. In the divide method
will pass eight as the second parameter to make the result have eight
decimal places. We'll also pass
the rounding mode unnecessary as the
third parameter. Now, let's create a method
to convert a fee rate in Bitcoins per KV bike to
Satoshi's per V byte. It'll be named BTC per KB
to Satoshi's per byte. It'll return a big integer and take as its parameter
a big decimal. First, we will multiply the
fee rate by 100 million. Then we'll divide the result by 1024 with two decimal places
and a floor rounding mode. Then let's convert the result to a big integer and store
it in the right variable. If the result is less than one, then we make it equal to one. Finally, we return
the converted rate. In the next video, we'll finish testing
and implementing the single random
draw coin selector C. Yeah.
50. Implementing the Single Random Draw Coin Selector: In this video, we'll finish implementing the single
random draw algorithm. In the single random draw coin selector test in a given block, we'll first create UTXOs using this method and store the
result in the UTXOs variable. These will be the
available UTXOs that will pass as the first parameter
to the tested method. So let's create this method. It will take a list of big
integer as its parameter, which will define the
amount of each UTXO. So for each element in the list, we'll call the
create UTXO method to create a single UTXO. Let's create this method. Let's instantiate a UTXO
with these parameters. You can copy these values
from this lesson's resources. Now, let's create an
additional UTXO and add it to the list of
UTXOs using this code. The reason to add an extra UTXO to the list is that
we want to allow the select method
to wrongly add it to the selected UTXOs list. If that's the case,
we'll know that the tested method is not
working as expected. Let's define these
two addresses. One to be the address to send and the other to be
the change address. You can copy them from the
Project and Resources page. Let's set the fee
rate to 0.0 002. In the wind block, we'll call the single random
draw select method with these parameters. In the then block will check if the selected UTXOs size is
equal to the expected number of inputs variable
in the where block. Let's add these test cases. You can find them on the
Project and Resources page. These test cases covered
transactions with 12.3 inputs and
with 1.2 outputs. The input amounts for
transactions with one output are expected
to not generate change or to
generate change with dust whose value is
added to the fee. The input amounts for
transactions with two outputs are expected to
generate non does change, which returns to the sender
via a second output. Let's run the test. It has failed as expected. Now, let's implement the single random
draw select method. First, we'll filter all
received UTXOs to create a list with only UTXOs that have one or
more conformations. So let's create this
method. To do that. We'll call the filter
method on the UTXOs stream, returning a list with
only confirmed UTXOs. Now, let's create
this variable to store the fee rate in
Satoshi's per V byte. For that, we'll use
the BTC per KB to Satoshi per bike method
from the Satoshi class. Now, let's store the shuffled
UTXOs in this variable, assigning the return of
this method call to it. Let's create this method. First, we'll instantiate
a new ArrayList containing the
UTXOs list content. Then we'll call the
collection shuffle method, passing the new list
as its parameter. Then we'll return the
shuffled coins list. Now let's instantiate
an ArrayList that will store the
selected UTXOs. And let's create this variable to store the total
input balance. Now for each UTXO from
the shuffled coins list, we'll add the UTXO to
the selected UTXOs list. And we'll add its amount in Satoshi's to the total
input balanced variable. If the total input balance is less than the amount ascend, we continue the execution
from the next loop iteration. After the if statement, when the total input
balance exceeds the amount to send
will instantiate an ArrayList with
the output addresses and add the address
to send to that list. And we'll create a list
with the addresses of the selected inputs using
this stream mapping. Now, let's calculate
the total fee in Satoshi's and stored
in this variable. For that, Let's call
the total fee method, passing the fee rate in
Satoshi's per V bite the input addresses and the output addresses
as its parameters. Let's create this method. Inside it will return the transaction size
calculator calculate method call passing
the input addresses and output addresses
as its parameters. Then we'll multiply
the result with the fee rate in
Satoshi's per V bite. Now we'll create the
adjusted target variable, which will be equal
to the sum of the amount to send
and the total fee. Now let's add the change address to the output
addresses list. We'll calculate the
total fee with change, calling the total fee method,
passing these parameters. And we'll calculate the
adjusted target with change, which will be equal to
the sum of the amount of sand and the total
fee with change. Now, if the result of
the input balanced fulfilled transaction
method with these parameters is true, we will exit the loop
using the break keyword. Let's create this method. It'll return the result of
the following statement. If the total input balance
is equal to or greater than the adjusted target and the
generated changes dust, then it means that
the selected UTXOs are enough to fulfill
this transaction. Another condition for
the selected UTXOs to fulfill the transaction is that they're amounts
sum is equal to or greater than the adjusted
target with change. Let's create the change is dust method will return
the dust calculator is dust method call passing the big integer
difference between the total input balance and the adjusted target
as its parameter. Finally, we'll simply return the selected UTXOs
from this method. Now, let's go to the single random draw coin selector test and run this test. Grade. The tests have passed.
51. How Segwit Transactions are Built and Validated: In this presentation, we'll see more details on how to
create a transaction. So let's say we already
used the coin selector to select some UTXOs to spend
as inputs in a transaction. In our wallet, we also
have the ability to derive the private keys necessary
to spend these UTXOs. These private keys will be necessary to sign the
transaction later. We've already chosen an
address to send some Bitcoins. And we have the change address where the change
will be sent to. After defining these things, it's time to construct
our transaction. Let's see the fields that are part of a Bitcoin transaction. First, the version field. It's used to define the rules that the transaction follows. Currently, version
one, which we will use is for common transactions. Version two is for transactions
following the BIP 68, which implements
rules to validate a transaction only
after a certain time. The lifetime field is used
for a similar purpose. Its value can represent
a certain time to add a transaction to a block or
zero to ignore this rule, then we have the seg
would flag and marker. It's only present in
Segway transactions. If its value is 0001, then it marks this transaction as having said what inputs, each transaction has one or
more transaction inputs. Each transaction input has a
transaction ID which defines a previous transaction
from which the current input is
spending Bitcoins. The output index indicates which output from the
previous transaction. The current input is
spending Bitcoins from. Together the transaction ID and the output index are
called the out point. You can view the outpoint
as a coordinate to identify which UTXO
and input refers to. Next, for each input, we have the scriptSig field. In segment inputs. This field is empty and its content is moved
to the witness field. In non segment transactions, its content is needed to unlock
the spending of the UTXO. The input is referring to. The n sequence field defines when the input is
valid to spend. When this field
value is equal to the maximum hexadecimal
value for its size, this field is ignored. A transaction also has
one or more outputs. Each output contains
a script pub key, which has script code
defining the rules to spend that output in
a future transaction. Each output also contains
an amount field which defines the number of Satoshi
locked in that output. Finally, transactions also
contain a witness field. For non Segway transactions, this field is empty. For Segway transactions, the witness contains
all the content that in non sequitur
transactions is in the scriptSig
of each input. Now let's talk
about how to build transaction inputs and the witness field
for segment inputs. For every selected UTXO, we have to build one
transaction input. The transaction ID field of the transaction
input will be equal to the UTXO transaction ID. The output index field will be equal to the UTXO output index, also known as Vout in the Bitcoin Core RPC
API documentation. The scriptSig will be empty and the end sequence will be equal
to this hexadecimal value, which is the maximum value
allowed for that field. We also have to build one
witness for each input, the witness field will
contain a signature obtained using the private key used
to derive the UTXO address. And a public key which
was derived using the previous private
key and which can also generate the same UTXO
address mentioned. We'll see more details about the signing process in
a future presentation. Notice the dummy
signatures and bupkis, which values can be
a bunch of zeros, can be used as placeholders to create and sign transactions. This trick facilitates
transaction v size calculation before
signing transactions. Now let's talk about how to
create segue what outputs. First. On a side note, it's possible to create
a transaction with different types of
inputs and outputs, e.g. in the same transaction, you can have segment and non
segment inputs and outputs. Back to the main subject. Generally, we want to create one output for the
address to which we want to send bitcoins and one output for the
change address. If the transaction
needs a change. Each transaction output has
a script pub key field. Each segment script pub key, also known as pay to
witness pub key hash or P2, WP k H has a witness
version of zero. The witness version is
one for taproot scripts. New versions can be added for
future scripts if needed. The second script
pub key item for sigmoid outputs is
a public key hash, which is obtained by Beck 32 decoding the address
for that output. The transaction output also
has an amount field in Satoshi's representing
the amount sent to that output address. Now, let's finish this
presentation by talking about the segment script
execution and validation. When a node receives
a transaction, it starts to validate it. For each input in a transaction, the node combines
the witness from an input with the script
pub key of the UTXO referred by the
outpoint and net input executes the combined
script and validates it. Let's see how that's done. Here is the script pub key of a UTXO and the witness
field that wants to spend that UTXO both generate the
following combined script. You may be asking
where the opcodes from the combined
script came from. It turns out that when
a node identifies the zero version on the
segment script pub key, it triggers a special rule that generates part of the
combined code below. Next, the combined script is executed just like
non-sequiturs scripts. From the combined script, the signature and public keys are added to an execution stack. Then the OPT up opcode duplicates the pub key
in the execution stack. Up hash 160 hashes, the last pub key
added to the stack. Then from the combined script, the pub key hash is
added to the stack. Up equal verify opcode removes the last two elements from the execution stack
and compares them. If they're equal, the script
continues to execute. Finally, the object
sig opcode checks if the signature is valid for the pub key remaining
in the stack. If it's valid, it returns
true to the execution stack. After all the script elements were added to the
execution stack, the transaction input
would be considered valid if the last
added element is true. This process must be repeated for every transaction input. For a transaction to
be considered valid, all its inputs must be valid. Just as a visual reminder, let's review from
where each element in the script pub key and witness came during transaction
construction. The pub key hash present in the script pub key
came from back 30 to decoding the address of
the UTXO that is being spent. The signature in
the witness came by applying the ECDSA algorithm, which needs a valid private key. The public key and
the witness came from the public key derived from
the mentioned private key. The same public key
was used in the past to generate the address
of the UTXO being spent by sequentially
applying the hash 160 algorithm and
back 32 encoding.
52. Implementing the Transaction Creator: In this video, we'll create a transaction creator service. At first, this service will only deal with segment
inputs and outputs, but later in the course,
we'll improve it. So in the api dot
services package, Let's create the transaction
creators service. Let's add the service
annotation to it. Let's also inject the
dust calculator into it. Now in the API test package, Let's create the transaction
creators service test class. It'll extend the
specification class. Let's inject the transaction
creators service into it, and let's instantiate
it in the setup method. Will need to instantiate it
with a new dust calculator passing 3,000 as it's
dust relay fee parameter. Before making this test, Let's go to the single random
draw coin selector test to make a little refactoring. Let's cut these two methods from this class and paste them into the new utils
class we'll create in the BYOD W test package. Let's make these
two methods static. Now, let's fix the
single random drug coin. Select your test to use these
methods from this class. Now, let's go to the transaction
creators service test. Let's create a test
called should create transaction with
hashtag n inputs, inputs and hashtag expected
n outputs, outputs. In the given body. Let's first
create this UTXOs variable and assign the utils dot create UTXOs method
call returned to it, passing the input amounts
variable as its parameter. Now, let's create this
address to send variable. Its content will be equal
to the variable with the same name in the single random draw coin selector test. Let's also create the
change address variable, which will be equal
to the variable with the same name in the single random draw a coin,
select your test. Let's create the
fee rate variable, which will be equal to 0.000 to. Let's also define the
expected total fee variable, which will be equal to the
expected size variable times the fee rate in
Satoshi's per V bite. Now, let's create the
variable total input amount. Its value will be equal to the
result of this expression, which will return the sum of every UTXO amount in Satoshi's. The amount to send
variable will be equal to 100 million Satoshi's. Let's also calculate the
expected change amount. It will be equal to
the input amount minus the amount of sand minus
the expected total fee. In the one-block, we'll call the transaction
creator create method will pass the following
parameters to this method. The result will be stored in
the transaction variable. In the then block will check if the transaction v size is equal to the expected
size variable. But first, let's update the
Bitcoin Java version to 0.4, 0.2 to make the get v
size method available. Now, we'll check
if the number of transaction outputs is equal to the expected and
outputs variable. We'll also check if the first output amount is equal to the amount
to send variable. Now, if the expected number of outputs is greater than one, will check if the change
amount is equal to the second output amount
in the transaction. In the where block. Let's add
the following test cases. You can copy them from the
Project and Resources page. These test cases covers scenarios
with and without change outputs and with input amounts that generate does
changes or not. They also covered transactions
with 12.3 inputs. Now let's create the transaction creators
service create method. Let's rename these parameters. Before implementing this method, Let's create the fee class
in the utils package. Here we'll create a
method to calculate the transactions total
fee in Satoshi's, let's call it total
calculated fee. It will receive a transaction in the fee rate in Bitcoin per KV bite will create the following code inside
a try catch block. First, we'll convert the
fee rate from Bitcoins per KV bike to Satoshi
is privy byte. Then we'll return the result by multiplication the transaction
v size by the fee rate in Satoshi's per
V bite will catch an IOException which
can be thrown by the good V size method and rapid in a new
runtime exception. Back to the transaction
creators service. In this method will first build the transaction inputs
using this method, passing the received
UTXOs as its parameter. Let's create this method. We'll return the result of the following UTXOs
stream transformation will not the stream into a new transaction input will pass the received UTXO
ID as its first parameter. The big integer value of the UTXO vowed as its
second parameter. These two parameters define the transaction input outpoint. Next, we'll pass a new script instantiated with an empty list. This parameter is the
transaction input scriptSig, which is empty for
segment inputs. Next, we'll pass a big integer instantiated with
these parameters, which defines the
input in sequence. This value is the maximum
value allowed for this field, making the nodes ignore it. Now, for each
transaction input in the stream will use the peak
method to set the witness, will instantiate the witness
with a list containing constants for a dummy
signature and a dummy pub key. Now let's create
these constants. The dummy signature
will be equal to the hexadecimal string
zero repeated 144 times, which size in bytes
is equal to 72. The dummy pub key will be equal
to the hexadecimal string zero repeated 66
times which size in bytes is equal to 33 will set these dummy values because
they will be necessary to correctly do the transaction
v size calculation, which will make in
the create method, the signing process will be done separately later when
the real values, we'll substitute the dummy ones. Finally, we'll
convert the stream into an ArrayList and return it. Now, we'll build the
transaction output for the address to send using
the buildup PUT method, passing the address to send and the amount to send
as its parameters. Let's create this method. First, we'll parse
the address prefix using the parse prefix
method, which will create. This method will
check if the address starts with one of the
valid segment prefixes, and it will return
the address prefix or an empty string if a valid
prefix was not found. Now we'll build a script object using the script class P2, WP k h script method. As its parameter
will pass the result of the back 30 to
decode method call, passing the prefix and the
address to the latter. The return of the back
30 to decode method is an object array where the second element
is the pub key hash, which will pass to the
P2 WP k h script method. This method will return the equivalent of
the script pub key, where the first
parameter is zero and the second parameter
is the pub key hash, as we explained in the
last presentation. Finally, we'll return a
new transaction output instantiated with the amount
and the created script. Now, we'll create an ArrayList to store the
transaction outputs, will instantiate
it, adding to it the address to send output
we've just created. Next, we'll create
the transaction will pass as its parameters, the big integer one, which is the
transaction version. The transaction inputs,
the transaction outputs. The big integer zero is the lock time which
will be ignored. And the boolean true to define this transaction as
a segue transaction. This parameter will make the transaction have the
Segway marker and flag. Now, let's calculate the
total transaction fee using the total calculated
F0 method which we've created previously
in the FI class. We'll pass the transaction and the fee rate as its parameters. Let's also calculate
the input zone for that will map the UTXO stream into
a stream of UTXOs amounts. Then we'll use the reduced
method to sum them. Finally, we'll
convert the result is a ptosis or zero if
the UTXOs don't exist. Next, we'll calculate the
change which will be equal to the input sum minus the amount of sand minus the total fee. Next, we'll add the
following if statement. Using the dust calculator will check if the
change is dust. If it is, then we'll return
the transaction as is. If it's not, we'll
add another output to the transaction using
the build output method, passing the change address
and the big integer one as its parameters will pass one to the output amount
just as a placeholder value, since we'll have to recalculate
the transaction fee for the transaction that now has two outputs which
demands a higher fee. So let's recalculate
the total fee using the total
calculated F0 method. Again, Let's
recalculate the change which will be equal to
the input sum minus the amount to send minus the
new calculated total fee. Now, let's remove the last
added transaction output from the transaction
outputs ArrayList. Let's check again if the new
calculated change is dust. If it is, then it will return the transaction without change. If it's not, we'll
add a new output to the transaction using
the build output method. This time, we'll pass the
real change value for the transaction with two
outputs to this method. Finally, we'll return
the transaction. Now let's go to the transaction
creators service test and run this test. Great, the tests
have passed since we've previously changed the single random draw
coin selector test. Let's also run it again to ensure we haven't
broken anything. They're great. It's working as usual.
53. The Send Transaction Dialog Window : In this video, we'll
start to create the window that will pop up when we click on
the Send button. That window will contain information about
the transaction. We're sending a
password field and an Okay button to confirm
the transaction broadcast. So in the FXML package, Let's create the send
underscore transaction underscore dialog dot FXML file. Let's erase the
generated boilerplate. Now, let's create a
dialogue pain tag. Now, let's change the view
to the Scene Builder. Let's set the dialogue
pain pref height to 300. And it's pref width to 650. And let's make the
min-height min-width to use the press size. Now, let's add a grid pane
to the dialog content. Let's add four more
rows to this grid pane. Now let's add a label
to the first row. Let's change its text to send
transaction question mark. Let's increase its font
size to 16 pixels. For the next rows, the
idea here is to add labels and data for the
sending transaction. Let's add a label to the second row and change its text to
amount to send colon. In the second column
of the same row. Let's add another label. This time it's text will
be empty and we'll add an Fx ID to it so that we can change its content
dynamically later. Let's add another label
in the third row, this time with the text total fees in the cell at the right. Let's add an empty label with another FX ID. In the row below. Let's add another pair of
labels for the total field. In the next row. Let's add
these labels for the fee rate. Now, let's do the same
for the address to send. In the last row. Let's add a label for the wallet password. In the cell at the left. Let's add a password field. It'll have an FX id
of wallet password. Sudden labels are missing a colon at the end.
Let's fix them. Now. Let's go to the
dialogue pain setup and add an OK button here. Let's also add a cancel button. The Cancel button text is
in my native language. If that's also your case, you can change it by
doing the following. Let's go to the editor view. First, let's add an Fx idea
of okay to the Okay button. And let's add an Fx idea of cancel to the cancel button tag. Now, let's remove this property. Let's set the button data
property to this value, and let's set its
text to cancel. Now, in the Scene Builder, we can see that the
change took effect. Now, let's go to
the Send tab FXML. In the Send button, Let's
set the action property to hashtag send so that when
we click on this button, the send method of the Send tab controller will be called. So let's create this method. Before implementing
it, Let's create a transaction DTL record
in the domains package. Ttl stands for data
transfer object, and it's a common design
pattern to use for objects whose main purpose
is to transfer data between classes
and methods. As such, the transaction
DTO will be used to transfer transaction
data between our services. Let's add the following
fields to it. At this point, you may be
asking what's the difference between the total actual fee
and the total calculated v? We'll get to that shortly. Now, let's go to the
Send tab controller. Let's add the following
fields to it. These fields will bind to the corresponding tags in
the Send tab FXML file. Now let's implement
the send method. First. We'll convert the amount to send text into a big decimal amount. Now, we'll create a
transaction DTO object using the create transaction
service will create. Let's inject this
service into this class. Let's create it in the GUI
dots services package. Now, let's finish injecting
it into this class. Here we'll call the
create method on it, passing the address to send
text and the amount variable. Let's create this method. Now, we'll use this method
to open the dialog window, passing the transaction
DTO as its parameter. Let's create it. After
opening the dialogue window, we'll call the clear method on the address to send
and the amount to send variables to erase the
content in these inputs. Now, let's go to the create
transaction service. Let's add the service
annotation to it. We'll implement
the create method. Now, the idea here is to use many of the
services we've built in the past videos to
create a transaction and some additional data for
the transaction DTO. For that, the first
thing we'll do is to use the estimate fee service to get the fee rate and store it
in the fee rate variable. So let's inject this
service into this class. And let's call the
estimate method on it. Now, we'll get the currently
loaded wallet addresses using the current wallet object. So let's inject it
into this class. Then we'll call
the get addresses as strings method on it. Next, we'll get all
our wallet UTXOs. For that, we'll use the
node list unspent client. So let's inject it
into this class. We'll call the list
unspent method on it, passing the addresses and the current wallet name
as its parameters. Now, we'll use the coin
selector to select the UTXOs that will be used
as inputs in our transaction. So let's inject the coin
selector into this class. Now, we'll call
the select method on it, passing the UTXOs, the amount converted in
Satoshi's the fee rate, the address to send, and the current wallet
change address, calling the current wallet get change address method,
which will create. So let's create this method. It will simply return the
change address property. Let's create this
property as a string. Let's move it to here. Let's also create a setter
for it. We'll use it later. Now, let's use the
transaction creators service to create a transaction. So let's inject this
service into this class. Let's call the
create method on it, passing the following
parameters to it. Now that we have our
transaction will build additional data to include
in the transaction DTL. Next. First, let's use the total actual fee method
from the FI class to get the total actual fee will pass the transaction and the selected
UTXOs as its parameters. Let's create this method. The difference between
the total calculated feet and the total actual fee
is that for the latter, we use the difference between the inputs and outputs
amounts to calculate it. While for the
former, we estimated using the transaction
size and the fee rate. In theory, it's expected that
both values be the same, but let's default to using the total actual
feet when possible, to guarantee that we're showing the user the actual transaction fee and to let the user spot
possible buggy three values. So here we'll calculate
the input amounts, some in Bitcoin using the following stream
transformation. We'll map the
selected UTXOs stream to produce a stream
of UTXOs amounts. Then use the reduced method to sum all values in the stream. And use the or else method to return zero if the
string is empty. Now, let's calculate the
output amongst some in Satoshi's will do the
same transformation we've done for the inputs, but with the transaction
outputs this time. Now we'll return the
input some in BTC is converted to Satoshi's
minus the output some. Back to the Create
transaction service. Now, let's create the total
calculated F0 variable and assign the result of the total calculated
F0 method call to it. This time, we'll pass the transaction and the fee
rate as its parameters. Now, let's calculate
the total spent, which as the name suggests, is the total amount in Satoshi's that the sender
spent on this transaction. For that, let's use the total spent method
passing the transaction, the total actual fee and the
address as its parameters. Let's create this method. With this if statement. Let's check if the address to
send belongs to our wallet. If not, then we'll return the total actual fee plus the first transaction
output amount. If our wallet contains it, that means we are transferring
bitcoins to ourselves, and thus we're spending
only the transaction fee. Finally, we'll return a
new transaction DTO object instantiated with
these parameters, will continue to implement the dialog window feature
in the next video. See you.
54. Showing Transaction Data in the Send Bitcoin Dialog Window: In this video, we'll finish implementing the
transaction dialog window. So let's go to the
Send tab controller. In the open dialogue method, Let's instantiate a new
dialogue object like this. Then let's call the unit
owner method on it, passing the window object as
its parameter, like this. Let's set the dialogue
title to send transaction. Now with the following code, we'll define that
when we click on the Close button of
the dialog window, the dialog window will close. Next, we'll create an
FXML loader object inside a try catch block. To instantiate it
will need to inject the dialogue FXML
resource into this class. Let's do it. Let's add a value annotation to
this field indicating the path of the sin
transaction dialogue FXML. Here we'll call the
get URL method on it. Then we'll use null
in these parameters. And the context get beam
method reference here. First, let's make the
context object of field in this class and assign it here. Now, we'll set the content
of the dialogue pain to the result of the FXML
loader load method call. Now with the following code, will get the syn transaction
dialogue controller from the FXML loader. Let's create this controller
in the controllers class. Finally, we'll pass the
transaction DTO object to the sin transaction
dialogue controller using the set
transaction method. Let's create this method. Here, we'll catch
an IOException that the FXML loader can throw and wrap it in a new
runtime exception. Then we'll show the
dialogue using this method. Now, before implementing the second transaction
dialogue controller, Let's go to the update
current wallet service. When we update the
current wallet, we must set its change address, which will be used when
we send a transaction. So let's do it. We'll do it by getting the second extended public
key from the created wallet. Remember that this
extended key was derived to generate
change addresses. Then we'll get its
first address and set it as the current
wallet change address. Now, let's go to the second transaction
dialogue controller. Let's set the Component
annotation to it. Now, let's go to the second
transaction dialogue FXML. Let's set the
dialogue pain FX ID. And let's set the FX
controller property to the recently created
controller path here. Now we'll set every tag with
an ethics ID in this file, this fields in the second
transaction dialogue controller by using this cool IDE feature. Now let's set every
field here is private. Let's add the FXML annotation
above all of them. Before implementing the
Set Transaction method, let's create the
transaction dialogue domain in the domains package. Will need this record soon. It'll model the fields will show in the transaction dialogue. So let's create these fields. Let's also create
this from method. It'll create a new
transaction dialogue from a transaction DTO. We'll use the Bitcoin format or format method to correctly parse the amount to send the total actual fee
and the total spent will also pass the fee
rate and the address from the transaction DTO to the transaction
dialogue instantiation. Back to the second transaction
dialogue controller. In the set transaction method, Let's set the transaction D TO field to the Received
Transaction DTO. Let's create this
field in this class. Now, let's create the transaction dialogue
object using it's from method passing the
transaction DTO as its parameter. Then we'll set the
amount to send text to the transaction
dialogue amount to send will set the
total fees field text to the transaction
dialogue, total fee. The total field text to
the transaction dialogue total the fee rate field text to the transaction dialogue. Fee rate concatenated with
the string BTC slash KV. Bite. Let's set the address
to send field texts to the transaction dialogue
address to send. Now let's create the initialized
method in this class. Here will bind the
cancel button to an action that will
close the dialog window. To do that, Let's call the
Lookup Button method on the dialogue pain passing
the cancel field to it. Then we'll call
this method to add an event handler to
the cancel button. The handler will receive an action event and an
event as its parameters. And the handler body will use this statement to close
the dialog window. Now, let's go to the
send Bitcoin test. Something is missing
in this test. After calling the send
Bitcoin and wait method, we have to instruct the
node to mine a block so our UTXO gets confirmed and can be used to
create a transaction. To handle that, Let's call the generate to address
method on the node generate to address client passing these parameters to it. And let's move this
code line to here. Now, let's run our node. And let's run this test. As expected, the
test has failed, but the transaction
dialogue opened and was populated with
transaction data. Now, let's improve this test. Will check if the values in the transaction
dialogue are correct. But first, let's make
this test more resilient. After clicking on
the send button will add code to make
sure we wait for the transaction
dialogue to appear before acting on
the dialog window. So let's create the weight
for dialogue method. Here, we'll call the
weight for method. Passing these parameters to wait timeout seconds in the
lambda body will get the dialogue pain and return only after
it is not null. Now, let's get the
value for each label in the transaction dialogue and store them in the
following variables. Let's add the transaction
row type here. And let's change
the table view name to transactions table. In the then block, let's add
the following assertions to check if the labels in the transaction
dialogue are correct. Now, in the where block, Let's add a test
case to populate the variables we've just
referenced in the then block. It's missing an equal,
equal sign here. Let's fix it. Let's
add another test case. Now, let's remove these lines. Will adapt the code to
create previous transactions according to the previous
UTXOs number variable. So for each UTXO defined in that variable will look up
for the receiving address. Then we'll call the send Bitcoin and wait method
with these parameters. Now, let's substitute
the parameter in the right method call for
the amount to send variable. Now, we have to make sure
that the fee rate is equal to 0.0 002 for the test to work. To do that, we'll inject the
node estimate fee service into this class and add the
mock Bean annotation to it. Then in the setup method, let's add the following code. It'll make sure that when the estimate method of
the service gets called, then it will return the
fee rate value we want. This lookup method perimeter is missing the letter S at the end. It's total fees. Now, let's run the test. It is failed, but for
the wrong reason, notice that the amount
to send label is empty. That's because there's
another label with the same FX ID in the
Send tab controller. There is also an address to send field with the same FX ID. Let's go to the
second transaction dialogue controller to fix that. Let's rename this field to
amount to send dialogue. And let's rename
the address to send field to address
to send dialogue. Let's go to the send
transaction dialogue FXML to change the labels FX IDs. Okay, We don't have to, because the IDE did
that job for us. Back to the send Bitcoin test. Let's update these parameters to look up for the
correct labels. Now, let's run our test. Okay, the tests have failed, but only because the
transaction table doesn't contain the expected
number of transactions. That means the transaction
dialogue labels are correct. Great.
55. How Transaction Signatures Work: In this presentation, we'll show how transaction signatures work. First, let's review the fields
of a transaction input. Transaction inputs have a transaction ID
field whose content indicates from which
transaction the UTXO being spent in
that input came. The output index
field indicates which UTXO from the previous transaction
the input is spending. Together, both fields
make an out point which unequivocally
indicates which UTXO, the input is spending. The scriptSig field is empty for segue transaction
inputs contains an unlocking scripts for non
segue transaction inputs. That unlocking scripts
is a signature and a public key for
common transactions. Finally, the transaction input contains the end sequence field. The end sequence field defines when the input is
valid to spend. This field is ignored when
its value is equal to the maximum hexadecimal
value for its size. For segue transaction inputs, the content which for
non Segway transactions is in the scriptSig is
moved to the witness field. The signature in that field
is an ECDSA signature obtained using the
private key used to derive the
address of the UTXO. The input is spending. The other field in the
witness is the public key, which is derived
from the private key we've just referred to. But wait, what is
an ECDSA signature? Ecdsa stands for Elliptic Curve digital
signature algorithm. It's a signing
algorithm that combines a private key and a message
to produce a signature. And ECDSA signature
has a property that allows verifying
if it's valid, that is, verifying if a given signature was produced by a
given private key and a message by using the public key derived
from that private key, the message and the signature. That way, we can verify
if a signature is valid without revealing
the private key used to generate it. You can use the ECDSA to
sign any message you want. In the case of a
transaction signature, the message being signed is a modified version of the
serialized transaction. So let's see how Bitcoin
transactions are signed. To sign a transaction, you have to sign each
transaction input separately. For each transaction input starting with an
insane transaction, which is a transaction
where its inputs have empty script,
CIGS and witnesses. You create a signature hash, which is the hash of
a modified version of the serialized transaction. To do that, you use a
signature hash algorithm, which we'll explain
in the next slide. The signature hash will
be the message to be signed with a private key valid for that input
and the signature hash use the ECDSA to
generate the signature. Remember that the valid
private key for an input is the same private key
used to generate the address from the UTXO
that input is spending. If the input is a segment input, add the signature
and the public key derived from the referred
private key to the witness. Otherwise, add these elements to the input scriptSig field. When nodes receive
a transaction, they verify if the transaction
signatures are valid. To do that, they use the signature
verification algorithm cited in the previous slide. They do that during
the script execution as explained in the
last presentation. Now, let's see how the
signature hash is generated. There are different signature
hash algorithms for segue, non segment and tap root
transaction inputs. However, the three
algorithms modify the transaction in some
way and produce a hash, which is the message
that will be signed. Let's see the signature
hash algorithm for a segment inputs. To create a signature hash for
a given transaction input, start with an empty string, then add the transaction
version to the string, concatenate all out points
from the transaction inputs. Hash the results using the hash 256 algorithm append the
resulting hash to the string, then concatenate all
inputs and sequences. Hash 256 the result, and append the hash
to the string. Now add the outpoint of the input being
signed to the string. Add the script pub key of the UTXOs spent by the input
being signed to the string. Add the amount of the UTXOs spent by the input being
signed to the string. Add the end sequence of the input being
signed to the string, concatenate all
transaction outputs. Hash 256, the result, append the resulting
hash to the string, add the transaction lock
time to the string. Add the hash type,
which is sig hash all for common transactions
to the string. There are other hash types, but they are rarely used. They define the
inputs and outputs. The input being signed can
go within a transaction. Sig hash. All means that the signature
produced is valid only in a transaction with
all the inputs and outputs of the
given transaction. Finally, hash 256,
the obtained string. The result is the signature hash for the input being signed. A signature hash
must be obtained for each transaction
input being signed. The process is
replicated by nodes when they verify the
transaction signatures.
56. How Our Wallet and Node Will Handle Sent Transactions: This presentation will
explain what happens when we send a transaction
to a Bitcoin node. Then we'll see how
our wallet currently handles receiving
transactions and how we plan to change it
to correctly calculate the transactions addresses and total wallet balances after
sending a transaction. So let's see how a Bitcoin node handles transactions
sent by our wallet. Our wallet will send
transactions to our node through the note RPC method called
send raw transaction. Our application will send the sign transaction in a
hexadecimal format to our node. After receiving the transaction, the node validates it. If the validation pass, the node broadcasts
the transaction to other nodes in
the Bitcoin network. The other nodes
do the same until all nodes in the network
acknowledged the transaction. The transaction remains
in the mental for awhile, waiting for a minor
to include it in a block and mined
the block with it. Eventually, a minor broadcasts the mind block with
the transaction to other nodes in the network. As the nodes receive the mind
block with the transaction, the transaction is
excluded from the mental and is
considered confirmed. Each block added
after the block with the transaction adds a
confirmation to the transaction. It is recommended to wait
for six conformations for a transaction to be
considered irreversible. Now, let's see a
naive model of how our wallet can handle
send transactions. After our wallet sends a
transaction to our node, the node will send
back through zero MQ, the same transaction
to our application. Our node task
service will detect the transaction and send it to our transaction
received listener. As most transactions will
have a change output, the transaction received
listener will usually verified that the change
address is from our wallet. And we'll use the
update UTXOs service to update our wallet addresses, transactions and balances, but relying only
on the transaction received listener
as it currently is to update our wallet will
raise some problems. First, the balance
calculation of the update UTXOs service does not consider the input balances. So we will have to modify
how our wallet calculates a dress and transaction
balances to discount the input amounts
from the change amounts. But we'll have to deal with a bigger problem
with this model. What about transactions
without change? In this case, the
transaction received listener won't consider
the receipt transaction is ours since it verifies only the output addresses and the scent output won't
be from our wallet. Thus, our application
wouldn't update. It's balanced for these cases, will adopt the following
strategy to overcome these problems when updating our wallet after
sending transactions. After sending the
transaction to our node, will publish a
transaction sent event which will be captured by a
transaction sent listener. Then the transaction
send listener will use the update UTXOs service
to update the wallet with the transaction to eliminate the issue of not updating
transactions without change, both the transaction received listener and the block
receive listener will use a node list transactions
client to search for all transactions related
to our wallet in the node, this client will send
a list transactions RPC call to our node, which will return all relevant transactions to the client. Then using the
return transactions, the update UTXOs
service will update all transactions in our wallet and their balances correctly.
57. Signing and Sending Segwit Transactions: In this video, we'll
build some classes and services that will allow us to sign and send transactions. So first, let's create a
class called transactions signer service in the api
dot services package. Let's add the service
annotation here. Now let's create the
sign method here. It'll take a transaction
object on mnemonic seed, a password, and a list of UTXO
DTLs a class will create. So let's create it in
the domains package. Now, we'll use the
stream range method to loop over each UTXO DTO. For each UTXO DTO, we'll call the sign method
passing the parameter a, which is the index
of the input to be signed, the UTXO DTO, which will get using
the i parameter, the transaction,
the mnemonic seed, and the wallet password. Let's create this method. Let's change this
parameter name to mnemonic seed string to
sign a transaction input. First we have to derive
the valid private key to unlock the UTXO that
input is spending. If you want to remember how that's done in greater detail, re-watch the HD wallets and Bitcoin addresses
presentation. So first, we'll instantiate
a mnemonic seed object here, passing the mnemonic seed
string and the instantiation. Now we'll derive a master key using the mnemonic
seed to master key method as its parameters
will pass the password. And this prefix. For our use case,
it's okay to pass the main net private
prefix even if we use it in another
environment because it affects only the master
key serialization, not the derivation process. In the process of deriving
the master key that to master key method first
derives the root seed, which is mentioned in the previously
referred presentation. Now, we'll derive the
extended private key using the master key CKD method as its first parameter will pass the UTXO DTO derivation path. First, let's create this
field in the UTXO DTO. And let's take the
opportunity to add the amount field
which will need next. The next parameter will be true, which indicates we want to derive an extended private key, an extended public key. The last parameter will be
the main net private prefix. Again, no problem using
the main net prefix here. Notice that the way we derived the extended private
key here is similar to how we derive the
extended public key and the extended
public key service. We use the same derivation
path we've used to create the extended public
key to derive the UTXO address for our wallet. Now, we'll obtain the desired
private key by simply extracting it from the
extended private key through the two
private key method. This method is available in the next Bitcoin Java version. So let's go to the POM
file to update it. Now inside a try catch block, we can finally use
the sign method from the transaction ECDSA signer
class from Bitcoin Java. This method will take the
transaction, the private key, the input index, the UTXO DTO amount
converted to Satoshi's. And the boolean true, which indicates that
this input is segmented. The same method will obtain a signature hash with
the private key. We'll use the ECDSA to obtain
a signature for that input. Then it will insert the
signature and the public key derived from the
past private key in the transaction witness. The UTXO amount is needed
for the signature hash, and the last true
parameter is needed for the correct signature
hash generation and to correctly add the signature
and the public key to the witness instead
of in the scriptSig, which is the case for
non segment inputs. Finally, we catch a
possible IOException that the sign method can throw and wrap it in a new
runtime exception. Now, let's create the node send raw transaction client in
the node dot client package. This service will be
necessary to send our transactions to
our Bitcoin node. Let's add the service
annotation to it. Let's inject the node
client into this class. And let's create
the send method. It'll accept a transaction
hexadecimal string as its parameter. Here we'll call the node
client make request method. We'll use it to
call the send raw transaction Bitcoin
node RPC method. As usual, let's pass a new parameterized
type reference object. Here. The URL is empty, and we'll pass the
transaction hex parameter as the last parameter. Now in the GUI dots
services package, Let's create the second
transaction service. This service will
be responsible for signing and sending the
creative transaction. After we click on
the Send button in the transaction dialogue, let's add the service
annotation to this class. And let's create the
sign and send method. It'll return a
future of type void. And it'll take the
transaction DTO and the wallet password
as its parameters. Now, first we will create
a list of UTXOs, CTOs, by converting the selected UTXOs from the transaction DTO. We'll call the method map on the selected UTXOs
stream and use the UTXO DTO builder to build the UTXO d t goes from
each UTXO object. So let's inject the UTXO DTO
builder into this class. Let's create it in the
GUI dot services package. Now, let's create
this build method. It'll return a UTXO DTO back to the center
transaction service. Here will return the list method called on the resulting stream. Let's refactor here to use the method reference
instead of a lambda. Now, let's implement
this method. First. Let's add the service
annotation to this class. Here we'll get the current
wallet address object corresponding to the
received UTXO address. So let's inject the current
wallet into this class first. Now we'll call the get
address method on it, passing the UTXO address
as its parameter. Let's create this method. It'll return an address object. Here will return the
result of calling the get addressed method
on the addresses field, passing the received
address as its parameter. Finally, we'll get the
current wallet address index and store it in the
address index variable. Now, we'll get the
same address from the current wallet and
get its address type, storing it in the
address type variable. Now, we'll obtain the UTXO
derivation path by calling the build derivation
path method with the address type and
the Address index. Let's create this method. Here. We'll use the address
configs object to obtain part of the
derivation path. So let's inject it
into this class. Actually, it'll be a list of address configs. Let's fix it. Now from the stream
of address configs, we'll use the filter
method to select the address config valid
for the address type. Then we'll use the find first method and get to obtain
the address config we want. Finally, we'll get the
derivation path from the address config and concatenate it with
the following string. We'll add a forward slash and
then add the address index. Here will return a new UTXO DTO passing the derivation
path and the UTXO amount. Back to the send
transaction service. Here, we'll need the
transaction signer service. So let's inject it
into this class. Now, we will call the
same method on it as its parameters will pass the
transaction DTO transaction, the current wallet
mnemonic seed. So let's inject the current
wallet into this class. Let's create this method
in the current wallet. It'll return a string. Here, will return the
mnemonic seed field. Let's create this field. And let's add a setter
for this field. The third parameter of this method is the
wallet password. The next parameter
is the UTXO CTOs. Now, in a try-catch block
will use the node send raw transaction client to send a signed transaction
to our node. So let's inject it
into this class. We'll call the
send method on it, passing the transaction DTLs
serialized transaction using the serialized method will catch an IOException and wrap it
in a new runtime exception. Now we'll use the
application Event Publisher. So let's inject it
into this class. We'll use it to publish a
new transactions sent event passing this and the transaction
DTO as its parameters. So let's create the transaction sent event in the GUI
dot events package. Let's change the
constructor signature first parameter to
accept an object. And let's pass this object
to the super constructor. And let's set the
transaction DTO field to the Received Transaction DTO. Let's add this field
to this class. And let's create
a getter for it. We'll use this event later. Back to the sign and send method will return a new
async result here, passing null as its parameter. Let's add the async
annotation to this method passing the default executor
service as its parameter. Remember that we're
doing that to run this method asynchronously, which is our policy when
communicating with our node. Okay, we've created the
mnemonic seed field in the current wallet class, but now we have to set it
after we create a new wallet. So let's go to the wallet record and add the mnemonic
seed field to it. Now let's go to the create
wallet service and add the mnemonic seed string here as the last parameter in the
wallet instantiation. Now in the update
current wallet service, let's set the current
wallet mnemonic seed to the wallet mnemonic seed. Now let's go to the second transaction
dialogue controller. In the initialized method, let's call the Lookup
Button method on the dialogue pain passing
the Okay button to it. Now let's add an event
handler to this button. The first parameter
is an action event, which defines the click on the
Okay button as the action. The second parameter is a
Lambda method that takes an event object and calls the sign and send
transaction method. So let's create this method. Here. We'll need the second
transaction service. So let's inject it
into this class. Now, we'll call the sign
and send method on it, passing the transaction
DTO and the wallet password text
as its parameters. Finally, we'll close
the dialog pain using the hide method on
the dialogue pane window. Let's run our node. And let's finally
test what we've done by running the
send Bitcoin test. Great, the tests have passed. The wallet, detected
our transaction and added it to the
transactions table. But wait, there's
something strange. I'll rewind the video
and pause it in the addresses table view
to check something. This is the addresses
table after our wallet sent our transaction in
the first test case, notice that the wallet detected
that one of our change addresses in the second
row received Bitcoins. Good. But since the address
in the first row has its UTXO in the input of the
transaction we've just sent, its current balance should be zero because its
output was spent. And since our
addresses table only shows that dresses with
balances greater than zero, this table should only show one row with the change address. Now, let's check the
transactions table for the same test case. Boops, the balance of the transaction we've
just sent is wrong. It should be equal to -0.5 bitcoin minus the
transaction fee. The minus sign
would indicate that we've sent a transaction
from our wallet. The total balance
text is also wrong. It should be equal to 0.5
minus the transaction fee. In the following video, we'll fix those issues.
58. Fixing Transaction and Addresses Tables and Balances: In this video, we'll fix some issues we had
in the last video. One of those issues is that
the addresses table shows outdated balances for addresses
whose output was spent. Since the spent address after being used has a zero balance, it shouldn't appear in
the addresses table. So let's adjust this test to verify if the
addresses table has only one row and
if that row has a balanced equal to the
expected change in Mount. Here, we'll get the
addresses table by using the lookup method. And in the then block will check if the addresses
table has one row. Let's also check if the first row balance is
equal to this variable, which we'll define
in the web block. Next, let's define
the change amount in the wear black cases. Another problem we've seen in the last video is
that the balance of the transaction is wrong
in the transactions table. That is happening because when our application receives a
transaction from our node, it's considering only
the change balance as the transaction balance to ensure the test
detects this error, let's add the following line
and then block will check if the first row in the
transactions table has a balanced equal to minus
the total spent variable. Balance is also wrong
since it's equal to the sum of all
transaction balances. So let's make our test
detects this problem. Here. We'll check if the total
balance text is correct. First, let's define
this variable. It'll be equal to
the text present in the total balance label of the window after we
send a transaction. Let's check if this variable is equal to the following text. And here let's define the funds variable and updated in
the end stream lambda. Now, let's run our node. And let's run this test. Okay, The test has failed because the addresses
table has two rows. Let's fix those issues. To do that, let's create the
transactions sent listener. But first, let's fix
something totally unrelated. The block received
listener is in the node dot events
package instead of the node dot listeners
package. Let's fix that. With that out of the way, let's create a transaction send listener in the GUI
dot listeners package. As its name suggests, this listener will handle the transaction sent event which the transaction service will publish after it
sends a transaction. Let's add the component
annotation to it. It will implement the
application listener with the transactions
sent event type. Let's implement its method. Here will lead the
update UTXOs service. So let's inject it
into this class. Now, let's call
the update method passing the event
transaction DTO. Now let's create this method. Here we will obtain
a list of UTXOs from the transaction DTO
selected UTXOs. We'll use a map on the
selected UTXOs stream and instantiate a new
UTXO for each received UTXO will pass every
received UTXO field in the UTXO instantiation, except the amount which will be zero since we've just spent it. Now to update our
wallet addresses, we'll call the update current wallet addresses
service update method, passing the UTXOs to it. And to update the
wallet transactions, let's call the update method on the Update Current Wallet
transaction service will pass the result of calling the transaction
row from method to it with the transaction
DTO as its parameter. Finally, let's call the
update current wallet balanced service update method to update the current
wallet balance. Let's create this from method in the transaction row class. Here will return a
new transaction row in a try-catch block. We'll instantiate it with the transaction DTO
transaction ID. The formatted
transaction DTO total spent negated using
the negate method. It will make the value negative
since we're spending it. It'll have zero confirmations. And its date will be
the current date. Finally, we'll catch an IOException which
can be thrown by the transaction ID method and rapid in a new
runtime exception. Back to the Update
UTXOs service. Now let's create
the update method which will receive a
transaction row in the update current Wallet
transaction service will use the platform
run later method since the following code will
modify the wallet interface. Here we'll pass a
lambda that will call the add transaction
row method on the current wallet will pass the transaction
road to this method. Now, let's create this method. Let's move this method
to this location close to the add
transaction rows method. Here we'll call the add
transaction row method on the transaction rows field, passing the transaction
row as its parameter. Let's create this method in
the transaction rows class. Here, we'll put the
transaction wrote in the transaction roadmap using the transaction
row ID as the key. Okay, with these modifications, we expect that at least the
addresses table be updated. Let's rerun the send Bitcoin
test to verify that. Okay, The addresses
tabled now is correct, but the test failed due to
the transactions table. The send transactions still
is with an outdated balance. That happened because although our sin transaction listener is updating the
transaction correctly, when our wallet receives the same transaction
from our node, it detects an update is only
the change address UTXO. It does not discount the fee
and the input we've spent. We must change how we update our current wallet
transactions to fix that. In the update UTXOs
service update method, which is called after
our application receives a transaction, will call the node RPC list
of transactions method. This method will return all
our wallet transactions, including those we send
it smart enough to return transactions with
negative balances if they are sent
from our wallet. With these transactions in hand, we can update them correctly
in our application. So let's use the node list of transactions client to obtain all transactions
of our wallet and store them in the node
transactions variable. Let's inject this
service into this class. And let's create it in
the api dot services dot no dot client package. Let's finish injecting
it into this class. Now, let's make the node
transaction record, which will model the return of the list transactions method, will create it in the domain
and start node package. Here we'll call the list
transactions method, passing the current wallet name. Let's build this method. And let's add the
service annotation here. Let's inject the node
client into this class. Here we'll obtain an array of type node transaction by calling the node client
make request method with the following parameters. The list transaction string, which is the name of
the node RPC method, a new parameterized
type reference. The wallet URL concatenated
with the wallet name. And asterisk string,
which indicates that we want all transactions
from that wallet. The max integer value, which means that we want
the maximum number of transactions in
the response zero, which tells the
method that we don't want to skip any transactions. And true, which indicates
that we want to watch only transactions
included in the response. Watch only transactions
are transactions. The node doesn't have the
private key to spend. This option makes sense
because our wallet doesn't pass any private
key to our node. Finally, we returned
the transactions array converted into a list. Now let's create the node
transaction record fields will add only the
fields will use. They are the T XID,
the confirmations, the amount, the address, which is the address that the sender intends
to send bitcoins to, not the change address. And the time, which is the timestamp of the
transaction creation. Back to the Update
UTXOs service. We'll call the update
node transactions method on the Update Current
Wallet transaction service. As its parameter will pass
the node transactions. Let's create this method. Here, we will obtain a list of transaction rows from
the no transactions. We'll call the filter method on the node transaction stream. As it's parameter will pass
a lambda that will take a node transaction and check if the current wallet contains
the node transaction address. Then we add an or
operator and check if the current wallet contains a transaction with the
no transaction ID. This filter will result in a
stream of transactions that contains only
transactions who is addressed to send
is one of our own, which covers receiving
transactions. The condition after
the or operator will make the transaction stream also includes transactions that our application already has. That means
transactions that were previously added
by the transaction send listener will
have their balanced correctly updated with
negative balances. Now, we'll not the
resulting stream using the transaction
row from method. Let's create this method
and move it to here. Here will return a
new transaction row instantiated with the
node transaction ID. The formatted node
transaction amount, the node transaction
confirmations, and the node transaction
time converted to a string. Now we convert the transaction
stream into a list. Finally, we use the platform run later method and using a lambda, we call the Add
transaction rows on the current wallet passing
the transaction rows to it. Let's remove this method since
we won't need it anymore. Let's also remove this method in the transaction row
for the same reason. One more thing here in the ad transaction rows method in the
transaction rows class, we have to update the
transaction row balance before our changes take effect. So let's include the
following code to do that. Now, let's go to the
send Bitcoin test. Before running it, let's
include asleep call after the test clicks on the OK button in the second
transaction dialogue. With that, we avoid the test failing due to
racing conditions. Now, let's run our test. Great, the tests have passed. Let's run all
application tests to ensure we haven't broken
anything in our wallets. I'll speed up the video now, since these tests will take some minutes to run in reality. Great. All the
tests have passed.
59. Changing Change Addresses. Adding Error Alert for Wrong Passwords: In this video, we'll add more tests to verify
if our wallet works as expected when sending transactions in
different scenarios. Let's see if our wallet can send two transactions,
one after the other. To do that, Let's duplicate this test in the send
Bitcoin test class. Let's change its name to. It should send Bitcoin to times. Let's fix its formatting. Let's change the wallet name
to my test while at ten. Now let's extract this
code section to a method. We'll call it send Bitcoin. Let's add the amount to send
as its second parameter. After creating a transaction, will click on the Okay
button to send it. Then we'll use this code line to make our node mine a block. With this, we expect our second transaction
to be confirmed, allowing us to use the
change UTXO generated by that transaction as an
input in the next transaction. Now we'll click on the Send tab. And we'll call the
sleep method with these parameters to wait for our transaction to be confirmed. After sending the
first transaction, we expect our first
change address to be shown in the
addresses table. We expect a different
change address to be used in the second
transaction because it's important not to
reuse addresses when receiving Bitcoins
for privacy reasons. So we'll compare the
change address in both transactions and check
if they're not equal. To do that, let's look up the addresses table
with this code line. Let's change the TableView type to address row for
correctness sake. Let's remove the
declaration here, and let's take the
opportunity to fix the same issue in the
previous test method. Will store the first change
address in this variable, getting it from the
addresses table. Then we'll call the
send Bitcoin method again to send the
second transaction. After the second transaction will look up the
addresses table again. Hopefully the first
row in the table will contain the
second change address. So let's query it
again and store the first row address and the second change
address variable. In the then block will add
the following assertion to check if the first
change address isn't equal to the second. Here, we will expect the
transaction table size to be equal to the previous
UTXOs number plus two. And let's change this part
of the total balance text. We expect the confirmed
balance to be equal to the funds
minus the total spent. Let's remove this test case, will only have one
case for this test. Let's change the
amount to send a 0.25. Let's update the total
spent accordingly. And let's change the
last four digits of the change amount. This change amount is the sum of the change amounts of
the two transactions. Now, let's run our node. Let's add the ignore
annotation to the first test, since we want to run
only the last test. And let's run it. The test has failed because the first change
address is equal to the second change
address. Let's fix this. Let's go to the update
current wallet addresses service will move this code
line to this location. Here we'll add an if
statement to check if the address being updated
is a change address. Let's add this constant
to this class. It'll be equal to address
type seg would change. In the body of the if statement, will set the current
wallet change address to the next address. Let's also add a
return statement to this if statement body. Let's rerun the test. Great, The test has passed. Now, we'll start to implement some error handling
for situations where things don't go well when we try to send transactions, e.g. what should happen when we use the wrong wallet password when attempting to send
a transaction. When our wallet sends a transaction with the
incorrect password, this causes the
transaction to be constructed with
invalid signatures. When our node receives it, it tries to validate
that transaction. Since it's signatures are wrong, it will return an error
message and won't relay the transaction
to other nodes. Currently, our wallet
is not handling the returned error and
will fail silently. Let's create a solution
for this situation. Our node returns an error after receiving our transaction, will show the user
an error alert saying the wallet couldn't
send the transaction. And y. First, let's create a
test for this scenario. In the send Bitcoin test class. Let's duplicate
the first method. Let's rename it to should not send Bitcoin with
Wrong password. And let's adjust its formatting. Let's also change the wallet
name to my test wallet 11. Here we'll click on
the password field. Let's define a variable
for this password. Let's set it to my test password and write it in the
password field. After receiving
Bitcoins, let's store the formatted balance in the
formatted funds variable. Since we won't be successful
in sending a transaction, we expect that the final
balance of our wallet doesn't change and continues to be
equal to this variable. Now, let's substitute
this code section with a send Bitcoin method call. After the transaction
dialogue appears, we'll click on the
wallet password field. And we'll write the
wrong password. After clicking on
the Okay button, we expect an error
message to appear. Let's define this error message and store it in this variable. Then, with this code, will try to store the node with this message in the
node query variable. The error alert will have an Okay button which
will try to click. In the then block, will check if the node query texts is
equal to the error message. Will also check if the address
in the addresses table has a balanced equal to the
formatted funds variable. We expect the transaction
table size to be equal to one, since we won't show the failed
transaction in the table. Let's also change the
expected transaction balance here to be equal to
the formatted funds. We also expect the
total balance and the confirmed
balance to be equal to the formatted funds variable. The expected unconfirmed
balance must be equal to zero with
eight decimal places. Let's remove the second test
case in the where block. Now let's implement the
error alert feature. In the second
transaction service. Let's add a catch statement here to catch an internal
server error. This is the error type that
the node will return if something goes wrong when we try to send a transaction to it. Now, we'll use the logger
to log this error. So let's add the logger to
this class as a static field. Will instantiate it using the logger factory
get logger method, passing this class
as its parameter. Then we'll call the
error method on it, passing the error get response body as
string method call. Then we'll return the
node error handler handle error method call
passing the error to it. Let's inject the node error
handler into this class. Let's create this class in the
GUI dots services package. Let's finish injecting
it into this class. Let's create this method in it. It will return a
future of type error. Going back to the location
which will call it, we see that the ID
indicates a type error. To fix that, Let's change
the return type of the sign and send method
to future of type error. But first, let's create the error record in the
domains dot node package. Let's copy this line
and paste it here. We'll change it to import this error class
we've just created. If we don't do that, Java will use its built-in
error class. Now, let's change this
type parameter to error. Let's pass the import
line to this class too. Let's add a Service
annotation to it. Now, before we continue
to implement the feature, let's run the last test
to check the format of the error message the
node returns in the logs. First, let's add the ignore
annotation to this test. So we only run the last test. Here, we see that the node
returned to JSON message. The important part
says that the script failed and 0 p equal
verify operation. That means the node failed due to an incorrect signature when executing the combined script to validate the transaction. Now that we know what
the node returns to the application when the
wallet password is wrong, Let's map this response to the incorrect password response
we want the user to see. In the node error handler. Let's add a try-catch block will convert the received error into a node error
wrapper object. So let's create this record in the domains dot node package. Following the node error
adjacent response as the specification will add an error field of
type node error here. So let's create the
node error record in the domains dot node package. It'll have a code field
and a message field. Back to the node error handler will need the object mapper. So let's inject it
into this class. Now, we'll call the
read value method on the object mapper as its parameters
will pass the error get response body as
string method call. And the node error
wrapper class. This code will convert the received error into a
node error wrapper object. Finally, we'll return a
new async result object instantiated with
an error object. To instantiate the error object, we'll use the error
from method passing the node error wrapper
error as its parameter. Let's create the front method. It'll return an error object. Let's add the message
field to this record. The front method will return a new error object instantiated
with an error message. To create the error message, let's add the error messages
field in this class. This field will be
responsible for mapping the node error messages to the messages we want to display. It'll be a map of string
keys and string values. Let's use the map of
method to create it. It's first key will be the error message
returned by the node, which we saw in the
application logs previously. The value will be the error
message we want to display. For this, we'll use the constant wrong password from the error messages
class we will create. So let's create this class in the domains dot node package. And let's add this
constant to it with the message we
want to display. Now, we'll call the
Get or default method on the error messages
field in the form method, its first parameter will
be the error message. It's second parameter will
be a default message, which we'll define
in this constant. So if the node returns an error message that
we haven't mapped yet, the error message displayed
will be an error occurred. Back to the node error handler. Let's add a catch
statement here to catch adjacent processing
exception that the object mapper can throw. Then we wrap it in a runtime
exception and throw it. Now let's go to the second transaction
dialogue controller. Here, we'll get the
result from the sign and send method and
store it in a variable. Just make sure to import
the correct error class. Now, we will need the
alert error service. So let's inject it
into this class. This service will
be responsible for showing an alert with
an error message. Let's create it in the GUI
dots services package. And let's finish injecting
it into this class. Now, let's call the
handle error method on the alert error or service object passing the result to it. Let's create this method. First. Let's add the service
annotation to this class. Here we'll declare
an error object. Then in a try-catch
block will set the error variable to the
result dot get method call. Then we'll catch an exception
that the result can throw and wrap it in a
new runtime exception. Now, in this if statement will check if the error
is not equal null. If it's not, then we'll call
the alert error method, passing the error message to it. Let's create this method. Here. We'll create a new alert object
with an alert type error. Then we'll set the
alert title to error. And we'll set the alert
header text to error or two. Let's set the alert content
text to the message variable. Finally, let's call the show
method on the alert object. Now, let's rerun the test. Great, The test has passed.
60. More Error Messages and Tests for the Send Bitcoin Feature: In this video, we will continue
adding more validations and alerts to notify the user of failed
transfer attempts. First, we'll create some test
cases for those scenarios. So let's duplicate this
test will add a test for the scenario where the
user wants to send a transaction with more
bitcoins than their funds. Let's rename it to
should not send Bitcoin without
funds greater than amount plus v.
Let's reformat it. And let's change the wallet
name to my test while at 12. Let's delete these lines. Here. We'll set an error
message variable to the content we want the
user to see for the error, the message will
be could not send transaction, not enough funds. Then with this code, will try to store
the UI element with this message in the
node query variable. And we'll try to click on the Okay button in
the alert dialog. In the then block will check if the node query text is equal to the message we
defined previously. Let's delete these variables in the where block since
we won't need them. And let's create the
previous amount variable here with the following value. Let's duplicate this line to
create a second test case. This amount value will be
one Satoshi lower than the necessary to
send 0.5 bitcoin. Now, let's delete this line. Let's replace these values with the previous
amount variable. Now, we'll create a
similar test scenario, but this time using a
wallet with no funds. So let's duplicate this test. Let's change its
name to should not send Bitcoin without any funds. Let's change the wallet name
to my test while at 13. Let's remove these lines since we won't
receive any funds. And let's leave
only the amount to send variable and
the where block. Now let's implement the feature in the create transaction
service class. Let's store the transaction
DTO in a variable here. And let's return this variable. Now. Before returning, it will call the validate
funds method, passing the transaction
DTO as its parameter. Let's create this method. Will use this method
to validate if the transaction
input amounts sum is greater than the transaction
output amongst some. So let's create this variable
to store the input amount. Some will assign the result of calling the transaction DTO, get input amounts
of method to it. Let's create this method. Here. We'll use a map on the selected UTXOs stream to create a stream of UTXO amounts. Then we'll use the
reduced method to sum each obtained amount. Finally, we'll use the
or else method to return the sum or zero if no
UTXOs are available. Now, let's create the get
output amounts of method here. We'll use a similar strategy to some of the output amounts. But instead of using
the selected UTXOs, will use a map on the
transaction output stream. The map method will return the output amounts
converted to Bitcoin. And we'll use the reduce and a Rails method the same way we did for the previous method. Back to the validate
funds method. Here, we'll create the
output some variable to store the result of calling
the get output amounts. Some method we've just created. Now we'll add an if statement to check if the input sum is smaller than the output sum
plus the total calculated F0. If it is, we throw a new create
transaction exception with the not enough
funds message. Let's create this constant
in the error messages class. Let's make both constants final. And let's create the create
transaction exception class in the GUI dot
exceptions package. It'll extend the
runtime exception and will pass its message
to the super constructor. Let's take the opportunity to add another validation here. What if we try to send a transaction without
a loaded wallet? To handle this case, let's use this if
statement to check if the current wallet
has any addresses. If it doesn't, we throw a create transaction exception with a wallet not
loaded message. Let's create this constant
to store this message. Now, let's go to the
Send tab controller. Let's wrap the
transaction DTL creation in a try-catch block. The open dialogue method call
to. Here, we'll catch it. Create transaction exception. We'll use the alert error or
service and the catch body. So let's inject it
into this class. Then we'll call the
alert error method, passing the exception
message as its parameter. Let's make this method public. Let's go to the
send Bitcoin test. Let's add the ignore
annotation to the test we made
in the last video. Let's run our node. Before running the test, let's adjust some details. Let's add a point at the
end of the error messages. In the send Bitcoin method. Let's add this parameter. It'll default to true and will only call the wafer dialogue
method if it's true. Now, let's set it to
false and these calls, because the transaction dialogue won't appear in these tests. Finally, let's run the tests. Grade the tests have passed. Let's remove all the ignore
annotations from this class. Now, we'll add a bunch of
new tests in a new class called send Bitcoin additional
test in this package. But don't worry, this
time we'll just copy the content of this class from the Project and Resources page. All of these tests must pass. Hopefully, we won't have to change anything in
our code for it. But they'll help to ensure our wallet will
send transactions correctly and detect
possible code breaks will make in the future. Let's reformat all of them. The first test we'll
verify if a wallet with a password sends
transactions correctly. The next test will generate
transactions without the need for change and check if their parameters are correct. This test will try to send a
transaction to an address of our own wallet and
check if the wallet correctly calculates its
amounts and balances. Finally, the last test will have a lot of
different test cases with different numbers of
previous UTXOs and amounts. For these tests to work, Let's move the weight for
dialogue method in the send Bitcoin test to
the GUI test class. Let's make it protected. Let's remove these imports
in the send Bitcoin test. One more thing in the
send Bitcoin method, in the send Bitcoin test, let's add the super
keyword here to ensure we will call the correct
weight for dialogue method, not the parameter
with the same name. We'll do another
test in this class. This time, we'll check
what happens when we try to send a transaction
with a dust amount. So let's duplicate this test. Let's change its name to it
should not send dust Bitcoin. Let's fix its formatting. Let's change its wallet name
to my test while at 18. Now, let's copy this code and add it to our method
to fund our wallet. Let's change the error
message to could not send transaction amount
to send is dust. Let's change the where block, adding the following
variables to it. We'll choose an amount to
send equal to 293 Satoshi's, which we know our node cannot accept since it'll
be considered dust. For the previous amount
will choose 0.1 bitcoin. Since we will trust our note to detect this kind of error, will wait for the
transaction dialogue to appear after we click
on the Send button. And let's add the click on the Okay button method call to confirm the transaction
of the dialogue. Now, let's go to
the error class. Let's add the following entry to the error messages map will map the dust message error
received from our node to the message present in the dust constant in the error
messages class. Let's create this constant. It will be equal to the message we've just defined in the test. Let's add the ignore
annotation to the other tests in this
class and run our new test. Great, The test has passed. Let's delete all the
ignore annotations we added previously. At this point, I recommend running all the
application tests. I did so and they all passed. I won't show it in this video because it would take
several minutes. I'll leave this as an
exercise for the student.
61. A Necessary Refactor: In this video, we'll start to refactor our application
to prepare it, to add support for
more address types. It'll be a relatively
big refactor, but we'll see later
that it will allow us to add support for
other address types, such as the nested
seg would address. The idea of this ref actor
is to centralize everything specific to an address type
in an address config object. By doing so, we'll be able to
add more addressed types to our wallets by adding new address config
objects to the project. After doing this refactor, we'll add a new address
type called nested segment, whose importance we will
explain in a few videos. So let's go to the
address config record to add some fields. We'll add the address
generator field. We'll use it to define
the service used to generate addresses for
different address types. A map with an environment
enum key and a string value. We'll use this field to register the address prefixes
for each environment. So let's create this enough. In the BYU w dot
domains package. It'll have the main net, test net and reg test constants. Let's add the extended
ki prefix field here. As the name suggests, it will define the prefix of the extended key that will
generate the addresses. The next field will be a predicate string as
the type parameter. It will receive a
lambda function with an address as
its parameter and an expression that
will return true if the address matches the
address config type and false. Otherwise, we'll add a script
pub key type field so we can identify which
address config to use by using the
script pub key type. Then we'll add it by function
with these type parameters will use this field to define a function to parse an
address from a given script, pub key, and a prefix. Now let's delete the
address conflict bean for the Segway change address. In this refactoring will
use only one address config bean for both the segment and the segment
change addresses. Let's change the Bean annotation
to reflect this change. Now, let's change this
address config instantiation to include each
resource related to this address type will change the derivation path field to
a map where the key will be an address type and
the value will be its corresponding
derivation path. Here we'll add two entries, one for the segment address type and its initial derivation path, and the other for
the Segway change and its initial derivation path. Now, as it's addressed, generator will add a new
segment address generator. For the address
prefixes field will add the following map will add one entry for each
environment and they're P2, WP k h address prefixes. The extended ki
prefix field will be the main net segue
prefix constant. As the address matcher will use the address matcher is
segue with Lambda function. So let's create this class
and the utils package. And let's create this function. It will be a function
that will take an address and return
true if the address starts with one of
these three prefixes present in segment addresses. The next field will be equal to the script P2 WP k h constant. Finally, the address parser
will be a reference to the script P2 WP k
h address method. Let's go back to the
address config to substitute the type of
the derivation path. It'll be a map with
address types as keys and strings as values. Now, let's create the address
config finder class in the api dot services package
will use this service as a way to retrieve the
right address can fake and other services
we'll refactor next. So let's add the services
annotation to it. And let's inject a list
of address configs here. Now, let's create this
method which will allow us to find an
address config by address. Its return type will be an optional of type address config. Then we'll filter a stream
of address configs and use the address matcher of the config in the filter lambda. Then we'll return the
optional address config corresponding to the
received address using the first method. Now let's create
another method which will return an optional
of type address config. This time, it will find an address config
by its script type. It'll be similar to
the previous method, but we will use the
filter to verify if the script pub key past matches the one from
the address config. We'll also use defined
first method in this case. Now we'll create
a method to find an address config
by address type. Again, we'll use a filter on
a stream of address configs. This time, the filter will contain a lambda
that will check if the address config address type is equal to the
past address type, or if it's equal to the corresponding
change address type. This time, we'll return the
address can fake without the optional object using
the orals throw method. This method will throw
an exception if it doesn't find the
appropriate address config. Now we're ready to start
refactoring other services to use our new address config
and address config Finder. Let's begin with the
create wallet service. Let's delete these two lines. Here. We'll use a flatMap
on the address config list. Then we'll pass a
lambda method as its parameter returning
in the lambda body the following
transformation will get the derivation paths of
the received address config. It's missing a final S letter. So let's rename this field
to derivation paths. Now, we'll get the entry set
of the derivation paths. Then we'll call the map
method on the stream of derivation path entries will return an extended pub
key for each entry using the extended pub key
service create method. As its parameters will
pass the master key. The entry value, which will be the derivation path,
the entry key, which will be the address type, and the Address config
extended ki prefix. Finally, we convert
the result to a list. Now let's go to the
extended pub key service. Let's delete these lines. Let's add a fourth
parameter to this method. It will be an
extended ki prefix. Here, we'll substitute
this argument for the extended ki
prefix, public prefix. Now let's go to the
segment address generator. Here, we'll add the prefix
as a second argument, and we'll use it as
a parameter here. Now, let's remove the address prefix
factory from this class. And let's add the
second argument to this interface,
generate method two. Now let's go to the address
sequential generator. In the generate method call, we will use the address
prefix factory. So let's inject it
into this class. Now let's call the
get method on it, passing the received address
type as its parameter. Now we'll refactor the
address generator factory. Let's delete all of these lines. Let's inject the address
config Finder into this class. Here we'll call the find by address type on the
address config finder, passing the address
type as its parameter. Then we'll return the phone address config
address generator. Now, let's refactor the
address prefix factory. Let's remove all of these lines. Then let's inject the address can think Finder
into this class. Again, we'll find
the address config by addressed type
using this method. Then we'll get the
address prefixes from the found address config. Finally, we'll return
the address prefix corresponding to the current Bitcoin environment like this. Let's fix the tests for the classes we've
just refactored, starting with the address
sequential generator test. Let's add the address
config finder as a field in this class. And let's assign it to a
mock of this class here. Now, let's instantiate a
new address configuration, setting it to this variable. Then let's use the one method to define a returned object when we call the find by
address type method on the address config finder. In this case, will return
the Segway config. So let's make this method public in the address
configuration class. Now let's change this address
prefix factory parameter to the main net string. And let's pass the
address config finder to the instantiation of the
address generator factory. Let's run this test. Whoops, the compilation
failed due to an error in the UTXO DTO builder.
Let's fix it. Here. We'll get the address config using the address config Finder. So let's inject it
into this class. Let's delete the address
config list from this class. Now, we'll find the address
config by address type here. Then we'll get the
derivation path using the received address type. Finally, we'll concatenate
the string slash and the address
index to the result. Let's run the test again. Great, The test has passed. Now let's fix the create
wallet service test. Let's delete these lines. Will instantiate the
address configuration here. Then we'll create the
address config list here. Adding the seg
would config to it. Let's remove all of these lines. Now, we'll instantiate the
necessary objects like this. Let's run this test. Grade. The test has passed. Now let's fix the extended
pub key service test. We'll add the main
net seg would prefix as the last parameter
of the create method. And let's run this test. Great, it has passed. Finally, let's fix the segment
address generator test. We'll remove the address prefix
factory from this class. And we'll pass the
main net P2 WP k h address prefix as the second parameter
to this method. Let's run this test. Grade. The test has passed. In the following video, we'll continue to
refactor our application.
62. A Necessary Refactor Part II: In this video, we'll continue to refactor our application. So let's start with the
address parser class. Let's inject the address
config Finder into this class. Let's remove this code. Now, we'll use the
address config Finder to find an address config
by script pub key type, passing the script pub
key type to this method. Then we'll map the result and apply the address
config address parser, passing the script pub key and the proper prefix
as its parameters. Now, we'll use the URL method to return an empty string if
an address can't be parsed. Let's return this
transformation. Now, let's go to the
current wallet class. Let's remove the
receiving address and change address
from this class. Instead of using
one field for each receiving and change
address will use one receiving addresses
field to model the different
addresses will use to receive Bitcoins in our wallet. So let's add the receiving
addresses field to this class. It'll be an observable map with address types as keys
and strings as values. Will instantiate it here with an observable HashMap from
the FX collections class. Let's delete the methods
that use the removed fields. Now let's create the set
receiving addresses method. We'll put an entry in the
receiving addresses map for each extended pub key. The key of each entry will be an address type obtained
using the value of method passing the extended pub key
type as its parameter. The value will be
the first address of the extended pub key. Now, let's create the set
receiving address method. We'll use this method to update the current receiving address for a specific address type. It'll accept an address index and an address type
as parameters. First, we'll get the next
address using the get address ID method passing
these parameters. Then we'll put the
obtained address in the receiving addresses using the address type as the key. Let's create the get
receiving address method. It'll take an address type and return the current
receiving address for that type will get it from the receiving addresses using the address type as the
key for the retrieval. Now, let's create a getter
for the receiving addresses. Let's rename it to get
observable receiving addresses. Now, let's go to the update current wallet
addresses service. Let's remove all of these lines inside a platform
run later call. Let's add a Lambda
function that will use the current wallet set receiving address to set the next address. Let's remove these fields
since we won't need them. Now, let's refactor the update
current wallet service. Let's remove these two lines. Then let's add a call to the set receiving addresses method we've just created in the
current wallet. Now, let's go to the
Receive tab controller. Let's remove this code. Here. We'll get the observable
receiving addresses from the current wallet
and add a listener to it. The listener will be the
following lambda function. With this if statement
will check if the receiving address that was changed was a segue
what address? If so, we'll set the text of the receiving address field
to the newly added address. Now we'll refactor how the change address is
used in the project. So let's create a class called a change
address type Finder. This class will be
responsible for finding the appropriate
change address type for a given address output. Although we can use
different address types as outputs in one transaction, it's recommended to use the
same type to complicate third parties in discovering which output was
used as the change. This service will allow us
to address this concern. Let's add the service
annotation to this class. Let's inject the address
config Finder into it. Now let's create
the find method. This method will
return an address type and take an address
as the parameter. Here we'll call the find by address method on the
address config finder. Then we'll map the
obtained value to the equivalent change address type using the following code. Finally, we use
the or else method to return the seg would
change address type. If the address
type wasn't found. We'll use the Segway change
address for this case. Although we'll allow
our application to send transactions to
most address types, we need to provide this fall back because we won't implement all the address types as receiving addresses
in our wallet. Now, let's go to the create
transaction service. We must refactor how the change address is
obtained in this class. Let's add the change
address variable here. Its value will be equal to
the result of the fine change address method call with the
address as its parameter. So let's create this method. Let's first substitute these
good change address method calls for the new change
address variable. Here we'll get the
address type by using the change
address type Finder. So let's inject it
into this class. Now, we'll call the find method on the change
address type finder, passing the address
as its parameter. Then we'll return a call to the get receiving
address method, passing the obtained address
type as its parameter. Now, let's go to the
address config class. We'll add fields related to
the transaction creation. Now. The first will be the transaction input builder with this new class as its type. Let's create this class in
the API services package. It'll be an interface. It's method will
be called Build, return a transaction input and take a UTXO as its parameter. Back to the address config. We'll add the input plus
output size field here. We'll use it later in the
dust calculator class. Let's also add the
scriptSig size field here. It'll be used in the
transaction size calculator. Now, let's also add the
transaction signer field. Let's create this class in
the api dot services package. It'll be an interface. It will have a sign method which will have the
following parameters. It will also throw
an IOException. Now in the address
configuration class, let's define the new fields in the segment address config. We'll add a new
segment input builder as the transaction
input builder. Let's create this class in
the api dot services package. It will implement
the transaction input Builder interface, and it will implement
the build method. To implement it, let's copy the following code inside
the build inputs method in the transaction creator
class and paste it here. Let's change this parameter
to a new ArrayList. We'll store this transaction
input in this variable. Then we'll call the
set witness method, passing a new witness
with the same arguments we've used in the transaction
creators service. Finally, we returned
the transaction input. Let's copy these constants and paste them to the segue
what input builder. And let's add the
service annotation here. Back to the address
configuration. We'll add 98 as the
input plus output size, zero as the scriptSig size, and a new Segway
transactions signer. Let's create this class in
the api dot services package. Let's implement the
transaction signer method. Let's add the service
annotation here. Let's go to the transaction
signer service. We'll copy this line
and paste it here. Let's substitute this parameter
for the index variable. And let's substitute
the UTXO amount for the amount variable here. Our address config
is finally ready. Now let's go to the
transaction size calculator. Let's use the cleanup
code IDE command to add the final keyword to every
field in this class. Let's inject the address
config Finder into this class. Let's delete this code. Let's redefine the all
inputs variable will map the stream of input addresses passing a reference to
the input size method. Let's create this method. It'll return a double and take an address
as its parameter. Here will return
a code similar to the previous one that
define the input size. But the scriptSig size
parameter will be taken by finding the
proper address config. Using the address config finder will throw an exception if we don't find the address config. If we find it, we'll take
the scriptSig size from it. Then we'll add the
sequence to it. Finally, we'll sum the
input size results using the double sum method. If we don't find any input, will return zero as the sum. Now, let's delete these lines. Like the input size. We'll call the map
method on the stream of output addresses will use the output size method to
return each output size. Here we'll return the
sum of the n value, the script pub key length, and the result of the
script pub key size method passing the address
as its parameter. Using an if statement will
check if the address is a seg would address by
using the Segway method. If it is, then we return the
script pub key constant. If it's not, we'll throw
the following exception. Remember that we won't use the address config to
calculate the output size because the address
config refers only to the addresses we can
accept in our wallets. Since we plan to support
more output addresses, then we will take as inputs, will have to handle the
output construction without using the
address config. Finally, we use the
reduced method to calculate the sum
of outputs sizes. And the or else method to return zero with no outputs exist. Let's delete this constant. Now. Let's go to the dust calculator. Let's inject the address
config Finder into this class. Let's add the address as a second parameter
to this dust method. Let's remove this line, will create the input plus
output size variable. We'll use the address
config Finder to find the address
config by address. If an address config
is not found. We'll throw an exception. If it is, then we
get its input plus output size property and
store it in a variable. Finally, we return the
formula to calculate if an output is dust using the
variable we've just defined. Let's remove this
constant from this class. Now, let's refactor the single
random draw coin selector will add the change address
parameter to these methods. And we pass the change address
to the dust method call. Let's refactor the
transaction creators service. Now, let's delete
these constants. Let's inject a list of script pub key builders
into this class. Let's create this class in
the api dot services path. It'll be an interface. It'll have a match method
which will return a Boolean and take as its
parameter and address. And it'll have a build
method which will return a script and take an
address as its parameter. Let's finish injecting this
field into this class. Let's also inject the address config Finder into this class. Let's add the change address
to these method calls. Let's remove this code
from this method, will create the script
variable and assign the result of the following
stream transformation to it. Will filter the script
pub key builders stream to find the one that matches
the address variable. If we don't find any, we throw an exception. Then we call the build method on the script pub key builder found passing the address
as its parameter. Finally, we return the
transaction output containing the amount
and the script variable. Now let's remove this and
used method from this class. In the build inputs method, Let's remove this code. Now, we'll call the map method
on the stream of UTXOs. Inside it will pass
the following lambda, will find the address config
using the UTXO address. If we don't find it, we throw an exception. Then we get the
transaction input builder from the address config and
call it spilled method, passing the UTXO to it. Finally, we use the
collect method to convert the resulting
stream into an ArrayList. Let's create the P2 WP k
h script builder class in the api dot services package. It will implement the script
pub key Builder interface. Let's implement the
interface methods. Let's inject the address prefix
factory into this class. In the match method will
return the result of calling the address matcher
is Segway test method. In the build method, we'll
first set the prefix variable to the address
prefix factory Get method call result will pass the Segway constant
as its parameter. Then we return the result of the P2 WP k h script method call as the parameter to
this method will pass the result of calling the back 30 to decode to hex method. First, let's go to
the POM file to update the Bitcoin Java
library to the 0.4, 0.4 version, which has the
best 32 method we need. We'll pass the prefix and
the address to this method. Now, let's go to the
transaction signer class for more refactoring. Let's inject the address
config Finder into this class. Let's delete this line. In its place, will call the same method passing
these parameters to it. Let's create this method will create the transaction
signer variable. We'll assign the result of the following
transformation to it. We'll use the address config Finder to find an
address config by addressed type will pass the UTXO DTO address
type to this method. But first, let's add the address type field
to the UTXO DTO record. And let's add the
address type to the UTXO DTO instantiation
in the UTXO DTO builder. Back to the transaction
signers service will get the address config
transaction signer from the phone address config. Finally, let's call the
transaction signers sign method, passing these parameters to it. Let's add a throws
clause to this method to indicate that this method
can throw an IOException. The refactoring is done. Now let's fix some tests, starting with the single random
draw coin selector test. Let's add the address
configuration to the setup method. Then let's add the
address configs list variable with the segment
Config inside it. Now let's instantiate a
new address config finder, passing the address
configs to it. Now let's run this test. Great, it has passed. Now, let's fix the transaction
creators service test. Like the previous test. Let's instantiate an address
configuration and create an address configs variable and an address config
finder variable. Let's also instantiate an
address prefix passing the reg test string and the
address config finder to it. Now let's create the script pub key
builders variable will assign a list to it with a new P2 WP k h script
builder inside it. Let's run this test. Great, it has passed. Now, let's fix the transaction
size calculator test. Like the previous tests, let's instantiate an address
configuration and create an address configs variable and an address config
finder variable. Let's change these case tests. We'll paste the test
cases you can find on the Project and
Resources page. Let's import the
end copies method. These test cases or
equal to the previous, but they have real
addresses this time, since they are now important to the transaction size
calculator logic. Let's run this test. Great, it has passed. Now, let's go to
the GUI test class. Let's add the main
net segment prefix constant to this method call. And let's add the reg test P2 WP k h address prefix constant as a parameter to
this method call. Now I recommend that you run
all the application tests. They all must pass.
63. Nested Segwit Addresses: In this presentation, we'll talk about nested segment addresses. So what is a nested segue? What address? Nested
segment addresses are also known as
rap segment P2, SSH dash p2, WPA, and WPA2. And P2 SSH addresses. The latter name describes
best what they are and address that encodes a pay to witness pub key hash script. Inside a pay-to-script
hash script. Most modern wallets
support these addresses. Their main advantage is that
they allow older wallets, which usually support
pay-to-script hash addresses, but don't support native
segment addresses to send bitcoins to them. Therefore, owners of nested
segment addresses can benefit from some advantages
of Segway transactions, such as cheaper transaction fees and receiving funds
from old wallets. So why the need for segue
native addresses? You may ask. The reason is that segue
native addresses make transactions even smaller than
nested segment addresses. Nested segment addresses
became available in 2017 after the seg would update, the rules to generate nested segment addresses
were defined in BIP 141. We've chosen nested
segment addresses as our wallets secondary receiving addresses to allow receiving Bitcoins
from old wallets. So to understand nested
segment addresses, let's first understand
P2 SSH addresses. P2 SSH is an acronym
for pay-to-script hash. P2 SSH addresses
are exactly that. They encode a hash generated
from a Bitcoin script. Let's see the process to
generate a P2 SH headdress. We begin with an hexadecimal
serialized redeem script. I'll redeem script is usually a multi-signature
Bitcoin script, but it can be any
other Bitcoin script. We use the hash 160 algorithm to generate a script hash from
the serialized redeem script. Then we combine the
script hash with a prefix and apply
the base 58 encoding algorithm to produce
a P2 SH address through base 58 decoding, we can obtain the
script hash back from a P2 SSH address,
a nested segue. What address is simply a base 58 encoded P2 SSH script was redeem script is
a segment script. Now let's learn how to build
a P2 SH script pub key. Remember that a script
pub key is part of a transaction output and is how the receiving address is
present in a transaction. So starting with
a P2 SSH address, we first base 58 decoded
to obtain a script hash. Then we build a
script consisting of an OP hash 160 opcode, the script hash and
an OP equal opcode. This is the P2 SH
script pub key. To send funds to
a P2 SSH address, you should put this script in the transaction output along
with the amount any wallet, but can send bitcoins
to a P2 SSH address, can send bitcoins
to a nested segue. What address? Now, let's see how to build a nested
segue transaction input, starting with the valid pub
key for a UTXO address. We then apply the hash 160 algorithm to
obtain a pub key hash. Combining the hash
with a zero prefix, we obtain a segment script. This is the redeem script, will add it to the
transaction input later. Now will sign a transaction. To do that, we first produce a signature hash from
an insider transaction using the same signature
hash algorithm we use to sign a
segue transaction. Then we combine the
signature hash with a valid private key to generate the signature using the ECDSA. Next, we show the sign
transaction input, the transaction ID output index, and then sequence fields work the same way as other
transaction types. We add the redeem script
in the scriptSig field, and we add the signature and the pub key to the
witness field. Notice that unlike
native seg would inputs, the scriptSig is not empty. In this case. The witness field of
nested segment inputs has the same elements as
native segment inputs. Now let's see how a
nested segment script is executed during transaction
validation by the nodes. Nested segment
validation requires the validation of two
combined scripts. Given a P2 SSH
script pub key from some UTXO and the
corresponding scriptSig from a transaction input. Both scripts are combined, forming the first
combined script. The redeem script, is passed to the combined script as a
single serialized element. In this first validation,
from left to right, each element in the combined
script is evaluated, starting with the redeem script, which is added to
the execution stack. Then the OP hash 160, apply the hash 160 and
the redeem script, transforming it
into a script hash. Then the script hash and the combined script is added
to the execution stack. Finally, the OP equal
opcode compares both Script Hashes and returns
true if they're equal. If they're not equal, the
transaction is deemed invalid. Next, a second script is formed by combining the witness
and the scriptSig. This time, the redeem script in the scriptSig is evaluated in its entirety just as it is in segue native script evaluations. And like segue native
script evaluation, the initial zero digit in
the scriptSig triggers a special rule that
converts it to the script present in
the combined script. From now on, the combined
script gets executed just like the combined script from segment native
transactions. From the combined script, the signature and pub keys are added to an execution stack. Then the OPT up opcode duplicates the pub key
in the execution stack. Up hash 160 hashes, the last pub key
added to the stack. Then from the combined script, the pub key hash is
added to the stack. Up equal verify opcode removes the last two elements from the execution stack
and compares them. If they're equal, the script
continues to execute. Finally, the object
sig opcode checks if the signature is valid for the pub key remaining
in the stack. If it's valid, it returns
true to the execution stack. After all the script elements were added to the
execution stack, the transaction
input is considered valid if the last
added element is true, this process must be repeated for every transaction input, all its inputs must be valid for a transaction to be
considered valid. Just as a visual reminder, let's review from where each element in the
script pub key, scriptSig and witness came during transaction construction. The script hash present in the script pub key
came from base 58, decoding the address of the
UTXO that is being spent. The pub key hash and the
scriptSig came from the hash 160s of a valid public key. The same public key is
present in the witness. The witness is signature came by applying the ECDSA algorithm, which needs a valid private key. The public key and
the witness came from the public key derived from
the mentioned private key. Now, let's see some parameters important to derive
nested segment addresses. On the Bitcoin main net, the prefix needed to derive
P2 SSH addresses is C4. After base 58 encoding a dresses with that
prefix start with the digit three on the Bitcoin
test net and the reg test, the prefix needed to derive
P2 SSH addresses is 05. After base 58 encoding addresses with that prefix start
with the digit to. The derivation path
used to derive nested segment
addresses begins with the 49 hardened
path for wallets, following the BIP 49 guidelines.
64. Receiving Bitcoin in Nested Segwit Addresses: In this video, we'll implement the nested seg would
address in our wallet, will add it as another option to receive Bitcoins in our wallet. Let's first prepare
our application to add some tests related to
the new address type. So let's go to the Node, get new address client. We need to make our
application able to call our node to generate
P2 SSH addresses. So we can create tests to send Bitcoin to these addresses. So let's use the
change signature IDE option to add a new
parameter to this method. The new parameter
will be a string, its name will be addressed type, and its default value will
be the string back 32. Let's add an empty string
to this method call. This parameter is used if we want to add a label
to our new address, since we won't need it, we'll set it to an empty string. And let's add the address
type as its last parameter. Okay, now we can ask our
node to create P2 SH addresses if the IDE
refactor worked. Now the current calls to
this method will have the string back 32 as
they're less parameters. Now, let's go to the
send Bitcoin test. Let's move this method
to the GUI test class. Let's make it protected. Instead of using the super
keyword here, let's use this. Now, let's go to the
receive Bitcoin test class. Let's copy all the
code in this class. In the GUI test package. Let's create a new
test class called receive Bitcoin
nested segue test. And let's paste the
content to this class. Let's make some
adjustments to this class. Let's change the class name to receive Bitcoin
nested segue test. Now let's change the
test method names. Let's add in a nested
segment address here. Let's do the same to all other
test methods indicating in their names that will receive Bitcoins in nested
segue what addresses? Now, let's adjust
this first test. We'll adjust its wallet name. Then let's modify the
lookup parameter here. So the address variable
gets assigned to the value of the field where the nested segment addresses. Here, instead of using the
address is valid method, we'll use the nested
segment address is valid method we'll create. Let's fix this code formatting. Now, let's create
the nested segue what address is valid method
in the GUI test class, we'll use the addresses
valid method as its base. So let's duplicate it. Let's fix its name. Now. Let's change the address type here to the nested
segwayed constant. And let's change
the derivation path here to start with 49. Let's create this constant
in the address type enum. Let's also create
the nested segue to change constant here. Here, let's change the prefix of the generated address to the test net P2 SH
address prefix. And instead of using the
segment address generator, will use the nested
segment address generator. Let's add this field
to this class. We'll use the auto wired annotation to inject
it into this class. Let's create this class in
the api dot services package. Will implement it later. Back to the receive Bitcoin
nested segue test class. Let's make the same adjustments to the other test methods. Now let's go to the address
configuration class. Let's duplicate this bean will use it as the base to create
the nested segwayed Config. Lets change these parameters to nested segment and
nested segue change. Let's change this method name
two nested segment config. Now let's change the
address config parameters. The first parameter will be the nested seg
would address type. In the second argument, let's change the map keys to nested segment and
nested segment change. And both derivation paths. We'll start with 49. We'll use the nested segment
address generator as the address generator. Let's make it implement the
address generator interface. Let's add the interface method. Now, let's set each
environment address prefix. The main net environment. We'll use the main net P2
SSH address prefix constant. The test net and reg
test environments. We'll use the test net P2
SH address prefix constant. Let's change the
extended ki prefix to the main net nested
segment prefix constant. The address matcher will be the as nested segue predicate. Let's create this field
in the address matcher. It will be similar to the
segment predicate above. But the matcher
will return true if the address starts with
three or two instead. Now, let's import it here. Let's use the P2
SSH constant here. Let's use the script nested segment address method
as the script parser. Will use the nested
segment input builder as the transaction
input builder. Let's create this class in
the api dot services package. Let's add the interface method. As the input plus output size. We'll set 180 as the
scriptSig size 23. And as the transaction
signer will use the nested Segway
transactions signer. Let's create this class in
the api dot services package. And let's add the
interface method. Our nested segue
config is ready. Now we need to implement the nested segment
address generator, input builder, and
transaction signer. Let's start with the nested
seg would address generator. First, let's add a
test for this service in the api dot
services test package. It'll extend the
specification class. Let's add the following
setup method. Let's add the security
ad provider method call. Let's add the nested
segment address generator as a field in this class. And let's instantiate it here. Now, let's go to the segment
address generator test. Let's copy this test
and paste it here. We'll use it as a base
for our new test. Instead of using the
segment address generator, will use the nested seg would
address generator here. Let's change the prefix
to this constant. Let's remove these
test cases and let's paste the ones valid
for nested segment addresses. They're available on the
Project and Resources page. Back to the nested segment
address generator. I mistakenly created this
class as a groovy class. Let's use this IDE feature to
convert this class to Java. Lets remove this code. Let's add the service
annotation to this class. Here we'll convert the
extended child key to a public key. Then we'll return the
nested segment address from compressed public
key method call on the obtain object passing
the prefix variable to it. Let's rename this
parameter to extended key. Now let's run this test. Great, it has passed. Now let's implement the nested
segue what input builder. Let's add the service
annotation to this class. Let's copy the build method in the segment input builder and use it as a base for
our new build method. Let's also paste these
two constants to the nested segue
input builder class. Now let's add the
dummy scriptSig nested segwayed constant
in the script ArrayList. Let's create this constant. It will be equal to the string
zero repeated 44 times. Here, we must pass this constant inside a list of
method actually. Now, let's implement the nested Segway
transactions signer. Let's add the service
annotation here. Let's copy the sign method in the Segway transactions
signer and use it as a base for
our new sign method. Here, instead of using the transaction ECDSA
signers sign method, we'll use the sine
nested segue method from the P2 SH transaction
ECDSA signer. Let's add the redeem script as the fourth argument
in this method. Let's remove this last argument. Let's create the redeem
script variable. This variable will be equal to the P2 WP k h
script method call. As its parameter will
pass the hash 160 of the corresponding
compressed public key of the received private key. The sign nested
segue method will take care of adding
the redeem script to the witness field
and the public key and signature to the
scriptSig field. Okay, the nested segue
config is finally ready. Now let's add the new
nested segment field to the Receive tab FXML. First, let's change
the receive address and the label text
for segment address. Now, let's duplicate
all this code and let's change this label text to nested seg would address. Let's also change this ethics ID two nested segue
receiving address. Let's move this field to the
second row of its container. To do that, let's add the grid pane row index
attribute to this field, and let's set it to one. Let's do the same
with this label. Now, let's go to the
Receive tab controller. Let's duplicate this
if block in this if statement will check
if the change to address is a nested
segment address. If so, then we'll
set the nested segue receiving address
TextField content to the change to address. So let's add this
field to this class. And let's add an FXML
annotation to it. Let's also add a
return statement here. Now, let's do some testing. Let's run our Bitcoin node. And let's run this test. Great, the tests have passed. In the next video, we'll finish adding
the nested segments support for our wallet. See, yeah.
65. Sending Bitcoin to Nested Segwit Addresses and Testing our Wallet on the Test Net: In this video, we will
implement the ability to send Bitcoin to nested
segue what addresses. We'll start by creating
tests for that feature. So in the GUI test package, Let's create the send Bitcoin
nested segue test class. It'll extend the GUI test class. Let's copy some tests from
the send Bitcoin test. They'll serve as a base
for our new tests. Let's copy this code
and paste it here. Now, we'll copy this first test, then paste it into
our new class. Let's do the same
with this test. Let's do the same
with the dusk test. Now, let's adjust the new tests. Let's change this test
name to send Bitcoin with nested segment inputs and
nested segue what outputs. Let's change the wallet name
to my test while at 23. And let's change this
lookup method call parameter two nested
segment receiving address. Here, let's change
this parameter to P2 SSH hyphen segment. This parameter will
make the node return a nested segment address for us so we can send Bitcoin to it. Now, let's adjust
the test cases. I have previously calculated the expected total
fees for these cases. They differ from
transaction fees with segue native inputs and outputs because their transaction
sizes differ. Let's make similar adjustments
for the next test. Now, let's create the
P2 SH script builder. It will implement the script
pub key Builder interface. Let's implement its methods. In the match method will
return these nested segue test method call passing the
address as its parameter. We'll return the P2 SH
script method call in the build method as its parameter will pass the base 58 decoded
address like this. Let's add the service
annotation to this class. Now, let's go to the transaction
size calculator test. Let's duplicate this method. We'll use it as a base to
create a test to verify the size calculation
of transactions using nested segment
inputs and outputs. So let's change the method
name to reflect that. Let's remove these test
cases and let's paste the test cases available on the Project and Resources page. Now, in the setup method, let's add the nested seg would config in the
address config list. Now let's go to the transaction size calculator in the script pub
key size method. Let's add the following if statement will test if the received address
is a nested segue. What address? If so, will return the script pub
key nested segwayed constant. Let's create this constant. Its value will be 23. Now let's run this test. Great, it has passed. Now let's run our node. And let's run the send
Bitcoin nested segue test. Great, the tests have passed. Now, let's play a little with our wallet in the
test net environment. To do that, Let's
change this variable in the bitcoin.com
file to test net. And let's restart our node. Let's wait for it
to synchronize. Depending on how
up-to-date your note is. It may take minutes to hours. Okay, I weighted sum hours. And finally, my notice sync. Let's run our application. Let's create a wallet. First. Let's receive some Bitcoins in our nested segment address. To do that, let's
use this faucet. Okay, the transaction
appeared in my wallet. Let's wait for a little
for it to confirm. Okay. Now the transaction
has one conformation. Now, let's send some funds
to the faucet address. Interestingly, this transaction
didn't need change and we ended up with no funds since we've spent
our only input. Let's check the last transaction
in the block explorer. Let's wait for a little
for it to confirm. I waited some minutes, and now it has three
conformations. The Block Explorer
shows the same. Great. Everything is
working as expected. Let's do one more test. Let's receive more Satoshi's first in our segment address, then in our nested
seg would address. Okay, now that both
transactions confirmed, let's send some funds to a nested segment
address from this page. Now the transaction
generated change. Since we've sent it to a
nested segment address, the generated change was
also sent to a nested segue. What address? After awhile, the transaction was confirmed. Great.
66. Sending Bitcoin to Legacy Addresses: In this video, we will
implement in our wallet the ability to send bitcoins
to P2 pKa H addresses, also known as legacy addresses. P2, pk h was the most
used to dress type before the rise in popularity of segment addresses
in the last years. Nowadays, it's
still very popular. So it's important for
our wallet to have the ability to send bitcoins
to those addresses. Let's start by refactoring how our wallet build script
pub keys so that it gets easier to add
support for sending bitcoin to other address
types in the future. For that, Let's create the script config record
in the domains package. Similar to the address config, this class will contain
parameters important to building transactions to
specifics script pub key types. So let's add the
following fields to it. The script pub key builder, the script pub key size, and the address
matcher predicate, which will help to identify the script config
type by address. Now let's create the
script configuration class in the api dot config package. Again, similar to the
address configuration class. This class will be responsible
for instantiating and defining all script pub key is our wallet can send Bitcoin to. Let's add the Configuration
annotation to it. Now, let's create the following
Bean annotated method. It'll return a script config
and define the P2 WP config. It's script pub key
builder will be the P2 WP k h script builder. It Script size will be equal to 22 and its address matcher
will be the segment Lambda. Let's go to the P2 WP
k h script builder. To make it easier
to instantiate, it will remove the address prefix factory from the service. Will build the prefix variable
by parsing the prefix from the address variable
using the parse prefix method. Let's create this method. We'll first verify if
the address starts with the test net P2 WP k h
address prefix constant. If so, we'll return
that constant. Then we'll make the
same statement, but using the reg test P2 WP k-th address prefix
constant instead. Finally, we'll return
the main net P2 WP k h address prefix constant if the code gets executed
to this point. Now let's create the bean
for the P2 SH script config. It'll use the P2
SH script builder. Its size will be 23 and its address matcher will be
the nested segment lambda. Now let's create the
script config find her class in the api
dot services package. As its name suggests, this service will be
responsible for finding the appropriate script
config for other services. Let's add the service
annotation to it. Now, let's inject the list of script configs into this class. Spring Boot magic will inject all script config beans into this list after the
application starts. Now, let's create the
find by addressed method. It'll return a script config and take an address
as its parameter, will return the following
stream transformation here will filter the script
can think stream using the address matcher
of each script config. We'll use to find
first method to get the first script
config filtered. If we don't find any, will use the URLs throw
method to throw an exception. Now, let's go to the transaction
creators service will use the script config finder
to refactor this class. First, let's remove the list of script pub key builders from this class since
we won't use it. And let's inject the script can think Finder into this class. Now in the build output method, Let's remove this code. Will create this script variable using the script config finder, find by addressed method. Then we'll call
the script pub key builder build method
on the result. Now, let's refactor the
transaction size calculator. Let's inject the script config
Finder into this class. Now, let's remove the code in the script pub key size method, in its place will use the
script config Finder to find a script config by address and return its script pub key size. Let's remove these imports and constants since we
won't use them. Now, let's go to the script
pub key Builder interface. Let's remove the
match method from it and from all its
implementations, since we won't use it anymore. The refactoring is done. Now before adding the
P2 pKa H Script Config lets create the send Bitcoin legacy test in
the GUI test package. It'll extend the GUI test class. We'll use this class to
verify if our wallet is correctly sending a transaction
to a legacy address. Let's copy the following
code from the send Bitcoin test and paste it here. We'll use it as a base
for our new test. Let's change its name to should send Bitcoin with
segment inputs, legacy output and
seg would change. Notice that although
we'll be able to send funds to a legacy address, we won't be able
to receive them to a legacy address
belonging to our wallet. Therefore, will use
segue native addresses to receive change in
these transactions. And this test name
reflects that. Let's change the wallet name
to my test while at 26. Let's change this parameter
to legacy so that we can create a legacy address that we'll use to send bitcoins to. Let's also change
the test cases using the following previously
calculated values. These values differ from other transactions
because P2, pKa, H script pub keys are a
little bigger than segue native script pub keys which impact the transaction
size and phi. Now let's go to the script
configuration class. Let's add the public
keyword here. Let's create the P2
pKa H config bean as its script pub
key builder will set a new p2 pk H
Script builder, which will create later. It's script pub key size will
be 25 and it's addressed. Matcher will be these legacy
Lambda we'll create later. Let's create the P2
pKa H Script builder in the api dot services package. Let's implement the
interface method. In the build method will
return the script P to P k h script method call as its parameter will pass
the base 58 hex decoded address using the base 58 decode with checksum to hex method. Now, let's go to the
address metric class. Let's duplicate this field. We'll use it as a base
for these legacy lambda. Here will change these
parameters to one, M and N. One is the prefix of legacy that
dresses in the main net. M and n are the prefixes valid for the other
environments. Let's import these
legacy lambda here. Okay, now our wallet can send Bitcoin to
legacy addresses. Now, let's adjust some tests. Let's go to the transaction
size calculator test. In the setup method, Let's instantiate the
script config finder. We'll pass a list of
script configs to it. Will take the
script configs from the script configuration class. So let's instantiate it here. Now, let's add the
script can figs here. Now, let's duplicate this test. Let's rename it to should
calculate transaction size for P2 WP k-th transaction inputs and p2 pk each
transaction outputs. This test name describes
well the scenario will test. Let's remove these test cases. Will paste these test
cases here which are available on the Project
and Resources page. Let's fix the formatting
of these test cases. Let's run this test. Grade. The tests have passed. Now, let's fix the transaction
creators service test. Let's copy this code
and paste it here. Let's remove the
address prefix factory and the script pub key builders. And let's fix the service
instantiation here. Let's run this test. Great, it has passed. Now let's fix the single
random draw coin selector test will need this piece
of code again. So let's copy it
and paste it here. Make sure the single
random draw coin selector is instantiated correctly. In my case, the ID
already did that. Let's run this test. Grade. The test has passed. Now, let's run our node. Before that, make sure the bitcoin.com file has the
reg test environment set. Now let's run the send
Bitcoin legacy test. Great, The test has passed.
67. Saving and Encrypting our Wallet: In the past videos, we implemented a fully functional application
that you can already use to create wallets and send and receive Bitcoins. But if we close the application, we lose every information
about the wallets we create in this
video and the next, we'll start to implement
the ability to save and load wallets
in our application. To store our application's data, we'll use the H SQL database. Sql is a SQL database
implemented in Java. We've chosen H SQL for
the following reasons. First, it's a
lightweight database. Secondly, it's compatible
and easy to use with the Spring Boot
Data JPA library. Finally, it can be easily encrypted as we'll
show in this video. Let's go to the poem
dot XML file to add the Spring Boot Data JPA and
the H SQL DB dependencies. Click on this button to
load the new dependencies. Now let's go to the application
dot properties file. We'll set some configurations
necessary for the database. This configuration
determines that each modification we do in
our entity classes, we'll update our
database accordingly. In Spring Boot, entity
annotated classes defined table structures,
as we'll see soon. Let's duplicate this line to use as a base for the
other configurations. We have to set the driver class named config to the
following class. The username and the password. You can set us
whatever you want. Since I'm using it
only for testing, I'll set these fields
to be Y0 W for now. But remember to change
these values to more secure strings before using this wallet
on the main net. Now, let's define the
URL of the database. Since we'll use a
file based database, will have to set this config to JDBC colon H SQL DB
colon file colon, the path we want the
database files to reside. In my case, the path
we'll start with the BYOD W folder
located in the seat dr. Root. Inside this folder,
I want to create specific folders for each
Bitcoin network environment. To do that, I'll refer to
the Bitcoin environment variable in this application
dot properties file, putting it between
curly braces and after $1 sign like this. Finally, I'll use the
data sub folder inside this folder and name
the DB files as my DB. And we finished the
line with a semicolon. Now inside the BYOD W package, Let's create the
database package. Inside this package, Let's
create the entities package. And let's create the wallet
entity class inside it. This class, we'll model registers in the
wallet database table. To do that, let's add the
entity annotation to it. And the table annotation with its name parameter
equal to Wallet. Now, let's add some
fields to this class. Each field in this class, we'll model a column
from the wallet table. We aim to save only the
necessary fields for obtaining the same wallet
data after loading it. So let's add the ID field. Will add this field to follow the good practice
of always adding an auto-increment identifier to a relational database table. To do that, let's add two
annotations to this field. The first is the ID Annotation, the second is the
generated value annotation with the following parameter. Let's also add a
column annotation with ID as its name parameter. The column annotation
will determine the corresponding
name of the column bound to this field
in the wallet table. We'll follow the
snake case style for the column name parameters where all the letters or lowercase and underscore
separate the words. Now let's create the name field. Let's also add the column
annotation to this field. Besides the name parameter, let's also add the unique equals true parameter and the nullable
equal to false parameter. These parameters will add the unique and non
nullable constraints in the table for this column. Therefore, the application
will raise an error if you try to create a register without a name in
the wallet table. Let's also create the
mnemonic seed field with the following annotation. The length parameter will limit the max length of this
field to 500 characters. Let's also add the nullable
equal false parameter to this annotation. Now, let's add the number
of generated addresses field with the column annotation following the snake case style. The last field will
be the created at. Let's add the created date
annotation to indicate that this field will use
the current date to register its values. Now, let's create
some constructors. First, an empty constructor, which is necessary
for Spring Boot to instantiate this
class correctly. Next, let's create
a constructor with all this class's
fields except the id. Let's create getters
for the same fields. Our wallet entity is done. Now inside the database package, Let's create the
repositories package. And let's create the wallet repository interface inside it. The wallet repository interface will be responsible
for interacting with the database by retrieving data from and saving data
to the wallet table. To achieve that, it'll extend the JPA repository interface will add type parameters
to this extension. The first test to be the entity that this repository
we'll manage. In this case, it'll be
the wallet entity class. The second has to be the
ID type of the entity. Therefore, it will
be the long class. With this simple interface, we already have access to common database operations
for the wallet table, such as inserting registers
and finding them by ID. All this is automagically
brought to you by the Spring Boot
Data JPA library. Now let's create the
services package in the database package. Inside it, let's create the
save wallet service class. This class will be
responsible for saving the wallet
after its creation. Let's add the service
annotation to it. Let's inject the wallet
repository into this class. Let's do the same with the initial number of
generated addresses field. Now, let's create the
save wallet method. It'll take a wallet
object as its parameter. To implement. It will
first instantiate a new wallet entity object with the following parameters
from the wallet. Then we'll call the wallet
repository save method, passing the wallet
entity as its parameter. *****. I mixed the order
of parameters here. Let's fix it. And let's add the
qualifier annotation for the correct injection
of this field here. That's it. With this simple code, we managed to save the
wallet information to the SQL database easily. Now let's create the
listeners package in the database package. And let's create a class
called save wallet listener. Inside it. This class will serve as a listener for
the created wallet event. So that after the application
creates a wallet, It will be responsible
for calling the save wallet service
to save the wallet. So let's add the component
annotation to it. It will implement the
application listener with the created wallet event
as its type parameter. Let's implement its method. Here, we'll call the
save wallet method, passing the event wallet
as its parameter. Let's create this method. We'll use the save
wallet service here. So let's inject it
into this class. Then we'll call the save
wallet method here, passing the wallet
as its parameter. Now, if everything
works correctly, our application
will save wallets in the database after
their creation. So let's run our
application to test this feature will test first
in the reg test environment. Make sure the VM option in the BYOD W application run
configuration is empty. For this specific test, we won't need to run
our node. Let's run it. Let's create a wallet. Okay, if everything
worked correctly, the wallet was saved. Let's close our application. Now. Let's see if the
database files were created in the specified folder. Great, the application created the reg test folder and the
DB files expected in it. Let's open them.
Idb dot log file. Here we can see all our
applications database operations in SQL
written in plain text. We can see that it has an insert command
containing wallet data, including its mnemonic seed. Good. But this is unsaved
since anyone who obtains this file can know all our
wallets, mnemonic seeds. We'll fix that soon by
encrypting these files. For now, let's see the Saved wallet data in
a more readable format. To do that, let's go to this website to
download the H SQL DB. Click on the download link to download the latest version. After downloading it, extract
its content to a folder. Let's open the bin folder located inside the
extracted folder. Now let's execute the run
managers swing dot that file. Let's go to the application
dot properties in our application to copy
the URL of the database. Now, let's paste its
content into this field. And let's substitute this
variable for reg test. Let's add here the same username and password we defined in our application dot
properties file. Let's click. Okay, great. We've managed to connect
to our database. Now, right-click on the
public dot wallet folder here and click on the
SQL select statement. Let's run this command by
clicking on this button. Great. Here is our wallet data
in a table format. We can see that our application successfully saved
the wallet data. Let's close this application. Now, as I said before, it's important that we
encrypt our database files. To do that, let's add
the following suffix to the database URL in the
application dot properties file. Here we'll define a crypto
key and encrypt type. The crib type will be AES, which is a symmetrical
cryptography that the application
will use to encrypt data before saving it in the database and decrypt
data when retrieving it. The crypt key will be the key necessary for both operations. Let's use the SQL DB manager again to generate
a valid crypt key. Click on the Okay button here. Let's execute the
following SQL command. Let's copy the generated
key into a text editor, then copy it again to the crypt key value in the
application dot properties. Now let's add the same
database configuration to the other property
files in the project. Let's remove the
reg test folder, create a distorts
the database files. And let's run our
application again. Let's create a new wallet. Now, let's open the
reg test folder and the generated
my db dot log file. Its content is entirely
unintelligible now, that means it was
successfully encrypted. Let's copy the
database URL again, including the encryption
suffix and open it using the H SQL DB manager. Great, We can see the
Saved wallet data again using the new URL
in this application.
68. The Load Wallet Menu: In this video, we'll start
to implement the ability to load previously saved
wallets in our application. First, let's make some
necessary refactoring. Let's go to the create
wallet service. We want to be able to
create wallets with different dates and numbers
of generated addresses. So let's add these as
parameters in this method. Now, let's substitute this date instantiation for the
created at variable. And let's add the number
of generated addresses variable as the last
parameter of this method. Now, let's go to the create
wallet dialogue controller to add these parameters to
the create method call. Let's inject the
initial number of generated addresses
into this class. Now, let's change the
signature of the ADA addresses method to include
the last parameter we've just added to this call. In the ADD address service, let's add the number
of generated addresses variable to this method
call and signature. Let's also pass it as
the last parameter of the address sequential
generator generate method. Let's change the signature of this method to include
this parameter. Now, let's delete the
initial number of generated addresses
field from this class. And let's substitute it here for the number of generated
addresses variable. Now let's go to the
update current wallet addresses service will set the initial number of
generated addresses field as the last parameter
of this method call. Now let's create the
load wallet test class in the GUI test package. It'll extend the GUI test class. Let's go to the send Bitcoin
test to copy this code part, since we'll need it
for our new test. And let's paste it here. Now, let's go to the
receiving Bitcoin test class. Let's copy this first test and use it as a base
for our new test. Let's rename it to should load wallet and receive Bitcoin. Let's format this test. Let's add a given
block to this test. Here, Let's define a wallet
name and password variables. Let's also define a
mnemonic seed variable. We'll use the
mnemonic seed service create method to generate it. Let's inject the
mnemonic seed service into the GUI test class. Now, let's call the create
wallet method here, passing the wallet
name, the password, and the mnemonic seed
as its parameters. Let's create this method
in the GUI test class. Here, we'll create a Wallet using the create wallet service. So let's inject it
into this class. Now, let's call
the create method passing these parameters to it. Now we'll use the
save wallet service. So let's inject it
into this class. And let's call it
save wallet method, passing the wallet
as its parameter. Finally, let's return
the wallet here. Back to the load wallet test. Now that we've created and saved a wallet will try to load it. So let's remove this code. Let's call the load
wallet method, passing the wallet
name as its parameter. Let's create this method
in the GUI test class. It will also take an
optional password parameter with an empty string
as its default value. First, we'll try to click on a component with the texts load. This component will be
the load menu button, which will be next
to the New button in the menu on the top
of the main window. Then we'll move the mouse to a component with
the text wallet. After that, we will
expect a sub menu containing all the loaded
wallet names to appear. Then we'll click on the one with the text equal to the
wallet named variable. Next, we will expect
a dialog window to appear with a password input
field and an Okay button. So we'll click on
this field which has the load wallet
password ethics ID. Now we'll type the
password in that field. Then we'll click on
the Okay button. Finally, let's call
the sleep method to wait for the wallet to load. Okay, the rest of the
test will be just like the receiving wallet tests
that we've used as a base for this test will send Bitcoin
to the loaded wallet and expect the addresses and transaction tables to be filled
with appropriate values. Now, let's go to the
playground dot FXML file to design the load menu. Let's add a menu control
to the menu bar. Let's change its text to load. Now, let's add a menu
control to the load menu. Let's delete this menu item
that was added automatically. Let's change this
menu texts to wallet. And let's delete this menu item. Now in the text editor view, let's add an Fx ID
to this menu tag. Let's set it to load menu FXML. Let's add it to the
main window controller. Will add an FXML annotation and change it to
a private field. Now, let's add the
load menu class in the observables package. This class will be responsible for managing the menu items that will be added
for each wallet we create in our application. Let's add a component
annotation to it. Let's create the menu
items field here. It'll be an observable set
of type wallet entity. We'll instantiate it using a
new observable set wrapper, passing a new
LinkedHashSet to it. Now let's create the good observable
menu items method here. It will return the menu items. Now let's create the
load menu listener in the GUI dot
listeners package. This listener will be
triggered by the gooey started event published after
the application starts. We'll use it to load
all wallet saved in the database and add
them to the load menu. Let's add a component
annotation to it. It will implement the
application listener class with the GUI started event
as its type parameter. Let's implement its method. We'll use the wallet
repository here. So let's inject it
into this class. Let's also inject the load
menu into this class. Then we'll call the final method to load all wallets
from the database. For each wallet will call
the add wallet method. Let's create this method wrapped in the platform
run later call, let's call the load
menu add method, passing the wallet
entity as its parameter. Let's create this method
and the load menu class. Here will simply call
the menu items add method passing the wallet
entity as its parameter. Now let's copy this line. Let's go to the Save
wallet service class. We'll paste it here. Let's inject the load
menu into this class. With this code, will make our newly saved wallet be
added to the load menu. Now, let's go to the
playground dot FXML. Let's copy these lines and paste them into the
main window FXML. Now, let's go to the
main window controller. In the initialized
method will bind all changes in the load menu
observable to the load menu. Fxml component will need
the load menu here. So let's inject it
into this class. Now, let's call the get observable menu
items method on it. And let's call the ad
listener method on the result as its parameter. We'll pass the following Lambda. The Lambda body will
get the element added and store it in the wallet
variable like this. Then we'll use an if
statement to check if the menu contains the
wallet like this. Let's create this method. Using the following stream
transformation will check if the load menu FXML contains a menu item with the same name as the wallet entity variable. Here, if the load menu FXML
does not contain the wallet, will proceed to add it to it. Will instantiate a
menu item object passing as its parameter,
the wallet name. Then we'll add the menu item to the load menu FXML, like this. Now, let's go to the
GUI test class in the start method
before each test will clean the database
so that wallets, wood duplicated names
won't be persisted. That way, we avoid
test crashes due to the unique name constraint
in the wallet table. We'll need the wallet
repository here. So let's inject it
into this class. Then we'll call the
delete all method on it. Now, let's run our node. And let's run the
load wallet test. Okay, the test ran until the
expected part and failed. We could see that our
load menu was created and populated with the
created wallet name. Good. In the next video, we'll continue to
implement this feature, C. Yeah.
69. Loading a Wallet: In this video, we'll continue to implement the
load wallet feature. To start, we'll build a dialog window that will appear after we
click on the name of the wallet we want to load and the load menu as a
base to build it, we'll use the create
wallet dialogue. So let's go to the create
wallet dialogue FXML. Let's copy all this content. Let's create the
load wallet dialogue FXML file in the FXML package. And let's pay The
copied content here. Let's change the
dialogue header text to the following phrase. Let's change the pref
height attribute to 300. Let's also remove these lines. Let's go to the Scene Builder
to see how it's looking. Let's change this label
text to wallet password. In the text editor view, let's change this fx ID
to load wallet password. And let's change the
FX controller of the dialogue pain tag to load
wallet dialogue controller. Let's create this controller. Let's add the component
annotation to it. And let's add all these FXML
fields to the controller. Now, let's go to the
main window controller here before adding the menu
item to the load menu FXML, we'll call the set on
action method on it, passing the following
Lambda to it. This will make the
application execute the open load wallet
dialogue method, passing the wallet as its parameter after we
click on this menu item. So let's create this method. Let's copy the content of the open create wallet
dialogue method and use it as a base
for our new method. Let's set the dialogue
title to load wallet. Let's change the FXML
loader instantiation first parameter to the load
wallet dialogue field. Let's inject this
field into this class. Let's add this value
annotation here and change its parameter to
the load wallet FXML file. Now, we have to
pass the wallet to the load wallet
dialogue controller. To do that, let's first get the controller from
the FXML loader. Then let's call this set
wallet method on it, passing the wallet to it. Let's create this method. Here. We will simply set the
wallet entity field to the wallet variable. Let's create this field. Now, let's create the
initialized method which will be executed
after the dialog appear. Let's copy this code and the create wallet dialogue
controller and pasted here. This will set the
action of closing the dialog window to
the cancel button. Let's also copy and paste this code to the new
initialized method. It will set the
Okay button action. But instead of calling
the create wallet method, it'll call the load wallet
method. Let's create it. First. We'll recover a fresh
wallet entity from the database and set it as
the wallet entity field. To do that, we'll need
the wallet repository. So let's inject it
into this class. Then we'll call the method
find by name on it, passing the wallet
entity name to it. Let's create this method. The method will be
just like this. Spring Boot Data JPA magic will automatically translate
this method name into a SQL query and recover
the wallet entity with the name passed as the
parameter from the database. Now, we will create
a wallet object. To do that, we'll need to
create wallet service. So let's inject it
into this class. We'll call the create method passing the following
parameters. All these parameters except
the wallet password, will be taken from
the wallet entity found in the database. The password will be taken from the dialogue password field. Let's inject the context
object into this class. Now, using the
context will publish a loaded wallet event passing this and the created
wallet to it. Finally, we'll call
the hide method to close the dialog
window like this. Let's create the loaded
wallet event class. Will pass the load wallet
dialogue controller parameter to the super constructor. And we'll set the
wallet field to the wallet constructor
parameter. Let's create a getter
for the wallet field. Now, let's do some refactoring in the created wallet
import listener. We want to make it to listen
for loaded wallet events. But the way it is now, it can only listen for
the created wallet event. To change that, Let's delete this implement statement and
this Override annotation. Let's change this
method's name to import wallet and its
parameter to a wallet object. Let's change the
import wallet method parameter to Wallet. Now let's create a method annotated with the event
listener annotation. It'll be called on
created wallet event, and it will receive a
created wallet event as its parameter. Here we'll call the
import wallet method, passing the event wallet
as its parameter. This refactoring will continue to make things work as before. But now, if we want to
listen for another event, we can just create another event listener annotated method passing the event we
want as its parameter. That's exactly what
we'll do now using the load wallet event will also call the import
wallet method here, passing the event wallet
as its parameter. That will make the Bitcoin
node import the wallet, just like when we
create a new wallet. Now, let's create the loaded
wallet listener class in the listeners package. Let's add the component
annotation to it. Let's create an event
listener annotated method called unloaded wallet event. It'll take a loaded wallet
event as its parameter. Here we'll call the
load wallet method passing the event wallet
as its parameter. Let's create this method. Here. We'll need the update
current wallet service. So let's inject it
into this class. And we'll call the
update method on it. Passing the wallet as its parameter will also need
the update UTXOs service. So let's inject it
into this class. Then we'll call the
update method on it, passing the wallet addresses
and name as its parameters. With these two code lines, we expect that the
current wallet will be changed to the loaded
wallet and its addresses, transactions and balances
to be updated accordingly. Now let's fix some tests. We broken the previous videos in the address sequential
generator test. Let's delete this
first parameter in the address sequential
generator instantiation. And let's set 20 as the last
parameter of this method. Let's run this test. Great, it's working as expected. Let's do the same with the
create wallet service test. Okay, It's working. Now. Let's run our node. Let's clean and
compile the project using this Maven IDE feature. And let's run the
load wallet test. It failed. The problem is in the
main window controller. Here we have to call the FXML loader get
controller method instead of the load method. Let's run the test again. Great, It has passed.
70. Plausible Deniability, Refactoring and More Tests for the Load Wallet Feature: In this video, we'll
add more tests and make some optimizations to
the load wallet feature. One of the characteristics that our wallet will have is
plausible deniability. Plausible deniability
in this context is a security feature
that will make you able to hide your
wallet addresses by entering a different password
to load your wallet. That means our wallet won't
have wrong passwords. Each entered password will give access to a different
set of addresses, but observing third parties
won't be able to tell if the loaded wallet addresses
have the bulk of your funds. Let's create a test
for this scenario. Let's duplicate this test and use it as a base
for our new test. Let's rename it to
should load wallet with different password
and receive Bitcoin. Let's reformat it. Let's increase the
wallet named number. Now let's define this
different password variable and pass it to the
load wallet method as its second argument. Let's also pass it as the last parameter of the
addresses valid method. Let's set the
password parameter as an optional parameter
to this method, setting its default value
to an empty string. Let's substitute the
empty string and the two master key method
for the password variable. Let's run our node. Let's comment this method
and run our new test. Great, it has passed. This test shows that our application already
supports any password for loading a wallet and
that each password will generate a separate
set of addresses. Let's uncomment this test. Given our way of viewing
a wallet as having multiple sets of addresses
according to their passwords. We have to fix an issue
in our application. When we make calls
to our Bitcoin node through our application, we use our wallet name. This is a problem
since for operations such as retrieving our
wallet transactions and UTXOs from our node will
collect the same information for different sets of addresses generated by
different passwords. To optimize our note
RPC calls and collect only information for
the generated addresses with a given password, will use the first
generated receiving address as the name of the
wallet important to our node. To do that, let's go
to the wallet record. Let's create the good
first address method here. It'll use the get
addresses method to return the first
address of the wallet. Now, we have to substitute every call to the wallet
name field that was made to communicate
with our node for a good first
address method call, let's use our ID to identify the usages of the name field
and do the refactoring. Now, let's go to the update
current wallet service and set the first address
of the current wallet. Let's create this method. Here. We'll set the
first address field to the received parameter. Let's create this field. And let's create
a getter for it. Now, let's substitute the relevant current
wallet getName method calls for current wallet get
first address method calls. Now let's go to the
load wallet test, comment the first test, and run the remaining test. Great things continue
to work as before. Let's uncomment this test. Now, let's go to the address
configuration class. Since we're now using
the first wallet address to interact with our node, the first address must
always be the same for a combination of mnemonic
seed and password. Therefore, we have to
guarantee that the list of address configs and
their addresses always be in the same order. To do that, let's use the order
annotation in both beans. Let's add the order
annotation with zero as its argument
in this first Bean. This will make Spring
inject this bean in address config lists
as the first element. Let's also change the
second argument of the address config object
to a linked Hashmap. Contrary to maps generated
by the map of method, the linked Hashmap maintains
the insertion order, will achieve this using
the following code. Now let's do the same refactor to the other red
dress config bean. And let's add the
order annotation here using one as its parameter, guaranteeing that it'll
be injected after the first bean enlists
of address configs. Now, let's do another
optimization. Importing addresses to our
node is sometimes slow. To avoid unnecessarily
importing addresses to our node will check if
they're already imported. To do it, Let's create
the node received by address client in the
node dot client package. This client will retrieve all the current loaded
addresses by our node. Let's add the service
annotation to it. And let's inject the
node client into it. Now, let's create the list
addresses method here. It'll return a list of node address objects
as its parameters. It will take a wallet name. Amen conf, which will define the minimum number
of conformations of the addresses returned. The include empty parameter, which will define
if the call will return addresses without funds. And the include
watch only parameter which will tell the
node if we want to retrieve addresses
which the node doesn't know their corresponding
private keys. Let's create the
node address record in the domains dot node package. It will have only
an address field. Using the node client. Let's make the RPC call here. It'll return a node
address array. The first parameter to the make request method will be the list received
by address string. Then we'll pass a new parameterized type
reference object. Next, the wallet URL. Finally, the listed dress method parameters we
described previously. Now will return a conversion of the node address array into a node address list like this. Now, let's go to the
import wallet service. Here we'll add an if
statement to check if the wallet addresses were
not imported like this. Let's create this method. Inside the if body will use the import addresses
method to import the addresses only if they
weren't imported before. Let's implement the
address important method. We'll get the imported
addresses using the node received
by address client. So let's inject it
into this class. Then we'll call the
list addresses method. Passing the following parameters will convert the result
into a stream and use the map method to extract
all node address addresses. Then we'll convert
the result into a set of addresses using
the collect method. Next, we'll convert the wallet addresses into a set like this. Now, by using the removal
method on the wallet addresses, will remove all imported
addresses from it. Finally, we'll return
the wallet addresses is empty method call. If the result is true, all wallet addresses
were already imported. Oh, we forgot to delete
this line. Let's do it. Now. Let's go to the
send Bitcoin test. From now on, we'll
add some assertions regarding the load wallet
feature to some tests. Then if we find any bugs in the process,
will correct them. Let's start with the
first send Bitcoin test. Let's delete this
line in its place. Let's add a weight load
wallet method call. Using this method will try to
make this test run faster. Let's create it in
the GUI test class. Here, let's call
the wafer method. Passing these parameters. At each iteration of this callback will use the
sleep method to wait for 1 s. Then we'll check if the current wallet
first address is set. So let's inject the current
wallet into this class. Using the node list wallets
client will check if the node loaded the address with the current wallet
first address. So let's inject this
service into this class. With this code will wait until
the node loads the wallet. Now, let's fix an issue that may cause test
failures sometimes. Here, the expected
total size parameter is equal to the sum of the current
iteration index plus one. Now let's create the last
receiving address variable will set its value to the receiving address
present in the window. Now, let's set some other
variables that will help us compare the values in the wallet before and after we load it. First, the transaction
table size. Then the first row
transaction table balance. Now let's call the
load wallet method, passing the wallet
name as its parameter. Let's set this variable here. Now, we'll set the
transactions table after load variable. We'll get it using
the lookup method just as before loading it. Then we'll click on the addresses tab and set the addresses table after load variable in the same way. Next, we'll click on the Receive tab and set the last receiving
address, afterload variable and the label text
after load variable. Now in the then block, Let's delete these two lines. And let's compare the last
receiving address variables before and after
loading the wallet. Then let's set the
addresses table size and first row addresses table
balanced variables here. And let's add these
two comparisons which are equal to the
ones we've just deleted. Now, let's compare the
addresses table sizes before and after
loading the wallet. And let's do the same to the addresses table
first row balance. Now let's delete
these two lines, substituting them for
the following lines, which make the same comparisons but use the
re-factored variables. And let's add the
following comparisons of the variables before and
after loading the wallet. Let's comment all other
tests in this file. And let's run the
remaining test. The test failed because
the last receiving address before and after loading
the wallet are different. In the next video,
we'll fix that.
71. Fixing Addresses, Transactions and Balances: In this video, we'll
fix the error of having different receiving
addresses before and after we load a wallet. It happens because we're using the UTXOs to update the current
receiving address index. After loading the wallet and
retrieving the wallet UTXOs, the old outputs used to update the receiving
address aren't part of the UTXO set anymore because
they were already spent. Thus, the receiving address
ends up not being updated. We'll fix that by transferring the code responsible
for updating the receiving addresses from the update current wallet addresses service
to a new class. The New Class we'll use the retrieved transactions
from the node containing all the wallets
used addresses to update the current
receiving addresses. So let's create this class which will be called
update current wallet receiving
address his service in the GUI dot services package. Let's add the service
annotation here. Now, let's copy and
paste some code from the update current wallet addresses service to this class. Let's add this
qualifier annotation. Now let's create the
update method here. It'll take a list of
node transactions. Using the map method
will get a stream of addresses from the stream
of node transactions. Then we'll filter the
stream to make it contain only addresses
in the current wallet. Then we'll call the mark is
used method on each address. Let's take this method from the update current wallet
addresses service. Now we'll call the
current wallet get addressed types method. Let's create this method. It'll return the
result of calling the addresses get
addressed types method. It'll return a set
of address types. Let's create this method
in the addresses class. Here will simply return the key set of the
addresses field. Now for each address type, we'll call the update
receiving address method. To do that will
change the type of this method parameter
to address type. And we'll delete this line since we will no longer needed. Now, let's go to the update current wallet
addresses service to delete the same
code we pasted to our new class and their usages. Now, let's go to the
update UTXOs service. Let's inject the
update current wallet receiving addresses
service into this class. And let's call the
update method from the injected service passing the node transactions
as its parameter. Now let's go to the
send Bitcoin test. Let's add the sleep method call here to avoid a
new race condition that may happen sometimes makes sure your node is running. And let's run this test. The receiving address
issue was fixed, but the test failed
due to another bug. The transaction
balance after loading a wallet is not the
incorrectly calculated. Let's fix this. To
solve this bug, we have to understand better how the Bitcoin Core list
transactions API works. Here is the documentation of the list transactions
bitcoin Core API. Notice that for
each transaction, the API returns only
one address field. The amount refers only to the Bitcoin amount
sent to that address being negative if
it does not belong to our wallet and
positive otherwise. If the transaction
has a change output, the API returns more than one register
for each transaction. Each register with
an address field of the transaction output. Let's see an example of
the result of this API for a transaction sent from our wallet with one
input and two outputs. For the same transaction, the API returned
three registers. We can see that they refer to the same transaction by looking
at their transaction IDs. Notice that the
first two registers have the same address. These registers refer
to the change output. The first has a positive amount indicating that it's an address
belonging to our wallet. That is the change amount. The second register has the
same amount, but as negative. That means our wallets sent
an amount to that address. It also has a negative fee, which is the transaction fee that we paid in the transaction. If we sum the amount of
the first two registers, we end up with zero, which is exactly the balance we paid ourselves in
the transaction. Finally, the third
register contains a negative amount indicating that we send Bitcoin
to that address. It also has a fee equal to the fee in the
second register. Considering how this API works, the correct way to calculate the transaction
balance is to sum the register amounts
returned for the same transaction and the fee of one of
those registers, the transaction balanced before loading a wallet is
correct because we're calculating it in a way that
takes the second transaction built before we send it to
our node into consideration. After we load our wallet, we lose the transaction
information that is not considering it to calculate
the transaction balance. So let's go to the
Update Current Wallet transaction service
to fix the issue. Our new solution won't
need this filter anymore. So let's delete this
line in its place. Let's add a call to the collect
method as its parameter. We'll call the collectors
grouping by method, passing a reference to
the node transaction TX ID with this code will
obtain a map where the keys will be transaction
IDs and the values will be lists of node transactions
with the same transaction ID. Let's call the values method on the result than the
stream method to obtain a stream of lists of node transactions
with the same T XID. The rest of this
method stays as is. Let's create the
transaction from method. It will take a list
of node transactions and return a transaction row. Let's move this method here. Let's first get the
transaction fee. Will take it from a stream
of no transaction fees. Let's create the fee field
in the node transaction. Then we'll filter the
stream for nominal fees. Then we'll call the fine first to get the
first few returned and use the URLs method to return zero if no fee is found. Now, let's calculate the amount. Using the map method will
get a stream containing all node transaction amounts from the node
transaction stream. Then we'll use the
reduced method, passing the big decimal add method reference to
some all amounts. Finally, we'll use
the or else method to return zero with no
transactions are found. Now let's copy and
paste this line here. We'll use the first node
transaction TX id as the first parameter of the
transaction row instantiation. Next, we'll add the
obtained transaction fee to the amount and pass the
amount as a parameter here. Finally, we'll pass the first node transaction
confirmation in time as the last transaction row instantiation parameters. Let's delete this method since
we won't need it anymore. Let's run this send
Bitcoin test again. The first test passed, but the second failed
due to a race condition. Let's quickly fix it with
the sleep method call here. We'll do a better fix for
this error in the next video. For now, let's comment
this line to run only the failed test grade. The test has passed. Let's uncomment the
rest of the test.
72. Updating the Number of Generated Addresses and Optimizing Tests: In this video, we'll add more test assertions to check
if we can correctly load a wallet with more
addresses generated than the initial number of
generated addresses config. But first, let's
optimize a test method to avoid some race
conditions in some tests. In the send Bitcoin test class. Let's delete these two sleep
method calls since our fixed will better handle
race conditions after we send bitcoins. Let's go to the GUI test class. Let's add this
optional parameter in the send Bitcoin and wait method with the default value equal to hashtag
receiving address. Then let's add the following and condition to this return value. With this code will
only stop waiting if the receiving address field is different than the
address variable. Therefore, we avoid
the error of sending Bitcoins to the same address
two times sequentially. Now, let's go to the
receive Bitcoin test. In the last test of this class, we'll add some
assertions to test if we can correctly
load a wallet with more addresses generated than the initial number of
generated addresses config. So let's set the wallet
named variable and substitute the parameter in the right method
for this variable. Now let's call the weight
load wallet method here and delete the sleep
method calls of this test. Now, after receiving
Bitcoins seven times, let's set the last receiving
address variable to the value present in the
receiving address field. Now, let's copy this line in the sand Bitcoin test
and paste it here. Let's change this variable
to address his table view. Let's do the same
with this other line. Now, let's copy and paste all these lines from the send
Bitcoin test to our test. Let's also copy and paste
this line to our new test to compare the last
receiving address before and after
loading the wallet. Let's add this line to compare the transaction table size before and after
loading the wallet. This line, we'll
do the same with the addresses table size. And this other line will compare the label text before and
after loading the wallet. Let's run our node. Let's comment the other tests in this class and run our new test. The test has failed due
to the difference between the last receiving address before and after
loading the wallet. Also, if you noticed
the number of addresses in the addresses
table after loading, it is only three when the
correct would be seven. This is because we're
not saving the number of generated addresses
after we generate them. Let's fix this. Let's go to the update
current wallet receiving addresses service will need
the wallet repository here. So let's inject it
into this class. Now, we'll call the
increment number of generated addresses method
on the wallet repository. As its parameters will
pass the initial number of generated addresses and
the current wallet name. Let's create this method. Let's change this
type parameter to the integer class and this
parameter name to increment. As its name suggests, this method will
increment the number of generated addresses
field in our wallet. To do that. Let's add the transactional
annotation to it. This annotation is
a requirement of the JPA library because it'll do the SQL operation
in two steps. Let's also add the
modifying annotation. This annotation is needed
by repository methods that modify registers and
tables, which is the case. Finally, let's add the query
annotation to this method. This annotation will
indicate the SQL operation we want to make
as its parameter. Let's add the following string. This SQL-like
statement will add to the number of
generated addresses, the increment value passed as the first argument to
the annotated method. The where clause will make
this operation occur only in the wallet with the name
equal to the name parameter. To bind the method parameters
to the SQL variables. Let's add these two poem
annotations to these parameters. Now let's run our test again. Great, it has passed. Our load wallet feature is done. Now, it's important to run all our application tests
to check if a bug appeared. But first, we'll fix an optimize
some of our tests to use the weight load wallet
method and delete some sleep method calls that
aren't needed anymore. Let's first uncommon
these tests. And let's make the
mentioned optimizations. In the nested segment tests, such as the ones
from this class, we must add some parameters to send Bitcoin and
wait method call. So let's do it. The last parameter of
these calls must be the string hashtag nested
segment receiving address. In some methods such as this, we also need to set
the ID plus one is the third argument of the send Bitcoin and wait method call. Now, we can run all
our application tests. They all must pass
in my machine. They're taking half an hour. So I left this as an
exercise for the student.
73. Importing a Wallet with the Mnemonic Seed: In this video, we'll implement the ability to import a wallet
with the mnemonic seed. It's important to have
this option to recover your wallet and cases
where you lose, destroy, or don't have access to
the hardware or files containing the application or database with your wallet data. In these cases, you would simply enter your backed up
seed and optionally your passphrase in
the import feature of the application to get
access back to your wallet. One more thing, you won't be restricted to
recover your wallet. Using this application, you'll be able to use
mnemonic seeds generated by this application to
recover your wallet and other BIP 39 compatible wallets. Also, you'll be able
to recover access to your Bitcoin wallet
addresses by importing your BIP 39 mnemonic seed generated by other wallets
into this application. Just make sure both
applications support the derivation pads of the
addresses you want to import. So let's start by creating
the import wallet test. It'll extend the GUI test class. Let's copy this code
from the send Bitcoin test and paste it here. Now let's create a test called should import wallet
and receive Bitcoin. In the one-block, let's set
the wallet named variable. And let's create a
mnemonic seed using the mnemonic seed
service create method. Now let's call the
import wallet method, passing the wallet name and mnemonic seed as its parameters. Let's create this method. Let's also add a password as a third optional
parameter to this method. Here we'll click on a menu
with the Text Import. Then we'll click on the Menu
item with the text wallet. Now, we'll click
on the component with an FX id of wallet name. This component will
be an input field where we'll enter the name
of the imported wallet. Next, we'll call the write
method passing the name variable as its parameter to
type the name in that field. Next, we'll click on
the password field, which will have an FX
id of wallet password. Then we'll write the
password in that field. Next, we'll click on the mnemonic seed field and write the mnemonic seed
variable in that field. Finally, we'll click
on the Okay button. Let's also add
asleep method call to wait for the wallet to load. Back to the import wallet
test using the lookup method, let's retrieve the address value present in the receiving
address field. Using the send Bitcoin
and wait method, Let's send 1,000 Satoshi's
to the retrieved address. Now we'll click on
the addresses tab. Then using the lookup method, we'll get the addresses table
and store it in a variable. We'll click on the
transactions tab, get the transactions table
and store it in a variable. Also, let's do the same with the total balance labeled text. Now, let's set two
variables to store the addresses table size and
the transactions table size. Let's add the
following assertions in that then block to check if the previously set variables
have the expected values. Now let's duplicate this test. Let's rename it to should import used wallet
and received block. In this test will
create a wallet and receive Bitcoins in it
before we import the wallet, then we'll import the wallet, generate a block in
our note and check if the application correctly loads and updates the imported wallet. So let's create a given
block here where we will create a wallet and
send one Bitcoin to it. Now, let's delete this line. Let's change this
variable name to import wallet name and increase
its wallet named number. We'll use this variable to
name the imported wallet, since we can't create two
wallets with the same name. Let's pass the password
variable to this method call. After importing the wallet, let's generate a block. To do that, let's first generate a note address using the
node get new address client. Then we call the generate
to address method, passing the node address as its parameter to
generate a block. Now, let's call the sleep method to wait for the
block to be mined. Finally, let's change the
expected total balance. Will expect a balance
of one confirmed Bitcoin and zero
unconfirmed Bitcoin. The rest of the test
stays as is. Now. Let's go to the
playground dot FXML to design the import
feature menu. Let's duplicate the load
menu tag and its contents. Let's change this text
attribute to import. This fx ID to import menu FXML. Let's also add the action attribute here with
its value referring to the method open-end port
wallet dialogue will create this method later to
respond to a click on this menu item by opening
a dialogue window. Let's go to the Scene
Builder to see how it looks. Okay, the Import menu was
added next to the load menu. Now let's copy and paste this code to the
main window FXML. Let's create this method in
the main window controller. It's code will be
very similar to the open create wallet
dialogue method. So let's copy it
and paste it here. Let's change the dialogue
title to import wallet. And let's substitute the
load wallet dialogue field for the import wallet
dialogue field here. Let's create this field and let's inject it
into this class. The value annotation
here will point to the import wallet dialogue FXML. Now let's create the input while a dialogue FXML in
the FXML package. Let's use the content of the create wallet dialogue
FXML as the base to build it. Now let's change the
dialogue header text. Instead of click on Create, let's ask the user to enter
a valid mnemonic seed. Next, let's change
the FX controller to import wallet
dialogue controller. Now, let's go to
the Scene Builder. Let's delete the Create button. Let's put a label here and change its text
to creation date. Let's add a date picker
control to the cell. Let's click on the
mnemonic seed text area and make it editable. Back to the text editor. Let's change this
Fxi de toilette name and this fx ID to
wallet password. Let's set the date picker
FX ID to creation date. Now let's create the import
wallet dialogue controller in the controllers package. Let's add the component
annotation to it. Let's add all these FXML
fields to the controller. Let's make all these
fields private. And let's add the FXML
annotation to them. Let's copy and paste the
initialized method from the create wallet
dialogue controller to our new controller. And let's do the same with the all required
inputs are fulfilled. Let's change this name
variable to wallet name. Let's also copy and paste
this method to our new class. In the Okay button event handler we'll call the import
wallet method. This method will simply create a Wallet using the
data type by the user. So let's use the create
wallet method from the create wallet
dialogue controller as a base to build it. Let's inject the create wallet
service into this class. Let's change these variables to the ones in this Controller. And let's inject the
initial number of generated addresses
into this class. Let's do the same with
this context field. Now, instead of using a new date as the
parameter to this method, will call the bill date method. Let's create this method. We'll use the
following logic here. If the user does
not choose a date, we'll use the date when
Bitcoin began to work. If he chooses, then we'll
use the chosen date. The date parameter will define how far in
the past the node we'll go to find information about the imported
wallet addresses. The older the state is, the longer the node will
take to import the wallet. So let's use the
default date method to define the default date. Here we'll use the simple
date format object with the following parameter. Then we'll try to return the simple date format
parse method call passing the default date
constant as its parameter. We'll use the catch
block to wrap the parse exception in
a runtime exception. Let's define this constant, which is the date when
Bitcoin began to work. Here. If the creation
date is not null, then we redefine the date variable to the following value. Finally, we return the date. After creating the wallet will publish the imported
wallet event. Let's create it in the
GUI dot events package. Here, we'll pass this parameter
to the super constructor, and we'll set the wallet field
to the wallet parameter. Let's create a getter
for this field. Now, let's go to the
Save wallet listener. This listener will respond to imported wallet events to
save imported wallets. Let's refactor it so it can listen to more than one event. To do that, let's remove
this implements statement. Let's change this method named
to uncreated wallet event. Let's add the event
listener annotation to it. Now let's create the unimportant
wallet event method, passing in imported wallet
event as its parameter. Let's copy and paste
this line here. And let's add the event listener annotation
to this method. Setting an order annotation to event listener annotated methods will make the order of execution of these listeners predictable. The smaller the order
annotation parameter is, the higher the priority
for this listener to run compared to other listeners
of the same event. So let's add this
annotation to this method. Let's pass zero as its parameter to make this listener the
first of its kind to run, Let's add the same annotation to the uncreated wallet
event method. We prioritize saving
the wallet first after its creation to guarantee
that if later listeners fail, at least we save its DataFirst. Now let's go to the created
wallet import listener. We want this listener to import the imported wallet
into the Bitcoin node. So let's duplicate the
unloaded wallet event method. Let's change its name to unimportant wallet event and make it except the
appropriate event. Now let's add the order
annotation to these methods. The imported and created wallet event listeners
will receive one as its parameters and the loaded
wallet event listener zero. This way, we
guaranteed that when the wallet tries to fetch
data from the node, it will already be
imported by the node. Now, let's go to the
loaded wallet listener and add a listener method for
the imported wallet event, as we just did in
the previous class. Now let's add an order
annotation to both methods, passing to as the parameter for the unimportant wallet
event method and one for the unloaded wallet
event method to make these listeners run after
all others for their events. Now, let's run our node. And let's run this test. The first test passed, but the second failed. Let's see why. Okay, a
constraint violation exception occurred because
we tried to save a wallet with the
same name two times. We forgot to change the
wallet name variable in the import wallet
method call for the import wallet name variable. Let's fix this and let's
run the faulty test again. Great, It has passed.
74. Validating Addresses: In this video, we'll implement
some validations before we create an import wallets
and try to send transactions. The first validation will prevent the wallet creation from continuing if the wallet name is equal to a previously
created one. So let's go to the
create wallet test to create a test
for this condition. It'll be called should not create wallet with
repeated name. Let's create a wallet
in the given block, creating the necessary variables and using them in the
create wallet method. In the one-block, let's
add some code to create a wallet with the same name as the one created in
the given block, but now using the GUI. After trying to
create the wallet, let's add asleep
method call here, we expect the application to
show an alert dialog window containing a message saying the wallet name already exists. So let's define a variable
containing the message. Now, we'll use the lookup
method to get a component with that message and store it in the node query variable. Finally, we'll click on the Okay button to close
the dialog window. In the then block. Let's add the following
comparison to check if the message in the
alert dialog window is equal to the error
message variable. Now, let's go to the create
wallet dialogue controller. In the create wallet method, we'll use an if statement with a validate wallet service to check if the wallet
name is valid. So let's inject this
service into this class. Let's create this service and the database dot
services package. And let's finish injecting
it into this class. In the if statement, let's call the wallet exists method on it, passing the name text
as its parameter. Let's create this method. We'll use the alert error
service and the if body. So let's inject it
into this class. Now, we'll call the
alert error method on it as its parameter. Let's pass this constant. Let's create this constant
in the error messages class. It'll have the same text
we defined in the test. Let's import this constant here and call the
return statement. Now, let's go to the
validate wallet service. In the wallet exists method will lead the wallet repository. So let's inject it
into this class. Now, we'll return the exists
by name method call on it, passing the wallet
name as its parameter. Let's create this method just by this method signature
Spring Data JPA magic already knows how to query the desired information
from the database. Let's go to the
create wallet test. Let's comment these tests. Run our node and
run our new test. Great, The test has passed. Let's uncomment these tests. Now, let's add the same
wallet name validation in the import wallet feature. So let's copy this test and paste it here in
the import wallet test. Let's change its
name to should not import wallet with
repeated name. Let's make some
adaptations to this test. First, let's increase
the wallet named number. Then let's modify it to import a wallet here instead
of creating it. And let's change the parameters
of these method calls. Now, let's go to the create wallet dialogue controller
and copy this if block here. And let's paste it here in the wallet dialogue controller. Let's inject the necessary
services into this class. Let's change this
variable to wallet name. Now, let's comment these
tests and run our new test. Grade. The test has passed. Let's uncomment these tests. Now, let's add an
address validation. When trying to
send transactions. Currently, we can type anything in the
address to send field. If we try to send a transaction to an invalid address and error occurs and his
silently ignored by the application without
the user noticing, will implement a
validation that will make an alert dialog appear on the screen if the address
to send is invalid. First, let's go to
the send Bitcoin test to create a test
for this scenario. Let's name it should not send
Bitcoin to invalid address. Let's add code to create a Wallet using the GUI
and the wind block. Let's wait for the
wallet creation. Now, let's copy and paste this code here to send
funds to the wallet. Let's create this
previous amount variable here and set its value to 0.1. Let's set one to the second range method called parameter. Now let's create a node address and generate a block for it. Let's click on the Send tab and call the send Bitcoin
method to try to send 0.01 Bitcoin to the
address to send variable. Since the address to send variable will contain
invalid addresses, we expect an alert
dialog window to appear containing a message saying the address to send is invalid. So let's create an error message variable with the
following message. Now, we'll use the lookup
method to get a component with that message and stored
in the node query variable. Finally, we'll click on the Okay button to close
the dialog window. Then block, let's add the following comparison
to check if the message in the alert dialog window is equal to the error
message variable. Let's define the address to send variable with some test
cases in the where block. Here, Let's pass
these test cases available on the Project
and Resources page. Each test case has an error. The different parts of
the code will detect. The first test case is a string with a non-existent prefix. The second case has a
correct reg test prefix, but a remaining invalid address. The next three addresses would be valid reg test addresses, but their last
characters were changed. During address construction. Part of the address uses the rest of the
address to be built. That part is the
address checksum and its function is to detect
address alterations. During address decoding, the
checksum is verified and we would expect an error to occur if this verification fails. The last three
addresses are valid, but they're not from the
reg test environment, so they must be
considered invalid. Let's go to the
script config finder. In the URLs throw method call, let's pass a lambda
whose body will return a new create transaction
exception with an invalid address constant from the error messages class. The create transaction exception is already caught later in the code and its message is already shown in an
alert dialog window. Let's create this constant
in the error messages class. It'll have the same text
we defined in the test. With this modification,
we expect the validation to cover
addresses with invalid prefixes. Now, let's go to the
transaction creators service. Let's wrap the code in the build output method
in a try-catch block. In the catch block will throw the create transaction exception passing the invalid
address constant. With this modification, we
expect the validation to cover errors from decoding addresses who's checksum
verification fails. Let's go to the
send Bitcoin test. Now, let's comment these
tests and run our new test. The last three
test cases failed. Our wallet succeeded in sending
funds to these addresses. This happened because
the address decoding discards the address prefixes. Of course, the transactions were sent only in the reg
test environment. Therefore, making these addresses
invalid is important to avoid situations where
you think you're running your wallet
in one environment, but it's actually
running in another. Let's fix this. Let's go to the
address matcher class. Let's create these
segment method. It'll return a
predicate of string. And it'll take an environment
enough as its parameter. Now, we'll create
a map object with environments as keys and
predicates of strings as values. We'll use the map of
method to create this map. For each environment will
set a Lambda to validate if the address parameter has the expected prefix
for that environment. For segue main net addresses, we expect that they don't
start with BCR and start with BC test net addresses. We'll start with TB and reg test ones. We'll start with BCR t. Then we'll return the map.get method call passing the environment
as its parameter. Now, let's duplicate this method and change its name to
his nested segment. In this case, may net addresses. We'll start with three tests, net and reg test addresses. We'll start with two. Let's duplicate this method
and change its name to his legacy main net addresses. We'll start with one. Test net and reg test addresses. We'll start with M or N. Now, let's remove these
Lambdas from this class. Now let's go to the address
configuration class. Let's add this Bitcoin
environment field. Let's add a value annotation
here to inject the value of this field using the Bitcoin environment application
dot properties setting. Now let's change the segment
parameter here to call it as a method and pass the Bitcoin environment field
as its parameter. Let's do the same to these
nested segment parameter here. Now, let's go to the script
configuration class. We'll do the same
modification we just did in the address
configuration class. Let's inject the Bitcoin
environment into this class and modify the
script config beans to call the proper
address matcher methods. Now, before running
our new test again, let's fix some tests. In the address sequential
generator test, we must set the
Bitcoin environment in the address configuration
instantiation like this. Let's run this test. Great, It's still passing. Now, let's do the same to the
create wallet service test. Okay, It's still passing. Let's do the same to the single random draw a coin
selector test. This time, we have to set the Bitcoin environment in
the script configuration to. Let's run it. Great,
It's still passing. Now, let's fix the transaction
creators service test. Okay, it's still working. Finally, let's do
the same fixed in the transaction size
calculator test. But this time we have to set the Bitcoin environments
to test net. Since we use test net
addresses in this test, grade, the tests have passed. Now let's go to the
send Bitcoin test. Let's comment these test
cases to run only the last three. Let's run it. Great, the tests have passed. Let's uncomment these tests. And let's fix this
test named letter. It send, not sent.
75. Creating a Progress Bar: In this video, we'll add a footer section to our
application's main window. The footer will contain a
progress bar and a text label. The progress bar
will be active when our wallet communicates
with our node, which can sometimes take awhile. The text label will show
a brief description of the activity our wallet is doing during that communication. So let's design that section
in the playground dot FXML. Let's go to the Scene Builder. First. Let's add an H box to the
bottom of the main window. Let's change the pref
width of the H box to 600 and the pref height to 19. Now let's add a progress
bar to the H box. Let's also add a label to it. We'll set the label
left margin to ten, and its text to an empty string. Now in the text editor, let's copy this code here and paste it into the
main window dot FXML. Now let's encapsulate
the progress bar and labeled components
into FXML files. First, let's create
the progress bar FXML. Let's delete this content. Let's create an ethics
root tag with a type pointing to the progress
bar Java FX class. Now let's copy the
attributes from the tag and the main window dot FXML and
paste them to our new FXML. Let's set this XML NSF X tag in the main window, FXML. Let's delete this progress
bar in its place. Let's add the progress
bar controller tag, which will create later. Now, let's create a footer dot FXML file to encapsulate
the label tag. Let's delete this code. Let's copy and paste
the label tag code from the main window FXML
to the new FXML. Let's change the label tag to an ethics root tag with a type pointing to the Java
FX labeled class. Let's add this XML NSF x
attribute to the new tag. Now let's add the missing
imports to this file. Back to the main window. Let's substitute the
copied label tag for the footer controller tag. Now let's create the progress bar controller
and imported here. Let's do the same with
the footer controller. Now, let's go to the
progress bar controller. Let's add the component
annotation to it. Let's use the Receive tab
controller constructor as a base to build the progress
bar controller constructor. Let's fix its name and value annotation to point
to the correct ones. And let's delete
the current wallet since we won't need it. Now, let's copy this constructor and paste it into the
footer controller. Let's fix the
constructor's name, and let's add the component
annotation to this class. Let's make the value annotation
point to the right FXML. Now let's make this class
extend the label class. And the progress bar
controller class will extend the
progress bar class. Let's go to the
GUI listener class and add both controllers to the set so they get configured
as custom components. Now, let's create an observable that will
model the progress state. It will be the async
progress class created in the
observables package. Let's add a component
annotation to it. To define the progress
bar state will create a double property
field called progress. Let's instantiate it here with a new simple double
property object using zero as its parameter. Later, the parameter zero, which sets the progress
bar state to inactive, will be passed to
the progress bar. Now let's create the
task description field. It'll be a string property and we'll define the
footer label text. Let's instantiate it using a
new simple string property. Let's create getters
for both properties. The IDE created two
getters for each field, one for the property and
the other for the value of the property will need only the getters
for the properties. So let's delete
the other getters. Now, let's create the
start method here. We'll use it to set
the progress value to the indeterminate
progress constant. The value of this
constant will make the progress bar show a bar
moving to the left and right, indicating that some task is
running in the background. Let's also define
the stop method, which will set the progress
value back to zero. So the progress bar
will be inactive, indicating that no task is
running in the background. Finally, let's create the
set task description method. It'll take a string
as its parameter. And it will set the
description parameter to the task description field. Now to control when the progress bar and
footer label change, we'll use an aspect. Let's create the
progress aspect class in the GUI dot services package. In Spring Boot. And aspect is a method modifier. We'll use an aspect to define that every
method annotated with a specific annotation will start the progress bar to find the
text and the footer label, execute the code
in the method and revert the state of the
progress bar and label. To do that, let's
add the aspect and component annotations
to this class. Let's inject the async progress observable into this class. Now let's create the activate
progress bar method. It'll return an object. Let's add the around
annotation to this method as its parameter will pass a string with
the text at annotation, open parenthesis,
activate progress bar, close parenthesis. This annotation defines
that the aspect will execute code before and
after it's modified methods. The activate progress
bar parameter, we'll define that the
aspect will modify methods annotated with an
annotation with that name. Now, let's add two
parameters to this method. The first will be the
preceding joint point, which will be a handle
for the annotated method. The second will be the
activate progress bar, which will give access to
the annotation parameters. So let's create this annotation, will create it in a new
GUI annotations package. To make it an annotation, we must modify this
keyword to an interface. Let's add the target
annotation to this file as its parameter
will pass the method constant. This will make the annotation
valid only in methods, not classes or fields. Let's also add the
retention annotation passing the runtime
constant as its parameter. This will make this
annotation active at runtime. Finally, let's add the string value field
to this annotation. This field will be populated by the parameter passed to
this annotation call. Back to the progress
aspect class. Here inside a platform run
later method call will pass a lambda whose body will call the async progress set
task description method, passing the activate
progress bar value as its parameter will also call the async progress
start method here. Now we'll call the preceding
join point proceed method and storage
returned object in the result variable. The proceed method will
make the annotated method run and
return its result. Now wrapped in a platform
run later method will change the states of the async progress fields again. But this time we'll set
the task description to an empty string and call the stop method on
the async progress. Finally, we'll return the result variable
containing the annotated method
returned object. Now let's go to the
progress bar controller. Let's inject the async progress observable into this class. Will bind the async
progresses progress field to this controllers
progress property in the initialized method. Let's go to the
footer controller. Let's inject the async progress observable into this class. In the initialized
method will bind the async progress
task description field to this controllers
text property. Now, for the progress
bar to work, we have to annotate the
methods we want to track the progress with the activate
progress bar annotation. Let's do it in the import
wallet service first. Let's pass the string
with the texts loading wallet as its parameter. Let's copy this annotation and paste it here
in the sign and send method of the sin
transaction service. Let's change its parameter
to sending transaction. Let's also add
this annotation to the update UTXOs service classes update method as its parameter. Let's pass the phrase
updating UTXOs. Now, let's run our node. And let's run the send Bitcoin legacy test to check
the progress bar in action. As we've seen, the
progress bar and footer label are
working as intended. Great.
76. Updating Dependencies: In this video, we'll update the dependencies of our project. Maintaining a
project's dependencies up-to-date is a good practice. By doing so, we
benefit from bugs and security vulnerability fixes
applied to our dependencies, maintaining our
application more secure. So let's go to the
palm dot xml file will increase the version of
our obsolete dependencies. Depending on when
you're updating them, newer versions may be available. Let's increase the Spring
Boot starter parent version. For me, the newest
version is 2.7. 0.5 will increase the
Java version to 19. Let's remove this
groovy version now, since it's not
necessary anymore, it's version will be
set automatically. Let's go to the project
structure settings. Let's update the
projects Java version. If you're needed, Java
version isn't available. You can click on add
SDK to download it. Let's set the project
language level to the latest available. Now, let's increase the
Java FX controls version. Let's do the same
with the JavaFX FXML. Now, let's update the
Java FX Maven plugin and the G Maven plugin
for correctness. Let's update the Bitcoin
Java version to 0.4, 0.4 in the Maven dependency
plug-in configuration. Now let's click on the
load Maven Changes button. Now let's update the Bitcoin
Core Node version to 23. Let's download it from the
Bitcoin core.org website. Let's download version 23 and install it in the same folder
as the previous version. I already did that, so I'll skip this part. Now. Make sure to set the reg
test environment flag in the bitcoin.com file. Before running our node, let's delete the reg test
folders inside the Bitcoin no data folder and our
application's database folder. We'll do that just to clean
our test environment. Now, let's run our node by
looking at the console. Notice that our node
is running version 23. Good. Now, we must do a
small modification for our wallet to work
correctly with the new Bitcoin
Core Node version. Let's go to the create wallet
RPC method documentation to understand it better. In Bitcoin Core version 23, the descriptors argument of the create wallet RPC
method defaults to true, while in the previous
version it was set to false. That makes our application
wallet unable to call some RPC methods
we use to fix it, will manually set it to false so that the change won't
affect us anymore. So let's go to the node
create wallet client. Let's add the following
parameters here. Let's add them to the create
wallet method's signature. Also, the disabled private
keys parameter will be a Boolean and define whether the created node wallet
will generate private keys. When set to true,
the blank parameter creates a node wallet
without seeds and keys. The passphrase sets and optional passphrase to
encrypt the NerdWallet. As the name suggests, the avoid reuse
parameter defines a policy to avoid address reuse. Finally, the descriptors
parameter defines whether the created node
wallet is a descriptor wallet. A descriptor wallet
is a new type of wallet in the Bitcoin
Core Node we won't use. Now, let's go to the node load
or create wallet service. Let's add these variables
to this method call. Now let's copy this code
and the node create wallet client and paste it here. Now let's go to the
wallet service. Let's add the following
values to this method call. Here, we'll set the
disabled private keys and blank parameters to true. Since our application,
while it won't require the node to create private
keys and seeds for us. The passphrase will
be an empty string. We'll set the avoid reuse
parameter to false, since our node will
generate addresses for us and the descriptors
parameter will be false. Now, let's go to
the GUI test class. In this method call, Let's pass the following values. Unlike the previous
case, this time, we want to generate private
keys and seeds because we'll use the node wallet to
send bitcoins in our tests. Therefore, the first two
parameters will be false. The third will be
an empty string. The avoid reuse parameter
will be set to true. So the node will avoid
reusing addresses for our test wallet and the descriptors
argument will be false. Now let's do one more modification
for some reason after updating our dependencies
when there are two okay buttons on the
screen during some tests, test FX ends up clicking
on the wrong one. To fix that, Let's go to
the alert error or service. Will add an Fx ID to the alert OK button
programmatically. To do it, Let's create
the ok. variable here. We'll use the lookup
method to obtain the button reference
and assign it to the ok. variable like this. Then we'll set the
ID to alert. Okay. Now let's go to the
create wallet test. In this test, instead of calling the click on method with
the okay parameter, let's pass a reference to the alert dialog Okay
button using its ID. Now, this test will unequivocally click on
the right okay button. Let's do the same with every other test with the
same problem in the project. Let's fix the one in
the import wallet test. Now, let's do it in the send
Bitcoin nested segue test. Finally, let's fix the tests
and the send Bitcoin test. Now, as an exercise, I'll leave it to the student to run all application tests.
77. Running our Wallet on the Main Net Environment: In this video, we'll
install and run our application on the
main net environment. Since we'll deal
with real money, it's highly recommended
that you guarantee that all the application
tests are passing. Also, it's advisable to start playing with
small Bitcoin amounts. First, let's update the Bitcoin Java
library to version 0.4, 0.5 in the POM file. This version fixes an
issue when importing the word list file when running the application
after installing it. Let's go to the mnemonic
seed service and remove this throws statements
since we no longer need it. Let's do the same to this create mnemonic seed method in the create wallet
dialogue controller. Now, let's run our node on
the main net environment. To do it, Let's go
to our bitcoin.com file and remove the reg
test configuration line, save the file and run
the Bitcoin node. Okay, now our node is running on the main
net environment. Here comes the sad part. It may take days
or weeks to sync our note on the main
net environment since, for the time being, it
has 436 gb in size. In my case, the
console indicates that the blockchain download
progress is about 52%. Later in this video, I'll show you the application running on my other machine, which has already
sync to completely. For now, let's modify our application to run it on
the main net environment. In the application
dot properties file, let's modify the
Bitcoin environment setting two main net. We'll assign the port 83 32
to the note RPC URI config. This is the default port
the Bitcoin node uses to expose its RPC API
on the main net. Next, let's set the crypt key in the spring data source URL
config to this environment variable that requires us to set this environment variable in our operating system before
we compile our code. By doing so, we increase the
security of our application, avoiding exposing the database
variables in our code. Let's do the same
with the values of the spring Data Source
username and password configs. Now let's generate secure
values for these configs. To do that, let's use the SQL DB manager will achieve that by calling the crypt key
function like this. Let's generate three values and copy and paste them
into a text file. It's important that
you keep these values in a safe place
since any change in these values before
compiling the application again will result in losing
access to the database. If that happens, you'll still
have access to your wallet. If you keep your wallets, mnemonic seed and password, but you'll have to delete
the database files and compile the
application again. Now, I'll paste these
environment variable values directly into the application
dot properties can fix. I'm doing that because at
least in my Windows machine, the application does
not interpolate these values with the
system environment. One's, the same problem
doesn't happen in Linux. So I think it's some kind of strange bug during compilation. Tell me in the
comments section if you experienced
the same problem. So we can find a solution
for this problem together. For now, we can move on with the application
dot properties is, is in the Maven tab. Let's right-click on
the Install button. Click on modify run
configuration. In the runner tab. Let's activate the skip tests checkbox so that
the Maven install command won't run the
application tests before installing
the application. If you're using newer
IntelliJ AID versions, this option may be available in the configuration
icon in this window. Click on the Okay button. Now, double-click on the
customized install option inside the Run
Configuration section. Great, the application
was successfully installed inside the indicated
folder in the console. Before running it,
make sure to install the Java SDK with the same
version as the application. I'll go to the Oracle website
to download and install it. After installing it, close
and open the terminal. Now, copy the path where your application installed
the application JAR file. You can use the command java
space double hyphen version to ensure your terminal runs
the correct Java version. Now run the command java space, hyphen jar space, the
path you just copied. Great, the application started successfully on the
main net environment. Let's try creating a new wallet. It successfully
created a new wallet, but since our node
is still sinking, the loading progress
bar would stay active for a long time before
we could test it. Now, I'll show the wallet working on the main
net environment in my Linux machine where the
node is 100% synchronized. So I've just installed the application the same
way we did for Windows. This shows a great
Java advantage, which is the smooth
cross-platform compatibility. Let's run the application. From now on, I'll
hide some data you'll see on the screen
for privacy reasons. First, I load a wallet. I've already created it correctly loaded as expected. Now, I'll create a new wallet. I'll annotate the
mnemonic seed using pen and paper before clicking
on the Okay button. Now, I'll copy the address and paste it into a text editor. Now, I'll load the
previous wallet, which has some funds in it. Now let's send some Satoshi's
to the address we copied from the addresses table. We can see that a new change
address received some funds. Great. In the transactions table, we see that a new
transaction with zero confirmation was created. Now let's open the wallet
that received funds. We can see that both
the addresses table and the transactions table have a new register indicating we've successfully received
some Satoshi's. Great, everything is
working correctly.
78. Extra class - Bitcoin Core v26: Welcome to our first
extra class on updating our application to communicate with Bitcoin core version 26. We'll see that there are
very good reasons for staying up to date with the
newest Bitcoin core version. First, let's visit this website to download Bitcoin
core version 26. Click on the appropriate version for your operating system. Since I'm using Windows, I'll download the file. Essentially, we'll
be overwriting the current version of
Bitcoin core that we have. Make sure to choose the same installation path
as our current one. In my case, I won't proceed with the installation since
I already have it. But for you, it should be as simple as clicking
the next button multiple times before running the newly installed version. Let's explore the immediate
benefits it offers, starting with Bitcoin
core version 25. We can see from its release
notes that it implements a new index that significantly
speeds up wallet Re scans. To opt in for this new filter, we simply add the
block filter equals one setting to our
Bitcoin config file. Upon restarting the node, it will begin building
this new index. It may take a few
hours to complete, but afterward we'll notice
that loading a wallet after some time without running our node becomes much faster. Now let's discuss some
changes introduced in version 26 release notes. We find that to use non
descriptor wallets, we need to set a deprecated RPC setting in our
Bitcoin config file. Subsequently, non
descriptor wallets will be deprecated in future core
releases. Don't worry. We'll demonstrate how to migrate our application to
descriptor wallets in the next extra class. For now, to ensure that
our current wallets will function with
Bitcoin core version 26, let's add this setting to
our Bitcoin config file. While at it, let's also
add the block filter equals one setting that
we discussed previously. Now let's run our
Bitcoin core node in the reg test environment. We're almost done, as always, with any significant change
we make in our application, we must run all our tests to ensure everything is
working correctly. I've already done that on my
end and found that one test stopped working when running against the new
Bitcoin core version. It turns out that the should
not send Bitcoin with the wrong password test from the send Bitcoin
test class failed. However, not because the transaction was
successfully sent, but because the
actual returned error differed from the expected one. I debugged it and
discovered that the error response
Bitcoin core sends in the new version now contains a different message,
which is this one. We simply remove the non from the beginning and add
failed at the end. Now the mapping between this
error response from core and our application should
be accurate. That's it. Now if you run our tests,
they should all pass. In the next extra class, we'll delve into
what a descriptor wallet is and how to adapt our application to
create this type of wallet. See you then.