diff --git a/src/lib_store/mocked/store.ml b/src/lib_store/mocked/store.ml index 8ce8b02cfc2407175cc713ee23852d3553517410..16e58846a42ce58f3e7bdaaa915f3ba01746b2f0 100644 --- a/src/lib_store/mocked/store.ml +++ b/src/lib_store/mocked/store.ml @@ -84,6 +84,7 @@ and chain_state = { (* Following fields are not safe to update concurrently and must be manipulated carefuly: *) current_head_data : block_descriptor Stored_data.t; + mutable last_finalized_block_level : Int32.t option; cementing_highwatermark_data : int32 option Stored_data.t; target_data : block_descriptor option Stored_data.t; checkpoint_data : block_descriptor Stored_data.t; @@ -399,7 +400,7 @@ module Block = struct message; max_operations_ttl; last_preserved_block_level; - _; + last_finalized_block_level; }; block_metadata; ops_metadata; @@ -528,10 +529,22 @@ module Block = struct let*! () = Store_events.(emit store_block) (hash, block_header.shell.level) in - let*! () = - Shared.use chain_store.chain_state (fun {validated_blocks; _} -> - Block_lru_cache.remove validated_blocks hash ; - Lwt.return_unit) + let* () = + Shared.update_with chain_store.chain_state (fun chain_state -> + Block_lru_cache.remove chain_state.validated_blocks hash ; + let new_last_finalized_block_level = + match chain_state.last_finalized_block_level with + | None -> Some last_finalized_block_level + | Some prev_lfbl -> + Some (Int32.max last_finalized_block_level prev_lfbl) + in + let new_chain_state = + { + chain_state with + last_finalized_block_level = new_last_finalized_block_level; + } + in + return (Some new_chain_state, ())) in Lwt_watcher.notify chain_store.block_watcher block ; Lwt_watcher.notify @@ -1162,11 +1175,14 @@ module Chain = struct in Lwt.return_some l - let may_update_checkpoint_and_target chain_store ~new_head ~new_head_lpbl + let may_update_checkpoint_and_target chain_store ~new_head ~new_head_lfbl ~checkpoint ~target = let open Lwt_result_syntax in let new_checkpoint = - if Compare.Int32.(snd new_head_lpbl > snd checkpoint) then new_head_lpbl + (* Ensure that the checkpoint is not above the current head *) + if Compare.Int32.(snd new_head_lfbl > snd checkpoint) then + if Compare.Int32.(snd new_head_lfbl > snd new_head) then new_head + else new_head_lfbl else checkpoint in match target with @@ -1219,30 +1235,33 @@ module Chain = struct Block.get_block_metadata chain_store new_head) in let*! target = Stored_data.get chain_state.target_data in - let new_head_lpbl = - Block.last_preserved_block_level new_head_metadata - in (* This write call will initialize the cementing highwatermark when it is not yet set or do nothing otherwise. *) - let*! lpbl_block_opt = - Block.locked_read_block_by_level_opt - chain_store - new_head - new_head_lpbl + let* lfbl_block_opt = + match chain_state.last_finalized_block_level with + | None -> return_none + | Some lfbl -> + let distance = + Int32.(to_int @@ max 0l (sub (Block.level new_head) lfbl)) + in + Block_store.read_block + chain_store.block_store + ~read_metadata:false + (Block (Block.hash new_head, distance)) in let* new_checkpoint, new_target = - match lpbl_block_opt with + match lfbl_block_opt with | None -> (* This case may occur when importing a rolling snapshot - where the lpbl block is not known. We may use the - checkpoint instead. *) + where the lfbl block is not known or when a node was + just started. We may use the checkpoint instead. *) return (checkpoint, target) - | Some lpbl_block -> + | Some lfbl_block -> may_update_checkpoint_and_target chain_store ~new_head:new_head_descr - ~new_head_lpbl:(Block.descriptor lpbl_block) + ~new_head_lfbl:(Block.descriptor lfbl_block) ~checkpoint ~target in @@ -1421,6 +1440,7 @@ module Chain = struct ~initial_data:Chain_id.Map.empty in let current_head = genesis_block in + let last_finalized_block_level = None in let active_testchain = None in let mempool = Mempool.empty in let live_blocks = Block_hash.Set.singleton genesis_block.hash in @@ -1430,6 +1450,7 @@ module Chain = struct return { current_head_data; + last_finalized_block_level; cementing_highwatermark_data; target_data; checkpoint_data; diff --git a/src/lib_store/unix/store.ml b/src/lib_store/unix/store.ml index b5a30e2d10d5c5a63c1eb8717aca74fbdc06af3a..af1d55190733316bd42eabfdb025e506f7040632 100644 --- a/src/lib_store/unix/store.ml +++ b/src/lib_store/unix/store.ml @@ -88,6 +88,7 @@ and chain_state = { (* Following fields are not safe to update concurrently and must be manipulated carefuly: *) current_head_data : block_descriptor Stored_data.t; + mutable last_finalized_block_level : Int32.t option; cementing_highwatermark_data : int32 option Stored_data.t; target_data : block_descriptor option Stored_data.t; checkpoint_data : block_descriptor Stored_data.t; @@ -452,7 +453,7 @@ module Block = struct message; max_operations_ttl; last_preserved_block_level; - _; + last_finalized_block_level; }; block_metadata; ops_metadata; @@ -581,10 +582,22 @@ module Block = struct let*! () = Store_events.(emit store_block) (hash, block_header.shell.level) in - let*! () = - Shared.use chain_store.chain_state (fun {validated_blocks; _} -> - Block_lru_cache.remove validated_blocks hash ; - Lwt.return_unit) + let* () = + Shared.update_with chain_store.chain_state (fun chain_state -> + Block_lru_cache.remove chain_state.validated_blocks hash ; + let new_last_finalized_block_level = + match chain_state.last_finalized_block_level with + | None -> Some last_finalized_block_level + | Some prev_lfbl -> + Some (Int32.max last_finalized_block_level prev_lfbl) + in + let new_chain_state = + { + chain_state with + last_finalized_block_level = new_last_finalized_block_level; + } + in + return (Some new_chain_state, ())) in Lwt_watcher.notify chain_store.block_watcher block ; Lwt_watcher.notify @@ -1303,7 +1316,10 @@ module Chain = struct ~checkpoint ~target = let open Lwt_result_syntax in let new_checkpoint = - if Compare.Int32.(snd new_head_lfbl > snd checkpoint) then new_head_lfbl + (* Ensure that the checkpoint is not above the current head *) + if Compare.Int32.(snd new_head_lfbl > snd checkpoint) then + if Compare.Int32.(snd new_head_lfbl > snd new_head) then new_head + else new_head_lfbl else checkpoint in match target with @@ -1462,18 +1478,24 @@ module Chain = struct chain_state cementing_highwatermark in - let*! lfbl_block_opt = - Block.locked_read_block_by_level_opt - chain_store - new_head - new_head_lpbl + let* lfbl_block_opt = + match chain_state.last_finalized_block_level with + | None -> return_none + | Some lfbl -> + let distance = + Int32.(to_int @@ max 0l (sub (Block.level new_head) lfbl)) + in + Block_store.read_block + chain_store.block_store + ~read_metadata:false + (Block (Block.hash new_head, distance)) in let* new_checkpoint, new_target = match lfbl_block_opt with | None -> - (* This case may occur when importing a rolling - snapshot where the lafl block is not known. - We may use the checkpoint instead. *) + (* This case may occur when importing a rolling snapshot + where the lfbl block is not known or when a node was + just started. We may use the checkpoint instead. *) return (checkpoint, target) | Some lfbl_block -> may_update_checkpoint_and_target @@ -1760,6 +1782,7 @@ module Chain = struct ~initial_data:Chain_id.Map.empty in let current_head = genesis_block in + let last_finalized_block_level = None in let active_testchain = None in let mempool = Mempool.empty in let live_blocks = Block_hash.Set.singleton genesis_block.hash in @@ -1769,6 +1792,7 @@ module Chain = struct return { current_head_data; + last_finalized_block_level; cementing_highwatermark_data; target_data; checkpoint_data; @@ -1856,6 +1880,7 @@ module Chain = struct match o with | None -> failwith "load_store: cannot read head" | Some current_head -> + let last_finalized_block_level = None in let active_testchain = None in let mempool = Mempool.empty in let live_blocks = Block_hash.Set.empty in @@ -1865,6 +1890,7 @@ module Chain = struct return { current_head_data; + last_finalized_block_level; cementing_highwatermark_data; target_data; checkpoint_data; diff --git a/src/lib_store/unix/test/test_store.ml b/src/lib_store/unix/test/test_store.ml index 1643dc91f755b2358a96a42de2f8fa0520e2cddf..729da6efbbb655d223fb5311ad78ed6d19c91148 100644 --- a/src/lib_store/unix/test/test_store.ml +++ b/src/lib_store/unix/test/test_store.ml @@ -246,34 +246,36 @@ let test_mem chain_store tbl = let*! () = test_not_mem "B6" in let*! () = test_not_mem "B8" in let* () = - let* prev_head = Store.Chain.set_head chain_store (vblock tbl "A6") in + let* prev_head = Store.Chain.set_head chain_store (vblock tbl "B7") in Assert.equal ~loc:__LOC__ prev_head (vblock tbl "A8") ; return_unit in let*! () = test_mem "A2" in - let*! () = test_mem "A3" in - let*! () = test_mem "A6" in + let*! () = test_not_mem "A3" in + let*! () = test_not_mem "A6" in let*! () = test_not_mem "A7" in let*! () = test_not_mem "A8" in - let*! () = test_not_mem "B1" in - let*! () = test_not_mem "B6" in + let*! () = test_mem "B1" in + let*! () = test_mem "B6" in + let*! () = test_mem "B7" in let*! () = test_not_mem "B8" in let* () = - let* prev_head = Store.Chain.set_head chain_store (vblock tbl "B6") in - Assert.equal ~loc:__LOC__ prev_head (vblock tbl "A6") ; + let* prev_head = Store.Chain.set_head chain_store (vblock tbl "A8") in + Assert.equal ~loc:__LOC__ prev_head (vblock tbl "B7") ; return_unit in let*! () = test_mem "A2" in - let*! () = test_not_mem "A3" in - let*! () = test_not_mem "A4" in - let*! () = test_not_mem "A6" in - let*! () = test_not_mem "A8" in - let*! () = test_mem "B1" in - let*! () = test_mem "B6" in + let*! () = test_mem "A3" in + let*! () = test_mem "A4" in + let*! () = test_mem "A6" in + let*! () = test_mem "A8" in + let*! () = test_not_mem "B1" in + let*! () = test_not_mem "B6" in + let*! () = test_not_mem "B7" in let*! () = test_not_mem "B8" in let* () = let* prev_head = Store.Chain.set_head chain_store (vblock tbl "B8") in - Assert.equal ~loc:__LOC__ prev_head (vblock tbl "B6") ; + Assert.equal ~loc:__LOC__ prev_head (vblock tbl "A8") ; return_unit in let*! () = test_mem "A2" in @@ -283,6 +285,7 @@ let test_mem chain_store tbl = let*! () = test_not_mem "A8" in let*! () = test_mem "B1" in let*! () = test_mem "B6" in + let*! () = test_mem "B7" in let*! () = test_mem "B8" in return_unit diff --git a/src/lib_store/unix/test/test_utils.ml b/src/lib_store/unix/test/test_utils.ml index a22805f12d85669abd44aebd929a1775f52493e6..26b434cf5de978bf4a3580f9e6daa8d179084f51 100644 --- a/src/lib_store/unix/test/test_utils.ml +++ b/src/lib_store/unix/test/test_utils.ml @@ -495,9 +495,7 @@ let store_raw_block chain_store ?resulting_context (raw_block : Block_repr.t) = message = Block_repr.message metadata; max_operations_ttl = Block_repr.max_operations_ttl metadata; last_finalized_block_level = - (* Not yet implemented. We use the last_preserved_block_level by - default.*) - Block_repr.last_preserved_block_level metadata; + Int32.(max 0l (sub (Block_repr.level raw_block) 2l)); last_preserved_block_level = Block_repr.last_preserved_block_level metadata; }; diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index a3fe1e08a3f541e8ef6776f0bd359e9b7982c2ac..d275e9f8c7721f0a965dd803fa392165b3dbb367 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -665,9 +665,9 @@ let finalize ?commit_message:message c fitness = message; max_operations_ttl = (Raw_context.constants c).max_operations_time_to_live; last_finalized_block_level = - Raw_level.to_int32 @@ Level.last_finalized_block_level c; + Raw_level.to_int32 (Level.last_finalized_block_level c); last_preserved_block_level = - Raw_level.to_int32 @@ Level.last_preserved_block_level c; + Raw_level.to_int32 (Level.last_preserved_block_level c); } let current_context c = Raw_context.recover c diff --git a/src/proto_alpha/lib_protocol/level_storage.ml b/src/proto_alpha/lib_protocol/level_storage.ml index 072ad1e9f18532c969078648d9c1416ff9a246a3..20be68068e99b3726d73317a9d26c0d5a521dca7 100644 --- a/src/proto_alpha/lib_protocol/level_storage.ml +++ b/src/proto_alpha/lib_protocol/level_storage.ml @@ -110,9 +110,10 @@ let last_preserved_block_level c = | Some cycle -> (first_level_in_cycle c cycle).level let last_finalized_block_level c = - (* Not implemented yet. We use the last_preserved_block_level by - default.*) - last_preserved_block_level c + let current = Raw_context.current_level c in + let current_raw = current.level in + let finalized_level = Raw_level_repr.sub current_raw 2 in + match finalized_level with None -> Raw_level_repr.root | Some l -> l let last_of_a_cycle ctxt level = let cycle_eras = Raw_context.cycle_eras ctxt in diff --git a/src/proto_demo_counter/lib_protocol/main.ml b/src/proto_demo_counter/lib_protocol/main.ml index c01034834b2c2ab85f18347734bdbc2fce02ef00..ea4a75c9a88792cb0921cdca9a7adbbd9e173ba4 100644 --- a/src/proto_demo_counter/lib_protocol/main.ml +++ b/src/proto_demo_counter/lib_protocol/main.ml @@ -263,11 +263,8 @@ let init _chain_id context block_header = context = init_context; fitness; max_operations_ttl = 0; - last_finalized_block_level = - (* Not yet implemented. We use the last_preserved_block_level by - default.*) - block_header.level; last_preserved_block_level = block_header.level; + last_finalized_block_level = Compare.Int32.max 0l Int32.(sub block_header.level 2l) ; } let compare_operations _ _ = 0 diff --git a/tezt/tests/storage_snapshots.ml b/tezt/tests/storage_snapshots.ml index c302169b16429552ad7eb08e22a91871593a3d16..8f94036c6592839ed1b41666c02cb7459a7c7d32 100644 --- a/tezt/tests/storage_snapshots.ml +++ b/tezt/tests/storage_snapshots.ml @@ -363,6 +363,13 @@ let test_drag_after_rolling_import = let* blocks_preservation_cycles, blocks_per_cycle, max_op_ttl = get_constants ~protocol client in + let finalized_block_distance = + match protocol with + | Nairobi | Oxford -> + (* Conservative TB finality *) + blocks_preservation_cycles * blocks_per_cycle + | Alpha -> (* TB finality *) 2 + in Log.info "Baking a few blocks" (* Baking enough blocks so that the caboose is not the genesis anymore (depending on the max_op_ttl)*) ; @@ -454,14 +461,21 @@ let test_drag_after_rolling_import = match history_mode with | Node.Full_history -> Test.fail "testing only rolling mode" | Node.Rolling_history -> - let checkpoint = - final_head - (blocks_preservation_cycles * blocks_per_cycle) - in + let checkpoint = final_head - finalized_block_distance in let savepoint = final_head - ((blocks_preservation_cycles + additional_cycles) * blocks_per_cycle) in - (checkpoint, savepoint, min savepoint (checkpoint - max_op_ttl)) + let caboose = + max + 0 + (min + savepoint + (final_head + - (blocks_preservation_cycles * blocks_per_cycle) + - max_op_ttl)) + in + (checkpoint, savepoint, caboose) in let* () = check_consistency_after_import