Shell: Extract the block-application function into a separate module

This commit is contained in:
Victor Allombert 2018-10-16 15:51:11 +02:00 committed by Grégoire Henry
parent 7cbfcfa608
commit ee640c8653
No known key found for this signature in database
GPG Key ID: 50D984F20BD445D2
12 changed files with 375 additions and 130 deletions

View File

@ -43,9 +43,10 @@ module Types = struct
include Worker_state include Worker_state
type state = { type state = {
protocol_validator: Protocol_validator.t ; protocol_validator: Protocol_validator.t ;
validation_process: Validator_process.t ;
limits : limits ; limits : limits ;
} }
type parameters = limits * Distributed_db.t type parameters = limits * Distributed_db.t * Validator_process.t
let view _state _parameters = () let view _state _parameters = ()
end end
@ -140,11 +141,10 @@ let may_patch_protocol
let apply_block let apply_block
chain_state chain_state
validation_process
pred (module Proto : Registered_protocol.T) pred (module Proto : Registered_protocol.T)
hash (header: Block_header.t) hash (header: Block_header.t)
operations = operations =
let pred_header = State.Block.header pred
and pred_hash = State.Block.hash pred in
check_header pred (List.length Proto.validation_passes) hash header >>=? fun () -> check_header pred (List.length Proto.validation_passes) hash header >>=? fun () ->
iteri2_p iteri2_p
(fun i ops quota -> (fun i ops quota ->
@ -176,92 +176,17 @@ let apply_block
fail (invalid_block hash Cannot_parse_block_header) fail (invalid_block hash Cannot_parse_block_header)
| Some protocol_data -> | Some protocol_data ->
return ({ shell = header.shell ; protocol_data } : Proto.block_header) return ({ shell = header.shell ; protocol_data } : Proto.block_header)
end >>=? fun header -> end >>=? fun _header ->
mapi2_s (fun pass -> map2_s begin fun op_hash op -> Validator_process.apply_block
match validation_process header operations chain_state
Data_encoding.Binary.of_bytes >>=? fun { validation_result ; block_data ; ops_metadata ; context_hash } ->
Proto.operation_data_encoding let validation_store =
op.Operation.proto with ({ context_hash ;
| None -> message = validation_result.message ;
fail (invalid_block hash (Cannot_parse_operation op_hash)) max_operations_ttl = validation_result.max_operations_ttl ;
| Some protocol_data -> last_allowed_fork_level = validation_result.last_allowed_fork_level} :
let op = { Proto.shell = op.shell ; protocol_data } in State.Block.validation_store) in
let allowed_pass = Proto.acceptable_passes op in return (validation_store, block_data, ops_metadata)
fail_unless (List.mem pass allowed_pass)
(invalid_block hash
(Unallowed_pass { operation = op_hash ;
pass ; allowed_pass } )) >>=? fun () ->
return op
end)
operation_hashes
operations >>=? fun parsed_operations ->
State.Block.context pred >>= fun pred_context ->
Context.reset_test_chain
pred_context pred_hash header.shell.timestamp >>= fun context ->
(* TODO wrap 'proto_error' into 'block_error' *)
Proto.begin_application
~chain_id: (State.Chain.id chain_state)
~predecessor_context:context
~predecessor_timestamp:pred_header.shell.timestamp
~predecessor_fitness:pred_header.shell.fitness
header >>=? fun state ->
fold_left_s
(fun (state, acc) ops ->
fold_left_s
(fun (state, acc) op ->
Proto.apply_operation state op >>=? fun (state, op_metadata) ->
return (state, op_metadata :: acc))
(state, []) ops >>=? fun (state, ops_metadata) ->
return (state, List.rev ops_metadata :: acc))
(state, []) parsed_operations >>=? fun (state, ops_metadata) ->
let ops_metadata = List.rev ops_metadata in
Proto.finalize_block state >>=? fun (validation_result, block_data) ->
may_patch_protocol
~level:header.shell.level validation_result >>=? fun validation_result ->
Context.get_protocol validation_result.context >>= fun new_protocol ->
let expected_proto_level =
if Protocol_hash.equal new_protocol Proto.hash then
pred_header.shell.proto_level
else
(pred_header.shell.proto_level + 1) mod 256 in
fail_when (header.shell.proto_level <> expected_proto_level)
(invalid_block hash @@ Invalid_proto_level {
found = header.shell.proto_level ;
expected = expected_proto_level ;
}) >>=? fun () ->
fail_when
Fitness.(validation_result.fitness <> header.shell.fitness)
(invalid_block hash @@ Invalid_fitness {
expected = header.shell.fitness ;
found = validation_result.fitness ;
}) >>=? fun () ->
begin
if Protocol_hash.equal new_protocol Proto.hash then
return validation_result
else
match Registered_protocol.get new_protocol with
| None ->
fail (Unavailable_protocol { block = hash ;
protocol = new_protocol })
| Some (module NewProto) ->
NewProto.init validation_result.context header.shell
end >>=? fun validation_result ->
let max_operations_ttl =
max 0
(min
((State.Block.max_operations_ttl pred)+1)
validation_result.max_operations_ttl) in
let validation_result =
{ validation_result with max_operations_ttl } in
let block_data =
Data_encoding.Binary.to_bytes_exn Proto.block_header_metadata_encoding block_data in
let ops_metadata =
List.map
(List.map
(Data_encoding.Binary.to_bytes_exn
Proto.operation_receipt_encoding))
ops_metadata in
return (validation_result, block_data, ops_metadata)
let check_chain_liveness chain_db hash (header: Block_header.t) = let check_chain_liveness chain_db hash (header: Block_header.t) =
let chain_state = Distributed_db.chain_state chain_db in let chain_state = Distributed_db.chain_state chain_db in
@ -282,6 +207,7 @@ let get_proto pred hash =
protocol = pred_protocol_hash }) protocol = pred_protocol_hash })
| Some p -> return p | Some p -> return p
let on_request let on_request
: type r. t -> r Request.t -> r tzresult Lwt.t : type r. t -> r Request.t -> r tzresult Lwt.t
= fun w = fun w
@ -313,6 +239,7 @@ let on_request
protect ?canceler begin fun () -> protect ?canceler begin fun () ->
apply_block apply_block
(Distributed_db.chain_state chain_db) (Distributed_db.chain_state chain_db)
bv.validation_process
pred proto hash pred proto hash
header operations >>=? fun (result, header_data, operations_data) -> header operations >>=? fun (result, header_data, operations_data) ->
Distributed_db.commit_block Distributed_db.commit_block
@ -343,9 +270,9 @@ let on_request
assert commited ; assert commited ;
return (Error errors) return (Error errors)
let on_launch _ _ (limits, db) = let on_launch _ _ (limits, db, validation_process) =
let protocol_validator = Protocol_validator.create db in let protocol_validator = Protocol_validator.create db in
Lwt.return { Types.protocol_validator ; limits } Lwt.return { Types.protocol_validator ; validation_process ; limits }
let on_error w r st errs = let on_error w r st errs =
Worker.record_event w (Validation_failure (r, st, errs)) ; Worker.record_event w (Validation_failure (r, st, errs)) ;
@ -366,14 +293,18 @@ let on_completion
(Event.Validation_failure (Request.view r, st, errs)) ; (Event.Validation_failure (Request.view r, st, errs)) ;
Lwt.return_unit Lwt.return_unit
let on_close w =
let bv = Worker.state w in
Validator_process.close bv.validation_process
let table = Worker.create_table Queue let table = Worker.create_table Queue
let create limits db = let create limits db validation_process_kind =
let module Handlers = struct let module Handlers = struct
type self = t type self = t
let on_launch = on_launch let on_launch = on_launch
let on_request = on_request let on_request = on_request
let on_close _ = Lwt.return_unit let on_close = on_close
let on_error = on_error let on_error = on_error
let on_completion = on_completion let on_completion = on_completion
let on_no_request _ = return_unit let on_no_request _ = return_unit
@ -382,7 +313,7 @@ let create limits db =
table table
limits.worker_limits limits.worker_limits
() ()
(limits, db) (limits, db, validation_process_kind)
(module Handlers) (module Handlers)
let shutdown = Worker.shutdown let shutdown = Worker.shutdown

