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

Last change on this file was fec97256, checked in by Alexandre Detiste <alexandre.detiste@…>, at 2025-01-06T21:51:37Z

trim Python2 syntax

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