123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)(* Copyright (c) 2020 Metastate AG <hello@metastate.dev> *)(* *)(* 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. *)(* *)(*****************************************************************************)openLwt.Infixletbase=58letzbase=Z.of_intbasemoduleAlphabet=structtypet={encode:string;decode:string}letmakealphabet=ifString.lengthalphabet<>basetheninvalid_arg"Base58: invalid alphabet (length)";letstr=Bytes.make256'\255'infori=0toString.lengthalphabet-1doletchar=int_of_charalphabet.[i]inifBytes.getstrchar<>'\255'thenFormat.kasprintfinvalid_arg"Base58: invalid alphabet (dup '%c' %d %d)"(char_of_intchar)(int_of_char@@Bytes.getstrchar)i;Bytes.setstrchar(char_of_inti)done;{encode=alphabet;decode=Bytes.to_stringstr}letbitcoin=make"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"letripple=make"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"letflickr=make"123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"letdefault=bitcoinletall_in_alphabetalphabetstring=letok=Array.make256falseinString.iter(funx->ok.(Char.codex)<-true)alphabet.encode;letres=reftrueinfori=0toString.lengthstring-1dores:=!res&&ok.(Char.codestring.[i])done;!resletppppf{encode;_}=Format.fprintfppf"%s"encodeendletcount_trailing_charsc=letlen=String.lengthsinletrecloopi=ifi<0thenlenelseifs.[i]<>cthenlen-i-1elseloop(i-1)inloop(len-1)letcount_leading_charsc=letlen=String.lengthsinletrecloopi=ifi=lenthenlenelseifs.[i]<>cthenielseloop(i+1)inloop0letof_char?(alphabet=Alphabet.default)x=letpos=alphabet.decode.[int_of_charx]inmatchposwith'\255'->None|_->Some(int_of_charpos)letto_char?(alphabet=Alphabet.default)x=alphabet.encode.[x]letraw_encode?(alphabet=Alphabet.default)s=letlen=String.lengthsinlets=String.initlen(funi->s.[len-i-1])inletzero=alphabet.encode.[0]inletzeros=count_trailing_chars'\000'inletres_len=((len*8)+4)/5inletres=Bytes.makeres_len'\000'inlets=Z.of_bitssinletrecloopsi=ifs=Z.zerothenielselet(s,r)=Z.div_remszbaseinBytes.setresi(to_char~alphabet(Z.to_intr));loops(i-1)inleti=loops(res_len-1)inletress=Bytes.sub_stringres(i+1)(res_len-i-1)inString.makezeroszero^ressletraw_decode?(alphabet=Alphabet.default)s=TzString.fold_left(funac->match(a,of_char~alphabetc)with|(Somea,Somei)->SomeZ.(add(of_inti)(mulazbase))|_->None)(SomeZ.zero)s|>Option.map(funres->letres=Z.to_bitsresinletres_tzeros=count_trailing_charres'\000'inletlen=String.lengthres-res_tzerosinletzeros=count_leading_charsalphabet.encode.[0]inString.makezeros'\000'^String.initlen(funi->res.[len-i-1]))letchecksums=lethash=Hacl.Hash.SHA256.(digest(digest(Bytes.of_strings)))inBytes.sub_stringhash04(* Append a 4-bytes cryptographic checksum before encoding string s *)letsafe_encode?alphabets=raw_encode?alphabet(s^checksums)letsafe_decode?alphabets=Option.bind(raw_decode?alphabets)(funs->letlen=String.lengthsiniflen<4thenNoneelse(* only if the string is long enough to extract a checksum do we check it *)letmsg=String.subs0(len-4)inletmsg_hash=String.subs(len-4)4inifmsg_hash<>checksummsgthenNoneelseSomemsg)typedata=..type'aencoding={prefix:string;length:int;encoded_prefix:string;encoded_length:int;to_raw:'a->string;of_raw:string->'aoption;wrap:'a->data;}letprefix{prefix;_}=prefixletsimple_decode?alphabet{prefix;of_raw;_}s=let(>??)=Option.bindinsafe_decode?alphabets>??TzString.remove_prefix~prefix>??of_rawletsimple_encode?alphabet{prefix;to_raw;_}d=safe_encode?alphabet(prefix^to_rawd)typeregistered_encoding=Encoding:'aencoding->registered_encodingmoduleMakeEncodings(E:sigvalencodings:registered_encodinglistend)=structletencodings=refE.encodingsletcheck_ambiguous_prefixprefixlengthencodings=List.iter(fun(Encoding{encoded_prefix=s;length=l;_})->iflength=l&&(TzString.remove_prefix~prefix:sprefix<>None||TzString.remove_prefix~prefixs<>None)thenFormat.ksprintfinvalid_arg"Base58.register_encoding: duplicate prefix: %S, %S."sprefix)encodingsletmake_encoded_prefixprefixlen=letzeros=safe_encode(prefix^String.makelen'\000')andones=safe_encode(prefix^String.makelen'\255')inletlen=String.lengthzerosinifString.lengthones<>lenthenFormat.ksprintfinvalid_arg"Base58.registered_encoding: variable length encoding.";letrecloopi=ifi=lenthenlenelseifzeros.[i]=ones.[i]thenloop(i+1)elseiinletlen=loop0iniflen=0theninvalid_arg"Base58.register_encoding: not a unique prefix.";(String.subzeros0len,String.lengthzeros)letregister_encoding~prefix~length~to_raw~of_raw~wrap=letto_rawx=lets=to_rawxinassert(String.lengths=length);sinletof_raws=assert(String.lengths=length);of_rawsinlet(encoded_prefix,encoded_length)=make_encoded_prefixprefixlengthincheck_ambiguous_prefixencoded_prefixencoded_length!encodings;letencoding={prefix;length;encoded_prefix;encoded_length;to_raw;of_raw;wrap}inencodings:=Encodingencoding::!encodings;encodingletcheck_encoded_prefixencpl=ifenc.encoded_prefix<>pthenFormat.kasprintfStdlib.failwith"Unexpected prefix %s (expected %s)"penc.encoded_prefix;ifenc.encoded_length<>lthenFormat.kasprintfStdlib.failwith"Unexpected encoded length %d for %s (expected %d)"lpenc.encoded_lengthletdecode?alphabets=letrecfinds=function|[]->None|Encoding{prefix;of_raw;wrap;_}::encodings->(matchTzString.remove_prefix~prefixswith|None->findsencodings|Somemsg->of_rawmsg|>Option.mapwrap)inOption.bind(safe_decode?alphabets)(funs->finds!encodings)endtype'aresolver=|Resolver:{encoding:'hencoding;resolver:'a->string->'hlistLwt.t;}->'aresolvermoduleMakeResolvers(R:sigtypecontextend)=structletresolvers=ref[]letregister_resolver(typea)(encoding:aencoding)(resolver:R.context->string->alistLwt.t)=resolvers:=Resolver{encoding;resolver}::!resolversletpartial_decode?(alphabet=Alphabet.default)requestlen=letzero=alphabet.encode.[0]inletlast=alphabet.encode.[base-1]inletn=String.lengthrequestinletmin=raw_decode~alphabet(request^String.make(len-n)zero)inletmax=raw_decode~alphabet(request^String.make(len-n)last)inmatch(min,max)with|(Somemin,Somemax)->letprefix_len=TzString.common_prefixminmaxinSome(String.submin0prefix_len)|_->Noneletcomplete?alphabetcontextrequest=letrecfinds=function|[]->Lwt.return_nil|Resolver{encoding;resolver}::resolvers->(ifnot(TzString.has_prefix~prefix:encoding.encoded_prefixs)thenfindsresolverselsematchpartial_decode?alphabetrequestencoding.encoded_lengthwith|None->findsresolvers|Someprefix->letlen=String.lengthprefixinletignored=String.lengthencoding.prefixinletmsg=iflen<=ignoredthen""else(assert(String.subprefix0ignored=encoding.prefix);String.subprefixignored(len-ignored))inresolvercontextmsg>|=funmsgs->List.filter_map(funmsg->letres=simple_encodeencoding?alphabetmsginTzString.remove_prefix~prefix:requestres|>Option.map(fun_->res))msgs)infindrequest!resolversendincludeMakeEncodings(structletencodings=[]end)includeMakeResolvers(structtypecontext=unitend)letregister_resolverencf=register_resolverenc(fun()s->fs)letcomplete?alphabets=complete?alphabet()smoduleMake(C:sigtypecontextend)=structincludeMakeEncodings(structletencodings=!encodingsend)includeMakeResolvers(structtypecontext=C.contextend)endmodulePrefix=struct(* These encoded prefixes are computed using scripts/base58_prefix.py
$ ./scripts/base58_prefix.py tz1 20
36 434591 [6L, 161L, 159L]
$ dune utop src/lib_crypto
utop # Tezos_crypto.Base58.make_encoded_prefix "\006\161\159" 20 ;;
- : string * int = ("tz1", 36)
*)(* 32 *)letblock_hash="\001\052"(* B(51) *)letoperation_hash="\005\116"(* o(51) *)letoperation_list_hash="\133\233"(* Lo(52) *)letoperation_list_list_hash="\029\159\109"(* LLo(53) *)letprotocol_hash="\002\170"(* P(51) *)letcontext_hash="\079\199"(* Co(52) *)letblock_metadata_hash="\234\249"(* bm(52) *)letoperation_metadata_hash="\005\183"(* r(51) *)letoperation_metadata_list_hash="\134\039"(* Lr(52) *)letoperation_metadata_list_list_hash="\029\159\182"(* LLr(53) *)(* 20 *)leted25519_public_key_hash="\006\161\159"(* tz1(36) *)letsecp256k1_public_key_hash="\006\161\161"(* tz2(36) *)letp256_public_key_hash="\006\161\164"(* tz3(36) *)(* 16 *)letcryptobox_public_key_hash="\153\103"(* id(30) *)(* 32 *)leted25519_seed="\013\015\058\007"(* edsk(54) *)leted25519_public_key="\013\015\037\217"(* edpk(54) *)letsecp256k1_secret_key="\017\162\224\201"(* spsk(54) *)letp256_secret_key="\016\081\238\189"(* p2sk(54) *)(* 56 *)leted25519_encrypted_seed="\007\090\060\179\041"(* edesk(88) *)letsecp256k1_encrypted_secret_key="\009\237\241\174\150"(* spesk(88) *)letp256_encrypted_secret_key="\009\048\057\115\171"(* p2esk(88) *)(* 60 *)letsecp256k1_encrypted_scalar="\001\131\036\086\248"(* seesk(93) *)(* 33 *)letsecp256k1_public_key="\003\254\226\086"(* sppk(55) *)letp256_public_key="\003\178\139\127"(* p2pk(55) *)letsecp256k1_scalar="\038\248\136"(* SSp(53) *)letsecp256k1_element="\005\092\000"(* GSp(54) *)(* 64 *)leted25519_secret_key="\043\246\078\007"(* edsk(98) *)leted25519_signature="\009\245\205\134\018"(* edsig(99) *)letsecp256k1_signature="\013\115\101\019\063"(* spsig1(99) *)letp256_signature="\054\240\044\052"(* p2sig(98) *)letgeneric_signature="\004\130\043"(* sig(96) *)(* 4 *)letchain_id="\087\082\000"(* Net(15) *)(* 169 *)letsapling_spending_key="\011\237\020\092"(* sask(241) *)(* 43 *)letsapling_address="\018\071\040\223"(* zet1(69) *)end