package main import ( "log" "github.com/boltdb/bolt" ) const dbFile = "blockchain.db" const blocksBucket = "blocks" // Blockchain keeps a sequence of Blocks type Blockchain struct { tip []byte } // BlockchainIterator is used to iterate over blockchain blocks type BlockchainIterator struct { currentHash []byte } // AddBlock saves provided data as a block in the blockchain func (bc *Blockchain) AddBlock(data string) { var lastHash []byte db, err := bolt.Open(dbFile, 0600, nil) if err != nil { log.Panic(err) } defer db.Close() err = db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) lastHash = b.Get([]byte("l")) return nil }) newBlock := NewBlock(data, lastHash) err = db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) err := b.Put(newBlock.Hash, newBlock.Serialize()) if err != nil { log.Panic(err) } err = b.Put([]byte("l"), newBlock.Hash) if err != nil { log.Panic(err) } bc.tip = newBlock.Hash return nil }) } // Iterator ... func (bc *Blockchain) Iterator() *BlockchainIterator { bci := &BlockchainIterator{bc.tip} return bci } // Next returns next block starting from the tip func (i *BlockchainIterator) Next() *Block { var block *Block db, err := bolt.Open(dbFile, 0600, nil) if err != nil { log.Panic(err) } defer db.Close() err = db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) encodedBlock := b.Get(i.currentHash) block = DeserializeBlock(encodedBlock) return nil }) i.currentHash = block.PrevBlockHash return block } // NewBlockchain creates a new Blockchain with genesis Block func NewBlockchain() *Blockchain { bc := Blockchain{} db, err := bolt.Open(dbFile, 0600, nil) if err != nil { log.Panic(err) } defer db.Close() err = db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) if b == nil { 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("l"), genesis.Hash) if err != nil { log.Panic(err) } bc.tip = genesis.Hash } else { tip := b.Get([]byte("l")) bc.tip = tip } return nil }) if err != nil { log.Panic(err) } return &bc }