blockchain-poc/blockchain.go

122 lines
2.2 KiB
Go

package main
import (
"fmt"
"log"
"github.com/boltdb/bolt"
)
var dbFile = "blockchain.db"
const blocksBucket = "blocks"
type Blockchain struct {
tip []byte
db *bolt.DB
}
type BlockchainIterator struct {
currentHash []byte
db *bolt.DB
}
func NewBlockchain() *Blockchain {
var tip []byte
db, err := bolt.Open(dbFile, 0600, nil)
err = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
if b == nil {
fmt.Println("No existing blockchain found. Creating a new one...")
genesis := NewGenesisBlock()
b, err := tx.CreateBucket([]byte(blocksBucket))
if err != nil {
log.Panic(err)
}
err = b.Put(genesis.Hash, genesis.Serialize())
if err != nil {
log.Panic(err)
}
err = b.Put([]byte("1"), genesis.Hash)
if err != nil {
log.Panic(err)
}
tip = genesis.Hash
} else {
tip = b.Get([]byte("1"))
}
return nil
})
if err != nil {
log.Panic(err)
}
bc := Blockchain{tip, db}
return &bc
}
// NewGenesisBlock creates a new Genesis Block to start the blockchain
func NewGenesisBlock() *Block {
return NewBlock("Genesis Block", []byte{})
}
// AddBlock adds a block to the blockchain
func (bc *Blockchain) AddBlock(data string) {
var lastHash []byte
err := bc.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
lastHash = b.Get([]byte("1"))
return nil
})
if err != nil {
log.Panic(err)
}
newBlock := NewBlock(data, lastHash)
err = bc.db.Update(func(tx *bolt.Tx) error {
var err error
b := tx.Bucket([]byte(blocksBucket))
if err = b.Put(newBlock.Hash, newBlock.Serialize()); err != nil {
log.Panic(err)
}
if err = b.Put([]byte("1"), newBlock.Hash); err != nil {
log.Panic(err)
}
bc.tip = newBlock.Hash
return nil
})
if err != nil {
log.Panic(err)
}
}
func (bc *Blockchain) Iterator() *BlockchainIterator {
bci := &BlockchainIterator{bc.tip, bc.db}
return bci
}
func (bci *BlockchainIterator) Next() *Block {
var block *Block
err := bci.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
encodedBlock := b.Get(bci.currentHash)
block = DeserializeBlock(encodedBlock)
return nil
})
if err != nil {
log.Panic(err)
}
bci.currentHash = block.PrevBlockHash
return block
}