如何获取余额
我们输入一个比特币地址,然后获取对应比特币地址拥有者对应的pubkey,由该公钥检测utxo集,获取未花费交易,累加value,从而获取地址对应的余额
代码实现
func (cli *CLI) getBalance(address, nodeID string) {
if !ValidateAddress(address) {
log.Panic("ERROR: Address is not valid")
}
bc := NewBlockchain(nodeID)
UTXOSet := UTXOSet{bc}
defer bc.db.Close()
balance := 0
pubKeyHash := Base58Decode([]byte(address))
pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-4]
UTXOs := UTXOSet.FindUTXO(pubKeyHash)
for _, out := range UTXOs {
balance += out.Value
}
fmt.Printf("Balance of '%s': %d\n", address, balance)
}
type Blockchain struct {
tip []byte
Db *bolt.DB
}
type UTXOSet struct {
Blockchain *Blockchain
}
第一步ValidateAddress(address)
检测地址的有效性,详细代码解释见我的博客
第二步从数据库中获取区块链实例,我们将区块链存储在数据库中,如果你没有使用数据库存储,可以直接获取区块链实例,然后创建一个utxo集合
func NewBlockchain(nodeID string) *Blockchain {
dbFile := fmt.Sprintf(dbFile, nodeID)
if dbExists(dbFile) == false {
fmt.Println("No existing blockchain found. Create one first.")
os.Exit(1)
}
var tip []byte
db, err := bolt.Open(dbFile, 0600, nil)
if err != nil {
log.Panic(err)
}
err = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
tip = b.Get([]byte("l"))
return nil
})
if err != nil {
log.Panic(err)
}
bc := Blockchain{tip, db}
return &bc
}
第三步将地址变为公钥的hash
该图片是由公钥变化为地址的示意图,从而解释代码意图
pubKeyHash := Base58Decode([]byte(address)) //解码
pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-4] //去掉版本号和校验码
第四步是找到所有的UTXO
func (u UTXOSet) FindUTXO(pubKeyHash []byte) []TXOutput {
var UTXOs []TXOutput
db := u.Blockchain.db
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(utxoBucket))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
outs := DeserializeOutputs(v)
for _, out := range outs.Outputs {
if out.IsLockedWithKey(pubKeyHash) { //如果公钥hash能够解锁UTXO集合中的交易输出,则该交易内部的值属于该地址
UTXOs = append(UTXOs, out) //添加到UTXOS中
}
}
}
return nil
})
if err != nil {
log.Panic(err)
}
return UTXOs
}
func (out *TXOutput) IsLockedWithKey(pubKeyHash []byte) bool {
return bytes.Compare(out.PubKeyHash, pubKeyHash) == 0
}
最后将输出后的UTXOs进行遍历,将余额进行累加,就能够找到余额