123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2019 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. *)(* *)(*****************************************************************************)openProtocol_client_contextopenProtocolopenAlpha_contexttypeerror+=Contract_has_no_scriptofContract.ttypeerror+=|Not_a_supported_multisig_contractof(Script_expr_hash.t*Script.expr)typeerror+=Contract_has_no_storageofContract.ttypeerror+=Contract_has_unexpected_storageofContract.ttypeerror+=Invalid_signatureofsignaturetypeerror+=Not_enough_signaturesofint*inttypeerror+=Action_deserialisation_errorofScript.exprtypeerror+=Bytes_deserialisation_errorofBytes.ttypeerror+=Bad_deserialized_contractof(Contract.t*Contract.t)typeerror+=Bad_deserialized_counterof(counter*counter)typeerror+=Non_positive_thresholdofinttypeerror+=Threshold_too_highofint*intlet()=register_error_kind`Permanent~id:"contractHasNoScript"~title:"The given contract is not a multisig contract because it has no script"~description:"A multisig command has referenced a scriptless smart contract instead \
of a multisig smart contract."~pp:(funppfcontract->Format.fprintfppf"Contract has no script %a."Contract.ppcontract)Data_encoding.(obj1(req"contract"Contract.encoding))(functionContract_has_no_scriptc->Somec|_->None)(func->Contract_has_no_scriptc);register_error_kind`Permanent~id:"notASupportedMultisigContract"~title:"The given contract is not one of the supported contracts"~description:"A multisig command has referenced a smart contract whose script is not \
one of the known multisig contract scripts."~pp:(funppf(hash,script)->Format.fprintfppf"Not a supported multisig contract %a.@\n\
The hash of this script is %a, it was not found among in the list of \
known multisig script hashes."Michelson_v1_printer.print_exprscriptScript_expr_hash.pphash)Data_encoding.(obj2(req"hash"Script_expr_hash.encoding)(req"script"Script.expr_encoding))(function|Not_a_supported_multisig_contract(h,c)->Some(h,c)|_->None)(fun(h,c)->Not_a_supported_multisig_contract(h,c));register_error_kind`Permanent~id:"contractHasNoStorage"~title:"The given contract is not a multisig contract because it has no storage"~description:"A multisig command has referenced a smart contract without storage \
instead of a multisig smart contract."~pp:(funppfcontract->Format.fprintfppf"Contract has no storage %a."Contract.ppcontract)Data_encoding.(obj1(req"contract"Contract.encoding))(functionContract_has_no_storagec->Somec|_->None)(func->Contract_has_no_storagec);register_error_kind`Permanent~id:"contractHasUnexpectedStorage"~title:"The storage of the given contract is not of the shape expected for a \
multisig contract"~description:"A multisig command has referenced a smart contract whose storage is of \
a different shape than the expected one."~pp:(funppfcontract->Format.fprintfppf"Contract has unexpected storage %a."Contract.ppcontract)Data_encoding.(obj1(req"contract"Contract.encoding))(functionContract_has_unexpected_storagec->Somec|_->None)(func->Contract_has_unexpected_storagec);register_error_kind`Permanent~id:"invalidSignature"~title:"The following signature did not match a public key in the given \
multisig contract"~description:"A signature was given for a multisig contract that matched none of the \
public keys of the contract signers"~pp:(funppfs->Format.fprintfppf"Invalid signature %s."(Tezos_crypto.Signature.V0.to_b58checks))Data_encoding.(obj1(req"invalid_signature"Tezos_crypto.Signature.V0.encoding))(functionInvalid_signatures->Somes|_->None)(funs->Invalid_signatures);register_error_kind`Permanent~id:"notEnoughSignatures"~title:"Not enough signatures were provided for this multisig action"~description:"To run an action on a multisig contract, you should provide at least as \
many signatures as indicated by the threshold stored in the multisig \
contract."~pp:(funppf(threshold,nsigs)->Format.fprintfppf"Not enough signatures: only %d signatures were given but the \
threshold is currently %d"nsigsthreshold)Data_encoding.(obj1(req"threshold_nsigs"(tup2int31int31)))(function|Not_enough_signatures(threshold,nsigs)->Some(threshold,nsigs)|_->None)(fun(threshold,nsigs)->Not_enough_signatures(threshold,nsigs));register_error_kind`Permanent~id:"actionDeserialisation"~title:"The expression is not a valid multisig action"~description:"When trying to deserialise an action from a sequence of bytes, we got \
an expression that does not correspond to a known multisig action"~pp:(funppfe->Format.fprintfppf"Action deserialisation error %a."Michelson_v1_printer.print_expre)Data_encoding.(obj1(req"expr"Script.expr_encoding))(functionAction_deserialisation_errore->Somee|_->None)(fune->Action_deserialisation_errore);register_error_kind`Permanent~id:"bytesDeserialisation"~title:"The byte sequence is not a valid multisig action"~description:"When trying to deserialise an action from a sequence of bytes, we got \
an error"~pp:(funppfb->Format.fprintfppf"Bytes deserialisation error %s."(Bytes.to_stringb))Data_encoding.(obj1(req"expr"bytes))(functionBytes_deserialisation_errorb->Someb|_->None)(funb->Bytes_deserialisation_errorb);register_error_kind`Permanent~id:"badDeserializedContract"~title:"The byte sequence is not for the given multisig contract"~description:"When trying to deserialise an action from a sequence of bytes, we got \
an action for another multisig contract"~pp:(funppf(received,expected)->Format.fprintfppf"Bad deserialized contract, received %a expected %a."Contract.ppreceivedContract.ppexpected)Data_encoding.(obj1(req"received_expected"(tup2Contract.encodingContract.encoding)))(functionBad_deserialized_contractb->Someb|_->None)(funb->Bad_deserialized_contractb);register_error_kind`Permanent~id:"Bad deserialized counter"~title:"Deserialized counter does not match the stored one"~description:"The byte sequence references a multisig counter that does not match the \
one currently stored in the given multisig contract"~pp:(funppf(received,expected)->Format.fprintfppf"Bad deserialized counter, received %d expected %d."receivedexpected)Data_encoding.(obj1(req"received_expected"(tup2int31int31)))(function|Bad_deserialized_counter(c1,c2)->Some(Z.to_intc1,Z.to_intc2)|_->None)(fun(c1,c2)->Bad_deserialized_counter(Z.of_intc1,Z.of_intc2));register_error_kind`Permanent~id:"thresholdTooHigh"~title:"Given threshold is too high"~description:"The given threshold is higher than the number of keys, this would lead \
to a frozen multisig contract"~pp:(funppf(threshold,nkeys)->Format.fprintfppf"Threshold too high: %d expected at most %d."thresholdnkeys)Data_encoding.(obj1(req"received_expected"(tup2int31int31)))(functionThreshold_too_high(c1,c2)->Some(c1,c2)|_->None)(fun(c1,c2)->Threshold_too_high(c1,c2));register_error_kind`Permanent~id:"nonPositiveThreshold"~title:"Given threshold is not positive"~description:"A multisig threshold should be a positive number"~pp:(funppfthreshold->Format.fprintfppf"Multisig threshold %d should be positive."threshold)Data_encoding.(obj1(req"threshold"int31))(functionNon_positive_thresholdt->Somet|_->None)(funt->Non_positive_thresholdt)(* The multisig contract script written by Arthur Breitman
https://github.com/murbard/smart-contracts/blob/master/multisig/michelson/multisig.tz *)(* Updated to take the chain id into account *)letmultisig_script_string="parameter (pair\n\
\ (pair :payload\n\
\ (nat %counter) # counter, used to prevent replay attacks\n\
\ (or :action # payload to sign, represents the requested \
action\n\
\ (pair :transfer # transfer tokens\n\
\ (mutez %amount) # amount to transfer\n\
\ (contract %dest unit)) # destination to transfer to\n\
\ (or\n\
\ (option %delegate key_hash) # change the delegate to \
this address\n\
\ (pair %change_keys # change the keys \
controlling the multisig\n\
\ (nat %threshold) # new threshold\n\
\ (list %keys key))))) # new list of keys\n\
\ (list %sigs (option signature))); # signatures\n\n\
storage (pair (nat %stored_counter) (pair (nat %threshold) (list %keys \
key))) ;\n\n\
code\n\
\ {\n\
\ UNPAIR ; SWAP ; DUP ; DIP { SWAP } ;\n\
\ DIP\n\
\ {\n\
\ UNPAIR ;\n\
\ # pair the payload with the current contract address, to ensure \
signatures\n\
\ # can't be replayed across different contracts if a key is reused.\n\
\ DUP ; SELF ; ADDRESS ; CHAIN_ID ; PAIR ; PAIR ;\n\
\ PACK ; # form the binary payload that we expect to be signed\n\
\ DIP { UNPAIR @counter ; DIP { SWAP } } ; SWAP\n\
\ } ;\n\n\
\ # Check that the counters match\n\
\ UNPAIR @stored_counter; DIP { SWAP };\n\
\ ASSERT_CMPEQ ;\n\n\
\ # Compute the number of valid signatures\n\
\ DIP { SWAP } ; UNPAIR @threshold @keys;\n\
\ DIP\n\
\ {\n\
\ # Running count of valid signatures\n\
\ PUSH @valid nat 0; SWAP ;\n\
\ ITER\n\
\ {\n\
\ DIP { SWAP } ; SWAP ;\n\
\ IF_CONS\n\
\ {\n\
\ IF_SOME\n\
\ { SWAP ;\n\
\ DIP\n\
\ {\n\
\ SWAP ; DIIP { DUUP } ;\n\
\ # Checks signatures, fails if invalid\n\
\ { DUUUP; DIP {CHECK_SIGNATURE}; SWAP; IF {DROP} \
{FAILWITH} };\n\
\ PUSH nat 1 ; ADD @valid } }\n\
\ { SWAP ; DROP }\n\
\ }\n\
\ {\n\
\ # There were fewer signatures in the list\n\
\ # than keys. Not all signatures must be present, but\n\
\ # they should be marked as absent using the option type.\n\
\ FAIL\n\
\ } ;\n\
\ SWAP\n\
\ }\n\
\ } ;\n\
\ # Assert that the threshold is less than or equal to the\n\
\ # number of valid signatures.\n\
\ ASSERT_CMPLE ;\n\
\ DROP ; DROP ;\n\n\
\ # Increment counter and place in storage\n\
\ DIP { UNPAIR ; PUSH nat 1 ; ADD @new_counter ; PAIR} ;\n\n\
\ # We have now handled the signature verification part,\n\
\ # produce the operation requested by the signers.\n\
\ NIL operation ; SWAP ;\n\
\ IF_LEFT\n\
\ { # Transfer tokens\n\
\ UNPAIR ; UNIT ; TRANSFER_TOKENS ; CONS }\n\
\ { IF_LEFT {\n\
\ # Change delegate\n\
\ SET_DELEGATE ; CONS }\n\
\ {\n\
\ # Change set of signatures\n\
\ DIP { SWAP ; CAR } ; SWAP ; PAIR ; SWAP }} ;\n\
\ PAIR }\n"(* Client_proto_context.originate expects the contract script as a Script.expr *)letmultisig_script:Script.expr=Michelson_v1_parser.parse_toplevel?check:(Sometrue)multisig_script_string|>Tezos_micheline.Micheline_parser.no_parsing_error|>function|Error_->assertfalse(* This is a top level assertion, it is asserted when the client's process runs. *)|Okparsing_result->parsing_result.Michelson_v1_parser.expandedletmultisig_script_hash=letbytes=Data_encoding.Binary.to_bytes_exnScript.expr_encodingmultisig_scriptinScript_expr_hash.hash_bytes[bytes](* The previous multisig script is the only one that the client can
originate but the client knows how to interact with several
versions of the multisig contract. For each version, the description
indicates which features are available and how to interact with
the contract. *)typemultisig_contract_description={hash:Script_expr_hash.t;(* The hash of the contract script *)requires_chain_id:bool;(* The signatures should contain the chain identifier *)generic:bool;(* False means that the contract uses a custom action type, true
means that the contract expects the action as a (lambda unit
(list operation)). *)}(* List of known multisig contracts hashes with their kinds *)letknown_multisig_contracts:multisig_contract_descriptionlist=lethash=multisig_script_hashin[{hash;requires_chain_id=true;generic=false};{hash=Script_expr_hash.of_b58check_exn"exprtw1v4KvQN414oEXdGuA1U3eQizuCdS8cipx8QGK8TbNLRwc3qL";requires_chain_id=true;generic=false;};{hash=Script_expr_hash.of_b58check_exn"expru4Ju9kf6MQ216FxUEsb9P6j8UhkPtsFcYP8r9XhQSRb47FZGfM";requires_chain_id=false;generic=false;};]letknown_multisig_hashes=List.map(fundescr->descr.hash)known_multisig_contractsletcheck_multisig_scriptscript:multisig_contract_descriptiontzresultLwt.t=letbytes=Data_encoding.force_bytesscriptinlethash=Script_expr_hash.hash_bytes[bytes]inmatchList.find_opt(fund->Script_expr_hash.(d.hash=hash))known_multisig_contractswith|None->fail(Not_a_supported_multisig_contract(hash,matchData_encoding.force_decodescriptwith|Somes->s|None->assertfalse))|Somed->returnd(* Returns [Ok ()] if [~contract] is an originated contract whose code
is [multisig_script] *)letcheck_multisig_contract(cctxt:#Protocol_client_context.full)~chain~blockcontract=Client_proto_context.get_scriptcctxt~chain~blockcontract>>=?funscript_opt->(matchscript_optwith|Somescript->returnscript.code|None->fail(Contract_has_no_scriptcontract))>>=?check_multisig_scriptletseq~locl=Tezos_micheline.Micheline.Seq(loc,l)letpair~locab=Tezos_micheline.Micheline.Prim(loc,Script.D_Pair,[a;b],[])letnone~loc()=Tezos_micheline.Micheline.Prim(loc,Script.D_None,[],[])letsome~loca=Tezos_micheline.Micheline.Prim(loc,Script.D_Some,[a],[])letleft~loca=Tezos_micheline.Micheline.Prim(loc,Script.D_Left,[a],[])letright~locb=Tezos_micheline.Micheline.Prim(loc,Script.D_Right,[b],[])letint~loci=Tezos_micheline.Micheline.Int(loc,i)letbytes~locs=Tezos_micheline.Micheline.Bytes(loc,s)(** * Actions *)typemultisig_action=|TransferofTez.t*Contract.t|Change_delegateofpublic_key_hashoption|Change_keysofZ.t*public_keylistletaction_to_expr~loc=function|Transfer(amount,destination)->left~loc(pair~loc(int~loc(Z.of_int64(Tez.to_mutezamount)))(bytes~loc(Data_encoding.Binary.to_bytes_exnContract.encodingdestination)))|Change_delegatedelegate_opt->right~loc(left~loc(matchdelegate_optwith|None->none~loc()|Somedelegate->some~loc(bytes~loc(Data_encoding.Binary.to_bytes_exnTezos_crypto.Signature.V0.Public_key_hash.encodingdelegate))))|Change_keys(threshold,keys)->right~loc(right~loc(pair~loc(int~locthreshold)(seq~loc(List.map(funk->bytes~loc(Data_encoding.Binary.to_bytes_exnTezos_crypto.Signature.V0.Public_key.encodingk))keys))))letaction_of_expre=letfail()=fail(Action_deserialisation_error(Tezos_micheline.Micheline.strip_locationse))inmatchewith|Tezos_micheline.Micheline.Prim(_,Script.D_Left,[Tezos_micheline.Micheline.Prim(_,Script.D_Pair,[Tezos_micheline.Micheline.Int(_,i);Tezos_micheline.Micheline.Bytes(_,s);],[]);],[])->(matchTez.of_mutez(Z.to_int64i)with|None->fail()|Someamount->return@@Transfer(amount,Data_encoding.Binary.of_bytes_exnContract.encodings))|Tezos_micheline.Micheline.Prim(_,Script.D_Right,[Tezos_micheline.Micheline.Prim(_,Script.D_Left,[Tezos_micheline.Micheline.Prim(_,Script.D_None,[],[])],[]);],[])->return@@Change_delegateNone|Tezos_micheline.Micheline.Prim(_,Script.D_Right,[Tezos_micheline.Micheline.Prim(_,Script.D_Left,[Tezos_micheline.Micheline.Prim(_,Script.D_Some,[Tezos_micheline.Micheline.Bytes(_,s)],[]);],[]);],[])->return@@Change_delegate(Some(Data_encoding.Binary.of_bytes_exnTezos_crypto.Signature.V0.Public_key_hash.encodings))|Tezos_micheline.Micheline.Prim(_,Script.D_Right,[Tezos_micheline.Micheline.Prim(_,Script.D_Right,[Tezos_micheline.Micheline.Prim(_,Script.D_Pair,[Tezos_micheline.Micheline.Int(_,threshold);Tezos_micheline.Micheline.Seq(_,key_bytes);],[]);],[]);],[])->List.map_es(function|Tezos_micheline.Micheline.Bytes(_,s)->return@@Data_encoding.Binary.of_bytes_exnTezos_crypto.Signature.V0.Public_key.encodings|_->fail())key_bytes>>=?funkeys->return@@Change_keys(threshold,keys)|_->fail()typekey_list=Tezos_crypto.Signature.V0.Public_key.tlist(* The relevant information that we can get about a multisig smart contract *)typemultisig_contract_information={counter:Z.t;threshold:Z.t;keys:key_list;}letmultisig_get_information(cctxt:#Protocol_client_context.full)~chain~blockcontract=letopenClient_proto_contextinletopenTezos_micheline.Michelineinget_storagecctxt~chain~blockcontract>>=?funstorage_opt->matchstorage_optwith|None->fail(Contract_has_no_storagecontract)|Somestorage->(matchrootstoragewith|Prim(_,D_Pair,[Int(_,counter);Prim(_,D_Pair,[Int(_,threshold);Seq(_,key_nodes)],_);],_)->List.map_es(function|String(_,key_str)->return@@Tezos_crypto.Signature.V0.Public_key.of_b58check_exnkey_str|_->fail(Contract_has_unexpected_storagecontract))key_nodes>>=?funkeys->return{counter;threshold;keys}|_->fail(Contract_has_unexpected_storagecontract))letmultisig_create_storage~counter~threshold~keys():Script.exprtzresultLwt.t=letloc=Tezos_micheline.Micheline_parser.location_zeroinletopenTezos_micheline.MichelineinList.map_es(funkey->letkey_str=Tezos_crypto.Signature.V0.Public_key.to_b58checkkeyinreturn(String(loc,key_str)))keys>>=?funl->return@@strip_locations@@pair~loc(int~loccounter)(pair~loc(int~locthreshold)(seq~locl))(* Client_proto_context.originate expects the initial storage as a string *)letmultisig_storage_string~counter~threshold~keys()=multisig_create_storage~counter~threshold~keys()>>=?funexpr->return@@Format.asprintf"%a"Michelson_v1_printer.print_exprexprletmultisig_create_param~counter~action~optional_signatures():Script.exprtzresultLwt.t=letloc=Tezos_micheline.Micheline_parser.location_zeroinletopenTezos_micheline.MichelineinList.map_es(funsig_opt->matchsig_optwith|None->return@@none~loc()|Somesignature->return@@some~loc(String(loc,Tezos_crypto.Signature.V0.to_b58checksignature)))optional_signatures>>=?funl->return@@strip_locations@@pair~loc(pair~loc(int~loccounter)(action_to_expr~locaction))(Seq(loc,l))letmultisig_param_string~counter~action~optional_signatures()=multisig_create_param~counter~action~optional_signatures()>>=?funexpr->return@@Format.asprintf"%a"Michelson_v1_printer.print_exprexprletget_contract_address_maybe_chain_id~descr~loc~chain_idcontract=letaddress=bytes~loc(Data_encoding.Binary.to_bytes_exnContract.encodingcontract)inifdescr.requires_chain_idthenletchain_id_bytes=bytes~loc(Data_encoding.Binary.to_bytes_exnChain_id.encodingchain_id)inpair~locchain_id_bytesaddresselseaddressletmultisig_bytes~counter~action~contract~chain_id~descr()=letloc=Tezos_micheline.Micheline_parser.location_zeroinlettriple=pair~loc(get_contract_address_maybe_chain_id~descr~loc~chain_idcontract)(pair~loc(int~loccounter)(action_to_expr~locaction))inletbytes=Data_encoding.Binary.to_bytes_exnScript.expr_encoding@@Tezos_micheline.Micheline.strip_locations@@tripleinreturn@@Bytes.concat(Bytes.of_string"")[Bytes.of_string"\005";bytes]letcheck_threshold~threshold~keys()=letthreshold=Z.to_intthresholdinifCompare.List_length_with.(keys<threshold)thenfail(Threshold_too_high(threshold,List.lengthkeys))elseifCompare.Int.(threshold<=0)thenfail(Non_positive_thresholdthreshold)elsereturn_unitletoriginate_multisig(cctxt:#Protocol_client_context.full)~chain~block?confirmations?dry_run?branch?fee?gas_limit?storage_limit?verbose_signing~delegate~threshold~keys~balance~source~src_pk~src_sk~fee_parameter()=multisig_storage_string~counter:Z.zero~threshold~keys()>>=?funinitial_storage->check_threshold~threshold~keys()>>=?fun()->Client_proto_context.originate_contractcctxt~chain~block?branch?confirmations?dry_run?fee?gas_limit?storage_limit?verbose_signing~delegate~initial_storage~balance~source~src_pk~src_sk~code:multisig_script~fee_parameter()typemultisig_prepared_action={bytes:Bytes.t;threshold:Z.t;keys:public_keylist;counter:Z.t;}letcheck_action~action()=matchactionwith|Change_keys(threshold,keys)->check_threshold~threshold~keys()|_->return_unitletprepare_multisig_transaction(cctxt:#Protocol_client_context.full)~chain~block~multisig_contract~action()=letcontract=multisig_contractincheck_multisig_contractcctxt~chain~blockcontract>>=?fundescr->check_action~action()>>=?fun()->multisig_get_informationcctxt~chain~blockcontract>>=?fun{counter;threshold;keys}->Chain_services.chain_idcctxt~chain()>>=?funchain_id->multisig_bytes~counter~action~contract~descr~chain_id()>>=?funbytes->return{bytes;threshold;keys;counter}letcheck_multisig_signatures~bytes~threshold~keyssignatures=letkey_array=Array.of_listkeysinletnkeys=Array.lengthkey_arrayinletopt_sigs_arr=Array.makenkeysNoneinletmatching_key_found=reffalseinletcheck_signature_against_key_numbersignatureikey=ifTezos_crypto.Signature.V0.checkkeysignaturebytesthen(matching_key_found:=true;opt_sigs_arr.(i)<-Somesignature)inList.iter_ep(funsignature->matching_key_found:=false;List.iteri(check_signature_against_key_numbersignature)keys;fail_unless!matching_key_found(Invalid_signaturesignature))signatures>>=?fun()->letopt_sigs=Array.to_listopt_sigs_arrinletsignature_count=List.fold_left(funnsig_opt->matchsig_optwithSome_->n+1|None->n)0opt_sigsinletthreshold_int=Z.to_intthresholdinifsignature_count>=threshold_intthenreturnopt_sigselsefail(Not_enough_signatures(threshold_int,signature_count))letcall_multisig(cctxt:#Protocol_client_context.full)~chain~block?confirmations?dry_run?verbose_signing?branch~source~src_pk~src_sk~multisig_contract~action~signatures~amount?fee?gas_limit?storage_limit?counter~fee_parameter()=prepare_multisig_transactioncctxt~chain~block~multisig_contract~action()>>=?fun{bytes;threshold;keys;counter=stored_counter}->check_multisig_signatures~bytes~threshold~keyssignatures>>=?funoptional_signatures->multisig_param_string~counter:stored_counter~action~optional_signatures()>>=?funarg->Client_proto_context.transfercctxt~chain~block?confirmations?dry_run?branch~source~src_pk~src_sk~destination:multisig_contract~arg~amount?fee?gas_limit?storage_limit?counter~fee_parameter?verbose_signing()letaction_of_bytes~multisig_contract~stored_counter~descr~chain_idbytes=ifCompare.Int.(Bytes.lengthbytes>=1)&&Compare.Int.(TzEndian.get_uint8bytes0=0x05)thenletnbytes=Bytes.subbytes1(Bytes.lengthbytes-1)inmatchData_encoding.Binary.of_bytes_optScript.expr_encodingnbyteswith|None->fail(Bytes_deserialisation_errorbytes)|Somee->(matchTezos_micheline.Micheline.rootewith|Tezos_micheline.Micheline.Prim(_,Script.D_Pair,[Tezos_micheline.Micheline.Bytes(_,contract_bytes);Tezos_micheline.Micheline.Prim(_,Script.D_Pair,[Tezos_micheline.Micheline.Int(_,counter);e],[]);],[])whennotdescr.requires_chain_id->letcontract=Data_encoding.Binary.of_bytes_exnContract.encodingcontract_bytesinifcounter=stored_counterthenifmultisig_contract=contractthenaction_of_expreelsefail(Bad_deserialized_contract(contract,multisig_contract))elsefail(Bad_deserialized_counter(counter,stored_counter))|Tezos_micheline.Micheline.Prim(_,Script.D_Pair,[Tezos_micheline.Micheline.Prim(_,Script.D_Pair,[Tezos_micheline.Micheline.Bytes(_,chain_id_bytes);Tezos_micheline.Micheline.Bytes(_,contract_bytes);],[]);Tezos_micheline.Micheline.Prim(_,Script.D_Pair,[Tezos_micheline.Micheline.Int(_,counter);e],[]);],[])whendescr.requires_chain_id->letcontract=Data_encoding.Binary.of_bytes_exnContract.encodingcontract_bytesinletcid=Data_encoding.Binary.of_bytes_exnChain_id.encodingchain_id_bytesinifcounter=stored_counterthenifmultisig_contract=contract&&chain_id=cidthenaction_of_expreelsefail(Bad_deserialized_contract(contract,multisig_contract))elsefail(Bad_deserialized_counter(counter,stored_counter))|_->fail(Bytes_deserialisation_errorbytes))elsefail(Bytes_deserialisation_errorbytes)letcall_multisig_on_bytes(cctxt:#Protocol_client_context.full)~chain~block?confirmations?dry_run?verbose_signing?branch~source~src_pk~src_sk~multisig_contract~bytes~signatures~amount?fee?gas_limit?storage_limit?counter~fee_parameter()=multisig_get_informationcctxt~chain~blockmultisig_contract>>=?funinfo->check_multisig_contractcctxt~chain~blockmultisig_contract>>=?fundescr->Chain_services.chain_idcctxt~chain()>>=?funchain_id->action_of_bytes~multisig_contract~stored_counter:info.counter~chain_id~descrbytes>>=?funaction->call_multisigcctxt~chain~block?confirmations?dry_run?branch~source~src_pk~src_sk~multisig_contract~action~signatures~amount?fee?gas_limit?storage_limit?counter~fee_parameter?verbose_signing()