-
Notifications
You must be signed in to change notification settings - Fork 3
/
block_validator.go
253 lines (228 loc) · 9.25 KB
/
block_validator.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
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/pkg/errors"
"github.com/PositionExchange/posichain/block"
consensus_engine "github.com/PositionExchange/posichain/consensus/engine"
"github.com/PositionExchange/posichain/core/state"
"github.com/PositionExchange/posichain/core/types"
"github.com/PositionExchange/posichain/crypto/bls"
"github.com/PositionExchange/posichain/internal/params"
)
// BlockValidator is responsible for validating block headers, uncles and
// processed state.
//
// BlockValidator implements validator.
type BlockValidator struct {
config *params.ChainConfig // Chain configuration options
bc *BlockChain // Canonical block chain
engine consensus_engine.Engine // Consensus engine used for validating
}
// NewBlockValidator returns a new block validator which is safe for re-use
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus_engine.Engine) *BlockValidator {
validator := &BlockValidator{
config: config,
engine: engine,
bc: blockchain,
}
return validator
}
// ValidateBody verifies the block header's transaction root.
// The headers are assumed to be already validated at this point.
func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Check whether the block's known, and if not, that it's linkable
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
return ErrKnownBlock
}
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus_engine.ErrUnknownAncestor
}
return consensus_engine.ErrPrunedAncestor
}
// Header validity is known at this point, check the uncles and transactions
header := block.Header()
//if err := v.engine.VerifyUncles(v.bc, block); err != nil {
// return err
//}
if hash := types.DeriveSha(
block.Transactions(),
block.StakingTransactions(),
); hash != header.TxHash() {
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash())
}
return nil
}
// ValidateState validates the various changes that happen after a state
// transition, such as amount of used gas, the receipt roots and the state root
// itself. ValidateState returns a database batch if the validation was a success
// otherwise nil and an error is returned.
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.DB, receipts types.Receipts, cxReceipts types.CXReceipts, usedGas uint64) error {
header := block.Header()
if block.GasUsed() != usedGas {
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
}
// Validate the received block's bloom with the one derived from the generated receipts.
// For valid blocks this should always validate to true.
rbloom := types.CreateBloom(receipts)
if rbloom != header.Bloom() {
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom(), rbloom)
}
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
receiptSha := types.DeriveSha(receipts)
if receiptSha != header.ReceiptHash() {
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash(), receiptSha)
}
if v.config.AcceptsCrossTx(block.Epoch()) {
cxsSha := cxReceipts.ComputeMerkleRoot()
if cxsSha != header.OutgoingReceiptHash() {
legacySha := types.DeriveMultipleShardsSha(cxReceipts)
if legacySha != header.OutgoingReceiptHash() {
return fmt.Errorf("invalid cross shard receipt root hash (remote: %x local: %x, legacy: %x)", header.OutgoingReceiptHash(), cxsSha, legacySha)
}
}
}
// Validate the state root against the received state root and throw
// an error if they don't match.
if root := statedb.IntermediateRoot(v.config.IsS3(header.Epoch())); header.Root() != root {
dump, _ := rlp.EncodeToBytes(header)
const msg = "invalid merkle root (remote: %x local: %x, rlp dump %s)"
return fmt.Errorf(msg, header.Root(), root, hex.EncodeToString(dump))
}
return nil
}
// ValidateHeader checks whether a header conforms to the consensus rules of a
// given engine. Verifying the seal may be done optionally here, or explicitly
// via the VerifySeal method.
func (v *BlockValidator) ValidateHeader(block *types.Block, seal bool) error {
if block == nil {
return errors.New("block is nil")
}
if h := block.Header(); h != nil {
return v.engine.VerifyHeader(v.bc, h, true)
}
return errors.New("header field was nil")
}
// ValidateHeaders verifies a batch of blocks' headers concurrently. The method returns a quit channel
// to abort the operations and a results channel to retrieve the async verifications
func (v *BlockValidator) ValidateHeaders(chain []*types.Block) (chan<- struct{}, <-chan error) {
// Start the parallel header verifier
headers := make([]*block.Header, len(chain))
seals := make([]bool, len(chain))
for i, block := range chain {
headers[i] = block.Header()
seals[i] = true
}
return v.engine.VerifyHeaders(v.bc, headers, seals)
}
// CalcGasLimit computes the gas limit of the next block after parent. It aims
// to keep the baseline gas above the provided floor, and increase it towards the
// ceil if the blocks are full. If the ceil is exceeded, it will always decrease
// the gas allowance.
func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
// contrib = (parentGasUsed * 3 / 2) / 1024
contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor
// decay = parentGasLimit / 1024 -1
decay := parent.GasLimit()/params.GasLimitBoundDivisor - 1
/*
strategy: gasLimit of block-to-mine is set based on parent's
gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we
increase it, otherwise lower it (or leave it unchanged if it's right
at that usage) the amount increased/decreased depends on how far away
from parentGasLimit * (2/3) parentGasUsed is.
*/
limit := parent.GasLimit() - decay + contrib
if limit < params.MinGasLimit {
limit = params.MinGasLimit
}
// If we're outside our allowed gas range, we try to hone towards them
if limit < gasFloor {
limit = parent.GasLimit() + decay
if limit > gasFloor {
limit = gasFloor
}
} else if limit > gasCeil {
limit = parent.GasLimit() - decay
if limit < gasCeil {
limit = gasCeil
}
}
return limit
}
// ValidateCXReceiptsProof checks whether the given CXReceiptsProof is consistency with itself
func (v *BlockValidator) ValidateCXReceiptsProof(cxp *types.CXReceiptsProof) error {
if !v.config.AcceptsCrossTx(cxp.Header.Epoch()) {
return errors.New("[ValidateCXReceiptsProof] cross shard receipt received before cx fork")
}
toShardID, err := cxp.GetToShardID()
if err != nil {
return errors.Wrapf(err, "[ValidateCXReceiptsProof] invalid shardID")
}
merkleProof := cxp.MerkleProof
shardRoot := common.Hash{}
foundMatchingShardID := false
byteBuffer := bytes.Buffer{}
// prepare to calculate source shard outgoing cxreceipts root hash
for j := 0; j < len(merkleProof.ShardIDs); j++ {
sKey := make([]byte, 4)
binary.BigEndian.PutUint32(sKey, merkleProof.ShardIDs[j])
byteBuffer.Write(sKey)
byteBuffer.Write(merkleProof.CXShardHashes[j][:])
if merkleProof.ShardIDs[j] == toShardID {
shardRoot = merkleProof.CXShardHashes[j]
foundMatchingShardID = true
}
}
if !foundMatchingShardID {
return errors.New("[ValidateCXReceiptsProof] Didn't find matching toShardID (no receipts for my shard)")
}
sha := types.DeriveSha(cxp.Receipts)
// (1) verify the CXReceipts trie root match
if sha != shardRoot {
return errors.New(
"[ValidateCXReceiptsProof] Trie Root of ReadCXReceipts Not Match",
)
}
// (2) verify the outgoingCXReceiptsHash match
outgoingHashFromSourceShard := crypto.Keccak256Hash(byteBuffer.Bytes())
if byteBuffer.Len() == 0 {
outgoingHashFromSourceShard = types.EmptyRootHash
}
if outgoingHashFromSourceShard != merkleProof.CXReceiptHash {
return errors.New(
"[ValidateCXReceiptsProof] IncomingReceiptRootHash from source shard not match",
)
}
// (3) verify the block hash matches
if cxp.Header.Hash() != merkleProof.BlockHash ||
cxp.Header.OutgoingReceiptHash() != merkleProof.CXReceiptHash {
return errors.New(
"[ValidateCXReceiptsProof] BlockHash or OutgoingReceiptHash not match in block Header",
)
}
// (4) verify blockHeader with seal
var commitSig bls.SerializedSignature
copy(commitSig[:], cxp.CommitSig)
return v.engine.VerifyHeaderSignature(v.bc, cxp.Header, commitSig, cxp.CommitBitmap)
}