2017-09-16 22:16:14 -05:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"log"
|
|
|
|
|
|
|
|
"github.com/boltdb/bolt"
|
|
|
|
)
|
|
|
|
|
|
|
|
const utxoBucket = "chainstate"
|
|
|
|
|
|
|
|
// UTXOSet represents UTXO set
|
2017-09-16 22:30:30 -05:00
|
|
|
type UTXOSet struct {
|
|
|
|
Blockchain *Blockchain
|
|
|
|
}
|
2017-09-16 22:16:14 -05:00
|
|
|
|
|
|
|
// Reindex rebuilds the UTXO set
|
2017-09-16 22:30:30 -05:00
|
|
|
func (u UTXOSet) Reindex() {
|
|
|
|
db := u.Blockchain.db
|
2017-09-16 22:16:14 -05:00
|
|
|
|
2017-09-16 22:30:30 -05:00
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
2017-09-16 22:16:14 -05:00
|
|
|
bucketName := []byte(utxoBucket)
|
|
|
|
b := tx.Bucket(bucketName)
|
|
|
|
|
|
|
|
if b != nil {
|
|
|
|
err := tx.DeleteBucket(bucketName)
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := tx.CreateBucket(bucketName)
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
|
2017-09-16 22:30:30 -05:00
|
|
|
UTXO := u.Blockchain.FindAllUTXO()
|
2017-09-16 22:16:14 -05:00
|
|
|
|
|
|
|
err = db.Update(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket([]byte(utxoBucket))
|
|
|
|
|
|
|
|
for txID, outs := range UTXO {
|
|
|
|
key, err := hex.DecodeString(txID)
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = b.Put(key, outs.Serialize())
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCount returns the number of transactions in the UTXO set
|
|
|
|
func (u UTXOSet) GetCount() int {
|
2017-09-16 22:30:30 -05:00
|
|
|
db := u.Blockchain.db
|
2017-09-16 22:16:14 -05:00
|
|
|
counter := 0
|
|
|
|
|
2017-09-16 22:30:30 -05:00
|
|
|
err := db.View(func(tx *bolt.Tx) error {
|
2017-09-16 22:16:14 -05:00
|
|
|
b := tx.Bucket([]byte(utxoBucket))
|
|
|
|
c := b.Cursor()
|
|
|
|
|
|
|
|
for k, _ := c.First(); k != nil; k, _ = c.Next() {
|
|
|
|
counter++
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
2017-09-16 22:30:30 -05:00
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
2017-09-16 22:16:14 -05:00
|
|
|
|
|
|
|
return counter
|
|
|
|
}
|
2017-09-16 23:01:18 -05:00
|
|
|
|
|
|
|
// FindUTXO finds UTXO for a public key hash
|
|
|
|
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) {
|
|
|
|
UTXOs = append(UTXOs, out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return UTXOs
|
|
|
|
}
|
2017-09-16 23:49:59 -05:00
|
|
|
|
|
|
|
// FindSpendableOutputs finds and returns unspent outputs to reference in inputs
|
|
|
|
func (u UTXOSet) FindSpendableOutputs(pubkeyHash []byte, amount int) (int, map[string][]int) {
|
|
|
|
unspentOutputs := make(map[string][]int)
|
|
|
|
accumulated := 0
|
|
|
|
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() {
|
|
|
|
txID := hex.EncodeToString(k)
|
|
|
|
outs := DeserializeOutputs(v)
|
|
|
|
|
|
|
|
for outIdx, out := range outs.Outputs {
|
|
|
|
if out.IsLockedWithKey(pubkeyHash) && accumulated < amount {
|
|
|
|
accumulated += out.Value
|
|
|
|
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return accumulated, unspentOutputs
|
|
|
|
}
|