INTERNAL HELPERS BELOW ApplyAndValidateBlock() plays the block against the state machine which returns a result that is compared against the candidate block header
(block *lib.Block, commit bool)
| 450 | |
| 451 | // ApplyAndValidateBlock() plays the block against the state machine which returns a result that is compared against the candidate block header |
| 452 | func (c *Controller) ApplyAndValidateBlock(block *lib.Block, commit bool) (b *lib.BlockResult, err lib.ErrorI) { |
| 453 | // define convenience variables for the block header, hash, and height |
| 454 | candidate, candidateHash, candidateHeight := block.BlockHeader, lib.BytesToString(block.BlockHeader.Hash), block.BlockHeader.Height |
| 455 | // check the last qc in the candidate and set it in the ephemeral indexer to prepare for block application |
| 456 | if err = c.CheckAndSetLastCertificate(candidate); err != nil { |
| 457 | // exit with error |
| 458 | return |
| 459 | } |
| 460 | // log the start of 'apply block' |
| 461 | c.log.Debugf("Applying block %s for height %d", candidateHash[:20], candidateHeight) |
| 462 | // apply the block against the state machine |
| 463 | compare, results, err := c.FSM.ApplyBlock(context.Background(), block, false) |
| 464 | if err != nil { |
| 465 | // exit with error |
| 466 | return |
| 467 | } |
| 468 | // if any transactions failed |
| 469 | if len(results.Failed) != 0 { |
| 470 | for _, f := range results.Failed { |
| 471 | c.log.Errorf("From: %s\nType:%s\nErr:%s", f.Address, f.Transaction.MessageType, f.Error.Error()) |
| 472 | } |
| 473 | return nil, lib.ErrFailedTransactions() |
| 474 | } |
| 475 | // compare the block headers for equality |
| 476 | compareHash, err := compare.SetHash() |
| 477 | if err != nil { |
| 478 | // exit with error |
| 479 | return |
| 480 | } |
| 481 | // use the hash to compare two block headers for equality |
| 482 | if !bytes.Equal(compareHash, candidate.Hash) { |
| 483 | c.debugDumpHeaderDiff(candidate, compare) |
| 484 | return nil, lib.ErrUnequalBlockHash() |
| 485 | } |
| 486 | // validate VDF if committing randomly since this randomness is pseudo-non-deterministic (among nodes) |
| 487 | if commit && compare.Height > 1 && candidate.Vdf != nil { |
| 488 | // this design has similar security guarantees but lowers the computational requirements at a per-node basis |
| 489 | if rand.Intn(100) == 0 { |
| 490 | // validate the VDF included in the block |
| 491 | if !crypto.VerifyVDF(candidate.LastBlockHash, candidate.Vdf.Output, candidate.Vdf.Proof, int(candidate.Vdf.Iterations)) { |
| 492 | // exit with vdf error |
| 493 | return nil, lib.ErrInvalidVDF() |
| 494 | } |
| 495 | } |
| 496 | } |
| 497 | // log that the proposal is valid |
| 498 | c.log.Infof("Block %s with %d txs is valid for height %d ✅ ", candidateHash[:20], len(block.Transactions), candidateHeight) |
| 499 | // exit with the valid results |
| 500 | return &lib.BlockResult{BlockHeader: candidate, Transactions: results.Results, Events: results.Events}, nil |
| 501 | } |
| 502 | |
| 503 | // HandlePeerBlock() validates and handles an inbound certificate (with a block) from a remote peer |
| 504 | func (c *Controller) HandlePeerBlock(msg *lib.BlockMessage, syncing bool) (*lib.QuorumCertificate, lib.ErrorI) { |
no test coverage detected