Merge 2e06c0a637
into 201e7a1652
This commit is contained in:
commit
94cd44f1cc
|
@ -4,6 +4,7 @@ A blockchain implementation in Go, as described in these articles:
|
|||
|
||||
1. [Basic Prototype](https://jeiwan.cc/posts/building-blockchain-in-go-part-1/)
|
||||
2. [Proof-of-Work](https://jeiwan.cc/posts/building-blockchain-in-go-part-2/)
|
||||
2. [Persistence and CLI](https://jeiwan.cc/posts/building-blockchain-in-go-part-3/)
|
||||
3. [Transactions 1](https://jeiwan.cc/posts/building-blockchain-in-go-part-4/)
|
||||
3. [Addresses](https://jeiwan.cc/posts/building-blockchain-in-go-part-5/)
|
||||
3. [Persistence and CLI](https://jeiwan.cc/posts/building-blockchain-in-go-part-3/)
|
||||
4. [Transactions 1](https://jeiwan.cc/posts/building-blockchain-in-go-part-4/)
|
||||
5. [Addresses](https://jeiwan.cc/posts/building-blockchain-in-go-part-5/)
|
||||
6. [Transactions 2](https://jeiwan.cc/posts/building-blockchain-in-go-part-6/)
|
||||
|
|
10
block.go
10
block.go
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/gob"
|
||||
"log"
|
||||
"time"
|
||||
|
@ -36,15 +35,14 @@ func NewGenesisBlock(coinbase *Transaction) *Block {
|
|||
|
||||
// HashTransactions returns a hash of the transactions in the block
|
||||
func (b *Block) HashTransactions() []byte {
|
||||
var txHashes [][]byte
|
||||
var txHash [32]byte
|
||||
var transactions [][]byte
|
||||
|
||||
for _, tx := range b.Transactions {
|
||||
txHashes = append(txHashes, tx.Hash())
|
||||
transactions = append(transactions, tx.Serialize())
|
||||
}
|
||||
txHash = sha256.Sum256(bytes.Join(txHashes, []byte{}))
|
||||
mTree := NewMerkleTree(transactions)
|
||||
|
||||
return txHash[:]
|
||||
return mTree.RootNode.Data
|
||||
}
|
||||
|
||||
// Serialize serializes the block
|
||||
|
|
|
@ -68,7 +68,7 @@ func CreateBlockchain(address string) *Blockchain {
|
|||
}
|
||||
|
||||
// NewBlockchain creates a new Blockchain with genesis Block
|
||||
func NewBlockchain(address string) *Blockchain {
|
||||
func NewBlockchain() *Blockchain {
|
||||
if dbExists() == false {
|
||||
fmt.Println("No existing blockchain found. Create one first.")
|
||||
os.Exit(1)
|
||||
|
@ -95,31 +95,6 @@ func NewBlockchain(address string) *Blockchain {
|
|||
return &bc
|
||||
}
|
||||
|
||||
// FindSpendableOutputs finds and returns unspent outputs to reference in inputs
|
||||
func (bc *Blockchain) FindSpendableOutputs(pubKeyHash []byte, amount int) (int, map[string][]int) {
|
||||
unspentOutputs := make(map[string][]int)
|
||||
unspentTXs := bc.FindUnspentTransactions(pubKeyHash)
|
||||
accumulated := 0
|
||||
|
||||
Work:
|
||||
for _, tx := range unspentTXs {
|
||||
txID := hex.EncodeToString(tx.ID)
|
||||
|
||||
for outIdx, out := range tx.Vout {
|
||||
if out.IsLockedWithKey(pubKeyHash) && accumulated < amount {
|
||||
accumulated += out.Value
|
||||
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)
|
||||
|
||||
if accumulated >= amount {
|
||||
break Work
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return accumulated, unspentOutputs
|
||||
}
|
||||
|
||||
// FindTransaction finds a transaction by its ID
|
||||
func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) {
|
||||
bci := bc.Iterator()
|
||||
|
@ -141,9 +116,9 @@ func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) {
|
|||
return Transaction{}, errors.New("Transaction is not found")
|
||||
}
|
||||
|
||||
// FindUnspentTransactions returns a list of transactions containing unspent outputs
|
||||
func (bc *Blockchain) FindUnspentTransactions(pubKeyHash []byte) []Transaction {
|
||||
var unspentTXs []Transaction
|
||||
// FindUTXO finds all unspent transaction outputs and returns transactions with spent outputs removed
|
||||
func (bc *Blockchain) FindUTXO() map[string]TXOutputs {
|
||||
UTXO := make(map[string]TXOutputs)
|
||||
spentTXOs := make(map[string][]int)
|
||||
bci := bc.Iterator()
|
||||
|
||||
|
@ -164,43 +139,25 @@ func (bc *Blockchain) FindUnspentTransactions(pubKeyHash []byte) []Transaction {
|
|||
}
|
||||
}
|
||||
|
||||
if out.IsLockedWithKey(pubKeyHash) {
|
||||
unspentTXs = append(unspentTXs, *tx)
|
||||
}
|
||||
outs := UTXO[txID]
|
||||
outs.Outputs = append(outs.Outputs, out)
|
||||
UTXO[txID] = outs
|
||||
}
|
||||
|
||||
if tx.IsCoinbase() == false {
|
||||
for _, in := range tx.Vin {
|
||||
if in.UsesKey(pubKeyHash) {
|
||||
inTxID := hex.EncodeToString(in.Txid)
|
||||
spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(block.PrevBlockHash) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return unspentTXs
|
||||
}
|
||||
|
||||
// FindUTXO finds and returns all unspent transaction outputs
|
||||
func (bc *Blockchain) FindUTXO(pubKeyHash []byte) []TXOutput {
|
||||
var UTXOs []TXOutput
|
||||
unspentTransactions := bc.FindUnspentTransactions(pubKeyHash)
|
||||
|
||||
for _, tx := range unspentTransactions {
|
||||
for _, out := range tx.Vout {
|
||||
if out.IsLockedWithKey(pubKeyHash) {
|
||||
UTXOs = append(UTXOs, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return UTXOs
|
||||
return UTXO
|
||||
}
|
||||
|
||||
// Iterator returns a BlockchainIterat
|
||||
|
@ -211,7 +168,7 @@ func (bc *Blockchain) Iterator() *BlockchainIterator {
|
|||
}
|
||||
|
||||
// MineBlock mines a new block with the provided transactions
|
||||
func (bc *Blockchain) MineBlock(transactions []*Transaction) {
|
||||
func (bc *Blockchain) MineBlock(transactions []*Transaction) *Block {
|
||||
var lastHash []byte
|
||||
|
||||
for _, tx := range transactions {
|
||||
|
@ -251,6 +208,8 @@ func (bc *Blockchain) MineBlock(transactions []*Transaction) {
|
|||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
return newBlock
|
||||
}
|
||||
|
||||
// SignTransaction signs inputs of a Transaction
|
||||
|
@ -270,6 +229,10 @@ func (bc *Blockchain) SignTransaction(tx *Transaction, privKey ecdsa.PrivateKey)
|
|||
|
||||
// VerifyTransaction verifies transaction input signatures
|
||||
func (bc *Blockchain) VerifyTransaction(tx *Transaction) bool {
|
||||
if tx.IsCoinbase() {
|
||||
return true
|
||||
}
|
||||
|
||||
prevTXs := make(map[string]Transaction)
|
||||
|
||||
for _, vin := range tx.Vin {
|
||||
|
|
13
cli.go
13
cli.go
|
@ -17,6 +17,7 @@ func (cli *CLI) printUsage() {
|
|||
fmt.Println(" getbalance -address ADDRESS - Get balance of ADDRESS")
|
||||
fmt.Println(" listaddresses - Lists all addresses from the wallet file")
|
||||
fmt.Println(" printchain - Print all the blocks of the blockchain")
|
||||
fmt.Println(" reindexutxo - Rebuilds the UTXO set")
|
||||
fmt.Println(" send -from FROM -to TO -amount AMOUNT - Send AMOUNT of coins from FROM address to TO")
|
||||
}
|
||||
|
||||
|
@ -35,8 +36,9 @@ func (cli *CLI) Run() {
|
|||
createBlockchainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError)
|
||||
createWalletCmd := flag.NewFlagSet("createwallet", flag.ExitOnError)
|
||||
listAddressesCmd := flag.NewFlagSet("listaddresses", flag.ExitOnError)
|
||||
sendCmd := flag.NewFlagSet("send", flag.ExitOnError)
|
||||
printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError)
|
||||
reindexUTXOCmd := flag.NewFlagSet("reindexutxo", flag.ExitOnError)
|
||||
sendCmd := flag.NewFlagSet("send", flag.ExitOnError)
|
||||
|
||||
getBalanceAddress := getBalanceCmd.String("address", "", "The address to get balance for")
|
||||
createBlockchainAddress := createBlockchainCmd.String("address", "", "The address to send genesis block reward to")
|
||||
|
@ -75,6 +77,11 @@ func (cli *CLI) Run() {
|
|||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
case "reindexutxo":
|
||||
err := reindexUTXOCmd.Parse(os.Args[2:])
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
default:
|
||||
cli.printUsage()
|
||||
os.Exit(1)
|
||||
|
@ -108,6 +115,10 @@ func (cli *CLI) Run() {
|
|||
cli.printChain()
|
||||
}
|
||||
|
||||
if reindexUTXOCmd.Parsed() {
|
||||
cli.reindexUTXO()
|
||||
}
|
||||
|
||||
if sendCmd.Parsed() {
|
||||
if *sendFrom == "" || *sendTo == "" || *sendAmount <= 0 {
|
||||
sendCmd.Usage()
|
||||
|
|
|
@ -10,6 +10,10 @@ func (cli *CLI) createBlockchain(address string) {
|
|||
log.Panic("ERROR: Address is not valid")
|
||||
}
|
||||
bc := CreateBlockchain(address)
|
||||
bc.db.Close()
|
||||
defer bc.db.Close()
|
||||
|
||||
UTXOSet := UTXOSet{bc}
|
||||
UTXOSet.Reindex()
|
||||
|
||||
fmt.Println("Done!")
|
||||
}
|
||||
|
|
|
@ -9,13 +9,14 @@ func (cli *CLI) getBalance(address string) {
|
|||
if !ValidateAddress(address) {
|
||||
log.Panic("ERROR: Address is not valid")
|
||||
}
|
||||
bc := NewBlockchain(address)
|
||||
bc := NewBlockchain()
|
||||
UTXOSet := UTXOSet{bc}
|
||||
defer bc.db.Close()
|
||||
|
||||
balance := 0
|
||||
pubKeyHash := Base58Decode([]byte(address))
|
||||
pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-4]
|
||||
UTXOs := bc.FindUTXO(pubKeyHash)
|
||||
UTXOs := UTXOSet.FindUTXO(pubKeyHash)
|
||||
|
||||
for _, out := range UTXOs {
|
||||
balance += out.Value
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
func (cli *CLI) printChain() {
|
||||
bc := NewBlockchain("")
|
||||
bc := NewBlockchain()
|
||||
defer bc.db.Close()
|
||||
|
||||
bci := bc.Iterator()
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (cli *CLI) reindexUTXO() {
|
||||
bc := NewBlockchain()
|
||||
UTXOSet := UTXOSet{bc}
|
||||
UTXOSet.Reindex()
|
||||
|
||||
count := UTXOSet.CountTransactions()
|
||||
fmt.Printf("Done! There are %d transactions in the UTXO set.\n", count)
|
||||
}
|
11
cli_send.go
11
cli_send.go
|
@ -13,10 +13,15 @@ func (cli *CLI) send(from, to string, amount int) {
|
|||
log.Panic("ERROR: Recipient address is not valid")
|
||||
}
|
||||
|
||||
bc := NewBlockchain(from)
|
||||
bc := NewBlockchain()
|
||||
UTXOSet := UTXOSet{bc}
|
||||
defer bc.db.Close()
|
||||
|
||||
tx := NewUTXOTransaction(from, to, amount, bc)
|
||||
bc.MineBlock([]*Transaction{tx})
|
||||
tx := NewUTXOTransaction(from, to, amount, &UTXOSet)
|
||||
cbTx := NewCoinbaseTX(from, "")
|
||||
txs := []*Transaction{cbTx, tx}
|
||||
|
||||
newBlock := bc.MineBlock(txs)
|
||||
UTXOSet.Update(newBlock)
|
||||
fmt.Println("Success!")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
// MerkleTree represent a Merkle tree
|
||||
type MerkleTree struct {
|
||||
RootNode *MerkleNode
|
||||
}
|
||||
|
||||
// MerkleNode represent a Merkle tree node
|
||||
type MerkleNode struct {
|
||||
Left *MerkleNode
|
||||
Right *MerkleNode
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// NewMerkleTree creates a new Merkle tree from a sequence of data
|
||||
func NewMerkleTree(data [][]byte) *MerkleTree {
|
||||
var nodes []MerkleNode
|
||||
|
||||
if len(data)%2 != 0 {
|
||||
data = append(data, data[len(data)-1])
|
||||
}
|
||||
|
||||
for _, datum := range data {
|
||||
node := NewMerkleNode(nil, nil, datum)
|
||||
nodes = append(nodes, *node)
|
||||
}
|
||||
|
||||
for i := 0; i < len(data)/2; i++ {
|
||||
var newLevel []MerkleNode
|
||||
|
||||
for j := 0; j < len(nodes); j += 2 {
|
||||
node := NewMerkleNode(&nodes[j], &nodes[j+1], nil)
|
||||
newLevel = append(newLevel, *node)
|
||||
}
|
||||
|
||||
nodes = newLevel
|
||||
}
|
||||
|
||||
mTree := MerkleTree{&nodes[0]}
|
||||
|
||||
return &mTree
|
||||
}
|
||||
|
||||
// NewMerkleNode creates a new Merkle tree node
|
||||
func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode {
|
||||
mNode := MerkleNode{}
|
||||
|
||||
if left == nil && right == nil {
|
||||
hash := sha256.Sum256(data)
|
||||
mNode.Data = hash[:]
|
||||
} else {
|
||||
prevHashes := append(left.Data, right.Data...)
|
||||
hash := sha256.Sum256(prevHashes)
|
||||
mNode.Data = hash[:]
|
||||
}
|
||||
|
||||
mNode.Left = left
|
||||
mNode.Right = right
|
||||
|
||||
return &mNode
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewMerkleNode(t *testing.T) {
|
||||
data := [][]byte{
|
||||
[]byte("node1"),
|
||||
[]byte("node2"),
|
||||
[]byte("node3"),
|
||||
}
|
||||
|
||||
// Level 1
|
||||
|
||||
n1 := NewMerkleNode(nil, nil, data[0])
|
||||
n2 := NewMerkleNode(nil, nil, data[1])
|
||||
n3 := NewMerkleNode(nil, nil, data[2])
|
||||
n4 := NewMerkleNode(nil, nil, data[2])
|
||||
|
||||
// Level 2
|
||||
n5 := NewMerkleNode(n1, n2, nil)
|
||||
n6 := NewMerkleNode(n3, n4, nil)
|
||||
|
||||
// Level 3
|
||||
n7 := NewMerkleNode(n5, n6, nil)
|
||||
|
||||
assert.Equal(
|
||||
t,
|
||||
"64b04b718d8b7c5b6fd17f7ec221945c034cfce3be4118da33244966150c4bd4",
|
||||
hex.EncodeToString(n5.Data),
|
||||
"Level 1 hash 1 is correct",
|
||||
)
|
||||
assert.Equal(
|
||||
t,
|
||||
"08bd0d1426f87a78bfc2f0b13eccdf6f5b58dac6b37a7b9441c1a2fab415d76c",
|
||||
hex.EncodeToString(n6.Data),
|
||||
"Level 1 hash 2 is correct",
|
||||
)
|
||||
assert.Equal(
|
||||
t,
|
||||
"4e3e44e55926330ab6c31892f980f8bfd1a6e910ff1ebc3f778211377f35227e",
|
||||
hex.EncodeToString(n7.Data),
|
||||
"Root hash is correct",
|
||||
)
|
||||
}
|
||||
|
||||
func TestNewMerkleTree(t *testing.T) {
|
||||
data := [][]byte{
|
||||
[]byte("node1"),
|
||||
[]byte("node2"),
|
||||
[]byte("node3"),
|
||||
}
|
||||
// Level 1
|
||||
n1 := NewMerkleNode(nil, nil, data[0])
|
||||
n2 := NewMerkleNode(nil, nil, data[1])
|
||||
n3 := NewMerkleNode(nil, nil, data[2])
|
||||
n4 := NewMerkleNode(nil, nil, data[2])
|
||||
|
||||
// Level 2
|
||||
n5 := NewMerkleNode(n1, n2, nil)
|
||||
n6 := NewMerkleNode(n3, n4, nil)
|
||||
|
||||
// Level 3
|
||||
n7 := NewMerkleNode(n5, n6, nil)
|
||||
|
||||
rootHash := fmt.Sprintf("%x", n7.Data)
|
||||
mTree := NewMerkleTree(data)
|
||||
|
||||
assert.Equal(t, rootHash, fmt.Sprintf("%x", mTree.RootNode.Data), "Merkle tree root hash is correct")
|
||||
}
|
|
@ -12,7 +12,7 @@ var (
|
|||
maxNonce = math.MaxInt64
|
||||
)
|
||||
|
||||
const targetBits = 24
|
||||
const targetBits = 16
|
||||
|
||||
// ProofOfWork represents a proof-of-work
|
||||
type ProofOfWork struct {
|
||||
|
|
|
@ -173,7 +173,13 @@ func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool {
|
|||
// NewCoinbaseTX creates a new coinbase transaction
|
||||
func NewCoinbaseTX(to, data string) *Transaction {
|
||||
if data == "" {
|
||||
data = fmt.Sprintf("Reward to '%s'", to)
|
||||
randData := make([]byte, 20)
|
||||
_, err := rand.Read(randData)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
data = fmt.Sprintf("%x", randData)
|
||||
}
|
||||
|
||||
txin := TXInput{[]byte{}, -1, nil, []byte(data)}
|
||||
|
@ -185,7 +191,7 @@ func NewCoinbaseTX(to, data string) *Transaction {
|
|||
}
|
||||
|
||||
// NewUTXOTransaction creates a new transaction
|
||||
func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transaction {
|
||||
func NewUTXOTransaction(from, to string, amount int, UTXOSet *UTXOSet) *Transaction {
|
||||
var inputs []TXInput
|
||||
var outputs []TXOutput
|
||||
|
||||
|
@ -195,7 +201,7 @@ func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transactio
|
|||
}
|
||||
wallet := wallets.GetWallet(from)
|
||||
pubKeyHash := HashPubKey(wallet.PublicKey)
|
||||
acc, validOutputs := bc.FindSpendableOutputs(pubKeyHash, amount)
|
||||
acc, validOutputs := UTXOSet.FindSpendableOutputs(pubKeyHash, amount)
|
||||
|
||||
if acc < amount {
|
||||
log.Panic("ERROR: Not enough funds")
|
||||
|
@ -222,7 +228,7 @@ func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transactio
|
|||
|
||||
tx := Transaction{nil, inputs, outputs}
|
||||
tx.ID = tx.Hash()
|
||||
bc.SignTransaction(&tx, wallet.PrivateKey)
|
||||
UTXOSet.Blockchain.SignTransaction(&tx, wallet.PrivateKey)
|
||||
|
||||
return &tx
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package main
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"log"
|
||||
)
|
||||
|
||||
// TXOutput represents a transaction output
|
||||
type TXOutput struct {
|
||||
|
@ -27,3 +31,34 @@ func NewTXOutput(value int, address string) *TXOutput {
|
|||
|
||||
return txo
|
||||
}
|
||||
|
||||
// TXOutputs collects TXOutput
|
||||
type TXOutputs struct {
|
||||
Outputs []TXOutput
|
||||
}
|
||||
|
||||
// Serialize serializes TXOutputs
|
||||
func (outs TXOutputs) Serialize() []byte {
|
||||
var buff bytes.Buffer
|
||||
|
||||
enc := gob.NewEncoder(&buff)
|
||||
err := enc.Encode(outs)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
return buff.Bytes()
|
||||
}
|
||||
|
||||
// DeserializeOutputs deserializes TXOutputs
|
||||
func DeserializeOutputs(data []byte) TXOutputs {
|
||||
var outputs TXOutputs
|
||||
|
||||
dec := gob.NewDecoder(bytes.NewReader(data))
|
||||
err := dec.Decode(&outputs)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
return outputs
|
||||
}
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"log"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
)
|
||||
|
||||
const utxoBucket = "chainstate"
|
||||
|
||||
// UTXOSet represents UTXO set
|
||||
type UTXOSet struct {
|
||||
Blockchain *Blockchain
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// CountTransactions returns the number of transactions in the UTXO set
|
||||
func (u UTXOSet) CountTransactions() int {
|
||||
db := u.Blockchain.db
|
||||
counter := 0
|
||||
|
||||
err := db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(utxoBucket))
|
||||
c := b.Cursor()
|
||||
|
||||
for k, _ := c.First(); k != nil; k, _ = c.Next() {
|
||||
counter++
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
return counter
|
||||
}
|
||||
|
||||
// Reindex rebuilds the UTXO set
|
||||
func (u UTXOSet) Reindex() {
|
||||
db := u.Blockchain.db
|
||||
bucketName := []byte(utxoBucket)
|
||||
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
err := tx.DeleteBucket(bucketName)
|
||||
if err != nil && err != bolt.ErrBucketNotFound {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
_, err = tx.CreateBucket(bucketName)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
UTXO := u.Blockchain.FindUTXO()
|
||||
|
||||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(bucketName)
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
// Update updates the UTXO set with transactions from the Block
|
||||
// The Block is considered to be the tip of a blockchain
|
||||
func (u UTXOSet) Update(block *Block) {
|
||||
db := u.Blockchain.db
|
||||
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(utxoBucket))
|
||||
|
||||
for _, tx := range block.Transactions {
|
||||
if tx.IsCoinbase() == false {
|
||||
for _, vin := range tx.Vin {
|
||||
updatedOuts := TXOutputs{}
|
||||
outsBytes := b.Get(vin.Txid)
|
||||
outs := DeserializeOutputs(outsBytes)
|
||||
|
||||
for outIdx, out := range outs.Outputs {
|
||||
if outIdx != vin.Vout {
|
||||
updatedOuts.Outputs = append(updatedOuts.Outputs, out)
|
||||
}
|
||||
}
|
||||
|
||||
if len(updatedOuts.Outputs) == 0 {
|
||||
err := b.Delete(vin.Txid)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
} else {
|
||||
err := b.Put(vin.Txid, updatedOuts.Serialize())
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
newOutputs := TXOutputs{}
|
||||
for _, out := range tx.Vout {
|
||||
newOutputs.Outputs = append(newOutputs.Outputs, out)
|
||||
}
|
||||
|
||||
err := b.Put(tx.ID, newOutputs.Serialize())
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue