| 1 | """ |
|---|
| 2 | Ported to Python 3. |
|---|
| 3 | """ |
|---|
| 4 | |
|---|
| 5 | # system-level upload+download roundtrip test, but using shares created from |
|---|
| 6 | # a previous run. This asserts that the current code is capable of decoding |
|---|
| 7 | # shares from a previous version. |
|---|
| 8 | |
|---|
| 9 | from typing import Any |
|---|
| 10 | |
|---|
| 11 | import os |
|---|
| 12 | from twisted.trial import unittest |
|---|
| 13 | from twisted.internet import defer, reactor |
|---|
| 14 | from allmydata import uri |
|---|
| 15 | from allmydata.storage.server import storage_index_to_dir |
|---|
| 16 | from allmydata.util import base32, fileutil, spans, log, hashutil |
|---|
| 17 | from allmydata.util.consumer import download_to_data, MemoryConsumer |
|---|
| 18 | from allmydata.immutable import upload, layout |
|---|
| 19 | from allmydata.test.no_network import GridTestMixin, NoNetworkServer |
|---|
| 20 | from allmydata.test.common import ShouldFailMixin |
|---|
| 21 | from allmydata.interfaces import NotEnoughSharesError, NoSharesError, \ |
|---|
| 22 | DownloadStopped |
|---|
| 23 | from allmydata.immutable.downloader.common import BadSegmentNumberError, \ |
|---|
| 24 | BadCiphertextHashError, COMPLETE, OVERDUE, DEAD |
|---|
| 25 | from allmydata.immutable.downloader.status import DownloadStatus |
|---|
| 26 | from allmydata.immutable.downloader.fetcher import SegmentFetcher |
|---|
| 27 | from allmydata.codec import CRSDecoder |
|---|
| 28 | from foolscap.eventual import eventually, fireEventually, flushEventualQueue |
|---|
| 29 | |
|---|
| 30 | def bchr(s): |
|---|
| 31 | return bytes([s]) |
|---|
| 32 | |
|---|
| 33 | plaintext = b"This is a moderate-sized file.\n" * 10 |
|---|
| 34 | mutable_plaintext = b"This is a moderate-sized mutable file.\n" * 10 |
|---|
| 35 | |
|---|
| 36 | # this chunk was generated by create_share(), written to disk, then pasted |
|---|
| 37 | # into this file. These shares were created by 1.2.0-r3247, a version that's |
|---|
| 38 | # probably fairly close to 1.3.0 . |
|---|
| 39 | #--------- BEGIN stored_shares.py -------------- |
|---|
| 40 | immutable_uri = b"URI:CHK:g4i6qkk7mlj4vkl5ncg6dwo73i:qcas2ebousfk3q5rkl2ncayeku52kpyse76v5yeel2t2eaa4f6ha:3:10:310" |
|---|
| 41 | immutable_shares = { |
|---|
| 42 | 0: { # client[0] |
|---|
| 43 | 0: base32.a2b(b"aaaaaaiaaacyeaaaaaaqaaaaaeaaaadiaaaaa2aaaaaciaaaacgaaaaavqaaaagmaaaab3aaaaazmksehmgmlmmeqkbxbljh5qnfq36b7h5ukgqccmy3665khphcxihkce7jukeuegdxtn26p353ork6qihitbshwucpopzvdnpkflg6vbvko7ohcmxjywpdkvjmuzq6hysxfl74mamn224nrsyl7czmvtwtss6kkzljridkffeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaactwxn7tsxj2nh6skwbghycguqfj7xrpegeporex5ebctynbgbl7y5y3kjcfritduzdk5rvwqs4lwzvb7fgvljgozbbtamhoriuzaeruaaqt2fbbxr5yv4vqeabkjqow6sd73dfqab3qban3htx6rn2y6mujdwaacbpvbyim4ewanv2vku44tunk7vdjkty2wkfm3jg67pqmm2newyib4aafazigyt6kxmirnlio5sdvbkvh43rwpctm6coigl64chn6z7w45rcaaccvmfgplu4kz5erphnx3xhzclypawi2j5zsvewmn4s2wbba4k2ktaab45y3kjcfritduzdk5rvwqs4lwzvb7fgvljgozbbtamhoriuzaeruaaaae4gg33emvrv63tbnvstumz2mnzhglddn5sgky27obqxeylnom5dqortgezc2mzngeycyy3spfyhi5dfpb2f62dbonudumzshkl7fjw5sp7x3yw4sdhze6qf7zgsjocpqtwl2gj5o6vufvixto3u2lddoj4xa5dumv4hix3sn5xxix3imfzwqortgi5fhno37hfotu2p5evmcmpqenjakt7pc6imi65cjp2icfhq2cmcx7rmnzswkzdfmrpxg2dbojsxgorrhizsy3tvnvpxgzlhnvsw45dthiytumjmonswo3lfnz2f643jpjstumz2gmyteldtnbqxezk7ojxw65c7nbqxg2b2gmzdubzmius26hljzu4j7gq5hdshwueqcfjc2bmveiyqbdxgyejetzovfrzws6tfhiztumzrgawhiyljnrpwg33emvrv64dbojqw24z2ha5dgmjsfuzs2mjqfr2g65dbnrpxg2dbojsxgorshiytalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabj5uiln36za2n4oyier7k5e4sx6newmmflfqhj7xffy32p5iohlyf33bdx5dafkfwr7rxwxjcsg3ljflkaae537llwnnykgf36h52dojfplbwi"), |
|---|
| 44 | 5: base32.a2b(b"aaaaaaiaaacyeaaaaaaqaaaaaeaaaadiaaaaa2aaaaaciaaaacgaaaaavqaaaagmaaaab3aaaaazmsdsvwbnfx2rnh7dusqniqomsdeetuafps6cawyb4pzxpkzal7w5ufaknxfnqw2qywv4c3a2zlumb2x2rx5osbxd3kqmebjndqf7zihbtagqczgwrka5rnywtsaeyijyh26okua2u7loep2nzo5etirjrxmp3yxpb4pheusaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaactwxn7tsxj2nh6skwbghycguqfj7xrpegeporex5ebctynbgbl7zs3zcg7igd2xoa4eu3lffqginpmoxrshqe6n3hzpocihgeu4vvymaadjz54nelgyi47767pkbsjwdjgsv7uyd5ntrztw6juavj7sd7wx7aaacx7wxlycyjniwxvby4ar546ncb4d3jnbhssnq4n4l4xeajurmn5diabgxwi6i5d2ysny3vavrm3a5lsuvng5mhbzk7axesyeddzw6uzmnluaakglpei35aypk5ydqstnmuwazbv5r26gi6atzxm7f5yja4ystswxbqaakbsqnrh4voyrc2wq53ehkcvkpzxdm6fgz4e4qmx5yeo35t7nz3ceaaaae4gg33emvrv63tbnvstumz2mnzhglddn5sgky27obqxeylnom5dqortgezc2mzngeycyy3spfyhi5dfpb2f62dbonudumzshkl7fjw5sp7x3yw4sdhze6qf7zgsjocpqtwl2gj5o6vufvixto3u2lddoj4xa5dumv4hix3sn5xxix3imfzwqortgi5fhno37hfotu2p5evmcmpqenjakt7pc6imi65cjp2icfhq2cmcx7rmnzswkzdfmrpxg2dbojsxgorrhizsy3tvnvpxgzlhnvsw45dthiytumjmonswo3lfnz2f643jpjstumz2gmyteldtnbqxezk7ojxw65c7nbqxg2b2gmzdubzmius26hljzu4j7gq5hdshwueqcfjc2bmveiyqbdxgyejetzovfrzws6tfhiztumzrgawhiyljnrpwg33emvrv64dbojqw24z2ha5dgmjsfuzs2mjqfr2g65dbnrpxg2dbojsxgorshiytalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabj5uiln36za2n4oyier7k5e4sx6newmmflfqhj7xffy32p5iohlyf33bdx5dafkfwr7rxwxjcsg3ljflkaae537llwnnykgf36h52dojfplbwi"), |
|---|
| 45 | }, |
|---|
| 46 | 1: { # client[1] |
|---|
| 47 | 2: base32.a2b(b"aaaaaaiaaacyeaaaaaaqaaaaaeaaaadiaaaaa2aaaaaciaaaacgaaaaavqaaaagmaaaab3aaaaazmj7um4zfgqo35m62ln6has6xz43klzjphj5eg46mb5x2jzgr6x6zb4voveo5uef53xbjbktr5rlupomy7x5b34amqeeg4r6obt6kpo2x4s3m3cwoo54oijyqfms3n3fethykhtglc47r4ci7ugqgz5d5fap3xzyhm4ehaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaactwxn7tsxj2nh6skwbghycguqfj7xrpegeporex5ebctynbgbl7zqkzg32wa74epeppqwneujs6tjptlm4qw75hoafobsoif3ok5odkaarmcwjw6vqh7bdzd34ftjfcmxu2l423hefx7j3qblqmtsbo3sxlq2qaewyffwgzojfi4uj2praj5azehnr4fhan5kdyewhtfncrqzoe42ijeaaikvquz5otrlhusf45w7o47ejpb4czdjhxgkuszrxslkyeedrljkmaabigkbwe7sv3celk2dxmq5ikvj7g4ntyu3hqtsbs7xar3pwp5xhmiqaa6k7uub7uqlamlqi2oduautemch242scu7cfor6kedxs6mm3uwjsmaaaae4gg33emvrv63tbnvstumz2mnzhglddn5sgky27obqxeylnom5dqortgezc2mzngeycyy3spfyhi5dfpb2f62dbonudumzshkl7fjw5sp7x3yw4sdhze6qf7zgsjocpqtwl2gj5o6vufvixto3u2lddoj4xa5dumv4hix3sn5xxix3imfzwqortgi5fhno37hfotu2p5evmcmpqenjakt7pc6imi65cjp2icfhq2cmcx7rmnzswkzdfmrpxg2dbojsxgorrhizsy3tvnvpxgzlhnvsw45dthiytumjmonswo3lfnz2f643jpjstumz2gmyteldtnbqxezk7ojxw65c7nbqxg2b2gmzdubzmius26hljzu4j7gq5hdshwueqcfjc2bmveiyqbdxgyejetzovfrzws6tfhiztumzrgawhiyljnrpwg33emvrv64dbojqw24z2ha5dgmjsfuzs2mjqfr2g65dbnrpxg2dbojsxgorshiytalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdp37hh2k4ys4d7qusb5e3dakjntythtcwcwfok7e52pu64zn4wrwbtlkzxzntwuwemi6e6mek5n4i7h3bw7nkat2zmqieftinxgzl2jfplbwi"), |
|---|
| 48 | 7: base32.a2b(b"aaaaaaiaaacyeaaaaaaqaaaaaeaaaadiaaaaa2aaaaaciaaaacgaaaaavqaaaagmaaaab3aaaaaznhsh2frhzxbutelvddtbuf3tfilhcj2zi3cxjyzy7pg7ewamazcblv76mvey54fxmch64chqfi24jmondc4uzitby3wjeui4nfp7kv6ufo67exptkvwk7cnbouvjiapyqzrps4r6ise4jhlr7mtp2tlizb5hyaqm3fhsvrmqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaactwxn7tsxj2nh6skwbghycguqfj7xrpegeporex5ebctynbgbl72g6h2oewtfcwgupjbjnh4k5k6d3k2fpi2q6nyidh3yo5ui6cslreaajms7f3pcsywhjbgrybzp64jzlsyjqbu7h4hvdlwf77ar6l63imdeqaaudfa3cpzk5rcfvnb3wioufku7togz4kntyjzazp3qi5x3h63tweiaagtt3y2iwnqrz77566udetmgsnfl7jqh23hdthn4tibkt7eh7np6aaakvpbzjdki64qaigkdj2bven3uigxbpurpwtrkjs4b6habv2ls7zqaac2g6h2oewtfcwgupjbjnh4k5k6d3k2fpi2q6nyidh3yo5ui6cslreaaaae4gg33emvrv63tbnvstumz2mnzhglddn5sgky27obqxeylnom5dqortgezc2mzngeycyy3spfyhi5dfpb2f62dbonudumzshkl7fjw5sp7x3yw4sdhze6qf7zgsjocpqtwl2gj5o6vufvixto3u2lddoj4xa5dumv4hix3sn5xxix3imfzwqortgi5fhno37hfotu2p5evmcmpqenjakt7pc6imi65cjp2icfhq2cmcx7rmnzswkzdfmrpxg2dbojsxgorrhizsy3tvnvpxgzlhnvsw45dthiytumjmonswo3lfnz2f643jpjstumz2gmyteldtnbqxezk7ojxw65c7nbqxg2b2gmzdubzmius26hljzu4j7gq5hdshwueqcfjc2bmveiyqbdxgyejetzovfrzws6tfhiztumzrgawhiyljnrpwg33emvrv64dbojqw24z2ha5dgmjsfuzs2mjqfr2g65dbnrpxg2dbojsxgorshiytalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdp37hh2k4ys4d7qusb5e3dakjntythtcwcwfok7e52pu64zn4wrwbtlkzxzntwuwemi6e6mek5n4i7h3bw7nkat2zmqieftinxgzl2jfplbwi"), |
|---|
| 49 | }, |
|---|
| 50 | 2: { # client[2] |
|---|
| 51 | 1: base32.a2b(b"aaaaaaiaaacyeaaaaaaqaaaaaeaaaadiaaaaa2aaaaaciaaaacgaaaaavqaaaagmaaaab3aaaaazmkrwrt6figauxkgqyk3nggp5eeoeq5htt7tke4gfqj2u5roieslao4fldcwlq4btzk4brhkaerqiih6mhudotttrb6xzmvnqgg33fjcqeuw6teb3gml2pmhsezisa5svnzlvqnbaz6kzdmhisbwgu6ocexf2ge2rvc67gneqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaactwxn7tsxj2nh6skwbghycguqfj7xrpegeporex5ebctynbgbl72piueg6hxcxswaqafjgb232ip7mmwaahoaebxm6o72fxldzsreoyaaif6uhbbtqsybwxkvkttsorvl6unfkpdkzivtne3356brtjus3bahqaee6riin4pofpfmbaaksmdvxuq76yzmaao4aidoz457ulowhtfci5qaafazigyt6kxmirnlio5sdvbkvh43rwpctm6coigl64chn6z7w45rcaaccvmfgplu4kz5erphnx3xhzclypawi2j5zsvewmn4s2wbba4k2ktaab45y3kjcfritduzdk5rvwqs4lwzvb7fgvljgozbbtamhoriuzaeruaaaae4gg33emvrv63tbnvstumz2mnzhglddn5sgky27obqxeylnom5dqortgezc2mzngeycyy3spfyhi5dfpb2f62dbonudumzshkl7fjw5sp7x3yw4sdhze6qf7zgsjocpqtwl2gj5o6vufvixto3u2lddoj4xa5dumv4hix3sn5xxix3imfzwqortgi5fhno37hfotu2p5evmcmpqenjakt7pc6imi65cjp2icfhq2cmcx7rmnzswkzdfmrpxg2dbojsxgorrhizsy3tvnvpxgzlhnvsw45dthiytumjmonswo3lfnz2f643jpjstumz2gmyteldtnbqxezk7ojxw65c7nbqxg2b2gmzdubzmius26hljzu4j7gq5hdshwueqcfjc2bmveiyqbdxgyejetzovfrzws6tfhiztumzrgawhiyljnrpwg33emvrv64dbojqw24z2ha5dgmjsfuzs2mjqfr2g65dbnrpxg2dbojsxgorshiytalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaugotrr7enihxy2b2nwodhxabihaf3ewc2hmcdjsqx5hi4h3rn7gnvpt3lzzo5qgbnlp4dybwr7dn7vu5hsiyo5pedlqcasb7csiuojfplbwi"), |
|---|
| 52 | 6: base32.a2b(b"aaaaaaiaaacyeaaaaaaqaaaaaeaaaadiaaaaa2aaaaaciaaaacgaaaaavqaaaagmaaaab3aaaaazm34cgyp37ou5ohrofmk6bf5gcppxeb2njwmiwasn3uh4ykeocvq4vydsw36ksh63fcil3o257zupffrruiuqlwjvbdcdjiuqrojiromunzxxc34io7zlfafprzlvmztph4qsp67ozxmwvivqwtvu6ckr7pffsikgi2supviqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaactwxn7tsxj2nh6skwbghycguqfj7xrpegeporex5ebctynbgbl7zlyoki2shxeacbsq2oqnjdo5cbvyl5el5u4ksmxapryanos4x6maaajms7f3pcsywhjbgrybzp64jzlsyjqbu7h4hvdlwf77ar6l63imdeqaaudfa3cpzk5rcfvnb3wioufku7togz4kntyjzazp3qi5x3h63tweiaagtt3y2iwnqrz77566udetmgsnfl7jqh23hdthn4tibkt7eh7np6aaakvpbzjdki64qaigkdj2bven3uigxbpurpwtrkjs4b6habv2ls7zqaac2g6h2oewtfcwgupjbjnh4k5k6d3k2fpi2q6nyidh3yo5ui6cslreaaaae4gg33emvrv63tbnvstumz2mnzhglddn5sgky27obqxeylnom5dqortgezc2mzngeycyy3spfyhi5dfpb2f62dbonudumzshkl7fjw5sp7x3yw4sdhze6qf7zgsjocpqtwl2gj5o6vufvixto3u2lddoj4xa5dumv4hix3sn5xxix3imfzwqortgi5fhno37hfotu2p5evmcmpqenjakt7pc6imi65cjp2icfhq2cmcx7rmnzswkzdfmrpxg2dbojsxgorrhizsy3tvnvpxgzlhnvsw45dthiytumjmonswo3lfnz2f643jpjstumz2gmyteldtnbqxezk7ojxw65c7nbqxg2b2gmzdubzmius26hljzu4j7gq5hdshwueqcfjc2bmveiyqbdxgyejetzovfrzws6tfhiztumzrgawhiyljnrpwg33emvrv64dbojqw24z2ha5dgmjsfuzs2mjqfr2g65dbnrpxg2dbojsxgorshiytalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaugotrr7enihxy2b2nwodhxabihaf3ewc2hmcdjsqx5hi4h3rn7gnvpt3lzzo5qgbnlp4dybwr7dn7vu5hsiyo5pedlqcasb7csiuojfplbwi"), |
|---|
| 53 | }, |
|---|
| 54 | 3: { # client[3] |
|---|
| 55 | 4: base32.a2b(b"aaaaaaiaaacyeaaaaaaqaaaaaeaaaadiaaaaa2aaaaaciaaaacgaaaaavqaaaagmaaaab3aaaaaznjqn7ehmj6f4p3fjyliuvwnfothumsfhs7ienw4uln6joaxopqlmcy5daa4njrkgj7nqm6tpnmz2dci2b356pljv4zjj5ayzfihi4g26qdei7kjtegjuv4d3k3t4orpufnft3edbondkpj5etjczwhyulukzuy5socyivdfqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaactwxn7tsxj2nh6skwbghycguqfj7xrpegeporex5ebctynbgbl7zpmr4r2hvre3rxkblczwb2xfjk2n2yodsv6bojfqightn5jsy2xiaatl3epeor5mjg4n2qkywnqovzkkwtowdq4vpqlsjmcbr43pkmwgv2aacx7wxlycyjniwxvby4ar546ncb4d3jnbhssnq4n4l4xeajurmn5diaagtt3y2iwnqrz77566udetmgsnfl7jqh23hdthn4tibkt7eh7np6aaakglpei35aypk5ydqstnmuwazbv5r26gi6atzxm7f5yja4ystswxbqaakbsqnrh4voyrc2wq53ehkcvkpzxdm6fgz4e4qmx5yeo35t7nz3ceaaaae4gg33emvrv63tbnvstumz2mnzhglddn5sgky27obqxeylnom5dqortgezc2mzngeycyy3spfyhi5dfpb2f62dbonudumzshkl7fjw5sp7x3yw4sdhze6qf7zgsjocpqtwl2gj5o6vufvixto3u2lddoj4xa5dumv4hix3sn5xxix3imfzwqortgi5fhno37hfotu2p5evmcmpqenjakt7pc6imi65cjp2icfhq2cmcx7rmnzswkzdfmrpxg2dbojsxgorrhizsy3tvnvpxgzlhnvsw45dthiytumjmonswo3lfnz2f643jpjstumz2gmyteldtnbqxezk7ojxw65c7nbqxg2b2gmzdubzmius26hljzu4j7gq5hdshwueqcfjc2bmveiyqbdxgyejetzovfrzws6tfhiztumzrgawhiyljnrpwg33emvrv64dbojqw24z2ha5dgmjsfuzs2mjqfr2g65dbnrpxg2dbojsxgorshiytalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacifoqlus3puiqkekp6g6fdecjcx2bak27angodamzoxugovlhtcj5xbly7teqwmf73fqk3clyfvs6hdauq5qnqahlxlmp2vrmnneedgjfplbwi"), |
|---|
| 56 | 9: base32.a2b(b"aaaaaaiaaacyeaaaaaaqaaaaaeaaaadiaaaaa2aaaaaciaaaacgaaaaavqaaaagmaaaab3aaaaazn2tz3qt62bgsdnvksvdegsylb2kbltouheryflpho7hugme7svk7so2v7hmcgc43tcyugybuqzgifvkllikfiiezvml7ilolb7ivwvrv4d4t2gbywa44ibqwogmjtffta4b2sfwqebfg7pptergeqm5wo3tndtf7p3vftabqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaactwxn7tsxj2nh6skwbghycguqfj7xrpegeporex5ebctynbgbl7y3m26swfhsb66ze4cmyhohaksid7fyljgkhag32ibc7vx2yj4j5saayg3gxuvrj4qpxwjhatgb3rycusa7zoc2jsrybw6saix5n6wcpcpmqaamxjsc6bwv4w4or2oylltmsbfbobvmenj3sa6lnq6iy4tugsnv72eaaybvqu3gmlomi3dnf2tum3hoseavpesyia2i2wqgwbmbtrgmotu6oaadirzs2idl54toffh4a2hehvg2e3zoed4dr6pcdpuqpnz2knte7gqqac6kfatp33ianoqvg6mdd4vaxa27lo6vpugbcvanhskaqq2kewn6kwaaaae4gg33emvrv63tbnvstumz2mnzhglddn5sgky27obqxeylnom5dqortgezc2mzngeycyy3spfyhi5dfpb2f62dbonudumzshkl7fjw5sp7x3yw4sdhze6qf7zgsjocpqtwl2gj5o6vufvixto3u2lddoj4xa5dumv4hix3sn5xxix3imfzwqortgi5fhno37hfotu2p5evmcmpqenjakt7pc6imi65cjp2icfhq2cmcx7rmnzswkzdfmrpxg2dbojsxgorrhizsy3tvnvpxgzlhnvsw45dthiytumjmonswo3lfnz2f643jpjstumz2gmyteldtnbqxezk7ojxw65c7nbqxg2b2gmzdubzmius26hljzu4j7gq5hdshwueqcfjc2bmveiyqbdxgyejetzovfrzws6tfhiztumzrgawhiyljnrpwg33emvrv64dbojqw24z2ha5dgmjsfuzs2mjqfr2g65dbnrpxg2dbojsxgorshiytalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacifoqlus3puiqkekp6g6fdecjcx2bak27angodamzoxugovlhtcj5xbly7teqwmf73fqk3clyfvs6hdauq5qnqahlxlmp2vrmnneedgjfplbwi"), |
|---|
| 57 | }, |
|---|
| 58 | 4: { # client[4] |
|---|
| 59 | 3: base32.a2b(b"aaaaaaiaaacyeaaaaaaqaaaaaeaaaadiaaaaa2aaaaaciaaaacgaaaaavqaaaagmaaaab3aaaaazmbduh5nwvcvpgrihhnjxacz2jvzu3prrdqewo3vmxkhu5yd3fa3eil56fyh5l7ojimghwbf2o6ri7cmppr34qflr5o4w6s5fekxhdt3qvlgsw5yp5wrmjjffhph5czd5kzoo7yyg5x3wgxxzdvwtuom2c5olao62ep77b7wqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaactwxn7tsxj2nh6skwbghycguqfj7xrpegeporex5ebctynbgbl73mcs3dmxesuoke5hyqe6qmsdwy6ctqg6vb4cldzswriymxconeesaarmcwjw6vqh7bdzd34ftjfcmxu2l423hefx7j3qblqmtsbo3sxlq2qaaudfa3cpzk5rcfvnb3wioufku7togz4kntyjzazp3qi5x3h63tweiaaikvquz5otrlhusf45w7o47ejpb4czdjhxgkuszrxslkyeedrljkmaajnqklmns4skrzitu7cat2bsio3dykoa32uhqjmpgk2fdbs4jzuqsiaa6k7uub7uqlamlqi2oduautemch242scu7cfor6kedxs6mm3uwjsmaaaae4gg33emvrv63tbnvstumz2mnzhglddn5sgky27obqxeylnom5dqortgezc2mzngeycyy3spfyhi5dfpb2f62dbonudumzshkl7fjw5sp7x3yw4sdhze6qf7zgsjocpqtwl2gj5o6vufvixto3u2lddoj4xa5dumv4hix3sn5xxix3imfzwqortgi5fhno37hfotu2p5evmcmpqenjakt7pc6imi65cjp2icfhq2cmcx7rmnzswkzdfmrpxg2dbojsxgorrhizsy3tvnvpxgzlhnvsw45dthiytumjmonswo3lfnz2f643jpjstumz2gmyteldtnbqxezk7ojxw65c7nbqxg2b2gmzdubzmius26hljzu4j7gq5hdshwueqcfjc2bmveiyqbdxgyejetzovfrzws6tfhiztumzrgawhiyljnrpwg33emvrv64dbojqw24z2ha5dgmjsfuzs2mjqfr2g65dbnrpxg2dbojsxgorshiytalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadusyxmwhtnfdeewwgxd25fwixycfdcy46ifqv4dhga23fko6dbl4ywo2d27n3zh3wd6zumhupvmtgspqrh6t7wbsghruzqd3imbo2tojfplbwi"), |
|---|
| 60 | 8: base32.a2b(b"aaaaaaiaaacyeaaaaaaqaaaaaeaaaadiaaaaa2aaaaaciaaaacgaaaaavqaaaagmaaaab3aaaaaznjzqcxwyhgwlcpzvfb2berhoyw47h72gkzofwgksryqd4r6xlyougvyg4p3wkz7u37zllskeswuuh4w2rylbxecomnmqfv7n5ex3thjzq7ykr7gjkvq3kmrlhmxu3wnsr4ipsdn546btavjzc6yppoii2mxgnnk4qbxqrltaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaactwxn7tsxj2nh6skwbghycguqfj7xrpegeporex5ebctynbgbl72kfatp33ianoqvg6mdd4vaxa27lo6vpugbcvanhskaqq2kewn6kwaayg3gxuvrj4qpxwjhatgb3rycusa7zoc2jsrybw6saix5n6wcpcpmqaamxjsc6bwv4w4or2oylltmsbfbobvmenj3sa6lnq6iy4tugsnv72eaaybvqu3gmlomi3dnf2tum3hoseavpesyia2i2wqgwbmbtrgmotu6oaadirzs2idl54toffh4a2hehvg2e3zoed4dr6pcdpuqpnz2knte7gqqac6kfatp33ianoqvg6mdd4vaxa27lo6vpugbcvanhskaqq2kewn6kwaaaae4gg33emvrv63tbnvstumz2mnzhglddn5sgky27obqxeylnom5dqortgezc2mzngeycyy3spfyhi5dfpb2f62dbonudumzshkl7fjw5sp7x3yw4sdhze6qf7zgsjocpqtwl2gj5o6vufvixto3u2lddoj4xa5dumv4hix3sn5xxix3imfzwqortgi5fhno37hfotu2p5evmcmpqenjakt7pc6imi65cjp2icfhq2cmcx7rmnzswkzdfmrpxg2dbojsxgorrhizsy3tvnvpxgzlhnvsw45dthiytumjmonswo3lfnz2f643jpjstumz2gmyteldtnbqxezk7ojxw65c7nbqxg2b2gmzdubzmius26hljzu4j7gq5hdshwueqcfjc2bmveiyqbdxgyejetzovfrzws6tfhiztumzrgawhiyljnrpwg33emvrv64dbojqw24z2ha5dgmjsfuzs2mjqfr2g65dbnrpxg2dbojsxgorshiytalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadusyxmwhtnfdeewwgxd25fwixycfdcy46ifqv4dhga23fko6dbl4ywo2d27n3zh3wd6zumhupvmtgspqrh6t7wbsghruzqd3imbo2tojfplbwi"), |
|---|
| 61 | }, |
|---|
| 62 | } |
|---|
| 63 | |
|---|
| 64 | mutable_uri = b"URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq" |
|---|
| 65 | mutable_shares = { |
|---|
| 66 | 0: { # client[0] |
|---|
| 67 | 2: base32.a2b(b"krqwq33febwxk5dbmjwgkiddn5xhiyljnzsxeidwgefhkckeaohgreckgxome2uhcps464pzydv5wsywald7wthurw2dp6qxtkeb5vtswoeshuyno24v5oble7xb4j6ij7wwqriaaaaaaaaabb5aaaaaaaaaacsoaaaaaakjl2ynu3wrm2mwdv3syv4r34b5mklbtjuv5i5bzcuiwgfnl4wtpombwn7l7ugdvv2xut7hwbttcjfsacuhc7ipf43gvrgrt5vj7hau52uenoywreckgxome2uhcps464pzydv5wsywaldqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarybcjqw7lbehfrbu7pwx26vvv3mbjprnqup7q7wxmdsoyiqaqzgbtv6cplrrgpzbnptsoqz7imnauamfaaaaaaaaaaamgaaaaaaaaaaaymaaaaghqaaacr4aaaayxaaaagnyaaaaaaaaaao4qaaaaaaaaacd2gcbacibqbudaskugjcdpodibaeaqkaadqiaq2abqqiaqqaucaeaqbvkghg3s5ivqiq4uiajdzagl7iqhz2uxwj6aqk47oscyqxyljmxkapldxhxigum2ejxflnps3yrxwwehop2zairilrouid5s6wxgnoqg2vq7rx77icxfnx7mq65niuknra3tezhpndwo7pdxtvtci645p4umyhdh5gp3kbdpypphvouaql662r6ufigp4wwm6emrsavlontn4wttg6lv7bcmq6ojw5utpvz3agoenovrkndncjzxog7sp2w7l6jkmzgfxd2asxos5khkjbxbuhgkd6j5yqlzsmk3kq67engtlgyd7hxk7nedw73bq2bs4353wtler23poucntgve22acfxdbyj2f6q2saj2agu2ptfk364d3zayddffxkcah4olnkczjonselwwrvdcu3vch3licaeirogosgsui3y4ovcyzleurbiunp7fsfk2fgmiek7b3jogvrhj4snvkpqjfh2w2lqnkvqvuoojmgrn6sll354mpomajbtlsv6fuguzgsxsm3l2ehxdxfdv6kcoyogaa5raetooahunf4ltvfdb4d7mwgpj4pg7tmcw55ku5vgx7tjqkdkuyq2uvhavkbsbujsrcfzve5ekuhftxm7nmtomibrblbwyxcr5mcy6qqwx66lrhejmgewrig74nzpriualhw4g22qaw423qeezqn6irea3vlgc3foz4egf6irincownoq7utv643vmtoueebigwrl6nehbos3ipsdx44tmucpvyui2jzgfulb5mrrcjuycmuzfigu6tf25lbysqn7n7smhqniddgctjt573rtd6o63wiaactacm7dw7giohzcgwe4okbijnmzlacetmfjjbasrd6whexjmwkaiaabdm4cpjolak4m47telnokjaxwodj7ont7n7vffnmhkzp3lyshkh3qaaohafr65kctby6wa34bjpnviviiwmwq5mft3yho4tmslaarpcg6biaajlxuwwafut5a6dsr7lq5fkmiik7icppic5ffjjmqaud4y746q2rzd42k42oitzukawdl2fupkoqcztfu7qf2flp55xh4lm6rzpdbb7gtnx4kaffym36rboalf2tbmatt46ra6igvjnvwmig6ivf6gqrhcietf373xrbm3bpeecz7luv7kv76i7pwa5xtubga37vnlu6hspejpsenxiptd23ipri7u5w7lz67mdjfrpahtp5j46obg4ct7c5lelfskzqw5hq7x7kd7pbcgq3gjbv53amzxjelwgxpf6ni74zb6aixhjjllivkthks6df52kvobtcnscytmjrrfbekvwmhtbcke2cgcyaj2cra7xmnd4bw2xe2qki5kycopo45ekfyxwzsmxuyxvjzqklnqjwm3j3gwcm75ftnrsvbj33w6eyr4dbz2tewum7vfsyfbb3ojw5ujtn22jxpr4nkmkqup6z7ukpp4cdxwyge2psc5suaxaltmp23gbawoo3qjeo44hgwgtkge2oowox3fpxwxkckaqgrxilfeyxmjp4cwf2rhpkbwtveqkukchv3u5rfkykwfj5zhleu3obsif6ldfclaef32wjpe5d6ddh2grdx2yt6tuhw53t6zuuumlw6t6i3e2ev7nh36e4shwbn3ew2bbahn6imgb5sismfttf5cdlr5kw6wvjoaqiaiz2onxecfx735dvon7epthnklq67tnqj4fvcwechbvmnkpiwd5fd36dirpshc7i7zj3rcr5y3kzps5nfnfnik27gdgemfn27iblcjh5bpkio6sr375bmxbh6fshbo7cvjzsdsirsafnbjzgl6ycqczwbacj5sxwgrzl6qbdhfbzev5fzutznzbasejqdjm3qxsdcuqvqau3kih2anr2itgmr44wmwtsk6bd42m2j436ptslaugmbbvtbzsukeqytkse7niovlilyfssn3ipzkqtclmetqrxxn7h56xn2ju7pjnuamd6ijfawn2jpjsrmnawaozeniosvhgovftoj24dl77ytdkxdl7ogappnlgkqsjyy43urtnj6tqf2psfptkbzyx4nu3rzgsqqi5ybx3pu6cvt6de67xutdz566wrkp2ymy5n7tqchmw77ss532noqcbfxv6quum6jmeed3exasdapvid6bilwzm5dcnutkcxktmsdryqopw5ntws3tnbd7um27clmxkgl2uinwzvv4tmo4axbj5zmgfd6sy2fiw6efdwjcyj4awdx3peuxcyh3ccee63w2nqaltierdrevl3f3hnjsrdrl4aosd23szhhaimhg2mjuocottcdsraoczh3waoyxx2skunaphe6w5gutu2z7cag3cx4pgsspfmspphuunzx357x6l36hj3tdys727rhawfwc4wc4ytgts4nrxlxl3xxzffunlhjhzj5guxljwxfrxwjfsg5c67pg3js7gvfqmpson6rjgiuwbsklranwhauq74lbesavftdzf7y3x5zwbi4uu6q2vqimbkttm7k6ycttsgknej2ylkwdxgtut7wecpepnb527pblj3vuzldjt3whsmstax536plulalxtxmvj6vvg4phofyaidhxhhkl4dfb6oabp3fi55jt77pw3jl55pwbsimjpdanuenll2xxctr6swaimuaqk4wvqa6rbgow3onr74v4alkuukc2tlmorvsjpgaazpun6pbfyorazzarhc2r7fjt55pmosowrqcpdwl2q34hcco2f3icmpktchxdvtpmitufnplqaifbtlktkpo7b22244n6dkmxtgcnxtixsit57uhh7rc5rqezjz7pfd7ojhrui5bcdzb7bo2nbo6o24lpwbg4bmqgbqpbwclq6kglgxefryxlkqydillki3545vcrelfw6reszml6emuyjscx377on2qpq26j5jrh5xmbwmpcyq6sewanlbmwwk2vqhq5zunbcyd6h5z3ms3bgfn7lflvev5vwmjnv5nzbgrmpamy453zuvy6xc6jp7tqgpmrlxup7suptejbacm6rdurdhcaori6i25wylgaikfov4dfgeswxdeerogy2m5tbzsdlr7pfhchd4wnokuipfwjzejxiruj5cljm66hvn47j3eseys3nsi6xdh566jgap5s5e7ytdkkhh5lsuv47oose4luozz427dzk577jccjg3n7b4myd565edmsywol3hgh2i54lcya6saaaaaaa"), |
|---|
| 68 | 7: base32.a2b(b"krqwq33febwxk5dbmjwgkiddn5xhiyljnzsxeidwgefhkckeaohgreckgxome2uhcps464pzydv5wsywald7wthurw2dp6qxtkeb5vtswoeshuyno24v5oble7xb4j6ij7wwqriaaaaaaaaabb5aaaaaaaaaacsoaaaaaakjl2ynu3wrm2mwdv3syv4r34b5mklbtjuv5i5bzcuiwgfnl4wtpombwn7l7ugdvv2xut7hwbttcjfsacuhc7ipf43gvrgrt5vj7hau52uenoywreckgxome2uhcps464pzydv5wsywaldqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarybcjqw7lbehfrbu7pwx26vvv3mbjprnqup7q7wxmdsoyiqaqzgbtv6cplrrgpzbnptsoqz7imnauamfaaaaaaaaaaamgaaaaaaaaaaaymaaaaghqaaacr4aaaayxaaaagnyaaaaaaaaaao4qaaaaaaaaacd2gcbacibqbudaskugjcdpodibaeaqkaadqiaq2abqqiaqqaucaeaqbvkghg3s5ivqiq4uiajdzagl7iqhz2uxwj6aqk47oscyqxyljmxkapldxhxigum2ejxflnps3yrxwwehop2zairilrouid5s6wxgnoqg2vq7rx77icxfnx7mq65niuknra3tezhpndwo7pdxtvtci645p4umyhdh5gp3kbdpypphvouaql662r6ufigp4wwm6emrsavlontn4wttg6lv7bcmq6ojw5utpvz3agoenovrkndncjzxog7sp2w7l6jkmzgfxd2asxos5khkjbxbuhgkd6j5yqlzsmk3kq67engtlgyd7hxk7nedw73bq2bs4353wtler23poucntgve22acfxdbyj2f6q2saj2agu2ptfk364d3zayddffxkcah4olnkczjonselwwrvdcu3vch3licaeirogosgsui3y4ovcyzleurbiunp7fsfk2fgmiek7b3jogvrhj4snvkpqjfh2w2lqnkvqvuoojmgrn6sll354mpomajbtlsv6fuguzgsxsm3l2ehxdxfdv6kcoyogaa5raetooahunf4ltvfdb4d7mwgpj4pg7tmcw55ku5vgx7tjqkdkuyq2uvhavkbsbujsrcfzve5ekuhftxm7nmtomibrblbwyxcr5mcy6qqwx66lrhejmgewrig74nzpriualhw4g22qaw423qeezqn6irea3vlgc3foz4egf6irincownoq7utv643vmtoueebigwrl6nehbos3ipsdx44tmucpvyui2jzgfulb5mrrcjuycmuzfigu6tf25lbysqn7n7smhqniddgctjt573rtd6o63wiaactacm7dw7giohzcgwe4okbijnmzlacetmfjjbasrd6whexjmwkaiaaazuum3xriq54h5v6afcrrl3kkbd46oizwulc5fbslmblxfc3ldyyqaaszc7rkciv6rhwt5gbgnl5u54ihnqrfyuh7s54r2444mrhcwgizieaak4ap2xhvuz664fw3kayv7z5vawqs7skj6frzp3ihmk7js3tr7cwpnbfwoefuyn6bqkj5kssx3rvvffqgd3mhb7pbtegk6qfvsopvzmsiftabaykw3qitiqcv2wwfvdud5lkbjigatrf4ndeejsij5ab3eyaqqgxfiyxtv674qwltgynickeznu5el6uhs2k75hq2rsxhco2kmxw4didbdjodmjf2nrne63du76fd6laa7ng7zq4i7bx2xtohfrgwlxls6h7ibfsbybdz46sow3tn4vao3ulciz75kfbb62jrz3omvnihr2jwthks6df52kvobtcnscytmjrrfbekvwmhtbcke2cgcyaj2cra7xmnd4bw2xe2qki5kycopo45ekfyxwzsmxuyxvjzqklnqjwm3j3gwcm75ftnrsvbj33w6eyr4dbz2tewum7vfsyfbb3ojw5ujtn22jxpr4nkmkqup6z7ukpp4cdxwyge2psc5suaxaltmp23gbawoo3qjeo44hgwgtkge2oowox3fpxwxkckaqgrxilfeyxmjp4cwf2rhpkbwtveqkukchv3u5rfkykwfj5zhleu3obsif6ldfclaef32wjpe5d6ddh2grdx2yt6tuhw53t6zuuumlw6t6i3e2ev7nh36e4shwbn3ew2bbahn6imgb5sismfttf5cdlr5kw6wvjoaqiaiz2onxecfx735dvon7epthnklq67tnqj4fvcwechbvmnkpiwd5fd36dirpshc7i7zj3rcr5y3kzps5nfnfnik27gdgemfn27iblcjh5bpkio6sr375bmxbh6fshbo7cvjzsdsirsafnbjzgl6ycqczwbacj5sxwgrzl6qbdhfbzev5fzutznzbasejqdjm3qxsdcuqvqau3kih2anr2itgmr44wmwtsk6bd42m2j436ptslaugmbbvtbzsukeqytkse7niovlilyfssn3ipzkqtclmetqrxxn7h56xn2ju7pjnuamd6ijfawn2jpjsrmnawaozeniosvhgovftoj24dl77ytdkxdl7ogappnlgkqsjyy43urtnj6tqf2psfptkbzyx4nu3rzgsqqi5ybx3pu6cvt6de67xutdz566wrkp2ymy5n7tqchmw77ss532noqcbfxv6quum6jmeed3exasdapvid6bilwzm5dcnutkcxktmsdryqopw5ntws3tnbd7um27clmxkgl2uinwzvv4tmo4axbj5zmgfd6sy2fiw6efdwjcyj4awdx3peuxcyh3ccee63w2nqaltierdrevl3f3hnjsrdrl4aosd23szhhaimhg2mjuocottcdsraoczh3waoyxx2skunaphe6w5gutu2z7cag3cx4pgsspfmspphuunzx357x6l36hj3tdys727rhawfwc4wc4ytgts4nrxlxl3xxzffunlhjhzj5guxljwxfrxwjfsg5c67pg3js7gvfqmpson6rjgiuwbsklranwhauq74lbesavftdzf7y3x5zwbi4uu6q2vqimbkttm7k6ycttsgknej2ylkwdxgtut7wecpepnb527pblj3vuzldjt3whsmstax536plulalxtxmvj6vvg4phofyaidhxhhkl4dfb6oabp3fi55jt77pw3jl55pwbsimjpdanuenll2xxctr6swaimuaqk4wvqa6rbgow3onr74v4alkuukc2tlmorvsjpgaazpun6pbfyorazzarhc2r7fjt55pmosowrqcpdwl2q34hcco2f3icmpktchxdvtpmitufnplqaifbtlktkpo7b22244n6dkmxtgcnxtixsit57uhh7rc5rqezjz7pfd7ojhrui5bcdzb7bo2nbo6o24lpwbg4bmqgbqpbwclq6kglgxefryxlkqydillki3545vcrelfw6reszml6emuyjscx377on2qpq26j5jrh5xmbwmpcyq6sewanlbmwwk2vqhq5zunbcyd6h5z3ms3bgfn7lflvev5vwmjnv5nzbgrmpamy453zuvy6xc6jp7tqgpmrlxup7suptejbacm6rdurdhcaori6i25wylgaikfov4dfgeswxdeerogy2m5tbzsdlr7pfhchd4wnokuipfwjzejxiruj5cljm66hvn47j3eseys3nsi6xdh566jgap5s5e7ytdkkhh5lsuv47oose4luozz427dzk577jccjg3n7b4myd565edmsywol3hgh2i54lcya6saaaaaaa"), |
|---|
| 69 | }, |
|---|
| 70 | 1: { # client[1] |
|---|
| 71 | 3: base32.a2b(b"krqwq33febwxk5dbmjwgkiddn5xhiyljnzsxeidwgefhkckeaohm5tnwcmfsfmep4exoamss5lqyleq2ehahoduym5vgk37pmxx2xekzrtlzfvhapzb2fe3quv6tv3atr3g6ykqaaaaaaaaabb5aaaaaaaaaacsoaaaaaakjl2ynvx5mk74p2on26ax4rlp5jcoto5jkz3ndmgbmurhez4a5rbuyr55acbwlgbndlebsdyvlt4ttog767zqpoq3n2a4pra5va2o5zvbttlh45tnwcmfsfmep4exoamss5lqyleq2ehaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarybcjqw7lbehfrbu7pwx26vvv3mbjprnqup7q7wxmdsoyiqaqzgbtv6cplrrgpzbnptsoqz7imnauamfaaaaaaaaaaamgaaaaaaaaaaaymaaaaghqaaacr4aaaayxaaaagnyaaaaaaaaaao4qaaaaaaaaacd2gcbacibqbudaskugjcdpodibaeaqkaadqiaq2abqqiaqqaucaeaqbvkghg3s5ivqiq4uiajdzagl7iqhz2uxwj6aqk47oscyqxyljmxkapldxhxigum2ejxflnps3yrxwwehop2zairilrouid5s6wxgnoqg2vq7rx77icxfnx7mq65niuknra3tezhpndwo7pdxtvtci645p4umyhdh5gp3kbdpypphvouaql662r6ufigp4wwm6emrsavlontn4wttg6lv7bcmq6ojw5utpvz3agoenovrkndncjzxog7sp2w7l6jkmzgfxd2asxos5khkjbxbuhgkd6j5yqlzsmk3kq67engtlgyd7hxk7nedw73bq2bs4353wtler23poucntgve22acfxdbyj2f6q2saj2agu2ptfk364d3zayddffxkcah4olnkczjonselwwrvdcu3vch3licaeirogosgsui3y4ovcyzleurbiunp7fsfk2fgmiek7b3jogvrhj4snvkpqjfh2w2lqnkvqvuoojmgrn6sll354mpomajbtlsv6fuguzgsxsm3l2ehxdxfdv6kcoyogaa5raetooahunf4ltvfdb4d7mwgpj4pg7tmcw55ku5vgx7tjqkdkuyq2uvhavkbsbujsrcfzve5ekuhftxm7nmtomibrblbwyxcr5mcy6qqwx66lrhejmgewrig74nzpriualhw4g22qaw423qeezqn6irea3vlgc3foz4egf6irincownoq7utv643vmtoueebigwrl6nehbos3ipsdx44tmucpvyui2jzgfulb5mrrcjuycmuzfigu6tf25lbysqn7n7smhqniddgctjt573rtd6o63wiaactacm7dw7giohzcgwe4okbijnmzlacetmfjjbasrd6whexjmwkaiaabdm4cpjolak4m47telnokjaxwodj7ont7n7vffnmhkzp3lyshkh3qaaohafr65kctby6wa34bjpnviviiwmwq5mft3yho4tmslaarpcg6biaaixzuvzu4rhtiubmgxuli6u5aftglj7alukw733opywz5ds6gcd6nf32llac2j6qpbzi7vyosvgeefpubhxubossuuwiakb6mp6pini4rja473klkmi52lzfwofja7bb6pixgcxkwdaerc2irfpnrqwh5o2remu3iv3dtib75ku63cb6xzj4h53nmsguanjpganh3ow5yzovjcsezsj2cunyvlpva63zx5sudxe2zrtcu5zoty2tjzzlhodaz6rxe62ehbiktd4pmaodaz6ajsrohw7tdga2dpaftzbhadsolylgwgtbymenwthks6df52kvobtcnscytmjrrfbekvwmhtbcke2cgcyaj2cra7xmnd4bw2xe2qki5kycopo45ekfyxwzsmxuyxvjzqklnqjwm3j3gwcm75ftnrsvbj33w6eyr4dbz2tewum7vfsyfbb3ojw5ujtn22jxpr4nkmkqup6z7ukpp4cdxwyge2psc5suaxaltmp23gbawoo3qjeo44hgwgtkge2oowox3fpxwxkckaqgrxilfeyxmjp4cwf2rhpkbwtveqkukchv3u5rfkykwfj5zhleu3obsif6ldfclaef32wjpe5d6ddh2grdx2yt6tuhw53t6zuuumlw6t6i3e2ev7nh36e4shwbn3ew2bbahn6imgb5sismfttf5cdlr5kw6wvjoaqiaiz2onxecfx735dvon7epthnklq67tnqj4fvcwechbvmnkpiwd5fd36dirpshc7i7zj3rcr5y3kzps5nfnfnik27gdgemfn27iblcjh5bpkio6sr375bmxbh6fshbo7cvjzsdsirsafnbjzgl6ycqczwbacj5sxwgrzl6qbdhfbzev5fzutznzbasejqdjm3qxsdcuqvqau3kih2anr2itgmr44wmwtsk6bd42m2j436ptslaugmbbvtbzsukeqytkse7niovlilyfssn3ipzkqtclmetqrxxn7h56xn2ju7pjnuamd6ijfawn2jpjsrmnawaozeniosvhgovftoj24dl77ytdkxdl7ogappnlgkqsjyy43urtnj6tqf2psfptkbzyx4nu3rzgsqqi5ybx3pu6cvt6de67xutdz566wrkp2ymy5n7tqchmw77ss532noqcbfxv6quum6jmeed3exasdapvid6bilwzm5dcnutkcxktmsdryqopw5ntws3tnbd7um27clmxkgl2uinwzvv4tmo4axbj5zmgfd6sy2fiw6efdwjcyj4awdx3peuxcyh3ccee63w2nqaltierdrevl3f3hnjsrdrl4aosd23szhhaimhg2mjuocottcdsraoczh3waoyxx2skunaphe6w5gutu2z7cag3cx4pgsspfmspphuunzx357x6l36hj3tdys727rhawfwc4wc4ytgts4nrxlxl3xxzffunlhjhzj5guxljwxfrxwjfsg5c67pg3js7gvfqmpson6rjgiuwbsklranwhauq74lbesavftdzf7y3x5zwbi4uu6q2vqimbkttm7k6ycttsgknej2ylkwdxgtut7wecpepnb527pblj3vuzldjt3whsmstax536plulalxtxmvj6vvg4phofyaidhxhhkl4dfb6oabp3fi55jt77pw3jl55pwbsimjpdanuenll2xxctr6swaimuaqk4wvqa6rbgow3onr74v4alkuukc2tlmorvsjpgaazpun6pbfyorazzarhc2r7fjt55pmosowrqcpdwl2q34hcco2f3icmpktchxdvtpmitufnplqaifbtlktkpo7b22244n6dkmxtgcnxtixsit57uhh7rc5rqezjz7pfd7ojhrui5bcdzb7bo2nbo6o24lpwbg4bmqgbqpbwclq6kglgxefryxlkqydillki3545vcrelfw6reszml6emuyjscx377on2qpq26j5jrh5xmbwmpcyq6sewanlbmwwk2vqhq5zunbcyd6h5z3ms3bgfn7lflvev5vwmjnv5nzbgrmpamy453zuvy6xc6jp7tqgpmrlxup7suptejbacm6rdurdhcaori6i25wylgaikfov4dfgeswxdeerogy2m5tbzsdlr7pfhchd4wnokuipfwjzejxiruj5cljm66hvn47j3eseys3nsi6xdh566jgap5s5e7ytdkkhh5lsuv47oose4luozz427dzk577jccjg3n7b4myd565edmsywol3hgh2i54lcya6saaaaaaa"), |
|---|
| 72 | 8: base32.a2b(b"krqwq33febwxk5dbmjwgkiddn5xhiyljnzsxeidwgefhkckeaohm5tnwcmfsfmep4exoamss5lqyleq2ehahoduym5vgk37pmxx2xekzrtlzfvhapzb2fe3quv6tv3atr3g6ykqaaaaaaaaabb5aaaaaaaaaacsoaaaaaakjl2ynvx5mk74p2on26ax4rlp5jcoto5jkz3ndmgbmurhez4a5rbuyr55acbwlgbndlebsdyvlt4ttog767zqpoq3n2a4pra5va2o5zvbttlh45tnwcmfsfmep4exoamss5lqyleq2ehaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarybcjqw7lbehfrbu7pwx26vvv3mbjprnqup7q7wxmdsoyiqaqzgbtv6cplrrgpzbnptsoqz7imnauamfaaaaaaaaaaamgaaaaaaaaaaaymaaaaghqaaacr4aaaayxaaaagnyaaaaaaaaaao4qaaaaaaaaacd2gcbacibqbudaskugjcdpodibaeaqkaadqiaq2abqqiaqqaucaeaqbvkghg3s5ivqiq4uiajdzagl7iqhz2uxwj6aqk47oscyqxyljmxkapldxhxigum2ejxflnps3yrxwwehop2zairilrouid5s6wxgnoqg2vq7rx77icxfnx7mq65niuknra3tezhpndwo7pdxtvtci645p4umyhdh5gp3kbdpypphvouaql662r6ufigp4wwm6emrsavlontn4wttg6lv7bcmq6ojw5utpvz3agoenovrkndncjzxog7sp2w7l6jkmzgfxd2asxos5khkjbxbuhgkd6j5yqlzsmk3kq67engtlgyd7hxk7nedw73bq2bs4353wtler23poucntgve22acfxdbyj2f6q2saj2agu2ptfk364d3zayddffxkcah4olnkczjonselwwrvdcu3vch3licaeirogosgsui3y4ovcyzleurbiunp7fsfk2fgmiek7b3jogvrhj4snvkpqjfh2w2lqnkvqvuoojmgrn6sll354mpomajbtlsv6fuguzgsxsm3l2ehxdxfdv6kcoyogaa5raetooahunf4ltvfdb4d7mwgpj4pg7tmcw55ku5vgx7tjqkdkuyq2uvhavkbsbujsrcfzve5ekuhftxm7nmtomibrblbwyxcr5mcy6qqwx66lrhejmgewrig74nzpriualhw4g22qaw423qeezqn6irea3vlgc3foz4egf6irincownoq7utv643vmtoueebigwrl6nehbos3ipsdx44tmucpvyui2jzgfulb5mrrcjuycmuzfigu6tf25lbysqn7n7smhqniddgctjt573rtd6o63wiaabduzspufh6gomrp7sycuerhgl7ah3x3mpc6watmzlp6y23afmlxcaabui4znebv66jxcst6andsd2tncn4xcb6by7hrbx2ihw45fgzsptiiaaybvqu3gmlomi3dnf2tum3hoseavpesyia2i2wqgwbmbtrgmotu6oaamprqe6ozjrouoeltzhezhntop7wb6bbnnr3ak6x3ihvsjlz77gffkdet4sc63bxykwaikdyxwoehbrggxdu6qcwquzsnaltcgn52nyy4ypqbthfg4txtnznap6dktqtgtmtu7icooojppbwyi5c22uehbveptbuhbi7q3d4wuvsrptnd6wrhxwtlkxe4kurp4fey52p2v6urgephzxmaqfhm7pq3wxbi2uj5ourg65xnhbo4lrp7nzrdmk3svespmmitccvtwom6wtqefpp73j67zybiu4wrjjqt7vhip4ipuaezkmdy7feothks6df52kvobtcnscytmjrrfbekvwmhtbcke2cgcyaj2cra7xmnd4bw2xe2qki5kycopo45ekfyxwzsmxuyxvjzqklnqjwm3j3gwcm75ftnrsvbj33w6eyr4dbz2tewum7vfsyfbb3ojw5ujtn22jxpr4nkmkqup6z7ukpp4cdxwyge2psc5suaxaltmp23gbawoo3qjeo44hgwgtkge2oowox3fpxwxkckaqgrxilfeyxmjp4cwf2rhpkbwtveqkukchv3u5rfkykwfj5zhleu3obsif6ldfclaef32wjpe5d6ddh2grdx2yt6tuhw53t6zuuumlw6t6i3e2ev7nh36e4shwbn3ew2bbahn6imgb5sismfttf5cdlr5kw6wvjoaqiaiz2onxecfx735dvon7epthnklq67tnqj4fvcwechbvmnkpiwd5fd36dirpshc7i7zj3rcr5y3kzps5nfnfnik27gdgemfn27iblcjh5bpkio6sr375bmxbh6fshbo7cvjzsdsirsafnbjzgl6ycqczwbacj5sxwgrzl6qbdhfbzev5fzutznzbasejqdjm3qxsdcuqvqau3kih2anr2itgmr44wmwtsk6bd42m2j436ptslaugmbbvtbzsukeqytkse7niovlilyfssn3ipzkqtclmetqrxxn7h56xn2ju7pjnuamd6ijfawn2jpjsrmnawaozeniosvhgovftoj24dl77ytdkxdl7ogappnlgkqsjyy43urtnj6tqf2psfptkbzyx4nu3rzgsqqi5ybx3pu6cvt6de67xutdz566wrkp2ymy5n7tqchmw77ss532noqcbfxv6quum6jmeed3exasdapvid6bilwzm5dcnutkcxktmsdryqopw5ntws3tnbd7um27clmxkgl2uinwzvv4tmo4axbj5zmgfd6sy2fiw6efdwjcyj4awdx3peuxcyh3ccee63w2nqaltierdrevl3f3hnjsrdrl4aosd23szhhaimhg2mjuocottcdsraoczh3waoyxx2skunaphe6w5gutu2z7cag3cx4pgsspfmspphuunzx357x6l36hj3tdys727rhawfwc4wc4ytgts4nrxlxl3xxzffunlhjhzj5guxljwxfrxwjfsg5c67pg3js7gvfqmpson6rjgiuwbsklranwhauq74lbesavftdzf7y3x5zwbi4uu6q2vqimbkttm7k6ycttsgknej2ylkwdxgtut7wecpepnb527pblj3vuzldjt3whsmstax536plulalxtxmvj6vvg4phofyaidhxhhkl4dfb6oabp3fi55jt77pw3jl55pwbsimjpdanuenll2xxctr6swaimuaqk4wvqa6rbgow3onr74v4alkuukc2tlmorvsjpgaazpun6pbfyorazzarhc2r7fjt55pmosowrqcpdwl2q34hcco2f3icmpktchxdvtpmitufnplqaifbtlktkpo7b22244n6dkmxtgcnxtixsit57uhh7rc5rqezjz7pfd7ojhrui5bcdzb7bo2nbo6o24lpwbg4bmqgbqpbwclq6kglgxefryxlkqydillki3545vcrelfw6reszml6emuyjscx377on2qpq26j5jrh5xmbwmpcyq6sewanlbmwwk2vqhq5zunbcyd6h5z3ms3bgfn7lflvev5vwmjnv5nzbgrmpamy453zuvy6xc6jp7tqgpmrlxup7suptejbacm6rdurdhcaori6i25wylgaikfov4dfgeswxdeerogy2m5tbzsdlr7pfhchd4wnokuipfwjzejxiruj5cljm66hvn47j3eseys3nsi6xdh566jgap5s5e7ytdkkhh5lsuv47oose4luozz427dzk577jccjg3n7b4myd565edmsywol3hgh2i54lcya6saaaaaaa"), |
|---|
| 73 | }, |
|---|
| 74 | 2: { # client[2] |
|---|
| 75 | 4: base32.a2b(b"krqwq33febwxk5dbmjwgkiddn5xhiyljnzsxeidwgefhkckeaohelfyqrvy7pzjh3tqx73xsfkpi3so4qjghlywdkwuioyjvbtgekiulaes4myuxydi2sudi2fkg2q5nkjrt3zaaaaaaaaaabb5aaaaaaaaaacsoaaaaaakjl2ynujj2kh34jfiungka3deihevw7p3mzhj7uobc3qnbfxqp3xfazrsicvtz3enqkn4xxlu5xvxjj2rtlv6j3w3kmpzn2jbrnuoafq2aacoulfyqrvy7pzjh3tqx73xsfkpi3so4qjgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarybcjqw7lbehfrbu7pwx26vvv3mbjprnqup7q7wxmdsoyiqaqzgbtv6cplrrgpzbnptsoqz7imnauamfaaaaaaaaaaamgaaaaaaaaaaaymaaaaghqaaacr4aaaayxaaaagnyaaaaaaaaaao4qaaaaaaaaacd2gcbacibqbudaskugjcdpodibaeaqkaadqiaq2abqqiaqqaucaeaqbvkghg3s5ivqiq4uiajdzagl7iqhz2uxwj6aqk47oscyqxyljmxkapldxhxigum2ejxflnps3yrxwwehop2zairilrouid5s6wxgnoqg2vq7rx77icxfnx7mq65niuknra3tezhpndwo7pdxtvtci645p4umyhdh5gp3kbdpypphvouaql662r6ufigp4wwm6emrsavlontn4wttg6lv7bcmq6ojw5utpvz3agoenovrkndncjzxog7sp2w7l6jkmzgfxd2asxos5khkjbxbuhgkd6j5yqlzsmk3kq67engtlgyd7hxk7nedw73bq2bs4353wtler23poucntgve22acfxdbyj2f6q2saj2agu2ptfk364d3zayddffxkcah4olnkczjonselwwrvdcu3vch3licaeirogosgsui3y4ovcyzleurbiunp7fsfk2fgmiek7b3jogvrhj4snvkpqjfh2w2lqnkvqvuoojmgrn6sll354mpomajbtlsv6fuguzgsxsm3l2ehxdxfdv6kcoyogaa5raetooahunf4ltvfdb4d7mwgpj4pg7tmcw55ku5vgx7tjqkdkuyq2uvhavkbsbujsrcfzve5ekuhftxm7nmtomibrblbwyxcr5mcy6qqwx66lrhejmgewrig74nzpriualhw4g22qaw423qeezqn6irea3vlgc3foz4egf6irincownoq7utv643vmtoueebigwrl6nehbos3ipsdx44tmucpvyui2jzgfulb5mrrcjuycmuzfigu6tf25lbysqn7n7smhqniddgctjt573rtd6o63wiaactacm7dw7giohzcgwe4okbijnmzlacetmfjjbasrd6whexjmwkaiaaazuum3xriq54h5v6afcrrl3kkbd46oizwulc5fbslmblxfc3ldyyqaavmjy6g336aewy42vw5rusytyi7vzs6y22c5jhxyt5w6gthcbjp4zaakhlvz26psskxjisz27qlpzw4annhegunhnvlyr35ijotdizegjf4lgx3o4dt3d6d4bjqexz2eu3dprjlmuvlkbfcpmkq2ceydywqqcqdhmdl2nm5ku6z6gnss2bsbn7ycab2ggktr3bjlzaeo5pb4meolrckviwiddsikieo4wyatlxtybmzkoh3fb2vxc34xb47ty2cyi55xjan6m4bbie7muzrzmjmzviwlotk6icove7ydpag6dlrjwu4svgs3y2ln5r463dmflqs3p4aa7dldhjb5kfpxq63tgquunkucsfvlkaiiisgthks6df52kvobtcnscytmjrrfbekvwmhtbcke2cgcyaj2cra7xmnd4bw2xe2qki5kycopo45ekfyxwzsmxuyxvjzqklnqjwm3j3gwcm75ftnrsvbj33w6eyr4dbz2tewum7vfsyfbb3ojw5ujtn22jxpr4nkmkqup6z7ukpp4cdxwyge2psc5suaxaltmp23gbawoo3qjeo44hgwgtkge2oowox3fpxwxkckaqgrxilfeyxmjp4cwf2rhpkbwtveqkukchv3u5rfkykwfj5zhleu3obsif6ldfclaef32wjpe5d6ddh2grdx2yt6tuhw53t6zuuumlw6t6i3e2ev7nh36e4shwbn3ew2bbahn6imgb5sismfttf5cdlr5kw6wvjoaqiaiz2onxecfx735dvon7epthnklq67tnqj4fvcwechbvmnkpiwd5fd36dirpshc7i7zj3rcr5y3kzps5nfnfnik27gdgemfn27iblcjh5bpkio6sr375bmxbh6fshbo7cvjzsdsirsafnbjzgl6ycqczwbacj5sxwgrzl6qbdhfbzev5fzutznzbasejqdjm3qxsdcuqvqau3kih2anr2itgmr44wmwtsk6bd42m2j436ptslaugmbbvtbzsukeqytkse7niovlilyfssn3ipzkqtclmetqrxxn7h56xn2ju7pjnuamd6ijfawn2jpjsrmnawaozeniosvhgovftoj24dl77ytdkxdl7ogappnlgkqsjyy43urtnj6tqf2psfptkbzyx4nu3rzgsqqi5ybx3pu6cvt6de67xutdz566wrkp2ymy5n7tqchmw77ss532noqcbfxv6quum6jmeed3exasdapvid6bilwzm5dcnutkcxktmsdryqopw5ntws3tnbd7um27clmxkgl2uinwzvv4tmo4axbj5zmgfd6sy2fiw6efdwjcyj4awdx3peuxcyh3ccee63w2nqaltierdrevl3f3hnjsrdrl4aosd23szhhaimhg2mjuocottcdsraoczh3waoyxx2skunaphe6w5gutu2z7cag3cx4pgsspfmspphuunzx357x6l36hj3tdys727rhawfwc4wc4ytgts4nrxlxl3xxzffunlhjhzj5guxljwxfrxwjfsg5c67pg3js7gvfqmpson6rjgiuwbsklranwhauq74lbesavftdzf7y3x5zwbi4uu6q2vqimbkttm7k6ycttsgknej2ylkwdxgtut7wecpepnb527pblj3vuzldjt3whsmstax536plulalxtxmvj6vvg4phofyaidhxhhkl4dfb6oabp3fi55jt77pw3jl55pwbsimjpdanuenll2xxctr6swaimuaqk4wvqa6rbgow3onr74v4alkuukc2tlmorvsjpgaazpun6pbfyorazzarhc2r7fjt55pmosowrqcpdwl2q34hcco2f3icmpktchxdvtpmitufnplqaifbtlktkpo7b22244n6dkmxtgcnxtixsit57uhh7rc5rqezjz7pfd7ojhrui5bcdzb7bo2nbo6o24lpwbg4bmqgbqpbwclq6kglgxefryxlkqydillki3545vcrelfw6reszml6emuyjscx377on2qpq26j5jrh5xmbwmpcyq6sewanlbmwwk2vqhq5zunbcyd6h5z3ms3bgfn7lflvev5vwmjnv5nzbgrmpamy453zuvy6xc6jp7tqgpmrlxup7suptejbacm6rdurdhcaori6i25wylgaikfov4dfgeswxdeerogy2m5tbzsdlr7pfhchd4wnokuipfwjzejxiruj5cljm66hvn47j3eseys3nsi6xdh566jgap5s5e7ytdkkhh5lsuv47oose4luozz427dzk577jccjg3n7b4myd565edmsywol3hgh2i54lcya6saaaaaaa"), |
|---|
| 76 | 9: base32.a2b(b"krqwq33febwxk5dbmjwgkiddn5xhiyljnzsxeidwgefhkckeaohelfyqrvy7pzjh3tqx73xsfkpi3so4qjghlywdkwuioyjvbtgekiulaes4myuxydi2sudi2fkg2q5nkjrt3zaaaaaaaaaabb5aaaaaaaaaacsoaaaaaakjl2ynujj2kh34jfiungka3deihevw7p3mzhj7uobc3qnbfxqp3xfazrsicvtz3enqkn4xxlu5xvxjj2rtlv6j3w3kmpzn2jbrnuoafq2aacoulfyqrvy7pzjh3tqx73xsfkpi3so4qjgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarybcjqw7lbehfrbu7pwx26vvv3mbjprnqup7q7wxmdsoyiqaqzgbtv6cplrrgpzbnptsoqz7imnauamfaaaaaaaaaaamgaaaaaaaaaaaymaaaaghqaaacr4aaaayxaaaagnyaaaaaaaaaao4qaaaaaaaaacd2gcbacibqbudaskugjcdpodibaeaqkaadqiaq2abqqiaqqaucaeaqbvkghg3s5ivqiq4uiajdzagl7iqhz2uxwj6aqk47oscyqxyljmxkapldxhxigum2ejxflnps3yrxwwehop2zairilrouid5s6wxgnoqg2vq7rx77icxfnx7mq65niuknra3tezhpndwo7pdxtvtci645p4umyhdh5gp3kbdpypphvouaql662r6ufigp4wwm6emrsavlontn4wttg6lv7bcmq6ojw5utpvz3agoenovrkndncjzxog7sp2w7l6jkmzgfxd2asxos5khkjbxbuhgkd6j5yqlzsmk3kq67engtlgyd7hxk7nedw73bq2bs4353wtler23poucntgve22acfxdbyj2f6q2saj2agu2ptfk364d3zayddffxkcah4olnkczjonselwwrvdcu3vch3licaeirogosgsui3y4ovcyzleurbiunp7fsfk2fgmiek7b3jogvrhj4snvkpqjfh2w2lqnkvqvuoojmgrn6sll354mpomajbtlsv6fuguzgsxsm3l2ehxdxfdv6kcoyogaa5raetooahunf4ltvfdb4d7mwgpj4pg7tmcw55ku5vgx7tjqkdkuyq2uvhavkbsbujsrcfzve5ekuhftxm7nmtomibrblbwyxcr5mcy6qqwx66lrhejmgewrig74nzpriualhw4g22qaw423qeezqn6irea3vlgc3foz4egf6irincownoq7utv643vmtoueebigwrl6nehbos3ipsdx44tmucpvyui2jzgfulb5mrrcjuycmuzfigu6tf25lbysqn7n7smhqniddgctjt573rtd6o63wiaabduzspufh6gomrp7sycuerhgl7ah3x3mpc6watmzlp6y23afmlxcaabui4znebv66jxcst6andsd2tncn4xcb6by7hrbx2ihw45fgzsptiiaaybvqu3gmlomi3dnf2tum3hoseavpesyia2i2wqgwbmbtrgmotu6oaalugjhzef5wdpqvmaquhrpm4iodcmnohj5afnbjte2axgem33u3rr7yycphmuyxkhcfz4tsmtwzxh73a7aqwwy5qfpl5ud2zev477tcsviylwmlv6fgp54rk4iwputjkcgegczq6uynbvebu67jf6f2foocphznw7jrdsvphppguypjwmkkhugm6yjnrjka2ycvxsyh5xohn3fvbbhl4tvhedbaix3zlwxeayabnldp3oqnkjger7yrxh44wuv3adb76jh3nl6h45t4ixj77himst5plmpdtexyoozpxzjmedge5leynxhziothks6df52kvobtcnscytmjrrfbekvwmhtbcke2cgcyaj2cra7xmnd4bw2xe2qki5kycopo45ekfyxwzsmxuyxvjzqklnqjwm3j3gwcm75ftnrsvbj33w6eyr4dbz2tewum7vfsyfbb3ojw5ujtn22jxpr4nkmkqup6z7ukpp4cdxwyge2psc5suaxaltmp23gbawoo3qjeo44hgwgtkge2oowox3fpxwxkckaqgrxilfeyxmjp4cwf2rhpkbwtveqkukchv3u5rfkykwfj5zhleu3obsif6ldfclaef32wjpe5d6ddh2grdx2yt6tuhw53t6zuuumlw6t6i3e2ev7nh36e4shwbn3ew2bbahn6imgb5sismfttf5cdlr5kw6wvjoaqiaiz2onxecfx735dvon7epthnklq67tnqj4fvcwechbvmnkpiwd5fd36dirpshc7i7zj3rcr5y3kzps5nfnfnik27gdgemfn27iblcjh5bpkio6sr375bmxbh6fshbo7cvjzsdsirsafnbjzgl6ycqczwbacj5sxwgrzl6qbdhfbzev5fzutznzbasejqdjm3qxsdcuqvqau3kih2anr2itgmr44wmwtsk6bd42m2j436ptslaugmbbvtbzsukeqytkse7niovlilyfssn3ipzkqtclmetqrxxn7h56xn2ju7pjnuamd6ijfawn2jpjsrmnawaozeniosvhgovftoj24dl77ytdkxdl7ogappnlgkqsjyy43urtnj6tqf2psfptkbzyx4nu3rzgsqqi5ybx3pu6cvt6de67xutdz566wrkp2ymy5n7tqchmw77ss532noqcbfxv6quum6jmeed3exasdapvid6bilwzm5dcnutkcxktmsdryqopw5ntws3tnbd7um27clmxkgl2uinwzvv4tmo4axbj5zmgfd6sy2fiw6efdwjcyj4awdx3peuxcyh3ccee63w2nqaltierdrevl3f3hnjsrdrl4aosd23szhhaimhg2mjuocottcdsraoczh3waoyxx2skunaphe6w5gutu2z7cag3cx4pgsspfmspphuunzx357x6l36hj3tdys727rhawfwc4wc4ytgts4nrxlxl3xxzffunlhjhzj5guxljwxfrxwjfsg5c67pg3js7gvfqmpson6rjgiuwbsklranwhauq74lbesavftdzf7y3x5zwbi4uu6q2vqimbkttm7k6ycttsgknej2ylkwdxgtut7wecpepnb527pblj3vuzldjt3whsmstax536plulalxtxmvj6vvg4phofyaidhxhhkl4dfb6oabp3fi55jt77pw3jl55pwbsimjpdanuenll2xxctr6swaimuaqk4wvqa6rbgow3onr74v4alkuukc2tlmorvsjpgaazpun6pbfyorazzarhc2r7fjt55pmosowrqcpdwl2q34hcco2f3icmpktchxdvtpmitufnplqaifbtlktkpo7b22244n6dkmxtgcnxtixsit57uhh7rc5rqezjz7pfd7ojhrui5bcdzb7bo2nbo6o24lpwbg4bmqgbqpbwclq6kglgxefryxlkqydillki3545vcrelfw6reszml6emuyjscx377on2qpq26j5jrh5xmbwmpcyq6sewanlbmwwk2vqhq5zunbcyd6h5z3ms3bgfn7lflvev5vwmjnv5nzbgrmpamy453zuvy6xc6jp7tqgpmrlxup7suptejbacm6rdurdhcaori6i25wylgaikfov4dfgeswxdeerogy2m5tbzsdlr7pfhchd4wnokuipfwjzejxiruj5cljm66hvn47j3eseys3nsi6xdh566jgap5s5e7ytdkkhh5lsuv47oose4luozz427dzk577jccjg3n7b4myd565edmsywol3hgh2i54lcya6saaaaaaa"), |
|---|
| 77 | }, |
|---|
| 78 | 3: { # client[3] |
|---|
| 79 | 1: base32.a2b(b"krqwq33febwxk5dbmjwgkiddn5xhiyljnzsxeidwgefhkckeaohar2c5jzdcrekne6myzpxl2z65d6ufdjuuyhabg2j57ecmy23jyflcp7djzupj4tfr345bkg7cmwxmpmn3h4iaaaaaaaaabb5aaaaaaaaaacsoaaaaaakjl2ynu3sjzjwrfjn4cwfspkueq47j6ej2uodmjsjexyray7dn6ut4nnuftdhhgxo3t3a5eoipsdy5evdihyeigny3c4adtpveplcwt76m7naar2c5jzdcrekne6myzpxl2z65d6ufdjuqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarybcjqw7lbehfrbu7pwx26vvv3mbjprnqup7q7wxmdsoyiqaqzgbtv6cplrrgpzbnptsoqz7imnauamfaaaaaaaaaaamgaaaaaaaaaaaymaaaaghqaaacr4aaaayxaaaagnyaaaaaaaaaao4qaaaaaaaaacd2gcbacibqbudaskugjcdpodibaeaqkaadqiaq2abqqiaqqaucaeaqbvkghg3s5ivqiq4uiajdzagl7iqhz2uxwj6aqk47oscyqxyljmxkapldxhxigum2ejxflnps3yrxwwehop2zairilrouid5s6wxgnoqg2vq7rx77icxfnx7mq65niuknra3tezhpndwo7pdxtvtci645p4umyhdh5gp3kbdpypphvouaql662r6ufigp4wwm6emrsavlontn4wttg6lv7bcmq6ojw5utpvz3agoenovrkndncjzxog7sp2w7l6jkmzgfxd2asxos5khkjbxbuhgkd6j5yqlzsmk3kq67engtlgyd7hxk7nedw73bq2bs4353wtler23poucntgve22acfxdbyj2f6q2saj2agu2ptfk364d3zayddffxkcah4olnkczjonselwwrvdcu3vch3licaeirogosgsui3y4ovcyzleurbiunp7fsfk2fgmiek7b3jogvrhj4snvkpqjfh2w2lqnkvqvuoojmgrn6sll354mpomajbtlsv6fuguzgsxsm3l2ehxdxfdv6kcoyogaa5raetooahunf4ltvfdb4d7mwgpj4pg7tmcw55ku5vgx7tjqkdkuyq2uvhavkbsbujsrcfzve5ekuhftxm7nmtomibrblbwyxcr5mcy6qqwx66lrhejmgewrig74nzpriualhw4g22qaw423qeezqn6irea3vlgc3foz4egf6irincownoq7utv643vmtoueebigwrl6nehbos3ipsdx44tmucpvyui2jzgfulb5mrrcjuycmuzfigu6tf25lbysqn7n7smhqniddgctjt573rtd6o63wiaactacm7dw7giohzcgwe4okbijnmzlacetmfjjbasrd6whexjmwkaiaabdm4cpjolak4m47telnokjaxwodj7ont7n7vffnmhkzp3lyshkh3qaarzybn64ru5rss7tmi4ttv26q66ebdvvrtyd3s5t7dmqku3uoefroaahxhmt46bsa3cpmjfwjyw3zijhhbqh3j2dbc42jaqj6wvmxoz7pecirykndmb6dylde5utzkpucky5pk3x4u6dphkq2ycmfuyvpg5lsudusosyofwfnokbe7qmld2xwaxah3qkywarndsfvp3rybq2y7q42silj5cnlbdxnabv2zhhix3h5o5kz2ttqzm34clnbo527obrxvqlxz3sofwcmz2kqs4c3ypj6o4ny4hkh6qu7ljs7xiygzmoojhnaxc6wjbnvnsu2socztfaegy6ft22tgtdudtok4z755vgj3etwmje73af2f2thks6df52kvobtcnscytmjrrfbekvwmhtbcke2cgcyaj2cra7xmnd4bw2xe2qki5kycopo45ekfyxwzsmxuyxvjzqklnqjwm3j3gwcm75ftnrsvbj33w6eyr4dbz2tewum7vfsyfbb3ojw5ujtn22jxpr4nkmkqup6z7ukpp4cdxwyge2psc5suaxaltmp23gbawoo3qjeo44hgwgtkge2oowox3fpxwxkckaqgrxilfeyxmjp4cwf2rhpkbwtveqkukchv3u5rfkykwfj5zhleu3obsif6ldfclaef32wjpe5d6ddh2grdx2yt6tuhw53t6zuuumlw6t6i3e2ev7nh36e4shwbn3ew2bbahn6imgb5sismfttf5cdlr5kw6wvjoaqiaiz2onxecfx735dvon7epthnklq67tnqj4fvcwechbvmnkpiwd5fd36dirpshc7i7zj3rcr5y3kzps5nfnfnik27gdgemfn27iblcjh5bpkio6sr375bmxbh6fshbo7cvjzsdsirsafnbjzgl6ycqczwbacj5sxwgrzl6qbdhfbzev5fzutznzbasejqdjm3qxsdcuqvqau3kih2anr2itgmr44wmwtsk6bd42m2j436ptslaugmbbvtbzsukeqytkse7niovlilyfssn3ipzkqtclmetqrxxn7h56xn2ju7pjnuamd6ijfawn2jpjsrmnawaozeniosvhgovftoj24dl77ytdkxdl7ogappnlgkqsjyy43urtnj6tqf2psfptkbzyx4nu3rzgsqqi5ybx3pu6cvt6de67xutdz566wrkp2ymy5n7tqchmw77ss532noqcbfxv6quum6jmeed3exasdapvid6bilwzm5dcnutkcxktmsdryqopw5ntws3tnbd7um27clmxkgl2uinwzvv4tmo4axbj5zmgfd6sy2fiw6efdwjcyj4awdx3peuxcyh3ccee63w2nqaltierdrevl3f3hnjsrdrl4aosd23szhhaimhg2mjuocottcdsraoczh3waoyxx2skunaphe6w5gutu2z7cag3cx4pgsspfmspphuunzx357x6l36hj3tdys727rhawfwc4wc4ytgts4nrxlxl3xxzffunlhjhzj5guxljwxfrxwjfsg5c67pg3js7gvfqmpson6rjgiuwbsklranwhauq74lbesavftdzf7y3x5zwbi4uu6q2vqimbkttm7k6ycttsgknej2ylkwdxgtut7wecpepnb527pblj3vuzldjt3whsmstax536plulalxtxmvj6vvg4phofyaidhxhhkl4dfb6oabp3fi55jt77pw3jl55pwbsimjpdanuenll2xxctr6swaimuaqk4wvqa6rbgow3onr74v4alkuukc2tlmorvsjpgaazpun6pbfyorazzarhc2r7fjt55pmosowrqcpdwl2q34hcco2f3icmpktchxdvtpmitufnplqaifbtlktkpo7b22244n6dkmxtgcnxtixsit57uhh7rc5rqezjz7pfd7ojhrui5bcdzb7bo2nbo6o24lpwbg4bmqgbqpbwclq6kglgxefryxlkqydillki3545vcrelfw6reszml6emuyjscx377on2qpq26j5jrh5xmbwmpcyq6sewanlbmwwk2vqhq5zunbcyd6h5z3ms3bgfn7lflvev5vwmjnv5nzbgrmpamy453zuvy6xc6jp7tqgpmrlxup7suptejbacm6rdurdhcaori6i25wylgaikfov4dfgeswxdeerogy2m5tbzsdlr7pfhchd4wnokuipfwjzejxiruj5cljm66hvn47j3eseys3nsi6xdh566jgap5s5e7ytdkkhh5lsuv47oose4luozz427dzk577jccjg3n7b4myd565edmsywol3hgh2i54lcya6saaaaaaa"), |
|---|
| 80 | 6: base32.a2b(b"krqwq33febwxk5dbmjwgkiddn5xhiyljnzsxeidwgefhkckeaohar2c5jzdcrekne6myzpxl2z65d6ufdjuuyhabg2j57ecmy23jyflcp7djzupj4tfr345bkg7cmwxmpmn3h4iaaaaaaaaabb5aaaaaaaaaacsoaaaaaakjl2ynu3sjzjwrfjn4cwfspkueq47j6ej2uodmjsjexyray7dn6ut4nnuftdhhgxo3t3a5eoipsdy5evdihyeigny3c4adtpveplcwt76m7naar2c5jzdcrekne6myzpxl2z65d6ufdjuqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarybcjqw7lbehfrbu7pwx26vvv3mbjprnqup7q7wxmdsoyiqaqzgbtv6cplrrgpzbnptsoqz7imnauamfaaaaaaaaaaamgaaaaaaaaaaaymaaaaghqaaacr4aaaayxaaaagnyaaaaaaaaaao4qaaaaaaaaacd2gcbacibqbudaskugjcdpodibaeaqkaadqiaq2abqqiaqqaucaeaqbvkghg3s5ivqiq4uiajdzagl7iqhz2uxwj6aqk47oscyqxyljmxkapldxhxigum2ejxflnps3yrxwwehop2zairilrouid5s6wxgnoqg2vq7rx77icxfnx7mq65niuknra3tezhpndwo7pdxtvtci645p4umyhdh5gp3kbdpypphvouaql662r6ufigp4wwm6emrsavlontn4wttg6lv7bcmq6ojw5utpvz3agoenovrkndncjzxog7sp2w7l6jkmzgfxd2asxos5khkjbxbuhgkd6j5yqlzsmk3kq67engtlgyd7hxk7nedw73bq2bs4353wtler23poucntgve22acfxdbyj2f6q2saj2agu2ptfk364d3zayddffxkcah4olnkczjonselwwrvdcu3vch3licaeirogosgsui3y4ovcyzleurbiunp7fsfk2fgmiek7b3jogvrhj4snvkpqjfh2w2lqnkvqvuoojmgrn6sll354mpomajbtlsv6fuguzgsxsm3l2ehxdxfdv6kcoyogaa5raetooahunf4ltvfdb4d7mwgpj4pg7tmcw55ku5vgx7tjqkdkuyq2uvhavkbsbujsrcfzve5ekuhftxm7nmtomibrblbwyxcr5mcy6qqwx66lrhejmgewrig74nzpriualhw4g22qaw423qeezqn6irea3vlgc3foz4egf6irincownoq7utv643vmtoueebigwrl6nehbos3ipsdx44tmucpvyui2jzgfulb5mrrcjuycmuzfigu6tf25lbysqn7n7smhqniddgctjt573rtd6o63wiaactacm7dw7giohzcgwe4okbijnmzlacetmfjjbasrd6whexjmwkaiaaazuum3xriq54h5v6afcrrl3kkbd46oizwulc5fbslmblxfc3ldyyqaaszc7rkciv6rhwt5gbgnl5u54ihnqrfyuh7s54r2444mrhcwgizieaalkclm4iljq34daut2vffpxdlkklamhwyod66dgimv5alle47lszewah5lt22m7poc3nvamk7462qlijpzfe7cy4x5udwfpuznzy7rlhx7ev5hmvxi5m3nctyofw2axz6a4fttdxoefezaqu7wur2rtcmxx5wxmpdkfflvzvawzr2oecq7yriklbc2nfyk4ezeulmdaktctlwcoz26jt3yx5gg2ez6jnhblc5swn7qbl6t3ebm2fmworvtrpxyqhegsly6xtpbh2yfdu6ww52ypka6cc4crgov33cdnbxyekdmjck2h55ni4othks6df52kvobtcnscytmjrrfbekvwmhtbcke2cgcyaj2cra7xmnd4bw2xe2qki5kycopo45ekfyxwzsmxuyxvjzqklnqjwm3j3gwcm75ftnrsvbj33w6eyr4dbz2tewum7vfsyfbb3ojw5ujtn22jxpr4nkmkqup6z7ukpp4cdxwyge2psc5suaxaltmp23gbawoo3qjeo44hgwgtkge2oowox3fpxwxkckaqgrxilfeyxmjp4cwf2rhpkbwtveqkukchv3u5rfkykwfj5zhleu3obsif6ldfclaef32wjpe5d6ddh2grdx2yt6tuhw53t6zuuumlw6t6i3e2ev7nh36e4shwbn3ew2bbahn6imgb5sismfttf5cdlr5kw6wvjoaqiaiz2onxecfx735dvon7epthnklq67tnqj4fvcwechbvmnkpiwd5fd36dirpshc7i7zj3rcr5y3kzps5nfnfnik27gdgemfn27iblcjh5bpkio6sr375bmxbh6fshbo7cvjzsdsirsafnbjzgl6ycqczwbacj5sxwgrzl6qbdhfbzev5fzutznzbasejqdjm3qxsdcuqvqau3kih2anr2itgmr44wmwtsk6bd42m2j436ptslaugmbbvtbzsukeqytkse7niovlilyfssn3ipzkqtclmetqrxxn7h56xn2ju7pjnuamd6ijfawn2jpjsrmnawaozeniosvhgovftoj24dl77ytdkxdl7ogappnlgkqsjyy43urtnj6tqf2psfptkbzyx4nu3rzgsqqi5ybx3pu6cvt6de67xutdz566wrkp2ymy5n7tqchmw77ss532noqcbfxv6quum6jmeed3exasdapvid6bilwzm5dcnutkcxktmsdryqopw5ntws3tnbd7um27clmxkgl2uinwzvv4tmo4axbj5zmgfd6sy2fiw6efdwjcyj4awdx3peuxcyh3ccee63w2nqaltierdrevl3f3hnjsrdrl4aosd23szhhaimhg2mjuocottcdsraoczh3waoyxx2skunaphe6w5gutu2z7cag3cx4pgsspfmspphuunzx357x6l36hj3tdys727rhawfwc4wc4ytgts4nrxlxl3xxzffunlhjhzj5guxljwxfrxwjfsg5c67pg3js7gvfqmpson6rjgiuwbsklranwhauq74lbesavftdzf7y3x5zwbi4uu6q2vqimbkttm7k6ycttsgknej2ylkwdxgtut7wecpepnb527pblj3vuzldjt3whsmstax536plulalxtxmvj6vvg4phofyaidhxhhkl4dfb6oabp3fi55jt77pw3jl55pwbsimjpdanuenll2xxctr6swaimuaqk4wvqa6rbgow3onr74v4alkuukc2tlmorvsjpgaazpun6pbfyorazzarhc2r7fjt55pmosowrqcpdwl2q34hcco2f3icmpktchxdvtpmitufnplqaifbtlktkpo7b22244n6dkmxtgcnxtixsit57uhh7rc5rqezjz7pfd7ojhrui5bcdzb7bo2nbo6o24lpwbg4bmqgbqpbwclq6kglgxefryxlkqydillki3545vcrelfw6reszml6emuyjscx377on2qpq26j5jrh5xmbwmpcyq6sewanlbmwwk2vqhq5zunbcyd6h5z3ms3bgfn7lflvev5vwmjnv5nzbgrmpamy453zuvy6xc6jp7tqgpmrlxup7suptejbacm6rdurdhcaori6i25wylgaikfov4dfgeswxdeerogy2m5tbzsdlr7pfhchd4wnokuipfwjzejxiruj5cljm66hvn47j3eseys3nsi6xdh566jgap5s5e7ytdkkhh5lsuv47oose4luozz427dzk577jccjg3n7b4myd565edmsywol3hgh2i54lcya6saaaaaaa"), |
|---|
| 81 | }, |
|---|
| 82 | 4: { # client[4] |
|---|
| 83 | 0: base32.a2b(b"krqwq33febwxk5dbmjwgkiddn5xhiyljnzsxeidwgefhkckeaohanguihdeqshi3vbil354mnoip7yzj3rpsvjbydjlngiqocl2s6dja4dqjzuaghaekxoithualnjp6artv6laaaaaaaaaabb5aaaaaaaaaacsoaaaaaakjl2ynvzguwqjavmynllmjm66qaqz4uh4dinujrxcaafvp5vvzrgueu3fxwkppvopapdw3p4hjezva23vxif5rzgacysmyo7tr4tjd44nnqpsanguihdeqshi3vbil354mnoip7yzj3rpqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarybcjqw7lbehfrbu7pwx26vvv3mbjprnqup7q7wxmdsoyiqaqzgbtv6cplrrgpzbnptsoqz7imnauamfaaaaaaaaaaamgaaaaaaaaaaaymaaaaghqaaacr4aaaayxaaaagnyaaaaaaaaaao4qaaaaaaaaacd2gcbacibqbudaskugjcdpodibaeaqkaadqiaq2abqqiaqqaucaeaqbvkghg3s5ivqiq4uiajdzagl7iqhz2uxwj6aqk47oscyqxyljmxkapldxhxigum2ejxflnps3yrxwwehop2zairilrouid5s6wxgnoqg2vq7rx77icxfnx7mq65niuknra3tezhpndwo7pdxtvtci645p4umyhdh5gp3kbdpypphvouaql662r6ufigp4wwm6emrsavlontn4wttg6lv7bcmq6ojw5utpvz3agoenovrkndncjzxog7sp2w7l6jkmzgfxd2asxos5khkjbxbuhgkd6j5yqlzsmk3kq67engtlgyd7hxk7nedw73bq2bs4353wtler23poucntgve22acfxdbyj2f6q2saj2agu2ptfk364d3zayddffxkcah4olnkczjonselwwrvdcu3vch3licaeirogosgsui3y4ovcyzleurbiunp7fsfk2fgmiek7b3jogvrhj4snvkpqjfh2w2lqnkvqvuoojmgrn6sll354mpomajbtlsv6fuguzgsxsm3l2ehxdxfdv6kcoyogaa5raetooahunf4ltvfdb4d7mwgpj4pg7tmcw55ku5vgx7tjqkdkuyq2uvhavkbsbujsrcfzve5ekuhftxm7nmtomibrblbwyxcr5mcy6qqwx66lrhejmgewrig74nzpriualhw4g22qaw423qeezqn6irea3vlgc3foz4egf6irincownoq7utv643vmtoueebigwrl6nehbos3ipsdx44tmucpvyui2jzgfulb5mrrcjuycmuzfigu6tf25lbysqn7n7smhqniddgctjt573rtd6o63wiaactacm7dw7giohzcgwe4okbijnmzlacetmfjjbasrd6whexjmwkaiaabdm4cpjolak4m47telnokjaxwodj7ont7n7vffnmhkzp3lyshkh3qaarzybn64ru5rss7tmi4ttv26q66ebdvvrtyd3s5t7dmqku3uoefroaaibdqu2gyd4hqwgj3jhsu7ievr26vxpzj4g6ovbvqeyljrk6n2xfidtwj6pazanrhwes3e4ln4uettqyd5u5bqroneqie7lkwlxm7xsbg4zhnlc2fybonhlpcatwlgdvk3jpn7sge4qnod2ufxgxc7rphbnunb52xrgmdgpojqhyfajxealxwdddlhhbttphrgv5zrub5mggbcec3honrtuuv3epex3s5yvkt2zmsaxfeu34psjwjltm4ys5qa72ryrmgjtmtu3i34jfmachhmgul2j2sddwydgvtpqnatglb3ejlhukxp3isthks6df52kvobtcnscytmjrrfbekvwmhtbcke2cgcyaj2cra7xmnd4bw2xe2qki5kycopo45ekfyxwzsmxuyxvjzqklnqjwm3j3gwcm75ftnrsvbj33w6eyr4dbz2tewum7vfsyfbb3ojw5ujtn22jxpr4nkmkqup6z7ukpp4cdxwyge2psc5suaxaltmp23gbawoo3qjeo44hgwgtkge2oowox3fpxwxkckaqgrxilfeyxmjp4cwf2rhpkbwtveqkukchv3u5rfkykwfj5zhleu3obsif6ldfclaef32wjpe5d6ddh2grdx2yt6tuhw53t6zuuumlw6t6i3e2ev7nh36e4shwbn3ew2bbahn6imgb5sismfttf5cdlr5kw6wvjoaqiaiz2onxecfx735dvon7epthnklq67tnqj4fvcwechbvmnkpiwd5fd36dirpshc7i7zj3rcr5y3kzps5nfnfnik27gdgemfn27iblcjh5bpkio6sr375bmxbh6fshbo7cvjzsdsirsafnbjzgl6ycqczwbacj5sxwgrzl6qbdhfbzev5fzutznzbasejqdjm3qxsdcuqvqau3kih2anr2itgmr44wmwtsk6bd42m2j436ptslaugmbbvtbzsukeqytkse7niovlilyfssn3ipzkqtclmetqrxxn7h56xn2ju7pjnuamd6ijfawn2jpjsrmnawaozeniosvhgovftoj24dl77ytdkxdl7ogappnlgkqsjyy43urtnj6tqf2psfptkbzyx4nu3rzgsqqi5ybx3pu6cvt6de67xutdz566wrkp2ymy5n7tqchmw77ss532noqcbfxv6quum6jmeed3exasdapvid6bilwzm5dcnutkcxktmsdryqopw5ntws3tnbd7um27clmxkgl2uinwzvv4tmo4axbj5zmgfd6sy2fiw6efdwjcyj4awdx3peuxcyh3ccee63w2nqaltierdrevl3f3hnjsrdrl4aosd23szhhaimhg2mjuocottcdsraoczh3waoyxx2skunaphe6w5gutu2z7cag3cx4pgsspfmspphuunzx357x6l36hj3tdys727rhawfwc4wc4ytgts4nrxlxl3xxzffunlhjhzj5guxljwxfrxwjfsg5c67pg3js7gvfqmpson6rjgiuwbsklranwhauq74lbesavftdzf7y3x5zwbi4uu6q2vqimbkttm7k6ycttsgknej2ylkwdxgtut7wecpepnb527pblj3vuzldjt3whsmstax536plulalxtxmvj6vvg4phofyaidhxhhkl4dfb6oabp3fi55jt77pw3jl55pwbsimjpdanuenll2xxctr6swaimuaqk4wvqa6rbgow3onr74v4alkuukc2tlmorvsjpgaazpun6pbfyorazzarhc2r7fjt55pmosowrqcpdwl2q34hcco2f3icmpktchxdvtpmitufnplqaifbtlktkpo7b22244n6dkmxtgcnxtixsit57uhh7rc5rqezjz7pfd7ojhrui5bcdzb7bo2nbo6o24lpwbg4bmqgbqpbwclq6kglgxefryxlkqydillki3545vcrelfw6reszml6emuyjscx377on2qpq26j5jrh5xmbwmpcyq6sewanlbmwwk2vqhq5zunbcyd6h5z3ms3bgfn7lflvev5vwmjnv5nzbgrmpamy453zuvy6xc6jp7tqgpmrlxup7suptejbacm6rdurdhcaori6i25wylgaikfov4dfgeswxdeerogy2m5tbzsdlr7pfhchd4wnokuipfwjzejxiruj5cljm66hvn47j3eseys3nsi6xdh566jgap5s5e7ytdkkhh5lsuv47oose4luozz427dzk577jccjg3n7b4myd565edmsywol3hgh2i54lcya6saaaaaaa"), |
|---|
| 84 | 5: base32.a2b(b"krqwq33febwxk5dbmjwgkiddn5xhiyljnzsxeidwgefhkckeaohanguihdeqshi3vbil354mnoip7yzj3rpsvjbydjlngiqocl2s6dja4dqjzuaghaekxoithualnjp6artv6laaaaaaaaaabb5aaaaaaaaaacsoaaaaaakjl2ynvzguwqjavmynllmjm66qaqz4uh4dinujrxcaafvp5vvzrgueu3fxwkppvopapdw3p4hjezva23vxif5rzgacysmyo7tr4tjd44nnqpsanguihdeqshi3vbil354mnoip7yzj3rpqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarybcjqw7lbehfrbu7pwx26vvv3mbjprnqup7q7wxmdsoyiqaqzgbtv6cplrrgpzbnptsoqz7imnauamfaaaaaaaaaaamgaaaaaaaaaaaymaaaaghqaaacr4aaaayxaaaagnyaaaaaaaaaao4qaaaaaaaaacd2gcbacibqbudaskugjcdpodibaeaqkaadqiaq2abqqiaqqaucaeaqbvkghg3s5ivqiq4uiajdzagl7iqhz2uxwj6aqk47oscyqxyljmxkapldxhxigum2ejxflnps3yrxwwehop2zairilrouid5s6wxgnoqg2vq7rx77icxfnx7mq65niuknra3tezhpndwo7pdxtvtci645p4umyhdh5gp3kbdpypphvouaql662r6ufigp4wwm6emrsavlontn4wttg6lv7bcmq6ojw5utpvz3agoenovrkndncjzxog7sp2w7l6jkmzgfxd2asxos5khkjbxbuhgkd6j5yqlzsmk3kq67engtlgyd7hxk7nedw73bq2bs4353wtler23poucntgve22acfxdbyj2f6q2saj2agu2ptfk364d3zayddffxkcah4olnkczjonselwwrvdcu3vch3licaeirogosgsui3y4ovcyzleurbiunp7fsfk2fgmiek7b3jogvrhj4snvkpqjfh2w2lqnkvqvuoojmgrn6sll354mpomajbtlsv6fuguzgsxsm3l2ehxdxfdv6kcoyogaa5raetooahunf4ltvfdb4d7mwgpj4pg7tmcw55ku5vgx7tjqkdkuyq2uvhavkbsbujsrcfzve5ekuhftxm7nmtomibrblbwyxcr5mcy6qqwx66lrhejmgewrig74nzpriualhw4g22qaw423qeezqn6irea3vlgc3foz4egf6irincownoq7utv643vmtoueebigwrl6nehbos3ipsdx44tmucpvyui2jzgfulb5mrrcjuycmuzfigu6tf25lbysqn7n7smhqniddgctjt573rtd6o63wiaactacm7dw7giohzcgwe4okbijnmzlacetmfjjbasrd6whexjmwkaiaaazuum3xriq54h5v6afcrrl3kkbd46oizwulc5fbslmblxfc3ldyyqaavmjy6g336aewy42vw5rusytyi7vzs6y22c5jhxyt5w6gthcbjp4zaajwnpw5yhhwh4hyctajptujjwg7cswzjkwucke6yvbuejqhrnbafadv245phzjfluujm5pyfx43oagwtsdkgtw2v4i56uexjrumsdes6go7556an26wmzpbskyrsx4qbzqcedilovhlkrlnhvsfr4mjwkw62mkf4kde7jgesu4ztbzc7xmuobydnxk5hdyyly6n7socvrsqw6z56v6osxr2vgxpz6jor7ciyclkungeaayume5hdrm6cbnvwgua4gc2fcpixfdbkiijnmlicribyoinnpu6zdce4mdfqyl4qzup3kkk5qju2wthks6df52kvobtcnscytmjrrfbekvwmhtbcke2cgcyaj2cra7xmnd4bw2xe2qki5kycopo45ekfyxwzsmxuyxvjzqklnqjwm3j3gwcm75ftnrsvbj33w6eyr4dbz2tewum7vfsyfbb3ojw5ujtn22jxpr4nkmkqup6z7ukpp4cdxwyge2psc5suaxaltmp23gbawoo3qjeo44hgwgtkge2oowox3fpxwxkckaqgrxilfeyxmjp4cwf2rhpkbwtveqkukchv3u5rfkykwfj5zhleu3obsif6ldfclaef32wjpe5d6ddh2grdx2yt6tuhw53t6zuuumlw6t6i3e2ev7nh36e4shwbn3ew2bbahn6imgb5sismfttf5cdlr5kw6wvjoaqiaiz2onxecfx735dvon7epthnklq67tnqj4fvcwechbvmnkpiwd5fd36dirpshc7i7zj3rcr5y3kzps5nfnfnik27gdgemfn27iblcjh5bpkio6sr375bmxbh6fshbo7cvjzsdsirsafnbjzgl6ycqczwbacj5sxwgrzl6qbdhfbzev5fzutznzbasejqdjm3qxsdcuqvqau3kih2anr2itgmr44wmwtsk6bd42m2j436ptslaugmbbvtbzsukeqytkse7niovlilyfssn3ipzkqtclmetqrxxn7h56xn2ju7pjnuamd6ijfawn2jpjsrmnawaozeniosvhgovftoj24dl77ytdkxdl7ogappnlgkqsjyy43urtnj6tqf2psfptkbzyx4nu3rzgsqqi5ybx3pu6cvt6de67xutdz566wrkp2ymy5n7tqchmw77ss532noqcbfxv6quum6jmeed3exasdapvid6bilwzm5dcnutkcxktmsdryqopw5ntws3tnbd7um27clmxkgl2uinwzvv4tmo4axbj5zmgfd6sy2fiw6efdwjcyj4awdx3peuxcyh3ccee63w2nqaltierdrevl3f3hnjsrdrl4aosd23szhhaimhg2mjuocottcdsraoczh3waoyxx2skunaphe6w5gutu2z7cag3cx4pgsspfmspphuunzx357x6l36hj3tdys727rhawfwc4wc4ytgts4nrxlxl3xxzffunlhjhzj5guxljwxfrxwjfsg5c67pg3js7gvfqmpson6rjgiuwbsklranwhauq74lbesavftdzf7y3x5zwbi4uu6q2vqimbkttm7k6ycttsgknej2ylkwdxgtut7wecpepnb527pblj3vuzldjt3whsmstax536plulalxtxmvj6vvg4phofyaidhxhhkl4dfb6oabp3fi55jt77pw3jl55pwbsimjpdanuenll2xxctr6swaimuaqk4wvqa6rbgow3onr74v4alkuukc2tlmorvsjpgaazpun6pbfyorazzarhc2r7fjt55pmosowrqcpdwl2q34hcco2f3icmpktchxdvtpmitufnplqaifbtlktkpo7b22244n6dkmxtgcnxtixsit57uhh7rc5rqezjz7pfd7ojhrui5bcdzb7bo2nbo6o24lpwbg4bmqgbqpbwclq6kglgxefryxlkqydillki3545vcrelfw6reszml6emuyjscx377on2qpq26j5jrh5xmbwmpcyq6sewanlbmwwk2vqhq5zunbcyd6h5z3ms3bgfn7lflvev5vwmjnv5nzbgrmpamy453zuvy6xc6jp7tqgpmrlxup7suptejbacm6rdurdhcaori6i25wylgaikfov4dfgeswxdeerogy2m5tbzsdlr7pfhchd4wnokuipfwjzejxiruj5cljm66hvn47j3eseys3nsi6xdh566jgap5s5e7ytdkkhh5lsuv47oose4luozz427dzk577jccjg3n7b4myd565edmsywol3hgh2i54lcya6saaaaaaa"), |
|---|
| 85 | }, |
|---|
| 86 | } |
|---|
| 87 | #--------- END stored_shares.py ---------------- |
|---|
| 88 | |
|---|
| 89 | class _Base(GridTestMixin, ShouldFailMixin): |
|---|
| 90 | |
|---|
| 91 | def create_shares(self, ignored=None): |
|---|
| 92 | u = upload.Data(plaintext, None) |
|---|
| 93 | d = self.c0.upload(u) |
|---|
| 94 | f = open("stored_shares.py", "w") |
|---|
| 95 | def _created_immutable(ur): |
|---|
| 96 | # write the generated shares and URI to a file, which can then be |
|---|
| 97 | # incorporated into this one next time. |
|---|
| 98 | f.write('immutable_uri = b"%s"\n' % ur.get_uri()) |
|---|
| 99 | f.write('immutable_shares = {\n') |
|---|
| 100 | si = uri.from_string(ur.get_uri()).get_storage_index() |
|---|
| 101 | si_dir = storage_index_to_dir(si) |
|---|
| 102 | for (i,ss,ssdir) in self.iterate_servers(): |
|---|
| 103 | sharedir = os.path.join(ssdir, "shares", si_dir) |
|---|
| 104 | shares = {} |
|---|
| 105 | for fn in os.listdir(sharedir): |
|---|
| 106 | shnum = int(fn) |
|---|
| 107 | sharedata = open(os.path.join(sharedir, fn), "rb").read() |
|---|
| 108 | shares[shnum] = sharedata |
|---|
| 109 | fileutil.rm_dir(sharedir) |
|---|
| 110 | if shares: |
|---|
| 111 | f.write(' %d: { # client[%d]\n' % (i, i)) |
|---|
| 112 | for shnum in sorted(shares.keys()): |
|---|
| 113 | f.write(' %d: base32.a2b(b"%s"),\n' % |
|---|
| 114 | (shnum, base32.b2a(shares[shnum]))) |
|---|
| 115 | f.write(' },\n') |
|---|
| 116 | f.write('}\n') |
|---|
| 117 | f.write('\n') |
|---|
| 118 | |
|---|
| 119 | d.addCallback(_created_immutable) |
|---|
| 120 | |
|---|
| 121 | d.addCallback(lambda ignored: |
|---|
| 122 | self.c0.create_mutable_file(mutable_plaintext)) |
|---|
| 123 | def _created_mutable(n): |
|---|
| 124 | f.write('mutable_uri = b"%s"\n' % n.get_uri()) |
|---|
| 125 | f.write('mutable_shares = {\n') |
|---|
| 126 | si = uri.from_string(n.get_uri()).get_storage_index() |
|---|
| 127 | si_dir = storage_index_to_dir(si) |
|---|
| 128 | for (i,ss,ssdir) in self.iterate_servers(): |
|---|
| 129 | sharedir = os.path.join(ssdir, "shares", si_dir) |
|---|
| 130 | shares = {} |
|---|
| 131 | for fn in os.listdir(sharedir): |
|---|
| 132 | shnum = int(fn) |
|---|
| 133 | sharedata = open(os.path.join(sharedir, fn), "rb").read() |
|---|
| 134 | shares[shnum] = sharedata |
|---|
| 135 | fileutil.rm_dir(sharedir) |
|---|
| 136 | if shares: |
|---|
| 137 | f.write(' %d: { # client[%d]\n' % (i, i)) |
|---|
| 138 | for shnum in sorted(shares.keys()): |
|---|
| 139 | f.write(' %d: base32.a2b(b"%s"),\n' % |
|---|
| 140 | (shnum, base32.b2a(shares[shnum]))) |
|---|
| 141 | f.write(' },\n') |
|---|
| 142 | f.write('}\n') |
|---|
| 143 | |
|---|
| 144 | f.close() |
|---|
| 145 | d.addCallback(_created_mutable) |
|---|
| 146 | |
|---|
| 147 | def _done(ignored): |
|---|
| 148 | f.close() |
|---|
| 149 | d.addCallback(_done) |
|---|
| 150 | |
|---|
| 151 | return d |
|---|
| 152 | |
|---|
| 153 | def load_shares(self, ignored=None): |
|---|
| 154 | # this uses the data generated by create_shares() to populate the |
|---|
| 155 | # storage servers with pre-generated shares |
|---|
| 156 | si = uri.from_string(immutable_uri).get_storage_index() |
|---|
| 157 | si_dir = storage_index_to_dir(si) |
|---|
| 158 | for i in immutable_shares: |
|---|
| 159 | shares = immutable_shares[i] |
|---|
| 160 | for shnum in shares: |
|---|
| 161 | dn = os.path.join(self.get_serverdir(i), "shares", si_dir) |
|---|
| 162 | fileutil.make_dirs(dn) |
|---|
| 163 | fn = os.path.join(dn, str(shnum)) |
|---|
| 164 | f = open(fn, "wb") |
|---|
| 165 | f.write(shares[shnum]) |
|---|
| 166 | f.close() |
|---|
| 167 | |
|---|
| 168 | si = uri.from_string(mutable_uri).get_storage_index() |
|---|
| 169 | si_dir = storage_index_to_dir(si) |
|---|
| 170 | for i in mutable_shares: |
|---|
| 171 | shares = mutable_shares[i] |
|---|
| 172 | for shnum in shares: |
|---|
| 173 | dn = os.path.join(self.get_serverdir(i), "shares", si_dir) |
|---|
| 174 | fileutil.make_dirs(dn) |
|---|
| 175 | fn = os.path.join(dn, str(shnum)) |
|---|
| 176 | f = open(fn, "wb") |
|---|
| 177 | f.write(shares[shnum]) |
|---|
| 178 | f.close() |
|---|
| 179 | |
|---|
| 180 | def download_immutable(self, ignored=None): |
|---|
| 181 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 182 | d = download_to_data(n) |
|---|
| 183 | def _got_data(data): |
|---|
| 184 | self.failUnlessEqual(data, plaintext) |
|---|
| 185 | d.addCallback(_got_data) |
|---|
| 186 | # make sure we can use the same node twice |
|---|
| 187 | d.addCallback(lambda ign: download_to_data(n)) |
|---|
| 188 | d.addCallback(_got_data) |
|---|
| 189 | return d |
|---|
| 190 | |
|---|
| 191 | def download_mutable(self, ignored=None): |
|---|
| 192 | n = self.c0.create_node_from_uri(mutable_uri) |
|---|
| 193 | d = n.download_best_version() |
|---|
| 194 | def _got_data(data): |
|---|
| 195 | self.failUnlessEqual(data, mutable_plaintext) |
|---|
| 196 | d.addCallback(_got_data) |
|---|
| 197 | return d |
|---|
| 198 | |
|---|
| 199 | class DownloadTest(_Base, unittest.TestCase): |
|---|
| 200 | def test_download(self): |
|---|
| 201 | self.basedir = self.mktemp() |
|---|
| 202 | self.set_up_grid() |
|---|
| 203 | self.c0 = self.g.clients[0] |
|---|
| 204 | |
|---|
| 205 | # do this to create the shares |
|---|
| 206 | #return self.create_shares() |
|---|
| 207 | |
|---|
| 208 | self.load_shares() |
|---|
| 209 | d = self.download_immutable() |
|---|
| 210 | d.addCallback(self.download_mutable) |
|---|
| 211 | return d |
|---|
| 212 | |
|---|
| 213 | def test_download_failover(self): |
|---|
| 214 | self.basedir = self.mktemp() |
|---|
| 215 | self.set_up_grid() |
|---|
| 216 | self.c0 = self.g.clients[0] |
|---|
| 217 | |
|---|
| 218 | self.load_shares() |
|---|
| 219 | si = uri.from_string(immutable_uri).get_storage_index() |
|---|
| 220 | si_dir = storage_index_to_dir(si) |
|---|
| 221 | |
|---|
| 222 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 223 | d = download_to_data(n) |
|---|
| 224 | def _got_data(data): |
|---|
| 225 | self.failUnlessEqual(data, plaintext) |
|---|
| 226 | d.addCallback(_got_data) |
|---|
| 227 | |
|---|
| 228 | def _clobber_some_shares(ign): |
|---|
| 229 | # find the three shares that were used, and delete them. Then |
|---|
| 230 | # download again, forcing the downloader to fail over to other |
|---|
| 231 | # shares |
|---|
| 232 | for s in n._cnode._node._shares: |
|---|
| 233 | for clientnum in immutable_shares: |
|---|
| 234 | for shnum in immutable_shares[clientnum]: |
|---|
| 235 | if s._shnum == shnum: |
|---|
| 236 | fn = os.path.join(self.get_serverdir(clientnum), |
|---|
| 237 | "shares", si_dir, str(shnum)) |
|---|
| 238 | os.unlink(fn) |
|---|
| 239 | d.addCallback(_clobber_some_shares) |
|---|
| 240 | d.addCallback(lambda ign: download_to_data(n)) |
|---|
| 241 | d.addCallback(_got_data) |
|---|
| 242 | |
|---|
| 243 | def _clobber_most_shares(ign): |
|---|
| 244 | # delete all but one of the shares that are still alive |
|---|
| 245 | live_shares = [s for s in n._cnode._node._shares if s.is_alive()] |
|---|
| 246 | save_me = live_shares[0]._shnum |
|---|
| 247 | for clientnum in immutable_shares: |
|---|
| 248 | for shnum in immutable_shares[clientnum]: |
|---|
| 249 | if shnum == save_me: |
|---|
| 250 | continue |
|---|
| 251 | fn = os.path.join(self.get_serverdir(clientnum), |
|---|
| 252 | "shares", si_dir, str(shnum)) |
|---|
| 253 | if os.path.exists(fn): |
|---|
| 254 | os.unlink(fn) |
|---|
| 255 | # now the download should fail with NotEnoughSharesError |
|---|
| 256 | return self.shouldFail(NotEnoughSharesError, "1shares", None, |
|---|
| 257 | download_to_data, n) |
|---|
| 258 | d.addCallback(_clobber_most_shares) |
|---|
| 259 | |
|---|
| 260 | def _clobber_all_shares(ign): |
|---|
| 261 | # delete the last remaining share |
|---|
| 262 | for clientnum in immutable_shares: |
|---|
| 263 | for shnum in immutable_shares[clientnum]: |
|---|
| 264 | fn = os.path.join(self.get_serverdir(clientnum), |
|---|
| 265 | "shares", si_dir, str(shnum)) |
|---|
| 266 | if os.path.exists(fn): |
|---|
| 267 | os.unlink(fn) |
|---|
| 268 | # now a new download should fail with NoSharesError. We want a |
|---|
| 269 | # new ImmutableFileNode so it will forget about the old shares. |
|---|
| 270 | # If we merely called create_node_from_uri() without first |
|---|
| 271 | # dereferencing the original node, the NodeMaker's _node_cache |
|---|
| 272 | # would give us back the old one. |
|---|
| 273 | n = None |
|---|
| 274 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 275 | return self.shouldFail(NoSharesError, "0shares", None, |
|---|
| 276 | download_to_data, n) |
|---|
| 277 | d.addCallback(_clobber_all_shares) |
|---|
| 278 | return d |
|---|
| 279 | |
|---|
| 280 | def test_lost_servers(self): |
|---|
| 281 | # while downloading a file (after seg[0], before seg[1]), lose the |
|---|
| 282 | # three servers that we were using. The download should switch over |
|---|
| 283 | # to other servers. |
|---|
| 284 | self.basedir = self.mktemp() |
|---|
| 285 | self.set_up_grid() |
|---|
| 286 | self.c0 = self.g.clients[0] |
|---|
| 287 | |
|---|
| 288 | # upload a file with multiple segments, so we can catch the download |
|---|
| 289 | # in the middle. |
|---|
| 290 | u = upload.Data(plaintext, None) |
|---|
| 291 | u.max_segment_size = 70 # 5 segs |
|---|
| 292 | d = self.c0.upload(u) |
|---|
| 293 | def _uploaded(ur): |
|---|
| 294 | self.uri = ur.get_uri() |
|---|
| 295 | self.n = self.c0.create_node_from_uri(self.uri) |
|---|
| 296 | return download_to_data(self.n) |
|---|
| 297 | d.addCallback(_uploaded) |
|---|
| 298 | def _got_data(data): |
|---|
| 299 | self.failUnlessEqual(data, plaintext) |
|---|
| 300 | d.addCallback(_got_data) |
|---|
| 301 | def _kill_some_shares(): |
|---|
| 302 | # find the shares that were used and delete them |
|---|
| 303 | shares = self.n._cnode._node._shares |
|---|
| 304 | self.killed_share_nums = sorted([s._shnum for s in shares]) |
|---|
| 305 | |
|---|
| 306 | # break the RIBucketReader references |
|---|
| 307 | # (we don't break the RIStorageServer references, because that |
|---|
| 308 | # isn't needed to test the current downloader implementation) |
|---|
| 309 | for s in shares: |
|---|
| 310 | s._rref.broken = True |
|---|
| 311 | def _download_again(ign): |
|---|
| 312 | # download again, deleting some shares after the first write |
|---|
| 313 | # to the consumer |
|---|
| 314 | c = StallingConsumer(_kill_some_shares) |
|---|
| 315 | return self.n.read(c) |
|---|
| 316 | d.addCallback(_download_again) |
|---|
| 317 | def _check_failover(c): |
|---|
| 318 | self.failUnlessEqual(b"".join(c.chunks), plaintext) |
|---|
| 319 | shares = self.n._cnode._node._shares |
|---|
| 320 | shnums = sorted([s._shnum for s in shares]) |
|---|
| 321 | self.failIfEqual(shnums, self.killed_share_nums) |
|---|
| 322 | d.addCallback(_check_failover) |
|---|
| 323 | return d |
|---|
| 324 | |
|---|
| 325 | def test_long_offset(self): |
|---|
| 326 | # bug #1154: mplayer doing a seek-to-end results in an offset of type |
|---|
| 327 | # 'long', rather than 'int', and apparently __len__ is required to |
|---|
| 328 | # return an int. Rewrote Spans/DataSpans to provide s.len() instead |
|---|
| 329 | # of len(s) . |
|---|
| 330 | self.basedir = self.mktemp() |
|---|
| 331 | self.set_up_grid() |
|---|
| 332 | self.c0 = self.g.clients[0] |
|---|
| 333 | self.load_shares() |
|---|
| 334 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 335 | |
|---|
| 336 | c = MemoryConsumer() |
|---|
| 337 | d = n.read(c, int(0), int(10)) |
|---|
| 338 | d.addCallback(lambda c: len(b"".join(c.chunks))) |
|---|
| 339 | d.addCallback(lambda size: self.failUnlessEqual(size, 10)) |
|---|
| 340 | return d |
|---|
| 341 | |
|---|
| 342 | def test_badguess(self): |
|---|
| 343 | self.basedir = self.mktemp() |
|---|
| 344 | self.set_up_grid() |
|---|
| 345 | self.c0 = self.g.clients[0] |
|---|
| 346 | self.load_shares() |
|---|
| 347 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 348 | |
|---|
| 349 | # Cause the downloader to guess a segsize that's too low, so it will |
|---|
| 350 | # ask for a segment number that's too high (beyond the end of the |
|---|
| 351 | # real list, causing BadSegmentNumberError), to exercise |
|---|
| 352 | # Segmentation._retry_bad_segment |
|---|
| 353 | n._cnode._maybe_create_download_node() |
|---|
| 354 | n._cnode._node._build_guessed_tables(90) |
|---|
| 355 | |
|---|
| 356 | con1 = MemoryConsumer() |
|---|
| 357 | # plaintext size of 310 bytes, wrong-segsize of 90 bytes, will make |
|---|
| 358 | # us think that file[180:200] is in the third segment (segnum=2), but |
|---|
| 359 | # really there's only one segment |
|---|
| 360 | d = n.read(con1, 180, 20) |
|---|
| 361 | def _done(res): |
|---|
| 362 | self.failUnlessEqual(b"".join(con1.chunks), plaintext[180:200]) |
|---|
| 363 | d.addCallback(_done) |
|---|
| 364 | return d |
|---|
| 365 | |
|---|
| 366 | def test_simultaneous_badguess(self): |
|---|
| 367 | self.basedir = self.mktemp() |
|---|
| 368 | self.set_up_grid() |
|---|
| 369 | self.c0 = self.g.clients[0] |
|---|
| 370 | |
|---|
| 371 | # upload a file with multiple segments, and a non-default segsize, to |
|---|
| 372 | # exercise the offset-guessing code. Because we don't tell the |
|---|
| 373 | # downloader about the unusual segsize, it will guess wrong, and have |
|---|
| 374 | # to do extra roundtrips to get the correct data. |
|---|
| 375 | u = upload.Data(plaintext, None) |
|---|
| 376 | u.max_segment_size = 70 # 5 segs, 8-wide hashtree |
|---|
| 377 | con1 = MemoryConsumer() |
|---|
| 378 | con2 = MemoryConsumer() |
|---|
| 379 | d = self.c0.upload(u) |
|---|
| 380 | def _uploaded(ur): |
|---|
| 381 | n = self.c0.create_node_from_uri(ur.get_uri()) |
|---|
| 382 | d1 = n.read(con1, 70, 20) |
|---|
| 383 | d2 = n.read(con2, 140, 20) |
|---|
| 384 | return defer.gatherResults([d1,d2]) |
|---|
| 385 | d.addCallback(_uploaded) |
|---|
| 386 | def _done(res): |
|---|
| 387 | self.failUnlessEqual(b"".join(con1.chunks), plaintext[70:90]) |
|---|
| 388 | self.failUnlessEqual(b"".join(con2.chunks), plaintext[140:160]) |
|---|
| 389 | d.addCallback(_done) |
|---|
| 390 | return d |
|---|
| 391 | |
|---|
| 392 | def test_simultaneous_goodguess(self): |
|---|
| 393 | self.basedir = self.mktemp() |
|---|
| 394 | self.set_up_grid() |
|---|
| 395 | self.c0 = self.g.clients[0] |
|---|
| 396 | |
|---|
| 397 | # upload a file with multiple segments, and a non-default segsize, to |
|---|
| 398 | # exercise the offset-guessing code. This time we *do* tell the |
|---|
| 399 | # downloader about the unusual segsize, so it can guess right. |
|---|
| 400 | u = upload.Data(plaintext, None) |
|---|
| 401 | u.max_segment_size = 70 # 5 segs, 8-wide hashtree |
|---|
| 402 | con1 = MemoryConsumer() |
|---|
| 403 | con2 = MemoryConsumer() |
|---|
| 404 | d = self.c0.upload(u) |
|---|
| 405 | def _uploaded(ur): |
|---|
| 406 | n = self.c0.create_node_from_uri(ur.get_uri()) |
|---|
| 407 | n._cnode._maybe_create_download_node() |
|---|
| 408 | n._cnode._node._build_guessed_tables(u.max_segment_size) |
|---|
| 409 | d1 = n.read(con1, 70, 20) |
|---|
| 410 | d2 = n.read(con2, 140, 20) |
|---|
| 411 | return defer.gatherResults([d1,d2]) |
|---|
| 412 | d.addCallback(_uploaded) |
|---|
| 413 | def _done(res): |
|---|
| 414 | self.failUnlessEqual(b"".join(con1.chunks), plaintext[70:90]) |
|---|
| 415 | self.failUnlessEqual(b"".join(con2.chunks), plaintext[140:160]) |
|---|
| 416 | d.addCallback(_done) |
|---|
| 417 | return d |
|---|
| 418 | |
|---|
| 419 | def test_sequential_goodguess(self): |
|---|
| 420 | self.basedir = self.mktemp() |
|---|
| 421 | self.set_up_grid() |
|---|
| 422 | self.c0 = self.g.clients[0] |
|---|
| 423 | data = (plaintext*100)[:30000] # multiple of k |
|---|
| 424 | |
|---|
| 425 | # upload a file with multiple segments, and a non-default segsize, to |
|---|
| 426 | # exercise the offset-guessing code. This time we *do* tell the |
|---|
| 427 | # downloader about the unusual segsize, so it can guess right. |
|---|
| 428 | u = upload.Data(data, None) |
|---|
| 429 | u.max_segment_size = 6000 # 5 segs, 8-wide hashtree |
|---|
| 430 | con1 = MemoryConsumer() |
|---|
| 431 | con2 = MemoryConsumer() |
|---|
| 432 | d = self.c0.upload(u) |
|---|
| 433 | def _uploaded(ur): |
|---|
| 434 | n = self.c0.create_node_from_uri(ur.get_uri()) |
|---|
| 435 | n._cnode._maybe_create_download_node() |
|---|
| 436 | n._cnode._node._build_guessed_tables(u.max_segment_size) |
|---|
| 437 | d = n.read(con1, 12000, 20) |
|---|
| 438 | def _read1(ign): |
|---|
| 439 | self.failUnlessEqual(b"".join(con1.chunks), data[12000:12020]) |
|---|
| 440 | return n.read(con2, 24000, 20) |
|---|
| 441 | d.addCallback(_read1) |
|---|
| 442 | def _read2(ign): |
|---|
| 443 | self.failUnlessEqual(b"".join(con2.chunks), data[24000:24020]) |
|---|
| 444 | d.addCallback(_read2) |
|---|
| 445 | return d |
|---|
| 446 | d.addCallback(_uploaded) |
|---|
| 447 | return d |
|---|
| 448 | |
|---|
| 449 | |
|---|
| 450 | def test_simultaneous_get_blocks(self): |
|---|
| 451 | self.basedir = self.mktemp() |
|---|
| 452 | self.set_up_grid() |
|---|
| 453 | self.c0 = self.g.clients[0] |
|---|
| 454 | |
|---|
| 455 | self.load_shares() |
|---|
| 456 | stay_empty = [] |
|---|
| 457 | |
|---|
| 458 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 459 | d = download_to_data(n) |
|---|
| 460 | def _use_shares(ign): |
|---|
| 461 | shares = list(n._cnode._node._shares) |
|---|
| 462 | s0 = shares[0] |
|---|
| 463 | # make sure .cancel works too |
|---|
| 464 | o0 = s0.get_block(0) |
|---|
| 465 | o0.subscribe(lambda **kwargs: stay_empty.append(kwargs)) |
|---|
| 466 | o1 = s0.get_block(0) |
|---|
| 467 | o2 = s0.get_block(0) |
|---|
| 468 | o0.cancel() |
|---|
| 469 | o3 = s0.get_block(1) # state=BADSEGNUM |
|---|
| 470 | d1 = defer.Deferred() |
|---|
| 471 | d2 = defer.Deferred() |
|---|
| 472 | d3 = defer.Deferred() |
|---|
| 473 | o1.subscribe(lambda **kwargs: d1.callback(kwargs)) |
|---|
| 474 | o2.subscribe(lambda **kwargs: d2.callback(kwargs)) |
|---|
| 475 | o3.subscribe(lambda **kwargs: d3.callback(kwargs)) |
|---|
| 476 | return defer.gatherResults([d1,d2,d3]) |
|---|
| 477 | d.addCallback(_use_shares) |
|---|
| 478 | def _done(res): |
|---|
| 479 | r1,r2,r3 = res |
|---|
| 480 | self.failUnlessEqual(r1["state"], "COMPLETE") |
|---|
| 481 | self.failUnlessEqual(r2["state"], "COMPLETE") |
|---|
| 482 | self.failUnlessEqual(r3["state"], "BADSEGNUM") |
|---|
| 483 | self.failUnless("block" in r1) |
|---|
| 484 | self.failUnless("block" in r2) |
|---|
| 485 | self.failIf(stay_empty) |
|---|
| 486 | d.addCallback(_done) |
|---|
| 487 | return d |
|---|
| 488 | |
|---|
| 489 | def test_simul_1fail_1cancel(self): |
|---|
| 490 | # This exercises an mplayer behavior in ticket #1154. I believe that |
|---|
| 491 | # mplayer made two simultaneous webapi GET requests: first one for an |
|---|
| 492 | # index region at the end of the (mp3/video) file, then one for the |
|---|
| 493 | # first block of the file (the order doesn't really matter). All GETs |
|---|
| 494 | # failed (NoSharesError) because of the type(__len__)==long bug. Each |
|---|
| 495 | # GET submitted a DownloadNode.get_segment() request, which was |
|---|
| 496 | # queued by the DN (DN._segment_requests), so the second one was |
|---|
| 497 | # blocked waiting on the first one. When the first one failed, |
|---|
| 498 | # DN.fetch_failed() was invoked, which errbacks the first GET, but |
|---|
| 499 | # left the other one hanging (the lost-progress bug mentioned in |
|---|
| 500 | # #1154 comment 10) |
|---|
| 501 | # |
|---|
| 502 | # Then mplayer sees that the index region GET failed, so it cancels |
|---|
| 503 | # the first-block GET (by closing the HTTP request), triggering |
|---|
| 504 | # stopProducer. The second GET was waiting in the Deferred (between |
|---|
| 505 | # n.get_segment() and self._request_retired), so its |
|---|
| 506 | # _cancel_segment_request was active, so was invoked. However, |
|---|
| 507 | # DN._active_segment was None since it was not working on any segment |
|---|
| 508 | # at that time, hence the error in #1154. |
|---|
| 509 | |
|---|
| 510 | self.basedir = self.mktemp() |
|---|
| 511 | self.set_up_grid() |
|---|
| 512 | self.c0 = self.g.clients[0] |
|---|
| 513 | |
|---|
| 514 | # upload a file with multiple segments, so we can catch the download |
|---|
| 515 | # in the middle. Tell the downloader, so it can guess correctly. |
|---|
| 516 | u = upload.Data(plaintext, None) |
|---|
| 517 | u.max_segment_size = 70 # 5 segs |
|---|
| 518 | d = self.c0.upload(u) |
|---|
| 519 | def _uploaded(ur): |
|---|
| 520 | # corrupt all the shares so the download will fail |
|---|
| 521 | def _corruptor(s, debug=False): |
|---|
| 522 | which = 48 # first byte of block0 |
|---|
| 523 | return s[:which] + bchr(ord(s[which:which+1])^0x01) + s[which+1:] |
|---|
| 524 | self.corrupt_all_shares(ur.get_uri(), _corruptor) |
|---|
| 525 | n = self.c0.create_node_from_uri(ur.get_uri()) |
|---|
| 526 | n._cnode._maybe_create_download_node() |
|---|
| 527 | n._cnode._node._build_guessed_tables(u.max_segment_size) |
|---|
| 528 | con1 = MemoryConsumer() |
|---|
| 529 | con2 = MemoryConsumer() |
|---|
| 530 | d = n.read(con1, int(0), int(20)) |
|---|
| 531 | d2 = n.read(con2, int(140), int(20)) |
|---|
| 532 | # con2 will be cancelled, so d2 should fail with DownloadStopped |
|---|
| 533 | def _con2_should_not_succeed(res): |
|---|
| 534 | self.fail("the second read should not have succeeded") |
|---|
| 535 | def _con2_failed(f): |
|---|
| 536 | self.failUnless(f.check(DownloadStopped)) |
|---|
| 537 | d2.addCallbacks(_con2_should_not_succeed, _con2_failed) |
|---|
| 538 | |
|---|
| 539 | def _con1_should_not_succeed(res): |
|---|
| 540 | self.fail("the first read should not have succeeded") |
|---|
| 541 | def _con1_failed(f): |
|---|
| 542 | self.failUnless(f.check(NoSharesError)) |
|---|
| 543 | con2.producer.stopProducing() |
|---|
| 544 | return d2 |
|---|
| 545 | d.addCallbacks(_con1_should_not_succeed, _con1_failed) |
|---|
| 546 | return d |
|---|
| 547 | d.addCallback(_uploaded) |
|---|
| 548 | return d |
|---|
| 549 | |
|---|
| 550 | def test_simultaneous_onefails(self): |
|---|
| 551 | self.basedir = self.mktemp() |
|---|
| 552 | self.set_up_grid() |
|---|
| 553 | self.c0 = self.g.clients[0] |
|---|
| 554 | |
|---|
| 555 | # upload a file with multiple segments, so we can catch the download |
|---|
| 556 | # in the middle. Tell the downloader, so it can guess correctly. |
|---|
| 557 | u = upload.Data(plaintext, None) |
|---|
| 558 | u.max_segment_size = 70 # 5 segs |
|---|
| 559 | d = self.c0.upload(u) |
|---|
| 560 | def _uploaded(ur): |
|---|
| 561 | # corrupt all the shares so the download will fail |
|---|
| 562 | def _corruptor(s, debug=False): |
|---|
| 563 | which = 48 # first byte of block0 |
|---|
| 564 | return s[:which] + bchr(ord(s[which:which+1])^0x01) + s[which+1:] |
|---|
| 565 | self.corrupt_all_shares(ur.get_uri(), _corruptor) |
|---|
| 566 | n = self.c0.create_node_from_uri(ur.get_uri()) |
|---|
| 567 | n._cnode._maybe_create_download_node() |
|---|
| 568 | n._cnode._node._build_guessed_tables(u.max_segment_size) |
|---|
| 569 | con1 = MemoryConsumer() |
|---|
| 570 | con2 = MemoryConsumer() |
|---|
| 571 | d = n.read(con1, int(0), int(20)) |
|---|
| 572 | d2 = n.read(con2, int(140), int(20)) |
|---|
| 573 | # con2 should wait for con1 to fail and then con2 should succeed. |
|---|
| 574 | # In particular, we should not lose progress. If this test fails, |
|---|
| 575 | # it will fail with a timeout error. |
|---|
| 576 | def _con2_should_succeed(res): |
|---|
| 577 | # this should succeed because we only corrupted the first |
|---|
| 578 | # segment of each share. The segment that holds [140:160] is |
|---|
| 579 | # fine, as are the hash chains and UEB. |
|---|
| 580 | self.failUnlessEqual(b"".join(con2.chunks), plaintext[140:160]) |
|---|
| 581 | d2.addCallback(_con2_should_succeed) |
|---|
| 582 | |
|---|
| 583 | def _con1_should_not_succeed(res): |
|---|
| 584 | self.fail("the first read should not have succeeded") |
|---|
| 585 | def _con1_failed(f): |
|---|
| 586 | self.failUnless(f.check(NoSharesError)) |
|---|
| 587 | # we *don't* cancel the second one here: this exercises a |
|---|
| 588 | # lost-progress bug from #1154. We just wait for it to |
|---|
| 589 | # succeed. |
|---|
| 590 | return d2 |
|---|
| 591 | d.addCallbacks(_con1_should_not_succeed, _con1_failed) |
|---|
| 592 | return d |
|---|
| 593 | d.addCallback(_uploaded) |
|---|
| 594 | return d |
|---|
| 595 | |
|---|
| 596 | def test_download_no_overrun(self): |
|---|
| 597 | self.basedir = self.mktemp() |
|---|
| 598 | self.set_up_grid() |
|---|
| 599 | self.c0 = self.g.clients[0] |
|---|
| 600 | |
|---|
| 601 | self.load_shares() |
|---|
| 602 | |
|---|
| 603 | # tweak the client's copies of server-version data, so it believes |
|---|
| 604 | # that they're old and can't handle reads that overrun the length of |
|---|
| 605 | # the share. This exercises a different code path. |
|---|
| 606 | for s in self.c0.storage_broker.get_connected_servers(): |
|---|
| 607 | v = s.get_version() |
|---|
| 608 | v1 = v[b"http://allmydata.org/tahoe/protocols/storage/v1"] |
|---|
| 609 | v1[b"tolerates-immutable-read-overrun"] = False |
|---|
| 610 | |
|---|
| 611 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 612 | d = download_to_data(n) |
|---|
| 613 | def _got_data(data): |
|---|
| 614 | self.failUnlessEqual(data, plaintext) |
|---|
| 615 | d.addCallback(_got_data) |
|---|
| 616 | return d |
|---|
| 617 | |
|---|
| 618 | def test_download_segment(self): |
|---|
| 619 | self.basedir = self.mktemp() |
|---|
| 620 | self.set_up_grid() |
|---|
| 621 | self.c0 = self.g.clients[0] |
|---|
| 622 | self.load_shares() |
|---|
| 623 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 624 | cn = n._cnode |
|---|
| 625 | (d,c) = cn.get_segment(0) |
|---|
| 626 | def _got_segment(offset_and_data_and_decodetime): |
|---|
| 627 | (offset, data, decodetime) = offset_and_data_and_decodetime |
|---|
| 628 | self.failUnlessEqual(offset, 0) |
|---|
| 629 | self.failUnlessEqual(len(data), len(plaintext)) |
|---|
| 630 | d.addCallback(_got_segment) |
|---|
| 631 | return d |
|---|
| 632 | |
|---|
| 633 | def test_download_segment_cancel(self): |
|---|
| 634 | self.basedir = self.mktemp() |
|---|
| 635 | self.set_up_grid() |
|---|
| 636 | self.c0 = self.g.clients[0] |
|---|
| 637 | self.load_shares() |
|---|
| 638 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 639 | cn = n._cnode |
|---|
| 640 | (d,c) = cn.get_segment(0) |
|---|
| 641 | fired = [] |
|---|
| 642 | d.addCallback(fired.append) |
|---|
| 643 | c.cancel() |
|---|
| 644 | d = fireEventually() |
|---|
| 645 | d.addCallback(flushEventualQueue) |
|---|
| 646 | def _check(ign): |
|---|
| 647 | self.failUnlessEqual(fired, []) |
|---|
| 648 | d.addCallback(_check) |
|---|
| 649 | return d |
|---|
| 650 | |
|---|
| 651 | def test_download_bad_segment(self): |
|---|
| 652 | self.basedir = self.mktemp() |
|---|
| 653 | self.set_up_grid() |
|---|
| 654 | self.c0 = self.g.clients[0] |
|---|
| 655 | self.load_shares() |
|---|
| 656 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 657 | cn = n._cnode |
|---|
| 658 | def _try_download(): |
|---|
| 659 | (d,c) = cn.get_segment(1) |
|---|
| 660 | return d |
|---|
| 661 | d = self.shouldFail(BadSegmentNumberError, "badseg", |
|---|
| 662 | "segnum=1, numsegs=1", |
|---|
| 663 | _try_download) |
|---|
| 664 | return d |
|---|
| 665 | |
|---|
| 666 | def test_download_segment_terminate(self): |
|---|
| 667 | self.basedir = self.mktemp() |
|---|
| 668 | self.set_up_grid() |
|---|
| 669 | self.c0 = self.g.clients[0] |
|---|
| 670 | self.load_shares() |
|---|
| 671 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 672 | cn = n._cnode |
|---|
| 673 | (d,c) = cn.get_segment(0) |
|---|
| 674 | fired = [] |
|---|
| 675 | d.addCallback(fired.append) |
|---|
| 676 | self.c0.terminator.disownServiceParent() |
|---|
| 677 | d = fireEventually() |
|---|
| 678 | d.addCallback(flushEventualQueue) |
|---|
| 679 | def _check(ign): |
|---|
| 680 | self.failUnlessEqual(fired, []) |
|---|
| 681 | d.addCallback(_check) |
|---|
| 682 | return d |
|---|
| 683 | |
|---|
| 684 | def test_pause(self): |
|---|
| 685 | self.basedir = self.mktemp() |
|---|
| 686 | self.set_up_grid() |
|---|
| 687 | self.c0 = self.g.clients[0] |
|---|
| 688 | self.load_shares() |
|---|
| 689 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 690 | c = PausingConsumer() |
|---|
| 691 | d = n.read(c) |
|---|
| 692 | def _downloaded(mc): |
|---|
| 693 | newdata = b"".join(mc.chunks) |
|---|
| 694 | self.failUnlessEqual(newdata, plaintext) |
|---|
| 695 | d.addCallback(_downloaded) |
|---|
| 696 | return d |
|---|
| 697 | |
|---|
| 698 | def test_pause_then_stop(self): |
|---|
| 699 | self.basedir = self.mktemp() |
|---|
| 700 | self.set_up_grid() |
|---|
| 701 | self.c0 = self.g.clients[0] |
|---|
| 702 | self.load_shares() |
|---|
| 703 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 704 | c = PausingAndStoppingConsumer() |
|---|
| 705 | d = self.shouldFail(DownloadStopped, "test_pause_then_stop", |
|---|
| 706 | "our Consumer called stopProducing()", |
|---|
| 707 | n.read, c) |
|---|
| 708 | return d |
|---|
| 709 | |
|---|
| 710 | def test_stop(self): |
|---|
| 711 | # use a download target that stops after the first segment (#473) |
|---|
| 712 | self.basedir = self.mktemp() |
|---|
| 713 | self.set_up_grid() |
|---|
| 714 | self.c0 = self.g.clients[0] |
|---|
| 715 | self.load_shares() |
|---|
| 716 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 717 | c = StoppingConsumer() |
|---|
| 718 | d = self.shouldFail(DownloadStopped, "test_stop", |
|---|
| 719 | "our Consumer called stopProducing()", |
|---|
| 720 | n.read, c) |
|---|
| 721 | return d |
|---|
| 722 | |
|---|
| 723 | def test_stop_immediately(self): |
|---|
| 724 | # and a target that stops right after registerProducer (maybe #1154) |
|---|
| 725 | self.basedir = self.mktemp() |
|---|
| 726 | self.set_up_grid() |
|---|
| 727 | self.c0 = self.g.clients[0] |
|---|
| 728 | self.load_shares() |
|---|
| 729 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 730 | |
|---|
| 731 | c = ImmediatelyStoppingConsumer() # stops after registerProducer |
|---|
| 732 | d = self.shouldFail(DownloadStopped, "test_stop_immediately", |
|---|
| 733 | "our Consumer called stopProducing()", |
|---|
| 734 | n.read, c) |
|---|
| 735 | return d |
|---|
| 736 | |
|---|
| 737 | def test_stop_immediately2(self): |
|---|
| 738 | # and a target that stops right after registerProducer (maybe #1154) |
|---|
| 739 | self.basedir = self.mktemp() |
|---|
| 740 | self.set_up_grid() |
|---|
| 741 | self.c0 = self.g.clients[0] |
|---|
| 742 | self.load_shares() |
|---|
| 743 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 744 | |
|---|
| 745 | c = MemoryConsumer() |
|---|
| 746 | d0 = n.read(c) |
|---|
| 747 | c.producer.stopProducing() |
|---|
| 748 | d = self.shouldFail(DownloadStopped, "test_stop_immediately", |
|---|
| 749 | "our Consumer called stopProducing()", |
|---|
| 750 | lambda: d0) |
|---|
| 751 | return d |
|---|
| 752 | |
|---|
| 753 | def test_download_segment_bad_ciphertext_hash(self): |
|---|
| 754 | # The crypttext_hash_tree asserts the integrity of the decoded |
|---|
| 755 | # ciphertext, and exists to detect two sorts of problems. The first |
|---|
| 756 | # is a bug in zfec decode. The second is the "two-sided t-shirt" |
|---|
| 757 | # attack (found by Christian Grothoff), in which a malicious uploader |
|---|
| 758 | # creates two sets of shares (one for file A, second for file B), |
|---|
| 759 | # uploads a combination of them (shares 0-4 of A, 5-9 of B), and then |
|---|
| 760 | # builds an otherwise normal UEB around those shares: their goal is |
|---|
| 761 | # to give their victim a filecap which sometimes downloads the good A |
|---|
| 762 | # contents, and sometimes the bad B contents, depending upon which |
|---|
| 763 | # servers/shares they can get to. Having a hash of the ciphertext |
|---|
| 764 | # forces them to commit to exactly one version. (Christian's prize |
|---|
| 765 | # for finding this problem was a t-shirt with two sides: the shares |
|---|
| 766 | # of file A on the front, B on the back). |
|---|
| 767 | |
|---|
| 768 | # creating a set of shares with this property is too hard, although |
|---|
| 769 | # it'd be nice to do so and confirm our fix. (it requires a lot of |
|---|
| 770 | # tampering with the uploader). So instead, we just damage the |
|---|
| 771 | # decoder. The tail decoder is rebuilt each time, so we need to use a |
|---|
| 772 | # file with multiple segments. |
|---|
| 773 | self.basedir = self.mktemp() |
|---|
| 774 | self.set_up_grid() |
|---|
| 775 | self.c0 = self.g.clients[0] |
|---|
| 776 | |
|---|
| 777 | u = upload.Data(plaintext, None) |
|---|
| 778 | u.max_segment_size = 60 # 6 segs |
|---|
| 779 | d = self.c0.upload(u) |
|---|
| 780 | def _uploaded(ur): |
|---|
| 781 | n = self.c0.create_node_from_uri(ur.get_uri()) |
|---|
| 782 | n._cnode._maybe_create_download_node() |
|---|
| 783 | n._cnode._node._build_guessed_tables(u.max_segment_size) |
|---|
| 784 | |
|---|
| 785 | d = download_to_data(n) |
|---|
| 786 | def _break_codec(data): |
|---|
| 787 | # the codec isn't created until the UEB is retrieved |
|---|
| 788 | node = n._cnode._node |
|---|
| 789 | vcap = node._verifycap |
|---|
| 790 | k, N = vcap.needed_shares, vcap.total_shares |
|---|
| 791 | bad_codec = BrokenDecoder() |
|---|
| 792 | bad_codec.set_params(node.segment_size, k, N) |
|---|
| 793 | node._codec = bad_codec |
|---|
| 794 | d.addCallback(_break_codec) |
|---|
| 795 | # now try to download it again. The broken codec will provide |
|---|
| 796 | # ciphertext that fails the hash test. |
|---|
| 797 | d.addCallback(lambda ign: |
|---|
| 798 | self.shouldFail(BadCiphertextHashError, "badhash", |
|---|
| 799 | "hash failure in " |
|---|
| 800 | "ciphertext_hash_tree: segnum=0", |
|---|
| 801 | download_to_data, n)) |
|---|
| 802 | return d |
|---|
| 803 | d.addCallback(_uploaded) |
|---|
| 804 | return d |
|---|
| 805 | |
|---|
| 806 | def OFFtest_download_segment_XXX(self): |
|---|
| 807 | self.basedir = self.mktemp() |
|---|
| 808 | self.set_up_grid() |
|---|
| 809 | self.c0 = self.g.clients[0] |
|---|
| 810 | |
|---|
| 811 | # upload a file with multiple segments, and a non-default segsize, to |
|---|
| 812 | # exercise the offset-guessing code. This time we *do* tell the |
|---|
| 813 | # downloader about the unusual segsize, so it can guess right. |
|---|
| 814 | u = upload.Data(plaintext, None) |
|---|
| 815 | u.max_segment_size = 70 # 5 segs, 8-wide hashtree |
|---|
| 816 | con1 = MemoryConsumer() |
|---|
| 817 | con2 = MemoryConsumer() |
|---|
| 818 | d = self.c0.upload(u) |
|---|
| 819 | def _uploaded(ur): |
|---|
| 820 | n = self.c0.create_node_from_uri(ur.get_uri()) |
|---|
| 821 | n._cnode._maybe_create_download_node() |
|---|
| 822 | n._cnode._node._build_guessed_tables(u.max_segment_size) |
|---|
| 823 | d1 = n.read(con1, 70, 20) |
|---|
| 824 | #d2 = n.read(con2, 140, 20) |
|---|
| 825 | d2 = defer.succeed(None) |
|---|
| 826 | return defer.gatherResults([d1,d2]) |
|---|
| 827 | d.addCallback(_uploaded) |
|---|
| 828 | def _done(res): |
|---|
| 829 | self.failUnlessEqual(b"".join(con1.chunks), plaintext[70:90]) |
|---|
| 830 | self.failUnlessEqual(b"".join(con2.chunks), plaintext[140:160]) |
|---|
| 831 | #d.addCallback(_done) |
|---|
| 832 | return d |
|---|
| 833 | |
|---|
| 834 | def test_duplicate_shares(self): |
|---|
| 835 | self.basedir = self.mktemp() |
|---|
| 836 | self.set_up_grid() |
|---|
| 837 | self.c0 = self.g.clients[0] |
|---|
| 838 | |
|---|
| 839 | self.load_shares() |
|---|
| 840 | # make sure everybody has a copy of sh0. The second server contacted |
|---|
| 841 | # will report two shares, and the ShareFinder will handle the |
|---|
| 842 | # duplicate by attaching both to the same CommonShare instance. |
|---|
| 843 | si = uri.from_string(immutable_uri).get_storage_index() |
|---|
| 844 | si_dir = storage_index_to_dir(si) |
|---|
| 845 | sh0_file = [sharefile |
|---|
| 846 | for (shnum, serverid, sharefile) |
|---|
| 847 | in self.find_uri_shares(immutable_uri) |
|---|
| 848 | if shnum == 0][0] |
|---|
| 849 | sh0_data = open(sh0_file, "rb").read() |
|---|
| 850 | for clientnum in immutable_shares: |
|---|
| 851 | if 0 in immutable_shares[clientnum]: |
|---|
| 852 | continue |
|---|
| 853 | cdir = self.get_serverdir(clientnum) |
|---|
| 854 | target = os.path.join(cdir, "shares", si_dir, "0") |
|---|
| 855 | outf = open(target, "wb") |
|---|
| 856 | outf.write(sh0_data) |
|---|
| 857 | outf.close() |
|---|
| 858 | |
|---|
| 859 | d = self.download_immutable() |
|---|
| 860 | return d |
|---|
| 861 | |
|---|
| 862 | def test_verifycap(self): |
|---|
| 863 | self.basedir = self.mktemp() |
|---|
| 864 | self.set_up_grid() |
|---|
| 865 | self.c0 = self.g.clients[0] |
|---|
| 866 | self.load_shares() |
|---|
| 867 | |
|---|
| 868 | n = self.c0.create_node_from_uri(immutable_uri) |
|---|
| 869 | vcap = n.get_verify_cap().to_string() |
|---|
| 870 | vn = self.c0.create_node_from_uri(vcap) |
|---|
| 871 | d = download_to_data(vn) |
|---|
| 872 | def _got_ciphertext(ciphertext): |
|---|
| 873 | self.failUnlessEqual(len(ciphertext), len(plaintext)) |
|---|
| 874 | self.failIfEqual(ciphertext, plaintext) |
|---|
| 875 | d.addCallback(_got_ciphertext) |
|---|
| 876 | return d |
|---|
| 877 | |
|---|
| 878 | class BrokenDecoder(CRSDecoder): |
|---|
| 879 | def decode(self, shares, shareids): |
|---|
| 880 | d = CRSDecoder.decode(self, shares, shareids) |
|---|
| 881 | def _decoded(buffers): |
|---|
| 882 | def _corruptor(s, which): |
|---|
| 883 | return s[:which] + bchr(ord(s[which:which+1])^0x01) + s[which+1:] |
|---|
| 884 | buffers[0] = _corruptor(buffers[0], 0) # flip lsb of first byte |
|---|
| 885 | return buffers |
|---|
| 886 | d.addCallback(_decoded) |
|---|
| 887 | return d |
|---|
| 888 | |
|---|
| 889 | |
|---|
| 890 | class PausingConsumer(MemoryConsumer): |
|---|
| 891 | def __init__(self): |
|---|
| 892 | MemoryConsumer.__init__(self) |
|---|
| 893 | self.size = 0 |
|---|
| 894 | self.writes = 0 |
|---|
| 895 | def write(self, data): |
|---|
| 896 | self.size += len(data) |
|---|
| 897 | self.writes += 1 |
|---|
| 898 | if self.writes <= 2: |
|---|
| 899 | # we happen to use 4 segments, and want to avoid pausing on the |
|---|
| 900 | # last one (since then the _unpause timer will still be running) |
|---|
| 901 | self.producer.pauseProducing() |
|---|
| 902 | reactor.callLater(0.1, self._unpause) |
|---|
| 903 | return MemoryConsumer.write(self, data) |
|---|
| 904 | def _unpause(self): |
|---|
| 905 | self.producer.resumeProducing() |
|---|
| 906 | |
|---|
| 907 | class PausingAndStoppingConsumer(PausingConsumer): |
|---|
| 908 | debug_stopped = False |
|---|
| 909 | def write(self, data): |
|---|
| 910 | if self.debug_stopped: |
|---|
| 911 | raise Exception("I'm stopped, don't write to me") |
|---|
| 912 | self.producer.pauseProducing() |
|---|
| 913 | eventually(self._stop) |
|---|
| 914 | def _stop(self): |
|---|
| 915 | self.debug_stopped = True |
|---|
| 916 | self.producer.stopProducing() |
|---|
| 917 | |
|---|
| 918 | class StoppingConsumer(PausingConsumer): |
|---|
| 919 | def write(self, data): |
|---|
| 920 | self.producer.stopProducing() |
|---|
| 921 | |
|---|
| 922 | class ImmediatelyStoppingConsumer(MemoryConsumer): |
|---|
| 923 | def registerProducer(self, p, streaming): |
|---|
| 924 | MemoryConsumer.registerProducer(self, p, streaming) |
|---|
| 925 | self.producer.stopProducing() |
|---|
| 926 | |
|---|
| 927 | class StallingConsumer(MemoryConsumer): |
|---|
| 928 | def __init__(self, halfway_cb): |
|---|
| 929 | MemoryConsumer.__init__(self) |
|---|
| 930 | self.halfway_cb = halfway_cb |
|---|
| 931 | self.writes = 0 |
|---|
| 932 | def write(self, data): |
|---|
| 933 | self.writes += 1 |
|---|
| 934 | if self.writes == 1: |
|---|
| 935 | self.halfway_cb() |
|---|
| 936 | return MemoryConsumer.write(self, data) |
|---|
| 937 | |
|---|
| 938 | class Corruption(_Base, unittest.TestCase): |
|---|
| 939 | |
|---|
| 940 | def _corrupt_flip(self, ign, imm_uri, which): |
|---|
| 941 | log.msg("corrupt %d" % which) |
|---|
| 942 | def _corruptor(s, debug=False): |
|---|
| 943 | return s[:which] + bchr(ord(s[which:which+1])^0x01) + s[which+1:] |
|---|
| 944 | self.corrupt_shares_numbered(imm_uri, [2], _corruptor) |
|---|
| 945 | |
|---|
| 946 | def _corrupt_set(self, ign, imm_uri, which, newvalue): |
|---|
| 947 | # type: (Any, bytes, int, int) -> None |
|---|
| 948 | """ |
|---|
| 949 | Replace a single byte share file number 2 for the given capability with a |
|---|
| 950 | new byte. |
|---|
| 951 | |
|---|
| 952 | :param imm_uri: Corrupt share number 2 belonging to this capability. |
|---|
| 953 | :param which: The byte position to replace. |
|---|
| 954 | :param newvalue: The new byte value to set in the share. |
|---|
| 955 | """ |
|---|
| 956 | log.msg("corrupt %d" % which) |
|---|
| 957 | def _corruptor(s, debug=False): |
|---|
| 958 | return s[:which] + bchr(newvalue) + s[which+1:] |
|---|
| 959 | self.corrupt_shares_numbered(imm_uri, [2], _corruptor) |
|---|
| 960 | |
|---|
| 961 | def test_each_byte(self): |
|---|
| 962 | """ |
|---|
| 963 | Test share selection behavior of the downloader in the face of certain |
|---|
| 964 | kinds of data corruption. |
|---|
| 965 | |
|---|
| 966 | 1. upload a small share to the no-network grid |
|---|
| 967 | 2. read all of the resulting share files out of the no-network storage servers |
|---|
| 968 | 3. for each of |
|---|
| 969 | |
|---|
| 970 | a. each byte of the share file version field |
|---|
| 971 | b. each byte of the immutable share version field |
|---|
| 972 | c. each byte of the immutable share data offset field |
|---|
| 973 | d. the most significant byte of the block_shares offset field |
|---|
| 974 | e. one of the bytes of one of the merkle trees |
|---|
| 975 | f. one of the bytes of the share hashes list |
|---|
| 976 | |
|---|
| 977 | i. flip the least significant bit in all of the the share files |
|---|
| 978 | ii. perform the download/check/restore process |
|---|
| 979 | |
|---|
| 980 | 4. add 2 ** 24 to the share file version number |
|---|
| 981 | 5. perform the download/check/restore process |
|---|
| 982 | |
|---|
| 983 | 6. add 2 ** 24 to the share version number |
|---|
| 984 | 7. perform the download/check/restore process |
|---|
| 985 | |
|---|
| 986 | The download/check/restore process is: |
|---|
| 987 | |
|---|
| 988 | 1. attempt to download the data |
|---|
| 989 | 2. assert that the recovered plaintext is correct |
|---|
| 990 | 3. assert that only the "correct" share numbers were used to reconstruct the plaintext |
|---|
| 991 | 4. restore all of the share files to their pristine condition |
|---|
| 992 | """ |
|---|
| 993 | # Setting catalog_detection=True performs an exhaustive test of the |
|---|
| 994 | # Downloader's response to corruption in the lsb of each byte of the |
|---|
| 995 | # 2070-byte share, with two goals: make sure we tolerate all forms of |
|---|
| 996 | # corruption (i.e. don't hang or return bad data), and make a list of |
|---|
| 997 | # which bytes can be corrupted without influencing the download |
|---|
| 998 | # (since we don't need every byte of the share). That takes 50s to |
|---|
| 999 | # run on my laptop and doesn't have any actual asserts, so we don't |
|---|
| 1000 | # normally do that. |
|---|
| 1001 | self.catalog_detection = False |
|---|
| 1002 | |
|---|
| 1003 | self.basedir = "download/Corruption/each_byte" |
|---|
| 1004 | self.set_up_grid() |
|---|
| 1005 | self.c0 = self.g.clients[0] |
|---|
| 1006 | |
|---|
| 1007 | # to exercise the block-hash-tree code properly, we need to have |
|---|
| 1008 | # multiple segments. We don't tell the downloader about the different |
|---|
| 1009 | # segsize, so it guesses wrong and must do extra roundtrips. |
|---|
| 1010 | u = upload.Data(plaintext, None) |
|---|
| 1011 | u.max_segment_size = 120 # 3 segs, 4-wide hashtree |
|---|
| 1012 | |
|---|
| 1013 | if self.catalog_detection: |
|---|
| 1014 | undetected = spans.Spans() |
|---|
| 1015 | |
|---|
| 1016 | def _download(ign, imm_uri, which, expected): |
|---|
| 1017 | n = self.c0.create_node_from_uri(imm_uri) |
|---|
| 1018 | n._cnode._maybe_create_download_node() |
|---|
| 1019 | # for this test to work, we need to have a new Node each time. |
|---|
| 1020 | # Make sure the NodeMaker's weakcache hasn't interfered. |
|---|
| 1021 | assert not n._cnode._node._shares |
|---|
| 1022 | d = download_to_data(n) |
|---|
| 1023 | def _got_data(data): |
|---|
| 1024 | self.failUnlessEqual(data, plaintext) |
|---|
| 1025 | shnums = sorted([s._shnum for s in n._cnode._node._shares]) |
|---|
| 1026 | no_sh2 = bool(2 not in shnums) |
|---|
| 1027 | sh2 = [s for s in n._cnode._node._shares if s._shnum == 2] |
|---|
| 1028 | sh2_had_corruption = False |
|---|
| 1029 | if sh2 and sh2[0].had_corruption: |
|---|
| 1030 | sh2_had_corruption = True |
|---|
| 1031 | num_needed = len(n._cnode._node._shares) |
|---|
| 1032 | if self.catalog_detection: |
|---|
| 1033 | detected = no_sh2 or sh2_had_corruption or (num_needed!=3) |
|---|
| 1034 | if not detected: |
|---|
| 1035 | undetected.add(which, 1) |
|---|
| 1036 | if expected == "no-sh2": |
|---|
| 1037 | self.failIfIn(2, shnums) |
|---|
| 1038 | elif expected == "2bad-need-3": |
|---|
| 1039 | self.failIf(no_sh2) |
|---|
| 1040 | self.failUnless(sh2[0].had_corruption) |
|---|
| 1041 | self.failUnlessEqual(num_needed, 3) |
|---|
| 1042 | elif expected == "need-4th": |
|---|
| 1043 | # XXX check with warner; what relevance does this |
|---|
| 1044 | # have for the "need-4th" stuff? |
|---|
| 1045 | #self.failIf(no_sh2) |
|---|
| 1046 | #self.failUnless(sh2[0].had_corruption) |
|---|
| 1047 | self.failIfEqual(num_needed, 3) |
|---|
| 1048 | d.addCallback(_got_data) |
|---|
| 1049 | return d |
|---|
| 1050 | |
|---|
| 1051 | |
|---|
| 1052 | d = self.c0.upload(u) |
|---|
| 1053 | def _uploaded(ur): |
|---|
| 1054 | imm_uri = ur.get_uri() |
|---|
| 1055 | self.shares = self.copy_shares(imm_uri) |
|---|
| 1056 | d = defer.succeed(None) |
|---|
| 1057 | # 'victims' is a list of corruption tests to run. Each one flips |
|---|
| 1058 | # the low-order bit of the specified offset in the share file (so |
|---|
| 1059 | # offset=0 is the MSB of the container version, offset=15 is the |
|---|
| 1060 | # LSB of the share version, offset=24 is the MSB of the |
|---|
| 1061 | # data-block-offset, and offset=48 is the first byte of the first |
|---|
| 1062 | # data-block). Each one also specifies what sort of corruption |
|---|
| 1063 | # we're expecting to see. |
|---|
| 1064 | no_sh2_victims = [0,1,2,3] # container version |
|---|
| 1065 | need3_victims = [ ] # none currently in this category |
|---|
| 1066 | # when the offsets are corrupted, the Share will be unable to |
|---|
| 1067 | # retrieve the data it wants (because it thinks that data lives |
|---|
| 1068 | # off in the weeds somewhere), and Share treats DataUnavailable |
|---|
| 1069 | # as abandon-this-share, so in general we'll be forced to look |
|---|
| 1070 | # for a 4th share. |
|---|
| 1071 | need_4th_victims = [12,13,14,15, # offset[data] |
|---|
| 1072 | 24,25,26,27, # offset[block_hashes] |
|---|
| 1073 | ] |
|---|
| 1074 | need_4th_victims.append(36) # block data |
|---|
| 1075 | # when corrupting hash trees, we must corrupt a value that isn't |
|---|
| 1076 | # directly set from somewhere else. Since we download data from |
|---|
| 1077 | # seg2, corrupt something on its hash chain, like [2] (the |
|---|
| 1078 | # right-hand child of the root) |
|---|
| 1079 | need_4th_victims.append(600+2*32) # block_hashes[2] |
|---|
| 1080 | # Share.loop is pretty conservative: it abandons the share at the |
|---|
| 1081 | # first sign of corruption. It doesn't strictly need to be this |
|---|
| 1082 | # way: if the UEB were corrupt, we could still get good block |
|---|
| 1083 | # data from that share, as long as there was a good copy of the |
|---|
| 1084 | # UEB elsewhere. If this behavior is relaxed, then corruption in |
|---|
| 1085 | # the following fields (which are present in multiple shares) |
|---|
| 1086 | # should fall into the "need3_victims" case instead of the |
|---|
| 1087 | # "need_4th_victims" case. |
|---|
| 1088 | need_4th_victims.append(824) # share_hashes |
|---|
| 1089 | corrupt_me = ([(i,"no-sh2") for i in no_sh2_victims] + |
|---|
| 1090 | [(i, "2bad-need-3") for i in need3_victims] + |
|---|
| 1091 | [(i, "need-4th") for i in need_4th_victims]) |
|---|
| 1092 | if self.catalog_detection: |
|---|
| 1093 | share_len = len(list(self.shares.values())[0]) |
|---|
| 1094 | corrupt_me = [(i, "") for i in range(share_len)] |
|---|
| 1095 | # This is a work around for ticket #2024. |
|---|
| 1096 | corrupt_me = corrupt_me[0:8]+corrupt_me[12:] |
|---|
| 1097 | for i,expected in corrupt_me: |
|---|
| 1098 | # All these tests result in a successful download. What we're |
|---|
| 1099 | # measuring is how many shares the downloader had to use. |
|---|
| 1100 | d.addCallback(self._corrupt_flip, imm_uri, i) |
|---|
| 1101 | d.addCallback(_download, imm_uri, i, expected) |
|---|
| 1102 | d.addCallback(lambda ign: self.restore_all_shares(self.shares)) |
|---|
| 1103 | d.addCallback(fireEventually) |
|---|
| 1104 | corrupt_values = [ |
|---|
| 1105 | # Make the container version for share number 2 look |
|---|
| 1106 | # unsupported. If you add support for immutable share file |
|---|
| 1107 | # version number much past 16 million then you will have to |
|---|
| 1108 | # update this test. Also maybe you have other problems. |
|---|
| 1109 | (1, 255, "no-sh2"), |
|---|
| 1110 | # Make the immutable share number 2 (not the container, the |
|---|
| 1111 | # thing inside the container) look unsupported. Ditto the |
|---|
| 1112 | # above about version numbers in the ballpark of 16 million. |
|---|
| 1113 | (13, 255, "need-4th"), |
|---|
| 1114 | ] |
|---|
| 1115 | for i,newvalue,expected in corrupt_values: |
|---|
| 1116 | d.addCallback(self._corrupt_set, imm_uri, i, newvalue) |
|---|
| 1117 | d.addCallback(_download, imm_uri, i, expected) |
|---|
| 1118 | d.addCallback(lambda ign: self.restore_all_shares(self.shares)) |
|---|
| 1119 | d.addCallback(fireEventually) |
|---|
| 1120 | return d |
|---|
| 1121 | d.addCallback(_uploaded) |
|---|
| 1122 | def _show_results(ign): |
|---|
| 1123 | share_len = len(list(self.shares.values())[0]) |
|---|
| 1124 | print() |
|---|
| 1125 | print("of [0:%d], corruption ignored in %s" % |
|---|
| 1126 | (share_len, undetected.dump())) |
|---|
| 1127 | if self.catalog_detection: |
|---|
| 1128 | d.addCallback(_show_results) |
|---|
| 1129 | # of [0:2070], corruption ignored in len=1133: |
|---|
| 1130 | # [4-11],[16-23],[28-31],[152-439],[600-663],[1309-2069] |
|---|
| 1131 | # [4-11]: container sizes |
|---|
| 1132 | # [16-23]: share block/data sizes |
|---|
| 1133 | # [152-375]: plaintext hash tree |
|---|
| 1134 | # [376-408]: crypttext_hash_tree[0] (root) |
|---|
| 1135 | # [408-439]: crypttext_hash_tree[1] (computed) |
|---|
| 1136 | # [600-631]: block hash tree[0] (root) |
|---|
| 1137 | # [632-663]: block hash tree[1] (computed) |
|---|
| 1138 | # [1309-]: reserved+unused UEB space |
|---|
| 1139 | return d |
|---|
| 1140 | |
|---|
| 1141 | def test_failure(self): |
|---|
| 1142 | # this test corrupts all shares in the same way, and asserts that the |
|---|
| 1143 | # download fails. |
|---|
| 1144 | |
|---|
| 1145 | self.basedir = "download/Corruption/failure" |
|---|
| 1146 | self.set_up_grid() |
|---|
| 1147 | self.c0 = self.g.clients[0] |
|---|
| 1148 | |
|---|
| 1149 | # to exercise the block-hash-tree code properly, we need to have |
|---|
| 1150 | # multiple segments. We don't tell the downloader about the different |
|---|
| 1151 | # segsize, so it guesses wrong and must do extra roundtrips. |
|---|
| 1152 | u = upload.Data(plaintext, None) |
|---|
| 1153 | u.max_segment_size = 120 # 3 segs, 4-wide hashtree |
|---|
| 1154 | |
|---|
| 1155 | d = self.c0.upload(u) |
|---|
| 1156 | def _uploaded(ur): |
|---|
| 1157 | imm_uri = ur.get_uri() |
|---|
| 1158 | self.shares = self.copy_shares(imm_uri) |
|---|
| 1159 | |
|---|
| 1160 | corrupt_me = [(48, "block data", "Last failure: None"), |
|---|
| 1161 | (600+2*32, "block_hashes[2]", "BadHashError"), |
|---|
| 1162 | (376+2*32, "crypttext_hash_tree[2]", "BadHashError"), |
|---|
| 1163 | (824, "share_hashes", "BadHashError"), |
|---|
| 1164 | ] |
|---|
| 1165 | def _download(imm_uri): |
|---|
| 1166 | n = self.c0.create_node_from_uri(imm_uri) |
|---|
| 1167 | n._cnode._maybe_create_download_node() |
|---|
| 1168 | # for this test to work, we need to have a new Node each time. |
|---|
| 1169 | # Make sure the NodeMaker's weakcache hasn't interfered. |
|---|
| 1170 | assert not n._cnode._node._shares |
|---|
| 1171 | return download_to_data(n) |
|---|
| 1172 | |
|---|
| 1173 | d = defer.succeed(None) |
|---|
| 1174 | for i,which,substring in corrupt_me: |
|---|
| 1175 | # All these tests result in a failed download. |
|---|
| 1176 | d.addCallback(self._corrupt_flip_all, imm_uri, i) |
|---|
| 1177 | d.addCallback(lambda ign, which=which, substring=substring: |
|---|
| 1178 | self.shouldFail(NoSharesError, which, |
|---|
| 1179 | substring, |
|---|
| 1180 | _download, imm_uri)) |
|---|
| 1181 | d.addCallback(lambda ign: self.restore_all_shares(self.shares)) |
|---|
| 1182 | d.addCallback(fireEventually) |
|---|
| 1183 | return d |
|---|
| 1184 | d.addCallback(_uploaded) |
|---|
| 1185 | |
|---|
| 1186 | return d |
|---|
| 1187 | |
|---|
| 1188 | def _corrupt_flip_all(self, ign: Any, imm_uri: bytes, which: int) -> None: |
|---|
| 1189 | """ |
|---|
| 1190 | Flip the least significant bit at a given byte position in all share files |
|---|
| 1191 | for the given capability. |
|---|
| 1192 | """ |
|---|
| 1193 | def _corruptor(s, debug=False): |
|---|
| 1194 | # type: (bytes, bool) -> bytes |
|---|
| 1195 | before_corruption = s[:which] |
|---|
| 1196 | after_corruption = s[which+1:] |
|---|
| 1197 | original_byte = s[which:which+1] |
|---|
| 1198 | corrupt_byte = bchr(ord(original_byte) ^ 0x01) |
|---|
| 1199 | return b"".join([before_corruption, corrupt_byte, after_corruption]) |
|---|
| 1200 | self.corrupt_all_shares(imm_uri, _corruptor) |
|---|
| 1201 | |
|---|
| 1202 | class DownloadV2(_Base, unittest.TestCase): |
|---|
| 1203 | # tests which exercise v2-share code. They first upload a file with |
|---|
| 1204 | # FORCE_V2 set. |
|---|
| 1205 | |
|---|
| 1206 | def setUp(self): |
|---|
| 1207 | d = defer.maybeDeferred(_Base.setUp, self) |
|---|
| 1208 | def _set_force_v2(ign): |
|---|
| 1209 | self.old_force_v2 = layout.FORCE_V2 |
|---|
| 1210 | layout.FORCE_V2 = True |
|---|
| 1211 | d.addCallback(_set_force_v2) |
|---|
| 1212 | return d |
|---|
| 1213 | def tearDown(self): |
|---|
| 1214 | layout.FORCE_V2 = self.old_force_v2 |
|---|
| 1215 | return _Base.tearDown(self) |
|---|
| 1216 | |
|---|
| 1217 | def test_download(self): |
|---|
| 1218 | self.basedir = self.mktemp() |
|---|
| 1219 | self.set_up_grid() |
|---|
| 1220 | self.c0 = self.g.clients[0] |
|---|
| 1221 | |
|---|
| 1222 | # upload a file |
|---|
| 1223 | u = upload.Data(plaintext, None) |
|---|
| 1224 | d = self.c0.upload(u) |
|---|
| 1225 | def _uploaded(ur): |
|---|
| 1226 | imm_uri = ur.get_uri() |
|---|
| 1227 | n = self.c0.create_node_from_uri(imm_uri) |
|---|
| 1228 | return download_to_data(n) |
|---|
| 1229 | d.addCallback(_uploaded) |
|---|
| 1230 | return d |
|---|
| 1231 | |
|---|
| 1232 | def test_download_no_overrun(self): |
|---|
| 1233 | self.basedir = self.mktemp() |
|---|
| 1234 | self.set_up_grid() |
|---|
| 1235 | self.c0 = self.g.clients[0] |
|---|
| 1236 | |
|---|
| 1237 | # tweak the client's copies of server-version data, so it believes |
|---|
| 1238 | # that they're old and can't handle reads that overrun the length of |
|---|
| 1239 | # the share. This exercises a different code path. |
|---|
| 1240 | for s in self.c0.storage_broker.get_connected_servers(): |
|---|
| 1241 | v = s.get_version() |
|---|
| 1242 | v1 = v[b"http://allmydata.org/tahoe/protocols/storage/v1"] |
|---|
| 1243 | v1[b"tolerates-immutable-read-overrun"] = False |
|---|
| 1244 | |
|---|
| 1245 | # upload a file |
|---|
| 1246 | u = upload.Data(plaintext, None) |
|---|
| 1247 | d = self.c0.upload(u) |
|---|
| 1248 | def _uploaded(ur): |
|---|
| 1249 | imm_uri = ur.get_uri() |
|---|
| 1250 | n = self.c0.create_node_from_uri(imm_uri) |
|---|
| 1251 | return download_to_data(n) |
|---|
| 1252 | d.addCallback(_uploaded) |
|---|
| 1253 | return d |
|---|
| 1254 | |
|---|
| 1255 | def OFF_test_no_overrun_corrupt_shver(self): # unnecessary |
|---|
| 1256 | self.basedir = self.mktemp() |
|---|
| 1257 | self.set_up_grid() |
|---|
| 1258 | self.c0 = self.g.clients[0] |
|---|
| 1259 | |
|---|
| 1260 | for s in self.c0.storage_broker.get_connected_servers(): |
|---|
| 1261 | v = s.get_version() |
|---|
| 1262 | v1 = v["http://allmydata.org/tahoe/protocols/storage/v1"] |
|---|
| 1263 | v1["tolerates-immutable-read-overrun"] = False |
|---|
| 1264 | |
|---|
| 1265 | # upload a file |
|---|
| 1266 | u = upload.Data(plaintext, None) |
|---|
| 1267 | d = self.c0.upload(u) |
|---|
| 1268 | def _uploaded(ur): |
|---|
| 1269 | imm_uri = ur.get_uri() |
|---|
| 1270 | def _do_corrupt(which, newvalue): |
|---|
| 1271 | def _corruptor(s, debug=False): |
|---|
| 1272 | return s[:which] + chr(newvalue) + s[which+1:] |
|---|
| 1273 | self.corrupt_shares_numbered(imm_uri, [0], _corruptor) |
|---|
| 1274 | _do_corrupt(12+3, 0x00) |
|---|
| 1275 | n = self.c0.create_node_from_uri(imm_uri) |
|---|
| 1276 | d = download_to_data(n) |
|---|
| 1277 | def _got_data(data): |
|---|
| 1278 | self.failUnlessEqual(data, plaintext) |
|---|
| 1279 | d.addCallback(_got_data) |
|---|
| 1280 | return d |
|---|
| 1281 | d.addCallback(_uploaded) |
|---|
| 1282 | return d |
|---|
| 1283 | |
|---|
| 1284 | class Status(unittest.TestCase): |
|---|
| 1285 | def test_status(self): |
|---|
| 1286 | now = 12345.1 |
|---|
| 1287 | ds = DownloadStatus("si-1", 123) |
|---|
| 1288 | self.failUnlessEqual(ds.get_status(), "idle") |
|---|
| 1289 | ev0 = ds.add_segment_request(0, now) |
|---|
| 1290 | self.failUnlessEqual(ds.get_status(), "fetching segment 0") |
|---|
| 1291 | ev0.activate(now+0.5) |
|---|
| 1292 | ev0.deliver(now+1, 0, 1000, 2.0) |
|---|
| 1293 | self.failUnlessEqual(ds.get_status(), "idle") |
|---|
| 1294 | ev2 = ds.add_segment_request(2, now+2) |
|---|
| 1295 | del ev2 # hush pyflakes |
|---|
| 1296 | ev1 = ds.add_segment_request(1, now+2) |
|---|
| 1297 | self.failUnlessEqual(ds.get_status(), "fetching segments 1,2") |
|---|
| 1298 | ev1.error(now+3) |
|---|
| 1299 | self.failUnlessEqual(ds.get_status(), |
|---|
| 1300 | "fetching segment 2; errors on segment 1") |
|---|
| 1301 | |
|---|
| 1302 | def test_progress(self): |
|---|
| 1303 | now = 12345.1 |
|---|
| 1304 | ds = DownloadStatus("si-1", 123) |
|---|
| 1305 | self.failUnlessEqual(ds.get_progress(), 0.0) |
|---|
| 1306 | e = ds.add_read_event(0, 1000, now) |
|---|
| 1307 | self.failUnlessEqual(ds.get_progress(), 0.0) |
|---|
| 1308 | e.update(500, 2.0, 2.0) |
|---|
| 1309 | self.failUnlessEqual(ds.get_progress(), 0.5) |
|---|
| 1310 | e.finished(now+2) |
|---|
| 1311 | self.failUnlessEqual(ds.get_progress(), 1.0) |
|---|
| 1312 | |
|---|
| 1313 | e1 = ds.add_read_event(1000, 2000, now+3) |
|---|
| 1314 | e2 = ds.add_read_event(4000, 2000, now+3) |
|---|
| 1315 | self.failUnlessEqual(ds.get_progress(), 0.0) |
|---|
| 1316 | e1.update(1000, 2.0, 2.0) |
|---|
| 1317 | self.failUnlessEqual(ds.get_progress(), 0.25) |
|---|
| 1318 | e2.update(1000, 2.0, 2.0) |
|---|
| 1319 | self.failUnlessEqual(ds.get_progress(), 0.5) |
|---|
| 1320 | e1.update(1000, 2.0, 2.0) |
|---|
| 1321 | e1.finished(now+4) |
|---|
| 1322 | # now there is only one outstanding read, and it is 50% done |
|---|
| 1323 | self.failUnlessEqual(ds.get_progress(), 0.5) |
|---|
| 1324 | e2.update(1000, 2.0, 2.0) |
|---|
| 1325 | e2.finished(now+5) |
|---|
| 1326 | self.failUnlessEqual(ds.get_progress(), 1.0) |
|---|
| 1327 | |
|---|
| 1328 | def test_active(self): |
|---|
| 1329 | now = 12345.1 |
|---|
| 1330 | ds = DownloadStatus("si-1", 123) |
|---|
| 1331 | self.failUnlessEqual(ds.get_active(), False) |
|---|
| 1332 | e1 = ds.add_read_event(0, 1000, now) |
|---|
| 1333 | self.failUnlessEqual(ds.get_active(), True) |
|---|
| 1334 | e2 = ds.add_read_event(1, 1000, now+1) |
|---|
| 1335 | self.failUnlessEqual(ds.get_active(), True) |
|---|
| 1336 | e1.finished(now+2) |
|---|
| 1337 | self.failUnlessEqual(ds.get_active(), True) |
|---|
| 1338 | e2.finished(now+3) |
|---|
| 1339 | self.failUnlessEqual(ds.get_active(), False) |
|---|
| 1340 | |
|---|
| 1341 | def make_server(clientid): |
|---|
| 1342 | tubid = hashutil.tagged_hash(b"clientid", clientid)[:20] |
|---|
| 1343 | return NoNetworkServer(tubid, None) |
|---|
| 1344 | def make_servers(clientids): |
|---|
| 1345 | servers = {} |
|---|
| 1346 | for clientid in clientids: |
|---|
| 1347 | servers[clientid] = make_server(clientid) |
|---|
| 1348 | return servers |
|---|
| 1349 | |
|---|
| 1350 | class MyShare: |
|---|
| 1351 | def __init__(self, shnum, server, rtt): |
|---|
| 1352 | self._shnum = shnum |
|---|
| 1353 | self._server = server |
|---|
| 1354 | self._dyhb_rtt = rtt |
|---|
| 1355 | |
|---|
| 1356 | def __repr__(self): |
|---|
| 1357 | return "sh%d-on-%s" % (self._shnum, str(self._server.get_name(), "ascii")) |
|---|
| 1358 | |
|---|
| 1359 | class MySegmentFetcher(SegmentFetcher): |
|---|
| 1360 | def __init__(self, *args, **kwargs): |
|---|
| 1361 | SegmentFetcher.__init__(self, *args, **kwargs) |
|---|
| 1362 | self._test_start_shares = [] |
|---|
| 1363 | def _start_share(self, share, shnum): |
|---|
| 1364 | self._test_start_shares.append(share) |
|---|
| 1365 | |
|---|
| 1366 | class FakeNode: |
|---|
| 1367 | def __init__(self): |
|---|
| 1368 | self.want_more = 0 |
|---|
| 1369 | self.failed = None |
|---|
| 1370 | self.processed = None |
|---|
| 1371 | self._si_prefix = "si_prefix" |
|---|
| 1372 | |
|---|
| 1373 | def want_more_shares(self): |
|---|
| 1374 | self.want_more += 1 |
|---|
| 1375 | |
|---|
| 1376 | def fetch_failed(self, fetcher, f): |
|---|
| 1377 | self.failed = f |
|---|
| 1378 | |
|---|
| 1379 | def process_blocks(self, segnum, blocks): |
|---|
| 1380 | self.processed = (segnum, blocks) |
|---|
| 1381 | |
|---|
| 1382 | def get_num_segments(self): |
|---|
| 1383 | return 1, True |
|---|
| 1384 | |
|---|
| 1385 | |
|---|
| 1386 | class Selection(unittest.TestCase): |
|---|
| 1387 | def test_failure(self): |
|---|
| 1388 | """If the fetch loop fails, it tell the Node the fetch failed.""" |
|---|
| 1389 | node = FakeNode() |
|---|
| 1390 | # Simulate a failure: |
|---|
| 1391 | node.get_num_segments = lambda: 1/0 |
|---|
| 1392 | sf = SegmentFetcher(node, 0, 3, None) |
|---|
| 1393 | sf.add_shares([]) |
|---|
| 1394 | d = flushEventualQueue() |
|---|
| 1395 | def _check1(ign): |
|---|
| 1396 | [_] = self.flushLoggedErrors(ZeroDivisionError) |
|---|
| 1397 | self.failUnless(node.failed) |
|---|
| 1398 | self.failUnless(node.failed.check(ZeroDivisionError)) |
|---|
| 1399 | d.addCallback(_check1) |
|---|
| 1400 | return d |
|---|
| 1401 | |
|---|
| 1402 | def test_no_shares(self): |
|---|
| 1403 | node = FakeNode() |
|---|
| 1404 | sf = SegmentFetcher(node, 0, 3, None) |
|---|
| 1405 | sf.add_shares([]) |
|---|
| 1406 | d = flushEventualQueue() |
|---|
| 1407 | def _check1(ign): |
|---|
| 1408 | self.failUnlessEqual(node.want_more, 1) |
|---|
| 1409 | self.failUnlessEqual(node.failed, None) |
|---|
| 1410 | sf.no_more_shares() |
|---|
| 1411 | return flushEventualQueue() |
|---|
| 1412 | d.addCallback(_check1) |
|---|
| 1413 | def _check2(ign): |
|---|
| 1414 | self.failUnless(node.failed) |
|---|
| 1415 | self.failUnless(node.failed.check(NoSharesError)) |
|---|
| 1416 | d.addCallback(_check2) |
|---|
| 1417 | return d |
|---|
| 1418 | |
|---|
| 1419 | def test_only_one_share(self): |
|---|
| 1420 | node = FakeNode() |
|---|
| 1421 | sf = MySegmentFetcher(node, 0, 3, None) |
|---|
| 1422 | serverA = make_server(b"peer-A") |
|---|
| 1423 | shares = [MyShare(0, serverA, 0.0)] |
|---|
| 1424 | sf.add_shares(shares) |
|---|
| 1425 | d = flushEventualQueue() |
|---|
| 1426 | def _check1(ign): |
|---|
| 1427 | self.failUnlessEqual(node.want_more, 1) |
|---|
| 1428 | self.failUnlessEqual(node.failed, None) |
|---|
| 1429 | sf.no_more_shares() |
|---|
| 1430 | return flushEventualQueue() |
|---|
| 1431 | d.addCallback(_check1) |
|---|
| 1432 | def _check2(ign): |
|---|
| 1433 | self.failUnless(node.failed) |
|---|
| 1434 | self.failUnless(node.failed.check(NotEnoughSharesError)) |
|---|
| 1435 | sname = serverA.get_name() |
|---|
| 1436 | self.failUnlessIn("complete= pending=sh0-on-%s overdue= unused=" % str(sname, "ascii"), |
|---|
| 1437 | str(node.failed)) |
|---|
| 1438 | d.addCallback(_check2) |
|---|
| 1439 | return d |
|---|
| 1440 | |
|---|
| 1441 | def test_good_diversity_early(self): |
|---|
| 1442 | node = FakeNode() |
|---|
| 1443 | sf = MySegmentFetcher(node, 0, 3, None) |
|---|
| 1444 | shares = [MyShare(i, make_server(b"peer-%d" % i), i) for i in range(10)] |
|---|
| 1445 | sf.add_shares(shares) |
|---|
| 1446 | d = flushEventualQueue() |
|---|
| 1447 | def _check1(ign): |
|---|
| 1448 | self.failUnlessEqual(node.want_more, 0) |
|---|
| 1449 | self.failUnlessEqual(sf._test_start_shares, shares[:3]) |
|---|
| 1450 | for sh in sf._test_start_shares: |
|---|
| 1451 | sf._block_request_activity(sh, sh._shnum, COMPLETE, |
|---|
| 1452 | "block-%d" % sh._shnum) |
|---|
| 1453 | return flushEventualQueue() |
|---|
| 1454 | d.addCallback(_check1) |
|---|
| 1455 | def _check2(ign): |
|---|
| 1456 | self.failIfEqual(node.processed, None) |
|---|
| 1457 | self.failUnlessEqual(node.processed, (0, {0: "block-0", |
|---|
| 1458 | 1: "block-1", |
|---|
| 1459 | 2: "block-2"}) ) |
|---|
| 1460 | d.addCallback(_check2) |
|---|
| 1461 | return d |
|---|
| 1462 | |
|---|
| 1463 | def test_good_diversity_late(self): |
|---|
| 1464 | node = FakeNode() |
|---|
| 1465 | sf = MySegmentFetcher(node, 0, 3, None) |
|---|
| 1466 | shares = [MyShare(i, make_server(b"peer-%d" % i), i) for i in range(10)] |
|---|
| 1467 | sf.add_shares([]) |
|---|
| 1468 | d = flushEventualQueue() |
|---|
| 1469 | def _check1(ign): |
|---|
| 1470 | self.failUnlessEqual(node.want_more, 1) |
|---|
| 1471 | sf.add_shares(shares) |
|---|
| 1472 | return flushEventualQueue() |
|---|
| 1473 | d.addCallback(_check1) |
|---|
| 1474 | def _check2(ign): |
|---|
| 1475 | self.failUnlessEqual(sf._test_start_shares, shares[:3]) |
|---|
| 1476 | for sh in sf._test_start_shares: |
|---|
| 1477 | sf._block_request_activity(sh, sh._shnum, COMPLETE, |
|---|
| 1478 | "block-%d" % sh._shnum) |
|---|
| 1479 | return flushEventualQueue() |
|---|
| 1480 | d.addCallback(_check2) |
|---|
| 1481 | def _check3(ign): |
|---|
| 1482 | self.failIfEqual(node.processed, None) |
|---|
| 1483 | self.failUnlessEqual(node.processed, (0, {0: "block-0", |
|---|
| 1484 | 1: "block-1", |
|---|
| 1485 | 2: "block-2"}) ) |
|---|
| 1486 | d.addCallback(_check3) |
|---|
| 1487 | return d |
|---|
| 1488 | |
|---|
| 1489 | def test_avoid_bad_diversity_late(self): |
|---|
| 1490 | node = FakeNode() |
|---|
| 1491 | sf = MySegmentFetcher(node, 0, 3, None) |
|---|
| 1492 | # we could satisfy the read entirely from the first server, but we'd |
|---|
| 1493 | # prefer not to. Instead, we expect to only pull one share from the |
|---|
| 1494 | # first server |
|---|
| 1495 | servers = make_servers([b"peer-A", b"peer-B", b"peer-C"]) |
|---|
| 1496 | shares = [MyShare(0, servers[b"peer-A"], 0.0), |
|---|
| 1497 | MyShare(1, servers[b"peer-A"], 0.0), |
|---|
| 1498 | MyShare(2, servers[b"peer-A"], 0.0), |
|---|
| 1499 | MyShare(3, servers[b"peer-B"], 1.0), |
|---|
| 1500 | MyShare(4, servers[b"peer-C"], 2.0), |
|---|
| 1501 | ] |
|---|
| 1502 | sf.add_shares([]) |
|---|
| 1503 | d = flushEventualQueue() |
|---|
| 1504 | def _check1(ign): |
|---|
| 1505 | self.failUnlessEqual(node.want_more, 1) |
|---|
| 1506 | sf.add_shares(shares) |
|---|
| 1507 | return flushEventualQueue() |
|---|
| 1508 | d.addCallback(_check1) |
|---|
| 1509 | def _check2(ign): |
|---|
| 1510 | self.failUnlessEqual(sf._test_start_shares, |
|---|
| 1511 | [shares[0], shares[3], shares[4]]) |
|---|
| 1512 | for sh in sf._test_start_shares: |
|---|
| 1513 | sf._block_request_activity(sh, sh._shnum, COMPLETE, |
|---|
| 1514 | "block-%d" % sh._shnum) |
|---|
| 1515 | return flushEventualQueue() |
|---|
| 1516 | d.addCallback(_check2) |
|---|
| 1517 | def _check3(ign): |
|---|
| 1518 | self.failIfEqual(node.processed, None) |
|---|
| 1519 | self.failUnlessEqual(node.processed, (0, {0: "block-0", |
|---|
| 1520 | 3: "block-3", |
|---|
| 1521 | 4: "block-4"}) ) |
|---|
| 1522 | d.addCallback(_check3) |
|---|
| 1523 | return d |
|---|
| 1524 | |
|---|
| 1525 | def test_suffer_bad_diversity_late(self): |
|---|
| 1526 | node = FakeNode() |
|---|
| 1527 | sf = MySegmentFetcher(node, 0, 3, None) |
|---|
| 1528 | # we satisfy the read entirely from the first server because we don't |
|---|
| 1529 | # have any other choice. |
|---|
| 1530 | serverA = make_server(b"peer-A") |
|---|
| 1531 | shares = [MyShare(0, serverA, 0.0), |
|---|
| 1532 | MyShare(1, serverA, 0.0), |
|---|
| 1533 | MyShare(2, serverA, 0.0), |
|---|
| 1534 | MyShare(3, serverA, 0.0), |
|---|
| 1535 | MyShare(4, serverA, 0.0), |
|---|
| 1536 | ] |
|---|
| 1537 | sf.add_shares([]) |
|---|
| 1538 | d = flushEventualQueue() |
|---|
| 1539 | def _check1(ign): |
|---|
| 1540 | self.failUnlessEqual(node.want_more, 1) |
|---|
| 1541 | sf.add_shares(shares) |
|---|
| 1542 | return flushEventualQueue() |
|---|
| 1543 | d.addCallback(_check1) |
|---|
| 1544 | def _check2(ign): |
|---|
| 1545 | self.failUnlessEqual(node.want_more, 3) |
|---|
| 1546 | self.failUnlessEqual(sf._test_start_shares, |
|---|
| 1547 | [shares[0], shares[1], shares[2]]) |
|---|
| 1548 | for sh in sf._test_start_shares: |
|---|
| 1549 | sf._block_request_activity(sh, sh._shnum, COMPLETE, |
|---|
| 1550 | "block-%d" % sh._shnum) |
|---|
| 1551 | return flushEventualQueue() |
|---|
| 1552 | d.addCallback(_check2) |
|---|
| 1553 | def _check3(ign): |
|---|
| 1554 | self.failIfEqual(node.processed, None) |
|---|
| 1555 | self.failUnlessEqual(node.processed, (0, {0: "block-0", |
|---|
| 1556 | 1: "block-1", |
|---|
| 1557 | 2: "block-2"}) ) |
|---|
| 1558 | d.addCallback(_check3) |
|---|
| 1559 | return d |
|---|
| 1560 | |
|---|
| 1561 | def test_suffer_bad_diversity_early(self): |
|---|
| 1562 | node = FakeNode() |
|---|
| 1563 | sf = MySegmentFetcher(node, 0, 3, None) |
|---|
| 1564 | # we satisfy the read entirely from the first server because we don't |
|---|
| 1565 | # have any other choice. |
|---|
| 1566 | serverA = make_server(b"peer-A") |
|---|
| 1567 | shares = [MyShare(0, serverA, 0.0), |
|---|
| 1568 | MyShare(1, serverA, 0.0), |
|---|
| 1569 | MyShare(2, serverA, 0.0), |
|---|
| 1570 | MyShare(3, serverA, 0.0), |
|---|
| 1571 | MyShare(4, serverA, 0.0), |
|---|
| 1572 | ] |
|---|
| 1573 | sf.add_shares(shares) |
|---|
| 1574 | d = flushEventualQueue() |
|---|
| 1575 | def _check1(ign): |
|---|
| 1576 | self.failUnlessEqual(node.want_more, 2) |
|---|
| 1577 | self.failUnlessEqual(sf._test_start_shares, |
|---|
| 1578 | [shares[0], shares[1], shares[2]]) |
|---|
| 1579 | for sh in sf._test_start_shares: |
|---|
| 1580 | sf._block_request_activity(sh, sh._shnum, COMPLETE, |
|---|
| 1581 | "block-%d" % sh._shnum) |
|---|
| 1582 | return flushEventualQueue() |
|---|
| 1583 | d.addCallback(_check1) |
|---|
| 1584 | def _check2(ign): |
|---|
| 1585 | self.failIfEqual(node.processed, None) |
|---|
| 1586 | self.failUnlessEqual(node.processed, (0, {0: "block-0", |
|---|
| 1587 | 1: "block-1", |
|---|
| 1588 | 2: "block-2"}) ) |
|---|
| 1589 | d.addCallback(_check2) |
|---|
| 1590 | return d |
|---|
| 1591 | |
|---|
| 1592 | def test_overdue(self): |
|---|
| 1593 | node = FakeNode() |
|---|
| 1594 | sf = MySegmentFetcher(node, 0, 3, None) |
|---|
| 1595 | shares = [MyShare(i, make_server(b"peer-%d" % i), i) for i in range(10)] |
|---|
| 1596 | sf.add_shares(shares) |
|---|
| 1597 | d = flushEventualQueue() |
|---|
| 1598 | def _check1(ign): |
|---|
| 1599 | self.failUnlessEqual(node.want_more, 0) |
|---|
| 1600 | self.failUnlessEqual(sf._test_start_shares, shares[:3]) |
|---|
| 1601 | for sh in sf._test_start_shares: |
|---|
| 1602 | sf._block_request_activity(sh, sh._shnum, OVERDUE) |
|---|
| 1603 | return flushEventualQueue() |
|---|
| 1604 | d.addCallback(_check1) |
|---|
| 1605 | def _check2(ign): |
|---|
| 1606 | self.failUnlessEqual(sf._test_start_shares, shares[:6]) |
|---|
| 1607 | for sh in sf._test_start_shares[3:]: |
|---|
| 1608 | sf._block_request_activity(sh, sh._shnum, COMPLETE, |
|---|
| 1609 | "block-%d" % sh._shnum) |
|---|
| 1610 | return flushEventualQueue() |
|---|
| 1611 | d.addCallback(_check2) |
|---|
| 1612 | def _check3(ign): |
|---|
| 1613 | self.failIfEqual(node.processed, None) |
|---|
| 1614 | self.failUnlessEqual(node.processed, (0, {3: "block-3", |
|---|
| 1615 | 4: "block-4", |
|---|
| 1616 | 5: "block-5"}) ) |
|---|
| 1617 | d.addCallback(_check3) |
|---|
| 1618 | return d |
|---|
| 1619 | |
|---|
| 1620 | def test_overdue_fails(self): |
|---|
| 1621 | node = FakeNode() |
|---|
| 1622 | sf = MySegmentFetcher(node, 0, 3, None) |
|---|
| 1623 | servers = make_servers([b"peer-%d" % i for i in range(6)]) |
|---|
| 1624 | shares = [MyShare(i, servers[b"peer-%d" % i], i) for i in range(6)] |
|---|
| 1625 | sf.add_shares(shares) |
|---|
| 1626 | sf.no_more_shares() |
|---|
| 1627 | d = flushEventualQueue() |
|---|
| 1628 | def _check1(ign): |
|---|
| 1629 | self.failUnlessEqual(node.want_more, 0) |
|---|
| 1630 | self.failUnlessEqual(sf._test_start_shares, shares[:3]) |
|---|
| 1631 | for sh in sf._test_start_shares: |
|---|
| 1632 | sf._block_request_activity(sh, sh._shnum, OVERDUE) |
|---|
| 1633 | return flushEventualQueue() |
|---|
| 1634 | d.addCallback(_check1) |
|---|
| 1635 | def _check2(ign): |
|---|
| 1636 | self.failUnlessEqual(sf._test_start_shares, shares[:6]) |
|---|
| 1637 | for sh in sf._test_start_shares[3:]: |
|---|
| 1638 | sf._block_request_activity(sh, sh._shnum, DEAD) |
|---|
| 1639 | return flushEventualQueue() |
|---|
| 1640 | d.addCallback(_check2) |
|---|
| 1641 | def _check3(ign): |
|---|
| 1642 | # we're still waiting |
|---|
| 1643 | self.failUnlessEqual(node.processed, None) |
|---|
| 1644 | self.failUnlessEqual(node.failed, None) |
|---|
| 1645 | # now complete one of the overdue ones, and kill one of the other |
|---|
| 1646 | # ones, leaving one hanging. This should trigger a failure, since |
|---|
| 1647 | # we cannot succeed. |
|---|
| 1648 | live = sf._test_start_shares[0] |
|---|
| 1649 | die = sf._test_start_shares[1] |
|---|
| 1650 | sf._block_request_activity(live, live._shnum, COMPLETE, "block") |
|---|
| 1651 | sf._block_request_activity(die, die._shnum, DEAD) |
|---|
| 1652 | return flushEventualQueue() |
|---|
| 1653 | d.addCallback(_check3) |
|---|
| 1654 | def _check4(ign): |
|---|
| 1655 | self.failUnless(node.failed) |
|---|
| 1656 | self.failUnless(node.failed.check(NotEnoughSharesError)) |
|---|
| 1657 | sname = servers[b"peer-2"].get_name() |
|---|
| 1658 | self.failUnlessIn("complete=sh0 pending= overdue=sh2-on-%s unused=" % str(sname, "ascii"), |
|---|
| 1659 | str(node.failed)) |
|---|
| 1660 | d.addCallback(_check4) |
|---|
| 1661 | return d |
|---|
| 1662 | |
|---|
| 1663 | def test_avoid_redundancy(self): |
|---|
| 1664 | node = FakeNode() |
|---|
| 1665 | sf = MySegmentFetcher(node, 0, 3, None) |
|---|
| 1666 | # we could satisfy the read entirely from the first server, but we'd |
|---|
| 1667 | # prefer not to. Instead, we expect to only pull one share from the |
|---|
| 1668 | # first server |
|---|
| 1669 | servers = make_servers([b"peer-A", b"peer-B", b"peer-C", b"peer-D", |
|---|
| 1670 | b"peer-E"]) |
|---|
| 1671 | shares = [MyShare(0, servers[b"peer-A"],0.0), |
|---|
| 1672 | MyShare(1, servers[b"peer-B"],1.0), |
|---|
| 1673 | MyShare(0, servers[b"peer-C"],2.0), # this will be skipped |
|---|
| 1674 | MyShare(1, servers[b"peer-D"],3.0), |
|---|
| 1675 | MyShare(2, servers[b"peer-E"],4.0), |
|---|
| 1676 | ] |
|---|
| 1677 | sf.add_shares(shares[:3]) |
|---|
| 1678 | d = flushEventualQueue() |
|---|
| 1679 | def _check1(ign): |
|---|
| 1680 | self.failUnlessEqual(node.want_more, 1) |
|---|
| 1681 | self.failUnlessEqual(sf._test_start_shares, |
|---|
| 1682 | [shares[0], shares[1]]) |
|---|
| 1683 | # allow sh1 to retire |
|---|
| 1684 | sf._block_request_activity(shares[1], 1, COMPLETE, "block-1") |
|---|
| 1685 | return flushEventualQueue() |
|---|
| 1686 | d.addCallback(_check1) |
|---|
| 1687 | def _check2(ign): |
|---|
| 1688 | # and then feed in the remaining shares |
|---|
| 1689 | sf.add_shares(shares[3:]) |
|---|
| 1690 | sf.no_more_shares() |
|---|
| 1691 | return flushEventualQueue() |
|---|
| 1692 | d.addCallback(_check2) |
|---|
| 1693 | def _check3(ign): |
|---|
| 1694 | self.failUnlessEqual(sf._test_start_shares, |
|---|
| 1695 | [shares[0], shares[1], shares[4]]) |
|---|
| 1696 | sf._block_request_activity(shares[0], 0, COMPLETE, "block-0") |
|---|
| 1697 | sf._block_request_activity(shares[4], 2, COMPLETE, "block-2") |
|---|
| 1698 | return flushEventualQueue() |
|---|
| 1699 | d.addCallback(_check3) |
|---|
| 1700 | def _check4(ign): |
|---|
| 1701 | self.failIfEqual(node.processed, None) |
|---|
| 1702 | self.failUnlessEqual(node.processed, (0, {0: "block-0", |
|---|
| 1703 | 1: "block-1", |
|---|
| 1704 | 2: "block-2"}) ) |
|---|
| 1705 | d.addCallback(_check4) |
|---|
| 1706 | return d |
|---|