123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2022 G.B. Fefe, <gb.fefe@protonmail.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. *)(* *)(*****************************************************************************)typeerror+=|Invalid_consensus_key_update_noopofCycle_repr.t|Invalid_consensus_key_update_active|Invalid_consensus_key_update_tz4ofBls.Public_key.tlet()=register_error_kind`Permanent~id:"delegate.consensus_key.invalid_noop"~title:"Invalid key for consensus key update"~description:"Tried to update the consensus key with the active key"~pp:(funppfcycle->Format.fprintfppf"Invalid key while updating a consensus key (already active since %a)."Cycle_repr.ppcycle)Data_encoding.(obj1(req"cycle"Cycle_repr.encoding))(functionInvalid_consensus_key_update_noopc->Somec|_->None)(func->Invalid_consensus_key_update_noopc);register_error_kind`Permanent~id:"delegate.consensus_key.active"~title:"Active consensus key"~description:"The delegate consensus key is already used by another delegate"~pp:(funppf()->Format.fprintfppf"The delegate consensus key is already used by another delegate")Data_encoding.empty(functionInvalid_consensus_key_update_active->Some()|_->None)(fun()->Invalid_consensus_key_update_active);register_error_kind`Permanent~id:"delegate.consensus_key.tz4"~title:"Consensus key cannot be a tz4"~description:"Consensus key cannot be a tz4 (BLS public key)."~pp:(funppfpk->Format.fprintfppf"The consensus key %a is forbidden as it is a BLS public key."Bls.Public_key_hash.pp(Bls.Public_key.hashpk))Data_encoding.(obj1(req"delegate_pk"Bls.Public_key.encoding))(functionInvalid_consensus_key_update_tz4pk->Somepk|_->None)(funpk->Invalid_consensus_key_update_tz4pk)typepk=Raw_context.consensus_pk={delegate:Signature.Public_key_hash.t;consensus_pk:Signature.Public_key.t;consensus_pkh:Signature.Public_key_hash.t;}typet={delegate:Signature.Public_key_hash.t;consensus_pkh:Signature.Public_key_hash.t;}letpkh{delegate;consensus_pkh;consensus_pk=_}={delegate;consensus_pkh}letzero={consensus_pkh=Signature.Public_key_hash.zero;delegate=Signature.Public_key_hash.zero;}letppppf{delegate;consensus_pkh}=Format.fprintfppf"@[<v 2>%a"Signature.Public_key_hash.ppdelegate;ifnot(Signature.Public_key_hash.equaldelegateconsensus_pkh)thenFormat.fprintfppf"@,Active key: %a"Signature.Public_key_hash.ppconsensus_pkh;Format.fprintfppf"@]"(* Invariant:
No two delegates use the same active consensus key at a given time.
To ensure that, {!Storage.Consensus_keys} contains keys that will be active
at cycle `current + preserved_cycles + 1`.
*)letcheck_unusedctxtpkh=letopenLwt_result_syntaxinlet*!is_active=Storage.Consensus_keys.memctxtpkhinfail_whenis_activeInvalid_consensus_key_update_activeletcheck_not_tz4:Signature.Public_key.t->unittzresult=letopenResult_syntaxinfunction|Blspk->tzfail(Invalid_consensus_key_update_tz4pk)|Ed25519_|Secp256k1_|P256_->return_unitletset_unused=Storage.Consensus_keys.removeletset_used=Storage.Consensus_keys.addletinitctxtdelegatepk=letopenLwt_result_syntaxinlet*?()=check_not_tz4pkinletpkh=Signature.Public_key.hashpkinlet*()=check_unusedctxtpkhinlet*!ctxt=set_usedctxtpkhinStorage.Contract.Consensus_key.initctxt(Contract_repr.Implicitdelegate)pkletactive_pubkeyctxtdelegate=letopenLwt_result_syntaxinlet*pk=Storage.Contract.Consensus_key.getctxt(Contract_repr.Implicitdelegate)inletpkh=Signature.Public_key.hashpkinreturn{consensus_pk=pk;consensus_pkh=pkh;delegate}letactive_keyctxtdelegate=letopenLwt_result_syntaxinlet*pk=active_pubkeyctxtdelegateinreturn(pkhpk)letraw_pending_updatesctxt?up_to_cycledelegate=letopenLwt_result_syntaxinletrelevant_cycles=letlevel=Raw_context.current_levelctxtinletfirst_cycle=Cycle_repr.succlevel.cycleinletlast_cycle=matchup_to_cyclewith|None->letpreserved_cycles=Constants_storage.preserved_cyclesctxtinCycle_repr.addfirst_cyclepreserved_cycles|Somecycle->cycleinCycle_repr.(first_cycle--->last_cycle)inletdelegate=Contract_repr.ImplicitdelegateinList.filter_map_es(funcycle->let*pending_for_cycle=Storage.Pending_consensus_keys.find(ctxt,cycle)delegateinpending_for_cycle|>Option.map(funpk->(cycle,pk))|>return)relevant_cyclesletpending_updatesctxtdelegate=letopenLwt_result_syntaxinlet*updates=raw_pending_updatesctxtdelegateinreturn(List.map(fun(c,pk)->(c,Signature.Public_key.hashpk,pk))updates)letraw_active_pubkey_for_cyclectxtdelegatecycle=letopenLwt_result_syntaxinlet*pendings=raw_pending_updatesctxt~up_to_cycle:cycledelegateinlet*active=active_pubkeyctxtdelegateinletcurrent_cycle=(Raw_context.current_levelctxt).cycleinmatchList.hd(List.revpendings)with|None->return(current_cycle,active.consensus_pk)|Some(cycle,pk)->return(cycle,pk)letactive_pubkey_for_cyclectxtdelegatecycle=letopenLwt_result_syntaxinlet+_,consensus_pk=raw_active_pubkey_for_cyclectxtdelegatecyclein{consensus_pk;consensus_pkh=Signature.Public_key.hashconsensus_pk;delegate;}letregister_updatectxtdelegatepk=letopenLwt_result_syntaxinletupdate_cycle=letcurrent_level=Raw_context.current_levelctxtinletpreserved_cycles=Constants_storage.preserved_cyclesctxtinCycle_repr.addcurrent_level.cycle(preserved_cycles+1)inlet*()=let*first_active_cycle,active_pubkey=raw_active_pubkey_for_cyclectxtdelegateupdate_cycleinfail_whenSignature.Public_key.(pk=active_pubkey)(Invalid_consensus_key_update_noopfirst_active_cycle)inlet*?()=check_not_tz4pkinletpkh=Signature.Public_key.hashpkinlet*()=check_unusedctxtpkhinlet*!ctxt=set_usedctxtpkhinlet*{consensus_pkh=old_pkh;_}=active_pubkey_for_cyclectxtdelegateupdate_cycleinlet*!ctxt=set_unusedctxtold_pkhinlet*!ctxt=Storage.Pending_consensus_keys.add(ctxt,update_cycle)(Contract_repr.Implicitdelegate)pkinreturnctxtletactivatectxt~new_cycle=letopenLwt_syntaxinlet*ctxt=Storage.Pending_consensus_keys.fold(ctxt,new_cycle)~order:`Undefined~init:ctxt~f:(fundelegatepkctxt->Storage.Contract.Consensus_key.addctxtdelegatepk)inStorage.Pending_consensus_keys.clear(ctxt,new_cycle)