-
Notifications
You must be signed in to change notification settings - Fork 1
/
mining.go
107 lines (101 loc) · 3.07 KB
/
mining.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package main
import (
"database/sql"
"encoding/json"
"log"
"time"
)
var currentDifficulty = GenesisBlockDifficulty
func miningRig() {
log.Println("Starting the PoW miner...")
if *miningRewardAddress == "" {
if len(currentWallet.Keys) == 0 {
log.Println("No miningAddress specified and no keys in current wallet. Stopping the miner.")
return
}
*miningRewardAddress = currentWallet.Keys[0].Public
}
for {
var min, max sql.NullInt64
dbtx, err := db.Begin()
if err != nil {
log.Println(err)
continue
}
err = dbtx.QueryRow("SELECT MIN(id), MAX(id) FROM utx").Scan(&min, &max)
if err != nil {
log.Println("Mining block min/max error", err)
continue
}
if min.Valid && max.Valid {
lastHash := ""
lastHeight := 0
err = dbtx.QueryRow("SELECT hash, height FROM block ORDER BY height DESC LIMIT 1").Scan(&lastHash, &lastHeight)
if err != nil {
log.Println(err)
continue
}
err = mineBlock(dbtx, uint64(min.Int64), uint64(max.Int64), lastHeight, lastHash, *miningRewardAddress)
if err == nil {
_, err = dbtx.Exec("DELETE FROM utx WHERE id BETWEEN ? AND ?", min.Int64, max.Int64)
if err != nil {
log.Panic(err)
}
dbtx.Commit()
} else {
log.Println(err)
dbtx.Rollback()
}
}
time.Sleep(5 * time.Second)
}
}
func mineBlock(dbtx *sql.Tx, min, max uint64, prevHeight int, prevHash string, rewardAddress string) error {
newHeight := prevHeight + 1
block := BlockWithHeader{Block: Block{TimeUTC: time.Now().Unix(), PreviousBlockHash: prevHash, Transactions: []BlockTransaction{}}}
coinbaseReward := getCoinbaseAtHeight(newHeight)
var err error
for i := min; i <= max; i++ {
txData := ""
err = dbtx.QueryRow("SELECT tx FROM utx WHERE id=?", i).Scan(&txData)
if err != nil {
return err
}
btx := BlockTransaction{}
err = json.Unmarshal([]byte(txData), &btx)
if err != nil {
return err
}
tx, err := btx.VerifyBasics()
if err != nil {
return err
}
coinbaseReward += tx.MinerFeeAmount
block.Transactions = append(block.Transactions, btx)
}
coinbaseTx := Tx{Flags: []string{"coinbase"}, Version: CurrentTxVersion, Outputs: []TxOutput{TxOutput{PubKey: rewardAddress, Amount: coinbaseReward}}}
coinbaseTxData := jsonifyWhateverToBytes(coinbaseTx)
txHash := getTxHashStr(coinbaseTxData)
coinbaseBtx := BlockTransaction{TxHash: txHash, TxData: string(coinbaseTxData)}
block.Transactions = append([]BlockTransaction{coinbaseBtx}, block.Transactions...)
block.StateHash, err = dbSimStateHashStr(dbtx, block)
if err != nil {
if block.StateHash != "" {
// Assume this contains a tx hash of an invalid tx, and remove it
dbtx.Exec("DELETE FROM utx WHERE hash=?", block.StateHash)
}
return err
}
block.BlockHeader.Hash = block.Mine(currentDifficulty)
err = dataDirSaveBlock(block, newHeight)
if err != nil {
log.Fatal(err)
}
err = dbImportCheckedBlock(dbtx, block, newHeight, block.BlockHeader.Hash)
if err != nil {
dataDirDeleteBlock(block, newHeight)
return err
}
log.Printf("!! Mined block %s at %d, %d transaction(s)\n", block.BlockHeader.Hash, newHeight, max-min+1)
return nil
}