/
staketree.go
211 lines (181 loc) · 6 KB
/
staketree.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
package txhelpers
import (
"fmt"
"os"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/database"
_ "github.com/decred/dcrd/database/ffldb" // init the ffldb driver
"github.com/decred/dcrd/dcrutil"
"github.com/decred/dcrd/rpcclient"
"github.com/decred/dcrd/wire"
)
const (
// dbType is the database backend type to use
dbType = "ffldb"
// DefaultStakeDbName is the default database name
DefaultStakeDbName = "ffldb_stake"
)
// TODO: maybe kill this function, but definitely make a new one that is a stake
// tree update with a single block or chunk of blocks. This does NOT SCALE!
// BuildStakeTree returns a database with a stake tree
func BuildStakeTree(blocks map[int64]*dcrutil.Block, netParams *chaincfg.Params,
nodeClient *rpcclient.Client, poolRequiredHeight int64, DBName ...string) (database.DB, []int64, error) {
if blocks[0] == nil || blocks[0].Height() != 0 {
return nil, nil, fmt.Errorf("Must start at height 0")
}
height := int64(len(blocks) - 1)
// Create a new database to store the accepted stake node data into.
dbName := DefaultStakeDbName
if len(DBName) > 0 {
dbName = DBName[0]
}
_ = os.RemoveAll(dbName)
db, err := database.Create(dbType, dbName, netParams.Net)
if err != nil {
return db, nil, fmt.Errorf("error creating db: %v", err)
}
//defer db.Close()
// Load the genesis block
var bestNode *stake.Node
err = db.Update(func(dbTx database.Tx) error {
var errLocal error
bestNode, errLocal = stake.InitDatabaseState(dbTx, netParams)
return errLocal
})
if err != nil {
db.Close()
return nil, nil, err
}
poolValues := make([]int64, height+1)
// a ticket treap would be nice, but a map will do for a cache
liveTicketMap := make(map[chainhash.Hash]int64)
err = db.Update(func(dbTx database.Tx) error {
for i := int64(1); i <= height; i++ {
block := blocks[i]
//header := &block.MsgBlock().Header
numLive := bestNode.PoolSize()
// if int(header.PoolSize) != numLive {
// fmt.Printf("bad number of live tickets: want %v, got %v (%v)\n",
// header.PoolSize, numLive, numLive-int(header.PoolSize))
// }
// if header.FinalState != bestNode.FinalState() {
// fmt.Printf("bad final state: want %x, got %x\n",
// header.FinalState, bestNode.FinalState())
// }
if i >= poolRequiredHeight {
liveTickets := bestNode.LiveTickets()
var amt int64
for _, hash := range liveTickets {
val, ok := liveTicketMap[hash]
if !ok {
tx, err := nodeClient.GetRawTransaction(&hash)
if err != nil {
fmt.Printf("Unable to get transaction %v: %v\n", hash, err)
continue
}
// This isn't quite right for pool tickets where the small
// pool fees are included in vout[0], but it's close.
val = tx.MsgTx().TxOut[0].Value
liveTicketMap[hash] = val
}
amt += val
}
poolValues[i] = amt
}
if i%100 == 0 {
fmt.Printf("%d (%d, %d)\n", i, len(liveTicketMap), numLive)
}
var ticketsToAdd []chainhash.Hash
if i >= netParams.StakeEnabledHeight {
matureHeight := (i - int64(netParams.TicketMaturity))
ticketsToAdd, _ = TicketsInBlock(blocks[matureHeight])
}
spentTickets := TicketsSpentInBlock(block)
for i := range spentTickets {
delete(liveTicketMap, spentTickets[i])
}
revokedTickets := RevokedTicketsInBlock(block)
for i := range revokedTickets {
delete(liveTicketMap, revokedTickets[i])
}
hB, errx := block.BlockHeaderBytes()
if errx != nil {
return fmt.Errorf("unable to serialize block header: %v", errx)
}
bestNode, err = bestNode.ConnectNode(stake.CalcHash256PRNGIV(hB),
spentTickets, revokedTickets, ticketsToAdd)
if err != nil {
return fmt.Errorf("couldn't connect node: %v", err.Error())
}
// Write the new node to db.
err = stake.WriteConnectedBestNode(dbTx, bestNode, *block.Hash())
if err != nil {
return fmt.Errorf("failure writing the best node: %v",
err.Error())
}
}
return nil
})
return db, poolValues, err
}
/// kang
// TicketsInBlock finds all the new tickets in the block.
func TicketsInBlock(bl *dcrutil.Block) ([]chainhash.Hash, []*wire.MsgTx) {
tickets := make([]chainhash.Hash, 0)
ticketsMsgTx := make([]*wire.MsgTx, 0)
for _, stx := range bl.STransactions() {
if stake.DetermineTxType(stx.MsgTx()) == stake.TxTypeSStx {
h := stx.Hash()
tickets = append(tickets, *h)
ticketsMsgTx = append(ticketsMsgTx, stx.MsgTx())
}
}
return tickets, ticketsMsgTx
}
// TicketTxnsInBlock finds all the new tickets in the block.
func TicketTxnsInBlock(bl *dcrutil.Block) ([]chainhash.Hash, []*dcrutil.Tx) {
tickets := make([]chainhash.Hash, 0)
ticketTxns := make([]*dcrutil.Tx, 0)
for _, stx := range bl.STransactions() {
if stake.DetermineTxType(stx.MsgTx()) == stake.TxTypeSStx {
h := stx.Hash()
tickets = append(tickets, *h)
ticketTxns = append(ticketTxns, stx)
}
}
return tickets, ticketTxns
}
// TicketsSpentInBlock finds all the tickets spent in the block.
func TicketsSpentInBlock(bl *dcrutil.Block) []chainhash.Hash {
tickets := make([]chainhash.Hash, 0)
for _, stx := range bl.STransactions() {
if stake.DetermineTxType(stx.MsgTx()) == stake.TxTypeSSGen {
// Hash of the original STtx
tickets = append(tickets, stx.MsgTx().TxIn[1].PreviousOutPoint.Hash)
}
}
return tickets
}
// VotesInBlock finds all the votes in the block.
func VotesInBlock(bl *dcrutil.Block) []chainhash.Hash {
votes := make([]chainhash.Hash, 0)
for _, stx := range bl.STransactions() {
if stake.DetermineTxType(stx.MsgTx()) == stake.TxTypeSSGen {
h := stx.Hash()
votes = append(votes, *h)
}
}
return votes
}
// RevokedTicketsInBlock finds all the revoked tickets in the block.
func RevokedTicketsInBlock(bl *dcrutil.Block) []chainhash.Hash {
tickets := make([]chainhash.Hash, 0)
for _, stx := range bl.STransactions() {
if stake.DetermineTxType(stx.MsgTx()) == stake.TxTypeSSRtx {
tickets = append(tickets, stx.MsgTx().TxIn[0].PreviousOutPoint.Hash)
}
}
return tickets
}