-
Notifications
You must be signed in to change notification settings - Fork 290
/
headercmt.go
156 lines (140 loc) · 5.53 KB
/
headercmt.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
// Copyright (c) 2019-2021 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/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/database/v3"
"github.com/decred/dcrd/dcrutil/v4"
"github.com/decred/dcrd/gcs/v3"
"github.com/decred/dcrd/wire"
)
const (
// HeaderCmtFilterIndex is the proof index for the filter header commitment.
HeaderCmtFilterIndex = 0
)
// headerCommitmentData houses information the block header commits to via the
// commitment root.
type headerCommitmentData struct {
filter *gcs.FilterV2
}
// CalcCommitmentRootV1 calculates and returns the required v1 block commitment
// root from the filter hash it commits to.
//
// This function is safe for concurrent access.
func CalcCommitmentRootV1(filterHash chainhash.Hash) chainhash.Hash {
// NOTE: The commitment root is actually the merkle root of a merkle tree
// whose leaves are each of the individual commitments. However, since
// there is only currently a single commitment, the merkle root will simply
// be the hash of the sole item, so there is no point in doing extra work.
//
// The callers could certainly simply avoid calling this function and do the
// same thing directly, however, providing versioned functions helps make
// it clear exactly what each header commitment version commits to and makes
// the code more consistent with multiple versions.
return filterHash
}
// FetchUtxoViewParentTemplate loads utxo details from the point of view of just
// having connected the given block, which must be a block template that
// connects to the parent of the tip of the main chain. In other words, the
// given block must be a sibling of the current tip of the main chain.
//
// This should typically only be used by mining code when it is unable to
// generate a template that extends the current tip due to being unable to
// acquire the minimum required number of votes to extend it.
//
// This function is safe for concurrent access however the returned view is NOT.
func (b *BlockChain) FetchUtxoViewParentTemplate(block *wire.MsgBlock) (*UtxoViewpoint, error) {
b.chainLock.Lock()
defer b.chainLock.Unlock()
// The block template must build off the parent of the current tip of the
// main chain.
tip := b.bestChain.Tip()
if tip.parent == nil {
str := fmt.Sprintf("unable to fetch utxos for non-existent parent of "+
"the current tip %s", tip.hash)
return nil, ruleError(ErrInvalidTemplateParent, str)
}
parentHash := block.Header.PrevBlock
if parentHash != tip.parent.hash {
str := fmt.Sprintf("previous block must be the parent of the current "+
"chain tip %s, but got %s", tip.parent.hash, parentHash)
return nil, ruleError(ErrInvalidTemplateParent, str)
}
// Since the block template is building on the parent of the current tip,
// undo the transactions and spend information for the tip block to reach
// the point of view of the block template.
view := NewUtxoViewpoint(b.utxoCache)
view.SetBestHash(&tip.hash)
tipBlock, err := b.fetchMainChainBlockByNode(tip)
if err != nil {
return nil, err
}
parent, err := b.fetchMainChainBlockByNode(tip.parent)
if err != nil {
return nil, err
}
// Determine if treasury agenda is active.
isTreasuryEnabled, err := b.isTreasuryAgendaActive(tip.parent)
if err != nil {
return nil, err
}
// Determine if the automatic ticket revocations agenda is active.
isAutoRevocationsEnabled, err := b.isAutoRevocationsAgendaActive(tip.parent)
if err != nil {
return nil, err
}
// Load all of the spent txos for the tip block from the spend journal.
var stxos []spentTxOut
err = b.db.View(func(dbTx database.Tx) error {
stxos, err = dbFetchSpendJournalEntry(dbTx, tipBlock, isTreasuryEnabled)
return err
})
if err != nil {
return nil, err
}
// Update the view to unspend all of the spent txos and remove the utxos
// created by the tip block. Also, if the block votes against its parent,
// reconnect all of the regular transactions.
err = view.disconnectBlock(tipBlock, parent, stxos, isTreasuryEnabled,
isAutoRevocationsEnabled)
if err != nil {
return nil, err
}
// The view is now from the point of view of the parent of the current tip
// block. However, calculating the commitment root requires the view to
// include outputs created in the candidate block, so update the view to
// mark all utxos referenced by the block as spent and add all transactions
// being created by the block to it. In the case the block votes against
// the parent, also disconnect all of the regular transactions in the parent
// block.
utilBlock := dcrutil.NewBlock(block)
err = view.connectBlock(b.db, utilBlock, parent, nil, isTreasuryEnabled,
isAutoRevocationsEnabled)
if err != nil {
return nil, err
}
return view, nil
}
// FilterByBlockHash returns the version 2 GCS filter for the given block hash
// when it exists. This function returns the filters regardless of whether or
// not their associated block is part of the main chain.
//
// An error that wraps ErrNoFilter will be returned when the filter for the
// given block hash does not exist.
//
// This function is safe for concurrent access.
func (b *BlockChain) FilterByBlockHash(hash *chainhash.Hash) (*gcs.FilterV2, error) {
var filter *gcs.FilterV2
err := b.db.View(func(dbTx database.Tx) error {
var err error
filter, err = dbFetchGCSFilter(dbTx, hash)
return err
})
if err == nil && filter == nil {
str := fmt.Sprintf("no filter available for block %s", hash)
err = contextError(ErrNoFilter, str)
}
return filter, err
}