-
Notifications
You must be signed in to change notification settings - Fork 516
/
fsm.go
717 lines (580 loc) · 22 KB
/
fsm.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
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
package polybft
import (
"bytes"
"errors"
"fmt"
"math/big"
"time"
"github.com/0xPolygon/go-ibft/messages"
"github.com/0xPolygon/go-ibft/messages/proto"
"github.com/0xPolygon/polygon-edge/consensus/polybft/bitmap"
"github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi"
bls "github.com/0xPolygon/polygon-edge/consensus/polybft/signer"
"github.com/0xPolygon/polygon-edge/consensus/polybft/validator"
"github.com/0xPolygon/polygon-edge/consensus/polybft/wallet"
"github.com/0xPolygon/polygon-edge/contracts"
"github.com/0xPolygon/polygon-edge/state"
"github.com/0xPolygon/polygon-edge/types"
"github.com/armon/go-metrics"
hcf "github.com/hashicorp/go-hclog"
)
type blockBuilder interface {
Reset() error
WriteTx(*types.Transaction) error
Fill()
Build(func(h *types.Header)) (*types.FullBlock, error)
GetState() *state.Transition
Receipts() []*types.Receipt
}
var (
errCommitEpochTxDoesNotExist = errors.New("commit epoch transaction is not found in the epoch ending block")
errCommitEpochTxNotExpected = errors.New("didn't expect commit epoch transaction in a non epoch ending block")
errCommitEpochTxSingleExpected = errors.New("only one commit epoch transaction is allowed " +
"in an epoch ending block")
errDistributeRewardsTxDoesNotExist = errors.New("distribute rewards transaction is " +
"not found in the epoch ending block")
errDistributeRewardsTxNotExpected = errors.New("didn't expect distribute rewards transaction " +
"in a non epoch ending block")
errDistributeRewardsTxSingleExpected = errors.New("only one distribute rewards transaction is " +
"allowed in an epoch ending block")
errProposalDontMatch = errors.New("failed to insert proposal, because the validated proposal " +
"is either nil or it does not match the received one")
errValidatorSetDeltaMismatch = errors.New("validator set delta mismatch")
errValidatorsUpdateInNonEpochEnding = errors.New("trying to update validator set in a non epoch ending block")
errValidatorDeltaNilInEpochEndingBlock = errors.New("validator set delta is nil in epoch ending block")
)
type fsm struct {
// PolyBFT consensus protocol configuration
config *PolyBFTConfig
// parent block header
parent *types.Header
// backend implements methods for retrieving data from block chain
backend blockchainBackend
// polybftBackend implements methods needed from the polybft
polybftBackend polybftBackend
// validators is the list of validators for this round
validators validator.ValidatorSet
// proposerSnapshot keeps information about new proposer
proposerSnapshot *ProposerSnapshot
// blockBuilder is the block builder for proposers
blockBuilder blockBuilder
// epochNumber denotes current epoch number
epochNumber uint64
// commitEpochInput holds info about a single epoch
// It is populated only for epoch-ending blocks.
commitEpochInput *contractsapi.CommitEpochValidatorSetFn
// distributeRewardsInput holds info about validators work in a single epoch
// mainly, how many blocks they signed during given epoch
// It is populated only for epoch-ending blocks.
distributeRewardsInput *contractsapi.DistributeRewardForRewardPoolFn
// isEndOfEpoch indicates if epoch reached its end
isEndOfEpoch bool
// isEndOfSprint indicates if sprint reached its end
isEndOfSprint bool
// proposerCommitmentToRegister is a commitment that is registered via state transaction by proposer
proposerCommitmentToRegister *CommitmentMessageSigned
// logger instance
logger hcf.Logger
// target is the block being computed
target *types.FullBlock
// exitEventRootHash is the calculated root hash for given checkpoint block
exitEventRootHash types.Hash
// newValidatorsDelta carries the updates of validator set on epoch ending block
newValidatorsDelta *validator.ValidatorSetDelta
}
// BuildProposal builds a proposal for the current round (used if proposer)
func (f *fsm) BuildProposal(currentRound uint64) ([]byte, error) {
start := time.Now().UTC()
defer metrics.SetGauge([]string{consensusMetricsPrefix, "block_building_time"},
float32(time.Now().UTC().Sub(start).Seconds()))
parent := f.parent
extraParent, err := GetIbftExtra(parent.ExtraData)
if err != nil {
return nil, err
}
extra := &Extra{Parent: extraParent.Committed}
// for non-epoch ending blocks, currentValidatorsHash is the same as the nextValidatorsHash
nextValidators := f.validators.Accounts()
if err := f.blockBuilder.Reset(); err != nil {
return nil, fmt.Errorf("failed to initialize block builder: %w", err)
}
if f.isEndOfEpoch {
tx, err := f.createCommitEpochTx()
if err != nil {
return nil, err
}
if err := f.blockBuilder.WriteTx(tx); err != nil {
return nil, fmt.Errorf("failed to apply commit epoch transaction: %w", err)
}
tx, err = f.createDistributeRewardsTx()
if err != nil {
return nil, err
}
if err := f.blockBuilder.WriteTx(tx); err != nil {
return nil, fmt.Errorf("failed to apply distribute rewards transaction: %w", err)
}
}
if f.config.IsBridgeEnabled() {
if err := f.applyBridgeCommitmentTx(); err != nil {
return nil, err
}
}
// fill the block with transactions
f.blockBuilder.Fill()
if f.isEndOfEpoch {
nextValidators, err = nextValidators.ApplyDelta(f.newValidatorsDelta)
if err != nil {
return nil, err
}
extra.Validators = f.newValidatorsDelta
}
currentValidatorsHash, err := f.validators.Accounts().Hash()
if err != nil {
return nil, err
}
nextValidatorsHash, err := nextValidators.Hash()
if err != nil {
return nil, err
}
extra.Checkpoint = &CheckpointData{
BlockRound: currentRound,
EpochNumber: f.epochNumber,
CurrentValidatorsHash: currentValidatorsHash,
NextValidatorsHash: nextValidatorsHash,
EventRoot: f.exitEventRootHash,
}
f.logger.Debug("[Build Proposal]", "Current validators hash", currentValidatorsHash,
"Next validators hash", nextValidatorsHash)
stateBlock, err := f.blockBuilder.Build(func(h *types.Header) {
h.ExtraData = extra.MarshalRLPTo(nil)
h.MixHash = PolyBFTMixDigest
})
if err != nil {
return nil, err
}
if f.logger.IsDebug() {
checkpointHash, err := extra.Checkpoint.Hash(f.backend.GetChainID(), f.Height(), stateBlock.Block.Hash())
if err != nil {
return nil, fmt.Errorf("failed to calculate proposal hash: %w", err)
}
f.logger.Debug("[FSM Build Proposal]",
"txs", len(stateBlock.Block.Transactions),
"proposal hash", checkpointHash.String())
}
f.target = stateBlock
return stateBlock.Block.MarshalRLP(), nil
}
// applyBridgeCommitmentTx builds state transaction which contains data for bridge commitment registration
func (f *fsm) applyBridgeCommitmentTx() error {
if f.proposerCommitmentToRegister != nil {
bridgeCommitmentTx, err := f.createBridgeCommitmentTx()
if err != nil {
return fmt.Errorf("creation of bridge commitment transaction failed: %w", err)
}
if err := f.blockBuilder.WriteTx(bridgeCommitmentTx); err != nil {
return fmt.Errorf("failed to apply bridge commitment state transaction. Error: %w", err)
}
}
return nil
}
// createBridgeCommitmentTx builds bridge commitment registration transaction
func (f *fsm) createBridgeCommitmentTx() (*types.Transaction, error) {
inputData, err := f.proposerCommitmentToRegister.EncodeAbi()
if err != nil {
return nil, fmt.Errorf("failed to encode input data for bridge commitment registration: %w", err)
}
return createStateTransactionWithData(f.Height(), contracts.StateReceiverContract, inputData), nil
}
// getValidatorsTransition applies delta to the current validators,
func (f *fsm) getValidatorsTransition(delta *validator.ValidatorSetDelta) (validator.AccountSet, error) {
nextValidators, err := f.validators.Accounts().ApplyDelta(delta)
if err != nil {
return nil, err
}
f.logger.Debug("getValidatorsTransition", "Next validators", nextValidators)
return nextValidators, nil
}
// createCommitEpochTx create a StateTransaction, which invokes ValidatorSet smart contract
// and sends all the necessary metadata to it.
func (f *fsm) createCommitEpochTx() (*types.Transaction, error) {
input, err := f.commitEpochInput.EncodeAbi()
if err != nil {
return nil, err
}
return createStateTransactionWithData(f.Height(), contracts.ValidatorSetContract, input), nil
}
// createDistributeRewardsTx create a StateTransaction, which invokes RewardPool smart contract
// and sends all the necessary metadata to it.
func (f *fsm) createDistributeRewardsTx() (*types.Transaction, error) {
input, err := f.distributeRewardsInput.EncodeAbi()
if err != nil {
return nil, err
}
return createStateTransactionWithData(f.Height(), contracts.RewardPoolContract, input), nil
}
// ValidateCommit is used to validate that a given commit is valid
func (f *fsm) ValidateCommit(signer []byte, seal []byte, proposalHash []byte) error {
from := types.BytesToAddress(signer)
validator := f.validators.Accounts().GetValidatorMetadata(from)
if validator == nil {
return fmt.Errorf("unable to resolve validator %s", from)
}
signature, err := bls.UnmarshalSignature(seal)
if err != nil {
return fmt.Errorf("failed to unmarshall signature: %w", err)
}
if !signature.Verify(validator.BlsKey, proposalHash, bls.DomainCheckpointManager) {
return fmt.Errorf("incorrect commit signature from %s", from)
}
return nil
}
// Validate validates a raw proposal (used if non-proposer)
func (f *fsm) Validate(proposal []byte) error {
var block types.Block
if err := block.UnmarshalRLP(proposal); err != nil {
return fmt.Errorf("failed to validate, cannot decode block data. Error: %w", err)
}
// validate header fields
if err := validateHeaderFields(f.parent, block.Header, f.config.BlockTimeDrift); err != nil {
return fmt.Errorf(
"failed to validate header (parent header# %d, current header#%d): %w",
f.parent.Number,
block.Number(),
err,
)
}
extra, err := GetIbftExtra(block.Header.ExtraData)
if err != nil {
return fmt.Errorf("cannot get extra data:%w", err)
}
parentExtra, err := GetIbftExtra(f.parent.ExtraData)
if err != nil {
return err
}
if extra.Checkpoint == nil {
return fmt.Errorf("checkpoint data for block %d is missing", block.Number())
}
if parentExtra.Checkpoint == nil {
return fmt.Errorf("checkpoint data for parent block %d is missing", f.parent.Number)
}
if err := extra.ValidateParentSignatures(block.Number(), f.polybftBackend, nil, f.parent, parentExtra,
f.backend.GetChainID(), bls.DomainCheckpointManager, f.logger); err != nil {
return err
}
if err := f.VerifyStateTransactions(block.Transactions); err != nil {
return err
}
currentValidators := f.validators.Accounts()
// validate validators delta
if f.isEndOfEpoch {
if extra.Validators == nil {
return errValidatorDeltaNilInEpochEndingBlock
}
if !extra.Validators.Equals(f.newValidatorsDelta) {
return errValidatorSetDeltaMismatch
}
} else if extra.Validators != nil {
// delta should be nil in non epoch ending blocks
return errValidatorsUpdateInNonEpochEnding
}
nextValidators, err := f.getValidatorsTransition(extra.Validators)
if err != nil {
return err
}
// validate checkpoint data
if err := extra.Checkpoint.Validate(parentExtra.Checkpoint,
currentValidators, nextValidators, f.exitEventRootHash); err != nil {
return err
}
if f.logger.IsTrace() && block.Number() > 1 {
validators, err := f.polybftBackend.GetValidators(block.Number()-2, nil)
if err != nil {
return fmt.Errorf("failed to retrieve validators:%w", err)
}
f.logger.Trace("[FSM Validate]", "Block", block.Number(), "parent validators", validators)
}
stateBlock, err := f.backend.ProcessBlock(f.parent, &block)
if err != nil {
return err
}
if f.logger.IsDebug() {
checkpointHash, err := extra.Checkpoint.Hash(f.backend.GetChainID(), block.Number(), block.Hash())
if err != nil {
return fmt.Errorf("failed to calculate proposal hash: %w", err)
}
f.logger.Debug("[FSM Validate]", "txs", len(block.Transactions), "proposal hash", checkpointHash)
}
f.target = stateBlock
return nil
}
// ValidateSender validates sender address and signature
func (f *fsm) ValidateSender(msg *proto.Message) error {
msgNoSig, err := msg.PayloadNoSig()
if err != nil {
return err
}
signerAddress, err := wallet.RecoverAddressFromSignature(msg.Signature, msgNoSig)
if err != nil {
return fmt.Errorf("failed to recover address from signature: %w", err)
}
// verify the signature came from the sender
if !bytes.Equal(msg.From, signerAddress.Bytes()) {
return fmt.Errorf("signer address %s doesn't match From field", signerAddress.String())
}
// verify the sender is in the active validator set
if !f.validators.Includes(signerAddress) {
return fmt.Errorf("signer address %s is not included in validator set", signerAddress.String())
}
return nil
}
func (f *fsm) VerifyStateTransactions(transactions []*types.Transaction) error {
var (
commitmentTxExists bool
commitEpochTxExists bool
distributeRewardsTxExists bool
)
for _, tx := range transactions {
if tx.Type != types.StateTx {
continue
}
decodedStateTx, err := decodeStateTransaction(tx.Input)
if err != nil {
return fmt.Errorf("unknown state transaction: tx = %v, err = %w", tx.Hash, err)
}
switch stateTxData := decodedStateTx.(type) {
case *CommitmentMessageSigned:
if !f.isEndOfSprint {
return fmt.Errorf("found commitment tx in block which should not contain it (tx hash=%s)", tx.Hash)
}
if commitmentTxExists {
return fmt.Errorf("only one commitment tx is allowed per block (tx hash=%s)", tx.Hash)
}
commitmentTxExists = true
if err = verifyBridgeCommitmentTx(f.Height(), tx.Hash, stateTxData, f.validators); err != nil {
return err
}
case *contractsapi.CommitEpochValidatorSetFn:
if commitEpochTxExists {
// if we already validated commit epoch tx,
// that means someone added more than one commit epoch tx to block,
// which is invalid
return errCommitEpochTxSingleExpected
}
commitEpochTxExists = true
if err := f.verifyCommitEpochTx(tx); err != nil {
return fmt.Errorf("error while verifying commit epoch transaction. error: %w", err)
}
case *contractsapi.DistributeRewardForRewardPoolFn:
if distributeRewardsTxExists {
// if we already validated distribute rewards tx,
// that means someone added more than one distribute rewards tx to block,
// which is invalid
return errDistributeRewardsTxSingleExpected
}
distributeRewardsTxExists = true
if err := f.verifyDistributeRewardsTx(tx); err != nil {
return fmt.Errorf("error while verifying distribute rewards transaction. error: %w", err)
}
default:
return fmt.Errorf("invalid state transaction data type: %v", stateTxData)
}
}
if f.isEndOfEpoch {
if !commitEpochTxExists {
// this is a check if commit epoch transaction is not in the list of transactions at all
// but it should be
return errCommitEpochTxDoesNotExist
}
if !distributeRewardsTxExists {
// this is a check if distribute rewards transaction is not in the list of transactions at all
// but it should be
return errDistributeRewardsTxDoesNotExist
}
}
return nil
}
// Insert inserts the sealed proposal
func (f *fsm) Insert(proposal []byte, committedSeals []*messages.CommittedSeal) (*types.FullBlock, error) {
newBlock := f.target
var proposedBlock types.Block
if err := proposedBlock.UnmarshalRLP(proposal); err != nil {
return nil, fmt.Errorf("failed to insert proposal, block unmarshaling failed: %w", err)
}
if newBlock == nil || newBlock.Block.Hash() != proposedBlock.Hash() {
// if this is the case, we will let syncer insert the block
return nil, errProposalDontMatch
}
// In this function we should try to return little to no errors since
// at this point everything we have to do is just commit something that
// we should have already computed beforehand.
extra, err := GetIbftExtra(newBlock.Block.Header.ExtraData)
if err != nil {
return nil, fmt.Errorf("failed to insert proposal, due to not being able to extract extra data: %w", err)
}
// create map for faster access to indexes
nodeIDIndexMap := make(map[types.Address]int, f.validators.Len())
for i, addr := range f.validators.Accounts().GetAddresses() {
nodeIDIndexMap[addr] = i
}
// populated bitmap according to nodeId from validator set and committed seals
// also populate slice of signatures
bitmap := bitmap.Bitmap{}
signatures := make(bls.Signatures, 0, len(committedSeals))
for _, commSeal := range committedSeals {
signerAddr := types.BytesToAddress(commSeal.Signer)
index, exists := nodeIDIndexMap[signerAddr]
if !exists {
return nil, fmt.Errorf("invalid node id = %s", signerAddr.String())
}
s, err := bls.UnmarshalSignature(commSeal.Signature)
if err != nil {
return nil, fmt.Errorf("invalid signature = %s", commSeal.Signature)
}
signatures = append(signatures, s)
bitmap.Set(uint64(index))
}
aggregatedSignature, err := signatures.Aggregate().Marshal()
if err != nil {
return nil, fmt.Errorf("could not aggregate seals: %w", err)
}
// include aggregated signature of all committed seals
// also includes bitmap which contains all indexes from validator set which provides there seals
extra.Committed = &Signature{
AggregatedSignature: aggregatedSignature,
Bitmap: bitmap,
}
// Write extra data to header
newBlock.Block.Header.ExtraData = extra.MarshalRLPTo(nil)
if err := f.backend.CommitBlock(newBlock); err != nil {
return nil, err
}
return newBlock, nil
}
// Height returns the height for the current round
func (f *fsm) Height() uint64 {
return f.parent.Number + 1
}
// ValidatorSet returns the validator set for the current round
func (f *fsm) ValidatorSet() validator.ValidatorSet {
return f.validators
}
// verifyCommitEpochTx creates commit epoch transaction and compares its hash with the one extracted from the block.
func (f *fsm) verifyCommitEpochTx(commitEpochTx *types.Transaction) error {
if f.isEndOfEpoch {
localCommitEpochTx, err := f.createCommitEpochTx()
if err != nil {
return err
}
if commitEpochTx.Hash != localCommitEpochTx.Hash {
return fmt.Errorf(
"invalid commit epoch transaction. Expected '%s', but got '%s' commit epoch transaction hash",
localCommitEpochTx.Hash,
commitEpochTx.Hash,
)
}
return nil
}
return errCommitEpochTxNotExpected
}
// verifyDistributeRewardsTx creates distribute rewards transaction
// and compares its hash with the one extracted from the block.
func (f *fsm) verifyDistributeRewardsTx(distributeRewardsTx *types.Transaction) error {
if f.isEndOfEpoch {
localDistributeRewardsTx, err := f.createDistributeRewardsTx()
if err != nil {
return err
}
if distributeRewardsTx.Hash != localDistributeRewardsTx.Hash {
return fmt.Errorf(
"invalid distribute rewards transaction. Expected '%s', but got '%s' distribute rewards hash",
localDistributeRewardsTx.Hash,
distributeRewardsTx.Hash,
)
}
return nil
}
return errDistributeRewardsTxNotExpected
}
// verifyBridgeCommitmentTx validates bridge commitment transaction
func verifyBridgeCommitmentTx(blockNumber uint64, txHash types.Hash,
commitment *CommitmentMessageSigned,
validators validator.ValidatorSet) error {
signers, err := validators.Accounts().GetFilteredValidators(commitment.AggSignature.Bitmap)
if err != nil {
return fmt.Errorf("failed to retrieve signers for state tx (%s): %w", txHash, err)
}
if !validators.HasQuorum(blockNumber, signers.GetAddressesAsSet()) {
return fmt.Errorf("quorum size not reached for state tx (%s)", txHash)
}
commitmentHash, err := commitment.Hash()
if err != nil {
return err
}
signature, err := bls.UnmarshalSignature(commitment.AggSignature.AggregatedSignature)
if err != nil {
return fmt.Errorf("error for state tx (%s) while unmarshaling signature: %w", txHash, err)
}
verified := signature.VerifyAggregated(signers.GetBlsKeys(), commitmentHash.Bytes(), bls.DomainStateReceiver)
if !verified {
return fmt.Errorf("invalid signature for state tx (%s)", txHash)
}
return nil
}
func validateHeaderFields(parent *types.Header, header *types.Header, blockTimeDrift uint64) error {
// header extra data must be higher or equal to ExtraVanity = 32 in order to be compliant with Ethereum blocks
if len(header.ExtraData) < ExtraVanity {
return fmt.Errorf("extra-data shorter than %d bytes (%d)", ExtraVanity, len(header.ExtraData))
}
// verify parent hash
if parent.Hash != header.ParentHash {
return fmt.Errorf("incorrect header parent hash (parent=%s, header parent=%s)", parent.Hash, header.ParentHash)
}
// verify parent number
if header.Number != parent.Number+1 {
return fmt.Errorf("invalid number")
}
// verify time is from the future
if header.Timestamp > (uint64(time.Now().UTC().Unix()) + blockTimeDrift) {
return fmt.Errorf("block from the future. block timestamp: %s, configured block time drift %d seconds",
time.Unix(int64(header.Timestamp), 0).Format(time.RFC3339), blockTimeDrift)
}
// verify header nonce is zero
if header.Nonce != types.ZeroNonce {
return fmt.Errorf("invalid nonce")
}
// verify that the gasUsed is <= gasLimit
if header.GasUsed > header.GasLimit {
return fmt.Errorf("invalid gas limit: have %v, max %v", header.GasUsed, header.GasLimit)
}
// verify time has passed
if header.Timestamp <= parent.Timestamp {
return fmt.Errorf("timestamp older than parent")
}
// verify mix digest
if header.MixHash != PolyBFTMixDigest {
return fmt.Errorf("mix digest is not correct")
}
// difficulty must be > 0
if header.Difficulty <= 0 {
return fmt.Errorf("difficulty should be greater than zero")
}
// calculated header hash must be correct
if header.Hash != types.HeaderHash(header) {
return fmt.Errorf("invalid header hash")
}
return nil
}
// createStateTransactionWithData creates a state transaction
// with provided target address and inputData parameter which is ABI encoded byte array.
func createStateTransactionWithData(blockNumber uint64, target types.Address, inputData []byte) *types.Transaction {
tx := &types.Transaction{
From: contracts.SystemCaller,
To: &target,
Type: types.StateTx,
Input: inputData,
Gas: types.StateTransactionGasLimit,
GasPrice: big.NewInt(0),
}
tx.ComputeHash(blockNumber)
return tx
}