How to make a cryptocurrency, part 2: Your first blockchain system 03-18-2018, 12:53 AM
#1
How to make a cryptocurrency, part 2: Your first blockchain system
It took me a while to decide not to shove everything in one thread, but here I am. In this tutorial, we're going start to make a simple blockchain implementation. In the next tutorial(s), we'll tun it into a full cryptocurrency. I expect that you have read and fully understand part 1, if not, then go do so, I'll wait. (https://sinister.li/Thread-How-to-make-a...blockchain)
Getting started:
Alright, we're going to have a simple one-node system, which we'll later expand to a multiple node currency or some other blockchain application. I'm going to write this in Go, it's a language I really like (and really have issues with), but remember that you should know C and an ASM before higher languages. Eventually, we'll add networking with multiple nodes, and node-discovery.
Here's the directory structure that I chose:
Code:
BlockCoin/
├──── client/
| └── main.go
└──── node/
└── main.goIn this thread we'll only be focusing on the node/main.go file.
Let's get to the code:
Alright, let's get to the code. Let's start with how every Go program starts:
Code:
package main
import (
"fmt"
)
/*
Dear Rob Pike,
Go is a decent language, but for the sake of us 'Gophers', please use a better form of error handling
Please don't kill me,
Ender
*/
func Handle(err error) {
if err != nil {
panic(err)
}
}
func main() {
}I'll just give you the import list right now:
Code:
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"fmt"
"math/big"
"time"
)Ok, now let's add some basic stuff... The block struct, the blockchain type, and the targetBits const:
Code:
const targetBits = 21 //Max of 256, the lower the harder. 21 is both a prime number, and a triangular number!
type Block struct {
Timestamp uint32 // Current timestamp
Data []byte
PrevHash []byte // The hash of the last block
BlockHash []byte // The hash of this block
Nonce uint32 // The nonce
}
type Blockchain []*BlockAlright, now we want to be able to simply mine blocks with one function call in main(), so let's write our main function to do that:
Code:
func main() {
chain := NewBlockchain()
chain.AddBlock("Here is some test data.")
chain.AddBlock("Later, we'll use transaction data instead")
}Alright, so now we have a good idea of how we want the interface to work. Let's start by writing the NewBlockchain() function:
Code:
/* An unneeded function that creates a new blockchain with a Genesis block.
I have no idea why I did this */
func NewBlockchain() Blockchain {
return Blockchain{ GenesisBlock() }
}Yeah... I'm generally against unnecessary functions, but I did it like this anyway...
Let's make the GenisisBlock() function:
Code:
//Creates a new Genesis block
func GenesisBlock() *Block {
return NewBlock( "GENISIS", []byte{} ) //creates a new block containing the data "GENISIS"
}It's good practice to comment your functions.
This is where we actually start getting into the details. NewBlock() is going to fill a Block struct with the data "GENISIS", an empty PrevHash, and it'll generate the rest of the data. First it should fill the block with what data we have, then call a function MineBlock() to get the other data, set the nonce in the block to the nonce returned, and return the block. I'm gonna start adding spoilers here so you can try on your own and check your work against mine:
Spoiler:
//Creates a new block
func NewBlock(data string, prevHash []byte) *Block {
block := &Block{ uint32(time.Now().Unix()), []byte(data), prevHash, []byte{}, 0 }
hash, nonce := MineBlock(block)
block.BlockHash = hash
block.Nonce = nonce
return block
}
func NewBlock(data string, prevHash []byte) *Block {
block := &Block{ uint32(time.Now().Unix()), []byte(data), prevHash, []byte{}, 0 }
hash, nonce := MineBlock(block)
block.BlockHash = hash
block.Nonce = nonce
return block
}
I wish SL had syntax highlighting and tab support, but then again, SL isn't a text editor...
Alright, now for the big one, let's implement the mining function MineBlock()
I'm going to show you this comment, and I want you to try it yourself after reading it:
Code:
/*
MineBlock(block *Block) ([]byte, uint32) -- Mines a block.
----------------------------------------------------------
Alright, let's go through the algorithm here:
First, we create a new bigInt with a value of 1,
then we left-shift that by 256 minus the target bits.
The reason for this, is that we can't have the target be larger than 256 bits,
or it will be larger than the SHA256 hash.
We then convert targetBits to a byte slice, and put it in targetBitsArray.
After this, we join all the block data into a 2d byte array,
then we begin the mining process.
In mining, we start the nonce at 0, then move up.
We hash the data, then check if the hash is less than the target.
If not, we increment the nonce, and repeat the same process all over again.
Once the hash is less than the target, we return the hash and
nonce.
*/Ok, you've tried it? I'm going to go through this one step by step:
Spoiler:
I think you've figured this part out:
Now for the creation of the target:
Getting everything into a slice:
Variable declarations:
Setting of the nonce, hashing the data, and setting hashInt to that:
Time to print a mining message, and jump into the main mining loop:
Now... There's a bug in the piece of code above. I want you to find it, although it is a tough one, and you probably won't think of it unless you have some experience with C or another lower-level lang...
I'll leave it here in a spoiler, but I highly recommend that you try to figure it out.
Alright, let's let the miner know that he's making money, and then let's return the hash (as a slice), and the nonce:
If you did this yourself and are reading through this, you probably noticed that I used a function ToBytes()...
Here's the code for that:
My whole MineBlock() function looks like this, yours should be somewhat different though:
Note that it's a bit redundant, that'll be for you to fix.
Code:
func MineBlock(block *Block) ([]byte, uint32) {Now for the creation of the target:
Code:
target := big.NewInt(1)
target.Lsh(target, uint(256-targetBits)) //The SHA256 hash should take 256 bits of memory, we left-shift by the size of target
targetBitsArray := ToBytes(targetBits)Getting everything into a slice:
Code:
data := bytes.Join(
[][]byte{
ToBytes(block.Timestamp),
block.Data,
block.PrevHash,
targetBitsArray,
ToBytes(uint32(block.Nonce)),
},
[]byte{},
)Variable declarations:
Code:
var hashInt big.Int
var hash [32]byte
var nonce uint32Setting of the nonce, hashing the data, and setting hashInt to that:
Code:
nonce = 0
hash = sha256.Sum256(data)
hashInt.SetBytes(hash[:]) //we need it as a slice, so we have [:]Time to print a mining message, and jump into the main mining loop:
Code:
fmt.Printf("Mining block containing '%s'\n", block.Data)
for hashInt.Cmp(target) != -1 {
nonce++
block.Nonce = nonce
data := bytes.Join(
[][]byte{
ToBytes(block.Timestamp),
block.Data,
block.PrevHash,
targetBitsArray,
ToBytes(uint32(block.Nonce)),
},
[]byte{},
)
hash = sha256.Sum256(data)
hashInt.SetBytes(hash[:])
}Now... There's a bug in the piece of code above. I want you to find it, although it is a tough one, and you probably won't think of it unless you have some experience with C or another lower-level lang...
I'll leave it here in a spoiler, but I highly recommend that you try to figure it out.
Spoiler: bug
It is possible for the nonce to overflow, since it is limited by the max uint32 size. I'm going to leave it up to you to solve it.
Alright, let's let the miner know that he's making money, and then let's return the hash (as a slice), and the nonce:
Code:
fmt.Println("Mined!")
return hash[:], nonce
}If you did this yourself and are reading through this, you probably noticed that I used a function ToBytes()...
Here's the code for that:
Code:
//Converts a uint32 to a byte array
func ToBytes(num uint32) []byte {
bs := make([]byte, 4)
binary.LittleEndian.PutUint32(bs, num)
return bs
}My whole MineBlock() function looks like this, yours should be somewhat different though:
Code:
func MineBlock(block *Block) ([]byte, uint32) {
target := big.NewInt(1)
target.Lsh(target, uint(256-targetBits)) //The SHA256 hash should take 256 bits of memory, we left-shift by the size of target
targetBitsArray := ToBytes(targetBits)
data := bytes.Join(
[][]byte{
ToBytes(block.Timestamp),
block.Data,
block.PrevHash,
targetBitsArray,
ToBytes(uint32(block.Nonce)),
},
[]byte{},
)
var hashInt big.Int
var hash [32]byte
var nonce uint32
nonce = 0
hash = sha256.Sum256(data)
hashInt.SetBytes(hash[:]) //we need it as a slice, so we have [:]
fmt.Printf("Mining block containing '%s'\n", block.Data)
for hashInt.Cmp(target) != -1 {
nonce++
block.Nonce = nonce
data := bytes.Join(
[][]byte{
ToBytes(block.Timestamp),
block.Data,
block.PrevHash,
targetBitsArray,
ToBytes(uint32(block.Nonce)),
},
[]byte{},
)
hash = sha256.Sum256(data)
hashInt.SetBytes(hash[:])
}
fmt.Println("Mined!")
return hash[:], nonce
}Note that it's a bit redundant, that'll be for you to fix.
Now, all we need is the AddBlock() function.
It should just get the hash of the last block, and use that to call NewBlock().
You should figure this one out yourself, but I'll still leave my code here.
Spoiler:
Did I mention to do it yourself first?
Spoiler:
Code:
func (bc Blockchain) AddBlock(data string) {
previousBlock := bc[ len(bc)-1 ]
newBlock := NewBlock(data, previousBlock.BlockHash)
bc = append(bc, newBlock)
}Conclusion:
You should now know how to implement a basic blockchain. I suggest that you try to make something like a token/ticket system, or an entire cryptocurrency from this. Or even just try to get it networked across multiple nodes.
Anyway, I'll be making more of these in the future, so keep an eye out for them.
Everything here is licensed under a modified Beerware license. I basically switched 'beer' for 'drink', and that's about it: (I'm a minor, so there's that)
Code:
/*
* ----------------------------------------------------------------------------
* "ENDER'S DRINK-WARE LICENSE":
* Ender wrote these files. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a drink in return.
* ----------------------------------------------------------------------------
*/Also thanks to Ciisco for helping me debug this
Don't forget to post below with questions or comments!
(This post was last modified: 07-22-2019, 09:01 AM by Blink.)











![[+]](https://sinister.li/images/modern/collapse_collapsed.png)