Learning Blockchain with Go
Blockchain is a decentralized technology for storing data in a chain of linked blocks. It provides transparent and secure storage of digital records. For example, in banking, where modifying previous transactions is impermissible, blockchain can be used.
Block
A block is the fundamental unit of the blockchain. It stores information along with metadata such as timestamp, hash, and the hash of the previous block. Blocks can store any information, but in our case, this information will be text representing transaction details. Let’s create a simple structure in Go to represent a block:
type Block struct {
Timestamp int64
Data []byte
PrevHash []byte
Hash []byte
}
Note that a block’s hash is the checksum of the whole block, including the previous hash. Hashing is one of those things that makes blockchain transparent.
To create a new block, let’s define this helper function.
func NewBlock(data []byte, prevHash []byte) *Block {
block := &Block{
Timestamp: time.Now().Unix(),
Data: data,
PrevHash: prevHash,
Hash: []byte{},
}
return block
}
One of the main components of a blockchain is the hashing algorithm used to calculate the hash of each block. In our implementation, we use the SHA-256 algorithm to calculate the hash based on the block’s timestamp, data, and the previous block’s hash. Let’s define it:
func (b *Block) CalculateHash() []byte {
timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))
headers := bytes.Join([][]byte{b.PrevHash, b.Data, timestamp}, []byte{})
hash := sha256.Sum256(headers)
return hash[:]
}
Let’s also define this handy method to print a block.
func (b *Block) Print() {
fmt.Printf("PrevHash : %x\n", b.PrevHash)
fmt.Printf("Data : %s\n", b.Data)
fmt.Printf("Hash : %x\n", b.Hash)
}
Blockchain
Now, let’s move on to the blockchain itself. In Go, we represent a blockchain using a struct that contains a slice of blocks:
type Blockchain struct {
Blocks []*Block
}
To create a new blockchain, we use the NewBlockchain function:
func NewBlockchain() *Blockchain {
var genesisBlock = NewBlock([]byte("Genesis Block"), []byte{})
genesisBlock.Hash = genesisBlock.CalculateHash()
return &Blockchain{[]*Block{genesisBlock}}
}
This function creates a new blockchain with a genesis block, which is the first block in the chain.
To add a new block to the blockchain, we have the AddBlock method:
func (bc *Blockchain) AddBlock(data string) {
prevBlock := bc.Blocks[len(bc.Blocks)-1]
newBlock := NewBlock([]byte(data), prevBlock.Hash)
newBlock.Hash = newBlock.CalculateHash()
bc.Blocks = append(bc.Blocks, newBlock)
}
This method takes the data for the new block, gets the hash of the previous block, creates a new block, calculates its hash, and appends it to the blockchain.
One important aspect of blockchain is its ability to detect tampering. We can check if a blockchain is valid using the IsValid method:
func (bc *Blockchain) IsValid() bool {
for i := 1; i < len(bc.Blocks); i++ {
block := bc.Blocks[i]
prevBlock := bc.Blocks[i-1]
if !bytes.Equal(block.Hash, block.CalculateHash()) {
return false
}
if !bytes.Equal(block.PrevHash, prevBlock.Hash) {
return false
}
}
return true
}
This method goes through each block in the chain and verifies that the hashes and links between blocks are correct. If any inconsistency is found, it means the blockchain has been tampered with.
We need to define a method to print the entire blockchain as well:
func (bc *Blockchain) Print(label string) {
print("\n==============================================\n\n")
fmt.Printf("Label: %s\n", label)
fmt.Printf("Valid: %t\n", bc.IsValid())
println()
for _, block := range bc.Blocks {
block.Print()
println()
}
}
And that’s it for now. Let’s try to use these definitions in our code to see what blockchain is all about in more detail.
In this code example, we create a new blockchain, add some blocks representing transactions, and print the original blockchain. Then, we try to tamper with a block’s data and print the tampered blockchain. The IsValid method detects the tampering, showing that the blockchain is no longer valid.
func main() {
bc := NewBlockchain()
// Basic usage
bc.AddBlock("Steve sent $250 to Bob")
bc.AddBlock("Alex sent $30 to Barbara")
bc.Print("Original Blockchain") // valid = true
// Tampering
bc.Blocks[1].Data = []byte("Steve sent $1000 to Alex")
bc.Print("Tampered Blockchain") // valid = false
}
Blockchain has the potential to change many industries by providing a secure and transparent way of recording transactions.
In this blog post, we’ve explored a basic blockchain implementation in Go, learning how blocks are created, hashes are calculated, and the chain is validated. There’s much more to discover, such as consensus algorithms and smart contracts.
I hope this post has given you a good understanding of how blockchain works and how it can be implemented.