source: trunk/src/allmydata/test/test_download.py

Last change on this file was 53084f7, checked in by Alexandre Detiste <alexandre.detiste@…>, at 2024-02-27T23:49:07Z

remove more Python2 compatibility

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