forked from febe19/bazo-miner
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathstaterollback.go
219 lines (183 loc) · 7.17 KB
/
staterollback.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
212
213
214
215
216
217
218
219
package miner
import (
"github.com/julwil/bazo-miner/protocol"
"github.com/julwil/bazo-miner/storage"
"sort"
)
func accStateChangeRollback(txSlice []*protocol.AccTx) {
for _, tx := range txSlice {
if tx.Header == 0 || tx.Header == 1 || tx.Header == 2 {
accHash := protocol.SerializeHashContent(tx.PubKey)
acc, err := storage.GetAccount(accHash)
if err != nil {
logger.Fatal("CRITICAL: An account that should have been saved does not exist.")
}
delete(storage.State, accHash)
switch tx.Header {
case 1:
delete(storage.RootKeys, accHash)
case 2:
storage.RootKeys[accHash] = acc
}
}
}
}
func fundsStateChangeRollback(txSlice []*protocol.FundsTx) {
//Rollback in reverse order than original state change
for cnt := len(txSlice) - 1; cnt >= 0; cnt-- {
tx := txSlice[cnt]
accSender, _ := storage.GetAccount(tx.From)
accReceiver, _ := storage.GetAccount(tx.To)
accSender.TxCnt -= 1
accSender.Balance += tx.Amount
accReceiver.Balance -= tx.Amount
//If new coins were issued, revert
if rootAcc, _ := storage.GetRootAccount(tx.From); rootAcc != nil {
rootAcc.Balance -= tx.Amount
rootAcc.Balance -= tx.Fee
}
}
}
//This method does search historic blocks which do not have any transactions inside and now have to be reactivated
// because a transaction validated in this block was aggregated later in a block which now is rolled back. Thus all the
// transactions in this block need ot be reactivated such that they are visible in the chain.
func reactivateHistoricBlockDueToRollback(tx protocol.Transaction) {
var fundsTx *protocol.FundsTx
var aggTx *protocol.AggTx
var blockHash [32]byte
switch tx.(type) {
case *protocol.FundsTx:
fundsTx = tx.(*protocol.FundsTx)
fundsTx.Aggregated = false
storage.WriteClosedTx(fundsTx)
blockHash = fundsTx.Block
case *protocol.AggTx:
aggTx = tx.(*protocol.AggTx)
aggTx.Aggregated = false
storage.WriteClosedTx(aggTx)
blockHash = aggTx.Block
}
//Search block in bucket & Change aggregated to false.
block := storage.ReadClosedBlockWithoutTx(blockHash)
if block == nil {
//Block is in closed blocks but not aggregated --> only change transactions to aggregated = false what is done above.
return
}
//Search all transactions which were validated in the "empty" block. This may be very time consuming.
for _, tx := range storage.ReadAllClosedFundsAndAggTransactions() {
switch tx.(type) {
case *protocol.FundsTx:
FTX := tx.(*protocol.FundsTx)
if FTX.Block == blockHash {
//Reactivate this transaction --> it is still closed but it has to be visible in the block and chain again.
block.FundsTxData = append(block.FundsTxData, tx.Hash())
storage.WriteClosedTx(FTX)
}
case *protocol.AggTx:
ATX := tx.(*protocol.AggTx)
if ATX.Block == blockHash {
//Reactivate this transaction
block.AggTxData = append(block.AggTxData, tx.Hash())
storage.WriteClosedTx(ATX)
}
}
}
sort.Sort(ByHash(block.AggTxData))
//Write block back to bucket closed blocks with transactions.
block.Aggregated = false
storage.WriteClosedBlock(block)
storage.DeleteClosedBlockWithoutTx(blockHash)
logger.Printf("UPDATE: Rollback Write (%x) into closedBlockBucket as (%x)", block.HashWithoutTx[0:8], block.Hash[0:8])
}
type ByHash [][32]byte
func (a ByHash) Len() int { return len(a) }
func (a ByHash) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByHash) Less(i, j int) bool { return string(a[i][0:32]) < string(a[j][0:32]) }
func aggregatedStateRollback(txSlice []*protocol.AggTx, blockHash [32]byte, minerHash [32]byte) {
//Rollback in reverse order than original state change
var fundsTxSlice []*protocol.FundsTx
for cnt := len(txSlice) - 1; cnt >= 0; cnt-- {
tx := txSlice[cnt]
//Adding all Aggregated FundsTx in reverse order.
for _, txHash := range tx.AggregatedTxSlice {
trx := storage.ReadClosedTx(txHash)
switch trx.(type) {
case *protocol.FundsTx:
//Only rollback FundsTx which are validated in the current block.
if trx.(*protocol.FundsTx).Block == blockHash {
fundsTxSlice = append([]*protocol.FundsTx{trx.(*protocol.FundsTx)}, fundsTxSlice...)
} else {
//Transaction is not validated in the current block --> Need to be reactivated in the historic block.
reactivateHistoricBlockDueToRollback(trx)
}
case *protocol.AggTx:
//AggTx can only be historic at this point and therefore need to be reactivated in the historic block.
reactivateHistoricBlockDueToRollback(trx)
}
}
//do normal rollback for fundsTx And Fees
sort.Sort(ByTxCount(fundsTxSlice))
fundsStateChangeRollback(fundsTxSlice)
collectTxFeesRollback(nil, fundsTxSlice, nil, nil, minerHash)
fundsTxSlice = fundsTxSlice[:0]
}
}
func configStateChangeRollback(txSlice []*protocol.ConfigTx, blockHash [32]byte) {
if len(txSlice) == 0 {
return
}
//Only rollback if the config changes lead to a parameterChange
//there might be the case that the client is not running the latest version, it's still confirming
//the transaction but does not understand the ID and thus is not changing the state
if parameterSlice[len(parameterSlice)-1].BlockHash != blockHash {
return
}
//remove the latest entry in the parameters slice$
parameterSlice = parameterSlice[:len(parameterSlice)-1]
activeParameters = ¶meterSlice[len(parameterSlice)-1]
logger.Printf("Config parameters rolled back. New configuration: %v", *activeParameters)
}
func stakeStateChangeRollback(txSlice []*protocol.StakeTx) {
//Rollback in reverse order than original state change
for cnt := len(txSlice) - 1; cnt >= 0; cnt-- {
tx := txSlice[cnt]
accSender, _ := storage.GetAccount(tx.Account)
//Rolling back stakingBlockHeight not needed
accSender.IsStaking = !accSender.IsStaking
}
}
func collectTxFeesRollback(accTx []*protocol.AccTx, fundsTx []*protocol.FundsTx, configTx []*protocol.ConfigTx, stakeTx []*protocol.StakeTx, minerHash [32]byte) {
minerAcc, _ := storage.GetAccount(minerHash)
//Subtract fees from sender (check if that is allowed has already been done in the block validation)
for _, tx := range accTx {
//Money was created out of thin air, no need to write back
minerAcc.Balance -= tx.Fee
}
for _, tx := range fundsTx {
minerAcc.Balance -= tx.Fee
senderAcc, _ := storage.GetAccount(tx.From)
senderAcc.Balance += tx.Fee
}
for _, tx := range configTx {
//Money was created out of thin air, no need to write back
minerAcc.Balance -= tx.Fee
}
for _, tx := range stakeTx {
minerAcc.Balance -= tx.Fee
senderAcc, _ := storage.GetAccount(tx.Account)
senderAcc.Balance += tx.Fee
}
}
func collectBlockRewardRollback(reward uint64, minerHash [32]byte) {
minerAcc, _ := storage.GetAccount(minerHash)
minerAcc.Balance -= reward
}
func collectSlashRewardRollback(reward uint64, block *protocol.Block) {
if block.SlashedAddress != [32]byte{} || block.ConflictingBlockHash1 != [32]byte{} || block.ConflictingBlockHash2 != [32]byte{} || block.ConflictingBlockHashWithoutTx1 != [32]byte{} || block.ConflictingBlockHashWithoutTx2 != [32]byte{} {
minerAcc, _ := storage.GetAccount(block.Beneficiary)
slashedAcc, _ := storage.GetAccount(block.SlashedAddress)
minerAcc.Balance -= reward
slashedAcc.Balance += activeParameters.StakingMinimum
slashedAcc.IsStaking = true
}
}