1\xfcgX\x15\xaa\x00\x00\u07d4\xad\xff\r\x1d\v\x97G\x1ev\u05c9\xd2\u470at\xf9\xbdT\xff\x89e\xea=\xb7UF`\x00\x00\u07d4\xae\x06,D\x86\x18d0u\xdez\x0004-\xce\xd6=\xba\u05c9,\xc6\u034c\u0082\xb3\x00\x00\xe0\x94\xae\x10\xe2z\x01O\r0k\xaf&mH\x97\u021a\xee\xe2\xe9t\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xae\x12k8,\xf2W\xfa\xd7\xf0\xbc}\x16)~T\xccrg\u0689\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x13\xa0\x85\x11\x11\x0f2\xe5;\xe4\x12xE\xc8C\xa1\xa5|{\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xae\x17\x9aF\r\xb6c&t=$\xe6u#\xa5{$m\xaf\u007f\x8a\x01\x00\a\xae|\xe5\xbb\xe4\x00\x00\u07d4\xae\"(ey\x90y\xaa\xf4\xf0gJ\f\u06ab\x02\xa6\xd5p\xff\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xae#\x9a\xcf\xfdN\xbe.\x1b\xa5\xb4\x17\x05r\xdcy\xcce3\xec\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xae/\x9c\x19\xacv\x13e\x94C#\x93\xb0G\x1d\b\x90!d\u04c9%\xdf\x05\u01a8\x97\xe4\x00\x00\u07d4\xae4\x86\x1d4\"S\x19O\xfcfR\xdf\xdeQ\xabD\xca\xd3\xfe\x89\x19F\bhc\x16\xbd\x80\x00\u07d4\xae6\xf7E!!\x91>\x80\x0e\x0f\xcd\x1ae\xa5G\x1c#\x84o\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xae?\x98\xa4C\xef\xe0\x0f>q\x1dR]\x98\x94\u071aa\x15{\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xaeG\xe2`\x9c\xfa\xfe6\x9df\xd4\x15\xd99\xde\x05\b\x1a\x98r\x8a\x05\xba\xec\xf0%\xf9\xb6P\x00\x00\u07d4\xaeO\x12.5\xc0\xb1\xd1\xe4\x06\x92\x91E|\x83\xc0\u007f\x96_\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaePU\x81L\xb8\xbe\f\x11{\xb8\xb1\xc8\u04b6;F\x98\xb7(\x89\x01\xbc\x93.\xc5s\xa3\x80\x00\u07d4\xaeS\x8cs\u0173\x8d\x8dXM~\xbd\xad\xef\xb1\\\xab\xe4\x83W\x896'\xe8\xf7\x127<\x00\x00\u07d4\xaeW\xcc\x12\x9a\x96\xa8\x99\x81\xda\xc6\r/\xfb\x87}]\xc5\xe42\x89<:#\x94\xb3\x96U\x00\x00\u07d4\xaeZ\xa1\xe6\u00b6\x0fo\xd3\xef\xe7!\xbbJq\x9c\xbe=o]\x89+$\u01b5Z^b\x00\x00\u07d4\xae\\\x9b\xda\xd3\xc5\u0221\"\x04D\xae\xa5\xc2)\xc1\x83\x9f\x1dd\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xae\\\xe35Z{\xa9\xb32v\f\tP\u00bcE\xa8_\xa9\xa0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xae]\"\x1a\xfc\xd3\u0493U\xf5\b\xea\xdf\xca@\x8c\xe3<\xa9\x03\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xaec[\xf781\x11\x9d-)\xc0\xd0O\xf8\xf8\xd8\u0425zF\x89Hz\x9a0E9D\x00\x00\xe0\x94\xaed\x81U\xa6X7\x0f\x92\x9b\xe3\x84\xf7\xe0\x01\x04~I\xddF\x8a\x02\xdf$\xae2\xbe D\x00\x00\xe0\x94\xaeo\fs\xfd\xd7|H\x97'Q!t\u0675\x02\x96a\x1cL\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xaep\xe6\x9d,J\n\xf8\x18\x80{\x1a'\x05\xf7\x9f\u0435\xdb\u01095e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xaew9\x12N\xd1S\x05%\x03\xfc\x10\x14\x10\xd1\xff\xd8\xcd\x13\xb7\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xaex\xbb\x84\x919\xa6\xba8\xae\x92\xa0\x9ai`\x1c\xc4\xcbb\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xae\x84\"\x10\xf4M\x14\u0124\u06d1\xfc\x9d;;P\x01O{\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xae\x84.\x81\x85\x8e\xcf\xed\xf6Plhm\xc2\x04\xac\x15\xbf\x8b$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xae\x89T\xf8\xd6\x16m\xe5\a\xcfa)}\x0f\xc7\xcak\x9eq(\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x9e\xcdk\u0755.\xf4\x97\xc0\x05\n\u0aca\x82\xa9\x18\x98\u0389\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xae\x9f\\?\xbb\xe0\u027c\xbf\x1a\xf8\xfft\xea(\v:]\x8b\b\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xae\xad\x88\u0589Ak\x1c\x91\xf26D!7[}\x82\xd0RR\n\xfb\\Wm\x9f~\xb9>\u048a\r\xd0A \xba\t\xcf\xe6\x00\x00\u07d4\xae\xc2\u007f\xf5\xd7\xf9\xdd\u0691\x18?F\xf9\xd5%C\xb6\xcd+/\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xae\xe4\x9dh\xad\xed\xb0\x81\xfdCpZ_x\xc7x\xfb\x90\xdeH\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xae\xf5\xb1\"X\xa1\x8d\xec\a\xd5\xec.1et\x91\x9dy\xd6\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xae\xfc\xfe\x88\xc8&\xcc\xf11\xd5N\xb4\ua7b8\x0ea\xe1\xee%\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xaf\x06\xf5\xfam\x12\x14\xecC\x96}\x1b\xd4\xdd\xe7J\xb8\x14\xa98\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4\xaf\x11H\xefl\x8e\x10=u0\xef\xc9\x16y\u026c'\x00\t\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaf >\"\x9d~mA\x9d\xf47\x8e\xa9\x87\x15Q_c\x14\x85\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xaf X\xc7(,\xf6|\x8c<\xf90\x13<\x89a|\xe7])\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\xaf&\xf7\u01bfE> x\xf0\x89S\u4c80\x04\xa2\xc1\xe2\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xaf0\x87\xe6.\x04\xbf\x90\rZT\xdc>\x94bt\u0692B;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf6\x14\u0736\x8a6\xe4ZN\x91\x1ebybG\"-Y[\x89z\x81\x06_\x11\x03\xbc\x00\x00\u07d4\xaf6\x15\u01c9\u0431\x15*\xd4\xdb%\xfe]\xcf\"(\x04\xcfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaf<\xb5\x96Y3\xe7\xda\u0603i;\x9c>\x15\xbe\xb6\x8aHs\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xafD\x93\xe8R\x1c\xa8\x9d\x95\xf5&|\x1a\xb6?\x9fEA\x1e\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xafL\xf4\x17\x85\x16\x1fW\x1d\f\xa6\x9c\x94\xf8\x02\x1fA)N\u028a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xafR\x9b\xdbE\x9c\xc1\x85\xbe\xe5\xa1\u014b\xf7\xe8\xcc\xe2\\\x15\r\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xafg\xfd>\x12\u007f\xd9\xdc6\xeb?\xcdj\x80\u01feOu2\xb2\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xafw\x1094Z40\x01\xbc\x0f\x8aY#\xb1&\xb6\rP\x9c\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xaf\u007fy\xcbAZ\x1f\xb8\u06fd\tF\a\xee\x8dA\xfb|Z;\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaf\x87\xd27\x1e\xf3x\x95\u007f\xbd\x05\xba/\x1df\x93\x1b\x01\u2e09%\xf2s\x93=\xb5p\x00\x00\u07d4\xaf\x88\x0f\xc7V}U\x95\xca\xcc\xe1\\?\xc1L\x87B\xc2l\x9e\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xaf\x8e\x1d\xcb1L\x95\r6\x87CM0\x98X\xe1\xa8s\x9c\u0509\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xaf\x99-\xd6i\xc0\x88>U\x15\xd3\xf3\x11*\x13\xf6\x17\xa4\xc3g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xa1\u056d8\xfe\xd4GY\xc0[\x89\x93\xc1\xaa\r\xac\xe1\x9f@\x89\x04V9\x18$O@\x00\x00\xe0\x94\xaf\xa59XnG\x19\x17J;F\xb9\xb3\xe6c\xa7\u0475\xb9\x87\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xa6\x94n\xff\xd5\xffS\x15O\x82\x01\x02S\xdfG\xae(\f\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xaf\xc8\xeb\u860b\xd4\x10Z\xccL\x01\x8eTj\x1e\x8f\x9cx\x88\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xaf\xcc}\xbb\x83V\xd8B\xd4:\xe7\xe2<\x84\"\xb0\"\xa3\b\x03\x8a\x06o\xfc\xbf\xd5\xe5\xa3\x00\x00\x00\u07d4\xaf\xd0\x19\xff6\xa0\x91U4ki\x97H\x15\xa1\xc9\x12\xc9\n\xa4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xda\xc5\xc1\xcbV\xe2E\xbfp3\x00f\xa8\x17\uabecL\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xdd\x1bxab\xb81~ \xf0\xe9y\xf4\xb2\xceHmv]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xf1\x04Z\xdf'\xa1\xaa2\x94a\xb2M\xe1\xba\u950ai\x8b\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\xaf\xf1\a\x96\v~\xc3N\u0590\xb6e\x02M`\x83\x8c\x19\x0fp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xaf\xf1\x1c\xcfi\x93\x04\xd5\xf5\x86*\xf8`\x83E\x1c&\xe7\x9a\xe5\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xaf\xf1at\nm\x90\x9f\xe9\x9cY\xa9\xb7yE\xc9\x1c\xc9\x14H\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xaf\xfc\x99\xd5\ubd28O\xe7x\x8d\x97\xdc\xe2t\xb08$\x048\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xfe\xa0G7\"\xcb\u007f\x0e\x0e\x86\xb9\xe1\x18\x83\xbfB\x8d\x8dT\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xb0\t\x96\xb0Vn\xcb>rC\xb8\"y\x88\u0733R\xc2\x18\x99\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xb0\x1e8\x9b(\xa3\x1d\x8eI\x95\xbd\xd7\xd7\xc8\x1b\xee\xab\x1eA\x19\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0-\x06(s3EE\u03a2\x92\x18\xe4\x05w`Y\x0ft#\x89\xac\xb6\xa1\xc7\xd9:\x88\x00\x00\u07d4\xb0/\xa2\x93\x87\xec\x12\xe3\u007fi\"\xacL\xe9\x8c[\t\xe0\xb0\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb06\x91k\xda\u03d4\xb6\x9eZ\x8ae`)u\xeb\x02a\x04\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb0A1\x0f\xe9\xee\u0586L\xed\u053e\xe5\x8d\xf8\x8e\xb4\xed<\xac\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb0U\xafL\xad\xfc\xfd\xb4%\xcfe\xbad1\a\x8f\a\xec\u056b\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0W\x11S\xdb\x1cN\u05ec\xae\xfe\x13\xec\xdf\xdbr\xe7\xe4\xf0j\x8a\x11\f\xffyj\xc1\x95 \x00\x00\u07d4\xb0n\xab\t\xa6\x10\u01a5=V\xa9F\xb2\xc44\x87\xac\x1d[-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0rI\xe0U\x04J\x91U5\x9a@)7\xbb\xd9T\xfeH\xb6\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0v\x182\x8a\x90\x13\a\xa1\xb7\xa0\xd0X\xfc\xd5xn\x9er\xfe\x8a\x06gI]JC0\xce\x00\x00\u07d4\xb0y\xbbM\x98f\x14:m\xa7*\xe7\xac\x00\"\x06)\x811\\\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4\xb0{\xcc\bZ\xb3\xf7)\xf2D\x00Ah7\xb6\x996\xba\x88s\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xb0{\xcf\x1c\xc5\xd4F.Q$\xc9e\xec\xf0\xd7\r\xc2z\xcau\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xb0|\xb9\xc1$\x05\xb7\x11\x80uC\u0113De\xf8\u007f\x98\xbd-\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb0\u007f\u07af\xf9\x1dD`\xfel\xd0\u8870\xbd\x8d\"\xa6.\x87\x8a\x01\x1d%)\xf3SZ\xb0\x00\x00\xe0\x94\xb0\x9f\xe6\xd44\x9b\x99\xbc7\x93\x80T\x02-T\xfc\xa3f\xf7\xaf\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\xb0\xaa\x00\x95\f\x0e\x81\xfa2\x10\x17>r\x9a\xaf\x16:'\xcdq\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb0\xacN\xfff\x80\xee\x14\x16\x9c\xda\xdb\xff\xdb0\x80Om%\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb0\xb3j\xf9\xae\xee\u07d7\xb6\xb0\"\x80\xf1\x14\xf19\x84\xea2`\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xb0\xb7y\xb9K\xfa<.\x1fX{\u031c~!x\x92\"7\x8f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xb0\xba\xeb0\xe3\x13wlLm$t\x02\xbaAg\xaf\u0361\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb0\xbb)\xa8a\xea\x1dBME\xac\u053f\u0112\xfb\x8e\xd8\t\xb7\x89\x04V9\x18$O@\x00\x00\xe0\x94\xb0\xc1\xb1w\xa2 \xe4\x1f|t\xd0|\u0785i\xc2\x1cu\xc2\xf9\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xb0\xc7\xceL\r\xc3\u00bb\xb9\x9c\xc1\x85{\x8aE_a\x17\x11\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb0\xce\xf8\xe8\xfb\x89\x84\xa6\x01\x9f\x01\xc6y\xf2r\xbb\xe6\x8f\\w\x89\b=lz\xabc`\x00\x00\xe0\x94\xb0\xd3+\xd7\xe4\u6577\xb0\x1a\xa3\xd0Ao\x80U}\xba\x99\x03\x8a\x03s\x9f\xf0\xf6\xe6\x130\x00\x00\xe0\x94\xb0\xd3\u0247+\x85\x05n\xa0\xc0\xe6\xd1\xec\xf7\xa7~<\u6ac5\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xb0\xe4i\u0206Y8\x15\xb3IV8Y]\xae\xf0f_\xaeb\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xb0\xe7`\xbb\a\xc0\x81wsE\xe0W\x8e\x8b\u0218\"mN;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\x040\x04\xec\x19A\xa8\xcfO+\x00\xb1W\x00\u076co\xf1~\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\x05\xdd=\x98|\xff\xd8\x13\xe9\xc8P\n\x80\xa1\xad%}V\u0189lj\xccg\u05f1\xd4\x00\x00\u07d4\xb1\x0f\u04a6G\x10/\x88\x1ft\xc9\xfb\xc3}\xa62\x94\x9f#u\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb1\x15\xee:\xb7d\x1e\x1a\xa6\xd0\x00\xe4\x1b\xfc\x1e\xc7!\f/2\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xb1\x17\x8a\xd4s\x83\xc3\x1c\x814\xa1\x94\x1c\xbc\xd4t\xd0bD\xe2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb1\x17\x95\x89\u1779\xd4\x15W\xbb\xec\x1c\xb2L\xcc-\xec\x1c\u007f\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb1\x19\u76a9\xb9\x16Re\x81\xcb\xf5!\xefGJ\xe8M\xcf\xf4\x89O\xba\x10\x01\xe5\xbe\xfe\x00\x00\u07d4\xb1\x1f\xa7\xfb'\n\xbc\xdfZ.\xab\x95\xaa0\u013566\uffc9+^:\xf1k\x18\x80\x00\x00\u07d4\xb1$\xbc\xb6\xff\xa40\xfc\xae.\x86\xb4_'\xe3\xf2\x1e\x81\xee\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1)\xa5\xcbq\x05\xfe\x81\v\u0615\xdcr\x06\xa9\x91\xa4TT\x88\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\xb1.\xd0{\x8a8\xadU\x066?\xc0z\vmy\x996\xbd\xaf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb14\xc0\x049\x1a\xb4\x99(x3zQ\xec$/B(WB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1?\x93\xaf0\xe8\xd7fs\x81\xb2\xb9[\xc1\xa6\x99\xd5\xe3\xe1)\x89\x16\u012b\xbe\xbe\xa0\x10\x00\x00\u07d4\xb1E\x92\x85\x86>\xa2\xdb7Y\xe5F\u03b3\xfb7a\xf5\x90\x9c\x89<\xd7*\x89@\x87\xe0\x80\x00\u07d4\xb1F\xa0\xb9%U<\xf0o\xca\xf5J\x1bM\xfe\xa6!)\aW\x89lnY\xe6|xT\x00\x00\xe0\x94\xb1Jz\xaa\x8fI\xf2\xfb\x9a\x81\x02\u05bb\xe4\u010a\xe7\xc0o\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1K\xbe\xffpr\tu\xdca\x91\xb2\xa4O\xf4\x9f&r\x87<\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xb1L\xc8\xde3\xd63\x826S\x9aH\x90 \xceFU\xa3+\u018a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1M\xdb\x03\x86\xfb`c\x98\xb8\xccGVZ\xfa\xe0\x0f\xf1\xd6j\x89\xa1*\xff\b>f\xf0\x00\x00\u07d4\xb1S\xf8(\xdd\amJ|\x1c%t\xbb-\xee\x1aD\xa3\x18\xa8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1T\x0e\x94\xcf\xf3F\\\xc3\u0447\xe7\xc8\u3f6f\x98FY\u2262\x15\xe4C\x90\xe33\x00\x00\u07d4\xb1X\xdbC\xfab\xd3\x0ee\xf3\u041b\xf7\x81\u01f6sr\uba89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1ar_\xdc\xed\xd1yR\xd5{#\xef([~K\x11i\xe8\x89\x02\xb6\xdf\xed6d\x95\x80\x00\u07d4\xb1dy\xba\x8e}\xf8\xf6>\x1b\x95\xd1I\u0345)\xd75\xc2\u0689-\xe3:j\xac2T\x80\x00\u07d4\xb1f\xe3}.P\x1a\xe7<\x84\x14+_\xfbZ\xa6U\xddZ\x99\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1\x83\xeb\xeeO\xcbB\xc2 \xe4wt\xf5\x9dlT\xd5\xe3*\xb1\x89V\xf7\xa9\xc3<\x04\xd1\x00\x00\u07d4\xb1\x88\a\x84D\x02~8g\x98\xa8\xaehi\x89\x19\xd5\xcc#\r\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xb1\x89j7\xe5\u0602Z-\x01vZ\xe5\xdeb\x99w\u0783R\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb1\x8eg\xa5\x05\n\x1d\xc9\xfb\x19\t\x19\xa3=\xa88\xefDP\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb1\xa2\xb4:t3\xdd\x15\v\xb8\"'\xedQ\x9c\u05b1B\u04c2\x89\x94mb\rtK\x88\x00\x00\u07d4\xb1\xc0\u040b6\xe1\x84\xf9\x95*@7\xe3\xe5:f}\a\nN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\xc3(\xfb\x98\xf2\xf1\x9a\xb6do\n|\x8cVo\xdaZ\x85@\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\xb1\xc7Qxi9\xbb\xa0\xd6q\xa6w\xa1X\u01ab\xe7&^F\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xb1\xcdK\xdf\xd1\x04H\x9a\x02n\u025dYs\a\xa0By\xf1s\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb1\u03d4\xf8\t\x15\x05\x05_\x01\n\xb4\xba\u0196\xe0\xca\x0fg\xa1\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xb1\u05b0\x1b\x94\xd8T\xfe\x8b7J\xa6^\x89\\\xf2*\xa2V\x0e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\xb1\u06e5%\v\xa9bWU$n\x06yg\xf2\xad/\a\x91\u078a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xb1\xe2\u0755\xe3\x9a\xe9w\\U\xae\xb1?\x12\xc2\xfa#0S\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\xe6\xe8\x10\xc2J\xb0H\x8d\xe9\xe0\x1eWH7\x82\x9f|w\u0409\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1\xe9\xc5\xf1\xd2\x1eauzk.\xe7Y\x13\xfcZ\x1aA\x01\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\x03\u049elV\xb9&\x99\u0139-\x1fo\x84d\x8d\xc4\u03fc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x16\xdcY\xe2|=ry\xf5\xcd[\xb2\xbe\u03f2`n\x14\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x1byy\xbf|\\\xa0\x1f\xa8-\xd6@\xb4\x1c9\xe6\u01bcu\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb2#\xbf\x1f\xbf\x80H\\\xa2\xb5V}\x98\xdb{\xc3SM\xd6i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2-PU\xd9b15\x96\x1ej\xbd'<\x90\xde\xea\x16\xa3\xe7\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb2-\xad\xd7\xe1\xe0R2\xa927\xba\xed\x98\xe0\u07d2\xb1\x86\x9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb24\x03_uDF<\xe1\xe2+\xc5S\x06F\x84\xc5\x13\xcdQ\x89\r\x89\xfa=\u010d\xcf\x00\x00\u07d4\xb2G\u03dcr\xecH*\xf3\xea\xa7Ye\x8fy=g\nW\f\x891p\x8a\xe0\x04T@\x00\x00\u07d4\xb2ghA\xee\x9f-1\xc1r\xe8#\x03\xb0\xfe\x9b\xbf\x9f\x1e\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb2y\xc7\xd3U\u0088\x03\x92\xaa\u046a!\xee\x86|;5\a\u07c9D[\xe3\xf2\uf1d4\x00\x00\u07d4\xb2|\x1a$ L\x1e\x11\x8du\x14\x9d\xd1\t1\x1e\a\xc0s\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb2\x81\x81\xa4X\xa4@\xf1\u01bb\x1d\xe8@\x02\x81\xa3\x14\x8fL5\x89\x14b\fW\xdd\xda\xe0\x00\x00\xe0\x94\xb2\x82E\x03|\xb1\x92\xf7W\x85\u02c6\xcb\xfe|\x93\r\xa2X\xb0\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xb2\x87\xf7\xf8\xd8\u00c7,\x1bXk\xcd}\n\xed\xbf~s'2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb2\x8b\xb3\x9f4fQ|\xd4o\x97\x9c\xf5\x96S\xee}\x8f\x15.\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xb2\x8d\xbf\xc6I\x98\x94\xf7:q\xfa\xa0\n\xbe\x0fK\xc9\u045f*\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\x96\x8f}5\xf2\b\x87\x161\xc6h{?=\xae\xab\xc6al\x89\bu\xc4\u007f(\x9fv\x00\x00\u07d4\xb2\x9f[|\x190\xd9\xf9z\x11^\x06pf\xf0\xb5M\xb4K;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb2\xa1D\xb1\xeag\xb9Q\x0f\"g\xf9\xda9\xd3\xf9=\xe2fB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xa2\xc2\x11\x16\x12\xfb\x8b\xbb\x8e}\xd97\x8dg\xf1\xa3\x84\xf0P\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb2\xa4\x98\xf0;\xd7\x17\x8b\u0627\x89\xa0\x0fR7\xafy\xa3\xe3\xf8\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\xb2\xaa/\x1f\x8e\x93\xe7\x97\x13\xd9,\xea\x9f\xfc\xe9\xa4\n\xf9\xc8-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xb5\x16\xfd\u045e\u007f8d\xb6\xd2\xcf\x1b%*AV\xf1\xb0;\x89\x02\xe9\x83\xc7a\x15\xfc\x00\x00\u07d4\xb2\xb7\u0374\xffKa\u0577\xce\v\"p\xbb\xb5&\x97C\xec\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xbd\xbe\u07d5\x90\x84v\xd7\x14\x8a7\f\u0193t6(\x05\u007f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2\xbf\xaaX\xb5\x19l\\\xb7\xf8\x9d\xe1_G\x9d\x188\xdeq=\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xb2\xc5>\xfa3\xfeJ:\x1a\x80 \\s\xec;\x1d\xbc\xad\x06\x02\x89h\x01\u06b3Y\x18\x93\x80\x00\xe0\x94\xb2\xd06\x05\x15\xf1}\xab\xa9\x0f\u02ec\x82\x05\xd5i\xb9\x15\u05ac\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xb2\xd1\xe9\x9a\xf9\x121\x85\x8epe\xdd\x19\x183\r\xc4\xc7G\u054a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xb2\u066b\x96d\xbc\xf6\xdf <4o\u0192\xfd\x9c\xba\xb9 ^\x89\x17\xbex\x97`e\x18\x00\x00\u07d4\xb2\u0777\x86\xd3yN'\x01\x87\xd0E\x1a\xd6\u0237\x9e\x0e\x87E\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\xe0\x85\xfd\xdd\x14h\xba\aA['NsN\x11#\u007f\xb2\xa9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\xe9\xd7k\xf5\x0f\xc3k\xf7\u04d4Kc\xe9\u0288\x9bi\x99h\x89\x902\xeab\xb7K\x10\x00\x00\xe0\x94\xb2\xf9\xc9r\xc1\xe9swU\xb3\xff\x1b0\x88s\x83\x969[&\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xb2\xfc\x84\xa3\xe5\nP\xaf\x02\xf9M\xa08>\u055fq\xff\x01\u05ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xb3\x05\v\xef\xf9\xde3\xc8\x0e\x1f\xa1R%\xe2\x8f,A:\xe3\x13\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xb3\x11\x96qJH\xdf\xf7&\xea\x943\xcd)\x12\xf1\xa4\x14\xb3\xb3\x89\x91Hx\xa8\xc0^\xe0\x00\x00\xe0\x94\xb3\x14[tPm\x1a\x8d\x04|\xdc\xdcU9*{SPy\x9a\x8a\x1bb)t\x1c\r=]\x80\x00\u07d4\xb3 \x83H6\xd1\xdb\xfd\xa9\xe7\xa3\x18M\x1a\xd1\xfdC \xcc\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3#\u073f.\xdd\xc58.\u4efb \x1c\xa3\x93\x1b\xe8\xb48\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb3$\x00\xfd\x13\xc5P\t\x17\xcb\x03{)\xfe\"\xe7\xd5\"\x8f-\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb3%gL\x01\xe3\xf7)\rR&3\x9f\xbe\xacg\xd2!'\x9f\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xb3(%\xd5\xf3\xdb$\x9e\xf4\xe8\\\xc4\xf31S\x95\x89v\u8f09\x1b-\xf9\xd2\x19\xf5y\x80\x00\u07d4\xb3*\xf3\xd3\xe8\xd0u4I&To.2\x88{\xf9;\x16\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3/\x1c&\x89\xa5\xcey\xf1\xbc\x97\v1XO\x1b\xcf\"\x83\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3<\x03#\xfb\xf9\xc2l\x1d\x8a\xc4N\xf7C\x91\u0400F\x96\u0689\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb3O\x04\xb8\xdbe\xbb\xa9\xc2n\xfcL\xe6\xef\xc5\x04\x81\xf3\xd6]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3U}9\xb5A\x1b\x84D__T\xf3\x8fb\xd2qM\x00\x87\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xb3X\xe9|p\xb6\x05\xb1\xd7\xd7)\u07f6@\xb4<^\xaf\xd1\xe7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xb3^\x8a\x1c\r\xac~\x0ef\u06ecsjY*\xbdD\x01%a\x88\xcf\xceU\xaa\x12\xb3\x00\x00\xe0\x94\xb3fx\x94\xb7\x86<\x06\x8a\xd3D\x87?\xcf\xf4\xb5g\x1e\x06\x89\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3qw1\xda\xd6Q2\xday-\x87`0\xe4j\xc2'\xbb\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3s\x1b\x04l\x8a\u0195\xa1'\xfdy\u0425\xd5\xfaj\xe6\xd1.\x89lO\xd1\xee$nx\x00\x00\u07d4\xb3|+\x9fPc{\xec\xe0\u0295\x92\b\xae\xfe\xe6F;\xa7 \x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb3\x88\xb5\xdf\xec\xd2\xc5\u4d56W|d%V\xdb\xfe'xU\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3\x8cNS{]\xf90\xd6Zt\xd0C\x83\x1dkH[\xbd\xe4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb3\x919Wa\x94\xa0\x86a\x95\x15\x1f3\xf2\x14\n\xd1\u0306\u03ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb3\x9fL\x00\xb2c\f\xab}\xb7)^\xf4=G\xd5\x01\xe1\u007f\u05c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb3\xa6K\x11vrOT\t\xe1AJ5#f\x1b\xae\xe7KJ\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xb3\xa6\xbdA\xf9\xd9\xc3 \x1e\x05\v\x87\x19\x8f\xbd\xa3\x994\"\x10\x89\xc4a\xe1\xdd\x10)\xb5\x80\x00\u07d4\xb3\xa8\xc2\xcb}5\x8eW9\x94\x1d\x94[\xa9\x04Z\x02:\x8b\xbb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xaeT\xfb\xa0\x9d>\xe1\u05bd\xd1\xe9W\x929\x19\x02L5\xfa\x89\x03\x8d,\xeee\xb2*\x80\x00\u07d4\xb3\xb7\xf4\x93\xb4J,\x8d\x80\xecx\xb1\xcd\xc7Ze+s\xb0l\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb3\xc2(s\x1d\x18m-\xed[_\xbe\x00Lfl\x8eF\x9b\x86\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4\xb3\xc2``\x9b\x9d\xf4\t^l]\xff9\x8e\xeb^-\xf4\x99\x85\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xb3\xc6[\x84Z\xbal\xd8\x16\xfb\xaa\xe9\x83\xe0\xe4l\x82\xaa\x86\"\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xc9H\x11\xe7\x17[\x14\x8b(\x1c\x1a\x84[\xfc\x9b\xb6\xfb\xc1\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3\xe2\x0e\xb4\xde\x18\xbd\x06\x02!h\x98\x94\xbe\u5bb2SQ\xee\x89\x03\xfc\x80\xcc\xe5\x16Y\x80\x00\u07d4\xb3\xe3\xc49\x06\x98\x80\x15f\x00\u0089.D\x8dA6\xc9-\x9b\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xb3\xf8*\x87\xe5\x9a9\xd0\u0480\x8f\aQ\xebr\xc22\x9c\xdc\u014a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb3\xfc\x1dh\x81\xab\xfc\xb8\xbe\xcc\v\xb0!\xb8\xb7;r3\u0751\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xb4\x05\x94\xc4\xf3fN\xf8I\u0326\"{\x8a%\xaai\t%\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb4\x1e\xaf]Q\xa5\xba\x1b\xa3\x9b\xb4\x18\u06f5O\xabu\x0e\xfb\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4$\u058d\x9d\r\x00\xce\xc1\x93\x8c\x85N\x15\xff\xb8\x80\xba\x01p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4%bs\x96+\xf61\xd0\x14U\\\xc1\xda\r\xcc1akI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb40g\xfep\u0675Ys\xbaX\xdcd\xdd\u007f1\x1eUBY\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb46W\xa5\x0e\xec\xbc0w\xe0\x05\xd8\xf8\xd9O7xv\xba\u0509\x01\xec\x1b:\x1f\xf7Z\x00\x00\u07d4\xb4<'\xf7\xa0\xa1\"\bK\x98\xf4\x83\x92%A\u0203l\xee,\x89&\u009eG\u0104L\x00\x00\xe0\x94\xb4A5v\x86\x9c\b\xf9Q*\xd3\x11\xfe\x92Y\x88\xa5-4\x14\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb4F\x05U$q\xa6\xee\xe4\u06abq\xff;\xb4\x13&\xd4s\xe0\x89-~=Q\xbaS\xd0\x00\x00\u07d4\xb4GW\x1d\xac\xbb>\u02f6\xd1\xcf\v\f\x8f88\xe5#$\xe2\x89\x01\xa3\x18f\u007f\xb4\x05\x80\x00\u07d4\xb4G\x83\xc8\xe5{H\a\x93\xcb\u059aE\xd9\f{O\fH\xac\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb4H\x15\xa0\xf2\x8eV\x9d\x0e\x92\x1aJ\u078f\xb2d%&Iz\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4\xb4Im\xdb'y\x9a\"$W\xd79y\x11g(\u8844[\x89\x8d\x81\x9e\xa6_\xa6/\x80\x00\xe0\x94\xb4RL\x95\xa7\x86\x0e!\x84\x02\x96\xa6\x16$@\x19B\x1cJ\xba\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb4\\\xca\r6\x82fbh<\xf7\u0432\xfd\xach\u007f\x02\xd0\u010965\u026d\xc5\u07a0\x00\x00\u0794\xb4d@\u01d7\xa5V\xe0L}\x91\x04f\x04\x91\xf9k\xb0v\xbf\x88\xce\xc7o\x0eqR\x00\x00\u07d4\xb4j\u0386^,P\xeaF\x98\xd2\x16\xabE]\xffZ\x11\xcdr\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4m\x11\x82\xe5\xaa\xca\xff\r&\xb2\xfc\xf7/<\x9f\xfb\xcd\xd9}\x89\xaa*`<\xdd\u007f,\x00\x00\u07d4\xb4\x89!\xc9h}U\x10tE\x84\x93n\x88\x86\xbd\xbf-\xf6\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\x98\xbb\x0fR\x00\x05\xb6!jD%\xb7Z\xa9\xad\xc5-b+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb4\xb1\x1d\x10\x9f`\x8f\xa8\xed\xd3\xfe\xa9\xf8\xc3\x15d\x9a\xeb=\x11\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb4\xb1K\xf4TU\u042b\b\x035\x8bu$\xa7+\xe1\xa2\x04[\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb4\xb1\x85\xd9C\xee+Xc\x1e3\xdf\xf5\xafhT\xc1y\x93\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xbf$\u02c3hk\xc4i\x86\x9f\xef\xb0D\xb9\tqi\x93\xe2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb4\xc2\x00@\xcc\u0661\xa3(=\xa4\u0522\xf3e\x82\bC\xd7\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xc8\x17\x0f{*\xb56\xd1\u0662[\xdd :\xe1(\x8d\xc3\u0549\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4\xd8/.i\x94?}\xe0\xf5\xf7t8y@o\xac.\x9c\xec\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb4\xddF\f\xd0\x16rZd\xb2.\xa4\xf8\xe0n\x06gN\x03>\x8a\x01#\x1b\xb8t\x85G\xa8\x00\x00\u07d4\xb4\xddT\x99\xda\xeb%\a\xfb-\xe1\"\x97s\x1dLr\xb1k\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb5\x04l\xb3\xdc\x1d\xed\xbd6E\x14\xa2\x84\x8eD\xc1\xdeN\xd1G\x8a\x03{}\x9b\xb8 @^\x00\x00\xe0\x94\xb5\b\xf9\x87\xb2\xde4\xaeL\xf1\x93\u0785\xbf\xf6\x13\x89b\x1f\x88\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb5\tU\xaan4\x15q\x98f\b\xbd\u0211\xc2\x13\x9fT\f\u07c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5\f\x14\x9a\x19\x06\xfa\xd2xo\xfb\x13Z\xabP\x177\xe9\xe5o\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\xb5\f\x9fW\x89\xaeD\xe2\xdc\xe0\x17\xc7\x14\xca\xf0\f\x83\x00\x84\u0089\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xb5\x14\x88,\x97\x9b\xb6B\xa8\r\u04c7T\u0578\xc8)m\x9a\a\x893\xc5I\x901r\f\x00\x00\u07d4\xb5\x1d\u0734\xddN\x8a\xe6\xbe3m\xd9eIq\xd9\xfe\xc8kA\x89\x16\xd4d\xf8=\u2500\x00\u07d4\xb5\x1eU\x8e\xb5Q/\xbc\xfa\x81\xf8\u043d\x93\x8cy\xeb\xb5$+\x89&\u009eG\u0104L\x00\x00\u07d4\xb5#\xff\xf9t\x98q\xb3S\x88C\x887\xf7\xe6\xe0\u07a9\xcbk\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb5-\xfbE\xde]t\xe3\xdf \x832\xbcW\x1c\x80\x9b\x8d\xcf2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb55\xf8\u06c7\x9f\xc6\u007f\xecX\x82J\\\xbenT\x98\xab\xa6\x92\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xb57\xd3jp\xee\xb8\xd3\xe5\xc8\r\xe8\x15\"\\\x11X\u02d2\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xb5;\xcb\x17L%\x184\x8b\x81\x8a\xec\xe0 6E\x96Fk\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5I>\xf1srDE\xcf4\\\x03]'\x9b\xa7Y\xf2\x8dQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb5S\xd2]kT!\xe8\x1c*\xd0^\v\x8b\xa7Q\xf8\xf0\x10\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5Tt\xbaX\xf0\xf2\xf4\x0el\xba\xbe\xd4\xea\x17n\x01\x1f\xca\u0589j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5U\xd0\x0f\x91\x90\xcc6w\xae\xf3\x14\xac\xd7?\xdc99\x92Y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5W\xab\x949\xefP\xd27\xb5S\xf0%\b6JFj\\\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5jx\x00(\x03\x9c\x81\xca\xf3{gu\xc6 \u7195Gd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5j\u04ae\xc6\xc8\xc3\xf1\x9e\x15\x15\xbb\xb7\u0751(RV\xb69\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5t\x13\x06\n\xf3\xf1N\xb4y\x06_\x1e\x9d\x19\xb3uz\xe8\u0309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xb5uI\xbf\xbc\x9b\xdd\x18\xf76\xb2&P\xe4\x8as`\x1f\xa6\\\x89\x18-~L\xfd\xa08\x00\x00\xe0\x94\xb5w\xb6\xbe\xfa\x05N\x9c\x04\x04a\x85P\x94\xb0\x02\xd7\xf5{\u05ca\x18#\xf3\xcfb\x1d#@\x00\x00\u07d4\xb5{\x04\xfa#\xd1 ?\xae\x06\x1e\xacEB\xcb`\xf3\xa5v7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xb5\x87\f\xe3B\xd43C36s\x03\x8bGd\xa4n\x92_>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\x87\xb4J,\xa7\x9eK\xc1\u074b\xfd\xd4: qP\xf2\xe7\xe0\x89\",\x8e\xb3\xfff@\x00\x00\u07d4\xb5\x89gm\x15\xa0DH4B0\xd4\xff'\xc9^\xdf\x12,I\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb5\x8bR\x86^\xa5]\x806\xf2\xfa\xb2`\x98\xb3R\u0283~\x18\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xb5\x90k\n\u9881X\xe8\xacU\x0e9\xda\bn\xe3\x15v#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xa4g\x96\x85\xfa\x14\x19l.\x920\xc8\xc4\xe3;\xff\xbc\x10\xe2\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb5\xa5\x89\u075f@q\u06f6\xfb\xa8\x9b?]]\xae}\x96\xc1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xa6\x06\xf4\xdd\u02f9G\x1e\xc6\u007fe\x8c\xaf+\x00\xees\x02^\x89\xeaun\xa9*\xfct\x00\x00\u07d4\xb5\xadQW\u0769!\xe6\xba\xfa\u0350\x86\xaes\xae\x1fa\x1d?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xad\xd1\u701f}\x03\x06\x9b\xfe\x88;\n\x93\"\x10\xbe\x87\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\xba)\x91|x\xa1\xd9\xe5\xc5\xc7\x13fl\x1eA\x1d\u007fi:\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb5\xc8\x16\xa8(<\xa4\xdfh\xa1\xa7=c\xbd\x80&\x04\x88\xdf\b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xca\xc5\xed\x03G}9\v\xb2g\xd4\xeb\xd4a\x01\xfb\xc2\xc3\u0689\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xb5\u037cA\x15@oR\u5a85\xd0\xfe\xa1p\u0497\x9c\u01fa\x89Hz\x9a0E9D\x00\x00\u0794\xb5\u0653M{)+\xcf`;(\x80t\x1e\xb7`(\x83\x83\xa0\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xb5\xddP\xa1]\xa3Ih\x89\nS\xb4\xf1?\xe1\xaf\b\x1b\xaa\xaa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb5\xfa\x81\x84\xe4>\xd3\u0e2b\x91!da\xb3R\x8d\x84\xfd\t\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb5\xfb~\xa2\xdd\xc1Y\x8bfz\x9dW\xdd9\xe8Z8\xf3]V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb6\x00B\x97R\xf3\x99\xc8\r\a4tK\xae\n\x02.\xcag\u0189\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x00\xfe\xabJ\xa9lSu\x04\xd9`W\"1Ai,\x19:\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb6\x04|\u07d3-\xb3\xe4\x04_Iv\x12#AS~\u0556\x1e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x15\xe9@\x14>\xb5\u007f\x87X\x93\xbc\x98\xa6\x1b=a\x8c\x1e\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb6\x1c4\xfc\xac\xdap\x1aZ\xa8p$Y\u07b0\u4b83\x8d\xf8\x8a\aiZ\x92\xc2\ro\xe0\x00\x00\xe0\x94\xb60d\xbd3U\xe6\xe0~-7p$\x12Z3wlJ\xfa\x8a\b7Z*\xbc\xca$@\x00\x00\u07d4\xb65\xa4\xbcq\xfb(\xfd\xd5\xd2\xc3\"\x98:V\u0084Bni\x89\t79SM(h\x00\x00\u07d4\xb6F\u07d8\xb4\x94BtkaR\\\x81\xa3\xb0K\xa3\x10bP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb6YA\xd4LP\xd2Ffg\r6Gf\xe9\x91\xc0.\x11\u0089 \x86\xac5\x10R`\x00\x00\xe0\x94\xb6[\u05c0\xc7CA\x15\x16 'VR#\xf4NT\x98\xff\x8c\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xb6d\x11\xe3\xa0-\xed\xb7&\xfay\x10}\xc9\v\xc1\xca\xe6MH\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb6fu\x14.1\x11\xa1\xc2\xea\x1e\xb2A\x9c\xfaB\xaa\xf7\xa24\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb6o\x92\x12K^c\x03XY\xe3\x90b\x88i\xdb\u07a9H^\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xb6rsJ\xfc\xc2$\xe2\xe6\t\xfcQ\xd4\xf0Ys'D\xc9H\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xb6w\x1b\v\xf3B\u007f\x9a\xe7\xa9>|.a\xeec\x94\x1f\xdb\b\x8a\x03\xfb&i)T\xbf\xc0\x00\x00\u07d4\xb6z\x80\xf1p\x19}\x96\xcd\xccJ\xb6\u02e6'\xb4\xaf\xa6\xe1,\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xb6\x88\x99\xe7a\rL\x93\xa255\xbc\xc4H\x94[\xa1fo\x1c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb6\xa8)3\xc9\xea\u06bd\x98\x1e]m`\xa6\x81\x8f\xf8\x06\xe3k\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb6\xaa\u02cc\xb3\v\xab*\xe4\xa2BF&\xe6\xe1+\x02\xd0F\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb6\xb3J&?\x10\xc3\xd2\xec\xeb\n\xccU\x9a{*\xb8\\\xe5e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xbf\xe1\xc3\xef\x94\xe1\x84o\xb9\xe3\xac\xfe\x9bP\xc3\xe9\x06\x923\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb6\xcdt2\xd5\x16\x1b\xe7\x97h\xadE\xde>Dz\a\x98 c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xceM\xc5`\xfcs\xdci\xfbzb\xe3\x88\xdb~r\xeavO\x894]\xf1i\xe9\xa3X\x00\x00\u07d4\xb6\xde\u03c2\x96\x98\x19\xba\x02\xde)\xb9\xb5\x93\xf2\x1bd\xee\xda\x0f\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\xe0\x94\xb6\xe6\xc3\"+ko\x9b\xe2\x87]*\x89\xf1'\xfbd\x10\x0f\xe2\x8a\x01\xb2\x1dS#\xcc0 \x00\x00\u07d4\xb6\xe8\xaf\xd9=\xfa\x9a\xf2\u007f9\xb4\xdf\x06\ag\x10\xbe\xe3\u07eb\x89\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xb6\xf7\x8d\xa4\xf4\xd0A\xb3\xbc\x14\xbc[\xa5\x19\xa5\xba\f2\xf1(\x8a$}\xd3,?\xe1\x95\x04\x80\x00\xe0\x94\xb6\xfb9xbP\b\x14&\xa3B\xc7\rG\xeeR\x1e[\xc5c\x8a\x03-&\xd1.\x98\v`\x00\x00\u07d4\xb7\r\xba\x93\x91h+J6Nw\xfe\x99%c\x01\xa6\xc0\xbf\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x16#\xf3Q\a\xcft1\xa8?\xb3\xd2\x04\xb2\x9e\u0c67\xf4\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xb7\x1a\x13\xba\x8e\x95\x16{\x803\x1bR\u059e7\x05O\xe7\xa8&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x1bb\xf4\xb4H\xc0+\x12\x01\xcb^9J\xe6'\xb0\xa5`\xee\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb7\" \xad\xe3d\xd06\x9f--\xa7\x83\xcaGM{\x9b4\u0389\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\xe0\x94\xb7#\r\x1d\x1f\xf2\xac\xa3f\x969\x14\xa7\x9d\xf9\xf7\xc5\xea,\x98\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xb7$\n\U000af433<\b\xae\x97d\x10>5\xdc\xe3c\x84(\x8a\x01\xca\xdd/\xe9hnc\x80\x00\u07d4\xb7'\xa9\xfc\x82\xe1\xcf\xfc\\\x17_\xa1HZ\x9b\xef\xa2\u037d\u04496'\xe8\xf7\x127<\x00\x00\u07d4\xb7,*\x01\x1c\r\xf5\x0f\xbbn(\xb2\n\xe1\xaa\xd2\x17\x88g\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb78-7\xdb\x03\x98\xacrA\f\xf9\x81=\xe9\xf8\xe1\uc36d\x8966\xc2^f\xec\xe7\x00\x00\u07d4\xb7;O\xf9\x9e\xb8\x8f\u061b\vmW\xa9\xbc3\x8e\x88o\xa0j\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xb7=jwU\x9c\x86\xcfet$)\x039K\xac\xf9n5p\x89\x04\xf1\xa7|\xcd;\xa0\x00\x00\u07d4\xb7Cr\xdb\xfa\x18\x1d\xc9$/9\xbf\x1d71\xdf\xfe+\xda\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7G\x9d\xabP\"\xc4\xd5\u06ea\xf8\xde\x17\x1bN\x95\x1d\u0464W\x89\x04V9\x18$O@\x00\x00\u07d4\xb7I\xb5N\x04\u0571\x9b\xdc\xed\xfb\x84\xdaw\x01\xabG\x8c'\xae\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb7N\xd2f`\x01\xc1c3\xcfz\xf5\x9eJ=H`6;\x9c\x89\n~\xbd^Cc\xa0\x00\x00\u07d4\xb7QI\xe1\x85\xf6\xe3\x92pWs\x90s\xa1\x82*\xe1\xcf\r\xf2\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xb7S\xa7_\x9e\xd1\v!d:\n=\xc0Qz\xc9k\x1a@h\x89\x15\xc8\x18[,\x1f\xf4\x00\x00\xe0\x94\xb7V\xadR\xf3\xbft\xa7\xd2LgG\x1e\b\x87Ci6PL\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb7Wn\x9d1M\xf4\x1e\xc5Pd\x94):\xfb\x1b\xd5\xd3\xf6]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb7X\x89o\x1b\xaa\x86O\x17\xeb\xed\x16\xd9S\x88o\xeeh\xaa\xe6\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb7h\xb5#N\xba:\x99h\xb3Mm\xdbH\x1c\x84\x19\xb3e]\x88\xcf\xceU\xaa\x12\xb3\x00\x00\u07d4\xb7\x82\xbf\xd1\xe2\xdep\xf4gdo\x9b\xc0\x9e\xa5\xb1\xfc\xf4P\xaf\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xb7\xa2\xc1\x03r\x8bs\x05\xb5\xaen\x96\x1c\x94\xee\x99\xc9\xfe\x8e+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xb7\xa3\x1a|8\xf3\xdb\t2.\xae\x11\xd2'!A\xea\"\x99\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xa6y\x1c\x16\xebN!b\xf1Ke7\xa0+=c\xbf\xc6\x02\x89*Rc\x91\xac\x93v\x00\x00\u07d4\xb7\xa7\xf7|4\x8f\x92\xa9\xf1\x10\fk\xd8)\xa8\xacm\u007f\u03d1\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xb7\xc0w\x94ft\xba\x93A\xfbLtz]P\xf5\xd2\xdad\x15\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb7\xc0\xd0\xcc\vM4-@b\xba\xc6$\xcc\xc3\xc7\f\xc6\xda?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb7\xc9\xf1+\x03\x8esCm\x17\xe1\xc1/\xfe\x1a\xec\u0373\xf5\x8c\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xb7\xcck\x1a\xcc2\u0632\x95\xdfh\xed\x9d^`\xb8\xf6L\xb6{\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xb7\xcehK\t\xab\xdaS8\x9a\x87Si\xf7\x19X\xae\xac;\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xd1.\x84\xa2\xe4\u01264Z\xf1\xdd\x1d\xa9\xf2PJ*\x99n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\xd2R\xee\x94\x02\xb0\xee\xf1D)_\x0ei\xf0\xdbXl\bq\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\xb7\u0541\xfe\n\xf1\xec8?;\xce\x00\xaf\x91\x99\xf3\xcf_\xe0\xcc\xe2\x8c\xd1J\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xb8R\x18\xf3B\xf8\x01.\u069f'Nc\xce!R\xb2\xdc\xfd\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb8UP\x10wn<\\\xb3\x11\xa5\xad\xee\xfe\x9e\x92\xbb\x9ad\xb9\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb8_&\xdd\x0er\xd9\u009e\xba\xf6\x97\xa8\xafwG,+X\xb5\x8a\x02\x85\x19\xac\xc7\x19\fp\x00\x00\u07d4\xb8_\xf0>{_\xc4\"\x98\x1f\xae^\x99A\xda\xcb\u06bau\x84\x89Hz\x9a0E9D\x00\x00\xe0\x94\xb8f\a\x02\x1bb\xd3@\xcf&R\xf3\xf9_\xd2\xdcgi\x8b\u07ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb8}\xe1\xbc\u0492i\xd5!\xb8v\x1c\u00dc\xfbC\x19\xd2\xea\u054965\u026d\xc5\u07a0\x00\x00\u07d4\xb8\u007fSv\xc2\xde\vl\xc3\xc1y\xc0`\x87\xaaG=kFt\x89Hz\x9a0E9D\x00\x00\u07d4\xb8\x84\xad\u060d\x83\xdcVJ\xb8\xe0\xe0,\xbd\xb69\x19\xae\xa8D\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x8a7\xc2\u007fx\xa6\x17\xd5\xc0\x91\xb7\u0577:7a\xe6_*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x94x\"\u056c\u79ad\x83&\xe9T\x96\"\x1e\v\xe6\xb7=\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\x9c\x03n\xd7\u0112\x87\x99!\xbeA\xe1\f\xa1i\x81\x98\xa7L\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xb8\x9fF2\xdfY\t\xe5\x8b*\x99d\xf7O\xeb\x9a;\x01\xe0\u014a\x04\x88u\xbc\xc6\xe7\xcb\xeb\x80\x00\u07d4\xb8\xa7\x9c\x84\x94^G\xa9\xc3C\x86\x83\u05b5\x84,\xffv\x84\xb1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\xa9y5'Y\xba\t\xe3Z\xa5\x93]\xf1u\xbf\xf6x\xa1\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\xab9\x80[\xd8!\x18Ol\xbd=$s4{\x12\xbf\x17\\\x89\x06hZ\xc1\xbf\xe3,\x00\x00\xe0\x94\xb8\xac\x11}\x9f\r\xba\x80\x90\x14E\x82:\x92\x11\x03\xa51o\x85Zew\x9d\x1b\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xb9\xe9\f\x11\x92\xb3\xd5\xd3\xe3\xab\a\x00\xf1\xbfe_]\xd44z\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4\xb9\xfd83\xe8\x8e|\xf1\xfa\x98y\xbd\xf5Z\xf4\xb9\x9c\xd5\xce?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xba\x02I\xe0\x1d\x94[\xef\x93\xee^\xc6\x19%\xe0<\\\xa5\t\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\x0f9\x02;\xdb)\xeb\x18b\xa9\xf9\x05\x9c\xab]0nf/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x10\xf2vB\x90\xf8uCCr\xf7\x9d\xbfq8\x01\u02ac\x01\x893\xc5I\x901r\f\x00\x00\u07d4\xba\x151\xfb\x9ey\x18\x96\xbc\xf3\xa8\x05X\xa3Y\xf6\xe7\xc1D\xbd\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xba\x17m\xbe2I\xe3E\xcdO\xa9g\xc0\xed\x13\xb2LG\u5189\x15\xae\xf9\xf1\xc3\x1c\u007f\x00\x00\xe0\x94\xba\x1f\x0e\x03\u02da\xa0!\xf4\xdc\xeb\xfa\x94\xe5\u0209\xc9\u01fc\x9e\x8a\x06\u0450\xc4u\x16\x9a \x00\x00\u07d4\xba\x1f\xca\xf2#\x93~\xf8\x9e\x85gU\x03\xbd\xb7\xcaj\x92\x8bx\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\xe0\x94\xba$\xfcCgS\xa79\xdb,\x8d@\xe6\xd4\xd0LR\x8e\x86\xfa\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xbaB\xf9\xaa\xceL\x18E\x04\xab\xf5BWb\xac\xa2oq\xfb\u0709\x02\a\a}\u0627\x9c\x00\x00\u07d4\xbaF\x9a\xa5\u00c6\xb1\x92\x95\u0521\xb5G;T\x03S9\f\x85\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbad@\xae\xb3s{\x8e\xf0\xf1\xaf\x9b\f\x15\xf4\xc2\x14\xff\xc7\u03c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbam1\xb9\xa2a\xd6@\xb5\u07a5\x1e\xf2\x16,1\t\xf1\uba0a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xbap\xe8\xb4u\x9c\f<\x82\xcc\x00\xacN\x9a\x94\xdd[\xaf\xb2\xb8\x890C\xfa3\xc4\x12\xd7\x00\x00\u07d4\xba\x8ac\xf3\xf4\r\u4a03\x88\xbcP!/\xea\x8e\x06O\xbb\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x8eF\u059d.#C\xd8l`\xd8,\xf4, A\xa0\xc1\u0089\x05k\xc7^-c\x10\x00\x00\u07d4\xba\xa4\xb6L+\x15\xb7\x9f_ BF\xfdp\xbc\xbd\x86\xe4\xa9*\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xba\u0212,J\xcc},\xb6\xfdY\xa1N\xb4\\\xf3\xe7\x02!K\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xba\xd25\xd5\b]\u01f0h\xa6|A&w\xb0>\x186\x88L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\xd4B^\x17\x1c>r\x97^\xb4j\xc0\xa0\x15\xdb1Z]\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xba\xdc*\xef\x9fYQ\xa8\u05cak5\xc3\u0433\xa4\xe6\xe2\xe79\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xba\xdeCY\x9e\x02\xf8OL0\x14W\x1c\x97k\x13\xa3le\xab\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\xe9\xb8/r\x99c\x14\be\x9d\xd7N\x89\x1c\xb8\xf3\x86\x0f\xe5\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xbb\x03f\xa7\u03fd4E\xa7\r\xb7\xfeZ\xe3H\x85uO\xd4h\x8a\x01M\xef,B\xeb\xd6@\x00\x00\u07d4\xbb\aj\xac\x92 \x80i\xea1\x8a1\xff\x8e\xeb\x14\xb7\xe9\x96\xe3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4\xbb\bW\xf1\xc9\x11\xb2K\x86\u0227\x06\x81G?\u6aa1\xcc\xe2\x89\x05k\xc7^-c\x10\x00\x00\u0794\xbb\x19\xbf\x91\u02edt\xcc\xeb_\x81\x1d\xb2~A\x1b\xc2\xea\x06V\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xbb'\u01a7\xf9\x10uGZ\xb2)a\x90@\xf8\x04\xc8\xeczj\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xbb7\x1cr\xc9\xf01l\xea+\xd9\xc6\xfb\xb4\a\x9ewT)\xef\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94\xbb;\x01\v\x18\xe6\xe2\xbe\x115\x87\x10&\xb7\xba\x15\xea\x0f\xde$\x8a\x02 |\x800\x9bwp\x00\x00\xe0\x94\xbb;\x90\x05\xf4o\xd2\xca;0\x16%\x99\x92\x8cw\xd9\xf6\xb6\x01\x8a\x01\xb1\xae\u007f+\x1b\xf7\xdb\x00\x00\u07d4\xbb?\xc0\xa2\x9c\x03Mq\b\x12\xdc\xc7u\xc8\u02b9\u048diu\x899\xd4\xe8D\xd1\xcf_\x00\x00\u07d4\xbbH\xea\xf5\x16\xce-\xec>A\xfe\xb4\xc6y\xe4\x95vA\x16O\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xbbKJKT\x80p\xffAC,\x9e\b\xa0\xcao\xa7\xbc\x9fv\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xbbV\xa4\x04r<\xff \xd0hT\x88\xb0Z\x02\xcd\xc3Z\xac\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbba\x8e%\"\x1a\u0667@\xb2\x99\xed\x14\x06\xbc94\xb0\xb1m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbba\xa0K\xff\xd5|\x10G\rE\u00d1\x03\xf6FP4v\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbbh#\xa1\xbd\x81\x9f\x13QU8&J-\xe0R\xb4D\"\b\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xbbl(J\xac\x8ai\xb7\\\u0770\x0f(\xe1EX;V\xbe\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xbbu\xcbPQ\xa0\xb0\x94KFs\xcau*\x97\x03\u007f|\x8c\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbb\x99;\x96\xee\x92Z\xda}\x99\u05c6W=?\x89\x18\f\u3a89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa3\u0180\x04$\x8eH\x95s\xab\xb2t6w\x06k$\u0227\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa4\xfa\xc3\xc4 9\xd8(\xe7B\xcd\xe0\xef\xff\xe7t\x94\x1b9\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\xbb\xa8\xab\"\xd2\xfe\xdb\xcf\xc6?hL\b\xaf\xdf\x1c\x17P\x90\xb5\x89\x05_)\xf3~N;\x80\x00\u07d4\xbb\xa9v\xf1\xa1!_u\x12\x87\x18\x92\xd4_pH\xac\xd3V\u0209lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbb\xab\x00\v\x04\b\xed\x01Z7\xc0GG\xbcF\x1a\xb1N\x15\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xbb\xab\xf6d;\xebK\xd0\x1c\x12\v\xd0Y\x8a\t\x87\xd8)g\u0449\xb52\x81x\xad\x0f*\x00\x00\u07d4\xbb\xb4\xee\x1d\x82\xf2\xe1VD,\xc938\xa2\xfc(o\xa2\x88d\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\xbb\xb5\xa0\xf4\x80,\x86H\x00\x9e\x8ai\x98\xaf5,\u0787TO\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\xbb\xb6C\xd2\x18{6J\xfc\x10\xa6\xfd6\x8d}U\xf5\r\x1a<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbb\xb8\xff\xe4?\x98\u078e\xae\x18F#\xaeRd\xe4$\u0438\u05c9\x05\xd5?\xfd\xe9(\b\x00\x00\u07d4\xbb\xbdn\u02f5u(\x91\xb4\u03b3\xcc\xe7:\x8fGpY7o\x89\x01\xf3\x99\xb1C\x8a\x10\x00\x00\u07d4\xbb\xbf9\xb1\xb6y\x95\xa4\"APO\x97\x03\u04a1JQV\x96\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbb\xc8\xea\xffc~\x94\xfc\u014d\x91\xdb\\\x89\x12\x1d\x06\xe1/\xff\x98\x80\x00\u07d4\xbc\u065e\xdc!`\xf2\x10\xa0^:\x1f\xa0\xb0CL\xed\x00C\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbc\u07ec\xb9\xd9\x02<4\x17\x18.\x91\x00\xe8\xea\x1d73\x93\xa3\x89\x034-`\xdf\xf1\x96\x00\x00\u07d4\xbc\xe1>\"2*\u03f3U\xcd!\xfd\r\xf6\f\xf9:\xdd&\u0189\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xbc\xe4\x04u\xd3E\xb0q-\xeep=\x87\xcdvW\xfc\u007f;b\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\xbc\xed\xc4&|\u02c9\xb3\x1b\xb7d\xd7!\x11q\x00\x8d\x94\xd4M\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbc\xfc\x98\xe5\xc8+j\xdb\x18\n?\xcb\x12\v\x9av\x90\xc8j?\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\x04;g\xc6>`\xf8A\xcc\xca\x15\xb1)\xcd\xfee\x90\xc8\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd\x04\u007f\xf1\xe6\x9c\u01b2\x9a\xd2d\x97\xa9\xa6\xf2z\x90?\xc4\u0749.\xe4IU\b\x98\xe4\x00\x00\u07d4\xbd\b\xe0\xcd\xde\xc0\x97\xdby\x01\ua05a=\x1f\xd9\u0789Q\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbd\t\x12l\x89\x1cJ\x83\x06\x80Y\xfe\x0e\x15ylFa\xa9\xf4\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xbd\f\\\u05d9\xeb\u0106B\xef\x97\xd7N\x8eB\x90d\xfe\u4489\x11\xac(\xa8\xc7)X\x00\x00\u07d4\xbd\x17\xee\xd8+\x9a%\x92\x01\x9a\x1b\x1b<\x0f\xba\xd4\\@\x8d\"\x89\r\x8drkqw\xa8\x00\x00\u07d4\xbd\x18\x037\v\u0771)\xd29\xfd\x16\xea\x85&\xa6\x18\x8a\u5389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbd+p\xfe\xcc7d\x0fiQO\xc7\xf3@IF\xaa\xd8k\x11\x89A\rXj \xa4\xc0\x00\x00\u07d4\xbd0\x97\xa7\x9b<\r.\xbf\xf0\xe6\xe8j\xb0\xed\xad\xbe\xd4p\x96\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xbd2]@)\xe0\xd8r\x9fm9\x9cG\x82$\xae\x9ez\xe4\x1e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xbdC*9\x16$\x9bG$):\xf9\x14nI\xb8(\n\u007f*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbdG\xf5\xf7n;\x93\x0f\xd9HR\t\xef\xa0\xd4v=\xa0uh\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbdK`\xfa\xect\n!\xe3\a\x13\x91\xf9j\xa54\xf7\xc1\xf4N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xbdK\u0571\"\xd8\xef{|\x8f\x06gE\x03 \xdb!\x16\x14.\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbdQ\xee.\xa1C\u05f1\u05b7~~D\xbd\xd7\xda\x12\U00105b09G~\x06\u0332\xb9(\x00\x00\u07d4\xbdY\tN\aO\x8dy\x14*\xb1H\x9f\x14\x8e2\x15\x1f \x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbdZ\x8c\x94\xbd\x8b\xe6G\x06D\xf7\f\x8f\x8a3\xa8\xa5\\cA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd^G:\xbc\xe8\xf9zi2\xf7|/\xac\xaf\x9c\xc0\xa0\x05\x14\x89<\x92X\xa1\x06\xa6\xb7\x00\x00\u07d4\xbd_F\u02ab,=K(\x93\x96\xbb\xb0\u007f *\x06\x11>\xd4\xc3\xfb\xa1\xa8\x91;\x19@~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\x9eV\xe9\x02\xf4\xbe\x1f\xc8v\x8d\x808\xba\xc6>*\u02ff\x8e\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xbd\xa4\xbe1~~K\xed\x84\xc0I^\xee2\xd6\a\xec8\xcaR\x89}2'yx\xefN\x80\x00\u07d4\xbd\xb6\v\x82:\x11s\xd4Z\a\x92$_\xb4\x96\xf1\xfd3\x01\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xba\xf6CM@\xd65[\x1e\x80\xe4\f\u012b\x9ch\xd9a\x16\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbd\xc0,\xd43\f\x93\xd6\xfb\xdaOm\xb2\xa8]\xf2/C\xc23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc4aF+c\"\xb4b\xbd\xb3?\"y\x9e\x81\b\xe2A}\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xbd\xc79\xa6\x99p\v.\x8e,JL{\x05\x8a\x0eQ=\u07be\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc7Hs\xaf\x92+\x9d\xf4t\x85;\x0f\xa7\xff\v\xf8\xc8&\x95\x89\xd8\xc9F\x00c\xd3\x1c\x00\x00\u07d4\xbd\xca*\x0f\xf3E\x88\xafb_\xa8\xe2\x8f\xc3\x01Z\xb5\xa3\xaa\x00\x89~\xd7?w5R\xfc\x00\x00\u07d4\xbd\xd3%N\x1b:m\xc6\xcc,i}Eq\x1a\xca!\xd5\x16\xb2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbd\u07e3M\x0e\xbf\x1b\x04\xafS\xb9\x9b\x82IJ\x9e=\x8a\xa1\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xbd\xe4\xc7?\x96\x9b\x89\xe9\u03aef\xa2\xb5\x18DH\x0e\x03\x8e\x9a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbd\xe9xj\x84\xe7[H\xf1\x8erm\u05cdp\xe4\xaf>\xd8\x02\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xbd\xed\x11a/\xb5\xc6\u0699\xd1\xe3\x0e2\v\xc0\x99Tf\x14\x1e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xbd\xed~\a\xd0q\x1ehM\xe6Z\u0232\xabW\xc5\\\x1a\x86E\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xbd\xf6\x93\xf83\xc3\xfeG\x17S\x18G\x88\xebK\xfeJ\xdc?\x96\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\xf6\xe6\x8c\f\xd7X@\x80\xe8G\xd7,\xbb#\xaa\xd4j\xeb\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbe\n/8_\t\xdb\xfc\xe9g2\xe1+\xb4\n\xc3I\x87\x1b\xa8\x89WL\x11^\x02\xb8\xbe\x00\x00\u07d4\xbe\f*\x80\xb9\xde\bK\x17(\x94\xa7l\xf4szOR\x9e\x1a\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\x1c\xd7\xf4\xc4r\a\th\xf3\xbd\xe2h6k!\xee\xea\x83!\x89\xe9\x1a|\u045f\xa3\xb0\x00\x00\u07d4\xbe#F\xa2\u007f\xf9\xb7\x02\x04OP\r\xef\xf2\xe7\xff\xe6\x82EA\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbe$q\xa6\u007f`G\x91\x87r\xd0\xe3h9%^\xd9\u0591\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbe+\"\x80R7h\xea\x8a\xc3\\\xd9\xe8\x88\xd6\nq\x93\x00\u0509lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe+2nx\xed\x10\xe5P\xfe\xe8\xef\xa8\xf8\a\x03\x96R/Z\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\xe0\x94\xbe0Zyn3\xbb\xf7\xf9\xae\xaee\x12\x95\x90f\xef\xda\x10\x10\x8a\x02M\xceT\xd3J\x1a\x00\x00\x00\u07d4\xbeG\x8e\x8e=\xdek\xd4\x03\xbb-\x1ce|C\x10\xee\x19'#\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\xbeN}\x98?.*ck\x11\x02\xecp9\xef\xeb\xc8B\u9349\x03\x93\xef\x1aQ'\xc8\x00\x00\u07d4\xbeO\xd0sap\"\xb6\u007f\\\x13I\x9b\x82\u007fv69\xe4\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbeRZ3\xea\x91aw\xf1r\x83\xfc\xa2\x9e\x8b5\v\u007fS\v\x89\x8f\x01\x9a\xafF\xe8x\x00\x00\u07d4\xbeS2/C\xfb\xb5\x84\x94\xd7\xcc\xe1\x9d\xda'+$P\xe8'\x89\n\xd7\u03afB\\\x15\x00\x00\u07d4\xbeS\x82F\xddNo\f \xbfZ\xd17<;F:\x13\x1e\x86\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbeZ`h\x99\x98c\x9a\xd7[\xc1\x05\xa3qt>\xef\x0fy@\x89\x1b2|s\xe1%z\x00\x00\u07d4\xbe\\\xba\x8d7By\x86\xe8\xca&\x00\xe8X\xbb\x03\xc3YR\x0f\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xbe`\x03~\x90qJK\x91~a\xf1\x93\xd84\x90g\x03\xb1:\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbec:77\xf6\x849\xba\xc7\xc9\nR\x14 X\ue38ao\x894\n\xad!\xb3\xb7\x00\x00\x00\xe0\x94\xbee\x9d\x85\xe7\xc3O\x883\xea\u007fH\x8d\xe1\xfb\xb5\xd4\x14\x9b\xef\x8a\x01\xeb\xd2:\xd9\u057br\x00\x00\u07d4\xbes'M\x8cZ\xa4J<\xbe\xfc\x82c\xc3{\xa1!\xb2\n\u04c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbe\x86\u0430C\x84\x19\u03b1\xa081\x927\xbaR\x06\xd7.F\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xbe\x8d\u007f\x18\xad\xfe]l\xc7u9I\x89\xe1\x93\f\x97\x9d\x00}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\x91\x86\xc3JRQJ\xbb\x91\a\x86\x0fgO\x97\xb8!\xbd[\x89\x1b\xa0\x1e\xe4\x06\x03\x10\x00\x00\u07d4\xbe\x93W\x93\xf4[p\xd8\x04]&T\xd8\xdd:\xd2K[a7\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xbe\x98\xa7\u007f\xd4\x10\x97\xb3OY\xd7X\x9b\xaa\xd0!e\x9f\xf7\x12\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xbe\x9b\x8c4\xb7\x8e\xe9G\xff\x81G.\xdaz\xf9\xd2\x04\xbc\x84f\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\xbe\xa0\r\xf1pg\xa4:\x82\xbc\x1d\xae\xca\xfbl\x140\x0e\x89\xe6\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xbe\xa0\xaf\xc9:\xae!\b\xa3\xfa\xc0Yb;\xf8o\xa5\x82\xa7^\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbe\xb35\x8cP\u03dfu\xff\xc7mD<,\u007fU\aZ\x05\x89\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xbe\xb4\xfd1UYC`E\u0739\x9dI\xdc\xec\x03\xf4\fB\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xc2\xe6\xde9\xc0|+\xaeUj\u03fe\xe2\xc4r\x8b\x99\x82\xe3\x89\x1f\x0f\xf8\xf0\x1d\xaa\xd4\x00\x00\u07d4\xbe\xc6d\x0fI\t\xb5\x8c\xbf\x1e\x80cB\x96\x1d`u\x95\tl\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\xc8\xca\xf7\xeeIF\x8f\xeeU.\xff:\xc5#N\xb9\xb1}B\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xce\xf6\x1c\x1cD+\xef|\xe0Ks\xad\xb2I\xa8\xba\x04~\x00\x896;V\u00e7T\xc8\x00\x00\u0794\xbe\xd4d\x9d\xf6F\u2052)\x03-\x88hUo\xe1\xe0S\u04c8\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xbe\xd4\xc8\xf0\x06\xa2|\x1e_|\xe2\x05\xdeu\xf5\x16\xbf\xb9\xf7d\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xbe\xe8\u0430\bB\x19T\xf9-\x00\r9\x0f\xb8\xf8\xe6X\xea\xee\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\xec\u05af\x90\f\x8b\x06J\xfc\xc6\a?-\x85\u055a\xf1\x19V\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe\xef\x94!8y\xe0&\"\x14+\xeaa)\tx\x93\x9a`\u05ca\x016\x85{2\xad\x86\x04\x80\x00\xe0\x94\xbe\xf0}\x97\xc3H\x1f\x9dj\xee\x1c\x98\xf9\xd9\x1a\x18\n2D+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xbe\xfbD\x8c\f_h?\xb6~\xe5p\xba\xf0\xdbV\x86Y\x97Q\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbf\x05\a\f,4!\x93\x11\xc4T\x8b&\x14\xa48\x81\r\xedm\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbf\x05\xff^\xcf\r\xf2\u07c8wY\xfb\x82t\xd928\xac&}\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\xbf\t\xd7pH\xe2p\xb6b3\x0e\x94\x86\xb3\x8bC\xcdx\x14\x95\x8a\\S\x9b{\xf4\xff(\x80\x00\x00\u07d4\xbf\x17\xf3\x97\xf8\xf4o\x1b\xaeE\u0447\x14\x8c\x06\xee\xb9Y\xfaM\x896I\u0156$\xbb0\x00\x00\u07d4\xbf\x186A\xed\xb8\x86\xce`\xb8\x19\x02a\xe1OB\xd9<\xce\x01\x89\x01[5W\xf1\x93\u007f\x80\x00\u07d4\xbf*\xeaZ\x1d\xcfn\u04f5\xe829D\xe9\x83\xfe\xdf\u046c\xfb\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbf@\x96\xbcT}\xbf\xc4\xe7H\t\xa3\x1c\x03\x9e{8\x9d^\x17\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xbfI\xc1H\x981eg\u0637\t\xc2\xe5\x05\x94\xb3f\xc6\u04cc\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4\xbfLs\xa7\xed\xe7\xb1d\xfe\a!\x14\x846T\xe4\xd8x\x1d\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfP\xce.&K\x9f\xe2\xb0h0az\xed\xf5\x02\xb25\x1bE\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfY\xae\xe2\x81\xfaC\xfe\x97\x19CQ\xa9\x85~\x01\xa3\xb8\x97\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbfh\u048a\xaf\x1e\xee\xfe\xf6F\xb6^\x8c\xc8\u0450\xf6\xc6\u069c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfi%\xc0\aQ\x00\x84@\xa6s\x9a\x02\xbf+l\u06ab^:\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfw\x01\xfcb%\u0561x\x15C\x8a\x89A\xd2\x1e\xbc]\x05\x9d\x89e\xea=\xb7UF`\x00\x00\u07d4\xbf\x8b\x80\x05\xd66\xa4\x96d\xf7Bu\xefBC\x8a\xcde\xac\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\x92A\x8a\fl1$M\"\x02`\xcb>\x86}\u05f4\xefI\x89\x05i\x00\xd3<\xa7\xfc\x00\x00\u07d4\xbf\x9a\xcdDE\xd9\xc9UF\x89\u02bb\xba\xb1\x88\x00\xff\x17A\u008965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\x9f'\x1fz~\x12\xe3m\xd2\xfe\x9f\xac\xeb\xf3\x85\xfeaB\xbd\x89\x03f\xf8O{\xb7\x84\x00\x00\u07d4\xbf\xa8\xc8X\xdf\x10,\xb1$!\x00\x8b\n1\xc4\xc7\x19\n\xd5`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\xae\xb9\x10ga}\u03cbD\x17+\x02\xafaVt\x83]\xba\x89\b\xb5\x9e\x88H\x13\b\x80\x00\xe0\x94\xbf\xb0\xea\x02\xfe\xb6\x1d\xec\x9e\"\xa5\a\tY3\x02\x99\xc40r\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xbf\xbc\xa4\x18\xd3R\x9c\xb3\x93\b\x10b\x03*n\x11\x83\u01b2\u070a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xbf\xbe\x05\u831c\xbb\xcc\x0e\x92\xa4\x05\xfa\xc1\xd8]\xe2H\xee$\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xbf\xbf\xbc\xb6V\u0099+\xe8\xfc\u0782\x19\xfb\xc5J\xad\u055f)\x8a\x02\x1e\x18\xd2\xc8!\xc7R\x00\x00\u07d4\xbf\xc5z\xa6f\xfa\u239f\x10zI\xcbP\x89\xa4\xe2!Q\u074965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\u02d70$c\x04p\r\xa9\vAS\xe7\x11Ab.\x1cA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbf\xd9<\x90\u009c\a\xbc_\xb5\xfcI\xae\xeaU\xa4\x0e\x13O5\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\xe0\x94\xbf\xe3\xa1\xfcn$\xc8\xf7\xb3%\x05`\x99\x1f\x93\u02e2\u03c0G\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xbf\u6f30\xf0\xc0xRd3$\xaa]\xf5\xfdb%\xab\xc3\u0289\x04\t\xe5+H6\x9a\x00\x00\u07d4\xbf\xf5\xdfv\x994\xb8\x94<\xa9\x13}\x0e\xfe\xf2\xfen\xbb\xb3N\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbf\xfbi)$\x1fx\x86\x93'>p\"\xe6\x0e>\xab\x1f\xe8O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x06O\x1d\x94t\xab\x91]V\x90l\x9f\xb3 \xa2\xc7\t\x8c\x9b\x89\x13h?\u007f<\x15\xd8\x00\x00\u07d4\xc0\a\xf0\xbd\xb6\xe7\x00\x92\x02\xb7\xaf>\xa9\t\x02i|r\x14\x13\x89\xa2\xa0\xe4>\u007f\xb9\x83\x00\x00\u07d4\xc0\n\xb0\x80\xb6C\xe1\u00ba\xe3c\xe0\u0455\xde.\xff\xfc\x1cD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\xc0 wD\x9a\x13Jz\xd1\xef~M\x92z\xff\xec\ueb75\xae\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc0$q\xe3\xfc.\xa0S&\x15\xa7W\x1dI2\x89\xc1<6\xef\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0-n\xad\xea\xcf\x1bx\xb3\u0285\x03\\c{\xb1\xce\x01\xf4\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc03\xb12Z\n\xf4Tr\xc2U'\x85;\x1f\x1c!\xfa5\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc03\xbe\x10\xcbHa;\xd5\xeb\xcb3\xedI\x02\xf3\x8bX0\x03\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc04[3\xf4\x9c\xe2\u007f\xe8,\xf7\xc8M\x14\x1ch\xf5\x90\xcev\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0=\xe4*\x10\x9bezd\xe9\"$\xc0\x8d\xc1'^\x80\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0@i\u07f1\x8b\tlxg\xf8\xbe\xe7zm\xc7Gz\xd0b\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xc0A?Z|-\x9aK\x81\b(\x9e\xf6\xec\xd2qx\x15$\xf4\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xc0C\xf2E-\u02d6\x02\xefb\xbd6\x0e\x03=\xd29q\xfe\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0OK\xd4\x04\x9f\x04F\x85\xb8\x83\xb6)Y\xaec\x1df~5\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0V\u053dk\xf3\u02ec\xace\xf8\xf5\xa0\xe3\x98\v\x85'@\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0[t\x06 \xf1s\xf1nRG\x1d\u00cb\x9cQJ\v\x15&\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\xc0i\xef\x0e\xb3B\x99\xab\xd2\xe3-\xab\xc4yD\xb2r3H$\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xc0l\xeb\xbb\xf7\xf5\x14\x9af\xf7\xeb\x97k>G\xd5e\x16\xda/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0r^\u00bd\xc3:\x1d\x82`q\u07a2\x9db\xd48Z\x8c%\x8a\b\xa0\x85\x13F:\xa6\x10\x00\x00\u07d4\xc0~8g\xad\xa0\x96\x80z\x05\x1al\x9c4\xcc;?J\xd3J\x89`\xf0f \xa8IE\x00\x00\u07d4\xc0\x89^\xfd\x05m\x9a:\x81\xc3\xdaW\x8a\xda1\x1b\xfb\x93V\u03c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x90\xfe#\xdc\xd8k5\x8c2\xe4\x8d*\xf9\x10$%\x9fef\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x9af\x17*\xea7\r\x9ac\xda\x04\xffq\xff\xbb\xfc\xff\u007f\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x9e<\xfc\x19\xf6\x05\xff>\xc9\xc9\xc7\x0e%@\xd7\xee\x97Cf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc0\xa0*\xb9N\xbeV\xd0E\xb4\x1bb\x9b\x98F.:\x02J\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0\xa3\x93\b\xa8\x0e\x9e\x84\xaa\xaf\x16\xac\x01\xe3\xb0\x1dt\xbdk-\x89\afM\xddL\x1c\v\x80\x00\u07d4\xc0\xa6\u02edwi*=\x88\xd1A\xefv\x9a\x99\xbb\x9e<\x99Q\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xc0\xa7\xe8C]\xff\x14\xc2Uws\x9d\xb5\\$\u057fW\xa3\u064a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xc0\xae\x14\xd7$\x83./\xce'x\xde\u007f{\x8d\xaf{\x12\xa9>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0\xaf\xb7\u0637\x93p\xcf\xd6c\u018c\u01b9p*7\u035e\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0\xb0\xb7\xa8\xa6\xe1\xac\xdd\x05\xe4\u007f\x94\xc0\x96\x88\xaa\x16\u01ed\x8d\x89\x03{m\x02\xacvq\x00\x00\xe0\x94\xc0\xb3\xf2D\xbc\xa7\xb7\xde[H\xa5>\u06dc\xbe\xab\vm\x88\xc0\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0\xc0M\x01\x06\x81\x0e>\xc0\xe5J\x19\U000ab157\xe6\x9aW=\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xc0\xca2w\x94.tE\x87K\xe3\x1c\xeb\x90)rqO\x18#\x89\r\x8drkqw\xa8\x00\x00\u07d4\xc0\u02ed<\xcd\xf6T\xda\"\xcb\xcf\\xe\x97\xca\x19U\xc1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\xcb\xf6\x03/\xa3\x9e|F\xffw\x8a\x94\xf7\xd4E\xfe\"\xcf0\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\xc0\xe0\xb9\x03\b\x8e\fc\xf5=\xd0iWTR\xaf\xf5$\x10\u00c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc0\xe4W\xbdV\xec6\xa1$k\xfa20\xff\xf3\x8eY&\xef\"\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xc0\xed\rJ\xd1\r\xe045\xb1S\xa0\xfc%\xde;\x93\xf4R\x04\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xc0\xf2\x9e\xd0\af\x11\xb5\xe5^\x13\x05G\xe6\x8aH\xe2m\xf5\u4262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc1\x13(x#\\]\u06e5\xd9\xf3\"\x8bR6\xe4p \xdco\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1\x17\r\xba\xad\xb3\xde\xe6\x19\x8e\xa5D\xba\xec\x93%\x18`\xfd\xa5\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\xc1&W=\x87\xb0\x17ZR\x95\xf1\xdd\a\xc5u\u03cc\xfa\x15\xf2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc1'\xaa\xb5\x90e\xa2\x86D\xa5k\xa3\xf1^.\xac\x13\xda)\x95\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xc1+\u007f@\u07da/{\xf9\x83f\x14\"\xab\x84\xc9\xc1\xf5\bX\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc1,\xfb{=\xf7\x0f\xce\xca\x0e\xde&5\x00\xe2xs\xf8\xed\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1/\x88\x1f\xa1\x12\xb8\x19\x9e\xcb\xc7>\xc4\x18W\x90\xe6\x14\xa2\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc18Lnq~\xbeK#\x01NQ\xf3\x1c\x9d\xf7\xe4\xe2[1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1C\x8c\x99\xddQ\xef\x1c\xa88j\xf0\xa3\x17\xe9\xb0AEx\x88\x89\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xc1c\x12(\xef\xbf*.:@\x92\xee\x89\x00\xc69\xed4\xfb\u02093\xc5I\x901r\f\x00\x00\u07d4\xc1u\xbe1\x94\xe6iB-\x15\xfe\xe8\x1e\xb9\xf2\xc5lg\xd9\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\x82v\x86\xc0\x16\x94\x85\xec\x15\xb3\xa7\xc8\xc0\x15\x17\xa2\x87M\xe1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xc1\x8a\xb4g\xfe\xb5\xa0\xaa\xdf\xff\x91#\x0f\xf0VFMx\xd8\x00\x89lk\x93[\x8b\xbd@\x00\x00\u0794\xc1\x95\x05CUM\x8aq0\x03\xf6b\xbba,\x10\xadL\xdf!\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc1\xa4\x1aZ'\x19\x92&\xe4\xc7\xeb\x19\x8b\x03\x1bY\x19o\x98B\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xc1\xb2\xa0\xfb\x9c\xadE\xcdi\x91\x92\xcd'T\v\x88\xd38By\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1\xb2\xaa\x8c\xb2\xbfb\xcd\xc1:G\xec\xc4e\u007f\xac\xaa\x99_\x98\x8967\x93\xfa\x96\u6980\x00\u07d4\xc1\xb5\x00\x01\x1c\xfb\xa9]|\xd66\xe9^l\xbfagFK%\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\xb9\xa5pM5\x1c\xfe\x98?y\xab\xee\xc3\u06fb\xae;\xb6)\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xcb\xd2\xe23*RL\xf2\x19\xb1\r\x87\x1c\xcc \xaf\x1f\xb0\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xc1\xcd\xc6\x01\xf8\x9c\x04(\xb3\x13\x02\u0447\xe0\xdc\b\xad}\x1cW\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xc1\u052f8\xe9\xbay\x90@\x89HI\xb8\xa8!\x93u\xf1\xacx\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc1\xe1@\x9c\xa5,%CQ4\xd0\x06\u00a6\xa8T-\xfbrs\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xc1\xeb\xa5hJ\xa1\xb2L\xbac\x15\x02c\xb7\xa9\x13\x1a\xee\u008d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xec\x81\xdd\x12=K|-\u0674\xd48\xa7\a,\x11\u0707L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc1\xf3\x9b\xd3]\xd9\xce\xc37\xb9oG\xc6w\x81\x81`\xdf7\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xc1\xff\xad\a\u06d6\x13\x8cK*S\x0e\xc1\xc7\xde)\xb8\xa0Y,\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xc2\x1f\xa6d:\x1f\x14\xc0)\x96\xadqD\xb7Y&\xe8~\xcbK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc24\nL\xa9L\x96x\xb7IL<\x85%(\xed\xe5\xeeR\x9f\x89\x02\xa3k\x05\xa3\xfd|\x80\x00\u07d4\xc29\xab\u07ee>\x9a\xf5E\u007fR\xed+\x91\xfd\n\xb4\xd9\xc7\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2;/\x92\x1c\xe4\xa3z%\x9e\u4b4b!X\xd1]fOY\x89\x01`\x89\x95\xe8\xbd?\x80\x00\u07d4\xc2C\x99\xb4\xbf\x86\xf73\x8f\xbfd^;\"\xb0\u0dd79\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2L\u03bc#D\xcc\xe5d\x17\xfbhL\xf8\x16\x13\xf0\xf4\xb9\xbd\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc2Rf\xc7gf2\xf1>\xf2\x9b\xe4U\ud50a\xddVw\x92\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\\\xf8&U\f\x8e\xaf\x10\xaf\"4\xfe\xf9\x04\u0779R\x13\xbe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc2f?\x81E\xdb\xfe\xc6\xc6F\xfc\\I\x96\x13E\xde\x1c\x9f\x11\x89%g\xacp9+\x88\x00\x00\u07d4\xc2pEh\x854+d\vL\xfc\x1bR\x0e\x1aTN\xe0\xd5q\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc2sv\xf4]!\xe1^\xde;&\xf2e_\xce\xe0,\xcc\x0f*\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc2w\x97q\xf0Smy\xa8p\x8fi1\xab\xc4K05\u964a\x047\u04ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc2\xc1>r\xd2h\xe7\x15\r\u01d9\xe7\xc6\xcf\x03\u0209T\xce\u05c9%\xf2s\x93=\xb5p\x00\x00\u07d4\xc2\xcb\x1a\xda]\xa9\xa0B8s\x81G\x93\xf1aD\xef6\xb2\xf3\x89HU~;p\x17\xdf\x00\x00\u07d4\xc2\xd1w\x8e\xf6\xee_\xe4\x88\xc1E\xf3Xkn\xbb\xe3\xfb\xb4E\x89>\x1f\xf1\xe0;U\xa8\x00\x00\xe0\x94\xc2\xd9\xee\xdb\xc9\x01\x92c\xd9\xd1l\u016e\a-\x1d=\xd9\xdb\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc2\xe0XJq4\x8c\xc3\x14\xb7; )\xb6#\v\x92\u06f1\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2\xe2\u0518\xf7\r\xcd\bY\xe5\v\x02:q\nmK!3\xbd\x8989\x11\xf0\f\xbc\xe1\x00\x00\u07d4\xc2\xed_\xfd\u046d\xd8U\xa2i/\xe0b\xb5\xd6\x18t#`\u0509A\rXj \xa4\xc0\x00\x00\u07d4\xc2\xee\x91\xd3\xefX\xc9\u0465\x89\x84N\xa1\xae1%\xd6\u017ai\x894\x95tD\xb8@\xe8\x00\x00\u07d4\xc2\xfa\xfd\xd3\n\xcbmg\x06\xe9)<\xb0&A\xf9\xed\xbe\a\xb5\x89Q\x00\x86\vC\x0fH\x00\x00\u07d4\xc2\xfd\v\xf7\xc7%\xef>\x04~Z\xe1\u009f\xe1\x8f\x12\xa7)\x9c\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\xfe}us\x1fcm\xcd\t\xdb\xda\x06q9;\xa0\xc8*}\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\xc3\x10z\x9a\xf32-R8\xdf\x012A\x911b\x959W}\x89\x1a\xb4\xe4d\xd4\x141\x00\x00\xe0\x94\xc3\x11\v\xe0\x1d\xc9sL\xfcn\x1c\xe0\u007f\x87\xd7}\x13E\xb7\xe1\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xc3 8\xcaR\xae\xe1\x97E\xbe\\1\xfc\xdcT\x14\x8b\xb2\xc4\u0409\x02\xb5\xaa\xd7,e \x00\x00\u07d4\xc3%\xc3R\x80\x1b\xa8\x83\xb3\"l_\xeb\r\xf9\xea\xe2\xd6\xe6S\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xc3.\xc7\xe4*\xd1l\xe3\xe2UZ\xd4\xc5C\x06\xed\xa0\xb2gX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc32\xdfP\xb1<\x014\x90\xa5\xd7\xc7]\xbf\xa3f\u0687\xb6\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc3:\u0373\xba\x1a\xab'P{\x86\xb1]g\xfa\xf9\x1e\xcfb\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3>\u0393Z\x8fN\xf98\xea~\x1b\xac\x87\u02d2]\x84\x90\u028a\a\x03\x8c\x16x\x1fxH\x00\x00\u07d4\xc3@\xf9\xb9\x1c&r\x8c1\xd1!\xd5\xd6\xfc;\xb5m=\x86$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3F\xcb\x1f\xbc\xe2\xab(]\x8eT\x01\xf4-\xd7#M7\xe8m\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\xe0\x94\xc3H=n\x88\xac\x1fJ\xe7<\xc4@\x8dl\x03\xab\xe0\xe4\x9d\u028a\x03\x99\x92d\x8a#\u0220\x00\x00\xe0\x94\xc3H\xfcZF\x13#\xb5{\xe3\x03\u02c96\x1b\x99\x19\x13\xdf(\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc3N;\xa12.\xd0W\x11\x83\xa2O\x94 N\xe4\x9c\x18fA\x89\x03'\xaf\uf927\xbc\x00\x00\xe0\x94\xc3[\x95\xa2\xa3s|\xb8\xf0\xf5\x96\xb3E$\x87+\xd3\r\xa24\x8a\x01\x98\xbe\x85#^-P\x00\x00\xe0\x94\xc3c\x1cv\x98\xb6\xc5\x11\x19\x89\xbfE''\xb3\xf99Zm\xea\x8a\x02C'X\x96d\x1d\xbe\x00\x00\u07d4\xc3l\vc\xbf\xd7\\/\x8e\xfb\x06\b\x83\xd8h\xcc\xcdl\xbd\xb4\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xc3uk\xcd\xcc~\xect\xed\x89j\xdf\xc35'Y0&n\b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u00c4\xacn\xe2|9\xe2\xf2x\xc2 \xbd\xfa[\xae\xd6&\xd9\u04c9 \x86\xac5\x10R`\x00\x00\u07d4\u00e0F\xe3\u04b2\xbfh\x14\x88\x82n2\xd9\xc0aQ\x8c\xfe\x8c\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\u00e9\"j\xe2u\xdf,\xab1+\x91\x10@cJ\x9c\x9c\x9e\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u00f9(\xa7o\xadex\xf0O\x05U\xe69R\xcd!\xd1R\n\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xc2)s)\xa6\xfd\x99\x11~T\xfcj\xf3y\xb4\xd5VT~\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc3\xc3\xc2Q\rg\x80 HZcs]\x13\a\xecL\xa60+\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc3\xcbk6\xafD?,n%\x8bJ9U:\x81\x87G\x81\x1f\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xc3\xdbVW\xbbr\xf1\rX\xf21\xfd\xdf\x11\x98\n\xffg\x86\x93\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xc3\u06df\xb6\xf4lH\n\xf3De\u05d7S\xb4\xe2\xb7Jg\u038a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc3\xddX\x908\x860;\x92\x86%%z\xe1\xa0\x13\xd7\x1a\xe2\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe0G\x1cd\xff5\xfaR2\xcc1!\xd1\u04cd\x1a\x0f\xb7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe2\f\x96\u07cdN8\xf5\v&Z\x98\xa9\x06\xd6\x1b\xc5\x1aq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\u31f0<\xe9\\\xcf\xd7\xfaQ\u0744\x01\x83\xbcCS(\t\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xf8\xf6r\x95\xa5\xcd\x04\x93d\xd0]#P\xa3\xe5.\x84\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc4\x01\xc4'\xcc\xcf\xf1\r\xec\xb8d /6\xf5\x80\x83\"\xa0\xa8\x89\xb4{Q\xa6\x9c\xd4\x02\x00\x00\u07d4\xc4\b\x8c\x02_>\x85\x01?T9\xfb4@\xa1s\x01\xe5D\xfe\x89~\t\xdbM\x9f?4\x00\x00\u07d4\xc4\x14a\xa3\u03fd2\u0246UU\xa4\x8117\xc0v1#`\x8965\xc6 G9\u0640\x00\u07d4\xc4 8\x8f\xbe\xe8J\xd6V\xddh\xcd\xc1\xfb\xaa\x93\x92x\v4\x89\n-\xcac\xaa\xf4\u0140\x00\u07d4\xc4\"P\xb0\xfeB\xe6\xb7\xdc\xd5\u0210\xa6\xf0\u020f__\xb5t\x89\b\x1e\xe4\x82SY\x84\x00\x00\u07d4\xc4-j\xebq\x0e:P\xbf\xb4Ml1\t)i\xa1\x1a\xa7\xf3\x89\b\"c\xca\xfd\x8c\xea\x00\x00\xe0\x94\xc4@\xc7\xca/\x96Kir\xeffJ\"a\xdd\xe8\x92a\x9d\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xc4K\xde\xc8\xc3l\\h\xba\xa2\xdd\xf1\xd41i2)rlC\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc4OJ\xb5\xbc`9|s~\xb0h3\x91\xb63\xf8\xa2G\x1b\x12\x1c\xa4\x89 .h\xf2\u00ae\xe4\x00\x00\u07d4\xc4h\x1es\xbb\x0e2\xf6\xb7& H1\xffi\xba\xa4\x87~2\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc4k\xbd\xefv\xd4\xca`\xd3\x16\xc0\u007f]\x1ax\x0e;\x16_~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc4}a\v9\x92P\xf7\x0e\xcf\x13\x89\xba\xb6),\x91&O#\x89\x0f\xa7\xe7\xb5\xdf<\xd0\x00\x00\u07d4\u0100;\xb4\a\xc7b\xf9\vu\x96\xe6\xfd\u1513\x1ev\x95\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0106Q\xc1\xd9\xc1k\xffL\x95T\x88l??&C\x1foh\x89#\xab\x95\x99\xc4?\b\x00\x00\u07d4\u0109\xc8?\xfb\xb0%*\xc0\xdb\xe3R\x12\x17c\x0e\x0fI\x1f\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u010bi<\xac\xef\xdb\xd6\xcb]x\x95\xa4.1\x962~&\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0113H\x9eV\u00fd\xd8)\x00}\xc2\xf9VA)\x06\xf7k\xfa\x89\x02\xa7\x91H\x8eqT\x00\x00\u07d4\u0116\u02f0E\x9aj\x01`\x0f\u0149\xa5Z2\xb4T!\u007f\x9d\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4\u011c\xfa\xa9g\xf3\xaf\xbfU\x03\x10a\xfcL\xef\x88\xf8]\xa5\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0136\xe5\xf0\x9c\xc1\xb9\r\xf0x\x03\xce=M\x13vj\x9cF\xf4\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u013e\xc9c\b\xa2\x0f\x90\u02b1\x83\x99\u0113\xfd=\x06Z\xbfE\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xc4\xc0\x1a\xfc>\x0f\x04R!\xda\x12\x84\u05c7\x85tD/\xb9\xac\x8a\x01\x92\xb5\u0249\x02J\x19\xc1\xbdo\x12\x80\x00\xe0\x94\xc5\x00\xb7 sN\xd2)8\u05cc^H\xb2\xba\x93g\xa5u\xba\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\u07d4\xc5\x0f\xe4\x15\xa6A\xb0\x85lNu\xbf\x96\x05\x15D\x1a\xfa5\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\x13L\xfb\xb1\xdfz \xb0\xedpWb.\xee\u0480\x94}\xad\x89\xcd\xff\x97\xfa\xbc\xb4`\x00\x00\xe0\x94\xc5\x17\xd01\\\x87\x88\x13\xc7\x17\u132f\xa1\xea\xb2eN\x01\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc5\x18y\x9aY%Wb\x13\xe2\x18\x96\xe0S\x9a\xbb\x85\xb0Z\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\"\xe2\x0f\xbf\x04\xed\u007fk\x05\xa3{G\x18\xd6\xfc\xe0\x14.\x1a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc5$\bmF\xc8\x11+\x12\x8b/\xafo|}\x81`\xa88l\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc5-\x1a\fs\u00a1\xbe\x84\x91Q\x85\xf8\xb3O\xaa\n\xdf\x1d\xe3\x89K\xe4\xea\xb3\xfa\x0f\xa6\x80\x00\xe0\x94\xc55\x94\xc7\u03f2\xa0\x8f(L\xc9\u05e6;\xbd\xfc\v1\x972\x8a\nk#(\xff:b\xc0\x00\x00\u07d4\xc57I(\xcd\xf1\x93pTC\xb1L\xc2\r\xa4#G<\xd9\u03c9\a}\x10P\x9b\xb3\xaf\x80\x00\u07d4\xc58\xa0\xff(*\xaa_Ku\u03f6,p\x03~\xe6}O\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5;P\xfd;+r\xbclC\v\xaf\x19JQU\x85\u04d8m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc5=y\xf7\u02dbp\x95/\xd3\x0f\xceX\xd5K\x9f\vY\xf6G\x8a\x01\x13\xe2\xd6tCE\xf8\x00\x00\u07d4\xc5I\u07c3\xc6\xf6^\xec\x0f\x1d\u0260\x93J\\_:P\xfd\x88\x89\x9d\xc0\\\xce(\u00b8\x00\x00\u07d4\xc5P\x05\xa6\xc3~\x8c\xa7\xe5C\xce%\x99s\xa3\xca\u0396\x1aJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5U\xb91V\xf0\x91\x01#\x80\x00\xe0\x94\u0166)\xa3\x96%R\u02ce\xde\u0609cj\xaf\xbd\f\x18\xcee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u016e\x86\xb0\xc6\xc7\xe3\x90\x0f\x13h\x10\\VS\u007f\xaf\x8dt>\x89\n1\x06+\xee\xedp\x00\x00\u07d4\u0170\t\xba\xea\xf7\x88\xa2v\xbd5\x81:\xd6[@\v\x84\x9f;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0175l\xd24&|(\xe8\x9cok\"f\xb0\x86\xa1/\x97\f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xc5\u01a4\x99\x8a3\xfe\xb7dCz\x8b\xe9)\xa7;\xa3J\ad\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x94\xc5\xc7=a\xcc\xe7\xc8\xfeL\x8f\xce)\xf3\x90\x92\xcd\x19>\x0f\xff\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc5\xc7Y\vV!\xec\xf85\x85\x88\u079bh\x90\xf2baC\U000498a1]\tQ\x9b\xe0\x00\x00\u07d4\xc5\xcd\xce\xe0\xe8]\x11}\xab\xbfSj?@i\xbfD?T\xe7\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xc5\u050c\xa2\xdb/\x85\xd8\xc5U\xcb\x0e\x9c\xfe\x82i6x?\x9e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc5\xde\x12\x03\xd3\xcc,\xea1\xc8.\xe2\xdeY\x16\x88\a\x99\xea\xfd\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc5\xe4\x88\xcf+Vw\x939q\xf6L\xb8 -\xd0WR\xa2\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\xe8\x12\xf7o\x15\xf2\xe1\xf2\xf9\xbcH#H<\x88\x04cog\x89\x03\xf5\x14\x19:\xbb\x84\x00\x00\u07d4\xc5\u94d34\xf1%.\u04ba&\x81D\x87\xdf\u0498+1(\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xc5\xebB)^\x9c\xad\xea\xf2\xaf\x12\xde\u078a\x8dS\xc5y\xc4i\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xc5\xed\xbb\xd2\xca\x03WeJ\xd0\xeaG\x93\xf8\xc5\xce\xcd0\xe2T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc5\xf6K\xab\xb7\x031B\xf2\x0eF\u05eab\x01\xed\x86\xf6q\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\xf6\x87qrF\u068a \r \xe5\u9f2c`\xb6\u007f8a\x89\x01\x8d\x99?4\xae\xf1\x00\x00\u07d4\xc6\x04[<5\vL\xe9\xca\fkuO\xb4\x1ai\xb9~\x99\x00\x892$\xf4'#\xd4T\x00\x00\u07d4\xc6\v\x04eN\x00;F\x83\x04\x1f\x1c\xbdk\u00cf\xda|\xdb\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x14F\xb7T\xc2N;\x16B\xd9\xe5\x17e\xb4\xd3\xe4k4\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x18R\x13!\xab\xaf[&Q:J\x95(\bo\"\n\xdco\x89\x01v\xb3D\xf2\xa7\x8c\x00\x00\u07d4\xc6#FW\xa8\a8A&\xf8\x96\x8c\xa1p\x8b\xb0{\xaaI<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6%\xf8\u024d'\xa0\x9a\x1b\u02bdQ(\xb1\u00a9HV\xaf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc65^\xc4v\x8cp\xa4\x9a\xf6\x95\x13\u0343\xa5\xbc\xa7\xe3\xb9\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc6:\xc4\x17\x99.\x9f\x9b`8n\xd9S\xe6\xd7\xdf\xf2\xb0\x90\xe8\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xc6<\u05c8!\x18\xb8\xa9\x1e\aML\x8fK\xa9\x18Q0;\x9a\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xc6R\x87\x1d\x19$\"\u01bc#_\xa0c\xb4J~\x1dC\u3149\bg\x0e\x9e\xc6Y\x8c\x00\x00\xe0\x94\xc6gD\x1e\u007f)y\x9a\xbaadQ\xd5;?H\x9f\x9e\x0fH\x8a\x02\xf2\x9a\xceh\xad\u0740\x00\x00\u07d4\xc6j\xe4\xce\xe8\u007f\xb352\x19\xf7\u007f\x1dd\x86\u0140(\x032\x89\x01\x9a\x16\xb0o\xf8\xcb\x00\x00\u07d4\xc6t\xf2\x8c\x8a\xfd\a?\x8by\x96\x91\xb2\xf0XM\xf9B\xe8D\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0197\xb7\x04w\u02b4.+\x8b&f\x81\xf4\xaesu\xbb%A\x8a\x01.W2\xba\xba\\\x98\x00\x00\u07d4\u019b\x85U9\xce\x1b\x04qG(\xee\xc2Z7\xf3g\x95\x1d\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u019b\xe4@\x13Mb\x80\x98\x01D\xa9\xf6M\x84t\x8a7\xf3I\x89&\u009eG\u0104L\x00\x00\u07d4\u019df<\x8d`\x90\x83\x91\xc8\xd26\x19\x153\xfd\xf7wV\x13\x89\x1aJ\xba\"\\ t\x00\x00\u0794\u01a2\x86\xe0e\xc8_:\xf7H\x12\xed\x8b\u04e8\xce]%\xe2\x1d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\u01a3\x0e\xf5\xbb3 \xf4\r\xc5\xe9\x81#\rR\xae:\xc1\x93\"\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\u01ae(}\xdb\xe1\x14\x9b\xa1m\xdc\xcaO\xe0j\xa2\uaa48\xa9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xc7\xc1\x917\x98\x97\u075c\x9d\x9a3\x83\x9cJ_b\xc0\x89\r\x89\xd8\xd8T\xb2$0h\x80\x00\xe0\x94\xc6\xcdh\xec56,Z\xd8L\x82\xadN\xdc#!%\x91-\x99\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u07d4\xc6\u0615N\x8f?\xc53\xd2\xd20\xff\x02\\\xb4\xdc\xe1O4&\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xdb\u06de\xfd^\xc1\xb3xn\x06q\xeb\"y\xb2S\xf2\x15\xed\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc6\xdf u\xeb\xd2@\xd4Hi\u00bek\u07c2\xe6=N\xf1\xf5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6\xe2\xf5\xaf\x97\x9a\x03\xfdr:\x1bn\xfar\x83\x18\u03dc\x18\x00\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xc6\xe3$\xbe\xeb[6v^\xcdFB`\xf7\xf2`\x06\xc5\xc6.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\xe4\xcc\fr\x83\xfc\x1c\x85\xbcH\x13\xef\xfa\xafr\xb4\x98#\xc0\x89\x0f\x03\x1e\xc9\xc8}\xd3\x00\x00\xe0\x94\xc6\xee5\x93B)i5)\xdcA\u067bq\xa2IfX\xb8\x8e\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xc6\xfb\x1e\xe3t\x17\u0400\xa0\xd0H\x92;\u06ba\xb0\x95\xd0w\u0189\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc7\x05'\xd4D\u0110\xe9\xfc?\\\xc4Nf\xebO0k8\x0f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\r\x85mb\x1e\xc1E0<\nd\x00\xcd\x17\xbb\xd6\xf5\xea\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc7\x0f\xa4Uv\xbf\x9c\x86_\x988\x93\x00,AI&\xf6\x10)\x89\x15\xb4\xaa\x8e\x97\x02h\x00\x00\u07d4\xc7\x11E\xe5)\u01e7\x14\xe6y\x03\xeeb\x06\xe4\xc3\x04+g'\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xc7\x1b*=q5\u04a8_\xb5\xa5q\u073ei^\x13\xfcC\u034965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\x1f\x1du\x87?3\u0732\xddK9\x87\xa1-\a\x91\xa5\xce'\x897\b\xba\xed=h\x90\x00\x00\u07d4\xc7\x1f\x92\xa3\xa5J{\x8c/^\xa4C\x05\xfc\u02c4\xee\xe21H\x89\x02\xb5\x9c\xa11\xd2\x06\x00\x00\u07d4\xc7!\xb2\xa7\xaaD\xc2\x12\x98\xe8P9\xd0\x0e.F\x0eg\v\x9c\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xc7,\xb3\x01%\x8e\x91\xbc\b\x99\x8a\x80]\u0452\xf2\\/\x9a5\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc76\x8b\x97\t\xa5\xc1\xb5\x1c\n\xdf\x18ze\xdf\x14\xe1+}\xba\x8a\x02\x02o\xc7\u007f\x03\u5b80\x00\u07d4\xc79%\x9e\u007f\x85\xf2e\x9b\xef_`\x9e\xd8k=Yl \x1e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc7>!\x12(\"\x15\xdc\ab\xf3+~\x80}\xcd\x1az\xae>\x8a\x01v\f\xbcb;\xb3P\x00\x00\xe0\x94\xc7If\x80B\xe7\x11#\xa6H\x97^\b\xedc\x82\xf8>\x05\xe2\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xc7J9\x95\xf8\a\xde\x1d\xb0\x1a.\xb9\xc6.\x97\xd0T\x8fio\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7Pl\x10\x19\x12\x1f\xf0\x8a,\x8c\x15\x91\xa6^\xb4\xbd\xfbJ?\x89 \x86\xac5\x10R`\x00\x00\u07d4\xc7\\7\xce-\xa0k\xbc@\b\x11Y\u01ba\x0f\x97n9\x93\xb1\x89:y#\x15\x1e\xcfX\x00\x00\u07d4\xc7]\"Y0j\xec}\xf0\"v\x8ci\x89\x9ae!\x85\xdb\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7`\x97\x1b\xbc\x18\x1cj|\xf7tA\xf2BG\u045c\xe9\xb4\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc7a0\xc7<\xb9!\x028\x02\\\x9d\xf9]\v\xe5J\xc6\u007f\xbe\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xc7e\xe0\x04v\x81\tG\x81j\xf1B\xd4m.\u7f28\xccO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc7g^VG\xb9\xd8\xda\xf4\xd3\xdf\xf1\xe5R\xf6\xb0qT\xac8\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xc7{\x01\xa6\xe9\x11\xfa\x98\x8d\x01\xa3\xab3dk\xee\xf9\xc18\xf3\x89'\x1bo\xa5\xdb\xe6\xcc\x00\x00\u07d4\u01c3z\u0420\xbf\x14\x18i7\xac\xe0lUF\xa3j\xa5OF\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01d8\x06\x03+\xc7\xd8(\xf1\x9a\u01a6@\u018e=\x82\x0f\xa4B\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u01d9\xe3N\x88\xff\x88\xbe}\xe2\x8e\x15\xe4\xf2\xa6=\v3\xc4\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u01ddPb\u01d6\xddwa\xf1\xf1>U\x8ds\xa5\x9f\x82\xf3\x8b\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\u01e0\x18\xf0\x96\x8aQ\xd1\xf6`<\\I\xdcT[\xcb\x0f\xf2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01ef\xf9\x19)yt\x89UZ/\xf1\xd1M\\iZ\x10\x83U\x8965\u026d\xc5\u07a0\x00\x00\u0794\u01f1\xc8>c ?\x95G&>\xf6(.}\xa3;n\xd6Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\u01f3\x9b\x06\x04Q\x00\f\xa1\x04\x9b\xa1T\xbc\xfa\x00\xff\x8a\xf2b\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\u01ff\x17\xc4\xc1\x1f\x98\x94\x1fP~w\bO\xff\xbd-\xbd=\xb5\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u01ff.\xd1\xed1)@\xeej\xde\xd1Qn&\x8eJ`HV\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc7\xd4O\xe3,\u007f\x8c\xd5\xf1\xa9t'\xb6\xcd:\xfc\x9eE\x02>\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xc7\xd5\xc7\x05@\x81\xe9\x18\xech{Z\xb3n\x97=\x18\x13)5\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xc7\xde^\x8e\xaf\xb5\xf6+\x1a\n\xf2\x19\\\xf7\x93\u01c9L\x92h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\xe30\xcd\f\x89\n\u025f\xe7q\xfc\xc7\xe7\xb0\t\xb7A=\x8a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\xea\xc3\x1a\xbc\xe6\xd5\xf1\u07a4\"\x02\xb6\xa6t\x15=\xb4z)\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc7\xecb\xb8\x04\xb1\xf6\x9b\x1e0p\xb5\xd3b\xc6/\xb3\t\xb0p\x8a\x02\xc4k\xf5A`f\x11\x00\x00\u07d4\xc7\xf7+\xb7X\x01k7G\x14\u0509\x9b\xce\"\xb4\xae\xc7\n1\x89:&\xc9G\x8f^-\x00\x00\u0794\xc8\v6\u047e\xaf\xba_\xccdM`\xacnF\xed)'\xe7\u0708\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xc8\x11\xc2\xe9\xaa\x1a\xc3F.\xba^\x88\xfc\xb5\x12\x0e\x9fn,\xa2\x89K\xe6\u0607\xbd\x87n\x00\x00\u07d4\xc8\x17\xdf\x1b\x91\xfa\xf3\x0f\xe3%\x15qr|\x97\x11\xb4]\x8f\x06\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xc8\x1f\xb7\xd2\x0f\u0480\x01\x92\xf0\xaa\xc1\x98\xd6\u05a3}?\xcb}\x89\x0e\x11I3\x1c-\xde\x00\x00\u07d4\xc8 \xc7\x11\xf0w\x05'8\a\xaa\xaam\xe4M\x0eKH\xbe.\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4\xc8#\x1b\xa5\xa4\x11\xa1>\"+)\xbf\xc1\b?v1X\xf2&\x8967\tlK\xcci\x00\x00\u07d4\xc86\xe2Jo\xcf)\x94;6\b\xe6b)\n!_e)\xea\x89\x0f\xd4Pd\xea\xee\x10\x00\x00\xe0\x94\xc8;\xa6\u0755I\xbe\x1d2\x87\xa5\xa6T\xd1\x06\xc3Lk]\xa2\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc8>\x9djX%;\uefb7\x93\xe6\xf2\x8b\x05JXI\x1bt\x89\x0fF\u00b6\xf5\xa9\x14\x00\x00\u07d4\xc8A\x88O\xa4x_\xb7s\xb2\x8e\x97\x15\xfa\xe9\x9aQ40]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8M\x9b\xea\n{\x9f\x14\x02 \xfd\x8b\x90\x97\u03ff\xd5\xed\xf5d\x89\x06\xab\x9e\u0091\xad}\x80\x00\u07d4\xc8RB\x8d+Xd\x97\xac\xd3\fV\xaa\x13\xfbU\x82\xf8D\x02\x893B\xd6\r\xff\x19`\x00\x00\u07d4\xc8S![\x9b\x9f-,\xd0t\x1eX^\x98{_\xb8\f!.\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc8S%\uaca5\x9b>\xd8c\xc8j_)\x06\xa0B)\xff\xa9\x89\x19=\u007f}%=\xe0\x00\x00\u07d4\xc8^\xf2}\x82\x04\x03\x80_\xc9\xed%\x9f\xffd\xac\xb8\xd64j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8akN\xc0\x91(\xcd\xff9\xd6\u4e6c\x86\xee\xc4q\xd5\xf2\x89\x01\r:\xa56\xe2\x94\x00\x00\xe0\x94\xc8a\x90\x90K\x8d\a\x9e\xc0\x10\xe4b\xcb\xff\xc9\b4\xff\xaa\\\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4\xc8q\r~\x8bZ;\u059aB\xfe\x0f\xa8\xb8|5\u007f\xdd\xcd\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc8sR\u06e5\x82\xee f\xb9\xc0\x02\xa9b\xe0\x03\x13Ox\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xc8|w\xe3\xc2J\xde\xcd\xcd\x108\xa3\x8bV\xe1\x8d\xea\u04f7\x02\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xc8}:\xe3\u0607\x04\u066b\x00\t\xdc\xc1\xa0\x06q1\xf8\xba<\x89j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\u0201N4R>8\xe1\xf9'\xa7\xdc\xe8FjDz\t6\x03\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0202U\xed\xdc\xf5!\xc6\xf8\x1d\x97\xf5\xa4!\x81\xc9\a=N\xf1\x89\x0f\u00d0D\xd0\n*\x80\x00\u07d4\u0205\xa1\x8a\xab\xf4T\x1b{{~\xcd0\xf6\xfa\u619d\x95i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u020c\xa1\xe6\xe5\xf4\xd5X\xd17\x80\xf4\x88\xf1\rJ\xd3\x13\r4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u020e\xecT\xd3\x05\xc9(\xcc(H\xc2\xfe\xe251\xac\xb9mI\x89lj\u04c2\xd4\xfba\x00\x00\xe0\x94\u021c\xf5\x04\xb9\xf3\xf85\x18\x1f\xd8BO\\\xcb\xc8\xe1\xbd\xdf}\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0222\xc4\xe5\x9e\x1c\u007f\xc5H\x05X\x048\xae\xd3\xe4J\xfd\xf0\x0e\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\u022aI\u301f\b\x99\xf2\x8a\xb5~gCp\x9dXA\x903\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\u022b\x1a<\xf4l\xb8\xb0d\xdf.\"-9`s\x94 2w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0231\x85\x05%\xd9F\xf2\xae\x84\xf3\x17\xb1Q\x88\xc56\xa5\u0706\x89\x91\x8d\xdc:B\xa3\xd4\x00\x00\u07d4\xc8\xd4\xe1Y\x9d\x03\xb7\x98\t\xe0\x13\n\x8d\u00c4\b\xf0^\x8c\u04c9\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xc8\xdd'\xf1k\xf2$P\xf5w\x1b\x9f\xe4\xedO\xfc\xb3\t6\xf4\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xc8\xdezVL\u007f@\x12\xa6\xf6\xd1\x0f\u040fG\x89\x0f\xbf\a\u0509\x10CV\x1a\x88)0\x00\x00\u07d4\xc8\xe2\xad\xebT^I\x9d\x98,\f\x11sc\u03b4\x89\u0171\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xc8\xe5X\xa3\xc5i~o\xb2:%\x94\u0200\xb7\xa1\xb6\x8f\x98`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc8\xf2\xb3 \xe6\xdf\xd7\t\x06\u0157\xba\xd2\xf9P\x13\x12\u01c2Y\x89Q\x93K\x8b:W\xd0\x00\x00\u07d4\xc9\x03\x00\xcb\x1d@w\xe6\xa6\xd7\xe1i\xa4`F\x8c\xf4\xa4\x92\u05c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\f7e\x15k\u028eH\x97\xab\x80$\x19\x15<\xbeR%\xa9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc9\x10\xa9pUl\x97\x16\xeaS\xaff\xdd\xef\x93\x141$\x91=\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94\xc9\x12{\u007ff)\xee\x13\xfc?`\xbc/Dg\xa2\aE\xa7b\x8a\x03|\x9a\xa4\xe7\xceB\x1d\x80\x00\u07d4\xc9\x1b\xb5b\xe4+\xd4a0\xe2\u04eeFR\xb6\xa4\ub1bc\x0f\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\xe0\x94\xc90\x88y\x05m\xfe\x13\x8e\xf8 \x8fy\xa9\x15\u01bc~p\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xc94\xbe\xca\xf7\x1f\"_\x8bJK\xf7\xb1\x97\xf4\xac\x9604\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9?\xbd\xe8\xd4m+\xcc\x0f\xa9\xb3;\u063a\u007f\x80B\x12Ue\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xc9@\x89U:\xe4\xc2,\xa0\x9f\xbc\x98\xf5pu\xcf.\u0155\x04\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc9A\x10\xe7\x1a\xfeW\x8a\xa2\x18\xe4\xfc(d\x03\xb03\n\u038d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc9F\u056c\xc14n\xba\nry\xa0\xac\x1dF\\\x99m\x82~\x8a\x03x=T_\xdf\n\xa4\x00\x00\u07d4\xc9J(\xfb20\xa9\xdd\xfa\x96Nw\x0f,\xe3\xc2S\xa7\xbeO\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc9JXR\x03\xda{\xba\xfd\x93\xe1X\x84\xe6`\u0531\xea\xd8T\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc9O|5\xc0'\xd4}\xf8\xefO\x9d\xf8Z\x92H\xa1}\xd2;\x89\x01\x9f\x8euY\x92L\x00\x00\u07d4\xc9Q\x90\f4\x1a\xbb\xb3\xba\xfb\xf7\xee )7pq\xdb\xc3j\x89\x11\xc2]\x00M\x01\xf8\x00\x00\u07d4\xc9S\xf94\xc0\xeb-\x0f\x14K\u06b0\x04\x83\xfd\x81\x94\x86\\\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9f&r\x8a\xaaLO\xb3\xd3\x1c&\xdf:\xf3\x10\b\x17\x10\u0449\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xc9gQel\n\x8e\xf45{sD2!4\xb9\x83PJ\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\u0240Hh\u007f+\xfc\u027d\x90\xed\x18slW\xed\xd3R\xb6]\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0241\xd3\x12\u0487\xd5X\x87\x1e\u0757:\xbbv\xb9y\xe5\xc3^\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0242Xmc\xb0\xd7L \x1b\x1a\xf8A\x83r\xe3\fv\x16\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u0249CO\x82Z\xaf\x9cU/h^\xba|\x11\xdbJ_\xc7:\x89\x1b(\u014d\x96\x96\xb4\x00\x00\u07d4\u0249\xee\xc3\a\u80db\x9dr7\xcf\xda\b\x82)b\xab\u41c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u0252\xbeY\xc6r\x1c\xafN\x02\x8f\x9e\x8f\x05\xc2\\UQ[\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0255{\xa9L\x1b)\xe5'~\xc3f\"pI\x04\xc6=\xc0#\x89h>\xfcg\x82d,\x00\x00\xe0\x94\u025a\x9c\xd6\xc9\xc1\xbe54\xee\u0352\xec\xc2/\\8\xe9Q[\x8a\x01\x05Y;:\x16\x9dw\x00\x00\xe0\x94\u026c\x01\xc3\xfb\t)\x03?\f\xcc~\x1a\xcf\uaae7\x94]G\x8a\x02\xa3j\x9e\x9c\xa4\xd2\x03\x80\x00\u07d4\u0276\x98\xe8\x98\xd2\rMO@\x8eNM\x06\x19\"\xaa\x85c\a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u0276\xb6\x86\x11\x16\x91\xeej\xa1\x97\xc7#\x1a\x88\xdc`\xbd)]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\u01ec\v\u0753B\xb5\xea\xd46\t#\xf6\x8cr\xa6\xbac:\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\xc8\r\xc1.{\xab\x86\xe9I\xd0\x1eL>\xd3_+\x9b\xba_\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\xd7dF\u056a\xdf\xf8\vh\xb9\x1b\b\u035b\xc8\xf5U\x1a\xc1\x89&\xb4\xbd\x91\x10\xdc\xe8\x00\x00\xe0\x94\xc9\u073b\x05oM\xb7\xd9\xda9\x93b\x02\u017d\x820\xb3\xb4w\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9\xe0&\b\x06h(\x84\x8a\xeb(\xc76r\xa1)%\x18\x1fM\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xca\x042\xcb\x15{Qy\xf0.\xbb\xa5\xc9\u0475O\xecM\x88\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xca\x12,\xf0\U00094216\xb7HC\xf4\x9a\xfe\u043a\x16\x18\xee\u05c9\x1e[\x8f\xa8\xfe*\xc0\x00\x00\xe0\x94\xca\"\u0363`m\xa5\xca\xd0\x13\xb8\aG\x06\xd7\xe9\xe7!\xa5\f\x8a\x01q\x81\xc6\xfa9\x81\x94\x00\x00\u07d4\xca#\xf6-\xff\rd`\x03lb\xe8@\xae\xc5W~\v\xef\u0489\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xca%\xff4\x93L\x19B\xe2*N{\xd5o\x14\x02\x1a\x1a\xf0\x88\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xca7?\xe3\xc9\x06\xb8\xc6U\x9e\xe4\x9c\xcd\a\xf3|\xd4\xfbRf\x89a\t=|,m8\x00\x00\u07d4\xcaA\u032c0\x17 R\xd5\"\xcd//\x95}$\x81S@\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xcaB\x88\x01N\xdd\xc5c/_\xac\xb5\xe3\x85\x17\xa8\xf8\xbc]\x98\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xcaB\x88c\xa5\xca06\x98\x92\xd6\x12\x18>\xf9\xfb\x1a\x04\xbc\xea\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xcaI\xa5\xf5\x8a\xdb\xef\xae#\xeeY\xee\xa2A\xcf\x04\x82b.\xaa\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcaL\xa9\xe4w\x9dS\x0e\u02ec\xd4~j\x80X\xcf\xdee\u064f\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xcae~\xc0o\xe5\xbc\t\xcf#\xe5*\xf7\xf8\f\xc3h\x9en\u07890\xca\x02O\x98{\x90\x00\x00\u07d4\xcaf\xb2(\x0f\xa2\x82\u0176v1\xceU+b\xeeU\xad\x84t\x89j\xc4\"\xf54\x92\x88\x00\x00\xe0\x94\xcal\x81\x8b\xef\xd2Q6\x1e\x02t@h\xbe\x99\u062a`\xb8J\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcap\xf4\u077f\x06\x9d!C\xbdk\xbc\u007fikRx\x9b2\u7262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xcatuvDjL\x8f0\xb0\x83@\xfe\xe1\x98\xdec\xec\x92\u03ca\x01|\x8e\x12\x06r*0\x00\x00\u07d4\xca{\xa3\xffSl~_\x0e\x158\x00\xbd8=\xb81)\x98\xe0\x89\t1\xac=k\xb2@\x00\x00\xe0\x94\u0282v\xc4w\xb4\xa0{\x80\x10{\x845\x94\x18\x96\a\xb5;\xec\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u0284\t\b>\x01\xb3\x97\xcf\x12\x92\x8a\x05\xb6\x84U\xceb\x01\u07c9V\xbcu\xe2\xd61\x00\x00\x00\u07d4\u0298\u01d8\x8e\xfa\b\xe9%\uf719ER\x03&\xe9\xf4;\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u029a\x04*j\x80o\xfc\x92\x17\x95\x00\xd2D)\xe8\xabR\x81\x17\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\u029d\xec\x02\x84\x1a\xdf\\\xc9 WjQ\x87\xed\u04bdCJ\x18\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u029f\xaa\x17T/\xaf\xbb8\x8e\xab!\xbcL\x94\u89f3G\x88\x89lk\x8f\xce\r\x18y\x80\x00\xe0\x94\u02aah\xeel\xdf\r4EJv\x9b\r\xa1H\xa1\xfa\xaa\x18e\x8a\x01\x87.\x1d\xe7\xfeR\xc0\x00\x00\u07d4\u02ad\x9d\xc2\rX\x9c\xe4(\xd8\xfd\xa3\xa9\xd5:`{y\x88\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u02b0\xd3,\xf3v\u007f\xa6\xb3S|\x842\x8b\xaa\x9fPE\x816\x8a\x01\xe5\xb8\xfa\x8f\xe2\xac\x00\x00\x00\u07d4\u02b9\xa3\x01\xe6\xbdF\xe9@5P(\xec\xcd@\xceMZ\x1a\u00c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u02b9\xa9z\xda\x06\\\x87\x81nh`\xa8\xf1Bo\xe6\xb3\xd7u\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u02ba\xb6'N\xd1P\x89s~({\xe8x\xb7W\x93Hd\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u02bd\xaf5OG \xa4f\xa7d\xa5(\xd6\x0e:H*9<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xca\xcbg^\t\x96#T\x04\ufbfb.\u02c1R'\x1bU\xe0\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xca\xd1O\x9e\xbb\xa7f\x80\xeb\x83k\a\x9c\u007f{\xaa\xf4\x81\xedm\x89\f\xef={\xd7\xd04\x00\x00\xe0\x94\xca\xe3\xa2S\xbc\xb2\xcfN\x13\xba\x80\u0098\xab\x04\x02\xda|*\xa0\x8a\x01$\xbc\r\u0752\xe5`\x00\x00\u07d4\xca\xef\x02{\x1a\xb5\x04\xc7?A\xf2\xa1\ty\xb4t\xf9~0\x9f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xca\xf4H\x1d\x9d\xb7\x8d\xc4\xf2_{J\u023d;\x1c\xa0\x10k1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xca\xfd\xe8U\x86L%\x98\xda<\xaf\xc0Z\u064d\U00089380H\x8a\x03\x00\xa8\xed\x96\xffJ\x94\x00\x00\xe0\x94\xcb\r\xd7\xcfN]\x86a\xf6\x02\x89C\xa4\xb9\xb7\\\x91D6\xa7\x8a\x19i6\x89t\xc0[\x00\x00\x00\u07d4\xcb\x1b\xb6\xf1\xda^\xb1\rH\x99\xf7\xe6\x1d\x06\xc1\xb0\x0f\u07f5-\x898E$\xccp\xb7x\x00\x00\u07d4\xcb=vl\x98?\x19+\xce\xca\xc7\x0fN\xe0=\xd9\xffqMQ\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcbB\xb4N\xb5\xfd`\xb5\x83~O\x9e\xb4rgR=\x1a\"\x9c\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xcbG\xbd0\u03e8\xecTh\xaa\xa6\xa9FB\xce\xd9\xc8\x19\xc8\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcbH\xfe\x82e\u066fU\xebp\x06\xbc3VE\xb0\xa3\xa1\x83\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xcbJ\x91M+\xb0)\xf3._\xef\\#LO\xec--\xd5w\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcbJ\xbf\u0082\xae\xd7n]W\xaf\xfd\xa5B\xc1\xf3\x82\xfc\xac\xf4\x8a\x01\xb9\x0f\x11\xc3\x18?\xaa\x00\x00\u07d4\xcbJ\xd0\xc7#\xdaF\xabV\xd5&\xda\f\x1d%\xc7=\xaf\xf1\n\x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\xcbK\xb1\xc6#\xba(\xdcB\xbd\xaa\xa6\xe7N\x1d*\xa1%l*\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xcbPXt\x12\x82#\x04\xeb\u02e0}\xab:\x0f\t\xff\xfe\u4189JD\x91\xbdm\xcd(\x00\x00\u07d4\xcbX\x99\v\u0350\u03ffm\x8f\t\x86\xf6\xfa`\x02v\xb9N-\x8964\xbf9\xab\x98x\x80\x00\u07d4\xcbh\xaeZ\xbe\x02\xdc\xf8\xcb\u016aq\x9c%\x81FQ\xaf\x8b\x85\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xcbty\x10\x9bC\xb2fW\xf4F_M\x18\xc6\xf9t\xbe_B\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xcb}+\x80\x89\xe91,\u026e\xaa's\xf3S\b\xecl*{\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u02c6\xed\xbc\x8b\xbb\x1f\x911\x02+\xe6IV^\xbd\xb0\x9e2\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u02d3\x19\x9b\x9c\x90\xbcI\x15\xbd\x85\x9e=B\x86m\xc8\xc1\x87I\x89\f\x90\xdf\a\xde\xf7\x8c\x00\x00\u07d4\u02d4\xe7o\xeb\xe2\b\x11g3\xe7n\x80]H\xd1\x12\xec\x9f\u028965\u026d\xc5\u07a0\x00\x00\u07d4\u02dbQ\x03\xe4\u0389\xafOd\x91aP\xbf\xf9\xee\u02df\xaa\\\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\\zP<\xc8\xe0\xd0Iq\xca\x05\xc7b\xf9\xb7b\xb4\x8b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\x88\xcd<\x1e\xb4\u055d\xdb\x06\xa6B\x1c\x14\xc3E\xa4{$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u02f3\x18\x9eK\xd7\xf4_\x17\x8b\x1c0\xc7n&1MJK\n\x89\x0f\xfe\vg|e\xa9\x80\x00\xe0\x94\u02f7\xbe\x17\x95?,\u0313\u1f19\x80[\xf4U\x11CNL\x8a\n\xae[\x9d\xf5m/ \x00\x00\xe0\x94\xcb\xc0KM\x8b\x82\xca\xf6p\x99o\x16\f6)@\xd6o\xcf\x1a\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcb\u07974\xb8\xe6\xaaS\x8c)\x1dm\u007f\xac\xed\xb0\xf38\xf8W\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcb\xe1\xb9H\x86M\x84t\xe7e\x14XX\xfc\xa4U\x0fxK\x92\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xe5/\xc53\xd7\xdd`\x8c\x92\xa2`\xb3|?E\u07b4\xeb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcb\xe8\x10\xfe\x0f\xec\xc9dGJ\x1d\xb9w(\xbc\x87\xe9s\xfc\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xf1j\x0f\xe2tRX\xcdR\xdb+\xf2\x19T\xc9u\xfcj\x15\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\xcb\xf3\u007f\xf8T\xa2\xf1\xceS\x93D\x94wx\x92\xd3\xeceW\x82\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xfaj\xf6\u0083\xb0F\xe2w,`c\xb0\xb2\x15S\xc4\x01\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcb\xfav\xdb\x04\xce8\xfb ]7\xb8\xd3w\xcf\x13\x80\xda\x03\x17\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcc\x03I\x85\xd3\xf2\x8c-9\xb1\xa3K\xce\xd4\u04f2\xb6\xca#N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xcc\x04\x8d\u01f9]\xca%\xdf&\xee\xfac\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc+_D\x8f5(\xd3\xfeA\xcc}\x1f\xa9\xc0\xdcv\xf1\xb7v\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc-\x04\xf0\xa4\x01q\x89\xb3@\xcaw\x19\x86A\xdc\xf6Ek\x91\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xccA\x9f\u0651+\x85\x13VY\xe7z\x93\xbc=\xf1\x82\xd4Q\x15\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccE\xfb:U[\xad\x80{8\x8a\x03W\xc8U _|u\xe8\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xccHAM*\xc4\xd4*Yb\xf2\x9e\xeeD\x97\t/C\x13R\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\xccJ/,\xf8l\xf3\xe43u\xf3`\xa4sF\x91\x19_\x14\x90\x89I\x15\x05;\xd1)\t\x80\x00\u07d4\xccO\x0f\xf2\xae\xb6}T\xce;\xc8\xc6Q\v\x9a\xe8>\x9d2\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xccO\xaa\xc0\v\xe6b\x8f\x92\xefk\x8c\xb1\xb1\xe7j\xac\x81\xfa\x18\x89\v\"\xa2\xea\xb0\xf0\xfd\x00\x00\xe0\x94\xccO\xebr\u07d8\xff5\xa18\xe0\x17a\xd1 ?\x9b~\xdf\n\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xcc`oQ\x13\x97\xa3\x8f\u01c7+\u04f0\xbd\x03\xc7\x1b\xbdv\x8b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcc`\xf86\xac\xde\xf3T\x8a\x1f\xef\u0321>\u01a97\xdbD\xa0\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\xccl\x03\xbd`>\t\xdeT\xe9\xc4\u056cmA\xcb\xceqW$\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xccl-\xf0\x0e\x86\xec\xa4\x0f!\xff\xda\x1ag\xa1i\x0fG|e\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\xccm{\x12\x06\x1b\xc9m\x10M`me\xff\xa3+\x006\xeb\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccs\xdd5kIy\xb5y\xb4\x01\xd4\xccz1\xa2h\xdd\xceZ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xccu\x8d\a\x1d%\xa62\n\xf6\x8c]\xc9\xc4\xf6\x95[\xa9E \x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcc{\x04\x81\xcc2\xe6\xfa\xef#\x86\xa0p\"\xbc\xb6\xd2\u00f4\xfc\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\u0314;\xe1\",\xd1@\n#\x99\xdd\x1bE\x94E\xcfmT\xa9\x8a\x02\xa7@\xaee6\xfc\x88\x00\x00\u07d4\u0315\x19\xd1\xf3\x98_k%^\xad\xed\x12\xd5bJ\x97'!\xe1\x8965\u026d\xc5\u07a0\x00\x00\u0794\u031a\xc7\x15\xcdo&\x10\xc5+XgdV\x88B\x97\x01\x8b)\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\u0320{\xb7\x94W\x1dJ\xcf\x04\x1d\xad\x87\xf0\xd1\xef1\x85\xb3\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u032b\xc6\x04\x8aSFD$\xfc\xf7n\xeb\x9en\x18\x01\xfa#\u0509\x02\xab{&\x0f\xf3\xfd\x00\x00\u07d4\u032e\r=\x85*}\xa3\x86\x0f\x066\x15L\nl\xa3\x16(\u0509\x05\xc6\xd1+k\xc1\xa0\x00\x00\u07d4\xcc\xca$\xd8\xc5mn,\a\xdb\bn\xc0~X[\xe2g\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcc\xd5!\x13-\x98l\xb9hi\x84&\"\xa7\u0762l>\xd0W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc\xf49u\xb7k\xfes_\xec<\xb7\xd4\xdd$\xf8\x05\xba\tb\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc\xf6*f?\x13S\xba.\xf8\xe6R\x1d\xc1\xec\xb6s\xec\x8e\xf7\x89\b=lz\xabc`\x00\x00\u07d4\xcc\xf7\x11\r\x1b\u0667K\xfd\x1d}}-\x9dU`~{\x83}\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xcc\xfdrW`\xa6\x88#\xff\x1e\x06/L\xc9~\x13`\xe8\u0657\x89\x15\xacV\xed\xc4\xd1,\x00\x00\u07d4\xcd\x02\x0f\x8e\xdf\xcfRG\x98\xa9\xb7:d\x034\xbb\xf7/\x80\xa5\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xcd\x06\xf8\xc1\xb5\u037d(\xe2\xd9kcF\xc3\xe8Z\x04\x83\xba$\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcd\a.n\x183\x13y\x95\x19m{\xb1r_\xef\x87a\xf6U\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcd\n\x16\x1b\xc3g\xae\t'\xa9*\xac\x9c\xf6\xe5\bg\x14\xef\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\n\xf3GN\"\xf0i\xec4\a\x87\r\xd7pD=[\x12\xb0\x89\x8e^\xb4\xeew\xb2\xef\x00\x00\u07d4\xcd\v\x02W\u70e3\xd2\xc2\u3e9dny\xb7^\xf9\x80$\u0509\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xcd\x10,\xd6\xdb=\xf1J\u05af\x0f\x87\xc7$y\x86\x1b\xfc=$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\x1ef\xedS\x9d\xd9/\xc4\v\xba\xa1\xfa\x16\u078c\x02\xc1ME\x89\fw\xe4%hc\xd8\x00\x00\u07d4\xcd\x1e\xd2c\xfb\xf6\xf6\xf7\xb4\x8a\xef\x8fs=2\x9dC\x82\xc7\u01c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4\xcd*6\xd7S\xe9\xe0\xed\x01*XMqh\aX{A\xd5j\x89\x0e+\xa7[\v\x1f\x1c\x00\x00\u07d4\xcd2\xa4\xa8\xa2\u007f\x1c\xc69T\xaacOxW\x05s4\u01e3\x89:\xd1fWlr\xd4\x00\x00\u07d4\xcd5\xff\x01\x0e\xc5\x01\xa7!\xa1\xb2\xf0z\x9c\xa5\x87}\xfc\xf9Z\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xcdC\x06\xd7\xf6\x94z\xc1tMN\x13\xb8\xef2\xcbe~\x1c\x00\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4\xcdC%\x8bs\x92\xa90\x83\x9aQ\xb2\xef\x8a\xd24\x12\xf7Z\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcdI\xbf\x18^p\xd0E\a\x99\x9f\x92\xa4\xdeDU1('\u040965\u026d\xc5\u07a0\x00\x00\u07d4\xcdU\x10\xa2B\u07f0\x18=\xe9%\xfb\xa8f\xe3\x12\xfa\xbc\x16W\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xcdVj\u05f8\x83\xf0\x1f\u04d9\x8a\x9aX\xa9\xde\xe4rM\u0725\x89\x030\xae\x185\xbe0\x00\x00\xe0\x94\xcdY\xf3\xdd\xe7~\t\x94\v\xef\xb6\xeeX\x03\x19e\xca\xe7\xa36\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcdr]p\xbe\x97\xe6w\xe3\xc8\xe8\\\v&\xef1\xe9\x95PE\x89Hz\x9a0E9D\x00\x00\xe0\x94\xcd~G\x90\x94d\xd8q\xb9\xa6\xdcv\xa8\xe9\x19]\xb3H^z\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xcd~\xce\bkKa\x9b;6\x93R\xee8\xb7\x1d\xdb\x06C\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcd\u007f\t\xd7\xedf\xd0\u00cb\u016dN2\xb7\xf2\xb0\x8d\xc1\xb3\r\x89>;\xb3M\xa2\xa4p\x00\x00\u07d4\u0355)I+\\)\xe4u\xac\xb9A@+=;\xa5\x06\x86\xb0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0355\xfaB=o\xc1 'J\xac\xde\x19\xf4\xee\xb7f\xf1\x04 \x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u035bL\xefs9\f\x83\xa8\xfdq\u05f5@\xa7\xf9\u03cb\x8c\x92\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\u0361t\x11\t\xc0&[?\xb2\xbf\x8d^\xc9\u00b8\xa34kc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0361\xb8\x86\u39d5\u027aw\x91N\n/\xe5go\x0f\\\u03c9\x05\xbf`\xeaB\xc2\x04\x00\x00\u07d4\u0364S\x0fK\x9b\xc5\t\x05\xb7\x9d\x17\u008f\xc4o\x954\x9b\u07c93\x10\xe0I\x11\xf1\xf8\x00\x00\u07d4\u036bF\xa5\x90 \x80do\xbf\x95B\x04 J\xe8\x84\x04\x82+\x89\x1d\x8a\x96\xe5\xc6\x06\xeb\x00\x00\u07d4\u0375\x97)\x900\x18?n-#\x853\xf4d*\xa5\x87T\xb6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xcd\xd5\u0601\xa76,\x90p\a;\u07fcu\xe7$S\xacQ\x0e\x89-\xa5\x18\xea\xe4\x8e\xe8\x00\x00\u07d4\xcd\xd6\rs\xef\xaa\xd8s\u027b\xfb\x17\x8c\xa1\xb7\x10Z\x81\xa6\x81\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xcd\xd9\xef\xacMm`\xbdq\xd9U\x85\xdc\xe5\u0557\x05\xc15d\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcd\xe3m\x81\xd1(\u015d\xa1Ee!\x93\xee\u00bf\xd9e\x86\xef\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcd\xea8o\x9d\x0f\xd8\x04\xd0(\x18\xf27\xb7\xd9\xfavF\xd3^\x89\xa3I\xd3m\x80\xecW\x80\x00\u07d4\xcd\xec\xf5gT3\u0370\xc2\xe5Zh\xdb]\x8b\xbexA\x9d\u0489\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xcd\xfd\x82\x173\x97%\xd7\xeb\xac\x11\xa66U\xf2e\xef\xf1\xcc=\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce\a\x9fQ\x88wt\xd8\x02\x1c\xb3\xb5u\xf5\x8f\x18\xe9\xac\xf9\x84\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xce\x18\x84\u077b\xb8\xe1\x0eM\xbanD\xfe\xee\u00a7\xe5\xf9/\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\x1b\f\xb4j\xae\xcf\u05db\x88\f\xad\x0f-\u068a\x8d\xed\u0431\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xce&\xf9\xa50_\x83\x81\tCT\xdb\xfc\x92fN\x84\xf9\x02\xb5\x89\fz\xaa\xb0Y\x1e\xec\x00\x00\u07d4\xce-\xea\xb5\x1c\n\x9a\xe0\x9c\xd2\x12\xc4\xfaL\xc5+S\xcc\r\xec\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xce.\r\xa8\x93F\x99\xbb\x1aU>U\xa0\xb8\\\x16\x945\xbe\xa3\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce:a\xf0F\x1b\x00\x93^\x85\xfa\x1e\xad\x82\xc4^Zd\u0508\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceK\x06]\xbc\xb20G 2b\xfbH\xc1\x18\x83d\x97tp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceS\xc8\xcd\xd7B\x96\xac\xa9\x87\xb2\xbc\x19\u00b8u\xa4\x87I\u0409\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xce^\x04\xf0\x18Ci\xbc\xfa\x06\xac\xa6o\xfa\x91\xbfY\xfa\x0f\xb9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xce^\xb6:{\xf4\xfb\xc2\xf6\u4ea0\u018a\xb1\xcbL\xf9\x8f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xceb\x12Z\xde\xc37\n\xc5!\x10\x95:Nv\v\xe9E\x1e;\x89\b=lz\xabc`\x00\x00\xe0\x94\xceq\bmL`%T\xb8-\xcb\xfc\xe8\x8d cMS\xccM\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4\u038akmP3\xb1I\x8b\x1f\xfe\xb4\x1aAU\x04\x05\xfa\x03\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0397\x86\xd3q/\xa2\x00\xe9\xf6\x857\xee\xaa\x1a\x06\xa6\xf4ZK\x89a\t=|,m8\x00\x00\u07d4\u039d!\u0192\xcd<\x01\xf2\x01\x1fP_\x87\x006\xfa\x8fl\u0489\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u03a2\x89f#\xf4\x91\x02\x87\xa2\xbd\u017e\x83\xae\xa3\xf2\xe6\xde\b\x8a\x01\xfbZ7Q\xe4\x90\xdc\x00\x00\u07d4\u03a3JM\xd9=\u066e\xfd9\x90\x02\xa9}\x99z\x1bK\x89\u0349QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\u03a4?pu\x81k`\xbb\xfc\u62d9:\xf0\x88\x12p\xf6\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\u03a8t3AS<\xb2\xf0\xb9\xc6\xef\xb8\xfd\xa8\rw\x16(%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u03b0\x89\xec\x8ax3~\x8e\xf8\x8d\xe1\x1bI\xe3\u0751\x0ft\x8f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u03b3=x\xe7Tz\x9d\xa2\xe8}Q\xae\xc5\xf3D\x1c\x87\x92:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u03b3\x898\x1dH\xa8\xaeO\xfcH:\u043b^ L\xfd\xb1\xec\x89('\xe6\xe4\xddb\xba\x80\x00\u07d4\xce\xc6\xfce\x85?\x9c\xce_\x8e\x84Fv6.\x15y\x01_\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xce\xd3\u01fe\x8d\xe7XQ@\x95*\xebP\x1d\xc1\xf8v\ucbf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\xd8\x1e\xc3S?\xf1\xbf\xeb\xf3\xe3\x84>\xe7@\xad\x11u\x8d>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xce\u0733\xa1\u0584?\xb6\xbe\xf6Ca}\xea\U000cf398\xdd_\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xce\xe6\x99\xc0pzx6%+)/\x04|\xe8\xad(\x9b/U\x89\x11\x9a\x1e!\xaaiV\x00\x00\u07d4\xce\xedG\xca[\x89\x9f\xd1b?!\xe9\xbdM\xb6Z\x10\u5c1d\x89\a8w@L\x1e\xee\x00\x00\u07d4\xce\xf7tQ\u07e2\xc6C\xe0\v\x15mlo\xf8N#s\xebf\x89\n1\x06+\xee\xedp\x00\x00\u07d4\xcf\x11i\x04\x1c\x17E\xe4[\x17$5\xa2\xfc\x99\xb4\x9a\xce+\x00\x89\x01\xbb\x88\xba\xab-|\x00\x00\xe0\x94\xcf\x15v\x12vN\x0f\u0596\xc8\xcb_\xba\x85\xdfL\r\xdc<\xb0\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u0794\xcf\x1b\xdby\x9b.\xa6<\xe14f\x8b\xdc\x19\x8bT\x84\x0f\x18\v\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xcf\"\x88\xefN\xbf\x88\xe8m\xb1=\x8a\x0e\v\xf5*\x05e\x82\u00c9\x89Po\xbf\x97@t\x00\x00\u07d4\xcf&Ni%\x13\t\x06\xc4\xd7\xc1\x85\x91\xaaA\xb2\xa6\u007foX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf&\xb4{\xd04\xbcP\x8elK\xcf\xd6\xc7\xd3\x004\x92Wa\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcf.*\xd65\xe9\x86\x1a\xe9\\\xb9\xba\xfc\xca\x03kR\x81\xf5\u038a\at2!~h6\x00\x00\x00\u07d4\xcf.s@B\xa3U\xd0_\xfb.9\x15\xb1h\x11\xf4Zi^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf4\x8f/\xe4{~A<\az{\xaf:u\xfb\xf8B\x86\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcf?\x91(\xb0r\x03\xa3\xe1\r}WU\xc0\u012b\xc6\xe2\xca\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xcf?\xbf\xa1\xfd2\u05e6\xe0\xe6\xf8\xefN\xabW\xbe4\x02\\L\x899\xa1\xc0\xf7YMH\x00\x00\u07d4\xcfAftn\x1d;\xc1\xf8\xd0qK\x01\xf1~\x8ab\xdf\x14d\x896w\x03n\xdf\n\xf6\x00\x00\u07d4\xcfO\x118\xf1\xbdk\xf5\xb6\u0505\xcc\xe4\xc1\x01\u007f\u02c5\xf0}\x89/\u043cw\xc3+\xff\x00\x00\u07d4\xcfZo\x9d\xf7Uy\xc6D\xf7\x94q\x12\x15\xb3\rw\xa0\xce@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf^\x0e\xac\u0473\x9d\x06U\xf2\xf7u5\xeff\b\xeb\x95\v\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcfhM\xfb\x83\x04r\x93U\xb5\x83\x15\xe8\x01\x9b\x1a\xa2\xad\x1b\xac\x89\x17r$\xaa\x84Lr\x00\x00\u07d4\xcfi@\x81\xc7m\x18\xc6L\xa7\x13\x82\xbe\\\xd6;<\xb4v\xf8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcfnR\xe6\xb7t\x80\xb1\x86~\xfe\xc6Dm\x9f\xc3\xcc5w\xe8\x89\f\t\x01\xf6\xbd\x98y\x00\x00\u07d4\u03c8: 2\x96g\xea\"j\x1e\x9a\x92*\x12\xf2\x1f\xaa\x03\x81V\x91\x8cO\u02dc\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xcf\xf7\xf8\x9aMB\x19\xa3\x82\x95%\x131V\x82\x10\xff\xc1\xc14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xcf\xf8\xd0k\x00\xe3\xf5\f\x19\x10\x99\xadV\xbaj\xe2eq\u0348\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcf\xfcI\xc1x~\ubcb5l\xab\xe9$\x04\xb66\x14}EX\x8a\x013\xe00\x8f@\xa3\u0680\x00\u07d4\xd0\bQ;'`J\x89\xba\x17c\xb6\xf8L\u6233F\x94[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x0f\x06r\x86\xc0\xfb\u0402\xf9\xf4\xa6\x10\x83\xecv\u07b3\xce\xe6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x15\xf6\xfc\xb8M\xf7\xbbA\x0e\x8c\x8f\x04\x89J\x88\x1d\xca\xc27\x898E$\xccp\xb7x\x00\x00\u07d4\xd0\x1a\xf9\x13O\xafRW\x17N\x8by\x18oB\xee5Nd-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0!\b\u04ae<\xab\x10\xcb\xcf\x16W\xaf\">\x02|\x82\x10\xf6\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xd0*\xfe\u03ce.\u00b6*\u022d Aa\xfd\x1f\xaew\x1d\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd01\x919\xfb\xab.\x8e*\xcc\xc1\xd9$\u0531\x1d\xf6ilZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd07\xd2\x15\xd1\x1d\x1d\xf3\xd5O\xbd2\x1c\u0495\xc5F^';\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xd0:-\xa4\x1e\x86\x8e\xd3\xfe\xf5t[\x96\xf5\xec\xa4b\xffo\u0689\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xd0?\xc1eWj\xae\xd5%\xe5P,\x8e\x14\x0f\x8b.\x86\x969\x8a\x01sV\u0633%\x01\xc8\x00\x00\u07d4\xd0C\xa0\x11\xecBp\xee~\u0239hsu\x15\xe5\x03\xf80(\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd0K\x86\x1b=\x9a\xccV:\x90\x16\x89\x94\x1a\xb1\xe1\x86\x11a\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd0ZD|\x91\x1d\xbb'[\xfb.Z7\xe5\xa7\x03\xa5o\x99\x97\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd0_\xfb+t\xf8g O\xe51e;\x02H\xe2\x1c\x13TN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0bX\x81q\u03d9\xbb\xebX\xf1&\xb8p\xf9\xa3r\x8da\xec\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\u07d4\xd0c\x8e\xa5q\x89\xa6\xa6\x99\x02J\u05ccq\xd99\xc1\xc2\xff\x8c\x89\x8e\xaeVg\x10\xfc \x00\x00\xe0\x94\xd0d\x8aX\x1b5\b\xe15\xa2\x93]\x12\xc9epE\xd8q\u028a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xd0q\x19)f\xebi\xc3R\x0f\xca:\xa4\xdd\x04)~\xa0KN\x89\x05\xf6\x8e\x811\xec\xf8\x00\x00\u07d4\xd0q\x85 \xea\xe0\xa4\xd6-p\xde\x1b\xe0\xcaC\x1c^\xea$\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd0w]\xba*\xf4\xc3\n:x6Y9\xcdq\xc2\xf9\u0795\u0489i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd0{\xe0\xf9\t\x97\xca\xf9\x03\u022c\x1dS\xcd\xe9\x04\xfb\x19\aA\x8968\x908\xb6\x99\xb4\x00\x00\u07d4\xd0~Q\x18d\xb1\u03d9i\xe3V\x06\x02\x82\x9e2\xfcNq\xf5\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u0400\x94\x98\xc5H\x04z\x1e**\xa6\xa2\x9c\xd6\x1a\x0e\xe2h\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0402'_tZ,\xac\x02v\xfb\xdb\x02\u0532\xa3\xab\x17\x11\xfe\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\u040f\xc0\x9a\x000\xfd\t(\xcd2\x11\x98X\x01\x82\xa7j\xae\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0413\xe8)\x81\x9f\xd2\xe2[\x978\x00\xbb=XA\xdd\x15-\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0414J\xa1\x85\xa13pa\xae \u071d\xd9l\x83\xb2\xbaF\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u0416V[|t\a\xd0e6X\x03U\xfd\xd6\xd29\x14J\xa1\x89\r\x8drkqw\xa8\x00\x00\u07d4\u041c\xb2\xe6\b-i:\x13\xe8\xd2\xf6\x8d\xd1\u0744a\xf5X@\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0426\xc6\xf9\xe9\u0133\x83\xd7\x16\xb3\x1d\xe7\x8dVAM\xe8\xfa\x91\x89\x10CV\x1a\x88)0\x00\x00\u07d4\u0427 \x9b\x80\xcf`\xdbb\xf5}\n]}R\x1ai`fU\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\u0428\xab\xd8\n\x19\x9bT\xb0\x8be\xf0\x1d \x9c'\xfe\xf0\x11[\x8a\x01a\xc6&\xdca\xa2\xef\x80\x00\xe0\x94\u042b\xccp\xc0B\x0e\x0e\x17/\x97\xd4;\x87\xd5\xe8\f3n\xa9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u042es]\x91^\x94hf\xe1\xfe\xa7~^\xa4f\xb5\xca\xdd\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0431\x1do+\u0394^\fjP \u00f5'S\xf8\x03\xf9\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xd0\xc1\x01\xfd\x1f\x01\xc6?k\x1d\x19\xbc\x92\r\x9f\x93#\x14\xb16\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd0\xc5Z\xbf\x97o\xdc=\xb2\xaf\u9f99\u0519HMWl\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\u0422\xadE\xf5\x9a\x9d\xcc\u0195\xd8_%\xcaF\xed1\xa5\xa3\x89-\x89W}}@ \x00\x00\u07d4\xd0\xd6,G\xea`\xfb\x90\xa3c\x92\t\xbb\xfd\xd4\xd93\x99\x1c\u0189\n\x84Jt$\xd9\xc8\x00\x00\u07d4\xd0\xdbEax o\\D0\xfe\x00Pc\x90<=zI\xa7\x89&I\x1eE\xa7S\xc0\x80\x00\u07d4\xd0\xe1\x94\xf3K\x1d\xb6\t(\x85\t\xcc\xd2\xe7;a1\xa2S\x8b\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xd0\xe3^\x04vF\xe7Y\xf4Qp\x93\xd6@\x86BQ\u007f\bM\x89\u054f\xa4h\x18\xec\u02c0\x00\u07d4\xd0\xeeM\x02\xcf$8,0\x90\xd3\xe9\x95`\xde6xs\\\u07c9\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xd0\xf0OR\x10\x9a\xeb\xec\x9a{\x1e\x932v\x1e\x9f\xe2\xb9{\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd0\xf9Yx\x11\xb0\xb9\x92\xbb}7W\xaa%\xb4\xc2V\x1d2\xe2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\x03\x02\xfa\xa1\x92\x9a2i\x04\xd3v\xbf\v\x8d\xc9:\xd0LL\x89a\t=|,m8\x00\x00\xe0\x94\xd1\x10\r\xd0\x0f\xe2\xdd\xf1\x81c\xad\x96M\vi\xf1\xf2\xe9e\x8a\x8a\x01C\x12\tU\xb2Pk\x00\x00\u07d4\xd1\x16\xf3\xdc\xd5\xdbtK\xd0\b\x88v\x87\xaa\x0e\xc9\xfdr\x92\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\x19A|Fs,\xf3M\x1a\x1a\xfby\xc3\xe7\xe2\u034e\xec\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1-w\xae\x01\xa9-5\x11{\xacpZ\xac\u0642\xd0.t\xc1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd15yK\x14\x9a\x18\xe1G\xd1nb\x1ai1\xf0\xa4\n\x96\x9a\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xd1C%8\xe3[vd\x95j\u4563*\xbd\xf0A\xa7\xa2\x1c\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xd1C\x82g#\x17\x04\xfcr\x80\xd5c\xad\xf4v8D\xa8\a\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd1S\x8e\x9a\x87\u5729\xec\x8eX&\xa5\xb7\x93\xf9\x9f\x96\xc4\u00c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd1d\x85\x03\xb1\xcc\u0178\xbe\x03\xfa\x1e\xc4\xf3\xee&~j\xdf{\x8a\x01;\xef\xbfQ\xee\xc0\x90\x00\x00\xe0\x94\xd1h,!Y\x01\x8d\xc3\xd0\u007f\b$\n\x8c`m\xafe\xf8\xe1\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xd1q\xc3\xf2%\x8a\xef5\xe5\x99\xc7\xda\x1a\xa0s\x00#M\xa9\xa6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1w\x8c\x13\xfb\xd9h\xbc\b<\xb7\xd1\x02O\xfe\x1fI\xd0,\xaa\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\xd1\u007f\xbe\"\xd9\x04b\xed7(\x06p\xa2\xea\v0\x86\xa0\xd6\u0589\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\u0441\x1cU\x97i\x80\xf0\x83\x90\x1d\x8a\r\xb2i\"-\xfb\\\xfe\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u044e\xb9\xe1\u0485\u06be\x93\xe5\u053a\xe7k\xee\xfeC\xb5!\xe8\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\u0453\xe5\x83\xd6\a\x05c\xe7\xb8b\xb9aJG\u9509\xf3\xe5\x8965f3\xeb\xd8\xea\x00\x00\u07d4\u0457\x8f.4@\u007f\xab\x1d\xc2\x18=\x95\xcf\xdab`\xb3Y\x82\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4\u045c\xaf9\xbb7\u007f\xdf,\xf1\x9b\xd4\xfbRY\x1c&1\xa6<\x8965\u026d\xc5\u07a0\x00\x00\u0794\u0463\x96\xdc\u06b2\xc7IA0\xb3\xfd0x 4\r\xfd\x8c\x1f\x88\xf9\"P\xe2\xdf\xd0\x00\x00\xe0\x94\u0467\x1b-\bX\xe82p\b]\x95\xa3\xb1T\x96P\x03^#\x8a\x03'\xbb\t\xd0j\xa8P\x00\x00\u07d4\u046c\xb5\xad\xc1\x189s%\x8dk\x85$\xff\xa2\x8f\xfe\xb2=\xe3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0473\u007f\x03\xcb\x10t$\xe9\xc4\xddW\\\xcdOL\xeeW\xe6\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\u0475\xa4T\xac4\x05\xbbAy \x8cl\x84\xde\x00k\u02db\xe9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xc4YT\xa6+\x91\x1a\xd7\x01\xff.\x90\x13\x1e\x8c\xeb\x89\xc9\\\x89K\x91\xa2\xdeE~\x88\x00\x00\u07d4\xd1\xc9np\xf0Z\xe0\xe6\xcd`!\xb2\b7P\xa7q|\xdeV\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\u0571\u007f\xfe-{\xbby\xcc}y0\xbc\xb2\xe5\x18\xfb\x1b\xbf\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd1\xda\f\x8f\xb7\xc2\x10\xe0\xf2\xeca\x8f\x85\xbd\xae}>sK\x1c\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd1\xddy\xfb\x15\x81`\xe5\xb4\xe8\xe2?1.j\x90\u007f\xbcMN\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xdeZ\xad:_\xd8\x03\U00071bb6\x10<\xb8\xe1O\xe7#\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd1\xe1\xf2\xb9\xc1l0\x98t\xde\xe7\xfa\xc3&u\xaf\xf1)\u00d8\x89\x03\xf2M\x8eJ\x00p\x00\x00\xe0\x94\xd1\xe5\xe24\xa9\xf4Bf\xa4\xa6$\x1a\x84\u05e1\xa5Z\u0567\xfe\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd1\xeaMr\xa6{[>\x0f1UY\xf5+\xd0aMq0i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1\xee\x90YW\xfe|\xc7\x0e\xc8\xf2\x86\x8bC\xfeG\xb1?\xeb\xff\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\xd1\xf1iM\"g\x1bZ\xadj\x94\x99\\6\x9f\xbea3go\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\xf4\xdc\x1d\u06ca\xbb\x88H\xa8\xb1N%\xf3\xb5Z\x85\x91\xc2f\x89\r\x8drkqw\xa8\x00\x00\u07d4\xd1\xfe\u042e\xe6\xf5\xdf\xd7\xe2Wi%L<\xfa\xd1Z\xde\u032a\x89'\x92\xc8\xfcKS(\x00\x00\u07d4\xd2\x05\x1c\xb3\xcbg\x04\xf0T\x8c\u0210\xab\n\x19\xdb4\x15\xb4*\x89\x12\x1b.^ddx\x00\x00\u07d4\xd2\x06\xaa\u07736\xd4^yr\xe9<\xb0uG\x1d\x15\x89{]\x89 \x86\xac5\x10R`\x00\x00\u07d4\xd2\tH+\xb5I\xab\xc4w{\xeam\u007fe\x00b\xc9\xc5z\x1c\x89\x11e\x1a\xc3\xe7\xa7X\x00\x00\u07d4\xd2\r\xcb\vxh+\x94\xbc0\x00(\x14H\xd5W\xa2\v\xfc\x83\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4\xd2\x10{57&\u00e2\xb4ef\xea\xa7\xd9\xf8\v]!\xdb\xe3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\x11\xb2\x1f\x1b\x12\xb5\ta\x81Y\r\xe0~\xf8\x1a\x89S~\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd2\x18\xef\xb4\u06d8\x1c\xddjy\u007fK\u050c|&)<\xeb@\x89\xa1Fk1\xc6C\x1c\x00\x00\xe0\x94\xd2\x1asA\xeb\x84\xfd\x15\x10T\xe5\u31fb%\xd3nI\x9c\t\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2$\xf8\x80\xf9G\x9a\x89\xd3/\t\xe5+\u9432\x88\x13\\\xef\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xd2/\f\xa4\xcdG\x9ef\x17u\x05;\xccI\xe3\x90\xf6p\u074a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd21\x92\x975\x13!\x02G\x1b\xa5\x90\a\xb6dL\xc0\xc1\xde>\x8967\tlK\xcci\x00\x00\u07d4\xd25\xd1\\\xb5\xec\xee\xbba)\x9e\x0e\x82\u007f\xa8'H\x91\x1d\x89\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2:$\xd7\xf9F\x83C\xc1C\xa4\x1ds\xb8\x8f|\xbec\xbe^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2=z\xff\xac\xdc>\x9f=\xaez\xfc\xb4\x00oX\xf8\xa4F\x00\x89\xc3(\t>a\xee@\x00\x00\u07d4\xd2C\x18L\x80\x1e]y\xd2\x06?5x\u06ee\x81\u7ce9\u02c9k\u0722h\x1e\x1a\xba\x00\x00\u07d4\xd2KfD\xf49\xc8\x05\x1d\xfcd\u04c1\xb8\xc8lu\xc1u8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2K\xf1--\xdfE}\xec\xb1xt\xef\xde R\xb6\\\xbbI\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2Q\xf9\x03\xae\x18rrY\xee\xe8A\xa1\x89\xa1\xf5i\xa5\xfdv\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd2R\x96\v\v\xf6\xb2\x84\x8f\u07ad\x80\x13m\xb5\xf5\a\xf8\xbe\x02\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2X\x1aU\xce#\xab\x10\u062d\x8cD7\x8fY\a\x9b\xd6\xf6X\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xd2Z\xec\xd7\xeb\x8b\xd64[\x06;]\xbd'\x1cw\xd3QD\x94\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xd2|#O\xf7\xac\xca\xce=\x99g\b\xf8\xf9\xb0Ip\xf9}6\x89Hz\x9a0E9D\x00\x00\u07d4\u0482\x98RM\xf5\xecK$\xb0\xff\xb9\u07c5\x17\n\x14Z\x9e\xb5\x89\x0f\x98\xa3\xb9\xb37\xe2\x00\x00\xe0\x94\u0483\xb8\xed\xb1\n%R\x8aD\x04\xde\x1ce\xe7A\r\xbc\xaag\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u0484\xa5\x03\x82\xf8:am9\xb8\xa9\xc0\xf3\x96\xe0\ubfe9]\x8966\xc2^f\xec\xe7\x00\x00\u07d4\u0488\xe7\xcb{\xa9\xf6 \xab\x0ftR\xe5\bc=\x1cZ\xa2v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u049d\xc0\x8e\xfb\xb3\xd7.&?x\xabv\x10\xd0\"m\xe7k\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u04a00\xac\x89R2_\x9e\x1d\xb3x\xa7\x14\x85\xa2N\x1b\a\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u04a4y@CG\xc5T:\xab)*\xe1\xbbJo\x15\x83W\xfa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u04a5\xa0$#\nW\xcc\xc6fv\v\x89\xb0\xe2l\xaf\u0449\u01ca\n\x96YZ\\n\x8a?\x80\x00\u07d4\u04a8\x03'\xcb\xe5\\L{\xd5\x1f\xf9\xdd\xe4\xcad\x8f\x9e\xb3\xf8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u04a8Oug\\b\xd8\f\x88ulB\x8e\xee+\xcb\x18T!\x89A\rXj \xa4\xc0\x00\x00\u07d4\u04ab\xd8J\x18\x10\x93\xe5\xe2)\x13oB\xd85\xe8#]\xe1\t\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\u04ac\r:X`^\x1d\x0f\x0e\xb3\xde%\xb2\xca\xd1)\xed`X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u04bfg\xa7\xf3\xc6\xceV\xb7\xbeAg]\xbb\xad\xfe~\xa9:3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd2\xdb\xeb\xe8\x9b\x03W\xae\xa9\x8b\xbe\x8eIc8\u07bb(\xe8\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2\xe2\x1e\xd5hh\xfa\xb2\x8e\tG\x92z\xda\xf2\x9f#\xeb\xadl\x89l\x18O\x13U\xd0\xe8\x00\x00\u07d4\xd2\xe8\x17s\x8a\xbf\x1f\xb4\x86X?\x80\xc3P1\x8b\xed\x86\f\x80\x89\r\x02\xce\xcf_]\x81\x00\x00\u07d4\xd2\xed\xd1\xdd\xd6\xd8m\xc0\x05\xba\xebT\x1d\"\xb6@\xd5\xc7\xca\xe5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\xf1\x99\x8e\x1c\xb1X\f\xecOl\x04}\xcd=\xce\xc5L\xf7<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2\xf2A%]\xd7\xc3\xf7<\a\x040q\xec\b\xdd\xd9\xc5\xcd\xe5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd2\xffg \x16\xf6;/\x859\x8fJo\xed\xbb`\xa5\r<\u0389\x12\x91$o[sJ\x00\x00\u07d4\xd3\rLC\xad\xcfU\xb2\xcbS\u0583#&A4I\x8d\x89\u038965\u026d\xc5\u07a0\x00\x00\u07d4\xd3\x0e\xe9\xa1+Mh\xab\xac\xe6\xba\u029a\u05ff\\\xd1\xfa\xf9\x1c\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xd3\x11\x8e\xa3\xc85\x05\xa9\u0613\xbbg\xe2\xde\x14-Sz>\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd3\x11\xbc\u05eaN\x9bO8?\xf3\xd0\u05b6\xe0~!\xe3p]\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd3\x15\xde\xea\x1d\x8c\x12q\xf9\xd11\x12c\xabG\xc0\a\xaf\xb6\xf5\x89\x03\xc8\x1dNeK@\x00\x00\u07d4\xd3+,y\xc3dx\xc5C\x19\x01\xf6\xd7\x00\xb0M\xbe\x9b\x88\x10\x89\x15w\x9a\x9d\xe6\xee\xb0\x00\x00\u07d4\xd3+EVF\x14Ql\x91\xb0\u007f\xa9\xf7-\xcfx|\xceN\x1c\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\xd30r\x811\xfe\x8e:\x15Hz4W<\x93E~*\xfe\x95\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd31\xc8#\x82Z\x9eRc\xd0R\u0611]M\xcd\xe0z\\7\x89\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d4\xd33btE\xf2\u05c7\x90\x1e\xf3;\xb2\xa8\xa3g^'\xff\xec\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3<\xf8+\xf1LY&@\xa0\x86\b\x91L#py\u057e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3Mp\x8ds\x98\x02E3\xa5\xa2\xb20\x9b\x19\xd3\xc5Qq\xbb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3N\x03\xd3j+\xd4\u045a_\xa1b\x18\xd1\xd6\x1e?\xfa\v\x15\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\xd3Pu\xcaa\xfeY\xd1#\x96\x9c6\xa8-\x1a\xb2\xd9\x18\xaa8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xd3g\x00\x9a\xb6X&;b\xc23:\x1c\x9eA@I\x8e\x13\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3g\x9aG\xdf-\x99\xa4\x9b\x01\u024d\x1c>\f\x98|\xe1\xe1X\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u04cf\xa2\xc4\xcc\x14z\xd0j\u0562\xf7Uy(\x1f\"\xa7\xcc\x1f\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\u04da]\xa4`9+\x94\v\u01ee8\xf1e\u007f\x8a\x01f\xc5H\b\x89\xdbw\x00\x00\xe0\x94\xd3\xd6\xe9\xfb\x82T/\u049e\xd9\xea6\t\x89\x1e\x15\x13\x96\xb6\xf7\x8a\voX\x8a\xa7\xbc\xf5\xc0\x00\x00\xe0\x94\xd3\xda\u0476\u040dE\x81\u032ee\xa8s-\xb6\xaci\xf0\u019e\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xd3\xdf;S\xcb;GU\xdeT\xe1\x80E\x1c\xc4L\x9e\x8a\u0a89#\u0114\t\xb9w\x82\x80\x00\u07d4\xd3\xf8s\xbd\x99V\x13W\x89\xab\x00\xeb\xc1\x95\xb9\"\xe9K%\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\x02\xb4\xf6\xa0\x99\xeb\xe7\x16\xcb\x14\xdfOy\xc0\xcd\x01\xc6\a\x1b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd4\r\x00U\xfd\x9a8H\x8a\xff\x92?\xd0=5\xecF\xd7\x11\xb3\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xd4\x0e\xd6j\xb3\xce\xff$\xca\x05\xec\xd4q\ufd12\xc1__\xfa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4\x18\x87\v\xc2\xe4\xfa{\x8aa!\xae\br\xd5RG\xb6%\x01\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xd4\x1d\u007f\xb4\x9f\xe7\x01\xba\xac%qpBl\u0273\x8c\xa3\xa9\xb2\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xd4 U\x92\x84@U\xb3\u01e1\xf8\f\xef\xe3\xb8\xebP\x9b\xcd\xe7\x89\t\xb3\xbf\xd3B\xa9\xfc\x80\x00\u07d4\xd4+ \xbd\x03\x11`\x8bf\xf8\xa6\xd1[*\x95\xe6\xde'\u017f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd44O}\\\xade\xd1~\\-\x0es#\x94=ob\xfe\x92\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xd4>\xe48\xd8=\xe9\xa3ub\xbbN(l\xb1\xbd\x19\xf4\x96M\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd4C4\xb4\xe2:\x16\x9a\f\x16\xbd!\xe8f\xbb\xa5-\x97\x05\x87\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94\xd4M\x81\xe1\x8fF\xe2\u03f5\xc1\xfc\xf5\x04\x1b\xc8V\x97g\xd1\x00\x8a\a\xb4B\xe6\x84\xf6Z\xa4\x00\x00\u07d4\xd4OJ\xc5\xfa\xd7k\xdc\x157\xa3\xb3\xafdr1\x9bA\r\x9d\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xd4O^\xdf+\xcf$3\xf2\x11\xda\xdd\f\xc4P\xdb\x1b\x00\x8e\x14\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xd4Oj\u00d2;_\xd71\xa4\xc4YD\xecO~\xc5*j\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd4[3A\xe8\xf1\\\x802\x93 \u00d7~;\x90\xe7\x82j~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4]]\xaa\x13\x8d\xd1\xd3t\xc7\x1b\x90\x19\x91h\x11\xf4\xb2\nN\x89\x1f9\x9b\x148\xa1\x00\x00\x00\u07d4\xd4`\xa4\xb9\b\xdd+\x05gY\xb4\x88\x85\vf\xa88\xfcw\xa8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd4g\xcf\x06L\bq\x98\x9b\x90\u0632\xeb\x14\xcc\xc6;6\b#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd4k\xaea\xb0'\xe5\xbbB.\x83\xa3\xf9\xc9?<\x8f\xc7}'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4o\x82#E)\x82\xa1\xee\xa0\x19\xa8\x81n\xfc-o\xc0\ah\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xd4uG\u007f\xa5c\x90\xd30\x17Q\x8dg\x11\x02\u007f\x05\U0008dfc9k\x11\x133\xd4\xfdL\x00\x00\u07d4\xd4|$.\xdf\xfe\xa0\x91\xbcT\xd5}\xf5\xd1\xfd\xb91\x01Gl\x89\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4\xd4}\x86\x85\xfa\xee\x14|R\x0f\u0646p\x91u\xbf/\x88k\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\u007fP\u07c9\xa1\xcf\xf9e\x13\xbe\xf1\xb2\xae:)q\xac\xcf,\x89-\x89W}}@ \x00\x00\u07d4\u0502\xe7\xf6\x8eA\xf28\xfeQx)\xde\x15G\u007f\xe0\xf6\xdd\x1d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u0507\x9f\xd1+\x1f:'\xf7\xe1\tv\x1b#\xca4\xfa#\x06K\x1c\xaf\x00Qn(pJ\x82\xa4\xf8\x89Hz\x9a0E9D\x00\x00\u07d4\xd5\x00\xe4\xd1\u0242K\xa9\xf5\xb65\u03e3\xa8\xc2\u00cb\xbdL\xed\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5\b\u04dcp\x91oj\xbcL\xc7\xf9\x99\xf0\x11\xf0w\x10X\x02\x89\x05rM$\xaf\xe7\u007f\x00\x00\u07d4\xd5\x0f\u007f\xa0>8\x98v\u04d0\x8b`\xa57\xa6pc\x04\xfbV\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd5\x13\xa4P\x80\xff/\xeb\xe6,\u0545J\xbe)\xeeDg\xf9\x96\x89\bN\x13\xbcO\xc5\xd8\x00\x00\u07d4\xd5'o\f\xd5\xff\xd5\xff\xb6?\x98\xb5p=U\x94\xed\xe0\x83\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5)KfbB0;m\xf0\xb1\u020d7B\x9b\xc8\xc9e\xaa\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xd5*\xec\xc6I98\xa2\x8c\xa1\xc3g\xb7\x01\xc2\x15\x98\xb6\xa0.\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xd5\x99x\xee \xa3\x8c?I\x8dc\xd5\u007f1\xa3\x9fj\x06\x8a\x022\xb3o\xfcg*\xb0\x00\x00\u07d4\u05568\xd3\xc5\xfa\xa7q\x1b\xf0\x85t_\x9d[\xdc#\u0518\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u055d\x92\xd2\xc8p\x19\x80\xcc\a<7]r\n\xf0dt<\f\x8a\x04\x05\xfd\xf7\u5bc5\xe0\x00\x00\u07d4\u0567\xbe\xc32\xad\xde\x18\xb3\x10KW\x92Tj\xa5\x9b\x87\x9bR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0571\x17\xec\x11n\xb8FA\x89a\xeb~\xdbb\x9c\xd0\xddi\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u0572\x84\x04\x010\xab\xf7\xc1\xd1cq#q\xcc~(\xadf\u0689j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0579\xd2w\u062a\xd2\x06\x97\xa5\x1fv\xe2\tx\x99k\xff\xe0U\x89\a\xc3\xfe<\aj\xb5\x00\x00\u07d4\u057d^\x84U\xc10\x16\x93W\xc4q\xe3\u6077\x99jrv\x89-\x9e(\x8f\x8a\xbb6\x00\x00\u07d4\xd5\u02e5\xb2k\xea]s\xfa\xbb\x1a\xba\xfa\xcd\xef\x85\xde\xf3h\u0309\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd5\xceU\u0476/YC\xc0?\x89\b\xe3\x1f\xe1h\x9d\x8a\x00\x00\u07d4\xd6\x06Q\xe3\x93x4#\xe5\xcc\x1b\xc5\xf8\x89\xe4N\xf7\xea$>\x89\x15\x9ev7\x11)\xc8\x00\x00\u07d4\xd6\t\xbfO\x14n\xeak\r\xc8\xe0m\xdc\xf4D\x8a\x1f\xcc\xc9\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\t\xec\v\xe7\r\n\xd2ong\xc9\xd4v+R\xeeQ\x12,\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6\nRX\a(R\r\xf7Tk\xc1\xe2\x83)\x17\x88\u06ee\f\x8964\x89\xef?\xf0\xd7\x00\x00\u07d4\xd6\v$s!\xa3*Z\xff\xb9k\x1e'\x99'\xccXM\xe9C\x89z\xd0 \xd6\xdd\xd7v\x00\x00\u07d4\xd6\x11\x02v\xcf\xe3\x1eB\x82ZW\u007fkC]\xbc\xc1\f\xf7d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd6\x12Y{\xc3\x17C\u01c63\xf63\xf29\xb1\xe9Bk\xd9%\x8a\x10\x17\xf7\u07d6\xbe\x17\x80\x00\x00\u07d4\xd6#J\xafE\xc6\xf2.f\xa2%\xff\xb9:\xddb\x9bN\xf8\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6.\u06d6\xfc\u259a\xaflT^\x96|\xf1\xc0\xbc\x80R\x05\x89\x04\xa5eSjZ\u0680\x00\u07d4\xd60\v2\x15\xb1\x1d\xe7b\xec\xdeKp\xb7\x92}\x01)\x15\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd69]\xb5\xa4\xbbf\xe6\x0fL\xfb\xcd\xf0\x05{\xb4\xd9xb\xe2\x891T\xc9r\x9d\x05x\x00\x00\xe0\x94\xd6J-P\xf8\x85\x857\x18\x8a$\xe0\xf5\r\xf1h\x1a\xb0~\u05ca\b7Z*\xbc\xca$@\x00\x00\u07d4\xd6X\n\xb5\xedL}\xfaPo\xa6\xfed\xad\\\xe1)pw2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6Y\x8b\x13\x86\xe9<\\\u02d6\x02\xffK\xbb\xec\xdb\xd3p\x1d\u0109\f%\xf4\xec\xb0A\xf0\x00\x00\u07d4\xd6dM@\xe9\v\xc9\u007f\xe7\xdf\xe7\u02bd2i\xfdW\x9b\xa4\xb3\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\xe0\x94\xd6g\f\x03m\xf7T\xbeC\xda\u074fP\xfe\xea(\x9d\x06\x1f\u058a\x01D\xa2\x904H\xce\xf7\x80\x00\u07d4\xd6hR:\x90\xf0)=e\xc58\xd2\xddlWg7\x10\x19n\x89\x02$,0\xb8S\xee\x00\x00\u07d4\xd6j\xb7\x92\x94\aL\x8bb}\x84-\xabA\xe1}\xd7\f]\xe5\x8965\u026d\xc5\u07a0\x00\x00\u0794\xd6j\xcc\r\x11\xb6\x89\u03a6\xd9\xea_\xf4\x01L\"J]\xc7\u0108\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xd6m\xdf\x11Y\xcf\"\xfd\x8czK\xc8\u0540wV\xd43\xc4>\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\u0587\xce\xc0\x05\x90\x87\xfd\xc7\x13\xd4\xd2\xd6^w\xda\xef\xed\xc1_\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u0588\xe7\x85\u024f\x00\xf8K:\xa1S3U\u01e2X\xe8yH\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u05a2.Y\x8d\xab\u04ce\xa6\xe9X\xbdy\u050d\u0756\x04\xf4\u07c965\u026d\xc5\u07a0\x00\x00\u07d4\u05a7\xacM\xe7\xb5\x10\xf0\xe8\xdeQ\x9d\x97?\xa4\xc0\x1b\xa84\x00\x89e\xea=\xb7UF`\x00\x00\u07d4\u05ac\xc2 \xba.Q\xdf\xcf!\xd4C6\x1e\xeav\\\xbd5\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05ac\xff\u043f\u065c8.{\xd5o\xf0\xe6\x14J\x9eR\xb0\x8e\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xd6\xc0\u043c\x93\xa6.%qtp\x0e\x10\xf0$\u0232?\x1f\x87\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd6\xcf\\\x1b\u03dd\xa6b\xbc\xea\"U\x90P\x99\xf9\xd6\xe8M\u030a\x01\u011eB\x01W\xd9\xc2\x00\x00\u07d4\xd6\xd05r\xa4RE\xdb\xd46\x8cO\x82\xc9W\x14\xbd!g\xe2\x89?\x00\xc3\xd6f\x86\xfc\x00\x00\u07d4\xd6\xd6wiX\xee#\x14:\x81\xad\xad\xeb\b8 \t\xe9\x96\u0089\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd6\xd9\xe3\x0f\bB\x01*qv\xa9\x17\xd9\xd2\x04\x8c\xa0s\x87Y\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6\xe0\x9e\x98\xfe\x13\x003!\x04\xc1\xca4\xfb\xfa\xc5T6N\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\xe8\xe9z\u90db\x9e\xe5\a\xee\xdb(\xed\xfbtw\x03\x149\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\uea18\u052e+q\x80'\xa1\x9c\xe9\xa5\xebs\x00\xab\xe3\u0289\x01}J\xce\xeec\u06c0\x00\xe0\x94\xd6\xf1\xe5[\x16\x94\b\x9e\xbc\xb4\xfe}x\x82\xaaf\u0217av\x8a\x04<#\xbd\xbe\x92\x9d\xb3\x00\x00\u07d4\xd6\xf4\xa7\xd0N\x8f\xaf \xe8\xc6\ub15c\xf7\xf7\x8d\xd2=z\x15\x89\a$\xde\xd1\xc7H\x14\x00\x00\u07d4\xd6\xfc\x04F\u01a8\xd4\n\xe3U\x1d\xb7\xe7\x01\xd1\xfa\x87nJI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x03\u01a4\xf1\x1d`\x19Ey\u054c'f\xa7\xef\x16\xc3\n)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x05%\x19uj\xf4%\x90\xf1S\x91\xb7#\xa0?\xa5d\xa9Q\x89\xfa61H\r\x01\xfd\x80\x00\u07d4\xd7\na+\xd6\u0769\xea\xb0\xdd\xdc\xffJ\xafA\"\u04cf\xea\xe4\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xd7\n\xd2\xc4\xe9\uefe67\xefV\xbdHj\u04a1\xe5\xbc\xe0\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x14\f\x8eZC\a\xfa\xb0\xcc'\xba\u0752\x95\x01\x8b\xf8yp\x89\x05\xf1\x01kPv\xd0\x00\x00\u07d4\xd7\x16J\xa2a\xc0\x9a\u0672\xb5\x06\x8dE>\xd8\xebj\xa10\x83\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\x1eC\xa4Qw\xadQ\xcb\xe0\xf7!\x84\xa5\xcbP9\x17(Z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x1f\xb10\xf0\x15\fVRi\xe0\x0e\xfbC\x90+R\xa4U\xa6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\"W8\xdc\xf3W\x848\xf8\xe7\u0233\x83~B\xe0J&/\x89\x18+\x8c\ubec3\xaa\x00\x00\u07d4\xd7'MP\x80M\x9cw\u0693\xfaH\x01V\xef\xe5{\xa5\x01\u0789i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd71\xbbk_<79^\t\u03ac\xcd\x14\xa9\x18\xa6\x06\a\x89\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xd7>\xd2\u0645\xb5\xf2\x1bU\xb2td;\xc6\xda\x03\x1d\x8e\u074d\x8a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xd7D\xac~S\x10\xbeijc\xb0\x03\xc4\v\xd097\x05a\u0189Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\xd7Jn\x8dj\xab4\u0385\x97h\x14\xc12{\xd6\xea\a\x84\u048a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xd7ZP*[gr\x87G\x0fe\u016aQ\xb8|\x10\x15\x05r\x8910\xb4dc\x85t\x00\x00\u07d4\xd7m\xba\xeb\xc3\rN\xf6{\x03\xe6\xe6\xec\xc6\xd8N\x00MP-\x89mv\xb9\x18\x8e\x13\x85\x00\x00\u07d4\xd7q\xd9\xe0\u028a\b\xa1\x13wW1CN\xb3'\x05\x99\xc4\r\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7x\x8e\xf2\x86X\xaa\x06\xccS\xe1\xf3\xf0\xdeX\xe5\xc3q\xbex\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xd7x\x92\xe2';#]v\x89\xe40\xe7\xae\ud73c\xe8\xa1\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u05c1\xf7\xfc\t\x18F\x11V\x85p\xb4\x98n,r\x87+~\u0409\x01\x15\x95a\x06]]\x00\x00\u07d4\u05c5\xa8\xf1\x8c8\xb9\xbcO\xfb\x9b\x8f\xa8\xc7r{\xd6B\xee\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u05ce\xcd%\xad\xc8k\xc2\x05\x1d\x96\xf6Sd\x86kB\xa4&\xb7\x89\xd20X\xbf/&\x12\x00\x00\xe0\x94\u05cf\x84\xe3\x89D\xa0\xe0%_\xae\xceH\xbaIP\u053d9\u048a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u05d4\x83\xf6\xa8DO%I\xd6\x11\xaf\xe0,C-\x15\xe1\x10Q\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05d85\xe4\x04\xfb\x86\xbf\x84_\xba\t\rk\xa2^\f\x88f\xa6\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\u05da\xff\x13\xba-\xa7]F$\f\xac\n$g\xc6V\x94\x98#\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\u05dd\xb5\xabCb\x1az=\xa7\x95\xe5\x89)\xf3\xdd%\xafg\u0649lj\xccg\u05f1\xd4\x00\x00\u07d4\u05e1C\x1e\xe4S\xd1\xe4\x9a\x05P\xd1%hy\xb4\xf5\xd1\x02\x01\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\u05ed\t\xc6\xd3&WhSU\xb5\xc6\uc39fW\xb4\ube42\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u05f7@\xdf\xf8\xc4Wf\x8f\xdft\xf6\xa2f\xbf\xc1\u0737#\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7\u0080>\u05f0\xe0\x83sQA\x1a\x8ef7\xd1h\xbc[\x05\x8a\x06A\xda\xf5\xc9\x1b\xd95\x80\x00\u07d4\xd7\xc6&]\xea\x11\x87l\x90;q\x8eL\u062b$\xfe&[\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xca\u007f\xdc\xfe\xbeE\x88\xef\xf5B\x1d\x15\"\xb6\x13(\xdf{\xf3\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4\xd7\u037dA\xff\xf2\r\xf7'\xc7\vbU\xc1\xbav\x06\x05Th\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xd1W\xe4\xc0\xa9d7\xa6\u0485t\x1d\xd2>\xc46\x1f\xa3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xd2\xc6\xfc\xa8\xad\x1fu9R\x10\xb5}\xe5\xdf\xd6s\x939\t\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94\xd7\xd3\xc7Y Y\x048\xb8,>\x95\x15\xbe.\xb6\xedz\x8b\x1a\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\xd7\xd7\xf2\u02a4b\xa4\x1b;0\xa3J\xeb;\xa6\x10\x10\xe2bo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xe7J\xfd\xba\xd5^\x96\u03bcZ7O,\x8bv\x86\x80\xf2\xb0\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\xe0\x94\xd7\xeb\x901b'\x1c\x1a\xfa5\xfei\xe3s\"\u0224\u049b\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd7\xeb\u0779\xf99\x87w\x9bh\x01U7T8\xdbe\xaf\xcbj\x89\x05t\x1a\xfe\xff\x94L\x00\x00\u07d4\xd7\xef4\x0ef\xb0\u05ef\xcc\xe2\n\x19\xcb{\xfc\x81\xda3\xd9N\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\xf3p\u053e\xd9\xd5|oI\u0259\xder\x9e\xe5i\xd3\xf4\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xfa_\xfb`H\xf9o\xb1\xab\xa0\x9e\xf8{\x1c\x11\xddp\x05\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\x06\x9f\x84\xb5!I?G\x15\x03\u007f2&\xb2_3\xb6\x05\x86\x89g\x8a\x93 b\xe4\x18\x00\x00\u0794\xd8\x15\xe1\xd9\xf4\xe2\xb5\xe5~4\x82k|\xfd\x88\x81\xb8Th\x90\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xd8\x1b\xd5K\xa2\xc4Jok\xeb\x15a\u058b\x80\xb5DNm\u0189?\x17\r~\xe4\"\xf8\x9c\x80-1({\x96q\xe8\x1c\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xd8K\x92/xA\xfcWt\xf0\x0e\x14`J\xe0\xdfB\xc8U\x1e\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xd8U\xb0<\xcb\x02\x9awG\xb1\xf0s\x03\xe0\xa6dy59\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8_\u07af*a\xf9]\xb9\x02\xf9\xb5\xa5<\x9b\x8f\x92f\u00ec\x89l\xf6Z~\x90G(\x00\x00\u07d4\xd8q^\xf9\x17o\x85\v.0\xeb\x8e8'\a\xf7w\xa6\xfb\xe9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8t\xb9\u07eeEj\x92\x9b\xa3\xb1\xa2~W,\x9b,\xec\u07f3\x89\t79SM(h\x00\x00\u07d4\u0613\n9\xc7sW\xc3\n\u04e0`\xf0\v\x06\x04c1\xfdb\x89,s\xc97t,P\x00\x00\u07d4\u061b\xc2q\xb2{\xa3\xabib\xc9JU\x90\x06\xae8\xd5\xf5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0637}\xb9\xb8\x1b\xbe\x90B{b\xf7\x02\xb2\x01\xff\u009f\xf6\x18\x892m\x1eC\x96\xd4\\\x00\x00\u07d4\xd8\xcdd\xe0(N\xecS\xaaF9\xaf\xc4u\b\x10\xb9\u007f\xabV\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xd6C\x84$\x9bwg\x94\x06;V\x98x\xd5\xe3\xb50\xa4\xb2\x89\t\xa0C\u0432\xf9V\x80\x00\u07d4\xd8\xd6T \xc1\x8c#'\xccZ\xf9t%\xf8W\xe4\xa9\xfdQ\xb3\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xd8\xe5\xc9g^\xf4\xde\xed&k\x86\x95o\xc4Y\x0e\xa7\u0522}\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd8\xe8GB\x92\xe7\xa0Q`L\xa1d\xc0pw\x83\xbb(\x85\xe8\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\u07d4\xd8\xebxP>\xc3\x1aT\xa9\x016x\x1a\xe1\t\x00Lt2W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\xee\xf4\xcfK\xeb\x01\xee \xd1\x11t\x8ba\xcbM?d\x1a\x01\x89\x94\x89#z\u06daP\x00\x00\u07d4\xd8\xf4\xba\xe6\xf8M\x91\rm}Z\xc9\x14\xb1\xe6\x83r\xf9A5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd8\xf6 6\xf0;v5\xb8X\xf1\x10?\x8a\x1d\x90\x19\xa8\x92\xb6\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xd8\xf6e\xfd\x8c\xd5\u00bc\xc6\xdd\xc0\xa8\xaeR\x1eM\u01aa``\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xd8\xf9$\fU\xcf\xf05RB\x80\xc0\x9e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xfe\b\x8f\xff\u0394\x8fQ7\xee#\xb0\x1d\x95\x9e\x84\xacB#\x89\f[T\xa9O\xc0\x17\x00\x00\u07d4\xd9\x0f0\t\xdbC~N\x11\u01c0\xbe\u0209os\x8de\xef\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\x10;\xb6\xb6zU\xa7\xfe\xce-\x1a\xf6-E|!x\x94m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\x13\xf0w\x19Iu\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd9D\u0226\x9f\xf2\xca\x12Ii\f\x12)\xc7\x19/6%\x10b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd9JW\x88*Rs\x9b\xbe*\x06G\xc8\f$\xf5\x8a+O\x1c\x89H\xb5N*\xdb\xe1+\x00\x00\xe0\x94\xd9SB\x95<\x8a!\xe8\xb65\xee\xfa\u01c1\x9b\xea0\xf1pG\x8a\x13\xf0l\u007f\xfe\xf0]@\x00\x00\u07d4\xd9\\\x90\xff\xbeT\x84\x86G\x80\xb8gIJ\x83\u0212V\xd6\xe4\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xd9g\x11T\x0e.\x99\x83C\xd4\xf5\x90\xb6\xfc\x8f\xac;\xb8\xb3\x1d\x89_Z@h\xb7\x1c\xb0\x00\x00\u07d4\xd9j\xc2Pt\t\u01e3\x83\xab.\xee\x18\"\xa5\xd78\xb3kV\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd9m\xb3;{Z\x95\f>\xfa-\xc3\x1b\x10\xba\x10\xa52\uf1c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd9wYe\xb7\x16Gfu\xa8\xd5\x13\xeb\x14\xbb\xf7\xb0|\xd1J\x8a\x01\x13.m-#\xc5\xe4\x00\x00\u07d4\xd9{\xc8J\xbdG\xc0[\xbfE{.\xf6Y\xd6\x1c\xa5\xe5\u43c9\x06\x9d\x17\x11\x9d\u0168\x00\x00\u07d4\xd9\u007fE&\u07a9\xb1c\xf8\xe8\xe3:k\u03d2\xfb\x90}\xe6\xec\x89\x0feJ\xafM\xb2\xf0\x00\x00\u07d4\xd9\u007f\xe6\xf5?*X\xf6\xd7mu*\xdft\xa8\xa2\xc1\x8e\x90t\x89\x10\xcd\xf9\xb6\x9aCW\x00\x00\u07d4\u0659\x99\xa2I\r\x94\x94\xa50\xca\xe4\xda\xf3\x85T\xf4\xddc>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\u065d\xf7B\x1b\x93\x82\xe4,\x89\xb0\x06\xc7\xf0\x87p*\aW\xc0\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\xe0\x94\u0677\x83\xd3\x1d2\xad\xc5\x0f\xa3\xea\u02a1]\x92\xb5h\xea\xebG\x8a\a3\xaf\x907L\x1b(\x00\x00\u07d4\xd9\xd3p\xfe\xc65v\xab\x15\xb3\x18\xbf\x9eX6M\u00a3U*\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xd9\xd4/\xd1>\xbdK\xf6\x9c\xac^\x9c~\x82H:\xb4m\xd7\xe9\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xd9\xe2~\xb0}\xfcq\xa7\x06\x06\f\u007f\a\x928\u0293\xe8\x859\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\xe3\x85~\xfd\x1e *D\x17p\xa7w\xa4\x9d\xccE\xe2\xe0\u04c9\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xd9\xec.\xfe\x99\xff\\\xf0\r\x03\xa81{\x92\xa2J\xefD\x1f~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd9\xec\x8f\xe6\x9bw\x16\xc0\x86Z\xf8\x88\xa1\x1b+\x12\xf7 \xed3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\xf1\xb2d\b\xf0\xecg\xad\x1d\ro\xe2.\x85\x15\xe1t\x06$\x89\x01M\x11 \u05f1`\x00\x00\u07d4\xd9\xf5G\xf2\xc1\xde\x0e\u064aS\xd1a\xdfWc]\xd2\x1a\x00\xbd\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xd9\xff\x11]\x01&l\x9fs\xb0c\xc1\xc28\xef5e\xe6;6\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xda\x06\x04N)#&\xffil\x0091h\xceF\xff\xac9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda*\x14\xf9r@\x15\u05d0\x14\xed\x8eY\th\x1dYaH\xf1\x89\x02\xa1\x0f\x0f\x8a\x91\xab\x80\x00\u07d4\xda*\u054ew\xde\xdd\xed\xe2\x18vF\xc4e\x94Z\x8d\xc3\xf6A\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\xda0\x17\xc1P\xdd\r\xce\u007f\u03c8\x1b\nH\xd0\xd1\xc7V\xc4\u01c9\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\xda4\xb2\xea\xe3\v\xaf\xe8\xda\xec\xcd\xe8\x19\xa7\x94\u0349\xe0\x95I\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdaJ_U\u007f;\xab9\n\x92\xf4\x9b\x9b\x90\n\xf3\fF\xae\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdaPU7S\u007f\xfb3\xc4\x15\xfe\xc6Ni\xba\xe0\x90\xc5\xf6\x0f\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdai\x8dd\xc6\\\u007f+,rS\x05\x9c\xd3\u0441\u0619\xb6\xb7\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\u07d4\xdaw2\xf0/.'.\xaf(\u07d7.\xcc\r\xde\xed\x9c\xf4\x98\x89\v \xbf\xbfig\x89\x00\x00\u07d4\xdaz\xd0%\xeb\xde%\xd2\"C\u02c3\x0e\xa1\xd3\xf6JVc#\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\u0685]SG\u007fP^\xc4\xc8\xd5\u8ed1\x80\u04c6\x81\x11\x9c\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\u0687^N/<\xab\xe4\xf3~\x0e\xae\xd7\xd1\xf6\xdc\xc6\xff\xefC\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u068b\xbe\xe1\x82\xe4U\xd2\t\x8a\xcb3\x8amE\xb4\xb1~\u0636\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0698.\x96C\xff\xec\xe7#\aZ@\xfewnZ\xce\x04\xb2\x9b\x89\b\xb8\xb6\u0259\x9b\xf2\x00\x00\u07d4\u069fUF\tF\u05ff\xb5p\xdd\xecu|\xa5w;XB\x9a\x89\x1b\x84]v\x9e\xb4H\x00\x00\u07d4\u06a1\xbdz\x91H\xfb\x86\\\xd6\x12\xdd5\xf1b\x86\x1d\x0f;\u0709\xa68\xabr\xd9,\x13\x80\x00\xe0\x94\u06a6<\xbd\xa4]\u0507\xa3\xf1\xcdJtj\x01\xbb^\x06\v\x90\x8a\x01\x04\x16\u0670*\x89$\x00\x00\u07d4\u06a7v\xa6uDi\u05f9&z\x89\xb8g%\xe7@\xda\x0f\xa0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u06ac\x91\xc1\xe8Y\xd5\xe5~\xd3\bKP \x0f\x97f\xe2\xc5+\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u06ac\xda\xf4\"&\xd1\\\xb1\u03d8\xfa\x15\x04\x8c\u007fL\xee\xfei\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\u06b6\xbc\u06c3\xcf$\xa0\xae\x1c\xb2\x1b;[\x83\xc2\xf3\x82I'\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\u06bb\b\x89\xfc\x04)&\xb0^\xf5{% \x91\n\xbcKAI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06bc\"PB\xa6Y,\xfa\x13\xeb\xe5N\xfaA\x04\bx\xa5\xa2\x89\x0e\x11\xfa\xd5\xd8\\\xa3\x00\x00\u07d4\xda\xc0\xc1w\xf1\x1c\\>>x\xf2\xef\xd6c\xd12!H\x85t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xd16\xb8\x81x\xb4\x83zlx\x0f\xeb\xa2&\xb9\x85i\xa9L\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xda\xdb\xfa\xfd\x8bb\xb9*$\xef\xd7RV\u0743\xab\xdb\u05fb\u06c9\x01\x11du\x9f\xfb2\x00\x00\u07d4\xda\xdc\x00\xaby'`\xcf1\xce\xe3R\xf8\x0elMcQ\x89lf\xe9\xa5Sx\xb8\x00\x00\u07d4\xda\xe0\xd3>\xaa4\x15i\xfa\x9f\xf5\x98&\x84\x85JJ2\x8an\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xe7 \x1e\xab\x8c\x063\x02\x93\ri9)\xd0\u007f\x95\xe7\x19b\x89\x91\xae\xc0(\xb4\x19\x81\x00\x00\u07d4\xda\xed\u052d\x10{'\x1e\x89Hl\xbf\x80\xeb\xd6!\u0757Ex\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x04\xfa\xd9\u011f\x9e\x88\v\xeb\x8f\xcf\x1d:8\x90\u4cc4o\x89CZ\xe6\xcc\fX\xe5\x00\x00\u07d4\xdb\f\u01cft\u0642{\u070ads'n\xb8O\u0717b\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x12\x93\xa5\x06\xe9\f\xad*Y\xe1\xb8V\x1f^f\x96\x1ag\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x19\xa3\x98\"06\x8f\x01w!\x9c\xb1\f\xb2Y\u0372%|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb#\xa6\xfe\xf1\xaf{X\x1ew,\xf9\x18\x82\u07b2Qo\xc0\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb$O\x97\xd9\xc4K\x15\x8a@\xed\x96\x06\xd9\xf7\xbd8\x9131\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xdb(\x8f\x80\xff\xe22\u00baG\u0314\xc7c\xcfo\u0278+\r\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xdb*\f\x9a\xb6M\xf5\x8d\u07f1\u06ec\xf8\xba\r\x89\xc8[1\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdb4t^\u0785v\xb4\x99\xdb\x01\xbe\xb7\xc1\xec\u0685\xcfJ\xbe\x89\x04V9\x18$O@\x00\x00\u07d4\xdb?%\x8a\xb2\xa3\xc2\xcf3\x9cD\x99\xf7ZK\xd1\xd3G.\x9e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xdbK\xc8;\x0ek\xaa\xdb\x11V\xc5\xcf\x06\xe0\xf7!\x80\x8cR\u01c9/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xdbc\x12-\xe7\x03}\xa4\x97\x151\xfa\u9bc5\x86x\x86\u0192\x89\x0f\x04%\xb0d\x1f4\x00\x00\u07d4\xdbl*s\xda\xc7BJ\xb0\xd01\xb6ga\x12%f\xc0\x10C\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xdbnV\f\x9b\xc6 \u053e\xa3\xa9MG\xf7\x88\v\xf4\u007f-_\x89\x04\xda\x0f\xdf\xcf\x05v\x00\x00\u07d4\xdbo\xf7\x1b=\xb0\x92\x8f\x83\x9e\x05\xa72;\xfbW\u049c\x87\xaa\x891T\xc9r\x9d\x05x\x00\x00\u07d4\xdbsF\vY\xd8\xe8PE\xd5\xe7R\xe6%Y\x87^BP.\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xdbw\xb8\x8d\xcbq/\xd1~\xe9\x1a[\x94t\x8dr\f\x90\xa9\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb}@7\b\x1fle\xf9Gk\x06\x87\xd9\u007f\x1e\x04M\n\x1d\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\u06c8.\xac\xed\xd0\xef\xf2cQ\x1b1*\u06fcY\u01b8\xb2[\x8a\x01\xedO\xdez\"6\xb0\x00\x00\u07d4\u06d3q\xb3\fL\x84NY\xe0>\x92K\xe6\x06\xa98\xd1\xd3\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06e4ym\f\xebM:\x83k\x84\xc9o\x91\n\xfc\x10?[\xa0\x89\t\b\xf4\x93\xf77A\x00\x00\u07d4\u06ed\xc6\x1e\xd5\xf0F\n\u007f\x18\xe5\x1b/\xb2aM\x92d\xa0\xe0\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u06f6\xacH@'\x04\x16B\xbb\xfd\x8d\x80\xf9\xd0\xc1\xcf3\xc1\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06fc\xbby\xbfG\x9aB\xadq\xdb\u02b7{Z\u07ea\x87,X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xdb\xc1\xce\x0eI\xb1\xa7\x05\xd2. 7\xae\xc8x\xee\ru\xc7\x03\x89\r\x8drkqw\xa8\x00\x00\u07d4\xdb\xc1\xd0\xee+\xabS\x11@\xde\x13w\"\xcd6\xbd\xb4\xe4q\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\u015e\u0609s\u07ad1\b\x84\":\xf4\x97c\xc0P0\xf1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xdb\xc6ie\xe4&\xff\x1a\xc8z\xd6\xebx\xc1\xd9Rq\x15\x8f\x9f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xdb\xcb\xcdzW\ua7724\x9b\x87\x8a\xf3K\x1a\xd6B\xa7\xf1\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\xd5\x1c\xdf,;\xfa\xcd\xff\x10b!\xde.\x19\xadmB\x04\x14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdb\xd7\x1e\xfaK\x93\u0209\xe7e\x93\xde`\x9c;\x04\u02ef\xbe\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xdb\xf5\xf0a\xa0\xf4\x8e^ia\x879\xa7}.\xc1\x97h\xd2\x01\x89\b=lz\xabc`\x00\x00\u07d4\xdb\xf8\xb19g\xf5Q%'-\xe0V%6\xc4P\xbaVU\xa0\x89n\xf5x\xf0n\f\xcb\x00\x00\u07d4\xdb\xfb\x1b\xb4d\xb8\xa5\x8eP\r.\xd8\u0797,E\xf5\xf1\xc0\xfb\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xdc\x06~\xd3\xe1-q\x1e\xd4u\xf5\x15n\xf7\xe7\x1a\x80\xd94\xb9\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xdc\b\u007f\x93\x90\xfb\x9e\x97j\xc2:\xb6\x89TJ\tB\xec !\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xdc\x1e\xb9\xb6\xe6CQ\xf5d$P\x96E\xf8>y\xee\xe7l\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\x1f\x19ya_\b!@\xb8\xbbx\xc6{'\xa1\x94'\x13\xb1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xdc#\xb2`\xfc\xc2n}\x10\xf4\xbd\x04J\xf7\x94W\x94`\xd9\u0689\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xdc)\x11\x97E\xd23s \xdaQ\xe1\x91\x00\xc9H\u0640\xb9\x15\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdc-\x15\xa6\x9fk\xb3;$j\xef@E\aQ\xc2\xf6uj\u0489l4\x10\x80\xbd\x1f\xb0\x00\x00\u07d4\xdc=\xaeY\xed\x0f\xe1\x8bXQ\x1eo\xe2\xfbi\xb2\x19h\x94#\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xdc?\x0evr\xf7\x1f\xe7R[\xa3\v\x97U\x18: \xb9\x16j\x8a\x02\b\x9c\xf5{[>\x96\x80\x00\xe0\x94\xdcCE\u0581.\x87\n\xe9\fV\x8cg\xd2\xc5g\u03f4\xf0<\x8a\x01k5-\xa5\xe0\xed0\x00\x00\u07d4\xdcD'[\x17\x15\xba\xea\x1b\x03EsZ)\xacB\xc9\xf5\x1bO\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xdcF\xc13%\u034e\xdf\x020\xd0h\x89d\x86\xf0\a\xbfN\xf1\x89Hz\x9a0E9D\x00\x00\u07d4\xdcQ\xb2\u071d$z\x1d\x0e[\xc3l\xa3\x15oz\xf2\x1f\xf9\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdcS\x05\xb4\x02\n\x06\xb4\x9de||\xa3L5\xc9\x1c_,V\x8a\x01}\xf6\xc1\r\xbe\xba\x97\x00\x00\u07d4\xdcW4[8\xe0\xf0g\u0263\x1d\x9d\xea\xc5'Z\x10\x94\x93!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdcWG}\xaf\xa4/p\\\u007f\xe4\x0e\xae\x9c\x81un\x02%\xf1\x89\x1b\x1b\x81(\xa7An\x00\x00\u07d4\xdc_Z\xd6c\xa6\xf2c2}d\xca\xc9\xcb\x13=,\x96\x05\x97\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdcp:_7\x94\xc8Ml\xb3TI\x18\xca\xe1J5\u00fdO\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\xdcs\x8f\xb2\x17\u03ad/iYL\b\x17\r\xe1\xaf\x10\xc4\x19\xe3\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xdcv\xe8[\xa5\v\x9b1\xec\x1e& \xbc\xe6\xe7\xc8\x05\x8c\x0e\xaf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0703\xb6\xfd\rQ!1 G\a\xea\xf7.\xa0\xc8\u027e\xf9v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u070c)\x12\xf0\x84\xa6\u0444\xaasc\x85\x13\u033c2n\x01\x02\x89F3\xbc6\xcb\xc2\xdc\x00\x00\u07d4\u0711\x1c\xf7\xdc]\u04016Vg\x05(\xe93\x8eg\x03G\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0730;\xfal\x111#NV\xb7\xea|Or\x14\x87Tkz\x89Hz\x9a0E9D\x00\x00\xe0\x94\u0736M\xf47X\xc7\u03d7O\xa6`HO\xbbq\x8f\x8cg\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdc\xc5-\x8f\x8d\x9f\xc7B\xa8\xb8'g\xf0US\x87\xc5c\xef\xff\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xdc\xcb7\x0e\u058a\xa9\"(0C\xef|\xad\x1b\x9d@?\xc3J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\u0324 E\xec>\x16P\x8b`?\xd96\xe7\xfd}\xe5\xf3j\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xdc\xd1\fU\xbb\x85OuD4\xf1!\x9c,\x9a\x98\xac\xe7\x9f\x03\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xdc\u057c\xa2\x00S\x95\xb6u\xfd\xe5\x03VY\xb2k\xfe\xfcI\xee\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xdc\u06fdN&\x04\xe4\x0e\x17\x10\xccg0(\x9d\xcc\xfa\u04c9-\x89\xf9]\xd2\xec'\xcc\xe0\x00\x00\u07d4\xdc\xe3\f1\xf3\xcafr\x1e\xcb!<\x80\x9a\xabV\x1d\x9bR\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdc\xf39eS\x13\x80\x161h\xfc\x11\xf6~\x89\xc6\xf1\xbc\x17\x8a\x89\x12'v\x854\x06\xb0\x80\x00\u07d4\xdc\xf6\xb6W&n\x91\xa4\xda\xe6\x03=\xda\xc1S2\u074d+4\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdc\xf9q\x9b\xe8|oFum\xb4\x89\x1d\xb9\xb6\x11\xd2F\x9cP\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdc\xff\xf3\xe8\xd2<*4\xb5k\u0473\xbdE\u01d3tC\"9\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xdd\x04\xee\xe7N\v\xf3\f?\x8dl,\u007fR\xe0Q\x92\x10\u07d3\x89\x04V9\x18$O@\x00\x00\xe0\x94\xdd&\xb4)\xfdC\xd8N\xc1y\x82S$\xba\u057f\xb9\x16\xb3`\x8a\x01\x16\xbf\x95\xbc\x842\x98\x00\x00\u07d4\xdd*#:\xde\xdef\xfe\x11&\xd6\xc1h#\xb6*\x02\x1f\xed\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd+\u07e9\x17\xc1\xf3\x10\xe6\xfa5\xaa\x8a\xf1i9\xc23\xcd}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xdd5\xcf\xdb\u02d93\x95Sz\xec\xc9\xf5\x90\x85\xa8\xd5\u0776\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xddG\x18\x9a>d9qg\xf0b\x0eHEe\xb7b\xbf\xbb\xf4\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xddM\xd6\xd3`3\xb0co\u030d\t8`\x9fM\xd6OJ\x86\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xddO_\xa2\x11\x1d\xb6\x8fk\xde5\x89\xb60)9[i\xa9-\x89\b\x96=\xd8\xc2\xc5\xe0\x00\x00\xe0\x94\xddc\x04/%\xed2\x88J\xd2n:\xd9Y\xeb\x94\xea6\xbfg\x8a\x04\x84\xd7\xfd\xe7\u0553\xf0\x00\x00\u07d4\xdde\xf6\xe1qc\xb5\xd2\x03d\x1fQ\xcc{$\xb0\x0f\x02\xc8\xfb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xddl\x06!\x93\xea\xc2=/\xdb\xf9\x97\xd5\x06:4k\xb3\xb4p\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdd{\u0366Y$\xaa\xa4\x9b\x80\x98J\xe1su\x02X\xb9(G\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdd\u007f\xf4A\xbao\xfe6q\xf3\xc0\u06bb\xff\x18#\xa5\x043p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0742T\x12\x1an\x94/\xc9\b(\xf2C\x1fQ\x1d\xad\u007f2\u6263\x9b)\xe1\xf3`\xe8\x00\x00\xe0\x94\u074a\xf9\xe7vR#\xf4DoD\xd3\xd5\t\x81\x9a==\xb4\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\u0755\xdb\xe3\x0f\x1f\x18w\xc5\xddv\x84\xae\xef0*\xb6\x88Q\x92\x8a\x01\xc5\xd8\xd6\xeb>2P\x00\x00\xe0\x94\u0756|L_\x8a\xe4~&o\xb4\x16\xaa\u0456N\xe3\xe7\xe8\u00ca\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u075bHZ;\x1c\xd3:j\x9cb\xf1\xe5\xbe\xe9'\x01\x85m%\x89\f3\x83\xed\x03\x1b~\x80\x00\xe0\x94\u0763q\xe6\x00\xd3\x06\x88\xd4q\x0e\b\x8e\x02\xfd\xf2\xb9RM_\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\u0764\xed*X\xa8\xdd \xa72u4{X\rq\xb9[\xf9\x9a\x89\x15\xa1<\xc2\x01\xe4\xdc\x00\x00\xe0\x94\u0764\xff}\xe4\x91\u0187\xdfEt\xdd\x1b\x17\xff\x8f$k\xa3\u044a\x04&\x84\xa4\x1a\xbf\xd8@\x00\x00\u07d4\u076bkQ\xa9\x03\v@\xfb\x95\xcf\vt\x8a\x05\x9c$\x17\xbe\u01c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u076bu\xfb/\xf9\xfe\u02c8\xf8\x94vh\x8e+\x00\xe3g\xeb\xf9\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\xe0\x94\u076b\xf1<<\x8e\xa4\xe3\xd7=x\xecqz\xfa\xfaC\x0eTy\x8a\b\xcf#\xf9\t\xc0\xfa\x00\x00\x00\u07d4\u076c1*\x96UBj\x9c\f\x9e\xfa?\xd8%Y\xefE\x05\xbf\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\u076ck\xf4\xbb\xdd}Y}\x9chm\x06\x95Y;\xed\xcc\xc7\xfa\x89.\xe4IU\b\x98\xe4\x00\x00\xe0\x94\u077d+\x93,v;\xa5\xb1\xb7\xae;6.\xac>\x8d@\x12\x1a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u077d\xdd\x1b\xbd8\xff\xad\xe00]0\xf0 (\xd9.\x9f:\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u077e\xe6\xf0\x94\xea\xe64 \xb0\x03\xfbGW\x14*\xeal\xd0\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd\u059c[\x9b\xf5\xebZ9\xce\xe7\xc34\x1a\x12\r\x97?\xdb4\x89k\xc1K\x8f\x8e\x1b5\x00\x00\xe0\x94\xdd\xdd{\x9en\xab@\x9b\x92&:\xc2r\u0680\x1bfO\x8aW\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4\xdd\xe6p\xd0\x169fuv\xa2-\xd0]2F\xd6\x1f\x06\xe0\x83\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94\xdd\xe7zG@\xba\b\xe7\xf7?\xbe:\x16t\x91)1t.\xeb\x8a\x044\xfeMC\x82\xf1\u0500\x00\u07d4\xdd\xe8\xf0\xc3\x1bt\x15Q\x1d\xce\xd1\xcd}F2>K\xd1\"2\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xdd\xe9i\xae\xf3N\xa8z\u0099\xb7Y~)+J\x01U\u030a\x89\x102\xf2YJ\x01s\x80\x00\u07d4\xdd\xf0\xcc\xe1\xfe\x99m\x91v5\xf0\a\x12\xf4\x05 \x91\xdf\xf9\xea\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdd\xf3\xadv58\x10\xbej\x89\xd71\xb7\x87\xf6\xf1q\x88a+\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xdd\xf5\x81\n\x0e\xb2\xfb.22;\xb2\u0255\t\xab2\x0f$\xac\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x94\xdd\xf9\\\x1e\x99\xce/\x9fV\x98\x05|\x19\xd5\xc9@'\xeeJn\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xdd\xfa\xfd\xbc|\x90\xf12\x0eT\xb9\x8f7F\x17\xfb\xd0\x1d\x10\x9f\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xdd\xfc\xca\x13\xf94\xf0\u03fe#\x1d\xa109\xd7\x04u\xe6\xa1\u040968\"\x16`\xa5\xaa\x80\x00\u07d4\xde\x02~\xfb\xb3\x85\x03\"n\xd8q\t\x9c\xb3\v\xdb\x02\xaf\x135\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xde\x06\xd5\xeawzN\xb1G^`]\xbc\xbfCDN\x807\xea\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xde\a\xfb[zFN;\xa7\xfb\xe0\x9e\x9a\xcb'\x1a\xf53\x8cX\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xde\x11!\x82\x9c\x9a\b(@\x87\xa4?\xbd/\xc1\x14*23\xb4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xde\x17kR\x84\xbc\xee:\x83\x8b\xa2Og\xfc|\xbfg\u05ce\xf6\x89\x02\t\xce\b\xc9b\xb0\x00\x00\u07d4\xde!\"\x93\xf8\xf1\xd21\xfa\x10\xe6\tG\rQ,\xb8\xff\xc5\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde0\xe4\x9eZ\xb3\x13!M/\x01\u072b\u0389@\xb8\x1b\x1cv\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xde3\xd7\b\xa3\xb8\x9e\x90\x9e\xafe;0\xfd\u00e5\xd5\u0334\xb3\x89\t\x9c\x88\"\x9f\xd4\xc2\x00\x00\u07d4\xde7B\x99\xc1\xd0}ySs\x85\x19\x0fD.\xf9\xca$\x06\x1f\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xdeB\xfc\xd2L\xe4#\x93\x830CgY_\x06\x8f\fa\a@\x89\x02r*p\xf1\xa9\xa0\x00\x00\u07d4\xdeP\x86\x8e\xb7\xe3\xc7\x197\xecs\xfa\x89\u074b\x9e\xe1\rE\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdeU\xde\x04X\xf8P\xb3~Mx\xa6A\xdd.\xb2\u074f8\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde[\x00_\xe8\u06ae\x8d\x1f\x05\xde>\xda\x04 f\xc6\xc4i\x1c\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xdea-\a$\xe8N\xa4\xa7\xfe\xaa=!B\xbd^\xe8-2\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdem61\x06\xccb8\xd2\xf0\x92\xf0\xf07!6\xd1\xcdP\u018a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xde}\xee\"\x0f\x04W\xa7\x18}V\xc1\xc4\x1f.\xb0\n\xc5`!\x89\"%\xf3\x9c\x85\x05*\x00\x00\u07d4\u0782\u030dJ\x1b\xb1\xd9CC\x92\x96[>\x80\xba\xd3\xc0=O\x89P\x18nu\u0797\xa6\x00\x00\u07d4\u0797\xf43\a\x00\xb4\x8cImC|\x91\xca\x1d\xe9\u0130\x1b\xa4\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\u07d4\u079e\xffLy\x88\x11\xd9h\xdc\xcbF\r\x9b\x06\x9c\xf3\x02x\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u07b1\xbc4\xd8mJM\xde%\x80\u063e\xaf\aN\xb0\xe1\xa2D\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\u07b2I]j\xca{*j-\x13\x8bn\x1aB\xe2\xdc1\x1f\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\u07b9rTGL\r/Zyp\xdc\xdb/R\xfb\x10\x98\xb8\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07b9\xa4\x9aC\x870 \xf0u\x91\x85\xe2\v\xbbL\U000c1ecf\x89\vx\xed\xb0\xbf.^\x00\x00\u07d4\u07bb\u0743\x1e\x0f \xaen7\x82R\xde\xcd\xf9/|\xf0\xc6X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xc3\xee\xc2d\nu,Fn+~~\u616f\xe9\xacA\xf4\x89G\u0257SYk(\x80\x00\u07d4\xde\xc8#s\xad\xe8\xeb\xcf*\xcbo\x8b\xc2AM\u05eb\xb7\rw\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xde\u0221\xa8\x98\xf1\xb8\x95\xd80\x1f\xe6J\xb3\xad]\xe9A\xf6\x89\x89*\xb4\xf6~\x8as\x0f\x80\x00\u07d4\xde\u025e\x97/\xcaqwP\x8c\x8e\x1aG\xac\"\xd7h\xac\xab|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xd8w7\x84\a\xb9Nx\x1cN\xf4\xaf|\xfc[\xc2 \xb5\x16\x89\x141y\xd8i\x11\x02\x00\x00\u07d4\xde\xe9B\xd5\xca\xf5\xfa\xc1\x14!\xd8k\x01\vE\x8e\\9)\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde\xee&\x89\xfa\x90\x06\xb5\x9c\xf2\x85#}\xe5;:\u007f\xd0\x148\x89\x18ey\xf2\x9e %\x00\x00\u07d4\xde\xfd\xdf\u055b\x8d,\x15N\xec\xf5\xc7\xc1g\xbf\v\xa2\x90]>\x89\x05\x12\xcb^&GB\x00\x00\u07d4\xde\xfe\x91A\xf4pE\x99\x15\x9d{\"=\xe4+\xff\xd8\x04\x96\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xdf\t\x8f^N=\xff\xa5\x1a\xf27\xbd\xa8e,Os\ud726\x89\x1b6\xa6DJ>\x18\x00\x00\xe0\x94\xdf\r\ba{\xd2R\xa9\x11\u07cb\xd4\x1a9\xb8=\u07c0\x96s\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdf\x0f\xf1\xf3\xd2z\x8e\xc9\xfb\x8fk\f\xb2T\xa6;\xba\x82$\xa5\x89\xec\xc5 )E\xd0\x02\x00\x00\u07d4\xdf\x1f\xa2\xe2\x0e1\x98^\xbe,\x0f\f\x93\xb5L\x0f\xb6z&K\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdf!\x1c\xd2\x12\x88\xd6\xc5o\xaef\xc3\xffTb]\u0531T'\x89\x87\x86\xcdvN\x1f,\x00\x00\u07d4\xdf#k\xf6\xab\xf4\xf3)7\x95\xbf\f(q\x8f\x93\u3c73k\x89Hz\x9a0E9D\x00\x00\u07d4\xdf1\x02_VI\xd2\xc6\xee\xa4\x1e\u04fd\xd3G\x1ay\x0fu\x9a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdf7\xc2.`:\xed\xb6\nbrS\xc4}\x8b\xa8f\xf6\xd9r\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xdf;r\u017dq\u0501N\x88\xa6#!\xa9=@\x11\xe3W\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf?W\xb8\xeed4\xd0G\"=\xeft\xb2\x0fc\xf9\xe4\xf9U\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d4\xdfD\xc4\u007f\xc3\x03\xacv\xe7O\x97\x19L\xcag\xb5\xbb<\x02?\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xdfG\xa6\x1brSQ\x93\xc5a\xcc\xccu\xc3\xf3\xce\b\x04\xa2\x0e\x89\x15\x93\\\vN=x\x00\x00\u07d4\xdfG\xa8\xef\x95\xf2\xf4\x9f\x8eoX\x18AT\x14]\x11\xf7'\x97\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xdfS\x003F\xd6\\^zdk\xc04\xf2\xb7\xd3/\xcb\xe5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfW5:\xaf\xf2\xaa\xdb\n\x04\xf9\x01N\x8d\xa7\x88N\x86X\x9c\x89\bH\x86\xa6nO\xb0\x00\x00\u07d4\xdf`\xf1\x8c\x81*\x11\xedN'v\xe7\xa8\x0e\xcf^S\x05\xb3\u05890\xca\x02O\x98{\x90\x00\x00\u07d4\xdfd\x85\xc4)z\xc1R\xb2\x89\xb1\x9d\xde2\xc7~\xc4\x17\xf4}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdff\n\x91\u06b9\xf70\xf6\x19\rP\xc89\x05aP\aV\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfn\xd6\x00jj\xbe\x88n\xd3=\x95\xa4\xde(\xfc\x12\x189'\x891T\xc9r\x9d\x05x\x00\x00\u07d4\u07c5\x10y>\xee\x81\x1c-\xab\x1c\x93\xc6\xf4G?0\xfb\xef[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07cdH\xb1\xeb\a\xb3\xc2\x17y\x0el-\xf0M\xc3\x19\xe7\xe8H\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u07e6\xb8\xb8\xad1\x84\xe3W\xda()Q\u05d1a\u03f0\x89\xbc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u07ef1\xe6\"\xc0=\x9e\x18\xa0\u0778\xbe`\xfb\xe3\xe6a\xbe\n\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\u07f1bn\xf4\x8a\x1d}uR\xa5\xe0)\x8f\x1f\xc2:;H-\x89\\\xe8\x95\u0754\x9e\xfa\x00\x00\xe0\x94\u07f4\u052d\xe5/\u0301\x8a\xccz,k\xb2\xb0\x02$e\x8fx\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u07fdB2\xc1|@z\x98\r\xb8\u007f\xfb\u036060\xe5\xc4Y\x89\x1d\xfc\u007f\x92I#S\x00\x00\u07d4\xdf\xcb\xdf\tEN\x1a^J@\xd3\xee\xf7\xc5\xcf\x1c\xd3\u0794\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf\xdb\xce\xc1\x01K\x96\xda!X\xcaQ>\x9c\x8d;\x9a\xf1\xc3\u0409lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdf\xde\xd2WK'\xd1a:}\x98\xb7\x15\x15\x9b\r\x00\xba\xab(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdf\xdfC9P\x8b\x0fnZ\xb1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe0\x06\x04b\xc4\u007f\xf9g\x9b\xae\xf0qY\xca\xe0\x8c)\xf2t\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\r\x15;\x106\x91C\xf9\u007fT\xb8\xd4\xca\"\x9e\xb3\xe8\xf3$\x89\b=lz\xabc`\x00\x00\u07d4\xe0\x12\xdbE8'\xa5\x8e\x16\xc16V\b\xd3n\xd6Xr\x05\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x15G\xbaB\xfc\xaf\xaf\x93\x93\x8b\xec\xf7i\x9ft)\n\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x16\xdc\x13\x8e%\x81[\x90\xbe?\xe9\xee\xe8\xff\xb2\xe1\x05bO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\x18Y\xf2B\xf1\xa0\xec`/\xa8\xa3\xb0\xb5v@\xec\x89\a^\x89\x1e\x16,\x17{\xe5\xcc\x00\x00\xe0\x94\xe0 \xe8cb\xb4\x87u(6\xa6\xde\v\xc0,\xd8\u061a\x8bj\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xe0#\xf0\x9b(\x87a,|\x9c\xf1\x98\x8e::`+3\x94\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0'\"\x13\xe8\xd2\xfd>\x96\xbdb\x17\xb2KK\xa0\x1bapy\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0+t\xa4v(\xbe1[\x1fv\xb3\x15\x05J\xd4J\xe9qo\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe02 \u0197\xbc\u048f&\xef\vt@J\x8b\xeb\x06\xb2\xba{\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe05/\u07c1\x9b\xa2e\xf1L\x06\xa61\\J\xc1\xfe\x13\x1b.\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe08\x8a\xed\xdd?\xe2\xadV\xf8WH\xe8\x0eq\n4\xb7\xc9.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0<\x00\xd0\x03\x88\xec\xbfO&=\n\xc7x\xbbA\xa5z@\u064966\xc9yd6t\x00\x00\u07d4\xe0I \xdcn\xcc\x1dn\xcc\bO\x88\xaa\n\xf5\u06d7\xbf\x89:\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xe0Ir\xa8<\xa4\x11+\xc8q\xc7-J\xe1al/\a(\u06c9\x0e\x81\xc7\u007f)\xa3/\x00\x00\u07d4\xe0O\xf5\xe5\xa7\u2bd9]\x88W\xce\x02\x90\xb5:+\x0e\xda]\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xe0P)\xac\xeb\axg[\xef\x17A\xab,\u0493\x1e\xf7\xc8K\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0V\xbf?\xf4\x1c&%o\xefQqf\x12\xb9\u04da\u0799\x9c\x89\x05k\xe7W\xa1.\n\x80\x00\u07d4\xe0a\xa4\xf2\xfcw\xb2\x96\u045a\xda#\x8eI\xa5\u02ce\xcb\xfap\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0f>\x8c\xd6g\x92\xa6A\xf5nP\x03f\x01G\x88\x0f\x01\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0f\x8f\xa8,\x14\xd6\xe8\xd9:S\x11>\xf2\x86/\xa8\x15\x81\xbc\x89//9\xfclT\x00\x00\x00\u07d4\xe0i\xc0\x173R\xb1\v\xf6\x83G\x19\xdb[\xed\x01\xad\xf9{\xbc\x89\x01\x064\xf8\xe52;\x00\x00\u07d4\xe0l)\xa8\x15\x17\xe0\u0507\xb6\u007f\xb0\xb6\xaa\xbcOW6\x83\x88\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xe0l\xb6)G\x04\xee\xa7C|/\xc3\xd3\as\xb7\xbf8\x88\x9a\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\xe0q7\xae\r\x11m\x0353\xc4\uad16\xf8\xa9\xfb\tV\x9c\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe0v\xdb0\xabHoy\x19N\xbb\xc4]\x8f\xab\x9a\x92B\xf6T\x8a\x01\x06`~4\x94\xba\xa0\x00\x00\u07d4\xe0~\xbb\xc7\xf4\xdaAnB\xc8\xd4\xf8B\xab\xa1b3\xc1%\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x81\xca\x1fH\x82\xdb`C\u0569\x19\a\x03\xfd\xe0\xab;\xf5m\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x83\xd3Hc\xe0\xe1\u007f\x92ky(\xed\xff1~\x99\x8e\x9cK\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x8b\x9a\xbak\xd9\u048b\xc2\x05gy\xd2\xfb\xf0\xf2\x85Z=\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x8b\u009c+H\xb1i\xff+\xdc\x16qLXnl\xb8\\\u03c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0\x8c`11\x06\xe3\xf93O\xe6\xf7\xe7bM!\x110\xc0w\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xe0\x9ch\xe6\x19\x98\xd9\xc8\x1b\x14\xe4\xee\x80+\xa7\xad\xf6\xd7L\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0\x9f\xeauZ\xee\x1aD\xc0\xa8\x9f\x03\xb5\u07b7b\xba3\x00o\x89;\xa2\x89\xbc\x94O\xf7\x00\x00\xe0\x94\xe0\xa2T\xac\t\xb9r[\xeb\xc8\xe4`C\x1d\xd0s.\xbc\xab\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe0\xaai6UU\xb7?(#3\xd1\xe3\f\x1b\xbd\a(T\xe8\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xe0\xba\u064e\ue598\xdb\xf6\xd7`\x85\xb7\x92=\xe5uN\x90m\x89\t\r\x97/22<\x00\x00\u07d4\xe0\u012b\x90r\xb4\xe6\xe3eJI\xf8\xa8\xdb\x02jK3\x86\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\u0380\xa4a\xb6H\xa5\x01\xfd\v\x82F\x90\u0206\x8b\x0eM\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\xcfi\x8a\x053'\xeb\xd1k}w\x00\t/\xe2\xe8T$F\x89\x05*4\u02f6\x1fW\x80\x00\xe0\x94\xe0\xd21\xe1D\xec\x91\a8l|\x9b\x02\xf1p,\xea\xa4\xf7\x00\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0\xd7kqf\xb1\xf3\xa1+@\x91\xee+)\u078c\xaa}\a\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\xe0\xb2\xe2\x9d\xdes\xafu\x98~\xe4Dl\x82\x9a\x18\x9c\x95\xbc\x89\b\x13\xcaV\x90m4\x00\x00\xe0\x94\xe0\xe9xu=\x98/\u007f\x9d\x1d#\x8a\x18\xbdH\x89\xae\xfeE\x1b\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\xe0\xf3r4|\x96\xb5_}C\x06\x03K\xeb\x83&o\xd9\tf\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\xf9\x03\xc1\xe4\x8a\xc4!\xabHR\x8f=J&H\b\x0f\xe0C\x897\b\xba\xed=h\x90\x00\x00\u07d4\xe0\xff\v\xd9\x15D9\u0125\xb7#>)\x1d}\x86\x8a\xf5?3\x89\x15y!jQ\xbb\xfb\x00\x00\xe0\x94\xe1\n\xc1\x9cTo\xc2T|a\xc19\xf5\xd1\xf4Zff\u0570\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\xe0\x94\xe1\fT\x00\x88\x11?\xa6\xec\x00\xb4\xb2\u0202O\x87\x96\xe9n\u010a2\x0fE\t\xab\x1e\xc7\xc0\x00\x00\xe0\x94\xe1\x17:$})\xd8#\x8d\xf0\x92/M\xf2Z\x05\xf2\xafw\u00ca\bx\xc9]V\x0f0G\x80\x00\xe0\x94\xe1 >\xb3\xa7#\xe9\x9c\" \x11|\xa6\xaf\xebf\xfaBOa\x8a\x02\x00\uf49e2V\xfe\x00\x00\xe0\x94\xe11\xf8~\xfc^\xf0~C\xf0\xf2\xf4\xa7G\xb5Q\xd7P\xd9\xe6\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\u07d4\xe13N\x99\x83y\xdf\xe9\x83\x17pby\x1b\x90\xf8\x0e\xe2-\x8d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe15@\xec\xee\x11\xb2\x12\xe8\xb7u\u070eq\xf3t\xaa\xe9\xb3\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1;=+\xbf\u073c\x87r\xa23\x15rL\x14%\x16|V\x88\x897\xf3y\x14\x1e\xd0K\x80\x00\u07d4\xe1D=\xbd\x95\xccA#\u007fa:HEi\x88\xa0Oh2\x82\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xe1F\x17\xf6\x02%\x01\xe9~{>-\x886\xaaa\xf0\xff-\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe1I\xb5rl\xafm^\xb5\xbf*\xccA\xd4\xe2\xdc2\x8d\u1089i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xe1T\xda\xea\xdbTX8\xcb\u01aa\fUu\x19\x02\xf5(h*\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4\xe1l\xe3Ya\xcdt\xbdY\r\x04\u012dJ\x19\x89\xe0V\x91\u0189\a\xea(2uw\b\x00\x00\u07d4\xe1r\xdf\xc8\xf8\f\xd1\xf8\u03459\xdc&\b \x14\xf5\xa8\xe3\u8262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xe1w\xe0\xc2\x01\xd35\xba9V\x92\x9cW\x15\x88\xb5\x1cR#\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1x\x12\xf6l^e\x94\x1e\x18lF\x92+n{/\x0e\xebF\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe1\x80\u079e\x86\xf5{\xaf\xac\u05d0O\x98&\xb6\xb4\xb2c7\xa3\x89-\x04\x1dpZ,`\x00\x00\xe0\x94\xe1\x92H\x9b\x85\xa9\x82\xc1\x882F\xd9\x15\xb2)\xcb\x13 \u007f8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xe1\x95\xbb\xc6,{tD\x04\x0e\xb9\x96#\x96Ovg\xb3v\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\x06\xfbs$\xe9\u07b7\x9e\x19\x904\x96\u0596\x1b\x9b\xe5f\x03\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe2\aW\x8e\x1fM\u06cf\xf6\u0546{9X-q\xb9\x81*\u0149\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xe2\b\x81*h@\x98\xf3\xdaN\xfej\xba%bV\xad\xfe?\xe6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2\tT\xd0\xf4\x10\x8c\x82\xd4\u0732\x14\x8d&\xbb\xd9$\xf6\xdd$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe2\v\xb9\xf3\x96d\x19\xe1K\xbb\xaa\xaag\x89\xe9$\x96\u03e4y\x89\xbb\xd8%\x03\aRv\x00\x00\u07d4\xe2\r\x1b\xcbq(m\xc7\x12\x8a\x9f\xc7\xc6\xed\u007fs8\x92\xee\xf5\x896d\xf8\xe7\xc2J\xf4\x00\x00\u0794\xe2\x19\x12\x15\x98?3\xfd3\xe2,\u0522I\x00T\xdaS\xfd\u0708\xdbD\xe0I\xbb,\x00\x00\u07d4\xe2\x19\x8c\x8c\xa1\xb3\x99\xf7R\x15a\xfdS\x84\xa7\x13/\xbaHk\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xe2\x1cw\x8e\xf2\xa0\xd7\xf7Q\xea\x8c\aM\x1f\x81\"C\x86>N\x8a\x01\x1f\xc7\x0e,\x8c\x8a\xe1\x80\x00\xe0\x94\xe2)\xe7F\xa8?,\xe2S\xb0\xb0>\xb1G$\x11\xb5~W\x00\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xe2+ \xc7x\x94F;\xafwL\xc2V\u057d\u06ff}\xdd\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe20\xfe\x1b\xff\x03\x18m\x02\x19\xf1]LH\x1b}Y\xbe(j\x89\x01\xfdt\x1e\x80\x88\x97\x00\x00\u07d4\xe27\xba\xa4\xdb\u0252n2\xa3\xd8]\x12d@-T\xdb\x01/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2A\t\xbe/Q=\x87I\x8e\x92j(d\x99uO\x9e\u051e\x890\x0e\xa8\xad\x1f'\xca\x00\x00\u07d4\xe2Fh<\u025d\xb7\u0125+\u02ec\xaa\xb0\xb3/k\xfc\x93\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2Z\x16{\x03\x1e\x84am\x0f\x01?1\xbd\xa9]\xcccP\xb9\x8a\x02\x8c*\xaa\u0243\xd0]\u0187st\xa8\xf4F\xee\xe9\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xe2\x8b\x06\"Y\xe9n\xeb<\x8dA\x04\x94?\x9e\xb3%\x89<\xf5\x89Hz\x9a0E9D\x00\x00\xe0\x94\u237c\x8e\xfd^Ajv.\xc0\xe0\x18\x86K\xb9\xaa\x83({\x8a\x051\xf2\x00\xab>\x03\n\x80\x00\u07d4\xe2\x90K\x1a\xef\xa0V9\x8bb4\xcb5\x81\x12\x88\xd76\xdbg\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u274a\xe4R\xdc\xf3\xb6\xacd^c\x04\t8UQ\xfa\xae\n\x89\x04Z\r\xa4\xad\xf5B\x00\x00\u07d4\xe2\xbb\xf8FA\xe3T\x1fl3\xe6\xedh:cZp\xbd\xe2\xec\x89\x1bA<\xfc\xbfY\xb7\x80\x00\u07d4\xe2\xcf6\n\xa22\x9e\xb7\x9d+\xf7\xca\x04\xa2z\x17\xc52\xe4\u0609\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xe2\xdf#\xf6\xea\x04\xbe\xcfJ\xb7\x01t\x8d\xc0\x961\x84U\\\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2\xe1\\`\xdd8\x1e:K\xe2Pq\xab$\x9aL\\Rd\u0689\u007fk\u011b\x81\xb57\x00\x00\u07d4\xe2\xe2nN\x1d\xcf0\xd0H\xccn\u03ddQ\xec\x12\x05\xa4\xe9&\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe2\xeei\x1f#~\xe6R\x9beW\xf2\xfc\xdd=\xcf\fY\xecc\x8a\x01'r\x9c\x14h| \x00\x00\u07d4\xe2\xef\xa5\xfc\xa7\x958\xce`h\xbf1\xd2\xc5\x16\xd4\xd5<\b\xe5\x89\a\x1c\xc4\b\xdfc@\x00\x00\xe0\x94\xe2\xef\u0429\xbc@~\xce\x03\xd6~\x8e\xc8\xe9\u0483\xf4\x8d*I\x8a\x02\x99\xb3;\xf9\u0144\xe0\x00\x00\u07d4\xe2\xf4\r5\x8f^?\xe7F>\xc7\x04\x80\xbd.\u04d8\xa7\x06;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\xf98=X\x10\xea{C\x18+\x87\x04\xb6+'\xf5\x92]9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe2\xff\x9e\xe4\xb6\xec\xc1AA\xcct\xcaR\xa9\xe7\xa2\xee\x14\xd9\b\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe3\x02\x12\xb2\x01\x1b\xb5k\xdb\xf1\xbc5i\x0f:N\x0f\xd9\x05\xea\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xe3\x03\x16\u007f=I`\xfe\x88\x1b2\x80\n+J\xef\xf1\xb0\x88\u0509lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\x04\xa3/\x05\xa87btJ\x95B\x97o\xf9\xb7#\xfa1\xea\x89Ur\xf2@\xa3F \x00\x00\u07d4\xe3\bCR\x04y7d\xf5\xfc\xbee\xebQ\x0fZtJeZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3\t\x97L\xe3\x9d`\xaa\xdf.ig2Q\xbf\x0e\x04v\n\x10\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xe3\x1bN\xef\x18L$\xab\t\x8e6\xc8\x02qK\xd4t=\xd0\u0509\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3!\xbbJ\x94j\xda\xfd\xad\xe4W\x1f\xb1\\\x00C\u04de\xe3_\x89Udu8+L\x9e\x00\x00\u07d4\xe3&<\xe8\xafm\xb3\xe4gXE\x02\xedq\t\x12^\xae\"\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3+\x1cG%\xa1\x87TI\u93d7\x0e\xb3\xe5@b\xd1X\x00\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3/\x95vmW\xb5\xcdK\x172\x89\u0587o\x9edU\x81\x94\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe38@\u063c\xa7\u0698\xa6\xf3\u0416\xd8=\xe7\x8bp\xb7\x1e\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe38\xe8Y\xfe.\x8c\x15UHH\xb7\\\xae\u0368w\xa0\xe82\x89a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4\xe3=\x98\x02 \xfa\xb2Y\xafj\x1fK8\xcf\x0e\xf3\xc6\xe2\xea\x1a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe3=\xf4\u0380\u0336*v\xb1+\xcd\xfc\xec\xc4b\x89\x97:\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe3?\xf9\x87T\x1d\xde\\\xde\u0a29m\xcc?3\xc3\xf2L\u008a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xe3A\v\xb7U|\xf9\x1dy\xfai\xd0\xdf\xea\n\xa0u@&Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3Ad-@\u04af\xce.\x91\a\xc6py\xacz&`\bl\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe3TS\xee\xf2\xcc2\x89\x10CR\x8d\t\x84i\x80\x00\xe0\x94\xe5\x10\xd6y\u007f\xba=f\x93\x83Z\x84N\xa2\xadT\x06\x91\x97\x1b\x8a\x03\xae9\xd4s\x83\xe8t\x00\x00\u07d4\xe5\x14!\xf8\xee\"\x10\xc7\x1e\xd8p\xfea\x82v\u0215J\xfb\xe9\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\x1e\xb8~\u007f\xb71\x1fR(\xc4y\xb4\x8e\u0247\x881\xacL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5!V1\xb1BH\xd4Z%R\x96\xbe\xd1\xfb\xfa\x030\xff5\x89G\x03\xe6\xebR\x91\xb8\x00\x00\xe0\x94\xe5(\xa0\xe5\xa2g\xd6g\xe99:e\x84\xe1\x9b4\u071b\xe9s\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xe54%\xd8\xdf\x1f\x11\xc3A\xffX\xae_\x148\xab\xf1\xcaS\u03c9\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\u07d4\xe5No\x9c\xffV\xe1\x9cF\x1e\xb4T\xf9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5A\x02SM\xe8\xf2>\xff\xb0\x93\xb3\x12B\xad;#?\xac\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe5E\xee\x84\xeaH\xe5d\x16\x1e\x94\x82\u055b\xcf@j`,\xa2\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xe5H\x1a\u007f\xedB\xb9\x01\xbb\xed x\x9b\u052d\xe5\r_\x83\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5Y\xb5\xfd3{\x9cUr\xa9\xbf\x9e\x0f%!\xf7\xd4F\xdb\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\\\x80R\n\x1b\x0fu[\x9a,\xd3\xce!Ov%e>\x8a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe5mC\x13$\xc9)\x11\xa1t\x9d\xf2\x92p\x9c\x14\xb7ze\u034a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\xe5})\x95\xb0\xeb\xdf?<\xa6\xc0\x15\xeb\x04&\r\xbb\x98\xb7\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\u51f1j\xbc\x8at\b\x1e6\x13\xe1CB\xc03u\xbf\bG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\x89\xfav\x98M\xb5\xec@\x04\xb4n\u8954\x92\xc3\aD\u0389\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xe5\x8d\xd228\xeen\xa7\xc2\x13\x8d8]\xf5\x00\xc3%\xf3v\xbe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe5\x95?\xeaIq\x04\xef\x9a\xd2\xd4\xe5\x84\x1c'\x1f\a5\x19\u0089&)\xf6n\fS\x00\x00\x00\xe0\x94\u5587\x97F\x8e\xf7g\x10\x1bv\x1dC\x1f\xce\x14\xab\xff\u06f4\x8a\x01\xb3\xd9i\xfaA\x1c\xa0\x00\x00\u07d4\xe5\x97\xf0\x83\xa4i\xc4Y\x1c=+\x1d,w'\x87\xbe\xfe'\xb2\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe5\x9b;\xd3\x00\x89?\x97#>\xf9G\xc4or\x17\xe3\x92\xf7\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xa3e4<\xc4\xeb\x1ew\x03h\xe1\xf1\x14Jw\xb82\xd7\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe5\xa3\xd7\xeb\x13\xb1\\\x10\x01w#m\x1b\xeb0\xd1~\xe1T \x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xaa\v\x83;\xb9\x16\xdc\x19\xa8\xddh?\x0e\xde$\x1d\x98\x8e\xba\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u5def\x14i\x86\xc0\xff\x8f\x85\xd2.l\xc34\a}\x84\xe8$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xb8&\x19l\x0e\x1b\xc1\x11\x9b\x02\x1c\xf6\xd2Y\xa6\x10\u0256p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\xb9o\u026c\x03\xd4H\xc1a:\xc9\x1d\x15\x97\x81E\xdb\xdf\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u5e40\u048e\xec\xe2\xc0o\xcal\x94s\x06\x8b7\u0526\xd6\xe9\x89%\xaf\u058c\xac+\x90\x00\x00\u07d4\u5eb4\xf0\xaf\u0629\u0463\x81\xb4Wa\xaa\x18\xf3\xd3\xcc\xe1\x05\x89Q\xbf\xd7\xc18x\xd1\x00\x00\u07d4\xe5\xbc\u020c;%on\xd5\xfeU\x0eJ\x18\x19\x8b\x943V\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xbd\xf3OL\xccH>L\xa50\xcc|\xf2\xbb\x18\xfe\xbe\x92\xb3\x89\x06\xd85\xa1\v\xbc\xd2\x00\x00\u07d4\xe5\u0713I\xcbR\xe1a\x19a\"\u03c7\xa3\x896\xe2\xc5\u007f4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xe38\x00\xa1\xb2\xe9k\xde\x101c\n\x95\x9a\xa0\a\xf2nQ\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\xe3~\x19@\x8f,\xfb\xec\x834\x9d\u0501S\xa4\xa7\x95\xa0\x8f\x89\u3bb5sr@\xa0\x00\x00\u07d4\xe5\xed\xc7>bo]4A\xa4U9\xb5\xf7\xa3\x98\u0153\xed\xf6\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xe5\xed\xf8\x12?$\x03\xce\x1a\x02\x99\xbe\xcfz\xactM\a_#\x89\n\xdaUGK\x814\x00\x00\u07d4\xe5\xf8\xefm\x97\x066\xb0\u072aO \x0f\xfd\xc9\xe7Z\xf1t\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xfb1\xa5\xca\xeej\x96\xde9;\xdb\xf8\x9f\xbee\xfe\x12[\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xfb\xe3I\x84\xb67\x19o3\x1cg\x9d\f\fG\xd84\x10\xe1\x89llD\xfeG\xec\x05\x00\x00\u07d4\xe6\tU\xdc\v\xc1V\xf6\xc4\x18I\xf6\xbdwk\xa4K\x0e\xf0\xa1\x89\x10C\x16'\xa0\x93;\x00\x00\u07d4\xe6\nU\xf2\u07d9m\u00ee\xdbil\b\xdd\xe09\xb2d\x1d\xe8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6\x11[\x13\xf9y_~\x95e\x02\xd5\aEg\u06b9E\xcek\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xe6\x1f(\t\x15\xc7t\xa3\x1d\"<\xf8\f\x06\x92f\xe5\xad\xf1\x9b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xe6/\x98e\a\x12\xeb\x15\x87S\xd8)r\xb8\u9723\xf6\x18w\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6/\x9d|d\xe8\xe2cZ\xeb\x88=\xd7;\xa6\x84\xee|\x10y\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe6>xt\x14\xb9\x04\x84x\xa5\a35\x9e\xcd\xd7\xe3dz\xa6\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe6FfXr\xe4\v\rz\xa2\xff\x82r\x9c\xaa\xba[\xc3\u8789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe6N\xf0\x12e\x8dT\xf8\xe8`\x9cN\x90#\xc0\x9f\xe8e\xc8;\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\xe6On\x1dd\x01\xb5l\akd\xa1\xb0\x86}\v/1\rN\x89\x02\u02edq\xc5:\xe5\x00\x00\u07d4\xe6g\xf6R\xf9W\u008c\x0ef\u04364\x17\xc8\f\x8c\x9d\xb8x\x89 \x9d\x92/RY\xc5\x00\x00\xe0\x94\xe6w\xc3\x1f\xd9\xcbr\x00u\u0724\x9f\x1a\xbc\xcdY\xec3\xf74\x8a\x01\xa6\u05be\xb1\xd4.\xe0\x00\x00\u07d4\xe6|,\x16e\u02038h\x81\x87b\x9fI\xe9\x9b`\xb2\u04fa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xe6\x9al\xdb:\x8a}\xb8\xe1\xf3\f\x8b\x84\xcds\xba\xe0+\xc0\xf8\x8a\x03\x94\xfd\xc2\xe4R\xf6q\x80\x00\u07d4\xe6\x9d\x1c7\x8bw\x1e\x0f\xef\xf0Q\xdbi\xd9f\xacgy\xf4\xed\x89\x1d\xfaj\xaa\x14\x97\x04\x00\x00\u07d4\xe6\x9f\xcc&\xed\"_{.7\x984\xc5$\xd7\f\x175\u5f09lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\xa3\x01\x0f\x02\x01\xbc\x94\xffg\xa2\xf6\x99\xdf\xc2\x06\xf9\xe7gB\x89/\xa7\xcb\xf6dd\x98\x00\x00\u07d4\xe6\xa6\xf6\xddop\xa4V\xf4\xec\x15\xefz\xd5\xe5\u06f6\x8b\xd7\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe6\xb2\x0f\x98\n\xd8S\xad\x04\xcb\xfc\x88|\xe6`\x1ck\xe0\xb2L\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u6cec?]M\xa5\xa8\x85}\v?0\xfcK+i+w\u05c9O%\x91\xf8\x96\xa6P\x00\x00\u07d4\xe6\xb9T_~\u0406\xe5R\x92F9\xf9\xa9\xed\xbb\xd5T\v>\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\xe0\x94\xe6\xbc\xd3\n\x8f\xa18\xc5\xd9\xe5\xf6\xc7\xd2\u0680i\x92\x81-\u034a7\x0e\xa0\xd4|\xf6\x1a\x80\x00\x00\u07d4\xe6\xc8\x1f\xfc\xec\xb4~\xcd\xc5\\\vq\xe4\x85_>^\x97\xfc\x1e\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\xe6\xcb&\vqmL\n\xb7&\xee\xeb\a\xc8pr\x04\xe2v\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe6\xcb?1$\xc9\xc9\xcc84\xb1'K\xc33dV\xa3\x8b\xac\x89\x17+\x1d\xe0\xa2\x13\xff\x00\x00\xe0\x94\xe6\xd2\"\t\xff\u0438u\t\xad\xe3\xa8\xe2\xefB\x98y\u02c9\xb5\x8a\x03\xa7\xaa\x9e\x18\x99\xca0\x00\x00\u07d4\xe6\u051f\x86\xc2(\xf4sg\xa3^\x88l\xaa\xcb'\x1eS\x94)\x89\x16^\xc0\x9d\xa7\xa1\x98\x00\x00\u07d4\xe6\xe6!\xea\xab\x01\xf2\x0e\xf0\x83k|\xadGFL\xb5\xfd<\x96\x89\x11!\x93B\xaf\xa2K\x00\x00\u07d4\xe6\xe8\x861{jf\xa5\xb4\xf8\x1b\xf1d\xc58\xc2d5\x17e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\u98ddu\x0f\xe9\x949N\xb6\x82\x86\xe5\xeab\xa6\x99x\x82\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xe6\xec\\\xf0\u011b\x9c1~\x1epc\x15\uf7b7\xc0\xbf\x11\xa7\x8a\x03\xa4i\xf3F~\x8e\xc0\x00\x00\u07d4\xe6\xf5\xebd\x9a\xfb\x99Y\x9cAK'\xa9\xc9\xc8U5\u007f\xa8x\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xe6\xfe\n\xfb\x9d\xce\xdd7\xb2\xe2,E\x1b\xa6\xfe\xabg4\x803\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe7\x10\xdc\u041b\x81\x01\xf9C{\xd9}\xb9\ns\xef\x99=\v\xf4\x89\x14\xee6\xc0Z\xc2R\x00\x00\u07d4\xe7'\xe6~\xf9\x11\xb8\x1fl\xf9\xc7?\xcb\xfe\xbc+\x02\xb5\xbf\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7.\x1d3\\\u009a\x96\xb9\xb1\xc0/\x00:\x16\xd9q\xe9\v\x9d\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe71\x1c\x953\xf0\t,rH\xc9s\x9b[,\x86J4\xb1\u0389\x97\xf9}l\xc2m\xfe\x00\x00\u07d4\xe7;\xfe\xad\xa6\xf0\xfd\x01o\xbc\x84>\xbc\xf6\xe3p\xa6[\xe7\f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe7<\xcfCg%\xc1Q\xe2U\xcc\xf5!\f\xfc\xe5\xa4?\x13\xe3\x89\x01\x15NS!}\xdb\x00\x00\u07d4\xe7B\xb1\xe6\x06\x9a\x8f\xfc'\f\xc6\x1f\xa1d\xac\x15SE\\\x10]\x04\x88~\x14\x89\x06\x96\xd8Y\x00 \xbb\x00\x00\u07d4\xe7\\\x1f\xb1w\b\x9f>X\xb1\x06y5\xa6Yn\xf1s\u007f\xb5\x89\x05j\x87\x9f\xa7uG\x00\x00\u07d4\xe7\\;8\xa5\x8a?3\xd5V\x90\xa5\xa5\x97f\xbe\x18^\x02\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7a\xd2\u007f\xa3P,\xc7k\xb1\xa6\bt\x0e\x14\x03\u03dd\xfci\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe7f\xf3O\xf1o<\xfc\xc9s!r\x1fC\xdd\xf5\xa3\x8b\f\xf4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xe7m\x94Z\xa8\x9d\xf1\xe4W\xaa4+1\x02\x8a^\x910\xb2\u03897\b\xba\xed=h\x90\x00\x00\u07d4\xe7s^\xc7e\x18\xfcj\xa9-\xa8qZ\x9e\xe3\xf6%x\x8f\x13\x89lM\x16\v\xaf\xa1\xb7\x80\x00\xe0\x94\xe7z\x89\xbdE\xdc\x04\xee\xb4\xe4\x1d{Ykp~nQ\xe7L\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xe7}}\uac96\u0234\xfa\a\xca;\xe1\x84\x16=Zm`l\x89\x05\x049\x04\xb6q\x19\x00\x00\u07d4\xe7\u007f\xeb\xab\xdf\b\x0f\x0f]\xca\x1d?Wf\xf2\xa7\x9c\x0f\xfa|\x89K\"\x9d(\xa8Ch\x00\x00\xe0\x94\u7025c\x06\xba\x1ek\xb31\x95,\"S\x9b\x85\x8a\xf9\xf7}\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xe7\x81\xecs-@\x12\x02\xbb\x9b\xd18`\x91\r\xd6\u009a\xc0\xb6\x89C8t\xf62\xcc`\x00\x00\u07d4\xe7\x84\xdc\xc8s\xaa\x8c\x15\x13\xec&\xff6\xbc\x92\xea\xc6\xd4\xc9h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\x91-L\xf4V,W=\xdc[q\xe3s\x10\xe3x\xef\x86\u0249\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xe7\x91\u0545\xb8\x996\xb2])\x8f\x9d5\xf9\xf9\xed\xc2Z)2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\x924\x9c\xe9\xf6\xf1O\x81\xd0g@\x96\xbe\xfa\x1f\x92!\xcd\xea\x89[]#J\r\xb48\x80\x00\u07d4\xe7\x96\xfdN\x83\x9bL\x95\xd7Q\x0f\xb7\xc5\xc7+\x83\xc6\xc3\xe3\u01c9\x1b\xc43\xf2?\x83\x14\x00\x00\xe0\x94\xe7\xa4/Y\xfe\xe0t\xe4\xfb\x13\xea\x9eW\xec\xf1\xccH(\"I\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xe7\xa4V\f\x84\xb2\x0e\x0f\xb5LIg\f)\x03\xb0\xa9lB\xa4\x89 j\xea\u01e9\x03\x98\x00\x00\u07d4\xe7\xa8\xe4q\xea\xfby\x8fET\xccnRg0\xfdV\xe6,}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u7f82\xc6Y<\x1e\xed\xdd*\xe0\xb1P\x01\xff \x1a\xb5{/\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\xe7\u01b5\xfc\x05\xfct\x8e[C\x81rdI\xa1\xc0\xad\x0f\xb0\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\xd1u$\xd0\v\xad\x82I|\x0f'\x15jd\u007f\xf5\x1d'\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe7\xd2\x13\x94\u007f\u02d0J\xd78H\v\x1e\xed/\\2\x9f'\xe8\x89\x01\x03\u00f1\xd3\xe9\xc3\x00\x00\u07d4\xe7\xd6$\x06 \xf4,^\u06f2\xed\xe6\xae\xc4=\xa4\xed\x9bWW\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xda`\x9d@\xcd\xe8\x0f\x00\xce[O\xfbj\xa9\u04304\x94\xfc\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xf0oi\x9b\xe3\x1cD\vC\xb4\xdb\x05\x01\xec\x0e%&\x16D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7\xf4\xd7\xfeoV\x1f\u007f\xa1\xda0\x05\xfd6TQ\xad\x89\u07c9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\xfd\x8f\xd9Y\xae\xd2v~\xa7\xfa\x96\f\xe1\xdbS\xaf\x80%s\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe8\x0e\u007f\xef\x18\xa5\xdb\x15\xb0\x14s\xf3\xadkx\xb2\xa2\xf8\xac\u0649\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe8\x13\u007f\xc1\xb2\xec|\xc7\x10:\xf9!\x89\x9bJ9\xe1\xd9Y\xa1\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xe8\x1c-4l\n\xdfL\xc5g\b\xf69K\xa6\xc8\u0226J\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8,X\xc5yC\x1bg5F\xb5:\x86E\x9a\xca\xf1\u079b\x93\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe84\xc6C\x18 \\\xa7\xddJ!\xab\xcb\b&l\xb2\x1f\xf0,\x8965\xc6 G9\u0640\x00\u07d4\xe86\x04\xe4\xffk\xe7\xf9o`\x18\xd3\xec0r\xecR]\xffk\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94\xe8E\xe3\x87\xc4\xcb\u07d8\"\x80\xf6\xaa\x01\xc4\x0eK\xe9X\u0772\x8a\x05K@\xb1\xf8R\xbd\xa0\x00\x00\u07d4\xe8H\xca~\xbf\xf5\xc2O\x9b\x9c1g\x97\xa4;\xf7\xc3V)-\x89\x06.\x11\\\x00\x8a\x88\x00\x00\u07d4\xe8KU\xb5%\xf1\x03\x9etK\x91\x8c\xb33$\x92\xe4^\xcaz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe8O\x80v\xa0\xf2\x96\x9e\xcd3>\xef\x8d\xe4\x10B\x98b\x91\xf2\x89\x17k4O*x\xc0\x00\x00\u07d4\xe8d\xfe\xc0~\xd1!Je1\x1e\x11\xe3)\xde\x04\r\x04\xf0\xfd\x89Y\u0283\xf5\xc4\x04\x96\x80\x00\u07d4\xe8}\xba\xc66\xa3w!\xdfT\xb0\x8a2\xefIY\xb5\xe4\xff\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe8~\x9b\xbf\xbb\xb7\x1c\x1at\ft\xc7#Bm\xf5]\x06=\u064a\x01\xb1\x92\x8c\x00\u01e68\x00\x00\u07d4\xe8~\xacm`+A\t\xc9g\x1b\xf5{\x95\f,\xfd\xb9\x9dU\x89\x02\xb4\xf2\x19r\xec\xce\x00\x00\xe0\x94\u807b\xbeir-\x81\xef\xec\xaaH\u0455*\x10\xa2\xbf\xac\x8f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xe8\x92Is\x8b~\xce\xd7\xcbfjf\xe4s\xbcv\x82/U\t\x8d\x89\xb9\x1c\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xc3\u04f0\xe1\u007f\x97\xd1\xe7V\xe6\x84\xf9N\x14p\xf9\x9c\x95\xa1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xe8\xc3\xf0E\xbb}8\xc9\xd2\U000d5c3a\x84\x92\xb2S#\t\x01\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\xe8\xccC\xbcO\x8a\xcf9\xbf\xf0N\xbf\xbfB\xaa\xc0j2\x84p\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe8\xd9B\xd8/\x17^\xcb\x1c\x16\xa4\x05\xb1\x01C\xb3\xf4k\x96:\x89\x1e\xd2\xe8\xffm\x97\x1c\x00\x00\u07d4\xe8\u077e\xd72\xeb\xfeu@\x96\xfd\xe9\bk\x8e\xa4\xa4\xcd\xc6\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xder^\xca]\xef\x80_\xf7\x94\x1d1\xac\x1c.4-\xfe\x95\x89\x85~\ro\x1d\xa7j\x00\x00\u07d4\xe8\xe9\x85\x05\x86\xe9OR\x99\xabIK\xb8!\xa5\xf4\f\x00\xbd\x04\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xe8\xea\u047b\x90\xcc\u00ee\xa2\xb0\xdc\u0175\x80VUFU\xd1\u054a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4\xe8\xea\xf1)D\t-\xc3Y\x9b9S\xfa|\xb1\xc9v\x1c\xc2F\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xe8\xedQ\xbb\xb3\xac\xe6\x9e\x06\x02K3\xf8hD\xc4sH\u06de\x8a\"\xf9\xea\x89\xf4\xa7\xd6\xc4\x00\x00\u07d4\xe8\xef\x10\r|\xe0\x89X2\xf2g\x8d\xf7-J\u03cc(\xb8\xe3\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xe8\xf2\x99i\xe7\\e\xe0\x1c\xe3\xd8aT }\n\x9e|v\xf2\x89\xa2/\xa9\xa7:'\x19\x80\x00\u07d4\xe8\xfc6\xb0\x13\x1e\xc1 \xac\x9e\x85\xaf\xc1\f\xe7\vV\u0636\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9\n5L\xec\x04\u059e]\x96\xdd\xc0\xc5\x13\x8d=3\x15\n\xa0\x89\x1b\x1a}\u03caD\u04c0\x00\xe0\x94\xe9\x13>}1\x84]_+f\xa2a\x87\x92\xe8i1\x1a\xcff\x8a\x05\x17\xc0\xcb\xf9\xa3\x90\x88\x00\x00\u07d4\xe9\x1d\xac\x01\x95\xb1\x9e7\xb5\x9bS\xf7\xc0\x17\xc0\xb29[\xa4L\x89e\xea=\xb7UF`\x00\x00\u07d4\xe9\x1f\xa0\xba\xda\u0779\xa9~\x88\xd3\xf4\xdb|U\u05bbt0\xfe\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xe9#\xc0aw\xb3B~\xa4H\xc0\xa6\xff\x01\x9bT\xccT\x8d\x95\x89\x01\xf7\x80\x01Fg\xf2\x80\x00\xe0\x94\xe9=G\xa8\u0288]T\fNRo%\xd5\xc6\xf2\xc1\b\u0138\x8a\x17\xda:\x04\u01f3\xe0\x00\x00\x00\u07d4\xe9E\x8fh\xbb',\xb5g:\x04\xf7\x81\xb4\x03Uo\u04e3\x87\x89\x03N\x8b\x88\xce\xe2\xd4\x00\x00\u07d4\xe9IA\xb6\x03`\x19\xb4\x01j0\xc1\x03}Zi\x03\xba\xba\xad\x89*H\xac\xabb\x04\xb0\x00\x00\u07d4\xe9I[\xa5\x84'(\xc0\ud5fe7\xd0\xe4\"\xb9\x8di ,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe9M\xed\x99\u0735r\xb9\xbb\x1d\u02e3/m\xee\x91\xe0W\x98N\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94\xe9QyR}\uc951l\xa9\xa3\x8f!\\\x1e\x9c\xe77\xb4\u024a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xe9U\x91\x85\xf1f\xfc\x95\x13\xccq\x11aD\xce-\xeb\x0f\x1dK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xe9^\x92\xbb\xc6\xde\a\xbf:f\x0e\xbf_\xeb\x1c\x8a5'\xe1\u0148\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xe9e\u06a3@9\xf7\xf0\xdfb7Z7\u5acar\xb3\x01\xe7\x8a\x01\x03\xfd\xde\u0373\xf5p\x00\x00\u07d4\xe9i\xea\x15\x95\xed\xc5\u0127\a\xcf\xde8\t)c2Q\xa2\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9k\x18N\x1f\x0fT\x92J\xc8t\xf6\v\xbfDptF\xb7+\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\xe0\x94\xe9m}L\xdd\x15U:NM1mmd\x80\xca<\xea\x1e8\x8a\x02\x95]\x02\xe1\xa15\xa0\x00\x00\u07d4\xe9n-8\x13\xef\xd1\x16_\x12\xf6\x02\xf9\u007fJb\x90\x9d\x1b;\xc0\xe9\xaa\"\u007f\x90\x89'\xcaK\xd7\x19\xf0\xb8\x00\x00\u07d4\xea,\x19}&\xe9\x8b\r\xa8>\x1br\u01c7a\x8c\x97\x9d=\xb0\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94\xea7y\xd1J\x13\xf6\u01c5f\xbc\xde@5\x91A:b9\u06ca)\xb7d2\xb9DQ \x00\x00\u07d4\xeaN\x80\x9e&j\xe5\xf1<\xdb\u33dd\x04V\xe68m\x12t\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\xe0\x94\xeaS\xc9T\xf4\xed\x97\xfdH\x10\x11\x1b\u06b6\x9e\xf9\x81\xef%\xb9\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xeaS\xd2ed\x85\x9d\x9e\x90\xbb\x0eS\xb7\xab\xf5`\xe0\x16,8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xea`Ci\x12\xdek\xf1\x87\u04e4r\xff\x8fS3\xa0\xf7\xed\x06\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xea`T\x9e\xc7U?Q\x1d!I\xf2\xd4fl\xbd\x92C\xd9<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeaf\xe7\xb8M\u037f6\xee\xa3\xe7[\x858*u\xf1\xa1]\x96\x89]\xbc\x91\x91&o\x11\x80\x00\u07d4\xeahlPW\t<\x17\x1cf\u06d9\xe0\x1b\x0e\xce\xcb0\x86\x83\x89\x14\u0768],\xe1G\x80\x00\u07d4\xeaj\xfe,\xc9(\xac\x83\x91\xeb\x1e\x16_\xc4\x00@\xe3t!\u7262\u007f\xa0c\xb2\xe2\xe6\x80\x00\u07d4\xeay\x05}\xab\xef^d\xe7\xb4O\u007f\x18d\x8e~S7\x18\u0489\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xea|Mm\xc7)\xcdk\x15|\x03\xad#|\xa1\x9a \x93F\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\x81h\xfb\xf2%\xe7\x86E\x9c\xa6\xbb\x18\xd9c\xd2kPS\t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xea\x81\u02868T\f\xd9\xd4\xd7=\x06\x0f,\xeb\xf2$\x1f\xfc>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xea\x83\x17\x19yYB@A\xd9\xd7\xc6z>\xce\x1d\xbbx\xbbU\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xea\x85'\xfe\xbf\xa1\xad\xe2\x9e&A\x93)\u04d3\xb9@\xbb\xb7\u0709lj\xccg\u05f1\xd4\x00\x00\u07d4\xea\x8f0\xb6\xe4\xc5\xe6R\x90\xfb\x98d%\x9b\u0159\x0f\xa8\ue289\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\x94\xf3(\b\xa2\uf29b\xf0\x86\x1d\x1d$\x04\xf7\xb7\xbe%\x8a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\xa4\\\xea\x02\xd8},\xc8\xfd\xa9CN-\x98[\xd4\x03\x15\x84\x89h\x1f\xc2\xccn+\x8b\x00\x00\xe0\x94\uac3d\x14\x83\t\x18l\xf8\xcb\xd1;r2\xd8\tZ\u02c3:\x8a\x02C\x9a\x88\x1cjq|\x00\x00\u07d4\uaed0\xd3y\x89\xaa\xb3\x1f\xea\xe5G\xe0\xe6\xf3\x99\x9c\xe6\xa3]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xc0\x82~\xff\fn?\xf2\x8a}JT\xf6\\\xb7h\x9d{\x99\x89\x9a\xd9\u67ddGR\x00\x00\u07d4\xea\xc1H(&\xac\xb6\x11\x1e\x19\xd3@\xa4_\xb8QWk\xed`\x89\x01\xbe\x8b\xab\x04\u067e\x80\x00\xe0\x94\xea\xc1{\x81\xedQ\x91\xfb\b\x02\xaaT3s\x13\x83A\a\xaa\xa4\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xea\u00efW\x84\x92\u007f\u9958\xfcN\xec8\xb8\x10/7\xbcX\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xea\u01b9\x88BT.\xa1\v\xb7O&\xd7\xc7H\x8fi\x8bdR\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xea\xc7h\xbf\x14\xb8\xf9C.i\xea\xa8*\x99\xfb\xeb\x94\xcd\f\x9c\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xea\xd2\x1c\x1d\xec\u03ff\x1c\\\xd9f\x88\xa2Gki\xba\a\xceJ\x89\x03\xf2M\x8eJ\x00p\x00\x00\u07d4\xea\xd4\xd2\xee\xfbv\xab\xaeU3\x96\x1e\xdd\x11@\x04\x06\xb2\x98\xfc\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xea\xd6Rb\xed]\x12-\xf2\xb2u\x14\x10\xf9\x8c2\xd1#\x8fQ\x89\x05\x83\x17\xedF\xb9\xb8\x00\x00\u07d4\xea\xd7P\x16\u3801Pr\xb6\xb1\b\xbc\xc1\xb7\x99\xac\xf08>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xea#\xaa\x05r\x00\xe7\xc9\xc1^\x8f\xf1\x90\xd0\xe6l\f\x0e\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xed\x16\xea\xf5\u06ab[\xf0)^^\a\u007fY\xfb\x82U\x90\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xea\xed\xcck\x8bib\xd5\xd9(\x8c\x15lW\x9dG\xc0\xa9\xfc\xff\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xea\xf5#\x88Tn\xc3Z\xcaolc\x93\xd8\xd6\t\xde:K\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xeb\x10E\x8d\xac\xa7\x9eJk$\xb2\x9a\x8a\x8a\xdaq\x1b\u007f.\xb6\x89\u063beI\xb0+\xb8\x00\x00\u07d4\xeb\x1c\xea{E\u047dM\x0e*\x00{\u04ff\xb3Tu\x9e,\x16\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xeb%H\x1f\u035c\"\x1f\x1a\xc7\xe5\xfd\x1e\u0353\a\xa1b\x15\xb8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xeb.\xf3\u04cf\xe6R@<\xd4\xc9\xd8^\xd7\xf0h,\xd7\xc2\u078a\t\x0fSF\b\xa7(\x80\x00\x00\xe0\x94\xeb;\xddY\xdc\u0765\xa9\xbb*\xc1d\x1f\xd0!\x80\xf5\xf3e`\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4\xeb<\xe7\xfc8\x1cQ\xdb}_\xbdi/\x8f\x9e\x05\x8aLp=\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xebE?Z:\xdd\u074a\xb5gP\xfa\xdb\x0f\xe7\xf9M\x9c\x89\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xebO\x00\xe2\x836\xea\t\x94%\x88\ueb12\x18\x11\xc5\"\x14<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebR\xab\x10U4\x922\x9c\x1cT\x83:\xe6\x10\xf3\x98\xa6[\x9d\x89\b=lz\xabc`\x00\x00\u07d4\xebW\r\xba\x97R'\xb1\xc4-n\x8d\xea,V\u026d\x96\x06p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebc\x94\xa7\xbf\xa4\u0489\x11\u0565\xb2>\x93\xf3^4\f\"\x94\x89\x04:w\xaa\xbd\x00x\x00\x00\u07d4\xebh\x10i\x1d\x1a\xe0\u045eG\xbd\"\u03be\u0cfa'\xf8\x8a\x89\x87\x85c\x15\xd8x\x15\x00\x00\u07d4\xebvBL\x0f\u0557\xd3\xe3A\xa9d*\xd1\xee\x11\x8b+W\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb| +F+|\u0145]t\x84u_n&\xefC\xa1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\x83\\\x1a\x91\x18\x17\x87\x8a3\xd1gV\x9e\xa3\xcd\u04c7\xf3(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\ub268\x82g\t\t\xcf7~\x9ex(n\xe9{\xa7\x8dF\u0089+|\xc2\xe9\xc3\"\\\x00\x00\xe0\x94\xeb\x90\u01d3\xb3S\x97a\xe1\xc8\x14\xa2\x96q\x14\x86\x92\x19>\xb4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xeb\x9c\xc9\xfe\bi\xd2\u06b5,\u01ea\xe8\xfdW\xad\xb3_\x9f\xeb\x89j\x93\xbb\x17\xaf\x81\xf8\x00\x00\xe0\x94\ub8c8\xb0\xda'\xc8{\x1c\xc0\xea\xc6\xc5{,Z\vE\x9c\x1a\x8a\x01p\xa0\xf5\x04\x0eP@\x00\x00\u07d4\xeb\xaa!m\xe9\xccZC\x03\x17\a\xd3o\xe6\u057e\xdc\x05\xbd\xf0\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xeb\xac+D\b\xefT1\xa1;\x85\b\xe8bP\x98!\x14\xe1E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb\xb6,\xf8\xe2,\x88K\x1b(\xc6\xfa\x88\xfb\xbc\x17\x93\x8a\xa7\x87\x89+By\x84\x03\u0278\x00\x00\u07d4\xeb\xb7\xd2\xe1\x1b\u01b5\x8f\n\x8dE\xc2\xf6\xde0\x10W\n\u0211\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4\xeb\xbbO,=\xa8\xbe>\xb6-\x1f\xfb\x1f\x95\x02a\u03d8\xec\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\xbdM\xb9\x01\x99R\u058b\x1b\x0fm\x8c\xf0h<\x008{\xb5\x89\x12\x04\x01V=}\x91\x00\x00\u07d4\xeb\xbe\xeb%\x91\x84\xa6\xe0\x1c\xcc\xfc\"\a\xbb\u0603xZ\xc9\n\x89!\x9b\xc1\xb0G\x83\xd3\x00\x00\u07d4\xeb\xd3V\x15j81#4=H\x84;\xff\xeda\x03\xe8f\xb3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xeb\xd3{%ec\xe3\fo\x92\x89\xa8\xe2p/\bR\x88\b3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xeb\xe4l\xc3\xc3L2\xf5\xad\xd6\xc3\x19[\xb4\x86\xc4q>\xb9\x18\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xeb\xff\x84\xbb\xefB0q\xe6\x04\xc3a\xbb\xa6w\xf5Y=\xefN\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xec\t'\xba\xc7\xdc6f\x9c(5J\xb1\xbe\x83\xd7\xee\xc3\t4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\x0e\x18\xa0\x1d\xc4\xdc]\xaa\xe5g\xc3\xfaL\u007f\x8f\x9bY\x02\x05\x89\x11\x1f\xfe@JA\xe6\x00\x00\xe0\x94\xec\x116,\xec\x81\t\x85\xd0\xeb\xbd{sE\x14D\x98[6\x9f\x8a\x06ZNIWpW1\x80\x00\u07d4\xec,\xb8\xb97\x8d\xff1\xae\xc3\xc2.\x0em\xad\xff1J\xb5\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xec0\xad\u0749[\x82\xee1\x9eT\xfb\x04\xcb+\xb09q\xf3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec;\x8bX\xa1'\x03\xe5\x81\xce_\xfd~!\xc5}\x1e\\f?\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xecHg\xd2\x17Z\xb5\xb9F\x93aYUFUF\x84\u0364`\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xecM\b\xaa.GIm\u0287\"]\xe3?+@\xa8\xa5\xb3o\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\xecX\xbc\r\f \xd8\xf4\x94efAS\xc5\xc1\x96\xfeY\u6f89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xec[\x19\x8a\x00\u03f5Z\x97\xb5\xd56D\xcf\xfa\x8a\x04\u04abE\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec]\xf2'\xbf\xa8]z\xd7kBn\x1c\xee\x96;\xc7\xf5\x19\u074965\u026d\xc5\u07a0\x00\x00\xe0\x94\xec_\xea\xfe!\f\x12\xbf\u0265\xd0Y%\xa1#\xf1\xe7?\xbe\xf8\x8a`\x8f\xcf=\x88t\x8d\x00\x00\x00\u07d4\xeci\x04\xba\xe1\xf6\x97\x90Y\x17\t\xb0`\x97\x83s?%s\xe3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xecs\x11L^@o\u06fe\t\xb4\xfab\x1b\xd7\x0e\xd5N\xa1\xef\x8a\x050%\xcd!o\xceP\x00\x00\u07d4\xecs\x83=\xe4\xb8\x10\xbb\x02x\x10\xfc\x8fi\xf5D\xe8<\x12\u044965\u026d\xc5\u07a0\x00\x00\u07d4\xecu\xb4\xa4u\x13\x12\v\xa5\xf8`9\x81O\x19\x98\xe3\x81z\u00c9\t\xb0\xbc\xe2\xe8\xfd\xba\x00\x00\u07d4\xecv\xf1.W\xa6U\x04\x03?,\v\xceo\xc0;\xd7\xfa\n\u0109\xc2\x12z\xf8X\xdap\x00\x00\u0794\xec\x80\x14\xef\xc7\xcb\xe5\xb0\xceP\xf3V,\xf4\xe6\u007f\x85\x93\xcd2\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xec\x82\xf5\r\x06G_hM\xf1\xb3\x92\xe0\r\xa3A\xaa\x14TD\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xec\x83\xe7\x98\u00d6\xb7\xa5^*\"$\xab\u0343K'\xeaE\x9c\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\x89\xf2\xb6x\xa1\xa1[\x914\xec^\xb7\fjb\a\x1f\xba\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\x8c\x1d{j\xac\xcdB\x9d\xb3\xa9\x1e\xe4\xc9\xeb\x1c\xa4\xf6\xf7<\x89\xe6d\x99\"\x88\xf2(\x00\x00\xe0\x94\xec\x98Q\xbd\x91rpa\x02g\xd6\x05\x18\xb5M<\xa2\xb3[\x17\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xec\x99\xe9]\xec\xe4o\xff\xfb\x17^\xb6@\x0f\xbe\xbb\b\ue6d5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xec\xa5\xf5\x87\x92\xb8\xc6-*\xf5Vq~\xe3\xee0(\xbeM\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xabZ\xba[\x82\x8d\xe1pS\x81\xf3\x8b\xc7D\xb3+\xa1\xb47\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\xec\xaf3P\xb7\xce\x14M\x06\x8b\x18`\x10\x85,\x84\xdd\f\xe0\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xb9LV\x8b\xfeY\xad\xe6Pd_O&0lsl\xac\xe4\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xec\xbeB^g\r9\tN \xfbVC\xa9\xd8\x18\xee\xd26\u078a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xec\xbe^\x1c\x9a\u04b1\xdc\xcf\n0_\xc9R/Fi\xdd:\xe7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xec\xcfz\x04W\xb5f\xb3F\xcag:\x18\x0fDA0!j\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4\xec\u0466(\x025\x1aAV\x8d#\x030\x04\xac\xc6\xc0\x05\xa5\u04c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xec\xd2v\xafd\u01dd\x1b\u0669+\x86\xb5\u835a\x95\xeb\x88\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xec\u0506\xfc\x19g\x91\xb9,\xf6\x12\xd3HaO\x91VH\x8b~\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\xda\xf92)\xb4^\xe6r\xf6]\xb5\x06\xfb^\xca\x00\xf7\xfc\xe6\x89W\x01\xf9m\xcc@\xee\x80\x00\u07d4\xec\xe1\x11g\vV<\u037e\xbc\xa5#\x84)\x0e\xcdh\xfe\\\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xec\xe1\x15&\x82\xb7Y\x8f\xe2\xd1\xe2\x1e\xc1U3\x88T5\xac\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xec\xe1)\bw\xb5\x83\xe3a\xa2\xd4\x1b\x00\x93F\xe6'N%8\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xec\xf0]\a\xea\x02n~\xbfIA\x00#5\xba\xf2\xfe\xd0\xf0\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\xf2L\xdd|\"\x92\x8cD\x1eiM\xe4\xaa1\xb0\xfa\xb5\x97x\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xec\xfd\x00M\x02\xf3l\xd4\u0634\xa8\xc1\xa9S;j\xf8\\\xd7\x16\x8a\x01\x0fA\xac\xb4\xbb;\x9c\x00\x00\xe0\x94\xed\x02\x06\xcb#1Q(\xf8\xca\xff&\xf6\xa3\v\x98Tg\xd0\"\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xed\x10e\xdb\u03dds\xc0O\xfcy\b\x87\r\x88\x14h\xc1\xe12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xed\x12vQ;o\u0186(\xa7A\x85\xc2\xe2\f\xbb\xcax\x17\xbf\x89\nZ\xa8P\t\xe3\x9c\x00\x00\xe0\x94\xed\x12\xa1\xba\x1f\xb8\xad\xfc\xb2\r\xfa\x19X.RZ\xa3\xb7E$\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xed\x16\xce9\xfe\xef;\xd7\xf5\xd1b\x04^\x0fg\xc0\xf0\x00F\xbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xed\x1a\\C\xc5t\xd4\xe94)\x9b$\xf1G,\u071f\xd6\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xed\x1b$\xb6\x91-Q\xb34\xac\r\xe6\xe7q\xc7\xc0EF\x95\xea\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xed\x1f\x1e\x11Z\r`\xce\x02\xfb%\xdf\x01M(\x9e:\f\xbe}\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xed10\\1\x9f\x92s\u04d3m\x8f[/q\u9c72)c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xed2z\x14\xd5\u03ed\u0641\x03\xfc\t\x99q\x8d~\xd7\x05(\xea\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d4\xed<\xbc7\x82\u03bdg\x98\x9b0\\A3\xb2\xcd\xe3\"\x11\xeb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xed@\x14S\x8c\xeefJ/\xbc\xb6\xdcf\x9fz\xb1m\v\xa5|\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedA\u188f\\\xaa\x848\x80\xefN\x8b\b\xbdl3\x14\x1e\u07c9*\xd5\xdd\xfaz\x8d\x83\x00\x00\xe0\x94\xedK\xe0J\x05-z\u0333\xdc\u03901\x9d\xba@ \xab,h\x8a\a\xf3zp\xea\xf3b\x17\x80\x00\xe0\x94\xedR\xa2\xcc\bi\u071e\x9f\x84+\u0415|G\xa8\xe9\xb0\xc9\xff\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xed[LA\xe7b\xd9B@Cs\xca\xf2\x1e\xd4a]%\xe6\xc1\x89m-O=\x95%\xb4\x00\x00\u07d4\xed`\u012bnT\x02\x061~5\x94zc\xa9\xcak\x03\xe2\u02c9\x03\x1a\u066d\vF\u007f\x80\x00\u07d4\xedd\x1e\x066\x8f\xb0\xef\xaa\x17\x03\xe0\x1f\xe4\x8fJhS\t\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedfC\xc0\xe8\x88K-2\x11\x857\x85\xa0\x8b\xf8\xf3>\u049f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xedp\xa3|\xdd\x1c\xbd\xa9tm\x93\x96X\xae*a\x81(\x85x\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\xedsFvn\x1agm\r\x06\xec\x82\x18g\xa2v\xa0\x83\xbf1\x89\u064a\t1\xcc-I\x00\x00\u07d4\xed\x86&\x16\xfc\xbf\xb3\xbe\xcbt\x06\xf7<\\\xbf\xf0\f\x94\aU\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xed\x9e\x03\f\xa7\\\xb1\u049e\xa0\x1d\rL\xdf\xdc\xcd8D\xb6\xe4\x89\x01\xac\xc1\x16\u03ef\xb1\x80\x00\xe0\x94\ud7bc\u02e4/\x98\x15\xe7\x823&m\xd6\xe85\xb6\xaf\xc3\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\ud7f1\xf5\xaf/\xbf\u007f\xfcP)\xce\xe4+p\xff\\'[\xf5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xed\xa4\xb2\xfaY\u0584\xb2z\x81\r\xf8\x97\x8as\xdf0\x8ac\u0089\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xed\xb4s59y\xa2\x06\x87\x9d\xe1D\xc1\n:\xcf\x12\xa7'OV9a\xf57R\x9d\x89\xc7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xeer\x88\xd9\x10\x86\xd9\xe2\xeb\x91\x00\x14\u066b\x90\xa0-x\u00a0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xee|=\xed|(\xf4Y\xc9/\xe1;M\x95\xba\xfb\xab\x026}\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xee\x86} \x91k\xd2\xe9\xc9\xec\xe0\x8a\xa0C\x85\xdbf|\x91.\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\ue25b\x02\xcb\xcb99\xcda\xde\x13B\xd5\x04\x82\xab\xb6\x852\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xee\x90m}_\x17H%\x81t\xbeL\xbc8\x93\x03\x02\xab{B\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\ue5ea\x8a\u019e\xdfz\x98}mp\x97\x9f\x8e\xc1\xfb\xcaz\x94\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xee\xa1\xe9y\x88\xdeu\xd8!\xcd(\xadh\"\xb2,\u0398\x8b1\x89\x1c0s\x1c\xec\x03 \x00\x00\xe0\x94\xee\u048c?\x06\x8e\tJ0K\x85<\x95\nh\t\xeb\xcb\x03\xe0\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xee\u04c4\xef-A\xd9\xd2\x03\x97NW\xc1#(\xeav\x0e\b\xea\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xdflB\x80\xe6\xeb\x05\xb94\xac\xe4(\xe1\x1dB1\xb5\x90[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xe7a\x84~3\xfda\u0653\x87\xee\x14b\x86\x94\u047f\xd5%\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xee\xe9\xd0Rn\xda\x01\xe41\x16\xa3\x952-\u0689pW\x8f9\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\xee\xf1\xbb\xb1\xe5\xa8?\u0782H\xf8\x8e\xe3\x01\x8a\xfa-\x132\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xfb\xa1-\xfc\x99gB\xdby\x04d\xca}';\xe6\xe8\x1b>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xfd\x05\xb0\xe3\xc4\x17\xd5[3C\x06\x04\x86\xcd\xd5\xe9*\xa7\xa6\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xef\r\xc7\xddzS\xd6\x12r\x8b\xcb\u04b2|\x19\xddM}fo\x89&A\x1c[5\xf0Z\x00\x00\u07d4\xef\x11RR\xb1\xb8E\u0345\u007f\x00-c\x0f\x1bo\xa3zNP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xef\x1c\x04w\xf1\x18M`\xac\u02b3t\xd3tUz\n>\x10\xf3\x89\b=lz\xabc`\x00\x00\u07d4\xef,4\xbbH}7b\xc3\u0327\x82\xcc\xddz\x8f\xbb\n\x991\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xef5\xf6\u0531\a^j\xa19\x15\x1c\x97K/FX\xf7\x058\x89<;\xc3?\x94\xe5\r\x80\x00\u07d4\xef9\u0291s\xdf\x15S\x1ds\xe6\xb7*hKQ\xba\x0f+\xb4\x89V\xa0\xb4un\xe28\x00\x00\u07d4\xefF<&y\xfb'\x91d\xe2\f=&\x915\x87s\xa0\xad\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xefG\xcf\a>6\xf2q\xd5\"\xd7\xfaNq \xadP\a\xa0\xbc\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\xefa\x15[\xa0\t\xdc\u07be\xf1\v(\xd9\xda=\x1b\xc6\xc9\xce\u0509\x034-`\xdf\xf1\x96\x00\x00\u0794\xefix\x1f2\xff\xce34o,\x9a\xe3\xf0\x84\x93\xf3\xe8/\x89\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xefv\xa4\u034f\xeb\xcb\u0278\x18\xf1x(\xf8\xd94s\xf3\xf3\u02c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\uf4c1\x8fhM\xb0\xc3g^\xc8\x132\xb3\x18>\xcc(\xa4\x95\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94\xef\x9fY\xae\xdaA\x8c\x14\x94h-\x94\x1a\xabI$\xb5\xf4\x92\x9a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\uf9b1\xf0\xdb`57\x82h\x91\xb8\xb4\xbc\x169\x84\xbb@\u03495e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xef\xbdR\xf9}\xa5\xfd:g:F\xcb\xf30D{~\x8a\xad\\\x89\x05l<\x9b\x80\xa0\xa6\x80\x00\xe0\x94\xef\xc8\xcf\x19c\u0269Rg\xb2(\xc0\x86#\x98\x89\xf4\xdf\xd4g\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xef\u02ae\x9f\xf6M,\xd9[RI\xdc\xff\xe7\xfa\xa0\xa0\xc0\xe4M\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xef\xcc\xe0k\xd6\b\x9d\x0eE\x8e\xf5a\xf5\xa6\x89H\n\xfep\x00\x89 \x86\xac5\x10R`\x00\x00\u07d4\xef\xe0g]\xa9\x8a]\xdap\u0356\x19k\x87\xf4\xe7&\xb43H\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xef\xe8\xff\x87\xfc&\x0e\agc\x8d\xd5\xd0/\xc4g.\x0e\xc0m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xeb\x19\x97\xaa\xd2w\xcc3C\x0ea\x11\xed\tCY@H\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xee\xa0\x10uo\x81\xdaK\xa2[r\x17\x87\xf0X\x17\v\uff49\x01\u009c\x9c\xf7p\xef\x00\x00\u07d4\xef\xf5\x1dr\xad\xfa\xe1C\xed\xf3\xa4+\x1a\xecU\xa2\xcc\xdd\v\x90\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xef\xf8kQ#\xbc\xdc\x17\xedL\xe8\xe0[~\x12\xe5\x13\x93\xa1\xf7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xef\xfc\x15\u41f1\xbe\xda\n\x8d\x13%\xbd\xb4\x17\"@\xdcT\n\x89\x03\x8599\xee\xe1\xde\x00\x00\xe0\x94\xf0\x11\x95\xd6W\xef<\x94.l\xb89I\xe5\xa2\v\\\xfa\x8b\x1e\x8a\x05ts\xd0]\xab\xae\x80\x00\x00\u07d4\xf0'\x96)Q\x01gB\x88\xc1\xd94g\x05=\x04\"\x19\xb7\x94\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\xf09h={=\"[\xc7\xd8\u07ed\xefc\x164A\xbeA\xe2\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xf0Jj7\x97\b\xb9B\x8dr*\xa2\xb0kw\xe8\x895\u03c9\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf0M,\x91\xef\xb6\xe9\xc4_\xfb\xe7KCL\x8c_+\x02\x8f\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf0W\xaaf\xcav~\xde\x12J\x1c[\x9c\xc5\xfc\x94\xef\v\x017\x89p\xa2K\u02b6\xf4]\x00\x00\u07d4\xf0[\xa8\u05f6\x859\xd930\v\xc9(\x9c=\x94t\xd0A\x9e\x89\x06\xda'\x02M\xd9`\x00\x00\u07d4\xf0\\\xee\xabeA\x05dp\x99Qw<\x84E\xad\x9fN\u01d7\x89\x10C\x16'\xa0\x93;\x00\x00\xe0\x94\xf0_\xcdL\rs\xaa\x16~US\xc8\xc0\xd6\xd4\xf2\xfa\xa3\x97W\x8a\x02\xd2\xd6l1p\xb2\x98\x00\x00\u07d4\xf0g\xe1\xf1\u0583UjL\xc4\xfd\f\x03\x13#\x9f2\xc4\xcf\u060965\u026d\xc5\u07a0\x00\x00\u07d4\xf0g\xfb\x10\u07f2\x93\u962b\xe5d\xc0U\xe34\x8f\x9f\xbf\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0h\xdf\xe9]\x15\xcd:\u007f\x98\xff\xa6\x88\xb44hB\xbe&\x90\x89D\n\xd8\x19\xe0\x97L\x00\x00\xe0\x94\xf0j\x85J<]\xc3m\x1cI\xf4\xc8}m\xb33\xb5~J\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf0y\xe1\xb1&_P\xe8\u0229\x8e\xc0\u01c1^\xb3\xae\xac\x9e\xb4\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\xe0\x94\xf0{\xd0\xe5\xc2\xcei\xc7\u0127$\xbd&\xbb\xfa\x9d*\x17\xca\x03\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xf0\x83*k\xb2U\x03\xee\xcaC[\xe3\x1b\v\xf9\x05\xca\x1f\xcfW\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf0\x9b>\x87\xf9\x13\xdd\xfdW\xae\x80I\xc71\u06e9\xb66\xdf\u00c9 \xf5\xb1\uab4d\x80\x00\x00\u07d4\xf0\xb14\v\x99oo\v\xf0\xd9V\x1c\x84\x9c\xaf\u007fD0\xbe\xfa\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xb1\xf9\xe2x2\xc6\xdei\x14\xd7\n\xfc#\x8ct\x99\x95\xac\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf0\xb4i\xea\xe8\x9d@\f\xe7\xd5\xd6j\x96\x95\x03p6\xb8\x89\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf0\xb9\u0583\u03a1+\xa6\x00\xba\xac\xe2\x19\xb0\xb3\xc9~\x8c\x00\xe4\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xbe\x0f\xafMy#\xfcDF\"\u0458\f\xf2\u0650\xaa\xb3\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xc0\x81\xdaR\xa9\xae6d*\xdf^\b _\x05\xc5Ah\xa6\x89\x06\x04o7\xe5\x94\\\x00\x00\u07d4\xf0\xc7\r\rm\xabvc\xaa\x9e\xd9\xce\xeaV~\xe2\u01b0'e\x89qC\x8a\u0167\x91\xa0\x80\x00\u07d4\xf0\xcb\xef\x84\xe1ic\x00\x98\xd4\xe3\x01\xb2\x02\b\xef\x05\x84j\u0249\x0e\v\x83EPkN\x00\x00\u07d4\xf0\xd2\x16c\u0630\x17n\x05\xfd\xe1\xb9\x0e\xf3\x1f\x850\xfd\xa9_\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf0\xd5\xc3\x1c\xcbl\xbe0\xc7\xc9\xea\x19\xf2h\xd1Y\x85\x1f\x8c\x9c\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xf0\xd6L\xf9\xdf\tt\x113\xd1pH_\xd2K\x00P\x11\xd5 \x89\x1b\b\x93A\xe1O\xcc\x00\x00\u07d4\xf0\xd8X\x10^\x1bd\x81\x01\xac?\x85\xa0\xf8\"+\xf4\xf8\x1dj\x89 \x86\xac5\x10R`\x00\x00\u07d4\xf0\xdcC\xf2\x05a\x91'P{+\x1c\x1c\xfd\xf3-(1\t \x89\x10^\xb7\x9b\x94\x17\b\x80\x00\u07d4\xf0\xe1\u07e4*\u07ac/\x17\xf6\xfd\xf5\x84\xc9Hb\xfdV3\x93\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf0\xe2d\x9c~j?,]\xfe3\xbb\xfb\xd9'\xca<5\nX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xe7\xfb\x9eB\nS@\xd56\xf4\x04\b4O\xea\xef\xc0j\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf1\x04b\xe5\x8f\xcc\a\U000d5121\x87c\x94Q\x16~\x85\x92\x01\x89\t4\xdd]3\xbc\x97\x00\x00\xe0\x94\xf1\x06a\xff\x94\x14\x0f >zH%rCy8\xbe\xc9\xc3\xf7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xf1\x14\xff\r\x0f$\xef\xf8\x96\xed\xdeTq\u07a4\x84\x82J\x99\xb3\x88\xbe -j\x0e\xda\x00\x00\u07d4\xf1\x16\xb0\xb4h\x0fS\xabr\xc9h\xba\x80.\x10\xaa\x1b\xe1\x1d\u0209\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xf1\x1c\xf5\xd3cto\xeehd\xd3\xca3m\xd8\x06y\xbb\x87\xae\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf1\x1e\x01\u01e9\xd1$\x99\x00_M\xaew\x16\tZ4\x17bw\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf1;\b0\x93\xbaVN-\xc61V\x8c\xf7T\r\x9a\x0e\xc7\x19\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf1O\x0e\xb8m\xb0\xebhu?\x16\x91\x8e]K\x80t7\xbd>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf1Qx\xff\xc4:\xa8\a\x0e\xce2~\x93\x0f\x80\x9a\xb1\xa5O\x9d\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xf1V\xdc\v*\x98\x1e[U\xd3\xf2\xf0;\x814\xe31\u06ed\xb7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf1]\x9dZ!\xb1\x92\x9ey\x03q\xa1\u007f\x16\xd9_\fie\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1^\x18,O\xbb\xady\xbd\x934\"B\xd4\xdc\xcf+\xe5\x89%\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf1bM\x98\ve3o\xea\u0166\xd5A%\x00\\\xfc\xf2\xaa\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1g\xf5\x86\x8d\xcfB3\xa7\x83\x06\th,\xaf-\xf4\xb1\xb8\a\x89\x81\xe5B\xe1\xa78?\x00\x00\u07d4\xf1m\xe1\x89\x1d\x81\x96F\x13\x95\xf9\xb16&[;\x95F\xf6\xef\x89\x01\xb2\x8e\x1f\x98\xbb\u0380\x00\u07d4\xf1z\x92\xe06\x1d\xba\xce\xcd\xc5\xde\r\x18\x94\x95Z\xf6\xa9\xb6\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1z\xdbt\x0fE\u02fd\xe3\tN~\x13qo\x81\x03\xf5c\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x8b\x14\xcb\xf6iC6\xd0\xfe\x12\xac\x1f%\xdf-\xa0\xc0]\xbb\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4\xf1\x9b98\x9dG\xb1\x1b\x8a,?\x1d\xa9\x12M\xec\xff\xbe\xfa\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x9f\x195\b9>M*\x12{ \xb2\x03\x1f9\xc8%\x81\u0189\xbd\xbdz\x83\xbd/l\x00\x00\u07d4\xf1\xa1\xf3 @yd\xfd<\x8f.,\u0224X\r\xa9O\x01\xea\x89ll!wU|D\x00\x00\u07d4\xf1\xb4\xec\xc65%\xf7C,=\x83O\xfe+\x97\x0f\xbe\xb8r\x12\x89\xa2\xa2@h\xfa\u0340\x00\x00\u07d4\U000753ef\xfa\x87\x94\xf5\n\xf8\xe8\x83\t\u01e6&TU\xd5\x1a\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xf1\xc8\u0129A\xb4b\x8c\rl0\xfd\xa5dR\u065c~\x1bd\x89N\x8c\xea\x1e\xdeu\x04\x00\x00\u07d4\xf1\xda@so\x99\xd5\xdf;\x06\x8a]t_\xaf\xc6F?\u0271\x89\x06\x96\xca#\x05\x8d\xa1\x00\x00\u07d4\xf1\u070a\xc8\x10B\xc6z\x9c\\c2!\xa8\xf76>e\x87\f\x9f(t$\u04a9`\x89J\xcfX\xe0rW\x10\x00\x00\u07d4\xf2B\u0684]B\u053fw\x9a\x00\xf2\x95\xb4\aP\xfeI\xea\x13\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2RY\xa5\xc99\xcd%\x96l\x9bc\x03\xd3s\x1cS\u077cL\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf2^Lp\xbcFV2\u021eV%\xa82\xa7r/k\xff\xab\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4\xf2k\xce\xdc\xe3\xfe\xad\u03a3\xbc>\x96\xeb\x10@\xdf\xd8\xff\u1809*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xf2py%v\xf0]QD\x93\xff\xd1\xf5\xe8K\xecK-\xf8\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2s,\xf2\xc1;\x8b\xb8\xe7I*\x98\x8f_\x89\xe3\x82s\xdd\u0209 \x86\xac5\x10R`\x00\x00\xe0\x94\xf2t.hY\xc5i\xd5\xf2\x10\x83Q\xe0\xbfM\xca5*H\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf2\x81:d\xc5&]\x02\x025\u02dc1\x9bl\x96\xf9\x06\xc4\x1e\x89\x12\xf99\u025e\u06b8\x00\x00\u07d4\xf2\x87\xffR\xf4a\x11z\xdb>\x1d\xaaq\x93-\x14\x93\xc6_.\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\xf2\xab\x11au\x02D\xd0\xec\xd0H\xee\r>Q\xab\xb1C\xa2\xfd\x89B\xfe+\x90ss\xbc\x00\x00\u07d4\xf2\xb4\xab,\x94'\xa9\x01^\xf6\xee\xff\xf5\xed\xb6\x019\xb7\x19\u0449&\u06d9*;\x18\x00\x00\x00\u07d4\xf2\xc0>*8\x99\x8c!d\x87`\xf1\xe5\xae~\xa3\a}\x85\"\x89\x8f?q\x93\xab\a\x9c\x00\x00\u0794\xf2\u0090N\x9f\xa6d\xa1\x1e\xe2VV\xd8\xfd,\xc0\u0665\"\xa0\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xf2\xc3b\xb0\xef\x99\x1b\xc8/\xb3nf\xffu\x93*\xe8\u0742%\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\xf2\xd0\xe9\x86\xd8\x14\xea\x13\xc8\xf4f\xa0S\x8cS\u0712&Q\xf0\x89J\xcfX\xe0rW\x10\x00\x00\xe0\x94\xf2\u04775w$\xecL\x03\x18[\x87\x9bc\xf5~&X\x91S\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf2\xd5v<\xe0s\x12~,\xed\xdeo\xab\xa7\x86\xc7<\xa9AA\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\xe0\x94\xf2\u055c\x89#u\x90s\xd6\xf4\x15\xaa\xf8\xeb\x06_\xf2\U000f614a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\xf2\xe9\x9f\\\xbb\x83kz\xd3bGW\x1a0,\xbeKH\x1ci\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf2\xed>w%J\u02c3#\x1d\xc0\x86\x0e\x1a\x11$+\xa6'\u06c9kV\x05\x15\x82\xa9p\x00\x00\xe0\x94\xf2\xed\xde7\xf9\xa8\u00dd\u07a2My\xf4\x01WW\xd0k\xf7\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf2\xef\xe9e`\xc9\xd9{r\xbd6DxC\x88\\\x1d\x90\xc21\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf2\xfb\xb6\u0607\xf8\xb8\xcc:\x86\x9a\xba\x84\u007f=\x1fd\xfcc\x97\xaae\xfbS\xa8\xf0z\x0f\x89:\xae0\xe8\xbc\xee\x89|\xf28\x1fa\x9f\x15\x00\x00\u07d4\xf3@\x83\xec\xea8P\x17\xaa@\xbd\xd3^\xf7\xef\xfbL\xe7v-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf3F\xd7\u0792t\x1c\b\xfcX\xa6M\xb5[\x06-\xde\x01-\x14\x89\x0f\xffk\x1fv\x1em\x00\x00\xe0\x94\xf3U\xd3\xec\f\xfb\x90}\x8d\xbb\x1b\xf3FNE\x81(\x19\v\xac\x8a\x01\v\x04n\u007f\r\x80\x10\x00\x00\u07d4\xf3m\xf0/\xbd\x89`sG\xaf\xce)i\xb9\xc4#jX\xa5\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3s\xe9\u06ac\f\x86u\xf5;yz\x16\x0fo\xc04\xaek#\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3{BeG\xa1d-\x8032H\x14\xf0\xed\xe3\x11O\xc2\x12\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xf3{\xf7\x8cXu\x15G\x11\xcbd\r7\xeam(\xcf\xcb\x12Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf3\x82\xdfX1U\xd8T\x8f?\x93D\f\xd5\xf6\x8c\xb7\x9d`&\x8a8u}\x02\u007f\xc1\xfd\\\x00\x00\xe0\x94\xf3\x82\xe4\xc2\x04\x10\xb9Q\b\x9e\x19\xba\x96\xa2\xfe\xe3\xd9\x1c\xce~\x8a\x01\x11\xfaV\xee\u00a88\x00\x00\xe0\x94\xf3\x8al\xa8\x01hS~\x97M\x14\xe1\xc3\xd19\x90\xa4L,\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xf3\x9a\x9dz\xa3X\x1d\xf0~\xe4'\x9a\xe6\xc3\x12\xef!\x036X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xb6h\xb3\xf1M\x92\x0e\xbc7\x90\x92\u06d8\x03\x1bg\xb2\x19\xb3\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\U000fe679\x10<\xe7U\n\xa7O\xf1\xdb\x18\xe0\x9d\xfe2\xe0\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xc1\xab\u049d\xc5{A\xdc\x19-\x0e8M\x02\x1d\xf0\xb4\xf6\u0509\x97\xae\f\u07cf\x86\xf8\x00\x00\u07d4\xf3\xc4qm\x1e\xe5'\x9a\x86\xd0\x16:\x14a\x81\x81\xe1a6\u01c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf3\u030b\xcbU\x94e\xf8\x1b\xfeX;\u05eb\n#\x06E;\x9e\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf3\u0588\xf0k\xbd\xbfP\xf9\x93,AE\xcb\xe4\x8e\xcd\xf6\x89\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf3\xdb\xcf\x13Z\u02dd\xee\x1aH\x9cY<\x02O\x03\u00bb\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xde_&\xefj\xde\xd6\xf0m;\x91\x13F\xeep@\x1d\xa4\xa0\x89\x13:\xb3}\x9f\x9d\x03\x00\x00\u07d4\xf3\xdfc\xa9q\x99\x93308;>\xd7W\v\x96\u0101#4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xe7OG\f}:?\x003x\x0fv\xa8\x9f>\xf6\x91\xe6\u02c9\xa3\xcf\xe61\xd1Cd\x00\x00\u07d4\xf3\xeb\x19H\xb9Q\xe2-\xf1ax)\xbf;\x8d\x86\x80\xeckh\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xf1\xfa9\x18\xca4\xe2\xcf~\x84g\v\x1fM\x8e\xca\x16\r\xb3\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xf3\xf2O\u009e @?\xc0\xe8\xf5\xeb\xbbU4&\xf7\x82p\xa2\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3\xfar5R\xa5\xd0Q.+b\xf4\x8d\xca{+\x81\x050[\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xf3\xfeQ\xfd\xe3D\x13\xc73\x18\xb9\xc8T7\xfe~\x82\x0fV\x1a\x896b2\\\u044f\xe0\x00\x00\u07d4\xf4\x00\xf9=_\\~?\xc3\x03\x12\x9a\xc8\xfb\f/xd\a\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\v\x13O\xea\"\u01b2\x9c\x84W\xf4\x9f\x00\x0f\x9c\xdax\x9a\u06c9 \x86\xac5\x10R`\x00\x00\u07d4\xf4\x15W\xdf\u07f1\xa1\xbd\xce\xfe\xfe.\xba\x1e!\xfe\nJ\x99B\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\x17z\r\x85\u050b\x0e&B\x11\xce*\xa2\xef\xd3\xf1\xb4\u007f\b\x89\xc2\xcc\xca&\xb7\xe8\x0e\x80\x00\u07d4\xf4/\x90R1\xc7p\xf0\xa4\x06\xf2\xb7h\x87\u007f\xb4\x9e\xee\x0f!\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf42\xb9\u06ef\x11\xbd\xbds\xb6Q\x9f\xc0\xa9\x04\x19\x87q\xaa\u0189\b=lz\xabc`\x00\x00\u07d4\xf4=\xa3\xa4\xe3\xf5\xfa\xb1\x04\u029b\xc1\xa0\xf7\xf3\xbbJV\xf3Q\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf4G\x10\x8b\x98\xdfd\xb5~\x87\x103\x88\\\x1a\xd7\x1d\xb1\xa3\xf9\x8a\x01v\xf4\x9e\xad4\x83P\x80\x00\u07d4\xf4O\x85Q\xac\xe93r\a\x12\xc5\u0111\u0376\xf2\xf9Qsl\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\xf4V\x05Z\x11\xab\x91\xfff\x8e.\xc9\"\x96\x1f*#\xe3\xdb%\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf4V\xa7[\xb9\x96U\xa7A,\xe9}\xa0\x81\x81m\xfd\xb2\xb1\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf4[\x1d\xcb.A\xdc'\xff\xa0$\u06ad\xf6\x19\xc1\x11u\xc0\x87\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xf4c\xa9\f\xb3\xf1>\x1f\x06CB66\xbe\xab\x84\xc1#\xb0m\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf4h\x90n~\xdffJ\xb0\u063e=\x83\xebz\xb3\xf7\xff\xdcx\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xf4i\x80\u3929\u049ajn\x90`E7\xa3\x11K\xcb(\x97\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf4kk\x9c|\xb5R\x82\x9c\x1d=\xfd\x8f\xfb\x11\xaa\xba\xe7\x82\xf6\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xf4v\xe1&\u007f\x86$|\xc9\b\x81o.z\xd58\x8c\x95-\xb0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf4v\xf2\xcbr\b\xa3.\x05\x1f\xd9N\xa8f)\x92c\x82\x87\xa2\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xf4{\xb14\xda0\xa8\x12\xd0\x03\xaf\x8d\u0338\x88\xf4K\xbfW$\x8a\x01\x19Y\xb7\xfe3\x95X\x00\x00\u07d4\xf4\x83\xf6\a\xa2\x1f\xcc(\x10\n\x01\x8cV\x8f\xfb\xe1@8\x04\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf4\x8e\x1f\x13\xf6\xafM\x84\xb3q\xd7\xdeK'=\x03\xa2c'\x8e\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xf4\x9cG\xb3\xef\xd8knj[\xc9A\x8d\x1f\x9f\xec\x81Ki\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xf4\x9fo\x9b\xaa\xbc\x01\x8c\x8f\x8e\x11\x9e\x01\x15\xf4\x91\xfc\x92\xa8\xa4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf4\xa3g\xb1f\u0499\x1a+\xfd\xa9\xf5dc\xa0\x9f%,\x1b\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\xa5\x1f\xceJ\x1d[\x94\xb0q\x83\x89\xbaNx\x14\x13\x9c\xa78\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf4\xa9\xd0\f\xef\xa9{zX\xef\x94\x17\xfcbg\xa5\x06\x909\xee\x89\x01.\x89(\u007f\xa7\x84\x00\x00\u07d4\xf4\xaa\xa3\xa6\x16>7\x06W{I\xc0v~\x94\x8ah\x1e\x16\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\xb1bn$\xf3\v\xca\xd9'!\xb2\x93r\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xf5U\xa2{\xb1\xe2\xfdN,\u01c4\xca\ue493\x9f\xc0n/\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5X\xa2\xb2\xdd&\u0755\x93\xaa\xe0E1\xfd<<\u00c5Kg\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xf5`H\xdd!\x81\u0523od\xfc\xec\xc6!T\x81\xe4*\xbc\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5dB\xf6\x0e!i\x13\x95\u043f\xfa\xa9\x19M\xca\xff\x12\u2dc9\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xf5yqJE\xeb\x8fR\xc3\xd5{\xbd\xef\xd2\xc1[./\x11\u07c9T\x91YV\xc4\t`\x00\x00\u07d4\xf5\x93\xc6R\x85\xeek\xbdf7\U000fe3c9\xad@\u0509\xf6U\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf5\x98\xdb.\t\xa8\xa5\xee}r\r+\\C\xbb\x12m\x11\xec\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf5\x9d\xab\x1b\xf8\xdf\x112~a\xf9\xb7\xa1KV:\x96\xec5T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf5\x9f\x9f\x02\xbb\u024e\xfe\t~\xab\xb7\x82\x10\x97\x90!\x89\x8b\xfd\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\xf5\xa5E\x9f\xcd\xd5\xe5\xb2s\x83\r\xf8\x8e\xeaL\xb7}\xda\u07f9\x89\x04\t\xe5+H6\x9a\x00\x00\u07d4\xf5\xa7gj\xd1H\xae\x9c\x1e\xf8\xb6\xf5\xe5\xa0\xc2\xc4s\xbe\x85\v\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5\xb0h\x98\x9d\xf2\x9c%5w\xd0@Z\xden\x0eu(\xf8\x9e\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xf5\xb6\xe9\x06\x1aN\xb0\x96\x16\aw\xe2gb\xcfH\xbd\u0635]\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xf5\xcf\xfb\xbabN~\xb3!\xbc\x83\xc6\f\xa6\x81\x99\xb4\xe3fq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5\xd1ER\xb1\xdc\xe0\xd6\xdc\x1f2\r\xa6\xff\u02231\xcdo\f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xf5\xd6\x1a\xc4\u0295G^[{\xff\xd5\xf2\xf6\x90\xb3\x16u\x96\x15\x8a\x06\x92\xae\x88\x97\b\x1d\x00\x00\x00\u07d4\xf5\xd9\xcf\x00\xd6X\xddEQzH\xa9\xd3\xf5\xf63T\x1aS=\x89\x06O_\xdfIOx\x00\x00\u07d4\xf5\xea\xdc\xd2\u0478ez\x12\x1f3\xc4X\xa8\xb1>v\xb6U&\x89\r\x8b\x0fZZ\xc2J\x00\x00\u07d4\xf6\a\xc2\x15\r>\x1b\x99\xf2O\xa1\xc7\xd5@\xad\xd3\\N\xbe\x1e\x89\xa7\xf1\xaa\a\xfc\x8f\xaa\x00\x00\u07d4\xf6\v\xd75T>k\xfd.\xa6\xf1\x1b\xffbs@\xbc\x03Z#\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6\f\x1bE\xf1d\xb9X\x0e 'Z\\9\xe1\xd7\x1e5\xf8\x91\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf6\x0fb\xd797\x95?\xef5\x16\x9e\x11\xd8r\xd2\xea1~\xec\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xf6\x12\x83\xb4\xbd\x85\x04\x05\x8c\xa3`\u94d9\x9bb\xcb\xc8\xcdg\x89\r\xd2\xd5\xfc\xf3\xbc\x9c\x00\x00\u07d4\xf6\x17\xb9g\xb9\xbdH_v\x95\xd2\xefQ\xfbw\x92\u0618\xf5\x00\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf6\x18\u0671\x04A\x14\x80\xa8c\xe6#\xfcU#-\x1aOH\xaa\x89\x0eh\x9emD\xb1f\x80\x00\u07d4\xf6\"\u5126b>\xaa\xf9\x9f+\xe4\x9eS\x80\xc5\xcb\xcf\\\u0609\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf62\xad\xffI\r\xa4\xb7-\x126\xd0KQ\x0ft\xd2\xfa\xa3\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4\xf69\xac1\u069fg'\x1b\xd1\x04\x02\xb7eN\\\xe7c\xbdG\x89\x15\xaf\x0fB\xba\xf9&\x00\x00\u07d4\xf6:W\x9b\xc3\xea\u00a9I\x04\x10\x12\x8d\xbc\xeb\xe6\xd9\u0782C\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xf6E\xdd|\x89\x00\x93\xe8\xe4\u022a\x92\xa6\xbb55\"\xd3\u0718\x89\aC\x9f\xa2\t\x9eX\x00\x00\xe0\x94\xf6H\xea\x89\xc2u%q\x01r\x94Ny\xed\xff\x84x\x03\xb7u\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf6JJ\xc8\xd5@\xa9(\x9ch\xd9`\xd5\xfb|\xc4Zw\x83\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6N\xcf!\x17\x93\x1cmSZ1\x1eO\xfe\xae\xf9\u0514\x05\xb8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf6O\xe0\x93\x9a\x8d\x1e\xea*\x0e\u035a\x970\xfdyX\xe31\t\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xf6V\x16\xbe\x9c\x8by~t\x15\"|\x918\xfa\xa0\x89\x17B\u05c9*\xd3s\xcef\x8e\x98\x00\x00\u07d4\xf6W\xfc\xbeh.\xb4\xe8\xdb\x15.\u03c9$V\x00\vQ=\x15\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf6X\x19\xacL\xc1L\x13\u007f\x05\xddyw\xc7\xda\xe0\x8d\x1aJ\xb5\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xf6{\xb8\xe2\x11\x8b\xbc\u0550'fn\xed\xf6\x94>\xc9\xf8\x80\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x84d\xbfd\xf2A\x13V\xe4\xd3%\x0e\xfe\xfe\\P\xa5\xf6[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf6\x86x[\x89r\va\x14_\ua017\x8dj\u030e\v\xc1\x96\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x8c^3\xfa\x97\x13\x9d\xf5\xb2\xe68\x86\xce4\xeb\xf3\u45dc\x89\xb3\xfaAi\xe2\xd8\xe0\x00\x00\u07d4\xf6\xa8cWW\xc5\xe8\xc14\xd2\r\x02\x8c\xf7x\u03c6\t\xe4j\x89O\x1dw/\xae\xc1|\x00\x00\u07d4\xf6\xb7\x82\xf4\xdc\xd7E\xa6\xc0\xe2\xe00`\x0e\x04\xa2K%\xe5B\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xf6\xbc7\xb1\u04a3x\x8dX\x9bm\xe2\x12\xdc\x17\x13\xb2\xf6\u738a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xf6\xc3\u010a\x1a\xc0\xa3G\x99\xf0M\xb8n\u01e9u\xfewh\xf3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf6\xd2]?=\x84m#\x9fR_\xa8\xca\xc9{\xc45x\u06ec\x890\x92\u007ft\xc9\xde\x00\x00\x00\u07d4\xf6\xea\xacp2\u0512\xef\x17\xfd`\x95\xaf\xc1\x1dcOV\xb3\x82\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xf6\xea\xd6}\xbf[~\xb13X\xe1\x0f6\x18\x9dS\xe6C\xcf\u03ca\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf6\xf1\xa4C\t\x05\x1ck%\xe4}\xff\x90\x9b\x17\x9b\xb9\xabY\x1c\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf7\x03(\xef\x97b_\xe7E\xfa\xa4\x9e\xe0\xf9\u052a;\r\xfbi\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\n\x99\x8aq{3\x8d\x1d\u0658T@\x9b\x1a3\x8d\ue930\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\rcz\x84\\\x06\xdbl\u0711\xe67\x1c\xe7\xc48\x8ab\x8e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x15R\x13D\x98\x92tK\xc6\x0f.\x04@\a\x88\xbd\x04\x1f\u0749\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\xe0\x94\xf7\x1bE4\xf2\x86\xe40\x93\xb1\xe1^\xfe\xa7I\xe7Y{\x8bW\x8a\x16\x1c\x13\xd34\x1c\x87(\x00\x00\u07d4\xf74\xec\x03rM\xde\xe5\xbbRy\xaa\x1a\xfc\xf6\x1b\f\xb4H\xa1\x89\xe5\xbf,\u0270\x97\x80\x00\x00\u07d4\xf76\u0716v\x00\x128\x8f\xe8\x8bf\xc0n\xfeW\xe0\xd7\xcf\n\x89q\xd7Z\xb9\xb9 P\x00\x00\u07d4\xf7:\xc4l ;\xe1S\x81\x11\xb1Q\xec\x82 \u01c6\xd8AD\x89\x0f\xf77x\x17\xb8+\x80\x00\u07d4\xf7=\xd9\xc1B\xb7\x1b\xce\x11\xd0n0\xe7\xe7\xd02\xf2\uc71e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf7A\x8a\xa0\xe7\x13\xd2H\"\x87v\xb2\xe7CB\"\xaeu\u3949lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7Nn\x14S\x82\xb4\u06c2\x1f\xe0\xf2\u0643\x88\xf4V\t\u019f\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf7P\f\x16o\x8b\xea/\x824v\x06\xe5\x02K\xe9\xe4\xf4\u0399\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7W\xfc\x87 \xd3\xc4\xfaRw\a^`\xbd\\A\x1a\xeb\xd9w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7[\xb3\x9cy\x97y\xeb\xc0J3m&\r\xa61F\xed\x98\u0409\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xf7h\xf3!\xfdd3\xd9kO5M<\xc1e,\x172\xf5\u007f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf7oi\xce\xe4\xfa\xa0\xa6;0\xae\x1ex\x81\xf4\xf7\x15ep\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf7w6\x1a=\u062bb\xe5\xf1\xb9\xb0GV\x8c\xc0\xb5UpL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7|{\x84QI\xef\xba\x19\xe2a\xbc|u\x15y\b\xaf\xa9\x90\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\u007f\x95\x87\xffz-r\x95\xf1\xf5q\u0206\xbd3\x92jR|\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xf7\x82X\xc1$\x81\xbc\xdd\u06f7*\x8c\xa0\xc0C\tra\xc6\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x98\xd1m\xa4\xe4`\xc4`\xcdH_\xae\x0f\xa0Y\x97\b\ub08965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\xa1\xad\xe2\xd0\xf5)\x12=\x10U\xf1\x9b\x17\x91\x9fV!Ng\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf7\xac\xff\x93K\x84\xda\ti\xdc7\xa8\xfc\xf6C\xb7\xd7\xfb\xedA\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf7\xb1Q\xcc^W\x1c\x17\xc7e9\xdb\xe9\x96L\xbbo\xe5\xdey\x89tq|\xfbh\x83\x10\x00\x00\u07d4\xf7\xb2\x9b\x82\x19\\\x88-\xabx\x97\u00ae\x95\xe7w\x10\xf5xu\x89w5Aa2\xdb\xfc\x00\x00\u07d4\xf7\xbcLD\x91\rZ\xed\xd6n\xd25U8\xa6\xb1\x93\xc3a\xec\x89\x05A\xde,-\x8db\x00\x00\u07d4\xf7\xc0\f\xdb\x1f\x02\x03\x10\u056c\xab{Ij\xaaD\xb7y\b^\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xf7\xc1\xb4C\x96\x8b\x11{]\u0677UW/\xcd9\xca^\xc0K\x89\x18\xb9h\u0092\xf1\xb5\x00\x00\xe0\x94\xf7\xc5\x0f\x92*\xd1ka\xc6\u047a\xa0E\xed\x81h\x15\xba\u010f\x8a\x02\xa99j\x97\x84\xad}\x00\x00\u07d4\xf7\xc7\b\x01Pq\xd4\xfb\n:*\t\xa4]\x15c\x96\xe34\x9e\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf7\xcb\u06e6\xbel\xfeh\xdb\xc2<+\x0f\xf50\xee\x05\"o\x84\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\xd0\xd3\x10\xac\xea\x18@a8\xba\xaa\xbb\xfe\x05q\xe8\r\xe8_\x89Hz\x9a0E9D\x00\x00\u07d4\xf7\u05ef LV\xf3\x1f\xd9C\x98\xe4\r\xf1\x96K\u063f\x12<\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xf7\xdc%\x11\x96\xfb\u02f7|\x94}|\x19F\xb0\xffe\x02\x1c\xea\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf7\xe4Z\x12\xaaq\x1cp\x9a\xce\xfe\x95\xf3;xa-*\xd2*\x8a\x0e\x06U\xe2\xf2k\xc9\x18\x00\x00\u07d4\xf7\xf4\x89\x8cLRm\x95_!\xf0U\xcbnG\xb9\x15\xe5\x19d\x89|\b`\xe5\xa8\r\xc0\x00\x00\u07d4\xf7\xf9\x1ez\xcb[\x81)\xa3\x06\x87|\xe3\x16\x8eoC\x8bf\xa1\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xf7\xfcE\xab\xf7oP\x88\xe2\u5d68\xd12\xf2\x8aMN\xc1\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\x06:\xf4\xcc\x1d\xd9a\x9a\xb5\u063f\xf3\xfc\xd1\xfa\xa8H\x82!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\bnBf\x1e\xa9)\xd2\u0761\xablt\x8c\xe3\x05]\x11\x1e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf8\bw\x86\xb4-\xa0N\xd6\xd1\xe0\xfe&\xf6\xc0\xee\xfe\x1e\x9fZ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf8\r6\x19p/\xa5\x83\x8cH9\x18Y\xa89\xfb\x9c\xe7\x16\x0f\x89l\a\xa7\u0471np\x00\x00\u07d4\xf8\x14y\x9fm\xdfM\xcb)\xc7\xee\x87\x0eu\xf9\xcc-52m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf8\x15\xc1\n\x03-\x13\xc3K\x89v\xfan;\xd2\xc9\x13\x1a\x8b\xa9\x89Hz\x9a0E9D\x00\x00\u07d4\xf8\x16\"\xe5WW\xda\xeafu\x97]\xd958\xda}\x16\x99\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8$\xee3\x1eJ\xc3\xccXv\x939[W\xec\xf6%\xa6\xc0\u0089V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4\xf8'\xd5n\xd2\xd3' \u052b\xf1\x03\xd6\xd0\xefM;\xcdU\x9b\x89\x01l\x80\x06W\x91\xa2\x80\x00\u07d4\xf8)\x85\x91R>P\xb1\x03\xf0\xb7\x01\xd6#\xcb\xf0\xf7EV\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8H\xfc\xe9\xaba\x1c}\x99 n#\xfa\u019a\u0508\xb9O\xe1\x89\x02\xa1\x12\x9d\t6r\x00\x00\u07d4\xf8O\t\n\xdf?\x8d\xb7\u1533P\xfb\xb7u\x00i\x9ff\xfd\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf8Q\xb0\x10\xf63\xc4\n\xf1\xa8\xf0js\ubeabe\az\xb5\x89\xee\x86D/\xcd\x06\xc0\x00\x00\u07d4\xf8X\x17\x1a\x04\xd3W\xa1;IA\xc1n~U\xdd\u0514\x13)\x89\x02F\xa5!\x8f*\x00\x00\x00\u07d4\xf8[\xab\x1c\xb3q\x0f\xc0_\xa1\x9f\xfa\xc2.gR\x1a\v\xa2\x1d\x89l\x955\u007f\xa6\xb3l\x00\x00\u07d4\xf8j>\xa8\a\x1fp\x95\xc7\u06ca\x05\xaePz\x89)\u06f8v\x89\x126\xef\xcb\u02f3@\x00\x00\u07d4\xf8pL\x16\xd2\xfd[\xa3\xa2\xc0\x1d\x0e\xb2\x04\x84\xe6\xec\xfa1\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8p\x99_\xe1\xe5\"2\x1duC7\xa4\\\f\x9d{8\x95\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf8s\xe5ze\xc9;n\x18\xcbu\xf0\xdc\a}[\x893\xdc\\\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf8ua\x9d\x8a#\xe4]\x89\x98\u0444\u0500\xc0t\x89p\x82*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8{\xb0{(\x9d\xf70\x1eT\xc0\xef\xdaj,\xf2\x91\xe8\x92\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u0794\xf8\x89\x00\xdbsyU\xb1Q\x9b\x1a}\x17\n\x18\x86L\xe5\x90\xeb\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf8\x8bX\xdb7B\vFL\v\xe8\x8bE\xee+\x95)\x0f\x8c\xfa\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf8\x96+u\xdb]$\xc7\xe8\xb7\xce\xf1\x06\x8c>g\u03bb0\xa5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xf8\xa0e\xf2\x87\xd9\x1dw\xcdbj\xf3\x8f\xfa\"\r\x9bU*+\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xf8\xa4\x9c\xa29\f\x1fm\\\x0ebQ;\a\x95qt?|\u0189\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf8\xa5\f\xee.h\x8c\xee\u3b24\u0522\x97%\xd4\a,\u0103\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xacJ9\xb5<\x110x \x97;D\x13e\xcf\xfeYof\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xae\x85{g\xa4\xa2\x89:?\xbe|z\x87\xff\x1c\x01\u01a6\xe7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8\xbf\x9c\x04\x87NZw\xf3\x8fL8R~\x80\xc6v\xf7\xb8\x87\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xc7\xf3J8\xb3\x18\x01\xdaC\x064w\xb1+'\xd0\xf2\x03\xff\x89\x1a\u04ba\xbao\xefH\x00\x00\u07d4\xf8\xca3l\x8e\x91\xbd \xe3\x14\xc2\v-\xd4`\x8b\x9c\x8b\x94Y\x89-\u071b\u0173,x\x00\x00\u07d4\xf8\xd1t$\xc7g\xbe\xa3\x12\x05s\x9a+W\xa7'r\x14\uef89\x02F\xdd\xf9yvh\x00\x00\u07d4\xf8\xd5-\xcc_\x96\xcc(\x00{>\u02f4\t\xf7\xe2*dl\xaa\x89\b\x16\x90\xe1\x81(H\x00\x00\u07d4\xf8\xdc\xe8g\xf0\xa3\x9c[\xef\x9e\xeb\xa6\t\"\x9e\xfa\x02g\x8bl\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xf2&\x14*B\x844\xab\x17\xa1\x86J%\x97\xf6J\xab/\x06\x89\tY\x8b/\xb2\xe9\xf2\x80\x00\u07d4\xf8\xf6d^\r\xeedK=\xad\x81\xd5q\uf6ef\x84\x00!\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x01\xc0\x0f\xc1\u06c8\xb6\x9cK\xc3%+\\\xa7\x0e\xa6\xee\\\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9=[\xcb\x06D\xb0\xcc\xe5\xfc\u0763C\xf5\x16\x8f\xfa\xb2\x87}\x89\vb\a\xb6}&\xf9\x00\x00\u07d4\xf9W\x0e\x92L\x95\u07bbpa6\x97\x92\xcf.\xfe\u00a8-^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf9d \x86\xb1\xfb\xaea\xa6\x80M\xbe_\xb1^\xc2\u04b57\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9d\x88i\x85\x90\xdc;,UVB\xb8q4\x8d\xfa\x06z\u0549\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9d\u064d(\x170\xba5\xb2\xe3\xa3\x14yn{B\xfe\xdfg\x89S\xb0\x87`\x98\xd8\f\x00\x00\u07d4\xf9e\ri\x89\xf1\x99\xab\x1c\xc4ycm\xed0\xf2A\x02\x1fe\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xf9h\x83X$Y\x90\x8c\x82v'\xe8o(\xe6F\xf9\xc7\xfcz\x8a\x01\u0127\x877\xcd\u03f8\x00\x00\u07d4\xf9kL\x00voSsj\x85t\xf8\"\xe6GL/!\xda-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9r\x9dH(,\x9e\x87\x16m^\xef-\x01\xed\xa9\xdb\xf7\x88!\x89\x05k\x83\xdd\xc7(T\x80\x00\u07d4\xf9v~N\xcbJY\x80Ru\b\u05fe\xc3\xd4^Ld\x9c\x13\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xf9x\xb0%\xb6B3U\\\xc3\xc1\x9a\xda\u007fA\x99\xc94\x8b\xf7\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4\xf9{V\xeb\u0577z\xbc\x9f\xba\u02eb\u0514\xb9\xd2\xc2!\xcd\x03\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf9\x81\x1f\xa1\x9d\xad\xbf\x02\x9f\x8b\xfeV\x9a\xdb\x18\"\x8c\x80H\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf9\x82Ps\fLa\xc5\u007f\x12\x985\xf2h\b\x94yEB\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xf9\x894gr\x99^\xc1\x90o\xaf\xfe\xba*\u007f\xe7\u079ck\xab\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf9\x98\xca4\x11s\nl\xd1\x0etU\xb0A\x0f\xb0\xf6\xd3\xff\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x9a\xeeDKW\x83\xc0\x93\xcf\xff\xd1\xc4c,\xf9\x90\x9f\xbb\x91\x1d/\x81\x92\xf8B\t\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf9\xbf\xb5\x9dS\x8a\xfcHt\xd4\xf5\x94\x1b\b\xc9s\x0e8\xe2K\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf9\xdd#\x90\b\x18/\xb5\x19\xfb0\xee\xdd \x93\xfe\xd1c\x9b\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9\u07ba\xec\xb5\xf39\xbe\xeaH\x94\xe5 K\xfa4\r\x06\u007f%\x89ZB\x84Fs\xb1d\x00\x00\xe0\x94\xf9\xe3tG@lA!\x97\xb2\u2bbc\x00\x1dn0\u024c`\x8a\x01\xc4y\xbbCI\xc0\xee\x00\x00\u07d4\xf9\xe7\"/\xaa\xf0\xf4\xda@\xc1\u0124\x0607:\t\xbe\u05f6\x89\x9bO\u0730\x94V$\x00\x00\u07d4\xf9\xec\xe0\"\xbc\xcd,\x924i\x11\xe7\x9d\xd5\x03\x03\xc0\x1e\x01\x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfa\x00\xc3v\xe8\x9c\x05\u81c1z\x9d\xd0t\x8d\x96\xf3A\xaa\x89\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xfa\f\x1a\x98\x8c\x8a\x17\xad5(\xeb(\xb3@\x9d\xaaX\"_&\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa\x10_\x1a\x11\xb6\xe4\xb1\xf5`\x12\xa2y\"\xe2\xac-\xa4\x81/\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfa\x14/\xe4~\u0697\xe6P;8k\x18\xa2\xbe\xdds\u0335\xb1\x89.\x15:\xd8\x15H\x10\x00\x00\u07d4\xfa\x14\xb5f#J\xbe\xe70B\xc3\x1d!qq\x82\u02e1J\xa1\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\xfa\x19\xd6\xf7\xa5\x0fO\a\x98\x93\xd1g\xbf\x14\xe2\x1d\x00s\u0456\x89\x1c\xbb:?\xf0\x8d\b\x00\x00\u07d4\xfa\x1f\x19q\xa7u\xc3PO\xefPy\xf6@\xc2\u013c\xe7\xac\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfa'\x9b\xfd\x87g\xf9V\xbf\u007f\xa0\xbdV`\x16\x8d\xa7V\x86\xbd\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xfa'\xccI\xd0\vl\x98s6\xa8u\xae9\xdaX\xfb\x04\x1b.\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfa(2\x99`=\x87X\xe8\u02b0\x82\x12],\x8f}DT)\x8a\x01[\xca\xcb\x1e\x05\x01\xae\x80\x00\u07d4\xfa+\xbc\xa1]?\u37ca2\x8e\x91\xf9\r\xa1Oz\xc6%=\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa/\u049d\x03\xfe\xe9\xa0x\x93\xdf:&\x9fV\xb7/.\x1ed\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfa3U2\x85\xa9sq\x9a\r_\x95o\xf8a\xb2\u061e\xd3\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\fK\x90?n\xa5.\xa7\xab{\x88c\xb6\xa6\x16\xadfP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\x1a\xa4H\x8b5\x1a\xa7V\f\xf5\xeec\n/\xd4\\2\"\x89/\xa4~j\xa74\r\x00\x00\u07d4\xfaA\tq\xad\"\x9c06\xf4\x1a\u03c5/*\u0259(\x19P\x89\u0633\x11\xa8\xdd\xfa|\x00\x00\u07d4\xfaD\xa8U\xe4\x04\xc8m\f\xa8\xef3$%\x1d\xfb4\x9cS\x9e\x89T\"S\xa1&\xce@\x00\x00\xe0\x94\xfaR\x01\xfe\x13B\xaf\x110{\x91B\xa0A$<\xa9./\t\x8a 8\x11j:\xc0C\x98\x00\x00\xe0\x94\xfa`\x86\x8a\xaf\xd4\xffL\\W\x91K\x8e\u054bBWs\u07e9\x8a\x01\xcf\xe5\xc8\b\xf3\x9f\xbc\x00\x00\u07d4\xfag\xb6{O7\xa0\x15\t\x15\x11\x0e\xde\a;\x05\xb8S\xbd\xa2\x89#\x19\xba\x94sq\xad\x00\x00\u07d4\xfah\xe0\xcb>\xdfQ\xf0\xa6\xf2\x11\u0272\xcb^\a<\x9b\xff\xe6\x89\x0f\xc969(\x01\xc0\x00\x00\xe0\x94\xfaj7\xf0\x18\xe9yg\x93\u007f\xc5\xe8a{\xa1\u05c6\xdd_w\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xfav\x06C[5l\xee%{\xd2\xfc\xd3\xd9\xea\xcb<\xd1\xc4\xe1\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfaz\xdff\v\x8d\x99\xce\x15\x93=|_\a/<\xbe\xb9\x9d3\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfa\x86\xca'\xbf(T\u0648p\x83\u007f\xb6\xf6\xdf\xe4\xbfdS\xfc\x89\x11u~\x85%\xcf\x14\x80\x00\u07d4\xfa\x8c\xf4\xe6'i\x8c]W\x88\xab\xb7\x88\x04\x17\xe7P#\x13\x99\x89\xe6\x1a6\x96\xee\xf6\x10\x00\x00\u07d4\xfa\x8e;\x1f\x13C9\x00s}\xaa\xf1\xf6)\x9cH\x87\xf8[_\x89&\u009eG\u0104L\x00\x00\u07d4\xfa\x9e\xc8\xef\xe0\x86\x86\xfaX\xc1\x813Xr\xbai\x85`\ucac9lj\xccg\u05f1\xd4\x00\x00\u07d4\xfa\xad\x90]\x84|{#A\x8a\xee\xcb\xe3\xad\u06cd\xd3\xf8\x92J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xfa\xae\xba\x8f\xc0\xbb\xdaU<\xa7.0\xef=s.&\xe8 A\x89H\x8d(*\xaf\xc9\xf6\x80\x00\u07d4\xfa\xb4\x87P\r\xf2\x0f\xb8>\xbe\xd9\x16y\x1dV\x17r\xad\xbe\xbf\x89lkLM\xa6\u077e\x00\x00\u07d4\xfa\xc5\u0294u\x80x\xfb\xfc\xcd\x19\xdb5X\xda~\u8827h\x897(\xa6+\r\xcf\xf6\x00\x00\u07d4\xfa\xd9j\xb6\xacv\x8a\xd5\t\x94R\xacGw\xbd\x1aG\xed\u010f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfa\xe7g\x19\xd9~\xacA\x87\x04(\xe9@'\x9d\x97\xddW\xb2\xf6\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xfa\u8053pG\x89Zf\f\xf2)v\x0f'\xe6h(\xd6C\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xfa\xe9,\x13p\xe9\u115a]\xf8;V\xd0\xf5\x86\xaa;@L\x89\x05\u0174\xf3\xd8C\x98\x00\x00\xe0\x94\xfa\xf5\xf0\xb7\xb6\xd5X\xf5\t\r\x9e\xa1\xfb-B%\x9cX`x\x8a\x01Z\xff\xb8B\fkd\x00\x00\xe0\x94\xfb\x12o\x0e\xc7i\xf4\x9d\xce\xfc\xa2\xf2\x00(dQX0\x84\xb8\x8a\x01\x0f\xcb\xc25\x03\x96\xbf\x00\x00\xe0\x94\xfb\x13^\xb1Z\x8b\xacr\xb6\x99\x154*`\xbb\xc0k~\a|\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\"<\x1e\"\xea\xc1&\x9b2\xee\x15jS\x85\x92.\xd3o\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb7\xcfkO\x81\xa9\xe2\"\xfb\xa2.\x9b\xd2KP\x98\xb73\u03c9\x02\x1auJm\xc5(\x00\x00\u07d4\xfb8`\xf4\x12\x1cC.\xbd\xc8\xecj\x031\xb1\xb7\ty.\x90\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\xfb9\x18\x9a\xf8v\xe7b\xc7\x1dl>t\x18\x93\xdf\"l\xed\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfb:\v\rkjq\x8fo\xc0)*\x82]\xc9$z\x90\xa5\u0409\n\xd6\xdd\x19\x9e\x97[\x00\x00\xe0\x94\xfb?\xa1\xac\b\xab\xa9\xcc;\xf0\xfe\x9dH8 h\x8fe\xb4\x10\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xfb?\xe0\x9b\xb86\x86\x15)\xd7Q\x8d\xa2v5\xf58PV\x15\x89K\xe3\x92\x16\xfd\xa0p\x00\x00\xe0\x94\xfbQ%\xbf\x0f^\xb0\xb6\xf0 \xe5k\xfc/\xdf=@,\t~\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfbU\x18qL\xef\xc3m\x04\x86]\xe5\x91^\xf0\xffG\xdf\xe7C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb_\xfa\xa0\xf7aW&5x\x91GX\x18\x93\x9d 7\u03d6\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfbh\\\x15\xe49\x96^\xf6&\xbf\r\x83L\u0468\x9f+V\x95\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xfbtK\x95\x1d\tK1\x02b\xc8\xf9\x86\xc8`\u07da\xb1\xdee\x89\x02\xd1\xc5\x15\xf1\xcbJ\x80\x00\u07d4\xfby\xab\u06d2\\U\xb9\xf9\x8e\xfe\xefd\xcf\xc9\xeba\xf5\x1b\xb1\x89a@\xc0V\xfb\n\xc8\x00\x00\u07d4\xfb\x81\x13\xf9M\x91s\xee\xfdZ0s\xf5\x16\x80:\x10\xb2\x86\xae\x89\x04V9\x18$O@\x00\x00\u07d4\xfb\x84,\xa2\xc5\xef\x139\x17\xa26\xa0\u052c@i\x01\x10\xb08\x89\x10\x96\x9ab\xbe\x15\x88\x00\x00\u07d4\xfb\x91\xfb\x1aiUS\xf0\u018e!'m\xec\xf0\xb89\t\xb8m\x89\x05l\x006\x17\xafx\x00\x00\u07d4\xfb\x94s\xcfw\x125\n\x1f\xa09Rs\xfc\x80V\aR\xe4\xfb\x89\x06\xaf!\x98\xba\x85\xaa\x00\x00\xe0\x94\xfb\x94\x9cd\u007f\xdc\xfd%\x14\xc7\u054e1\xf2\x8aS-\x8cX3\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\xa5HmS\xc6\xe2@IBA\xab\xf8~C\xc7`\rA:\x89k\xbfaIIH4\x00\x00\u07d4\xfb\xb1a\xfe\x87_\t)\nK&+\xc6\x01\x10\x84\x8f\r\"&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb\xbb\xeb\u03fe#^W\xdd#\x06\xad\x1a\x9e\u0141\xc7\xf9\xf4\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfb\xc0\x1d\xb5NG\xcd\xc3\xc48iJ\xb7\x17\xa8V\xc2?\xe6\xe9\x8a\x01\xcaqP\xab\x17OG\x00\x00\xe0\x94\xfb\xcf\xccJ{\x0f&\xcf&\xe9\xf33!2\xe2\xfcj#\af\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfb\xe7\x16\"\xbc\xbd1\xc1\xa3iv\xe7\xe5\xf6p\xc0\u007f\xfe\x16\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfb\xed\xe3,4\x9f3\x00\xefL\xd3;M\xe7\xdc\x18\xe4C\xd3&\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xfb\xf2\x04\xc8\x13\xf86\xd89b\u01c7\fx\b\xca4\u007f\xd3>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfb\xf7Y3\xe0\x1bu\xb1T\xef\x06i\ak\xe8\u007fb\xdf\xfa\xe1\x8a\x10\x84cr\xf2I\xd4\xc0\x00\x00\u07d4\xfc\x00\x96\xb2\x1e\x95\xac\xb8\xd6\x19\xd1v\xa4\xa1\xd8\xd5)\xba\xdb\xef\x89\x14\xd9i;\xcb\xec\x02\x80\x00\xe0\x94\xfc\x00\xa4 \xa3a\a\xdf\xd5\xf4\x95\x12\x8a_\u5af2\xdb\x0f4\x8a\x01C\x17\x9d\x86\x91\x10 \x00\x00\xe0\x94\xfc\x01\x8ai\n\xd6tm\xbe:\u03d7\x12\xdd\xcaR\xb6%\x009\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfc\x02s@3\xe5\u007fpQ~\n\xfc~\xe6$a\xf0o\xad\x8e\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfc\x0e\xe6\xf7\u00b3qJ\xe9\x91lEVf\x05\xb6V\xf3$A\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xfc\x10\xb7\xa6{2h\xd53\x1b\xfbj\x14\xde\xf5\xeaJ\x16,\xa3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfc\x15\u02d9\xa8\xd1\x03\v\x12w\n\xdd\x03:y\xee\r\f\x90\x8c\x89\x12\xfa\x00\xbdR\xe6$\x00\x00\u07d4\xfc)R\xb4\u011f\xed\u043c\x05(\xa3\bI^mj\x1cq\u0589lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc,\x1f\x88\x96\x1d\x01\x9c>\x9e\xa30\t\x15.\x06\x93\xfb\xf8\x8a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xfc6\x11\x05\u0750\xf9\xed\xe5fI\x9di\xe9\x13\x03\x95\xf1*\u020aS\xa4\xfe/ N\x80\xe0\x00\x00\u07d4\xfc7/\xf6\x92|\xb3\x96\xd9\xcf)\x805\x00\x11\r\xa62\xbcR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc9\xbeA\tK\x19\x97\xd2\x16\x9e\x82d\xc2\u00fa\xa6\u025b\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc=\"k\xb3jX\xf5&V\x88W\xb0\xbb\x12\xd1\t\xec\x93\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfcC\x82\x9a\u01c7\xff\x88\xaa\xf1\x83\xba5*\xad\xbfZ\x15\xb1\x93\x89\u05ac\n+\x05R\xe0\x00\x00\u07d4\xfcI\xc1C\x9aA\u05b3\xcf&\xbbg\xe06R$\xe5\xe3\x8f_\x8966\u05ef^\u024e\x00\x00\u07d4\xfcU\x00\x82Q\x05\xcfq*1\x8a^\x9c;\xfci\u021d\f\x12\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xfcf\xfa\xba'\u007fK]\xe6J\xd4^\xb1\x9c1\xe0\f\xed>\u054a\x011\xbe\xb9%\xff\xd3 \x00\x00\xe0\x94\xfc~\"\xa5\x03\xecZ\xbe\x9b\b\xc5\v\xd1I\x99\xf5 \xfaH\x84\x8a\x01ZG}\xfb\xe1\xea\x14\x80\x00\u07d4\xfc\x82\x15\xa0\xa6\x99\x13\xf6*C\xbf\x1c\x85\x90\xb9\xdd\xcd\r\x8d\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc\x98\x9c\xb4\x87\xbf\x1a}\x17\xe4\xc1\xb7\u0137\xaa\xfd\xdak\n\x8d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfc\x9b4td\xb2\xf9\x92\x9d\x80~\x03\x9d\xaeH\xd3\u064d\xe3y\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xfc\xa4;\xbc#\xa0\xd3!\xba\x9eF\xb9)s\\\xe7\xd8\xef\f\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfc\xa7>\xff\x87q\xc0\x10;\xa3\xcc\x1a\x9c%\x94H\xc7*\xbf\v\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xad\xa3\x00(?k\xcc\x13J\x91Eg`\xb0\xd7}\xe4\x10\xe0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc\xbc\\q\xac\xe7\x97AE\v\x01,\xf6\xb8\xd3\xf1}\xb6\x8ap\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfc\xbd\x85\xfe\xeajuO\xcf4ID\x9e7\xff\x97\x84\xf7w<\x89\xa7J\xdai\xab\xd7x\x00\x00\xe0\x94\xfc\xc9\u0524&.z\x02z\xb7Q\x91\x10\xd8\x02\u0115\xce\xea9\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\xfc\xcd\r\x1e\xce\xe2z\xdd\xea\x95\xf6\x85z\xee\xc8\u01e0K(\xee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfc\u0434\x82|\xd2\b\xff\xbf^u\x9d\xba\x8c<\xc6\x1d\x8c,<\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfc\xe0\x89c\\\xe9z\xba\xc0kD\x81\x9b\xe5\xbb\n>.\v7\x89\x05\x03\x92\nv0\xa7\x80\x00\u07d4\xfc\xf1\x99\xf8\xb8T\"/\x18.N\x1d\t\x9dN2>*\xae\x01\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xfc:P\x04\xd6xa?\v6\xa6B&\x9a\u007f7\x1c?j\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x19\x1a5\x15}x\x13s\xfbA\x1b\xf9\xf2R\x90\x04|^\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x1f\xaa4{\x0f\u0300L-\xa8l6\xd5\xf1\u044bp\x87\xbb\x89\x02\xd6\xeb$z\x96\xf6\x00\x00\u07d4\xfd\x1f\xb5\xa8\x9a\x89\xa7!\xb8yph\xfb\xc4\u007f>\x9dR\xe1I\x89\f\u0435\x83\u007f\xc6X\x00\x00\u07d4\xfd OOJ\xba%%\xbar\x8a\xfd\xf7\x87\x92\xcb\u07b75\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd'W\xcc5Q\xa0\x95\x87\x8d\x97\x87V\x15\xfe\fj2\xaa\x8a\x89 m\xb1R\x99\xbe\xac\x00\x00\u07d4\xfd(r\u045eW\x85<\xfa\x16\xef\xfe\x93\u0431\xd4{O\x93\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd))'\x1e\x9d \x95\xa2dv~{\r\xf5.\xa0\xd1\xd4\x00\x89\xa2\xa1\xeb%\x1bZ\xe4\x00\x00\u07d4\xfd7z8Rr\x90\f\xb46\xa3\xbbyb\xcd\xff\xe9?]\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd@$+\xb3Jp\x85^\xf0\xfd\x90\xf3\x80-\xec!6\xb3'\x89h\xa8u\a>)$\x00\x00\xe0\x94\xfdE,9i\xec\xe3\x80\x1cT \xf1\xcd\u02a1\xc7\x1e\xd2=\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xfdKU\x1fo\xdb\u0366\xc5\x11\xb5\xbb7\"P\xa6\xb7\x83\xe54\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xfdK\x98\x95X\xae\x11\xbe\f;6\xe2\xd6\xf2\xa5J\x93C\xca.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfdM\xe8\xe3t\x8a(\x9c\xf7\xd0`Q}\x9d88\x8d\xb0\x1f\xb8\x89\r\x8drkqw\xa8\x00\x00\u07d4\xfdZc\x15\u007f\x91O\u04d8\uac5c\x13}\xd9U\v\xb7q\\\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xfd`\u04b5\xaf=5\xf7\xaa\xf0\u00d3\x05.y\xc4\xd8#\u0645\x89\x03\x0e\xb5\r.\x14\b\x00\x00\u07d4\xfdhm\xe5?\xa9\u007f\x99c\x9e%hT\x97 \xbcX\x8c\x9e\xfc\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xfd~\u078fR@\xa0eA\xebi\x9dx,/\x9a\xfb!p\xf6\x89Hz\x9a0E9D\x00\x00\u07d4\xfd\x81+\u019f\xb1p\xefW\xe22~\x80\xaf\xfd\x14\xf8\xe4\xb6\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\x88\xd1\x14\"\x0f\b\x1c\xb3\xd5\xe1[\xe8\x15*\xb0sfWj\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xfd\x91\x856\xa8\xef\xa6\xf6\xce\xfe\x1f\xa1\x159\x95\xfe\xf5\xe3=;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfd\x92\x0fr&\x82\xaf\xb5\xafE\x1b\x05D\xd4\xf4\x1b;\x9dWB\x89~R\x05j\x12?<\x00\x00\u07d4\xfd\x95y\xf1\x19\xbb\xc8\x19\xa0+a\u3348\x03\xc9B\xf2M2\x89\x05\xb9~\x90\x81\xd9@\x00\x00\u07d4\xfd\xa0\xce\x153\a\a\xf1\v\xce2\x01\x17- \x18\xb9\xdd\xeat\x89\x02\xd0A\xd7\x05\xa2\xc6\x00\x00\xe0\x94\xfd\xa3\x04(\x19\xaf>f)\x00\xe1\xb9+CX\xed\xa6\xe9%\x90\x8a\x19\a\xa2\x84\u054fc\xe0\x00\x00\u07d4\xfd\xa6\x81\x0e\xa5\xac\x98]o\xfb\xf1\xc5\x11\xf1\xc1B\xed\xcf\xdd\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd\xb39D\xf26\x06\x15\xe5\xbe#\x95w\u0221\x9b\xa5-\x98\x87\x89 \x9d\x92/RY\xc5\x00\x00\u07d4\xfd\xbaSY\xf7\xec;\xc7p\xacI\x97]\x84N\xc9qbV\xf1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xfd\xc4\xd4vZ\x94/[\xf9i1\xa9\xe8\xccz\xb8\xb7W\xffL\x8a\x12lG\x8a\x0e>\xa8`\x00\x00\xe0\x94\xfd\xcd]\x80\xb1\x05\x89zW\xab\xc4xev\x8b)\x00RB\x95\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794\xfd\xd1\x19_y}O5q}\x15\xe6\xf9\x81\n\x9a?\xf5T`\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xfd\xd5\x02\xa7N\x81;\u03e3U\xce\xda<\x17ojhq\xaf\u007f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfd\u357c\vm\\\xbbL\x1d\x8f\xea>\vK\xffc^\x9d\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xea\xac*\xcf\x1d\x13\x8e\x19\xf2\xfc?\x9f\xb7E\x92\xe3\ud04a\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xfd\xec\xc8-\xdf\xc5a\x92\xe2oV<=h\xcbTJ\x96\xbf\xed\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\xfd\xf4#C\x01\x9b\v\fk\xf2`\xb1s\xaf\xab~E\xb9\xd6!\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xfd\xf4I\xf1\b\xc6\xfbOZ+\b\x1e\xed~E\u645eM%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xfda4\xc0J\x8a\xb7\xeb\x16\xf0\x06C\xf8\xfe\xd7\u06aa\ucc89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfe\x00\xbfC\x99\x11\xa5S\x98-\xb68\x03\x92E\xbc\xf02\xdb\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfe\x01n\xc1~\xc5\xf1\x0e;\xb9\x8f\xf4\xa1\xed\xa0E\x15v\x82\xab\x89\x14_T\x02\xe7\xb2\xe6\x00\x00\u07d4\xfe\x0e0\xe2\x14)\rt=\xd3\x0e\xb0\x82\xf1\xf0\xa5\"Z\xdea\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe!\v\x8f\x04\xdcmOv!j\xcf\xcb\u055b\xa8;\xe9\xb60\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\"\xa0\xb3\x88f\x8d\x1a\xe2d>w\x1d\xac\xf3\x8aCB#\u0309\xd8\xdb^\xbd{&8\x00\x00\u07d4\xfe6&\x88\x84_\xa2D\u0300~K\x110\xeb7A\xa8\x05\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe8'\xd5v0\u03c7a\xd5\x12y{\v\x85\x8eG\x8b\xbd\x12\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfeA\x8bB\x1a\x9cm76\x02y\x04u\xd20>\x11\xa7Y0\x897\b\xba\xed=h\x90\x00\x00\u07d4\xfeBI\x12yP\xe2\xf8\x96\xec\x0e~.=\x05Z\xab\x10U\x0f\x89$=M\x18\"\x9c\xa2\x00\x00\xe0\x94\xfeM\x84\x03!o\xd5qW+\xf1\xbd\xb0\x1d\x00W\x89x\u0588\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xfeS\xb9I\x89\u0619d\xda aS\x95&\xbb\xe9y\xdd.\xa9\x89h\xa8u\a>)$\x00\x00\u07d4\xfeT\x9b\xbf\xe6G@\x18\x98\x92\x93%8\u06afF\u04b6\x1dO\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfea]\x97\\\b\x87\xe0\xc9\x11>\xc7)\x84 \xa7\x93\xaf\x8b\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfee\xc4\x18\x8dy\"Wi\td D\xfd\xc5#\x95V\x01e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfei\u007f\xf2,\xa5G\xbf\xc9^3\xd9`\xda`\\gc\xf3[\x89G\xd4\x11\x9f\xd9`\x94\x00\x00\u07d4\xfej\x89[y\\\xb4\xbf\x85\x90=<\xe0\x9cZ\xa49S\u04ff\x89\xb8Pz\x82\a( \x00\x00\u07d4\xfeo_B\xb6\x19;\x1a\xd1b\x06\u4bf5#\x9dM}\xb4^\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xfep\x11\xb6\x98\xbf3q\x13-tE\xb1\x9e\xb5\xb0\x945j\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x80\xe9#-\xea\xff\x19\xba\xf9\x98i\x88:K\xdf\x00\x04\xe5<\x89.b\xf2\ni\xbe@\x00\x00\u07d4\xfe\x8en6eW\r\xffz\x1b\xdaiz\xa5\x89\xc0\xb4\xe9\x02J\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x8f\x1f\u072b\u007f\xbe\u0266\xa3\xfc\xc5\aa\x96\x00P\\6\xa3\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xfe\x91\xec\xcf+\xd5f\xaf\xa1\x16\x96\xc5\x04\x9f\xa8Lic\nR\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xfe\x96\xc4\xcd8\x15b@\x1a\xa3*\x86\xe6[\x9dR\xfa\x8a\xee'\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\xfe\x98\xc6d\xc3\xe4G\xa9^i\xbdX!q\xb7\x17n\xa2\xa6\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfe\x9a\xd1.\xf0]m\x90&\x1f\x96\xc84\n\x03\x81\x97M\xf4w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x9c\x0f\xff\xef\xb8\x03\b\x12V\xc0\xcfMfY\xe6\xd3>\xb4\xfb\x89R\xd5B\x80O\x1c\xe0\x00\x00\u07d4\xfe\x9c\xfc;\xb2\x93\u0772\x85\xe6%\xf3X/t\xa6\xb0\xa5\xa6\u0349j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xfe\x9e\x11\x97\u05d7JvH\xdc\u01e01\x12\xa8\x8e\xdb\xc9\x04]\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94\xfe\xac\xa2\xactbK\xf3H\xda\u0258QC\xcf\xd6R\xa4\xbeU\x8a\x05\x89\u007f\u02f0)\x14\b\x80\x00\u07d4\xfe\xad\x18\x03\xe5\xe77\xa6\x8e\x18G-\x9a\xc7\x15\xf0\x99L\u00be\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfe\xb8\xb8\xe2\xafqj\xe4\x1f\xc7\xc0K\xcf)T\x01VF\x1ek\x89TQt\xa5(\xa7z\x00\x00\u07d4\xfe\xb9-0\xbf\x01\xff\x9a\x19\x01flUsS+\xfa\a\xee\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe\xbc1s\xbc\x90r\x13cT\x00+{O\xb3\xbf\xc5?\"\xf1\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xfe\xbdH\xd0\xff\xdb\xd5el\xd5\xe6\x866:a\x14R(\xf2y\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xfe\xbd\x9f\x81\xcfx\xbd_\xb6\u0139\xa2K\xd4\x14\xbb\x9b\xfaLN\x89k\xe1\x0f\xb8\xedn\x13\x80\x00\u07d4\xfe\xc0o\xe2{D\u01c4\xb29n\xc9/{\x92:\xd1~\x90w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\xc1NT\x85\xde+>\xef^t\xc4aF\u06ceEN\x035\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\xfe\xd8Gm\x10\u0544\xb3\x8b\xfag7`\x0e\xf1\x9d5\xc4\x1e\u0609b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xfe\xef;n\xab\xc9J\xff\xd31\f\x1cM\x0ee7^\x13\x11\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\xf0\x9dp$?9\xed\x8c\xd8\x00\xbf\x96QG\x9e\x8fJ\xca<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe\xf3\xb3\u07ad\x1ai&\u051a\xa3+\x12\xc2*\xf5M\x9f\xf9\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xff\v|\xb7\x1d\xa9\xd4\xc1\xean\xcc(\xeb\xdaPLc\xf8/\u04498\x8a\x88]\xf2\xfcl\x00\x00\u07d4\xff\f\xc6\u73c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xff'&)AH\xb8lx\xa97$\x97\xe4Y\x89\x8e\xd3\xfe\xe3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xff=\xedz@\u04ef\xf0\u05e8\xc4_\xa6\x13j\xa0C=\xb4W\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xff>\xeeW\xc3Mm\xae\x97\r\x8b1\x11\x17\xc55\x86\xcd5\x02\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xff>\xf6\xba\x15\x1c!\xb5\x99\x86\xaed\xf6\xe8\"\x8b\u0262\xc73\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffA\xd9\xe1\xb4\xef\xfe\x18\u0630\xd1\xf6?\xc4%_\xb4\xe0l=\x89Hz\x9a0E9D\x00\x00\u07d4\xffE\xcb4\xc9(6M\x9c\xc9\u063b\x0074ta\x8f\x06\xf3\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xffI\xa7u\x81N\xc0\x00Q\xa7\x95\xa8u\xde$Y.\xa4\x00\u050a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xffJ@\x8fP\xe9\xe7!F\xa2\x8c\xe4\xfc\x8d\x90'\x1f\x11n\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xffM\x9c\x84\x84\xc4\x10T\x89H\xa4\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xe0\x94L*\xe4\x82Y5\x05\xf0\x16<\xde\xfc\a>\x81\xc6<\xdaA\a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa8\xe8\xf1G2e\x8eKQ\xe8q\x191\x05:\x8ai\xba\xf2\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe1\x94\u0665\x17\x9f\t\x1d\x85\x05\x1d<\x98'\x85\xef\xd1E\\\uc199\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\u08bdBX\xd2v\x887\xba\xa2j(\xfeq\xdc\a\x9f\x84\u01cbJG\xe3\xc1$H\xf4\xad\x00\x00\x00"
-const sepoliaAllocData = "\xf9\x01\xee\u0791i\x16\xa8{\x823?BE\x04f#\xb27\x94\xc6\\\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\x10\xf5\xd4XT\xe08\a\x14\x85\xac\x9e@#\b\u03c0\xd2\xd2\xfe\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\u0794y\x9d2\x9e_X4\x19\x16|\xd7\"\x96$\x85\x92n3\x8fJ\x88\r\u0db3\xa7d\x00\x00\xe0\x94|\xf5\xb7\x9b\xfe)\x1ag\xab\x02\xb3\x93\xe4V\xcc\xc4\xc2f\xf7S\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\x8b\u007f\tw\xbbO\x0f\xbepv\xfa\"\xbc$\xac\xa0CX?^\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xa2\xa6\xd949\x14O\xfeM'\xc9\xe0\x88\xdc\u0637\x83\x94bc\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xaa\xec\x869DA\xf9\x15\xbc\xe3\xe6\xab9\x99w\xe9\x90o;i\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u1532\x1c3\xde\x1f\xab?\xa1T\x99\xc6+Y\xfe\f\xc3%\x00 \u044bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\xbc\x11)Y6\xaay\u0554\x13\x9d\xe1\xb2\xe1&)AO;\u06ca\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xbe\xef2\xca[\x9a\x19\x8d'\xb4\xe0/LpC\x9f\xe6\x03V\u03ca\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94\xd7\xd7lX\xb3\xa5\x19\xe9\xfal\xc4\xd2-\xc0\x17%\x9b\u011f\x1e\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\xd7\xed\xdbx\xed)[<\x96)$\x0e\x89$\xfb\x8d\x88t\xdd\u060a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\u0665\x17\x9f\t\x1d\x85\x05\x1d<\x98'\x85\xef\xd1E\\\uc199\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xe2\xe2e\x90(\x147\x84\xd5W\xbc\xeco\xf3\xa0r\x10H\x88\n\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xf4|\xae\x1c\xf7\x9c\xa6u\x8b\xfcx}\xbd!\u6f7eq\x12\xb8\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00"
-const holeskyAllocData = "\xf9,\x85\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\x7f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\u0791i\x16\xa8{\x823?BE\x04f#\xb27\x94\xc6\\\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94\v\xe9I\x92\x8f\xf1\x99\xc9\xeb\xa9\xe1\x10\xdb!\n\xa5\xc9N\xfa\u040b|\x13\xbcK,\x13\x8e\u0344h\xa0\x03\x7f\x05\x8a\x9d\xaf\xady\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xf9!2\x94BBBBBBBBBBBBBBBBBBBB\x80\xf9!\x19\x80\xb9\x18\xd6`\x80`@R`\x046\x10a\x00?W`\x005`\xe0\x1c\x80c\x01\xff\u0267\x14a\x00DW\x80c\"\x89Q\x18\x14a\x00\xa4W\x80cb\x1f\xd10\x14a\x01\xbaW\x80c\xc5\xf2\x89/\x14a\x02DW[`\x00\x80\xfd[4\x80\x15a\x00PW`\x00\x80\xfd[Pa\x00\x90`\x04\x806\x03` \x81\x10\x15a\x00gW`\x00\x80\xfd[P5\x7f\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16a\x02kV[`@\x80Q\x91\x15\x15\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[a\x01\xb8`\x04\x806\x03`\x80\x81\x10\x15a\x00\xbaW`\x00\x80\xfd[\x81\x01\x90` \x81\x01\x815d\x01\x00\x00\x00\x00\x81\x11\x15a\x00\xd5W`\x00\x80\xfd[\x82\x01\x83` \x82\x01\x11\x15a\x00\xe7W`\x00\x80\xfd[\x805\x90` \x01\x91\x84`\x01\x83\x02\x84\x01\x11d\x01\x00\x00\x00\x00\x83\x11\x17\x15a\x01\tW`\x00\x80\xfd[\x91\x93\x90\x92\x90\x91` \x81\x01\x905d\x01\x00\x00\x00\x00\x81\x11\x15a\x01'W`\x00\x80\xfd[\x82\x01\x83` \x82\x01\x11\x15a\x019W`\x00\x80\xfd[\x805\x90` \x01\x91\x84`\x01\x83\x02\x84\x01\x11d\x01\x00\x00\x00\x00\x83\x11\x17\x15a\x01[W`\x00\x80\xfd[\x91\x93\x90\x92\x90\x91` \x81\x01\x905d\x01\x00\x00\x00\x00\x81\x11\x15a\x01yW`\x00\x80\xfd[\x82\x01\x83` \x82\x01\x11\x15a\x01\x8bW`\x00\x80\xfd[\x805\x90` \x01\x91\x84`\x01\x83\x02\x84\x01\x11d\x01\x00\x00\x00\x00\x83\x11\x17\x15a\x01\xadW`\x00\x80\xfd[\x91\x93P\x91P5a\x03\x04V[\x00[4\x80\x15a\x01\xc6W`\x00\x80\xfd[Pa\x01\xcfa\x10\xb5V[`@\x80Q` \x80\x82R\x83Q\x81\x83\x01R\x83Q\x91\x92\x83\x92\x90\x83\x01\x91\x85\x01\x90\x80\x83\x83`\x00[\x83\x81\x10\x15a\x02\tW\x81\x81\x01Q\x83\x82\x01R` \x01a\x01\xf1V[PPPP\x90P\x90\x81\x01\x90`\x1f\x16\x80\x15a\x026W\x80\x82\x03\x80Q`\x01\x83` \x03a\x01\x00\n\x03\x19\x16\x81R` \x01\x91P[P\x92PPP`@Q\x80\x91\x03\x90\xf3[4\x80\x15a\x02PW`\x00\x80\xfd[Pa\x02Ya\x10\xc7V[`@\x80Q\x91\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[`\x00\x7f\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x82\x16\x7f\x01\xff\u0267\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x80a\x02\xfeWP\x7f\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x82\x16\x7f\x85d\t\a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14[\x92\x91PPV[`0\x86\x14a\x03]W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`&\x81R` \x01\x80a\x18\x05`&\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[` \x84\x14a\x03\xb6W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`6\x81R` \x01\x80a\x17\x9c`6\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[``\x82\x14a\x04\x0fW`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`)\x81R` \x01\x80a\x18x`)\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[g\r\u0db3\xa7d\x00\x004\x10\x15a\x04pW`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`&\x81R` \x01\x80a\x18R`&\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[c;\x9a\xca\x004\x06\x15a\x04\xcdW`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`3\x81R` \x01\x80a\x17\xd2`3\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[c;\x9a\xca\x004\x04g\xff\xff\xff\xff\xff\xff\xff\xff\x81\x11\x15a\x055W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`'\x81R` \x01\x80a\x18+`'\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[``a\x05@\x82a\x14\xbaV[\x90P\x7fd\x9b\xbcb\xd0\xe3\x13B\xaf\xeaN\\\xd8-@I\xe7\xe1\xee\x91/\xc0\x88\x9a\xa7\x90\x80;\xe3\x908\u0149\x89\x89\x89\x85\x8a\x8aa\x05u` Ta\x14\xbaV[`@\x80Q`\xa0\x80\x82R\x81\x01\x89\x90R\x90\x81\x90` \x82\x01\x90\x82\x01``\x83\x01`\x80\x84\x01`\xc0\x85\x01\x8e\x8e\x80\x82\x847`\x00\x83\x82\x01R`\x1f\x01\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x16\x90\x91\x01\x87\x81\x03\x86R\x8c\x81R` \x01\x90P\x8c\x8c\x80\x82\x847`\x00\x83\x82\x01\x81\x90R`\x1f\x90\x91\x01\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x16\x90\x92\x01\x88\x81\x03\x86R\x8cQ\x81R\x8cQ` \x91\x82\x01\x93\x91\x8e\x01\x92P\x90\x81\x90\x84\x90\x84\x90[\x83\x81\x10\x15a\x06HW\x81\x81\x01Q\x83\x82\x01R` \x01a\x060V[PPPP\x90P\x90\x81\x01\x90`\x1f\x16\x80\x15a\x06uW\x80\x82\x03\x80Q`\x01\x83` \x03a\x01\x00\n\x03\x19\x16\x81R` \x01\x91P[P\x86\x81\x03\x83R\x88\x81R` \x01\x89\x89\x80\x82\x847`\x00\x83\x82\x01\x81\x90R`\x1f\x90\x91\x01\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x16\x90\x92\x01\x88\x81\x03\x84R\x89Q\x81R\x89Q` \x91\x82\x01\x93\x91\x8b\x01\x92P\x90\x81\x90\x84\x90\x84\x90[\x83\x81\x10\x15a\x06\xefW\x81\x81\x01Q\x83\x82\x01R` \x01a\x06\xd7V[PPPP\x90P\x90\x81\x01\x90`\x1f\x16\x80\x15a\a\x1cW\x80\x82\x03\x80Q`\x01\x83` \x03a\x01\x00\n\x03\x19\x16\x81R` \x01\x91P[P\x9dPPPPPPPPPPPPPP`@Q\x80\x91\x03\x90\xa1`\x00`\x02\x8a\x8a`\x00`\x80\x1b`@Q` \x01\x80\x84\x84\x80\x82\x847\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x94\x16\x91\x90\x93\x01\x90\x81R`@\x80Q\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x81\x84\x03\x01\x81R`\x10\x90\x92\x01\x90\x81\x90R\x81Q\x91\x95P\x93P\x83\x92P` \x85\x01\x91P\x80\x83\x83[` \x83\x10a\a\xfcW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\a\xbfV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\bYW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\bnW`\x00\x80\xfd[PQ\x90P`\x00`\x02\x80a\b\x84`@\x84\x8a\x8ca\x16\xfeV[`@Q` \x01\x80\x83\x83\x80\x82\x847\x80\x83\x01\x92PPP\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\b\xf8W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\b\xbbV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\tUW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\tjW`\x00\x80\xfd[PQ`\x02a\t{\x89`@\x81\x8da\x16\xfeV[`@Q`\x00\x90` \x01\x80\x84\x84\x80\x82\x847\x91\x90\x91\x01\x92\x83RPP`@\x80Q\x80\x83\x03\x81R` \x92\x83\x01\x91\x82\x90R\x80Q\x90\x94P\x90\x92P\x82\x91\x84\x01\x90\x80\x83\x83[` \x83\x10a\t\xf4W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\t\xb7V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\nQW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\nfW`\x00\x80\xfd[PQ`@\x80Q` \x81\x81\x01\x94\x90\x94R\x80\x82\x01\x92\x90\x92R\x80Q\x80\x83\x03\x82\x01\x81R``\x90\x92\x01\x90\x81\x90R\x81Q\x91\x92\x90\x91\x82\x91\x84\x01\x90\x80\x83\x83[` \x83\x10a\n\xdaW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\n\x9dV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\v7W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\vLW`\x00\x80\xfd[PQ`@\x80Q` \x81\x01\x85\x81R\x92\x93P`\x00\x92`\x02\x92\x83\x92\x87\x92\x8f\x92\x8f\x92\x01\x83\x83\x80\x82\x847\x80\x83\x01\x92PPP\x93PPPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\v\xd9W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\v\x9cV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\f6W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\fKW`\x00\x80\xfd[PQ`@Q\x86Q`\x02\x91\x88\x91`\x00\x91\x88\x91` \x91\x82\x01\x91\x82\x91\x90\x86\x01\x90\x80\x83\x83[` \x83\x10a\f\xa9W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\flV[`\x01\x83` \x03a\x01\x00\n\x03\x80\x19\x82Q\x16\x81\x84Q\x16\x80\x82\x17\x85RPPPPPP\x90P\x01\x83g\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16g\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x81R`\x18\x01\x82\x81R` \x01\x93PPPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\rNW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\r\x11V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\r\xabW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\r\xc0W`\x00\x80\xfd[PQ`@\x80Q` \x81\x81\x01\x94\x90\x94R\x80\x82\x01\x92\x90\x92R\x80Q\x80\x83\x03\x82\x01\x81R``\x90\x92\x01\x90\x81\x90R\x81Q\x91\x92\x90\x91\x82\x91\x84\x01\x90\x80\x83\x83[` \x83\x10a\x0e4W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\r\xf7V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x0e\x91W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x0e\xa6W`\x00\x80\xfd[PQ\x90P\x85\x81\x14a\x0f\x02W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`T\x81R` \x01\x80a\x17H`T\x919``\x01\x91PP`@Q\x80\x91\x03\x90\xfd[` Tc\xff\xff\xff\xff\x11a\x0f`W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`!\x81R` \x01\x80a\x17'`!\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[` \x80T`\x01\x01\x90\x81\x90U`\x00[` \x81\x10\x15a\x10\xa9W\x81`\x01\x16`\x01\x14\x15a\x0f\xa0W\x82`\x00\x82` \x81\x10a\x0f\x91W\xfe[\x01UPa\x10\xac\x95PPPPPPV[`\x02`\x00\x82` \x81\x10a\x0f\xafW\xfe[\x01T\x84`@Q` \x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x10%W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x0f\xe8V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x10\x82W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x10\x97W`\x00\x80\xfd[PQ\x92P`\x02\x82\x04\x91P`\x01\x01a\x0fnV[P\xfe[PPPPPPPV[``a\x10\xc2` Ta\x14\xbaV[\x90P\x90V[` T`\x00\x90\x81\x90\x81[` \x81\x10\x15a\x12\xf0W\x81`\x01\x16`\x01\x14\x15a\x11\xe6W`\x02`\x00\x82` \x81\x10a\x10\xf5W\xfe[\x01T\x84`@Q` \x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x11kW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x11.V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x11\xc8W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x11\xddW`\x00\x80\xfd[PQ\x92Pa\x12\xe2V[`\x02\x83`!\x83` \x81\x10a\x11\xf6W\xfe[\x01T`@Q` \x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x12kW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x12.V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x12\xc8W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x12\xddW`\x00\x80\xfd[PQ\x92P[`\x02\x82\x04\x91P`\x01\x01a\x10\xd1V[P`\x02\x82a\x12\xff` Ta\x14\xbaV[`\x00`@\x1b`@Q` \x01\x80\x84\x81R` \x01\x83\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x13ZW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x13\x1dV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x95\x90\x95\x16\x92\x01\x91\x82RP`@\x80Q\x80\x83\x03\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x01\x81R`\x18\x90\x92\x01\x90\x81\x90R\x81Q\x91\x95P\x93P\x83\x92\x85\x01\x91P\x80\x83\x83[` \x83\x10a\x14?W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x14\x02V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x14\x9cW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x14\xb1W`\x00\x80\xfd[PQ\x92PPP\x90V[`@\x80Q`\b\x80\x82R\x81\x83\x01\x90\x92R``\x91` \x82\x01\x81\x806\x837\x01\x90PP\x90P`\xc0\x82\x90\x1b\x80`\a\x1a`\xf8\x1b\x82`\x00\x81Q\x81\x10a\x14\xf4W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x06\x1a`\xf8\x1b\x82`\x01\x81Q\x81\x10a\x157W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x05\x1a`\xf8\x1b\x82`\x02\x81Q\x81\x10a\x15zW\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x04\x1a`\xf8\x1b\x82`\x03\x81Q\x81\x10a\x15\xbdW\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x03\x1a`\xf8\x1b\x82`\x04\x81Q\x81\x10a\x16\x00W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x02\x1a`\xf8\x1b\x82`\x05\x81Q\x81\x10a\x16CW\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x01\x1a`\xf8\x1b\x82`\x06\x81Q\x81\x10a\x16\x86W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x00\x1a`\xf8\x1b\x82`\a\x81Q\x81\x10a\x16\xc9W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SPP\x91\x90PV[`\x00\x80\x85\x85\x11\x15a\x17\rW\x81\x82\xfd[\x83\x86\x11\x15a\x17\x19W\x81\x82\xfd[PP\x82\x01\x93\x91\x90\x92\x03\x91PV\xfeDepositContract: merkle tree fullDepositContract: reconstructed DepositData does not match supplied deposit_data_rootDepositContract: invalid withdrawal_credentials lengthDepositContract: deposit value not multiple of gweiDepositContract: invalid pubkey lengthDepositContract: deposit value too highDepositContract: deposit value too lowDepositContract: invalid signature length\xa2dipfsX\"\x12 \x1d\xd2o7\xa6!p0\t\xab\xf1nw\u6713\xdcP\u01dd\xb7\xf6\xcc7T>>\x0e=\xec\u0717dsolcC\x00\x06\v\x003\xf9\b<\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\xa0\xf5\xa5\xfdB\xd1j 0'\x98\xefn\xd3\t\x97\x9bC\x00=# \xd9\xf0\xe8\xea\x981\xa9'Y\xfbK\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\xa0\xdbV\x11N\x00\xfd\xd4\xc1\xf8\\\x89+\xf3Z\u0268\x92\x89\xaa\xec\xb1\xeb\u0429l\xde`jt\x8b]q\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\xa0\u01c0\t\xfd\xf0\x7f\xc5j\x11\xf1\"7\x06X\xa3S\xaa\xa5B\xedc\xe4LK\xc1_\xf4\xcd\x10Z\xb3<\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\xa0Sm\x98\x83\x7f-\xd1e\xa5]^\xea\xe9\x14\x85\x95Dr\xd5o$m\xf2V\xbf<\xae\x195*\x12<\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\xa0\x9e\xfd\xe0R\xaa\x15B\x9f\xae\x05\xba\xd4\u0431\xd7\xc6M\xa6M\x03\u05e1\x85JX\x8c,\xb8C\f\r0\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\xa0\u060d\xdf\xee\xd4\x00\xa8uU\x96\xb2\x19B\xc1I~\x11L0.a\x18)\x0f\x91\xe6w)v\x04\x1f\xa1\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00(\xa0\x87\xeb\r\u06e5~5\xf6\u0486g8\x02\xa4\xafYu\xe2%\x06\xc7\xcfLd\xbbk\xe5\xee\x11R\x7f,\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\xa0&\x84dv\xfd_\xc5J]C8Qg\xc9QD\xf2d?S<\xc8[\xb9\xd1kx/\x8d}\xb1\x93\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\xa0Pm\x86X-%$\x05\xb8@\x01\x87\x92\xca\u04bf\x12Y\xf1\xefZ\xa5\xf8\x87\xe1<\xb2\xf0\tOQ\xe1\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\xa0\xff\xff\n\xd7\xe6Yw/\x954\xc1\x95\xc8\x15\xef\xc4\x01N\xf1\xe1\xda\xedD\x04\xc0c\x85\xd1\x11\x92\xe9+\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\xa0l\xf0A'\xdb\x05D\x1c\xd83\x10zR\xbe\x85(h\x89\x0eC\x17\xe6\xa0*\xb4v\x83\xaau\x96B \xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-\xa0\xb7\xd0_\x87_\x14\x00'\xefQ\x18\xa2${\xbb\x84\u038f/\x0f\x11#b0\x85\xda\xf7\x96\f2\x9f_\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\xa0\xdfj\xf5\xf5\xbb\xdbk\xe9\uf2a6\x18\u4fc0s\x96\bg\x17\x1e)go\x8b(M\xeaj\b\xa8^\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00/\xa0\xb5\x8d\x90\x0f^\x18.\x01t\u0285\x18.\xec\x9f:\t\xf6\xa6\xc0\xdfcw\xa5\x10\xd7\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x009\xa01 o\xa8\nP\xbbj\xbe)\bPX\xf1b\x12!*`\xee\xc8\xf0I\xfe\u02d2\xd8\xc8\xe0\xa8K\xc0\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\xa0!5+\xfe\xcb\xed\xdd\u94c3\x9faL=\xac\n>\xe3uC\xf9\xb4\x12\xb1a\x99\xdc\x15\x8e#\xb5D\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\xa0a\x9e1'$\xbbm|1S\xed\x9d\xe7\x91\xd7d\xa3f\xb3\x89\xaf\x13\u014b\xf8\xa8\xd9\x04\x81\xa4ge\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\xa0|\xdd)\x86&\x82Pb\x8d\f\x10\xe3\x85\u014ca\x91\xe6\xfb\xe0Q\x91\xbc\xc0O\x13?,\xear\xc1\xc4\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\xa0\x84\x890\xbd{\xa8\xca\xc5Fa\a!\x13\xfb'\x88i\xe0{\xb8X\x7f\x919)37M\x01{\xcb\xe1\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\xa0\x88i\xff,\"\xb2\x8c\xc1\x05\x10\u06452\x92\x803(\xbeO\xb0\xe8\x04\x95\u8ecd'\x1f[\x88\x966\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xa0\xb5\xfe(\xe7\x9f\x1b\x85\x0f\x86X$l\u9da1\u7d1f\xc0m\xb7\x14>\x8f\xe0\xb4\xf2\xb0\xc5R:\\\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\xa0\x98^\x92\x9fp\xaf(\u043d\u0469\n\x80\x8f\x97\x7fY||w\x8cH\x9e\x98\u04fd\x89\x10\xd3\x1a\xc0\xf7\xe1\x94F#\x96\u677f\xa4U\xf4\x05\xf4\u0742\xf3\x01J\xf8\x00;r\x8b\xa5o\xa5\xb9\x90\x19\xa5\xc8\x00\x00\x00\xe0\x94I\xdf<\xca&p\xeb\rY\x11F\xb1cY\xfe3nGo)\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94K\xc6V\xb3M\xe28\x96\xfa`i\u0246/5[t\x04\x01\xaf\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe0\x94M\v\x04\xb4\x05\u01b6,|\xfc:\xe5GYt~,\vFb\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94MIl\xcc(\x05\x8b\x1dt\xb7\xa1\x95Af>!\x15O\x9c\x84\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94P\x9avg\xac\x8d\x03 \xe3ar\xc1\x92Pja\x88\xaa\x84\xf6\x8b|\x13\xbcK,\x13\xfa<]\xc1\xaa\x19;\xc6\x03=\xfd\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94jz\xa9\xb8\x82\xd5\v\xb7\xbc]\xa1\xa2Dq\x9c\x99\xf1/\x06\xa3\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94l\xc99|;8s\x9d\xac\xbf\xaah\xea\xd5\xf5\xd7{\xa5\xf4U\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94s\xb2\xe0\xe5E\x10#\x9e\"\u0313o\vJm\xe1\xac\xf0\xab\u078bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94v,\xa6,\xa2T\x9a\xd8\x06v;:\xa1\xea1|B\x9b\xdb\u068a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94w\x8f_\x13\u013ex\xa3\xa4\xd7\x14\x1b\xcb&\x99\x97\x02\xf4\a\u03cbR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\x83M\xbfZ\x03\xe2\x9c%\xbcUE\x9c\u039c\x02\x1e\xeb\xe6v\xad\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\x87]%\xeeK\xc6\x04\xc7\x1b\xafb6\xa8H\x8f\"9\x9b\xedK\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u150d\xf7\x87\x8d5q\xbe\xf5\xe5\xa7D\xf9b\x87\xc8\xd2\x03\x86\xd7Z\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\x9eAZ\to\xf7vP\u0712]\xeaTe\x85\xb4\xad\xb3\"\xb6\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xa0vke\xa4\xf7\xb1\xday\xa1\xafy\xaciTV\uf886D\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xa2\x9b\x14JD\x9eAJG,`\u01ea\xf1\xaa\xff\xe3)\x02\x1d\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xa5S\x95Vk\vT9[2F\xf9j\v\xdcK\x8aH=\xf9\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xac\x9b\xa7/\xb6\x1a\xa7\xc3\x1a\x95\xdf\n\x8bn\xbaoA\xef\x87^\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xb0I\x8c\x15\x87\x9d\xb2\xeeTq\u0512l_\xaa%\u0260\x96\x83\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xb0J\xef*=-\x86\xb0\x10\x06\xcc\xd43\x9a.\x94=\x9cd\x80\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u1531\x9f\xb4\xc1\xf2\x802~`\xed7\xb1\xdcn\xe7u3S\x93\x14\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\xbb\x97{.\xe8\xa1\x11\u05c8\xb3G}$ x\u04387\xe7+\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xc2\x1c\xb9\u025c1m\x18c\x14/}\xd8m\xd5Im\x81\xa8\u058a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xc4s\xd4\x12\xdcR\xe3I\x86\"\t\x92L\x89\x81\xb2\xeeB\ah\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94\u010e#\xc5\xf6\xe1\xea\v\xae\xf6S\a4\xed\u00d6\x8fy\xaf.\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94\xc6\xe2E\x99\x91\xbf\xe2|\xcam\x86r/5\xda#\xa1\xe4\u02d7\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\xc9\xca+\xa9\xa2}\xe1\xdbX\x9d\x8c3\xab\x8e\u07e2\x11\x1b1\xfb\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xd1\xf7~L\x1cE\x18n\x86S\u0109\xf9\x0e\x00\x8asYr\x96\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe2\x94\u04d9NM2\x02\xdd#\xc8I}\x7fu\xbf\x16G\xd1\xda\x1b\xb1\x8c\x01\x9d\x97\x1eO\xe8@\x1et\x00\x00\x00\xe0\x94\u0726\u9d0e\xa8j\xeb\xfd\xf9\x92\x99I\x12@B)kn4\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xe0\x99\x1e\x84@A\xbeo\x11\xb9\x9d\xa5\xb1\x14\xb6\xbc\xf8N\xbdW\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94\u08bdBX\xd2v\x887\xba\xa2j(\xfeq\xdc\a\x9f\x84\u01cbR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\xea(\xd0\x02\x04/\u0649\x8d\r\xb0\x16\xbe\x97X\xee\xaf\xe3\\\x1e\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xef\xa7EO\x11\x16\x80yu\xa4u\vFi^\x96xP\xde]\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94\xfb\xfdo\xa9\xf7:\u01a0X\xe0\x12Y\x03L(\x00\x1b\xef\x82G\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00"
diff --git a/core/genesis_test.go b/core/genesis_test.go
deleted file mode 100644
index 1d85b510ca..0000000000
--- a/core/genesis_test.go
+++ /dev/null
@@ -1,327 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package core
-
-import (
- "bytes"
- "encoding/json"
- "math/big"
- "reflect"
- "testing"
-
- "github.com/davecgh/go-spew/spew"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/triedb/pathdb"
-)
-
-func TestInvalidCliqueConfig(t *testing.T) {
- block := DefaultGoerliGenesisBlock()
- block.ExtraData = []byte{}
- db := rawdb.NewMemoryDatabase()
- if _, err := block.Commit(db, trie.NewDatabase(db, nil)); err == nil {
- t.Fatal("Expected error on invalid clique config")
- }
-}
-
-func TestSetupGenesis(t *testing.T) {
- testSetupGenesis(t, rawdb.HashScheme)
- testSetupGenesis(t, rawdb.PathScheme)
-}
-
-func testSetupGenesis(t *testing.T, scheme string) {
- var (
- customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
- customg = Genesis{
- Config: ¶ms.ChainConfig{HomesteadBlock: big.NewInt(3)},
- Alloc: GenesisAlloc{
- {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
- },
- }
- oldcustomg = customg
- )
- oldcustomg.Config = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(2)}
-
- tests := []struct {
- name string
- fn func(ethdb.Database) (*params.ChainConfig, common.Hash, error)
- wantConfig *params.ChainConfig
- wantHash common.Hash
- wantErr error
- }{
- {
- name: "genesis without ChainConfig",
- fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), new(Genesis))
- },
- wantErr: errGenesisNoConfig,
- wantConfig: params.AllEthashProtocolChanges,
- },
- {
- name: "no block in DB, genesis == nil",
- fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
- },
- wantHash: params.MainnetGenesisHash,
- wantConfig: params.MainnetChainConfig,
- },
- {
- name: "mainnet block in DB, genesis == nil",
- fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- DefaultGenesisBlock().MustCommit(db, trie.NewDatabase(db, newDbConfig(scheme)))
- return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
- },
- wantHash: params.MainnetGenesisHash,
- wantConfig: params.MainnetChainConfig,
- },
- {
- name: "custom block in DB, genesis == nil",
- fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- tdb := trie.NewDatabase(db, newDbConfig(scheme))
- customg.Commit(db, tdb)
- return SetupGenesisBlock(db, tdb, nil)
- },
- wantHash: customghash,
- wantConfig: customg.Config,
- },
- {
- name: "custom block in DB, genesis == goerli",
- fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- tdb := trie.NewDatabase(db, newDbConfig(scheme))
- customg.Commit(db, tdb)
- return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
- },
- wantErr: &GenesisMismatchError{Stored: customghash, New: params.GoerliGenesisHash},
- wantHash: params.GoerliGenesisHash,
- wantConfig: params.GoerliChainConfig,
- },
- {
- name: "compatible config in DB",
- fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- tdb := trie.NewDatabase(db, newDbConfig(scheme))
- oldcustomg.Commit(db, tdb)
- return SetupGenesisBlock(db, tdb, &customg)
- },
- wantHash: customghash,
- wantConfig: customg.Config,
- },
- {
- name: "incompatible config in DB",
- fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- // Commit the 'old' genesis block with Homestead transition at #2.
- // Advance to block #4, past the homestead transition block of customg.
- tdb := trie.NewDatabase(db, newDbConfig(scheme))
- oldcustomg.Commit(db, tdb)
-
- bc, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), &oldcustomg, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
- defer bc.Stop()
-
- _, blocks, _ := GenerateChainWithGenesis(&oldcustomg, ethash.NewFaker(), 4, nil)
- bc.InsertChain(blocks)
-
- // This should return a compatibility error.
- return SetupGenesisBlock(db, tdb, &customg)
- },
- wantHash: customghash,
- wantConfig: customg.Config,
- wantErr: ¶ms.ConfigCompatError{
- What: "Homestead fork block",
- StoredBlock: big.NewInt(2),
- NewBlock: big.NewInt(3),
- RewindToBlock: 1,
- },
- },
- }
-
- for _, test := range tests {
- db := rawdb.NewMemoryDatabase()
- config, hash, err := test.fn(db)
- // Check the return values.
- if !reflect.DeepEqual(err, test.wantErr) {
- spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true}
- t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(err), spew.NewFormatter(test.wantErr))
- }
- if !reflect.DeepEqual(config, test.wantConfig) {
- t.Errorf("%s:\nreturned %v\nwant %v", test.name, config, test.wantConfig)
- }
- if hash != test.wantHash {
- t.Errorf("%s: returned hash %s, want %s", test.name, hash.Hex(), test.wantHash.Hex())
- } else if err == nil {
- // Check database content.
- stored := rawdb.ReadBlock(db, test.wantHash, 0)
- if stored.Hash() != test.wantHash {
- t.Errorf("%s: block in DB has hash %s, want %s", test.name, stored.Hash(), test.wantHash)
- }
- }
- }
-}
-
-// TestGenesisHashes checks the congruity of default genesis data to
-// corresponding hardcoded genesis hash values.
-func TestGenesisHashes(t *testing.T) {
- for i, c := range []struct {
- genesis *Genesis
- want common.Hash
- }{
- {DefaultGenesisBlock(), params.MainnetGenesisHash},
- {DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
- {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
- } {
- // Test via MustCommit
- db := rawdb.NewMemoryDatabase()
- if have := c.genesis.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults)).Hash(); have != c.want {
- t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
- }
- // Test via ToBlock
- if have := c.genesis.ToBlock().Hash(); have != c.want {
- t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
- }
- }
-}
-
-func TestGenesis_Commit(t *testing.T) {
- genesis := &Genesis{
- BaseFee: big.NewInt(params.InitialBaseFee),
- Config: params.TestChainConfig,
- // difficulty is nil
- }
-
- db := rawdb.NewMemoryDatabase()
- genesisBlock := genesis.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
-
- if genesis.Difficulty != nil {
- t.Fatalf("assumption wrong")
- }
-
- // This value should have been set as default in the ToBlock method.
- if genesisBlock.Difficulty().Cmp(params.GenesisDifficulty) != 0 {
- t.Errorf("assumption wrong: want: %d, got: %v", params.GenesisDifficulty, genesisBlock.Difficulty())
- }
-
- // Expect the stored total difficulty to be the difficulty of the genesis block.
- stored := rawdb.ReadTd(db, genesisBlock.Hash(), genesisBlock.NumberU64())
-
- if stored.Cmp(genesisBlock.Difficulty()) != 0 {
- t.Errorf("inequal difficulty; stored: %v, genesisBlock: %v", stored, genesisBlock.Difficulty())
- }
-}
-
-func TestReadWriteGenesisAlloc(t *testing.T) {
- var (
- db = rawdb.NewMemoryDatabase()
- alloc = &GenesisAlloc{
- {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
- {2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
- }
- hash, _ = alloc.hash(false)
- )
- blob, _ := json.Marshal(alloc)
- rawdb.WriteGenesisStateSpec(db, hash, blob)
-
- var reload GenesisAlloc
- err := reload.UnmarshalJSON(rawdb.ReadGenesisStateSpec(db, hash))
- if err != nil {
- t.Fatalf("Failed to load genesis state %v", err)
- }
- if len(reload) != len(*alloc) {
- t.Fatal("Unexpected genesis allocation")
- }
- for addr, account := range reload {
- want, ok := (*alloc)[addr]
- if !ok {
- t.Fatal("Account is not found")
- }
- if !reflect.DeepEqual(want, account) {
- t.Fatal("Unexpected account")
- }
- }
-}
-
-func newDbConfig(scheme string) *trie.Config {
- if scheme == rawdb.HashScheme {
- return trie.HashDefaults
- }
- return &trie.Config{PathDB: pathdb.Defaults}
-}
-
-func TestVerkleGenesisCommit(t *testing.T) {
- var verkleTime uint64 = 0
- verkleConfig := ¶ms.ChainConfig{
- ChainID: big.NewInt(1),
- HomesteadBlock: big.NewInt(0),
- DAOForkBlock: nil,
- DAOForkSupport: false,
- EIP150Block: big.NewInt(0),
- EIP155Block: big.NewInt(0),
- EIP158Block: big.NewInt(0),
- ByzantiumBlock: big.NewInt(0),
- ConstantinopleBlock: big.NewInt(0),
- PetersburgBlock: big.NewInt(0),
- IstanbulBlock: big.NewInt(0),
- MuirGlacierBlock: big.NewInt(0),
- BerlinBlock: big.NewInt(0),
- LondonBlock: big.NewInt(0),
- ArrowGlacierBlock: big.NewInt(0),
- GrayGlacierBlock: big.NewInt(0),
- MergeNetsplitBlock: nil,
- ShanghaiTime: &verkleTime,
- CancunTime: &verkleTime,
- PragueTime: &verkleTime,
- VerkleTime: &verkleTime,
- TerminalTotalDifficulty: big.NewInt(0),
- TerminalTotalDifficultyPassed: true,
- Ethash: nil,
- Clique: nil,
- }
-
- genesis := &Genesis{
- BaseFee: big.NewInt(params.InitialBaseFee),
- Config: verkleConfig,
- Timestamp: verkleTime,
- Difficulty: big.NewInt(0),
- Alloc: GenesisAlloc{
- {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
- },
- }
-
- expected := common.Hex2Bytes("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b")
- got := genesis.ToBlock().Root().Bytes()
- if !bytes.Equal(got, expected) {
- t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
- }
-
- db := rawdb.NewMemoryDatabase()
- triedb := trie.NewDatabase(db, &trie.Config{IsVerkle: true, PathDB: pathdb.Defaults})
- block := genesis.MustCommit(db, triedb)
- if !bytes.Equal(block.Root().Bytes(), expected) {
- t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
- }
-
- // Test that the trie is verkle
- if !triedb.IsVerkle() {
- t.Fatalf("expected trie to be verkle")
- }
-
- if !rawdb.ExistsAccountTrieNode(db, nil) {
- t.Fatal("could not find node")
- }
-}
diff --git a/core/headerchain.go b/core/headerchain.go
deleted file mode 100644
index 519a32ab80..0000000000
--- a/core/headerchain.go
+++ /dev/null
@@ -1,678 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package core
-
-import (
- crand "crypto/rand"
- "errors"
- "fmt"
- "math"
- "math/big"
- mrand "math/rand"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/lru"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-const (
- headerCacheLimit = 512
- tdCacheLimit = 1024
- numberCacheLimit = 2048
-)
-
-// HeaderChain implements the basic block header chain logic that is shared by
-// core.BlockChain and light.LightChain. It is not usable in itself, only as
-// a part of either structure.
-//
-// HeaderChain is responsible for maintaining the header chain including the
-// header query and updating.
-//
-// The components maintained by headerchain includes: (1) total difficulty
-// (2) header (3) block hash -> number mapping (4) canonical number -> hash mapping
-// and (5) head header flag.
-//
-// It is not thread safe either, the encapsulating chain structures should do
-// the necessary mutex locking/unlocking.
-type HeaderChain struct {
- config *params.ChainConfig
- chainDb ethdb.Database
- genesisHeader *types.Header
-
- currentHeader atomic.Value // Current head of the header chain (may be above the block chain!)
- currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
-
- headerCache *lru.Cache[common.Hash, *types.Header]
- tdCache *lru.Cache[common.Hash, *big.Int] // most recent total difficulties
- numberCache *lru.Cache[common.Hash, uint64] // most recent block numbers
-
- procInterrupt func() bool
-
- rand *mrand.Rand
- engine consensus.Engine
-}
-
-// NewHeaderChain creates a new HeaderChain structure. ProcInterrupt points
-// to the parent's interrupt semaphore.
-func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) {
- // Seed a fast but crypto originating random generator
- seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
- if err != nil {
- return nil, err
- }
- hc := &HeaderChain{
- config: config,
- chainDb: chainDb,
- headerCache: lru.NewCache[common.Hash, *types.Header](headerCacheLimit),
- tdCache: lru.NewCache[common.Hash, *big.Int](tdCacheLimit),
- numberCache: lru.NewCache[common.Hash, uint64](numberCacheLimit),
- procInterrupt: procInterrupt,
- rand: mrand.New(mrand.NewSource(seed.Int64())),
- engine: engine,
- }
- hc.genesisHeader = hc.GetHeaderByNumber(0)
- if hc.genesisHeader == nil {
- return nil, ErrNoGenesis
- }
- hc.currentHeader.Store(hc.genesisHeader)
- if head := rawdb.ReadHeadBlockHash(chainDb); head != (common.Hash{}) {
- if chead := hc.GetHeaderByHash(head); chead != nil {
- hc.currentHeader.Store(chead)
- }
- }
- hc.currentHeaderHash = hc.CurrentHeader().Hash()
- headHeaderGauge.Update(hc.CurrentHeader().Number.Int64())
- return hc, nil
-}
-
-// GetBlockNumber retrieves the block number belonging to the given hash
-// from the cache or database
-func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 {
- if cached, ok := hc.numberCache.Get(hash); ok {
- return &cached
- }
- number := rawdb.ReadHeaderNumber(hc.chainDb, hash)
- if number != nil {
- hc.numberCache.Add(hash, *number)
- }
- return number
-}
-
-type headerWriteResult struct {
- status WriteStatus
- ignored int
- imported int
- lastHash common.Hash
- lastHeader *types.Header
-}
-
-// Reorg reorgs the local canonical chain into the specified chain. The reorg
-// can be classified into two cases: (a) extend the local chain (b) switch the
-// head to the given header.
-func (hc *HeaderChain) Reorg(headers []*types.Header) error {
- // Short circuit if nothing to reorg.
- if len(headers) == 0 {
- return nil
- }
- // If the parent of the (first) block is already the canon header,
- // we don't have to go backwards to delete canon blocks, but simply
- // pile them onto the existing chain. Otherwise, do the necessary
- // reorgs.
- var (
- first = headers[0]
- last = headers[len(headers)-1]
- batch = hc.chainDb.NewBatch()
- )
- if first.ParentHash != hc.currentHeaderHash {
- // Delete any canonical number assignments above the new head
- for i := last.Number.Uint64() + 1; ; i++ {
- hash := rawdb.ReadCanonicalHash(hc.chainDb, i)
- if hash == (common.Hash{}) {
- break
- }
- rawdb.DeleteCanonicalHash(batch, i)
- }
- // Overwrite any stale canonical number assignments, going
- // backwards from the first header in this import until the
- // cross link between two chains.
- var (
- header = first
- headNumber = header.Number.Uint64()
- headHash = header.Hash()
- )
- for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash {
- rawdb.WriteCanonicalHash(batch, headHash, headNumber)
- if headNumber == 0 {
- break // It shouldn't be reached
- }
- headHash, headNumber = header.ParentHash, header.Number.Uint64()-1
- header = hc.GetHeader(headHash, headNumber)
- if header == nil {
- return fmt.Errorf("missing parent %d %x", headNumber, headHash)
- }
- }
- }
- // Extend the canonical chain with the new headers
- for i := 0; i < len(headers)-1; i++ {
- hash := headers[i+1].ParentHash // Save some extra hashing
- num := headers[i].Number.Uint64()
- rawdb.WriteCanonicalHash(batch, hash, num)
- rawdb.WriteHeadHeaderHash(batch, hash)
- }
- // Write the last header
- hash := headers[len(headers)-1].Hash()
- num := headers[len(headers)-1].Number.Uint64()
- rawdb.WriteCanonicalHash(batch, hash, num)
- rawdb.WriteHeadHeaderHash(batch, hash)
-
- if err := batch.Write(); err != nil {
- return err
- }
- // Last step update all in-memory head header markers
- hc.currentHeaderHash = last.Hash()
- hc.currentHeader.Store(types.CopyHeader(last))
- headHeaderGauge.Update(last.Number.Int64())
- return nil
-}
-
-// WriteHeaders writes a chain of headers into the local chain, given that the
-// parents are already known. The chain head header won't be updated in this
-// function, the additional SetCanonical is expected in order to finish the entire
-// procedure.
-func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
- if len(headers) == 0 {
- return 0, nil
- }
- ptd := hc.GetTd(headers[0].ParentHash, headers[0].Number.Uint64()-1)
- if ptd == nil {
- return 0, consensus.ErrUnknownAncestor
- }
- var (
- newTD = new(big.Int).Set(ptd) // Total difficulty of inserted chain
- inserted []rawdb.NumberHash // Ephemeral lookup of number/hash for the chain
- parentKnown = true // Set to true to force hc.HasHeader check the first iteration
- batch = hc.chainDb.NewBatch()
- )
- for i, header := range headers {
- var hash common.Hash
- // The headers have already been validated at this point, so we already
- // know that it's a contiguous chain, where
- // headers[i].Hash() == headers[i+1].ParentHash
- if i < len(headers)-1 {
- hash = headers[i+1].ParentHash
- } else {
- hash = header.Hash()
- }
- number := header.Number.Uint64()
- newTD.Add(newTD, header.Difficulty)
-
- // If the parent was not present, store it
- // If the header is already known, skip it, otherwise store
- alreadyKnown := parentKnown && hc.HasHeader(hash, number)
- if !alreadyKnown {
- // Irrelevant of the canonical status, write the TD and header to the database.
- rawdb.WriteTd(batch, hash, number, newTD)
- hc.tdCache.Add(hash, new(big.Int).Set(newTD))
-
- rawdb.WriteHeader(batch, header)
- inserted = append(inserted, rawdb.NumberHash{Number: number, Hash: hash})
- hc.headerCache.Add(hash, header)
- hc.numberCache.Add(hash, number)
- }
- parentKnown = alreadyKnown
- }
- // Skip the slow disk write of all headers if interrupted.
- if hc.procInterrupt() {
- log.Debug("Premature abort during headers import")
- return 0, errors.New("aborted")
- }
- // Commit to disk!
- if err := batch.Write(); err != nil {
- log.Crit("Failed to write headers", "error", err)
- }
- return len(inserted), nil
-}
-
-// writeHeadersAndSetHead writes a batch of block headers and applies the last
-// header as the chain head if the fork choicer says it's ok to update the chain.
-// Note: This method is not concurrent-safe with inserting blocks simultaneously
-// into the chain, as side effects caused by reorganisations cannot be emulated
-// without the real blocks. Hence, writing headers directly should only be done
-// in two scenarios: pure-header mode of operation (light clients), or properly
-// separated header/block phases (non-archive clients).
-func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *ForkChoice) (*headerWriteResult, error) {
- inserted, err := hc.WriteHeaders(headers)
- if err != nil {
- return nil, err
- }
- var (
- lastHeader = headers[len(headers)-1]
- lastHash = headers[len(headers)-1].Hash()
- result = &headerWriteResult{
- status: NonStatTy,
- ignored: len(headers) - inserted,
- imported: inserted,
- lastHash: lastHash,
- lastHeader: lastHeader,
- }
- )
- // Ask the fork choicer if the reorg is necessary
- if reorg, err := forker.ReorgNeeded(hc.CurrentHeader(), lastHeader); err != nil {
- return nil, err
- } else if !reorg {
- if inserted != 0 {
- result.status = SideStatTy
- }
- return result, nil
- }
- // Special case, all the inserted headers are already on the canonical
- // header chain, skip the reorg operation.
- if hc.GetCanonicalHash(lastHeader.Number.Uint64()) == lastHash && lastHeader.Number.Uint64() <= hc.CurrentHeader().Number.Uint64() {
- return result, nil
- }
- // Apply the reorg operation
- if err := hc.Reorg(headers); err != nil {
- return nil, err
- }
- result.status = CanonStatTy
- return result, nil
-}
-
-func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header) (int, error) {
- // Do a sanity check that the provided chain is actually ordered and linked
- for i := 1; i < len(chain); i++ {
- if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 {
- hash := chain[i].Hash()
- parentHash := chain[i-1].Hash()
- // Chain broke ancestry, log a message (programming error) and skip insertion
- log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", hash,
- "parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", parentHash)
-
- return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].Number,
- parentHash.Bytes()[:4], i, chain[i].Number, hash.Bytes()[:4], chain[i].ParentHash[:4])
- }
- // If the header is a banned one, straight out abort
- if BadHashes[chain[i].ParentHash] {
- return i - 1, ErrBannedHash
- }
- // If it's the last header in the cunk, we need to check it too
- if i == len(chain)-1 && BadHashes[chain[i].Hash()] {
- return i, ErrBannedHash
- }
- }
- // Start the parallel verifier
- abort, results := hc.engine.VerifyHeaders(hc, chain)
- defer close(abort)
-
- // Iterate over the headers and ensure they all check out
- for i := range chain {
- // If the chain is terminating, stop processing blocks
- if hc.procInterrupt() {
- log.Debug("Premature abort during headers verification")
- return 0, errors.New("aborted")
- }
- // Otherwise wait for headers checks and ensure they pass
- if err := <-results; err != nil {
- return i, err
- }
- }
-
- return 0, nil
-}
-
-// InsertHeaderChain inserts the given headers and does the reorganisations.
-//
-// The validity of the headers is NOT CHECKED by this method, i.e. they need to be
-// validated by ValidateHeaderChain before calling InsertHeaderChain.
-//
-// This insert is all-or-nothing. If this returns an error, no headers were written,
-// otherwise they were all processed successfully.
-//
-// The returned 'write status' says if the inserted headers are part of the canonical chain
-// or a side chain.
-func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time, forker *ForkChoice) (WriteStatus, error) {
- if hc.procInterrupt() {
- return 0, errors.New("aborted")
- }
- res, err := hc.writeHeadersAndSetHead(chain, forker)
- if err != nil {
- return 0, err
- }
- // Report some public statistics so the user has a clue what's going on
- context := []interface{}{
- "count", res.imported,
- "elapsed", common.PrettyDuration(time.Since(start)),
- }
- if last := res.lastHeader; last != nil {
- context = append(context, "number", last.Number, "hash", res.lastHash)
- if timestamp := time.Unix(int64(last.Time), 0); time.Since(timestamp) > time.Minute {
- context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
- }
- }
- if res.ignored > 0 {
- context = append(context, []interface{}{"ignored", res.ignored}...)
- }
- log.Debug("Imported new block headers", context...)
- return res.status, err
-}
-
-// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
-// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
-// number of blocks to be individually checked before we reach the canonical chain.
-//
-// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
-func (hc *HeaderChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
- if ancestor > number {
- return common.Hash{}, 0
- }
- if ancestor == 1 {
- // in this case it is cheaper to just read the header
- if header := hc.GetHeader(hash, number); header != nil {
- return header.ParentHash, number - 1
- }
- return common.Hash{}, 0
- }
- for ancestor != 0 {
- if rawdb.ReadCanonicalHash(hc.chainDb, number) == hash {
- ancestorHash := rawdb.ReadCanonicalHash(hc.chainDb, number-ancestor)
- if rawdb.ReadCanonicalHash(hc.chainDb, number) == hash {
- number -= ancestor
- return ancestorHash, number
- }
- }
- if *maxNonCanonical == 0 {
- return common.Hash{}, 0
- }
- *maxNonCanonical--
- ancestor--
- header := hc.GetHeader(hash, number)
- if header == nil {
- return common.Hash{}, 0
- }
- hash = header.ParentHash
- number--
- }
- return hash, number
-}
-
-// GetTd retrieves a block's total difficulty in the canonical chain from the
-// database by hash and number, caching it if found.
-func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {
- // Short circuit if the td's already in the cache, retrieve otherwise
- if cached, ok := hc.tdCache.Get(hash); ok {
- return cached
- }
- td := rawdb.ReadTd(hc.chainDb, hash, number)
- if td == nil {
- return nil
- }
- // Cache the found body for next time and return
- hc.tdCache.Add(hash, td)
- return td
-}
-
-// GetHeader retrieves a block header from the database by hash and number,
-// caching it if found.
-func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header {
- // Short circuit if the header's already in the cache, retrieve otherwise
- if header, ok := hc.headerCache.Get(hash); ok {
- return header
- }
- header := rawdb.ReadHeader(hc.chainDb, hash, number)
- if header == nil {
- return nil
- }
- // Cache the found header for next time and return
- hc.headerCache.Add(hash, header)
- return header
-}
-
-// GetHeaderByHash retrieves a block header from the database by hash, caching it if
-// found.
-func (hc *HeaderChain) GetHeaderByHash(hash common.Hash) *types.Header {
- number := hc.GetBlockNumber(hash)
- if number == nil {
- return nil
- }
- return hc.GetHeader(hash, *number)
-}
-
-// HasHeader checks if a block header is present in the database or not.
-// In theory, if header is present in the database, all relative components
-// like td and hash->number should be present too.
-func (hc *HeaderChain) HasHeader(hash common.Hash, number uint64) bool {
- if hc.numberCache.Contains(hash) || hc.headerCache.Contains(hash) {
- return true
- }
- return rawdb.HasHeader(hc.chainDb, hash, number)
-}
-
-// GetHeaderByNumber retrieves a block header from the database by number,
-// caching it (associated with its hash) if found.
-func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header {
- hash := rawdb.ReadCanonicalHash(hc.chainDb, number)
- if hash == (common.Hash{}) {
- return nil
- }
- return hc.GetHeader(hash, number)
-}
-
-// GetHeadersFrom returns a contiguous segment of headers, in rlp-form, going
-// backwards from the given number.
-// If the 'number' is higher than the highest local header, this method will
-// return a best-effort response, containing the headers that we do have.
-func (hc *HeaderChain) GetHeadersFrom(number, count uint64) []rlp.RawValue {
- // If the request is for future headers, we still return the portion of
- // headers that we are able to serve
- if current := hc.CurrentHeader().Number.Uint64(); current < number {
- if count > number-current {
- count -= number - current
- number = current
- } else {
- return nil
- }
- }
- var headers []rlp.RawValue
- // If we have some of the headers in cache already, use that before going to db.
- hash := rawdb.ReadCanonicalHash(hc.chainDb, number)
- if hash == (common.Hash{}) {
- return nil
- }
- for count > 0 {
- header, ok := hc.headerCache.Get(hash)
- if !ok {
- break
- }
- rlpData, _ := rlp.EncodeToBytes(header)
- headers = append(headers, rlpData)
- hash = header.ParentHash
- count--
- number--
- }
- // Read remaining from db
- if count > 0 {
- headers = append(headers, rawdb.ReadHeaderRange(hc.chainDb, number, count)...)
- }
- return headers
-}
-
-func (hc *HeaderChain) GetCanonicalHash(number uint64) common.Hash {
- return rawdb.ReadCanonicalHash(hc.chainDb, number)
-}
-
-// CurrentHeader retrieves the current head header of the canonical chain. The
-// header is retrieved from the HeaderChain's internal cache.
-func (hc *HeaderChain) CurrentHeader() *types.Header {
- return hc.currentHeader.Load().(*types.Header)
-}
-
-// SetCurrentHeader sets the in-memory head header marker of the canonical chan
-// as the given header.
-func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
- hc.currentHeader.Store(head)
- hc.currentHeaderHash = head.Hash()
- headHeaderGauge.Update(head.Number.Int64())
-}
-
-type (
- // UpdateHeadBlocksCallback is a callback function that is called by SetHead
- // before head header is updated. The method will return the actual block it
- // updated the head to (missing state) and a flag if setHead should continue
- // rewinding till that forcefully (exceeded ancient limits)
- UpdateHeadBlocksCallback func(ethdb.KeyValueWriter, *types.Header) (*types.Header, bool)
-
- // DeleteBlockContentCallback is a callback function that is called by SetHead
- // before each header is deleted.
- DeleteBlockContentCallback func(ethdb.KeyValueWriter, common.Hash, uint64)
-)
-
-// SetHead rewinds the local chain to a new head. Everything above the new head
-// will be deleted and the new one set.
-func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, delFn DeleteBlockContentCallback) {
- hc.setHead(head, 0, updateFn, delFn)
-}
-
-// SetHeadWithTimestamp rewinds the local chain to a new head timestamp. Everything
-// above the new head will be deleted and the new one set.
-func (hc *HeaderChain) SetHeadWithTimestamp(time uint64, updateFn UpdateHeadBlocksCallback, delFn DeleteBlockContentCallback) {
- hc.setHead(0, time, updateFn, delFn)
-}
-
-// setHead rewinds the local chain to a new head block or a head timestamp.
-// Everything above the new head will be deleted and the new one set.
-func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn UpdateHeadBlocksCallback, delFn DeleteBlockContentCallback) {
- // Sanity check that there's no attempt to undo the genesis block. This is
- // a fairly synthetic case where someone enables a timestamp based fork
- // below the genesis timestamp. It's nice to not allow that instead of the
- // entire chain getting deleted.
- if headTime > 0 && hc.genesisHeader.Time > headTime {
- // Note, a critical error is quite brutal, but we should really not reach
- // this point. Since pre-timestamp based forks it was impossible to have
- // a fork before block 0, the setHead would always work. With timestamp
- // forks it becomes possible to specify below the genesis. That said, the
- // only time we setHead via timestamp is with chain config changes on the
- // startup, so failing hard there is ok.
- log.Crit("Rejecting genesis rewind via timestamp", "target", headTime, "genesis", hc.genesisHeader.Time)
- }
- var (
- parentHash common.Hash
- batch = hc.chainDb.NewBatch()
- origin = true
- )
- done := func(header *types.Header) bool {
- if headTime > 0 {
- return header.Time <= headTime
- }
- return header.Number.Uint64() <= headBlock
- }
- for hdr := hc.CurrentHeader(); hdr != nil && !done(hdr); hdr = hc.CurrentHeader() {
- num := hdr.Number.Uint64()
-
- // Rewind chain to new head
- parent := hc.GetHeader(hdr.ParentHash, num-1)
- if parent == nil {
- parent = hc.genesisHeader
- }
- parentHash = parent.Hash()
-
- // Notably, since geth has the possibility for setting the head to a low
- // height which is even lower than ancient head.
- // In order to ensure that the head is always no higher than the data in
- // the database (ancient store or active store), we need to update head
- // first then remove the relative data from the database.
- //
- // Update head first(head fast block, head full block) before deleting the data.
- markerBatch := hc.chainDb.NewBatch()
- if updateFn != nil {
- newHead, force := updateFn(markerBatch, parent)
- if force && ((headTime > 0 && newHead.Time < headTime) || (headTime == 0 && newHead.Number.Uint64() < headBlock)) {
- log.Warn("Force rewinding till ancient limit", "head", newHead.Number.Uint64())
- headBlock, headTime = newHead.Number.Uint64(), 0 // Target timestamp passed, continue rewind in block mode (cleaner)
- }
- }
- // Update head header then.
- rawdb.WriteHeadHeaderHash(markerBatch, parentHash)
- if err := markerBatch.Write(); err != nil {
- log.Crit("Failed to update chain markers", "error", err)
- }
- hc.currentHeader.Store(parent)
- hc.currentHeaderHash = parentHash
- headHeaderGauge.Update(parent.Number.Int64())
-
- // If this is the first iteration, wipe any leftover data upwards too so
- // we don't end up with dangling daps in the database
- var nums []uint64
- if origin {
- for n := num + 1; len(rawdb.ReadAllHashes(hc.chainDb, n)) > 0; n++ {
- nums = append([]uint64{n}, nums...) // suboptimal, but we don't really expect this path
- }
- origin = false
- }
- nums = append(nums, num)
-
- // Remove the related data from the database on all sidechains
- for _, num := range nums {
- // Gather all the side fork hashes
- hashes := rawdb.ReadAllHashes(hc.chainDb, num)
- if len(hashes) == 0 {
- // No hashes in the database whatsoever, probably frozen already
- hashes = append(hashes, hdr.Hash())
- }
- for _, hash := range hashes {
- if delFn != nil {
- delFn(batch, hash, num)
- }
- rawdb.DeleteHeader(batch, hash, num)
- rawdb.DeleteTd(batch, hash, num)
- }
- rawdb.DeleteCanonicalHash(batch, num)
- }
- }
- // Flush all accumulated deletions.
- if err := batch.Write(); err != nil {
- log.Crit("Failed to rewind block", "error", err)
- }
- // Clear out any stale content from the caches
- hc.headerCache.Purge()
- hc.tdCache.Purge()
- hc.numberCache.Purge()
-}
-
-// SetGenesis sets a new genesis block header for the chain
-func (hc *HeaderChain) SetGenesis(head *types.Header) {
- hc.genesisHeader = head
-}
-
-// Config retrieves the header chain's chain configuration.
-func (hc *HeaderChain) Config() *params.ChainConfig { return hc.config }
-
-// Engine retrieves the header chain's consensus engine.
-func (hc *HeaderChain) Engine() consensus.Engine { return hc.engine }
-
-// GetBlock implements consensus.ChainReader, and returns nil for every input as
-// a header chain does not have blocks available for retrieval.
-func (hc *HeaderChain) GetBlock(hash common.Hash, number uint64) *types.Block {
- return nil
-}
diff --git a/core/headerchain_test.go b/core/headerchain_test.go
deleted file mode 100644
index 2c0323e6f7..0000000000
--- a/core/headerchain_test.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package core
-
-import (
- "errors"
- "fmt"
- "math/big"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/trie"
-)
-
-func verifyUnbrokenCanonchain(hc *HeaderChain) error {
- h := hc.CurrentHeader()
- for {
- canonHash := rawdb.ReadCanonicalHash(hc.chainDb, h.Number.Uint64())
- if exp := h.Hash(); canonHash != exp {
- return fmt.Errorf("Canon hash chain broken, block %d got %x, expected %x",
- h.Number, canonHash[:8], exp[:8])
- }
- // Verify that we have the TD
- if td := rawdb.ReadTd(hc.chainDb, canonHash, h.Number.Uint64()); td == nil {
- return fmt.Errorf("Canon TD missing at block %d", h.Number)
- }
- if h.Number.Uint64() == 0 {
- break
- }
- h = hc.GetHeader(h.ParentHash, h.Number.Uint64()-1)
- }
- return nil
-}
-
-func testInsert(t *testing.T, hc *HeaderChain, chain []*types.Header, wantStatus WriteStatus, wantErr error, forker *ForkChoice) {
- t.Helper()
-
- status, err := hc.InsertHeaderChain(chain, time.Now(), forker)
- if status != wantStatus {
- t.Errorf("wrong write status from InsertHeaderChain: got %v, want %v", status, wantStatus)
- }
- // Always verify that the header chain is unbroken
- if err := verifyUnbrokenCanonchain(hc); err != nil {
- t.Fatal(err)
- }
- if !errors.Is(err, wantErr) {
- t.Fatalf("unexpected error from InsertHeaderChain: %v", err)
- }
-}
-
-// This test checks status reporting of InsertHeaderChain.
-func TestHeaderInsertion(t *testing.T) {
- var (
- db = rawdb.NewMemoryDatabase()
- gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
- )
- gspec.Commit(db, trie.NewDatabase(db, nil))
- hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
- if err != nil {
- t.Fatal(err)
- }
- // chain A: G->A1->A2...A128
- genDb, chainA := makeHeaderChainWithGenesis(gspec, 128, ethash.NewFaker(), 10)
- // chain B: G->A1->B1...B128
- chainB := makeHeaderChain(gspec.Config, chainA[0], 128, ethash.NewFaker(), genDb, 10)
-
- forker := NewForkChoice(hc, nil)
- // Inserting 64 headers on an empty chain, expecting
- // 1 callbacks, 1 canon-status, 0 sidestatus,
- testInsert(t, hc, chainA[:64], CanonStatTy, nil, forker)
-
- // Inserting 64 identical headers, expecting
- // 0 callbacks, 0 canon-status, 0 sidestatus,
- testInsert(t, hc, chainA[:64], NonStatTy, nil, forker)
-
- // Inserting the same some old, some new headers
- // 1 callbacks, 1 canon, 0 side
- testInsert(t, hc, chainA[32:96], CanonStatTy, nil, forker)
-
- // Inserting side blocks, but not overtaking the canon chain
- testInsert(t, hc, chainB[0:32], SideStatTy, nil, forker)
-
- // Inserting more side blocks, but we don't have the parent
- testInsert(t, hc, chainB[34:36], NonStatTy, consensus.ErrUnknownAncestor, forker)
-
- // Inserting more sideblocks, overtaking the canon chain
- testInsert(t, hc, chainB[32:97], CanonStatTy, nil, forker)
-
- // Inserting more A-headers, taking back the canonicality
- testInsert(t, hc, chainA[90:100], CanonStatTy, nil, forker)
-
- // And B becomes canon again
- testInsert(t, hc, chainB[97:107], CanonStatTy, nil, forker)
-
- // And B becomes even longer
- testInsert(t, hc, chainB[107:128], CanonStatTy, nil, forker)
-}
diff --git a/core/mkalloc.go b/core/mkalloc.go
deleted file mode 100644
index 12c40c14fb..0000000000
--- a/core/mkalloc.go
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-//go:build none
-// +build none
-
-/*
-The mkalloc tool creates the genesis allocation constants in genesis_alloc.go
-It outputs a const declaration that contains an RLP-encoded list of (address, balance) tuples.
-
- go run mkalloc.go genesis.json
-*/
-package main
-
-import (
- "encoding/json"
- "fmt"
- "math/big"
- "os"
- "strconv"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/rlp"
- "golang.org/x/exp/slices"
-)
-
-type allocItem struct {
- Addr *big.Int
- Balance *big.Int
- Misc *allocItemMisc `rlp:"optional"`
-}
-
-type allocItemMisc struct {
- Nonce uint64
- Code []byte
- Slots []allocItemStorageItem
-}
-
-type allocItemStorageItem struct {
- Key common.Hash
- Val common.Hash
-}
-
-func makelist(g *core.Genesis) []allocItem {
- items := make([]allocItem, 0, len(g.Alloc))
- for addr, account := range g.Alloc {
- var misc *allocItemMisc
- if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 {
- misc = &allocItemMisc{
- Nonce: account.Nonce,
- Code: account.Code,
- Slots: make([]allocItemStorageItem, 0, len(account.Storage)),
- }
- for key, val := range account.Storage {
- misc.Slots = append(misc.Slots, allocItemStorageItem{key, val})
- }
- slices.SortFunc(misc.Slots, func(a, b allocItemStorageItem) int {
- return a.Key.Cmp(b.Key)
- })
- }
- bigAddr := new(big.Int).SetBytes(addr.Bytes())
- items = append(items, allocItem{bigAddr, account.Balance, misc})
- }
- slices.SortFunc(items, func(a, b allocItem) int {
- return a.Addr.Cmp(b.Addr)
- })
- return items
-}
-
-func makealloc(g *core.Genesis) string {
- a := makelist(g)
- data, err := rlp.EncodeToBytes(a)
- if err != nil {
- panic(err)
- }
- return strconv.QuoteToASCII(string(data))
-}
-
-func main() {
- if len(os.Args) != 2 {
- fmt.Fprintln(os.Stderr, "Usage: mkalloc genesis.json")
- os.Exit(1)
- }
-
- g := new(core.Genesis)
- file, err := os.Open(os.Args[1])
- if err != nil {
- panic(err)
- }
- if err := json.NewDecoder(file).Decode(g); err != nil {
- panic(err)
- }
- fmt.Println("const allocData =", makealloc(g))
-}
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
deleted file mode 100644
index d9a89fe90c..0000000000
--- a/core/rawdb/accessors_chain.go
+++ /dev/null
@@ -1,984 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/misc/eip4844"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
- "golang.org/x/exp/slices"
-)
-
-// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
-func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash {
- var data []byte
- db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
- data, _ = reader.Ancient(ChainFreezerHashTable, number)
- if len(data) == 0 {
- // Get it by hash from leveldb
- data, _ = db.Get(headerHashKey(number))
- }
- return nil
- })
- return common.BytesToHash(data)
-}
-
-// WriteCanonicalHash stores the hash assigned to a canonical block number.
-func WriteCanonicalHash(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
- if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil {
- log.Crit("Failed to store number to hash mapping", "err", err)
- }
-}
-
-// DeleteCanonicalHash removes the number to hash canonical mapping.
-func DeleteCanonicalHash(db ethdb.KeyValueWriter, number uint64) {
- if err := db.Delete(headerHashKey(number)); err != nil {
- log.Crit("Failed to delete number to hash mapping", "err", err)
- }
-}
-
-// ReadAllHashes retrieves all the hashes assigned to blocks at a certain heights,
-// both canonical and reorged forks included.
-func ReadAllHashes(db ethdb.Iteratee, number uint64) []common.Hash {
- prefix := headerKeyPrefix(number)
-
- hashes := make([]common.Hash, 0, 1)
- it := db.NewIterator(prefix, nil)
- defer it.Release()
-
- for it.Next() {
- if key := it.Key(); len(key) == len(prefix)+32 {
- hashes = append(hashes, common.BytesToHash(key[len(key)-32:]))
- }
- }
- return hashes
-}
-
-type NumberHash struct {
- Number uint64
- Hash common.Hash
-}
-
-// ReadAllHashesInRange retrieves all the hashes assigned to blocks at certain
-// heights, both canonical and reorged forks included.
-// This method considers both limits to be _inclusive_.
-func ReadAllHashesInRange(db ethdb.Iteratee, first, last uint64) []*NumberHash {
- var (
- start = encodeBlockNumber(first)
- keyLength = len(headerPrefix) + 8 + 32
- hashes = make([]*NumberHash, 0, 1+last-first)
- it = db.NewIterator(headerPrefix, start)
- )
- defer it.Release()
- for it.Next() {
- key := it.Key()
- if len(key) != keyLength {
- continue
- }
- num := binary.BigEndian.Uint64(key[len(headerPrefix) : len(headerPrefix)+8])
- if num > last {
- break
- }
- hash := common.BytesToHash(key[len(key)-32:])
- hashes = append(hashes, &NumberHash{num, hash})
- }
- return hashes
-}
-
-// ReadAllCanonicalHashes retrieves all canonical number and hash mappings at the
-// certain chain range. If the accumulated entries reaches the given threshold,
-// abort the iteration and return the semi-finish result.
-func ReadAllCanonicalHashes(db ethdb.Iteratee, from uint64, to uint64, limit int) ([]uint64, []common.Hash) {
- // Short circuit if the limit is 0.
- if limit == 0 {
- return nil, nil
- }
- var (
- numbers []uint64
- hashes []common.Hash
- )
- // Construct the key prefix of start point.
- start, end := headerHashKey(from), headerHashKey(to)
- it := db.NewIterator(nil, start)
- defer it.Release()
-
- for it.Next() {
- if bytes.Compare(it.Key(), end) >= 0 {
- break
- }
- if key := it.Key(); len(key) == len(headerPrefix)+8+1 && bytes.Equal(key[len(key)-1:], headerHashSuffix) {
- numbers = append(numbers, binary.BigEndian.Uint64(key[len(headerPrefix):len(headerPrefix)+8]))
- hashes = append(hashes, common.BytesToHash(it.Value()))
- // If the accumulated entries reaches the limit threshold, return.
- if len(numbers) >= limit {
- break
- }
- }
- }
- return numbers, hashes
-}
-
-// ReadHeaderNumber returns the header number assigned to a hash.
-func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
- data, _ := db.Get(headerNumberKey(hash))
- if len(data) != 8 {
- return nil
- }
- number := binary.BigEndian.Uint64(data)
- return &number
-}
-
-// WriteHeaderNumber stores the hash->number mapping.
-func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
- key := headerNumberKey(hash)
- enc := encodeBlockNumber(number)
- if err := db.Put(key, enc); err != nil {
- log.Crit("Failed to store hash to number mapping", "err", err)
- }
-}
-
-// DeleteHeaderNumber removes hash->number mapping.
-func DeleteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash) {
- if err := db.Delete(headerNumberKey(hash)); err != nil {
- log.Crit("Failed to delete hash to number mapping", "err", err)
- }
-}
-
-// ReadHeadHeaderHash retrieves the hash of the current canonical head header.
-func ReadHeadHeaderHash(db ethdb.KeyValueReader) common.Hash {
- data, _ := db.Get(headHeaderKey)
- if len(data) == 0 {
- return common.Hash{}
- }
- return common.BytesToHash(data)
-}
-
-// WriteHeadHeaderHash stores the hash of the current canonical head header.
-func WriteHeadHeaderHash(db ethdb.KeyValueWriter, hash common.Hash) {
- if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
- log.Crit("Failed to store last header's hash", "err", err)
- }
-}
-
-// ReadHeadBlockHash retrieves the hash of the current canonical head block.
-func ReadHeadBlockHash(db ethdb.KeyValueReader) common.Hash {
- data, _ := db.Get(headBlockKey)
- if len(data) == 0 {
- return common.Hash{}
- }
- return common.BytesToHash(data)
-}
-
-// WriteHeadBlockHash stores the head block's hash.
-func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
- if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
- log.Crit("Failed to store last block's hash", "err", err)
- }
-}
-
-// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block.
-func ReadHeadFastBlockHash(db ethdb.KeyValueReader) common.Hash {
- data, _ := db.Get(headFastBlockKey)
- if len(data) == 0 {
- return common.Hash{}
- }
- return common.BytesToHash(data)
-}
-
-// WriteHeadFastBlockHash stores the hash of the current fast-sync head block.
-func WriteHeadFastBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
- if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil {
- log.Crit("Failed to store last fast block's hash", "err", err)
- }
-}
-
-// ReadFinalizedBlockHash retrieves the hash of the finalized block.
-func ReadFinalizedBlockHash(db ethdb.KeyValueReader) common.Hash {
- data, _ := db.Get(headFinalizedBlockKey)
- if len(data) == 0 {
- return common.Hash{}
- }
- return common.BytesToHash(data)
-}
-
-// WriteFinalizedBlockHash stores the hash of the finalized block.
-func WriteFinalizedBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
- if err := db.Put(headFinalizedBlockKey, hash.Bytes()); err != nil {
- log.Crit("Failed to store last finalized block's hash", "err", err)
- }
-}
-
-// ReadLastPivotNumber retrieves the number of the last pivot block. If the node
-// full synced, the last pivot will always be nil.
-func ReadLastPivotNumber(db ethdb.KeyValueReader) *uint64 {
- data, _ := db.Get(lastPivotKey)
- if len(data) == 0 {
- return nil
- }
- var pivot uint64
- if err := rlp.DecodeBytes(data, &pivot); err != nil {
- log.Error("Invalid pivot block number in database", "err", err)
- return nil
- }
- return &pivot
-}
-
-// WriteLastPivotNumber stores the number of the last pivot block.
-func WriteLastPivotNumber(db ethdb.KeyValueWriter, pivot uint64) {
- enc, err := rlp.EncodeToBytes(pivot)
- if err != nil {
- log.Crit("Failed to encode pivot block number", "err", err)
- }
- if err := db.Put(lastPivotKey, enc); err != nil {
- log.Crit("Failed to store pivot block number", "err", err)
- }
-}
-
-// ReadTxIndexTail retrieves the number of oldest indexed block
-// whose transaction indices has been indexed.
-func ReadTxIndexTail(db ethdb.KeyValueReader) *uint64 {
- data, _ := db.Get(txIndexTailKey)
- if len(data) != 8 {
- return nil
- }
- number := binary.BigEndian.Uint64(data)
- return &number
-}
-
-// WriteTxIndexTail stores the number of oldest indexed block
-// into database.
-func WriteTxIndexTail(db ethdb.KeyValueWriter, number uint64) {
- if err := db.Put(txIndexTailKey, encodeBlockNumber(number)); err != nil {
- log.Crit("Failed to store the transaction index tail", "err", err)
- }
-}
-
-// ReadFastTxLookupLimit retrieves the tx lookup limit used in fast sync.
-func ReadFastTxLookupLimit(db ethdb.KeyValueReader) *uint64 {
- data, _ := db.Get(fastTxLookupLimitKey)
- if len(data) != 8 {
- return nil
- }
- number := binary.BigEndian.Uint64(data)
- return &number
-}
-
-// WriteFastTxLookupLimit stores the txlookup limit used in fast sync into database.
-func WriteFastTxLookupLimit(db ethdb.KeyValueWriter, number uint64) {
- if err := db.Put(fastTxLookupLimitKey, encodeBlockNumber(number)); err != nil {
- log.Crit("Failed to store transaction lookup limit for fast sync", "err", err)
- }
-}
-
-// ReadHeaderRange returns the rlp-encoded headers, starting at 'number', and going
-// backwards towards genesis. This method assumes that the caller already has
-// placed a cap on count, to prevent DoS issues.
-// Since this method operates in head-towards-genesis mode, it will return an empty
-// slice in case the head ('number') is missing. Hence, the caller must ensure that
-// the head ('number') argument is actually an existing header.
-//
-// N.B: Since the input is a number, as opposed to a hash, it's implicit that
-// this method only operates on canon headers.
-func ReadHeaderRange(db ethdb.Reader, number uint64, count uint64) []rlp.RawValue {
- var rlpHeaders []rlp.RawValue
- if count == 0 {
- return rlpHeaders
- }
- i := number
- if count-1 > number {
- // It's ok to request block 0, 1 item
- count = number + 1
- }
- limit, _ := db.Ancients()
- // First read live blocks
- if i >= limit {
- // If we need to read live blocks, we need to figure out the hash first
- hash := ReadCanonicalHash(db, number)
- for ; i >= limit && count > 0; i-- {
- if data, _ := db.Get(headerKey(i, hash)); len(data) > 0 {
- rlpHeaders = append(rlpHeaders, data)
- // Get the parent hash for next query
- hash = types.HeaderParentHashFromRLP(data)
- } else {
- break // Maybe got moved to ancients
- }
- count--
- }
- }
- if count == 0 {
- return rlpHeaders
- }
- // read remaining from ancients
- data, err := db.AncientRange(ChainFreezerHeaderTable, i+1-count, count, 0)
- if err != nil {
- log.Error("Failed to read headers from freezer", "err", err)
- return rlpHeaders
- }
- if uint64(len(data)) != count {
- log.Warn("Incomplete read of headers from freezer", "wanted", count, "read", len(data))
- return rlpHeaders
- }
- // The data is on the order [h, h+1, .., n] -- reordering needed
- for i := range data {
- rlpHeaders = append(rlpHeaders, data[len(data)-1-i])
- }
- return rlpHeaders
-}
-
-// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
-func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
- var data []byte
- db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
- // First try to look up the data in ancient database. Extra hash
- // comparison is necessary since ancient database only maintains
- // the canonical data.
- data, _ = reader.Ancient(ChainFreezerHeaderTable, number)
- if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
- return nil
- }
- // If not, try reading from leveldb
- data, _ = db.Get(headerKey(number, hash))
- return nil
- })
- return data
-}
-
-// HasHeader verifies the existence of a block header corresponding to the hash.
-func HasHeader(db ethdb.Reader, hash common.Hash, number uint64) bool {
- if isCanon(db, number, hash) {
- return true
- }
- if has, err := db.Has(headerKey(number, hash)); !has || err != nil {
- return false
- }
- return true
-}
-
-// ReadHeader retrieves the block header corresponding to the hash.
-func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header {
- data := ReadHeaderRLP(db, hash, number)
- if len(data) == 0 {
- return nil
- }
- header := new(types.Header)
- if err := rlp.DecodeBytes(data, header); err != nil {
- log.Error("Invalid block header RLP", "hash", hash, "err", err)
- return nil
- }
- return header
-}
-
-// WriteHeader stores a block header into the database and also stores the hash-
-// to-number mapping.
-func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) {
- var (
- hash = header.Hash()
- number = header.Number.Uint64()
- )
- // Write the hash -> number mapping
- WriteHeaderNumber(db, hash, number)
-
- // Write the encoded header
- data, err := rlp.EncodeToBytes(header)
- if err != nil {
- log.Crit("Failed to RLP encode header", "err", err)
- }
- key := headerKey(number, hash)
- if err := db.Put(key, data); err != nil {
- log.Crit("Failed to store header", "err", err)
- }
-}
-
-// DeleteHeader removes all block header data associated with a hash.
-func DeleteHeader(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
- deleteHeaderWithoutNumber(db, hash, number)
- if err := db.Delete(headerNumberKey(hash)); err != nil {
- log.Crit("Failed to delete hash to number mapping", "err", err)
- }
-}
-
-// deleteHeaderWithoutNumber removes only the block header but does not remove
-// the hash to number mapping.
-func deleteHeaderWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
- if err := db.Delete(headerKey(number, hash)); err != nil {
- log.Crit("Failed to delete header", "err", err)
- }
-}
-
-// isCanon is an internal utility method, to check whether the given number/hash
-// is part of the ancient (canon) set.
-func isCanon(reader ethdb.AncientReaderOp, number uint64, hash common.Hash) bool {
- h, err := reader.Ancient(ChainFreezerHashTable, number)
- if err != nil {
- return false
- }
- return bytes.Equal(h, hash[:])
-}
-
-// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
-func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
- // First try to look up the data in ancient database. Extra hash
- // comparison is necessary since ancient database only maintains
- // the canonical data.
- var data []byte
- db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
- // Check if the data is in ancients
- if isCanon(reader, number, hash) {
- data, _ = reader.Ancient(ChainFreezerBodiesTable, number)
- return nil
- }
- // If not, try reading from leveldb
- data, _ = db.Get(blockBodyKey(number, hash))
- return nil
- })
- return data
-}
-
-// ReadCanonicalBodyRLP retrieves the block body (transactions and uncles) for the canonical
-// block at number, in RLP encoding.
-func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
- var data []byte
- db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
- data, _ = reader.Ancient(ChainFreezerBodiesTable, number)
- if len(data) > 0 {
- return nil
- }
- // Block is not in ancients, read from leveldb by hash and number.
- // Note: ReadCanonicalHash cannot be used here because it also
- // calls ReadAncients internally.
- hash, _ := db.Get(headerHashKey(number))
- data, _ = db.Get(blockBodyKey(number, common.BytesToHash(hash)))
- return nil
- })
- return data
-}
-
-// WriteBodyRLP stores an RLP encoded block body into the database.
-func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp rlp.RawValue) {
- if err := db.Put(blockBodyKey(number, hash), rlp); err != nil {
- log.Crit("Failed to store block body", "err", err)
- }
-}
-
-// HasBody verifies the existence of a block body corresponding to the hash.
-func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool {
- if isCanon(db, number, hash) {
- return true
- }
- if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
- return false
- }
- return true
-}
-
-// ReadBody retrieves the block body corresponding to the hash.
-func ReadBody(db ethdb.Reader, hash common.Hash, number uint64) *types.Body {
- data := ReadBodyRLP(db, hash, number)
- if len(data) == 0 {
- return nil
- }
- body := new(types.Body)
- if err := rlp.DecodeBytes(data, body); err != nil {
- log.Error("Invalid block body RLP", "hash", hash, "err", err)
- return nil
- }
- return body
-}
-
-// WriteBody stores a block body into the database.
-func WriteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64, body *types.Body) {
- data, err := rlp.EncodeToBytes(body)
- if err != nil {
- log.Crit("Failed to RLP encode body", "err", err)
- }
- WriteBodyRLP(db, hash, number, data)
-}
-
-// DeleteBody removes all block body data associated with a hash.
-func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
- if err := db.Delete(blockBodyKey(number, hash)); err != nil {
- log.Crit("Failed to delete block body", "err", err)
- }
-}
-
-// ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding.
-func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
- var data []byte
- db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
- // Check if the data is in ancients
- if isCanon(reader, number, hash) {
- data, _ = reader.Ancient(ChainFreezerDifficultyTable, number)
- return nil
- }
- // If not, try reading from leveldb
- data, _ = db.Get(headerTDKey(number, hash))
- return nil
- })
- return data
-}
-
-// ReadTd retrieves a block's total difficulty corresponding to the hash.
-func ReadTd(db ethdb.Reader, hash common.Hash, number uint64) *big.Int {
- data := ReadTdRLP(db, hash, number)
- if len(data) == 0 {
- return nil
- }
- td := new(big.Int)
- if err := rlp.DecodeBytes(data, td); err != nil {
- log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
- return nil
- }
- return td
-}
-
-// WriteTd stores the total difficulty of a block into the database.
-func WriteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64, td *big.Int) {
- data, err := rlp.EncodeToBytes(td)
- if err != nil {
- log.Crit("Failed to RLP encode block total difficulty", "err", err)
- }
- if err := db.Put(headerTDKey(number, hash), data); err != nil {
- log.Crit("Failed to store block total difficulty", "err", err)
- }
-}
-
-// DeleteTd removes all block total difficulty data associated with a hash.
-func DeleteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
- if err := db.Delete(headerTDKey(number, hash)); err != nil {
- log.Crit("Failed to delete block total difficulty", "err", err)
- }
-}
-
-// HasReceipts verifies the existence of all the transaction receipts belonging
-// to a block.
-func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
- if isCanon(db, number, hash) {
- return true
- }
- if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil {
- return false
- }
- return true
-}
-
-// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
-func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
- var data []byte
- db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
- // Check if the data is in ancients
- if isCanon(reader, number, hash) {
- data, _ = reader.Ancient(ChainFreezerReceiptTable, number)
- return nil
- }
- // If not, try reading from leveldb
- data, _ = db.Get(blockReceiptsKey(number, hash))
- return nil
- })
- return data
-}
-
-// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
-// The receipt metadata fields are not guaranteed to be populated, so they
-// should not be used. Use ReadReceipts instead if the metadata is needed.
-func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts {
- // Retrieve the flattened receipt slice
- data := ReadReceiptsRLP(db, hash, number)
- if len(data) == 0 {
- return nil
- }
- // Convert the receipts from their storage form to their internal representation
- storageReceipts := []*types.ReceiptForStorage{}
- if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
- log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
- return nil
- }
- receipts := make(types.Receipts, len(storageReceipts))
- for i, storageReceipt := range storageReceipts {
- receipts[i] = (*types.Receipt)(storageReceipt)
- }
- return receipts
-}
-
-// ReadReceipts retrieves all the transaction receipts belonging to a block, including
-// its corresponding metadata fields. If it is unable to populate these metadata
-// fields then nil is returned.
-//
-// The current implementation populates these metadata fields by reading the receipts'
-// corresponding block body, so if the block body is not found it will return nil even
-// if the receipt itself is stored.
-func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, time uint64, config *params.ChainConfig) types.Receipts {
- // We're deriving many fields from the block body, retrieve beside the receipt
- receipts := ReadRawReceipts(db, hash, number)
- if receipts == nil {
- return nil
- }
- body := ReadBody(db, hash, number)
- if body == nil {
- log.Error("Missing body but have receipt", "hash", hash, "number", number)
- return nil
- }
- header := ReadHeader(db, hash, number)
-
- var baseFee *big.Int
- if header == nil {
- baseFee = big.NewInt(0)
- } else {
- baseFee = header.BaseFee
- }
- // Compute effective blob gas price.
- var blobGasPrice *big.Int
- if header != nil && header.ExcessBlobGas != nil {
- blobGasPrice = eip4844.CalcBlobFee(*header.ExcessBlobGas)
- }
- if err := receipts.DeriveFields(config, hash, number, time, baseFee, blobGasPrice, body.Transactions); err != nil {
- log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
- return nil
- }
- return receipts
-}
-
-// WriteReceipts stores all the transaction receipts belonging to a block.
-func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, receipts types.Receipts) {
- // Convert the receipts into their storage form and serialize them
- storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
- for i, receipt := range receipts {
- storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
- }
- bytes, err := rlp.EncodeToBytes(storageReceipts)
- if err != nil {
- log.Crit("Failed to encode block receipts", "err", err)
- }
- // Store the flattened receipt slice
- if err := db.Put(blockReceiptsKey(number, hash), bytes); err != nil {
- log.Crit("Failed to store block receipts", "err", err)
- }
-}
-
-// DeleteReceipts removes all receipt data associated with a block hash.
-func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
- if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
- log.Crit("Failed to delete block receipts", "err", err)
- }
-}
-
-// storedReceiptRLP is the storage encoding of a receipt.
-// Re-definition in core/types/receipt.go.
-// TODO: Re-use the existing definition.
-type storedReceiptRLP struct {
- PostStateOrStatus []byte
- CumulativeGasUsed uint64
- Logs []*types.Log
-}
-
-// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps
-// the list of logs. When decoding a stored receipt into this object we
-// avoid creating the bloom filter.
-type receiptLogs struct {
- Logs []*types.Log
-}
-
-// DecodeRLP implements rlp.Decoder.
-func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error {
- var stored storedReceiptRLP
- if err := s.Decode(&stored); err != nil {
- return err
- }
- r.Logs = stored.Logs
- return nil
-}
-
-// DeriveLogFields fills the logs in receiptLogs with information such as block number, txhash, etc.
-func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, txs types.Transactions) error {
- logIndex := uint(0)
- if len(txs) != len(receipts) {
- return errors.New("transaction and receipt count mismatch")
- }
- for i := 0; i < len(receipts); i++ {
- txHash := txs[i].Hash()
- // The derived log fields can simply be set from the block and transaction
- for j := 0; j < len(receipts[i].Logs); j++ {
- receipts[i].Logs[j].BlockNumber = number
- receipts[i].Logs[j].BlockHash = hash
- receipts[i].Logs[j].TxHash = txHash
- receipts[i].Logs[j].TxIndex = uint(i)
- receipts[i].Logs[j].Index = logIndex
- logIndex++
- }
- }
- return nil
-}
-
-// ReadLogs retrieves the logs for all transactions in a block. In case
-// receipts is not found, a nil is returned.
-// Note: ReadLogs does not derive unstored log fields.
-func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log {
- // Retrieve the flattened receipt slice
- data := ReadReceiptsRLP(db, hash, number)
- if len(data) == 0 {
- return nil
- }
- receipts := []*receiptLogs{}
- if err := rlp.DecodeBytes(data, &receipts); err != nil {
- log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
- return nil
- }
-
- logs := make([][]*types.Log, len(receipts))
- for i, receipt := range receipts {
- logs[i] = receipt.Logs
- }
- return logs
-}
-
-// ReadBlock retrieves an entire block corresponding to the hash, assembling it
-// back from the stored header and body. If either the header or body could not
-// be retrieved nil is returned.
-//
-// Note, due to concurrent download of header and block body the header and thus
-// canonical hash can be stored in the database but the body data not (yet).
-func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
- header := ReadHeader(db, hash, number)
- if header == nil {
- return nil
- }
- body := ReadBody(db, hash, number)
- if body == nil {
- return nil
- }
- return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles).WithWithdrawals(body.Withdrawals)
-}
-
-// WriteBlock serializes a block into the database, header and body separately.
-func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
- WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
- WriteHeader(db, block.Header())
-}
-
-// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.
-func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts, td *big.Int) (int64, error) {
- var (
- tdSum = new(big.Int).Set(td)
- stReceipts []*types.ReceiptForStorage
- )
- return db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- for i, block := range blocks {
- // Convert receipts to storage format and sum up total difficulty.
- stReceipts = stReceipts[:0]
- for _, receipt := range receipts[i] {
- stReceipts = append(stReceipts, (*types.ReceiptForStorage)(receipt))
- }
- header := block.Header()
- if i > 0 {
- tdSum.Add(tdSum, header.Difficulty)
- }
- if err := writeAncientBlock(op, block, header, stReceipts, tdSum); err != nil {
- return err
- }
- }
- return nil
- })
-}
-
-func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage, td *big.Int) error {
- num := block.NumberU64()
- if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
- return fmt.Errorf("can't add block %d hash: %v", num, err)
- }
- if err := op.Append(ChainFreezerHeaderTable, num, header); err != nil {
- return fmt.Errorf("can't append block header %d: %v", num, err)
- }
- if err := op.Append(ChainFreezerBodiesTable, num, block.Body()); err != nil {
- return fmt.Errorf("can't append block body %d: %v", num, err)
- }
- if err := op.Append(ChainFreezerReceiptTable, num, receipts); err != nil {
- return fmt.Errorf("can't append block %d receipts: %v", num, err)
- }
- if err := op.Append(ChainFreezerDifficultyTable, num, td); err != nil {
- return fmt.Errorf("can't append block %d total difficulty: %v", num, err)
- }
- return nil
-}
-
-// DeleteBlock removes all block data associated with a hash.
-func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
- DeleteReceipts(db, hash, number)
- DeleteHeader(db, hash, number)
- DeleteBody(db, hash, number)
- DeleteTd(db, hash, number)
-}
-
-// DeleteBlockWithoutNumber removes all block data associated with a hash, except
-// the hash to number mapping.
-func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
- DeleteReceipts(db, hash, number)
- deleteHeaderWithoutNumber(db, hash, number)
- DeleteBody(db, hash, number)
- DeleteTd(db, hash, number)
-}
-
-const badBlockToKeep = 10
-
-type badBlock struct {
- Header *types.Header
- Body *types.Body
-}
-
-// ReadBadBlock retrieves the bad block with the corresponding block hash.
-func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block {
- blob, err := db.Get(badBlockKey)
- if err != nil {
- return nil
- }
- var badBlocks []*badBlock
- if err := rlp.DecodeBytes(blob, &badBlocks); err != nil {
- return nil
- }
- for _, bad := range badBlocks {
- if bad.Header.Hash() == hash {
- return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals)
- }
- }
- return nil
-}
-
-// ReadAllBadBlocks retrieves all the bad blocks in the database.
-// All returned blocks are sorted in reverse order by number.
-func ReadAllBadBlocks(db ethdb.Reader) []*types.Block {
- blob, err := db.Get(badBlockKey)
- if err != nil {
- return nil
- }
- var badBlocks []*badBlock
- if err := rlp.DecodeBytes(blob, &badBlocks); err != nil {
- return nil
- }
- var blocks []*types.Block
- for _, bad := range badBlocks {
- blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals))
- }
- return blocks
-}
-
-// WriteBadBlock serializes the bad block into the database. If the cumulated
-// bad blocks exceeds the limitation, the oldest will be dropped.
-func WriteBadBlock(db ethdb.KeyValueStore, block *types.Block) {
- blob, err := db.Get(badBlockKey)
- if err != nil {
- log.Warn("Failed to load old bad blocks", "error", err)
- }
- var badBlocks []*badBlock
- if len(blob) > 0 {
- if err := rlp.DecodeBytes(blob, &badBlocks); err != nil {
- log.Crit("Failed to decode old bad blocks", "error", err)
- }
- }
- for _, b := range badBlocks {
- if b.Header.Number.Uint64() == block.NumberU64() && b.Header.Hash() == block.Hash() {
- log.Info("Skip duplicated bad block", "number", block.NumberU64(), "hash", block.Hash())
- return
- }
- }
- badBlocks = append(badBlocks, &badBlock{
- Header: block.Header(),
- Body: block.Body(),
- })
- slices.SortFunc(badBlocks, func(a, b *badBlock) int {
- // Note: sorting in descending number order.
- return -a.Header.Number.Cmp(b.Header.Number)
- })
- if len(badBlocks) > badBlockToKeep {
- badBlocks = badBlocks[:badBlockToKeep]
- }
- data, err := rlp.EncodeToBytes(badBlocks)
- if err != nil {
- log.Crit("Failed to encode bad blocks", "err", err)
- }
- if err := db.Put(badBlockKey, data); err != nil {
- log.Crit("Failed to write bad blocks", "err", err)
- }
-}
-
-// DeleteBadBlocks deletes all the bad blocks from the database
-func DeleteBadBlocks(db ethdb.KeyValueWriter) {
- if err := db.Delete(badBlockKey); err != nil {
- log.Crit("Failed to delete bad blocks", "err", err)
- }
-}
-
-// FindCommonAncestor returns the last common ancestor of two block headers
-func FindCommonAncestor(db ethdb.Reader, a, b *types.Header) *types.Header {
- for bn := b.Number.Uint64(); a.Number.Uint64() > bn; {
- a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
- if a == nil {
- return nil
- }
- }
- for an := a.Number.Uint64(); an < b.Number.Uint64(); {
- b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1)
- if b == nil {
- return nil
- }
- }
- for a.Hash() != b.Hash() {
- a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
- if a == nil {
- return nil
- }
- b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1)
- if b == nil {
- return nil
- }
- }
- return a
-}
-
-// ReadHeadHeader returns the current canonical head header.
-func ReadHeadHeader(db ethdb.Reader) *types.Header {
- headHeaderHash := ReadHeadHeaderHash(db)
- if headHeaderHash == (common.Hash{}) {
- return nil
- }
- headHeaderNumber := ReadHeaderNumber(db, headHeaderHash)
- if headHeaderNumber == nil {
- return nil
- }
- return ReadHeader(db, headHeaderHash, *headHeaderNumber)
-}
-
-// ReadHeadBlock returns the current canonical head block.
-func ReadHeadBlock(db ethdb.Reader) *types.Block {
- headBlockHash := ReadHeadBlockHash(db)
- if headBlockHash == (common.Hash{}) {
- return nil
- }
- headBlockNumber := ReadHeaderNumber(db, headBlockHash)
- if headBlockNumber == nil {
- return nil
- }
- return ReadBlock(db, headBlockHash, *headBlockNumber)
-}
diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go
deleted file mode 100644
index a7ceb72998..0000000000
--- a/core/rawdb/accessors_chain_test.go
+++ /dev/null
@@ -1,933 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "encoding/hex"
- "fmt"
- "math/big"
- "math/rand"
- "os"
- "reflect"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
- "golang.org/x/crypto/sha3"
-)
-
-// Tests block header storage and retrieval operations.
-func TestHeaderStorage(t *testing.T) {
- db := NewMemoryDatabase()
-
- // Create a test header to move around the database and make sure it's really new
- header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
- if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
- t.Fatalf("Non existent header returned: %v", entry)
- }
- // Write and verify the header in the database
- WriteHeader(db, header)
- if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry == nil {
- t.Fatalf("Stored header not found")
- } else if entry.Hash() != header.Hash() {
- t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header)
- }
- if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
- t.Fatalf("Stored header RLP not found")
- } else {
- hasher := sha3.NewLegacyKeccak256()
- hasher.Write(entry)
-
- if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() {
- t.Fatalf("Retrieved RLP header mismatch: have %v, want %v", entry, header)
- }
- }
- // Delete the header and verify the execution
- DeleteHeader(db, header.Hash(), header.Number.Uint64())
- if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
- t.Fatalf("Deleted header returned: %v", entry)
- }
-}
-
-// Tests block body storage and retrieval operations.
-func TestBodyStorage(t *testing.T) {
- db := NewMemoryDatabase()
-
- // Create a test body to move around the database and make sure it's really new
- body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
-
- hasher := sha3.NewLegacyKeccak256()
- rlp.Encode(hasher, body)
- hash := common.BytesToHash(hasher.Sum(nil))
-
- if entry := ReadBody(db, hash, 0); entry != nil {
- t.Fatalf("Non existent body returned: %v", entry)
- }
- // Write and verify the body in the database
- WriteBody(db, hash, 0, body)
- if entry := ReadBody(db, hash, 0); entry == nil {
- t.Fatalf("Stored body not found")
- } else if types.DeriveSha(types.Transactions(entry.Transactions), newTestHasher()) != types.DeriveSha(types.Transactions(body.Transactions), newTestHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
- t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
- }
- if entry := ReadBodyRLP(db, hash, 0); entry == nil {
- t.Fatalf("Stored body RLP not found")
- } else {
- hasher := sha3.NewLegacyKeccak256()
- hasher.Write(entry)
-
- if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {
- t.Fatalf("Retrieved RLP body mismatch: have %v, want %v", entry, body)
- }
- }
- // Delete the body and verify the execution
- DeleteBody(db, hash, 0)
- if entry := ReadBody(db, hash, 0); entry != nil {
- t.Fatalf("Deleted body returned: %v", entry)
- }
-}
-
-// Tests block storage and retrieval operations.
-func TestBlockStorage(t *testing.T) {
- db := NewMemoryDatabase()
-
- // Create a test block to move around the database and make sure it's really new
- block := types.NewBlockWithHeader(&types.Header{
- Extra: []byte("test block"),
- UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyTxsHash,
- ReceiptHash: types.EmptyReceiptsHash,
- })
- if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
- t.Fatalf("Non existent block returned: %v", entry)
- }
- if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil {
- t.Fatalf("Non existent header returned: %v", entry)
- }
- if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil {
- t.Fatalf("Non existent body returned: %v", entry)
- }
- // Write and verify the block in the database
- WriteBlock(db, block)
- if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil {
- t.Fatalf("Stored block not found")
- } else if entry.Hash() != block.Hash() {
- t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
- }
- if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry == nil {
- t.Fatalf("Stored header not found")
- } else if entry.Hash() != block.Header().Hash() {
- t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header())
- }
- if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil {
- t.Fatalf("Stored body not found")
- } else if types.DeriveSha(types.Transactions(entry.Transactions), newTestHasher()) != types.DeriveSha(block.Transactions(), newTestHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
- t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
- }
- // Delete the block and verify the execution
- DeleteBlock(db, block.Hash(), block.NumberU64())
- if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
- t.Fatalf("Deleted block returned: %v", entry)
- }
- if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil {
- t.Fatalf("Deleted header returned: %v", entry)
- }
- if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil {
- t.Fatalf("Deleted body returned: %v", entry)
- }
-}
-
-// Tests that partial block contents don't get reassembled into full blocks.
-func TestPartialBlockStorage(t *testing.T) {
- db := NewMemoryDatabase()
- block := types.NewBlockWithHeader(&types.Header{
- Extra: []byte("test block"),
- UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyTxsHash,
- ReceiptHash: types.EmptyReceiptsHash,
- })
- // Store a header and check that it's not recognized as a block
- WriteHeader(db, block.Header())
- if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
- t.Fatalf("Non existent block returned: %v", entry)
- }
- DeleteHeader(db, block.Hash(), block.NumberU64())
-
- // Store a body and check that it's not recognized as a block
- WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
- if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
- t.Fatalf("Non existent block returned: %v", entry)
- }
- DeleteBody(db, block.Hash(), block.NumberU64())
-
- // Store a header and a body separately and check reassembly
- WriteHeader(db, block.Header())
- WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
-
- if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil {
- t.Fatalf("Stored block not found")
- } else if entry.Hash() != block.Hash() {
- t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
- }
-}
-
-// Tests block storage and retrieval operations.
-func TestBadBlockStorage(t *testing.T) {
- db := NewMemoryDatabase()
-
- // Create a test block to move around the database and make sure it's really new
- block := types.NewBlockWithHeader(&types.Header{
- Number: big.NewInt(1),
- Extra: []byte("bad block"),
- UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyTxsHash,
- ReceiptHash: types.EmptyReceiptsHash,
- })
- if entry := ReadBadBlock(db, block.Hash()); entry != nil {
- t.Fatalf("Non existent block returned: %v", entry)
- }
- // Write and verify the block in the database
- WriteBadBlock(db, block)
- if entry := ReadBadBlock(db, block.Hash()); entry == nil {
- t.Fatalf("Stored block not found")
- } else if entry.Hash() != block.Hash() {
- t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
- }
- // Write one more bad block
- blockTwo := types.NewBlockWithHeader(&types.Header{
- Number: big.NewInt(2),
- Extra: []byte("bad block two"),
- UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyTxsHash,
- ReceiptHash: types.EmptyReceiptsHash,
- })
- WriteBadBlock(db, blockTwo)
-
- // Write the block one again, should be filtered out.
- WriteBadBlock(db, block)
- badBlocks := ReadAllBadBlocks(db)
- if len(badBlocks) != 2 {
- t.Fatalf("Failed to load all bad blocks")
- }
-
- // Write a bunch of bad blocks, all the blocks are should sorted
- // in reverse order. The extra blocks should be truncated.
- for _, n := range rand.Perm(100) {
- block := types.NewBlockWithHeader(&types.Header{
- Number: big.NewInt(int64(n)),
- Extra: []byte("bad block"),
- UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyTxsHash,
- ReceiptHash: types.EmptyReceiptsHash,
- })
- WriteBadBlock(db, block)
- }
- badBlocks = ReadAllBadBlocks(db)
- if len(badBlocks) != badBlockToKeep {
- t.Fatalf("The number of persised bad blocks in incorrect %d", len(badBlocks))
- }
- for i := 0; i < len(badBlocks)-1; i++ {
- if badBlocks[i].NumberU64() < badBlocks[i+1].NumberU64() {
- t.Fatalf("The bad blocks are not sorted #[%d](%d) < #[%d](%d)", i, i+1, badBlocks[i].NumberU64(), badBlocks[i+1].NumberU64())
- }
- }
-
- // Delete all bad blocks
- DeleteBadBlocks(db)
- badBlocks = ReadAllBadBlocks(db)
- if len(badBlocks) != 0 {
- t.Fatalf("Failed to delete bad blocks")
- }
-}
-
-// Tests block total difficulty storage and retrieval operations.
-func TestTdStorage(t *testing.T) {
- db := NewMemoryDatabase()
-
- // Create a test TD to move around the database and make sure it's really new
- hash, td := common.Hash{}, big.NewInt(314)
- if entry := ReadTd(db, hash, 0); entry != nil {
- t.Fatalf("Non existent TD returned: %v", entry)
- }
- // Write and verify the TD in the database
- WriteTd(db, hash, 0, td)
- if entry := ReadTd(db, hash, 0); entry == nil {
- t.Fatalf("Stored TD not found")
- } else if entry.Cmp(td) != 0 {
- t.Fatalf("Retrieved TD mismatch: have %v, want %v", entry, td)
- }
- // Delete the TD and verify the execution
- DeleteTd(db, hash, 0)
- if entry := ReadTd(db, hash, 0); entry != nil {
- t.Fatalf("Deleted TD returned: %v", entry)
- }
-}
-
-// Tests that canonical numbers can be mapped to hashes and retrieved.
-func TestCanonicalMappingStorage(t *testing.T) {
- db := NewMemoryDatabase()
-
- // Create a test canonical number and assigned hash to move around
- hash, number := common.Hash{0: 0xff}, uint64(314)
- if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) {
- t.Fatalf("Non existent canonical mapping returned: %v", entry)
- }
- // Write and verify the TD in the database
- WriteCanonicalHash(db, hash, number)
- if entry := ReadCanonicalHash(db, number); entry == (common.Hash{}) {
- t.Fatalf("Stored canonical mapping not found")
- } else if entry != hash {
- t.Fatalf("Retrieved canonical mapping mismatch: have %v, want %v", entry, hash)
- }
- // Delete the TD and verify the execution
- DeleteCanonicalHash(db, number)
- if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) {
- t.Fatalf("Deleted canonical mapping returned: %v", entry)
- }
-}
-
-// Tests that head headers and head blocks can be assigned, individually.
-func TestHeadStorage(t *testing.T) {
- db := NewMemoryDatabase()
-
- blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
- blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
- blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")})
-
- // Check that no head entries are in a pristine database
- if entry := ReadHeadHeaderHash(db); entry != (common.Hash{}) {
- t.Fatalf("Non head header entry returned: %v", entry)
- }
- if entry := ReadHeadBlockHash(db); entry != (common.Hash{}) {
- t.Fatalf("Non head block entry returned: %v", entry)
- }
- if entry := ReadHeadFastBlockHash(db); entry != (common.Hash{}) {
- t.Fatalf("Non fast head block entry returned: %v", entry)
- }
- // Assign separate entries for the head header and block
- WriteHeadHeaderHash(db, blockHead.Hash())
- WriteHeadBlockHash(db, blockFull.Hash())
- WriteHeadFastBlockHash(db, blockFast.Hash())
-
- // Check that both heads are present, and different (i.e. two heads maintained)
- if entry := ReadHeadHeaderHash(db); entry != blockHead.Hash() {
- t.Fatalf("Head header hash mismatch: have %v, want %v", entry, blockHead.Hash())
- }
- if entry := ReadHeadBlockHash(db); entry != blockFull.Hash() {
- t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash())
- }
- if entry := ReadHeadFastBlockHash(db); entry != blockFast.Hash() {
- t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash())
- }
-}
-
-// Tests that receipts associated with a single block can be stored and retrieved.
-func TestBlockReceiptStorage(t *testing.T) {
- db := NewMemoryDatabase()
-
- // Create a live block since we need metadata to reconstruct the receipt
- tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
- tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)
-
- body := &types.Body{Transactions: types.Transactions{tx1, tx2}}
-
- // Create the two receipts to manage afterwards
- receipt1 := &types.Receipt{
- Status: types.ReceiptStatusFailed,
- CumulativeGasUsed: 1,
- Logs: []*types.Log{
- {Address: common.BytesToAddress([]byte{0x11})},
- {Address: common.BytesToAddress([]byte{0x01, 0x11})},
- },
- TxHash: tx1.Hash(),
- ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
- GasUsed: 111111,
- }
- receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
-
- receipt2 := &types.Receipt{
- PostState: common.Hash{2}.Bytes(),
- CumulativeGasUsed: 2,
- Logs: []*types.Log{
- {Address: common.BytesToAddress([]byte{0x22})},
- {Address: common.BytesToAddress([]byte{0x02, 0x22})},
- },
- TxHash: tx2.Hash(),
- ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
- GasUsed: 222222,
- }
- receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
- receipts := []*types.Receipt{receipt1, receipt2}
-
- // Check that no receipt entries are in a pristine database
- hash := common.BytesToHash([]byte{0x03, 0x14})
- if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); len(rs) != 0 {
- t.Fatalf("non existent receipts returned: %v", rs)
- }
- // Insert the body that corresponds to the receipts
- WriteBody(db, hash, 0, body)
-
- // Insert the receipt slice into the database and check presence
- WriteReceipts(db, hash, 0, receipts)
- if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); len(rs) == 0 {
- t.Fatalf("no receipts returned")
- } else {
- if err := checkReceiptsRLP(rs, receipts); err != nil {
- t.Fatalf(err.Error())
- }
- }
- // Delete the body and ensure that the receipts are no longer returned (metadata can't be recomputed)
- DeleteBody(db, hash, 0)
- if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); rs != nil {
- t.Fatalf("receipts returned when body was deleted: %v", rs)
- }
- // Ensure that receipts without metadata can be returned without the block body too
- if err := checkReceiptsRLP(ReadRawReceipts(db, hash, 0), receipts); err != nil {
- t.Fatalf(err.Error())
- }
- // Sanity check that body alone without the receipt is a full purge
- WriteBody(db, hash, 0, body)
-
- DeleteReceipts(db, hash, 0)
- if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); len(rs) != 0 {
- t.Fatalf("deleted receipts returned: %v", rs)
- }
-}
-
-func checkReceiptsRLP(have, want types.Receipts) error {
- if len(have) != len(want) {
- return fmt.Errorf("receipts sizes mismatch: have %d, want %d", len(have), len(want))
- }
- for i := 0; i < len(want); i++ {
- rlpHave, err := rlp.EncodeToBytes(have[i])
- if err != nil {
- return err
- }
- rlpWant, err := rlp.EncodeToBytes(want[i])
- if err != nil {
- return err
- }
- if !bytes.Equal(rlpHave, rlpWant) {
- return fmt.Errorf("receipt #%d: receipt mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant))
- }
- }
- return nil
-}
-
-func TestAncientStorage(t *testing.T) {
- // Freezer style fast import the chain.
- frdir := t.TempDir()
- db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false)
- if err != nil {
- t.Fatalf("failed to create database with ancient backend")
- }
- defer db.Close()
-
- // Create a test block
- block := types.NewBlockWithHeader(&types.Header{
- Number: big.NewInt(0),
- Extra: []byte("test block"),
- UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyTxsHash,
- ReceiptHash: types.EmptyReceiptsHash,
- })
- // Ensure nothing non-existent will be read
- hash, number := block.Hash(), block.NumberU64()
- if blob := ReadHeaderRLP(db, hash, number); len(blob) > 0 {
- t.Fatalf("non existent header returned")
- }
- if blob := ReadBodyRLP(db, hash, number); len(blob) > 0 {
- t.Fatalf("non existent body returned")
- }
- if blob := ReadReceiptsRLP(db, hash, number); len(blob) > 0 {
- t.Fatalf("non existent receipts returned")
- }
- if blob := ReadTdRLP(db, hash, number); len(blob) > 0 {
- t.Fatalf("non existent td returned")
- }
-
- // Write and verify the header in the database
- WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil}, big.NewInt(100))
-
- if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
- t.Fatalf("no header returned")
- }
- if blob := ReadBodyRLP(db, hash, number); len(blob) == 0 {
- t.Fatalf("no body returned")
- }
- if blob := ReadReceiptsRLP(db, hash, number); len(blob) == 0 {
- t.Fatalf("no receipts returned")
- }
- if blob := ReadTdRLP(db, hash, number); len(blob) == 0 {
- t.Fatalf("no td returned")
- }
-
- // Use a fake hash for data retrieval, nothing should be returned.
- fakeHash := common.BytesToHash([]byte{0x01, 0x02, 0x03})
- if blob := ReadHeaderRLP(db, fakeHash, number); len(blob) != 0 {
- t.Fatalf("invalid header returned")
- }
- if blob := ReadBodyRLP(db, fakeHash, number); len(blob) != 0 {
- t.Fatalf("invalid body returned")
- }
- if blob := ReadReceiptsRLP(db, fakeHash, number); len(blob) != 0 {
- t.Fatalf("invalid receipts returned")
- }
- if blob := ReadTdRLP(db, fakeHash, number); len(blob) != 0 {
- t.Fatalf("invalid td returned")
- }
-}
-
-func TestCanonicalHashIteration(t *testing.T) {
- var cases = []struct {
- from, to uint64
- limit int
- expect []uint64
- }{
- {1, 8, 0, nil},
- {1, 8, 1, []uint64{1}},
- {1, 8, 10, []uint64{1, 2, 3, 4, 5, 6, 7}},
- {1, 9, 10, []uint64{1, 2, 3, 4, 5, 6, 7, 8}},
- {2, 9, 10, []uint64{2, 3, 4, 5, 6, 7, 8}},
- {9, 10, 10, nil},
- }
- // Test empty db iteration
- db := NewMemoryDatabase()
- numbers, _ := ReadAllCanonicalHashes(db, 0, 10, 10)
- if len(numbers) != 0 {
- t.Fatalf("No entry should be returned to iterate an empty db")
- }
- // Fill database with testing data.
- for i := uint64(1); i <= 8; i++ {
- WriteCanonicalHash(db, common.Hash{}, i)
- WriteTd(db, common.Hash{}, i, big.NewInt(10)) // Write some interferential data
- }
- for i, c := range cases {
- numbers, _ := ReadAllCanonicalHashes(db, c.from, c.to, c.limit)
- if !reflect.DeepEqual(numbers, c.expect) {
- t.Fatalf("Case %d failed, want %v, got %v", i, c.expect, numbers)
- }
- }
-}
-
-func TestHashesInRange(t *testing.T) {
- mkHeader := func(number, seq int) *types.Header {
- h := types.Header{
- Difficulty: new(big.Int),
- Number: big.NewInt(int64(number)),
- GasLimit: uint64(seq),
- }
- return &h
- }
- db := NewMemoryDatabase()
- // For each number, write N versions of that particular number
- total := 0
- for i := 0; i < 15; i++ {
- for ii := 0; ii < i; ii++ {
- WriteHeader(db, mkHeader(i, ii))
- total++
- }
- }
- if have, want := len(ReadAllHashesInRange(db, 10, 10)), 10; have != want {
- t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
- }
- if have, want := len(ReadAllHashesInRange(db, 10, 9)), 0; have != want {
- t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
- }
- if have, want := len(ReadAllHashesInRange(db, 0, 100)), total; have != want {
- t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
- }
- if have, want := len(ReadAllHashesInRange(db, 9, 10)), 9+10; have != want {
- t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
- }
- if have, want := len(ReadAllHashes(db, 10)), 10; have != want {
- t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
- }
- if have, want := len(ReadAllHashes(db, 16)), 0; have != want {
- t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
- }
- if have, want := len(ReadAllHashes(db, 1)), 1; have != want {
- t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
- }
-}
-
-// This measures the write speed of the WriteAncientBlocks operation.
-func BenchmarkWriteAncientBlocks(b *testing.B) {
- // Open freezer database.
- frdir := b.TempDir()
- db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false)
- if err != nil {
- b.Fatalf("failed to create database with ancient backend")
- }
- defer db.Close()
-
- // Create the data to insert. The blocks must have consecutive numbers, so we create
- // all of them ahead of time. However, there is no need to create receipts
- // individually for each block, just make one batch here and reuse it for all writes.
- const batchSize = 128
- const blockTxs = 20
- allBlocks := makeTestBlocks(b.N, blockTxs)
- batchReceipts := makeTestReceipts(batchSize, blockTxs)
- b.ResetTimer()
-
- // The benchmark loop writes batches of blocks, but note that the total block count is
- // b.N. This means the resulting ns/op measurement is the time it takes to write a
- // single block and its associated data.
- var td = big.NewInt(55)
- var totalSize int64
- for i := 0; i < b.N; i += batchSize {
- length := batchSize
- if i+batchSize > b.N {
- length = b.N - i
- }
-
- blocks := allBlocks[i : i+length]
- receipts := batchReceipts[:length]
- writeSize, err := WriteAncientBlocks(db, blocks, receipts, td)
- if err != nil {
- b.Fatal(err)
- }
- totalSize += writeSize
- }
-
- // Enable MB/s reporting.
- b.SetBytes(totalSize / int64(b.N))
-}
-
-// makeTestBlocks creates fake blocks for the ancient write benchmark.
-func makeTestBlocks(nblock int, txsPerBlock int) []*types.Block {
- key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- signer := types.LatestSignerForChainID(big.NewInt(8))
-
- // Create transactions.
- txs := make([]*types.Transaction, txsPerBlock)
- for i := 0; i < len(txs); i++ {
- var err error
- to := common.Address{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
- txs[i], err = types.SignNewTx(key, signer, &types.LegacyTx{
- Nonce: 2,
- GasPrice: big.NewInt(30000),
- Gas: 0x45454545,
- To: &to,
- })
- if err != nil {
- panic(err)
- }
- }
-
- // Create the blocks.
- blocks := make([]*types.Block, nblock)
- for i := 0; i < nblock; i++ {
- header := &types.Header{
- Number: big.NewInt(int64(i)),
- Extra: []byte("test block"),
- }
- blocks[i] = types.NewBlockWithHeader(header).WithBody(txs, nil)
- blocks[i].Hash() // pre-cache the block hash
- }
- return blocks
-}
-
-// makeTestReceipts creates fake receipts for the ancient write benchmark.
-func makeTestReceipts(n int, nPerBlock int) []types.Receipts {
- receipts := make([]*types.Receipt, nPerBlock)
- for i := 0; i < len(receipts); i++ {
- receipts[i] = &types.Receipt{
- Status: types.ReceiptStatusSuccessful,
- CumulativeGasUsed: 0x888888888,
- Logs: make([]*types.Log, 5),
- }
- }
- allReceipts := make([]types.Receipts, n)
- for i := 0; i < n; i++ {
- allReceipts[i] = receipts
- }
- return allReceipts
-}
-
-type fullLogRLP struct {
- Address common.Address
- Topics []common.Hash
- Data []byte
- BlockNumber uint64
- TxHash common.Hash
- TxIndex uint
- BlockHash common.Hash
- Index uint
-}
-
-func newFullLogRLP(l *types.Log) *fullLogRLP {
- return &fullLogRLP{
- Address: l.Address,
- Topics: l.Topics,
- Data: l.Data,
- BlockNumber: l.BlockNumber,
- TxHash: l.TxHash,
- TxIndex: l.TxIndex,
- BlockHash: l.BlockHash,
- Index: l.Index,
- }
-}
-
-// Tests that logs associated with a single block can be retrieved.
-func TestReadLogs(t *testing.T) {
- db := NewMemoryDatabase()
-
- // Create a live block since we need metadata to reconstruct the receipt
- tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
- tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)
-
- body := &types.Body{Transactions: types.Transactions{tx1, tx2}}
-
- // Create the two receipts to manage afterwards
- receipt1 := &types.Receipt{
- Status: types.ReceiptStatusFailed,
- CumulativeGasUsed: 1,
- Logs: []*types.Log{
- {Address: common.BytesToAddress([]byte{0x11})},
- {Address: common.BytesToAddress([]byte{0x01, 0x11})},
- },
- TxHash: tx1.Hash(),
- ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
- GasUsed: 111111,
- }
- receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
-
- receipt2 := &types.Receipt{
- PostState: common.Hash{2}.Bytes(),
- CumulativeGasUsed: 2,
- Logs: []*types.Log{
- {Address: common.BytesToAddress([]byte{0x22})},
- {Address: common.BytesToAddress([]byte{0x02, 0x22})},
- },
- TxHash: tx2.Hash(),
- ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
- GasUsed: 222222,
- }
- receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
- receipts := []*types.Receipt{receipt1, receipt2}
-
- hash := common.BytesToHash([]byte{0x03, 0x14})
- // Check that no receipt entries are in a pristine database
- if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); len(rs) != 0 {
- t.Fatalf("non existent receipts returned: %v", rs)
- }
- // Insert the body that corresponds to the receipts
- WriteBody(db, hash, 0, body)
-
- // Insert the receipt slice into the database and check presence
- WriteReceipts(db, hash, 0, receipts)
-
- logs := ReadLogs(db, hash, 0)
- if len(logs) == 0 {
- t.Fatalf("no logs returned")
- }
- if have, want := len(logs), 2; have != want {
- t.Fatalf("unexpected number of logs returned, have %d want %d", have, want)
- }
- if have, want := len(logs[0]), 2; have != want {
- t.Fatalf("unexpected number of logs[0] returned, have %d want %d", have, want)
- }
- if have, want := len(logs[1]), 2; have != want {
- t.Fatalf("unexpected number of logs[1] returned, have %d want %d", have, want)
- }
-
- for i, pr := range receipts {
- for j, pl := range pr.Logs {
- rlpHave, err := rlp.EncodeToBytes(newFullLogRLP(logs[i][j]))
- if err != nil {
- t.Fatal(err)
- }
- rlpWant, err := rlp.EncodeToBytes(newFullLogRLP(pl))
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(rlpHave, rlpWant) {
- t.Fatalf("receipt #%d: receipt mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant))
- }
- }
- }
-}
-
-func TestDeriveLogFields(t *testing.T) {
- // Create a few transactions to have receipts for
- to2 := common.HexToAddress("0x2")
- to3 := common.HexToAddress("0x3")
- txs := types.Transactions{
- types.NewTx(&types.LegacyTx{
- Nonce: 1,
- Value: big.NewInt(1),
- Gas: 1,
- GasPrice: big.NewInt(1),
- }),
- types.NewTx(&types.LegacyTx{
- To: &to2,
- Nonce: 2,
- Value: big.NewInt(2),
- Gas: 2,
- GasPrice: big.NewInt(2),
- }),
- types.NewTx(&types.AccessListTx{
- To: &to3,
- Nonce: 3,
- Value: big.NewInt(3),
- Gas: 3,
- GasPrice: big.NewInt(3),
- }),
- }
- // Create the corresponding receipts
- receipts := []*receiptLogs{
- {
- Logs: []*types.Log{
- {Address: common.BytesToAddress([]byte{0x11})},
- {Address: common.BytesToAddress([]byte{0x01, 0x11})},
- },
- },
- {
- Logs: []*types.Log{
- {Address: common.BytesToAddress([]byte{0x22})},
- {Address: common.BytesToAddress([]byte{0x02, 0x22})},
- },
- },
- {
- Logs: []*types.Log{
- {Address: common.BytesToAddress([]byte{0x33})},
- {Address: common.BytesToAddress([]byte{0x03, 0x33})},
- },
- },
- }
-
- // Derive log metadata fields
- number := big.NewInt(1)
- hash := common.BytesToHash([]byte{0x03, 0x14})
- if err := deriveLogFields(receipts, hash, number.Uint64(), txs); err != nil {
- t.Fatal(err)
- }
-
- // Iterate over all the computed fields and check that they're correct
- logIndex := uint(0)
- for i := range receipts {
- for j := range receipts[i].Logs {
- if receipts[i].Logs[j].BlockNumber != number.Uint64() {
- t.Errorf("receipts[%d].Logs[%d].BlockNumber = %d, want %d", i, j, receipts[i].Logs[j].BlockNumber, number.Uint64())
- }
- if receipts[i].Logs[j].BlockHash != hash {
- t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), hash.String())
- }
- if receipts[i].Logs[j].TxHash != txs[i].Hash() {
- t.Errorf("receipts[%d].Logs[%d].TxHash = %s, want %s", i, j, receipts[i].Logs[j].TxHash.String(), txs[i].Hash().String())
- }
- if receipts[i].Logs[j].TxIndex != uint(i) {
- t.Errorf("receipts[%d].Logs[%d].TransactionIndex = %d, want %d", i, j, receipts[i].Logs[j].TxIndex, i)
- }
- if receipts[i].Logs[j].Index != logIndex {
- t.Errorf("receipts[%d].Logs[%d].Index = %d, want %d", i, j, receipts[i].Logs[j].Index, logIndex)
- }
- logIndex++
- }
- }
-}
-
-func BenchmarkDecodeRLPLogs(b *testing.B) {
- // Encoded receipts from block 0x14ee094309fbe8f70b65f45ebcc08fb33f126942d97464aad5eb91cfd1e2d269
- buf, err := os.ReadFile("testdata/stored_receipts.bin")
- if err != nil {
- b.Fatal(err)
- }
- b.Run("ReceiptForStorage", func(b *testing.B) {
- b.ReportAllocs()
- var r []*types.ReceiptForStorage
- for i := 0; i < b.N; i++ {
- if err := rlp.DecodeBytes(buf, &r); err != nil {
- b.Fatal(err)
- }
- }
- })
- b.Run("rlpLogs", func(b *testing.B) {
- b.ReportAllocs()
- var r []*receiptLogs
- for i := 0; i < b.N; i++ {
- if err := rlp.DecodeBytes(buf, &r); err != nil {
- b.Fatal(err)
- }
- }
- })
-}
-
-func TestHeadersRLPStorage(t *testing.T) {
- // Have N headers in the freezer
- frdir := t.TempDir()
-
- db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false)
- if err != nil {
- t.Fatalf("failed to create database with ancient backend")
- }
- defer db.Close()
- // Create blocks
- var chain []*types.Block
- var pHash common.Hash
- for i := 0; i < 100; i++ {
- block := types.NewBlockWithHeader(&types.Header{
- Number: big.NewInt(int64(i)),
- Extra: []byte("test block"),
- UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyTxsHash,
- ReceiptHash: types.EmptyReceiptsHash,
- ParentHash: pHash,
- })
- chain = append(chain, block)
- pHash = block.Hash()
- }
- var receipts []types.Receipts = make([]types.Receipts, 100)
- // Write first half to ancients
- WriteAncientBlocks(db, chain[:50], receipts[:50], big.NewInt(100))
- // Write second half to db
- for i := 50; i < 100; i++ {
- WriteCanonicalHash(db, chain[i].Hash(), chain[i].NumberU64())
- WriteBlock(db, chain[i])
- }
- checkSequence := func(from, amount int) {
- headersRlp := ReadHeaderRange(db, uint64(from), uint64(amount))
- if have, want := len(headersRlp), amount; have != want {
- t.Fatalf("have %d headers, want %d", have, want)
- }
- for i, headerRlp := range headersRlp {
- var header types.Header
- if err := rlp.DecodeBytes(headerRlp, &header); err != nil {
- t.Fatal(err)
- }
- if have, want := header.Number.Uint64(), uint64(from-i); have != want {
- t.Fatalf("wrong number, have %d want %d", have, want)
- }
- }
- }
- checkSequence(99, 20) // Latest block and 19 parents
- checkSequence(99, 50) // Latest block -> all db blocks
- checkSequence(99, 51) // Latest block -> one from ancients
- checkSequence(99, 52) // Latest blocks -> two from ancients
- checkSequence(50, 2) // One from db, one from ancients
- checkSequence(49, 1) // One from ancients
- checkSequence(49, 50) // All ancient ones
- checkSequence(99, 100) // All blocks
- checkSequence(0, 1) // Only genesis
- checkSequence(1, 1) // Only block 1
- checkSequence(1, 2) // Genesis + block 1
-}
diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go
deleted file mode 100644
index 4f2ef0a880..0000000000
--- a/core/rawdb/accessors_indexes.go
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// ReadTxLookupEntry retrieves the positional metadata associated with a transaction
-// hash to allow retrieving the transaction or receipt by hash.
-func ReadTxLookupEntry(db ethdb.Reader, hash common.Hash) *uint64 {
- data, _ := db.Get(txLookupKey(hash))
- if len(data) == 0 {
- return nil
- }
- // Database v6 tx lookup just stores the block number
- if len(data) < common.HashLength {
- number := new(big.Int).SetBytes(data).Uint64()
- return &number
- }
- // Database v4-v5 tx lookup format just stores the hash
- if len(data) == common.HashLength {
- return ReadHeaderNumber(db, common.BytesToHash(data))
- }
- // Finally try database v3 tx lookup format
- var entry LegacyTxLookupEntry
- if err := rlp.DecodeBytes(data, &entry); err != nil {
- log.Error("Invalid transaction lookup entry RLP", "hash", hash, "blob", data, "err", err)
- return nil
- }
- return &entry.BlockIndex
-}
-
-// writeTxLookupEntry stores a positional metadata for a transaction,
-// enabling hash based transaction and receipt lookups.
-func writeTxLookupEntry(db ethdb.KeyValueWriter, hash common.Hash, numberBytes []byte) {
- if err := db.Put(txLookupKey(hash), numberBytes); err != nil {
- log.Crit("Failed to store transaction lookup entry", "err", err)
- }
-}
-
-// WriteTxLookupEntries is identical to WriteTxLookupEntry, but it works on
-// a list of hashes
-func WriteTxLookupEntries(db ethdb.KeyValueWriter, number uint64, hashes []common.Hash) {
- numberBytes := new(big.Int).SetUint64(number).Bytes()
- for _, hash := range hashes {
- writeTxLookupEntry(db, hash, numberBytes)
- }
-}
-
-// WriteTxLookupEntriesByBlock stores a positional metadata for every transaction from
-// a block, enabling hash based transaction and receipt lookups.
-func WriteTxLookupEntriesByBlock(db ethdb.KeyValueWriter, block *types.Block) {
- numberBytes := block.Number().Bytes()
- for _, tx := range block.Transactions() {
- writeTxLookupEntry(db, tx.Hash(), numberBytes)
- }
-}
-
-// DeleteTxLookupEntry removes all transaction data associated with a hash.
-func DeleteTxLookupEntry(db ethdb.KeyValueWriter, hash common.Hash) {
- if err := db.Delete(txLookupKey(hash)); err != nil {
- log.Crit("Failed to delete transaction lookup entry", "err", err)
- }
-}
-
-// DeleteTxLookupEntries removes all transaction lookups for a given block.
-func DeleteTxLookupEntries(db ethdb.KeyValueWriter, hashes []common.Hash) {
- for _, hash := range hashes {
- DeleteTxLookupEntry(db, hash)
- }
-}
-
-// ReadTransaction retrieves a specific transaction from the database, along with
-// its added positional metadata.
-func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
- blockNumber := ReadTxLookupEntry(db, hash)
- if blockNumber == nil {
- return nil, common.Hash{}, 0, 0
- }
- blockHash := ReadCanonicalHash(db, *blockNumber)
- if blockHash == (common.Hash{}) {
- return nil, common.Hash{}, 0, 0
- }
- body := ReadBody(db, blockHash, *blockNumber)
- if body == nil {
- log.Error("Transaction referenced missing", "number", *blockNumber, "hash", blockHash)
- return nil, common.Hash{}, 0, 0
- }
- for txIndex, tx := range body.Transactions {
- if tx.Hash() == hash {
- return tx, blockHash, *blockNumber, uint64(txIndex)
- }
- }
- log.Error("Transaction not found", "number", *blockNumber, "hash", blockHash, "txhash", hash)
- return nil, common.Hash{}, 0, 0
-}
-
-// ReadReceipt retrieves a specific transaction receipt from the database, along with
-// its added positional metadata.
-func ReadReceipt(db ethdb.Reader, hash common.Hash, config *params.ChainConfig) (*types.Receipt, common.Hash, uint64, uint64) {
- // Retrieve the context of the receipt based on the transaction hash
- blockNumber := ReadTxLookupEntry(db, hash)
- if blockNumber == nil {
- return nil, common.Hash{}, 0, 0
- }
- blockHash := ReadCanonicalHash(db, *blockNumber)
- if blockHash == (common.Hash{}) {
- return nil, common.Hash{}, 0, 0
- }
- blockHeader := ReadHeader(db, blockHash, *blockNumber)
- if blockHeader == nil {
- return nil, common.Hash{}, 0, 0
- }
- // Read all the receipts from the block and return the one with the matching hash
- receipts := ReadReceipts(db, blockHash, *blockNumber, blockHeader.Time, config)
- for receiptIndex, receipt := range receipts {
- if receipt.TxHash == hash {
- return receipt, blockHash, *blockNumber, uint64(receiptIndex)
- }
- }
- log.Error("Receipt not found", "number", *blockNumber, "hash", blockHash, "txhash", hash)
- return nil, common.Hash{}, 0, 0
-}
-
-// ReadBloomBits retrieves the compressed bloom bit vector belonging to the given
-// section and bit index from the.
-func ReadBloomBits(db ethdb.KeyValueReader, bit uint, section uint64, head common.Hash) ([]byte, error) {
- return db.Get(bloomBitsKey(bit, section, head))
-}
-
-// WriteBloomBits stores the compressed bloom bits vector belonging to the given
-// section and bit index.
-func WriteBloomBits(db ethdb.KeyValueWriter, bit uint, section uint64, head common.Hash, bits []byte) {
- if err := db.Put(bloomBitsKey(bit, section, head), bits); err != nil {
- log.Crit("Failed to store bloom bits", "err", err)
- }
-}
-
-// DeleteBloombits removes all compressed bloom bits vector belonging to the
-// given section range and bit index.
-func DeleteBloombits(db ethdb.Database, bit uint, from uint64, to uint64) {
- start, end := bloomBitsKey(bit, from, common.Hash{}), bloomBitsKey(bit, to, common.Hash{})
- it := db.NewIterator(nil, start)
- defer it.Release()
-
- for it.Next() {
- if bytes.Compare(it.Key(), end) >= 0 {
- break
- }
- if len(it.Key()) != len(bloomBitsPrefix)+2+8+32 {
- continue
- }
- db.Delete(it.Key())
- }
- if it.Error() != nil {
- log.Crit("Failed to delete bloom bits", "err", it.Error())
- }
-}
diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go
deleted file mode 100644
index 124389ba7a..0000000000
--- a/core/rawdb/accessors_indexes_test.go
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "math/big"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/internal/blocktest"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-var newTestHasher = blocktest.NewHasher
-
-// Tests that positional lookup metadata can be stored and retrieved.
-func TestLookupStorage(t *testing.T) {
- tests := []struct {
- name string
- writeTxLookupEntriesByBlock func(ethdb.Writer, *types.Block)
- }{
- {
- "DatabaseV6",
- func(db ethdb.Writer, block *types.Block) {
- WriteTxLookupEntriesByBlock(db, block)
- },
- },
- {
- "DatabaseV4-V5",
- func(db ethdb.Writer, block *types.Block) {
- for _, tx := range block.Transactions() {
- db.Put(txLookupKey(tx.Hash()), block.Hash().Bytes())
- }
- },
- },
- {
- "DatabaseV3",
- func(db ethdb.Writer, block *types.Block) {
- for index, tx := range block.Transactions() {
- entry := LegacyTxLookupEntry{
- BlockHash: block.Hash(),
- BlockIndex: block.NumberU64(),
- Index: uint64(index),
- }
- data, _ := rlp.EncodeToBytes(entry)
- db.Put(txLookupKey(tx.Hash()), data)
- }
- },
- },
- }
-
- for _, tc := range tests {
- t.Run(tc.name, func(t *testing.T) {
- db := NewMemoryDatabase()
-
- tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
- tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
- tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
- txs := []*types.Transaction{tx1, tx2, tx3}
-
- block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, newTestHasher())
-
- // Check that no transactions entries are in a pristine database
- for i, tx := range txs {
- if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
- t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn)
- }
- }
- // Insert all the transactions into the database, and verify contents
- WriteCanonicalHash(db, block.Hash(), block.NumberU64())
- WriteBlock(db, block)
- tc.writeTxLookupEntriesByBlock(db, block)
-
- for i, tx := range txs {
- if txn, hash, number, index := ReadTransaction(db, tx.Hash()); txn == nil {
- t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
- } else {
- if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
- t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
- }
- if tx.Hash() != txn.Hash() {
- t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
- }
- }
- }
- // Delete the transactions and check purge
- for i, tx := range txs {
- DeleteTxLookupEntry(db, tx.Hash())
- if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
- t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
- }
- }
- })
- }
-}
-
-func TestDeleteBloomBits(t *testing.T) {
- // Prepare testing data
- db := NewMemoryDatabase()
- for i := uint(0); i < 2; i++ {
- for s := uint64(0); s < 2; s++ {
- WriteBloomBits(db, i, s, params.MainnetGenesisHash, []byte{0x01, 0x02})
- WriteBloomBits(db, i, s, params.SepoliaGenesisHash, []byte{0x01, 0x02})
- }
- }
- check := func(bit uint, section uint64, head common.Hash, exist bool) {
- bits, _ := ReadBloomBits(db, bit, section, head)
- if exist && !bytes.Equal(bits, []byte{0x01, 0x02}) {
- t.Fatalf("Bloombits mismatch")
- }
- if !exist && len(bits) > 0 {
- t.Fatalf("Bloombits should be removed")
- }
- }
- // Check the existence of written data.
- check(0, 0, params.MainnetGenesisHash, true)
- check(0, 0, params.SepoliaGenesisHash, true)
-
- // Check the existence of deleted data.
- DeleteBloombits(db, 0, 0, 1)
- check(0, 0, params.MainnetGenesisHash, false)
- check(0, 0, params.SepoliaGenesisHash, false)
- check(0, 1, params.MainnetGenesisHash, true)
- check(0, 1, params.SepoliaGenesisHash, true)
-
- // Check the existence of deleted data.
- DeleteBloombits(db, 0, 0, 2)
- check(0, 0, params.MainnetGenesisHash, false)
- check(0, 0, params.SepoliaGenesisHash, false)
- check(0, 1, params.MainnetGenesisHash, false)
- check(0, 1, params.SepoliaGenesisHash, false)
-
- // Bit1 shouldn't be affect.
- check(1, 0, params.MainnetGenesisHash, true)
- check(1, 0, params.SepoliaGenesisHash, true)
- check(1, 1, params.MainnetGenesisHash, true)
- check(1, 1, params.SepoliaGenesisHash, true)
-}
diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go
deleted file mode 100644
index 859566f722..0000000000
--- a/core/rawdb/accessors_metadata.go
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "encoding/json"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// ReadDatabaseVersion retrieves the version number of the database.
-func ReadDatabaseVersion(db ethdb.KeyValueReader) *uint64 {
- var version uint64
-
- enc, _ := db.Get(databaseVersionKey)
- if len(enc) == 0 {
- return nil
- }
- if err := rlp.DecodeBytes(enc, &version); err != nil {
- return nil
- }
-
- return &version
-}
-
-// WriteDatabaseVersion stores the version number of the database
-func WriteDatabaseVersion(db ethdb.KeyValueWriter, version uint64) {
- enc, err := rlp.EncodeToBytes(version)
- if err != nil {
- log.Crit("Failed to encode database version", "err", err)
- }
- if err = db.Put(databaseVersionKey, enc); err != nil {
- log.Crit("Failed to store the database version", "err", err)
- }
-}
-
-// ReadChainConfig retrieves the consensus settings based on the given genesis hash.
-func ReadChainConfig(db ethdb.KeyValueReader, hash common.Hash) *params.ChainConfig {
- data, _ := db.Get(configKey(hash))
- if len(data) == 0 {
- return nil
- }
- var config params.ChainConfig
- if err := json.Unmarshal(data, &config); err != nil {
- log.Error("Invalid chain config JSON", "hash", hash, "err", err)
- return nil
- }
- return &config
-}
-
-// WriteChainConfig writes the chain config settings to the database.
-func WriteChainConfig(db ethdb.KeyValueWriter, hash common.Hash, cfg *params.ChainConfig) {
- if cfg == nil {
- return
- }
- data, err := json.Marshal(cfg)
- if err != nil {
- log.Crit("Failed to JSON encode chain config", "err", err)
- }
- if err := db.Put(configKey(hash), data); err != nil {
- log.Crit("Failed to store chain config", "err", err)
- }
-}
-
-// ReadGenesisStateSpec retrieves the genesis state specification based on the
-// given genesis (block-)hash.
-func ReadGenesisStateSpec(db ethdb.KeyValueReader, blockhash common.Hash) []byte {
- data, _ := db.Get(genesisStateSpecKey(blockhash))
- return data
-}
-
-// WriteGenesisStateSpec writes the genesis state specification into the disk.
-func WriteGenesisStateSpec(db ethdb.KeyValueWriter, blockhash common.Hash, data []byte) {
- if err := db.Put(genesisStateSpecKey(blockhash), data); err != nil {
- log.Crit("Failed to store genesis state", "err", err)
- }
-}
-
-// crashList is a list of unclean-shutdown-markers, for rlp-encoding to the
-// database
-type crashList struct {
- Discarded uint64 // how many ucs have we deleted
- Recent []uint64 // unix timestamps of 10 latest unclean shutdowns
-}
-
-const crashesToKeep = 10
-
-// PushUncleanShutdownMarker appends a new unclean shutdown marker and returns
-// the previous data
-// - a list of timestamps
-// - a count of how many old unclean-shutdowns have been discarded
-func PushUncleanShutdownMarker(db ethdb.KeyValueStore) ([]uint64, uint64, error) {
- var uncleanShutdowns crashList
- // Read old data
- if data, err := db.Get(uncleanShutdownKey); err == nil {
- if err := rlp.DecodeBytes(data, &uncleanShutdowns); err != nil {
- return nil, 0, err
- }
- }
- var discarded = uncleanShutdowns.Discarded
- var previous = make([]uint64, len(uncleanShutdowns.Recent))
- copy(previous, uncleanShutdowns.Recent)
- // Add a new (but cap it)
- uncleanShutdowns.Recent = append(uncleanShutdowns.Recent, uint64(time.Now().Unix()))
- if count := len(uncleanShutdowns.Recent); count > crashesToKeep+1 {
- numDel := count - (crashesToKeep + 1)
- uncleanShutdowns.Recent = uncleanShutdowns.Recent[numDel:]
- uncleanShutdowns.Discarded += uint64(numDel)
- }
- // And save it again
- data, _ := rlp.EncodeToBytes(uncleanShutdowns)
- if err := db.Put(uncleanShutdownKey, data); err != nil {
- log.Warn("Failed to write unclean-shutdown marker", "err", err)
- return nil, 0, err
- }
- return previous, discarded, nil
-}
-
-// PopUncleanShutdownMarker removes the last unclean shutdown marker
-func PopUncleanShutdownMarker(db ethdb.KeyValueStore) {
- var uncleanShutdowns crashList
- // Read old data
- if data, err := db.Get(uncleanShutdownKey); err != nil {
- log.Warn("Error reading unclean shutdown markers", "error", err)
- } else if err := rlp.DecodeBytes(data, &uncleanShutdowns); err != nil {
- log.Error("Error decoding unclean shutdown markers", "error", err) // Should mos def _not_ happen
- }
- if l := len(uncleanShutdowns.Recent); l > 0 {
- uncleanShutdowns.Recent = uncleanShutdowns.Recent[:l-1]
- }
- data, _ := rlp.EncodeToBytes(uncleanShutdowns)
- if err := db.Put(uncleanShutdownKey, data); err != nil {
- log.Warn("Failed to clear unclean-shutdown marker", "err", err)
- }
-}
-
-// UpdateUncleanShutdownMarker updates the last marker's timestamp to now.
-func UpdateUncleanShutdownMarker(db ethdb.KeyValueStore) {
- var uncleanShutdowns crashList
- // Read old data
- if data, err := db.Get(uncleanShutdownKey); err != nil {
- log.Warn("Error reading unclean shutdown markers", "error", err)
- } else if err := rlp.DecodeBytes(data, &uncleanShutdowns); err != nil {
- log.Warn("Error decoding unclean shutdown markers", "error", err)
- }
- // This shouldn't happen because we push a marker on Backend instantiation
- count := len(uncleanShutdowns.Recent)
- if count == 0 {
- log.Warn("No unclean shutdown marker to update")
- return
- }
- uncleanShutdowns.Recent[count-1] = uint64(time.Now().Unix())
- data, _ := rlp.EncodeToBytes(uncleanShutdowns)
- if err := db.Put(uncleanShutdownKey, data); err != nil {
- log.Warn("Failed to write unclean-shutdown marker", "err", err)
- }
-}
-
-// ReadTransitionStatus retrieves the eth2 transition status from the database
-func ReadTransitionStatus(db ethdb.KeyValueReader) []byte {
- data, _ := db.Get(transitionStatusKey)
- return data
-}
-
-// WriteTransitionStatus stores the eth2 transition status to the database
-func WriteTransitionStatus(db ethdb.KeyValueWriter, data []byte) {
- if err := db.Put(transitionStatusKey, data); err != nil {
- log.Crit("Failed to store the eth2 transition status", "err", err)
- }
-}
diff --git a/core/rawdb/accessors_snapshot.go b/core/rawdb/accessors_snapshot.go
deleted file mode 100644
index 3c82b3f731..0000000000
--- a/core/rawdb/accessors_snapshot.go
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "encoding/binary"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
-)
-
-// ReadSnapshotDisabled retrieves if the snapshot maintenance is disabled.
-func ReadSnapshotDisabled(db ethdb.KeyValueReader) bool {
- disabled, _ := db.Has(snapshotDisabledKey)
- return disabled
-}
-
-// WriteSnapshotDisabled stores the snapshot pause flag.
-func WriteSnapshotDisabled(db ethdb.KeyValueWriter) {
- if err := db.Put(snapshotDisabledKey, []byte("42")); err != nil {
- log.Crit("Failed to store snapshot disabled flag", "err", err)
- }
-}
-
-// DeleteSnapshotDisabled deletes the flag keeping the snapshot maintenance disabled.
-func DeleteSnapshotDisabled(db ethdb.KeyValueWriter) {
- if err := db.Delete(snapshotDisabledKey); err != nil {
- log.Crit("Failed to remove snapshot disabled flag", "err", err)
- }
-}
-
-// ReadSnapshotRoot retrieves the root of the block whose state is contained in
-// the persisted snapshot.
-func ReadSnapshotRoot(db ethdb.KeyValueReader) common.Hash {
- data, _ := db.Get(SnapshotRootKey)
- if len(data) != common.HashLength {
- return common.Hash{}
- }
- return common.BytesToHash(data)
-}
-
-// WriteSnapshotRoot stores the root of the block whose state is contained in
-// the persisted snapshot.
-func WriteSnapshotRoot(db ethdb.KeyValueWriter, root common.Hash) {
- if err := db.Put(SnapshotRootKey, root[:]); err != nil {
- log.Crit("Failed to store snapshot root", "err", err)
- }
-}
-
-// DeleteSnapshotRoot deletes the hash of the block whose state is contained in
-// the persisted snapshot. Since snapshots are not immutable, this method can
-// be used during updates, so a crash or failure will mark the entire snapshot
-// invalid.
-func DeleteSnapshotRoot(db ethdb.KeyValueWriter) {
- if err := db.Delete(SnapshotRootKey); err != nil {
- log.Crit("Failed to remove snapshot root", "err", err)
- }
-}
-
-// ReadAccountSnapshot retrieves the snapshot entry of an account trie leaf.
-func ReadAccountSnapshot(db ethdb.KeyValueReader, hash common.Hash) []byte {
- data, _ := db.Get(accountSnapshotKey(hash))
- return data
-}
-
-// WriteAccountSnapshot stores the snapshot entry of an account trie leaf.
-func WriteAccountSnapshot(db ethdb.KeyValueWriter, hash common.Hash, entry []byte) {
- if err := db.Put(accountSnapshotKey(hash), entry); err != nil {
- log.Crit("Failed to store account snapshot", "err", err)
- }
-}
-
-// DeleteAccountSnapshot removes the snapshot entry of an account trie leaf.
-func DeleteAccountSnapshot(db ethdb.KeyValueWriter, hash common.Hash) {
- if err := db.Delete(accountSnapshotKey(hash)); err != nil {
- log.Crit("Failed to delete account snapshot", "err", err)
- }
-}
-
-// ReadStorageSnapshot retrieves the snapshot entry of an storage trie leaf.
-func ReadStorageSnapshot(db ethdb.KeyValueReader, accountHash, storageHash common.Hash) []byte {
- data, _ := db.Get(storageSnapshotKey(accountHash, storageHash))
- return data
-}
-
-// WriteStorageSnapshot stores the snapshot entry of an storage trie leaf.
-func WriteStorageSnapshot(db ethdb.KeyValueWriter, accountHash, storageHash common.Hash, entry []byte) {
- if err := db.Put(storageSnapshotKey(accountHash, storageHash), entry); err != nil {
- log.Crit("Failed to store storage snapshot", "err", err)
- }
-}
-
-// DeleteStorageSnapshot removes the snapshot entry of an storage trie leaf.
-func DeleteStorageSnapshot(db ethdb.KeyValueWriter, accountHash, storageHash common.Hash) {
- if err := db.Delete(storageSnapshotKey(accountHash, storageHash)); err != nil {
- log.Crit("Failed to delete storage snapshot", "err", err)
- }
-}
-
-// IterateStorageSnapshots returns an iterator for walking the entire storage
-// space of a specific account.
-func IterateStorageSnapshots(db ethdb.Iteratee, accountHash common.Hash) ethdb.Iterator {
- return NewKeyLengthIterator(db.NewIterator(storageSnapshotsKey(accountHash), nil), len(SnapshotStoragePrefix)+2*common.HashLength)
-}
-
-// ReadSnapshotJournal retrieves the serialized in-memory diff layers saved at
-// the last shutdown. The blob is expected to be max a few 10s of megabytes.
-func ReadSnapshotJournal(db ethdb.KeyValueReader) []byte {
- data, _ := db.Get(snapshotJournalKey)
- return data
-}
-
-// WriteSnapshotJournal stores the serialized in-memory diff layers to save at
-// shutdown. The blob is expected to be max a few 10s of megabytes.
-func WriteSnapshotJournal(db ethdb.KeyValueWriter, journal []byte) {
- if err := db.Put(snapshotJournalKey, journal); err != nil {
- log.Crit("Failed to store snapshot journal", "err", err)
- }
-}
-
-// DeleteSnapshotJournal deletes the serialized in-memory diff layers saved at
-// the last shutdown
-func DeleteSnapshotJournal(db ethdb.KeyValueWriter) {
- if err := db.Delete(snapshotJournalKey); err != nil {
- log.Crit("Failed to remove snapshot journal", "err", err)
- }
-}
-
-// ReadSnapshotGenerator retrieves the serialized snapshot generator saved at
-// the last shutdown.
-func ReadSnapshotGenerator(db ethdb.KeyValueReader) []byte {
- data, _ := db.Get(snapshotGeneratorKey)
- return data
-}
-
-// WriteSnapshotGenerator stores the serialized snapshot generator to save at
-// shutdown.
-func WriteSnapshotGenerator(db ethdb.KeyValueWriter, generator []byte) {
- if err := db.Put(snapshotGeneratorKey, generator); err != nil {
- log.Crit("Failed to store snapshot generator", "err", err)
- }
-}
-
-// DeleteSnapshotGenerator deletes the serialized snapshot generator saved at
-// the last shutdown
-func DeleteSnapshotGenerator(db ethdb.KeyValueWriter) {
- if err := db.Delete(snapshotGeneratorKey); err != nil {
- log.Crit("Failed to remove snapshot generator", "err", err)
- }
-}
-
-// ReadSnapshotRecoveryNumber retrieves the block number of the last persisted
-// snapshot layer.
-func ReadSnapshotRecoveryNumber(db ethdb.KeyValueReader) *uint64 {
- data, _ := db.Get(snapshotRecoveryKey)
- if len(data) == 0 {
- return nil
- }
- if len(data) != 8 {
- return nil
- }
- number := binary.BigEndian.Uint64(data)
- return &number
-}
-
-// WriteSnapshotRecoveryNumber stores the block number of the last persisted
-// snapshot layer.
-func WriteSnapshotRecoveryNumber(db ethdb.KeyValueWriter, number uint64) {
- var buf [8]byte
- binary.BigEndian.PutUint64(buf[:], number)
- if err := db.Put(snapshotRecoveryKey, buf[:]); err != nil {
- log.Crit("Failed to store snapshot recovery number", "err", err)
- }
-}
-
-// DeleteSnapshotRecoveryNumber deletes the block number of the last persisted
-// snapshot layer.
-func DeleteSnapshotRecoveryNumber(db ethdb.KeyValueWriter) {
- if err := db.Delete(snapshotRecoveryKey); err != nil {
- log.Crit("Failed to remove snapshot recovery number", "err", err)
- }
-}
-
-// ReadSnapshotSyncStatus retrieves the serialized sync status saved at shutdown.
-func ReadSnapshotSyncStatus(db ethdb.KeyValueReader) []byte {
- data, _ := db.Get(snapshotSyncStatusKey)
- return data
-}
-
-// WriteSnapshotSyncStatus stores the serialized sync status to save at shutdown.
-func WriteSnapshotSyncStatus(db ethdb.KeyValueWriter, status []byte) {
- if err := db.Put(snapshotSyncStatusKey, status); err != nil {
- log.Crit("Failed to store snapshot sync status", "err", err)
- }
-}
diff --git a/core/rawdb/accessors_state.go b/core/rawdb/accessors_state.go
deleted file mode 100644
index 9ce58e7d27..0000000000
--- a/core/rawdb/accessors_state.go
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "encoding/binary"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
-)
-
-// ReadPreimage retrieves a single preimage of the provided hash.
-func ReadPreimage(db ethdb.KeyValueReader, hash common.Hash) []byte {
- data, _ := db.Get(preimageKey(hash))
- return data
-}
-
-// WritePreimages writes the provided set of preimages to the database.
-func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) {
- for hash, preimage := range preimages {
- if err := db.Put(preimageKey(hash), preimage); err != nil {
- log.Crit("Failed to store trie preimage", "err", err)
- }
- }
- preimageCounter.Inc(int64(len(preimages)))
- preimageHitCounter.Inc(int64(len(preimages)))
-}
-
-// ReadCode retrieves the contract code of the provided code hash.
-func ReadCode(db ethdb.KeyValueReader, hash common.Hash) []byte {
- // Try with the prefixed code scheme first, if not then try with legacy
- // scheme.
- data := ReadCodeWithPrefix(db, hash)
- if len(data) != 0 {
- return data
- }
- data, _ = db.Get(hash.Bytes())
- return data
-}
-
-// ReadCodeWithPrefix retrieves the contract code of the provided code hash.
-// The main difference between this function and ReadCode is this function
-// will only check the existence with latest scheme(with prefix).
-func ReadCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) []byte {
- data, _ := db.Get(codeKey(hash))
- return data
-}
-
-// HasCode checks if the contract code corresponding to the
-// provided code hash is present in the db.
-func HasCode(db ethdb.KeyValueReader, hash common.Hash) bool {
- // Try with the prefixed code scheme first, if not then try with legacy
- // scheme.
- if ok := HasCodeWithPrefix(db, hash); ok {
- return true
- }
- ok, _ := db.Has(hash.Bytes())
- return ok
-}
-
-// HasCodeWithPrefix checks if the contract code corresponding to the
-// provided code hash is present in the db. This function will only check
-// presence using the prefix-scheme.
-func HasCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) bool {
- ok, _ := db.Has(codeKey(hash))
- return ok
-}
-
-// WriteCode writes the provided contract code database.
-func WriteCode(db ethdb.KeyValueWriter, hash common.Hash, code []byte) {
- if err := db.Put(codeKey(hash), code); err != nil {
- log.Crit("Failed to store contract code", "err", err)
- }
-}
-
-// DeleteCode deletes the specified contract code from the database.
-func DeleteCode(db ethdb.KeyValueWriter, hash common.Hash) {
- if err := db.Delete(codeKey(hash)); err != nil {
- log.Crit("Failed to delete contract code", "err", err)
- }
-}
-
-// ReadStateID retrieves the state id with the provided state root.
-func ReadStateID(db ethdb.KeyValueReader, root common.Hash) *uint64 {
- data, err := db.Get(stateIDKey(root))
- if err != nil || len(data) == 0 {
- return nil
- }
- number := binary.BigEndian.Uint64(data)
- return &number
-}
-
-// WriteStateID writes the provided state lookup to database.
-func WriteStateID(db ethdb.KeyValueWriter, root common.Hash, id uint64) {
- var buff [8]byte
- binary.BigEndian.PutUint64(buff[:], id)
- if err := db.Put(stateIDKey(root), buff[:]); err != nil {
- log.Crit("Failed to store state ID", "err", err)
- }
-}
-
-// DeleteStateID deletes the specified state lookup from the database.
-func DeleteStateID(db ethdb.KeyValueWriter, root common.Hash) {
- if err := db.Delete(stateIDKey(root)); err != nil {
- log.Crit("Failed to delete state ID", "err", err)
- }
-}
-
-// ReadPersistentStateID retrieves the id of the persistent state from the database.
-func ReadPersistentStateID(db ethdb.KeyValueReader) uint64 {
- data, _ := db.Get(persistentStateIDKey)
- if len(data) != 8 {
- return 0
- }
- return binary.BigEndian.Uint64(data)
-}
-
-// WritePersistentStateID stores the id of the persistent state into database.
-func WritePersistentStateID(db ethdb.KeyValueWriter, number uint64) {
- if err := db.Put(persistentStateIDKey, encodeBlockNumber(number)); err != nil {
- log.Crit("Failed to store the persistent state ID", "err", err)
- }
-}
-
-// ReadTrieJournal retrieves the serialized in-memory trie nodes of layers saved at
-// the last shutdown.
-func ReadTrieJournal(db ethdb.KeyValueReader) []byte {
- data, _ := db.Get(trieJournalKey)
- return data
-}
-
-// WriteTrieJournal stores the serialized in-memory trie nodes of layers to save at
-// shutdown.
-func WriteTrieJournal(db ethdb.KeyValueWriter, journal []byte) {
- if err := db.Put(trieJournalKey, journal); err != nil {
- log.Crit("Failed to store tries journal", "err", err)
- }
-}
-
-// DeleteTrieJournal deletes the serialized in-memory trie nodes of layers saved at
-// the last shutdown.
-func DeleteTrieJournal(db ethdb.KeyValueWriter) {
- if err := db.Delete(trieJournalKey); err != nil {
- log.Crit("Failed to remove tries journal", "err", err)
- }
-}
-
-// ReadStateHistoryMeta retrieves the metadata corresponding to the specified
-// state history. Compute the position of state history in freezer by minus
-// one since the id of first state history starts from one(zero for initial
-// state).
-func ReadStateHistoryMeta(db ethdb.AncientReaderOp, id uint64) []byte {
- blob, err := db.Ancient(stateHistoryMeta, id-1)
- if err != nil {
- return nil
- }
- return blob
-}
-
-// ReadStateHistoryMetaList retrieves a batch of meta objects with the specified
-// start position and count. Compute the position of state history in freezer by
-// minus one since the id of first state history starts from one(zero for initial
-// state).
-func ReadStateHistoryMetaList(db ethdb.AncientReaderOp, start uint64, count uint64) ([][]byte, error) {
- return db.AncientRange(stateHistoryMeta, start-1, count, 0)
-}
-
-// ReadStateAccountIndex retrieves the state root corresponding to the specified
-// state history. Compute the position of state history in freezer by minus one
-// since the id of first state history starts from one(zero for initial state).
-func ReadStateAccountIndex(db ethdb.AncientReaderOp, id uint64) []byte {
- blob, err := db.Ancient(stateHistoryAccountIndex, id-1)
- if err != nil {
- return nil
- }
- return blob
-}
-
-// ReadStateStorageIndex retrieves the state root corresponding to the specified
-// state history. Compute the position of state history in freezer by minus one
-// since the id of first state history starts from one(zero for initial state).
-func ReadStateStorageIndex(db ethdb.AncientReaderOp, id uint64) []byte {
- blob, err := db.Ancient(stateHistoryStorageIndex, id-1)
- if err != nil {
- return nil
- }
- return blob
-}
-
-// ReadStateAccountHistory retrieves the state root corresponding to the specified
-// state history. Compute the position of state history in freezer by minus one
-// since the id of first state history starts from one(zero for initial state).
-func ReadStateAccountHistory(db ethdb.AncientReaderOp, id uint64) []byte {
- blob, err := db.Ancient(stateHistoryAccountData, id-1)
- if err != nil {
- return nil
- }
- return blob
-}
-
-// ReadStateStorageHistory retrieves the state root corresponding to the specified
-// state history. Compute the position of state history in freezer by minus one
-// since the id of first state history starts from one(zero for initial state).
-func ReadStateStorageHistory(db ethdb.AncientReaderOp, id uint64) []byte {
- blob, err := db.Ancient(stateHistoryStorageData, id-1)
- if err != nil {
- return nil
- }
- return blob
-}
-
-// ReadStateHistory retrieves the state history from database with provided id.
-// Compute the position of state history in freezer by minus one since the id
-// of first state history starts from one(zero for initial state).
-func ReadStateHistory(db ethdb.AncientReaderOp, id uint64) ([]byte, []byte, []byte, []byte, []byte, error) {
- meta, err := db.Ancient(stateHistoryMeta, id-1)
- if err != nil {
- return nil, nil, nil, nil, nil, err
- }
- accountIndex, err := db.Ancient(stateHistoryAccountIndex, id-1)
- if err != nil {
- return nil, nil, nil, nil, nil, err
- }
- storageIndex, err := db.Ancient(stateHistoryStorageIndex, id-1)
- if err != nil {
- return nil, nil, nil, nil, nil, err
- }
- accountData, err := db.Ancient(stateHistoryAccountData, id-1)
- if err != nil {
- return nil, nil, nil, nil, nil, err
- }
- storageData, err := db.Ancient(stateHistoryStorageData, id-1)
- if err != nil {
- return nil, nil, nil, nil, nil, err
- }
- return meta, accountIndex, storageIndex, accountData, storageData, nil
-}
-
-// WriteStateHistory writes the provided state history to database. Compute the
-// position of state history in freezer by minus one since the id of first state
-// history starts from one(zero for initial state).
-func WriteStateHistory(db ethdb.AncientWriter, id uint64, meta []byte, accountIndex []byte, storageIndex []byte, accounts []byte, storages []byte) {
- db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- op.AppendRaw(stateHistoryMeta, id-1, meta)
- op.AppendRaw(stateHistoryAccountIndex, id-1, accountIndex)
- op.AppendRaw(stateHistoryStorageIndex, id-1, storageIndex)
- op.AppendRaw(stateHistoryAccountData, id-1, accounts)
- op.AppendRaw(stateHistoryStorageData, id-1, storages)
- return nil
- })
-}
diff --git a/core/rawdb/accessors_sync.go b/core/rawdb/accessors_sync.go
deleted file mode 100644
index 2dc08b3b72..0000000000
--- a/core/rawdb/accessors_sync.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// ReadSkeletonSyncStatus retrieves the serialized sync status saved at shutdown.
-func ReadSkeletonSyncStatus(db ethdb.KeyValueReader) []byte {
- data, _ := db.Get(skeletonSyncStatusKey)
- return data
-}
-
-// WriteSkeletonSyncStatus stores the serialized sync status to save at shutdown.
-func WriteSkeletonSyncStatus(db ethdb.KeyValueWriter, status []byte) {
- if err := db.Put(skeletonSyncStatusKey, status); err != nil {
- log.Crit("Failed to store skeleton sync status", "err", err)
- }
-}
-
-// DeleteSkeletonSyncStatus deletes the serialized sync status saved at the last
-// shutdown
-func DeleteSkeletonSyncStatus(db ethdb.KeyValueWriter) {
- if err := db.Delete(skeletonSyncStatusKey); err != nil {
- log.Crit("Failed to remove skeleton sync status", "err", err)
- }
-}
-
-// ReadSkeletonHeader retrieves a block header from the skeleton sync store,
-func ReadSkeletonHeader(db ethdb.KeyValueReader, number uint64) *types.Header {
- data, _ := db.Get(skeletonHeaderKey(number))
- if len(data) == 0 {
- return nil
- }
- header := new(types.Header)
- if err := rlp.DecodeBytes(data, header); err != nil {
- log.Error("Invalid skeleton header RLP", "number", number, "err", err)
- return nil
- }
- return header
-}
-
-// WriteSkeletonHeader stores a block header into the skeleton sync store.
-func WriteSkeletonHeader(db ethdb.KeyValueWriter, header *types.Header) {
- data, err := rlp.EncodeToBytes(header)
- if err != nil {
- log.Crit("Failed to RLP encode header", "err", err)
- }
- key := skeletonHeaderKey(header.Number.Uint64())
- if err := db.Put(key, data); err != nil {
- log.Crit("Failed to store skeleton header", "err", err)
- }
-}
-
-// DeleteSkeletonHeader removes all block header data associated with a hash.
-func DeleteSkeletonHeader(db ethdb.KeyValueWriter, number uint64) {
- if err := db.Delete(skeletonHeaderKey(number)); err != nil {
- log.Crit("Failed to delete skeleton header", "err", err)
- }
-}
-
-const (
- StateSyncUnknown = uint8(0) // flags the state snap sync is unknown
- StateSyncRunning = uint8(1) // flags the state snap sync is not completed yet
- StateSyncFinished = uint8(2) // flags the state snap sync is completed
-)
-
-// ReadSnapSyncStatusFlag retrieves the state snap sync status flag.
-func ReadSnapSyncStatusFlag(db ethdb.KeyValueReader) uint8 {
- blob, err := db.Get(snapSyncStatusFlagKey)
- if err != nil || len(blob) != 1 {
- return StateSyncUnknown
- }
- return blob[0]
-}
-
-// WriteSnapSyncStatusFlag stores the state snap sync status flag into database.
-func WriteSnapSyncStatusFlag(db ethdb.KeyValueWriter, flag uint8) {
- if err := db.Put(snapSyncStatusFlagKey, []byte{flag}); err != nil {
- log.Crit("Failed to store sync status flag", "err", err)
- }
-}
diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go
deleted file mode 100644
index ea3367db36..0000000000
--- a/core/rawdb/accessors_trie.go
+++ /dev/null
@@ -1,347 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see
-
-package rawdb
-
-import (
- "fmt"
- "sync"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "golang.org/x/crypto/sha3"
-)
-
-// HashScheme is the legacy hash-based state scheme with which trie nodes are
-// stored in the disk with node hash as the database key. The advantage of this
-// scheme is that different versions of trie nodes can be stored in disk, which
-// is very beneficial for constructing archive nodes. The drawback is it will
-// store different trie nodes on the same path to different locations on the disk
-// with no data locality, and it's unfriendly for designing state pruning.
-//
-// Now this scheme is still kept for backward compatibility, and it will be used
-// for archive node and some other tries(e.g. light trie).
-const HashScheme = "hash"
-
-// PathScheme is the new path-based state scheme with which trie nodes are stored
-// in the disk with node path as the database key. This scheme will only store one
-// version of state data in the disk, which means that the state pruning operation
-// is native. At the same time, this scheme will put adjacent trie nodes in the same
-// area of the disk with good data locality property. But this scheme needs to rely
-// on extra state diffs to survive deep reorg.
-const PathScheme = "path"
-
-// hasher is used to compute the sha256 hash of the provided data.
-type hasher struct{ sha crypto.KeccakState }
-
-var hasherPool = sync.Pool{
- New: func() interface{} { return &hasher{sha: sha3.NewLegacyKeccak256().(crypto.KeccakState)} },
-}
-
-func newHasher() *hasher {
- return hasherPool.Get().(*hasher)
-}
-
-func (h *hasher) hash(data []byte) common.Hash {
- return crypto.HashData(h.sha, data)
-}
-
-func (h *hasher) release() {
- hasherPool.Put(h)
-}
-
-// ReadAccountTrieNode retrieves the account trie node and the associated node
-// hash with the specified node path.
-func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) ([]byte, common.Hash) {
- data, err := db.Get(accountTrieNodeKey(path))
- if err != nil {
- return nil, common.Hash{}
- }
- h := newHasher()
- defer h.release()
- return data, h.hash(data)
-}
-
-// HasAccountTrieNode checks the account trie node presence with the specified
-// node path and the associated node hash.
-func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte, hash common.Hash) bool {
- data, err := db.Get(accountTrieNodeKey(path))
- if err != nil {
- return false
- }
- h := newHasher()
- defer h.release()
- return h.hash(data) == hash
-}
-
-// ExistsAccountTrieNode checks the presence of the account trie node with the
-// specified node path, regardless of the node hash.
-func ExistsAccountTrieNode(db ethdb.KeyValueReader, path []byte) bool {
- has, err := db.Has(accountTrieNodeKey(path))
- if err != nil {
- return false
- }
- return has
-}
-
-// WriteAccountTrieNode writes the provided account trie node into database.
-func WriteAccountTrieNode(db ethdb.KeyValueWriter, path []byte, node []byte) {
- if err := db.Put(accountTrieNodeKey(path), node); err != nil {
- log.Crit("Failed to store account trie node", "err", err)
- }
-}
-
-// DeleteAccountTrieNode deletes the specified account trie node from the database.
-func DeleteAccountTrieNode(db ethdb.KeyValueWriter, path []byte) {
- if err := db.Delete(accountTrieNodeKey(path)); err != nil {
- log.Crit("Failed to delete account trie node", "err", err)
- }
-}
-
-// ReadStorageTrieNode retrieves the storage trie node and the associated node
-// hash with the specified node path.
-func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) ([]byte, common.Hash) {
- data, err := db.Get(storageTrieNodeKey(accountHash, path))
- if err != nil {
- return nil, common.Hash{}
- }
- h := newHasher()
- defer h.release()
- return data, h.hash(data)
-}
-
-// HasStorageTrieNode checks the storage trie node presence with the provided
-// node path and the associated node hash.
-func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte, hash common.Hash) bool {
- data, err := db.Get(storageTrieNodeKey(accountHash, path))
- if err != nil {
- return false
- }
- h := newHasher()
- defer h.release()
- return h.hash(data) == hash
-}
-
-// ExistsStorageTrieNode checks the presence of the storage trie node with the
-// specified account hash and node path, regardless of the node hash.
-func ExistsStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) bool {
- has, err := db.Has(storageTrieNodeKey(accountHash, path))
- if err != nil {
- return false
- }
- return has
-}
-
-// WriteStorageTrieNode writes the provided storage trie node into database.
-func WriteStorageTrieNode(db ethdb.KeyValueWriter, accountHash common.Hash, path []byte, node []byte) {
- if err := db.Put(storageTrieNodeKey(accountHash, path), node); err != nil {
- log.Crit("Failed to store storage trie node", "err", err)
- }
-}
-
-// DeleteStorageTrieNode deletes the specified storage trie node from the database.
-func DeleteStorageTrieNode(db ethdb.KeyValueWriter, accountHash common.Hash, path []byte) {
- if err := db.Delete(storageTrieNodeKey(accountHash, path)); err != nil {
- log.Crit("Failed to delete storage trie node", "err", err)
- }
-}
-
-// ReadLegacyTrieNode retrieves the legacy trie node with the given
-// associated node hash.
-func ReadLegacyTrieNode(db ethdb.KeyValueReader, hash common.Hash) []byte {
- data, err := db.Get(hash.Bytes())
- if err != nil {
- return nil
- }
- return data
-}
-
-// HasLegacyTrieNode checks if the trie node with the provided hash is present in db.
-func HasLegacyTrieNode(db ethdb.KeyValueReader, hash common.Hash) bool {
- ok, _ := db.Has(hash.Bytes())
- return ok
-}
-
-// WriteLegacyTrieNode writes the provided legacy trie node to database.
-func WriteLegacyTrieNode(db ethdb.KeyValueWriter, hash common.Hash, node []byte) {
- if err := db.Put(hash.Bytes(), node); err != nil {
- log.Crit("Failed to store legacy trie node", "err", err)
- }
-}
-
-// DeleteLegacyTrieNode deletes the specified legacy trie node from database.
-func DeleteLegacyTrieNode(db ethdb.KeyValueWriter, hash common.Hash) {
- if err := db.Delete(hash.Bytes()); err != nil {
- log.Crit("Failed to delete legacy trie node", "err", err)
- }
-}
-
-// HasTrieNode checks the trie node presence with the provided node info and
-// the associated node hash.
-func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash, scheme string) bool {
- switch scheme {
- case HashScheme:
- return HasLegacyTrieNode(db, hash)
- case PathScheme:
- if owner == (common.Hash{}) {
- return HasAccountTrieNode(db, path, hash)
- }
- return HasStorageTrieNode(db, owner, path, hash)
- default:
- panic(fmt.Sprintf("Unknown scheme %v", scheme))
- }
-}
-
-// ReadTrieNode retrieves the trie node from database with the provided node info
-// and associated node hash.
-// hashScheme-based lookup requires the following:
-// - hash
-//
-// pathScheme-based lookup requires the following:
-// - owner
-// - path
-func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash, scheme string) []byte {
- switch scheme {
- case HashScheme:
- return ReadLegacyTrieNode(db, hash)
- case PathScheme:
- var (
- blob []byte
- nHash common.Hash
- )
- if owner == (common.Hash{}) {
- blob, nHash = ReadAccountTrieNode(db, path)
- } else {
- blob, nHash = ReadStorageTrieNode(db, owner, path)
- }
- if nHash != hash {
- return nil
- }
- return blob
- default:
- panic(fmt.Sprintf("Unknown scheme %v", scheme))
- }
-}
-
-// WriteTrieNode writes the trie node into database with the provided node info
-// and associated node hash.
-// hashScheme-based lookup requires the following:
-// - hash
-//
-// pathScheme-based lookup requires the following:
-// - owner
-// - path
-func WriteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, node []byte, scheme string) {
- switch scheme {
- case HashScheme:
- WriteLegacyTrieNode(db, hash, node)
- case PathScheme:
- if owner == (common.Hash{}) {
- WriteAccountTrieNode(db, path, node)
- } else {
- WriteStorageTrieNode(db, owner, path, node)
- }
- default:
- panic(fmt.Sprintf("Unknown scheme %v", scheme))
- }
-}
-
-// DeleteTrieNode deletes the trie node from database with the provided node info
-// and associated node hash.
-// hashScheme-based lookup requires the following:
-// - hash
-//
-// pathScheme-based lookup requires the following:
-// - owner
-// - path
-func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, scheme string) {
- switch scheme {
- case HashScheme:
- DeleteLegacyTrieNode(db, hash)
- case PathScheme:
- if owner == (common.Hash{}) {
- DeleteAccountTrieNode(db, path)
- } else {
- DeleteStorageTrieNode(db, owner, path)
- }
- default:
- panic(fmt.Sprintf("Unknown scheme %v", scheme))
- }
-}
-
-// ReadStateScheme reads the state scheme of persistent state, or none
-// if the state is not present in database.
-func ReadStateScheme(db ethdb.Reader) string {
- // Check if state in path-based scheme is present
- blob, _ := ReadAccountTrieNode(db, nil)
- if len(blob) != 0 {
- return PathScheme
- }
- // The root node might be deleted during the initial snap sync, check
- // the persistent state id then.
- if id := ReadPersistentStateID(db); id != 0 {
- return PathScheme
- }
- // In a hash-based scheme, the genesis state is consistently stored
- // on the disk. To assess the scheme of the persistent state, it
- // suffices to inspect the scheme of the genesis state.
- header := ReadHeader(db, ReadCanonicalHash(db, 0), 0)
- if header == nil {
- return "" // empty datadir
- }
- blob = ReadLegacyTrieNode(db, header.Root)
- if len(blob) == 0 {
- return "" // no state in disk
- }
- return HashScheme
-}
-
-// ParseStateScheme checks if the specified state scheme is compatible with
-// the stored state.
-//
-// - If the provided scheme is none, use the scheme consistent with persistent
-// state, or fallback to hash-based scheme if state is empty.
-//
-// - If the provided scheme is hash, use hash-based scheme or error out if not
-// compatible with persistent state scheme.
-//
-// - If the provided scheme is path: use path-based scheme or error out if not
-// compatible with persistent state scheme.
-func ParseStateScheme(provided string, disk ethdb.Database) (string, error) {
- // If state scheme is not specified, use the scheme consistent
- // with persistent state, or fallback to hash mode if database
- // is empty.
- stored := ReadStateScheme(disk)
- if provided == "" {
- if stored == "" {
- // use default scheme for empty database, flip it when
- // path mode is chosen as default
- log.Info("State schema set to default", "scheme", "hash")
- return HashScheme, nil
- }
- log.Info("State scheme set to already existing", "scheme", stored)
- return stored, nil // reuse scheme of persistent scheme
- }
- // If state scheme is specified, ensure it's compatible with
- // persistent state.
- if stored == "" || provided == stored {
- log.Info("State scheme set by user", "scheme", provided)
- return provided, nil
- }
- return "", fmt.Errorf("incompatible state scheme, stored: %s, provided: %s", stored, provided)
-}
diff --git a/core/rawdb/ancient_scheme.go b/core/rawdb/ancient_scheme.go
deleted file mode 100644
index e88867af0e..0000000000
--- a/core/rawdb/ancient_scheme.go
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import "path/filepath"
-
-// The list of table names of chain freezer.
-const (
- // ChainFreezerHeaderTable indicates the name of the freezer header table.
- ChainFreezerHeaderTable = "headers"
-
- // ChainFreezerHashTable indicates the name of the freezer canonical hash table.
- ChainFreezerHashTable = "hashes"
-
- // ChainFreezerBodiesTable indicates the name of the freezer block body table.
- ChainFreezerBodiesTable = "bodies"
-
- // ChainFreezerReceiptTable indicates the name of the freezer receipts table.
- ChainFreezerReceiptTable = "receipts"
-
- // ChainFreezerDifficultyTable indicates the name of the freezer total difficulty table.
- ChainFreezerDifficultyTable = "diffs"
-)
-
-// chainFreezerNoSnappy configures whether compression is disabled for the ancient-tables.
-// Hashes and difficulties don't compress well.
-var chainFreezerNoSnappy = map[string]bool{
- ChainFreezerHeaderTable: false,
- ChainFreezerHashTable: true,
- ChainFreezerBodiesTable: false,
- ChainFreezerReceiptTable: false,
- ChainFreezerDifficultyTable: true,
-}
-
-const (
- // stateHistoryTableSize defines the maximum size of freezer data files.
- stateHistoryTableSize = 2 * 1000 * 1000 * 1000
-
- // stateHistoryAccountIndex indicates the name of the freezer state history table.
- stateHistoryMeta = "history.meta"
- stateHistoryAccountIndex = "account.index"
- stateHistoryStorageIndex = "storage.index"
- stateHistoryAccountData = "account.data"
- stateHistoryStorageData = "storage.data"
-)
-
-var stateFreezerNoSnappy = map[string]bool{
- stateHistoryMeta: true,
- stateHistoryAccountIndex: false,
- stateHistoryStorageIndex: false,
- stateHistoryAccountData: false,
- stateHistoryStorageData: false,
-}
-
-// The list of identifiers of ancient stores.
-var (
- ChainFreezerName = "chain" // the folder name of chain segment ancient store.
- StateFreezerName = "state" // the folder name of reverse diff ancient store.
-)
-
-// freezers the collections of all builtin freezers.
-var freezers = []string{ChainFreezerName, StateFreezerName}
-
-// NewStateFreezer initializes the freezer for state history.
-func NewStateFreezer(ancientDir string, readOnly bool) (*ResettableFreezer, error) {
- return NewResettableFreezer(filepath.Join(ancientDir, StateFreezerName), "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
-}
diff --git a/core/rawdb/ancient_utils.go b/core/rawdb/ancient_utils.go
deleted file mode 100644
index 428cda544b..0000000000
--- a/core/rawdb/ancient_utils.go
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "fmt"
- "path/filepath"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
-)
-
-type tableSize struct {
- name string
- size common.StorageSize
-}
-
-// freezerInfo contains the basic information of the freezer.
-type freezerInfo struct {
- name string // The identifier of freezer
- head uint64 // The number of last stored item in the freezer
- tail uint64 // The number of first stored item in the freezer
- sizes []tableSize // The storage size per table
-}
-
-// count returns the number of stored items in the freezer.
-func (info *freezerInfo) count() uint64 {
- return info.head - info.tail + 1
-}
-
-// size returns the storage size of the entire freezer.
-func (info *freezerInfo) size() common.StorageSize {
- var total common.StorageSize
- for _, table := range info.sizes {
- total += table.size
- }
- return total
-}
-
-func inspect(name string, order map[string]bool, reader ethdb.AncientReader) (freezerInfo, error) {
- info := freezerInfo{name: name}
- for t := range order {
- size, err := reader.AncientSize(t)
- if err != nil {
- return freezerInfo{}, err
- }
- info.sizes = append(info.sizes, tableSize{name: t, size: common.StorageSize(size)})
- }
- // Retrieve the number of last stored item
- ancients, err := reader.Ancients()
- if err != nil {
- return freezerInfo{}, err
- }
- info.head = ancients - 1
-
- // Retrieve the number of first stored item
- tail, err := reader.Tail()
- if err != nil {
- return freezerInfo{}, err
- }
- info.tail = tail
- return info, nil
-}
-
-// inspectFreezers inspects all freezers registered in the system.
-func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) {
- var infos []freezerInfo
- for _, freezer := range freezers {
- switch freezer {
- case ChainFreezerName:
- info, err := inspect(ChainFreezerName, chainFreezerNoSnappy, db)
- if err != nil {
- return nil, err
- }
- infos = append(infos, info)
-
- case StateFreezerName:
- if ReadStateScheme(db) != PathScheme {
- continue
- }
- datadir, err := db.AncientDatadir()
- if err != nil {
- return nil, err
- }
- f, err := NewStateFreezer(datadir, true)
- if err != nil {
- return nil, err
- }
- defer f.Close()
-
- info, err := inspect(StateFreezerName, stateFreezerNoSnappy, f)
- if err != nil {
- return nil, err
- }
- infos = append(infos, info)
-
- default:
- return nil, fmt.Errorf("unknown freezer, supported ones: %v", freezers)
- }
- }
- return infos, nil
-}
-
-// InspectFreezerTable dumps out the index of a specific freezer table. The passed
-// ancient indicates the path of root ancient directory where the chain freezer can
-// be opened. Start and end specify the range for dumping out indexes.
-// Note this function can only be used for debugging purposes.
-func InspectFreezerTable(ancient string, freezerName string, tableName string, start, end int64) error {
- var (
- path string
- tables map[string]bool
- )
- switch freezerName {
- case ChainFreezerName:
- path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
- case StateFreezerName:
- path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
- default:
- return fmt.Errorf("unknown freezer, supported ones: %v", freezers)
- }
- noSnappy, exist := tables[tableName]
- if !exist {
- var names []string
- for name := range tables {
- names = append(names, name)
- }
- return fmt.Errorf("unknown table, supported ones: %v", names)
- }
- table, err := newFreezerTable(path, tableName, noSnappy, true)
- if err != nil {
- return err
- }
- table.dumpIndexStdout(start, end)
- return nil
-}
diff --git a/core/rawdb/chain_freezer.go b/core/rawdb/chain_freezer.go
deleted file mode 100644
index bb2c409dbb..0000000000
--- a/core/rawdb/chain_freezer.go
+++ /dev/null
@@ -1,303 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "fmt"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
-)
-
-const (
- // freezerRecheckInterval is the frequency to check the key-value database for
- // chain progression that might permit new blocks to be frozen into immutable
- // storage.
- freezerRecheckInterval = time.Minute
-
- // freezerBatchLimit is the maximum number of blocks to freeze in one batch
- // before doing an fsync and deleting it from the key-value store.
- freezerBatchLimit = 30000
-)
-
-// chainFreezer is a wrapper of freezer with additional chain freezing feature.
-// The background thread will keep moving ancient chain segments from key-value
-// database to flat files for saving space on live database.
-type chainFreezer struct {
- threshold atomic.Uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests)
-
- *Freezer
- quit chan struct{}
- wg sync.WaitGroup
- trigger chan chan struct{} // Manual blocking freeze trigger, test determinism
-}
-
-// newChainFreezer initializes the freezer for ancient chain data.
-func newChainFreezer(datadir string, namespace string, readonly bool) (*chainFreezer, error) {
- freezer, err := NewChainFreezer(datadir, namespace, readonly)
- if err != nil {
- return nil, err
- }
- cf := chainFreezer{
- Freezer: freezer,
- quit: make(chan struct{}),
- trigger: make(chan chan struct{}),
- }
- cf.threshold.Store(params.FullImmutabilityThreshold)
- return &cf, nil
-}
-
-// Close closes the chain freezer instance and terminates the background thread.
-func (f *chainFreezer) Close() error {
- select {
- case <-f.quit:
- default:
- close(f.quit)
- }
- f.wg.Wait()
- return f.Freezer.Close()
-}
-
-// freeze is a background thread that periodically checks the blockchain for any
-// import progress and moves ancient data from the fast database into the freezer.
-//
-// This functionality is deliberately broken off from block importing to avoid
-// incurring additional data shuffling delays on block propagation.
-func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
- var (
- backoff bool
- triggered chan struct{} // Used in tests
- nfdb = &nofreezedb{KeyValueStore: db}
- )
- timer := time.NewTimer(freezerRecheckInterval)
- defer timer.Stop()
-
- for {
- select {
- case <-f.quit:
- log.Info("Freezer shutting down")
- return
- default:
- }
- if backoff {
- // If we were doing a manual trigger, notify it
- if triggered != nil {
- triggered <- struct{}{}
- triggered = nil
- }
- select {
- case <-timer.C:
- backoff = false
- timer.Reset(freezerRecheckInterval)
- case triggered = <-f.trigger:
- backoff = false
- case <-f.quit:
- return
- }
- }
- // Retrieve the freezing threshold.
- hash := ReadHeadBlockHash(nfdb)
- if hash == (common.Hash{}) {
- log.Debug("Current full block hash unavailable") // new chain, empty database
- backoff = true
- continue
- }
- number := ReadHeaderNumber(nfdb, hash)
- threshold := f.threshold.Load()
- frozen := f.frozen.Load()
- switch {
- case number == nil:
- log.Error("Current full block number unavailable", "hash", hash)
- backoff = true
- continue
-
- case *number < threshold:
- log.Debug("Current full block not old enough to freeze", "number", *number, "hash", hash, "delay", threshold)
- backoff = true
- continue
-
- case *number-threshold <= frozen:
- log.Debug("Ancient blocks frozen already", "number", *number, "hash", hash, "frozen", frozen)
- backoff = true
- continue
- }
- head := ReadHeader(nfdb, hash, *number)
- if head == nil {
- log.Error("Current full block unavailable", "number", *number, "hash", hash)
- backoff = true
- continue
- }
-
- // Seems we have data ready to be frozen, process in usable batches
- var (
- start = time.Now()
- first, _ = f.Ancients()
- limit = *number - threshold
- )
- if limit-first > freezerBatchLimit {
- limit = first + freezerBatchLimit
- }
- ancients, err := f.freezeRange(nfdb, first, limit)
- if err != nil {
- log.Error("Error in block freeze operation", "err", err)
- backoff = true
- continue
- }
-
- // Batch of blocks have been frozen, flush them before wiping from leveldb
- if err := f.Sync(); err != nil {
- log.Crit("Failed to flush frozen tables", "err", err)
- }
-
- // Wipe out all data from the active database
- batch := db.NewBatch()
- for i := 0; i < len(ancients); i++ {
- // Always keep the genesis block in active database
- if first+uint64(i) != 0 {
- DeleteBlockWithoutNumber(batch, ancients[i], first+uint64(i))
- DeleteCanonicalHash(batch, first+uint64(i))
- }
- }
- if err := batch.Write(); err != nil {
- log.Crit("Failed to delete frozen canonical blocks", "err", err)
- }
- batch.Reset()
-
- // Wipe out side chains also and track dangling side chains
- var dangling []common.Hash
- frozen = f.frozen.Load() // Needs reload after during freezeRange
- for number := first; number < frozen; number++ {
- // Always keep the genesis block in active database
- if number != 0 {
- dangling = ReadAllHashes(db, number)
- for _, hash := range dangling {
- log.Trace("Deleting side chain", "number", number, "hash", hash)
- DeleteBlock(batch, hash, number)
- }
- }
- }
- if err := batch.Write(); err != nil {
- log.Crit("Failed to delete frozen side blocks", "err", err)
- }
- batch.Reset()
-
- // Step into the future and delete any dangling side chains
- if frozen > 0 {
- tip := frozen
- for len(dangling) > 0 {
- drop := make(map[common.Hash]struct{})
- for _, hash := range dangling {
- log.Debug("Dangling parent from Freezer", "number", tip-1, "hash", hash)
- drop[hash] = struct{}{}
- }
- children := ReadAllHashes(db, tip)
- for i := 0; i < len(children); i++ {
- // Dig up the child and ensure it's dangling
- child := ReadHeader(nfdb, children[i], tip)
- if child == nil {
- log.Error("Missing dangling header", "number", tip, "hash", children[i])
- continue
- }
- if _, ok := drop[child.ParentHash]; !ok {
- children = append(children[:i], children[i+1:]...)
- i--
- continue
- }
- // Delete all block data associated with the child
- log.Debug("Deleting dangling block", "number", tip, "hash", children[i], "parent", child.ParentHash)
- DeleteBlock(batch, children[i], tip)
- }
- dangling = children
- tip++
- }
- if err := batch.Write(); err != nil {
- log.Crit("Failed to delete dangling side blocks", "err", err)
- }
- }
-
- // Log something friendly for the user
- context := []interface{}{
- "blocks", frozen - first, "elapsed", common.PrettyDuration(time.Since(start)), "number", frozen - 1,
- }
- if n := len(ancients); n > 0 {
- context = append(context, []interface{}{"hash", ancients[n-1]}...)
- }
- log.Debug("Deep froze chain segment", context...)
-
- // Avoid database thrashing with tiny writes
- if frozen-first < freezerBatchLimit {
- backoff = true
- }
- }
-}
-
-func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hashes []common.Hash, err error) {
- hashes = make([]common.Hash, 0, limit-number)
-
- _, err = f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- for ; number <= limit; number++ {
- // Retrieve all the components of the canonical block.
- hash := ReadCanonicalHash(nfdb, number)
- if hash == (common.Hash{}) {
- return fmt.Errorf("canonical hash missing, can't freeze block %d", number)
- }
- header := ReadHeaderRLP(nfdb, hash, number)
- if len(header) == 0 {
- return fmt.Errorf("block header missing, can't freeze block %d", number)
- }
- body := ReadBodyRLP(nfdb, hash, number)
- if len(body) == 0 {
- return fmt.Errorf("block body missing, can't freeze block %d", number)
- }
- receipts := ReadReceiptsRLP(nfdb, hash, number)
- if len(receipts) == 0 {
- return fmt.Errorf("block receipts missing, can't freeze block %d", number)
- }
- td := ReadTdRLP(nfdb, hash, number)
- if len(td) == 0 {
- return fmt.Errorf("total difficulty missing, can't freeze block %d", number)
- }
-
- // Write to the batch.
- if err := op.AppendRaw(ChainFreezerHashTable, number, hash[:]); err != nil {
- return fmt.Errorf("can't write hash to Freezer: %v", err)
- }
- if err := op.AppendRaw(ChainFreezerHeaderTable, number, header); err != nil {
- return fmt.Errorf("can't write header to Freezer: %v", err)
- }
- if err := op.AppendRaw(ChainFreezerBodiesTable, number, body); err != nil {
- return fmt.Errorf("can't write body to Freezer: %v", err)
- }
- if err := op.AppendRaw(ChainFreezerReceiptTable, number, receipts); err != nil {
- return fmt.Errorf("can't write receipts to Freezer: %v", err)
- }
- if err := op.AppendRaw(ChainFreezerDifficultyTable, number, td); err != nil {
- return fmt.Errorf("can't write td to Freezer: %v", err)
- }
-
- hashes = append(hashes, hash)
- }
- return nil
- })
-
- return hashes, err
-}
diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go
deleted file mode 100644
index 56bb15b718..0000000000
--- a/core/rawdb/chain_iterator.go
+++ /dev/null
@@ -1,355 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "runtime"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/prque"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// InitDatabaseFromFreezer reinitializes an empty database from a previous batch
-// of frozen ancient blocks. The method iterates over all the frozen blocks and
-// injects into the database the block hash->number mappings.
-func InitDatabaseFromFreezer(db ethdb.Database) {
- // If we can't access the freezer or it's empty, abort
- frozen, err := db.Ancients()
- if err != nil || frozen == 0 {
- return
- }
- var (
- batch = db.NewBatch()
- start = time.Now()
- logged = start.Add(-7 * time.Second) // Unindex during import is fast, don't double log
- hash common.Hash
- )
- for i := uint64(0); i < frozen; {
- // We read 100K hashes at a time, for a total of 3.2M
- count := uint64(100_000)
- if i+count > frozen {
- count = frozen - i
- }
- data, err := db.AncientRange(ChainFreezerHashTable, i, count, 32*count)
- if err != nil {
- log.Crit("Failed to init database from freezer", "err", err)
- }
- for j, h := range data {
- number := i + uint64(j)
- hash = common.BytesToHash(h)
- WriteHeaderNumber(batch, hash, number)
- // If enough data was accumulated in memory or we're at the last block, dump to disk
- if batch.ValueSize() > ethdb.IdealBatchSize {
- if err := batch.Write(); err != nil {
- log.Crit("Failed to write data to db", "err", err)
- }
- batch.Reset()
- }
- }
- i += uint64(len(data))
- // If we've spent too much time already, notify the user of what we're doing
- if time.Since(logged) > 8*time.Second {
- log.Info("Initializing database from freezer", "total", frozen, "number", i, "hash", hash, "elapsed", common.PrettyDuration(time.Since(start)))
- logged = time.Now()
- }
- }
- if err := batch.Write(); err != nil {
- log.Crit("Failed to write data to db", "err", err)
- }
- batch.Reset()
-
- WriteHeadHeaderHash(db, hash)
- WriteHeadFastBlockHash(db, hash)
- log.Info("Initialized database from freezer", "blocks", frozen, "elapsed", common.PrettyDuration(time.Since(start)))
-}
-
-type blockTxHashes struct {
- number uint64
- hashes []common.Hash
-}
-
-// iterateTransactions iterates over all transactions in the (canon) block
-// number(s) given, and yields the hashes on a channel. If there is a signal
-// received from interrupt channel, the iteration will be aborted and result
-// channel will be closed.
-func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool, interrupt chan struct{}) chan *blockTxHashes {
- // One thread sequentially reads data from db
- type numberRlp struct {
- number uint64
- rlp rlp.RawValue
- }
- if to == from {
- return nil
- }
- threads := to - from
- if cpus := runtime.NumCPU(); threads > uint64(cpus) {
- threads = uint64(cpus)
- }
- var (
- rlpCh = make(chan *numberRlp, threads*2) // we send raw rlp over this channel
- hashesCh = make(chan *blockTxHashes, threads*2) // send hashes over hashesCh
- )
- // lookup runs in one instance
- lookup := func() {
- n, end := from, to
- if reverse {
- n, end = to-1, from-1
- }
- defer close(rlpCh)
- for n != end {
- data := ReadCanonicalBodyRLP(db, n)
- // Feed the block to the aggregator, or abort on interrupt
- select {
- case rlpCh <- &numberRlp{n, data}:
- case <-interrupt:
- return
- }
- if reverse {
- n--
- } else {
- n++
- }
- }
- }
- // process runs in parallel
- var nThreadsAlive atomic.Int32
- nThreadsAlive.Store(int32(threads))
- process := func() {
- defer func() {
- // Last processor closes the result channel
- if nThreadsAlive.Add(-1) == 0 {
- close(hashesCh)
- }
- }()
- for data := range rlpCh {
- var body types.Body
- if err := rlp.DecodeBytes(data.rlp, &body); err != nil {
- log.Warn("Failed to decode block body", "block", data.number, "error", err)
- return
- }
- var hashes []common.Hash
- for _, tx := range body.Transactions {
- hashes = append(hashes, tx.Hash())
- }
- result := &blockTxHashes{
- hashes: hashes,
- number: data.number,
- }
- // Feed the block to the aggregator, or abort on interrupt
- select {
- case hashesCh <- result:
- case <-interrupt:
- return
- }
- }
- }
- go lookup() // start the sequential db accessor
- for i := 0; i < int(threads); i++ {
- go process()
- }
- return hashesCh
-}
-
-// indexTransactions creates txlookup indices of the specified block range.
-//
-// This function iterates canonical chain in reverse order, it has one main advantage:
-// We can write tx index tail flag periodically even without the whole indexing
-// procedure is finished. So that we can resume indexing procedure next time quickly.
-//
-// There is a passed channel, the whole procedure will be interrupted if any
-// signal received.
-func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
- // short circuit for invalid range
- if from >= to {
- return
- }
- var (
- hashesCh = iterateTransactions(db, from, to, true, interrupt)
- batch = db.NewBatch()
- start = time.Now()
- logged = start.Add(-7 * time.Second)
- // Since we iterate in reverse, we expect the first number to come
- // in to be [to-1]. Therefore, setting lastNum to means that the
- // prqueue gap-evaluation will work correctly
- lastNum = to
- queue = prque.New[int64, *blockTxHashes](nil)
- // for stats reporting
- blocks, txs = 0, 0
- )
- for chanDelivery := range hashesCh {
- // Push the delivery into the queue and process contiguous ranges.
- // Since we iterate in reverse, so lower numbers have lower prio, and
- // we can use the number directly as prio marker
- queue.Push(chanDelivery, int64(chanDelivery.number))
- for !queue.Empty() {
- // If the next available item is gapped, return
- if _, priority := queue.Peek(); priority != int64(lastNum-1) {
- break
- }
- // For testing
- if hook != nil && !hook(lastNum-1) {
- break
- }
- // Next block available, pop it off and index it
- delivery := queue.PopItem()
- lastNum = delivery.number
- WriteTxLookupEntries(batch, delivery.number, delivery.hashes)
- blocks++
- txs += len(delivery.hashes)
- // If enough data was accumulated in memory or we're at the last block, dump to disk
- if batch.ValueSize() > ethdb.IdealBatchSize {
- WriteTxIndexTail(batch, lastNum) // Also write the tail here
- if err := batch.Write(); err != nil {
- log.Crit("Failed writing batch to db", "error", err)
- return
- }
- batch.Reset()
- }
- // If we've spent too much time already, notify the user of what we're doing
- if time.Since(logged) > 8*time.Second {
- log.Info("Indexing transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "total", to-from, "elapsed", common.PrettyDuration(time.Since(start)))
- logged = time.Now()
- }
- }
- }
- // Flush the new indexing tail and the last committed data. It can also happen
- // that the last batch is empty because nothing to index, but the tail has to
- // be flushed anyway.
- WriteTxIndexTail(batch, lastNum)
- if err := batch.Write(); err != nil {
- log.Crit("Failed writing batch to db", "error", err)
- return
- }
- select {
- case <-interrupt:
- log.Debug("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
- default:
- log.Debug("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
- }
-}
-
-// IndexTransactions creates txlookup indices of the specified block range. The from
-// is included while to is excluded.
-//
-// This function iterates canonical chain in reverse order, it has one main advantage:
-// We can write tx index tail flag periodically even without the whole indexing
-// procedure is finished. So that we can resume indexing procedure next time quickly.
-//
-// There is a passed channel, the whole procedure will be interrupted if any
-// signal received.
-func IndexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) {
- indexTransactions(db, from, to, interrupt, nil)
-}
-
-// indexTransactionsForTesting is the internal debug version with an additional hook.
-func indexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
- indexTransactions(db, from, to, interrupt, hook)
-}
-
-// unindexTransactions removes txlookup indices of the specified block range.
-//
-// There is a passed channel, the whole procedure will be interrupted if any
-// signal received.
-func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
- // short circuit for invalid range
- if from >= to {
- return
- }
- var (
- hashesCh = iterateTransactions(db, from, to, false, interrupt)
- batch = db.NewBatch()
- start = time.Now()
- logged = start.Add(-7 * time.Second)
- // we expect the first number to come in to be [from]. Therefore, setting
- // nextNum to from means that the prqueue gap-evaluation will work correctly
- nextNum = from
- queue = prque.New[int64, *blockTxHashes](nil)
- // for stats reporting
- blocks, txs = 0, 0
- )
- // Otherwise spin up the concurrent iterator and unindexer
- for delivery := range hashesCh {
- // Push the delivery into the queue and process contiguous ranges.
- queue.Push(delivery, -int64(delivery.number))
- for !queue.Empty() {
- // If the next available item is gapped, return
- if _, priority := queue.Peek(); -priority != int64(nextNum) {
- break
- }
- // For testing
- if hook != nil && !hook(nextNum) {
- break
- }
- delivery := queue.PopItem()
- nextNum = delivery.number + 1
- DeleteTxLookupEntries(batch, delivery.hashes)
- txs += len(delivery.hashes)
- blocks++
-
- // If enough data was accumulated in memory or we're at the last block, dump to disk
- // A batch counts the size of deletion as '1', so we need to flush more
- // often than that.
- if blocks%1000 == 0 {
- WriteTxIndexTail(batch, nextNum)
- if err := batch.Write(); err != nil {
- log.Crit("Failed writing batch to db", "error", err)
- return
- }
- batch.Reset()
- }
- // If we've spent too much time already, notify the user of what we're doing
- if time.Since(logged) > 8*time.Second {
- log.Info("Unindexing transactions", "blocks", blocks, "txs", txs, "total", to-from, "elapsed", common.PrettyDuration(time.Since(start)))
- logged = time.Now()
- }
- }
- }
- // Flush the new indexing tail and the last committed data. It can also happen
- // that the last batch is empty because nothing to unindex, but the tail has to
- // be flushed anyway.
- WriteTxIndexTail(batch, nextNum)
- if err := batch.Write(); err != nil {
- log.Crit("Failed writing batch to db", "error", err)
- return
- }
- select {
- case <-interrupt:
- log.Debug("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
- default:
- log.Debug("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
- }
-}
-
-// UnindexTransactions removes txlookup indices of the specified block range.
-// The from is included while to is excluded.
-//
-// There is a passed channel, the whole procedure will be interrupted if any
-// signal received.
-func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) {
- unindexTransactions(db, from, to, interrupt, nil)
-}
-
-// unindexTransactionsForTesting is the internal debug version with an additional hook.
-func unindexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
- unindexTransactions(db, from, to, interrupt, hook)
-}
diff --git a/core/rawdb/chain_iterator_test.go b/core/rawdb/chain_iterator_test.go
deleted file mode 100644
index 9580cd92a8..0000000000
--- a/core/rawdb/chain_iterator_test.go
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "math/big"
- "reflect"
- "sort"
- "sync"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
-)
-
-func TestChainIterator(t *testing.T) {
- // Construct test chain db
- chainDb := NewMemoryDatabase()
-
- var block *types.Block
- var txs []*types.Transaction
- to := common.BytesToAddress([]byte{0x11})
- block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newTestHasher()) // Empty genesis block
- WriteBlock(chainDb, block)
- WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
- for i := uint64(1); i <= 10; i++ {
- var tx *types.Transaction
- if i%2 == 0 {
- tx = types.NewTx(&types.LegacyTx{
- Nonce: i,
- GasPrice: big.NewInt(11111),
- Gas: 1111,
- To: &to,
- Value: big.NewInt(111),
- Data: []byte{0x11, 0x11, 0x11},
- })
- } else {
- tx = types.NewTx(&types.AccessListTx{
- ChainID: big.NewInt(1337),
- Nonce: i,
- GasPrice: big.NewInt(11111),
- Gas: 1111,
- To: &to,
- Value: big.NewInt(111),
- Data: []byte{0x11, 0x11, 0x11},
- })
- }
- txs = append(txs, tx)
- block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newTestHasher())
- WriteBlock(chainDb, block)
- WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
- }
-
- var cases = []struct {
- from, to uint64
- reverse bool
- expect []int
- }{
- {0, 11, true, []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}},
- {0, 0, true, nil},
- {0, 5, true, []int{4, 3, 2, 1, 0}},
- {10, 11, true, []int{10}},
- {0, 11, false, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
- {0, 0, false, nil},
- {10, 11, false, []int{10}},
- }
- for i, c := range cases {
- var numbers []int
- hashCh := iterateTransactions(chainDb, c.from, c.to, c.reverse, nil)
- if hashCh != nil {
- for h := range hashCh {
- numbers = append(numbers, int(h.number))
- if len(h.hashes) > 0 {
- if got, exp := h.hashes[0], txs[h.number-1].Hash(); got != exp {
- t.Fatalf("block %d: hash wrong, got %x exp %x", h.number, got, exp)
- }
- }
- }
- }
- if !c.reverse {
- sort.Ints(numbers)
- } else {
- sort.Sort(sort.Reverse(sort.IntSlice(numbers)))
- }
- if !reflect.DeepEqual(numbers, c.expect) {
- t.Fatalf("Case %d failed, visit element mismatch, want %v, got %v", i, c.expect, numbers)
- }
- }
-}
-
-func TestIndexTransactions(t *testing.T) {
- // Construct test chain db
- chainDb := NewMemoryDatabase()
-
- var block *types.Block
- var txs []*types.Transaction
- to := common.BytesToAddress([]byte{0x11})
-
- // Write empty genesis block
- block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newTestHasher())
- WriteBlock(chainDb, block)
- WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
-
- for i := uint64(1); i <= 10; i++ {
- var tx *types.Transaction
- if i%2 == 0 {
- tx = types.NewTx(&types.LegacyTx{
- Nonce: i,
- GasPrice: big.NewInt(11111),
- Gas: 1111,
- To: &to,
- Value: big.NewInt(111),
- Data: []byte{0x11, 0x11, 0x11},
- })
- } else {
- tx = types.NewTx(&types.AccessListTx{
- ChainID: big.NewInt(1337),
- Nonce: i,
- GasPrice: big.NewInt(11111),
- Gas: 1111,
- To: &to,
- Value: big.NewInt(111),
- Data: []byte{0x11, 0x11, 0x11},
- })
- }
- txs = append(txs, tx)
- block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newTestHasher())
- WriteBlock(chainDb, block)
- WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
- }
- // verify checks whether the tx indices in the range [from, to)
- // is expected.
- verify := func(from, to int, exist bool, tail uint64) {
- for i := from; i < to; i++ {
- if i == 0 {
- continue
- }
- number := ReadTxLookupEntry(chainDb, txs[i-1].Hash())
- if exist && number == nil {
- t.Fatalf("Transaction index %d missing", i)
- }
- if !exist && number != nil {
- t.Fatalf("Transaction index %d is not deleted", i)
- }
- }
- number := ReadTxIndexTail(chainDb)
- if number == nil || *number != tail {
- t.Fatalf("Transaction tail mismatch")
- }
- }
- IndexTransactions(chainDb, 5, 11, nil)
- verify(5, 11, true, 5)
- verify(0, 5, false, 5)
-
- IndexTransactions(chainDb, 0, 5, nil)
- verify(0, 11, true, 0)
-
- UnindexTransactions(chainDb, 0, 5, nil)
- verify(5, 11, true, 5)
- verify(0, 5, false, 5)
-
- UnindexTransactions(chainDb, 5, 11, nil)
- verify(0, 11, false, 11)
-
- // Testing corner cases
- signal := make(chan struct{})
- var once sync.Once
- indexTransactionsForTesting(chainDb, 5, 11, signal, func(n uint64) bool {
- if n <= 8 {
- once.Do(func() {
- close(signal)
- })
- return false
- }
- return true
- })
- verify(9, 11, true, 9)
- verify(0, 9, false, 9)
- IndexTransactions(chainDb, 0, 9, nil)
-
- signal = make(chan struct{})
- var once2 sync.Once
- unindexTransactionsForTesting(chainDb, 0, 11, signal, func(n uint64) bool {
- if n >= 8 {
- once2.Do(func() {
- close(signal)
- })
- return false
- }
- return true
- })
- verify(8, 11, true, 8)
- verify(0, 8, false, 8)
-}
diff --git a/core/rawdb/database.go b/core/rawdb/database.go
deleted file mode 100644
index 18b5bccb51..0000000000
--- a/core/rawdb/database.go
+++ /dev/null
@@ -1,666 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "errors"
- "fmt"
- "os"
- "path"
- "path/filepath"
- "strings"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/ethdb/leveldb"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
- "github.com/ethereum/go-ethereum/ethdb/pebble"
- "github.com/ethereum/go-ethereum/log"
- "github.com/olekukonko/tablewriter"
-)
-
-// freezerdb is a database wrapper that enables freezer data retrievals.
-type freezerdb struct {
- ancientRoot string
- ethdb.KeyValueStore
- ethdb.AncientStore
-}
-
-// AncientDatadir returns the path of root ancient directory.
-func (frdb *freezerdb) AncientDatadir() (string, error) {
- return frdb.ancientRoot, nil
-}
-
-// Close implements io.Closer, closing both the fast key-value store as well as
-// the slow ancient tables.
-func (frdb *freezerdb) Close() error {
- var errs []error
- if err := frdb.AncientStore.Close(); err != nil {
- errs = append(errs, err)
- }
- if err := frdb.KeyValueStore.Close(); err != nil {
- errs = append(errs, err)
- }
- if len(errs) != 0 {
- return fmt.Errorf("%v", errs)
- }
- return nil
-}
-
-// Freeze is a helper method used for external testing to trigger and block until
-// a freeze cycle completes, without having to sleep for a minute to trigger the
-// automatic background run.
-func (frdb *freezerdb) Freeze(threshold uint64) error {
- if frdb.AncientStore.(*chainFreezer).readonly {
- return errReadOnly
- }
- // Set the freezer threshold to a temporary value
- defer func(old uint64) {
- frdb.AncientStore.(*chainFreezer).threshold.Store(old)
- }(frdb.AncientStore.(*chainFreezer).threshold.Load())
- frdb.AncientStore.(*chainFreezer).threshold.Store(threshold)
-
- // Trigger a freeze cycle and block until it's done
- trigger := make(chan struct{}, 1)
- frdb.AncientStore.(*chainFreezer).trigger <- trigger
- <-trigger
- return nil
-}
-
-// nofreezedb is a database wrapper that disables freezer data retrievals.
-type nofreezedb struct {
- ethdb.KeyValueStore
-}
-
-// HasAncient returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) {
- return false, errNotSupported
-}
-
-// Ancient returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
- return nil, errNotSupported
-}
-
-// AncientRange returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) AncientRange(kind string, start, max, maxByteSize uint64) ([][]byte, error) {
- return nil, errNotSupported
-}
-
-// Ancients returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) Ancients() (uint64, error) {
- return 0, errNotSupported
-}
-
-// Tail returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) Tail() (uint64, error) {
- return 0, errNotSupported
-}
-
-// AncientSize returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) AncientSize(kind string) (uint64, error) {
- return 0, errNotSupported
-}
-
-// ModifyAncients is not supported.
-func (db *nofreezedb) ModifyAncients(func(ethdb.AncientWriteOp) error) (int64, error) {
- return 0, errNotSupported
-}
-
-// TruncateHead returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) TruncateHead(items uint64) (uint64, error) {
- return 0, errNotSupported
-}
-
-// TruncateTail returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) {
- return 0, errNotSupported
-}
-
-// Sync returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) Sync() error {
- return errNotSupported
-}
-
-func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReaderOp) error) (err error) {
- // Unlike other ancient-related methods, this method does not return
- // errNotSupported when invoked.
- // The reason for this is that the caller might want to do several things:
- // 1. Check if something is in the freezer,
- // 2. If not, check leveldb.
- //
- // This will work, since the ancient-checks inside 'fn' will return errors,
- // and the leveldb work will continue.
- //
- // If we instead were to return errNotSupported here, then the caller would
- // have to explicitly check for that, having an extra clause to do the
- // non-ancient operations.
- return fn(db)
-}
-
-// MigrateTable processes the entries in a given table in sequence
-// converting them to a new format if they're of an old format.
-func (db *nofreezedb) MigrateTable(kind string, convert convertLegacyFn) error {
- return errNotSupported
-}
-
-// AncientDatadir returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) AncientDatadir() (string, error) {
- return "", errNotSupported
-}
-
-// NewDatabase creates a high level database on top of a given key-value data
-// store without a freezer moving immutable chain segments into cold storage.
-func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
- return &nofreezedb{KeyValueStore: db}
-}
-
-// resolveChainFreezerDir is a helper function which resolves the absolute path
-// of chain freezer by considering backward compatibility.
-func resolveChainFreezerDir(ancient string) string {
- // Check if the chain freezer is already present in the specified
- // sub folder, if not then two possibilities:
- // - chain freezer is not initialized
- // - chain freezer exists in legacy location (root ancient folder)
- freezer := path.Join(ancient, ChainFreezerName)
- if !common.FileExist(freezer) {
- if !common.FileExist(ancient) {
- // The entire ancient store is not initialized, still use the sub
- // folder for initialization.
- } else {
- // Ancient root is already initialized, then we hold the assumption
- // that chain freezer is also initialized and located in root folder.
- // In this case fallback to legacy location.
- freezer = ancient
- log.Info("Found legacy ancient chain path", "location", ancient)
- }
- }
- return freezer
-}
-
-// NewDatabaseWithFreezer creates a high level database on top of a given key-
-// value data store with a freezer moving immutable chain segments into cold
-// storage. The passed ancient indicates the path of root ancient directory
-// where the chain freezer can be opened.
-func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly bool) (ethdb.Database, error) {
- // Create the idle freezer instance
- frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly)
- if err != nil {
- printChainMetadata(db)
- return nil, err
- }
- // Since the freezer can be stored separately from the user's key-value database,
- // there's a fairly high probability that the user requests invalid combinations
- // of the freezer and database. Ensure that we don't shoot ourselves in the foot
- // by serving up conflicting data, leading to both datastores getting corrupted.
- //
- // - If both the freezer and key-value store are empty (no genesis), we just
- // initialized a new empty freezer, so everything's fine.
- // - If the key-value store is empty, but the freezer is not, we need to make
- // sure the user's genesis matches the freezer. That will be checked in the
- // blockchain, since we don't have the genesis block here (nor should we at
- // this point care, the key-value/freezer combo is valid).
- // - If neither the key-value store nor the freezer is empty, cross validate
- // the genesis hashes to make sure they are compatible. If they are, also
- // ensure that there's no gap between the freezer and subsequently leveldb.
- // - If the key-value store is not empty, but the freezer is, we might just be
- // upgrading to the freezer release, or we might have had a small chain and
- // not frozen anything yet. Ensure that no blocks are missing yet from the
- // key-value store, since that would mean we already had an old freezer.
-
- // If the genesis hash is empty, we have a new key-value store, so nothing to
- // validate in this method. If, however, the genesis hash is not nil, compare
- // it to the freezer content.
- if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 {
- if frozen, _ := frdb.Ancients(); frozen > 0 {
- // If the freezer already contains something, ensure that the genesis blocks
- // match, otherwise we might mix up freezers across chains and destroy both
- // the freezer and the key-value store.
- frgenesis, err := frdb.Ancient(ChainFreezerHashTable, 0)
- if err != nil {
- printChainMetadata(db)
- return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
- } else if !bytes.Equal(kvgenesis, frgenesis) {
- printChainMetadata(db)
- return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis)
- }
- // Key-value store and freezer belong to the same network. Ensure that they
- // are contiguous, otherwise we might end up with a non-functional freezer.
- if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 {
- // Subsequent header after the freezer limit is missing from the database.
- // Reject startup if the database has a more recent head.
- if head := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); head > frozen-1 {
- // Find the smallest block stored in the key-value store
- // in range of [frozen, head]
- var number uint64
- for number = frozen; number <= head; number++ {
- if present, _ := db.Has(headerHashKey(number)); present {
- break
- }
- }
- // We are about to exit on error. Print database metadata before exiting
- printChainMetadata(db)
- return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ",
- frozen-1, number, head)
- }
- // Database contains only older data than the freezer, this happens if the
- // state was wiped and reinited from an existing freezer.
- }
- // Otherwise, key-value store continues where the freezer left off, all is fine.
- // We might have duplicate blocks (crash after freezer write but before key-value
- // store deletion, but that's fine).
- } else {
- // If the freezer is empty, ensure nothing was moved yet from the key-value
- // store, otherwise we'll end up missing data. We check block #1 to decide
- // if we froze anything previously or not, but do take care of databases with
- // only the genesis block.
- if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) {
- // Key-value store contains more data than the genesis block, make sure we
- // didn't freeze anything yet.
- if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 {
- printChainMetadata(db)
- return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
- }
- // Block #1 is still in the database, we're allowed to init a new freezer
- }
- // Otherwise, the head header is still the genesis, we're allowed to init a new
- // freezer.
- }
- }
- // Freezer is consistent with the key-value database, permit combining the two
- if !frdb.readonly {
- frdb.wg.Add(1)
- go func() {
- frdb.freeze(db)
- frdb.wg.Done()
- }()
- }
- return &freezerdb{
- ancientRoot: ancient,
- KeyValueStore: db,
- AncientStore: frdb,
- }, nil
-}
-
-// NewMemoryDatabase creates an ephemeral in-memory key-value database without a
-// freezer moving immutable chain segments into cold storage.
-func NewMemoryDatabase() ethdb.Database {
- return NewDatabase(memorydb.New())
-}
-
-// NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database
-// with an initial starting capacity, but without a freezer moving immutable
-// chain segments into cold storage.
-func NewMemoryDatabaseWithCap(size int) ethdb.Database {
- return NewDatabase(memorydb.NewWithCap(size))
-}
-
-// NewLevelDBDatabase creates a persistent key-value database without a freezer
-// moving immutable chain segments into cold storage.
-func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
- db, err := leveldb.New(file, cache, handles, namespace, readonly)
- if err != nil {
- return nil, err
- }
- log.Info("Using LevelDB as the backing database")
- return NewDatabase(db), nil
-}
-
-// NewPebbleDBDatabase creates a persistent key-value database without a freezer
-// moving immutable chain segments into cold storage.
-func NewPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly, ephemeral bool) (ethdb.Database, error) {
- db, err := pebble.New(file, cache, handles, namespace, readonly, ephemeral)
- if err != nil {
- return nil, err
- }
- return NewDatabase(db), nil
-}
-
-const (
- dbPebble = "pebble"
- dbLeveldb = "leveldb"
-)
-
-// PreexistingDatabase checks the given data directory whether a database is already
-// instantiated at that location, and if so, returns the type of database (or the
-// empty string).
-func PreexistingDatabase(path string) string {
- if _, err := os.Stat(filepath.Join(path, "CURRENT")); err != nil {
- return "" // No pre-existing db
- }
- if matches, err := filepath.Glob(filepath.Join(path, "OPTIONS*")); len(matches) > 0 || err != nil {
- if err != nil {
- panic(err) // only possible if the pattern is malformed
- }
- return dbPebble
- }
- return dbLeveldb
-}
-
-// OpenOptions contains the options to apply when opening a database.
-// OBS: If AncientsDirectory is empty, it indicates that no freezer is to be used.
-type OpenOptions struct {
- Type string // "leveldb" | "pebble"
- Directory string // the datadir
- AncientsDirectory string // the ancients-dir
- Namespace string // the namespace for database relevant metrics
- Cache int // the capacity(in megabytes) of the data caching
- Handles int // number of files to be open simultaneously
- ReadOnly bool
- // Ephemeral means that filesystem sync operations should be avoided: data integrity in the face of
- // a crash is not important. This option should typically be used in tests.
- Ephemeral bool
-}
-
-// openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble.
-//
-// type == null type != null
-// +----------------------------------------
-// db is non-existent | pebble default | specified type
-// db is existent | from db | specified type (if compatible)
-func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) {
- // Reject any unsupported database type
- if len(o.Type) != 0 && o.Type != dbLeveldb && o.Type != dbPebble {
- return nil, fmt.Errorf("unknown db.engine %v", o.Type)
- }
- // Retrieve any pre-existing database's type and use that or the requested one
- // as long as there's no conflict between the two types
- existingDb := PreexistingDatabase(o.Directory)
- if len(existingDb) != 0 && len(o.Type) != 0 && o.Type != existingDb {
- return nil, fmt.Errorf("db.engine choice was %v but found pre-existing %v database in specified data directory", o.Type, existingDb)
- }
- if o.Type == dbPebble || existingDb == dbPebble {
- log.Info("Using pebble as the backing database")
- return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
- }
- if o.Type == dbLeveldb || existingDb == dbLeveldb {
- log.Info("Using leveldb as the backing database")
- return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
- }
- // No pre-existing database, no user-requested one either. Default to Pebble.
- log.Info("Defaulting to pebble as the backing database")
- return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
-}
-
-// Open opens both a disk-based key-value database such as leveldb or pebble, but also
-// integrates it with a freezer database -- if the AncientDir option has been
-// set on the provided OpenOptions.
-// The passed o.AncientDir indicates the path of root ancient directory where
-// the chain freezer can be opened.
-func Open(o OpenOptions) (ethdb.Database, error) {
- kvdb, err := openKeyValueDatabase(o)
- if err != nil {
- return nil, err
- }
- if len(o.AncientsDirectory) == 0 {
- return kvdb, nil
- }
- frdb, err := NewDatabaseWithFreezer(kvdb, o.AncientsDirectory, o.Namespace, o.ReadOnly)
- if err != nil {
- kvdb.Close()
- return nil, err
- }
- return frdb, nil
-}
-
-type counter uint64
-
-func (c counter) String() string {
- return fmt.Sprintf("%d", c)
-}
-
-func (c counter) Percentage(current uint64) string {
- return fmt.Sprintf("%d", current*100/uint64(c))
-}
-
-// stat stores sizes and count for a parameter
-type stat struct {
- size common.StorageSize
- count counter
-}
-
-// Add size to the stat and increase the counter by 1
-func (s *stat) Add(size common.StorageSize) {
- s.size += size
- s.count++
-}
-
-func (s *stat) Size() string {
- return s.size.String()
-}
-
-func (s *stat) Count() string {
- return s.count.String()
-}
-
-// InspectDatabase traverses the entire database and checks the size
-// of all different categories of data.
-func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
- it := db.NewIterator(keyPrefix, keyStart)
- defer it.Release()
-
- var (
- count int64
- start = time.Now()
- logged = time.Now()
-
- // Key-value store statistics
- headers stat
- bodies stat
- receipts stat
- tds stat
- numHashPairings stat
- hashNumPairings stat
- legacyTries stat
- stateLookups stat
- accountTries stat
- storageTries stat
- codes stat
- txLookups stat
- accountSnaps stat
- storageSnaps stat
- preimages stat
- bloomBits stat
- beaconHeaders stat
- cliqueSnaps stat
-
- // Les statistic
- chtTrieNodes stat
- bloomTrieNodes stat
-
- // Meta- and unaccounted data
- metadata stat
- unaccounted stat
-
- // Totals
- total common.StorageSize
- )
- // Inspect key-value database first.
- for it.Next() {
- var (
- key = it.Key()
- size = common.StorageSize(len(key) + len(it.Value()))
- )
- total += size
- switch {
- case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
- headers.Add(size)
- case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
- bodies.Add(size)
- case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
- receipts.Add(size)
- case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
- tds.Add(size)
- case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
- numHashPairings.Add(size)
- case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
- hashNumPairings.Add(size)
- case IsLegacyTrieNode(key, it.Value()):
- legacyTries.Add(size)
- case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
- stateLookups.Add(size)
- case IsAccountTrieNode(key):
- accountTries.Add(size)
- case IsStorageTrieNode(key):
- storageTries.Add(size)
- case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
- codes.Add(size)
- case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
- txLookups.Add(size)
- case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
- accountSnaps.Add(size)
- case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
- storageSnaps.Add(size)
- case bytes.HasPrefix(key, PreimagePrefix) && len(key) == (len(PreimagePrefix)+common.HashLength):
- preimages.Add(size)
- case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength):
- metadata.Add(size)
- case bytes.HasPrefix(key, genesisPrefix) && len(key) == (len(genesisPrefix)+common.HashLength):
- metadata.Add(size)
- case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
- bloomBits.Add(size)
- case bytes.HasPrefix(key, BloomBitsIndexPrefix):
- bloomBits.Add(size)
- case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8):
- beaconHeaders.Add(size)
- case bytes.HasPrefix(key, CliqueSnapshotPrefix) && len(key) == 7+common.HashLength:
- cliqueSnaps.Add(size)
- case bytes.HasPrefix(key, ChtTablePrefix) ||
- bytes.HasPrefix(key, ChtIndexTablePrefix) ||
- bytes.HasPrefix(key, ChtPrefix): // Canonical hash trie
- chtTrieNodes.Add(size)
- case bytes.HasPrefix(key, BloomTrieTablePrefix) ||
- bytes.HasPrefix(key, BloomTrieIndexPrefix) ||
- bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub
- bloomTrieNodes.Add(size)
- default:
- var accounted bool
- for _, meta := range [][]byte{
- databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, headFinalizedBlockKey,
- lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
- snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
- uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
- persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey, snapSyncStatusFlagKey,
- } {
- if bytes.Equal(key, meta) {
- metadata.Add(size)
- accounted = true
- break
- }
- }
- if !accounted {
- unaccounted.Add(size)
- }
- }
- count++
- if count%1000 == 0 && time.Since(logged) > 8*time.Second {
- log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
- logged = time.Now()
- }
- }
- // Display the database statistic of key-value store.
- stats := [][]string{
- {"Key-Value store", "Headers", headers.Size(), headers.Count()},
- {"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
- {"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
- {"Key-Value store", "Difficulties", tds.Size(), tds.Count()},
- {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
- {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
- {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
- {"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
- {"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
- {"Key-Value store", "Hash trie nodes", legacyTries.Size(), legacyTries.Count()},
- {"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()},
- {"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()},
- {"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()},
- {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
- {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
- {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
- {"Key-Value store", "Beacon sync headers", beaconHeaders.Size(), beaconHeaders.Count()},
- {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
- {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
- {"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()},
- {"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
- }
- // Inspect all registered append-only file store then.
- ancients, err := inspectFreezers(db)
- if err != nil {
- return err
- }
- for _, ancient := range ancients {
- for _, table := range ancient.sizes {
- stats = append(stats, []string{
- fmt.Sprintf("Ancient store (%s)", strings.Title(ancient.name)),
- strings.Title(table.name),
- table.size.String(),
- fmt.Sprintf("%d", ancient.count()),
- })
- }
- total += ancient.size()
- }
- table := tablewriter.NewWriter(os.Stdout)
- table.SetHeader([]string{"Database", "Category", "Size", "Items"})
- table.SetFooter([]string{"", "Total", total.String(), " "})
- table.AppendBulk(stats)
- table.Render()
-
- if unaccounted.size > 0 {
- log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
- }
- return nil
-}
-
-// printChainMetadata prints out chain metadata to stderr.
-func printChainMetadata(db ethdb.KeyValueStore) {
- fmt.Fprintf(os.Stderr, "Chain metadata\n")
- for _, v := range ReadChainMetadata(db) {
- fmt.Fprintf(os.Stderr, " %s\n", strings.Join(v, ": "))
- }
- fmt.Fprintf(os.Stderr, "\n\n")
-}
-
-// ReadChainMetadata returns a set of key/value pairs that contains information
-// about the database chain status. This can be used for diagnostic purposes
-// when investigating the state of the node.
-func ReadChainMetadata(db ethdb.KeyValueStore) [][]string {
- pp := func(val *uint64) string {
- if val == nil {
- return ""
- }
- return fmt.Sprintf("%d (%#x)", *val, *val)
- }
- data := [][]string{
- {"databaseVersion", pp(ReadDatabaseVersion(db))},
- {"headBlockHash", fmt.Sprintf("%v", ReadHeadBlockHash(db))},
- {"headFastBlockHash", fmt.Sprintf("%v", ReadHeadFastBlockHash(db))},
- {"headHeaderHash", fmt.Sprintf("%v", ReadHeadHeaderHash(db))},
- {"lastPivotNumber", pp(ReadLastPivotNumber(db))},
- {"len(snapshotSyncStatus)", fmt.Sprintf("%d bytes", len(ReadSnapshotSyncStatus(db)))},
- {"snapshotDisabled", fmt.Sprintf("%v", ReadSnapshotDisabled(db))},
- {"snapshotJournal", fmt.Sprintf("%d bytes", len(ReadSnapshotJournal(db)))},
- {"snapshotRecoveryNumber", pp(ReadSnapshotRecoveryNumber(db))},
- {"snapshotRoot", fmt.Sprintf("%v", ReadSnapshotRoot(db))},
- {"txIndexTail", pp(ReadTxIndexTail(db))},
- {"fastTxLookupLimit", pp(ReadFastTxLookupLimit(db))},
- }
- if b := ReadSkeletonSyncStatus(db); b != nil {
- data = append(data, []string{"SkeletonSyncStatus", string(b)})
- }
- return data
-}
diff --git a/core/rawdb/database_test.go b/core/rawdb/database_test.go
deleted file mode 100644
index a0d7b5ec66..0000000000
--- a/core/rawdb/database_test.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go
deleted file mode 100644
index b7824ddc0d..0000000000
--- a/core/rawdb/freezer.go
+++ /dev/null
@@ -1,509 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "errors"
- "fmt"
- "math"
- "os"
- "path/filepath"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/metrics"
- "github.com/gofrs/flock"
-)
-
-var (
- // errReadOnly is returned if the freezer is opened in read only mode. All the
- // mutations are disallowed.
- errReadOnly = errors.New("read only")
-
- // errUnknownTable is returned if the user attempts to read from a table that is
- // not tracked by the freezer.
- errUnknownTable = errors.New("unknown table")
-
- // errOutOrderInsertion is returned if the user attempts to inject out-of-order
- // binary blobs into the freezer.
- errOutOrderInsertion = errors.New("the append operation is out-order")
-
- // errSymlinkDatadir is returned if the ancient directory specified by user
- // is a symbolic link.
- errSymlinkDatadir = errors.New("symbolic link datadir is not supported")
-)
-
-// freezerTableSize defines the maximum size of freezer data files.
-const freezerTableSize = 2 * 1000 * 1000 * 1000
-
-// Freezer is a memory mapped append-only database to store immutable ordered
-// data into flat files:
-//
-// - The append-only nature ensures that disk writes are minimized.
-// - The memory mapping ensures we can max out system memory for caching without
-// reserving it for go-ethereum. This would also reduce the memory requirements
-// of Geth, and thus also GC overhead.
-type Freezer struct {
- frozen atomic.Uint64 // Number of blocks already frozen
- tail atomic.Uint64 // Number of the first stored item in the freezer
-
- // This lock synchronizes writers and the truncate operation, as well as
- // the "atomic" (batched) read operations.
- writeLock sync.RWMutex
- writeBatch *freezerBatch
-
- readonly bool
- tables map[string]*freezerTable // Data tables for storing everything
- instanceLock *flock.Flock // File-system lock to prevent double opens
- closeOnce sync.Once
-}
-
-// NewChainFreezer is a small utility method around NewFreezer that sets the
-// default parameters for the chain storage.
-func NewChainFreezer(datadir string, namespace string, readonly bool) (*Freezer, error) {
- return NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerNoSnappy)
-}
-
-// NewFreezer creates a freezer instance for maintaining immutable ordered
-// data according to the given parameters.
-//
-// The 'tables' argument defines the data tables. If the value of a map
-// entry is true, snappy compression is disabled for the table.
-func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]bool) (*Freezer, error) {
- // Create the initial freezer object
- var (
- readMeter = metrics.NewRegisteredMeter(namespace+"ancient/read", nil)
- writeMeter = metrics.NewRegisteredMeter(namespace+"ancient/write", nil)
- sizeGauge = metrics.NewRegisteredGauge(namespace+"ancient/size", nil)
- )
- // Ensure the datadir is not a symbolic link if it exists.
- if info, err := os.Lstat(datadir); !os.IsNotExist(err) {
- if info.Mode()&os.ModeSymlink != 0 {
- log.Warn("Symbolic link ancient database is not supported", "path", datadir)
- return nil, errSymlinkDatadir
- }
- }
- flockFile := filepath.Join(datadir, "FLOCK")
- if err := os.MkdirAll(filepath.Dir(flockFile), 0755); err != nil {
- return nil, err
- }
- // Leveldb uses LOCK as the filelock filename. To prevent the
- // name collision, we use FLOCK as the lock name.
- lock := flock.New(flockFile)
- tryLock := lock.TryLock
- if readonly {
- tryLock = lock.TryRLock
- }
- if locked, err := tryLock(); err != nil {
- return nil, err
- } else if !locked {
- return nil, errors.New("locking failed")
- }
- // Open all the supported data tables
- freezer := &Freezer{
- readonly: readonly,
- tables: make(map[string]*freezerTable),
- instanceLock: lock,
- }
-
- // Create the tables.
- for name, disableSnappy := range tables {
- table, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly)
- if err != nil {
- for _, table := range freezer.tables {
- table.Close()
- }
- lock.Unlock()
- return nil, err
- }
- freezer.tables[name] = table
- }
- var err error
- if freezer.readonly {
- // In readonly mode only validate, don't truncate.
- // validate also sets `freezer.frozen`.
- err = freezer.validate()
- } else {
- // Truncate all tables to common length.
- err = freezer.repair()
- }
- if err != nil {
- for _, table := range freezer.tables {
- table.Close()
- }
- lock.Unlock()
- return nil, err
- }
-
- // Create the write batch.
- freezer.writeBatch = newFreezerBatch(freezer)
-
- log.Info("Opened ancient database", "database", datadir, "readonly", readonly)
- return freezer, nil
-}
-
-// Close terminates the chain freezer, unmapping all the data files.
-func (f *Freezer) Close() error {
- f.writeLock.Lock()
- defer f.writeLock.Unlock()
-
- var errs []error
- f.closeOnce.Do(func() {
- for _, table := range f.tables {
- if err := table.Close(); err != nil {
- errs = append(errs, err)
- }
- }
- if err := f.instanceLock.Unlock(); err != nil {
- errs = append(errs, err)
- }
- })
- if errs != nil {
- return fmt.Errorf("%v", errs)
- }
- return nil
-}
-
-// HasAncient returns an indicator whether the specified ancient data exists
-// in the freezer.
-func (f *Freezer) HasAncient(kind string, number uint64) (bool, error) {
- if table := f.tables[kind]; table != nil {
- return table.has(number), nil
- }
- return false, nil
-}
-
-// Ancient retrieves an ancient binary blob from the append-only immutable files.
-func (f *Freezer) Ancient(kind string, number uint64) ([]byte, error) {
- if table := f.tables[kind]; table != nil {
- return table.Retrieve(number)
- }
- return nil, errUnknownTable
-}
-
-// AncientRange retrieves multiple items in sequence, starting from the index 'start'.
-// It will return
-// - at most 'count' items,
-// - if maxBytes is specified: at least 1 item (even if exceeding the maxByteSize),
-// but will otherwise return as many items as fit into maxByteSize.
-// - if maxBytes is not specified, 'count' items will be returned if they are present.
-func (f *Freezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
- if table := f.tables[kind]; table != nil {
- return table.RetrieveItems(start, count, maxBytes)
- }
- return nil, errUnknownTable
-}
-
-// Ancients returns the length of the frozen items.
-func (f *Freezer) Ancients() (uint64, error) {
- return f.frozen.Load(), nil
-}
-
-// Tail returns the number of first stored item in the freezer.
-func (f *Freezer) Tail() (uint64, error) {
- return f.tail.Load(), nil
-}
-
-// AncientSize returns the ancient size of the specified category.
-func (f *Freezer) AncientSize(kind string) (uint64, error) {
- // This needs the write lock to avoid data races on table fields.
- // Speed doesn't matter here, AncientSize is for debugging.
- f.writeLock.RLock()
- defer f.writeLock.RUnlock()
-
- if table := f.tables[kind]; table != nil {
- return table.size()
- }
- return 0, errUnknownTable
-}
-
-// ReadAncients runs the given read operation while ensuring that no writes take place
-// on the underlying freezer.
-func (f *Freezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) {
- f.writeLock.RLock()
- defer f.writeLock.RUnlock()
-
- return fn(f)
-}
-
-// ModifyAncients runs the given write operation.
-func (f *Freezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize int64, err error) {
- if f.readonly {
- return 0, errReadOnly
- }
- f.writeLock.Lock()
- defer f.writeLock.Unlock()
-
- // Roll back all tables to the starting position in case of error.
- prevItem := f.frozen.Load()
- defer func() {
- if err != nil {
- // The write operation has failed. Go back to the previous item position.
- for name, table := range f.tables {
- err := table.truncateHead(prevItem)
- if err != nil {
- log.Error("Freezer table roll-back failed", "table", name, "index", prevItem, "err", err)
- }
- }
- }
- }()
-
- f.writeBatch.reset()
- if err := fn(f.writeBatch); err != nil {
- return 0, err
- }
- item, writeSize, err := f.writeBatch.commit()
- if err != nil {
- return 0, err
- }
- f.frozen.Store(item)
- return writeSize, nil
-}
-
-// TruncateHead discards any recent data above the provided threshold number.
-// It returns the previous head number.
-func (f *Freezer) TruncateHead(items uint64) (uint64, error) {
- if f.readonly {
- return 0, errReadOnly
- }
- f.writeLock.Lock()
- defer f.writeLock.Unlock()
-
- oitems := f.frozen.Load()
- if oitems <= items {
- return oitems, nil
- }
- for _, table := range f.tables {
- if err := table.truncateHead(items); err != nil {
- return 0, err
- }
- }
- f.frozen.Store(items)
- return oitems, nil
-}
-
-// TruncateTail discards any recent data below the provided threshold number.
-func (f *Freezer) TruncateTail(tail uint64) (uint64, error) {
- if f.readonly {
- return 0, errReadOnly
- }
- f.writeLock.Lock()
- defer f.writeLock.Unlock()
-
- old := f.tail.Load()
- if old >= tail {
- return old, nil
- }
- for _, table := range f.tables {
- if err := table.truncateTail(tail); err != nil {
- return 0, err
- }
- }
- f.tail.Store(tail)
- return old, nil
-}
-
-// Sync flushes all data tables to disk.
-func (f *Freezer) Sync() error {
- var errs []error
- for _, table := range f.tables {
- if err := table.Sync(); err != nil {
- errs = append(errs, err)
- }
- }
- if errs != nil {
- return fmt.Errorf("%v", errs)
- }
- return nil
-}
-
-// validate checks that every table has the same boundary.
-// Used instead of `repair` in readonly mode.
-func (f *Freezer) validate() error {
- if len(f.tables) == 0 {
- return nil
- }
- var (
- head uint64
- tail uint64
- name string
- )
- // Hack to get boundary of any table
- for kind, table := range f.tables {
- head = table.items.Load()
- tail = table.itemHidden.Load()
- name = kind
- break
- }
- // Now check every table against those boundaries.
- for kind, table := range f.tables {
- if head != table.items.Load() {
- return fmt.Errorf("freezer tables %s and %s have differing head: %d != %d", kind, name, table.items.Load(), head)
- }
- if tail != table.itemHidden.Load() {
- return fmt.Errorf("freezer tables %s and %s have differing tail: %d != %d", kind, name, table.itemHidden.Load(), tail)
- }
- }
- f.frozen.Store(head)
- f.tail.Store(tail)
- return nil
-}
-
-// repair truncates all data tables to the same length.
-func (f *Freezer) repair() error {
- var (
- head = uint64(math.MaxUint64)
- tail = uint64(0)
- )
- for _, table := range f.tables {
- items := table.items.Load()
- if head > items {
- head = items
- }
- hidden := table.itemHidden.Load()
- if hidden > tail {
- tail = hidden
- }
- }
- for _, table := range f.tables {
- if err := table.truncateHead(head); err != nil {
- return err
- }
- if err := table.truncateTail(tail); err != nil {
- return err
- }
- }
- f.frozen.Store(head)
- f.tail.Store(tail)
- return nil
-}
-
-// convertLegacyFn takes a raw freezer entry in an older format and
-// returns it in the new format.
-type convertLegacyFn = func([]byte) ([]byte, error)
-
-// MigrateTable processes the entries in a given table in sequence
-// converting them to a new format if they're of an old format.
-func (f *Freezer) MigrateTable(kind string, convert convertLegacyFn) error {
- if f.readonly {
- return errReadOnly
- }
- f.writeLock.Lock()
- defer f.writeLock.Unlock()
-
- table, ok := f.tables[kind]
- if !ok {
- return errUnknownTable
- }
- // forEach iterates every entry in the table serially and in order, calling `fn`
- // with the item as argument. If `fn` returns an error the iteration stops
- // and that error will be returned.
- forEach := func(t *freezerTable, offset uint64, fn func(uint64, []byte) error) error {
- var (
- items = t.items.Load()
- batchSize = uint64(1024)
- maxBytes = uint64(1024 * 1024)
- )
- for i := offset; i < items; {
- if i+batchSize > items {
- batchSize = items - i
- }
- data, err := t.RetrieveItems(i, batchSize, maxBytes)
- if err != nil {
- return err
- }
- for j, item := range data {
- if err := fn(i+uint64(j), item); err != nil {
- return err
- }
- }
- i += uint64(len(data))
- }
- return nil
- }
- // TODO(s1na): This is a sanity-check since as of now no process does tail-deletion. But the migration
- // process assumes no deletion at tail and needs to be modified to account for that.
- if table.itemOffset.Load() > 0 || table.itemHidden.Load() > 0 {
- return errors.New("migration not supported for tail-deleted freezers")
- }
- ancientsPath := filepath.Dir(table.index.Name())
- // Set up new dir for the migrated table, the content of which
- // we'll at the end move over to the ancients dir.
- migrationPath := filepath.Join(ancientsPath, "migration")
- newTable, err := newFreezerTable(migrationPath, kind, table.noCompression, false)
- if err != nil {
- return err
- }
- var (
- batch = newTable.newBatch()
- out []byte
- start = time.Now()
- logged = time.Now()
- offset = newTable.items.Load()
- )
- if offset > 0 {
- log.Info("found previous migration attempt", "migrated", offset)
- }
- // Iterate through entries and transform them
- if err := forEach(table, offset, func(i uint64, blob []byte) error {
- if i%10000 == 0 && time.Since(logged) > 16*time.Second {
- log.Info("Processing legacy elements", "count", i, "elapsed", common.PrettyDuration(time.Since(start)))
- logged = time.Now()
- }
- out, err = convert(blob)
- if err != nil {
- return err
- }
- if err := batch.AppendRaw(i, out); err != nil {
- return err
- }
- return nil
- }); err != nil {
- return err
- }
- if err := batch.commit(); err != nil {
- return err
- }
- log.Info("Replacing old table files with migrated ones", "elapsed", common.PrettyDuration(time.Since(start)))
- // Release and delete old table files. Note this won't
- // delete the index file.
- table.releaseFilesAfter(0, true)
-
- if err := newTable.Close(); err != nil {
- return err
- }
- files, err := os.ReadDir(migrationPath)
- if err != nil {
- return err
- }
- // Move migrated files to ancients dir.
- for _, f := range files {
- // This will replace the old index file as a side-effect.
- if err := os.Rename(filepath.Join(migrationPath, f.Name()), filepath.Join(ancientsPath, f.Name())); err != nil {
- return err
- }
- }
- // Delete by now empty dir.
- if err := os.Remove(migrationPath); err != nil {
- return err
- }
- return nil
-}
diff --git a/core/rawdb/freezer_batch.go b/core/rawdb/freezer_batch.go
deleted file mode 100644
index 84a63a4518..0000000000
--- a/core/rawdb/freezer_batch.go
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright 2021 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "fmt"
-
- "github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/golang/snappy"
-)
-
-// This is the maximum amount of data that will be buffered in memory
-// for a single freezer table batch.
-const freezerBatchBufferLimit = 2 * 1024 * 1024
-
-// freezerBatch is a write operation of multiple items on a freezer.
-type freezerBatch struct {
- tables map[string]*freezerTableBatch
-}
-
-func newFreezerBatch(f *Freezer) *freezerBatch {
- batch := &freezerBatch{tables: make(map[string]*freezerTableBatch, len(f.tables))}
- for kind, table := range f.tables {
- batch.tables[kind] = table.newBatch()
- }
- return batch
-}
-
-// Append adds an RLP-encoded item of the given kind.
-func (batch *freezerBatch) Append(kind string, num uint64, item interface{}) error {
- return batch.tables[kind].Append(num, item)
-}
-
-// AppendRaw adds an item of the given kind.
-func (batch *freezerBatch) AppendRaw(kind string, num uint64, item []byte) error {
- return batch.tables[kind].AppendRaw(num, item)
-}
-
-// reset initializes the batch.
-func (batch *freezerBatch) reset() {
- for _, tb := range batch.tables {
- tb.reset()
- }
-}
-
-// commit is called at the end of a write operation and
-// writes all remaining data to tables.
-func (batch *freezerBatch) commit() (item uint64, writeSize int64, err error) {
- // Check that count agrees on all batches.
- item = uint64(math.MaxUint64)
- for name, tb := range batch.tables {
- if item < math.MaxUint64 && tb.curItem != item {
- return 0, 0, fmt.Errorf("table %s is at item %d, want %d", name, tb.curItem, item)
- }
- item = tb.curItem
- }
-
- // Commit all table batches.
- for _, tb := range batch.tables {
- if err := tb.commit(); err != nil {
- return 0, 0, err
- }
- writeSize += tb.totalBytes
- }
- return item, writeSize, nil
-}
-
-// freezerTableBatch is a batch for a freezer table.
-type freezerTableBatch struct {
- t *freezerTable
-
- sb *snappyBuffer
- encBuffer writeBuffer
- dataBuffer []byte
- indexBuffer []byte
- curItem uint64 // expected index of next append
- totalBytes int64 // counts written bytes since reset
-}
-
-// newBatch creates a new batch for the freezer table.
-func (t *freezerTable) newBatch() *freezerTableBatch {
- batch := &freezerTableBatch{t: t}
- if !t.noCompression {
- batch.sb = new(snappyBuffer)
- }
- batch.reset()
- return batch
-}
-
-// reset clears the batch for reuse.
-func (batch *freezerTableBatch) reset() {
- batch.dataBuffer = batch.dataBuffer[:0]
- batch.indexBuffer = batch.indexBuffer[:0]
- batch.curItem = batch.t.items.Load()
- batch.totalBytes = 0
-}
-
-// Append rlp-encodes and adds data at the end of the freezer table. The item number is a
-// precautionary parameter to ensure data correctness, but the table will reject already
-// existing data.
-func (batch *freezerTableBatch) Append(item uint64, data interface{}) error {
- if item != batch.curItem {
- return fmt.Errorf("%w: have %d want %d", errOutOrderInsertion, item, batch.curItem)
- }
-
- // Encode the item.
- batch.encBuffer.Reset()
- if err := rlp.Encode(&batch.encBuffer, data); err != nil {
- return err
- }
- encItem := batch.encBuffer.data
- if batch.sb != nil {
- encItem = batch.sb.compress(encItem)
- }
- return batch.appendItem(encItem)
-}
-
-// AppendRaw injects a binary blob at the end of the freezer table. The item number is a
-// precautionary parameter to ensure data correctness, but the table will reject already
-// existing data.
-func (batch *freezerTableBatch) AppendRaw(item uint64, blob []byte) error {
- if item != batch.curItem {
- return fmt.Errorf("%w: have %d want %d", errOutOrderInsertion, item, batch.curItem)
- }
-
- encItem := blob
- if batch.sb != nil {
- encItem = batch.sb.compress(blob)
- }
- return batch.appendItem(encItem)
-}
-
-func (batch *freezerTableBatch) appendItem(data []byte) error {
- // Check if item fits into current data file.
- itemSize := int64(len(data))
- itemOffset := batch.t.headBytes + int64(len(batch.dataBuffer))
- if itemOffset+itemSize > int64(batch.t.maxFileSize) {
- // It doesn't fit, go to next file first.
- if err := batch.commit(); err != nil {
- return err
- }
- if err := batch.t.advanceHead(); err != nil {
- return err
- }
- itemOffset = 0
- }
-
- // Put data to buffer.
- batch.dataBuffer = append(batch.dataBuffer, data...)
- batch.totalBytes += itemSize
-
- // Put index entry to buffer.
- entry := indexEntry{filenum: batch.t.headId, offset: uint32(itemOffset + itemSize)}
- batch.indexBuffer = entry.append(batch.indexBuffer)
- batch.curItem++
-
- return batch.maybeCommit()
-}
-
-// maybeCommit writes the buffered data if the buffer is full enough.
-func (batch *freezerTableBatch) maybeCommit() error {
- if len(batch.dataBuffer) > freezerBatchBufferLimit {
- return batch.commit()
- }
- return nil
-}
-
-// commit writes the batched items to the backing freezerTable.
-func (batch *freezerTableBatch) commit() error {
- // Write data. The head file is fsync'd after write to ensure the
- // data is truly transferred to disk.
- _, err := batch.t.head.Write(batch.dataBuffer)
- if err != nil {
- return err
- }
- if err := batch.t.head.Sync(); err != nil {
- return err
- }
- dataSize := int64(len(batch.dataBuffer))
- batch.dataBuffer = batch.dataBuffer[:0]
-
- // Write indices. The index file is fsync'd after write to ensure the
- // data indexes are truly transferred to disk.
- _, err = batch.t.index.Write(batch.indexBuffer)
- if err != nil {
- return err
- }
- if err := batch.t.index.Sync(); err != nil {
- return err
- }
- indexSize := int64(len(batch.indexBuffer))
- batch.indexBuffer = batch.indexBuffer[:0]
-
- // Update headBytes of table.
- batch.t.headBytes += dataSize
- batch.t.items.Store(batch.curItem)
-
- // Update metrics.
- batch.t.sizeGauge.Inc(dataSize + indexSize)
- batch.t.writeMeter.Mark(dataSize + indexSize)
- return nil
-}
-
-// snappyBuffer writes snappy in block format, and can be reused. It is
-// reset when WriteTo is called.
-type snappyBuffer struct {
- dst []byte
-}
-
-// compress snappy-compresses the data.
-func (s *snappyBuffer) compress(data []byte) []byte {
- // The snappy library does not care what the capacity of the buffer is,
- // but only checks the length. If the length is too small, it will
- // allocate a brand new buffer.
- // To avoid that, we check the required size here, and grow the size of the
- // buffer to utilize the full capacity.
- if n := snappy.MaxEncodedLen(len(data)); len(s.dst) < n {
- if cap(s.dst) < n {
- s.dst = make([]byte, n)
- }
- s.dst = s.dst[:n]
- }
-
- s.dst = snappy.Encode(s.dst, data)
- return s.dst
-}
-
-// writeBuffer implements io.Writer for a byte slice.
-type writeBuffer struct {
- data []byte
-}
-
-func (wb *writeBuffer) Write(data []byte) (int, error) {
- wb.data = append(wb.data, data...)
- return len(data), nil
-}
-
-func (wb *writeBuffer) Reset() {
- wb.data = wb.data[:0]
-}
diff --git a/core/rawdb/freezer_meta.go b/core/rawdb/freezer_meta.go
deleted file mode 100644
index 9eef9df351..0000000000
--- a/core/rawdb/freezer_meta.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "io"
- "os"
-
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-const freezerVersion = 1 // The initial version tag of freezer table metadata
-
-// freezerTableMeta wraps all the metadata of the freezer table.
-type freezerTableMeta struct {
- // Version is the versioning descriptor of the freezer table.
- Version uint16
-
- // VirtualTail indicates how many items have been marked as deleted.
- // Its value is equal to the number of items removed from the table
- // plus the number of items hidden in the table, so it should never
- // be lower than the "actual tail".
- VirtualTail uint64
-}
-
-// newMetadata initializes the metadata object with the given virtual tail.
-func newMetadata(tail uint64) *freezerTableMeta {
- return &freezerTableMeta{
- Version: freezerVersion,
- VirtualTail: tail,
- }
-}
-
-// readMetadata reads the metadata of the freezer table from the
-// given metadata file.
-func readMetadata(file *os.File) (*freezerTableMeta, error) {
- _, err := file.Seek(0, io.SeekStart)
- if err != nil {
- return nil, err
- }
- var meta freezerTableMeta
- if err := rlp.Decode(file, &meta); err != nil {
- return nil, err
- }
- return &meta, nil
-}
-
-// writeMetadata writes the metadata of the freezer table into the
-// given metadata file.
-func writeMetadata(file *os.File, meta *freezerTableMeta) error {
- _, err := file.Seek(0, io.SeekStart)
- if err != nil {
- return err
- }
- return rlp.Encode(file, meta)
-}
-
-// loadMetadata loads the metadata from the given metadata file.
-// Initializes the metadata file with the given "actual tail" if
-// it's empty.
-func loadMetadata(file *os.File, tail uint64) (*freezerTableMeta, error) {
- stat, err := file.Stat()
- if err != nil {
- return nil, err
- }
- // Write the metadata with the given actual tail into metadata file
- // if it's non-existent. There are two possible scenarios here:
- // - the freezer table is empty
- // - the freezer table is legacy
- // In both cases, write the meta into the file with the actual tail
- // as the virtual tail.
- if stat.Size() == 0 {
- m := newMetadata(tail)
- if err := writeMetadata(file, m); err != nil {
- return nil, err
- }
- return m, nil
- }
- m, err := readMetadata(file)
- if err != nil {
- return nil, err
- }
- // Update the virtual tail with the given actual tail if it's even
- // lower than it. Theoretically it shouldn't happen at all, print
- // a warning here.
- if m.VirtualTail < tail {
- log.Warn("Updated virtual tail", "have", m.VirtualTail, "now", tail)
- m.VirtualTail = tail
- if err := writeMetadata(file, m); err != nil {
- return nil, err
- }
- }
- return m, nil
-}
diff --git a/core/rawdb/freezer_meta_test.go b/core/rawdb/freezer_meta_test.go
deleted file mode 100644
index ba1a95e453..0000000000
--- a/core/rawdb/freezer_meta_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "os"
- "testing"
-)
-
-func TestReadWriteFreezerTableMeta(t *testing.T) {
- f, err := os.CreateTemp(os.TempDir(), "*")
- if err != nil {
- t.Fatalf("Failed to create file %v", err)
- }
- err = writeMetadata(f, newMetadata(100))
- if err != nil {
- t.Fatalf("Failed to write metadata %v", err)
- }
- meta, err := readMetadata(f)
- if err != nil {
- t.Fatalf("Failed to read metadata %v", err)
- }
- if meta.Version != freezerVersion {
- t.Fatalf("Unexpected version field")
- }
- if meta.VirtualTail != uint64(100) {
- t.Fatalf("Unexpected virtual tail field")
- }
-}
-
-func TestInitializeFreezerTableMeta(t *testing.T) {
- f, err := os.CreateTemp(os.TempDir(), "*")
- if err != nil {
- t.Fatalf("Failed to create file %v", err)
- }
- meta, err := loadMetadata(f, uint64(100))
- if err != nil {
- t.Fatalf("Failed to read metadata %v", err)
- }
- if meta.Version != freezerVersion {
- t.Fatalf("Unexpected version field")
- }
- if meta.VirtualTail != uint64(100) {
- t.Fatalf("Unexpected virtual tail field")
- }
-}
diff --git a/core/rawdb/freezer_resettable.go b/core/rawdb/freezer_resettable.go
deleted file mode 100644
index 7a85489738..0000000000
--- a/core/rawdb/freezer_resettable.go
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "os"
- "path/filepath"
- "sync"
-
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
-)
-
-const tmpSuffix = ".tmp"
-
-// freezerOpenFunc is the function used to open/create a freezer.
-type freezerOpenFunc = func() (*Freezer, error)
-
-// ResettableFreezer is a wrapper of the freezer which makes the
-// freezer resettable.
-type ResettableFreezer struct {
- freezer *Freezer
- opener freezerOpenFunc
- datadir string
- lock sync.RWMutex
-}
-
-// NewResettableFreezer creates a resettable freezer, note freezer is
-// only resettable if the passed file directory is exclusively occupied
-// by the freezer. And also the user-configurable ancient root directory
-// is **not** supported for reset since it might be a mount and rename
-// will cause a copy of hundreds of gigabyte into local directory. It
-// needs some other file based solutions.
-//
-// The reset function will delete directory atomically and re-create the
-// freezer from scratch.
-func NewResettableFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]bool) (*ResettableFreezer, error) {
- if err := cleanup(datadir); err != nil {
- return nil, err
- }
- opener := func() (*Freezer, error) {
- return NewFreezer(datadir, namespace, readonly, maxTableSize, tables)
- }
- freezer, err := opener()
- if err != nil {
- return nil, err
- }
- return &ResettableFreezer{
- freezer: freezer,
- opener: opener,
- datadir: datadir,
- }, nil
-}
-
-// Reset deletes the file directory exclusively occupied by the freezer and
-// recreate the freezer from scratch. The atomicity of directory deletion
-// is guaranteed by the rename operation, the leftover directory will be
-// cleaned up in next startup in case crash happens after rename.
-func (f *ResettableFreezer) Reset() error {
- f.lock.Lock()
- defer f.lock.Unlock()
-
- if err := f.freezer.Close(); err != nil {
- return err
- }
- tmp := tmpName(f.datadir)
- if err := os.Rename(f.datadir, tmp); err != nil {
- return err
- }
- if err := os.RemoveAll(tmp); err != nil {
- return err
- }
- freezer, err := f.opener()
- if err != nil {
- return err
- }
- f.freezer = freezer
- return nil
-}
-
-// Close terminates the chain freezer, unmapping all the data files.
-func (f *ResettableFreezer) Close() error {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.Close()
-}
-
-// HasAncient returns an indicator whether the specified ancient data exists
-// in the freezer
-func (f *ResettableFreezer) HasAncient(kind string, number uint64) (bool, error) {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.HasAncient(kind, number)
-}
-
-// Ancient retrieves an ancient binary blob from the append-only immutable files.
-func (f *ResettableFreezer) Ancient(kind string, number uint64) ([]byte, error) {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.Ancient(kind, number)
-}
-
-// AncientRange retrieves multiple items in sequence, starting from the index 'start'.
-// It will return
-// - at most 'count' items,
-// - if maxBytes is specified: at least 1 item (even if exceeding the maxByteSize),
-// but will otherwise return as many items as fit into maxByteSize.
-// - if maxBytes is not specified, 'count' items will be returned if they are present.
-func (f *ResettableFreezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.AncientRange(kind, start, count, maxBytes)
-}
-
-// Ancients returns the length of the frozen items.
-func (f *ResettableFreezer) Ancients() (uint64, error) {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.Ancients()
-}
-
-// Tail returns the number of first stored item in the freezer.
-func (f *ResettableFreezer) Tail() (uint64, error) {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.Tail()
-}
-
-// AncientSize returns the ancient size of the specified category.
-func (f *ResettableFreezer) AncientSize(kind string) (uint64, error) {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.AncientSize(kind)
-}
-
-// ReadAncients runs the given read operation while ensuring that no writes take place
-// on the underlying freezer.
-func (f *ResettableFreezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.ReadAncients(fn)
-}
-
-// ModifyAncients runs the given write operation.
-func (f *ResettableFreezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize int64, err error) {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.ModifyAncients(fn)
-}
-
-// TruncateHead discards any recent data above the provided threshold number.
-// It returns the previous head number.
-func (f *ResettableFreezer) TruncateHead(items uint64) (uint64, error) {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.TruncateHead(items)
-}
-
-// TruncateTail discards any recent data below the provided threshold number.
-// It returns the previous value
-func (f *ResettableFreezer) TruncateTail(tail uint64) (uint64, error) {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.TruncateTail(tail)
-}
-
-// Sync flushes all data tables to disk.
-func (f *ResettableFreezer) Sync() error {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.Sync()
-}
-
-// MigrateTable processes the entries in a given table in sequence
-// converting them to a new format if they're of an old format.
-func (f *ResettableFreezer) MigrateTable(kind string, convert convertLegacyFn) error {
- f.lock.RLock()
- defer f.lock.RUnlock()
-
- return f.freezer.MigrateTable(kind, convert)
-}
-
-// cleanup removes the directory located in the specified path
-// has the name with deletion marker suffix.
-func cleanup(path string) error {
- parent := filepath.Dir(path)
- if _, err := os.Lstat(parent); os.IsNotExist(err) {
- return nil
- }
- dir, err := os.Open(parent)
- if err != nil {
- return err
- }
- names, err := dir.Readdirnames(0)
- if err != nil {
- return err
- }
- if cerr := dir.Close(); cerr != nil {
- return cerr
- }
- for _, name := range names {
- if name == filepath.Base(path)+tmpSuffix {
- log.Info("Removed leftover freezer directory", "name", name)
- return os.RemoveAll(filepath.Join(parent, name))
- }
- }
- return nil
-}
-
-func tmpName(path string) string {
- return filepath.Join(filepath.Dir(path), filepath.Base(path)+tmpSuffix)
-}
diff --git a/core/rawdb/freezer_resettable_test.go b/core/rawdb/freezer_resettable_test.go
deleted file mode 100644
index d741bc14e5..0000000000
--- a/core/rawdb/freezer_resettable_test.go
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "os"
- "testing"
-
- "github.com/ethereum/go-ethereum/ethdb"
-)
-
-func TestResetFreezer(t *testing.T) {
- items := []struct {
- id uint64
- blob []byte
- }{
- {0, bytes.Repeat([]byte{0}, 2048)},
- {1, bytes.Repeat([]byte{1}, 2048)},
- {2, bytes.Repeat([]byte{2}, 2048)},
- }
- f, _ := NewResettableFreezer(t.TempDir(), "", false, 2048, freezerTestTableDef)
- defer f.Close()
-
- f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- for _, item := range items {
- op.AppendRaw("test", item.id, item.blob)
- }
- return nil
- })
- for _, item := range items {
- blob, _ := f.Ancient("test", item.id)
- if !bytes.Equal(blob, item.blob) {
- t.Fatal("Unexpected blob")
- }
- }
-
- // Reset freezer
- f.Reset()
- count, _ := f.Ancients()
- if count != 0 {
- t.Fatal("Failed to reset freezer")
- }
- for _, item := range items {
- blob, _ := f.Ancient("test", item.id)
- if len(blob) != 0 {
- t.Fatal("Unexpected blob")
- }
- }
-
- // Fill the freezer
- f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- for _, item := range items {
- op.AppendRaw("test", item.id, item.blob)
- }
- return nil
- })
- for _, item := range items {
- blob, _ := f.Ancient("test", item.id)
- if !bytes.Equal(blob, item.blob) {
- t.Fatal("Unexpected blob")
- }
- }
-}
-
-func TestFreezerCleanup(t *testing.T) {
- items := []struct {
- id uint64
- blob []byte
- }{
- {0, bytes.Repeat([]byte{0}, 2048)},
- {1, bytes.Repeat([]byte{1}, 2048)},
- {2, bytes.Repeat([]byte{2}, 2048)},
- }
- datadir := t.TempDir()
- f, _ := NewResettableFreezer(datadir, "", false, 2048, freezerTestTableDef)
- f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- for _, item := range items {
- op.AppendRaw("test", item.id, item.blob)
- }
- return nil
- })
- f.Close()
- os.Rename(datadir, tmpName(datadir))
-
- // Open the freezer again, trigger cleanup operation
- f, _ = NewResettableFreezer(datadir, "", false, 2048, freezerTestTableDef)
- f.Close()
-
- if _, err := os.Lstat(tmpName(datadir)); !os.IsNotExist(err) {
- t.Fatal("Failed to cleanup leftover directory")
- }
-}
diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go
deleted file mode 100644
index 4b9d510e82..0000000000
--- a/core/rawdb/freezer_table.go
+++ /dev/null
@@ -1,990 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "os"
- "path/filepath"
- "sync"
- "sync/atomic"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/metrics"
- "github.com/golang/snappy"
-)
-
-var (
- // errClosed is returned if an operation attempts to read from or write to the
- // freezer table after it has already been closed.
- errClosed = errors.New("closed")
-
- // errOutOfBounds is returned if the item requested is not contained within the
- // freezer table.
- errOutOfBounds = errors.New("out of bounds")
-
- // errNotSupported is returned if the database doesn't support the required operation.
- errNotSupported = errors.New("this operation is not supported")
-)
-
-// indexEntry contains the number/id of the file that the data resides in, as well as the
-// offset within the file to the end of the data.
-// In serialized form, the filenum is stored as uint16.
-type indexEntry struct {
- filenum uint32 // stored as uint16 ( 2 bytes )
- offset uint32 // stored as uint32 ( 4 bytes )
-}
-
-const indexEntrySize = 6
-
-// unmarshalBinary deserializes binary b into the rawIndex entry.
-func (i *indexEntry) unmarshalBinary(b []byte) {
- i.filenum = uint32(binary.BigEndian.Uint16(b[:2]))
- i.offset = binary.BigEndian.Uint32(b[2:6])
-}
-
-// append adds the encoded entry to the end of b.
-func (i *indexEntry) append(b []byte) []byte {
- offset := len(b)
- out := append(b, make([]byte, indexEntrySize)...)
- binary.BigEndian.PutUint16(out[offset:], uint16(i.filenum))
- binary.BigEndian.PutUint32(out[offset+2:], i.offset)
- return out
-}
-
-// bounds returns the start- and end- offsets, and the file number of where to
-// read there data item marked by the two index entries. The two entries are
-// assumed to be sequential.
-func (i *indexEntry) bounds(end *indexEntry) (startOffset, endOffset, fileId uint32) {
- if i.filenum != end.filenum {
- // If a piece of data 'crosses' a data-file,
- // it's actually in one piece on the second data-file.
- // We return a zero-indexEntry for the second file as start
- return 0, end.offset, end.filenum
- }
- return i.offset, end.offset, end.filenum
-}
-
-// freezerTable represents a single chained data table within the freezer (e.g. blocks).
-// It consists of a data file (snappy encoded arbitrary data blobs) and an indexEntry
-// file (uncompressed 64 bit indices into the data file).
-type freezerTable struct {
- items atomic.Uint64 // Number of items stored in the table (including items removed from tail)
- itemOffset atomic.Uint64 // Number of items removed from the table
-
- // itemHidden is the number of items marked as deleted. Tail deletion is
- // only supported at file level which means the actual deletion will be
- // delayed until the entire data file is marked as deleted. Before that
- // these items will be hidden to prevent being visited again. The value
- // should never be lower than itemOffset.
- itemHidden atomic.Uint64
-
- noCompression bool // if true, disables snappy compression. Note: does not work retroactively
- readonly bool
- maxFileSize uint32 // Max file size for data-files
- name string
- path string
-
- head *os.File // File descriptor for the data head of the table
- index *os.File // File descriptor for the indexEntry file of the table
- meta *os.File // File descriptor for metadata of the table
- files map[uint32]*os.File // open files
- headId uint32 // number of the currently active head file
- tailId uint32 // number of the earliest file
-
- headBytes int64 // Number of bytes written to the head file
- readMeter metrics.Meter // Meter for measuring the effective amount of data read
- writeMeter metrics.Meter // Meter for measuring the effective amount of data written
- sizeGauge metrics.Gauge // Gauge for tracking the combined size of all freezer tables
-
- logger log.Logger // Logger with database path and table name embedded
- lock sync.RWMutex // Mutex protecting the data file descriptors
-}
-
-// newFreezerTable opens the given path as a freezer table.
-func newFreezerTable(path, name string, disableSnappy, readonly bool) (*freezerTable, error) {
- return newTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, freezerTableSize, disableSnappy, readonly)
-}
-
-// newTable opens a freezer table, creating the data and index files if they are
-// non-existent. Both files are truncated to the shortest common length to ensure
-// they don't go out of sync.
-func newTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, maxFilesize uint32, noCompression, readonly bool) (*freezerTable, error) {
- // Ensure the containing directory exists and open the indexEntry file
- if err := os.MkdirAll(path, 0755); err != nil {
- return nil, err
- }
- var idxName string
- if noCompression {
- idxName = fmt.Sprintf("%s.ridx", name) // raw index file
- } else {
- idxName = fmt.Sprintf("%s.cidx", name) // compressed index file
- }
- var (
- err error
- index *os.File
- meta *os.File
- )
- if readonly {
- // Will fail if table index file or meta file is not existent
- index, err = openFreezerFileForReadOnly(filepath.Join(path, idxName))
- if err != nil {
- return nil, err
- }
- meta, err = openFreezerFileForReadOnly(filepath.Join(path, fmt.Sprintf("%s.meta", name)))
- if err != nil {
- return nil, err
- }
- } else {
- index, err = openFreezerFileForAppend(filepath.Join(path, idxName))
- if err != nil {
- return nil, err
- }
- meta, err = openFreezerFileForAppend(filepath.Join(path, fmt.Sprintf("%s.meta", name)))
- if err != nil {
- return nil, err
- }
- }
- // Create the table and repair any past inconsistency
- tab := &freezerTable{
- index: index,
- meta: meta,
- files: make(map[uint32]*os.File),
- readMeter: readMeter,
- writeMeter: writeMeter,
- sizeGauge: sizeGauge,
- name: name,
- path: path,
- logger: log.New("database", path, "table", name),
- noCompression: noCompression,
- readonly: readonly,
- maxFileSize: maxFilesize,
- }
- if err := tab.repair(); err != nil {
- tab.Close()
- return nil, err
- }
- // Initialize the starting size counter
- size, err := tab.sizeNolock()
- if err != nil {
- tab.Close()
- return nil, err
- }
- tab.sizeGauge.Inc(int64(size))
-
- return tab, nil
-}
-
-// repair cross-checks the head and the index file and truncates them to
-// be in sync with each other after a potential crash / data loss.
-func (t *freezerTable) repair() error {
- // Create a temporary offset buffer to init files with and read indexEntry into
- buffer := make([]byte, indexEntrySize)
-
- // If we've just created the files, initialize the index with the 0 indexEntry
- stat, err := t.index.Stat()
- if err != nil {
- return err
- }
- if stat.Size() == 0 {
- if _, err := t.index.Write(buffer); err != nil {
- return err
- }
- }
- // Ensure the index is a multiple of indexEntrySize bytes
- if overflow := stat.Size() % indexEntrySize; overflow != 0 {
- if t.readonly {
- return fmt.Errorf("index file(path: %s, name: %s) size is not a multiple of %d", t.path, t.name, indexEntrySize)
- }
- if err := truncateFreezerFile(t.index, stat.Size()-overflow); err != nil {
- return err
- } // New file can't trigger this path
- }
- // Retrieve the file sizes and prepare for truncation
- if stat, err = t.index.Stat(); err != nil {
- return err
- }
- offsetsSize := stat.Size()
-
- // Open the head file
- var (
- firstIndex indexEntry
- lastIndex indexEntry
- contentSize int64
- contentExp int64
- verbose bool
- )
- // Read index zero, determine what file is the earliest
- // and what item offset to use
- t.index.ReadAt(buffer, 0)
- firstIndex.unmarshalBinary(buffer)
-
- // Assign the tail fields with the first stored index.
- // The total removed items is represented with an uint32,
- // which is not enough in theory but enough in practice.
- // TODO: use uint64 to represent total removed items.
- t.tailId = firstIndex.filenum
- t.itemOffset.Store(uint64(firstIndex.offset))
-
- // Load metadata from the file
- meta, err := loadMetadata(t.meta, t.itemOffset.Load())
- if err != nil {
- return err
- }
- t.itemHidden.Store(meta.VirtualTail)
-
- // Read the last index, use the default value in case the freezer is empty
- if offsetsSize == indexEntrySize {
- lastIndex = indexEntry{filenum: t.tailId, offset: 0}
- } else {
- t.index.ReadAt(buffer, offsetsSize-indexEntrySize)
- lastIndex.unmarshalBinary(buffer)
- }
- // Print an error log if the index is corrupted due to an incorrect
- // last index item. While it is theoretically possible to have a zero offset
- // by storing all zero-size items, it is highly unlikely to occur in practice.
- if lastIndex.offset == 0 && offsetsSize/indexEntrySize > 1 {
- log.Error("Corrupted index file detected", "lastOffset", lastIndex.offset, "indexes", offsetsSize/indexEntrySize)
- }
- if t.readonly {
- t.head, err = t.openFile(lastIndex.filenum, openFreezerFileForReadOnly)
- } else {
- t.head, err = t.openFile(lastIndex.filenum, openFreezerFileForAppend)
- }
- if err != nil {
- return err
- }
- if stat, err = t.head.Stat(); err != nil {
- return err
- }
- contentSize = stat.Size()
-
- // Keep truncating both files until they come in sync
- contentExp = int64(lastIndex.offset)
- for contentExp != contentSize {
- if t.readonly {
- return fmt.Errorf("freezer table(path: %s, name: %s, num: %d) is corrupted", t.path, t.name, lastIndex.filenum)
- }
- verbose = true
- // Truncate the head file to the last offset pointer
- if contentExp < contentSize {
- t.logger.Warn("Truncating dangling head", "indexed", contentExp, "stored", contentSize)
- if err := truncateFreezerFile(t.head, contentExp); err != nil {
- return err
- }
- contentSize = contentExp
- }
- // Truncate the index to point within the head file
- if contentExp > contentSize {
- t.logger.Warn("Truncating dangling indexes", "indexes", offsetsSize/indexEntrySize, "indexed", contentExp, "stored", contentSize)
- if err := truncateFreezerFile(t.index, offsetsSize-indexEntrySize); err != nil {
- return err
- }
- offsetsSize -= indexEntrySize
-
- // Read the new head index, use the default value in case
- // the freezer is already empty.
- var newLastIndex indexEntry
- if offsetsSize == indexEntrySize {
- newLastIndex = indexEntry{filenum: t.tailId, offset: 0}
- } else {
- t.index.ReadAt(buffer, offsetsSize-indexEntrySize)
- newLastIndex.unmarshalBinary(buffer)
- }
- // We might have slipped back into an earlier head-file here
- if newLastIndex.filenum != lastIndex.filenum {
- // Release earlier opened file
- t.releaseFile(lastIndex.filenum)
- if t.head, err = t.openFile(newLastIndex.filenum, openFreezerFileForAppend); err != nil {
- return err
- }
- if stat, err = t.head.Stat(); err != nil {
- // TODO, anything more we can do here?
- // A data file has gone missing...
- return err
- }
- contentSize = stat.Size()
- }
- lastIndex = newLastIndex
- contentExp = int64(lastIndex.offset)
- }
- }
- // Sync() fails for read-only files on windows.
- if !t.readonly {
- // Ensure all reparation changes have been written to disk
- if err := t.index.Sync(); err != nil {
- return err
- }
- if err := t.head.Sync(); err != nil {
- return err
- }
- if err := t.meta.Sync(); err != nil {
- return err
- }
- }
- // Update the item and byte counters and return
- t.items.Store(t.itemOffset.Load() + uint64(offsetsSize/indexEntrySize-1)) // last indexEntry points to the end of the data file
- t.headBytes = contentSize
- t.headId = lastIndex.filenum
-
- // Delete the leftover files because of head deletion
- t.releaseFilesAfter(t.headId, true)
-
- // Delete the leftover files because of tail deletion
- t.releaseFilesBefore(t.tailId, true)
-
- // Close opened files and preopen all files
- if err := t.preopen(); err != nil {
- return err
- }
- if verbose {
- t.logger.Info("Chain freezer table opened", "items", t.items.Load(), "deleted", t.itemOffset.Load(), "hidden", t.itemHidden.Load(), "tailId", t.tailId, "headId", t.headId, "size", t.headBytes)
- } else {
- t.logger.Debug("Chain freezer table opened", "items", t.items.Load(), "size", common.StorageSize(t.headBytes))
- }
- return nil
-}
-
-// preopen opens all files that the freezer will need. This method should be called from an init-context,
-// since it assumes that it doesn't have to bother with locking
-// The rationale for doing preopen is to not have to do it from within Retrieve, thus not needing to ever
-// obtain a write-lock within Retrieve.
-func (t *freezerTable) preopen() (err error) {
- // The repair might have already opened (some) files
- t.releaseFilesAfter(0, false)
-
- // Open all except head in RDONLY
- for i := t.tailId; i < t.headId; i++ {
- if _, err = t.openFile(i, openFreezerFileForReadOnly); err != nil {
- return err
- }
- }
- if t.readonly {
- t.head, err = t.openFile(t.headId, openFreezerFileForReadOnly)
- } else {
- // Open head in read/write
- t.head, err = t.openFile(t.headId, openFreezerFileForAppend)
- }
- return err
-}
-
-// truncateHead discards any recent data above the provided threshold number.
-func (t *freezerTable) truncateHead(items uint64) error {
- t.lock.Lock()
- defer t.lock.Unlock()
-
- // Ensure the given truncate target falls in the correct range
- existing := t.items.Load()
- if existing <= items {
- return nil
- }
- if items < t.itemHidden.Load() {
- return errors.New("truncation below tail")
- }
- // We need to truncate, save the old size for metrics tracking
- oldSize, err := t.sizeNolock()
- if err != nil {
- return err
- }
- // Something's out of sync, truncate the table's offset index
- log := t.logger.Debug
- if existing > items+1 {
- log = t.logger.Warn // Only loud warn if we delete multiple items
- }
- log("Truncating freezer table", "items", existing, "limit", items)
-
- // Truncate the index file first, the tail position is also considered
- // when calculating the new freezer table length.
- length := items - t.itemOffset.Load()
- if err := truncateFreezerFile(t.index, int64(length+1)*indexEntrySize); err != nil {
- return err
- }
- if err := t.index.Sync(); err != nil {
- return err
- }
- // Calculate the new expected size of the data file and truncate it
- var expected indexEntry
- if length == 0 {
- expected = indexEntry{filenum: t.tailId, offset: 0}
- } else {
- buffer := make([]byte, indexEntrySize)
- if _, err := t.index.ReadAt(buffer, int64(length*indexEntrySize)); err != nil {
- return err
- }
- expected.unmarshalBinary(buffer)
- }
- // We might need to truncate back to older files
- if expected.filenum != t.headId {
- // If already open for reading, force-reopen for writing
- t.releaseFile(expected.filenum)
- newHead, err := t.openFile(expected.filenum, openFreezerFileForAppend)
- if err != nil {
- return err
- }
- // Release any files _after the current head -- both the previous head
- // and any files which may have been opened for reading
- t.releaseFilesAfter(expected.filenum, true)
-
- // Set back the historic head
- t.head = newHead
- t.headId = expected.filenum
- }
- if err := truncateFreezerFile(t.head, int64(expected.offset)); err != nil {
- return err
- }
- if err := t.head.Sync(); err != nil {
- return err
- }
- // All data files truncated, set internal counters and return
- t.headBytes = int64(expected.offset)
- t.items.Store(items)
-
- // Retrieve the new size and update the total size counter
- newSize, err := t.sizeNolock()
- if err != nil {
- return err
- }
- t.sizeGauge.Dec(int64(oldSize - newSize))
- return nil
-}
-
-// sizeHidden returns the total data size of hidden items in the freezer table.
-// This function assumes the lock is already held.
-func (t *freezerTable) sizeHidden() (uint64, error) {
- hidden, offset := t.itemHidden.Load(), t.itemOffset.Load()
- if hidden <= offset {
- return 0, nil
- }
- indices, err := t.getIndices(hidden-1, 1)
- if err != nil {
- return 0, err
- }
- return uint64(indices[1].offset), nil
-}
-
-// truncateTail discards any recent data before the provided threshold number.
-func (t *freezerTable) truncateTail(items uint64) error {
- t.lock.Lock()
- defer t.lock.Unlock()
-
- // Ensure the given truncate target falls in the correct range
- if t.itemHidden.Load() >= items {
- return nil
- }
- if t.items.Load() < items {
- return errors.New("truncation above head")
- }
- // Load the new tail index by the given new tail position
- var (
- newTailId uint32
- buffer = make([]byte, indexEntrySize)
- )
- if t.items.Load() == items {
- newTailId = t.headId
- } else {
- offset := items - t.itemOffset.Load()
- if _, err := t.index.ReadAt(buffer, int64((offset+1)*indexEntrySize)); err != nil {
- return err
- }
- var newTail indexEntry
- newTail.unmarshalBinary(buffer)
- newTailId = newTail.filenum
- }
- // Save the old size for metrics tracking. This needs to be done
- // before any updates to either itemHidden or itemOffset.
- oldSize, err := t.sizeNolock()
- if err != nil {
- return err
- }
- // Update the virtual tail marker and hidden these entries in table.
- t.itemHidden.Store(items)
- if err := writeMetadata(t.meta, newMetadata(items)); err != nil {
- return err
- }
- // Hidden items still fall in the current tail file, no data file
- // can be dropped.
- if t.tailId == newTailId {
- return nil
- }
- // Hidden items fall in the incorrect range, returns the error.
- if t.tailId > newTailId {
- return fmt.Errorf("invalid index, tail-file %d, item-file %d", t.tailId, newTailId)
- }
- // Count how many items can be deleted from the file.
- var (
- newDeleted = items
- deleted = t.itemOffset.Load()
- )
- // Hidden items exceed the current tail file, drop the relevant data files.
- for current := items - 1; current >= deleted; current -= 1 {
- if _, err := t.index.ReadAt(buffer, int64((current-deleted+1)*indexEntrySize)); err != nil {
- return err
- }
- var pre indexEntry
- pre.unmarshalBinary(buffer)
- if pre.filenum != newTailId {
- break
- }
- newDeleted = current
- }
- // Commit the changes of metadata file first before manipulating
- // the indexes file.
- if err := t.meta.Sync(); err != nil {
- return err
- }
- // Close the index file before shorten it.
- if err := t.index.Close(); err != nil {
- return err
- }
- // Truncate the deleted index entries from the index file.
- err = copyFrom(t.index.Name(), t.index.Name(), indexEntrySize*(newDeleted-deleted+1), func(f *os.File) error {
- tailIndex := indexEntry{
- filenum: newTailId,
- offset: uint32(newDeleted),
- }
- _, err := f.Write(tailIndex.append(nil))
- return err
- })
- if err != nil {
- return err
- }
- // Reopen the modified index file to load the changes
- t.index, err = openFreezerFileForAppend(t.index.Name())
- if err != nil {
- return err
- }
- // Sync the file to ensure changes are flushed to disk
- if err := t.index.Sync(); err != nil {
- return err
- }
- // Release any files before the current tail
- t.tailId = newTailId
- t.itemOffset.Store(newDeleted)
- t.releaseFilesBefore(t.tailId, true)
-
- // Retrieve the new size and update the total size counter
- newSize, err := t.sizeNolock()
- if err != nil {
- return err
- }
- t.sizeGauge.Dec(int64(oldSize - newSize))
- return nil
-}
-
-// Close closes all opened files.
-func (t *freezerTable) Close() error {
- t.lock.Lock()
- defer t.lock.Unlock()
-
- var errs []error
- doClose := func(f *os.File, sync bool, close bool) {
- if sync && !t.readonly {
- if err := f.Sync(); err != nil {
- errs = append(errs, err)
- }
- }
- if close {
- if err := f.Close(); err != nil {
- errs = append(errs, err)
- }
- }
- }
- // Trying to fsync a file opened in rdonly causes "Access denied"
- // error on Windows.
- doClose(t.index, true, true)
- doClose(t.meta, true, true)
-
- // The preopened non-head data-files are all opened in readonly.
- // The head is opened in rw-mode, so we sync it here - but since it's also
- // part of t.files, it will be closed in the loop below.
- doClose(t.head, true, false) // sync but do not close
-
- for _, f := range t.files {
- doClose(f, false, true) // close but do not sync
- }
- t.index = nil
- t.meta = nil
- t.head = nil
-
- if errs != nil {
- return fmt.Errorf("%v", errs)
- }
- return nil
-}
-
-// openFile assumes that the write-lock is held by the caller
-func (t *freezerTable) openFile(num uint32, opener func(string) (*os.File, error)) (f *os.File, err error) {
- var exist bool
- if f, exist = t.files[num]; !exist {
- var name string
- if t.noCompression {
- name = fmt.Sprintf("%s.%04d.rdat", t.name, num)
- } else {
- name = fmt.Sprintf("%s.%04d.cdat", t.name, num)
- }
- f, err = opener(filepath.Join(t.path, name))
- if err != nil {
- return nil, err
- }
- t.files[num] = f
- }
- return f, err
-}
-
-// releaseFile closes a file, and removes it from the open file cache.
-// Assumes that the caller holds the write lock
-func (t *freezerTable) releaseFile(num uint32) {
- if f, exist := t.files[num]; exist {
- delete(t.files, num)
- f.Close()
- }
-}
-
-// releaseFilesAfter closes all open files with a higher number, and optionally also deletes the files
-func (t *freezerTable) releaseFilesAfter(num uint32, remove bool) {
- for fnum, f := range t.files {
- if fnum > num {
- delete(t.files, fnum)
- f.Close()
- if remove {
- os.Remove(f.Name())
- }
- }
- }
-}
-
-// releaseFilesBefore closes all open files with a lower number, and optionally also deletes the files
-func (t *freezerTable) releaseFilesBefore(num uint32, remove bool) {
- for fnum, f := range t.files {
- if fnum < num {
- delete(t.files, fnum)
- f.Close()
- if remove {
- os.Remove(f.Name())
- }
- }
- }
-}
-
-// getIndices returns the index entries for the given from-item, covering 'count' items.
-// N.B: The actual number of returned indices for N items will always be N+1 (unless an
-// error is returned).
-// OBS: This method assumes that the caller has already verified (and/or trimmed) the range
-// so that the items are within bounds. If this method is used to read out of bounds,
-// it will return error.
-func (t *freezerTable) getIndices(from, count uint64) ([]*indexEntry, error) {
- // Apply the table-offset
- from = from - t.itemOffset.Load()
-
- // For reading N items, we need N+1 indices.
- buffer := make([]byte, (count+1)*indexEntrySize)
- if _, err := t.index.ReadAt(buffer, int64(from*indexEntrySize)); err != nil {
- return nil, err
- }
- var (
- indices []*indexEntry
- offset int
- )
- for i := from; i <= from+count; i++ {
- index := new(indexEntry)
- index.unmarshalBinary(buffer[offset:])
- offset += indexEntrySize
- indices = append(indices, index)
- }
- if from == 0 {
- // Special case if we're reading the first item in the freezer. We assume that
- // the first item always start from zero(regarding the deletion, we
- // only support deletion by files, so that the assumption is held).
- // This means we can use the first item metadata to carry information about
- // the 'global' offset, for the deletion-case
- indices[0].offset = 0
- indices[0].filenum = indices[1].filenum
- }
- return indices, nil
-}
-
-// Retrieve looks up the data offset of an item with the given number and retrieves
-// the raw binary blob from the data file.
-func (t *freezerTable) Retrieve(item uint64) ([]byte, error) {
- items, err := t.RetrieveItems(item, 1, 0)
- if err != nil {
- return nil, err
- }
- return items[0], nil
-}
-
-// RetrieveItems returns multiple items in sequence, starting from the index 'start'.
-// It will return at most 'max' items, but will abort earlier to respect the
-// 'maxBytes' argument. However, if the 'maxBytes' is smaller than the size of one
-// item, it _will_ return one element and possibly overflow the maxBytes.
-func (t *freezerTable) RetrieveItems(start, count, maxBytes uint64) ([][]byte, error) {
- // First we read the 'raw' data, which might be compressed.
- diskData, sizes, err := t.retrieveItems(start, count, maxBytes)
- if err != nil {
- return nil, err
- }
- var (
- output = make([][]byte, 0, count)
- offset int // offset for reading
- outputSize int // size of uncompressed data
- )
- // Now slice up the data and decompress.
- for i, diskSize := range sizes {
- item := diskData[offset : offset+diskSize]
- offset += diskSize
- decompressedSize := diskSize
- if !t.noCompression {
- decompressedSize, _ = snappy.DecodedLen(item)
- }
- if i > 0 && maxBytes != 0 && uint64(outputSize+decompressedSize) > maxBytes {
- break
- }
- if !t.noCompression {
- data, err := snappy.Decode(nil, item)
- if err != nil {
- return nil, err
- }
- output = append(output, data)
- } else {
- output = append(output, item)
- }
- outputSize += decompressedSize
- }
- return output, nil
-}
-
-// retrieveItems reads up to 'count' items from the table. It reads at least
-// one item, but otherwise avoids reading more than maxBytes bytes. Freezer
-// will ignore the size limitation and continuously allocate memory to store
-// data if maxBytes is 0. It returns the (potentially compressed) data, and
-// the sizes.
-func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []int, error) {
- t.lock.RLock()
- defer t.lock.RUnlock()
-
- // Ensure the table and the item are accessible
- if t.index == nil || t.head == nil || t.meta == nil {
- return nil, nil, errClosed
- }
- var (
- items = t.items.Load() // the total items(head + 1)
- hidden = t.itemHidden.Load() // the number of hidden items
- )
- // Ensure the start is written, not deleted from the tail, and that the
- // caller actually wants something
- if items <= start || hidden > start || count == 0 {
- return nil, nil, errOutOfBounds
- }
- if start+count > items {
- count = items - start
- }
- var output []byte // Buffer to read data into
- if maxBytes != 0 {
- output = make([]byte, 0, maxBytes)
- } else {
- output = make([]byte, 0, 1024) // initial buffer cap
- }
- // readData is a helper method to read a single data item from disk.
- readData := func(fileId, start uint32, length int) error {
- output = grow(output, length)
- dataFile, exist := t.files[fileId]
- if !exist {
- return fmt.Errorf("missing data file %d", fileId)
- }
- if _, err := dataFile.ReadAt(output[len(output)-length:], int64(start)); err != nil {
- return fmt.Errorf("%w, fileid: %d, start: %d, length: %d", err, fileId, start, length)
- }
- return nil
- }
- // Read all the indexes in one go
- indices, err := t.getIndices(start, count)
- if err != nil {
- return nil, nil, err
- }
- var (
- sizes []int // The sizes for each element
- totalSize = 0 // The total size of all data read so far
- readStart = indices[0].offset // Where, in the file, to start reading
- unreadSize = 0 // The size of the as-yet-unread data
- )
-
- for i, firstIndex := range indices[:len(indices)-1] {
- secondIndex := indices[i+1]
- // Determine the size of the item.
- offset1, offset2, _ := firstIndex.bounds(secondIndex)
- size := int(offset2 - offset1)
- // Crossing a file boundary?
- if secondIndex.filenum != firstIndex.filenum {
- // If we have unread data in the first file, we need to do that read now.
- if unreadSize > 0 {
- if err := readData(firstIndex.filenum, readStart, unreadSize); err != nil {
- return nil, nil, err
- }
- unreadSize = 0
- }
- readStart = 0
- }
- if i > 0 && uint64(totalSize+size) > maxBytes && maxBytes != 0 {
- // About to break out due to byte limit being exceeded. We don't
- // read this last item, but we need to do the deferred reads now.
- if unreadSize > 0 {
- if err := readData(secondIndex.filenum, readStart, unreadSize); err != nil {
- return nil, nil, err
- }
- }
- break
- }
- // Defer the read for later
- unreadSize += size
- totalSize += size
- sizes = append(sizes, size)
- if i == len(indices)-2 || (uint64(totalSize) > maxBytes && maxBytes != 0) {
- // Last item, need to do the read now
- if err := readData(secondIndex.filenum, readStart, unreadSize); err != nil {
- return nil, nil, err
- }
- break
- }
- }
-
- // Update metrics.
- t.readMeter.Mark(int64(totalSize))
- return output, sizes, nil
-}
-
-// has returns an indicator whether the specified number data is still accessible
-// in the freezer table.
-func (t *freezerTable) has(number uint64) bool {
- return t.items.Load() > number && t.itemHidden.Load() <= number
-}
-
-// size returns the total data size in the freezer table.
-func (t *freezerTable) size() (uint64, error) {
- t.lock.RLock()
- defer t.lock.RUnlock()
-
- return t.sizeNolock()
-}
-
-// sizeNolock returns the total data size in the freezer table. This function
-// assumes the lock is already held.
-func (t *freezerTable) sizeNolock() (uint64, error) {
- stat, err := t.index.Stat()
- if err != nil {
- return 0, err
- }
- hidden, err := t.sizeHidden()
- if err != nil {
- return 0, err
- }
- total := uint64(t.maxFileSize)*uint64(t.headId-t.tailId) + uint64(t.headBytes) + uint64(stat.Size()) - hidden
- return total, nil
-}
-
-// advanceHead should be called when the current head file would outgrow the file limits,
-// and a new file must be opened. The caller of this method must hold the write-lock
-// before calling this method.
-func (t *freezerTable) advanceHead() error {
- t.lock.Lock()
- defer t.lock.Unlock()
-
- // We open the next file in truncated mode -- if this file already
- // exists, we need to start over from scratch on it.
- nextID := t.headId + 1
- newHead, err := t.openFile(nextID, openFreezerFileTruncated)
- if err != nil {
- return err
- }
- // Commit the contents of the old file to stable storage and
- // tear it down. It will be re-opened in read-only mode.
- if err := t.head.Sync(); err != nil {
- return err
- }
- t.releaseFile(t.headId)
- t.openFile(t.headId, openFreezerFileForReadOnly)
-
- // Swap out the current head.
- t.head = newHead
- t.headBytes = 0
- t.headId = nextID
- return nil
-}
-
-// Sync pushes any pending data from memory out to disk. This is an expensive
-// operation, so use it with care.
-func (t *freezerTable) Sync() error {
- t.lock.Lock()
- defer t.lock.Unlock()
- if t.index == nil || t.head == nil || t.meta == nil {
- return errClosed
- }
- var err error
- trackError := func(e error) {
- if e != nil && err == nil {
- err = e
- }
- }
-
- trackError(t.index.Sync())
- trackError(t.meta.Sync())
- trackError(t.head.Sync())
- return err
-}
-
-func (t *freezerTable) dumpIndexStdout(start, stop int64) {
- t.dumpIndex(os.Stdout, start, stop)
-}
-
-func (t *freezerTable) dumpIndexString(start, stop int64) string {
- var out bytes.Buffer
- out.WriteString("\n")
- t.dumpIndex(&out, start, stop)
- return out.String()
-}
-
-func (t *freezerTable) dumpIndex(w io.Writer, start, stop int64) {
- meta, err := readMetadata(t.meta)
- if err != nil {
- fmt.Fprintf(w, "Failed to decode freezer table %v\n", err)
- return
- }
- fmt.Fprintf(w, "Version %d count %d, deleted %d, hidden %d\n", meta.Version,
- t.items.Load(), t.itemOffset.Load(), t.itemHidden.Load())
-
- buf := make([]byte, indexEntrySize)
-
- fmt.Fprintf(w, "| number | fileno | offset |\n")
- fmt.Fprintf(w, "|--------|--------|--------|\n")
-
- for i := uint64(start); ; i++ {
- if _, err := t.index.ReadAt(buf, int64((i+1)*indexEntrySize)); err != nil {
- break
- }
- var entry indexEntry
- entry.unmarshalBinary(buf)
- fmt.Fprintf(w, "| %03d | %03d | %03d | \n", i, entry.filenum, entry.offset)
- if stop > 0 && i >= uint64(stop) {
- break
- }
- }
- fmt.Fprintf(w, "|--------------------------|\n")
-}
diff --git a/core/rawdb/freezer_table_test.go b/core/rawdb/freezer_table_test.go
deleted file mode 100644
index 4471463932..0000000000
--- a/core/rawdb/freezer_table_test.go
+++ /dev/null
@@ -1,1369 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "math/rand"
- "os"
- "path/filepath"
- "reflect"
- "testing"
- "testing/quick"
-
- "github.com/davecgh/go-spew/spew"
- "github.com/ethereum/go-ethereum/metrics"
- "github.com/stretchr/testify/require"
-)
-
-// TestFreezerBasics test initializing a freezertable from scratch, writing to the table,
-// and reading it back.
-func TestFreezerBasics(t *testing.T) {
- t.Parallel()
- // set cutoff at 50 bytes
- f, err := newTable(os.TempDir(),
- fmt.Sprintf("unittest-%d", rand.Uint64()),
- metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- defer f.Close()
-
- // Write 15 bytes 255 times, results in 85 files
- writeChunks(t, f, 255, 15)
-
- //print(t, f, 0)
- //print(t, f, 1)
- //print(t, f, 2)
- //
- //db[0] = 000000000000000000000000000000
- //db[1] = 010101010101010101010101010101
- //db[2] = 020202020202020202020202020202
-
- for y := 0; y < 255; y++ {
- exp := getChunk(15, y)
- got, err := f.Retrieve(uint64(y))
- if err != nil {
- t.Fatalf("reading item %d: %v", y, err)
- }
- if !bytes.Equal(got, exp) {
- t.Fatalf("test %d, got \n%x != \n%x", y, got, exp)
- }
- }
- // Check that we cannot read too far
- _, err = f.Retrieve(uint64(255))
- if err != errOutOfBounds {
- t.Fatal(err)
- }
-}
-
-// TestFreezerBasicsClosing tests same as TestFreezerBasics, but also closes and reopens the freezer between
-// every operation
-func TestFreezerBasicsClosing(t *testing.T) {
- t.Parallel()
- // set cutoff at 50 bytes
- var (
- fname = fmt.Sprintf("basics-close-%d", rand.Uint64())
- rm, wm, sg = metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- f *freezerTable
- err error
- )
- f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
-
- // Write 15 bytes 255 times, results in 85 files.
- // In-between writes, the table is closed and re-opened.
- for x := 0; x < 255; x++ {
- data := getChunk(15, x)
- batch := f.newBatch()
- require.NoError(t, batch.AppendRaw(uint64(x), data))
- require.NoError(t, batch.commit())
- f.Close()
-
- f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- }
- defer f.Close()
-
- for y := 0; y < 255; y++ {
- exp := getChunk(15, y)
- got, err := f.Retrieve(uint64(y))
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(got, exp) {
- t.Fatalf("test %d, got \n%x != \n%x", y, got, exp)
- }
- f.Close()
- f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- }
-}
-
-// TestFreezerRepairDanglingHead tests that we can recover if index entries are removed
-func TestFreezerRepairDanglingHead(t *testing.T) {
- t.Parallel()
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("dangling_headtest-%d", rand.Uint64())
-
- // Fill table
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // Write 15 bytes 255 times
- writeChunks(t, f, 255, 15)
-
- // The last item should be there
- if _, err = f.Retrieve(0xfe); err != nil {
- t.Fatal(err)
- }
- f.Close()
- }
-
- // open the index
- idxFile, err := os.OpenFile(filepath.Join(os.TempDir(), fmt.Sprintf("%s.ridx", fname)), os.O_RDWR, 0644)
- if err != nil {
- t.Fatalf("Failed to open index file: %v", err)
- }
- // Remove 4 bytes
- stat, err := idxFile.Stat()
- if err != nil {
- t.Fatalf("Failed to stat index file: %v", err)
- }
- idxFile.Truncate(stat.Size() - 4)
- idxFile.Close()
-
- // Now open it again
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // The last item should be missing
- if _, err = f.Retrieve(0xff); err == nil {
- t.Errorf("Expected error for missing index entry")
- }
- // The one before should still be there
- if _, err = f.Retrieve(0xfd); err != nil {
- t.Fatalf("Expected no error, got %v", err)
- }
- }
-}
-
-// TestFreezerRepairDanglingHeadLarge tests that we can recover if very many index entries are removed
-func TestFreezerRepairDanglingHeadLarge(t *testing.T) {
- t.Parallel()
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("dangling_headtest-%d", rand.Uint64())
-
- // Fill a table and close it
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // Write 15 bytes 255 times
- writeChunks(t, f, 255, 15)
-
- // The last item should be there
- if _, err = f.Retrieve(f.items.Load() - 1); err != nil {
- t.Fatal(err)
- }
- f.Close()
- }
-
- // open the index
- idxFile, err := os.OpenFile(filepath.Join(os.TempDir(), fmt.Sprintf("%s.ridx", fname)), os.O_RDWR, 0644)
- if err != nil {
- t.Fatalf("Failed to open index file: %v", err)
- }
- // Remove everything but the first item, and leave data unaligned
- // 0-indexEntry, 1-indexEntry, corrupt-indexEntry
- idxFile.Truncate(2*indexEntrySize + indexEntrySize/2)
- idxFile.Close()
-
- // Now open it again
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // The first item should be there
- if _, err = f.Retrieve(0); err != nil {
- t.Fatal(err)
- }
- // The second item should be missing
- if _, err = f.Retrieve(1); err == nil {
- t.Errorf("Expected error for missing index entry")
- }
- // We should now be able to store items again, from item = 1
- batch := f.newBatch()
- for x := 1; x < 0xff; x++ {
- require.NoError(t, batch.AppendRaw(uint64(x), getChunk(15, ^x)))
- }
- require.NoError(t, batch.commit())
- f.Close()
- }
-
- // And if we open it, we should now be able to read all of them (new values)
- {
- f, _ := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- for y := 1; y < 255; y++ {
- exp := getChunk(15, ^y)
- got, err := f.Retrieve(uint64(y))
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(got, exp) {
- t.Fatalf("test %d, got \n%x != \n%x", y, got, exp)
- }
- }
- }
-}
-
-// TestSnappyDetection tests that we fail to open a snappy database and vice versa
-func TestSnappyDetection(t *testing.T) {
- t.Parallel()
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("snappytest-%d", rand.Uint64())
-
- // Open with snappy
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // Write 15 bytes 255 times
- writeChunks(t, f, 255, 15)
- f.Close()
- }
-
- // Open without snappy
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, false, false)
- if err != nil {
- t.Fatal(err)
- }
- if _, err = f.Retrieve(0); err == nil {
- f.Close()
- t.Fatalf("expected empty table")
- }
- }
-
- // Open with snappy
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // There should be 255 items
- if _, err = f.Retrieve(0xfe); err != nil {
- f.Close()
- t.Fatalf("expected no error, got %v", err)
- }
- }
-}
-
-func assertFileSize(f string, size int64) error {
- stat, err := os.Stat(f)
- if err != nil {
- return err
- }
- if stat.Size() != size {
- return fmt.Errorf("error, expected size %d, got %d", size, stat.Size())
- }
- return nil
-}
-
-// TestFreezerRepairDanglingIndex checks that if the index has more entries than there are data,
-// the index is repaired
-func TestFreezerRepairDanglingIndex(t *testing.T) {
- t.Parallel()
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("dangling_indextest-%d", rand.Uint64())
-
- // Fill a table and close it
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // Write 15 bytes 9 times : 150 bytes
- writeChunks(t, f, 9, 15)
-
- // The last item should be there
- if _, err = f.Retrieve(f.items.Load() - 1); err != nil {
- f.Close()
- t.Fatal(err)
- }
- f.Close()
- // File sizes should be 45, 45, 45 : items[3, 3, 3)
- }
-
- // Crop third file
- fileToCrop := filepath.Join(os.TempDir(), fmt.Sprintf("%s.0002.rdat", fname))
- // Truncate third file: 45 ,45, 20
- {
- if err := assertFileSize(fileToCrop, 45); err != nil {
- t.Fatal(err)
- }
- file, err := os.OpenFile(fileToCrop, os.O_RDWR, 0644)
- if err != nil {
- t.Fatal(err)
- }
- file.Truncate(20)
- file.Close()
- }
-
- // Open db it again
- // It should restore the file(s) to
- // 45, 45, 15
- // with 3+3+1 items
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- defer f.Close()
- if f.items.Load() != 7 {
- t.Fatalf("expected %d items, got %d", 7, f.items.Load())
- }
- if err := assertFileSize(fileToCrop, 15); err != nil {
- t.Fatal(err)
- }
- }
-}
-
-func TestFreezerTruncate(t *testing.T) {
- t.Parallel()
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("truncation-%d", rand.Uint64())
-
- // Fill table
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // Write 15 bytes 30 times
- writeChunks(t, f, 30, 15)
-
- // The last item should be there
- if _, err = f.Retrieve(f.items.Load() - 1); err != nil {
- t.Fatal(err)
- }
- f.Close()
- }
-
- // Reopen, truncate
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- defer f.Close()
- f.truncateHead(10) // 150 bytes
- if f.items.Load() != 10 {
- t.Fatalf("expected %d items, got %d", 10, f.items.Load())
- }
- // 45, 45, 45, 15 -- bytes should be 15
- if f.headBytes != 15 {
- t.Fatalf("expected %d bytes, got %d", 15, f.headBytes)
- }
- }
-}
-
-// TestFreezerRepairFirstFile tests a head file with the very first item only half-written.
-// That will rewind the index, and _should_ truncate the head file
-func TestFreezerRepairFirstFile(t *testing.T) {
- t.Parallel()
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("truncationfirst-%d", rand.Uint64())
-
- // Fill table
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // Write 80 bytes, splitting out into two files
- batch := f.newBatch()
- require.NoError(t, batch.AppendRaw(0, getChunk(40, 0xFF)))
- require.NoError(t, batch.AppendRaw(1, getChunk(40, 0xEE)))
- require.NoError(t, batch.commit())
-
- // The last item should be there
- if _, err = f.Retrieve(1); err != nil {
- t.Fatal(err)
- }
- f.Close()
- }
-
- // Truncate the file in half
- fileToCrop := filepath.Join(os.TempDir(), fmt.Sprintf("%s.0001.rdat", fname))
- {
- if err := assertFileSize(fileToCrop, 40); err != nil {
- t.Fatal(err)
- }
- file, err := os.OpenFile(fileToCrop, os.O_RDWR, 0644)
- if err != nil {
- t.Fatal(err)
- }
- file.Truncate(20)
- file.Close()
- }
-
- // Reopen
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- if f.items.Load() != 1 {
- f.Close()
- t.Fatalf("expected %d items, got %d", 0, f.items.Load())
- }
-
- // Write 40 bytes
- batch := f.newBatch()
- require.NoError(t, batch.AppendRaw(1, getChunk(40, 0xDD)))
- require.NoError(t, batch.commit())
-
- f.Close()
-
- // Should have been truncated down to zero and then 40 written
- if err := assertFileSize(fileToCrop, 40); err != nil {
- t.Fatal(err)
- }
- }
-}
-
-// TestFreezerReadAndTruncate tests:
-// - we have a table open
-// - do some reads, so files are open in readonly
-// - truncate so those files are 'removed'
-// - check that we did not keep the rdonly file descriptors
-func TestFreezerReadAndTruncate(t *testing.T) {
- t.Parallel()
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("read_truncate-%d", rand.Uint64())
-
- // Fill table
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // Write 15 bytes 30 times
- writeChunks(t, f, 30, 15)
-
- // The last item should be there
- if _, err = f.Retrieve(f.items.Load() - 1); err != nil {
- t.Fatal(err)
- }
- f.Close()
- }
-
- // Reopen and read all files
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- if f.items.Load() != 30 {
- f.Close()
- t.Fatalf("expected %d items, got %d", 0, f.items.Load())
- }
- for y := byte(0); y < 30; y++ {
- f.Retrieve(uint64(y))
- }
-
- // Now, truncate back to zero
- f.truncateHead(0)
-
- // Write the data again
- batch := f.newBatch()
- for x := 0; x < 30; x++ {
- require.NoError(t, batch.AppendRaw(uint64(x), getChunk(15, ^x)))
- }
- require.NoError(t, batch.commit())
- f.Close()
- }
-}
-
-func TestFreezerOffset(t *testing.T) {
- t.Parallel()
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("offset-%d", rand.Uint64())
-
- // Fill table
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
- if err != nil {
- t.Fatal(err)
- }
-
- // Write 6 x 20 bytes, splitting out into three files
- batch := f.newBatch()
- require.NoError(t, batch.AppendRaw(0, getChunk(20, 0xFF)))
- require.NoError(t, batch.AppendRaw(1, getChunk(20, 0xEE)))
-
- require.NoError(t, batch.AppendRaw(2, getChunk(20, 0xdd)))
- require.NoError(t, batch.AppendRaw(3, getChunk(20, 0xcc)))
-
- require.NoError(t, batch.AppendRaw(4, getChunk(20, 0xbb)))
- require.NoError(t, batch.AppendRaw(5, getChunk(20, 0xaa)))
- require.NoError(t, batch.commit())
-
- t.Log(f.dumpIndexString(0, 100))
- f.Close()
- }
-
- // Now crop it.
- {
- // delete files 0 and 1
- for i := 0; i < 2; i++ {
- p := filepath.Join(os.TempDir(), fmt.Sprintf("%v.%04d.rdat", fname, i))
- if err := os.Remove(p); err != nil {
- t.Fatal(err)
- }
- }
- // Read the index file
- p := filepath.Join(os.TempDir(), fmt.Sprintf("%v.ridx", fname))
- indexFile, err := os.OpenFile(p, os.O_RDWR, 0644)
- if err != nil {
- t.Fatal(err)
- }
- indexBuf := make([]byte, 7*indexEntrySize)
- indexFile.Read(indexBuf)
-
- // Update the index file, so that we store
- // [ file = 2, offset = 4 ] at index zero
-
- zeroIndex := indexEntry{
- filenum: uint32(2), // First file is 2
- offset: uint32(4), // We have removed four items
- }
- buf := zeroIndex.append(nil)
-
- // Overwrite index zero
- copy(indexBuf, buf)
-
- // Remove the four next indices by overwriting
- copy(indexBuf[indexEntrySize:], indexBuf[indexEntrySize*5:])
- indexFile.WriteAt(indexBuf, 0)
-
- // Need to truncate the moved index items
- indexFile.Truncate(indexEntrySize * (1 + 2))
- indexFile.Close()
- }
-
- // Now open again
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
- if err != nil {
- t.Fatal(err)
- }
- defer f.Close()
- t.Log(f.dumpIndexString(0, 100))
-
- // It should allow writing item 6.
- batch := f.newBatch()
- require.NoError(t, batch.AppendRaw(6, getChunk(20, 0x99)))
- require.NoError(t, batch.commit())
-
- checkRetrieveError(t, f, map[uint64]error{
- 0: errOutOfBounds,
- 1: errOutOfBounds,
- 2: errOutOfBounds,
- 3: errOutOfBounds,
- })
- checkRetrieve(t, f, map[uint64][]byte{
- 4: getChunk(20, 0xbb),
- 5: getChunk(20, 0xaa),
- 6: getChunk(20, 0x99),
- })
- }
-
- // Edit the index again, with a much larger initial offset of 1M.
- {
- // Read the index file
- p := filepath.Join(os.TempDir(), fmt.Sprintf("%v.ridx", fname))
- indexFile, err := os.OpenFile(p, os.O_RDWR, 0644)
- if err != nil {
- t.Fatal(err)
- }
- indexBuf := make([]byte, 3*indexEntrySize)
- indexFile.Read(indexBuf)
-
- // Update the index file, so that we store
- // [ file = 2, offset = 1M ] at index zero
-
- zeroIndex := indexEntry{
- offset: uint32(1000000), // We have removed 1M items
- filenum: uint32(2), // First file is 2
- }
- buf := zeroIndex.append(nil)
-
- // Overwrite index zero
- copy(indexBuf, buf)
- indexFile.WriteAt(indexBuf, 0)
- indexFile.Close()
- }
-
- // Check that existing items have been moved to index 1M.
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
- if err != nil {
- t.Fatal(err)
- }
- defer f.Close()
- t.Log(f.dumpIndexString(0, 100))
-
- checkRetrieveError(t, f, map[uint64]error{
- 0: errOutOfBounds,
- 1: errOutOfBounds,
- 2: errOutOfBounds,
- 3: errOutOfBounds,
- 999999: errOutOfBounds,
- })
- checkRetrieve(t, f, map[uint64][]byte{
- 1000000: getChunk(20, 0xbb),
- 1000001: getChunk(20, 0xaa),
- })
- }
-}
-
-func assertTableSize(t *testing.T, f *freezerTable, size int) {
- t.Helper()
- if got, err := f.size(); got != uint64(size) {
- t.Fatalf("expected size of %d bytes, got %d, err: %v", size, got, err)
- }
-}
-
-func TestTruncateTail(t *testing.T) {
- t.Parallel()
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("truncate-tail-%d", rand.Uint64())
-
- // Fill table
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
- if err != nil {
- t.Fatal(err)
- }
-
- // Write 7 x 20 bytes, splitting out into four files
- batch := f.newBatch()
- require.NoError(t, batch.AppendRaw(0, getChunk(20, 0xFF)))
- require.NoError(t, batch.AppendRaw(1, getChunk(20, 0xEE)))
- require.NoError(t, batch.AppendRaw(2, getChunk(20, 0xdd)))
- require.NoError(t, batch.AppendRaw(3, getChunk(20, 0xcc)))
- require.NoError(t, batch.AppendRaw(4, getChunk(20, 0xbb)))
- require.NoError(t, batch.AppendRaw(5, getChunk(20, 0xaa)))
- require.NoError(t, batch.AppendRaw(6, getChunk(20, 0x11)))
- require.NoError(t, batch.commit())
-
- // nothing to do, all the items should still be there.
- f.truncateTail(0)
- fmt.Println(f.dumpIndexString(0, 1000))
- checkRetrieve(t, f, map[uint64][]byte{
- 0: getChunk(20, 0xFF),
- 1: getChunk(20, 0xEE),
- 2: getChunk(20, 0xdd),
- 3: getChunk(20, 0xcc),
- 4: getChunk(20, 0xbb),
- 5: getChunk(20, 0xaa),
- 6: getChunk(20, 0x11),
- })
- // maxFileSize*fileCount + headBytes + indexFileSize - hiddenBytes
- expected := 20*7 + 48 - 0
- assertTableSize(t, f, expected)
-
- // truncate single element( item 0 ), deletion is only supported at file level
- f.truncateTail(1)
- fmt.Println(f.dumpIndexString(0, 1000))
- checkRetrieveError(t, f, map[uint64]error{
- 0: errOutOfBounds,
- })
- checkRetrieve(t, f, map[uint64][]byte{
- 1: getChunk(20, 0xEE),
- 2: getChunk(20, 0xdd),
- 3: getChunk(20, 0xcc),
- 4: getChunk(20, 0xbb),
- 5: getChunk(20, 0xaa),
- 6: getChunk(20, 0x11),
- })
- expected = 20*7 + 48 - 20
- assertTableSize(t, f, expected)
-
- // Reopen the table, the deletion information should be persisted as well
- f.Close()
- f, err = newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
- if err != nil {
- t.Fatal(err)
- }
- checkRetrieveError(t, f, map[uint64]error{
- 0: errOutOfBounds,
- })
- checkRetrieve(t, f, map[uint64][]byte{
- 1: getChunk(20, 0xEE),
- 2: getChunk(20, 0xdd),
- 3: getChunk(20, 0xcc),
- 4: getChunk(20, 0xbb),
- 5: getChunk(20, 0xaa),
- 6: getChunk(20, 0x11),
- })
-
- // truncate two elements( item 0, item 1 ), the file 0 should be deleted
- f.truncateTail(2)
- checkRetrieveError(t, f, map[uint64]error{
- 0: errOutOfBounds,
- 1: errOutOfBounds,
- })
- checkRetrieve(t, f, map[uint64][]byte{
- 2: getChunk(20, 0xdd),
- 3: getChunk(20, 0xcc),
- 4: getChunk(20, 0xbb),
- 5: getChunk(20, 0xaa),
- 6: getChunk(20, 0x11),
- })
- expected = 20*5 + 36 - 0
- assertTableSize(t, f, expected)
-
- // Reopen the table, the above testing should still pass
- f.Close()
- f, err = newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
- if err != nil {
- t.Fatal(err)
- }
- defer f.Close()
-
- checkRetrieveError(t, f, map[uint64]error{
- 0: errOutOfBounds,
- 1: errOutOfBounds,
- })
- checkRetrieve(t, f, map[uint64][]byte{
- 2: getChunk(20, 0xdd),
- 3: getChunk(20, 0xcc),
- 4: getChunk(20, 0xbb),
- 5: getChunk(20, 0xaa),
- 6: getChunk(20, 0x11),
- })
-
- // truncate 3 more elements( item 2, 3, 4), the file 1 should be deleted
- // file 2 should only contain item 5
- f.truncateTail(5)
- checkRetrieveError(t, f, map[uint64]error{
- 0: errOutOfBounds,
- 1: errOutOfBounds,
- 2: errOutOfBounds,
- 3: errOutOfBounds,
- 4: errOutOfBounds,
- })
- checkRetrieve(t, f, map[uint64][]byte{
- 5: getChunk(20, 0xaa),
- 6: getChunk(20, 0x11),
- })
- expected = 20*3 + 24 - 20
- assertTableSize(t, f, expected)
-
- // truncate all, the entire freezer should be deleted
- f.truncateTail(7)
- checkRetrieveError(t, f, map[uint64]error{
- 0: errOutOfBounds,
- 1: errOutOfBounds,
- 2: errOutOfBounds,
- 3: errOutOfBounds,
- 4: errOutOfBounds,
- 5: errOutOfBounds,
- 6: errOutOfBounds,
- })
- expected = 12
- assertTableSize(t, f, expected)
-}
-
-func TestTruncateHead(t *testing.T) {
- t.Parallel()
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("truncate-head-blow-tail-%d", rand.Uint64())
-
- // Fill table
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
- if err != nil {
- t.Fatal(err)
- }
-
- // Write 7 x 20 bytes, splitting out into four files
- batch := f.newBatch()
- require.NoError(t, batch.AppendRaw(0, getChunk(20, 0xFF)))
- require.NoError(t, batch.AppendRaw(1, getChunk(20, 0xEE)))
- require.NoError(t, batch.AppendRaw(2, getChunk(20, 0xdd)))
- require.NoError(t, batch.AppendRaw(3, getChunk(20, 0xcc)))
- require.NoError(t, batch.AppendRaw(4, getChunk(20, 0xbb)))
- require.NoError(t, batch.AppendRaw(5, getChunk(20, 0xaa)))
- require.NoError(t, batch.AppendRaw(6, getChunk(20, 0x11)))
- require.NoError(t, batch.commit())
-
- f.truncateTail(4) // Tail = 4
-
- // NewHead is required to be 3, the entire table should be truncated
- f.truncateHead(4)
- checkRetrieveError(t, f, map[uint64]error{
- 0: errOutOfBounds, // Deleted by tail
- 1: errOutOfBounds, // Deleted by tail
- 2: errOutOfBounds, // Deleted by tail
- 3: errOutOfBounds, // Deleted by tail
- 4: errOutOfBounds, // Deleted by Head
- 5: errOutOfBounds, // Deleted by Head
- 6: errOutOfBounds, // Deleted by Head
- })
-
- // Append new items
- batch = f.newBatch()
- require.NoError(t, batch.AppendRaw(4, getChunk(20, 0xbb)))
- require.NoError(t, batch.AppendRaw(5, getChunk(20, 0xaa)))
- require.NoError(t, batch.AppendRaw(6, getChunk(20, 0x11)))
- require.NoError(t, batch.commit())
-
- checkRetrieve(t, f, map[uint64][]byte{
- 4: getChunk(20, 0xbb),
- 5: getChunk(20, 0xaa),
- 6: getChunk(20, 0x11),
- })
-}
-
-func checkRetrieve(t *testing.T, f *freezerTable, items map[uint64][]byte) {
- t.Helper()
-
- for item, wantBytes := range items {
- value, err := f.Retrieve(item)
- if err != nil {
- t.Fatalf("can't get expected item %d: %v", item, err)
- }
- if !bytes.Equal(value, wantBytes) {
- t.Fatalf("item %d has wrong value %x (want %x)", item, value, wantBytes)
- }
- }
-}
-
-func checkRetrieveError(t *testing.T, f *freezerTable, items map[uint64]error) {
- t.Helper()
-
- for item, wantError := range items {
- value, err := f.Retrieve(item)
- if err == nil {
- t.Fatalf("unexpected value %x for item %d, want error %v", item, value, wantError)
- }
- if err != wantError {
- t.Fatalf("wrong error for item %d: %v", item, err)
- }
- }
-}
-
-// Gets a chunk of data, filled with 'b'
-func getChunk(size int, b int) []byte {
- data := make([]byte, size)
- for i := range data {
- data[i] = byte(b)
- }
- return data
-}
-
-// TODO (?)
-// - test that if we remove several head-files, aswell as data last data-file,
-// the index is truncated accordingly
-// Right now, the freezer would fail on these conditions:
-// 1. have data files d0, d1, d2, d3
-// 2. remove d2,d3
-//
-// However, all 'normal' failure modes arising due to failing to sync() or save a file
-// should be handled already, and the case described above can only (?) happen if an
-// external process/user deletes files from the filesystem.
-
-func writeChunks(t *testing.T, ft *freezerTable, n int, length int) {
- t.Helper()
-
- batch := ft.newBatch()
- for i := 0; i < n; i++ {
- if err := batch.AppendRaw(uint64(i), getChunk(length, i)); err != nil {
- t.Fatalf("AppendRaw(%d, ...) returned error: %v", i, err)
- }
- }
- if err := batch.commit(); err != nil {
- t.Fatalf("Commit returned error: %v", err)
- }
-}
-
-// TestSequentialRead does some basic tests on the RetrieveItems.
-func TestSequentialRead(t *testing.T) {
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("batchread-%d", rand.Uint64())
- { // Fill table
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // Write 15 bytes 30 times
- writeChunks(t, f, 30, 15)
- f.dumpIndexStdout(0, 30)
- f.Close()
- }
- { // Open it, iterate, verify iteration
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false)
- if err != nil {
- t.Fatal(err)
- }
- items, err := f.RetrieveItems(0, 10000, 100000)
- if err != nil {
- t.Fatal(err)
- }
- if have, want := len(items), 30; have != want {
- t.Fatalf("want %d items, have %d ", want, have)
- }
- for i, have := range items {
- want := getChunk(15, i)
- if !bytes.Equal(want, have) {
- t.Fatalf("data corruption: have\n%x\n, want \n%x\n", have, want)
- }
- }
- f.Close()
- }
- { // Open it, iterate, verify byte limit. The byte limit is less than item
- // size, so each lookup should only return one item
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
- if err != nil {
- t.Fatal(err)
- }
- items, err := f.RetrieveItems(0, 10000, 10)
- if err != nil {
- t.Fatal(err)
- }
- if have, want := len(items), 1; have != want {
- t.Fatalf("want %d items, have %d ", want, have)
- }
- for i, have := range items {
- want := getChunk(15, i)
- if !bytes.Equal(want, have) {
- t.Fatalf("data corruption: have\n%x\n, want \n%x\n", have, want)
- }
- }
- f.Close()
- }
-}
-
-// TestSequentialReadByteLimit does some more advanced tests on batch reads.
-// These tests check that when the byte limit hits, we correctly abort in time,
-// but also properly do all the deferred reads for the previous data, regardless
-// of whether the data crosses a file boundary or not.
-func TestSequentialReadByteLimit(t *testing.T) {
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("batchread-2-%d", rand.Uint64())
- { // Fill table
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // Write 10 bytes 30 times,
- // Splitting it at every 100 bytes (10 items)
- writeChunks(t, f, 30, 10)
- f.Close()
- }
- for i, tc := range []struct {
- items uint64
- limit uint64
- want int
- }{
- {9, 89, 8},
- {10, 99, 9},
- {11, 109, 10},
- {100, 89, 8},
- {100, 99, 9},
- {100, 109, 10},
- } {
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true, false)
- if err != nil {
- t.Fatal(err)
- }
- items, err := f.RetrieveItems(0, tc.items, tc.limit)
- if err != nil {
- t.Fatal(err)
- }
- if have, want := len(items), tc.want; have != want {
- t.Fatalf("test %d: want %d items, have %d ", i, want, have)
- }
- for ii, have := range items {
- want := getChunk(10, ii)
- if !bytes.Equal(want, have) {
- t.Fatalf("test %d: data corruption item %d: have\n%x\n, want \n%x\n", i, ii, have, want)
- }
- }
- f.Close()
- }
- }
-}
-
-// TestSequentialReadNoByteLimit tests the batch-read if maxBytes is not specified.
-// Freezer should return the requested items regardless the size limitation.
-func TestSequentialReadNoByteLimit(t *testing.T) {
- rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
- fname := fmt.Sprintf("batchread-3-%d", rand.Uint64())
- { // Fill table
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true, false)
- if err != nil {
- t.Fatal(err)
- }
- // Write 10 bytes 30 times,
- // Splitting it at every 100 bytes (10 items)
- writeChunks(t, f, 30, 10)
- f.Close()
- }
- for i, tc := range []struct {
- items uint64
- want int
- }{
- {1, 1},
- {30, 30},
- {31, 30},
- } {
- {
- f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true, false)
- if err != nil {
- t.Fatal(err)
- }
- items, err := f.RetrieveItems(0, tc.items, 0)
- if err != nil {
- t.Fatal(err)
- }
- if have, want := len(items), tc.want; have != want {
- t.Fatalf("test %d: want %d items, have %d ", i, want, have)
- }
- for ii, have := range items {
- want := getChunk(10, ii)
- if !bytes.Equal(want, have) {
- t.Fatalf("test %d: data corruption item %d: have\n%x\n, want \n%x\n", i, ii, have, want)
- }
- }
- f.Close()
- }
- }
-}
-
-func TestFreezerReadonly(t *testing.T) {
- tmpdir := os.TempDir()
- // Case 1: Check it fails on non-existent file.
- _, err := newTable(tmpdir,
- fmt.Sprintf("readonlytest-%d", rand.Uint64()),
- metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, true)
- if err == nil {
- t.Fatal("readonly table instantiation should fail for non-existent table")
- }
-
- // Case 2: Check that it fails on invalid index length.
- fname := fmt.Sprintf("readonlytest-%d", rand.Uint64())
- idxFile, err := openFreezerFileForAppend(filepath.Join(tmpdir, fmt.Sprintf("%s.ridx", fname)))
- if err != nil {
- t.Errorf("Failed to open index file: %v\n", err)
- }
- // size should not be a multiple of indexEntrySize.
- idxFile.Write(make([]byte, 17))
- idxFile.Close()
- _, err = newTable(tmpdir, fname,
- metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, true)
- if err == nil {
- t.Errorf("readonly table instantiation should fail for invalid index size")
- }
-
- // Case 3: Open table non-readonly table to write some data.
- // Then corrupt the head file and make sure opening the table
- // again in readonly triggers an error.
- fname = fmt.Sprintf("readonlytest-%d", rand.Uint64())
- f, err := newTable(tmpdir, fname,
- metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, false)
- if err != nil {
- t.Fatalf("failed to instantiate table: %v", err)
- }
- writeChunks(t, f, 8, 32)
- // Corrupt table file
- if _, err := f.head.Write([]byte{1, 1}); err != nil {
- t.Fatal(err)
- }
- if err := f.Close(); err != nil {
- t.Fatal(err)
- }
- _, err = newTable(tmpdir, fname,
- metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, true)
- if err == nil {
- t.Errorf("readonly table instantiation should fail for corrupt table file")
- }
-
- // Case 4: Write some data to a table and later re-open it as readonly.
- // Should be successful.
- fname = fmt.Sprintf("readonlytest-%d", rand.Uint64())
- f, err = newTable(tmpdir, fname,
- metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, false)
- if err != nil {
- t.Fatalf("failed to instantiate table: %v\n", err)
- }
- writeChunks(t, f, 32, 128)
- if err := f.Close(); err != nil {
- t.Fatal(err)
- }
- f, err = newTable(tmpdir, fname,
- metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, true)
- if err != nil {
- t.Fatal(err)
- }
- v, err := f.Retrieve(10)
- if err != nil {
- t.Fatal(err)
- }
- exp := getChunk(128, 10)
- if !bytes.Equal(v, exp) {
- t.Errorf("retrieved value is incorrect")
- }
-
- // Case 5: Now write some data via a batch.
- // This should fail either during AppendRaw or Commit
- batch := f.newBatch()
- writeErr := batch.AppendRaw(32, make([]byte, 1))
- if writeErr == nil {
- writeErr = batch.commit()
- }
- if writeErr == nil {
- t.Fatalf("Writing to readonly table should fail")
- }
-}
-
-// randTest performs random freezer table operations.
-// Instances of this test are created by Generate.
-type randTest []randTestStep
-
-type randTestStep struct {
- op int
- items []uint64 // for append and retrieve
- blobs [][]byte // for append
- target uint64 // for truncate(head/tail)
- err error // for debugging
-}
-
-const (
- opReload = iota
- opAppend
- opRetrieve
- opTruncateHead
- opTruncateHeadAll
- opTruncateTail
- opTruncateTailAll
- opCheckAll
- opMax // boundary value, not an actual op
-)
-
-func getVals(first uint64, n int) [][]byte {
- var ret [][]byte
- for i := 0; i < n; i++ {
- val := make([]byte, 8)
- binary.BigEndian.PutUint64(val, first+uint64(i))
- ret = append(ret, val)
- }
- return ret
-}
-
-func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
- var (
- deleted uint64 // The number of deleted items from tail
- items []uint64 // The index of entries in table
-
- // getItems retrieves the indexes for items in table.
- getItems = func(n int) []uint64 {
- length := len(items)
- if length == 0 {
- return nil
- }
- var ret []uint64
- index := rand.Intn(length)
- for i := index; len(ret) < n && i < length; i++ {
- ret = append(ret, items[i])
- }
- return ret
- }
-
- // addItems appends the given length items into the table.
- addItems = func(n int) []uint64 {
- var first = deleted
- if len(items) != 0 {
- first = items[len(items)-1] + 1
- }
- var ret []uint64
- for i := 0; i < n; i++ {
- ret = append(ret, first+uint64(i))
- }
- items = append(items, ret...)
- return ret
- }
- )
-
- var steps randTest
- for i := 0; i < size; i++ {
- step := randTestStep{op: r.Intn(opMax)}
- switch step.op {
- case opReload, opCheckAll:
- case opAppend:
- num := r.Intn(3)
- step.items = addItems(num)
- if len(step.items) == 0 {
- step.blobs = nil
- } else {
- step.blobs = getVals(step.items[0], num)
- }
- case opRetrieve:
- step.items = getItems(r.Intn(3))
- case opTruncateHead:
- if len(items) == 0 {
- step.target = deleted
- } else {
- index := r.Intn(len(items))
- items = items[:index]
- step.target = deleted + uint64(index)
- }
- case opTruncateHeadAll:
- step.target = deleted
- items = items[:0]
- case opTruncateTail:
- if len(items) == 0 {
- step.target = deleted
- } else {
- index := r.Intn(len(items))
- items = items[index:]
- deleted += uint64(index)
- step.target = deleted
- }
- case opTruncateTailAll:
- step.target = deleted + uint64(len(items))
- items = items[:0]
- deleted = step.target
- }
- steps = append(steps, step)
- }
- return reflect.ValueOf(steps)
-}
-
-func runRandTest(rt randTest) bool {
- fname := fmt.Sprintf("randtest-%d", rand.Uint64())
- f, err := newTable(os.TempDir(), fname, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, false)
- if err != nil {
- panic("failed to initialize table")
- }
- var values [][]byte
- for i, step := range rt {
- switch step.op {
- case opReload:
- f.Close()
- f, err = newTable(os.TempDir(), fname, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, false)
- if err != nil {
- rt[i].err = fmt.Errorf("failed to reload table %v", err)
- }
- case opCheckAll:
- tail := f.itemHidden.Load()
- head := f.items.Load()
-
- if tail == head {
- continue
- }
- got, err := f.RetrieveItems(f.itemHidden.Load(), head-tail, 100000)
- if err != nil {
- rt[i].err = err
- } else {
- if !reflect.DeepEqual(got, values) {
- rt[i].err = fmt.Errorf("mismatch on retrieved values %v %v", got, values)
- }
- }
-
- case opAppend:
- batch := f.newBatch()
- for i := 0; i < len(step.items); i++ {
- batch.AppendRaw(step.items[i], step.blobs[i])
- }
- batch.commit()
- values = append(values, step.blobs...)
-
- case opRetrieve:
- var blobs [][]byte
- if len(step.items) == 0 {
- continue
- }
- tail := f.itemHidden.Load()
- for i := 0; i < len(step.items); i++ {
- blobs = append(blobs, values[step.items[i]-tail])
- }
- got, err := f.RetrieveItems(step.items[0], uint64(len(step.items)), 100000)
- if err != nil {
- rt[i].err = err
- } else {
- if !reflect.DeepEqual(got, blobs) {
- rt[i].err = fmt.Errorf("mismatch on retrieved values %v %v %v", got, blobs, step.items)
- }
- }
-
- case opTruncateHead:
- f.truncateHead(step.target)
-
- length := f.items.Load() - f.itemHidden.Load()
- values = values[:length]
-
- case opTruncateHeadAll:
- f.truncateHead(step.target)
- values = nil
-
- case opTruncateTail:
- prev := f.itemHidden.Load()
- f.truncateTail(step.target)
-
- truncated := f.itemHidden.Load() - prev
- values = values[truncated:]
-
- case opTruncateTailAll:
- f.truncateTail(step.target)
- values = nil
- }
- // Abort the test on error.
- if rt[i].err != nil {
- return false
- }
- }
- f.Close()
- return true
-}
-
-func TestRandom(t *testing.T) {
- if err := quick.Check(runRandTest, nil); err != nil {
- if cerr, ok := err.(*quick.CheckError); ok {
- t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
- }
- t.Fatal(err)
- }
-}
diff --git a/core/rawdb/freezer_test.go b/core/rawdb/freezer_test.go
deleted file mode 100644
index b4bd6a382a..0000000000
--- a/core/rawdb/freezer_test.go
+++ /dev/null
@@ -1,482 +0,0 @@
-// Copyright 2021 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "errors"
- "fmt"
- "math/big"
- "math/rand"
- "os"
- "path"
- "sync"
- "testing"
-
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/stretchr/testify/require"
-)
-
-var freezerTestTableDef = map[string]bool{"test": true}
-
-func TestFreezerModify(t *testing.T) {
- t.Parallel()
-
- // Create test data.
- var valuesRaw [][]byte
- var valuesRLP []*big.Int
- for x := 0; x < 100; x++ {
- v := getChunk(256, x)
- valuesRaw = append(valuesRaw, v)
- iv := big.NewInt(int64(x))
- iv = iv.Exp(iv, iv, nil)
- valuesRLP = append(valuesRLP, iv)
- }
-
- tables := map[string]bool{"raw": true, "rlp": false}
- f, _ := newFreezerForTesting(t, tables)
- defer f.Close()
-
- // Commit test data.
- _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- for i := range valuesRaw {
- if err := op.AppendRaw("raw", uint64(i), valuesRaw[i]); err != nil {
- return err
- }
- if err := op.Append("rlp", uint64(i), valuesRLP[i]); err != nil {
- return err
- }
- }
- return nil
- })
- if err != nil {
- t.Fatal("ModifyAncients failed:", err)
- }
-
- // Dump indexes.
- for _, table := range f.tables {
- t.Log(table.name, "index:", table.dumpIndexString(0, int64(len(valuesRaw))))
- }
-
- // Read back test data.
- checkAncientCount(t, f, "raw", uint64(len(valuesRaw)))
- checkAncientCount(t, f, "rlp", uint64(len(valuesRLP)))
- for i := range valuesRaw {
- v, _ := f.Ancient("raw", uint64(i))
- if !bytes.Equal(v, valuesRaw[i]) {
- t.Fatalf("wrong raw value at %d: %x", i, v)
- }
- ivEnc, _ := f.Ancient("rlp", uint64(i))
- want, _ := rlp.EncodeToBytes(valuesRLP[i])
- if !bytes.Equal(ivEnc, want) {
- t.Fatalf("wrong RLP value at %d: %x", i, ivEnc)
- }
- }
-}
-
-// This checks that ModifyAncients rolls back freezer updates
-// when the function passed to it returns an error.
-func TestFreezerModifyRollback(t *testing.T) {
- t.Parallel()
-
- f, dir := newFreezerForTesting(t, freezerTestTableDef)
-
- theError := errors.New("oops")
- _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- // Append three items. This creates two files immediately,
- // because the table size limit of the test freezer is 2048.
- require.NoError(t, op.AppendRaw("test", 0, make([]byte, 2048)))
- require.NoError(t, op.AppendRaw("test", 1, make([]byte, 2048)))
- require.NoError(t, op.AppendRaw("test", 2, make([]byte, 2048)))
- return theError
- })
- if err != theError {
- t.Errorf("ModifyAncients returned wrong error %q", err)
- }
- checkAncientCount(t, f, "test", 0)
- f.Close()
-
- // Reopen and check that the rolled-back data doesn't reappear.
- tables := map[string]bool{"test": true}
- f2, err := NewFreezer(dir, "", false, 2049, tables)
- if err != nil {
- t.Fatalf("can't reopen freezer after failed ModifyAncients: %v", err)
- }
- defer f2.Close()
- checkAncientCount(t, f2, "test", 0)
-}
-
-// This test runs ModifyAncients and Ancient concurrently with each other.
-func TestFreezerConcurrentModifyRetrieve(t *testing.T) {
- t.Parallel()
-
- f, _ := newFreezerForTesting(t, freezerTestTableDef)
- defer f.Close()
-
- var (
- numReaders = 5
- writeBatchSize = uint64(50)
- written = make(chan uint64, numReaders*6)
- wg sync.WaitGroup
- )
- wg.Add(numReaders + 1)
-
- // Launch the writer. It appends 10000 items in batches.
- go func() {
- defer wg.Done()
- defer close(written)
- for item := uint64(0); item < 10000; item += writeBatchSize {
- _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- for i := uint64(0); i < writeBatchSize; i++ {
- item := item + i
- value := getChunk(32, int(item))
- if err := op.AppendRaw("test", item, value); err != nil {
- return err
- }
- }
- return nil
- })
- if err != nil {
- panic(err)
- }
- for i := 0; i < numReaders; i++ {
- written <- item + writeBatchSize
- }
- }
- }()
-
- // Launch the readers. They read random items from the freezer up to the
- // current frozen item count.
- for i := 0; i < numReaders; i++ {
- go func() {
- defer wg.Done()
- for frozen := range written {
- for rc := 0; rc < 80; rc++ {
- num := uint64(rand.Intn(int(frozen)))
- value, err := f.Ancient("test", num)
- if err != nil {
- panic(fmt.Errorf("error reading %d (frozen %d): %v", num, frozen, err))
- }
- if !bytes.Equal(value, getChunk(32, int(num))) {
- panic(fmt.Errorf("wrong value at %d", num))
- }
- }
- }
- }()
- }
-
- wg.Wait()
-}
-
-// This test runs ModifyAncients and TruncateHead concurrently with each other.
-func TestFreezerConcurrentModifyTruncate(t *testing.T) {
- f, _ := newFreezerForTesting(t, freezerTestTableDef)
- defer f.Close()
-
- var item = make([]byte, 256)
-
- for i := 0; i < 10; i++ {
- // First reset and write 100 items.
- if _, err := f.TruncateHead(0); err != nil {
- t.Fatal("truncate failed:", err)
- }
- _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- for i := uint64(0); i < 100; i++ {
- if err := op.AppendRaw("test", i, item); err != nil {
- return err
- }
- }
- return nil
- })
- if err != nil {
- t.Fatal("modify failed:", err)
- }
- checkAncientCount(t, f, "test", 100)
-
- // Now append 100 more items and truncate concurrently.
- var (
- wg sync.WaitGroup
- truncateErr error
- modifyErr error
- )
- wg.Add(3)
- go func() {
- _, modifyErr = f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
- for i := uint64(100); i < 200; i++ {
- if err := op.AppendRaw("test", i, item); err != nil {
- return err
- }
- }
- return nil
- })
- wg.Done()
- }()
- go func() {
- _, truncateErr = f.TruncateHead(10)
- wg.Done()
- }()
- go func() {
- f.AncientSize("test")
- wg.Done()
- }()
- wg.Wait()
-
- // Now check the outcome. If the truncate operation went through first, the append
- // fails, otherwise it succeeds. In either case, the freezer should be positioned
- // at 10 after both operations are done.
- if truncateErr != nil {
- t.Fatal("concurrent truncate failed:", err)
- }
- if !(errors.Is(modifyErr, nil) || errors.Is(modifyErr, errOutOrderInsertion)) {
- t.Fatal("wrong error from concurrent modify:", modifyErr)
- }
- checkAncientCount(t, f, "test", 10)
- }
-}
-
-func TestFreezerReadonlyValidate(t *testing.T) {
- tables := map[string]bool{"a": true, "b": true}
- dir := t.TempDir()
- // Open non-readonly freezer and fill individual tables
- // with different amount of data.
- f, err := NewFreezer(dir, "", false, 2049, tables)
- if err != nil {
- t.Fatal("can't open freezer", err)
- }
- var item = make([]byte, 1024)
- aBatch := f.tables["a"].newBatch()
- require.NoError(t, aBatch.AppendRaw(0, item))
- require.NoError(t, aBatch.AppendRaw(1, item))
- require.NoError(t, aBatch.AppendRaw(2, item))
- require.NoError(t, aBatch.commit())
- bBatch := f.tables["b"].newBatch()
- require.NoError(t, bBatch.AppendRaw(0, item))
- require.NoError(t, bBatch.commit())
- if f.tables["a"].items.Load() != 3 {
- t.Fatalf("unexpected number of items in table")
- }
- if f.tables["b"].items.Load() != 1 {
- t.Fatalf("unexpected number of items in table")
- }
- require.NoError(t, f.Close())
-
- // Re-openening as readonly should fail when validating
- // table lengths.
- _, err = NewFreezer(dir, "", true, 2049, tables)
- if err == nil {
- t.Fatal("readonly freezer should fail with differing table lengths")
- }
-}
-
-func TestFreezerConcurrentReadonly(t *testing.T) {
- t.Parallel()
-
- tables := map[string]bool{"a": true}
- dir := t.TempDir()
-
- f, err := NewFreezer(dir, "", false, 2049, tables)
- if err != nil {
- t.Fatal("can't open freezer", err)
- }
- var item = make([]byte, 1024)
- batch := f.tables["a"].newBatch()
- items := uint64(10)
- for i := uint64(0); i < items; i++ {
- require.NoError(t, batch.AppendRaw(i, item))
- }
- require.NoError(t, batch.commit())
- if loaded := f.tables["a"].items.Load(); loaded != items {
- t.Fatalf("unexpected number of items in table, want: %d, have: %d", items, loaded)
- }
- require.NoError(t, f.Close())
-
- var (
- wg sync.WaitGroup
- fs = make([]*Freezer, 5)
- errs = make([]error, 5)
- )
- for i := 0; i < 5; i++ {
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
-
- f, err := NewFreezer(dir, "", true, 2049, tables)
- if err == nil {
- fs[i] = f
- } else {
- errs[i] = err
- }
- }(i)
- }
-
- wg.Wait()
-
- for i := range fs {
- if err := errs[i]; err != nil {
- t.Fatal("failed to open freezer", err)
- }
- require.NoError(t, fs[i].Close())
- }
-}
-
-func newFreezerForTesting(t *testing.T, tables map[string]bool) (*Freezer, string) {
- t.Helper()
-
- dir := t.TempDir()
- // note: using low max table size here to ensure the tests actually
- // switch between multiple files.
- f, err := NewFreezer(dir, "", false, 2049, tables)
- if err != nil {
- t.Fatal("can't open freezer", err)
- }
- return f, dir
-}
-
-// checkAncientCount verifies that the freezer contains n items.
-func checkAncientCount(t *testing.T, f *Freezer, kind string, n uint64) {
- t.Helper()
-
- if frozen, _ := f.Ancients(); frozen != n {
- t.Fatalf("Ancients() returned %d, want %d", frozen, n)
- }
-
- // Check at index n-1.
- if n > 0 {
- index := n - 1
- if ok, _ := f.HasAncient(kind, index); !ok {
- t.Errorf("HasAncient(%q, %d) returned false unexpectedly", kind, index)
- }
- if _, err := f.Ancient(kind, index); err != nil {
- t.Errorf("Ancient(%q, %d) returned unexpected error %q", kind, index, err)
- }
- }
-
- // Check at index n.
- index := n
- if ok, _ := f.HasAncient(kind, index); ok {
- t.Errorf("HasAncient(%q, %d) returned true unexpectedly", kind, index)
- }
- if _, err := f.Ancient(kind, index); err == nil {
- t.Errorf("Ancient(%q, %d) didn't return expected error", kind, index)
- } else if err != errOutOfBounds {
- t.Errorf("Ancient(%q, %d) returned unexpected error %q", kind, index, err)
- }
-}
-
-func TestRenameWindows(t *testing.T) {
- var (
- fname = "file.bin"
- fname2 = "file2.bin"
- data = []byte{1, 2, 3, 4}
- data2 = []byte{2, 3, 4, 5}
- data3 = []byte{3, 5, 6, 7}
- dataLen = 4
- )
-
- // Create 2 temp dirs
- dir1 := t.TempDir()
- dir2 := t.TempDir()
-
- // Create file in dir1 and fill with data
- f, err := os.Create(path.Join(dir1, fname))
- if err != nil {
- t.Fatal(err)
- }
- f2, err := os.Create(path.Join(dir1, fname2))
- if err != nil {
- t.Fatal(err)
- }
- f3, err := os.Create(path.Join(dir2, fname2))
- if err != nil {
- t.Fatal(err)
- }
- if _, err := f.Write(data); err != nil {
- t.Fatal(err)
- }
- if _, err := f2.Write(data2); err != nil {
- t.Fatal(err)
- }
- if _, err := f3.Write(data3); err != nil {
- t.Fatal(err)
- }
- if err := f.Close(); err != nil {
- t.Fatal(err)
- }
- if err := f2.Close(); err != nil {
- t.Fatal(err)
- }
- if err := f3.Close(); err != nil {
- t.Fatal(err)
- }
- if err := os.Rename(f.Name(), path.Join(dir2, fname)); err != nil {
- t.Fatal(err)
- }
- if err := os.Rename(f2.Name(), path.Join(dir2, fname2)); err != nil {
- t.Fatal(err)
- }
-
- // Check file contents
- f, err = os.Open(path.Join(dir2, fname))
- if err != nil {
- t.Fatal(err)
- }
- defer f.Close()
- defer os.Remove(f.Name())
- buf := make([]byte, dataLen)
- if _, err := f.Read(buf); err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(buf, data) {
- t.Errorf("unexpected file contents. Got %v\n", buf)
- }
-
- f, err = os.Open(path.Join(dir2, fname2))
- if err != nil {
- t.Fatal(err)
- }
- defer f.Close()
- defer os.Remove(f.Name())
- if _, err := f.Read(buf); err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(buf, data2) {
- t.Errorf("unexpected file contents. Got %v\n", buf)
- }
-}
-
-func TestFreezerCloseSync(t *testing.T) {
- t.Parallel()
- f, _ := newFreezerForTesting(t, map[string]bool{"a": true, "b": true})
- defer f.Close()
-
- // Now, close and sync. This mimics the behaviour if the node is shut down,
- // just as the chain freezer is writing.
- // 1: thread-1: chain treezer writes, via freezeRange (holds lock)
- // 2: thread-2: Close called, waits for write to finish
- // 3: thread-1: finishes writing, releases lock
- // 4: thread-2: obtains lock, completes Close()
- // 5: thread-1: calls f.Sync()
- if err := f.Close(); err != nil {
- t.Fatal(err)
- }
- if err := f.Sync(); err == nil {
- t.Fatalf("want error, have nil")
- } else if have, want := err.Error(), "[closed closed]"; have != want {
- t.Fatalf("want %v, have %v", have, want)
- }
-}
diff --git a/core/rawdb/freezer_utils.go b/core/rawdb/freezer_utils.go
deleted file mode 100644
index 752e95ba6a..0000000000
--- a/core/rawdb/freezer_utils.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "io"
- "os"
- "path/filepath"
-)
-
-// copyFrom copies data from 'srcPath' at offset 'offset' into 'destPath'.
-// The 'destPath' is created if it doesn't exist, otherwise it is overwritten.
-// Before the copy is executed, there is a callback can be registered to
-// manipulate the dest file.
-// It is perfectly valid to have destPath == srcPath.
-func copyFrom(srcPath, destPath string, offset uint64, before func(f *os.File) error) error {
- // Create a temp file in the same dir where we want it to wind up
- f, err := os.CreateTemp(filepath.Dir(destPath), "*")
- if err != nil {
- return err
- }
- fname := f.Name()
-
- // Clean up the leftover file
- defer func() {
- if f != nil {
- f.Close()
- }
- os.Remove(fname)
- }()
- // Apply the given function if it's not nil before we copy
- // the content from the src.
- if before != nil {
- if err := before(f); err != nil {
- return err
- }
- }
- // Open the source file
- src, err := os.Open(srcPath)
- if err != nil {
- return err
- }
- if _, err = src.Seek(int64(offset), 0); err != nil {
- src.Close()
- return err
- }
- // io.Copy uses 32K buffer internally.
- _, err = io.Copy(f, src)
- if err != nil {
- src.Close()
- return err
- }
- // Rename the temporary file to the specified dest name.
- // src may be same as dest, so needs to be closed before
- // we do the final move.
- src.Close()
-
- if err := f.Close(); err != nil {
- return err
- }
- f = nil
- return os.Rename(fname, destPath)
-}
-
-// openFreezerFileForAppend opens a freezer table file and seeks to the end
-func openFreezerFileForAppend(filename string) (*os.File, error) {
- // Open the file without the O_APPEND flag
- // because it has differing behaviour during Truncate operations
- // on different OS's
- file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0644)
- if err != nil {
- return nil, err
- }
- // Seek to end for append
- if _, err = file.Seek(0, io.SeekEnd); err != nil {
- return nil, err
- }
- return file, nil
-}
-
-// openFreezerFileForReadOnly opens a freezer table file for read only access
-func openFreezerFileForReadOnly(filename string) (*os.File, error) {
- return os.OpenFile(filename, os.O_RDONLY, 0644)
-}
-
-// openFreezerFileTruncated opens a freezer table making sure it is truncated
-func openFreezerFileTruncated(filename string) (*os.File, error) {
- return os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
-}
-
-// truncateFreezerFile resizes a freezer table file and seeks to the end
-func truncateFreezerFile(file *os.File, size int64) error {
- if err := file.Truncate(size); err != nil {
- return err
- }
- // Seek to end for append
- if _, err := file.Seek(0, io.SeekEnd); err != nil {
- return err
- }
- return nil
-}
-
-// grow prepares the slice space for new item, and doubles the slice capacity
-// if space is not enough.
-func grow(buf []byte, n int) []byte {
- if cap(buf)-len(buf) < n {
- newcap := 2 * cap(buf)
- if newcap-len(buf) < n {
- newcap = len(buf) + n
- }
- nbuf := make([]byte, len(buf), newcap)
- copy(nbuf, buf)
- buf = nbuf
- }
- buf = buf[:len(buf)+n]
- return buf
-}
diff --git a/core/rawdb/freezer_utils_test.go b/core/rawdb/freezer_utils_test.go
deleted file mode 100644
index 829cbfb4f3..0000000000
--- a/core/rawdb/freezer_utils_test.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "os"
- "testing"
-)
-
-func TestCopyFrom(t *testing.T) {
- var (
- content = []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}
- prefix = []byte{0x9, 0xa, 0xb, 0xc, 0xd, 0xf}
- )
- var cases = []struct {
- src, dest string
- offset uint64
- writePrefix bool
- }{
- {"foo", "bar", 0, false},
- {"foo", "bar", 1, false},
- {"foo", "bar", 8, false},
- {"foo", "foo", 0, false},
- {"foo", "foo", 1, false},
- {"foo", "foo", 8, false},
- {"foo", "bar", 0, true},
- {"foo", "bar", 1, true},
- {"foo", "bar", 8, true},
- }
- for _, c := range cases {
- os.WriteFile(c.src, content, 0600)
-
- if err := copyFrom(c.src, c.dest, c.offset, func(f *os.File) error {
- if !c.writePrefix {
- return nil
- }
- f.Write(prefix)
- return nil
- }); err != nil {
- os.Remove(c.src)
- t.Fatalf("Failed to copy %v", err)
- }
-
- blob, err := os.ReadFile(c.dest)
- if err != nil {
- os.Remove(c.src)
- os.Remove(c.dest)
- t.Fatalf("Failed to read %v", err)
- }
- want := content[c.offset:]
- if c.writePrefix {
- want = append(prefix, want...)
- }
- if !bytes.Equal(blob, want) {
- t.Fatal("Unexpected value")
- }
- os.Remove(c.src)
- os.Remove(c.dest)
- }
-}
diff --git a/core/rawdb/key_length_iterator.go b/core/rawdb/key_length_iterator.go
deleted file mode 100644
index d1c5af269a..0000000000
--- a/core/rawdb/key_length_iterator.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import "github.com/ethereum/go-ethereum/ethdb"
-
-// KeyLengthIterator is a wrapper for a database iterator that ensures only key-value pairs
-// with a specific key length will be returned.
-type KeyLengthIterator struct {
- requiredKeyLength int
- ethdb.Iterator
-}
-
-// NewKeyLengthIterator returns a wrapped version of the iterator that will only return key-value
-// pairs where keys with a specific key length will be returned.
-func NewKeyLengthIterator(it ethdb.Iterator, keyLen int) ethdb.Iterator {
- return &KeyLengthIterator{
- Iterator: it,
- requiredKeyLength: keyLen,
- }
-}
-
-func (it *KeyLengthIterator) Next() bool {
- // Return true as soon as a key with the required key length is discovered
- for it.Iterator.Next() {
- if len(it.Iterator.Key()) == it.requiredKeyLength {
- return true
- }
- }
-
- // Return false when we exhaust the keys in the underlying iterator.
- return false
-}
diff --git a/core/rawdb/key_length_iterator_test.go b/core/rawdb/key_length_iterator_test.go
deleted file mode 100644
index 654efc5b55..0000000000
--- a/core/rawdb/key_length_iterator_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2022 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "encoding/binary"
- "testing"
-)
-
-func TestKeyLengthIterator(t *testing.T) {
- db := NewMemoryDatabase()
-
- keyLen := 8
- expectedKeys := make(map[string]struct{})
- for i := 0; i < 100; i++ {
- key := make([]byte, keyLen)
- binary.BigEndian.PutUint64(key, uint64(i))
- if err := db.Put(key, []byte{0x1}); err != nil {
- t.Fatal(err)
- }
- expectedKeys[string(key)] = struct{}{}
-
- longerKey := make([]byte, keyLen*2)
- binary.BigEndian.PutUint64(longerKey, uint64(i))
- if err := db.Put(longerKey, []byte{0x1}); err != nil {
- t.Fatal(err)
- }
- }
-
- it := NewKeyLengthIterator(db.NewIterator(nil, nil), keyLen)
- for it.Next() {
- key := it.Key()
- _, exists := expectedKeys[string(key)]
- if !exists {
- t.Fatalf("Found unexpected key %d", binary.BigEndian.Uint64(key))
- }
- delete(expectedKeys, string(key))
- if len(key) != keyLen {
- t.Fatalf("Found unexpected key in key length iterator with length %d", len(key))
- }
- }
-
- if len(expectedKeys) != 0 {
- t.Fatalf("Expected all keys of length %d to be removed from expected keys during iteration", keyLen)
- }
-}
diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go
deleted file mode 100644
index be03723553..0000000000
--- a/core/rawdb/schema.go
+++ /dev/null
@@ -1,339 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Package rawdb contains a collection of low level database accessors.
-package rawdb
-
-import (
- "bytes"
- "encoding/binary"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/metrics"
-)
-
-// The fields below define the low level database schema prefixing.
-var (
- // databaseVersionKey tracks the current database version.
- databaseVersionKey = []byte("DatabaseVersion")
-
- // headHeaderKey tracks the latest known header's hash.
- headHeaderKey = []byte("LastHeader")
-
- // headBlockKey tracks the latest known full block's hash.
- headBlockKey = []byte("LastBlock")
-
- // headFastBlockKey tracks the latest known incomplete block's hash during fast sync.
- headFastBlockKey = []byte("LastFast")
-
- // headFinalizedBlockKey tracks the latest known finalized block hash.
- headFinalizedBlockKey = []byte("LastFinalized")
-
- // persistentStateIDKey tracks the id of latest stored state(for path-based only).
- persistentStateIDKey = []byte("LastStateID")
-
- // lastPivotKey tracks the last pivot block used by fast sync (to reenable on sethead).
- lastPivotKey = []byte("LastPivot")
-
- // fastTrieProgressKey tracks the number of trie entries imported during fast sync.
- fastTrieProgressKey = []byte("TrieSync")
-
- // snapshotDisabledKey flags that the snapshot should not be maintained due to initial sync.
- snapshotDisabledKey = []byte("SnapshotDisabled")
-
- // SnapshotRootKey tracks the hash of the last snapshot.
- SnapshotRootKey = []byte("SnapshotRoot")
-
- // snapshotJournalKey tracks the in-memory diff layers across restarts.
- snapshotJournalKey = []byte("SnapshotJournal")
-
- // snapshotGeneratorKey tracks the snapshot generation marker across restarts.
- snapshotGeneratorKey = []byte("SnapshotGenerator")
-
- // snapshotRecoveryKey tracks the snapshot recovery marker across restarts.
- snapshotRecoveryKey = []byte("SnapshotRecovery")
-
- // snapshotSyncStatusKey tracks the snapshot sync status across restarts.
- snapshotSyncStatusKey = []byte("SnapshotSyncStatus")
-
- // skeletonSyncStatusKey tracks the skeleton sync status across restarts.
- skeletonSyncStatusKey = []byte("SkeletonSyncStatus")
-
- // trieJournalKey tracks the in-memory trie node layers across restarts.
- trieJournalKey = []byte("TrieJournal")
-
- // txIndexTailKey tracks the oldest block whose transactions have been indexed.
- txIndexTailKey = []byte("TransactionIndexTail")
-
- // fastTxLookupLimitKey tracks the transaction lookup limit during fast sync.
- fastTxLookupLimitKey = []byte("FastTransactionLookupLimit")
-
- // badBlockKey tracks the list of bad blocks seen by local
- badBlockKey = []byte("InvalidBlock")
-
- // uncleanShutdownKey tracks the list of local crashes
- uncleanShutdownKey = []byte("unclean-shutdown") // config prefix for the db
-
- // transitionStatusKey tracks the eth2 transition status.
- transitionStatusKey = []byte("eth2-transition")
-
- // snapSyncStatusFlagKey flags that status of snap sync.
- snapSyncStatusFlagKey = []byte("SnapSyncStatus")
-
- // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
- headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
- headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
- headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
- headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
-
- blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
- blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
-
- txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
- bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
- SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value
- SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value
- CodePrefix = []byte("c") // CodePrefix + code hash -> account code
- skeletonHeaderPrefix = []byte("S") // skeletonHeaderPrefix + num (uint64 big endian) -> header
-
- // Path-based storage scheme of merkle patricia trie.
- trieNodeAccountPrefix = []byte("A") // trieNodeAccountPrefix + hexPath -> trie node
- trieNodeStoragePrefix = []byte("O") // trieNodeStoragePrefix + accountHash + hexPath -> trie node
- stateIDPrefix = []byte("L") // stateIDPrefix + state root -> state id
-
- PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage
- configPrefix = []byte("ethereum-config-") // config prefix for the db
- genesisPrefix = []byte("ethereum-genesis-") // genesis state prefix for the db
-
- // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
- BloomBitsIndexPrefix = []byte("iB")
-
- ChtPrefix = []byte("chtRootV2-") // ChtPrefix + chtNum (uint64 big endian) -> trie root hash
- ChtTablePrefix = []byte("cht-")
- ChtIndexTablePrefix = []byte("chtIndexV2-")
-
- BloomTriePrefix = []byte("bltRoot-") // BloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash
- BloomTrieTablePrefix = []byte("blt-")
- BloomTrieIndexPrefix = []byte("bltIndex-")
-
- CliqueSnapshotPrefix = []byte("clique-")
-
- BestUpdateKey = []byte("update-") // bigEndian64(syncPeriod) -> RLP(types.LightClientUpdate) (nextCommittee only referenced by root hash)
- FixedCommitteeRootKey = []byte("fixedRoot-") // bigEndian64(syncPeriod) -> committee root hash
- SyncCommitteeKey = []byte("committee-") // bigEndian64(syncPeriod) -> serialized committee
-
- preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
- preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
-)
-
-// LegacyTxLookupEntry is the legacy TxLookupEntry definition with some unnecessary
-// fields.
-type LegacyTxLookupEntry struct {
- BlockHash common.Hash
- BlockIndex uint64
- Index uint64
-}
-
-// encodeBlockNumber encodes a block number as big endian uint64
-func encodeBlockNumber(number uint64) []byte {
- enc := make([]byte, 8)
- binary.BigEndian.PutUint64(enc, number)
- return enc
-}
-
-// headerKeyPrefix = headerPrefix + num (uint64 big endian)
-func headerKeyPrefix(number uint64) []byte {
- return append(headerPrefix, encodeBlockNumber(number)...)
-}
-
-// headerKey = headerPrefix + num (uint64 big endian) + hash
-func headerKey(number uint64, hash common.Hash) []byte {
- return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
-}
-
-// headerTDKey = headerPrefix + num (uint64 big endian) + hash + headerTDSuffix
-func headerTDKey(number uint64, hash common.Hash) []byte {
- return append(headerKey(number, hash), headerTDSuffix...)
-}
-
-// headerHashKey = headerPrefix + num (uint64 big endian) + headerHashSuffix
-func headerHashKey(number uint64) []byte {
- return append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)
-}
-
-// headerNumberKey = headerNumberPrefix + hash
-func headerNumberKey(hash common.Hash) []byte {
- return append(headerNumberPrefix, hash.Bytes()...)
-}
-
-// blockBodyKey = blockBodyPrefix + num (uint64 big endian) + hash
-func blockBodyKey(number uint64, hash common.Hash) []byte {
- return append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
-}
-
-// blockReceiptsKey = blockReceiptsPrefix + num (uint64 big endian) + hash
-func blockReceiptsKey(number uint64, hash common.Hash) []byte {
- return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
-}
-
-// txLookupKey = txLookupPrefix + hash
-func txLookupKey(hash common.Hash) []byte {
- return append(txLookupPrefix, hash.Bytes()...)
-}
-
-// accountSnapshotKey = SnapshotAccountPrefix + hash
-func accountSnapshotKey(hash common.Hash) []byte {
- return append(SnapshotAccountPrefix, hash.Bytes()...)
-}
-
-// storageSnapshotKey = SnapshotStoragePrefix + account hash + storage hash
-func storageSnapshotKey(accountHash, storageHash common.Hash) []byte {
- buf := make([]byte, len(SnapshotStoragePrefix)+common.HashLength+common.HashLength)
- n := copy(buf, SnapshotStoragePrefix)
- n += copy(buf[n:], accountHash.Bytes())
- copy(buf[n:], storageHash.Bytes())
- return buf
-}
-
-// storageSnapshotsKey = SnapshotStoragePrefix + account hash + storage hash
-func storageSnapshotsKey(accountHash common.Hash) []byte {
- return append(SnapshotStoragePrefix, accountHash.Bytes()...)
-}
-
-// bloomBitsKey = bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash
-func bloomBitsKey(bit uint, section uint64, hash common.Hash) []byte {
- key := append(append(bloomBitsPrefix, make([]byte, 10)...), hash.Bytes()...)
-
- binary.BigEndian.PutUint16(key[1:], uint16(bit))
- binary.BigEndian.PutUint64(key[3:], section)
-
- return key
-}
-
-// skeletonHeaderKey = skeletonHeaderPrefix + num (uint64 big endian)
-func skeletonHeaderKey(number uint64) []byte {
- return append(skeletonHeaderPrefix, encodeBlockNumber(number)...)
-}
-
-// preimageKey = PreimagePrefix + hash
-func preimageKey(hash common.Hash) []byte {
- return append(PreimagePrefix, hash.Bytes()...)
-}
-
-// codeKey = CodePrefix + hash
-func codeKey(hash common.Hash) []byte {
- return append(CodePrefix, hash.Bytes()...)
-}
-
-// IsCodeKey reports whether the given byte slice is the key of contract code,
-// if so return the raw code hash as well.
-func IsCodeKey(key []byte) (bool, []byte) {
- if bytes.HasPrefix(key, CodePrefix) && len(key) == common.HashLength+len(CodePrefix) {
- return true, key[len(CodePrefix):]
- }
- return false, nil
-}
-
-// configKey = configPrefix + hash
-func configKey(hash common.Hash) []byte {
- return append(configPrefix, hash.Bytes()...)
-}
-
-// genesisStateSpecKey = genesisPrefix + hash
-func genesisStateSpecKey(hash common.Hash) []byte {
- return append(genesisPrefix, hash.Bytes()...)
-}
-
-// stateIDKey = stateIDPrefix + root (32 bytes)
-func stateIDKey(root common.Hash) []byte {
- return append(stateIDPrefix, root.Bytes()...)
-}
-
-// accountTrieNodeKey = trieNodeAccountPrefix + nodePath.
-func accountTrieNodeKey(path []byte) []byte {
- return append(trieNodeAccountPrefix, path...)
-}
-
-// storageTrieNodeKey = trieNodeStoragePrefix + accountHash + nodePath.
-func storageTrieNodeKey(accountHash common.Hash, path []byte) []byte {
- buf := make([]byte, len(trieNodeStoragePrefix)+common.HashLength+len(path))
- n := copy(buf, trieNodeStoragePrefix)
- n += copy(buf[n:], accountHash.Bytes())
- copy(buf[n:], path)
- return buf
-}
-
-// IsLegacyTrieNode reports whether a provided database entry is a legacy trie
-// node. The characteristics of legacy trie node are:
-// - the key length is 32 bytes
-// - the key is the hash of val
-func IsLegacyTrieNode(key []byte, val []byte) bool {
- if len(key) != common.HashLength {
- return false
- }
- return bytes.Equal(key, crypto.Keccak256(val))
-}
-
-// ResolveAccountTrieNodeKey reports whether a provided database entry is an
-// account trie node in path-based state scheme, and returns the resolved
-// node path if so.
-func ResolveAccountTrieNodeKey(key []byte) (bool, []byte) {
- if !bytes.HasPrefix(key, trieNodeAccountPrefix) {
- return false, nil
- }
- // The remaining key should only consist a hex node path
- // whose length is in the range 0 to 64 (64 is excluded
- // since leaves are always wrapped with shortNode).
- if len(key) >= len(trieNodeAccountPrefix)+common.HashLength*2 {
- return false, nil
- }
- return true, key[len(trieNodeAccountPrefix):]
-}
-
-// IsAccountTrieNode reports whether a provided database entry is an account
-// trie node in path-based state scheme.
-func IsAccountTrieNode(key []byte) bool {
- ok, _ := ResolveAccountTrieNodeKey(key)
- return ok
-}
-
-// ResolveStorageTrieNode reports whether a provided database entry is a storage
-// trie node in path-based state scheme, and returns the resolved account hash
-// and node path if so.
-func ResolveStorageTrieNode(key []byte) (bool, common.Hash, []byte) {
- if !bytes.HasPrefix(key, trieNodeStoragePrefix) {
- return false, common.Hash{}, nil
- }
- // The remaining key consists of 2 parts:
- // - 32 bytes account hash
- // - hex node path whose length is in the range 0 to 64
- if len(key) < len(trieNodeStoragePrefix)+common.HashLength {
- return false, common.Hash{}, nil
- }
- if len(key) >= len(trieNodeStoragePrefix)+common.HashLength+common.HashLength*2 {
- return false, common.Hash{}, nil
- }
- accountHash := common.BytesToHash(key[len(trieNodeStoragePrefix) : len(trieNodeStoragePrefix)+common.HashLength])
- return true, accountHash, key[len(trieNodeStoragePrefix)+common.HashLength:]
-}
-
-// IsStorageTrieNode reports whether a provided database entry is a storage
-// trie node in path-based state scheme.
-func IsStorageTrieNode(key []byte) bool {
- ok, _, _ := ResolveStorageTrieNode(key)
- return ok
-}
diff --git a/core/rawdb/table.go b/core/rawdb/table.go
deleted file mode 100644
index 19e4ed5b5c..0000000000
--- a/core/rawdb/table.go
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "github.com/ethereum/go-ethereum/ethdb"
-)
-
-// table is a wrapper around a database that prefixes each key access with a pre-
-// configured string.
-type table struct {
- db ethdb.Database
- prefix string
-}
-
-// NewTable returns a database object that prefixes all keys with a given string.
-func NewTable(db ethdb.Database, prefix string) ethdb.Database {
- return &table{
- db: db,
- prefix: prefix,
- }
-}
-
-// Close is a noop to implement the Database interface.
-func (t *table) Close() error {
- return nil
-}
-
-// Has retrieves if a prefixed version of a key is present in the database.
-func (t *table) Has(key []byte) (bool, error) {
- return t.db.Has(append([]byte(t.prefix), key...))
-}
-
-// Get retrieves the given prefixed key if it's present in the database.
-func (t *table) Get(key []byte) ([]byte, error) {
- return t.db.Get(append([]byte(t.prefix), key...))
-}
-
-// HasAncient is a noop passthrough that just forwards the request to the underlying
-// database.
-func (t *table) HasAncient(kind string, number uint64) (bool, error) {
- return t.db.HasAncient(kind, number)
-}
-
-// Ancient is a noop passthrough that just forwards the request to the underlying
-// database.
-func (t *table) Ancient(kind string, number uint64) ([]byte, error) {
- return t.db.Ancient(kind, number)
-}
-
-// AncientRange is a noop passthrough that just forwards the request to the underlying
-// database.
-func (t *table) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
- return t.db.AncientRange(kind, start, count, maxBytes)
-}
-
-// Ancients is a noop passthrough that just forwards the request to the underlying
-// database.
-func (t *table) Ancients() (uint64, error) {
- return t.db.Ancients()
-}
-
-// Tail is a noop passthrough that just forwards the request to the underlying
-// database.
-func (t *table) Tail() (uint64, error) {
- return t.db.Tail()
-}
-
-// AncientSize is a noop passthrough that just forwards the request to the underlying
-// database.
-func (t *table) AncientSize(kind string) (uint64, error) {
- return t.db.AncientSize(kind)
-}
-
-// ModifyAncients runs an ancient write operation on the underlying database.
-func (t *table) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (int64, error) {
- return t.db.ModifyAncients(fn)
-}
-
-func (t *table) ReadAncients(fn func(reader ethdb.AncientReaderOp) error) (err error) {
- return t.db.ReadAncients(fn)
-}
-
-// TruncateHead is a noop passthrough that just forwards the request to the underlying
-// database.
-func (t *table) TruncateHead(items uint64) (uint64, error) {
- return t.db.TruncateHead(items)
-}
-
-// TruncateTail is a noop passthrough that just forwards the request to the underlying
-// database.
-func (t *table) TruncateTail(items uint64) (uint64, error) {
- return t.db.TruncateTail(items)
-}
-
-// Sync is a noop passthrough that just forwards the request to the underlying
-// database.
-func (t *table) Sync() error {
- return t.db.Sync()
-}
-
-// MigrateTable processes the entries in a given table in sequence
-// converting them to a new format if they're of an old format.
-func (t *table) MigrateTable(kind string, convert convertLegacyFn) error {
- return t.db.MigrateTable(kind, convert)
-}
-
-// AncientDatadir returns the ancient datadir of the underlying database.
-func (t *table) AncientDatadir() (string, error) {
- return t.db.AncientDatadir()
-}
-
-// Put inserts the given value into the database at a prefixed version of the
-// provided key.
-func (t *table) Put(key []byte, value []byte) error {
- return t.db.Put(append([]byte(t.prefix), key...), value)
-}
-
-// Delete removes the given prefixed key from the database.
-func (t *table) Delete(key []byte) error {
- return t.db.Delete(append([]byte(t.prefix), key...))
-}
-
-// NewIterator creates a binary-alphabetical iterator over a subset
-// of database content with a particular key prefix, starting at a particular
-// initial key (or after, if it does not exist).
-func (t *table) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
- innerPrefix := append([]byte(t.prefix), prefix...)
- iter := t.db.NewIterator(innerPrefix, start)
- return &tableIterator{
- iter: iter,
- prefix: t.prefix,
- }
-}
-
-// Stat returns a particular internal stat of the database.
-func (t *table) Stat(property string) (string, error) {
- return t.db.Stat(property)
-}
-
-// Compact flattens the underlying data store for the given key range. In essence,
-// deleted and overwritten versions are discarded, and the data is rearranged to
-// reduce the cost of operations needed to access them.
-//
-// A nil start is treated as a key before all keys in the data store; a nil limit
-// is treated as a key after all keys in the data store. If both is nil then it
-// will compact entire data store.
-func (t *table) Compact(start []byte, limit []byte) error {
- // If no start was specified, use the table prefix as the first value
- if start == nil {
- start = []byte(t.prefix)
- } else {
- start = append([]byte(t.prefix), start...)
- }
- // If no limit was specified, use the first element not matching the prefix
- // as the limit
- if limit == nil {
- limit = []byte(t.prefix)
- for i := len(limit) - 1; i >= 0; i-- {
- // Bump the current character, stopping if it doesn't overflow
- limit[i]++
- if limit[i] > 0 {
- break
- }
- // Character overflown, proceed to the next or nil if the last
- if i == 0 {
- limit = nil
- }
- }
- } else {
- limit = append([]byte(t.prefix), limit...)
- }
- // Range correctly calculated based on table prefix, delegate down
- return t.db.Compact(start, limit)
-}
-
-// NewBatch creates a write-only database that buffers changes to its host db
-// until a final write is called, each operation prefixing all keys with the
-// pre-configured string.
-func (t *table) NewBatch() ethdb.Batch {
- return &tableBatch{t.db.NewBatch(), t.prefix}
-}
-
-// NewBatchWithSize creates a write-only database batch with pre-allocated buffer.
-func (t *table) NewBatchWithSize(size int) ethdb.Batch {
- return &tableBatch{t.db.NewBatchWithSize(size), t.prefix}
-}
-
-// NewSnapshot creates a database snapshot based on the current state.
-// The created snapshot will not be affected by all following mutations
-// happened on the database.
-func (t *table) NewSnapshot() (ethdb.Snapshot, error) {
- return t.db.NewSnapshot()
-}
-
-// tableBatch is a wrapper around a database batch that prefixes each key access
-// with a pre-configured string.
-type tableBatch struct {
- batch ethdb.Batch
- prefix string
-}
-
-// Put inserts the given value into the batch for later committing.
-func (b *tableBatch) Put(key, value []byte) error {
- return b.batch.Put(append([]byte(b.prefix), key...), value)
-}
-
-// Delete inserts a key removal into the batch for later committing.
-func (b *tableBatch) Delete(key []byte) error {
- return b.batch.Delete(append([]byte(b.prefix), key...))
-}
-
-// ValueSize retrieves the amount of data queued up for writing.
-func (b *tableBatch) ValueSize() int {
- return b.batch.ValueSize()
-}
-
-// Write flushes any accumulated data to disk.
-func (b *tableBatch) Write() error {
- return b.batch.Write()
-}
-
-// Reset resets the batch for reuse.
-func (b *tableBatch) Reset() {
- b.batch.Reset()
-}
-
-// tableReplayer is a wrapper around a batch replayer which truncates
-// the added prefix.
-type tableReplayer struct {
- w ethdb.KeyValueWriter
- prefix string
-}
-
-// Put implements the interface KeyValueWriter.
-func (r *tableReplayer) Put(key []byte, value []byte) error {
- trimmed := key[len(r.prefix):]
- return r.w.Put(trimmed, value)
-}
-
-// Delete implements the interface KeyValueWriter.
-func (r *tableReplayer) Delete(key []byte) error {
- trimmed := key[len(r.prefix):]
- return r.w.Delete(trimmed)
-}
-
-// Replay replays the batch contents.
-func (b *tableBatch) Replay(w ethdb.KeyValueWriter) error {
- return b.batch.Replay(&tableReplayer{w: w, prefix: b.prefix})
-}
-
-// tableIterator is a wrapper around a database iterator that prefixes each key access
-// with a pre-configured string.
-type tableIterator struct {
- iter ethdb.Iterator
- prefix string
-}
-
-// Next moves the iterator to the next key/value pair. It returns whether the
-// iterator is exhausted.
-func (iter *tableIterator) Next() bool {
- return iter.iter.Next()
-}
-
-// Error returns any accumulated error. Exhausting all the key/value pairs
-// is not considered to be an error.
-func (iter *tableIterator) Error() error {
- return iter.iter.Error()
-}
-
-// Key returns the key of the current key/value pair, or nil if done. The caller
-// should not modify the contents of the returned slice, and its contents may
-// change on the next call to Next.
-func (iter *tableIterator) Key() []byte {
- key := iter.iter.Key()
- if key == nil {
- return nil
- }
- return key[len(iter.prefix):]
-}
-
-// Value returns the value of the current key/value pair, or nil if done. The
-// caller should not modify the contents of the returned slice, and its contents
-// may change on the next call to Next.
-func (iter *tableIterator) Value() []byte {
- return iter.iter.Value()
-}
-
-// Release releases associated resources. Release should always succeed and can
-// be called multiple times without causing error.
-func (iter *tableIterator) Release() {
- iter.iter.Release()
-}
diff --git a/core/rawdb/table_test.go b/core/rawdb/table_test.go
deleted file mode 100644
index aa6adf3e72..0000000000
--- a/core/rawdb/table_test.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rawdb
-
-import (
- "bytes"
- "testing"
-
- "github.com/ethereum/go-ethereum/ethdb"
-)
-
-func TestTableDatabase(t *testing.T) { testTableDatabase(t, "prefix") }
-func TestEmptyPrefixTableDatabase(t *testing.T) { testTableDatabase(t, "") }
-
-type testReplayer struct {
- puts [][]byte
- dels [][]byte
-}
-
-func (r *testReplayer) Put(key []byte, value []byte) error {
- r.puts = append(r.puts, key)
- return nil
-}
-
-func (r *testReplayer) Delete(key []byte) error {
- r.dels = append(r.dels, key)
- return nil
-}
-
-func testTableDatabase(t *testing.T, prefix string) {
- db := NewTable(NewMemoryDatabase(), prefix)
-
- var entries = []struct {
- key []byte
- value []byte
- }{
- {[]byte{0x01, 0x02}, []byte{0x0a, 0x0b}},
- {[]byte{0x03, 0x04}, []byte{0x0c, 0x0d}},
- {[]byte{0x05, 0x06}, []byte{0x0e, 0x0f}},
-
- {[]byte{0xff, 0xff, 0x01}, []byte{0x1a, 0x1b}},
- {[]byte{0xff, 0xff, 0x02}, []byte{0x1c, 0x1d}},
- {[]byte{0xff, 0xff, 0x03}, []byte{0x1e, 0x1f}},
- }
-
- // Test Put/Get operation
- for _, entry := range entries {
- db.Put(entry.key, entry.value)
- }
- for _, entry := range entries {
- got, err := db.Get(entry.key)
- if err != nil {
- t.Fatalf("Failed to get value: %v", err)
- }
- if !bytes.Equal(got, entry.value) {
- t.Fatalf("Value mismatch: want=%v, got=%v", entry.value, got)
- }
- }
-
- // Test batch operation
- db = NewTable(NewMemoryDatabase(), prefix)
- batch := db.NewBatch()
- for _, entry := range entries {
- batch.Put(entry.key, entry.value)
- }
- batch.Write()
- for _, entry := range entries {
- got, err := db.Get(entry.key)
- if err != nil {
- t.Fatalf("Failed to get value: %v", err)
- }
- if !bytes.Equal(got, entry.value) {
- t.Fatalf("Value mismatch: want=%v, got=%v", entry.value, got)
- }
- }
-
- // Test batch replayer
- r := &testReplayer{}
- batch.Replay(r)
- for index, entry := range entries {
- got := r.puts[index]
- if !bytes.Equal(got, entry.key) {
- t.Fatalf("Key mismatch: want=%v, got=%v", entry.key, got)
- }
- }
-
- check := func(iter ethdb.Iterator, expCount, index int) {
- count := 0
- for iter.Next() {
- key, value := iter.Key(), iter.Value()
- if !bytes.Equal(key, entries[index].key) {
- t.Fatalf("Key mismatch: want=%v, got=%v", entries[index].key, key)
- }
- if !bytes.Equal(value, entries[index].value) {
- t.Fatalf("Value mismatch: want=%v, got=%v", entries[index].value, value)
- }
- index += 1
- count++
- }
- if count != expCount {
- t.Fatalf("Wrong number of elems, exp %d got %d", expCount, count)
- }
- iter.Release()
- }
- // Test iterators
- check(db.NewIterator(nil, nil), 6, 0)
- // Test iterators with prefix
- check(db.NewIterator([]byte{0xff, 0xff}, nil), 3, 3)
- // Test iterators with start point
- check(db.NewIterator(nil, []byte{0xff, 0xff, 0x02}), 2, 4)
- // Test iterators with prefix and start point
- check(db.NewIterator([]byte{0xee}, nil), 0, 0)
- check(db.NewIterator(nil, []byte{0x00}), 6, 0)
-}
diff --git a/core/rawdb/testdata/stored_receipts.bin b/core/rawdb/testdata/stored_receipts.bin
deleted file mode 100644
index 8204fae09bdef1db96cc7ababca9b938910f8a75..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 99991
zcmdRX2|QHY|NqQbv&$M0*>^3Hq(r17$cCw^swPZ^}D6(aVRQ7}xOJs{I
zks>>pZ2vps!M(a;p6Qw2^L+o~^>RP=oXdIQL?9MkC9EbI}|`LWLTVwATKWIKgZ~6!6tr2vXRSd6$EY1Q81#zZgoQj^?if
zA(=xjT0tN}V?BiBjAj!nd6K{0@m5LnId$v=L}5{R(JL@Wqae5oCsfdvk%`+EFZo@9
zOB;gj8S?jwpdozF)8EcwmtugcUcX(13s}q|I3BK31;@k4NP@Y5xoAFUq6MB^Z}hF3
zS^YQJOJ773Tdg?fd4GLu?GvOQi<)ZL70R~u+6|Y`^GSe?5OyI7aJ)bi;4+S{JBFtY
zgag5|C0E$t)kh}$u1(Yc@2{lsXAjOpA=z65w+J3TIHCr4qy)DJlSKX4-z5e=wTqAt
zi^Yn!gC}a7L6A8(+Zd+nNH6*~TQN*N07p%Q?!niky98qRj#GxI-7Qq|;(bl*AXp^A
zIprQNs}a{ko$ZQ3$$jtjozI}brbPsTceg0Fe`GZDNGn-oC2-zwO$|f=tPIHUuD0P=
z*N3Z9#DZhaVH?O9^IWy(>n+Nfee|B14tK{Pwuu3Ux;V4x3npOLgjCECB8DLPCCrJ8
z3HqC0kgJ2>E(EA6;d8|7IkCUKTfkV1W49ni^sRUqXRp~z17aVu@rw!z;+TP>E(g!l
zYj)ZXS=9;pgS76V0WVUBJ8)&NQ*}Qi9orQ~2`{?tH1@$=w36c;Xj=
z@w+ThJuO`3&vEgaI?uJ4C+`AHSHMF
zR6gf3W{O>u3f~Nv)&^c&IpK|_eY^MWIq6-EL1)%RH3yG9UBBIg1@Xm-{_Co+?OWxU
z#n4OAvrUP%S2%9w?8bR-9nS^#?7*c8OY{*4bf@7>6%H|DZ8ozj`un@d9vqVty10GR
z_nFvs>CerNX?vd`3&0n_Q9w*QNSFNCgRL6hsy#qKzlk`4ehVdO0HM({(u9CH!BKnD
zG_Vd_w+oIZQvNM{xG~3aX_BZ-1WYXNUvR#G#sh4j@bOnoqDJU{$M*8*mjK0i9*LME
z2##*k!K=;BgJSMq;rpbnulMRo&)sHx%pvZzeY=;GdaeQh_nd)az_o@clSTW$oI1B<)71G5!E
z$}M_HPZL2?_RJ68f=gV
zuRylcCfB5#i+wQjZ`CJ)=s`JIk#Jgb^0tMw8FttYcOl?7|J|^EX05~lDV+dABNPZS
zSX_V)4xE<-J!z)PHas5G>QbAU4}%`pWTJ+jj<#@33eAu3;g1$IByuc*enW6JyqE#x
z)JgsowqtKjPfcKgn3Ph)z3|OU5TwE>nc2+x4xE=kmcFc0Mpv`@iBC?JvcaIF2
zthqixcI1xE&+x}aH&=8_hd{vD2FDZXdEwe)@b!e!2kQbdZXUlR?61?stYc6!
zNH1opy%^LuiHgX8;~KrOEO)BSNc%h8M&FI{8x1jNFpN0oD&)p=A6oI%`o45gQPGI$
zkChMwkg{%fg-iRD54hWza04S^yDPXF3b9QPJqR2!{dMU|{zFU?Bw~&rF$B?rz%4LG
zs)niSj*1ga-#4kwAN^}M+t^$veA&zhQkMhKfP)9$V9d^FZC3cv`fz|6bj^Y&cur3`
zRY5p>L(PuKg+Pe#w;QG`3?;B2$Ertxp(NFU9Ngj$8jNmrqwq+(jI)jChRLrYPOp>>
zggfqoD1em#nd~C}NF#|lzi5OlLg#@Y0)a7ogf$cOs0KP#B#0giZd1vRQBN+ujQ}{r
zqwLLJbY~k5J^V4K_;!YGhYUj+v9*&RF_9ZbR{{Ktbyvb_Bh%JmGV_yMngOZbny0_D}$zhw_K
z>nbTJusM*tp~PhUP7wtu>@E*D(zTa=ZPoP`Z!$f&|pMxH4krp
zgqUy2@bpL6;3>aWIuWpi1(}@EN!-{|MvhGX{6g3g;cu>w>te{{kzQ;a+88n!5^Y4;
z@bz16@wSe0^<_MrQx|1QbK&7Iym>DaKm_Z5QO<&Hoh?M}ZPH
zIvB2K=Ue4@QuFVC3uD~4O8vof7FCMdf$B>-IcF`AF2r^>1Z^AB_VaH$TKqG+Li`@B
z&rZv`8mJO{;85WVxgCZ_f|-DZ1uo$~Zdee=_O)aeFZ@h9>$^}&P+{981J7L7lKOt2
zVSzJEY{NpZ(BOE%*RVjL!kx{#99$d_wm>YN_=Vt7q!;_?kOJ(XAPRfnSQ%e{VL&3i
zcv<-|^cWIVCxTPZ1wep+dD{^E3K+wZs5%j}o$lI}E1sYKneC)g;C*uL1I*XPa?xNw
zV0rDt$9Uuv020X)WRib7>B>yk!vN8rPcI@TD<4L3_5rsLg6K5mb`Q{`Vh&M#5*e5B
zt6hmA%MtUYwPf6K-s;usjbR95yG@+Dqo5!x?U#zQ*%XB<1(`xe&5vudl9Ed98*o9v6-}
zlSZnF2V1Ye#=o4bK;7bq)VB5ClhmF-B{jXW=_C4|^mxcVDJsABU=uL12}~F`=-;Y%
ze?|`&DO8@zJc;gc!4(bITtCbY91k~;2FDXhAFMk+xdMkl<-f8X3rqpal_u`tZ+wW_
z$hG>^jGFo0j1rEYKRLn@p4d!3T$POm8-}}?h;L)&!g>=hVO_(mhMl*rvDGS=05J3M
zpTc2=yKhMBGf(IkM<^eyk
z;H;fWY**AIkG{_n3+}}<
z7?Rr4VK~O_art}c6g25IRWj3bfO+#M~
zhtKR=41-EcMVTW3#h-{?w|F?yD#?J|;g`qQDITQ{C|H=x0Y
z;MjpEfR(qf?U+JbSjU39@IQ4d98es*B}Kh(`s#Zg*{omd
z)g@U1PPad&b5UAVH`a3Lc_oka@9}dXHzo$`buDU_A4{OdKH^`jB
z7bGy!j_ZO1AJLGk3sSc`IyO|vqBZbh^I=)P)F9O(yX(~+jxhB?AYD2Acyvd4zo8+8-t>O)8R#K?b7|jtHa0Fn&@n#I1GWLFql*u
zB;P3apsYEXvk}!z>!7kHfsn=9ut9!x~hA
z<@V`~a*=d}hHbON^34B$w>|Y0Pf*do+eE%Q$xNc6`nw^J-i}O`lbyR)klbH8{q2?f
zb7tPJKOhQAG3yBIvH^+SP2g
zr-CRfphR-LEdFk67|ITer2v$YLPtr)Wk|?al$L#me4h7gtR>^g|gd|R}h7p+1*#L
zQaNoic*z@rhhUsLp!8_J;Cxp0$8D0KBv}8SZ%xrHexcVL&tM9TQtkw
zC-0%0Tn;62!bo=qK&hP1G>+0u3@>nCTQXDx?{@RD=qY}u42`6^%6vip6|tQa!Savo
zXmjQLA>rG9#gVzvlbxpB#QHoQuyjZ2;@gyt0kK$0H`Q;(q1>#N+thl)c9uMp&ys!(
z>q`Z)xGG{%5-j*Nj4#(nGbbS!`>4)N=I@N}tx1JWGZ70v@q*-6>4agtOPwVtKY%Mk%&y0sl99&Ae4>8#0X)~W3uHY3oR+0=X!R85b
z+UgS{+Tj(cAhLWIrt_T|xdh!axpMoi#@ZjT8u_mt93YL*e{{*j6{Xq0!!ePR4U*>J^?W>cO-pG~4qb
zAY8`-8s4)f?Rsg!+t*PjM*ge*_!L9|vG7AasbFXhQ7xNmbyYat{Cg}#a&4&F}_)wlMUtO@Cb>c6SeRIvN9Hol(?dc8FJ5P?H>KffCCOR`yNzUbw
z*Y9SPHXMlPbDI*_T2fX%>|Yg*2PI;YTzT@VRv;Ez;;L0*I4&bXcB!(jPu-b^YN-*t
z_#>!#Pf{YuQGR4lPItSQN%nXu^Y$(CZ>LPCDkbruWIpu8^OoCCBk(BmtI-MRvb#TeP3}XNpWAQE3zh{GjFG9
zeI&JK&E_{fu4ad!AYvTtdGApvQp6`m39i^rmvL0bVk2IZ0F4Niu8~n}iK}5UaVSaA
z8{F28hyEn<;J17kfV$POuZFVDoC+?*ZG$C((zOI5-j>
zhQ{4*a)zV;$~BVhhWZMYO56)vVbK%$>RW>7{|p@93mRXeNanEu;WpiA+0`GNP7A45
zW%++<%+ptfFhsP+(voEqU_EyC2f5|iD5CEF^7OtMYIJ4xbGe_FAO5^;t)qnvLQYx>+)=Od5S+TXBN6XLzR4)6`l{SqfcmIv2sX-(TedzSTvP
zo_69zk6UfPNX;<_F5!Q$F@p^`>=?QcoLPzeLxON!xV(^yh&O=VZ)kHI_T#(f|56m;>&
zpuynxux_;T%3(ObUa?M&MvC7~yx5!`q5xLL%8KC?*r$E)fFr6!CuqB|{XNI9*&4wV
zuMjS>uJhAxV=fnTg
z`ZNy$-;qQ+b>Nss$bu%LU+DNeQ
z7D<{FXZFX{IyU5MqaE{|;%
zMzjNT8}<5s>8l;Bprb=h$%-@2B#XO4AqanBs<(Zyqz!}X4(;3jzbJgLiNi>Tqi
zhQrMdJd6%iYgE+Ptaq6_##blz1(Gee8bWwD0FbYrPq!ROqQ(U#Sv9@q-C1hHgc0bF
z_|lc3(GFq|l$wz@lrwg#Rk}Pb-}hEc+f(#%vKB-E0sV0g-blleZ@|ooW!B)tYTO}9
z5PLXCC5by6-iV|7(0(40Wkh`=EuR;a>A(lk^$)eqTBYCby?Y3P({3-b=EY?5sK+ig
z&M7973d%QC-$4|h`9L7gyg|
z5QQb#LavlPjlfo6BkQ>v0c%2)A00h1me0d+93%8H`I8C~<4x?utv>TrF4~;4OfqK!
zr~AarW~#(I4+scxy@)-SK83V(DowVA~n^x?U;iX0ZbQ`_o!t#8S
z5x1l1s3~qo)6tCe*uY~HTX2ViDg_vnD$>goJ&8dnPDBRz47N!=yOX%){w`)2Ph*{^
zp|~h1V4;qv+RGL^Gj^5#1xu&n^i7ejcW&8V3YNc7jLiVv9AP6BBYp^Z;73PPttE&q
zf8UX7n{yb7?WB}tB;U1Lw{!O|-~G>_n+|gBp*piKHa3}>%1DU%i|5C3^=b)#yAq~Q
z)wb+9)-MHt%$mO2Z@69$R|}ArtmB?JV13AgvK*3g(xb!jmt%tczufOpvei%RmwjAf
zwFMU^-xse@WX$3B-<%yEVJoYDIf*w3-?b>#o{a(8bGLMkAEd(7-CRezA^Q6i>wv#lg-90{G
zN3s@%M?`_cP)V@!UW-6UkyJ}tJYSU^1m$C+lNZFJJS{>Te1_C0)=!4i)o4Q$z*z!h
zA?Nd=%2<>q1`d-9rNf|FN<)u{aNUsrXK^=bv1wWs=*I1j_tzGhJm@mAP*(f2&*g3V
zji_hK?~C4x$X0oPiahwma8%IcLLZ!5AUjbtZcwx3y9$E6z4gx%k?_=~Fy
zPAd$r&I^?6sdh0moPIBXfV#w@>YQYEq*@thkIW)yUR=gLY+&%wSf
zhYbPB4A1XgC%f$jd>Lsx!ExsZFWTgDE5*q#>@HkPm-Ttgo-({)tqV)25~$U|YX(aO
z0zqH-jBHMV`)|3gI6VRyVfVt++?UP%$mP1PHwP)Lr=C2!>Ic>M?F{`}2qXYi06IPS
z;mjSn{`VG&eIuCWW3PJ=NRK#jP&}N-1Q&=4vl#VB&_vqcjkl!R#uIA14u8D#{Vh#-
zc%u=P|1^F>ng1rdJre?9xv`u7o2)2a00iTIFF#0HmM2*rCAhRe!oVj4tPpkd%M%ZjXB0t(BNPi<`WGa2v|P}$1)eJo`fH0<{?CR!@g3JN4810y{(
zf}D(wW~mCWWCdHhPN0!1rq1YBDqG%1O(xc-x_^D1bWMrh*Mw%ubsJMfDe5eGX;RR0
z`bq){1V)Xf@>-+#!BHIc)BX@^(ky!}d+MsqYe(Oq)AE;@T5H$&au|-QPeDf;5MY1R
zYXaa1pvQp&+Iwr%TuVxd_WpI@c$eCtcT(aL%=%vmRU<^7rO3a
z(8w2VB}z3eG9NA9-36)-ooyi5z+nUZqmK2(^-4B__y0YVO`cA{_Ah&4-bp%acv;eJ
z!Zcbmvn)!nXCk=ai0xO>4L}KUsUt_JAFGDEs(3lr@Tp)+@s4eX<*S{_u2RHA9MbE|
zI>U)5V9ND5JQ^tzd#|wO0cB_Ru^~rS3FGI$q=u*|7pSNfLN7q{8@XQi
zsMr1Tm*hB>f2II|lv?S5g=y!k{yz;~Mb{oGH`TCyj*Vms=k6-wY
zOtc~oL)d(L{*kNJsVa$v6JeRY9y30uB%a$4g@r;tP8Lx?S=P}4CPtZ=$u{O
z^o~h;`oz)6LZ6R2K4PKFGOh5j=7#e9J7Zuhr}?4;9pa6+*{EC
zd+(bR#Y)gNB_Z@678i;Y`&>WVg@Dr$_3|>d$GL$BR9}*#O6bbDW;#N(_#vyb1kEMe$XAhyS2G!YPDdF)kx
zKg8!rao-LGmv|Zz2$sU3TOlmDwmTwexmrJXptwh`_R~TX02aZ&&0NMzJXlJdLU-d(
zzLYzqkUw0}bmNYOu2h2~t37|E1FoO$p=qV4ue_AQMkwtxJXiw0X=$CUT(
zj7jRPkN#`c@Z74Oh#E3fhA4oPsret%?_>>~TeNnzDZ`;$Yx3(=m!9~=EwS1!WU$gDr2>~Hu+4n?jwdH4;VMWUGc6_(HZc1c9
z6hJ6(BE?uc0?Va~EkDWJQ;;t~?lrZ1u`|1hsRLiF=&s@C4G<93KW0QPwby8TIi|Nm
zWK;OXz^XQHh{95oTs7xCa45<4}@fRZt`Fz1G#y564eOerujS8}otf;dyA&{K~BV
zKL3wvQa9Wj3VhDfAKPo3nuiZ1x$)bJE~nLS?UL)RkGM+YrollnO8xm-*NIxMUTdIv
z5EbOPGm_Rz7rKt>@5RuQAQWVJDZ_}2z9~L+96{5J4=j#dY{3IH-YtAh0rt=*f<3rz
zlbO3+cljmR-0eE;E3$1F66AS_2M>0oJV?RVbvnuE(Qtu_VMuB0?K(W@h?+YRM5o{Wu_H$=zH}F2tJPzEvIl2VE-c%a
z0wA&INOQaH{{r3Lxn1{)KVZNR$tac0aGFoKNsp}d&>Ed}yoAw5>^rr`KPEG9O&r(Z|=+O9)`O%r@05?C24Yew|B
zPjn!?d{(7TG*2`Jxovs*t+9tOrW!4Iwe9;iZh@_>a_#6~p;+A*wn3TIM`pf0_7yEu
z{v1#Fj!d1RQ`2w%@b~~<4ti<2(DN$?!RO-Y!?TT1s~!b8bd0aHYkOdrPh|j(|6SgX
ze|7m(P`$P)^a|D$d5v19(p~rlR24KdHVt0?*eir>H+?z`U8l-G(>!rbL^~W#!@U)`Weu(4`dEUF>-7mo5ZjQTjHKZtDy^
z6;+7Bf#J!xdsSy7RF1q?&b$MiErxQhK1dfst5|G*-H18;gbDJ^<`guPP3YO-;4xgY
ze&yBNlTmmEazt^-_*G)VBhPP{Uug4I@6Hu`>p`Ud
zOIelut!+qtf%>=-FCf8-yO`uZeJFY~nwO||!c%FBdQ)&>DCrpA01z)bWx0SG<
z^rQ+I{B_BO38H{l{2`45S4OWdUiWCC8|ihmPG;isL`zVq>ko$4{@zg>7rCXcxydSO
zCEL~ly-abPr{$}Zb9c}2P%0VbE16%?Q#q$PTHqzf=#-Qw#dsWIbef{5nPk1i*0SNgqY?HwyY4l7`>Js%Ri(b1SB`$oMGk5hs3Ng$ULOi%_GJbCU&j
z-U~KGeAW)wg0F{>@nlUf=G);a+4UX8RoEW!7K01P-0a$;Edw5kH#58{x33W9&W?Ar
zzY1M1KVCVOyxJr*QiFQ+om5uC_Mufp*U&J?=Yvo;<5RFxh+8J!1sJ3oAPmxDkPZ{i
zg5Wk3wxds9t>d7u6J14N(!xhKp6@8C;>)62%^`34`N8q_!VQDaXCrDKYf-3$?KF6M
zSo@uk@v5g)yV6h0TV8rZ-atJ;$+RsEUV3h_G+^mD03FV3JK8FPO`|m-BbzGo^T@#u
z!kynX`U`%Ir>5b*cDl8{0)Lm+7{+v|MfpWO5
z!iJs0-45a@n7EAex+5+!;S4-xV{HEO=?V*Pjon&b$}aG~8-G9aZGAgBc9p4N*>r4~
z!wHXL9S;sW*`6(|b-2wFdS^tr(foz;5VYTuAK=dtH0x<^e!nZ`=@{jT5_^lj`5Y`c
zl1(3F=MjplLSX&(PU}vR`R~~o!K276U9N!|v*fA)T?5u?>$=cjsPEJY76+n^QwX9Oq`c9Rr+>i3I0@>$B9@the#L*L5`$FDa1s+PSMS6w$3}eXN0wcb3kq8T`%3Lc}_Cd-O
zPmb>_sMO>mcEsoT=}Mc295>sc`xkpRYToHS{-{J44F;6%f|S_w^Q
zD&24W=R7C8u^$S9Vf>GuUev$K8#(N;KPk-l9n|2$8O?fhE%%!1^nBP4g~6BxJc_hW
zEyM8w?dBG+Ar@U~F^sg*0`X4pUZH{}>Q&xwnM314ykHyX(85HtN!Fq9ZhO{WDjl#d
zQTt2>%kdm?CuQk+2#Dz?Rv}&-x^)}YOt2J%2fnu4h)#wmEJa)v?M;P2dLg|c;xQPc
zGyeOd?Xy!Qca+Gp4v*G^`%l?4k@p(XZ&jz#DN>d+GAd_^gjnGcUmY0G*{ATd_AJZm
zV8g_FwR*!41uzVh;rJ@+YGD0uItcDMpfk1i_S271=gOzAQMFf^Dreu;CmqMHXH|_X
zXe=K%6hx0VX`C6h`Ywoq$N6LXlu#uu?`zLhtZo*2!~QG27NP*xh~$_cK)>?vQ=!Ld
zDhda7m{Ekhio=YyM~#7^;Y3!rKy27T_5ojsJo^jhD|^;5n1rMsOV05+^kygJwe=c8
z>KBOZ0QPSdy=%Kxhb9??OmZu`9LUcj(1Zqq;ONWHDqVT0`LN<_%7s0zk5Uh8y9rSM
zDU+Q-g}JXV$32D@S%SiLTld&$a0g4=+u!fy;vVdYqQgDd6Lm}+H|M>t&q6Za&uTEr
zcgg6dH^_L-=W1M%BcnRT41voWPj(?Xtz4~C-H+!^_~)&%WTi(T3ZVR8MWk_JGj7Ov
zzeVHUW8L@GzV&JKkJ=w=m7pb3+%en2r8eM0NzUXi_PvAcEe<8RcHO_nWn%}IAnSqd
zH=%JVyT3^!8qunzx8XyHo3pvDKDe-l9c4v%lU@ytH8%fax5%-m)nGO9?ehx^E0#OD&bj{-dOmW=
zk$2MWX?ca4Aa>vtJs5^{-58Z<+#x6dcj$hCJv`X_40pIzh&y~L#~xy(t8xAuCnJt4
z13U}4L=CIYo}^Qz$n`09dH?Dt-Oj-7GhZ)!fPmK&*<^Y48C{avU{4SV{oIMy$qfIS
zTd=uRRLE1tmO;FX7s*f(Y(qY8@%b`J{vD+GOi>)oPY+~zSi&ETRwT0%uY$Xo6;+!
z!BLZX7NP)BCQooRIid*Mkq5v&G>Zi`_Gux#5*ckU6lx^uC5akRkoi>xtbehYRG%^?
zXgjmr7HQcfC0FrI($ptY#j{Ve{1ZN^5Fl`*s2T)qmt8COi`Rd~ZPz*~Qg+c_OLdt1
zI$m(tGTq$$FcKJn!zKL3BXDfG2y<}*udgR@;&1;bRaC%b$A%mc(UBeSugI*{~wetR|YC2lkE}MS3Nj_r;*}f%U56
z?^4yxnf>b7$8BCICcrtZ>%Q;J~~iQzt$-yS@uP!E;*v`Imhm_6eUM
z3Si|qnW6x@n)W}7uJC^vU6BV+$>6waM>AT4#+f*7(JOv2OAYtUYX7>b=N
zt~>Y@EPbGB%5Vkuds#t3$S@SFNUs!Ebqoa^2B{P@%6x0<3%iF}Yj4pU?LH;dbaKDF
zKHy-KSvy)1?dj8>&}h*$6-Qj(L@qQ%CF}F6FrrMD)xz-#8k{KX_a(s!8X*e&^&D^
zykr6&yb$WKApM~LwBxY!5&729KVk_|JPl1XWfBv)In8m%Xs2Y3doehf4j_
zPKA*HJs_NEVtYV>*@jd2(;jfqJ4-*TzkD!&ch_|3;$Df3Sx(62C^T;c6Hy>4yV3xP9?hRltXpf
z5Clf-6R&_Y-87oYEm1p6LeA!v;su8*&wXwcK>G%l
z@IPqZuqg{w$oo0rO#JqbJmKlKZ|!W#ha79L+1|I!6;Axg{D#sj->mJyy#GL(lVKAl|c?Q9@fHiMzxc39%R#M|LAvZIKE#&|4zv;~_20&wk|Z-?I-Tj5NI!
zW@wA2;vr{W?DpOf5G7ZJz*3q;D}kf*E6c@imU7B06Gn&Kr5pT4ln^Gvqr5+#-(S5+e!9u9sQJk
zu9ZFXr&@x~RBVJ$MJ&qpc6&J-%BNPeBtxn3#@KOtB7KZ%`RuL2eWqlSAqQUnBb0$+
zJm@);EssftQaSZJOTf?LA_j?jtEepaax&M2)cqrr+uQtJ<4|TFI7l*-A)Wn$A8&o9
zY87(McOEWaW}rCr{-2;k8oG?Um_u3GOEQ%Ac$$SMv`&-NB-F8uJS}D`BPWNh&=c#I
z8_ANL5ZIO|M{(|ZsidgmH?Jt!c}@<-hafU!S;qp9G5w1@+9N(
zq-lctwjZaSXvOT;Ly=8oz9!rE?Vq4Tu)kM7fkXLB@D0gOTK>Ed((@`cPR9NlXKl;r
z!N+BvR{tZEtF$SNaV7pN%$j5<%fjO6_GIXzZ9{{7*f1q@S6&9w{v(up5z)X7J-{z>
z8E8p{QX?k+w6(RtzK~VibcOZ>rm;1wGXDsrAXRn59Lmd&Nrp0Po2_YVX;*540)>&z
z+v)f3OICjUM<{8{YcdS
z_E{?fQ?r`^c2E8hN|c(=IvmOu`fEsrGLTxoJ$Td5`{TZoUzt|VWI6=L?E6P3>62eI
z&!H^OCmG6%hx;Ck9(ev?bElT$mmizO>&_?^{Uek(E`f=~OYYa$y$&yJD{+VX`nfIo
zdDx$Y$l-j`Q?>*)yxJuT$NTT`Y&(c`h+P>gZWwcbu1Ry3{9ecaKwt$HvtI
zy*{CP#Abh@@#f~Q2m%VULD;)cc+e4b2ah1SD1T)x=5xB?ce<62M=~Jxgpz%RwkN-5
z1&fX}JI?&0E>xmrioolB)Ibjju(83iHoHM#!O0if%u3Z72T_T2ovrmc-Dy<*oq;TCeM?j}9y?kR5{6zt0tnA5uh*Zy
z;h6o4sbPZk`bn-3IR$*J-P~ogJE%SZU|;)w!(x8jFoZpniDPgFS0wIGxdMAA=V!*{
zr@T)3-^~_Rjp93`qW0c@DZI{_*s)6H_cF0#_Ow27#*FK%$bETc&Rp5OEDS6Z0t)n5
z*s%&8bVQ9+2%D*7P^2qzIy@hH>S1TE-J}C55rS%U6$@|y<({TUJ4GS(i!^iTZ&7mGQw`yv}>N&Z_ja{
z5g3jUg)+Y;=)?8fHt4~e;JyysD87euyM16`fY}7g
z3-3ZB&7;f&ZJU)>Cip%7n*P~>YeenE#IIX>d-3f<1->jY|Dv{tpq`svEZtmp-xprt
z?R0=^%r8t(*$jQm3b0AT?E_15FqvSxGIxrQ3GfncT7#uJ7r(*M^27*jVae;W;
z@=|k!*WB__|G_OmhRAHcXQD1`$I+FAhTo=riFJ@sW4BdgB|P32o0OH_bXWUWQG1?5
zgRyJyPFFFhD*fOJWFU(4RSx1#{22%Hj-_mBWRm`4|83AV$qG_aULf6
zR4wIQYd8-;(1UZrNJjChjZ6=_yabo(kw6+zSVH8wc4toR92QAHP;sq@jXPd*gfzn2rw2Y&p(>8=H~h#sFQGK}w!NM)`nIp8v@kcaA=xk#a?Y4Qs_U$5P2C
zZ~ZJH|KwURgHtY-48El$6z{6G^4xS~;PYwJ#z?QWujet;2Z8EZ(#jd0AN0yy!MgU9
z_!Gro?cRX~VtW{GQ^W24(rJu*XZ!ueA7AmYRAlag;5^@YRQt{NHiX!j#-keqtnUUP
zPp*R~AQpezTwgwR4D84CFnGWbHNYhZ4t5y6hzBI?@o+YMNa1{J^J?t#!+5|Ewd_TZ
znC>y&t9CHo0w7Y&Nq9vMLg7;B8lz-#u~#fq
zCrF+aP8Nw>2*M-$Tdgefj~~6`iUSNWHC1O{PTV}Jx2<#%jRzBAYxWwQDScckOA42T
zV;*9gU%sE(-N5OuBgbES-Mx`{;p#*p_FVke=GO~7T9;7S^X<^s~I
zTci_1V+;5h9-Kt=<^*vPEbGOh#7QVU+ArAN91pnvmc;4Dqukg>a`Av8iaCPBY^<+O
z9HXfX(zMIqOG@7x{7Y)U1>%THncG*J&xaozQOpqp*E&?t+L-rxDB6O0mDhUZL6P)h
zv&*>OFBi`Yhg$$RQp^zu5>s5YO`Cj8?YqqZdQlxQ;Zgf9_(3-_gP1E2Ub8-HX4gj+
zpvQwt_>X%$oDIEKX5!JVAajc%cf2^pi8AO5r-c;IzKK=mb^oU@il7a;dUwX$%TLxT|$Z0W$tCyFed)cdVC1b78p!hh@)
z2xME{4#pBbQA=sCIgq@~=OD|b6n`#yz$@TPliVxdc){xxAfWK#e5u06A`tV$;)|aT
z$KSmX
zeG_C`%0;eepuvMPO>Bcl&|%jcm-VM&|sVOQX0RK
z62B@+-wc#~CcrrLSwj%|NyQ7^;E-E=QG
zYeK=)rBI1w&(EfvW>YJnuijc@0Jtymky2QstN?-g-cql$_T2ZDlxpu=e5-r0_t?pe
z89!;K?-f3Md7jw%`@MVGHGo5TWGHQ0xRIRW)*6#%@sN^Y65mlR|3hPqOLD=5)V-{E
zbzdvlAqpUz$gZ4UH#?Gm8w*ZBa0vt7Te9;W+jWw&p_`U@fZlaGw6vQDC!_d|Cw#zR#>0ZQIdUoi-RRdP{@n2fJvBfB(9NN;!H{0eF#br0Sh8T0WO6c
zj@xYgW5cQC_EZryR`D1Xi65iCFv9EA<5_d6MvB!<2Ku&N
z0fcm{ptEuK7mDKt1rOcqde7t!eP74|#J_|?@+buMzTJZ`ez-@_IFp%%yyuK23z`-cZATV+=HR0-0GpOsxaVMEd?C)__|MG-JROW=#
z4h7c^^ipJhol`hIVr{2658SsS_eFXQW}d>3yJ3*6LF8s4zpU@K=tpl(hxTrJ8oBx2CPG32f217FXqV_-LqN0EKYbPw2Ad_OH7)_ZPxyQ=-MoP3NXWhip6lj=`Q`;YMi
z|G%?G$b0zy|IQvE=V!uwWoOu+YVIpL*%ZQ;S?U6Fru{xl4`8;;8ojI}`kln0CJ&oT
z?YO%38PXPHTRpxuiGc;TP}i3TEA-aiD*W-SADH35CH%)T
z9K=?^pbRGUVmS}aB^KvTQ8y`jI@XtJhypVlIMc+=a0q4_P66)>XThcB;gb5eK>UC3
zm7N9Kz#L+@czC%v1W~IJDE=eLd$P{ZYdor>>y+IQ#`LJh4vmjEwG+25?;HgNIb=>q
zuhF-87&3bjEl?c2AhTX#AZ>FPo8w*S+Y+d8E+yy(T)OV4IMMWdlj{7@zlO7o&4t33
z4R70mptPss?~seT2Y_2Ix5ef*tKzhc>n26L)5jBHG{TAgyci`*M?u#OPhPnI2H`AD
zmHsA*6zCh{2%-lO%5{0`w0IscG{+B)sD_UqI9TT38@?HzSW`01%O7l1Fv!(GaJd3%
zcnO~)X3vTJ_1yx-VjQ~#F`{q9%Q$<@W*QLtn2ldlSU|i7);Nd_1e^liUHJ>#!NZ@O
z#ath0<*7Jw0&HPkruxx%(&2h%V0yB~y7@5ZakDAk#rk7CT(lkd(#M6*3|=A81RgH)
zXeg$|vw5cmCT(N9rE~Aen79``_nwS>FF$OJ_IeaIA*t%cGsfzMZnq93_SsXv7Wvi8
zk!96%8tJ3nTeL1ou1B=vMKh0!fCBGP?9)zo&=EDMB#5qyBHq|T`;A(N4jrFrD!vuh*pCQZw7J~0
z$rfOPL)PuF!edjao~xm1$x4S`hUg9>hWYN0U#*ZhEN^84-S4d)dfSyz*7uMIW2*Vx
zEur?$?yy0>Y$%+dJD8ti$n49TR>zwrIcUyxr~$oz3SK~BP&<*{NTj&)T+cMMs*R>(9_h@du8`$9_>S+?!~0>uZ+x
z9kQeTAtxdr}ckX
zUeNb@O0-q?@UM|RV0P`Q4WIRiQl1nA=*zM=27r)MqHmuBh
z56Tjp$O;z-ge_(tTTE2Pb#b>-rnfg{2j-SCxYI=3&pzC|O^D3v4Y8dCI*2isf=;Zw
z=r4w>&LpS+0nA+NDBtD*++KYd
zB>H9K;kb^NX0VwDtrT)Z=W1Mj9)ON%`;U81k0q@tX|Nj&&pyD|rWiKlb>nbI#i(19
zs;t)?2s%jG*b_RW6htBZmv+`qQ~T;mAGyiX`aZ0BRb7xs_?`|QHYxl+B;v|L&o!De
ztq(rzNM<+iNRhQhCVqd*cXX|TSS6cl;{-qcYjH8k1me-)csPQ=@%=DAa6F7`0uu%f
z;sc*RlT&c#pX^10E&B(^?#gq|fA2W*
zW3pvWE`P?CG3eXr!OB>ff@xM62b0Dz7zY!lKqpPLuQZ%~QtWQ7jjJ>-;BW8A56+xB
zx90NRB71pLD*+wRLe9*48#Op~xY#mXXFGNo9r&)-&i{hir;|6Hi6R}?@2%cY+3@2G
zxfKoVTfc#46hVge$M)s4^Ef?Y|=jFn=qO<66D-Nb!J~|Y%(>Kkr4G4&yVHm)e^w%
ze4PW2F^9(u)Q`&rNFT^X2}NAWebs+S1wFV*zT0l#tt&d_^VHg>NgwG;X3h4bnw3dy
z*(jcEo+|m1{zKD|eLYob9vDZ)}
zgQzjvn-`(nbs2}M!UNUqP1!yq`K_&@h2*xk;CppMR^*Q%1LN`b=%LIDY`za63Lt9u
z`*+Lmq6N!T@h=+@wPs7yve7CFW`nyLku?A@a3cd^i6Lm4AafvM5cA-$%Z+ds0_aG)
z+z3}}IryFeXk~s{)o$D@k7wUAVgWulOY<^)xx$WOcdwNu{;?+WVbCM_zVP>fO8mh>
z)XOe6&T{9y_V7kBFy|Lwdqaj3uM2RspOIq-SNjd#(ie#6sa%!GYf<(JY;&BD@682x
zBWkAi(#%51=K9E{^*?9dp^Q0Qxr~5~%a>v*Y2F0nya#fv!D8N-f=unTO
zL-W6~k0GxC{DyUv?%dl-Nb&Kibi6UtG&F-~aRX4n!Sjl)im8`?iaV6)S#t&|5cO}fyFJL)Zo#RAatJih#*hrmoK(mTS-1*#l!l1XPD@Y&Sp7cJ1)4)
zLFggq)1WNbZ=R8VD|shm1n>&Dg#Xwp=p@^EYc~cw=?H2BnFEWIXDN4{vGyiQ1iS*y
zG|9aJju!%60mi#j{;O1%2LE-wKrEj4g!GPL`e4+S6lPEr=j
z7rB=uO!Q0n50zgxP&H?%j!VTP$5I6%te$jM955tN{AAb
ziYOG4Kc8Y}9(
z{|1Gu9!@hjFh5lE;L0ScCm0sojTAR5IME>)76wCtH;dxOpPV?Z3vSXBO+)8w8xgtX
z?0nm@sj?ZwBGsNDhGTB|vpj!dVvNBMPs0n0UOXN2cp45skKzZJwbn7|M9#%{7C|?
zX_I9n(}Sxa+1ZdL^G_e`76}n8xa&X}&pM{@rjGwE
zlG_adr|hvWb@ONKO0J}P_U%#CIr^*<<&X++B7*wcnLav6$yRqy+&pIzY`ma@fxVs&
zDe^}W3(9q%`Gg1v-$*#c78gt7D(*zz)64(MSZPgQv}T!Y()&V4I|ZH@tJaVB$7gPZ``X8
z^~~akIrrFKfRT~4Haf5t({S5-saKF6otu@~g5iR)(>W0MU7qrf*~WLC_O;`#YrQ2O
zbdN?%1yTV7n?w@f3w+T8;^lL!^1QyERZ2_du>1<8^)Ut7SRS5Y{2i`37d31QHEsO#m|z^U1)|ZUwwXT+!aM5BX25zx($kZSlmsN9*Z6Y62r2
zS)UTD4V-^C$+Cq**-PF-`&xC*fajVvh#pX1tY(0v{V2XKfkOUvM04DI+yu|-BUs+C
zHk1A{k%gTtzYfkaQL6jKRXPGV1j&(Q;;52CMGq%AfSSI!5?CGnxdPDizzcx{6w)(XlIukBsL*Y#D7!hDHEN~7m2*Pg{6q^D2j-2cTu@~xjU4(%
zhjywhp^(+XX$Arg6+H+XhGg{w!-Bh!;)Vq$I>a}Xy4wb$k|CKTU0}#yngkzQMxr@l
z$5`!f+XJ#AyZT9G(4(SW@!y$`bG8eAM>5>Wa>bg-8Cs&heR77DzVXT@Iq-y>+k|uR
zmH^pj*rDz<0xW>v1R^8
zAV7a86mo!6=sKYlf1h9hJ-|T&a^(gyJnH2}
zp6Y0ZuRK04ydmP~(d3O2>W0F(4jZK6dGGE1ZEzT*a2!_9?R#m7G!S2*J_zVR=7A*T
zKK@OdPrKq`&&0P_+Sk%IrHwQ~(@#Ki-ce^kun?EggzmX&xnu(%MNw1UxZLhR@vPm8
zMsDps?^wa|QI=fyVBnsl%UkArJ%KkF${3{xWxBzj6o{Bn(SyMOuY(P$SL_Juya?dP
z^4L+q!AcH!{u`mXp4T-&a1Y>GMRE544&7{i=cMeA;J^EJFy1Jovl=RI!Wnfh4}RCc
z(DnMbQ2PGRe_x+0;Vels6PE1x8|gZ+oNAd8auz39yLZ7g9*4x6WYmOAzzu~P%0~$w
zy=)YtpY4lHve5LWM_c-EFodkB>Q=$;FDkpH?b(QkhFYg$tX;SqLtHA{_-P(-tqmcp
zNR~a1%+uiGZ_zB<;nEY(dOE{qSD!~D1O;pk6OuuY%JI}20cpvVBoMwVO*m&ZP_{(Q
zsq{)!um|?bPPP33*XehVJ1XpS+WjIf+`EK>%TDmVgv_0bGG!VNpx);q)Rw2M?qt26
z7^m2fxnT3+J@$|a80e2NP2QVQp(#|}uu_ELnLeZYepsZt-cSKZb%J~FFQ+<9ayTx{
zqC`xCnPxamX@6xIqEUOs3Zy#0u?Cbz*#tN^xy+jCMAX_eUdmEag8u{CjZE<-j5G4;
z%PBU_DBN@N##D%uZJ4OqEG5+B-dC_gABQBjrFUH&^s4}$gVr5@p=f*&Piqf!U~?a-
zU}4i67NTl*6W1}O)UOP_sX}fCw%y&vbv9CDNn944^2VK7YSLF;B|vcKtzvD
zFcO<Tn0mqu+RskoMS*>CkC;~;$D#9gVF+s4iT_;>4dUi^SE>X<0`%)tA
zKA3~s8ITZCv7$6=onLXPEe?zV)*U8!a7f3=l{yu7mLIXSH)(e@mxUN~FSTpFe<9zr
zoxb(~xhr)twxWA|Zx()1I_1H7_12r$S{Ijog#ZPECebJr=+%j_G>t+y<&(K-dBe7Y*}JAYN?5}G1O={
zFenw~mk>H70y?rfB`P{Q_Qdpi*L%{Hv?qKLuT>>P>@}A833Qa{l>Y^~pXrpYyzTCq
z+&S!?yb8}=Y)Dkt#qGEvD^Kgx%}uGkeMh`%U`&Ttgnhj|mlpV{h_HOV?P>wUl7p;bdxf?1MQpH6%)!X_ch3B?^ju7G$vq4nX
zXw;{`+N-KYlZy=6S`Yuw_ElIG+KJj+-EqLnM;O7#vT4{i46w?w8HV-bGA%K8u9=@c
zFgz+#zVR~;qL4t~x<*9Hh1OiocP;Vi>EZs~xkaT{GvC#;ZMY`
zZ2YWoDT^g?4HCFJrJG+%p8ygh0cR8L!N1{bQbkpc_)uBBTn!f76pej+@_kVj*6~Bw
zWx&~lV@+{q6HYG3&L&A~Q-~=`Obecnaj4x?O0gauN~a=N_OmKZa$?tH
zrY#$tu~vS^WPl+c^`4+dyH$)@Uqw5+Gq1*@?7sA+a~}^)%MKp?
z#6*hNxp7>Wf9r
zjGEvM=!v~8pPXm)pFQ|P^DH&v`A14zee36Ho=r*W?2U=K`D9+*6`O`{3CnY}0$g!m
z^?_*+xNis5?UcohF|Fo>Pvts2C4~1^nvpvKpz?MwEA3?7r)9iuG{v>aZ}dZr-pL3g
za0c6QLNn|8i1RGmgMVY5U9BnFH^R1N2;mzK7cQC;*)@%Wyo7Bc;aF3Ao`sVO@;pn@
z8d#GQq^ATwxepg{)ZiM2Bk>>{QEhsNbue&yNt=WeCy~CQx|QIZ4A_`5PaV!YU#JvBw8XQ&H&o