An Introduction to Ethereum and Brainy Contracts: Bitcoin & The Blockchain
An Introduction to Ethereum and Wise Contracts: Bitcoin & The Blockchain
Learn about Bitcoin and the genius behind the blockchain concept as we delve into Ethereum
Bitcoin took the world by surprise in the year two thousand nine and popularized the idea of decentralized secure monetary transactions. The concepts behind it, however, can be extended to much more than just digital currencies. Ethereum attempts to do that, marrying the power of decentralized transactions with a Turing-complete contract system. Read on as we explore how it works!
“Ethereum marries the power of decentralized transactions with Turing-complete contracts!”
This is part one of a three post series.
Introduction: Bitcoin and the Double-Spending Problem
In 2009, someone, under the alias of Satoshi Nakamoto, released this iconic Bitcoin whitepaper. Bitcoin was poised to solve a very specific problem: how can the double-spending problem be solved without a central authority acting as arbiter to each transaction?
To be fair, this problem had been in the minds of researchers for some time before Bitcoin was released. But where previous solutions were of research quality, Bitcoin succeeded in bringing a working, production ready design to the masses.
The earliest references to some of the concepts directly applied to Bitcoin are from the 1990s. In 2005, Nick Szabo, a computer scientist, introduced the concept of Bitgold, a precursor to Bitcoin, sharing many of its concepts. The similarities inbetween Bitgold and Bitcoin are sufficient that some people have speculated he might be Satoshi Nakamoto.
The double-spending problem is a specific case of transaction processing. Transactions, by definition, must either happen or not. Additionally, some (but not all) transactions must provide the ensure of happening before or after other transactions (in other words, they must be atomic). Atomicity gives rise to the notion of ordering: transactions either happen or not before or after other transactions. A lack of atomicity is precisely the problem of the double-spending problem: “spending”, or sending money from spender A to receiver B, must happen at a specific point in time, and before and after any other transactions. If this were not the case, it would be possible to spend money more than once in separate but simultaneous transactions.
When it comes to everyday monetary operations, transactions are usually arbitrated by banks. When a user logs-in to his or her home banking system and performs a wire transfer, it is the bank that makes sure any past and future operations are consistent. Albeit the process might seem elementary to outsiders, it is actually fairly an involved process with clearing procedures and settlement requirements. In fact, some of these procedures consider the chance of a double-spending situation and what to do in those cases. It should not come as a surprise that these fairly involved processes, resulting in considerable but seemingly unlikely to surmount delays, where the target of computer science researchers.
So, the main problem any transactional system appliead to finance must address is “how to order transactions when there is no central authority”. Furthermore, there can be no doubts as to whether the sequence of past transactions is valid. For a monetary system to succeed, there can be no way any parties can modify previous transactions. In other words, a “vetting process” for past transactions must also be in place. This is precisely what the blockchain system in Bitcoin was designed to address.
If you are interested in reading about systems that must reach consensus and the problems they face, the paper for The Byzantine Generals Problem is a good begin.
Albeit at this point the concept of what a blockchain is is murky, before getting into details about it, let’s go over the problems the blockchain attempts to address.
Public-key cryptography is a fine implement to deal with one of the problems: validating transactions. Public-key cryptography relies on the asymmetrical mathematical complexity of a very specific set of problems. The asymmetry in public-key cryptography is embodied in the existance of two keys: a public and a private key. These keys are used in tandem for specific purposes. In particular:
- Data encrypted with the public-key can only be decrypted by using the private-key.
- Data signed with the private-key can be verified using the public-key.
The private-key cannot be derived from the public-key, but the public-key can be derived from the private-key. The public-key is meant to be securely collective and can usually be loosely exposed to anyone.
Of interest for creating a verifiable set of transactions is the operation of signing data. Let’s see how a very ordinary transaction can be verified through the use of public-key cryptography.
Let’s say there is an account holder A who possesses fifty coins. These coins weere sent to him as part of a previous transaction. Account holder A now wants to send these coins to account holder B. B, and anybody else who wants to scrutinize this transaction, must be able to verify that it was actually A who sent the coins to B. Furthermore, they must be able to see B redeemed them, and noone else. Obviously, they should also be able to find the exact point in time, relative to other transactions, in which this transaction took place. However, at this point we cannot do this. We can, fortunately, do everything else.
For our ordinary example, let’s say the data in the transaction is just an identifier for the previous transaction (the one that gave A fifty coins in very first place), the public-key of the current possessor and the signature from the previous holder (confirming he or she sent those coins to A in very first place):
The number of coins of the current transaction is superfluous: it is simply the same amount as the previous transaction linked in it.
An interesting thing to note is that we have defined transactions IDs as simply the hash of their binary representation. In other words, a transaction ID is simply its hash (using an, at this point, unspecified hashing algorithm). This is convenient for several reasons we will explain later on. For now, it is just one possible way of doing things.
Let’s take the code apart and write it down step-by-step:
- A fresh transaction is constructed pointing to the previous transaction (the one that holds A’s fifty coins) and including B’s public signature (fresh transaction = old transaction ID plus receiver’s public key).
- A signature is produced using the fresh transaction and the previous transaction holder’s private key (A’s private key).
That’s it. The signature in the fresh transaction creates a verifiable link inbetween the fresh transaction and the old one. The fresh transaction points to the old one explicitly and the fresh transaction’s signature can only be generated by the holder of the private-key of the old transaction (the old transaction explicitly tells us who this is through the owner-pubkey field). So the old transaction holds the public-key of the one who can spend it, and the fresh transaction holds the public-key of the one who received it, along with the signature created with the spender’s private-key.
If this seems hard to seize at this point, think of it this way: it is all derived from this plain expression: data signed with the private-key can be verified using the public-key. There is nothing more to it. The spender simply signs data that says “I am the possessor of transaction ID XXX, I hereby send every coin in it to B”. B, and anybody else, can check that it was A, and only A, who wrote that. To do so, they need only access to A’s public-key, which is available in the transaction itself. It is mathematically assured that no key other than A’s private-key can be used in tandem with A’s public-key. So by simply having access to A’s public-key anyone can see it was A who sent money to B. This makes B the rightful possessor of that money. Of course, this is a simplification. There are two things we have not considered: who said those fifty coins where of A’s property (or, in other words, did A just take ownership of some random transaction, is he or she the rightful proprietor?) and when exactly did A send the coins to B (was it before or after other transactions?).
If you are interested in learning more about the math behind public-key cryptography, a plain introduction with code samples is available in chapter seven of The JWT Handbook.
Before getting into the matter of ordering, let’s very first tackle the problem of coin genesis. We assumed A was the rightful proprietor of the fifty coins in our example because the transaction that gave A his or her coins was simply modeled like any other transaction: it had A’s public-key in the possessor field, and it did point to a previous transaction. So, who gave those coins to A? What’s more, who gave the coins to that other person? We need only go after the transaction links. Each transaction points to the previous one in the chain, so where did those fifty coins come from? At some point that chain must end.
To understand how this works, it is best to consider an actual case, so let’s see how Bitcoin treats it. Coins in Bitcoin were and are created in two different ways. Very first there is the unique genesis block. The genesis block is a special, hardcoded transaction that points to no other previous transaction. It is the very first transaction in the system, has a specific amount of Bitcoins, and points to a public-key that belongs to Bitcoin creator Satoshi Nakamoto. Some of the coins in this transaction were sent to some addresses, but they never were indeed used that much. Most of the coins in Bitcoin come from another place: they are an incentive. As we will see in the next section about ordering transactions, the scheme employed to do this requires knots in the network to contribute work in the form of computations. To create an incentive for more knots to contribute computations, a certain amount of coins are awarded to contributing knots when they successfully finish a task. This incentive essentially results in special transactions that give birth to fresh coins. These transactions are also completes to links of transactions, as well as the genesis block. Each coin in Bitcoin can be traced to either one of these incentives or the genesis block. Many cryptocurrency systems adopt this model of coin genesis, each with its own nuances and requirements for coin creation. In Bitcoin, per design, as more coins get created, less coins are awarded as incentive. Eventually, coin creation will cease.
The thickest contribution Bitcoin brought to existing cryptocurrency schemes was a decentralized way to make transactions atomic. Before Bitcoin, researchers proposed different schemes to achieve this. One of those schemes was a ordinary voting system. To better understand the magic of Bitcoin’s treatment, it is better to explore these attempts.
In a voting system, each transaction gets broadcast by the knot performing it. So, to proceed with the example of A sending fifty coins to B, A prepares a fresh transaction pointing to the one that gave him or her those fifty coins, then puts B’s public-key in it and uses his or her own private-key (A’s) to sign it. This transaction is then sent to each knot known by A in the network. Let’s say that in addition to A and B, there are three other knots: C, D, E.
Now let’s imagine A is in fact a malicious knot. Albeit it emerges A wants to send B fifty coins, at the same time A broadcasts this transaction, it also broadcasts a different one: A sends those same fifty coins to C.
Note how previous-transaction-id points to the same transaction. A sends at the same time this transaction to different knots in the network. Who gets the fifty coins? Worse, if those fifty coins were sent in exchange for something, A might get goods from B and C albeit one of them won’t get the coins.
Since this is a distributed network, each knot should have some weight in the decision. Let’s consider the voting system mentioned before. Each knot should now cast a vote on whether to pick which transaction goes very first.
Each knot casts a vote and A to B gets picked as the transaction that should go very first. Obviously, this invalidates the A to C transaction that points to the same coins as A to B . It would emerge this solution works, but only superficially so. Let’s see why.
Very first, let’s consider the case A has colluded with some other knot. Did E cast a random vote or was it in some way motivated by A to pick one transaction over the other? There is no real way to determine this.
Secondly, our model does not consider the speed of propagation of transactions. In a reasonably large network of knots, some knots may see some transactions before others. This causes votes to be unbalanced. It is not possible to determine whether a future transaction might invalidate the ones that have arrived. Even more, it is not possible to determine whether the transaction that just arrived was made before or after some other transaction waiting for a vote. Unless transactions are seen by all knots, votes can be unfair. Worse, some knot could actively delay the propagation of a transaction.
Lastly, a malicious knot could inject invalid transactions to cause a targeted denial of service. This could be used to favor certain transactions over others.
Votes do not fix these problems because they are inherent to the design of the system. Whatever is used to favor one transaction over the other cannot be left to choice. As long as a single knot, or group of knots, can, in some way, favor some transactions over others, the system cannot work. It is precisely this element that made the design of cryptocurrencies such a hard endeavor. A stroke of genius was needed to overcome such a profound design issue.
The problem of malicious knots casting a vote in distributed systems is best known as The Byzantine Generals Problem. Albeit there is mathematical proof that this problem can be overcome as long as there is a certain ratio of non-malicious knots, this does not solve the problem for cryptocurrencies: knots are cheap to add. Therefore, a different solution is necessary.
Physics to the Rescue
Whatever system is used to ensure some transactions are preferred over others, no knot should be able to choose which of these are with 100% certainty. And there is only one way one can be sure this is the case: if it is a physical impossibility for the knot to be able to do this. Knots are cheap to add, so no matter how many knots a malicious user controls, it should still be hard for him or her to use this to his or her advantage.
The response is CPU power. What if ordering transactions required a certain amount of work, verifiable work, in such a way that it would be hard to perform originally, but cheap to verify. In a sense, cryptography works under the same principles: certain related operations are computationally infeasible to perform while others are cheap. Encrypting data is cheap next to brute-forcing the encryption key. Deriving the public-key from the private-key is cheap, while it is infeasible to do it the other way around. Hashing data is cheap, while finding a hash with a specific set of requirements (by modifying the input data) is not. And that is the main operation Bitcoin and other cryptocurrencies rely on to make sure no knot can get ahead of others, on average. Let’s see how this works.
Very first, let’s define what a block is. A block is simply a group of transactions. Inwards the block, these transactions are set in a specific order and fulfill the basic requirements of any transaction. In particular, an invalid transaction (such as one taking funds from an account with no funds) cannot be part of a block. In addition to the transactions, a block carries something called proof-of-work. The proof-of-work is data the permits any knot to verify that the one who created this block performed a considerable amount of computational work. In other words, no knot can create a valid block without performing an indefinite but considerable amount of work. We will see how this works later, but for now know that creating any block requires a certain amount of computing power and that any other knot can check that that power has been spent by whomever created the block.
Now let’s go back to our previous example of a malicious knot, A, double-spending fifty coins by attempting to create to two separate transactions at the same time, one sending money to B and the other to C. After A broadcasts both transactions to the network, every knot working on creating blocks (which may include A) pick a number of transactions and order them in whichever way they choose. These knots will note that two incompatible transactions are part of the same block and will discard one. They are free to pick which one to discard. After placing these transactions in the order they chose, each knot starts solving the puzzle of finding a hash for the block that fits the conditions set by the protocol. One ordinary condition could be “find a hash for this block with three leading zeroes”. To iterate over possible solutions for this problem, the block contains a special variable field known as the “nonce”. Each knot must iterate as many times as necessary until they find the nonce that creates a block with a hash that fits the conditions set by the protocol (three leading zeroes). Since each switch in the nonce basically results in a random output for a cryptographically secure hash function, finding the nonce is a game of chance and can only be sped up by enlargening computation power. Even then, a less powerful knot might find the right nonce before a more powerful knot, due to the randomness of the problem.
This creates an interesting script because even if A is a malicious knot and controls another knot (for example, E) any other knot on the network still has a chance of finding a different valid block. In other words, this scheme makes it hard for malicious knots to take control of the network.
Still, the case of a big number of malicious knots colluding and sharing CPU power must be considered. In fact, an entity controlling a majority of the knots (in terms of CPU power, not number) could exercise a double-spending attack by creating blocks swifter than other knots. Big enough networks rely on the difficulty of amassing CPU power. While in a voting system an attacker need only add knots to the network (which is effortless, as free access to the network is a design target), in a CPU power based scheme an attacker faces a physical limitation: getting access to more and more powerful hardware.
At last we can attempt a utter definition of what a blockchain is and how it works. A blockchain is a verifiable transaction database carrying an ordered list of all transactions that ever occurred. Transactions are stored in blocks. Block creation is a purposely computationally intensive task. The difficulty of creation of a valid block coerces anyone to spend a certain amount of work. This ensures malicious users in a big enough network cannot lightly outpass fair users. Each block in the network points to the previous block, effectively creating a chain. The longer a block has been in the blockchain (the further it is from the last block), the lesser the probability it can ever be liquidated from it. In other words, the older the block, the more secure it is.
One significant detail we left in previous paragraphs is what happens when two different knots find different but still valid blocks at the same time. In a sense, this looks like the same problem transactions had: which one to pick. In contrast with transactions, the proof-of-work system required for each block lets us find a convenient solution: since each block requires a certain amount of work, it is only natural that the only valid blockchain is the one with most blocks in it. Think about it: if the proof-of-work system works because each block requests a certain amount of work (and time), the longest set of valid blocks is the hardest to break. If a malicious knot or group of knots were to attempt to create a different set of valid blocks, by always picking the longest blockchain, they would always have to redo a fatter number of blocks (because each knot points to the previous one, switching one block coerces a switch in all blocks after it). This is also the reason malicious groups of knots need to control over 50% of the computational power of the network to actually carry any attack. Less than that, and the rest of the network will create a longer blockchain swifter.
Valid blocks that are valid but find their way into shorter forks of the blockchain are discarded if a longer version of the blockchain is computed by other knots. The transactions in the discarded blocks are sent again to the pool of transactions awaiting inclusion into future blocks. This causes fresh transactions to remain in an uncofirmed state until they find their way into the longest possible blockchain. Knots periodically receive newer versions of the blockchain from other knots.
It is entirely possible for the network to be forked if a reasonably large number of knots gets disconnected at the same time from another part of the network. If this happens, each fork will proceed creating blocks in isolation from the other. If the networks merge again in the future, the knots will compare the different versions of the blockchains and pick the longer one. The fork with the greater computational power will always win. If the fork were to be sustained for a long enough period of time, a big number of transactions would be undone when the merge took place. It is for this reason that forks are problematic.
Forks can also be caused by a switch in the protocol or the software running the knots. These switches can result in knots invalidating blocks that are considered valid by other knots. The effect is identical to a network-related fork.
Aside: a Perpetual Message System Using Webtasks and Bitcoin
Albeit we have not delved into the specifics of how Bitcoin or Ethereum treat transactions, there is a certain programmability built into them. Bitcoin permits for certain conditions to be specified in each transaction. If these conditions are met, the transaction can be spent. Ethereum, on the other palm, goes much further: a Turing-complete programming language is built into the system. We will concentrate on Ethereum in the next post in this series, but for now we will take a look at creative ways in which the concepts of the blockchain can be exploited for more than just sending money. For this, we will develop a plain perpetual message system on top of Bitcoin. How will it work?
We have seen the blockchain stores transactions that can be verified. Each transaction is signed by the one who can perform it and then broadcast to the network. It is then stored inwards a block after performing a proof-of-work. This means that any information embedded in the transaction is stored forever inwards the blockchain. The timestamp of the block serves as proof of the message’s date, and the proof-of-work process serves as proof of its immutable nature.
Bitcoin uses a scripting system that describes steps a user must perform to spend money. The most common script is simply “prove you are the holder of a certain private-key by signing this message with it”. This is known as the “pay to pubkey hash” script. In decompiled form it looks like:
Where <sig> and <pubKey> are provided by the spender and the rest is specified by the original sender of the money. This is simply a sequence of mixed data and operations. The interpreter for this script is a stack-based virtual machine. The details of execution are out of scope for this article, but you can find a nice summary at the Bitcoin Wiki. The significant take from this is that transactions can have data embedded in them in the scripts.
In fact, there exists a valid opcode for embedding data inwards a transaction: the OP_RETURN opcode. Whatever data goes after the OP_RETURN opcode is stored in the transaction. Of course, there is a limit for the amount of data permitted: 40-bytes. This is very little, but still certain interesting applications can be performed with such a little amount of storage. One of them is our perpetual message system. Another interesting use case is the “proof of existence” concept. By storing a hash of an asset in the blockchain, it serves as proof of its existence at the point it was added to a block. In fact, there already exists such a project. There is nothing preventing you from using our perpetual message system for a similar use. Yet other uses permit the system to prepare transactions that can only be spent after conditions are met, or when the spender provides proof of having a certain digital asset, of when a certain minimum number of users agree to spend it. Programmability opens up many possibilities and makes for yet another superb benefit of cryptocurrencies in contrast with traditional monetary systems.
Our system will work as an HTTP service. Data will we passed in JSON format as the bod of POST requests. The service will have three endpoints plus one for debugging.
The /fresh endpoint
It creates a fresh user using the username and password passed in. Sample bod:
The response is of the form:
The /address endpoint
Comes back the address for an existing user. Sample figure:
The response is identical to the /fresh endpoint.
The /message endpoint
Broadcasts a transaction to the Bitcoin network with the message stored in it. A fee is usually required for the network to accept the transaction (tho’ some knots may accept transactions with no fees). Messages can be at most thirty three bytes long. Sample figure:
The response is either a transaction id or an error message. Sample of a successful response:
Using the transaction id one can see the message stored in it. One can use any publicly available blockchain explorer to do this.
The /debugNew endpoint
Similar to the /fresh endpoint but permits one to create an user with an existing Bitcoin private key (and address). Sample assets:
The response is identical to the /fresh endpoint.
The most interesting endpoint is the one that builds and broadcasts the transaction ( /message ). We use the bitcore-lib and bitcore-explorers libraries to do this:
The code is fairly plain:
- Gets the unspent transactions for an address (i.e. the coins available, the balance).
- Build a fresh transaction using the unspent transactions as input.
- Point the transaction to a fresh, empty address. Assign zero coins to that address (do not send money unnecessarily).
- Set the fee.
- Set the address where the unspent money will get sent back (the switch address).
- Add our message.
- Broadcast the transaction.
Bitcoin requires transactions to be constructed using the money from previous transactions. That is, when coins are sent, it is not the origin address that is specified, rather it is the transactions pointing to that address that are included in a fresh transaction that points to a different destination address. From these transactions is subtracted the money that is then sent to the destination. In our case, we use these transactions to pay for the fee. Everything else gets sent back to our address.
Deploying the Example
Thanks to the power of Webtasks, deploying and using this code is a lump of cake. Very first clone the repository:
Now make sure you have the Webtask command-line implements installed:
If you haven’t done so, initialize your Webtask credentials (this is a one time process):
Now deploy the project:
Your project is now ready to test! Use CURL to attempt it out:
You now have to add some funds to your fresh Bitcoin address. If you are on Bitcoin’s testnet, you can simply use a faucet.
Faucets are Bitcoin websites that give free coins to addresses. These are effortless to get for the testnet. For the “livenet” you need to buy Bitcoins using a Bitcoin exchange.
Now send a message!
Now you can look at the transaction using a blockchain explorer and the transaction id. If you go down to the bottom of the page in the link before you will see our message with a prefix WTMSG: test . This will get stored in the blockchain forever.
Attempt it yourself! The webtask at https://wt-sebastian_peyrott-auth0_com-0.run.webtask.io/bitcoin-perpetual-message/ is live. You will need to create your own account and fund it, tho’.
You can also get the total code for this example and run it!
Blockchains enable distributed, verified transactions. At the same time they provide a creative solution to the double-spending problem. This has enabled the rise of cryptcurrencies, of which Bitcoin is the most popular example. Millions of dollars in Bitcoins are traded each day, and the trend is not providing any signs of slowing down. Bitcoin provides a limited set of operations to customize transactions. Still, many creative applications have appeared through the combination of blockchains and computations. Ethereum is the greatest example of these: marrying decentralized transactions with a Turing-complete execution environment. In the next post in the series we will take a closer look at how Ethereum differs from Bitcoin and how the concept of decentralized applications was brought to life by it.