This repository has been archived by the owner on Aug 27, 2022. It is now read-only.
/
accept.go
349 lines (317 loc) · 10.1 KB
/
accept.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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
// Copyright (c) 2017-2018 The qitmeer developers
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"fmt"
"github.com/Qitmeer/qitmeer/core/blockchain/token"
"github.com/Qitmeer/qitmeer/core/blockdag"
"github.com/Qitmeer/qitmeer/core/types"
"github.com/Qitmeer/qitmeer/database"
"github.com/Qitmeer/qitmeer/engine/txscript"
"github.com/Qitmeer/qitmeer/params"
"math"
"time"
)
// checkCoinbaseUniqueHeight checks to ensure that for all blocks height > 1 the
// coinbase contains the height encoding to make coinbase hash collisions
// impossible.
func checkCoinbaseUniqueHeight(blockHeight uint64, block *types.SerializedBlock) error {
// check height
serializedHeight, err := ExtractCoinbaseHeight(block.Block().Transactions[0])
if err != nil {
return err
}
if uint64(serializedHeight) != blockHeight {
str := fmt.Sprintf("the coinbase signature script serialized "+
"block height is %d when %d was expected",
serializedHeight, blockHeight)
return ruleError(ErrCoinbaseHeight, str)
}
return nil
}
// IsFinalizedTransaction determines whether or not a transaction is finalized.
func IsFinalizedTransaction(tx *types.Tx, blockHeight uint64, blockTime time.Time) bool {
// Lock time of zero means the transaction is finalized.
msgTx := tx.Transaction()
lockTime := msgTx.LockTime
if lockTime == 0 {
return true
}
// The lock time field of a transaction is either a block height at
// which the transaction is finalized or a timestamp depending on if the
// value is before the txscript.LockTimeThreshold. When it is under the
// threshold it is a block height.
var blockTimeOrHeight int64
if lockTime < txscript.LockTimeThreshold {
//TODO, remove the type conversion
blockTimeOrHeight = int64(blockHeight)
} else {
blockTimeOrHeight = blockTime.Unix()
}
if int64(lockTime) < blockTimeOrHeight {
return true
}
// At this point, the transaction's lock time hasn't occurred yet, but
// the transaction might still be finalized if the sequence number
// for all transaction inputs is maxed out.
for _, txIn := range msgTx.TxIn {
if txIn.Sequence != math.MaxUint32 {
return false
}
}
return true
}
// maybeAcceptBlock potentially accepts a block into the block chain and, if
// accepted, returns the length of the fork the block extended. It performs
// several validation checks which depend on its position within the block chain
// before adding it. The block is expected to have already gone through
// ProcessBlock before calling this function with it. In the case the block
// extends the best chain or is now the tip of the best chain due to causing a
// reorganize, the fork length will be 0.
//
// The flags are also passed to checkBlockContext and connectBestChain. See
// their documentation for how the flags modify their behavior.
//
// This function MUST be called with the chain state lock held (for writes).
func (b *BlockChain) maybeAcceptBlock(block *types.SerializedBlock, flags BehaviorFlags) error {
// This function should never be called with orphan blocks or the
// genesis block.
b.ChainLock()
defer func() {
b.ChainUnlock()
b.flushNotifications()
}()
newNode := NewBlockNode(&block.Block().Header, block.Block().Parents)
mainParent := b.bd.GetMainParentByHashs(block.Block().Parents)
if mainParent == nil {
return fmt.Errorf("Can't find main parent\n")
}
// The block must pass all of the validation rules which depend on the
// position of the block within the block chain.
err := b.checkBlockContext(block, mainParent, flags)
if err != nil {
return err
}
// Prune stake nodes which are no longer needed before creating a new
// node.
b.pruner.pruneChainIfNeeded()
//dag
newOrders, oldOrders, ib, isMainChainTipChange := b.bd.AddBlock(newNode)
if newOrders == nil || newOrders.Len() == 0 || ib == nil {
return fmt.Errorf("Irreparable error![%s]\n", newNode.GetHash().String())
}
block.SetOrder(uint64(ib.GetOrder()))
block.SetHeight(ib.GetHeight())
// Insert the block into the database if it's not already there. Even
// though it is possible the block will ultimately fail to connect, it
// has already passed all proof-of-work and validity tests which means
// it would be prohibitively expensive for an attacker to fill up the
// disk with a bunch of blocks that fail to connect. This is necessary
// since it allows block download to be decoupled from the much more
// expensive connection logic. It also has some other nice properties
// such as making blocks that never become part of the main chain or
// blocks that fail to connect available for further analysis.
//
// Also, store the associated block index entry.
err = b.db.Update(func(dbTx database.Tx) error {
if err := dbMaybeStoreBlock(dbTx, block); err != nil {
return err
}
return nil
})
if err != nil {
panic(err.Error())
}
// Connect the passed block to the chain while respecting proper chain
// selection according to the chain with the most proof of work. This
// also handles validation of the transaction scripts.
_, err = b.connectDagChain(ib, block, newOrders, oldOrders)
if err != nil {
log.Warn(fmt.Sprintf("%s", err))
}
err = b.updateBestState(ib, block, newOrders)
if err != nil {
panic(err.Error())
}
// Notify the caller that the new block was accepted into the block
// chain. The caller would typically want to react by relaying the
// inventory to other peers.
b.sendNotification(BlockAccepted, &BlockAcceptedNotifyData{
IsMainChainTipChange: isMainChainTipChange,
Block: block,
Flags: flags,
})
return nil
}
func (b *BlockChain) FastAcceptBlock(block *types.SerializedBlock, flags BehaviorFlags) error {
b.ChainLock()
defer func() {
b.ChainUnlock()
b.flushNotifications()
}()
newNode := NewBlockNode(&block.Block().Header, block.Block().Parents)
fastAdd := flags&BFFastAdd == BFFastAdd
if !fastAdd {
mainParent := b.bd.GetMainParentByHashs(block.Block().Parents)
if mainParent == nil {
return fmt.Errorf("Can't find main parent\n")
}
// The block must pass all of the validation rules which depend on the
// position of the block within the block chain.
err := b.checkBlockContext(block, mainParent, flags)
if err != nil {
return err
}
}
//dag
newOrders, oldOrders, ib, _ := b.bd.AddBlock(newNode)
if newOrders == nil || newOrders.Len() == 0 || ib == nil {
return fmt.Errorf("Irreparable error![%s]\n", newNode.GetHash().String())
}
block.SetOrder(uint64(ib.GetOrder()))
block.SetHeight(ib.GetHeight())
err := b.db.Update(func(dbTx database.Tx) error {
if err := dbMaybeStoreBlock(dbTx, block); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
_, err = b.connectDagChain(ib, block, newOrders, oldOrders)
if err != nil {
log.Warn(fmt.Sprintf("%s", err))
}
return b.updateBestState(ib, block, newOrders)
}
func (b *BlockChain) updateTokenState(node blockdag.IBlock, block *types.SerializedBlock, rollback bool) error {
if rollback {
if uint32(node.GetID()) == b.TokenTipID {
state := b.GetTokenState(b.TokenTipID)
if state != nil {
err := b.db.Update(func(dbTx database.Tx) error {
return token.DBRemoveTokenState(dbTx, uint32(node.GetID()))
})
if err != nil {
return err
}
b.TokenTipID = state.PrevStateID
}
}
return nil
}
updates := []token.ITokenUpdate{}
for _, tx := range block.Transactions() {
if tx.IsDuplicate {
log.Trace(fmt.Sprintf("updateTokenBalance skip duplicate tx %v", tx.Hash()))
continue
}
if types.IsTokenTx(tx.Tx) {
update, err := token.NewUpdateFromTx(tx.Tx)
if err != nil {
return err
}
updates = append(updates, update)
}
}
if len(updates) <= 0 {
return nil
}
state := b.GetTokenState(b.TokenTipID)
if state == nil {
state = &token.TokenState{PrevStateID: uint32(blockdag.MaxId), Updates: updates}
} else {
state.PrevStateID = b.TokenTipID
state.Updates = updates
}
err := state.Update()
if err != nil {
return err
}
err = b.db.Update(func(dbTx database.Tx) error {
return token.DBPutTokenState(dbTx, uint32(node.GetID()), state)
})
if err != nil {
return err
}
b.TokenTipID = uint32(node.GetID())
return state.Commit()
}
func (b *BlockChain) GetTokenState(bid uint32) *token.TokenState {
var state *token.TokenState
err := b.db.View(func(dbTx database.Tx) error {
ts, err := token.DBFetchTokenState(dbTx, bid)
if err != nil {
return err
}
state = ts
return nil
})
if err != nil {
log.Error(err.Error())
return nil
}
return state
}
func (b *BlockChain) GetCurTokenState() *token.TokenState {
b.ChainRLock()
defer b.ChainRUnlock()
return b.GetTokenState(b.TokenTipID)
}
func (b *BlockChain) GetCurTokenOwners(coinId types.CoinID) ([]byte, error) {
b.ChainRLock()
defer b.ChainRUnlock()
state := b.GetTokenState(b.TokenTipID)
if state == nil {
return nil, fmt.Errorf("Token state error\n")
}
tt, ok := state.Types[coinId]
if !ok {
return nil, fmt.Errorf("It doesn't exist: Coin id (%d)\n", coinId)
}
return tt.Owners, nil
}
func (b *BlockChain) CheckTokenState(block *types.SerializedBlock) error {
updates := []token.ITokenUpdate{}
for _, tx := range block.Transactions() {
if tx.IsDuplicate {
log.Trace(fmt.Sprintf("updateTokenBalance skip duplicate tx %v", tx.Hash()))
continue
}
if types.IsTokenTx(tx.Tx) {
update, err := token.NewUpdateFromTx(tx.Tx)
if err != nil {
return err
}
updates = append(updates, update)
}
}
if len(updates) <= 0 {
return nil
}
state := b.GetTokenState(b.TokenTipID)
if state == nil {
state = &token.TokenState{PrevStateID: uint32(blockdag.MaxId), Updates: updates}
} else {
state.PrevStateID = b.TokenTipID
state.Updates = updates
}
return state.Update()
}
func (b *BlockChain) IsValidTxType(tt types.TxType) bool {
txTypesCfg := types.StdTxs
ok, err := b.isDeploymentActive(params.DeploymentToken)
if err == nil && ok && len(types.NonStdTxs) > 0 {
txTypesCfg = append(txTypesCfg, types.NonStdTxs...)
}
for _, txt := range txTypesCfg {
if txt == tt {
return true
}
}
return false
}