package main
import (
bolt "boltdb"
"fmt"
"os"
)
const dbFile = "blockChain.db"
const blockBucket = "bucket"
const lastHashKey = "key"
const genesisInfo = "YuanShuai will be a happy boy"
type BlockChain struct {
//blocks []*Block
db *bolt.DB //数据库操作句柄
tail []byte // 最后一个区块哈希值
}
//校验文件是否存在
func isDBExit() bool {
_, err := os.Stat(dbFile)
if os.IsNotExist(err) {
return false
}
return true
}
//创建blockChain数据库文件
func InitBlockChain(address string) *BlockChain {
if isDBExit() {
fmt.Println(" blockChain db exit already,shouldn't created")
os.Exit(1)
}
db, err := bolt.Open(dbFile, 0600, nil) //打开bolt数据库
CheckErr("InitBlockChain", err)
// (db *DB) Update(fn func(*Tx) error) error {
var lastHash []byte
db.Update(func(tx *bolt.Tx) error {
//没有bucket ,要去创建创世区块,将数据填写到数据库的bucket中
coinbase := NewCoinbaseTx(address, genesisInfo)
genesis := NewGenesisBlock(coinbase)
bucket, err := tx.CreateBucket([]byte(blockBucket))
CheckErr("InitBlockChain1", err)
//写入数据库操作
bucket.Put(genesis.Hash, genesis.Serialize())
CheckErr("InitBlockChain2", err)
bucket.Put([]byte(lastHashKey), genesis.Hash)
CheckErr("InitBlockChain3", err)
lastHash = genesis.Hash
return nil
})
return &BlockChain{db, lastHash}
}
func GetBlockChainHandler() *BlockChain {
if !isDBExit() {
fmt.Println("Pls create blockChain db")
os.Exit(1)
}
db, err := bolt.Open(dbFile, 0600, nil) //打开bolt数据库
CheckErr("NewBlockChain1", err)
var lastHash []byte
db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(blockBucket))
if bucket != nil {
//取出最后区块的哈希值
lastHash = bucket.Get([]byte(lastHashKey))
}
return nil
})
return &BlockChain{db, lastHash}
}
func (bc *BlockChain) AddBlock(txs []*Transaction) {
//preBlockHash := bc.blocks[len(bc.blocks)-1].Hash
//block := NewBlock(data ,preBlockHash)
//bc.blocks =append(bc.blocks,block)
var prevBlockHash []byte
bc.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(blockBucket))
if bucket == nil {
os.Exit(1)
}
prevBlockHash = bucket.Get([]byte(lastHashKey))
return nil
})
block := NewBlock(txs, prevBlockHash)
err := bc.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(blockBucket))
if bucket == nil {
os.Exit(1)
}
err := bucket.Put(block.Hash, block.Serialize())
CheckErr("AddBlock1", err)
err = bucket.Put([]byte(lastHashKey), block.Hash)
CheckErr("AddBlock2", err)
bc.tail = block.Hash
return nil
})
CheckErr("AddBlock3", err)
}
//迭代器编写,里面包含了一个游标,一直往前移动,完成整个容器的遍历
type BlockChainIterator struct {
currHash []byte
db *bolt.DB
}
//创建迭代器,初始化指向最后一个区块
func (bc *BlockChain) NewIterator() *BlockChainIterator {
return &BlockChainIterator{currHash: bc.tail, db: bc.db}
}
func (it *BlockChainIterator) Next() (block *Block) {
err := it.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(blockBucket))
if bucket == nil {
return nil
}
data := bucket.Get(it.currHash)
block = Deserialize(data)
it.currHash = block.PrevBlockHash
return nil
})
CheckErr("Next()", err)
return
}
//返回指定地址能否支配的UTXO的交易集合
func (bc *BlockChain) FindUTXOTransactions(address string) []Transaction{
var UTXOTransactions []Transaction //包含目标utxo的交易集合
//存储使用过的utxo的集合 map[交易id] []int64(0x1111111:0,1,都是给Alice转账)
spentUTXO := make(map[string][]int64)
it := bc.NewIterator()
for {
block := it.Next() //遍历区块
for _, tx := range block.Transactions { //遍历交易
//遍历input,找到已经消耗的utxo,把他们放到一个集合中
//需要两个子弹来标识使用的utxo, 交易ID 和 output 的索引
if !tx.IsCoinbase() {
for _, input := range tx.TXInputs {
if input.CanUnlockUTXOWith(address) {
//map[txid][]int64
spentUTXO[string(input.TXID)] = append(spentUTXO[string(input.TXID)], input.Vout)
}
}
}
OUTPUTS:
for currIndex, output := range tx.TXOutputs { //遍历output,找到所有可以支配的utxo
//检查当前的output是否已经被消耗,如果消耗,那么进行下一个output的校验
if spentUTXO[string(tx.TXID)] != nil { //非空,代表当前交易里有消耗的utxo
indexes := spentUTXO[string(tx.TXID)]
for _, index := range indexes {
//当前的索引和消耗的索引比较,相同,表明这个output肯定被消耗了,直接跳过,进行下一个output判断
if int64(currIndex) == index {
continue OUTPUTS
}
}
}
if output.CanBeUnlockedWith(address) { //如果当前地址是这个utxo的所有者,满足条件
UTXOTransactions = append(UTXOTransactions, *tx)
}
}
}
if len(block.PrevBlockHash) == 0 {
break
}
}
return UTXOTransactions
}
func (bc *BlockChain)FindUTXO(address string) []TXOutput{
var UTXOs []TXOutput
txs := bc.FindUTXOTransactions(address)
for _,tx := range txs{ //遍历交易
for _,utxo :=range tx.TXOutputs{ // 遍历output
if utxo.CanBeUnlockedWith(address){
UTXOs = append(UTXOs ,utxo)
}
}
}
return UTXOs
}
//validUTXOs,total = bc.FindSuitableUTXOs(from , amount)
func (bc *BlockChain)FindSuitableUTXOs(address string , amount float64)(map[string][]int64,float64){
txs := bc.FindUTXOTransactions(address)
validUTXOs :=make(map[string][]int64)
//遍历交易
var total float64
FIND:
for _,tx :=range txs{
outputs := tx.TXOutputs
//遍历outputs (utxo)
for index,output := range outputs{
if output.CanBeUnlockedWith(address){
if total <amount{ //判断当前收集的utxo的总金额是否大于需要花费的金额
total +=output.Value
validUTXOs[string(tx.TXID)] = append(validUTXOs[string(tx.TXID)],int64(index))
}else {
break FIND
}
}
}
}
return validUTXOs,total
}