View File

@ -33,7 +33,9 @@ type limits = {
type error += Closed of unit type error += Closed of unit
val create: val create:
limits -> Distributed_db.t -> t Lwt.t limits -> Distributed_db.t ->
Validator_process.t ->
t Lwt.t
val validate: val validate:
t -> t ->

View File

@ -146,7 +146,7 @@ val commit_block:
Block_hash.t -> Block_hash.t ->
Block_header.t -> MBytes.t -> Block_header.t -> MBytes.t ->
Operation.t list list -> MBytes.t list list -> Operation.t list list -> MBytes.t list list ->
Tezos_protocol_environment_shell.validation_result -> State.Block.validation_store ->
State.Block.t option tzresult Lwt.t State.Block.t option tzresult Lwt.t
(** Store on disk all the data associated to an invalid block. *) (** Store on disk all the data associated to an invalid block. *)

View File

@ -177,7 +177,8 @@ let create
?(sandboxed = false) ?(sandboxed = false)
{ genesis ; store_root ; context_root ; { genesis ; store_root ; context_root ;
patch_context ; p2p = p2p_params ; patch_context ; p2p = p2p_params ;
test_chain_max_tll = max_child_ttl ; checkpoint } test_chain_max_tll = max_child_ttl ;
checkpoint }
peer_validator_limits peer_validator_limits
block_validator_limits block_validator_limits
prevalidator_limits prevalidator_limits
@ -187,15 +188,18 @@ let create
| Some (config, _limits) -> not config.P2p.disable_mempool | Some (config, _limits) -> not config.P2p.disable_mempool
| None -> true in | None -> true in
init_p2p ~sandboxed p2p_params >>=? fun p2p -> init_p2p ~sandboxed p2p_params >>=? fun p2p ->
State.read State.init
~store_root ~context_root ?patch_context genesis >>=? fun (state, mainchain_state) -> ~store_root ~context_root ?patch_context genesis >>=? fun (state, mainchain_state) ->
may_update_checkpoint mainchain_state checkpoint >>= fun () -> may_update_checkpoint mainchain_state checkpoint >>= fun () ->
let distributed_db = Distributed_db.create state p2p in let distributed_db = Distributed_db.create state p2p in
Validator_process.(init ~context_root Internal) >>= fun validation_process ->
Validator.create state distributed_db Validator.create state distributed_db
peer_validator_limits peer_validator_limits
block_validator_limits block_validator_limits
validation_process
prevalidator_limits prevalidator_limits
chain_validator_limits >>= fun validator -> chain_validator_limits
>>= fun validator ->
Validator.activate validator Validator.activate validator
?max_child_ttl ~start_prevalidator mainchain_state >>= fun mainchain_validator -> ?max_child_ttl ~start_prevalidator mainchain_state >>= fun mainchain_validator ->
let shutdown () = let shutdown () =

View File

@ -625,6 +625,13 @@ module Block = struct
} }
type block = t type block = t
type validation_store = {
context_hash: Context_hash.t;
message: string option;
max_operations_ttl: int;
last_allowed_fork_level: Int32.t;
}
module Header = struct module Header = struct
type t = hashed_header = { type t = hashed_header = {
@ -833,8 +840,7 @@ module Block = struct
?(dont_enforce_context_hash = false) ?(dont_enforce_context_hash = false)
chain_state block_header block_header_metadata chain_state block_header block_header_metadata
operations operations_metadata operations operations_metadata
{ Tezos_protocol_environment_shell.context ; message ; { context_hash ; message ; max_operations_ttl ; last_allowed_fork_level } =
max_operations_ttl ; last_allowed_fork_level } =
let bytes = Block_header.to_bytes block_header in let bytes = Block_header.to_bytes block_header in
let hash = Block_header.hash_raw bytes in let hash = Block_header.hash_raw bytes in
fail_unless fail_unless
@ -872,8 +878,12 @@ module Block = struct
fail_unless fail_unless
acceptable_block acceptable_block
(Checkpoint_error (hash, None)) >>=? fun () -> (Checkpoint_error (hash, None)) >>=? fun () ->
Context.commit let commit = context_hash in
~time:block_header.shell.timestamp ?message context >>= fun commit -> Context.exists chain_state.context_index.data commit
>>= fun exists ->
fail_unless exists
(failure "State.Block.store: context hash not found in context")
>>=? fun _ ->
fail_unless fail_unless
(dont_enforce_context_hash (dont_enforce_context_hash
|| Context_hash.equal block_header.shell.context commit) || Context_hash.equal block_header.shell.context commit)
@ -1257,6 +1267,25 @@ let may_create_chain state chain genesis =
state genesis state genesis
let read let read
global_store
context_index
main_chain =
let global_data = {
chains = Chain_id.Table.create 17 ;
global_store ;
context_index ;
} in
let state = {
global_data = Shared.create global_data ;
protocol_store = Shared.create @@ Store.Protocol.get global_store ;
main_chain ;
protocol_watcher = Lwt_watcher.create_input () ;
block_watcher = Lwt_watcher.create_input () ;
} in
Chain.read_all state >>=? fun () ->
return state
let init
?patch_context ?patch_context
?(store_mapsize=4_096_000_000_000L) ?(store_mapsize=4_096_000_000_000L)
?(context_mapsize=40_960_000_000L) ?(context_mapsize=40_960_000_000L)
@ -1267,20 +1296,8 @@ let read
Context.init Context.init
~mapsize:context_mapsize ?patch_context ~mapsize:context_mapsize ?patch_context
context_root >>= fun context_index -> context_root >>= fun context_index ->
let global_data = {
chains = Chain_id.Table.create 17 ;
global_store ;
context_index ;
} in
let main_chain = Chain_id.of_block_hash genesis.Chain.block in let main_chain = Chain_id.of_block_hash genesis.Chain.block in
let state = { read global_store context_index main_chain >>=? fun state ->
global_data = Shared.create global_data ;
protocol_store = Shared.create @@ Store.Protocol.get global_store ;
main_chain ;
protocol_watcher = Lwt_watcher.create_input () ;
block_watcher = Lwt_watcher.create_input () ;
} in
Chain.read_all state >>=? fun () ->
may_create_chain state main_chain genesis >>= fun main_chain_state -> may_create_chain state main_chain genesis >>= fun main_chain_state ->
return (state, main_chain_state) return (state, main_chain_state)

View File

@ -119,6 +119,13 @@ module Block : sig
type t type t
type block = t type block = t
type validation_store = {
context_hash: Context_hash.t ;
message: string option ;
max_operations_ttl: int ;
last_allowed_fork_level: Int32.t ;
}
val known: Chain.t -> Block_hash.t -> bool Lwt.t val known: Chain.t -> Block_hash.t -> bool Lwt.t
val known_valid: Chain.t -> Block_hash.t -> bool Lwt.t val known_valid: Chain.t -> Block_hash.t -> bool Lwt.t
val known_invalid: Chain.t -> Block_hash.t -> bool Lwt.t val known_invalid: Chain.t -> Block_hash.t -> bool Lwt.t
@ -135,7 +142,7 @@ module Block : sig
Chain.t -> Chain.t ->
Block_header.t -> MBytes.t -> Block_header.t -> MBytes.t ->
Operation.t list list -> MBytes.t list list -> Operation.t list list -> MBytes.t list list ->
Tezos_protocol_environment_shell.validation_result -> validation_store ->
block option tzresult Lwt.t block option tzresult Lwt.t
val store_invalid: val store_invalid:
@ -311,7 +318,7 @@ end
(** Read the internal state of the node and initialize (** Read the internal state of the node and initialize
the databases. *) the databases. *)
val read: val init:
?patch_context:(Context.t -> Context.t Lwt.t) -> ?patch_context:(Context.t -> Context.t Lwt.t) ->
?store_mapsize:int64 -> ?store_mapsize:int64 ->
?context_mapsize:int64 -> ?context_mapsize:int64 ->

View File

@ -39,8 +39,8 @@ let genesis_time = Time.of_seconds 0L
let state_genesis_block = let state_genesis_block =
{ {
State.Chain.time = genesis_time; State.Chain.time = genesis_time ;
State.Chain.block= genesis_hash; State.Chain.block= genesis_hash ;
State.Chain.protocol = genesis_protocol State.Chain.protocol = genesis_protocol
} }
@ -70,7 +70,7 @@ let incr_fitness fitness =
let init_chain base_dir : State.Chain.t Lwt.t = let init_chain base_dir : State.Chain.t Lwt.t =
let store_root = base_dir // "store" in let store_root = base_dir // "store" in
let context_root = base_dir // "context" in let context_root = base_dir // "context" in
State.read State.init
~store_root ~context_root state_genesis_block >>= function ~store_root ~context_root state_genesis_block >>= function
| Error _ -> Pervasives.failwith "read err" | Error _ -> Pervasives.failwith "read err"
| Ok (_state, chain) -> | Ok (_state, chain) ->
@ -105,12 +105,15 @@ let make_empty_chain (chain:State.Chain.t) n : Block_hash.t Lwt.t =
State.Block.read_exn chain genesis_hash >>= fun genesis -> State.Block.read_exn chain genesis_hash >>= fun genesis ->
State.Block.context genesis >>= fun empty_context -> State.Block.context genesis >>= fun empty_context ->
let header = State.Block.header genesis in let header = State.Block.header genesis in
let timestamp = State.Block.timestamp genesis in
Context.hash ~time:timestamp empty_context
>>= fun empty_context_hash ->
Context.commit Context.commit
~time:header.shell.timestamp empty_context >>= fun context -> ~time:header.shell.timestamp empty_context >>= fun context ->
let header = { header with shell = { header.shell with context } } in let header = { header with shell = { header.shell with context } } in
let empty_result : Tezos_protocol_environment_shell.validation_result = { let empty_result = {
context = empty_context ; State.Block.
fitness = [] ; context_hash = empty_context_hash ;
message = None ; message = None ;
max_operations_ttl = 0 ; max_operations_ttl = 0 ;
last_allowed_fork_level = 0l ; last_allowed_fork_level = 0l ;

View File

@ -121,8 +121,15 @@ let build_valid_chain state vtbl pred names =
(* no operations *) (* no operations *)
Proto.finalize_block vstate Proto.finalize_block vstate
end >>=? fun (ctxt, _metadata) -> end >>=? fun (ctxt, _metadata) ->
Context.commit ~time:block.shell.timestamp ctxt.context
>>= fun context_hash ->
State.Block.store state State.Block.store state
block zero [[op]] [[zero]] ctxt >>=? fun _vblock -> block zero [[op]] [[zero]]
({context_hash;
message = ctxt.message;
max_operations_ttl = ctxt.max_operations_ttl;
last_allowed_fork_level = ctxt.last_allowed_fork_level} :
State.Block.validation_store) >>=? fun _vblock ->
State.Block.read state hash >>=? fun vblock -> State.Block.read state hash >>=? fun vblock ->
Hashtbl.add vtbl name vblock ; Hashtbl.add vtbl name vblock ;
return vblock return vblock
@ -168,7 +175,7 @@ let wrap_state_init f base_dir =
begin begin
let store_root = base_dir // "store" in let store_root = base_dir // "store" in
let context_root = base_dir // "context" in let context_root = base_dir // "context" in
State.read State.init
~store_mapsize:4_096_000_000L ~store_mapsize:4_096_000_000L
~context_mapsize:4_096_000_000L ~context_mapsize:4_096_000_000L
~store_root ~store_root

View File

@ -43,12 +43,15 @@ type t = {
let create state db let create state db
peer_validator_limits peer_validator_limits
block_validator_limits block_validator_limits
validation_process
prevalidator_limits prevalidator_limits
chain_validator_limits = chain_validator_limits
Block_validator.create block_validator_limits db >>= fun block_validator -> =
Block_validator.create block_validator_limits db validation_process >>= fun block_validator ->
let valid_block_input = Lwt_watcher.create_input () in let valid_block_input = Lwt_watcher.create_input () in
Lwt.return Lwt.return
{ state ; db ; block_validator ; { state ; db ;
block_validator ;
block_validator_limits ; prevalidator_limits ; block_validator_limits ; prevalidator_limits ;
peer_validator_limits ; chain_validator_limits ; peer_validator_limits ; chain_validator_limits ;
valid_block_input ; valid_block_input ;
@ -67,7 +70,8 @@ let activate v ?max_child_ttl ~start_prevalidator chain_state =
?max_child_ttl ?max_child_ttl
~start_prevalidator ~start_prevalidator
v.peer_validator_limits v.prevalidator_limits v.peer_validator_limits v.prevalidator_limits
v.block_validator v.valid_block_input v.db chain_state v.block_validator
v.valid_block_input v.db chain_state
v.chain_validator_limits in v.chain_validator_limits in
Chain_id.Table.add v.active_chains chain_id nv ; Chain_id.Table.add v.active_chains chain_id nv ;
nv nv

View File

@ -32,6 +32,7 @@ val create:
Distributed_db.t -> Distributed_db.t ->
Peer_validator.limits -> Peer_validator.limits ->
Block_validator.limits -> Block_validator.limits ->
Validator_process.t ->
Prevalidator.limits -> Prevalidator.limits ->
Chain_validator.limits -> Chain_validator.limits ->
t Lwt.t t Lwt.t

View File

@ -0,0 +1,223 @@
(*****************************************************************************)
(* *)
(* Open Source License *)
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* *)
(* Permission is hereby granted, free of charge, to any person obtaining a *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)
(* and/or sell copies of the Software, and to permit persons to whom the *)
(* Software is furnished to do so, subject to the following conditions: *)
(* *)
(* The above copyright notice and this permission notice shall be included *)
(* in all copies or substantial portions of the Software. *)
(* *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)
(* DEALINGS IN THE SOFTWARE. *)
(* *)
(*****************************************************************************)
type application_result = {
validation_result: Tezos_protocol_environment_shell.validation_result ;
block_data: Secp256k1.watermark ;
ops_metadata: Secp256k1.watermark list list ;
context_hash: Context_hash.t ;
}
type error +=
| Failed_to_checkout_context of Context_hash.t
let () =
register_error_kind
`Permanent
~id:"Validator_process.failed_to_checkout_context"
~title: "Fail during checkout context"
~description: "The context checkout failed using a given hash"
~pp:(fun ppf (hash:Context_hash.t) ->
Format.fprintf ppf
"@[Failed to checkout the context with hash %a@]"
Context_hash.pp_short hash)
Data_encoding.(obj1 (req "hash" Context_hash.encoding))
(function
| Failed_to_checkout_context h -> Some h
| _ -> None)
(fun h -> Failed_to_checkout_context h)
(** The standard block validation method *)
module SeqValidator = struct
include Logging.Make (struct let name = "sequential validator process" end)
type validation_context = {
context_index : Context.index ;
}
type t = validation_context
let init context_root =
lwt_log_notice "Intialized" >>= fun _ ->
Context.init context_root >>= fun context_index ->
Lwt.return { context_index }
let close _ =
lwt_log_notice "Shutting down ..." >>= fun _ ->
Lwt.return ()
let get_context index hash =
Context.checkout index hash >>= function
| None -> fail (Failed_to_checkout_context hash)
| Some ctx -> return ctx
open Block_validator_errors
let invalid_block block error = Invalid_block { block ; error }
let apply_block
validator_process
(header : Block_header.t)
operations
chain_state =
State.Block.read
chain_state header.shell.predecessor >>=? fun pred ->
State.Block.context pred >>= fun pred_context ->
Context.get_protocol pred_context >>= fun pred_protocol_hash ->
let hash = Block_header.hash header in
let chain_id = State.Chain.id chain_state in
begin
match Registered_protocol.get pred_protocol_hash with
| None ->
fail (Unavailable_protocol { block = hash ;
protocol = pred_protocol_hash })
| Some p -> return p
end >>=? fun (module Proto) ->
let pred_header = State.Block.header pred in
get_context
validator_process.context_index
pred_header.shell.context >>=? fun pred_context ->
let pred_hash = State.Block.hash pred in
Context.reset_test_chain
pred_context pred_hash header.shell.timestamp >>= fun context ->
let max_operations_ttl = State.Block.max_operations_ttl pred in
let operation_hashes = List.map (List.map Operation.hash) operations in
begin
match
Data_encoding.Binary.of_bytes
Proto.block_header_data_encoding
header.protocol_data with
| None ->
fail (invalid_block hash Cannot_parse_block_header)
| Some protocol_data ->
return ({ shell = header.shell ; protocol_data } : Proto.block_header)
end >>=? fun header ->
mapi2_s (fun pass -> map2_s begin fun op_hash op ->
match
Data_encoding.Binary.of_bytes
Proto.operation_data_encoding
op.Operation.proto with
| None ->
fail (invalid_block hash (Cannot_parse_operation op_hash))
| Some protocol_data ->
let op = { Proto.shell = op.shell ; protocol_data } in
let allowed_pass = Proto.acceptable_passes op in
fail_unless (List.mem pass allowed_pass)
(invalid_block hash
(Unallowed_pass { operation = op_hash ;
pass ; allowed_pass } )) >>=? fun () ->
return op
end)
operation_hashes
operations >>=? fun parsed_operations ->
(* TODO wrap 'proto_error' into 'block_error' *)
Proto.begin_application
~chain_id: chain_id
~predecessor_context:context
~predecessor_timestamp:pred_header.shell.timestamp
~predecessor_fitness:pred_header.shell.fitness
header >>=? fun state ->
fold_left_s
(fun (state, acc) ops ->
fold_left_s
(fun (state, acc) op ->
Proto.apply_operation state op >>=? fun (state, op_metadata) ->
return (state, op_metadata :: acc))
(state, []) ops >>=? fun (state, ops_metadata) ->
return (state, List.rev ops_metadata :: acc))
(state, []) parsed_operations >>=? fun (state, ops_metadata) ->
let ops_metadata = List.rev ops_metadata in
Proto.finalize_block state >>=? fun (validation_result, block_data) ->
Context.get_protocol validation_result.context >>= fun new_protocol ->
let expected_proto_level =
if Protocol_hash.equal new_protocol Proto.hash then
pred_header.shell.proto_level
else
(pred_header.shell.proto_level + 1) mod 256 in
fail_when (header.shell.proto_level <> expected_proto_level)
(invalid_block hash @@ Invalid_proto_level {
found = header.shell.proto_level ;
expected = expected_proto_level ;
}) >>=? fun () ->
fail_when
Fitness.(validation_result.fitness <> header.shell.fitness)
(invalid_block hash @@ Invalid_fitness {
expected = header.shell.fitness ;
found = validation_result.fitness ;
}) >>=? fun () ->
begin
if Protocol_hash.equal new_protocol Proto.hash then
return validation_result
else
match Registered_protocol.get new_protocol with
| None ->
fail (Unavailable_protocol { block = hash ;
protocol = new_protocol })
| Some (module NewProto) ->
NewProto.init validation_result.context header.shell
end >>=? fun validation_result ->
let max_operations_ttl =
max 0
(min
((max_operations_ttl)+1)
validation_result.max_operations_ttl) in
let validation_result =
{ validation_result with max_operations_ttl } in
let block_data =
Data_encoding.Binary.to_bytes_exn
Proto.block_header_metadata_encoding block_data in
let ops_metadata =
List.map
(List.map
(Data_encoding.Binary.to_bytes_exn
Proto.operation_receipt_encoding))
ops_metadata in
Context.commit
~time:header.shell.timestamp
?message:validation_result.message
validation_result.context >>= fun context_hash ->
return ({ validation_result ; block_data ;
ops_metadata ; context_hash })
end
type kind =
| Internal
type t =
| Sequential of SeqValidator.t
let init ~context_root = function
| Internal ->
SeqValidator.init context_root >>= fun v ->
Lwt.return (Sequential v)
let close = function
| Sequential vp -> SeqValidator.close vp
let apply_block = function
| Sequential vp -> SeqValidator.apply_block vp

View File

@ -0,0 +1,46 @@
(*****************************************************************************)
(* *)
(* Open Source License *)
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* *)
(* Permission is hereby granted, free of charge, to any person obtaining a *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)
(* and/or sell copies of the Software, and to permit persons to whom the *)
(* Software is furnished to do so, subject to the following conditions: *)
(* *)
(* The above copyright notice and this permission notice shall be included *)
(* in all copies or substantial portions of the Software. *)
(* *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)
(* DEALINGS IN THE SOFTWARE. *)
(* *)
(*****************************************************************************)
type kind =
| Internal
type t
val init : context_root:string -> kind -> t Lwt.t
val close : t -> unit Lwt.t
type application_result = {
validation_result: Tezos_protocol_environment_shell.validation_result ;
block_data: Secp256k1.watermark ;
ops_metadata: Secp256k1.watermark list list ;
context_hash: Context_hash.t ;
}
val apply_block :
t ->
Block_header.t ->
Operation.t list list ->
State.Chain.t ->
application_result tzresult Lwt.t