Recover performs crash recovery, restoring the blockchain to a complete state. It returns the latest confirmed block and the corresponding state snapshot. If the blockchain is empty (missing initial block), this function returns a nil block and an empty snapshot.
(ctx context.Context)
| 16 | // If the blockchain is empty (missing initial block), this function |
| 17 | // returns a nil block and an empty snapshot. |
| 18 | func (c *Chain) Recover(ctx context.Context) (*legacy.Block, *state.Snapshot, error) { |
| 19 | snapshot, snapshotHeight, err := c.store.LatestSnapshot(ctx) |
| 20 | if err != nil { |
| 21 | return nil, nil, errors.Wrap(err, "getting latest snapshot") |
| 22 | } |
| 23 | var b *legacy.Block |
| 24 | if snapshotHeight > 0 { |
| 25 | b, err = c.store.GetBlock(ctx, snapshotHeight) |
| 26 | if err != nil { |
| 27 | return nil, nil, errors.Wrap(err, "getting snapshot block") |
| 28 | } |
| 29 | c.lastQueuedSnapshot = b.Time() |
| 30 | } |
| 31 | if snapshot == nil { |
| 32 | snapshot = state.Empty() |
| 33 | } |
| 34 | |
| 35 | // The true height of the blockchain might be higher than the |
| 36 | // height at which the state snapshot was taken. Replay all |
| 37 | // existing blocks higher than the snapshot height. |
| 38 | height, err := c.store.Height(ctx) |
| 39 | if err != nil { |
| 40 | return nil, nil, errors.Wrap(err, "getting blockchain height") |
| 41 | } |
| 42 | |
| 43 | // Bring the snapshot up to date with the latest block |
| 44 | for h := snapshotHeight + 1; h <= height; h++ { |
| 45 | b, err = c.store.GetBlock(ctx, h) |
| 46 | if err != nil { |
| 47 | return nil, nil, errors.Wrap(err, "getting block") |
| 48 | } |
| 49 | err = snapshot.ApplyBlock(legacy.MapBlock(b)) |
| 50 | if err != nil { |
| 51 | return nil, nil, errors.Wrap(err, "applying block") |
| 52 | } |
| 53 | if b.AssetsMerkleRoot != snapshot.Tree.RootHash() { |
| 54 | return nil, nil, fmt.Errorf("block %d has state root %s; snapshot has root %s", |
| 55 | b.Height, b.AssetsMerkleRoot, snapshot.Tree.RootHash()) |
| 56 | } |
| 57 | } |
| 58 | if b != nil { |
| 59 | // All blocks before the latest one have been fully processed |
| 60 | // (saved in the db, callbacks invoked). The last one may have |
| 61 | // been too, but make sure just in case. Also "finalize" the last |
| 62 | // block (notifying other processes of the latest block height) |
| 63 | // and maybe persist the snapshot. |
| 64 | err = c.CommitAppliedBlock(ctx, b, snapshot) |
| 65 | if err != nil { |
| 66 | return nil, nil, errors.Wrap(err, "committing block") |
| 67 | } |
| 68 | } |
| 69 | return b, snapshot, nil |
| 70 | } |