123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2021 Nomadic Labs, <contact@nomadic-labs.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. *)(* *)(*****************************************************************************)moduleStore=Tezos_context_memory.ContextmoduleProof=Tezos_context_sigs.Context.Proof_typesmoduleStorelike=structincludeStoretypetree=Store.treeletfind=Store.Tree.findletfind_tree=Store.Tree.find_treeletunshallow=Tree.unshallowendmoduleGet_data=Tezos_context_sigs.Context.With_get_data((Storelike:Tezos_context_sigs.Context.Storelike))typeinput={printer:Tezos_client_base.Client_context.printer;min_agreement:float;chain:Tezos_shell_services.Block_services.chain;block:Tezos_shell_services.Block_services.block;key:stringlist;mproof:Proof.treeProof.t;}letkey_to_string=String.concat";"letmin_agreeing_endpointsmin_agreementnb_endpoints=min_agreement*.float_of_intnb_endpoints|>Float.ceil|>int_of_floatmoduleMake(Light_proto:Light_proto.PROTO_RPCS)=structtypevalidation_result=Valid|Invalidofstringletvalidateurikey(data_proof:Proof.treeProof.t)(incoming_mproof:Proof.treeProof.toptiontzresult)=matchincoming_mproofwith|Errortrace->Lwt.return@@Invalid(Format.asprintf"Light mode: endpoint %s failed to provide merkle tree for key \
%s. Error is: %a"(Uri.to_stringuri)(key_to_stringkey)pp_print_tracetrace)|OkNone->Lwt.return@@Invalid(Format.asprintf"Light mode: endpoint %s doesn't contain key %s"(Uri.to_stringuri)(key_to_stringkey))|Ok(Somemproof)->(letopenLwt_syntaxinlet*res=Store.verify_tree_proofmproof(Get_data.get_dataProof.Hole[key])inmatchreswith|Ok_->ifProof.proof_hash_eqmproofdata_proofthenreturnValidelsereturn(Invalid"Light mode: proofs were not equal")|Error_->return(Invalid"Light mode: proof could not be verified to derive a tree"))letcount_validsvalidations=letcount_valid=functionValid->1|Invalid_->0inList.fold_left(funsumvalidation->sum+count_validvalidation)0validationsletwarn_invalids(printer:Tezos_client_base.Client_context.printer)validations=Lwt_list.iter_s(funv->matchvwith|Valid->Lwt.return_unit|Invaliderrmsg->printer#warning"%s\n"errmsg)validationsletconsensus({printer;min_agreement;chain;block;key;mproof}:input)validating_endpoints=letopenLwt_syntaxin(* + 1 because there's the endpoint that provides data, that doesn't
validate *)letnb_endpoints=List.lengthvalidating_endpoints+1inletmin_agreeing_endpoints=min_agreeing_endpointsmin_agreementnb_endpointsinassert(min_agreeing_endpoints<=nb_endpoints);(* When checking that shapes agree, we must ignore the key where the
data is, because the validating endpoints return trees that do NOT
contain this key. *)letcheck_merkle_tree_with_endpoint(uri,rpc_context)=let*other_mproof=Light_proto.merkle_tree({rpc_context;chain;block;mode=Client}:Proxy.proxy_getter_input)keyProof.Holeinvalidateurikeymproofother_mproofinlet*validations=Lwt_list.map_pcheck_merkle_tree_with_endpointvalidating_endpointsin(* +1 because the endpoint that provided data obviously agrees *)letnb_agreements=count_validsvalidations+1inletagreement_reached=nb_agreements>=min_agreeing_endpointsinlet*()=warn_invalidsprintervalidationsinlet*()=ifagreement_reachedthenLwt.return_unitelseprinter#warning"Light mode: min_agreement=%f, %d endpoints, %s%d agreeing \
endpoints, whereas %d (%d*%f rounded up) is the minimum; so about \
to fail."min_agreementnb_endpoints(ifnb_agreements>0then"only "else"")nb_agreementsmin_agreeing_endpointsnb_endpointsmin_agreementinreturnagreement_reachedend