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