Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file sapling_helpers.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2020-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. *)(* *)(*****************************************************************************)openProtocolmoduleCommon=structletmemo_size_of_inti=matchAlpha_context.Sapling.Memo_size.parse_z@@Z.of_intiwith|Okmemo_size->memo_size|Error_->assertfalseletint_of_memo_sizems=Alpha_context.Sapling.Memo_size.unparse_to_zms|>Z.to_intletassert_trueres=letopenLwt_result_syntaxinlet+resinassertresletassert_falseres=letopenLwt_result_syntaxinlet+resinassert(notres)letassert_someres=letopenLwt_result_syntaxinlet+result=resinmatchresultwithSomes->s|None->assertfalseletassert_noneres=letopenLwt_result_syntaxinlet*result=resinmatchresultwithSome_->assertfalse|None->return_unitletassert_errorres=letopenLwt_syntaxinlet*result=resinmatchresultwithOk_->assertfalse|Error_->return_ok_unitletprint?(prefix="")ev=Format.printf"%s: %s\n"prefixData_encoding.(Json.to_string(Json.constructev))letto_hexxencoding=Hex.show(Hex.of_bytesData_encoding.Binary.(to_bytes_exnencodingx))letrandomized_byte?posvencoding=letbytes=Data_encoding.Binary.(to_bytes_exnencodingv)inletrecaux()=letrandom_char=Random.int256|>char_of_intinletpos=Option.value~default:(Random.int(Bytes.lengthbytes))posinifrandom_char=Bytes.getbytesposthenaux()elseBytes.setbytesposrandom_charinaux();Data_encoding.Binary.(of_bytes_exnencodingbytes)typewallet={sk:Tezos_sapling.Core.Wallet.Spending_key.t;vk:Tezos_sapling.Core.Wallet.Viewing_key.t;}letwallet_gen()=letsk=Tezos_sapling.Core.Wallet.Spending_key.of_seed(Tezos_crypto.Hacl.Rand.gen32)inletvk=Tezos_sapling.Core.Wallet.Viewing_key.of_skskin{sk;vk}letgen_addrnvk=letrecauxnindexres=ifCompare.Int.(<=)n0thenreselseletnew_index,new_addr=Tezos_sapling.Core.Client.Viewing_key.new_addressvkindexinaux(n-1)new_index(new_addr::res)inauxnTezos_sapling.Core.Client.Viewing_key.default_index[]letgen_nf()=let{vk;_}=wallet_gen()inletaddr=snd@@Tezos_sapling.Core.Wallet.Viewing_key.(new_addressvkdefault_index)inletamount=10Linletrcm=Tezos_sapling.Core.Client.Rcm.random()inletposition=10LinTezos_sapling.Core.Client.Nullifier.computeaddrvk~amountrcm~positionletgen_cm_cipher~memo_size()=letopenTezos_sapling.Core.Clientinlet{vk;_}=wallet_gen()inletaddr=snd@@Tezos_sapling.Core.Wallet.Viewing_key.(new_addressvkdefault_index)inletamount=10Linletrcm=Tezos_sapling.Core.Client.Rcm.random()inletcm=Commitment.computeaddr~amountrcminletcipher=letpayload_enc=Data_encoding.Binary.to_bytes_exnData_encoding.bytes(Tezos_crypto.Hacl.Rand.gen(memo_size+4+16+11+32+8))inData_encoding.Binary.of_bytes_exnCiphertext.encoding(Bytes.concatBytes.empty[Bytes.create(32+32);payload_enc;Bytes.create(24+64+16+24);])in(cm,cipher)(* rebuilds from empty at each call *)letclient_state_of_diff~memo_size(root,diff)=letopenAlpha_context.Saplinginletcs=Tezos_sapling.Storage.add(Tezos_sapling.Storage.empty~memo_size)diff.commitments_and_ciphertextsinassert(Tezos_sapling.Storage.get_rootcs=root);List.fold_left(funsnf->Tezos_sapling.Storage.add_nullifiersnf)csdiff.nullifiersendmoduleAlpha_context_helpers=structincludeCommonletinit()=letopenLwt_result_wrap_syntaxinlet*b,_contract=Context.init1()inlet+@ctxt,_,_=Alpha_context.prepareb.context~level:b.header.shell.level~predecessor_timestamp:b.header.shell.timestamp~timestamp:b.header.shell.timestamp(* ~fitness:b.header.shell.fitness *)inctxt(* takes a state obtained from Sapling.empty_state or Sapling.state_from_id and
passed through Sapling.verify_update *)letfinalizectx=letopenLwt_result_wrap_syntaxinletopenAlpha_contextinletopenSaplinginfunction|{id=None;diff;memo_size}->let*@ctx,id=Sapling.fresh~temporary:falsectxinletinit=Lazy_storage.Alloc{memo_size}inletlazy_storage_diff=Lazy_storage.Update{init;updates=diff}inletdiffs=[Lazy_storage.makeSapling_stateidlazy_storage_diff]inlet+@ctx,_added_size=Lazy_storage.applyctxdiffsin(ctx,id)|{id=Someid;diff;_}->letinit=Lazy_storage.Existinginletlazy_storage_diff=Lazy_storage.Update{init;updates=diff}inletdiffs=[Lazy_storage.makeSapling_stateidlazy_storage_diff]inlet+@ctx,_added_size=Lazy_storage.applyctxdiffsin(ctx,id)(* disk only version *)letverify_updatectx?memo_size?idvt=letopenLwt_result_wrap_syntaxinletanti_replay="anti-replay"inlet*vs,ctx=matchidwith|None->let*memo_size=matchmemo_sizewith|None->(matchvt.Environment.Sapling.UTXO.outputswith|[]->failwith"Can't infer memo_size from empty outputs"|output::_->return@@Environment.Sapling.Ciphertext.get_memo_sizeoutput.ciphertext)|Somememo_size->returnmemo_sizeinletmemo_size=memo_size_of_intmemo_sizeinletvs=Alpha_context.Sapling.empty_state~memo_size()inreturn(vs,ctx)|Someid->(* let*! result = Storage.Sapling.Roots.get (Obj.magic ctx, id) 0l in *)(* let*?@ _, root = result in *)(* print ~prefix:"verify: " Environment.Sapling.Hash.encoding root ; *)let*@result=Alpha_context.Sapling.state_from_idctxidinreturnresultinlet*@ctx,res=Alpha_context.Sapling.verify_updatectxvsvtanti_replayinmatchreswith|None->return_none|Some(_balance,vs)->let*ctx,id=finalizectxvsinletfake_fitness=Alpha_context.(letlevel=matchRaw_level.of_int320lwith|Error_->assertfalse|Okl->linFitness.create_without_locked_round~level~predecessor_round:Round.zero~round:Round.zero|>Fitness.to_raw)inletectx=(Alpha_context.finalizectxfake_fitness).contextin(* bump the level *)let+@ctx,_,_=Alpha_context.prepareectx~level:Alpha_context.(Raw_level.to_int32Level.((succctx(currentctx)).level))~predecessor_timestamp:(Time.Protocol.of_secondsInt64.zero)~timestamp:(Time.Protocol.of_secondsInt64.zero)inSome(ctx,id)(* Same as before but for legacy *)letverify_update_legacyctx?memo_size?idvt=letopenLwt_result_wrap_syntaxinletanti_replay="anti-replay"inlet*vs,ctx=matchidwith|None->let*memo_size=matchmemo_sizewith|None->(matchvt.Environment.Sapling.UTXO.Legacy.outputswith|[]->failwith"Can't infer memo_size from empty outputs"|output::_->return@@Environment.Sapling.Ciphertext.get_memo_sizeoutput.ciphertext)|Somememo_size->returnmemo_sizeinletmemo_size=memo_size_of_intmemo_sizeinletvs=Alpha_context.Sapling.empty_state~memo_size()inreturn(vs,ctx)|Someid->(* let*! result = Storage.Sapling.Roots.get (Obj.magic ctx, id) 0l in *)(* let*?@ _, root = result in *)(* print ~prefix:"verify: " Environment.Sapling.Hash.encoding root ; *)let*@result=Alpha_context.Sapling.state_from_idctxidinreturnresultinlet*@ctx,res=Alpha_context.Sapling.Legacy.verify_updatectxvsvtanti_replayinmatchreswith|None->return_none|Some(_balance,vs)->let*ctx,id=finalizectxvsinletfake_fitness=Alpha_context.(letlevel=matchRaw_level.of_int320lwith|Error_->assertfalse|Okl->linFitness.create_without_locked_round~level~predecessor_round:Round.zero~round:Round.zero|>Fitness.to_raw)inletectx=(Alpha_context.finalizectxfake_fitness).contextin(* bump the level *)let+@ctx,_,_=Alpha_context.prepareectx~level:Alpha_context.(Raw_level.to_int32Level.((succctx(currentctx)).level))~predecessor_timestamp:(Time.Protocol.of_secondsInt64.zero)~timestamp:(Time.Protocol.of_secondsInt64.zero)inSome(ctx,id)lettransfer_inputs_outputswcsis=(* Tezos_sapling.Storage.size cs *)(* |> fun (a, b) -> *)(* Printf.printf "%Ld %Ld" a b ; *)letinputs=List.map(funi->Tezos_sapling.Forge.Input.getcs(Int64.of_inti)w.vk|>WithExceptions.Option.get~loc:__LOC__|>snd)isinletaddr=snd@@Tezos_sapling.Core.Wallet.Viewing_key.(new_addressw.vkdefault_index)inletmemo_size=Tezos_sapling.Storage.get_memo_sizecsinleto=Tezos_sapling.Forge.make_outputaddr1000000L(Bytes.creatememo_size)in(inputs,[o])lettransferwcsis=letanti_replay="anti-replay"inletins,outs=transfer_inputs_outputswcsisin(* change the wallet of this last line *)Tezos_sapling.Forge.forge_transactioninsoutsw.skanti_replay~bound_data:""cslettransfer_legacywcsis=letanti_replay="anti-replay"inletins,outs=transfer_inputs_outputswcsisin(* change the wallet of this last line *)Tezos_sapling.Forge.forge_transaction_legacyinsoutsw.skanti_replaycsletclient_state_alphactxid=letopenLwt_result_wrap_syntaxinlet*@diff=Alpha_context.Sapling.get_diffctxid()inlet+@{memo_size;_},_ctx=Alpha_context.Sapling.state_from_idctxidinletmemo_size=int_of_memo_sizememo_sizeinclient_state_of_diff~memo_sizediffend(*
Interpreter level
*)moduleInterpreter_helpers=structincludeCommonincludeContract_helpers(** Returns a block in which the contract is originated.
Also returns the associated anti-replay string and KT1 address. *)letoriginate_contract_hashfilestoragesrcbbaker=letopenLwt_result_syntaxinlet+dst,b=originate_contract_hashfilestoragesrcbbakerinletanti_replay=Format.asprintf"%a%a"Contract_hash.ppdstChain_id.ppChain_id.zeroin(dst,b,anti_replay)lethex_shield~memo_sizewalletanti_replay=letps=Tezos_sapling.Storage.empty~memo_sizeinletaddr=snd@@Tezos_sapling.Core.Wallet.Viewing_key.(new_addresswallet.vkdefault_index)inletoutput=Tezos_sapling.Forge.make_outputaddr15L(Bytes.creatememo_size)inletpt=Tezos_sapling.Forge.forge_transaction[][output]wallet.skanti_replay~bound_data:""psinlethex_string="0x"^Hex.show(Hex.of_bytesData_encoding.Binary.(to_bytes_exnTezos_sapling.Core.Client.UTXO.transaction_encodingpt))inhex_string(* Make a transaction and sync a local client state. [to_exclude] is the list
of addresses that cannot bake the block*)lettransac_and_sync~memo_sizeblockparametersamountsrcdstbaker=letopenLwt_result_syntaxinletamount_tez=Test_tez.(Alpha_context.Tez.one_mutez*!Int64.of_intamount)inletfee=Test_tez.of_int10inlet*operation=Op.transaction~gas_limit:Max~fee(Bblock)src(Alpha_context.Contract.Originateddst)amount_tez~parametersinlet*incr=Incremental.begin_construction~policy:Block.(By_accountbaker)blockinlet*incr=Incremental.add_operationincroperationinlet*block=Incremental.finalize_blockincrinlet+diff=Alpha_services.Contract.single_sapling_get_diffBlock.rpc_ctxtblockdst~offset_commitment:0L~offset_nullifier:0L()inletstate=client_state_of_diff~memo_sizediffin(block,state)(* Returns a list of printed shield transactions and their total amount. *)letshield~memo_sizesknumber_transacvkprinteranti_replay=letstate=Tezos_sapling.Storage.empty~memo_sizeinletrecauxnumber_transacnumber_outputsindexamount_outputtotalres=ifCompare.Int.(number_transac<=0)then(res,total)elseletnew_index,new_addr=Tezos_sapling.Core.Wallet.Viewing_key.(new_addressvkindex)inletoutputs=WithExceptions.List.init~loc:__LOC__number_outputs(fun_->Tezos_sapling.Forge.make_outputnew_addramount_output(Bytes.creatememo_size))inlettr_hex=to_hex(Tezos_sapling.Forge.forge_transaction~number_dummy_inputs:0~number_dummy_outputs:0[]outputsskanti_replay~bound_data:""state)Tezos_sapling.Core.Client.UTXO.transaction_encodinginaux(number_transac-1)(number_outputs+1)new_index(Int64.add20Lamount_output)(total+(number_outputs*Int64.to_intamount_output))(printertr_hex::res)inauxnumber_transac2Tezos_sapling.Core.Wallet.Viewing_key.default_index20L0[](* This fails if the operation is not correct wrt the block *)letnext_blockblockoperation=letopenLwt_result_syntaxinlet*incr=Incremental.begin_constructionblockinlet*incr=Incremental.add_operationincroperationinIncremental.finalize_blockincrend