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\x80\xc2?\x80\xc2@\x80\xc2A\x80\xc2B\x80\xc2C\x80\xc2D\x80\xc2E\x80\xc2F\x80\xc2G\x80\xc2H\x80\xc2I\x80\xc2J\x80\xc2K\x80\xc2L\x80\xc2M\x80\xc2N\x80\xc2O\x80\xc2P\x80\xc2Q\x80\xc2R\x80\xc2S\x80\xc2T\x80\xc2U\x80\xc2V\x80\xc2W\x80\xc2X\x80\xc2Y\x80\xc2Z\x80\xc2[\x80\xc2\\\x80\xc2]\x80\xc2^\x80\xc2_\x80\xc2`\x80\xc2a\x80\xc2b\x80\xc2c\x80\xc2d\x80\xc2e\x80\xc2f\x80\xc2g\x80\xc2h\x80\xc2i\x80\xc2j\x80\xc2k\x80\xc2l\x80\xc2m\x80\xc2n\x80\xc2o\x80\xc2p\x80\xc2q\x80\xc2r\x80\xc2s\x80\xc2t\x80\xc2u\x80\xc2v\x80\xc2w\x80\xc2x\x80\xc2y\x80\xc2z\x80\xc2{\x80\xc2|\x80\xc2}\x80\xc2~\x80\xc2\u007f\x80\u00c1\x80\x80\u00c1\x81\x80\u00c1\x82\x80\u00c1\x83\x80\u00c1\x84\x80\u00c1\x85\x80\u00c1\x86\x80\u00c1\x87\x80\u00c1\x88\x80\u00c1\x89\x80\u00c1\x8a\x80\u00c1\x8b\x80\u00c1\x8c\x80\u00c1\x8d\x80\u00c1\x8e\x80\u00c1\x8f\x80\u00c1\x90\x80\u00c1\x91\x80\u00c1\x92\x80\u00c1\x93\x80\u00c1\x94\x80\u00c1\x95\x80\u00c1\x96\x80\u00c1\x97\x80\u00c1\x98\x80\u00c1\x99\x80\u00c1\x9a\x80\u00c1\x9b\x80\u00c1\x9c\x80\u00c1\x9d\x80\u00c1\x9e\x80\u00c1\x9f\x80\u00c1\xa0\x80\u00c1\xa1\x80\u00c1\xa2\x80\u00c1\xa3\x80\u00c1\xa4\x80\u00c1\xa5\x80\u00c1\xa6\x80\u00c1\xa7\x80\u00c1\xa8\x80\u00c1\xa9\x80\u00c1\xaa\x80\u00c1\xab\x80\u00c1\xac\x80\u00c1\xad\x80\u00c1\xae\x80\u00c1\xaf\x80\u00c1\xb0\x80\u00c1\xb1\x80\u00c1\xb2\x80\u00c1\xb3\x80\u00c1\xb4\x80\u00c1\xb5\x80\u00c1\xb6\x80\u00c1\xb7\x80\u00c1\xb8\x80\u00c1\xb9\x80\u00c1\xba\x80\u00c1\xbb\x80\u00c1\xbc\x80\u00c1\xbd\x80\u00c1\xbe\x80\u00c1\xbf\x80\u00c1\xc0\x80\u00c1\xc1\x80\u00c1\u0080\u00c1\u00c0\u00c1\u0100\u00c1\u0140\u00c1\u0180\u00c1\u01c0\u00c1\u0200\u00c1\u0240\u00c1\u0280\u00c1\u02c0\u00c1\u0300\u00c1\u0340\u00c1\u0380\u00c1\u03c0\u00c1\u0400\u00c1\u0440\u00c1\u0480\u00c1\u04c0\u00c1\u0500\u00c1\u0540\u00c1\u0580\u00c1\u05c0\u00c1\u0600\u00c1\u0640\u00c1\u0680\u00c1\u06c0\u00c1\u0700\u00c1\u0740\u00c1\u0780\u00c1\u07c0\u00c1\xe0\x80\u00c1\xe1\x80\u00c1\xe2\x80\u00c1\xe3\x80\u00c1\xe4\x80\u00c1\xe5\x80\u00c1\xe6\x80\u00c1\xe7\x80\u00c1\xe8\x80\u00c1\xe9\x80\u00c1\xea\x80\u00c1\xeb\x80\u00c1\xec\x80\u00c1\xed\x80\u00c1\xee\x80\u00c1\xef\x80\u00c1\xf0\x80\u00c1\xf1\x80\u00c1\xf2\x80\u00c1\xf3\x80\u00c1\xf4\x80\u00c1\xf5\x80\u00c1\xf6\x80\u00c1\xf7\x80\u00c1\xf8\x80\u00c1\xf9\x80\u00c1\xfa\x80\u00c1\xfb\x80\u00c1\xfc\x80\u00c1\xfd\x80\u00c1\xfe\x80\u00c1\xff\x80\u3507KT\xa8\xbd\x15)f\xd6?pk\xae\x1f\xfe\xb0A\x19!\xe5\x8d\f\x9f,\x9c\xd0Ft\xed\xea@\x00\x00\x00"
const rinkebyAllocData = "\xf9\x03\xb7\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\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\xf6\x941\xb9\x8d\x14\x00{\xde\xe67)\x80\x86\x98\x8a\v\xbd1\x18E#\xa0\x02\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"
const goerliAllocData = "\xf9\x04\x06\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\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"
diff --git a/core/genesis_test.go b/core/genesis_test.go
index ba3423e32a..0e966bab03 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -17,6 +17,7 @@
package core
import (
+ "encoding/json"
"math/big"
"reflect"
"testing"
@@ -28,12 +29,14 @@ import (
"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"
)
func TestInvalidCliqueConfig(t *testing.T) {
block := DefaultGoerliGenesisBlock()
block.ExtraData = []byte{}
- if _, err := block.Commit(nil); err == nil {
+ db := rawdb.NewMemoryDatabase()
+ if _, err := block.Commit(db, trie.NewDatabase(db)); err == nil {
t.Fatal("Expected error on invalid clique config")
}
}
@@ -60,7 +63,7 @@ func TestSetupGenesis(t *testing.T) {
{
name: "genesis without ChainConfig",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- return SetupGenesisBlock(db, new(Genesis))
+ return SetupGenesisBlock(db, trie.NewDatabase(db), new(Genesis))
},
wantErr: errGenesisNoConfig,
wantConfig: params.AllEthashProtocolChanges,
@@ -68,7 +71,7 @@ func TestSetupGenesis(t *testing.T) {
{
name: "no block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- return SetupGenesisBlock(db, nil)
+ return SetupGenesisBlock(db, trie.NewDatabase(db), nil)
},
wantHash: params.MainnetGenesisHash,
wantConfig: params.MainnetChainConfig,
@@ -77,7 +80,7 @@ func TestSetupGenesis(t *testing.T) {
name: "mainnet block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
DefaultGenesisBlock().MustCommit(db)
- return SetupGenesisBlock(db, nil)
+ return SetupGenesisBlock(db, trie.NewDatabase(db), nil)
},
wantHash: params.MainnetGenesisHash,
wantConfig: params.MainnetChainConfig,
@@ -86,26 +89,26 @@ func TestSetupGenesis(t *testing.T) {
name: "custom block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
customg.MustCommit(db)
- return SetupGenesisBlock(db, nil)
+ return SetupGenesisBlock(db, trie.NewDatabase(db), nil)
},
wantHash: customghash,
wantConfig: customg.Config,
},
{
- name: "custom block in DB, genesis == ropsten",
+ name: "custom block in DB, genesis == goerli",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
customg.MustCommit(db)
- return SetupGenesisBlock(db, DefaultRopstenGenesisBlock())
+ return SetupGenesisBlock(db, trie.NewDatabase(db), DefaultGoerliGenesisBlock())
},
- wantErr: &GenesisMismatchError{Stored: customghash, New: params.RopstenGenesisHash},
- wantHash: params.RopstenGenesisHash,
- wantConfig: params.RopstenChainConfig,
+ 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) {
oldcustomg.MustCommit(db)
- return SetupGenesisBlock(db, &customg)
+ return SetupGenesisBlock(db, trie.NewDatabase(db), &customg)
},
wantHash: customghash,
wantConfig: customg.Config,
@@ -117,22 +120,22 @@ func TestSetupGenesis(t *testing.T) {
// Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db)
- bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil)
+ bc, _ := NewBlockChain(db, nil, &oldcustomg, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
defer bc.Stop()
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)
bc.InsertChain(blocks)
- bc.CurrentBlock()
+
// This should return a compatibility error.
- return SetupGenesisBlock(db, &customg)
+ return SetupGenesisBlock(db, trie.NewDatabase(db), &customg)
},
wantHash: customghash,
wantConfig: customg.Config,
wantErr: ¶ms.ConfigCompatError{
- What: "Homestead fork block",
- StoredConfig: big.NewInt(2),
- NewConfig: big.NewInt(3),
- RewindTo: 1,
+ What: "Homestead fork block",
+ StoredBlock: big.NewInt(2),
+ NewBlock: big.NewInt(3),
+ RewindToBlock: 1,
},
},
}
@@ -169,7 +172,6 @@ func TestGenesisHashes(t *testing.T) {
}{
{DefaultGenesisBlock(), params.MainnetGenesisHash},
{DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
- {DefaultRopstenGenesisBlock(), params.RopstenGenesisHash},
{DefaultRinkebyGenesisBlock(), params.RinkebyGenesisHash},
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
} {
@@ -193,6 +195,7 @@ func TestGenesis_Commit(t *testing.T) {
db := rawdb.NewMemoryDatabase()
genesisBlock := genesis.MustCommit(db)
+
if genesis.Difficulty != nil {
t.Fatalf("assumption wrong")
}
@@ -219,7 +222,8 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
}
hash, _ = alloc.deriveHash()
)
- alloc.flush(db)
+ blob, _ := json.Marshal(alloc)
+ rawdb.WriteGenesisStateSpec(db, hash, blob)
var reload GenesisAlloc
err := reload.UnmarshalJSON(rawdb.ReadGenesisStateSpec(db, hash))
diff --git a/core/headerchain.go b/core/headerchain.go
index d8c415f336..aed3c720c6 100644
--- a/core/headerchain.go
+++ b/core/headerchain.go
@@ -27,6 +27,7 @@ import (
"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"
@@ -34,7 +35,6 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
- lru "github.com/hashicorp/golang-lru"
)
const (
@@ -64,9 +64,9 @@ type HeaderChain struct {
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 // Cache for the most recent block headers
- tdCache *lru.Cache // Cache for the most recent block total difficulties
- numberCache *lru.Cache // Cache for the most recent block numbers
+ 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
@@ -77,10 +77,6 @@ type HeaderChain struct {
// 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) {
- headerCache, _ := lru.New(headerCacheLimit)
- tdCache, _ := lru.New(tdCacheLimit)
- numberCache, _ := lru.New(numberCacheLimit)
-
// Seed a fast but crypto originating random generator
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
@@ -89,9 +85,9 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
hc := &HeaderChain{
config: config,
chainDb: chainDb,
- headerCache: headerCache,
- tdCache: tdCache,
- numberCache: numberCache,
+ 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,
@@ -115,8 +111,7 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
// from the cache or database
func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 {
if cached, ok := hc.numberCache.Get(hash); ok {
- number := cached.(uint64)
- return &number
+ return &cached
}
number := rawdb.ReadHeaderNumber(hc.chainDb, hash)
if number != nil {
@@ -394,7 +389,7 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time,
if res.ignored > 0 {
context = append(context, []interface{}{"ignored", res.ignored}...)
}
- log.Info("Imported new block headers", context...)
+ log.Debug("Imported new block headers", context...)
return res.status, err
}
@@ -442,7 +437,7 @@ func (hc *HeaderChain) GetAncestor(hash common.Hash, number, ancestor uint64, ma
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.(*big.Int)
+ return cached
}
td := rawdb.ReadTd(hc.chainDb, hash, number)
if td == nil {
@@ -458,7 +453,7 @@ func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {
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.(*types.Header)
+ return header
}
header := rawdb.ReadHeader(hc.chainDb, hash, number)
if header == nil {
@@ -525,10 +520,9 @@ func (hc *HeaderChain) GetHeadersFrom(number, count uint64) []rlp.RawValue {
if !ok {
break
}
- h := header.(*types.Header)
- rlpData, _ := rlp.EncodeToBytes(h)
+ rlpData, _ := rlp.EncodeToBytes(header)
headers = append(headers, rlpData)
- hash = h.ParentHash
+ hash = header.ParentHash
count--
number--
}
@@ -562,7 +556,7 @@ type (
// 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) (uint64, bool)
+ UpdateHeadBlocksCallback func(ethdb.KeyValueWriter, *types.Header) (*types.Header, bool)
// DeleteBlockContentCallback is a callback function that is called by SetHead
// before each header is deleted.
@@ -572,15 +566,46 @@ type (
// 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
)
- for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() {
+ 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 block chain to new head.
+ // Rewind chain to new head
parent := hc.GetHeader(hdr.ParentHash, num-1)
if parent == nil {
parent = hc.genesisHeader
@@ -597,9 +622,9 @@ func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, d
markerBatch := hc.chainDb.NewBatch()
if updateFn != nil {
newHead, force := updateFn(markerBatch, parent)
- if force && newHead < head {
- log.Warn("Force rewinding till ancient limit", "head", newHead)
- head = newHead
+ 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.
diff --git a/core/headerchain_test.go b/core/headerchain_test.go
index ed0522671f..08d19f6950 100644
--- a/core/headerchain_test.go
+++ b/core/headerchain_test.go
@@ -27,8 +27,8 @@ import (
"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/log"
"github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/trie"
)
func verifyUnbrokenCanonchain(hc *HeaderChain) error {
@@ -70,19 +70,18 @@ func testInsert(t *testing.T, hc *HeaderChain, chain []*types.Header, wantStatus
// This test checks status reporting of InsertHeaderChain.
func TestHeaderInsertion(t *testing.T) {
var (
- db = rawdb.NewMemoryDatabase()
- genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
+ db = rawdb.NewMemoryDatabase()
+ gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
)
-
- hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false })
+ gspec.Commit(db, trie.NewDatabase(db))
+ hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
if err != nil {
t.Fatal(err)
}
// chain A: G->A1->A2...A128
- chainA := makeHeaderChain(genesis.Header(), 128, ethash.NewFaker(), db, 10)
+ genDb, chainA := makeHeaderChainWithGenesis(gspec, 128, ethash.NewFaker(), 10)
// chain B: G->A1->B1...B128
- chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10)
- log.Root().SetHandler(log.StdoutHandler)
+ chainB := makeHeaderChain(gspec.Config, chainA[0], 128, ethash.NewFaker(), genDb, 10)
forker := NewForkChoice(hc, nil)
// Inserting 64 headers on an empty chain, expecting
diff --git a/core/mkalloc.go b/core/mkalloc.go
index df167d7082..e4c2ec0d83 100644
--- a/core/mkalloc.go
+++ b/core/mkalloc.go
@@ -18,12 +18,10 @@
// +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.
- 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
-
+ go run mkalloc.go genesis.json
*/
package main
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
index aeba3690d2..e4dac3407f 100644
--- a/core/rawdb/accessors_chain.go
+++ b/core/rawdb/accessors_chain.go
@@ -37,7 +37,7 @@ import (
func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash {
var data []byte
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
- data, _ = reader.Ancient(chainFreezerHashTable, number)
+ data, _ = reader.Ancient(ChainFreezerHashTable, number)
if len(data) == 0 {
// Get it by hash from leveldb
data, _ = db.Get(headerHashKey(number))
@@ -259,8 +259,7 @@ func WriteLastPivotNumber(db ethdb.KeyValueWriter, pivot uint64) {
}
// ReadTxIndexTail retrieves the number of oldest indexed block
-// whose transaction indices has been indexed. If the corresponding entry
-// is non-existent in database it means the indexing has been finished.
+// whose transaction indices has been indexed.
func ReadTxIndexTail(db ethdb.KeyValueReader) *uint64 {
data, _ := db.Get(txIndexTailKey)
if len(data) != 8 {
@@ -335,7 +334,7 @@ func ReadHeaderRange(db ethdb.Reader, number uint64, count uint64) []rlp.RawValu
}
// read remaining from ancients
max := count * 700
- data, err := db.AncientRange(chainFreezerHeaderTable, i+1-count, count, max)
+ data, err := db.AncientRange(ChainFreezerHeaderTable, i+1-count, count, max)
if err == nil && uint64(len(data)) == count {
// the data is on the order [h, h+1, .., n] -- reordering needed
for i := range data {
@@ -352,7 +351,7 @@ func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValu
// 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)
+ data, _ = reader.Ancient(ChainFreezerHeaderTable, number)
if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
return nil
}
@@ -428,7 +427,7 @@ func deleteHeaderWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
// 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)
+ h, err := reader.Ancient(ChainFreezerHashTable, number)
if err != nil {
return false
}
@@ -444,7 +443,7 @@ func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
// Check if the data is in ancients
if isCanon(reader, number, hash) {
- data, _ = reader.Ancient(chainFreezerBodiesTable, number)
+ data, _ = reader.Ancient(ChainFreezerBodiesTable, number)
return nil
}
// If not, try reading from leveldb
@@ -459,7 +458,7 @@ func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue
func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
var data []byte
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
- data, _ = reader.Ancient(chainFreezerBodiesTable, number)
+ data, _ = reader.Ancient(ChainFreezerBodiesTable, number)
if len(data) > 0 {
return nil
}
@@ -527,7 +526,7 @@ func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
// Check if the data is in ancients
if isCanon(reader, number, hash) {
- data, _ = reader.Ancient(chainFreezerDifficultyTable, number)
+ data, _ = reader.Ancient(ChainFreezerDifficultyTable, number)
return nil
}
// If not, try reading from leveldb
@@ -587,7 +586,7 @@ func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawVa
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
// Check if the data is in ancients
if isCanon(reader, number, hash) {
- data, _ = reader.Ancient(chainFreezerReceiptTable, number)
+ data, _ = reader.Ancient(ChainFreezerReceiptTable, number)
return nil
}
// If not, try reading from leveldb
@@ -670,10 +669,11 @@ func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
// 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.LogForStorage
+ Logs []*types.Log
}
// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps
@@ -689,10 +689,7 @@ func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error {
if err := s.Decode(&stored); err != nil {
return err
}
- r.Logs = make([]*types.Log, len(stored.Logs))
- for i, log := range stored.Logs {
- r.Logs[i] = (*types.Log)(log)
- }
+ r.Logs = stored.Logs
return nil
}
@@ -717,9 +714,9 @@ func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, t
return nil
}
-// ReadLogs retrieves the logs for all transactions in a block. The log fields
-// are populated with metadata. In case the receipts or the block body
-// are not found, a nil is returned.
+// 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, config *params.ChainConfig) [][]*types.Log {
// Retrieve the flattened receipt slice
data := ReadReceiptsRLP(db, hash, number)
@@ -728,39 +725,10 @@ func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64, config *params.C
}
receipts := []*receiptLogs{}
if err := rlp.DecodeBytes(data, &receipts); err != nil {
- // Receipts might be in the legacy format, try decoding that.
- // TODO: to be removed after users migrated
- if logs := readLegacyLogs(db, hash, number, config); logs != nil {
- return logs
- }
log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
return nil
}
- body := ReadBody(db, hash, number)
- if body == nil {
- log.Error("Missing body but have receipt", "hash", hash, "number", number)
- return nil
- }
- if err := deriveLogFields(receipts, hash, number, body.Transactions); err != nil {
- log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
- return nil
- }
- logs := make([][]*types.Log, len(receipts))
- for i, receipt := range receipts {
- logs[i] = receipt.Logs
- }
- return logs
-}
-
-// readLegacyLogs is a temporary workaround for when trying to read logs
-// from a block which has its receipt stored in the legacy format. It'll
-// be removed after users have migrated their freezer databases.
-func readLegacyLogs(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) [][]*types.Log {
- receipts := ReadReceipts(db, hash, number, config)
- if receipts == nil {
- return nil
- }
logs := make([][]*types.Log, len(receipts))
for i, receipt := range receipts {
logs[i] = receipt.Logs
@@ -783,7 +751,7 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
if body == nil {
return nil
}
- return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
+ return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles).WithWithdrawals(body.Withdrawals)
}
// WriteBlock serializes a block into the database, header and body separately.
@@ -819,19 +787,19 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts
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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ if err := op.Append(ChainFreezerDifficultyTable, num, td); err != nil {
return fmt.Errorf("can't append block %d total difficulty: %v", num, err)
}
return nil
@@ -883,7 +851,7 @@ func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block {
}
for _, bad := range badBlocks {
if bad.Header.Hash() == hash {
- return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles)
+ return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals)
}
}
return nil
@@ -902,7 +870,7 @@ func ReadAllBadBlocks(db ethdb.Reader) []*types.Block {
}
var blocks []*types.Block
for _, bad := range badBlocks {
- blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles))
+ blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals))
}
return blocks
}
diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go
index 21d23e1f0c..84eae1d90d 100644
--- a/core/rawdb/accessors_chain_test.go
+++ b/core/rawdb/accessors_chain_test.go
@@ -113,8 +113,8 @@ func TestBlockStorage(t *testing.T) {
block := types.NewBlockWithHeader(&types.Header{
Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyRootHash,
- ReceiptHash: types.EmptyRootHash,
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
})
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
t.Fatalf("Non existent block returned: %v", entry)
@@ -161,8 +161,8 @@ func TestPartialBlockStorage(t *testing.T) {
block := types.NewBlockWithHeader(&types.Header{
Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyRootHash,
- ReceiptHash: types.EmptyRootHash,
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
})
// Store a header and check that it's not recognized as a block
WriteHeader(db, block.Header())
@@ -198,8 +198,8 @@ func TestBadBlockStorage(t *testing.T) {
Number: big.NewInt(1),
Extra: []byte("bad block"),
UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyRootHash,
- ReceiptHash: types.EmptyRootHash,
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
})
if entry := ReadBadBlock(db, block.Hash()); entry != nil {
t.Fatalf("Non existent block returned: %v", entry)
@@ -216,8 +216,8 @@ func TestBadBlockStorage(t *testing.T) {
Number: big.NewInt(2),
Extra: []byte("bad block two"),
UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyRootHash,
- ReceiptHash: types.EmptyRootHash,
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
})
WriteBadBlock(db, blockTwo)
@@ -235,8 +235,8 @@ func TestBadBlockStorage(t *testing.T) {
Number: big.NewInt(int64(n)),
Extra: []byte("bad block"),
UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyRootHash,
- ReceiptHash: types.EmptyRootHash,
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
})
WriteBadBlock(db, block)
}
@@ -446,8 +446,8 @@ func TestAncientStorage(t *testing.T) {
Number: big.NewInt(0),
Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyRootHash,
- ReceiptHash: types.EmptyRootHash,
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
})
// Ensure nothing non-existent will be read
hash, number := block.Hash(), block.NumberU64()
@@ -750,10 +750,6 @@ func TestReadLogs(t *testing.T) {
t.Fatalf("unexpected number of logs[1] returned, have %d want %d", have, want)
}
- // Fill in log fields so we can compare their rlp encoding
- if err := types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, 0, body.Transactions); err != nil {
- t.Fatal(err)
- }
for i, pr := range receipts {
for j, pl := range pr.Logs {
rlpHave, err := rlp.EncodeToBytes(newFullLogRLP(logs[i][j]))
@@ -893,8 +889,8 @@ func TestHeadersRLPStorage(t *testing.T) {
Number: big.NewInt(int64(i)),
Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyRootHash,
- ReceiptHash: types.EmptyRootHash,
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
ParentHash: pHash,
})
chain = append(chain, block)
diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go
index 7a9e6442f0..2ff29d1add 100644
--- a/core/rawdb/accessors_metadata.go
+++ b/core/rawdb/accessors_metadata.go
@@ -82,15 +82,15 @@ func WriteChainConfig(db ethdb.KeyValueWriter, hash common.Hash, cfg *params.Cha
}
// ReadGenesisStateSpec retrieves the genesis state specification based on the
-// given genesis hash.
-func ReadGenesisStateSpec(db ethdb.KeyValueReader, hash common.Hash) []byte {
- data, _ := db.Get(genesisStateSpecKey(hash))
+// 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, hash common.Hash, data []byte) {
- if err := db.Put(genesisStateSpecKey(hash), data); err != nil {
+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)
}
}
diff --git a/core/rawdb/accessors_state.go b/core/rawdb/accessors_state.go
index 41e21b6ca4..39900df23e 100644
--- a/core/rawdb/accessors_state.go
+++ b/core/rawdb/accessors_state.go
@@ -28,6 +28,17 @@ func ReadPreimage(db ethdb.KeyValueReader, hash common.Hash) []byte {
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
@@ -48,12 +59,6 @@ func ReadCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) []byte {
return data
}
-// ReadTrieNode retrieves the trie node of the provided hash.
-func ReadTrieNode(db ethdb.KeyValueReader, hash common.Hash) []byte {
- data, _ := db.Get(hash.Bytes())
- 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 {
@@ -74,23 +79,6 @@ func HasCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) bool {
return ok
}
-// HasTrieNode checks if the trie node with the provided hash is present in db.
-func HasTrieNode(db ethdb.KeyValueReader, hash common.Hash) bool {
- ok, _ := db.Has(hash.Bytes())
- return ok
-}
-
-// 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)))
-}
-
// 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 {
@@ -98,23 +86,9 @@ func WriteCode(db ethdb.KeyValueWriter, hash common.Hash, code []byte) {
}
}
-// WriteTrieNode writes the provided trie node database.
-func WriteTrieNode(db ethdb.KeyValueWriter, hash common.Hash, node []byte) {
- if err := db.Put(hash.Bytes(), node); err != nil {
- log.Crit("Failed to store trie node", "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)
}
}
-
-// DeleteTrieNode deletes the specified trie node from the database.
-func DeleteTrieNode(db ethdb.KeyValueWriter, hash common.Hash) {
- if err := db.Delete(hash.Bytes()); err != nil {
- log.Crit("Failed to delete trie node", "err", err)
- }
-}
diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go
new file mode 100644
index 0000000000..e240213025
--- /dev/null
+++ b/core/rawdb/accessors_trie.go
@@ -0,0 +1,263 @@
+// 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 = "hashScheme"
+
+// 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 = "pathScheme"
+
+// nodeHasher used to derive the hash of trie node.
+type nodeHasher struct{ sha crypto.KeccakState }
+
+var hasherPool = sync.Pool{
+ New: func() interface{} { return &nodeHasher{sha: sha3.NewLegacyKeccak256().(crypto.KeccakState)} },
+}
+
+func newNodeHasher() *nodeHasher { return hasherPool.Get().(*nodeHasher) }
+func returnHasherToPool(h *nodeHasher) { hasherPool.Put(h) }
+
+func (h *nodeHasher) hashData(data []byte) (n common.Hash) {
+ h.sha.Reset()
+ h.sha.Write(data)
+ h.sha.Read(n[:])
+ return n
+}
+
+// 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{}
+ }
+ hasher := newNodeHasher()
+ defer returnHasherToPool(hasher)
+ return data, hasher.hashData(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
+ }
+ hasher := newNodeHasher()
+ defer returnHasherToPool(hasher)
+ return hasher.hashData(data) == hash
+}
+
+// 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{}
+ }
+ hasher := newNodeHasher()
+ defer returnHasherToPool(hasher)
+ return data, hasher.hashData(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
+ }
+ hasher := newNodeHasher()
+ defer returnHasherToPool(hasher)
+ return hasher.hashData(data) == hash
+}
+
+// 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))
+ }
+}
diff --git a/core/rawdb/ancient_scheme.go b/core/rawdb/ancient_scheme.go
index 3da061cbd9..b0428c5f5b 100644
--- a/core/rawdb/ancient_scheme.go
+++ b/core/rawdb/ancient_scheme.go
@@ -16,34 +16,32 @@
package rawdb
-import "fmt"
-
// The list of table names of chain freezer.
const (
- // chainFreezerHeaderTable indicates the name of the freezer header table.
- chainFreezerHeaderTable = "headers"
+ // ChainFreezerHeaderTable indicates the name of the freezer header table.
+ ChainFreezerHeaderTable = "headers"
- // chainFreezerHashTable indicates the name of the freezer canonical hash table.
- chainFreezerHashTable = "hashes"
+ // ChainFreezerHashTable indicates the name of the freezer canonical hash table.
+ ChainFreezerHashTable = "hashes"
- // chainFreezerBodiesTable indicates the name of the freezer block body table.
- chainFreezerBodiesTable = "bodies"
+ // ChainFreezerBodiesTable indicates the name of the freezer block body table.
+ ChainFreezerBodiesTable = "bodies"
- // chainFreezerReceiptTable indicates the name of the freezer receipts table.
- chainFreezerReceiptTable = "receipts"
+ // ChainFreezerReceiptTable indicates the name of the freezer receipts table.
+ ChainFreezerReceiptTable = "receipts"
- // chainFreezerDifficultyTable indicates the name of the freezer total difficulty table.
- chainFreezerDifficultyTable = "diffs"
+ // 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,
+ ChainFreezerHeaderTable: false,
+ ChainFreezerHashTable: true,
+ ChainFreezerBodiesTable: false,
+ ChainFreezerReceiptTable: false,
+ ChainFreezerDifficultyTable: true,
}
// The list of identifiers of ancient stores.
@@ -53,34 +51,3 @@ var (
// freezers the collections of all builtin freezers.
var freezers = []string{chainFreezerName}
-
-// 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
- 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/ancient_utils.go b/core/rawdb/ancient_utils.go
new file mode 100644
index 0000000000..363a911aee
--- /dev/null
+++ b/core/rawdb/ancient_utils.go
@@ -0,0 +1,121 @@
+// 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"
+
+ "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
+}
+
+// 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:
+ // Chain ancient store is a bit special. It's always opened along
+ // with the key-value store, inspect the chain store directly.
+ info := freezerInfo{name: freezer}
+ // Retrieve storage size of every contained table.
+ for table := range chainFreezerNoSnappy {
+ size, err := db.AncientSize(table)
+ if err != nil {
+ return nil, err
+ }
+ info.sizes = append(info.sizes, tableSize{name: table, size: common.StorageSize(size)})
+ }
+ // Retrieve the number of last stored item
+ ancients, err := db.Ancients()
+ if err != nil {
+ return nil, err
+ }
+ info.head = ancients - 1
+
+ // Retrieve the number of first stored item
+ tail, err := db.Tail()
+ if err != nil {
+ return nil, err
+ }
+ info.tail = tail
+ 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
+ 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
index 7d9c9c0156..167afc3889 100644
--- a/core/rawdb/chain_freezer.go
+++ b/core/rawdb/chain_freezer.go
@@ -55,8 +55,8 @@ type chainFreezer struct {
}
// newChainFreezer initializes the freezer for ancient chain data.
-func newChainFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]bool) (*chainFreezer, error) {
- freezer, err := NewFreezer(datadir, namespace, readonly, maxTableSize, tables)
+func newChainFreezer(datadir string, namespace string, readonly bool) (*chainFreezer, error) {
+ freezer, err := NewChainFreezer(datadir, namespace, readonly)
if err != nil {
return nil, err
}
@@ -70,14 +70,13 @@ func newChainFreezer(datadir string, namespace string, readonly bool, maxTableSi
// Close closes the chain freezer instance and terminates the background thread.
func (f *chainFreezer) Close() error {
- err := f.Freezer.Close()
select {
case <-f.quit:
default:
close(f.quit)
}
f.wg.Wait()
- return err
+ return f.Freezer.Close()
}
// freeze is a background thread that periodically checks the blockchain for any
@@ -86,12 +85,14 @@ func (f *chainFreezer) Close() error {
// 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) {
- nfdb := &nofreezedb{KeyValueStore: db}
-
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:
@@ -106,8 +107,9 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
triggered = nil
}
select {
- case <-time.NewTimer(freezerRecheckInterval).C:
+ case <-timer.C:
backoff = false
+ timer.Reset(freezerRecheckInterval)
case triggered = <-f.trigger:
backoff = false
case <-f.quit:
@@ -278,19 +280,19 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
}
// Write to the batch.
- if err := op.AppendRaw(chainFreezerHashTable, number, hash[:]); err != nil {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ if err := op.AppendRaw(ChainFreezerDifficultyTable, number, td); err != nil {
return fmt.Errorf("can't write td to Freezer: %v", err)
}
diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go
index 867fed63ad..102943516e 100644
--- a/core/rawdb/chain_iterator.go
+++ b/core/rawdb/chain_iterator.go
@@ -50,7 +50,7 @@ func InitDatabaseFromFreezer(db ethdb.Database) {
if i+count > frozen {
count = frozen - i
}
- data, err := db.AncientRange(chainFreezerHashTable, i, count, 32*count)
+ data, err := db.AncientRange(ChainFreezerHashTable, i, count, 32*count)
if err != nil {
log.Crit("Failed to init database from freezer", "err", err)
}
@@ -191,7 +191,7 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
// in to be [to-1]. Therefore, setting lastNum to means that the
// prqueue gap-evaluation will work correctly
lastNum = to
- queue = prque.New(nil)
+ queue = prque.New[int64, *blockTxHashes](nil)
// for stats reporting
blocks, txs = 0, 0
)
@@ -210,7 +210,7 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
break
}
// Next block available, pop it off and index it
- delivery := queue.PopItem().(*blockTxHashes)
+ delivery := queue.PopItem()
lastNum = delivery.number
WriteTxLookupEntries(batch, delivery.number, delivery.hashes)
blocks++
@@ -243,7 +243,7 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
case <-interrupt:
log.Debug("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
default:
- log.Info("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
+ log.Debug("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
}
}
@@ -282,7 +282,7 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
// 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(nil)
+ queue = prque.New[int64, *blockTxHashes](nil)
// for stats reporting
blocks, txs = 0, 0
)
@@ -299,7 +299,7 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
if hook != nil && !hook(nextNum) {
break
}
- delivery := queue.PopItem().(*blockTxHashes)
+ delivery := queue.PopItem()
nextNum = delivery.number + 1
DeleteTxLookupEntries(batch, delivery.hashes)
txs += len(delivery.hashes)
@@ -335,7 +335,7 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
case <-interrupt:
log.Debug("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
default:
- log.Info("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
+ log.Debug("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
}
}
diff --git a/core/rawdb/database.go b/core/rawdb/database.go
index 1eaf033bbe..5b7299f38f 100644
--- a/core/rawdb/database.go
+++ b/core/rawdb/database.go
@@ -22,6 +22,8 @@ import (
"fmt"
"os"
"path"
+ "path/filepath"
+ "strings"
"sync/atomic"
"time"
@@ -198,7 +200,7 @@ func resolveChainFreezerDir(ancient string) string {
// 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, freezerTableSize, chainFreezerNoSnappy)
+ frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly)
if err != nil {
return nil, err
}
@@ -229,7 +231,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
// 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)
+ frgenesis, err := frdb.Ancient(ChainFreezerHashTable, 0)
if err != nil {
return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
} else if !bytes.Equal(kvgenesis, frgenesis) {
@@ -240,8 +242,8 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
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 *ReadHeaderNumber(db, ReadHeadHeaderHash(db)) > frozen-1 {
- return nil, fmt.Errorf("gap (#%d) in the chain between ancients and leveldb", frozen)
+ if ldbNum := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); ldbNum > frozen-1 {
+ return nil, fmt.Errorf("gap in the chain between ancients (#%d) and leveldb (#%d) ", frozen, ldbNum)
}
// Database contains only older data than the freezer, this happens if the
// state was wiped and reinited from an existing freezer.
@@ -301,19 +303,84 @@ func NewLevelDBDatabase(file string, cache int, handles int, namespace string, r
if err != nil {
return nil, err
}
+ log.Info("Using LevelDB as the backing database")
return NewDatabase(db), nil
}
-// NewLevelDBDatabaseWithFreezer creates a persistent key-value database 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 NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, ancient string, namespace string, readonly bool) (ethdb.Database, error) {
- kvdb, err := leveldb.New(file, cache, handles, namespace, readonly)
+const (
+ dbPebble = "pebble"
+ dbLeveldb = "leveldb"
+)
+
+// hasPreexistingDb 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 hasPreexistingDb(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
+}
+
+// openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble.
+//
+// type == null type != null
+// +----------------------------------------
+// db is non-existent | leveldb default | specified type
+// db is existent | from db | specified type (if compatible)
+func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) {
+ existingDb := hasPreexistingDb(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 {
+ if PebbleEnabled {
+ log.Info("Using pebble as the backing database")
+ return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
+ } else {
+ return nil, errors.New("db.engine 'pebble' not supported on this platform")
+ }
+ }
+ if len(o.Type) != 0 && o.Type != dbLeveldb {
+ return nil, fmt.Errorf("unknown db.engine %v", o.Type)
+ }
+ log.Info("Using leveldb as the backing database")
+ // Use leveldb, either as default (no explicit choice), or pre-existing, or chosen explicitly
+ return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
+}
+
+// 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
}
- frdb, err := NewDatabaseWithFreezer(kvdb, ancient, namespace, readonly)
+ 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
@@ -379,13 +446,6 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
beaconHeaders stat
cliqueSnaps stat
- // Ancient store statistics
- ancientHeadersSize common.StorageSize
- ancientBodiesSize common.StorageSize
- ancientReceiptsSize common.StorageSize
- ancientTdsSize common.StorageSize
- ancientHashesSize common.StorageSize
-
// Les statistic
chtTrieNodes stat
bloomTrieNodes stat
@@ -439,15 +499,15 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
bloomBits.Add(size)
case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8):
beaconHeaders.Add(size)
- case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength:
+ case bytes.HasPrefix(key, CliqueSnapshotPrefix) && len(key) == 7+common.HashLength:
cliqueSnaps.Add(size)
- case bytes.HasPrefix(key, []byte("cht-")) ||
- bytes.HasPrefix(key, []byte("chtIndexV2-")) ||
- bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie
+ case bytes.HasPrefix(key, ChtTablePrefix) ||
+ bytes.HasPrefix(key, ChtIndexTablePrefix) ||
+ bytes.HasPrefix(key, ChtPrefix): // Canonical hash trie
chtTrieNodes.Add(size)
- case bytes.HasPrefix(key, []byte("blt-")) ||
- bytes.HasPrefix(key, []byte("bltIndex-")) ||
- bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub
+ case bytes.HasPrefix(key, BloomTrieTablePrefix) ||
+ bytes.HasPrefix(key, BloomTrieIndexPrefix) ||
+ bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub
bloomTrieNodes.Add(size)
default:
var accounted bool
@@ -473,20 +533,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
logged = time.Now()
}
}
- // Inspect append-only file store then.
- ancientSizes := []*common.StorageSize{&ancientHeadersSize, &ancientBodiesSize, &ancientReceiptsSize, &ancientHashesSize, &ancientTdsSize}
- for i, category := range []string{chainFreezerHeaderTable, chainFreezerBodiesTable, chainFreezerReceiptTable, chainFreezerHashTable, chainFreezerDifficultyTable} {
- if size, err := db.AncientSize(category); err == nil {
- *ancientSizes[i] += common.StorageSize(size)
- total += common.StorageSize(size)
- }
- }
- // Get number of ancient rows inside the freezer
- ancients := counter(0)
- if count, err := db.Ancients(); err == nil {
- ancients = counter(count)
- }
- // Display the database statistic.
+ // 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()},
@@ -504,14 +551,25 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
{"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()},
- {"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()},
- {"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()},
- {"Ancient store", "Receipt lists", ancientReceiptsSize.String(), ancients.String()},
- {"Ancient store", "Difficulties", ancientTdsSize.String(), ancients.String()},
- {"Ancient store", "Block number->hash", ancientHashesSize.String(), ancients.String()},
{"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(), " "})
@@ -521,6 +579,5 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
if unaccounted.size > 0 {
log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
}
-
return nil
}
diff --git a/mobile/geth_other.go b/core/rawdb/databases_64bit.go
similarity index 52%
rename from mobile/geth_other.go
rename to core/rawdb/databases_64bit.go
index c5cad4a7ba..139ce7d347 100644
--- a/mobile/geth_other.go
+++ b/core/rawdb/databases_64bit.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2023 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
@@ -12,12 +12,26 @@
// 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 .
+// along with the go-ethereum library. If not, see
-//go:build !android && !ios
-// +build !android,!ios
+//go:build arm64 || amd64
-package geth
+package rawdb
-// clientIdentifier is a hard coded identifier to report into the network.
-var clientIdentifier = "GethMobile"
+import (
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/ethdb/pebble"
+)
+
+// Pebble is unsuported on 32bit architecture
+const PebbleEnabled = true
+
+// 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 bool) (ethdb.Database, error) {
+ db, err := pebble.New(file, cache, handles, namespace, readonly)
+ if err != nil {
+ return nil, err
+ }
+ return NewDatabase(db), nil
+}
diff --git a/mobile/logger.go b/core/rawdb/databases_non64bit.go
similarity index 58%
rename from mobile/logger.go
rename to core/rawdb/databases_non64bit.go
index 7078c4fd2c..b8ab2ecada 100644
--- a/mobile/logger.go
+++ b/core/rawdb/databases_non64bit.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2023 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
@@ -14,15 +14,21 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package geth
+//go:build !(arm64 || amd64)
+
+package rawdb
import (
- "os"
+ "errors"
- "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/ethdb"
)
-// SetVerbosity sets the global verbosity level (between 0 and 6 - see logger/verbosity.go).
-func SetVerbosity(level int) {
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(level), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
+// Pebble is unsuported on 32bit architecture
+const PebbleEnabled = false
+
+// 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 bool) (ethdb.Database, error) {
+ return nil, errors.New("pebble is not supported on this platform")
}
diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go
index 6dea98c3d3..04c326c4f9 100644
--- a/core/rawdb/freezer.go
+++ b/core/rawdb/freezer.go
@@ -57,10 +57,10 @@ 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.
+// - 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 {
// WARNING: The `frozen` and `tail` fields are accessed atomically. On 32 bit platforms, only
// 64-bit aligned fields can be atomic. The struct is guaranteed to be so aligned,
@@ -79,6 +79,12 @@ type Freezer struct {
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.
//
@@ -188,9 +194,9 @@ func (f *Freezer) Ancient(kind string, number uint64) ([]byte, error) {
// AncientRange retrieves multiple items in sequence, starting from the index 'start'.
// It will return
-// - at most 'max' items,
-// - at least 1 item (even if exceeding the maxByteSize), but will otherwise
-// return as many items as fit into maxByteSize.
+// - at most 'max' items,
+// - at least 1 item (even if exceeding the maxByteSize), but will otherwise
+// return as many items as fit into maxByteSize.
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)
@@ -318,30 +324,35 @@ func (f *Freezer) Sync() error {
return nil
}
-// validate checks that every table has the same length.
+// 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 (
- length uint64
- name string
+ head uint64
+ tail uint64
+ name string
)
- // Hack to get length of any table
+ // Hack to get boundary of any table
for kind, table := range f.tables {
- length = atomic.LoadUint64(&table.items)
+ head = atomic.LoadUint64(&table.items)
+ tail = atomic.LoadUint64(&table.itemHidden)
name = kind
break
}
- // Now check every table against that length
+ // Now check every table against those boundaries.
for kind, table := range f.tables {
- items := atomic.LoadUint64(&table.items)
- if length != items {
- return fmt.Errorf("freezer tables %s and %s have differing lengths: %d != %d", kind, name, items, length)
+ if head != atomic.LoadUint64(&table.items) {
+ return fmt.Errorf("freezer tables %s and %s have differing head: %d != %d", kind, name, atomic.LoadUint64(&table.items), head)
+ }
+ if tail != atomic.LoadUint64(&table.itemHidden) {
+ return fmt.Errorf("freezer tables %s and %s have differing tail: %d != %d", kind, name, atomic.LoadUint64(&table.itemHidden), tail)
}
}
- atomic.StoreUint64(&f.frozen, length)
+ atomic.StoreUint64(&f.frozen, head)
+ atomic.StoreUint64(&f.tail, tail)
return nil
}
diff --git a/core/rawdb/freezer_resettable.go b/core/rawdb/freezer_resettable.go
new file mode 100644
index 0000000000..f9a56c6de5
--- /dev/null
+++ b/core/rawdb/freezer_resettable.go
@@ -0,0 +1,233 @@
+// 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"
+)
+
+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 'max' items,
+// - at least 1 item (even if exceeding the maxByteSize), but will otherwise
+// return as many items as fit into maxByteSize
+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.
+func (f *ResettableFreezer) TruncateHead(items uint64) error {
+ f.lock.RLock()
+ defer f.lock.RUnlock()
+
+ return f.freezer.TruncateHead(items)
+}
+
+// TruncateTail discards any recent data below the provided threshold number.
+func (f *ResettableFreezer) TruncateTail(tail 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 {
+ 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
new file mode 100644
index 0000000000..d741bc14e5
--- /dev/null
+++ b/core/rawdb/freezer_resettable_test.go
@@ -0,0 +1,107 @@
+// 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
index 3fe691cf6d..b111797d52 100644
--- a/core/rawdb/freezer_table.go
+++ b/core/rawdb/freezer_table.go
@@ -148,20 +148,12 @@ func newTable(path string, name string, readMeter metrics.Meter, writeMeter metr
meta *os.File
)
if readonly {
- // Will fail if table doesn't exist
+ // 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
}
- // TODO(rjl493456442) change it to read-only mode. Open the metadata file
- // in rw mode. It's a temporary solution for now and should be changed
- // whenever the tail deletion is actually used. The reason for this hack is
- // the additional meta file for each freezer table is added in order to support
- // tail deletion, but for most legacy nodes this file is missing. This check
- // will suddenly break lots of database relevant commands. So the metadata file
- // is always opened for mutation and nothing else will be written except
- // the initialization.
- meta, err = openFreezerFileForAppend(filepath.Join(path, fmt.Sprintf("%s.meta", name)))
+ meta, err = openFreezerFileForReadOnly(filepath.Join(path, fmt.Sprintf("%s.meta", name)))
if err != nil {
return nil, err
}
@@ -237,6 +229,7 @@ func (t *freezerTable) repair() error {
lastIndex indexEntry
contentSize int64
contentExp int64
+ verbose bool
)
// Read index zero, determine what file is the earliest
// and what item offset to use
@@ -280,9 +273,10 @@ func (t *freezerTable) repair() error {
// Keep truncating both files until they come in sync
contentExp = int64(lastIndex.offset)
for contentExp != contentSize {
+ verbose = true
// Truncate the head file to the last offset pointer
if contentExp < contentSize {
- t.logger.Warn("Truncating dangling head", "indexed", common.StorageSize(contentExp), "stored", common.StorageSize(contentSize))
+ t.logger.Warn("Truncating dangling head", "indexed", contentExp, "stored", contentSize)
if err := truncateFreezerFile(t.head, contentExp); err != nil {
return err
}
@@ -290,7 +284,7 @@ func (t *freezerTable) repair() error {
}
// Truncate the index to point within the head file
if contentExp > contentSize {
- t.logger.Warn("Truncating dangling indexes", "indexed", common.StorageSize(contentExp), "stored", common.StorageSize(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
}
@@ -351,7 +345,11 @@ func (t *freezerTable) repair() error {
if err := t.preopen(); err != nil {
return err
}
- t.logger.Debug("Chain freezer table opened", "items", t.items, "size", common.StorageSize(t.headBytes))
+ if verbose {
+ t.logger.Info("Chain freezer table opened", "items", t.items, "size", t.headBytes)
+ } else {
+ t.logger.Debug("Chain freezer table opened", "items", t.items, "size", common.StorageSize(t.headBytes))
+ }
return nil
}
@@ -561,21 +559,31 @@ func (t *freezerTable) Close() error {
defer t.lock.Unlock()
var errs []error
- if err := t.index.Close(); err != nil {
- errs = append(errs, err)
- }
- t.index = nil
-
- if err := t.meta.Close(); err != nil {
- errs = append(errs, err)
+ 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)
+ }
+ }
}
- t.meta = nil
-
+ // 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 {
- if err := f.Close(); err != nil {
- errs = append(errs, err)
- }
+ doClose(f, false, true) // close but do not sync
}
+ t.index = nil
+ t.meta = nil
t.head = nil
if errs != nil {
@@ -732,7 +740,7 @@ func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []i
defer t.lock.RUnlock()
// Ensure the table and the item are accessible
- if t.index == nil || t.head == nil {
+ if t.index == nil || t.head == nil || t.meta == nil {
return nil, nil, errClosed
}
var (
@@ -860,8 +868,11 @@ func (t *freezerTable) advanceHead() error {
if err != nil {
return err
}
-
- // Close old file, and reopen in RDONLY mode.
+ // 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)
@@ -875,13 +886,22 @@ func (t *freezerTable) advanceHead() error {
// 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 {
- if err := t.index.Sync(); err != nil {
- return err
+ t.lock.Lock()
+ defer t.lock.Unlock()
+ if t.index == nil || t.head == nil || t.meta == nil {
+ return errClosed
}
- if err := t.meta.Sync(); err != nil {
- return err
+ var err error
+ trackError := func(e error) {
+ if e != nil && err == nil {
+ err = e
+ }
}
- return t.head.Sync()
+
+ trackError(t.index.Sync())
+ trackError(t.meta.Sync())
+ trackError(t.head.Sync())
+ return err
}
func (t *freezerTable) dumpIndexStdout(start, stop int64) {
@@ -901,7 +921,8 @@ func (t *freezerTable) dumpIndex(w io.Writer, start, stop int64) {
fmt.Fprintf(w, "Failed to decode freezer table %v\n", err)
return
}
- fmt.Fprintf(w, "Version %d deleted %d, hidden %d\n", meta.Version, atomic.LoadUint64(&t.itemOffset), atomic.LoadUint64(&t.itemHidden))
+ fmt.Fprintf(w, "Version %d count %d, deleted %d, hidden %d\n", meta.Version,
+ atomic.LoadUint64(&t.items), atomic.LoadUint64(&t.itemOffset), atomic.LoadUint64(&t.itemHidden))
buf := make([]byte, indexEntrySize)
diff --git a/core/rawdb/freezer_table_test.go b/core/rawdb/freezer_table_test.go
index ea28e71756..6181d4d72c 100644
--- a/core/rawdb/freezer_table_test.go
+++ b/core/rawdb/freezer_table_test.go
@@ -27,17 +27,12 @@ import (
"sync/atomic"
"testing"
"testing/quick"
- "time"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/metrics"
"github.com/stretchr/testify/require"
)
-func init() {
- rand.Seed(time.Now().Unix())
-}
-
// TestFreezerBasics test initializing a freezertable from scratch, writing to the table,
// and reading it back.
func TestFreezerBasics(t *testing.T) {
diff --git a/core/rawdb/freezer_test.go b/core/rawdb/freezer_test.go
index 630911ec86..5896e43ce2 100644
--- a/core/rawdb/freezer_test.go
+++ b/core/rawdb/freezer_test.go
@@ -190,7 +190,7 @@ func TestFreezerConcurrentModifyTruncate(t *testing.T) {
var item = make([]byte, 256)
- for i := 0; i < 1000; i++ {
+ for i := 0; i < 10; i++ {
// First reset and write 100 items.
if err := f.TruncateHead(0); err != nil {
t.Fatal("truncate failed:", err)
@@ -407,3 +407,25 @@ func TestRenameWindows(t *testing.T) {
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/schema.go b/core/rawdb/schema.go
index d5f751da3a..fd5ab1ad4c 100644
--- a/core/rawdb/schema.go
+++ b/core/rawdb/schema.go
@@ -100,12 +100,26 @@ var (
CodePrefix = []byte("c") // CodePrefix + code hash -> account code
skeletonHeaderPrefix = []byte("S") // skeletonHeaderPrefix + num (uint64 big endian) -> header
+ // Path-based trie node scheme.
+ trieNodeAccountPrefix = []byte("A") // trieNodeAccountPrefix + hexPath -> trie node
+ trieNodeStoragePrefix = []byte("O") // trieNodeStoragePrefix + accountHash + hexPath -> trie node
+
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
- // Chain index prefixes (use `i` + single byte to avoid mixing data types).
- BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
+ // 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-")
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
@@ -224,3 +238,13 @@ func configKey(hash common.Hash) []byte {
func genesisStateSpecKey(hash common.Hash) []byte {
return append(genesisPrefix, hash.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 {
+ return append(append(trieNodeStoragePrefix, accountHash.Bytes()...), path...)
+}
diff --git a/core/rlp_test.go b/core/rlp_test.go
index bf5a934ce5..a2fb4937f8 100644
--- a/core/rlp_test.go
+++ b/core/rlp_test.go
@@ -23,7 +23,6 @@ import (
"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/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
@@ -33,10 +32,9 @@ import (
func getBlock(transactions int, uncles int, dataSize int) *types.Block {
var (
- aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
- // Generate a canonical chain to act as the main dataset
+ aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
engine = ethash.NewFaker()
- db = rawdb.NewMemoryDatabase()
+
// A sender who makes transactions, has some funds
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
@@ -45,11 +43,9 @@ func getBlock(transactions int, uncles int, dataSize int) *types.Block {
Config: params.TestChainConfig,
Alloc: GenesisAlloc{address: {Balance: funds}},
}
- genesis = gspec.MustCommit(db)
)
-
// We need to generate as many blocks +1 as uncles
- blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, uncles+1,
+ _, blocks, _ := GenerateChainWithGenesis(gspec, engine, uncles+1,
func(n int, b *BlockGen) {
if n == uncles {
// Add transactions and stuff on the last block
diff --git a/core/tx_cacher.go b/core/sender_cacher.go
similarity index 88%
rename from core/tx_cacher.go
rename to core/sender_cacher.go
index b1e5d676a2..4be53619eb 100644
--- a/core/tx_cacher.go
+++ b/core/sender_cacher.go
@@ -22,8 +22,8 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
-// senderCacher is a concurrent transaction sender recoverer and cacher.
-var senderCacher = newTxSenderCacher(runtime.NumCPU())
+// SenderCacher is a concurrent transaction sender recoverer and cacher.
+var SenderCacher = newTxSenderCacher(runtime.NumCPU())
// txSenderCacherRequest is a request for recovering transaction senders with a
// specific signature scheme and caching it into the transactions themselves.
@@ -67,10 +67,10 @@ func (cacher *txSenderCacher) cache() {
}
}
-// recover recovers the senders from a batch of transactions and caches them
+// Recover recovers the senders from a batch of transactions and caches them
// back into the same data structures. There is no validation being done, nor
// any reaction to invalid signatures. That is up to calling code later.
-func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transaction) {
+func (cacher *txSenderCacher) Recover(signer types.Signer, txs []*types.Transaction) {
// If there's nothing to recover, abort
if len(txs) == 0 {
return
@@ -89,10 +89,10 @@ func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transact
}
}
-// recoverFromBlocks recovers the senders from a batch of blocks and caches them
+// RecoverFromBlocks recovers the senders from a batch of blocks and caches them
// back into the same data structures. There is no validation being done, nor
// any reaction to invalid signatures. That is up to calling code later.
-func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*types.Block) {
+func (cacher *txSenderCacher) RecoverFromBlocks(signer types.Signer, blocks []*types.Block) {
count := 0
for _, block := range blocks {
count += len(block.Transactions())
@@ -101,5 +101,5 @@ func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*t
for _, block := range blocks {
txs = append(txs, block.Transactions()...)
}
- cacher.recover(signer, txs)
+ cacher.Recover(signer, txs)
}
diff --git a/core/state/database.go b/core/state/database.go
index 96b6bcfe65..d3c36c10ac 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -20,13 +20,12 @@ import (
"errors"
"fmt"
- "github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/lru"
"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/trie"
- lru "github.com/hashicorp/golang-lru"
)
const (
@@ -43,7 +42,7 @@ type Database interface {
OpenTrie(root common.Hash) (Trie, error)
// OpenStorageTrie opens the storage trie of an account.
- OpenStorageTrie(addrHash, root common.Hash) (Trie, error)
+ OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error)
// CopyTrie returns an independent copy of the given trie.
CopyTrie(Trie) Trie
@@ -54,6 +53,9 @@ type Database interface {
// ContractCodeSize retrieves a particular contracts code's size.
ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
+ // DiskDB returns the underlying key-value disk database.
+ DiskDB() ethdb.KeyValueStore
+
// TrieDB retrieves the low level trie database used for data storage.
TrieDB() *trie.Database
}
@@ -71,8 +73,13 @@ type Trie interface {
// trie.MissingNodeError is returned.
TryGet(key []byte) ([]byte, error)
- // TryGetAccount abstract an account read from the trie.
- TryGetAccount(key []byte) (*types.StateAccount, error)
+ // TryGetAccount abstracts an account read from the trie. It retrieves the
+ // account blob from the trie with provided account address and decodes it
+ // with associated decoding algorithm. If the specified account is not in
+ // the trie, nil will be returned. If the trie is corrupted(e.g. some nodes
+ // are missing or the account blob is incorrect for decoding), an error will
+ // be returned.
+ TryGetAccount(address common.Address) (*types.StateAccount, error)
// TryUpdate associates key with value in the trie. If value has length zero, any
// existing value is deleted from the trie. The value bytes must not be modified
@@ -80,15 +87,17 @@ type Trie interface {
// database, a trie.MissingNodeError is returned.
TryUpdate(key, value []byte) error
- // TryUpdateAccount abstract an account write to the trie.
- TryUpdateAccount(key []byte, account *types.StateAccount) error
+ // TryUpdateAccount abstracts an account write to the trie. It encodes the
+ // provided account object with associated algorithm and then updates it
+ // in the trie with provided address.
+ TryUpdateAccount(address common.Address, account *types.StateAccount) error
// TryDelete removes any existing value for key from the trie. If a node was not
// found in the database, a trie.MissingNodeError is returned.
TryDelete(key []byte) error
// TryDeleteAccount abstracts an account deletion from the trie.
- TryDeleteAccount(key []byte) error
+ TryDeleteAccount(address common.Address) error
// Hash returns the root hash of the trie. It does not write to the database and
// can be used even if the trie doesn't have one.
@@ -100,7 +109,7 @@ type Trie interface {
// The returned nodeset can be nil if the trie is clean(nothing to commit).
// Once the trie is committed, it's not usable anymore. A new trie must
// be created with new root and updated trie database for following usage
- Commit(collectLeaf bool) (common.Hash, *trie.NodeSet, error)
+ Commit(collectLeaf bool) (common.Hash, *trie.NodeSet)
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
// starts at the key after the given start key.
@@ -127,23 +136,34 @@ func NewDatabase(db ethdb.Database) Database {
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
// large memory cache.
func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
- csc, _ := lru.New(codeSizeCacheSize)
return &cachingDB{
- db: trie.NewDatabaseWithConfig(db, config),
- codeSizeCache: csc,
- codeCache: fastcache.New(codeCacheSize),
+ disk: db,
+ codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
+ codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
+ triedb: trie.NewDatabaseWithConfig(db, config),
+ }
+}
+
+// NewDatabaseWithNodeDB creates a state database with an already initialized node database.
+func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database {
+ return &cachingDB{
+ disk: db,
+ codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
+ codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
+ triedb: triedb,
}
}
type cachingDB struct {
- db *trie.Database
- codeSizeCache *lru.Cache
- codeCache *fastcache.Cache
+ disk ethdb.KeyValueStore
+ codeSizeCache *lru.Cache[common.Hash, int]
+ codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
+ triedb *trie.Database
}
// OpenTrie opens the main account trie at a specific root hash.
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
- tr, err := trie.NewStateTrie(common.Hash{}, root, db.db)
+ tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
if err != nil {
return nil, err
}
@@ -151,8 +171,8 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
}
// OpenStorageTrie opens the storage trie of an account.
-func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
- tr, err := trie.NewStateTrie(addrHash, root, db.db)
+func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error) {
+ tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, addrHash, root), db.triedb)
if err != nil {
return nil, err
}
@@ -171,12 +191,13 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
// ContractCode retrieves a particular contract's code.
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
- if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
+ code, _ := db.codeCache.Get(codeHash)
+ if len(code) > 0 {
return code, nil
}
- code := rawdb.ReadCode(db.db.DiskDB(), codeHash)
+ code = rawdb.ReadCode(db.disk, codeHash)
if len(code) > 0 {
- db.codeCache.Set(codeHash.Bytes(), code)
+ db.codeCache.Add(codeHash, code)
db.codeSizeCache.Add(codeHash, len(code))
return code, nil
}
@@ -187,12 +208,13 @@ func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error
// code can't be found in the cache, then check the existence with **new**
// db scheme.
func (db *cachingDB) ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error) {
- if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
+ code, _ := db.codeCache.Get(codeHash)
+ if len(code) > 0 {
return code, nil
}
- code := rawdb.ReadCodeWithPrefix(db.db.DiskDB(), codeHash)
+ code = rawdb.ReadCodeWithPrefix(db.disk, codeHash)
if len(code) > 0 {
- db.codeCache.Set(codeHash.Bytes(), code)
+ db.codeCache.Add(codeHash, code)
db.codeSizeCache.Add(codeHash, len(code))
return code, nil
}
@@ -202,13 +224,18 @@ func (db *cachingDB) ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]b
// ContractCodeSize retrieves a particular contracts code's size.
func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
- return cached.(int), nil
+ return cached, nil
}
code, err := db.ContractCode(addrHash, codeHash)
return len(code), err
}
+// DiskDB returns the underlying key-value disk database.
+func (db *cachingDB) DiskDB() ethdb.KeyValueStore {
+ return db.disk
+}
+
// TrieDB retrieves any intermediate trie-node caching layer.
func (db *cachingDB) TrieDB() *trie.Database {
- return db.db
+ return db.triedb
}
diff --git a/core/state/dump.go b/core/state/dump.go
index bfcc035435..d834cbad37 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -29,7 +29,7 @@ import (
"github.com/ethereum/go-ethereum/trie"
)
-// DumpConfig is a set of options to control what portions of the statewill be
+// DumpConfig is a set of options to control what portions of the state will be
// iterated and collected.
type DumpConfig struct {
SkipCode bool
@@ -168,7 +168,12 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
}
if !conf.SkipStorage {
account.Storage = make(map[common.Hash]string)
- storageIt := trie.NewIterator(obj.getTrie(s.db).NodeIterator(nil))
+ tr, err := obj.getTrie(s.db)
+ if err != nil {
+ log.Error("Failed to load storage trie", "err", err)
+ continue
+ }
+ storageIt := trie.NewIterator(tr.NodeIterator(nil))
for storageIt.Next() {
_, content, _, err := rlp.Split(storageIt.Value)
if err != nil {
diff --git a/core/state/iterator.go b/core/state/iterator.go
index 611df52431..29c4abfc21 100644
--- a/core/state/iterator.go
+++ b/core/state/iterator.go
@@ -109,7 +109,7 @@ func (it *NodeIterator) step() error {
if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), &account); err != nil {
return err
}
- dataTrie, err := it.state.db.OpenStorageTrie(common.BytesToHash(it.stateIt.LeafKey()), account.Root)
+ dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, common.BytesToHash(it.stateIt.LeafKey()), account.Root)
if err != nil {
return err
}
@@ -117,7 +117,7 @@ func (it *NodeIterator) step() error {
if !it.dataIt.Next(true) {
it.dataIt = nil
}
- if !bytes.Equal(account.CodeHash, emptyCodeHash) {
+ if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
it.codeHash = common.BytesToHash(account.CodeHash)
addrHash := common.BytesToHash(it.stateIt.LeafKey())
it.code, err = it.state.db.ContractCode(addrHash, common.BytesToHash(account.CodeHash))
diff --git a/core/state/iterator_test.go b/core/state/iterator_test.go
index d1afe9ca3e..ab06cb422f 100644
--- a/core/state/iterator_test.go
+++ b/core/state/iterator_test.go
@@ -17,20 +17,19 @@
package state
import (
- "bytes"
"testing"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/core/rawdb"
)
// Tests that the node iterator indeed walks over the entire database contents.
func TestNodeIteratorCoverage(t *testing.T) {
// Create some arbitrary test state to iterate
- db, root, _ := makeTestState()
- db.TrieDB().Commit(root, false, nil)
+ db, sdb, root, _ := makeTestState()
+ sdb.TrieDB().Commit(root, false)
- state, err := New(root, db, nil)
+ state, err := New(root, sdb, nil)
if err != nil {
t.Fatalf("failed to create state trie at %x: %v", root, err)
}
@@ -41,29 +40,54 @@ func TestNodeIteratorCoverage(t *testing.T) {
hashes[it.Hash] = struct{}{}
}
}
- // Cross check the iterated hashes and the database/nodepool content
- for hash := range hashes {
- if _, err = db.TrieDB().Node(hash); err != nil {
- _, err = db.ContractCode(common.Hash{}, hash)
- }
- if err != nil {
- t.Errorf("failed to retrieve reported node %x", hash)
- }
- }
- for _, hash := range db.TrieDB().Nodes() {
- if _, ok := hashes[hash]; !ok {
- t.Errorf("state entry not reported %x", hash)
+ // Check in-disk nodes
+ var (
+ seenNodes = make(map[common.Hash]struct{})
+ seenCodes = make(map[common.Hash]struct{})
+ )
+ it := db.NewIterator(nil, nil)
+ for it.Next() {
+ ok, hash := isTrieNode(sdb.TrieDB().Scheme(), it.Key(), it.Value())
+ if !ok {
+ continue
}
+ seenNodes[hash] = struct{}{}
}
- it := db.TrieDB().DiskDB().(ethdb.Database).NewIterator(nil, nil)
+ it.Release()
+
+ // Check in-disk codes
+ it = db.NewIterator(nil, nil)
for it.Next() {
- key := it.Key()
- if bytes.HasPrefix(key, []byte("secure-key-")) {
+ ok, hash := rawdb.IsCodeKey(it.Key())
+ if !ok {
continue
}
- if _, ok := hashes[common.BytesToHash(key)]; !ok {
- t.Errorf("state entry not reported %x", key)
+ if _, ok := hashes[common.BytesToHash(hash)]; !ok {
+ t.Errorf("state entry not reported %x", it.Key())
}
+ seenCodes[common.BytesToHash(hash)] = struct{}{}
}
it.Release()
+
+ // Cross check the iterated hashes and the database/nodepool content
+ for hash := range hashes {
+ _, ok := seenNodes[hash]
+ if !ok {
+ _, ok = seenCodes[hash]
+ }
+ if !ok {
+ t.Errorf("failed to retrieve reported node %x", hash)
+ }
+ }
+}
+
+// isTrieNode is a helper function which reports if the provided
+// database entry belongs to a trie node or not.
+func isTrieNode(scheme string, key, val []byte) (bool, common.Hash) {
+ if scheme == rawdb.HashScheme {
+ if len(key) == common.HashLength {
+ return true, common.BytesToHash(key)
+ }
+ }
+ return false, common.Hash{}
}
diff --git a/core/state/journal.go b/core/state/journal.go
index 57a692dc7f..1722fb4c02 100644
--- a/core/state/journal.go
+++ b/core/state/journal.go
@@ -138,6 +138,11 @@ type (
address *common.Address
slot *common.Hash
}
+
+ transientStorageChange struct {
+ account *common.Address
+ key, prevalue common.Hash
+ }
)
func (ch createObjectChange) revert(s *StateDB) {
@@ -151,8 +156,8 @@ func (ch createObjectChange) dirtied() *common.Address {
func (ch resetObjectChange) revert(s *StateDB) {
s.setStateObject(ch.prev)
- if !ch.prevdestruct && s.snap != nil {
- delete(s.snapDestructs, ch.prev.addrHash)
+ if !ch.prevdestruct {
+ delete(s.stateObjectsDestruct, ch.prev.address)
}
}
@@ -213,6 +218,14 @@ func (ch storageChange) dirtied() *common.Address {
return ch.account
}
+func (ch transientStorageChange) revert(s *StateDB) {
+ s.setTransientState(*ch.account, ch.key, ch.prevalue)
+}
+
+func (ch transientStorageChange) dirtied() *common.Address {
+ return nil
+}
+
func (ch refundChange) revert(s *StateDB) {
s.refund = ch.prev
}
diff --git a/core/state/metrics.go b/core/state/metrics.go
index 35d2df92dd..e702ef3a81 100644
--- a/core/state/metrics.go
+++ b/core/state/metrics.go
@@ -19,10 +19,12 @@ package state
import "github.com/ethereum/go-ethereum/metrics"
var (
- accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
- storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
- accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
- storageDeletedMeter = metrics.NewRegisteredMeter("state/delete/storage", nil)
- accountTrieCommittedMeter = metrics.NewRegisteredMeter("state/commit/accountnodes", nil)
- storageTriesCommittedMeter = metrics.NewRegisteredMeter("state/commit/storagenodes", nil)
+ accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
+ storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
+ accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
+ storageDeletedMeter = metrics.NewRegisteredMeter("state/delete/storage", nil)
+ accountTrieUpdatedMeter = metrics.NewRegisteredMeter("state/update/accountnodes", nil)
+ storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil)
+ accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil)
+ storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil)
)
diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go
index 2da2eda8b7..2bd5658e0a 100644
--- a/core/state/pruner/pruner.go
+++ b/core/state/pruner/pruner.go
@@ -31,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"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/rlp"
@@ -55,60 +54,63 @@ const (
rangeCompactionThreshold = 100000
)
-var (
- // emptyRoot is the known root hash of an empty trie.
- emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
-
- // emptyCode is the known hash of the empty EVM bytecode.
- emptyCode = crypto.Keccak256(nil)
-)
+// Config includes all the configurations for pruning.
+type Config struct {
+ Datadir string // The directory of the state database
+ Cachedir string // The directory of state clean cache
+ BloomSize uint64 // The Megabytes of memory allocated to bloom-filter
+}
// Pruner is an offline tool to prune the stale state with the
// help of the snapshot. The workflow of pruner is very simple:
//
-// - iterate the snapshot, reconstruct the relevant state
-// - iterate the database, delete all other state entries which
-// don't belong to the target state and the genesis state
+// - iterate the snapshot, reconstruct the relevant state
+// - iterate the database, delete all other state entries which
+// don't belong to the target state and the genesis state
//
// It can take several hours(around 2 hours for mainnet) to finish
// the whole pruning work. It's recommended to run this offline tool
// periodically in order to release the disk usage and improve the
// disk read performance to some extent.
type Pruner struct {
- db ethdb.Database
- stateBloom *stateBloom
- datadir string
- trieCachePath string
- headHeader *types.Header
- snaptree *snapshot.Tree
+ config Config
+ chainHeader *types.Header
+ db ethdb.Database
+ stateBloom *stateBloom
+ snaptree *snapshot.Tree
}
// NewPruner creates the pruner instance.
-func NewPruner(db ethdb.Database, datadir, trieCachePath string, bloomSize uint64) (*Pruner, error) {
+func NewPruner(db ethdb.Database, config Config) (*Pruner, error) {
headBlock := rawdb.ReadHeadBlock(db)
if headBlock == nil {
- return nil, errors.New("Failed to load head block")
+ return nil, errors.New("failed to load head block")
}
- snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, headBlock.Root(), false, false, false)
+ snapconfig := snapshot.Config{
+ CacheSize: 256,
+ Recovery: false,
+ NoBuild: true,
+ AsyncBuild: false,
+ }
+ snaptree, err := snapshot.New(snapconfig, db, trie.NewDatabase(db), headBlock.Root())
if err != nil {
return nil, err // The relevant snapshot(s) might not exist
}
// Sanitize the bloom filter size if it's too small.
- if bloomSize < 256 {
- log.Warn("Sanitizing bloomfilter size", "provided(MB)", bloomSize, "updated(MB)", 256)
- bloomSize = 256
+ if config.BloomSize < 256 {
+ log.Warn("Sanitizing bloomfilter size", "provided(MB)", config.BloomSize, "updated(MB)", 256)
+ config.BloomSize = 256
}
- stateBloom, err := newStateBloomWithSize(bloomSize)
+ stateBloom, err := newStateBloomWithSize(config.BloomSize)
if err != nil {
return nil, err
}
return &Pruner{
- db: db,
- stateBloom: stateBloom,
- datadir: datadir,
- trieCachePath: trieCachePath,
- headHeader: headBlock.Header(),
- snaptree: snaptree,
+ config: config,
+ chainHeader: headBlock.Header(),
+ db: db,
+ stateBloom: stateBloom,
+ snaptree: snaptree,
}, nil
}
@@ -236,12 +238,12 @@ func (p *Pruner) Prune(root common.Hash) error {
// reuse it for pruning instead of generating a new one. It's
// mandatory because a part of state may already be deleted,
// the recovery procedure is necessary.
- _, stateBloomRoot, err := findBloomFilter(p.datadir)
+ _, stateBloomRoot, err := findBloomFilter(p.config.Datadir)
if err != nil {
return err
}
if stateBloomRoot != (common.Hash{}) {
- return RecoverPruning(p.datadir, p.db, p.trieCachePath)
+ return RecoverPruning(p.config.Datadir, p.db, p.config.Cachedir)
}
// If the target state root is not specified, use the HEAD-127 as the
// target. The reason for picking it is:
@@ -252,7 +254,7 @@ func (p *Pruner) Prune(root common.Hash) error {
// Retrieve all snapshot layers from the current HEAD.
// In theory there are 128 difflayers + 1 disk layer present,
// so 128 diff layers are expected to be returned.
- layers = p.snaptree.Snapshots(p.headHeader.Root, 128, true)
+ layers = p.snaptree.Snapshots(p.chainHeader.Root, 128, true)
if len(layers) != 128 {
// Reject if the accumulated diff layers are less than 128. It
// means in most of normal cases, there is no associated state
@@ -265,7 +267,7 @@ func (p *Pruner) Prune(root common.Hash) error {
// Ensure the root is really present. The weak assumption
// is the presence of root can indicate the presence of the
// entire trie.
- if !rawdb.HasTrieNode(p.db, root) {
+ if !rawdb.HasLegacyTrieNode(p.db, root) {
// The special case is for clique based networks(rinkeby, goerli
// and some other private networks), it's possible that two
// consecutive blocks will have same root. In this case snapshot
@@ -279,7 +281,7 @@ func (p *Pruner) Prune(root common.Hash) error {
// as the pruning target.
var found bool
for i := len(layers) - 2; i >= 2; i-- {
- if rawdb.HasTrieNode(p.db, layers[i].Root()) {
+ if rawdb.HasLegacyTrieNode(p.db, layers[i].Root()) {
root = layers[i].Root()
found = true
log.Info("Selecting middle-layer as the pruning target", "root", root, "depth", i)
@@ -294,7 +296,7 @@ func (p *Pruner) Prune(root common.Hash) error {
}
} else {
if len(layers) > 0 {
- log.Info("Selecting bottom-most difflayer as the pruning target", "root", root, "height", p.headHeader.Number.Uint64()-127)
+ log.Info("Selecting bottom-most difflayer as the pruning target", "root", root, "height", p.chainHeader.Number.Uint64()-127)
} else {
log.Info("Selecting user-specified state as the pruning target", "root", root)
}
@@ -303,7 +305,7 @@ func (p *Pruner) Prune(root common.Hash) error {
// It's necessary otherwise in the next restart we will hit the
// deleted state root in the "clean cache" so that the incomplete
// state is picked for usage.
- deleteCleanTrieCache(p.trieCachePath)
+ deleteCleanTrieCache(p.config.Cachedir)
// All the state roots of the middle layer should be forcibly pruned,
// otherwise the dangling state will be left.
@@ -325,7 +327,7 @@ func (p *Pruner) Prune(root common.Hash) error {
if err := extractGenesis(p.db, p.stateBloom); err != nil {
return err
}
- filterName := bloomFilterName(p.datadir, root)
+ filterName := bloomFilterName(p.config.Datadir, root)
log.Info("Writing state bloom to disk", "name", filterName)
if err := p.stateBloom.Commit(filterName, filterName+stateBloomFileTempSuffix); err != nil {
@@ -352,7 +354,7 @@ func RecoverPruning(datadir string, db ethdb.Database, trieCachePath string) err
}
headBlock := rawdb.ReadHeadBlock(db)
if headBlock == nil {
- return errors.New("Failed to load head block")
+ return errors.New("failed to load head block")
}
// Initialize the snapshot tree in recovery mode to handle this special case:
// - Users run the `prune-state` command multiple times
@@ -362,7 +364,13 @@ func RecoverPruning(datadir string, db ethdb.Database, trieCachePath string) err
// - The state HEAD is rewound already because of multiple incomplete `prune-state`
// In this case, even the state HEAD is not exactly matched with snapshot, it
// still feasible to recover the pruning correctly.
- snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, headBlock.Root(), false, false, true)
+ snapconfig := snapshot.Config{
+ CacheSize: 256,
+ Recovery: true,
+ NoBuild: true,
+ AsyncBuild: false,
+ }
+ snaptree, err := snapshot.New(snapconfig, db, trie.NewDatabase(db), headBlock.Root())
if err != nil {
return err // The relevant snapshot(s) might not exist
}
@@ -410,7 +418,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
if genesis == nil {
return errors.New("missing genesis block")
}
- t, err := trie.NewStateTrie(common.Hash{}, genesis.Root(), trie.NewDatabase(db))
+ t, err := trie.NewStateTrie(trie.StateTrieID(genesis.Root()), trie.NewDatabase(db))
if err != nil {
return err
}
@@ -429,8 +437,9 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
return err
}
- if acc.Root != emptyRoot {
- storageTrie, err := trie.NewStateTrie(common.BytesToHash(accIter.LeafKey()), acc.Root, trie.NewDatabase(db))
+ if acc.Root != types.EmptyRootHash {
+ id := trie.StorageTrieID(genesis.Root(), common.BytesToHash(accIter.LeafKey()), acc.Root)
+ storageTrie, err := trie.NewStateTrie(id, trie.NewDatabase(db))
if err != nil {
return err
}
@@ -445,7 +454,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
return storageIter.Error()
}
}
- if !bytes.Equal(acc.CodeHash, emptyCode) {
+ if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
stateBloom.Put(acc.CodeHash, nil)
}
}
diff --git a/core/state/snapshot/account.go b/core/state/snapshot/account.go
index b92e942950..b5634972ad 100644
--- a/core/state/snapshot/account.go
+++ b/core/state/snapshot/account.go
@@ -21,6 +21,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -41,10 +42,10 @@ func SlimAccount(nonce uint64, balance *big.Int, root common.Hash, codehash []by
Nonce: nonce,
Balance: balance,
}
- if root != emptyRoot {
+ if root != types.EmptyRootHash {
slim.Root = root[:]
}
- if !bytes.Equal(codehash, emptyCode[:]) {
+ if !bytes.Equal(codehash, types.EmptyCodeHash[:]) {
slim.CodeHash = codehash
}
return slim
@@ -68,10 +69,10 @@ func FullAccount(data []byte) (Account, error) {
return Account{}, err
}
if len(account.Root) == 0 {
- account.Root = emptyRoot[:]
+ account.Root = types.EmptyRootHash[:]
}
if len(account.CodeHash) == 0 {
- account.CodeHash = emptyCode[:]
+ account.CodeHash = types.EmptyCodeHash[:]
}
return account, nil
}
diff --git a/core/state/snapshot/conversion.go b/core/state/snapshot/conversion.go
index 0f3934cb42..ed7cb963ad 100644
--- a/core/state/snapshot/conversion.go
+++ b/core/state/snapshot/conversion.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"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/rlp"
@@ -43,7 +44,7 @@ type trieKV struct {
type (
// trieGeneratorFn is the interface of trie generation which can
// be implemented by different trie algorithm.
- trieGeneratorFn func(db ethdb.KeyValueWriter, owner common.Hash, in chan (trieKV), out chan (common.Hash))
+ trieGeneratorFn func(db ethdb.KeyValueWriter, scheme string, owner common.Hash, in chan (trieKV), out chan (common.Hash))
// leafCallbackFn is the callback invoked at the leaves of the trie,
// returns the subtrie root with the specified subtrie identifier.
@@ -52,12 +53,12 @@ type (
// GenerateAccountTrieRoot takes an account iterator and reproduces the root hash.
func GenerateAccountTrieRoot(it AccountIterator) (common.Hash, error) {
- return generateTrieRoot(nil, it, common.Hash{}, stackTrieGenerate, nil, newGenerateStats(), true)
+ return generateTrieRoot(nil, "", it, common.Hash{}, stackTrieGenerate, nil, newGenerateStats(), true)
}
// GenerateStorageTrieRoot takes a storage iterator and reproduces the root hash.
func GenerateStorageTrieRoot(account common.Hash, it StorageIterator) (common.Hash, error) {
- return generateTrieRoot(nil, it, account, stackTrieGenerate, nil, newGenerateStats(), true)
+ return generateTrieRoot(nil, "", it, account, stackTrieGenerate, nil, newGenerateStats(), true)
}
// GenerateTrie takes the whole snapshot tree as the input, traverses all the
@@ -71,9 +72,10 @@ func GenerateTrie(snaptree *Tree, root common.Hash, src ethdb.Database, dst ethd
}
defer acctIt.Release()
- got, err := generateTrieRoot(dst, acctIt, common.Hash{}, stackTrieGenerate, func(dst ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) {
+ scheme := snaptree.triedb.Scheme()
+ got, err := generateTrieRoot(dst, scheme, acctIt, common.Hash{}, stackTrieGenerate, func(dst ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) {
// Migrate the code first, commit the contract code into the tmp db.
- if codeHash != emptyCode {
+ if codeHash != types.EmptyCodeHash {
code := rawdb.ReadCode(src, codeHash)
if len(code) == 0 {
return common.Hash{}, errors.New("failed to read contract code")
@@ -87,7 +89,7 @@ func GenerateTrie(snaptree *Tree, root common.Hash, src ethdb.Database, dst ethd
}
defer storageIt.Release()
- hash, err := generateTrieRoot(dst, storageIt, accountHash, stackTrieGenerate, nil, stat, false)
+ hash, err := generateTrieRoot(dst, scheme, storageIt, accountHash, stackTrieGenerate, nil, stat, false)
if err != nil {
return common.Hash{}, err
}
@@ -136,7 +138,7 @@ func (stat *generateStats) progressAccounts(account common.Hash, done uint64) {
stat.head = account
}
-// finishAccounts updates the gemerator stats for the finished account range.
+// finishAccounts updates the generator stats for the finished account range.
func (stat *generateStats) finishAccounts(done uint64) {
stat.lock.Lock()
defer stat.lock.Unlock()
@@ -242,7 +244,7 @@ func runReport(stats *generateStats, stop chan bool) {
// generateTrieRoot generates the trie hash based on the snapshot iterator.
// It can be used for generating account trie, storage trie or even the
// whole state which connects the accounts and the corresponding storages.
-func generateTrieRoot(db ethdb.KeyValueWriter, it Iterator, account common.Hash, generatorFn trieGeneratorFn, leafCallback leafCallbackFn, stats *generateStats, report bool) (common.Hash, error) {
+func generateTrieRoot(db ethdb.KeyValueWriter, scheme string, it Iterator, account common.Hash, generatorFn trieGeneratorFn, leafCallback leafCallbackFn, stats *generateStats, report bool) (common.Hash, error) {
var (
in = make(chan trieKV) // chan to pass leaves
out = make(chan common.Hash, 1) // chan to collect result
@@ -253,7 +255,7 @@ func generateTrieRoot(db ethdb.KeyValueWriter, it Iterator, account common.Hash,
wg.Add(1)
go func() {
defer wg.Done()
- generatorFn(db, account, in, out)
+ generatorFn(db, scheme, account, in, out)
}()
// Spin up a go-routine for progress logging
if report && stats != nil {
@@ -360,8 +362,14 @@ func generateTrieRoot(db ethdb.KeyValueWriter, it Iterator, account common.Hash,
return stop(nil)
}
-func stackTrieGenerate(db ethdb.KeyValueWriter, owner common.Hash, in chan trieKV, out chan common.Hash) {
- t := trie.NewStackTrieWithOwner(db, owner)
+func stackTrieGenerate(db ethdb.KeyValueWriter, scheme string, owner common.Hash, in chan trieKV, out chan common.Hash) {
+ var nodeWriter trie.NodeWriteFunc
+ if db != nil {
+ nodeWriter = func(owner common.Hash, path []byte, hash common.Hash, blob []byte) {
+ rawdb.WriteTrieNode(db, owner, path, hash, blob, scheme)
+ }
+ }
+ t := trie.NewStackTrieWithOwner(nodeWriter, owner)
for leaf := range in {
t.TryUpdate(leaf.key[:], leaf.value)
}
diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go
index 822c91f15c..f916a020e7 100644
--- a/core/state/snapshot/difflayer.go
+++ b/core/state/snapshot/difflayer.go
@@ -68,7 +68,7 @@ var (
bloomFuncs = math.Round((bloomSize / float64(aggregatorItemLimit)) * math.Log(2))
// the bloom offsets are runtime constants which determines which part of the
- // the account/storage hash the hasher functions looks at, to determine the
+ // account/storage hash the hasher functions looks at, to determine the
// bloom key for an account/slot. This is randomized at init(), so that the
// global population of nodes do not all display the exact same behaviour with
// regards to bloom content
diff --git a/core/state/snapshot/difflayer_test.go b/core/state/snapshot/difflayer_test.go
index 59db920481..674a031b16 100644
--- a/core/state/snapshot/difflayer_test.go
+++ b/core/state/snapshot/difflayer_test.go
@@ -18,6 +18,7 @@ package snapshot
import (
"bytes"
+ crand "crypto/rand"
"math/rand"
"testing"
@@ -73,7 +74,7 @@ func TestMergeBasics(t *testing.T) {
if rand.Intn(2) == 0 {
accStorage := make(map[common.Hash][]byte)
value := make([]byte, 32)
- rand.Read(value)
+ crand.Read(value)
accStorage[randomHash()] = value
storage[h] = accStorage
}
@@ -294,7 +295,7 @@ func BenchmarkSearchSlot(b *testing.B) {
accStorage := make(map[common.Hash][]byte)
for i := 0; i < 5; i++ {
value := make([]byte, 32)
- rand.Read(value)
+ crand.Read(value)
accStorage[randomHash()] = value
storage[accountKey] = accStorage
}
@@ -330,7 +331,7 @@ func BenchmarkFlatten(b *testing.B) {
accStorage := make(map[common.Hash][]byte)
for i := 0; i < 20; i++ {
value := make([]byte, 32)
- rand.Read(value)
+ crand.Read(value)
accStorage[randomHash()] = value
}
storage[accountKey] = accStorage
@@ -379,7 +380,7 @@ func BenchmarkJournal(b *testing.B) {
accStorage := make(map[common.Hash][]byte)
for i := 0; i < 200; i++ {
value := make([]byte, 32)
- rand.Read(value)
+ crand.Read(value)
accStorage[randomHash()] = value
}
storage[accountKey] = accStorage
diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go
index bf714db4c2..d46705d31e 100644
--- a/core/state/snapshot/generate.go
+++ b/core/state/snapshot/generate.go
@@ -27,21 +27,14 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
var (
- // emptyRoot is the known root hash of an empty trie.
- emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
-
- // emptyCode is the known hash of the empty EVM bytecode.
- emptyCode = crypto.Keccak256Hash(nil)
-
// accountCheckRange is the upper limit of the number of accounts involved in
// each range check. This is a value estimated based on experience. If this
// range is too large, the failure rate of range proof will increase. Otherwise,
@@ -166,7 +159,7 @@ func (result *proofResult) forEach(callback func(key []byte, val []byte) error)
//
// The proof result will be returned if the range proving is finished, otherwise
// the error will be returned to abort the entire procedure.
-func (dl *diskLayer) proveRange(ctx *generatorContext, owner common.Hash, root common.Hash, prefix []byte, kind string, origin []byte, max int, valueConvertFn func([]byte) ([]byte, error)) (*proofResult, error) {
+func (dl *diskLayer) proveRange(ctx *generatorContext, trieId *trie.ID, prefix []byte, kind string, origin []byte, max int, valueConvertFn func([]byte) ([]byte, error)) (*proofResult, error) {
var (
keys [][]byte
vals [][]byte
@@ -233,8 +226,9 @@ func (dl *diskLayer) proveRange(ctx *generatorContext, owner common.Hash, root c
}(time.Now())
// The snap state is exhausted, pass the entire key/val set for verification
+ root := trieId.Root
if origin == nil && !diskMore {
- stackTr := trie.NewStackTrieWithOwner(nil, owner)
+ stackTr := trie.NewStackTrie(nil)
for i, key := range keys {
stackTr.TryUpdate(key, vals[i])
}
@@ -248,7 +242,7 @@ func (dl *diskLayer) proveRange(ctx *generatorContext, owner common.Hash, root c
return &proofResult{keys: keys, vals: vals}, nil
}
// Snap state is chunked, generate edge proofs for verification.
- tr, err := trie.New(owner, root, dl.triedb)
+ tr, err := trie.New(trieId, dl.triedb)
if err != nil {
ctx.stats.Log("Trie missing, state snapshotting paused", dl.root, dl.genMarker)
return nil, errMissingTrie
@@ -313,9 +307,9 @@ type onStateCallback func(key []byte, val []byte, write bool, delete bool) error
// generateRange generates the state segment with particular prefix. Generation can
// either verify the correctness of existing state through range-proof and skip
// generation, or iterate trie to regenerate state on demand.
-func (dl *diskLayer) generateRange(ctx *generatorContext, owner common.Hash, root common.Hash, prefix []byte, kind string, origin []byte, max int, onState onStateCallback, valueConvertFn func([]byte) ([]byte, error)) (bool, []byte, error) {
+func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefix []byte, kind string, origin []byte, max int, onState onStateCallback, valueConvertFn func([]byte) ([]byte, error)) (bool, []byte, error) {
// Use range prover to check the validity of the flat state in the range
- result, err := dl.proveRange(ctx, owner, root, prefix, kind, origin, max, valueConvertFn)
+ result, err := dl.proveRange(ctx, trieId, prefix, kind, origin, max, valueConvertFn)
if err != nil {
return false, nil, err
}
@@ -359,25 +353,28 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, owner common.Hash, roo
}
// We use the snap data to build up a cache which can be used by the
// main account trie as a primary lookup when resolving hashes
- var snapNodeCache ethdb.KeyValueStore
+ var resolver trie.NodeResolver
if len(result.keys) > 0 {
- snapNodeCache = memorydb.New()
- snapTrieDb := trie.NewDatabase(snapNodeCache)
- snapTrie, _ := trie.New(owner, common.Hash{}, snapTrieDb)
+ mdb := rawdb.NewMemoryDatabase()
+ tdb := trie.NewDatabase(mdb)
+ snapTrie := trie.NewEmpty(tdb)
for i, key := range result.keys {
snapTrie.Update(key, result.vals[i])
}
- root, nodes, _ := snapTrie.Commit(false)
+ root, nodes := snapTrie.Commit(false)
if nodes != nil {
- snapTrieDb.Update(trie.NewWithNodeSet(nodes))
+ tdb.Update(trie.NewWithNodeSet(nodes))
+ tdb.Commit(root, false)
+ }
+ resolver = func(owner common.Hash, path []byte, hash common.Hash) []byte {
+ return rawdb.ReadTrieNode(mdb, owner, path, hash, tdb.Scheme())
}
- snapTrieDb.Commit(root, false, nil)
}
// Construct the trie for state iteration, reuse the trie
// if it's already opened with some nodes resolved.
tr := result.tr
if tr == nil {
- tr, err = trie.New(owner, root, dl.triedb)
+ tr, err = trie.New(trieId, dl.triedb)
if err != nil {
ctx.stats.Log("Trie missing, state snapshotting paused", dl.root, dl.genMarker)
return false, nil, errMissingTrie
@@ -400,7 +397,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, owner common.Hash, roo
start = time.Now()
internal time.Duration
)
- nodeIt.AddResolver(snapNodeCache)
+ nodeIt.AddResolver(resolver)
for iter.Next() {
if last != nil && bytes.Compare(iter.Key, last) > 0 {
@@ -460,7 +457,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, owner common.Hash, roo
} else {
snapAccountTrieReadCounter.Inc((time.Since(start) - internal).Nanoseconds())
}
- logger.Debug("Regenerated state range", "root", root, "last", hexutil.Encode(last),
+ logger.Debug("Regenerated state range", "root", trieId.Root, "last", hexutil.Encode(last),
"count", count, "created", created, "updated", updated, "untouched", untouched, "deleted", deleted)
// If there are either more trie items, or there are more snap items
@@ -511,7 +508,7 @@ func (dl *diskLayer) checkAndFlush(ctx *generatorContext, current []byte) error
// generateStorages generates the missing storage slots of the specific contract.
// It's supposed to restart the generation from the given origin position.
-func generateStorages(ctx *generatorContext, dl *diskLayer, account common.Hash, storageRoot common.Hash, storeMarker []byte) error {
+func generateStorages(ctx *generatorContext, dl *diskLayer, stateRoot common.Hash, account common.Hash, storageRoot common.Hash, storeMarker []byte) error {
onStorage := func(key []byte, val []byte, write bool, delete bool) error {
defer func(start time.Time) {
snapStorageWriteCounter.Inc(time.Since(start).Nanoseconds())
@@ -540,7 +537,8 @@ func generateStorages(ctx *generatorContext, dl *diskLayer, account common.Hash,
// Loop for re-generating the missing storage slots.
var origin = common.CopyBytes(storeMarker)
for {
- exhausted, last, err := dl.generateRange(ctx, account, storageRoot, append(rawdb.SnapshotStoragePrefix, account.Bytes()...), snapStorage, origin, storageCheckRange, onStorage, nil)
+ id := trie.StorageTrieID(stateRoot, account, storageRoot)
+ exhausted, last, err := dl.generateRange(ctx, id, append(rawdb.SnapshotStoragePrefix, account.Bytes()...), snapStorage, origin, storageCheckRange, onStorage, nil)
if err != nil {
return err // The procedure it aborted, either by external signal or internal error.
}
@@ -587,10 +585,10 @@ func generateAccounts(ctx *generatorContext, dl *diskLayer, accMarker []byte) er
if accMarker == nil || !bytes.Equal(account[:], accMarker) {
dataLen := len(val) // Approximate size, saves us a round of RLP-encoding
if !write {
- if bytes.Equal(acc.CodeHash, emptyCode[:]) {
+ if bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) {
dataLen -= 32
}
- if acc.Root == emptyRoot {
+ if acc.Root == types.EmptyRootHash {
dataLen -= 32
}
snapRecoveredAccountMeter.Mark(1)
@@ -617,14 +615,14 @@ func generateAccounts(ctx *generatorContext, dl *diskLayer, accMarker []byte) er
// If the iterated account is the contract, create a further loop to
// verify or regenerate the contract storage.
- if acc.Root == emptyRoot {
+ if acc.Root == types.EmptyRootHash {
ctx.removeStorageAt(account)
} else {
var storeMarker []byte
if accMarker != nil && bytes.Equal(account[:], accMarker) && len(dl.genMarker) > common.HashLength {
storeMarker = dl.genMarker[common.HashLength:]
}
- if err := generateStorages(ctx, dl, account, acc.Root, storeMarker); err != nil {
+ if err := generateStorages(ctx, dl, dl.root, account, acc.Root, storeMarker); err != nil {
return err
}
}
@@ -640,7 +638,8 @@ func generateAccounts(ctx *generatorContext, dl *diskLayer, accMarker []byte) er
}
origin := common.CopyBytes(accMarker)
for {
- exhausted, last, err := dl.generateRange(ctx, common.Hash{}, dl.root, rawdb.SnapshotAccountPrefix, snapAccount, origin, accountRange, onAccount, FullAccountRLP)
+ id := trie.StateTrieID(dl.root)
+ exhausted, last, err := dl.generateRange(ctx, id, rawdb.SnapshotAccountPrefix, snapAccount, origin, accountRange, onAccount, FullAccountRLP)
if err != nil {
return err // The procedure it aborted, either by external signal or internal error.
}
diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go
index 5e5ded61ea..1bac4fd560 100644
--- a/core/state/snapshot/generate_test.go
+++ b/core/state/snapshot/generate_test.go
@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"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/rlp"
@@ -49,9 +50,9 @@ func TestGeneration(t *testing.T) {
var helper = newHelper()
stRoot := helper.makeStorageTrie(common.Hash{}, common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, false)
- helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
- helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
- helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
@@ -83,16 +84,16 @@ func TestGenerateExistentState(t *testing.T) {
var helper = newHelper()
stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
- helper.addSnapAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addSnapAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
- helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
- helper.addSnapAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
+ helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addSnapAccount("acc-2", &Account{Balance: big.NewInt(2), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()})
stRoot = helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()})
- helper.addSnapAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addSnapAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
root, snap := helper.CommitAndGenerate()
@@ -117,12 +118,12 @@ func checkSnapRoot(t *testing.T, snap *diskLayer, trieRoot common.Hash) {
accIt := snap.AccountIterator(common.Hash{})
defer accIt.Release()
- snapRoot, err := generateTrieRoot(nil, accIt, common.Hash{}, stackTrieGenerate,
+ snapRoot, err := generateTrieRoot(nil, "", accIt, common.Hash{}, stackTrieGenerate,
func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) {
storageIt, _ := snap.StorageIterator(accountHash, common.Hash{})
defer storageIt.Release()
- hash, err := generateTrieRoot(nil, storageIt, accountHash, stackTrieGenerate, nil, stat, false)
+ hash, err := generateTrieRoot(nil, "", storageIt, accountHash, stackTrieGenerate, nil, stat, false)
if err != nil {
return common.Hash{}, err
}
@@ -149,7 +150,7 @@ type testHelper struct {
func newHelper() *testHelper {
diskdb := rawdb.NewMemoryDatabase()
triedb := trie.NewDatabase(diskdb)
- accTrie, _ := trie.NewStateTrie(common.Hash{}, common.Hash{}, triedb)
+ accTrie, _ := trie.NewStateTrie(trie.StateTrieID(common.Hash{}), triedb)
return &testHelper{
diskdb: diskdb,
triedb: triedb,
@@ -182,14 +183,15 @@ func (t *testHelper) addSnapStorage(accKey string, keys []string, vals []string)
}
func (t *testHelper) makeStorageTrie(stateRoot, owner common.Hash, keys []string, vals []string, commit bool) []byte {
- stTrie, _ := trie.NewStateTrie(owner, common.Hash{}, t.triedb)
+ id := trie.StorageTrieID(stateRoot, owner, common.Hash{})
+ stTrie, _ := trie.NewStateTrie(id, t.triedb)
for i, k := range keys {
stTrie.Update([]byte(k), []byte(vals[i]))
}
if !commit {
return stTrie.Hash().Bytes()
}
- root, nodes, _ := stTrie.Commit(false)
+ root, nodes := stTrie.Commit(false)
if nodes != nil {
t.nodes.Merge(nodes)
}
@@ -197,12 +199,12 @@ func (t *testHelper) makeStorageTrie(stateRoot, owner common.Hash, keys []string
}
func (t *testHelper) Commit() common.Hash {
- root, nodes, _ := t.accTrie.Commit(true)
+ root, nodes := t.accTrie.Commit(true)
if nodes != nil {
t.nodes.Merge(nodes)
}
t.triedb.Update(t.nodes)
- t.triedb.Commit(root, false, nil)
+ t.triedb.Commit(root, false)
return root
}
@@ -220,10 +222,12 @@ func (t *testHelper) CommitAndGenerate() (common.Hash, *diskLayer) {
// - miss in the beginning
// - miss in the middle
// - miss in the end
+//
// - the contract(non-empty storage) has wrong storage slots
// - wrong slots in the beginning
// - wrong slots in the middle
// - wrong slots in the end
+//
// - the contract(non-empty storage) has extra storage slots
// - extra slots in the beginning
// - extra slots in the middle
@@ -232,28 +236,28 @@ func TestGenerateExistentStateWithWrongStorage(t *testing.T) {
helper := newHelper()
// Account one, empty root but non-empty database
- helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
// Account two, non empty root but empty database
stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
// Miss slots
{
// Account three, non empty root but misses slots in the beginning
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-3", []string{"key-2", "key-3"}, []string{"val-2", "val-3"})
// Account four, non empty root but misses slots in the middle
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-4", []string{"key-1", "key-3"}, []string{"val-1", "val-3"})
// Account five, non empty root but misses slots in the end
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-5")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-5", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-5", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"})
}
@@ -261,22 +265,22 @@ func TestGenerateExistentStateWithWrongStorage(t *testing.T) {
{
// Account six, non empty root but wrong slots in the beginning
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"badval-1", "val-2", "val-3"})
// Account seven, non empty root but wrong slots in the middle
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-7")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-7", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-7", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "badval-2", "val-3"})
// Account eight, non empty root but wrong slots in the end
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-8")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-8", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-8", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "badval-3"})
// Account 9, non empty root but rotated slots
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-9")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-9", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-9", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"})
}
@@ -284,17 +288,17 @@ func TestGenerateExistentStateWithWrongStorage(t *testing.T) {
{
// Account 10, non empty root but extra slots in the beginning
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-10")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-10", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-10", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-10", []string{"key-0", "key-1", "key-2", "key-3"}, []string{"val-0", "val-1", "val-2", "val-3"})
// Account 11, non empty root but extra slots in the middle
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-11")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-11", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-11", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-11", []string{"key-1", "key-2", "key-2-1", "key-3"}, []string{"val-1", "val-2", "val-2-1", "val-3"})
// Account 12, non empty root but extra slots in the end
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-12")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-12", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-12", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-12", []string{"key-1", "key-2", "key-3", "key-4"}, []string{"val-1", "val-2", "val-3", "val-4"})
}
@@ -334,25 +338,25 @@ func TestGenerateExistentStateWithWrongAccounts(t *testing.T) {
// Missing accounts, only in the trie
{
- helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // Beginning
- helper.addTrieAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // Middle
- helper.addTrieAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // End
+ helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Beginning
+ helper.addTrieAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Middle
+ helper.addTrieAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // End
}
// Wrong accounts
{
- helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: common.Hex2Bytes("0x1234")})
- helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
- helper.addSnapAccount("acc-3", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
+ helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addSnapAccount("acc-3", &Account{Balance: big.NewInt(1), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()})
}
// Extra accounts, only in the snap
{
- helper.addSnapAccount("acc-0", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyRoot.Bytes()}) // before the beginning
- helper.addSnapAccount("acc-5", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: common.Hex2Bytes("0x1234")}) // Middle
- helper.addSnapAccount("acc-7", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyRoot.Bytes()}) // after the end
+ helper.addSnapAccount("acc-0", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // before the beginning
+ helper.addSnapAccount("acc-5", &Account{Balance: big.NewInt(1), Root: types.EmptyRootHash.Bytes(), CodeHash: common.Hex2Bytes("0x1234")}) // Middle
+ helper.addSnapAccount("acc-7", &Account{Balance: big.NewInt(1), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()}) // after the end
}
root, snap := helper.CommitAndGenerate()
@@ -381,14 +385,14 @@ func TestGenerateCorruptAccountTrie(t *testing.T) {
// without any storage slots to keep the test smaller.
helper := newHelper()
- helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074
- helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
- helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4
+ helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074
+ helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
+ helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4
root := helper.Commit() // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978
// Delete an account trie leaf and ensure the generator chokes
- helper.triedb.Commit(root, false, nil)
+ helper.triedb.Commit(root, false)
helper.diskdb.Delete(common.HexToHash("0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7").Bytes())
snap := generateSnapshot(helper.diskdb, helper.triedb, 16, root)
@@ -416,10 +420,10 @@ func TestGenerateMissingStorageTrie(t *testing.T) {
helper := newHelper()
stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
- helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
- helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
+ helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
+ helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
stRoot = helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
+ helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
root := helper.Commit()
@@ -450,10 +454,10 @@ func TestGenerateCorruptStorageTrie(t *testing.T) {
helper := newHelper()
stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
- helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
- helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
+ helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
+ helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
stRoot = helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
+ helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
root := helper.Commit()
@@ -485,18 +489,18 @@ func TestGenerateWithExtraAccounts(t *testing.T) {
[]string{"val-1", "val-2", "val-3", "val-4", "val-5"},
true,
)
- acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}
+ acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
// Identical in the snap
key := hashData([]byte("acc-1"))
- rawdb.WriteAccountSnapshot(helper.triedb.DiskDB(), key, val)
- rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-1")), []byte("val-1"))
- rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-2")), []byte("val-2"))
- rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-3")), []byte("val-3"))
- rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-4")), []byte("val-4"))
- rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-5")), []byte("val-5"))
+ rawdb.WriteAccountSnapshot(helper.diskdb, key, val)
+ rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-1")), []byte("val-1"))
+ rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-2")), []byte("val-2"))
+ rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-3")), []byte("val-3"))
+ rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-4")), []byte("val-4"))
+ rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-5")), []byte("val-5"))
}
{
// Account two exists only in the snapshot
@@ -505,18 +509,18 @@ func TestGenerateWithExtraAccounts(t *testing.T) {
[]string{"val-1", "val-2", "val-3", "val-4", "val-5"},
true,
)
- acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}
+ acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
key := hashData([]byte("acc-2"))
- rawdb.WriteAccountSnapshot(helper.triedb.DiskDB(), key, val)
- rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("b-key-1")), []byte("b-val-1"))
- rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("b-key-2")), []byte("b-val-2"))
- rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("b-key-3")), []byte("b-val-3"))
+ rawdb.WriteAccountSnapshot(helper.diskdb, key, val)
+ rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("b-key-1")), []byte("b-val-1"))
+ rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("b-key-2")), []byte("b-val-2"))
+ rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("b-key-3")), []byte("b-val-3"))
}
root := helper.Commit()
// To verify the test: If we now inspect the snap db, there should exist extraneous storage items
- if data := rawdb.ReadStorageSnapshot(helper.triedb.DiskDB(), hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data == nil {
+ if data := rawdb.ReadStorageSnapshot(helper.diskdb, hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data == nil {
t.Fatalf("expected snap storage to exist")
}
snap := generateSnapshot(helper.diskdb, helper.triedb, 16, root)
@@ -534,7 +538,7 @@ func TestGenerateWithExtraAccounts(t *testing.T) {
snap.genAbort <- stop
<-stop
// If we now inspect the snap db, there should exist no extraneous storage items
- if data := rawdb.ReadStorageSnapshot(helper.triedb.DiskDB(), hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data != nil {
+ if data := rawdb.ReadStorageSnapshot(helper.diskdb, hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data != nil {
t.Fatalf("expected slot to be removed, got %v", string(data))
}
}
@@ -556,7 +560,7 @@ func TestGenerateWithManyExtraAccounts(t *testing.T) {
[]string{"val-1", "val-2", "val-3"},
true,
)
- acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}
+ acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
@@ -570,8 +574,7 @@ func TestGenerateWithManyExtraAccounts(t *testing.T) {
{
// 100 accounts exist only in snapshot
for i := 0; i < 1000; i++ {
- //acc := &Account{Balance: big.NewInt(int64(i)), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
- acc := &Account{Balance: big.NewInt(int64(i)), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
+ acc := &Account{Balance: big.NewInt(int64(i)), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
key := hashData([]byte(fmt.Sprintf("acc-%d", i)))
rawdb.WriteAccountSnapshot(helper.diskdb, key, val)
@@ -608,7 +611,7 @@ func TestGenerateWithExtraBeforeAndAfter(t *testing.T) {
}
helper := newHelper()
{
- acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
+ acc := &Account{Balance: big.NewInt(1), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.Update(common.HexToHash("0x03").Bytes(), val)
helper.accTrie.Update(common.HexToHash("0x07").Bytes(), val)
@@ -645,7 +648,7 @@ func TestGenerateWithMalformedSnapdata(t *testing.T) {
}
helper := newHelper()
{
- acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
+ acc := &Account{Balance: big.NewInt(1), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.Update(common.HexToHash("0x03").Bytes(), val)
@@ -684,7 +687,7 @@ func TestGenerateFromEmptySnap(t *testing.T) {
for i := 0; i < 400; i++ {
stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte(fmt.Sprintf("acc-%d", i))), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addTrieAccount(fmt.Sprintf("acc-%d", i),
- &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
}
root, snap := helper.CommitAndGenerate()
t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4
@@ -721,7 +724,7 @@ func TestGenerateWithIncompleteStorage(t *testing.T) {
for i := 0; i < 8; i++ {
accKey := fmt.Sprintf("acc-%d", i)
stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte(accKey)), stKeys, stVals, true)
- helper.addAccount(accKey, &Account{Balance: big.NewInt(int64(i)), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount(accKey, &Account{Balance: big.NewInt(int64(i)), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
var moddedKeys []string
var moddedVals []string
for ii := 0; ii < 8; ii++ {
@@ -813,11 +816,11 @@ func TestGenerateCompleteSnapshotWithDanglingStorage(t *testing.T) {
var helper = newHelper()
stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
- helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()})
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
@@ -848,11 +851,11 @@ func TestGenerateBrokenSnapshotWithDanglingStorage(t *testing.T) {
var helper = newHelper()
stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
- helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
+ helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: types.EmptyRootHash.Bytes(), CodeHash: types.EmptyCodeHash.Bytes()})
helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()})
+ helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
populateDangling(helper.diskdb)
diff --git a/core/state/snapshot/iterator_fast.go b/core/state/snapshot/iterator_fast.go
index 435c28e96f..1a042c7cd3 100644
--- a/core/state/snapshot/iterator_fast.go
+++ b/core/state/snapshot/iterator_fast.go
@@ -276,7 +276,7 @@ func (fi *fastIterator) next(idx int) bool {
return false
}
// The elem we're placing it next to has the same value,
- // so whichever winds up on n+1 will need further iteraton
+ // so whichever winds up on n+1 will need further iteration
clash = n + 1
return cur.priority < fi.iterators[n+1].priority
diff --git a/core/state/snapshot/iterator_test.go b/core/state/snapshot/iterator_test.go
index 2c7e876e08..54614427a5 100644
--- a/core/state/snapshot/iterator_test.go
+++ b/core/state/snapshot/iterator_test.go
@@ -18,6 +18,7 @@ package snapshot
import (
"bytes"
+ crand "crypto/rand"
"encoding/binary"
"fmt"
"math/rand"
@@ -47,7 +48,7 @@ func TestAccountIteratorBasics(t *testing.T) {
if rand.Intn(2) == 0 {
accStorage := make(map[common.Hash][]byte)
value := make([]byte, 32)
- rand.Read(value)
+ crand.Read(value)
accStorage[randomHash()] = value
storage[h] = accStorage
}
@@ -79,7 +80,7 @@ func TestStorageIteratorBasics(t *testing.T) {
var nilstorage int
for i := 0; i < 100; i++ {
- rand.Read(value)
+ crand.Read(value)
if rand.Intn(2) == 0 {
accStorage[randomHash()] = common.CopyBytes(value)
} else {
@@ -819,7 +820,7 @@ func TestStorageIteratorDeletions(t *testing.T) {
// only spit out 200 values eventually.
//
// The value-fetching benchmark is easy on the binary iterator, since it never has to reach
-// down at any depth for retrieving the values -- all are on the toppmost layer
+// down at any depth for retrieving the values -- all are on the topmost layer
//
// BenchmarkAccountIteratorTraversal/binary_iterator_keys-6 2239 483674 ns/op
// BenchmarkAccountIteratorTraversal/binary_iterator_values-6 2403 501810 ns/op
diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go
index 9a22f27373..c1a4cc3d47 100644
--- a/core/state/snapshot/journal.go
+++ b/core/state/snapshot/journal.go
@@ -120,7 +120,7 @@ func loadAndParseJournal(db ethdb.KeyValueStore, base *diskLayer) (snapshot, jou
}
// loadSnapshot loads a pre-existing state snapshot backed by a key-value store.
-func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root common.Hash, recovery bool) (snapshot, bool, error) {
+func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, root common.Hash, cache int, recovery bool, noBuild bool) (snapshot, bool, error) {
// If snapshotting is disabled (initial sync in progress), don't do anything,
// wait for the chain to permit us to do something meaningful
if rawdb.ReadSnapshotDisabled(diskdb) {
@@ -140,7 +140,7 @@ func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int,
}
snapshot, generator, err := loadAndParseJournal(diskdb, base)
if err != nil {
- log.Warn("Failed to load new-format journal", "error", err)
+ log.Warn("Failed to load journal", "error", err)
return nil, false, err
}
// Entire snapshot journal loaded, sanity check the head. If the loaded
@@ -164,13 +164,16 @@ func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int,
// disk layer.
log.Warn("Snapshot is not continuous with chain", "snaproot", head, "chainroot", root)
}
- // Everything loaded correctly, resume any suspended operations
+ // Load the disk layer status from the generator if it's not complete
if !generator.Done {
- // Whether or not wiping was in progress, load any generator progress too
base.genMarker = generator.Marker
if base.genMarker == nil {
base.genMarker = []byte{}
}
+ }
+ // Everything loaded correctly, resume any suspended operations
+ // if the background generation is allowed
+ if !generator.Done && !noBuild {
base.genPending = make(chan struct{})
base.genAbort = make(chan chan *generatorStats)
diff --git a/core/state/snapshot/metrics.go b/core/state/snapshot/metrics.go
index 43f417a0de..b2e884588b 100644
--- a/core/state/snapshot/metrics.go
+++ b/core/state/snapshot/metrics.go
@@ -36,7 +36,7 @@ var (
snapAccountProveCounter = metrics.NewRegisteredCounter("state/snapshot/generation/duration/account/prove", nil)
// snapAccountTrieReadCounter measures time spent on the account trie iteration
snapAccountTrieReadCounter = metrics.NewRegisteredCounter("state/snapshot/generation/duration/account/trieread", nil)
- // snapAccountSnapReadCounter measues time spent on the snapshot account iteration
+ // snapAccountSnapReadCounter measures time spent on the snapshot account iteration
snapAccountSnapReadCounter = metrics.NewRegisteredCounter("state/snapshot/generation/duration/account/snapread", nil)
// snapAccountWriteCounter measures time spent on writing/updating/deleting accounts
snapAccountWriteCounter = metrics.NewRegisteredCounter("state/snapshot/generation/duration/account/write", nil)
diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go
index 76200851e4..0f3fa2c7a4 100644
--- a/core/state/snapshot/snapshot.go
+++ b/core/state/snapshot/snapshot.go
@@ -148,6 +148,14 @@ type snapshot interface {
StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool)
}
+// Config includes the configurations for snapshots.
+type Config struct {
+ CacheSize int // Megabytes permitted to use for read caches
+ Recovery bool // Indicator that the snapshots is in the recovery mode
+ NoBuild bool // Indicator that the snapshots generation is disallowed
+ AsyncBuild bool // The snapshot generation is allowed to be constructed asynchronously
+}
+
// Tree is an Ethereum state snapshot tree. It consists of one persistent base
// layer backed by a key-value store, on top of which arbitrarily many in-memory
// diff layers are topped. The memory diffs can form a tree with branching, but
@@ -158,9 +166,9 @@ type snapshot interface {
// storage data to avoid expensive multi-level trie lookups; and to allow sorted,
// cheap iteration of the account/storage tries for sync aid.
type Tree struct {
+ config Config // Snapshots configurations
diskdb ethdb.KeyValueStore // Persistent database to store the snapshot
triedb *trie.Database // In-memory cache to access the trie through
- cache int // Megabytes permitted to use for read caches
layers map[common.Hash]snapshot // Collection of all known layers
lock sync.RWMutex
@@ -179,30 +187,32 @@ type Tree struct {
// If the memory layers in the journal do not match the disk layer (e.g. there is
// a gap) or the journal is missing, there are two repair cases:
//
-// - if the 'recovery' parameter is true, all memory diff-layers will be discarded.
-// This case happens when the snapshot is 'ahead' of the state trie.
-// - otherwise, the entire snapshot is considered invalid and will be recreated on
-// a background thread.
-func New(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root common.Hash, async bool, rebuild bool, recovery bool) (*Tree, error) {
+// - if the 'recovery' parameter is true, memory diff-layers and the disk-layer
+// will all be kept. This case happens when the snapshot is 'ahead' of the
+// state trie.
+// - otherwise, the entire snapshot is considered invalid and will be recreated on
+// a background thread.
+func New(config Config, diskdb ethdb.KeyValueStore, triedb *trie.Database, root common.Hash) (*Tree, error) {
// Create a new, empty snapshot tree
snap := &Tree{
+ config: config,
diskdb: diskdb,
triedb: triedb,
- cache: cache,
layers: make(map[common.Hash]snapshot),
}
- if !async {
- defer snap.waitBuild()
- }
// Attempt to load a previously persisted snapshot and rebuild one if failed
- head, disabled, err := loadSnapshot(diskdb, triedb, cache, root, recovery)
+ head, disabled, err := loadSnapshot(diskdb, triedb, root, config.CacheSize, config.Recovery, config.NoBuild)
if disabled {
log.Warn("Snapshot maintenance disabled (syncing)")
return snap, nil
}
+ // Create the building waiter iff the background generation is allowed
+ if !config.NoBuild && !config.AsyncBuild {
+ defer snap.waitBuild()
+ }
if err != nil {
- if rebuild {
- log.Warn("Failed to load snapshot, regenerating", "err", err)
+ log.Warn("Failed to load snapshot", "err", err)
+ if !config.NoBuild {
snap.Rebuild(root)
return snap, nil
}
@@ -727,7 +737,7 @@ func (t *Tree) Rebuild(root common.Hash) {
// generator will run a wiper first if there's not one running right now.
log.Info("Rebuilding state snapshot")
t.layers = map[common.Hash]snapshot{
- root: generateSnapshot(t.diskdb, t.triedb, t.cache, root),
+ root: generateSnapshot(t.diskdb, t.triedb, t.config.CacheSize, root),
}
}
@@ -766,14 +776,14 @@ func (t *Tree) Verify(root common.Hash) error {
}
defer acctIt.Release()
- got, err := generateTrieRoot(nil, acctIt, common.Hash{}, stackTrieGenerate, func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) {
+ got, err := generateTrieRoot(nil, "", acctIt, common.Hash{}, stackTrieGenerate, func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) {
storageIt, err := t.StorageIterator(root, accountHash, common.Hash{})
if err != nil {
return common.Hash{}, err
}
defer storageIt.Release()
- hash, err := generateTrieRoot(nil, storageIt, accountHash, stackTrieGenerate, nil, stat, false)
+ hash, err := generateTrieRoot(nil, "", storageIt, accountHash, stackTrieGenerate, nil, stat, false)
if err != nil {
return common.Hash{}, err
}
@@ -835,7 +845,7 @@ func (t *Tree) generating() (bool, error) {
return layer.genMarker != nil, nil
}
-// diskRoot is a external helper function to return the disk layer root.
+// DiskRoot is a external helper function to return the disk layer root.
func (t *Tree) DiskRoot() common.Hash {
t.lock.Lock()
defer t.lock.Unlock()
diff --git a/core/state/snapshot/snapshot_test.go b/core/state/snapshot/snapshot_test.go
index 7c8077b652..6893f6001e 100644
--- a/core/state/snapshot/snapshot_test.go
+++ b/core/state/snapshot/snapshot_test.go
@@ -17,6 +17,7 @@
package snapshot
import (
+ crand "crypto/rand"
"encoding/binary"
"fmt"
"math/big"
@@ -27,13 +28,14 @@ import (
"github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
// randomHash generates a random blob of data and returns it as a hash.
func randomHash() common.Hash {
var hash common.Hash
- if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil {
+ if n, err := crand.Read(hash[:]); n != common.HashLength || err != nil {
panic(err)
}
return hash
@@ -46,7 +48,7 @@ func randomAccount() []byte {
Balance: big.NewInt(rand.Int63()),
Nonce: rand.Uint64(),
Root: root[:],
- CodeHash: emptyCode[:],
+ CodeHash: types.EmptyCodeHash[:],
}
data, _ := rlp.EncodeToBytes(a)
return data
@@ -166,7 +168,7 @@ func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) {
if err := snaps.Cap(common.HexToHash("0x03"), 1); err != nil {
t.Fatalf("failed to merge accumulator onto disk: %v", err)
}
- // Since the base layer was modified, ensure that data retrievald on the external reference fail
+ // Since the base layer was modified, ensure that data retrievals on the external reference fail
if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
}
@@ -224,7 +226,7 @@ func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) {
if err := snaps.Cap(common.HexToHash("0x04"), 1); err != nil {
t.Fatalf("failed to flatten diff layer into accumulator: %v", err)
}
- // Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail
+ // Since the accumulator diff layer was modified, ensure that data retrievals on the external reference fail
if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index c92874be6e..c8236b350c 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -31,8 +31,6 @@ import (
"github.com/ethereum/go-ethereum/trie"
)
-var emptyCodeHash = crypto.Keccak256(nil)
-
type Code []byte
func (c Code) String() string {
@@ -63,7 +61,7 @@ func (s Storage) Copy() Storage {
// The usage pattern is as follows:
// First you need to obtain a state object.
// Account values can be accessed and modified through the object.
-// Finally, call CommitTrie to write the modified storage trie into a database.
+// Finally, call commitTrie to write the modified storage trie into a database.
type stateObject struct {
address common.Address
addrHash common.Hash // hash of ethereum address of the account
@@ -84,7 +82,6 @@ type stateObject struct {
originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
dirtyStorage Storage // Storage entries that have been modified in the current transaction execution
- fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.
// Cache flags.
// When an object is marked suicided it will be delete from the trie
@@ -96,7 +93,7 @@ type stateObject struct {
// empty returns whether the account is considered empty.
func (s *stateObject) empty() bool {
- return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
+ return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes())
}
// newObject creates a state object.
@@ -105,10 +102,10 @@ func newObject(db *StateDB, address common.Address, data types.StateAccount) *st
data.Balance = new(big.Int)
}
if data.CodeHash == nil {
- data.CodeHash = emptyCodeHash
+ data.CodeHash = types.EmptyCodeHash.Bytes()
}
if data.Root == (common.Hash{}) {
- data.Root = emptyRoot
+ data.Root = types.EmptyRootHash
}
return &stateObject{
db: db,
@@ -148,33 +145,31 @@ func (s *stateObject) touch() {
}
}
-func (s *stateObject) getTrie(db Database) Trie {
+// getTrie returns the associated storage trie. The trie will be opened
+// if it's not loaded previously. An error will be returned if trie can't
+// be loaded.
+func (s *stateObject) getTrie(db Database) (Trie, error) {
if s.trie == nil {
// Try fetching from prefetcher first
// We don't prefetch empty tries
- if s.data.Root != emptyRoot && s.db.prefetcher != nil {
+ if s.data.Root != types.EmptyRootHash && s.db.prefetcher != nil {
// When the miner is creating the pending state, there is no
// prefetcher
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
}
if s.trie == nil {
- var err error
- s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root)
+ tr, err := db.OpenStorageTrie(s.db.originalRoot, s.addrHash, s.data.Root)
if err != nil {
- s.trie, _ = db.OpenStorageTrie(s.addrHash, common.Hash{})
- s.setError(fmt.Errorf("can't create storage trie: %v", err))
+ return nil, err
}
+ s.trie = tr
}
}
- return s.trie
+ return s.trie, nil
}
// GetState retrieves a value from the account storage trie.
func (s *stateObject) GetState(db Database, key common.Hash) common.Hash {
- // If the fake storage is set, only lookup the state here(in the debugging mode)
- if s.fakeStorage != nil {
- return s.fakeStorage[key]
- }
// If we have a dirty value for this state entry, return it
value, dirty := s.dirtyStorage[key]
if dirty {
@@ -186,10 +181,6 @@ func (s *stateObject) GetState(db Database, key common.Hash) common.Hash {
// GetCommittedState retrieves a value from the committed account storage trie.
func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
- // If the fake storage is set, only lookup the state here(in the debugging mode)
- if s.fakeStorage != nil {
- return s.fakeStorage[key]
- }
// If we have a pending write or clean cached, return that
if value, pending := s.pendingStorage[key]; pending {
return value
@@ -197,21 +188,21 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
if value, cached := s.originStorage[key]; cached {
return value
}
+ // If the object was destructed in *this* block (and potentially resurrected),
+ // the storage has been cleared out, and we should *not* consult the previous
+ // database about any storage values. The only possible alternatives are:
+ // 1) resurrect happened, and new slot values were set -- those should
+ // have been handles via pendingStorage above.
+ // 2) we don't have new values, and can deliver empty response back
+ if _, destructed := s.db.stateObjectsDestruct[s.address]; destructed {
+ return common.Hash{}
+ }
// If no live objects are available, attempt to use snapshots
var (
enc []byte
err error
)
if s.db.snap != nil {
- // If the object was destructed in *this* block (and potentially resurrected),
- // the storage has been cleared out, and we should *not* consult the previous
- // snapshot about any storage values. The only possible alternatives are:
- // 1) resurrect happened, and new slot values were set -- those should
- // have been handles via pendingStorage above.
- // 2) we don't have new values, and can deliver empty response back
- if _, destructed := s.db.snapDestructs[s.addrHash]; destructed {
- return common.Hash{}
- }
start := time.Now()
enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key.Bytes()))
if metrics.EnabledExpensive {
@@ -221,7 +212,12 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
// If the snapshot is unavailable or reading from it fails, load from the database.
if s.db.snap == nil || err != nil {
start := time.Now()
- enc, err = s.getTrie(db).TryGet(key.Bytes())
+ tr, err := s.getTrie(db)
+ if err != nil {
+ s.setError(err)
+ return common.Hash{}
+ }
+ enc, err = tr.TryGet(key.Bytes())
if metrics.EnabledExpensive {
s.db.StorageReads += time.Since(start)
}
@@ -249,11 +245,6 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
// SetState updates a value in account storage.
func (s *stateObject) SetState(db Database, key, value common.Hash) {
- // If the fake storage is set, put the temporary state update here.
- if s.fakeStorage != nil {
- s.fakeStorage[key] = value
- return
- }
// If the new value is the same as old, don't set
prev := s.GetState(db, key)
if prev == value {
@@ -268,24 +259,6 @@ func (s *stateObject) SetState(db Database, key, value common.Hash) {
s.setState(key, value)
}
-// SetStorage replaces the entire state storage with the given one.
-//
-// After this function is called, all original state will be ignored and state
-// lookup only happens in the fake state storage.
-//
-// Note this function should only be used for debugging purpose.
-func (s *stateObject) SetStorage(storage map[common.Hash]common.Hash) {
- // Allocate fake storage if it's nil.
- if s.fakeStorage == nil {
- s.fakeStorage = make(Storage)
- }
- for key, value := range storage {
- s.fakeStorage[key] = value
- }
- // Don't bother journal since this function should only be used for
- // debugging and the `fake` storage won't be committed to database.
-}
-
func (s *stateObject) setState(key, value common.Hash) {
s.dirtyStorage[key] = value
}
@@ -300,7 +273,7 @@ func (s *stateObject) finalise(prefetch bool) {
slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure
}
}
- if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != emptyRoot {
+ if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
s.db.prefetcher.prefetch(s.addrHash, s.data.Root, slotsToPrefetch)
}
if len(s.dirtyStorage) > 0 {
@@ -309,23 +282,29 @@ func (s *stateObject) finalise(prefetch bool) {
}
// updateTrie writes cached storage modifications into the object's storage trie.
-// It will return nil if the trie has not been loaded and no changes have been made
-func (s *stateObject) updateTrie(db Database) Trie {
+// It will return nil if the trie has not been loaded and no changes have been
+// made. An error will be returned if the trie can't be loaded/updated correctly.
+func (s *stateObject) updateTrie(db Database) (Trie, error) {
// Make sure all dirty slots are finalized into the pending storage area
s.finalise(false) // Don't prefetch anymore, pull directly if need be
if len(s.pendingStorage) == 0 {
- return s.trie
+ return s.trie, nil
}
// Track the amount of time wasted on updating the storage trie
if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now())
}
// The snapshot storage map for the object
- var storage map[common.Hash][]byte
+ var (
+ storage map[common.Hash][]byte
+ hasher = s.db.hasher
+ )
+ tr, err := s.getTrie(db)
+ if err != nil {
+ s.setError(err)
+ return nil, err
+ }
// Insert all the pending updates into the trie
- tr := s.getTrie(db)
- hasher := s.db.hasher
-
usedStorage := make([][]byte, 0, len(s.pendingStorage))
for key, value := range s.pendingStorage {
// Skip noop changes, persist actual changes
@@ -336,12 +315,18 @@ func (s *stateObject) updateTrie(db Database) Trie {
var v []byte
if (value == common.Hash{}) {
- s.setError(tr.TryDelete(key[:]))
+ if err := tr.TryDelete(key[:]); err != nil {
+ s.setError(err)
+ return nil, err
+ }
s.db.StorageDeleted += 1
} else {
// Encoding []byte cannot fail, ok to ignore the error.
v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:]))
- s.setError(tr.TryUpdate(key[:], v))
+ if err := tr.TryUpdate(key[:], v); err != nil {
+ s.setError(err)
+ return nil, err
+ }
s.db.StorageUpdated += 1
}
// If state snapshotting is active, cache the data til commit
@@ -363,40 +348,48 @@ func (s *stateObject) updateTrie(db Database) Trie {
if len(s.pendingStorage) > 0 {
s.pendingStorage = make(Storage)
}
- return tr
+ return tr, nil
}
-// UpdateRoot sets the trie root to the current root hash of
+// UpdateRoot sets the trie root to the current root hash of. An error
+// will be returned if trie root hash is not computed correctly.
func (s *stateObject) updateRoot(db Database) {
+ tr, err := s.updateTrie(db)
+ if err != nil {
+ s.setError(fmt.Errorf("updateRoot (%x) error: %w", s.address, err))
+ return
+ }
// If nothing changed, don't bother with hashing anything
- if s.updateTrie(db) == nil {
+ if tr == nil {
return
}
// Track the amount of time wasted on hashing the storage trie
if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now())
}
- s.data.Root = s.trie.Hash()
+ s.data.Root = tr.Hash()
}
-// CommitTrie the storage trie of the object to db.
-// This updates the trie root.
-func (s *stateObject) CommitTrie(db Database) (*trie.NodeSet, error) {
- // If nothing changed, don't bother with hashing anything
- if s.updateTrie(db) == nil {
- return nil, nil
+// commitTrie submits the storage changes into the storage trie and re-computes
+// the root. Besides, all trie changes will be collected in a nodeset and returned.
+func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) {
+ tr, err := s.updateTrie(db)
+ if err != nil {
+ return nil, err
}
if s.dbErr != nil {
return nil, s.dbErr
}
+ // If nothing changed, don't bother with committing anything
+ if tr == nil {
+ return nil, nil
+ }
// Track the amount of time wasted on committing the storage trie
if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now())
}
- root, nodes, err := s.trie.Commit(false)
- if err == nil {
- s.data.Root = root
- }
+ root, nodes := tr.Commit(false)
+ s.data.Root = root
return nodes, err
}
@@ -454,7 +447,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
// Attribute accessors
//
-// Returns the address of the contract/account
+// Address returns the address of the contract/account
func (s *stateObject) Address() common.Address {
return s.address
}
@@ -464,7 +457,7 @@ func (s *stateObject) Code(db Database) []byte {
if s.code != nil {
return s.code
}
- if bytes.Equal(s.CodeHash(), emptyCodeHash) {
+ if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
return nil
}
code, err := db.ContractCode(s.addrHash, common.BytesToHash(s.CodeHash()))
@@ -487,7 +480,7 @@ func (s *stateObject) CodeSize(db Database) int {
if s.code != nil {
return len(s.code)
}
- if bytes.Equal(s.CodeHash(), emptyCodeHash) {
+ if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
return 0
}
size, err := db.ContractCodeSize(s.addrHash, common.BytesToHash(s.CodeHash()))
@@ -537,7 +530,7 @@ func (s *stateObject) Nonce() uint64 {
return s.data.Nonce
}
-// Never called, but must be present to allow stateObject to be used
+// Value is never called, but must be present to allow stateObject to be used
// as a vm.Account interface that also satisfies the vm.ContractRef
// interface. Interfaces are awesome.
func (s *stateObject) Value() *big.Int {
diff --git a/core/state/statedb.go b/core/state/statedb.go
index f0720a1c22..312c0638b9 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
@@ -40,11 +41,6 @@ type revision struct {
journalIndex int
}
-var (
- // emptyRoot is the known root hash of an empty trie.
- emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
-)
-
type proofList [][]byte
func (n *proofList) Put(key []byte, value []byte) error {
@@ -71,16 +67,16 @@ type StateDB struct {
// It will be updated when the Commit is called.
originalRoot common.Hash
- snaps *snapshot.Tree
- snap snapshot.Snapshot
- snapDestructs map[common.Hash]struct{}
- snapAccounts map[common.Hash][]byte
- snapStorage map[common.Hash]map[common.Hash][]byte
+ snaps *snapshot.Tree
+ snap snapshot.Snapshot
+ snapAccounts map[common.Hash][]byte
+ snapStorage map[common.Hash]map[common.Hash][]byte
// This map holds 'live' objects, which will get modified while processing a state transition.
- stateObjects map[common.Address]*stateObject
- stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie
- stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
+ stateObjects map[common.Address]*stateObject
+ stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie
+ stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
+ stateObjectsDestruct map[common.Address]struct{} // State objects destructed in the block
// DB error.
// State objects are used by the consensus core and VM which are
@@ -102,6 +98,9 @@ type StateDB struct {
// Per-transaction access list
accessList *accessList
+ // Transient storage
+ transientStorage transientStorage
+
// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal *journal
@@ -120,6 +119,7 @@ type StateDB struct {
SnapshotAccountReads time.Duration
SnapshotStorageReads time.Duration
SnapshotCommits time.Duration
+ TrieDBCommits time.Duration
AccountUpdated int
StorageUpdated int
@@ -137,22 +137,23 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
return nil, err
}
sdb := &StateDB{
- db: db,
- trie: tr,
- originalRoot: root,
- snaps: snaps,
- stateObjects: make(map[common.Address]*stateObject),
- stateObjectsPending: make(map[common.Address]struct{}),
- stateObjectsDirty: make(map[common.Address]struct{}),
- logs: make(map[common.Hash][]*types.Log),
- preimages: make(map[common.Hash][]byte),
- journal: newJournal(),
- accessList: newAccessList(),
- hasher: crypto.NewKeccakState(),
+ db: db,
+ trie: tr,
+ originalRoot: root,
+ snaps: snaps,
+ stateObjects: make(map[common.Address]*stateObject),
+ stateObjectsPending: make(map[common.Address]struct{}),
+ stateObjectsDirty: make(map[common.Address]struct{}),
+ stateObjectsDestruct: make(map[common.Address]struct{}),
+ logs: make(map[common.Hash][]*types.Log),
+ preimages: make(map[common.Hash][]byte),
+ journal: newJournal(),
+ accessList: newAccessList(),
+ transientStorage: newTransientStorage(),
+ hasher: crypto.NewKeccakState(),
}
if sdb.snaps != nil {
if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil {
- sdb.snapDestructs = make(map[common.Hash]struct{})
sdb.snapAccounts = make(map[common.Hash][]byte)
sdb.snapStorage = make(map[common.Hash]map[common.Hash][]byte)
}
@@ -208,9 +209,12 @@ func (s *StateDB) AddLog(log *types.Log) {
s.logSize++
}
-func (s *StateDB) GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log {
+// GetLogs returns the logs matching the specified transaction hash, and annotates
+// them with the given blockNumber and blockHash.
+func (s *StateDB) GetLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash) []*types.Log {
logs := s.logs[hash]
for _, l := range logs {
+ l.BlockNumber = blockNumber
l.BlockHash = blockHash
}
return logs
@@ -338,13 +342,19 @@ func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) {
// GetStorageProof returns the Merkle proof for given storage slot.
func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
- var proof proofList
- trie := s.StorageTrie(a)
+ trie, err := s.StorageTrie(a)
+ if err != nil {
+ return nil, err
+ }
if trie == nil {
- return proof, errors.New("storage trie for requested address does not exist")
+ return nil, errors.New("storage trie for requested address does not exist")
}
- err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
- return proof, err
+ var proof proofList
+ err = trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
+ if err != nil {
+ return nil, err
+ }
+ return proof, nil
}
// GetCommittedState retrieves a value from the given account's committed storage trie.
@@ -361,15 +371,18 @@ func (s *StateDB) Database() Database {
return s.db
}
-// StorageTrie returns the storage trie of an account.
-// The return value is a copy and is nil for non-existent accounts.
-func (s *StateDB) StorageTrie(addr common.Address) Trie {
+// StorageTrie returns the storage trie of an account. The return value is a copy
+// and is nil for non-existent accounts. An error will be returned if storage trie
+// is existent but can't be loaded correctly.
+func (s *StateDB) StorageTrie(addr common.Address) (Trie, error) {
stateObject := s.getStateObject(addr)
if stateObject == nil {
- return nil
+ return nil, nil
}
cpy := stateObject.deepCopy(s)
- cpy.updateTrie(s.db)
+ if _, err := cpy.updateTrie(s.db); err != nil {
+ return nil, err
+ }
return cpy.getTrie(s.db)
}
@@ -432,9 +445,15 @@ func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {
// SetStorage replaces the entire storage for the specified account with given
// storage. This function should only be used for debugging.
func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) {
+ // SetStorage needs to wipe existing storage. We achieve this by pretending
+ // that the account self-destructed earlier in this block, by flagging
+ // it in stateObjectsDestruct. The effect of doing so is that storage lookups
+ // will not hit disk, since it is assumed that the disk-data is belonging
+ // to a previous incarnation of the object.
+ s.stateObjectsDestruct[addr] = struct{}{}
stateObject := s.GetOrNewStateObject(addr)
- if stateObject != nil {
- stateObject.SetStorage(storage)
+ for k, v := range storage {
+ stateObject.SetState(s.db, k, v)
}
}
@@ -459,6 +478,35 @@ func (s *StateDB) Suicide(addr common.Address) bool {
return true
}
+// SetTransientState sets transient storage for a given account. It
+// adds the change to the journal so that it can be rolled back
+// to its previous value if there is a revert.
+func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash) {
+ prev := s.GetTransientState(addr, key)
+ if prev == value {
+ return
+ }
+
+ s.journal.append(transientStorageChange{
+ account: &addr,
+ key: key,
+ prevalue: prev,
+ })
+
+ s.setTransientState(addr, key, value)
+}
+
+// setTransientState is a lower level setter for transient storage. It
+// is called during a revert to prevent modifications to the journal.
+func (s *StateDB) setTransientState(addr common.Address, key, value common.Hash) {
+ s.transientStorage.Set(addr, key, value)
+}
+
+// GetTransientState gets transient storage for a given account.
+func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash {
+ return s.transientStorage.Get(addr, key)
+}
+
//
// Setting, updating & deleting state object methods.
//
@@ -471,7 +519,7 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
}
// Encode the account and update the account trie
addr := obj.Address()
- if err := s.trie.TryUpdateAccount(addr[:], &obj.data); err != nil {
+ if err := s.trie.TryUpdateAccount(addr, &obj.data); err != nil {
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err))
}
@@ -492,7 +540,7 @@ func (s *StateDB) deleteStateObject(obj *stateObject) {
}
// Delete the account from the trie
addr := obj.Address()
- if err := s.trie.TryDeleteAccount(addr[:]); err != nil {
+ if err := s.trie.TryDeleteAccount(addr); err != nil {
s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err))
}
}
@@ -535,10 +583,10 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
Root: common.BytesToHash(acc.Root),
}
if len(data.CodeHash) == 0 {
- data.CodeHash = emptyCodeHash
+ data.CodeHash = types.EmptyCodeHash.Bytes()
}
if data.Root == (common.Hash{}) {
- data.Root = emptyRoot
+ data.Root = types.EmptyRootHash
}
}
}
@@ -546,7 +594,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if data == nil {
start := time.Now()
var err error
- data, err = s.trie.TryGetAccount(addr.Bytes())
+ data, err = s.trie.TryGetAccount(addr)
if metrics.EnabledExpensive {
s.AccountReads += time.Since(start)
}
@@ -587,10 +635,10 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that!
var prevdestruct bool
- if s.snap != nil && prev != nil {
- _, prevdestruct = s.snapDestructs[prev.addrHash]
+ if prev != nil {
+ _, prevdestruct = s.stateObjectsDestruct[prev.address]
if !prevdestruct {
- s.snapDestructs[prev.addrHash] = struct{}{}
+ s.stateObjectsDestruct[prev.address] = struct{}{}
}
}
newobj = newObject(s, addr, types.StateAccount{})
@@ -612,8 +660,8 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
// a contract does the following:
//
-// 1. sends funds to sha(account ++ (nonce + 1))
-// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
+// 1. sends funds to sha(account ++ (nonce + 1))
+// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
//
// Carrying over the balance ensures that Ether doesn't disappear.
func (s *StateDB) CreateAccount(addr common.Address) {
@@ -628,7 +676,11 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
if so == nil {
return nil
}
- it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
+ tr, err := so.getTrie(db.db)
+ if err != nil {
+ return err
+ }
+ it := trie.NewIterator(tr.NodeIterator(nil))
for it.Next() {
key := common.BytesToHash(db.trie.GetKey(it.Key))
@@ -657,18 +709,19 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
func (s *StateDB) Copy() *StateDB {
// Copy all the basic fields, initialize the memory ones
state := &StateDB{
- db: s.db,
- trie: s.db.CopyTrie(s.trie),
- originalRoot: s.originalRoot,
- stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)),
- stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
- stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
- refund: s.refund,
- logs: make(map[common.Hash][]*types.Log, len(s.logs)),
- logSize: s.logSize,
- preimages: make(map[common.Hash][]byte, len(s.preimages)),
- journal: newJournal(),
- hasher: crypto.NewKeccakState(),
+ db: s.db,
+ trie: s.db.CopyTrie(s.trie),
+ originalRoot: s.originalRoot,
+ stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)),
+ stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
+ stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
+ stateObjectsDestruct: make(map[common.Address]struct{}, len(s.stateObjectsDestruct)),
+ refund: s.refund,
+ logs: make(map[common.Hash][]*types.Log, len(s.logs)),
+ logSize: s.logSize,
+ preimages: make(map[common.Hash][]byte, len(s.preimages)),
+ journal: newJournal(),
+ hasher: crypto.NewKeccakState(),
}
// Copy the dirty states, logs, and preimages
for addr := range s.journal.dirties {
@@ -678,7 +731,7 @@ func (s *StateDB) Copy() *StateDB {
// nil
if object, exist := s.stateObjects[addr]; exist {
// Even though the original object is dirty, we are not copying the journal,
- // so we need to make sure that anyside effect the journal would have caused
+ // so we need to make sure that any side-effect the journal would have caused
// during a commit (or similar op) is already applied to the copy.
state.stateObjects[addr] = object.deepCopy(state)
@@ -686,9 +739,10 @@ func (s *StateDB) Copy() *StateDB {
state.stateObjectsPending[addr] = struct{}{} // Mark the copy pending to force external (account) commits
}
}
- // Above, we don't copy the actual journal. This means that if the copy is copied, the
- // loop above will be a no-op, since the copy's journal is empty.
- // Thus, here we iterate over stateObjects, to enable copies of copies
+ // Above, we don't copy the actual journal. This means that if the copy
+ // is copied, the loop above will be a no-op, since the copy's journal
+ // is empty. Thus, here we iterate over stateObjects, to enable copies
+ // of copies.
for addr := range s.stateObjectsPending {
if _, exist := state.stateObjects[addr]; !exist {
state.stateObjects[addr] = s.stateObjects[addr].deepCopy(state)
@@ -701,6 +755,10 @@ func (s *StateDB) Copy() *StateDB {
}
state.stateObjectsDirty[addr] = struct{}{}
}
+ // Deep copy the destruction flag.
+ for addr := range s.stateObjectsDestruct {
+ state.stateObjectsDestruct[addr] = struct{}{}
+ }
for hash, logs := range s.logs {
cpy := make([]*types.Log, len(logs))
for i, l := range logs {
@@ -712,12 +770,14 @@ func (s *StateDB) Copy() *StateDB {
for hash, preimage := range s.preimages {
state.preimages[hash] = preimage
}
- // Do we need to copy the access list? In practice: No. At the start of a
- // transaction, the access list is empty. In practice, we only ever copy state
- // _between_ transactions/blocks, never in the middle of a transaction.
- // However, it doesn't cost us much to copy an empty list, so we do it anyway
- // to not blow up if we ever decide copy it in the middle of a transaction
+ // Do we need to copy the access list and transient storage?
+ // In practice: No. At the start of a transaction, these two lists are empty.
+ // In practice, we only ever copy state _between_ transactions/blocks, never
+ // in the middle of a transaction. However, it doesn't cost us much to copy
+ // empty lists, so we do it anyway to not blow up if we ever decide copy them
+ // in the middle of a transaction.
state.accessList = s.accessList.Copy()
+ state.transientStorage = s.transientStorage.Copy()
// If there's a prefetcher running, make an inactive copy of it that can
// only access data but does not actively preload (since the user will not
@@ -727,16 +787,13 @@ func (s *StateDB) Copy() *StateDB {
}
if s.snaps != nil {
// In order for the miner to be able to use and make additions
- // to the snapshot tree, we need to copy that aswell.
+ // to the snapshot tree, we need to copy that as well.
// Otherwise, any block mined by ourselves will cause gaps in the tree,
// and force the miner to operate trie-backed only
state.snaps = s.snaps
state.snap = s.snap
+
// deep copy needed
- state.snapDestructs = make(map[common.Hash]struct{})
- for k, v := range s.snapDestructs {
- state.snapDestructs[k] = v
- }
state.snapAccounts = make(map[common.Hash][]byte)
for k, v := range s.snapAccounts {
state.snapAccounts[k] = v
@@ -804,14 +861,17 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
if obj.suicided || (deleteEmptyObjects && obj.empty()) {
obj.deleted = true
+ // We need to maintain account deletions explicitly (will remain
+ // set indefinitely).
+ s.stateObjectsDestruct[obj.address] = struct{}{}
+
// If state snapshotting is active, also mark the destruction there.
// Note, we can't do this only at the end of a block because multiple
// transactions within the same block might self destruct and then
// resurrect an account; but the snapshotter needs both events.
if s.snap != nil {
- s.snapDestructs[obj.addrHash] = struct{}{} // We need to maintain account deletions explicitly (will remain set indefinitely)
- delete(s.snapAccounts, obj.addrHash) // Clear out any previously updated account data (may be recreated via a ressurrect)
- delete(s.snapStorage, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a ressurrect)
+ delete(s.snapAccounts, obj.addrHash) // Clear out any previously updated account data (may be recreated via a resurrect)
+ delete(s.snapStorage, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a resurrect)
}
} else {
obj.finalise(true) // Prefetch slots in the background
@@ -894,9 +954,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
return s.trie.Hash()
}
-// Prepare sets the current transaction hash and index which are
-// used when the EVM emits new state logs.
-func (s *StateDB) Prepare(thash common.Hash, ti int) {
+// SetTxContext sets the current transaction hash and index which are
+// used when the EVM emits new state logs. It should be invoked before
+// transaction execution.
+func (s *StateDB) SetTxContext(thash common.Hash, ti int) {
s.thash = thash
s.txIndex = ti
}
@@ -919,11 +980,13 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
// Commit objects to the trie, measuring the elapsed time
var (
- accountTrieNodes int
- storageTrieNodes int
- nodes = trie.NewMergedNodeSet()
+ accountTrieNodesUpdated int
+ accountTrieNodesDeleted int
+ storageTrieNodesUpdated int
+ storageTrieNodesDeleted int
+ nodes = trie.NewMergedNodeSet()
)
- codeWriter := s.db.TrieDB().DiskDB().NewBatch()
+ codeWriter := s.db.DiskDB().NewBatch()
for addr := range s.stateObjectsDirty {
if obj := s.stateObjects[addr]; !obj.deleted {
// Write any contract code associated with the state object
@@ -932,7 +995,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
obj.dirtyCode = false
}
// Write any storage changes in the state object to its storage trie
- set, err := obj.CommitTrie(s.db)
+ set, err := obj.commitTrie(s.db)
if err != nil {
return common.Hash{}, err
}
@@ -941,9 +1004,17 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
if err := nodes.Merge(set); err != nil {
return common.Hash{}, err
}
- storageTrieNodes += set.Len()
+ updates, deleted := set.Size()
+ storageTrieNodesUpdated += updates
+ storageTrieNodesDeleted += deleted
}
}
+ // If the contract is destructed, the storage is still left in the
+ // database as dangling data. Theoretically it's should be wiped from
+ // database as well, but in hash-based-scheme it's extremely hard to
+ // determine that if the trie nodes are also referenced by other storage,
+ // and in path-based-scheme some technical challenges are still unsolved.
+ // Although it won't affect the correctness but please fix it TODO(rjl493456442).
}
if len(s.stateObjectsDirty) > 0 {
s.stateObjectsDirty = make(map[common.Address]struct{})
@@ -958,16 +1029,13 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
if metrics.EnabledExpensive {
start = time.Now()
}
- root, set, err := s.trie.Commit(true)
- if err != nil {
- return common.Hash{}, err
- }
+ root, set := s.trie.Commit(true)
// Merge the dirty nodes of account trie into global set
if set != nil {
if err := nodes.Merge(set); err != nil {
return common.Hash{}, err
}
- accountTrieNodes = set.Len()
+ accountTrieNodesUpdated, accountTrieNodesDeleted = set.Size()
}
if metrics.EnabledExpensive {
s.AccountCommits += time.Since(start)
@@ -976,19 +1044,19 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
storageUpdatedMeter.Mark(int64(s.StorageUpdated))
accountDeletedMeter.Mark(int64(s.AccountDeleted))
storageDeletedMeter.Mark(int64(s.StorageDeleted))
- accountTrieCommittedMeter.Mark(int64(accountTrieNodes))
- storageTriesCommittedMeter.Mark(int64(storageTrieNodes))
+ accountTrieUpdatedMeter.Mark(int64(accountTrieNodesUpdated))
+ accountTrieDeletedMeter.Mark(int64(accountTrieNodesDeleted))
+ storageTriesUpdatedMeter.Mark(int64(storageTrieNodesUpdated))
+ storageTriesDeletedMeter.Mark(int64(storageTrieNodesDeleted))
s.AccountUpdated, s.AccountDeleted = 0, 0
s.StorageUpdated, s.StorageDeleted = 0, 0
}
// If snapshotting is enabled, update the snapshot tree with this new version
if s.snap != nil {
- if metrics.EnabledExpensive {
- defer func(start time.Time) { s.SnapshotCommits += time.Since(start) }(time.Now())
- }
+ start := time.Now()
// Only update if there's a state transition (skip empty Clique blocks)
if parent := s.snap.Root(); parent != root {
- if err := s.snaps.Update(root, parent, s.snapDestructs, s.snapAccounts, s.snapStorage); err != nil {
+ if err := s.snaps.Update(root, parent, s.convertAccountSet(s.stateObjectsDestruct), s.snapAccounts, s.snapStorage); err != nil {
log.Warn("Failed to update snapshot tree", "from", parent, "to", root, "err", err)
}
// Keep 128 diff layers in the memory, persistent layer is 129th.
@@ -999,42 +1067,73 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
log.Warn("Failed to cap snapshot tree", "root", root, "layers", 128, "err", err)
}
}
- s.snap, s.snapDestructs, s.snapAccounts, s.snapStorage = nil, nil, nil, nil
+ if metrics.EnabledExpensive {
+ s.SnapshotCommits += time.Since(start)
+ }
+ s.snap, s.snapAccounts, s.snapStorage = nil, nil, nil
+ }
+ if len(s.stateObjectsDestruct) > 0 {
+ s.stateObjectsDestruct = make(map[common.Address]struct{})
+ }
+ if root == (common.Hash{}) {
+ root = types.EmptyRootHash
}
- if err := s.db.TrieDB().Update(nodes); err != nil {
- return common.Hash{}, err
+ origin := s.originalRoot
+ if origin == (common.Hash{}) {
+ origin = types.EmptyRootHash
}
- s.originalRoot = root
- return root, err
+ if root != origin {
+ start := time.Now()
+ if err := s.db.TrieDB().Update(nodes); err != nil {
+ return common.Hash{}, err
+ }
+ s.originalRoot = root
+ if metrics.EnabledExpensive {
+ s.TrieDBCommits += time.Since(start)
+ }
+ }
+ return root, nil
}
-// PrepareAccessList handles the preparatory steps for executing a state transition with
-// regards to both EIP-2929 and EIP-2930:
+// Prepare handles the preparatory steps for executing a state transition with.
+// This method must be invoked before state transition.
//
+// Berlin fork:
// - Add sender to access list (2929)
// - Add destination to access list (2929)
// - Add precompiles to access list (2929)
// - Add the contents of the optional tx access list (2930)
//
-// This method should only be called if Berlin/2929+2930 is applicable at the current number.
-func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
- // Clear out any leftover from previous executions
- s.accessList = newAccessList()
-
- s.AddAddressToAccessList(sender)
- if dst != nil {
- s.AddAddressToAccessList(*dst)
- // If it's a create-tx, the destination will be added inside evm.create
- }
- for _, addr := range precompiles {
- s.AddAddressToAccessList(addr)
- }
- for _, el := range list {
- s.AddAddressToAccessList(el.Address)
- for _, key := range el.StorageKeys {
- s.AddSlotToAccessList(el.Address, key)
+// Potential EIPs:
+// - Reset access list (Berlin)
+// - Add coinbase to access list (EIP-3651)
+// - Reset transient storage (EIP-1153)
+func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
+ if rules.IsBerlin {
+ // Clear out any leftover from previous executions
+ al := newAccessList()
+ s.accessList = al
+
+ al.AddAddress(sender)
+ if dst != nil {
+ al.AddAddress(*dst)
+ // If it's a create-tx, the destination will be added inside evm.create
+ }
+ for _, addr := range precompiles {
+ al.AddAddress(addr)
+ }
+ for _, el := range list {
+ al.AddAddress(el.Address)
+ for _, key := range el.StorageKeys {
+ al.AddSlot(el.Address, key)
+ }
+ }
+ if rules.IsShanghai { // EIP-3651: warm coinbase
+ al.AddAddress(coinbase)
}
}
+ // Reset transient storage at the beginning of transaction execution
+ s.transientStorage = newTransientStorage()
}
// AddAddressToAccessList adds the given address to the access list
@@ -1086,3 +1185,17 @@ func (s *StateDB) TakeStateSpecimen() *types.StateSpecimen {
func (s *StateDB) GetStateSpecimen() *types.StateSpecimen {
return s.stateSpecimen
}
+
+// convertAccountSet converts a provided account set from address keyed to hash keyed.
+func (s *StateDB) convertAccountSet(set map[common.Address]struct{}) map[common.Hash]struct{} {
+ ret := make(map[common.Hash]struct{})
+ for addr := range set {
+ obj, exist := s.stateObjects[addr]
+ if !exist {
+ ret[crypto.Keccak256Hash(addr[:])] = struct{}{}
+ } else {
+ ret[obj.addrHash] = struct{}{}
+ }
+ }
+ return ret
+}
diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go
index 6fe36a7ecf..8aa59e3ee5 100644
--- a/core/state/statedb_test.go
+++ b/core/state/statedb_test.go
@@ -55,7 +55,7 @@ func TestUpdateLeaks(t *testing.T) {
}
root := state.IntermediateRoot(false)
- if err := state.Database().TrieDB().Commit(root, false, nil); err != nil {
+ if err := state.Database().TrieDB().Commit(root, false); err != nil {
t.Errorf("can not commit trie %v to persistent database", root.Hex())
}
@@ -106,7 +106,7 @@ func TestIntermediateLeaks(t *testing.T) {
if err != nil {
t.Fatalf("failed to commit transition state: %v", err)
}
- if err = transState.Database().TrieDB().Commit(transRoot, false, nil); err != nil {
+ if err = transState.Database().TrieDB().Commit(transRoot, false); err != nil {
t.Errorf("can not commit trie %v to persistent database", transRoot.Hex())
}
@@ -114,7 +114,7 @@ func TestIntermediateLeaks(t *testing.T) {
if err != nil {
t.Fatalf("failed to commit final state: %v", err)
}
- if err = finalState.Database().TrieDB().Commit(finalRoot, false, nil); err != nil {
+ if err = finalState.Database().TrieDB().Commit(finalRoot, false); err != nil {
t.Errorf("can not commit trie %v to persistent database", finalRoot.Hex())
}
@@ -342,6 +342,16 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
},
args: make([]int64, 1),
},
+ {
+ name: "SetTransientState",
+ fn: func(a testAction, s *StateDB) {
+ var key, val common.Hash
+ binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
+ binary.BigEndian.PutUint16(val[:], uint16(a.args[1]))
+ s.SetTransientState(addr, key, val)
+ },
+ args: make([]int64, 2),
+ },
}
action := actions[r.Intn(len(actions))]
var nameargs []string
@@ -463,9 +473,9 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
state.GetRefund(), checkstate.GetRefund())
}
- if !reflect.DeepEqual(state.GetLogs(common.Hash{}, common.Hash{}), checkstate.GetLogs(common.Hash{}, common.Hash{})) {
+ if !reflect.DeepEqual(state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{})) {
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
- state.GetLogs(common.Hash{}, common.Hash{}), checkstate.GetLogs(common.Hash{}, common.Hash{}))
+ state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{}))
}
return nil
}
@@ -938,7 +948,7 @@ func TestFlushOrderDataLoss(t *testing.T) {
if err := statedb.TrieDB().Cap(1024); err != nil {
t.Fatalf("failed to cap trie dirty cache: %v", err)
}
- if err := statedb.TrieDB().Commit(root, false, nil); err != nil {
+ if err := statedb.TrieDB().Commit(root, false); err != nil {
t.Fatalf("failed to commit state trie: %v", err)
}
// Reopen the state trie from flushed disk and verify it
@@ -954,3 +964,37 @@ func TestFlushOrderDataLoss(t *testing.T) {
}
}
}
+
+func TestStateDBTransientStorage(t *testing.T) {
+ memDb := rawdb.NewMemoryDatabase()
+ db := NewDatabase(memDb)
+ state, _ := New(common.Hash{}, db, nil)
+
+ key := common.Hash{0x01}
+ value := common.Hash{0x02}
+ addr := common.Address{}
+
+ state.SetTransientState(addr, key, value)
+ if exp, got := 1, state.journal.length(); exp != got {
+ t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
+ }
+ // the retrieved value should equal what was set
+ if got := state.GetTransientState(addr, key); got != value {
+ t.Fatalf("transient storage mismatch: have %x, want %x", got, value)
+ }
+
+ // revert the transient state being set and then check that the
+ // value is now the empty hash
+ state.journal.revert(state, 0)
+ if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got {
+ t.Fatalf("transient storage mismatch: have %x, want %x", got, exp)
+ }
+
+ // set transient state and then copy the statedb and ensure that
+ // the transient state is copied
+ state.SetTransientState(addr, key, value)
+ cpy := state.Copy()
+ if got := cpy.GetTransientState(addr, key); got != value {
+ t.Fatalf("transient storage mismatch: have %x, want %x", got, value)
+ }
+}
diff --git a/core/state/sync.go b/core/state/sync.go
index 00a4c67aa3..61097c6462 100644
--- a/core/state/sync.go
+++ b/core/state/sync.go
@@ -27,7 +27,7 @@ import (
)
// NewStateSync create a new state trie download scheduler.
-func NewStateSync(root common.Hash, database ethdb.KeyValueReader, onLeaf func(keys [][]byte, leaf []byte) error) *trie.Sync {
+func NewStateSync(root common.Hash, database ethdb.KeyValueReader, onLeaf func(keys [][]byte, leaf []byte) error, scheme string) *trie.Sync {
// Register the storage slot callback if the external callback is specified.
var onSlot func(keys [][]byte, path []byte, leaf []byte, parent common.Hash, parentPath []byte) error
if onLeaf != nil {
@@ -52,6 +52,6 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, onLeaf func(k
syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), path, parent, parentPath)
return nil
}
- syncer = trie.NewSync(root, database, onAccount)
+ syncer = trie.NewSync(root, database, onAccount, scheme)
return syncer
}
diff --git a/core/state/sync_test.go b/core/state/sync_test.go
index 3d9fe556d2..aff91268aa 100644
--- a/core/state/sync_test.go
+++ b/core/state/sync_test.go
@@ -39,10 +39,11 @@ type testAccount struct {
}
// makeTestState create a sample test state to test node-wise reconstruction.
-func makeTestState() (Database, common.Hash, []*testAccount) {
+func makeTestState() (ethdb.Database, Database, common.Hash, []*testAccount) {
// Create an empty state
- db := NewDatabase(rawdb.NewMemoryDatabase())
- state, _ := New(common.Hash{}, db, nil)
+ db := rawdb.NewMemoryDatabase()
+ sdb := NewDatabase(db)
+ state, _ := New(common.Hash{}, sdb, nil)
// Fill it with some arbitrary data
var accounts []*testAccount
@@ -63,7 +64,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
if i%5 == 0 {
for j := byte(0); j < 5; j++ {
hash := crypto.Keccak256Hash([]byte{i, i, i, i, i, j, j})
- obj.SetState(db, hash, hash)
+ obj.SetState(sdb, hash, hash)
}
}
state.updateStateObject(obj)
@@ -72,7 +73,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
root, _ := state.Commit(false)
// Return the generated state
- return db, root, accounts
+ return db, sdb, root, accounts
}
// checkStateAccounts cross references a reconstructed state with an expected
@@ -104,7 +105,7 @@ func checkTrieConsistency(db ethdb.Database, root common.Hash) error {
if v, _ := db.Get(root[:]); v == nil {
return nil // Consider a non existent state consistent.
}
- trie, err := trie.New(common.Hash{}, root, trie.NewDatabase(db))
+ trie, err := trie.New(trie.StateTrieID(root), trie.NewDatabase(db))
if err != nil {
return err
}
@@ -132,8 +133,8 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
// Tests that an empty state is not scheduled for syncing.
func TestEmptyStateSync(t *testing.T) {
- empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
- sync := NewStateSync(empty, rawdb.NewMemoryDatabase(), nil)
+ db := trie.NewDatabase(rawdb.NewMemoryDatabase())
+ sync := NewStateSync(types.EmptyRootHash, rawdb.NewMemoryDatabase(), nil, db.Scheme())
if paths, nodes, codes := sync.Missing(1); len(paths) != 0 || len(nodes) != 0 || len(codes) != 0 {
t.Errorf("content requested for empty state: %v, %v, %v", nodes, paths, codes)
}
@@ -170,15 +171,15 @@ type stateElement struct {
func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
// Create a random state to copy
- srcDb, srcRoot, srcAccounts := makeTestState()
+ _, srcDb, srcRoot, srcAccounts := makeTestState()
if commit {
- srcDb.TrieDB().Commit(srcRoot, false, nil)
+ srcDb.TrieDB().Commit(srcRoot, false)
}
- srcTrie, _ := trie.New(common.Hash{}, srcRoot, srcDb.TrieDB())
+ srcTrie, _ := trie.New(trie.StateTrieID(srcRoot), srcDb.TrieDB())
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
- sched := NewStateSync(srcRoot, dstDb, nil)
+ sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
var (
nodeElements []stateElement
@@ -222,7 +223,8 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
if err := rlp.DecodeBytes(srcTrie.Get(node.syncPath[0]), &acc); err != nil {
t.Fatalf("failed to decode account on path %x: %v", node.syncPath[0], err)
}
- stTrie, err := trie.New(common.BytesToHash(node.syncPath[0]), acc.Root, srcDb.TrieDB())
+ id := trie.StorageTrieID(srcRoot, common.BytesToHash(node.syncPath[0]), acc.Root)
+ stTrie, err := trie.New(id, srcDb.TrieDB())
if err != nil {
t.Fatalf("failed to retriev storage trie for path %x: %v", node.syncPath[1], err)
}
@@ -280,11 +282,11 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
// partial results are returned, and the others sent only later.
func TestIterativeDelayedStateSync(t *testing.T) {
// Create a random state to copy
- srcDb, srcRoot, srcAccounts := makeTestState()
+ _, srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
- sched := NewStateSync(srcRoot, dstDb, nil)
+ sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
var (
nodeElements []stateElement
@@ -373,11 +375,11 @@ func TestIterativeRandomStateSyncBatched(t *testing.T) { testIterativeRandomS
func testIterativeRandomStateSync(t *testing.T, count int) {
// Create a random state to copy
- srcDb, srcRoot, srcAccounts := makeTestState()
+ _, srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
- sched := NewStateSync(srcRoot, dstDb, nil)
+ sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
nodeQueue := make(map[string]stateElement)
codeQueue := make(map[common.Hash]struct{})
@@ -453,11 +455,11 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
// partial results are returned (Even those randomly), others sent only later.
func TestIterativeRandomDelayedStateSync(t *testing.T) {
// Create a random state to copy
- srcDb, srcRoot, srcAccounts := makeTestState()
+ _, srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
- sched := NewStateSync(srcRoot, dstDb, nil)
+ sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
nodeQueue := make(map[string]stateElement)
codeQueue := make(map[common.Hash]struct{})
@@ -543,7 +545,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
// the database.
func TestIncompleteStateSync(t *testing.T) {
// Create a random state to copy
- srcDb, srcRoot, srcAccounts := makeTestState()
+ db, srcDb, srcRoot, srcAccounts := makeTestState()
// isCodeLookup to save some hashing
var isCode = make(map[common.Hash]struct{})
@@ -552,16 +554,17 @@ func TestIncompleteStateSync(t *testing.T) {
isCode[crypto.Keccak256Hash(acc.code)] = struct{}{}
}
}
- isCode[common.BytesToHash(emptyCodeHash)] = struct{}{}
- checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
+ isCode[types.EmptyCodeHash] = struct{}{}
+ checkTrieConsistency(db, srcRoot)
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
- sched := NewStateSync(srcRoot, dstDb, nil)
+ sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
var (
- addedCodes []common.Hash
- addedNodes []common.Hash
+ addedCodes []common.Hash
+ addedPaths []string
+ addedHashes []common.Hash
)
nodeQueue := make(map[string]stateElement)
codeQueue := make(map[common.Hash]struct{})
@@ -598,15 +601,16 @@ func TestIncompleteStateSync(t *testing.T) {
var nodehashes []common.Hash
if len(nodeQueue) > 0 {
results := make([]trie.NodeSyncResult, 0, len(nodeQueue))
- for key, element := range nodeQueue {
+ for path, element := range nodeQueue {
data, err := srcDb.TrieDB().Node(element.hash)
if err != nil {
t.Fatalf("failed to retrieve node data for %x", element.hash)
}
- results = append(results, trie.NodeSyncResult{Path: key, Data: data})
+ results = append(results, trie.NodeSyncResult{Path: path, Data: data})
if element.hash != srcRoot {
- addedNodes = append(addedNodes, element.hash)
+ addedPaths = append(addedPaths, element.path)
+ addedHashes = append(addedHashes, element.hash)
}
nodehashes = append(nodehashes, element.hash)
}
@@ -654,12 +658,18 @@ func TestIncompleteStateSync(t *testing.T) {
}
rawdb.WriteCode(dstDb, node, val)
}
- for _, node := range addedNodes {
- val := rawdb.ReadTrieNode(dstDb, node)
- rawdb.DeleteTrieNode(dstDb, node)
+ scheme := srcDb.TrieDB().Scheme()
+ for i, path := range addedPaths {
+ owner, inner := trie.ResolvePath([]byte(path))
+ hash := addedHashes[i]
+ val := rawdb.ReadTrieNode(dstDb, owner, inner, hash, scheme)
+ if val == nil {
+ t.Error("missing trie node")
+ }
+ rawdb.DeleteTrieNode(dstDb, owner, inner, hash, scheme)
if err := checkStateConsistency(dstDb, srcRoot); err == nil {
- t.Errorf("trie inconsistency not caught, missing: %v", node.Hex())
+ t.Errorf("trie inconsistency not caught, missing: %v", path)
}
- rawdb.WriteTrieNode(dstDb, node, val)
+ rawdb.WriteTrieNode(dstDb, owner, inner, hash, val, scheme)
}
}
diff --git a/core/state/transient_storage.go b/core/state/transient_storage.go
new file mode 100644
index 0000000000..66e563efa7
--- /dev/null
+++ b/core/state/transient_storage.go
@@ -0,0 +1,55 @@
+// 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 state
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// transientStorage is a representation of EIP-1153 "Transient Storage".
+type transientStorage map[common.Address]Storage
+
+// newTransientStorage creates a new instance of a transientStorage.
+func newTransientStorage() transientStorage {
+ return make(transientStorage)
+}
+
+// Set sets the transient-storage `value` for `key` at the given `addr`.
+func (t transientStorage) Set(addr common.Address, key, value common.Hash) {
+ if _, ok := t[addr]; !ok {
+ t[addr] = make(Storage)
+ }
+ t[addr][key] = value
+}
+
+// Get gets the transient storage for `key` at the given `addr`.
+func (t transientStorage) Get(addr common.Address, key common.Hash) common.Hash {
+ val, ok := t[addr]
+ if !ok {
+ return common.Hash{}
+ }
+ return val[key]
+}
+
+// Copy does a deep copy of the transientStorage
+func (t transientStorage) Copy() transientStorage {
+ storage := make(transientStorage)
+ for key, value := range t {
+ storage[key] = value.Copy()
+ }
+ return storage
+}
diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go
index 83e8966d4c..f142c86bbf 100644
--- a/core/state/trie_prefetcher.go
+++ b/core/state/trie_prefetcher.go
@@ -126,6 +126,9 @@ func (p *triePrefetcher) copy() *triePrefetcher {
// If the prefetcher is already a copy, duplicate the data
if p.fetches != nil {
for root, fetch := range p.fetches {
+ if fetch == nil {
+ continue
+ }
copy.fetches[root] = p.db.CopyTrie(fetch)
}
return copy
@@ -147,7 +150,7 @@ func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, keys [][]
id := p.trieID(owner, root)
fetcher := p.fetchers[id]
if fetcher == nil {
- fetcher = newSubfetcher(p.db, owner, root)
+ fetcher = newSubfetcher(p.db, p.root, owner, root)
p.fetchers[id] = fetcher
}
fetcher.schedule(keys)
@@ -203,6 +206,7 @@ func (p *triePrefetcher) trieID(owner common.Hash, root common.Hash) string {
// the trie being worked on is retrieved from the prefetcher.
type subfetcher struct {
db Database // Database to load trie nodes through
+ state common.Hash // Root hash of the state to prefetch
owner common.Hash // Owner of the trie, usually account hash
root common.Hash // Root hash of the trie to prefetch
trie Trie // Trie being populated with nodes
@@ -222,9 +226,10 @@ type subfetcher struct {
// newSubfetcher creates a goroutine to prefetch state items belonging to a
// particular root hash.
-func newSubfetcher(db Database, owner common.Hash, root common.Hash) *subfetcher {
+func newSubfetcher(db Database, state common.Hash, owner common.Hash, root common.Hash) *subfetcher {
sf := &subfetcher{
db: db,
+ state: state,
owner: owner,
root: root,
wake: make(chan struct{}, 1),
@@ -295,7 +300,7 @@ func (sf *subfetcher) loop() {
}
sf.trie = trie
} else {
- trie, err := sf.db.OpenStorageTrie(sf.owner, sf.root)
+ trie, err := sf.db.OpenStorageTrie(sf.state, sf.owner, sf.root)
if err != nil {
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
return
@@ -331,11 +336,7 @@ func (sf *subfetcher) loop() {
if _, ok := sf.seen[string(task)]; ok {
sf.dups++
} else {
- if len(task) == len(common.Address{}) {
- sf.trie.TryGetAccount(task)
- } else {
- sf.trie.TryGet(task)
- }
+ sf.trie.TryGet(task)
sf.seen[string(task)] = struct{}{}
}
}
diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go
index 10a1722940..867b47db53 100644
--- a/core/state_prefetcher.go
+++ b/core/state_prefetcher.go
@@ -67,7 +67,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
if err != nil {
return // Also invalid block, bail out
}
- statedb.Prepare(tx.Hash(), i)
+ statedb.SetTxContext(tx.Hash(), i)
if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil {
return // Ugh, something went horribly wrong, bail out
}
diff --git a/core/state_processor.go b/core/state_processor.go
index e511697c5f..163ea0a020 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -78,21 +78,26 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
if err != nil {
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
}
- statedb.Prepare(tx.Hash(), i)
- receipt, err := applyTransaction(msg, p.config, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
+ statedb.SetTxContext(tx.Hash(), i)
+ receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
if err != nil {
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...)
}
+ // Fail if Shanghai not enabled and len(withdrawals) is non-zero.
+ withdrawals := block.Withdrawals()
+ if len(withdrawals) > 0 && !p.config.IsShanghai(block.Time()) {
+ return nil, nil, 0, fmt.Errorf("withdrawals before shanghai")
+ }
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
- p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles())
+ p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), withdrawals)
return receipts, allLogs, *usedGas, nil
}
-func applyTransaction(msg types.Message, config *params.ChainConfig, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
+func applyTransaction(msg types.Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
// Create a new context to be used in the EVM environment.
txContext := NewEVMTxContext(msg)
evm.Reset(txContext, statedb)
@@ -129,7 +134,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, author *com
}
// Set the receipt logs and create the bloom filter.
- receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
+ receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
receipt.BlockHash = blockHash
receipt.BlockNumber = blockNumber
@@ -149,5 +154,5 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext(header, bc, author)
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
- return applyTransaction(msg, config, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
+ return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
}
diff --git a/core/state_processor_test.go b/core/state_processor_test.go
index aa8e4bebf9..59391fa861 100644
--- a/core/state_processor_test.go
+++ b/core/state_processor_test.go
@@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus"
+ "github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -35,6 +36,8 @@ import (
"golang.org/x/crypto/sha3"
)
+func u64(val uint64) *uint64 { return &val }
+
// TestStateProcessorErrors tests the output from the 'core' errors
// as defined in core/error.go. These errors are generated when the
// blockchain imports bad blocks, meaning blocks which have valid headers but
@@ -75,6 +78,17 @@ func TestStateProcessorErrors(t *testing.T) {
}), signer, key1)
return tx
}
+ var mkDynamicCreationTx = func(nonce uint64, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, data []byte) *types.Transaction {
+ tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{
+ Nonce: nonce,
+ GasTipCap: gasTipCap,
+ GasFeeCap: gasFeeCap,
+ Gas: gasLimit,
+ Value: big.NewInt(0),
+ Data: data,
+ }), signer, key1)
+ return tx
+ }
{ // Tests against a 'recent' chain definition
var (
db = rawdb.NewMemoryDatabase()
@@ -91,8 +105,7 @@ func TestStateProcessorErrors(t *testing.T) {
},
},
}
- genesis = gspec.MustCommit(db)
- blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+ blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
)
defer blockchain.Stop()
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
@@ -197,7 +210,7 @@ func TestStateProcessorErrors(t *testing.T) {
want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
},
} {
- block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
+ block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config)
_, err := blockchain.InsertChain(types.Blocks{block})
if err == nil {
t.Fatal("block imported without errors")
@@ -232,8 +245,7 @@ func TestStateProcessorErrors(t *testing.T) {
},
},
}
- genesis = gspec.MustCommit(db)
- blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+ blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
)
defer blockchain.Stop()
for i, tt := range []struct {
@@ -247,7 +259,7 @@ func TestStateProcessorErrors(t *testing.T) {
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported",
},
} {
- block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
+ block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config)
_, err := blockchain.InsertChain(types.Blocks{block})
if err == nil {
t.Fatal("block imported without errors")
@@ -272,8 +284,7 @@ func TestStateProcessorErrors(t *testing.T) {
},
},
}
- genesis = gspec.MustCommit(db)
- blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+ blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
)
defer blockchain.Stop()
for i, tt := range []struct {
@@ -287,7 +298,73 @@ func TestStateProcessorErrors(t *testing.T) {
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
},
} {
- block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
+ block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config)
+ _, err := blockchain.InsertChain(types.Blocks{block})
+ if err == nil {
+ t.Fatal("block imported without errors")
+ }
+ if have, want := err.Error(), tt.want; have != want {
+ t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
+ }
+ }
+ }
+
+ // ErrMaxInitCodeSizeExceeded, for this we need extra Shanghai (EIP-3860) enabled.
+ {
+ var (
+ db = rawdb.NewMemoryDatabase()
+ gspec = &Genesis{
+ Config: ¶ms.ChainConfig{
+ ChainID: big.NewInt(1),
+ HomesteadBlock: big.NewInt(0),
+ 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: big.NewInt(0),
+ TerminalTotalDifficulty: big.NewInt(0),
+ TerminalTotalDifficultyPassed: true,
+ ShanghaiTime: u64(0),
+ },
+ Alloc: GenesisAlloc{
+ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
+ Balance: big.NewInt(1000000000000000000), // 1 ether
+ Nonce: 0,
+ },
+ },
+ }
+ genesis = gspec.MustCommit(db)
+ blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
+ tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
+ smallInitCode = [320]byte{}
+ )
+ defer blockchain.Stop()
+ for i, tt := range []struct {
+ txs []*types.Transaction
+ want string
+ }{
+ { // ErrMaxInitCodeSizeExceeded
+ txs: []*types.Transaction{
+ mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header()), tooBigInitCode[:]),
+ },
+ want: "could not apply tx 0 [0x832b54a6c3359474a9f504b1003b2cc1b6fcaa18e4ef369eb45b5d40dad6378f]: max initcode size exceeded: code size 49153 limit 49152",
+ },
+ { // ErrIntrinsicGas: Not enough gas to cover init code
+ txs: []*types.Transaction{
+ mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header()), smallInitCode[:]),
+ },
+ want: "could not apply tx 0 [0x39b7436cb432d3662a25626474282c5c4c1a213326fd87e4e18a91477bae98b2]: intrinsic gas too low: have 54299, want 54300",
+ },
+ } {
+ block := GenerateBadBlock(genesis, beacon.New(ethash.NewFaker()), tt.txs, gspec.Config)
_, err := blockchain.InsertChain(types.Blocks{block})
if err == nil {
t.Fatal("block imported without errors")
@@ -304,23 +381,31 @@ func TestStateProcessorErrors(t *testing.T) {
// valid to be considered for import:
// - valid pow (fake), ancestry, difficulty, gaslimit etc
func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block {
- header := &types.Header{
- ParentHash: parent.Hash(),
- Coinbase: parent.Coinbase(),
- Difficulty: engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{
+ difficulty := big.NewInt(0)
+ if !config.TerminalTotalDifficultyPassed {
+ difficulty = engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{
Number: parent.Number(),
Time: parent.Time(),
Difficulty: parent.Difficulty(),
UncleHash: parent.UncleHash(),
- }),
- GasLimit: parent.GasLimit(),
- Number: new(big.Int).Add(parent.Number(), common.Big1),
- Time: parent.Time() + 10,
- UncleHash: types.EmptyUncleHash,
+ })
+ }
+
+ header := &types.Header{
+ ParentHash: parent.Hash(),
+ Coinbase: parent.Coinbase(),
+ Difficulty: difficulty,
+ GasLimit: parent.GasLimit(),
+ Number: new(big.Int).Add(parent.Number(), common.Big1),
+ Time: parent.Time() + 10,
+ UncleHash: types.EmptyUncleHash,
}
if config.IsLondon(header.Number) {
header.BaseFee = misc.CalcBaseFee(config, parent.Header())
}
+ if config.IsShanghai(header.Time) {
+ header.WithdrawalsHash = &types.EmptyWithdrawalsHash
+ }
var receipts []*types.Receipt
// The post-state result doesn't need to be correct (this is a bad block), but we do need something there
// Preferably something unique. So let's use a combo of blocknum + txhash
@@ -338,5 +423,8 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
}
header.Root = common.BytesToHash(hasher.Sum(nil))
// Assemble and return the final block for sealing
+ if config.IsShanghai(header.Time) {
+ return types.NewBlockWithWithdrawals(header, txs, nil, receipts, []*types.Withdrawal{}, trie.NewStackTrie(nil))
+ }
return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil))
}
diff --git a/core/state_transition.go b/core/state_transition.go
index 0946c0372e..653c6b1836 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -31,23 +31,28 @@ import (
var emptyCodeHash = crypto.Keccak256Hash(nil)
-/*
-The State Transitioning Model
-
-A state transition is a change made when a transaction is applied to the current world state
-The state transitioning model does all the necessary work to work out a valid new state root.
-
-1) Nonce handling
-2) Pre pay gas
-3) Create a new state object if the recipient is \0*32
-4) Value transfer
-== If contract creation ==
- 4a) Attempt to run transaction data
- 4b) If valid, use result as code for the new state object
-== end ==
-5) Run Script section
-6) Derive new state root
-*/
+// StateTransition represents a state transition.
+//
+// == The State Transitioning Model
+//
+// A state transition is a change made when a transaction is applied to the current world
+// state. The state transitioning model does all the necessary work to work out a valid new
+// state root.
+//
+// 1. Nonce handling
+// 2. Pre pay gas
+// 3. Create a new state object if the recipient is nil
+// 4. Value transfer
+//
+// == If contract creation ==
+//
+// 4a. Attempt to run transaction data
+// 4b. If valid, use result as code for the new state object
+//
+// == end ==
+//
+// 5. Run Script section
+// 6. Derive new state root
type StateTransition struct {
gp *GasPool
msg Message
@@ -115,7 +120,7 @@ func (result *ExecutionResult) Revert() []byte {
}
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
-func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool) (uint64, error) {
+func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool, isEIP3860 bool) (uint64, error) {
// Set the starting gas for the raw transaction
var gas uint64
if isContractCreation && isHomestead {
@@ -123,8 +128,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
} else {
gas = params.TxGas
}
+ dataLen := uint64(len(data))
// Bump the required gas by the amount of transactional data
- if len(data) > 0 {
+ if dataLen > 0 {
// Zero and non-zero bytes are priced differently
var nz uint64
for _, byt := range data {
@@ -142,11 +148,19 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
}
gas += nz * nonZeroGas
- z := uint64(len(data)) - nz
+ z := dataLen - nz
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
return 0, ErrGasUintOverflow
}
gas += z * params.TxDataZeroGas
+
+ if isContractCreation && isEIP3860 {
+ lenWords := toWordSize(dataLen)
+ if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords {
+ return 0, ErrGasUintOverflow
+ }
+ gas += lenWords * params.InitCodeWordGas
+ }
}
if accessList != nil {
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
@@ -155,6 +169,15 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
return gas, nil
}
+// toWordSize returns the ceiled word size required for init code payment calculation.
+func toWordSize(size uint64) uint64 {
+ if size > math.MaxUint64-31 {
+ return math.MaxUint64/32 + 1
+ }
+
+ return (size + 31) / 32
+}
+
// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
@@ -262,13 +285,10 @@ func (st *StateTransition) preCheck() error {
// TransitionDb will transition the state by applying the current message and
// returning the evm execution result with following fields.
//
-// - used gas:
-// total gas used (including gas being refunded)
-// - returndata:
-// the returned data from evm
-// - concrete execution error:
-// various **EVM** error which aborts the execution,
-// e.g. ErrOutOfGas, ErrExecutionReverted
+// - used gas: total gas used (including gas being refunded)
+// - returndata: the returned data from evm
+// - concrete execution error: various EVM errors which abort the execution, e.g.
+// ErrOutOfGas, ErrExecutionReverted
//
// However if any consensus issue encountered, return the error directly with
// nil evm execution result.
@@ -298,12 +318,12 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
var (
msg = st.msg
sender = vm.AccountRef(msg.From())
- rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil)
+ rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil, st.evm.Context.Time)
contractCreation = msg.To() == nil
)
// Check clauses 4-5, subtract intrinsic gas if everything is correct
- gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul)
+ gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
if err != nil {
return nil, err
}
@@ -317,10 +337,16 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex())
}
- // Set up the initial access list.
- if rules.IsBerlin {
- st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
+ // Check whether the init code size has been exceeded.
+ if rules.IsShanghai && contractCreation && len(st.data) > params.MaxInitCodeSize {
+ return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize)
}
+
+ // Execute the preparatory steps for state transition which includes:
+ // - prepare accessList(post-berlin)
+ // - reset transient storage(eip 1153)
+ st.state.Prepare(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
+
var (
ret []byte
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
diff --git a/core/tx_journal.go b/core/txpool/journal.go
similarity index 91%
rename from core/tx_journal.go
rename to core/txpool/journal.go
index 62344f5646..1b330b0c3c 100644
--- a/core/tx_journal.go
+++ b/core/txpool/journal.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"errors"
@@ -41,23 +41,23 @@ type devNull struct{}
func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil }
func (*devNull) Close() error { return nil }
-// txJournal is a rotating log of transactions with the aim of storing locally
+// journal is a rotating log of transactions with the aim of storing locally
// created transactions to allow non-executed ones to survive node restarts.
-type txJournal struct {
+type journal struct {
path string // Filesystem path to store the transactions at
writer io.WriteCloser // Output stream to write new transactions into
}
// newTxJournal creates a new transaction journal to
-func newTxJournal(path string) *txJournal {
- return &txJournal{
+func newTxJournal(path string) *journal {
+ return &journal{
path: path,
}
}
// load parses a transaction journal dump from disk, loading its contents into
// the specified pool.
-func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
+func (journal *journal) load(add func([]*types.Transaction) []error) error {
// Open the journal for loading any past transactions
input, err := os.Open(journal.path)
if errors.Is(err, fs.ErrNotExist) {
@@ -118,7 +118,7 @@ func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
}
// insert adds the specified transaction to the local disk journal.
-func (journal *txJournal) insert(tx *types.Transaction) error {
+func (journal *journal) insert(tx *types.Transaction) error {
if journal.writer == nil {
return errNoActiveJournal
}
@@ -130,7 +130,7 @@ func (journal *txJournal) insert(tx *types.Transaction) error {
// rotate regenerates the transaction journal based on the current contents of
// the transaction pool.
-func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error {
+func (journal *journal) rotate(all map[common.Address]types.Transactions) error {
// Close the current journal (if any is open)
if journal.writer != nil {
if err := journal.writer.Close(); err != nil {
@@ -170,7 +170,7 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro
}
// close flushes the transaction journal contents to disk and closes the file.
-func (journal *txJournal) close() error {
+func (journal *journal) close() error {
var err error
if journal.writer != nil {
diff --git a/core/tx_list.go b/core/txpool/list.go
similarity index 87%
rename from core/tx_list.go
rename to core/txpool/list.go
index f141a03bbd..062cbbf63e 100644
--- a/core/tx_list.go
+++ b/core/txpool/list.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"container/heap"
@@ -45,34 +45,35 @@ func (h *nonceHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
+ old[n-1] = 0
*h = old[0 : n-1]
return x
}
-// txSortedMap is a nonce->transaction hash map with a heap based index to allow
+// sortedMap is a nonce->transaction hash map with a heap based index to allow
// iterating over the contents in a nonce-incrementing way.
-type txSortedMap struct {
+type sortedMap struct {
items map[uint64]*types.Transaction // Hash map storing the transaction data
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
cache types.Transactions // Cache of the transactions already sorted
}
-// newTxSortedMap creates a new nonce-sorted transaction map.
-func newTxSortedMap() *txSortedMap {
- return &txSortedMap{
+// newSortedMap creates a new nonce-sorted transaction map.
+func newSortedMap() *sortedMap {
+ return &sortedMap{
items: make(map[uint64]*types.Transaction),
index: new(nonceHeap),
}
}
// Get retrieves the current transactions associated with the given nonce.
-func (m *txSortedMap) Get(nonce uint64) *types.Transaction {
+func (m *sortedMap) Get(nonce uint64) *types.Transaction {
return m.items[nonce]
}
// Put inserts a new transaction into the map, also updating the map's nonce
// index. If a transaction already exists with the same nonce, it's overwritten.
-func (m *txSortedMap) Put(tx *types.Transaction) {
+func (m *sortedMap) Put(tx *types.Transaction) {
nonce := tx.Nonce()
if m.items[nonce] == nil {
heap.Push(m.index, nonce)
@@ -83,7 +84,7 @@ func (m *txSortedMap) Put(tx *types.Transaction) {
// Forward removes all transactions from the map with a nonce lower than the
// provided threshold. Every removed transaction is returned for any post-removal
// maintenance.
-func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
+func (m *sortedMap) Forward(threshold uint64) types.Transactions {
var removed types.Transactions
// Pop off heap items until the threshold is reached
@@ -104,7 +105,7 @@ func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
// Filter, as opposed to 'filter', re-initialises the heap after the operation is done.
// If you want to do several consecutive filterings, it's therefore better to first
// do a .filter(func1) followed by .Filter(func2) or reheap()
-func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
+func (m *sortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
removed := m.filter(filter)
// If transactions were removed, the heap and cache are ruined
if len(removed) > 0 {
@@ -113,7 +114,7 @@ func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transac
return removed
}
-func (m *txSortedMap) reheap() {
+func (m *sortedMap) reheap() {
*m.index = make([]uint64, 0, len(m.items))
for nonce := range m.items {
*m.index = append(*m.index, nonce)
@@ -124,7 +125,7 @@ func (m *txSortedMap) reheap() {
// filter is identical to Filter, but **does not** regenerate the heap. This method
// should only be used if followed immediately by a call to Filter or reheap()
-func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transactions {
+func (m *sortedMap) filter(filter func(*types.Transaction) bool) types.Transactions {
var removed types.Transactions
// Collect all the transactions to filter out
@@ -142,7 +143,7 @@ func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transac
// Cap places a hard limit on the number of items, returning all transactions
// exceeding that limit.
-func (m *txSortedMap) Cap(threshold int) types.Transactions {
+func (m *sortedMap) Cap(threshold int) types.Transactions {
// Short circuit if the number of items is under the limit
if len(m.items) <= threshold {
return nil
@@ -167,7 +168,7 @@ func (m *txSortedMap) Cap(threshold int) types.Transactions {
// Remove deletes a transaction from the maintained map, returning whether the
// transaction was found.
-func (m *txSortedMap) Remove(nonce uint64) bool {
+func (m *sortedMap) Remove(nonce uint64) bool {
// Short circuit if no transaction is present
_, ok := m.items[nonce]
if !ok {
@@ -193,7 +194,7 @@ func (m *txSortedMap) Remove(nonce uint64) bool {
// Note, all transactions with nonces lower than start will also be returned to
// prevent getting into and invalid state. This is not something that should ever
// happen but better to be self correcting than failing!
-func (m *txSortedMap) Ready(start uint64) types.Transactions {
+func (m *sortedMap) Ready(start uint64) types.Transactions {
// Short circuit if no transactions are available
if m.index.Len() == 0 || (*m.index)[0] > start {
return nil
@@ -211,11 +212,11 @@ func (m *txSortedMap) Ready(start uint64) types.Transactions {
}
// Len returns the length of the transaction map.
-func (m *txSortedMap) Len() int {
+func (m *sortedMap) Len() int {
return len(m.items)
}
-func (m *txSortedMap) flatten() types.Transactions {
+func (m *sortedMap) flatten() types.Transactions {
// If the sorting was not cached yet, create and cache it
if m.cache == nil {
m.cache = make(types.Transactions, 0, len(m.items))
@@ -230,7 +231,7 @@ func (m *txSortedMap) flatten() types.Transactions {
// Flatten creates a nonce-sorted slice of transactions based on the loosely
// sorted internal representation. The result of the sorting is cached in case
// it's requested again before any modifications are made to the contents.
-func (m *txSortedMap) Flatten() types.Transactions {
+func (m *sortedMap) Flatten() types.Transactions {
// Copy the cache to prevent accidental modifications
cache := m.flatten()
txs := make(types.Transactions, len(cache))
@@ -240,36 +241,36 @@ func (m *txSortedMap) Flatten() types.Transactions {
// LastElement returns the last element of a flattened list, thus, the
// transaction with the highest nonce
-func (m *txSortedMap) LastElement() *types.Transaction {
+func (m *sortedMap) LastElement() *types.Transaction {
cache := m.flatten()
return cache[len(cache)-1]
}
-// txList is a "list" of transactions belonging to an account, sorted by account
+// list is a "list" of transactions belonging to an account, sorted by account
// nonce. The same type can be used both for storing contiguous transactions for
// the executable/pending queue; and for storing gapped transactions for the non-
// executable/future queue, with minor behavioral changes.
-type txList struct {
- strict bool // Whether nonces are strictly continuous or not
- txs *txSortedMap // Heap indexed sorted hash map of the transactions
+type list struct {
+ strict bool // Whether nonces are strictly continuous or not
+ txs *sortedMap // Heap indexed sorted hash map of the transactions
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
}
-// newTxList create a new transaction list for maintaining nonce-indexable fast,
+// newList create a new transaction list for maintaining nonce-indexable fast,
// gapped, sortable transaction lists.
-func newTxList(strict bool) *txList {
- return &txList{
+func newList(strict bool) *list {
+ return &list{
strict: strict,
- txs: newTxSortedMap(),
+ txs: newSortedMap(),
costcap: new(big.Int),
}
}
// Overlaps returns whether the transaction specified has the same nonce as one
// already contained within the list.
-func (l *txList) Overlaps(tx *types.Transaction) bool {
+func (l *list) Overlaps(tx *types.Transaction) bool {
return l.txs.Get(tx.Nonce()) != nil
}
@@ -278,7 +279,7 @@ func (l *txList) Overlaps(tx *types.Transaction) bool {
//
// If the new transaction is accepted into the list, the lists' cost and gas
// thresholds are also potentially updated.
-func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
+func (l *list) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
// If there's an older better transaction, abort
old := l.txs.Get(tx.Nonce())
if old != nil {
@@ -316,7 +317,7 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
// Forward removes all transactions from the list with a nonce lower than the
// provided threshold. Every removed transaction is returned for any post-removal
// maintenance.
-func (l *txList) Forward(threshold uint64) types.Transactions {
+func (l *list) Forward(threshold uint64) types.Transactions {
return l.txs.Forward(threshold)
}
@@ -329,7 +330,7 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
// a point in calculating all the costs or if the balance covers all. If the threshold
// is lower than the costgas cap, the caps will be reset to a new high after removing
// the newly invalidated transactions.
-func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) {
+func (l *list) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) {
// If all transactions are below the threshold, short circuit
if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit {
return nil, nil
@@ -362,14 +363,14 @@ func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions
// Cap places a hard limit on the number of items, returning all transactions
// exceeding that limit.
-func (l *txList) Cap(threshold int) types.Transactions {
+func (l *list) Cap(threshold int) types.Transactions {
return l.txs.Cap(threshold)
}
// Remove deletes a transaction from the maintained list, returning whether the
// transaction was found, and also returning any transaction invalidated due to
// the deletion (strict mode only).
-func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
+func (l *list) Remove(tx *types.Transaction) (bool, types.Transactions) {
// Remove the transaction from the set
nonce := tx.Nonce()
if removed := l.txs.Remove(nonce); !removed {
@@ -389,30 +390,30 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
// Note, all transactions with nonces lower than start will also be returned to
// prevent getting into and invalid state. This is not something that should ever
// happen but better to be self correcting than failing!
-func (l *txList) Ready(start uint64) types.Transactions {
+func (l *list) Ready(start uint64) types.Transactions {
return l.txs.Ready(start)
}
// Len returns the length of the transaction list.
-func (l *txList) Len() int {
+func (l *list) Len() int {
return l.txs.Len()
}
// Empty returns whether the list of transactions is empty or not.
-func (l *txList) Empty() bool {
+func (l *list) Empty() bool {
return l.Len() == 0
}
// Flatten creates a nonce-sorted slice of transactions based on the loosely
// sorted internal representation. The result of the sorting is cached in case
// it's requested again before any modifications are made to the contents.
-func (l *txList) Flatten() types.Transactions {
+func (l *list) Flatten() types.Transactions {
return l.txs.Flatten()
}
// LastElement returns the last element of a flattened list, thus, the
// transaction with the highest nonce
-func (l *txList) LastElement() *types.Transaction {
+func (l *list) LastElement() *types.Transaction {
return l.txs.LastElement()
}
@@ -468,8 +469,8 @@ func (h *priceHeap) Pop() interface{} {
return x
}
-// txPricedList is a price-sorted heap to allow operating on transactions pool
-// contents in a price-incrementing way. It's built opon the all transactions
+// pricedList is a price-sorted heap to allow operating on transactions pool
+// contents in a price-incrementing way. It's built upon the all transactions
// in txpool but only interested in the remote part. It means only remote transactions
// will be considered for tracking, sorting, eviction, etc.
//
@@ -479,14 +480,14 @@ func (h *priceHeap) Pop() interface{} {
// In some cases (during a congestion, when blocks are full) the urgent heap can provide
// better candidates for inclusion while in other cases (at the top of the baseFee peak)
// the floating heap is better. When baseFee is decreasing they behave similarly.
-type txPricedList struct {
+type pricedList struct {
// Number of stale price points to (re-heap trigger).
// This field is accessed atomically, and must be the first field
// to ensure it has correct alignment for atomic.AddInt64.
// See https://golang.org/pkg/sync/atomic/#pkg-note-BUG.
stales int64
- all *txLookup // Pointer to the map of all transactions
+ all *lookup // Pointer to the map of all transactions
urgent, floating priceHeap // Heaps of prices of all the stored **remote** transactions
reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list
}
@@ -497,15 +498,15 @@ const (
floatingRatio = 1
)
-// newTxPricedList creates a new price-sorted transaction heap.
-func newTxPricedList(all *txLookup) *txPricedList {
- return &txPricedList{
+// newPricedList creates a new price-sorted transaction heap.
+func newPricedList(all *lookup) *pricedList {
+ return &pricedList{
all: all,
}
}
// Put inserts a new transaction into the heap.
-func (l *txPricedList) Put(tx *types.Transaction, local bool) {
+func (l *pricedList) Put(tx *types.Transaction, local bool) {
if local {
return
}
@@ -516,7 +517,7 @@ func (l *txPricedList) Put(tx *types.Transaction, local bool) {
// Removed notifies the prices transaction list that an old transaction dropped
// from the pool. The list will just keep a counter of stale objects and update
// the heap if a large enough ratio of transactions go stale.
-func (l *txPricedList) Removed(count int) {
+func (l *pricedList) Removed(count int) {
// Bump the stale counter, but exit if still too low (< 25%)
stales := atomic.AddInt64(&l.stales, int64(count))
if int(stales) <= (len(l.urgent.list)+len(l.floating.list))/4 {
@@ -528,7 +529,7 @@ func (l *txPricedList) Removed(count int) {
// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
// lowest priced (remote) transaction currently being tracked.
-func (l *txPricedList) Underpriced(tx *types.Transaction) bool {
+func (l *pricedList) Underpriced(tx *types.Transaction) bool {
// Note: with two queues, being underpriced is defined as being worse than the worst item
// in all non-empty queues if there is any. If both queues are empty then nothing is underpriced.
return (l.underpricedFor(&l.urgent, tx) || len(l.urgent.list) == 0) &&
@@ -538,7 +539,7 @@ func (l *txPricedList) Underpriced(tx *types.Transaction) bool {
// underpricedFor checks whether a transaction is cheaper than (or as cheap as) the
// lowest priced (remote) transaction in the given heap.
-func (l *txPricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool {
+func (l *pricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool {
// Discard stale price points if found at the heap start
for len(h.list) > 0 {
head := h.list[0]
@@ -562,7 +563,7 @@ func (l *txPricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool
// priced list and returns them for further removal from the entire pool.
//
// Note local transaction won't be considered for eviction.
-func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) {
+func (l *pricedList) Discard(slots int, force bool) (types.Transactions, bool) {
drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop
for slots > 0 {
if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 {
@@ -601,7 +602,7 @@ func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool)
}
// Reheap forcibly rebuilds the heap based on the current remote transaction set.
-func (l *txPricedList) Reheap() {
+func (l *pricedList) Reheap() {
l.reheapMu.Lock()
defer l.reheapMu.Unlock()
start := time.Now()
@@ -629,7 +630,7 @@ func (l *txPricedList) Reheap() {
// SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not
// necessary to call right before SetBaseFee when processing a new block.
-func (l *txPricedList) SetBaseFee(baseFee *big.Int) {
+func (l *pricedList) SetBaseFee(baseFee *big.Int) {
l.urgent.baseFee = baseFee
l.Reheap()
}
diff --git a/core/tx_list_test.go b/core/txpool/list_test.go
similarity index 84%
rename from core/tx_list_test.go
rename to core/txpool/list_test.go
index ef49cae1dd..4e1a5d5e83 100644
--- a/core/tx_list_test.go
+++ b/core/txpool/list_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"math/big"
@@ -27,7 +27,7 @@ import (
// Tests that transactions can be added to strict lists and list contents and
// nonce boundaries are correctly maintained.
-func TestStrictTxListAdd(t *testing.T) {
+func TestStrictListAdd(t *testing.T) {
// Generate a list of transactions to insert
key, _ := crypto.GenerateKey()
@@ -36,9 +36,9 @@ func TestStrictTxListAdd(t *testing.T) {
txs[i] = transaction(uint64(i), 0, key)
}
// Insert the transactions in a random order
- list := newTxList(true)
+ list := newList(true)
for _, v := range rand.Perm(len(txs)) {
- list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
+ list.Add(txs[v], DefaultConfig.PriceBump)
}
// Verify internal state
if len(list.txs.items) != len(txs) {
@@ -51,7 +51,7 @@ func TestStrictTxListAdd(t *testing.T) {
}
}
-func BenchmarkTxListAdd(b *testing.B) {
+func BenchmarkListAdd(b *testing.B) {
// Generate a list of transactions to insert
key, _ := crypto.GenerateKey()
@@ -60,13 +60,13 @@ func BenchmarkTxListAdd(b *testing.B) {
txs[i] = transaction(uint64(i), 0, key)
}
// Insert the transactions in a random order
- priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit))
+ priceLimit := big.NewInt(int64(DefaultConfig.PriceLimit))
b.ResetTimer()
for i := 0; i < b.N; i++ {
- list := newTxList(true)
+ list := newList(true)
for _, v := range rand.Perm(len(txs)) {
- list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
- list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump)
+ list.Add(txs[v], DefaultConfig.PriceBump)
+ list.Filter(priceLimit, DefaultConfig.PriceBump)
}
}
}
diff --git a/core/tx_noncer.go b/core/txpool/noncer.go
similarity index 75%
rename from core/tx_noncer.go
rename to core/txpool/noncer.go
index d6d2200775..ba7fbedad5 100644
--- a/core/tx_noncer.go
+++ b/core/txpool/noncer.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"sync"
@@ -23,18 +23,18 @@ import (
"github.com/ethereum/go-ethereum/core/state"
)
-// txNoncer is a tiny virtual state database to manage the executable nonces of
+// noncer is a tiny virtual state database to manage the executable nonces of
// accounts in the pool, falling back to reading from a real state database if
// an account is unknown.
-type txNoncer struct {
+type noncer struct {
fallback *state.StateDB
nonces map[common.Address]uint64
lock sync.Mutex
}
-// newTxNoncer creates a new virtual state database to track the pool nonces.
-func newTxNoncer(statedb *state.StateDB) *txNoncer {
- return &txNoncer{
+// newNoncer creates a new virtual state database to track the pool nonces.
+func newNoncer(statedb *state.StateDB) *noncer {
+ return &noncer{
fallback: statedb.Copy(),
nonces: make(map[common.Address]uint64),
}
@@ -42,21 +42,23 @@ func newTxNoncer(statedb *state.StateDB) *txNoncer {
// get returns the current nonce of an account, falling back to a real state
// database if the account is unknown.
-func (txn *txNoncer) get(addr common.Address) uint64 {
+func (txn *noncer) get(addr common.Address) uint64 {
// We use mutex for get operation is the underlying
// state will mutate db even for read access.
txn.lock.Lock()
defer txn.lock.Unlock()
if _, ok := txn.nonces[addr]; !ok {
- txn.nonces[addr] = txn.fallback.GetNonce(addr)
+ if nonce := txn.fallback.GetNonce(addr); nonce != 0 {
+ txn.nonces[addr] = nonce
+ }
}
return txn.nonces[addr]
}
// set inserts a new virtual nonce into the virtual state database to be returned
// whenever the pool requests it instead of reaching into the real state database.
-func (txn *txNoncer) set(addr common.Address, nonce uint64) {
+func (txn *noncer) set(addr common.Address, nonce uint64) {
txn.lock.Lock()
defer txn.lock.Unlock()
@@ -64,13 +66,15 @@ func (txn *txNoncer) set(addr common.Address, nonce uint64) {
}
// setIfLower updates a new virtual nonce into the virtual state database if the
-// the new one is lower.
-func (txn *txNoncer) setIfLower(addr common.Address, nonce uint64) {
+// new one is lower.
+func (txn *noncer) setIfLower(addr common.Address, nonce uint64) {
txn.lock.Lock()
defer txn.lock.Unlock()
if _, ok := txn.nonces[addr]; !ok {
- txn.nonces[addr] = txn.fallback.GetNonce(addr)
+ if nonce := txn.fallback.GetNonce(addr); nonce != 0 {
+ txn.nonces[addr] = nonce
+ }
}
if txn.nonces[addr] <= nonce {
return
@@ -79,7 +83,7 @@ func (txn *txNoncer) setIfLower(addr common.Address, nonce uint64) {
}
// setAll sets the nonces for all accounts to the given map.
-func (txn *txNoncer) setAll(all map[common.Address]uint64) {
+func (txn *noncer) setAll(all map[common.Address]uint64) {
txn.lock.Lock()
defer txn.lock.Unlock()
diff --git a/core/tx_pool.go b/core/txpool/txpool.go
similarity index 92%
rename from core/tx_pool.go
rename to core/txpool/txpool.go
index 1c25442dd9..c805201866 100644
--- a/core/tx_pool.go
+++ b/core/txpool/txpool.go
@@ -14,10 +14,11 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"errors"
+ "fmt"
"math"
"math/big"
"sort"
@@ -28,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/consensus/misc"
+ "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
@@ -65,7 +67,7 @@ var (
// configured for the transaction pool.
ErrUnderpriced = errors.New("transaction underpriced")
- // ErrTxPoolOverflow is returned if the transaction pool is full and can't accpet
+ // ErrTxPoolOverflow is returned if the transaction pool is full and can't accept
// another remote transaction.
ErrTxPoolOverflow = errors.New("txpool is full")
@@ -112,6 +114,7 @@ var (
invalidTxMeter = metrics.NewRegisteredMeter("txpool/invalid", nil)
underpricedTxMeter = metrics.NewRegisteredMeter("txpool/underpriced", nil)
overflowedTxMeter = metrics.NewRegisteredMeter("txpool/overflowed", nil)
+
// throttleTxMeter counts how many transactions are rejected due to too-many-changes between
// txpool reorgs.
throttleTxMeter = metrics.NewRegisteredMeter("txpool/throttle", nil)
@@ -146,11 +149,11 @@ type blockChain interface {
GetBlock(hash common.Hash, number uint64) *types.Block
StateAt(root common.Hash) (*state.StateDB, error)
- SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
+ SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
}
-// TxPoolConfig are the configuration parameters of the transaction pool.
-type TxPoolConfig struct {
+// Config are the configuration parameters of the transaction pool.
+type Config struct {
Locals []common.Address // Addresses that should be treated by default as local
NoLocals bool // Whether local transaction handling should be disabled
Journal string // Journal of local transactions to survive node restarts
@@ -167,9 +170,9 @@ type TxPoolConfig struct {
Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
}
-// DefaultTxPoolConfig contains the default configurations for the transaction
+// DefaultConfig contains the default configurations for the transaction
// pool.
-var DefaultTxPoolConfig = TxPoolConfig{
+var DefaultConfig = Config{
Journal: "transactions.rlp",
Rejournal: time.Hour,
@@ -186,39 +189,39 @@ var DefaultTxPoolConfig = TxPoolConfig{
// sanitize checks the provided user configurations and changes anything that's
// unreasonable or unworkable.
-func (config *TxPoolConfig) sanitize() TxPoolConfig {
+func (config *Config) sanitize() Config {
conf := *config
if conf.Rejournal < time.Second {
log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
conf.Rejournal = time.Second
}
if conf.PriceLimit < 1 {
- log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit)
- conf.PriceLimit = DefaultTxPoolConfig.PriceLimit
+ log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit)
+ conf.PriceLimit = DefaultConfig.PriceLimit
}
if conf.PriceBump < 1 {
- log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump)
- conf.PriceBump = DefaultTxPoolConfig.PriceBump
+ log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultConfig.PriceBump)
+ conf.PriceBump = DefaultConfig.PriceBump
}
if conf.AccountSlots < 1 {
- log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultTxPoolConfig.AccountSlots)
- conf.AccountSlots = DefaultTxPoolConfig.AccountSlots
+ log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultConfig.AccountSlots)
+ conf.AccountSlots = DefaultConfig.AccountSlots
}
if conf.GlobalSlots < 1 {
- log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultTxPoolConfig.GlobalSlots)
- conf.GlobalSlots = DefaultTxPoolConfig.GlobalSlots
+ log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultConfig.GlobalSlots)
+ conf.GlobalSlots = DefaultConfig.GlobalSlots
}
if conf.AccountQueue < 1 {
- log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultTxPoolConfig.AccountQueue)
- conf.AccountQueue = DefaultTxPoolConfig.AccountQueue
+ log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultConfig.AccountQueue)
+ conf.AccountQueue = DefaultConfig.AccountQueue
}
if conf.GlobalQueue < 1 {
- log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultTxPoolConfig.GlobalQueue)
- conf.GlobalQueue = DefaultTxPoolConfig.GlobalQueue
+ log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultConfig.GlobalQueue)
+ conf.GlobalQueue = DefaultConfig.GlobalQueue
}
if conf.Lifetime < 1 {
- log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime)
- conf.Lifetime = DefaultTxPoolConfig.Lifetime
+ log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultConfig.Lifetime)
+ conf.Lifetime = DefaultConfig.Lifetime
}
return conf
}
@@ -231,7 +234,7 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
// current state) and future transactions. Transactions move between those
// two states over time as they are received and processed.
type TxPool struct {
- config TxPoolConfig
+ config Config
chainconfig *params.ChainConfig
chain blockChain
gasPrice *big.Int
@@ -243,21 +246,22 @@ type TxPool struct {
istanbul bool // Fork indicator whether we are in the istanbul stage.
eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions.
+ shanghai bool // Fork indicator whether we are in the Shanghai stage.
currentState *state.StateDB // Current state in the blockchain head
- pendingNonces *txNoncer // Pending state tracking virtual nonces
+ pendingNonces *noncer // Pending state tracking virtual nonces
currentMaxGas uint64 // Current gas limit for transaction caps
locals *accountSet // Set of local transaction to exempt from eviction rules
- journal *txJournal // Journal of local transaction to back up to disk
+ journal *journal // Journal of local transaction to back up to disk
- pending map[common.Address]*txList // All currently processable transactions
- queue map[common.Address]*txList // Queued but non-processable transactions
+ pending map[common.Address]*list // All currently processable transactions
+ queue map[common.Address]*list // Queued but non-processable transactions
beats map[common.Address]time.Time // Last heartbeat from each known account
- all *txLookup // All transactions to allow lookups
- priced *txPricedList // All transactions sorted by price
+ all *lookup // All transactions to allow lookups
+ priced *pricedList // All transactions sorted by price
- chainHeadCh chan ChainHeadEvent
+ chainHeadCh chan core.ChainHeadEvent
chainHeadSub event.Subscription
reqResetCh chan *txpoolResetRequest
reqPromoteCh chan *accountSet
@@ -276,7 +280,7 @@ type txpoolResetRequest struct {
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
// transactions from the network.
-func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
+func NewTxPool(config Config, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
// Sanitize the input to ensure no vulnerable gas prices are set
config = (&config).sanitize()
@@ -286,11 +290,11 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
chainconfig: chainconfig,
chain: chain,
signer: types.LatestSigner(chainconfig),
- pending: make(map[common.Address]*txList),
- queue: make(map[common.Address]*txList),
+ pending: make(map[common.Address]*list),
+ queue: make(map[common.Address]*list),
beats: make(map[common.Address]time.Time),
- all: newTxLookup(),
- chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
+ all: newLookup(),
+ chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
reqResetCh: make(chan *txpoolResetRequest),
reqPromoteCh: make(chan *accountSet),
queueTxEventCh: make(chan *types.Transaction),
@@ -304,7 +308,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
log.Info("Setting new local account", "address", addr)
pool.locals.add(addr)
}
- pool.priced = newTxPricedList(pool.all)
+ pool.priced = newPricedList(pool.all)
pool.reset(nil, chain.CurrentBlock().Header())
// Start the reorg loop early so it can handle requests generated during journal loading.
@@ -427,7 +431,7 @@ func (pool *TxPool) Stop() {
// SubscribeNewTxsEvent registers a subscription of NewTxsEvent and
// starts sending event to the given channel.
-func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- NewTxsEvent) event.Subscription {
+func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return pool.scope.Track(pool.txFeed.Subscribe(ch))
}
@@ -498,11 +502,11 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common
pool.mu.Lock()
defer pool.mu.Unlock()
- pending := make(map[common.Address]types.Transactions)
+ pending := make(map[common.Address]types.Transactions, len(pool.pending))
for addr, list := range pool.pending {
pending[addr] = list.Flatten()
}
- queued := make(map[common.Address]types.Transactions)
+ queued := make(map[common.Address]types.Transactions, len(pool.queue))
for addr, list := range pool.queue {
queued[addr] = list.Flatten()
}
@@ -586,16 +590,20 @@ func (pool *TxPool) local() map[common.Address]types.Transactions {
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
// Accept only legacy transactions until EIP-2718/2930 activates.
if !pool.eip2718 && tx.Type() != types.LegacyTxType {
- return ErrTxTypeNotSupported
+ return core.ErrTxTypeNotSupported
}
// Reject dynamic fee transactions until EIP-1559 activates.
if !pool.eip1559 && tx.Type() == types.DynamicFeeTxType {
- return ErrTxTypeNotSupported
+ return core.ErrTxTypeNotSupported
}
// Reject transactions over defined size to prevent DOS attacks
- if uint64(tx.Size()) > txMaxSize {
+ if tx.Size() > txMaxSize {
return ErrOversizedData
}
+ // Check whether the init code size has been exceeded.
+ if pool.shanghai && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize {
+ return fmt.Errorf("%w: code size %v limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize)
+ }
// Transactions can't be negative. This may never happen using RLP decoded
// transactions but may occur if you create a transaction using the RPC.
if tx.Value().Sign() < 0 {
@@ -607,14 +615,14 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
}
// Sanity check for extremely large numbers
if tx.GasFeeCap().BitLen() > 256 {
- return ErrFeeCapVeryHigh
+ return core.ErrFeeCapVeryHigh
}
if tx.GasTipCap().BitLen() > 256 {
- return ErrTipVeryHigh
+ return core.ErrTipVeryHigh
}
// Ensure gasFeeCap is greater than or equal to gasTipCap.
if tx.GasFeeCapIntCmp(tx.GasTipCap()) < 0 {
- return ErrTipAboveFeeCap
+ return core.ErrTipAboveFeeCap
}
// Make sure the transaction is signed properly.
from, err := types.Sender(pool.signer, tx)
@@ -627,20 +635,20 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
}
// Ensure the transaction adheres to nonce ordering
if pool.currentState.GetNonce(from) > tx.Nonce() {
- return ErrNonceTooLow
+ return core.ErrNonceTooLow
}
// Transactor should have enough funds to cover the costs
// cost == V + GP * GL
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
- return ErrInsufficientFunds
+ return core.ErrInsufficientFunds
}
// Ensure the transaction has more gas than the basic tx fee.
- intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul)
+ intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
if err != nil {
return err
}
if tx.Gas() < intrGas {
- return ErrIntrinsicGas
+ return core.ErrIntrinsicGas
}
return nil
}
@@ -759,7 +767,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local boo
// Try to insert the transaction into the future queue
from, _ := types.Sender(pool.signer, tx) // already validated
if pool.queue[from] == nil {
- pool.queue[from] = newTxList(false)
+ pool.queue[from] = newList(false)
}
inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump)
if !inserted {
@@ -811,7 +819,7 @@ func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) {
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool {
// Try to insert the transaction into the pending queue
if pool.pending[addr] == nil {
- pool.pending[addr] = newTxList(true)
+ pool.pending[addr] = newList(true)
}
list := pool.pending[addr]
@@ -850,7 +858,7 @@ func (pool *TxPool) AddLocals(txs []*types.Transaction) []error {
}
// AddLocal enqueues a single local transaction into the pool if it is valid. This is
-// a convenience wrapper aroundd AddLocals.
+// a convenience wrapper around AddLocals.
func (pool *TxPool) AddLocal(tx *types.Transaction) error {
errs := pool.AddLocals([]*types.Transaction{tx})
return errs[0]
@@ -865,7 +873,7 @@ func (pool *TxPool) AddRemotes(txs []*types.Transaction) []error {
return pool.addTxs(txs, false, false)
}
-// This is like AddRemotes, but waits for pool reorganization. Tests use this method.
+// AddRemotesSync is like AddRemotes, but waits for pool reorganization. Tests use this method.
func (pool *TxPool) AddRemotesSync(txs []*types.Transaction) []error {
return pool.addTxs(txs, false, true)
}
@@ -1078,7 +1086,7 @@ func (pool *TxPool) scheduleReorgLoop() {
launchNextRun bool
reset *txpoolResetRequest
dirtyAccounts *accountSet
- queuedEvents = make(map[common.Address]*txSortedMap)
+ queuedEvents = make(map[common.Address]*sortedMap)
)
for {
// Launch next background reorg if needed
@@ -1091,7 +1099,7 @@ func (pool *TxPool) scheduleReorgLoop() {
launchNextRun = false
reset, dirtyAccounts = nil, nil
- queuedEvents = make(map[common.Address]*txSortedMap)
+ queuedEvents = make(map[common.Address]*sortedMap)
}
select {
@@ -1120,7 +1128,7 @@ func (pool *TxPool) scheduleReorgLoop() {
// request one later if they want the events sent.
addr, _ := types.Sender(pool.signer, tx)
if _, ok := queuedEvents[addr]; !ok {
- queuedEvents[addr] = newTxSortedMap()
+ queuedEvents[addr] = newSortedMap()
}
queuedEvents[addr].Put(tx)
@@ -1139,7 +1147,7 @@ func (pool *TxPool) scheduleReorgLoop() {
}
// runReorg runs reset and promoteExecutables on behalf of scheduleReorgLoop.
-func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirtyAccounts *accountSet, events map[common.Address]*txSortedMap) {
+func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirtyAccounts *accountSet, events map[common.Address]*sortedMap) {
defer func(t0 time.Time) {
reorgDurationTimer.Update(time.Since(t0))
}(time.Now())
@@ -1202,7 +1210,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
for _, tx := range promoted {
addr, _ := types.Sender(pool.signer, tx)
if _, ok := events[addr]; !ok {
- events[addr] = newTxSortedMap()
+ events[addr] = newSortedMap()
}
events[addr].Put(tx)
}
@@ -1211,7 +1219,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
for _, set := range events {
txs = append(txs, set.Flatten()...)
}
- pool.txFeed.Send(NewTxsEvent{txs})
+ pool.txFeed.Send(core.NewTxsEvent{Txs: txs})
}
}
@@ -1238,7 +1246,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
if rem == nil {
// This can happen if a setHead is performed, where we simply discard the old
// head from the chain.
- // If that is the case, we don't have the lost transactions any more, and
+ // If that is the case, we don't have the lost transactions anymore, and
// there's nothing to add
if newNum >= oldNum {
// If we reorged to a same or higher number, then it's not a case of setHead
@@ -1291,12 +1299,12 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
return
}
pool.currentState = statedb
- pool.pendingNonces = newTxNoncer(statedb)
+ pool.pendingNonces = newNoncer(statedb)
pool.currentMaxGas = newHead.GasLimit
// Inject any transactions discarded due to reorgs
log.Debug("Reinjecting stale transactions", "count", len(reinject))
- senderCacher.recover(pool.signer, reinject)
+ core.SenderCacher.Recover(pool.signer, reinject)
pool.addTxsLocked(reinject, false)
// Update all fork indicator by next pending block number.
@@ -1304,6 +1312,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
pool.istanbul = pool.chainconfig.IsIstanbul(next)
pool.eip2718 = pool.chainconfig.IsBerlin(next)
pool.eip1559 = pool.chainconfig.IsLondon(next)
+ pool.shanghai = pool.chainconfig.IsShanghai(uint64(time.Now().Unix()))
}
// promoteExecutables moves transactions that have become processable from the
@@ -1386,7 +1395,7 @@ func (pool *TxPool) truncatePending() {
pendingBeforeCap := pending
// Assemble a spam order to penalize large transactors first
- spammers := prque.New(nil)
+ spammers := prque.New[int64, common.Address](nil)
for addr, list := range pool.pending {
// Only evict transactions from high rollers
if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots {
@@ -1398,12 +1407,12 @@ func (pool *TxPool) truncatePending() {
for pending > pool.config.GlobalSlots && !spammers.Empty() {
// Retrieve the next offender if not local address
offender, _ := spammers.Pop()
- offenders = append(offenders, offender.(common.Address))
+ offenders = append(offenders, offender)
// Equalize balances until all the same or below threshold
if len(offenders) > 1 {
// Calculate the equalization threshold for all current offenders
- threshold := pool.pending[offender.(common.Address)].Len()
+ threshold := pool.pending[offender].Len()
// Iteratively reduce all offenders until below limit or threshold reached
for pending > pool.config.GlobalSlots && pool.pending[offenders[len(offenders)-2]].Len() > threshold {
@@ -1554,8 +1563,6 @@ func (pool *TxPool) demoteUnexecutables() {
pool.enqueueTx(hash, tx, false, false)
}
pendingGauge.Dec(int64(len(gapped)))
- // This might happen in a reorg, so log it to the metering
- blockReorgInvalidatedTx.Mark(int64(len(gapped)))
}
// Delete the entire pending entry if it became empty.
if list.Empty() {
@@ -1588,7 +1595,7 @@ type accountSet struct {
// derivations.
func newAccountSet(signer types.Signer, addrs ...common.Address) *accountSet {
as := &accountSet{
- accounts: make(map[common.Address]struct{}),
+ accounts: make(map[common.Address]struct{}, len(addrs)),
signer: signer,
}
for _, addr := range addrs {
@@ -1646,7 +1653,7 @@ func (as *accountSet) merge(other *accountSet) {
as.cache = nil
}
-// txLookup is used internally by TxPool to track transactions while allowing
+// lookup is used internally by TxPool to track transactions while allowing
// lookup without mutex contention.
//
// Note, although this type is properly protected against concurrent access, it
@@ -1658,16 +1665,16 @@ func (as *accountSet) merge(other *accountSet) {
//
// This lookup set combines the notion of "local transactions", which is useful
// to build upper-level structure.
-type txLookup struct {
+type lookup struct {
slots int
lock sync.RWMutex
locals map[common.Hash]*types.Transaction
remotes map[common.Hash]*types.Transaction
}
-// newTxLookup returns a new txLookup structure.
-func newTxLookup() *txLookup {
- return &txLookup{
+// newLookup returns a new lookup structure.
+func newLookup() *lookup {
+ return &lookup{
locals: make(map[common.Hash]*types.Transaction),
remotes: make(map[common.Hash]*types.Transaction),
}
@@ -1676,7 +1683,7 @@ func newTxLookup() *txLookup {
// Range calls f on each key and value present in the map. The callback passed
// should return the indicator whether the iteration needs to be continued.
// Callers need to specify which set (or both) to be iterated.
-func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction, local bool) bool, local bool, remote bool) {
+func (t *lookup) Range(f func(hash common.Hash, tx *types.Transaction, local bool) bool, local bool, remote bool) {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1697,7 +1704,7 @@ func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction, local b
}
// Get returns a transaction if it exists in the lookup, or nil if not found.
-func (t *txLookup) Get(hash common.Hash) *types.Transaction {
+func (t *lookup) Get(hash common.Hash) *types.Transaction {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1708,7 +1715,7 @@ func (t *txLookup) Get(hash common.Hash) *types.Transaction {
}
// GetLocal returns a transaction if it exists in the lookup, or nil if not found.
-func (t *txLookup) GetLocal(hash common.Hash) *types.Transaction {
+func (t *lookup) GetLocal(hash common.Hash) *types.Transaction {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1716,7 +1723,7 @@ func (t *txLookup) GetLocal(hash common.Hash) *types.Transaction {
}
// GetRemote returns a transaction if it exists in the lookup, or nil if not found.
-func (t *txLookup) GetRemote(hash common.Hash) *types.Transaction {
+func (t *lookup) GetRemote(hash common.Hash) *types.Transaction {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1724,7 +1731,7 @@ func (t *txLookup) GetRemote(hash common.Hash) *types.Transaction {
}
// Count returns the current number of transactions in the lookup.
-func (t *txLookup) Count() int {
+func (t *lookup) Count() int {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1732,7 +1739,7 @@ func (t *txLookup) Count() int {
}
// LocalCount returns the current number of local transactions in the lookup.
-func (t *txLookup) LocalCount() int {
+func (t *lookup) LocalCount() int {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1740,7 +1747,7 @@ func (t *txLookup) LocalCount() int {
}
// RemoteCount returns the current number of remote transactions in the lookup.
-func (t *txLookup) RemoteCount() int {
+func (t *lookup) RemoteCount() int {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1748,7 +1755,7 @@ func (t *txLookup) RemoteCount() int {
}
// Slots returns the current number of slots used in the lookup.
-func (t *txLookup) Slots() int {
+func (t *lookup) Slots() int {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1756,7 +1763,7 @@ func (t *txLookup) Slots() int {
}
// Add adds a transaction to the lookup.
-func (t *txLookup) Add(tx *types.Transaction, local bool) {
+func (t *lookup) Add(tx *types.Transaction, local bool) {
t.lock.Lock()
defer t.lock.Unlock()
@@ -1771,7 +1778,7 @@ func (t *txLookup) Add(tx *types.Transaction, local bool) {
}
// Remove removes a transaction from the lookup.
-func (t *txLookup) Remove(hash common.Hash) {
+func (t *lookup) Remove(hash common.Hash) {
t.lock.Lock()
defer t.lock.Unlock()
@@ -1792,7 +1799,7 @@ func (t *txLookup) Remove(hash common.Hash) {
// RemoteToLocals migrates the transactions belongs to the given locals to locals
// set. The assumption is held the locals set is thread-safe to be used.
-func (t *txLookup) RemoteToLocals(locals *accountSet) int {
+func (t *lookup) RemoteToLocals(locals *accountSet) int {
t.lock.Lock()
defer t.lock.Unlock()
@@ -1808,7 +1815,7 @@ func (t *txLookup) RemoteToLocals(locals *accountSet) int {
}
// RemotesBelowTip finds all remote transactions below the given tip threshold.
-func (t *txLookup) RemotesBelowTip(threshold *big.Int) types.Transactions {
+func (t *lookup) RemotesBelowTip(threshold *big.Int) types.Transactions {
found := make(types.Transactions, 0, 128)
t.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool {
if tx.GasTipCapIntCmp(threshold) < 0 {
diff --git a/core/tx_pool_test.go b/core/txpool/txpool_test.go
similarity index 91%
rename from core/tx_pool_test.go
rename to core/txpool/txpool_test.go
index 2fd0f529f8..bd82622f8d 100644
--- a/core/tx_pool_test.go
+++ b/core/txpool/txpool_test.go
@@ -14,10 +14,11 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"crypto/ecdsa"
+ crand "crypto/rand"
"errors"
"fmt"
"math/big"
@@ -28,6 +29,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
@@ -40,14 +42,14 @@ import (
var (
// testTxPoolConfig is a transaction pool configuration without stateful disk
// sideeffects used during testing.
- testTxPoolConfig TxPoolConfig
+ testTxPoolConfig Config
// eip1559Config is a chain config with EIP-1559 enabled at block 0.
eip1559Config *params.ChainConfig
)
func init() {
- testTxPoolConfig = DefaultTxPoolConfig
+ testTxPoolConfig = DefaultConfig
testTxPoolConfig.Journal = ""
cpy := *params.TestChainConfig
@@ -76,7 +78,7 @@ func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) {
return bc.statedb, nil
}
-func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription {
+func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
return bc.chainHeadFeed.Subscribe(ch)
}
@@ -91,7 +93,7 @@ func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ec
func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey, bytes uint64) *types.Transaction {
data := make([]byte, bytes)
- rand.Read(data)
+ crand.Read(data)
tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gaslimit, gasprice, data), types.HomesteadSigner{}, key)
return tx
@@ -112,11 +114,11 @@ func dynamicFeeTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int,
return tx
}
-func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
- return setupTxPoolWithConfig(params.TestChainConfig)
+func setupPool() (*TxPool, *ecdsa.PrivateKey) {
+ return setupPoolWithConfig(params.TestChainConfig)
}
-func setupTxPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey) {
+func setupPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey) {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
blockchain := &testBlockChain{10000000, statedb, new(event.Feed)}
@@ -128,8 +130,8 @@ func setupTxPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateK
return pool, key
}
-// validateTxPoolInternals checks various consistency invariants within the pool.
-func validateTxPoolInternals(pool *TxPool) error {
+// validatePoolInternals checks various consistency invariants within the pool.
+func validatePoolInternals(pool *TxPool) error {
pool.mu.RLock()
defer pool.mu.RUnlock()
@@ -161,7 +163,7 @@ func validateTxPoolInternals(pool *TxPool) error {
// validateEvents checks that the correct number of transaction addition events
// were fired on the pool's event feed.
-func validateEvents(events chan NewTxsEvent, count int) error {
+func validateEvents(events chan core.NewTxsEvent, count int) error {
var received []*types.Transaction
for len(received) < count {
@@ -218,7 +220,7 @@ func (c *testChain) State() (*state.StateDB, error) {
// This test simulates a scenario where a new block is imported during a
// state reset and tests whether the pending state is in sync with the
// block head event that initiated the resetState().
-func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
+func TestStateChangeDuringReset(t *testing.T) {
t.Parallel()
var (
@@ -275,28 +277,28 @@ func testSetNonce(pool *TxPool, addr common.Address, nonce uint64) {
func TestInvalidTransactions(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
tx := transaction(0, 100, key)
from, _ := deriveSender(tx)
testAddBalance(pool, from, big.NewInt(1))
- if err := pool.AddRemote(tx); !errors.Is(err, ErrInsufficientFunds) {
- t.Error("expected", ErrInsufficientFunds)
+ if err := pool.AddRemote(tx); !errors.Is(err, core.ErrInsufficientFunds) {
+ t.Error("expected", core.ErrInsufficientFunds)
}
balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice()))
testAddBalance(pool, from, balance)
- if err := pool.AddRemote(tx); !errors.Is(err, ErrIntrinsicGas) {
- t.Error("expected", ErrIntrinsicGas, "got", err)
+ if err := pool.AddRemote(tx); !errors.Is(err, core.ErrIntrinsicGas) {
+ t.Error("expected", core.ErrIntrinsicGas, "got", err)
}
testSetNonce(pool, from, 1)
testAddBalance(pool, from, big.NewInt(0xffffffffffffff))
tx = transaction(0, 100000, key)
- if err := pool.AddRemote(tx); !errors.Is(err, ErrNonceTooLow) {
- t.Error("expected", ErrNonceTooLow)
+ if err := pool.AddRemote(tx); !errors.Is(err, core.ErrNonceTooLow) {
+ t.Error("expected", core.ErrNonceTooLow)
}
tx = transaction(1, 100000, key)
@@ -309,10 +311,10 @@ func TestInvalidTransactions(t *testing.T) {
}
}
-func TestTransactionQueue(t *testing.T) {
+func TestQueue(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
tx := transaction(0, 100, key)
@@ -340,10 +342,10 @@ func TestTransactionQueue(t *testing.T) {
}
}
-func TestTransactionQueue2(t *testing.T) {
+func TestQueue2(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
tx1 := transaction(0, 100, key)
@@ -366,10 +368,10 @@ func TestTransactionQueue2(t *testing.T) {
}
}
-func TestTransactionNegativeValue(t *testing.T) {
+func TestNegativeValue(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key)
@@ -380,43 +382,43 @@ func TestTransactionNegativeValue(t *testing.T) {
}
}
-func TestTransactionTipAboveFeeCap(t *testing.T) {
+func TestTipAboveFeeCap(t *testing.T) {
t.Parallel()
- pool, key := setupTxPoolWithConfig(eip1559Config)
+ pool, key := setupPoolWithConfig(eip1559Config)
defer pool.Stop()
tx := dynamicFeeTx(0, 100, big.NewInt(1), big.NewInt(2), key)
- if err := pool.AddRemote(tx); err != ErrTipAboveFeeCap {
- t.Error("expected", ErrTipAboveFeeCap, "got", err)
+ if err := pool.AddRemote(tx); err != core.ErrTipAboveFeeCap {
+ t.Error("expected", core.ErrTipAboveFeeCap, "got", err)
}
}
-func TestTransactionVeryHighValues(t *testing.T) {
+func TestVeryHighValues(t *testing.T) {
t.Parallel()
- pool, key := setupTxPoolWithConfig(eip1559Config)
+ pool, key := setupPoolWithConfig(eip1559Config)
defer pool.Stop()
veryBigNumber := big.NewInt(1)
veryBigNumber.Lsh(veryBigNumber, 300)
tx := dynamicFeeTx(0, 100, big.NewInt(1), veryBigNumber, key)
- if err := pool.AddRemote(tx); err != ErrTipVeryHigh {
- t.Error("expected", ErrTipVeryHigh, "got", err)
+ if err := pool.AddRemote(tx); err != core.ErrTipVeryHigh {
+ t.Error("expected", core.ErrTipVeryHigh, "got", err)
}
tx2 := dynamicFeeTx(0, 100, veryBigNumber, big.NewInt(1), key)
- if err := pool.AddRemote(tx2); err != ErrFeeCapVeryHigh {
- t.Error("expected", ErrFeeCapVeryHigh, "got", err)
+ if err := pool.AddRemote(tx2); err != core.ErrFeeCapVeryHigh {
+ t.Error("expected", core.ErrFeeCapVeryHigh, "got", err)
}
}
-func TestTransactionChainFork(t *testing.T) {
+func TestChainFork(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
@@ -442,10 +444,10 @@ func TestTransactionChainFork(t *testing.T) {
}
}
-func TestTransactionDoubleNonce(t *testing.T) {
+func TestDoubleNonce(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
@@ -493,10 +495,10 @@ func TestTransactionDoubleNonce(t *testing.T) {
}
}
-func TestTransactionMissingNonce(t *testing.T) {
+func TestMissingNonce(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
@@ -516,11 +518,11 @@ func TestTransactionMissingNonce(t *testing.T) {
}
}
-func TestTransactionNonceRecovery(t *testing.T) {
+func TestNonceRecovery(t *testing.T) {
t.Parallel()
const n = 10
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
@@ -542,11 +544,11 @@ func TestTransactionNonceRecovery(t *testing.T) {
// Tests that if an account runs out of funds, any pending and queued transactions
// are dropped.
-func TestTransactionDropping(t *testing.T) {
+func TestDropping(t *testing.T) {
t.Parallel()
// Create a test account and fund it
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@@ -646,7 +648,7 @@ func TestTransactionDropping(t *testing.T) {
// Tests that if a transaction is dropped from the current pending pool (e.g. out
// of fund), all consecutive (still valid, but not executable) transactions are
// postponed back into the future queue to prevent broadcasting them.
-func TestTransactionPostponing(t *testing.T) {
+func TestPostponing(t *testing.T) {
t.Parallel()
// Create the pool to test the postponing with
@@ -759,18 +761,18 @@ func TestTransactionPostponing(t *testing.T) {
// Tests that if the transaction pool has both executable and non-executable
// transactions from an origin account, filling the nonce gap moves all queued
// ones into the pending pool.
-func TestTransactionGapFilling(t *testing.T) {
+func TestGapFilling(t *testing.T) {
t.Parallel()
// Create a test account and fund it
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
testAddBalance(pool, account, big.NewInt(1000000))
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue+5)
+ events := make(chan core.NewTxsEvent, testTxPoolConfig.AccountQueue+5)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -789,7 +791,7 @@ func TestTransactionGapFilling(t *testing.T) {
if err := validateEvents(events, 1); err != nil {
t.Fatalf("original event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Fill the nonce gap and ensure all transactions become pending
@@ -806,18 +808,18 @@ func TestTransactionGapFilling(t *testing.T) {
if err := validateEvents(events, 2); err != nil {
t.Fatalf("gap-filling event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that if the transaction count belonging to a single account goes above
// some threshold, the higher transactions are dropped to prevent DOS attacks.
-func TestTransactionQueueAccountLimiting(t *testing.T) {
+func TestQueueAccountLimiting(t *testing.T) {
t.Parallel()
// Create a test account and fund it
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@@ -851,14 +853,14 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
//
// This logic should not hold for local transactions, unless the local tracking
// mechanism is disabled.
-func TestTransactionQueueGlobalLimiting(t *testing.T) {
- testTransactionQueueGlobalLimiting(t, false)
+func TestQueueGlobalLimiting(t *testing.T) {
+ testQueueGlobalLimiting(t, false)
}
-func TestTransactionQueueGlobalLimitingNoLocals(t *testing.T) {
- testTransactionQueueGlobalLimiting(t, true)
+func TestQueueGlobalLimitingNoLocals(t *testing.T) {
+ testQueueGlobalLimiting(t, true)
}
-func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
+func testQueueGlobalLimiting(t *testing.T, nolocals bool) {
t.Parallel()
// Create the pool to test the limit enforcement with
@@ -941,14 +943,14 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
//
// This logic should not hold for local transactions, unless the local tracking
// mechanism is disabled.
-func TestTransactionQueueTimeLimiting(t *testing.T) {
- testTransactionQueueTimeLimiting(t, false)
+func TestQueueTimeLimiting(t *testing.T) {
+ testQueueTimeLimiting(t, false)
}
-func TestTransactionQueueTimeLimitingNoLocals(t *testing.T) {
- testTransactionQueueTimeLimiting(t, true)
+func TestQueueTimeLimitingNoLocals(t *testing.T) {
+ testQueueTimeLimiting(t, true)
}
-func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
+func testQueueTimeLimiting(t *testing.T, nolocals bool) {
// Reduce the eviction interval to a testable amount
defer func(old time.Duration) { evictionInterval = old }(evictionInterval)
evictionInterval = time.Millisecond * 100
@@ -985,7 +987,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -1000,7 +1002,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -1020,7 +1022,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -1037,7 +1039,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -1067,7 +1069,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -1086,7 +1088,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1094,18 +1096,18 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
// Tests that even if the transaction count belonging to a single account goes
// above some threshold, as long as the transactions are executable, they are
// accepted.
-func TestTransactionPendingLimiting(t *testing.T) {
+func TestPendingLimiting(t *testing.T) {
t.Parallel()
// Create a test account and fund it
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
testAddBalance(pool, account, big.NewInt(1000000))
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue+5)
+ events := make(chan core.NewTxsEvent, testTxPoolConfig.AccountQueue+5)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -1127,7 +1129,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
if err := validateEvents(events, int(testTxPoolConfig.AccountQueue+5)); err != nil {
t.Fatalf("event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1135,7 +1137,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
// Tests that if the transaction count belonging to multiple accounts go above
// some hard threshold, the higher transactions are dropped to prevent DOS
// attacks.
-func TestTransactionPendingGlobalLimiting(t *testing.T) {
+func TestPendingGlobalLimiting(t *testing.T) {
t.Parallel()
// Create the pool to test the limit enforcement with
@@ -1175,7 +1177,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
if pending > int(config.GlobalSlots) {
t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, config.GlobalSlots)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1183,11 +1185,11 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
// Test the limit on transaction size is enforced correctly.
// This test verifies every transaction having allowed size
// is added to the pool, and longer transactions are rejected.
-func TestTransactionAllowedTxSize(t *testing.T) {
+func TestAllowedTxSize(t *testing.T) {
t.Parallel()
// Create a test account and fund it
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@@ -1231,13 +1233,13 @@ func TestTransactionAllowedTxSize(t *testing.T) {
if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that if transactions start being capped, transactions are also removed from 'all'
-func TestTransactionCapClearsFromAll(t *testing.T) {
+func TestCapClearsFromAll(t *testing.T) {
t.Parallel()
// Create the pool to test the limit enforcement with
@@ -1263,7 +1265,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
}
// Import the batch and verify that limits have been enforced
pool.AddRemotes(txs)
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1271,7 +1273,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
// Tests that if the transaction count belonging to multiple accounts go above
// some hard threshold, if they are under the minimum guaranteed slot count then
// the transactions are still kept.
-func TestTransactionPendingMinimumAllowance(t *testing.T) {
+func TestPendingMinimumAllowance(t *testing.T) {
t.Parallel()
// Create the pool to test the limit enforcement with
@@ -1309,7 +1311,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), config.AccountSlots)
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1319,7 +1321,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
// from the pending pool to the queue.
//
// Note, local transactions are never allowed to be dropped.
-func TestTransactionPoolRepricing(t *testing.T) {
+func TestRepricing(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -1330,7 +1332,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
defer pool.Stop()
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -1371,7 +1373,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateEvents(events, 7); err != nil {
t.Fatalf("original event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Reprice the pool and check that underpriced transactions get dropped
@@ -1387,7 +1389,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateEvents(events, 0); err != nil {
t.Fatalf("reprice event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Check that we can't add the old transactions back
@@ -1403,7 +1405,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateEvents(events, 0); err != nil {
t.Fatalf("post-reprice event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// However we can add local underpriced transactions
@@ -1417,7 +1419,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateEvents(events, 1); err != nil {
t.Fatalf("post-reprice local event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// And we can fill gaps with properly priced transactions
@@ -1433,7 +1435,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateEvents(events, 5); err != nil {
t.Fatalf("post-reprice event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1443,15 +1445,15 @@ func TestTransactionPoolRepricing(t *testing.T) {
// gapped transactions back from the pending pool to the queue.
//
// Note, local transactions are never allowed to be dropped.
-func TestTransactionPoolRepricingDynamicFee(t *testing.T) {
+func TestRepricingDynamicFee(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
- pool, _ := setupTxPoolWithConfig(eip1559Config)
+ pool, _ := setupPoolWithConfig(eip1559Config)
defer pool.Stop()
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -1492,7 +1494,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) {
if err := validateEvents(events, 7); err != nil {
t.Fatalf("original event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Reprice the pool and check that underpriced transactions get dropped
@@ -1508,7 +1510,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) {
if err := validateEvents(events, 0); err != nil {
t.Fatalf("reprice event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Check that we can't add the old transactions back
@@ -1527,7 +1529,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) {
if err := validateEvents(events, 0); err != nil {
t.Fatalf("post-reprice event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// However we can add local underpriced transactions
@@ -1541,7 +1543,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) {
if err := validateEvents(events, 1); err != nil {
t.Fatalf("post-reprice local event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// And we can fill gaps with properly priced transactions
@@ -1560,14 +1562,14 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) {
if err := validateEvents(events, 5); err != nil {
t.Fatalf("post-reprice event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that setting the transaction pool gas price to a higher value does not
// remove local transactions (legacy & dynamic fee).
-func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
+func TestRepricingKeepsLocals(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -1618,7 +1620,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, expQueued)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1640,7 +1642,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
// pending transactions are moved into the queue.
//
// Note, local transactions are never allowed to be dropped.
-func TestTransactionPoolUnderpricing(t *testing.T) {
+func TestUnderpricing(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -1655,7 +1657,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
defer pool.Stop()
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -1689,7 +1691,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
if err := validateEvents(events, 3); err != nil {
t.Fatalf("original event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding an underpriced transaction on block limit fails
@@ -1716,7 +1718,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
if err := validateEvents(events, 1); err != nil {
t.Fatalf("additional event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding local transactions can push out even higher priced ones
@@ -1738,7 +1740,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
if err := validateEvents(events, 2); err != nil {
t.Fatalf("local event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1746,7 +1748,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
// Tests that more expensive transactions push out cheap ones from the pool, but
// without producing instability by creating gaps that start jumping transactions
// back and forth between queued/pending.
-func TestTransactionPoolStableUnderpricing(t *testing.T) {
+func TestStableUnderpricing(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -1761,7 +1763,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
defer pool.Stop()
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -1788,7 +1790,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
if err := validateEvents(events, int(config.GlobalSlots)); err != nil {
t.Fatalf("original event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding high priced transactions drops a cheap, but doesn't produce a gap
@@ -1805,7 +1807,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
if err := validateEvents(events, 1); err != nil {
t.Fatalf("additional event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1815,17 +1817,17 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
// expensive ones and any gapped pending transactions are moved into the queue.
//
// Note, local transactions are never allowed to be dropped.
-func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
+func TestUnderpricingDynamicFee(t *testing.T) {
t.Parallel()
- pool, _ := setupTxPoolWithConfig(eip1559Config)
+ pool, _ := setupPoolWithConfig(eip1559Config)
defer pool.Stop()
pool.config.GlobalSlots = 2
pool.config.GlobalQueue = 2
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -1859,7 +1861,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
if err := validateEvents(events, 3); err != nil {
t.Fatalf("original event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -1893,7 +1895,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
if err := validateEvents(events, 1); err != nil {
t.Fatalf("additional event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding local transactions can push out even higher priced ones
@@ -1915,7 +1917,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
if err := validateEvents(events, 2); err != nil {
t.Fatalf("local event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1925,7 +1927,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
func TestDualHeapEviction(t *testing.T) {
t.Parallel()
- pool, _ := setupTxPoolWithConfig(eip1559Config)
+ pool, _ := setupPoolWithConfig(eip1559Config)
defer pool.Stop()
pool.config.GlobalSlots = 10
@@ -1972,13 +1974,13 @@ func TestDualHeapEviction(t *testing.T) {
check(highTip, "effective tip")
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that the pool rejects duplicate transactions.
-func TestTransactionDeduplication(t *testing.T) {
+func TestDeduplication(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -2037,14 +2039,14 @@ func TestTransactionDeduplication(t *testing.T) {
if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that the pool rejects replacement transactions that don't meet the minimum
// price bump required.
-func TestTransactionReplacement(t *testing.T) {
+func TestReplacement(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -2055,7 +2057,7 @@ func TestTransactionReplacement(t *testing.T) {
defer pool.Stop()
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -2117,23 +2119,23 @@ func TestTransactionReplacement(t *testing.T) {
if err := validateEvents(events, 0); err != nil {
t.Fatalf("queued replacement event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that the pool rejects replacement dynamic fee transactions that don't
// meet the minimum price bump required.
-func TestTransactionReplacementDynamicFee(t *testing.T) {
+func TestReplacementDynamicFee(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
- pool, key := setupTxPoolWithConfig(eip1559Config)
+ pool, key := setupPoolWithConfig(eip1559Config)
defer pool.Stop()
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -2158,7 +2160,7 @@ func TestTransactionReplacementDynamicFee(t *testing.T) {
stages := []string{"pending", "queued"}
for _, stage := range stages {
// Since state is empty, 0 nonce txs are "executable" and can go
- // into pending immediately. 2 nonce txs are "happed
+ // into pending immediately. 2 nonce txs are "gapped"
nonce := uint64(0)
if stage == "queued" {
nonce = 2
@@ -2227,17 +2229,17 @@ func TestTransactionReplacementDynamicFee(t *testing.T) {
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that local transactions are journaled to disk, but remote transactions
// get discarded between restarts.
-func TestTransactionJournaling(t *testing.T) { testTransactionJournaling(t, false) }
-func TestTransactionJournalingNoLocals(t *testing.T) { testTransactionJournaling(t, true) }
+func TestJournaling(t *testing.T) { testJournaling(t, false) }
+func TestJournalingNoLocals(t *testing.T) { testJournaling(t, true) }
-func testTransactionJournaling(t *testing.T, nolocals bool) {
+func testJournaling(t *testing.T, nolocals bool) {
t.Parallel()
// Create a temporary file for the journal
@@ -2290,7 +2292,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Terminate the old pool, bump the local nonce, create a new pool and ensure relevant transaction survive
@@ -2313,7 +2315,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Bump the nonce temporarily and ensure the newly invalidated transaction is removed
@@ -2339,15 +2341,15 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
pool.Stop()
}
-// TestTransactionStatusCheck tests that the pool can correctly retrieve the
+// TestStatusCheck tests that the pool can correctly retrieve the
// pending status of individual transactions.
-func TestTransactionStatusCheck(t *testing.T) {
+func TestStatusCheck(t *testing.T) {
t.Parallel()
// Create the pool to test the status retrievals with
@@ -2381,7 +2383,7 @@ func TestTransactionStatusCheck(t *testing.T) {
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Retrieve the status of each transaction and validate them
@@ -2402,7 +2404,7 @@ func TestTransactionStatusCheck(t *testing.T) {
}
// Test the transaction slots consumption is computed correctly
-func TestTransactionSlotCount(t *testing.T) {
+func TestSlotCount(t *testing.T) {
t.Parallel()
key, _ := crypto.GenerateKey()
@@ -2427,7 +2429,7 @@ func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 1
func benchmarkPendingDemotion(b *testing.B, size int) {
// Add a batch of transactions to a pool one by one
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@@ -2452,7 +2454,7 @@ func BenchmarkFuturePromotion10000(b *testing.B) { benchmarkFuturePromotion(b, 1
func benchmarkFuturePromotion(b *testing.B, size int) {
// Add a batch of transactions to a pool one by one
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@@ -2470,17 +2472,17 @@ func benchmarkFuturePromotion(b *testing.B, size int) {
}
// Benchmarks the speed of batched transaction insertion.
-func BenchmarkPoolBatchInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100, false) }
-func BenchmarkPoolBatchInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000, false) }
-func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000, false) }
+func BenchmarkBatchInsert100(b *testing.B) { benchmarkBatchInsert(b, 100, false) }
+func BenchmarkBatchInsert1000(b *testing.B) { benchmarkBatchInsert(b, 1000, false) }
+func BenchmarkBatchInsert10000(b *testing.B) { benchmarkBatchInsert(b, 10000, false) }
-func BenchmarkPoolBatchLocalInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100, true) }
-func BenchmarkPoolBatchLocalInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000, true) }
-func BenchmarkPoolBatchLocalInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000, true) }
+func BenchmarkBatchLocalInsert100(b *testing.B) { benchmarkBatchInsert(b, 100, true) }
+func BenchmarkBatchLocalInsert1000(b *testing.B) { benchmarkBatchInsert(b, 1000, true) }
+func BenchmarkBatchLocalInsert10000(b *testing.B) { benchmarkBatchInsert(b, 10000, true) }
-func benchmarkPoolBatchInsert(b *testing.B, size int, local bool) {
+func benchmarkBatchInsert(b *testing.B, size int, local bool) {
// Generate a batch of transactions to enqueue into the pool
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@@ -2524,7 +2526,7 @@ func BenchmarkInsertRemoteWithAllLocals(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
- pool, _ := setupTxPool()
+ pool, _ := setupPool()
testAddBalance(pool, account, big.NewInt(100000000))
for _, local := range locals {
pool.AddLocal(local)
@@ -2540,9 +2542,9 @@ func BenchmarkInsertRemoteWithAllLocals(b *testing.B) {
}
// Benchmarks the speed of batch transaction insertion in case of multiple accounts.
-func BenchmarkPoolMultiAccountBatchInsert(b *testing.B) {
+func BenchmarkMultiAccountBatchInsert(b *testing.B) {
// Generate a batch of transactions to enqueue into the pool
- pool, _ := setupTxPool()
+ pool, _ := setupPool()
defer pool.Stop()
b.ReportAllocs()
batches := make(types.Transactions, b.N)
diff --git a/core/types/block.go b/core/types/block.go
index 7525a88f5a..82ad3ce99c 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -31,11 +31,6 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)
-var (
- EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
- EmptyUncleHash = rlpHash([]*Header(nil))
-)
-
// A BlockNonce is a 64-bit hash which proves (combined with the
// mix-hash) that a sufficient amount of computation has been carried
// out on a block.
@@ -87,6 +82,9 @@ type Header struct {
// BaseFee was added by EIP-1559 and is ignored in legacy headers.
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
+ // WithdrawalsHash was added by EIP-4895 and is ignored in legacy headers.
+ WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
+
/*
TODO (MariusVanDerWijden) Add this field once needed
// Random was added during the merge and contains the BeaconState randomness
@@ -117,7 +115,11 @@ var headerSize = common.StorageSize(reflect.TypeOf(Header{}).Size())
// Size returns the approximate memory used by all internal contents. It is used
// to approximate and limit the memory consumption of various caches.
func (h *Header) Size() common.StorageSize {
- return headerSize + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen())/8)
+ var baseFeeBits int
+ if h.BaseFee != nil {
+ baseFeeBits = h.BaseFee.BitLen()
+ }
+ return headerSize + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+baseFeeBits)/8)
}
// SanityCheck checks a few basic things -- these checks are way beyond what
@@ -145,14 +147,17 @@ func (h *Header) SanityCheck() error {
}
// EmptyBody returns true if there is no additional 'body' to complete the header
-// that is: no transactions and no uncles.
+// that is: no transactions, no uncles and no withdrawals.
func (h *Header) EmptyBody() bool {
- return h.TxHash == EmptyRootHash && h.UncleHash == EmptyUncleHash
+ if h.WithdrawalsHash == nil {
+ return h.TxHash == EmptyTxsHash && h.UncleHash == EmptyUncleHash
+ }
+ return h.TxHash == EmptyTxsHash && h.UncleHash == EmptyUncleHash && *h.WithdrawalsHash == EmptyWithdrawalsHash
}
// EmptyReceipts returns true if there are no receipts for this header/block.
func (h *Header) EmptyReceipts() bool {
- return h.ReceiptHash == EmptyRootHash
+ return h.ReceiptHash == EmptyReceiptsHash
}
// Body is a simple (mutable, non-safe) data container for storing and moving
@@ -160,6 +165,7 @@ func (h *Header) EmptyReceipts() bool {
type Body struct {
Transactions []*Transaction
Uncles []*Header
+ Withdrawals []*Withdrawal `rlp:"optional"`
}
// Block represents an entire block in the Ethereum blockchain.
@@ -167,6 +173,7 @@ type Block struct {
header *Header
uncles []*Header
transactions Transactions
+ withdrawals Withdrawals
// caches
hash atomic.Value
@@ -180,9 +187,10 @@ type Block struct {
// "external" block encoding. used for eth protocol, etc.
type extblock struct {
- Header *Header
- Txs []*Transaction
- Uncles []*Header
+ Header *Header
+ Txs []*Transaction
+ Uncles []*Header
+ Withdrawals []*Withdrawal `rlp:"optional"`
}
// NewBlock creates a new block. The input data is copied,
@@ -197,7 +205,7 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*
// TODO: panic if len(txs) != len(receipts)
if len(txs) == 0 {
- b.header.TxHash = EmptyRootHash
+ b.header.TxHash = EmptyTxsHash
} else {
b.header.TxHash = DeriveSha(Transactions(txs), hasher)
b.transactions = make(Transactions, len(txs))
@@ -205,7 +213,7 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*
}
if len(receipts) == 0 {
- b.header.ReceiptHash = EmptyRootHash
+ b.header.ReceiptHash = EmptyReceiptsHash
} else {
b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher)
b.header.Bloom = CreateBloom(receipts)
@@ -224,6 +232,28 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*
return b
}
+// NewBlockWithWithdrawals creates a new block with withdrawals. The input data
+// is copied, changes to header and to the field values will not
+// affect the block.
+//
+// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
+// are ignored and set to values derived from the given txs, uncles
+// and receipts.
+func NewBlockWithWithdrawals(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, withdrawals []*Withdrawal, hasher TrieHasher) *Block {
+ b := NewBlock(header, txs, uncles, receipts, hasher)
+
+ if withdrawals == nil {
+ b.header.WithdrawalsHash = nil
+ } else if len(withdrawals) == 0 {
+ b.header.WithdrawalsHash = &EmptyWithdrawalsHash
+ } else {
+ h := DeriveSha(Withdrawals(withdrawals), hasher)
+ b.header.WithdrawalsHash = &h
+ }
+
+ return b.WithWithdrawals(withdrawals)
+}
+
// NewBlockWithHeader creates a block with the given header data. The
// header data is copied, changes to header and to the field values
// will not affect the block.
@@ -248,6 +278,9 @@ func CopyHeader(h *Header) *Header {
cpy.Extra = make([]byte, len(h.Extra))
copy(cpy.Extra, h.Extra)
}
+ if h.WithdrawalsHash != nil {
+ *cpy.WithdrawalsHash = *h.WithdrawalsHash
+ }
return &cpy
}
@@ -258,17 +291,18 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
if err := s.Decode(&eb); err != nil {
return err
}
- b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs
- b.size.Store(common.StorageSize(rlp.ListSize(size)))
+ b.header, b.uncles, b.transactions, b.withdrawals = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals
+ b.size.Store(rlp.ListSize(size))
return nil
}
// EncodeRLP serializes b into the Ethereum RLP block format.
func (b *Block) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, extblock{
- Header: b.header,
- Txs: b.transactions,
- Uncles: b.uncles,
+ Header: b.header,
+ Txs: b.transactions,
+ Uncles: b.uncles,
+ Withdrawals: b.withdrawals,
})
}
@@ -311,21 +345,25 @@ func (b *Block) BaseFee() *big.Int {
return new(big.Int).Set(b.header.BaseFee)
}
+func (b *Block) Withdrawals() Withdrawals {
+ return b.withdrawals
+}
+
func (b *Block) Header() *Header { return CopyHeader(b.header) }
// Body returns the non-header content of the block.
-func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} }
+func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles, b.withdrawals} }
// Size returns the true RLP encoded storage size of the block, either by encoding
// and returning it, or returning a previously cached value.
-func (b *Block) Size() common.StorageSize {
+func (b *Block) Size() uint64 {
if size := b.size.Load(); size != nil {
- return size.(common.StorageSize)
+ return size.(uint64)
}
c := writeCounter(0)
rlp.Encode(&c, b)
- b.size.Store(common.StorageSize(c))
- return common.StorageSize(c)
+ b.size.Store(uint64(c))
+ return uint64(c)
}
// SanityCheck can be used to prevent that unbounded fields are
@@ -334,7 +372,7 @@ func (b *Block) SanityCheck() error {
return b.header.SanityCheck()
}
-type writeCounter common.StorageSize
+type writeCounter uint64
func (c *writeCounter) Write(b []byte) (int, error) {
*c += writeCounter(len(b))
@@ -357,6 +395,7 @@ func (b *Block) WithSeal(header *Header) *Block {
header: &cpy,
transactions: b.transactions,
uncles: b.uncles,
+ withdrawals: b.withdrawals,
}
}
@@ -374,6 +413,15 @@ func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block {
return block
}
+// WithWithdrawals sets the withdrawal contents of a block, does not return a new block.
+func (b *Block) WithWithdrawals(withdrawals []*Withdrawal) *Block {
+ if withdrawals != nil {
+ b.withdrawals = make([]*Withdrawal, len(withdrawals))
+ copy(b.withdrawals, withdrawals)
+ }
+ return b
+}
+
// Hash returns the keccak256 hash of b's header.
// The hash is computed on the first call and cached thereafter.
func (b *Block) Hash() common.Hash {
diff --git a/core/types/block_test.go b/core/types/block_test.go
index 9e7f581b1d..49197c9237 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -53,7 +53,7 @@ func TestBlockEncoding(t *testing.T) {
check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743))
- check("Size", block.Size(), common.StorageSize(len(blockEnc)))
+ check("Size", block.Size(), uint64(len(blockEnc)))
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil)
tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
@@ -90,7 +90,7 @@ func TestEIP1559BlockEncoding(t *testing.T) {
check("Hash", block.Hash(), common.HexToHash("c7252048cd273fe0dac09650027d07f0e3da4ee0675ebbb26627cea92729c372"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743))
- check("Size", block.Size(), common.StorageSize(len(blockEnc)))
+ check("Size", block.Size(), uint64(len(blockEnc)))
check("BaseFee", block.BaseFee(), new(big.Int).SetUint64(params.InitialBaseFee))
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil)
@@ -153,7 +153,7 @@ func TestEIP2718BlockEncoding(t *testing.T) {
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743))
- check("Size", block.Size(), common.StorageSize(len(blockEnc)))
+ check("Size", block.Size(), uint64(len(blockEnc)))
// Create legacy tx.
to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go
index 74746d033a..5c8b81652d 100644
--- a/core/types/gen_header_json.go
+++ b/core/types/gen_header_json.go
@@ -16,23 +16,24 @@ var _ = (*headerMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (h Header) MarshalJSON() ([]byte, error) {
type Header struct {
- ParentHash common.Hash `json:"parentHash" gencodec:"required"`
- UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
- Coinbase common.Address `json:"miner"`
- Root common.Hash `json:"stateRoot" gencodec:"required"`
- TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
- ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
- Bloom Bloom `json:"logsBloom" gencodec:"required"`
- Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
- Number *hexutil.Big `json:"number" gencodec:"required"`
- GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
- GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
- Time hexutil.Uint64 `json:"timestamp" gencodec:"required"`
- Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
- MixDigest common.Hash `json:"mixHash"`
- Nonce BlockNonce `json:"nonce"`
- BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
- Hash common.Hash `json:"hash"`
+ ParentHash common.Hash `json:"parentHash" gencodec:"required"`
+ UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
+ Coinbase common.Address `json:"miner"`
+ Root common.Hash `json:"stateRoot" gencodec:"required"`
+ TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
+ ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
+ Bloom Bloom `json:"logsBloom" gencodec:"required"`
+ Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
+ Number *hexutil.Big `json:"number" gencodec:"required"`
+ GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
+ Time hexutil.Uint64 `json:"timestamp" gencodec:"required"`
+ Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
+ MixDigest common.Hash `json:"mixHash"`
+ Nonce BlockNonce `json:"nonce"`
+ BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
+ WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
+ Hash common.Hash `json:"hash"`
}
var enc Header
enc.ParentHash = h.ParentHash
@@ -51,6 +52,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
enc.MixDigest = h.MixDigest
enc.Nonce = h.Nonce
enc.BaseFee = (*hexutil.Big)(h.BaseFee)
+ enc.WithdrawalsHash = h.WithdrawalsHash
enc.Hash = h.Hash()
return json.Marshal(&enc)
}
@@ -58,22 +60,23 @@ func (h Header) MarshalJSON() ([]byte, error) {
// UnmarshalJSON unmarshals from JSON.
func (h *Header) UnmarshalJSON(input []byte) error {
type Header struct {
- ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
- UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
- Coinbase *common.Address `json:"miner"`
- Root *common.Hash `json:"stateRoot" gencodec:"required"`
- TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
- ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
- Bloom *Bloom `json:"logsBloom" gencodec:"required"`
- Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
- Number *hexutil.Big `json:"number" gencodec:"required"`
- GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
- GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
- Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
- Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
- MixDigest *common.Hash `json:"mixHash"`
- Nonce *BlockNonce `json:"nonce"`
- BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
+ ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
+ UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
+ Coinbase *common.Address `json:"miner"`
+ Root *common.Hash `json:"stateRoot" gencodec:"required"`
+ TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
+ ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
+ Bloom *Bloom `json:"logsBloom" gencodec:"required"`
+ Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
+ Number *hexutil.Big `json:"number" gencodec:"required"`
+ GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
+ Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
+ Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
+ MixDigest *common.Hash `json:"mixHash"`
+ Nonce *BlockNonce `json:"nonce"`
+ BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
+ WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
}
var dec Header
if err := json.Unmarshal(input, &dec); err != nil {
@@ -139,5 +142,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
if dec.BaseFee != nil {
h.BaseFee = (*big.Int)(dec.BaseFee)
}
+ if dec.WithdrawalsHash != nil {
+ h.WithdrawalsHash = dec.WithdrawalsHash
+ }
return nil
}
diff --git a/core/types/gen_header_rlp.go b/core/types/gen_header_rlp.go
index e1a6873318..7fd2cf8f2d 100644
--- a/core/types/gen_header_rlp.go
+++ b/core/types/gen_header_rlp.go
@@ -41,7 +41,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
w.WriteBytes(obj.MixDigest[:])
w.WriteBytes(obj.Nonce[:])
_tmp1 := obj.BaseFee != nil
- if _tmp1 {
+ _tmp2 := obj.WithdrawalsHash != nil
+ if _tmp1 || _tmp2 {
if obj.BaseFee == nil {
w.Write(rlp.EmptyString)
} else {
@@ -51,6 +52,13 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
w.WriteBigInt(obj.BaseFee)
}
}
+ if _tmp2 {
+ if obj.WithdrawalsHash == nil {
+ w.Write([]byte{0x80})
+ } else {
+ w.WriteBytes(obj.WithdrawalsHash[:])
+ }
+ }
w.ListEnd(_tmp0)
return w.Flush()
}
diff --git a/core/types/gen_withdrawal_json.go b/core/types/gen_withdrawal_json.go
new file mode 100644
index 0000000000..983a7f7a12
--- /dev/null
+++ b/core/types/gen_withdrawal_json.go
@@ -0,0 +1,55 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package types
+
+import (
+ "encoding/json"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*withdrawalMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (w Withdrawal) MarshalJSON() ([]byte, error) {
+ type Withdrawal struct {
+ Index hexutil.Uint64 `json:"index"`
+ Validator hexutil.Uint64 `json:"validatorIndex"`
+ Address common.Address `json:"address"`
+ Amount hexutil.Uint64 `json:"amount"`
+ }
+ var enc Withdrawal
+ enc.Index = hexutil.Uint64(w.Index)
+ enc.Validator = hexutil.Uint64(w.Validator)
+ enc.Address = w.Address
+ enc.Amount = hexutil.Uint64(w.Amount)
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (w *Withdrawal) UnmarshalJSON(input []byte) error {
+ type Withdrawal struct {
+ Index *hexutil.Uint64 `json:"index"`
+ Validator *hexutil.Uint64 `json:"validatorIndex"`
+ Address *common.Address `json:"address"`
+ Amount *hexutil.Uint64 `json:"amount"`
+ }
+ var dec Withdrawal
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Index != nil {
+ w.Index = uint64(*dec.Index)
+ }
+ if dec.Validator != nil {
+ w.Validator = uint64(*dec.Validator)
+ }
+ if dec.Address != nil {
+ w.Address = *dec.Address
+ }
+ if dec.Amount != nil {
+ w.Amount = uint64(*dec.Amount)
+ }
+ return nil
+}
diff --git a/core/types/gen_withdrawal_rlp.go b/core/types/gen_withdrawal_rlp.go
new file mode 100644
index 0000000000..d0b4e0147a
--- /dev/null
+++ b/core/types/gen_withdrawal_rlp.go
@@ -0,0 +1,20 @@
+// Code generated by rlpgen. DO NOT EDIT.
+
+//go:build !norlpgen
+// +build !norlpgen
+
+package types
+
+import "github.com/ethereum/go-ethereum/rlp"
+import "io"
+
+func (obj *Withdrawal) EncodeRLP(_w io.Writer) error {
+ w := rlp.NewEncoderBuffer(_w)
+ _tmp0 := w.List()
+ w.WriteUint64(obj.Index)
+ w.WriteUint64(obj.Validator)
+ w.WriteBytes(obj.Address[:])
+ w.WriteUint64(obj.Amount)
+ w.ListEnd(_tmp0)
+ return w.Flush()
+}
diff --git a/core/types/hashes.go b/core/types/hashes.go
new file mode 100644
index 0000000000..3bad430be5
--- /dev/null
+++ b/core/types/hashes.go
@@ -0,0 +1,42 @@
+// Copyright 2023 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 types
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+var (
+ // EmptyRootHash is the known root hash of an empty trie.
+ EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // EmptyUncleHash is the known hash of the empty uncle set.
+ EmptyUncleHash = rlpHash([]*Header(nil)) // 1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347
+
+ // EmptyCodeHash is the known hash of the empty EVM bytecode.
+ EmptyCodeHash = crypto.Keccak256Hash(nil) // c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
+
+ // EmptyTxsHash is the known hash of the empty transaction set.
+ EmptyTxsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // EmptyReceiptsHash is the known hash of the empty receipt set.
+ EmptyReceiptsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // EmptyWithdrawalsHash is the known hash of the empty withdrawal set.
+ EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+)
diff --git a/core/types/legacy.go b/core/types/legacy.go
deleted file mode 100644
index 14ed30d883..0000000000
--- a/core/types/legacy.go
+++ /dev/null
@@ -1,53 +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 types
-
-import (
- "errors"
-
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// IsLegacyStoredReceipts tries to parse the RLP-encoded blob
-// first as an array of v3 stored receipt, then v4 stored receipt and
-// returns true if successful.
-func IsLegacyStoredReceipts(raw []byte) (bool, error) {
- var v3 []v3StoredReceiptRLP
- if err := rlp.DecodeBytes(raw, &v3); err == nil {
- return true, nil
- }
- var v4 []v4StoredReceiptRLP
- if err := rlp.DecodeBytes(raw, &v4); err == nil {
- return true, nil
- }
- var v5 []storedReceiptRLP
- // Check to see valid fresh stored receipt
- if err := rlp.DecodeBytes(raw, &v5); err == nil {
- return false, nil
- }
- return false, errors.New("value is not a valid receipt encoding")
-}
-
-// ConvertLegacyStoredReceipts takes the RLP encoding of an array of legacy
-// stored receipts and returns a fresh RLP-encoded stored receipt.
-func ConvertLegacyStoredReceipts(raw []byte) ([]byte, error) {
- var receipts []ReceiptForStorage
- if err := rlp.DecodeBytes(raw, &receipts); err != nil {
- return nil, err
- }
- return rlp.EncodeToBytes(&receipts)
-}
diff --git a/core/types/log.go b/core/types/log.go
index eb30957b12..e489191368 100644
--- a/core/types/log.go
+++ b/core/types/log.go
@@ -64,24 +64,13 @@ type logMarshaling struct {
//go:generate go run ../../rlp/rlpgen -type rlpLog -out gen_log_rlp.go
+// rlpLog is used to RLP-encode both the consensus and storage formats.
type rlpLog struct {
Address common.Address
Topics []common.Hash
Data []byte
}
-// legacyRlpStorageLog is the previous storage encoding of a log including some redundant fields.
-type legacyRlpStorageLog struct {
- Address common.Address
- Topics []common.Hash
- Data []byte
- BlockNumber uint64
- TxHash common.Hash
- TxIndex uint
- BlockHash common.Hash
- Index uint
-}
-
// EncodeRLP implements rlp.Encoder.
func (l *Log) EncodeRLP(w io.Writer) error {
rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
@@ -97,44 +86,3 @@ func (l *Log) DecodeRLP(s *rlp.Stream) error {
}
return err
}
-
-// LogForStorage is a wrapper around a Log that handles
-// backward compatibility with prior storage formats.
-type LogForStorage Log
-
-// EncodeRLP implements rlp.Encoder.
-func (l *LogForStorage) EncodeRLP(w io.Writer) error {
- rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
- return rlp.Encode(w, &rl)
-}
-
-// DecodeRLP implements rlp.Decoder.
-//
-// Note some redundant fields(e.g. block number, tx hash etc) will be assembled later.
-func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error {
- blob, err := s.Raw()
- if err != nil {
- return err
- }
- var dec rlpLog
- err = rlp.DecodeBytes(blob, &dec)
- if err == nil {
- *l = LogForStorage{
- Address: dec.Address,
- Topics: dec.Topics,
- Data: dec.Data,
- }
- } else {
- // Try to decode log with previous definition.
- var dec legacyRlpStorageLog
- err = rlp.DecodeBytes(blob, &dec)
- if err == nil {
- *l = LogForStorage{
- Address: dec.Address,
- Topics: dec.Topics,
- Data: dec.Data,
- }
- }
- }
- return err
-}
diff --git a/core/types/receipt.go b/core/types/receipt.go
index bdf4845147..4404b27889 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -93,28 +93,7 @@ type receiptRLP struct {
type storedReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
- Logs []*LogForStorage
-}
-
-// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4.
-type v4StoredReceiptRLP struct {
- PostStateOrStatus []byte
- CumulativeGasUsed uint64
- TxHash common.Hash
- ContractAddress common.Address
- Logs []*LogForStorage
- GasUsed uint64
-}
-
-// v3StoredReceiptRLP is the original storage encoding of a receipt including some unnecessary fields.
-type v3StoredReceiptRLP struct {
- PostStateOrStatus []byte
- CumulativeGasUsed uint64
- Bloom Bloom
- TxHash common.Hash
- ContractAddress common.Address
- Logs []*LogForStorage
- GasUsed uint64
+ Logs []*Log
}
// NewReceipt creates a barebone transaction receipt, copying the init fields.
@@ -292,82 +271,20 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
// fields of a receipt from an RLP stream.
func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
- // Retrieve the entire receipt blob as we need to try multiple decoders
- blob, err := s.Raw()
- if err != nil {
- return err
- }
- // Try decoding from the newest format for future proofness, then the older one
- // for old nodes that just upgraded. V4 was an intermediate unreleased format so
- // we do need to decode it, but it's not common (try last).
- if err := decodeStoredReceiptRLP(r, blob); err == nil {
- return nil
- }
- if err := decodeV3StoredReceiptRLP(r, blob); err == nil {
- return nil
- }
- return decodeV4StoredReceiptRLP(r, blob)
-}
-
-func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
var stored storedReceiptRLP
- if err := rlp.DecodeBytes(blob, &stored); err != nil {
+ if err := s.Decode(&stored); err != nil {
return err
}
if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
return err
}
r.CumulativeGasUsed = stored.CumulativeGasUsed
- r.Logs = make([]*Log, len(stored.Logs))
- for i, log := range stored.Logs {
- r.Logs[i] = (*Log)(log)
- }
+ r.Logs = stored.Logs
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
return nil
}
-func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
- var stored v4StoredReceiptRLP
- if err := rlp.DecodeBytes(blob, &stored); err != nil {
- return err
- }
- if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
- return err
- }
- r.CumulativeGasUsed = stored.CumulativeGasUsed
- r.TxHash = stored.TxHash
- r.ContractAddress = stored.ContractAddress
- r.GasUsed = stored.GasUsed
- r.Logs = make([]*Log, len(stored.Logs))
- for i, log := range stored.Logs {
- r.Logs[i] = (*Log)(log)
- }
- r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
-
- return nil
-}
-
-func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
- var stored v3StoredReceiptRLP
- if err := rlp.DecodeBytes(blob, &stored); err != nil {
- return err
- }
- if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
- return err
- }
- r.CumulativeGasUsed = stored.CumulativeGasUsed
- r.Bloom = stored.Bloom
- r.TxHash = stored.TxHash
- r.ContractAddress = stored.ContractAddress
- r.GasUsed = stored.GasUsed
- r.Logs = make([]*Log, len(stored.Logs))
- for i, log := range stored.Logs {
- r.Logs[i] = (*Log)(log)
- }
- return nil
-}
-
// Receipts implements DerivableList for receipts.
type Receipts []*Receipt
diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go
index bba18d2a7b..f44bb80b04 100644
--- a/core/types/receipt_test.go
+++ b/core/types/receipt_test.go
@@ -91,128 +91,6 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) {
}
}
-func TestLegacyReceiptDecoding(t *testing.T) {
- tests := []struct {
- name string
- encode func(*Receipt) ([]byte, error)
- }{
- {
- "StoredReceiptRLP",
- encodeAsStoredReceiptRLP,
- },
- {
- "V4StoredReceiptRLP",
- encodeAsV4StoredReceiptRLP,
- },
- {
- "V3StoredReceiptRLP",
- encodeAsV3StoredReceiptRLP,
- },
- }
-
- tx := NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
- receipt := &Receipt{
- Status: ReceiptStatusFailed,
- CumulativeGasUsed: 1,
- Logs: []*Log{
- {
- Address: common.BytesToAddress([]byte{0x11}),
- Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
- Data: []byte{0x01, 0x00, 0xff},
- },
- {
- Address: common.BytesToAddress([]byte{0x01, 0x11}),
- Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
- Data: []byte{0x01, 0x00, 0xff},
- },
- },
- TxHash: tx.Hash(),
- ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
- GasUsed: 111111,
- }
- receipt.Bloom = CreateBloom(Receipts{receipt})
-
- for _, tc := range tests {
- t.Run(tc.name, func(t *testing.T) {
- enc, err := tc.encode(receipt)
- if err != nil {
- t.Fatalf("Error encoding receipt: %v", err)
- }
- var dec ReceiptForStorage
- if err := rlp.DecodeBytes(enc, &dec); err != nil {
- t.Fatalf("Error decoding RLP receipt: %v", err)
- }
- // Check whether all consensus fields are correct.
- if dec.Status != receipt.Status {
- t.Fatalf("Receipt status mismatch, want %v, have %v", receipt.Status, dec.Status)
- }
- if dec.CumulativeGasUsed != receipt.CumulativeGasUsed {
- t.Fatalf("Receipt CumulativeGasUsed mismatch, want %v, have %v", receipt.CumulativeGasUsed, dec.CumulativeGasUsed)
- }
- if dec.Bloom != receipt.Bloom {
- t.Fatalf("Bloom data mismatch, want %v, have %v", receipt.Bloom, dec.Bloom)
- }
- if len(dec.Logs) != len(receipt.Logs) {
- t.Fatalf("Receipt log number mismatch, want %v, have %v", len(receipt.Logs), len(dec.Logs))
- }
- for i := 0; i < len(dec.Logs); i++ {
- if dec.Logs[i].Address != receipt.Logs[i].Address {
- t.Fatalf("Receipt log %d address mismatch, want %v, have %v", i, receipt.Logs[i].Address, dec.Logs[i].Address)
- }
- if !reflect.DeepEqual(dec.Logs[i].Topics, receipt.Logs[i].Topics) {
- t.Fatalf("Receipt log %d topics mismatch, want %v, have %v", i, receipt.Logs[i].Topics, dec.Logs[i].Topics)
- }
- if !bytes.Equal(dec.Logs[i].Data, receipt.Logs[i].Data) {
- t.Fatalf("Receipt log %d data mismatch, want %v, have %v", i, receipt.Logs[i].Data, dec.Logs[i].Data)
- }
- }
- })
- }
-}
-
-func encodeAsStoredReceiptRLP(want *Receipt) ([]byte, error) {
- stored := &storedReceiptRLP{
- PostStateOrStatus: want.statusEncoding(),
- CumulativeGasUsed: want.CumulativeGasUsed,
- Logs: make([]*LogForStorage, len(want.Logs)),
- }
- for i, log := range want.Logs {
- stored.Logs[i] = (*LogForStorage)(log)
- }
- return rlp.EncodeToBytes(stored)
-}
-
-func encodeAsV4StoredReceiptRLP(want *Receipt) ([]byte, error) {
- stored := &v4StoredReceiptRLP{
- PostStateOrStatus: want.statusEncoding(),
- CumulativeGasUsed: want.CumulativeGasUsed,
- TxHash: want.TxHash,
- ContractAddress: want.ContractAddress,
- Logs: make([]*LogForStorage, len(want.Logs)),
- GasUsed: want.GasUsed,
- }
- for i, log := range want.Logs {
- stored.Logs[i] = (*LogForStorage)(log)
- }
- return rlp.EncodeToBytes(stored)
-}
-
-func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) {
- stored := &v3StoredReceiptRLP{
- PostStateOrStatus: want.statusEncoding(),
- CumulativeGasUsed: want.CumulativeGasUsed,
- Bloom: want.Bloom,
- TxHash: want.TxHash,
- ContractAddress: want.ContractAddress,
- Logs: make([]*LogForStorage, len(want.Logs)),
- GasUsed: want.GasUsed,
- }
- for i, log := range want.Logs {
- stored.Logs[i] = (*LogForStorage)(log)
- }
- return rlp.EncodeToBytes(stored)
-}
-
// Tests that receipt data can be correctly derived from the contextual infos
func TestDeriveFields(t *testing.T) {
// Create a few transactions to have receipts for
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 715ede15db..353e0e599c 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -131,7 +131,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
var inner LegacyTx
err := s.Decode(&inner)
if err == nil {
- tx.setDecoded(&inner, int(rlp.ListSize(size)))
+ tx.setDecoded(&inner, rlp.ListSize(size))
}
return err
default:
@@ -142,7 +142,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
}
inner, err := tx.decodeTyped(b)
if err == nil {
- tx.setDecoded(inner, len(b))
+ tx.setDecoded(inner, uint64(len(b)))
}
return err
}
@@ -158,7 +158,7 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
if err != nil {
return err
}
- tx.setDecoded(&data, len(b))
+ tx.setDecoded(&data, uint64(len(b)))
return nil
}
// It's an EIP2718 typed transaction envelope.
@@ -166,7 +166,7 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
if err != nil {
return err
}
- tx.setDecoded(inner, len(b))
+ tx.setDecoded(inner, uint64(len(b)))
return nil
}
@@ -190,11 +190,11 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
}
// setDecoded sets the inner transaction and size after decoding.
-func (tx *Transaction) setDecoded(inner TxData, size int) {
+func (tx *Transaction) setDecoded(inner TxData, size uint64) {
tx.inner = inner
tx.time = time.Now()
if size > 0 {
- tx.size.Store(common.StorageSize(size))
+ tx.size.Store(size)
}
}
@@ -372,16 +372,21 @@ func (tx *Transaction) Hash() common.Hash {
return h
}
-// Size returns the true RLP encoded storage size of the transaction, either by
-// encoding and returning it, or returning a previously cached value.
-func (tx *Transaction) Size() common.StorageSize {
+// Size returns the true encoded storage size of the transaction, either by encoding
+// and returning it, or returning a previously cached value.
+func (tx *Transaction) Size() uint64 {
if size := tx.size.Load(); size != nil {
- return size.(common.StorageSize)
+ return size.(uint64)
}
c := writeCounter(0)
rlp.Encode(&c, &tx.inner)
- tx.size.Store(common.StorageSize(c))
- return common.StorageSize(c)
+
+ size := uint64(c)
+ if tx.Type() != LegacyTxType {
+ size += 1 // type byte
+ }
+ tx.size.Store(size)
+ return size
}
// WithSignature returns a new transaction with the given signature.
@@ -503,6 +508,7 @@ func (s *TxByPriceAndTime) Pop() interface{} {
old := *s
n := len(old)
x := old[n-1]
+ old[n-1] = nil
*s = old[0 : n-1]
return x
}
diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go
index aad31a5a97..2566d0b8d6 100644
--- a/core/types/transaction_marshalling.go
+++ b/core/types/transaction_marshalling.go
@@ -51,55 +51,55 @@ type txJSON struct {
}
// MarshalJSON marshals as JSON with a hash.
-func (t *Transaction) MarshalJSON() ([]byte, error) {
+func (tx *Transaction) MarshalJSON() ([]byte, error) {
var enc txJSON
// These are set for all tx types.
- enc.Hash = t.Hash()
- enc.Type = hexutil.Uint64(t.Type())
+ enc.Hash = tx.Hash()
+ enc.Type = hexutil.Uint64(tx.Type())
// Other fields are set conditionally depending on tx type.
- switch tx := t.inner.(type) {
+ switch itx := tx.inner.(type) {
case *LegacyTx:
- enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
- enc.Gas = (*hexutil.Uint64)(&tx.Gas)
- enc.GasPrice = (*hexutil.Big)(tx.GasPrice)
- enc.Value = (*hexutil.Big)(tx.Value)
- enc.Data = (*hexutil.Bytes)(&tx.Data)
- enc.To = t.To()
- enc.V = (*hexutil.Big)(tx.V)
- enc.R = (*hexutil.Big)(tx.R)
- enc.S = (*hexutil.Big)(tx.S)
+ enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
+ enc.Gas = (*hexutil.Uint64)(&itx.Gas)
+ enc.GasPrice = (*hexutil.Big)(itx.GasPrice)
+ enc.Value = (*hexutil.Big)(itx.Value)
+ enc.Data = (*hexutil.Bytes)(&itx.Data)
+ enc.To = tx.To()
+ enc.V = (*hexutil.Big)(itx.V)
+ enc.R = (*hexutil.Big)(itx.R)
+ enc.S = (*hexutil.Big)(itx.S)
case *AccessListTx:
- enc.ChainID = (*hexutil.Big)(tx.ChainID)
- enc.AccessList = &tx.AccessList
- enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
- enc.Gas = (*hexutil.Uint64)(&tx.Gas)
- enc.GasPrice = (*hexutil.Big)(tx.GasPrice)
- enc.Value = (*hexutil.Big)(tx.Value)
- enc.Data = (*hexutil.Bytes)(&tx.Data)
- enc.To = t.To()
- enc.V = (*hexutil.Big)(tx.V)
- enc.R = (*hexutil.Big)(tx.R)
- enc.S = (*hexutil.Big)(tx.S)
+ enc.ChainID = (*hexutil.Big)(itx.ChainID)
+ enc.AccessList = &itx.AccessList
+ enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
+ enc.Gas = (*hexutil.Uint64)(&itx.Gas)
+ enc.GasPrice = (*hexutil.Big)(itx.GasPrice)
+ enc.Value = (*hexutil.Big)(itx.Value)
+ enc.Data = (*hexutil.Bytes)(&itx.Data)
+ enc.To = tx.To()
+ enc.V = (*hexutil.Big)(itx.V)
+ enc.R = (*hexutil.Big)(itx.R)
+ enc.S = (*hexutil.Big)(itx.S)
case *DynamicFeeTx:
- enc.ChainID = (*hexutil.Big)(tx.ChainID)
- enc.AccessList = &tx.AccessList
- enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
- enc.Gas = (*hexutil.Uint64)(&tx.Gas)
- enc.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap)
- enc.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap)
- enc.Value = (*hexutil.Big)(tx.Value)
- enc.Data = (*hexutil.Bytes)(&tx.Data)
- enc.To = t.To()
- enc.V = (*hexutil.Big)(tx.V)
- enc.R = (*hexutil.Big)(tx.R)
- enc.S = (*hexutil.Big)(tx.S)
+ enc.ChainID = (*hexutil.Big)(itx.ChainID)
+ enc.AccessList = &itx.AccessList
+ enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
+ enc.Gas = (*hexutil.Uint64)(&itx.Gas)
+ enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap)
+ enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap)
+ enc.Value = (*hexutil.Big)(itx.Value)
+ enc.Data = (*hexutil.Bytes)(&itx.Data)
+ enc.To = tx.To()
+ enc.V = (*hexutil.Big)(itx.V)
+ enc.R = (*hexutil.Big)(itx.R)
+ enc.S = (*hexutil.Big)(itx.S)
}
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
-func (t *Transaction) UnmarshalJSON(input []byte) error {
+func (tx *Transaction) UnmarshalJSON(input []byte) error {
var dec txJSON
if err := json.Unmarshal(input, &dec); err != nil {
return err
@@ -268,7 +268,7 @@ func (t *Transaction) UnmarshalJSON(input []byte) error {
}
// Now set the inner transaction.
- t.setDecoded(inner, 0)
+ tx.setDecoded(inner, 0)
// TODO: check hash here?
return nil
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
index 1d0d2a4c75..87f0390a6f 100644
--- a/core/types/transaction_signing.go
+++ b/core/types/transaction_signing.go
@@ -190,7 +190,7 @@ func (s londonSigner) Sender(tx *Transaction) (common.Address, error) {
// id, add 27 to become equivalent to unprotected Homestead signatures.
V = new(big.Int).Add(V, big.NewInt(27))
if tx.ChainId().Cmp(s.chainId) != 0 {
- return common.Address{}, ErrInvalidChainId
+ return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId)
}
return recoverPlain(s.Hash(tx), R, S, V, true)
}
@@ -208,7 +208,7 @@ func (s londonSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big
// Check that chain ID of tx matches the signer. We also accept ID zero here,
// because it indicates that the chain ID was not specified in the tx.
if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
- return nil, nil, nil, ErrInvalidChainId
+ return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
}
R, S, _ = decodeSignature(sig)
V = big.NewInt(int64(sig[64]))
@@ -270,7 +270,7 @@ func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) {
return common.Address{}, ErrTxTypeNotSupported
}
if tx.ChainId().Cmp(s.chainId) != 0 {
- return common.Address{}, ErrInvalidChainId
+ return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId)
}
return recoverPlain(s.Hash(tx), R, S, V, true)
}
@@ -283,7 +283,7 @@ func (s eip2930Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *bi
// Check that chain ID of tx matches the signer. We also accept ID zero here,
// because it indicates that the chain ID was not specified in the tx.
if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
- return nil, nil, nil, ErrInvalidChainId
+ return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
}
R, S, _ = decodeSignature(sig)
V = big.NewInt(int64(sig[64]))
@@ -364,7 +364,7 @@ func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
return HomesteadSigner{}.Sender(tx)
}
if tx.ChainId().Cmp(s.chainId) != 0 {
- return common.Address{}, ErrInvalidChainId
+ return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId)
}
V, R, S := tx.RawSignatureValues()
V = new(big.Int).Sub(V, s.chainIdMul)
@@ -400,7 +400,7 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
})
}
-// HomesteadTransaction implements TransactionInterface using the
+// HomesteadSigner implements Signer interface using the
// homestead rules.
type HomesteadSigner struct{ FrontierSigner }
@@ -427,6 +427,8 @@ func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
return recoverPlain(hs.Hash(tx), r, s, v, true)
}
+// FrontierSigner implements Signer interface using the
+// frontier rules.
type FrontierSigner struct{}
func (s FrontierSigner) ChainID() *big.Int {
diff --git a/core/types/transaction_signing_test.go b/core/types/transaction_signing_test.go
index 1c775f129d..2a9ceb0952 100644
--- a/core/types/transaction_signing_test.go
+++ b/core/types/transaction_signing_test.go
@@ -17,6 +17,7 @@
package types
import (
+ "errors"
"math/big"
"testing"
@@ -126,8 +127,8 @@ func TestChainId(t *testing.T) {
}
_, err = Sender(NewEIP155Signer(big.NewInt(2)), tx)
- if err != ErrInvalidChainId {
- t.Error("expected error:", ErrInvalidChainId)
+ if !errors.Is(err, ErrInvalidChainId) {
+ t.Error("expected error:", ErrInvalidChainId, err)
}
_, err = Sender(NewEIP155Signer(big.NewInt(1)), tx)
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index 67e5b3cce3..4b96c6b91a 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -20,6 +20,7 @@ import (
"bytes"
"crypto/ecdsa"
"encoding/json"
+ "errors"
"fmt"
"math/big"
"math/rand"
@@ -170,14 +171,14 @@ func TestEIP2930Signer(t *testing.T) {
t.Errorf("test %d: wrong sig hash: got %x, want %x", i, sigHash, test.wantSignerHash)
}
sender, err := Sender(test.signer, test.tx)
- if err != test.wantSenderErr {
+ if !errors.Is(err, test.wantSenderErr) {
t.Errorf("test %d: wrong Sender error %q", i, err)
}
if err == nil && sender != keyAddr {
t.Errorf("test %d: wrong sender address %x", i, sender)
}
signedTx, err := SignTx(test.tx, test.signer, key)
- if err != test.wantSignErr {
+ if !errors.Is(err, test.wantSignErr) {
t.Fatalf("test %d: wrong SignTx error %q", i, err)
}
if signedTx != nil {
@@ -530,3 +531,71 @@ func assertEqual(orig *Transaction, cpy *Transaction) error {
}
return nil
}
+
+func TestTransactionSizes(t *testing.T) {
+ signer := NewLondonSigner(big.NewInt(123))
+ key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ to := common.HexToAddress("0x01")
+ for i, txdata := range []TxData{
+ &AccessListTx{
+ ChainID: big.NewInt(123),
+ Nonce: 0,
+ To: nil,
+ Value: big.NewInt(1000),
+ Gas: 21000,
+ GasPrice: big.NewInt(100000),
+ },
+ &LegacyTx{
+ Nonce: 1,
+ GasPrice: big.NewInt(500),
+ Gas: 1000000,
+ To: &to,
+ Value: big.NewInt(1),
+ },
+ &AccessListTx{
+ ChainID: big.NewInt(123),
+ Nonce: 1,
+ GasPrice: big.NewInt(500),
+ Gas: 1000000,
+ To: &to,
+ Value: big.NewInt(1),
+ AccessList: AccessList{
+ AccessTuple{
+ Address: common.HexToAddress("0x01"),
+ StorageKeys: []common.Hash{common.HexToHash("0x01")},
+ }},
+ },
+ &DynamicFeeTx{
+ ChainID: big.NewInt(123),
+ Nonce: 1,
+ Gas: 1000000,
+ To: &to,
+ Value: big.NewInt(1),
+ GasTipCap: big.NewInt(500),
+ GasFeeCap: big.NewInt(500),
+ },
+ } {
+ tx, err := SignNewTx(key, signer, txdata)
+ if err != nil {
+ t.Fatalf("test %d: %v", i, err)
+ }
+ bin, _ := tx.MarshalBinary()
+
+ // Check initial calc
+ if have, want := int(tx.Size()), len(bin); have != want {
+ t.Errorf("test %d: size wrong, have %d want %d", i, have, want)
+ }
+ // Check cached version too
+ if have, want := int(tx.Size()), len(bin); have != want {
+ t.Errorf("test %d: (cached) size wrong, have %d want %d", i, have, want)
+ }
+ // Check unmarshalled version too
+ utx := new(Transaction)
+ if err := utx.UnmarshalBinary(bin); err != nil {
+ t.Fatalf("test %d: failed to unmarshal tx: %v", i, err)
+ }
+ if have, want := int(utx.Size()), len(bin); have != want {
+ t.Errorf("test %d: (unmarshalled) size wrong, have %d want %d", i, have, want)
+ }
+ }
+}
diff --git a/core/types/access_list_tx.go b/core/types/tx_access_list.go
similarity index 100%
rename from core/types/access_list_tx.go
rename to core/types/tx_access_list.go
diff --git a/core/types/dynamic_fee_tx.go b/core/types/tx_dynamic_fee.go
similarity index 100%
rename from core/types/dynamic_fee_tx.go
rename to core/types/tx_dynamic_fee.go
diff --git a/core/types/legacy_tx.go b/core/types/tx_legacy.go
similarity index 100%
rename from core/types/legacy_tx.go
rename to core/types/tx_legacy.go
diff --git a/core/types/withdrawal.go b/core/types/withdrawal.go
new file mode 100644
index 0000000000..d1ad918f98
--- /dev/null
+++ b/core/types/withdrawal.go
@@ -0,0 +1,56 @@
+// 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 types
+
+import (
+ "bytes"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+//go:generate go run github.com/fjl/gencodec -type Withdrawal -field-override withdrawalMarshaling -out gen_withdrawal_json.go
+//go:generate go run ../../rlp/rlpgen -type Withdrawal -out gen_withdrawal_rlp.go
+
+// Withdrawal represents a validator withdrawal from the consensus layer.
+type Withdrawal struct {
+ Index uint64 `json:"index"` // monotonically increasing identifier issued by consensus layer
+ Validator uint64 `json:"validatorIndex"` // index of validator associated with withdrawal
+ Address common.Address `json:"address"` // target address for withdrawn ether
+ Amount uint64 `json:"amount"` // value of withdrawal in Gwei
+}
+
+// field type overrides for gencodec
+type withdrawalMarshaling struct {
+ Index hexutil.Uint64
+ Validator hexutil.Uint64
+ Amount hexutil.Uint64
+}
+
+// Withdrawals implements DerivableList for withdrawals.
+type Withdrawals []*Withdrawal
+
+// Len returns the length of s.
+func (s Withdrawals) Len() int { return len(s) }
+
+// EncodeIndex encodes the i'th withdrawal to w. Note that this does not check for errors
+// because we assume that *Withdrawal will only ever contain valid withdrawals that were either
+// constructed by decoding or via public API in this package.
+func (s Withdrawals) EncodeIndex(i int, w *bytes.Buffer) {
+ rlp.Encode(w, s[i])
+}
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 1b832b6386..9a52616657 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/crypto/bls12381"
"github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/ethereum/go-ethereum/params"
+ big2 "github.com/holiman/big"
"golang.org/x/crypto/ripemd160"
)
@@ -235,7 +236,7 @@ func (c *dataCopy) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
}
func (c *dataCopy) Run(in []byte) ([]byte, error) {
- return in, nil
+ return common.CopyBytes(in), nil
}
// bigModExp implements a native big integer exponential modular operation.
@@ -263,10 +264,10 @@ var (
// modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198
//
-// def mult_complexity(x):
-// if x <= 64: return x ** 2
-// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072
-// else: return x ** 2 // 16 + 480 * x - 199680
+// def mult_complexity(x):
+// if x <= 64: return x ** 2
+// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072
+// else: return x ** 2 // 16 + 480 * x - 199680
//
// where is x is max(length_of_MODULUS, length_of_BASE)
func modexpMultComplexity(x *big.Int) *big.Int {
@@ -377,15 +378,22 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
}
// Retrieve the operands and execute the exponentiation
var (
- base = new(big.Int).SetBytes(getData(input, 0, baseLen))
- exp = new(big.Int).SetBytes(getData(input, baseLen, expLen))
- mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen))
+ base = new(big2.Int).SetBytes(getData(input, 0, baseLen))
+ exp = new(big2.Int).SetBytes(getData(input, baseLen, expLen))
+ mod = new(big2.Int).SetBytes(getData(input, baseLen+expLen, modLen))
+ v []byte
)
- if mod.BitLen() == 0 {
+ switch {
+ case mod.BitLen() == 0:
// Modulo 0 is undefined, return zero
return common.LeftPadBytes([]byte{}, int(modLen)), nil
+ case base.BitLen() == 1: // a bit length of 1 means it's 1 (or -1).
+ //If base == 1, then we can just return base % mod (if mod >= 1, which it is)
+ v = base.Mod(base, mod).Bytes()
+ default:
+ v = base.Exp(base, exp, mod).Bytes()
}
- return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil
+ return common.LeftPadBytes(v, int(modLen)), nil
}
// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
@@ -935,7 +943,7 @@ func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
return nil, errBLS12381G2PointSubgroup
}
- // Update pairing engine with G1 and G2 ponits
+ // Update pairing engine with G1 and G2 points
e.AddPair(p1, p2)
}
// Prepare 32 byte output
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 93f5c399a6..29ff27c552 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -20,18 +20,21 @@ import (
"fmt"
"sort"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
var activators = map[int]func(*JumpTable){
3855: enable3855,
+ 3860: enable3860,
3529: enable3529,
3198: enable3198,
2929: enable2929,
2200: enable2200,
1884: enable1884,
1344: enable1344,
+ 1153: enable1153,
}
// EnableEIP enables the given EIP on the config.
@@ -169,6 +172,45 @@ func enable3198(jt *JumpTable) {
}
}
+// enable1153 applies EIP-1153 "Transient Storage"
+// - Adds TLOAD that reads from transient storage
+// - Adds TSTORE that writes to transient storage
+func enable1153(jt *JumpTable) {
+ jt[TLOAD] = &operation{
+ execute: opTload,
+ constantGas: params.WarmStorageReadCostEIP2929,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ }
+
+ jt[TSTORE] = &operation{
+ execute: opTstore,
+ constantGas: params.WarmStorageReadCostEIP2929,
+ minStack: minStack(2, 0),
+ maxStack: maxStack(2, 0),
+ }
+}
+
+// opTload implements TLOAD opcode
+func opTload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ loc := scope.Stack.peek()
+ hash := common.Hash(loc.Bytes32())
+ val := interpreter.evm.StateDB.GetTransientState(scope.Contract.Address(), hash)
+ loc.SetBytes(val.Bytes())
+ return nil, nil
+}
+
+// opTstore implements TSTORE opcode
+func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ loc := scope.Stack.pop()
+ val := scope.Stack.pop()
+ interpreter.evm.StateDB.SetTransientState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
+ return nil, nil
+}
+
// opBaseFee implements BASEFEE opcode
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee)
@@ -192,3 +234,10 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
scope.Stack.push(new(uint256.Int))
return nil, nil
}
+
+// ebnable3860 enables "EIP-3860: Limit and meter initcode"
+// https://eips.ethereum.org/EIPS/eip-3860
+func enable3860(jt *JumpTable) {
+ jt[CREATE].dynamicGas = gasCreateEip3860
+ jt[CREATE2].dynamicGas = gasCreate2Eip3860
+}
diff --git a/core/vm/errors.go b/core/vm/errors.go
index 004f8ef1c8..fbbf19e178 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -29,6 +29,7 @@ var (
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
ErrContractAddressCollision = errors.New("contract address collision")
ErrExecutionReverted = errors.New("execution reverted")
+ ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
ErrInvalidJump = errors.New("invalid jump destination")
ErrWriteProtection = errors.New("write protection")
diff --git a/core/vm/evm.go b/core/vm/evm.go
index dd55618bf8..d78ea07926 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -19,7 +19,6 @@ package vm
import (
"math/big"
"sync/atomic"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
@@ -72,10 +71,10 @@ type BlockContext struct {
Coinbase common.Address // Provides information for COINBASE
GasLimit uint64 // Provides information for GASLIMIT
BlockNumber *big.Int // Provides information for NUMBER
- Time *big.Int // Provides information for TIME
+ Time uint64 // Provides information for TIME
Difficulty *big.Int // Provides information for DIFFICULTY
BaseFee *big.Int // Provides information for BASEFEE
- Random *common.Hash // Provides information for RANDOM
+ Random *common.Hash // Provides information for PREVRANDAO
}
// TxContext provides the EVM with information about a transaction.
@@ -132,9 +131,9 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
StateDB: statedb,
Config: config,
chainConfig: chainConfig,
- chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),
+ chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
}
- evm.interpreter = NewEVMInterpreter(evm, config)
+ evm.interpreter = NewEVMInterpreter(evm)
return evm
}
@@ -161,6 +160,14 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
return evm.interpreter
}
+// SetBlockContext updates the block context of the EVM.
+func (evm *EVM) SetBlockContext(blockCtx BlockContext) {
+ evm.Context = blockCtx
+ num := blockCtx.BlockNumber
+ timestamp := blockCtx.Time
+ evm.chainRules = evm.chainConfig.Rules(num, blockCtx.Random != nil, timestamp)
+}
+
// Call executes the contract associated with the addr with the given input as
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
@@ -183,7 +190,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if evm.Config.Debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
- evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
+ evm.Config.Tracer.CaptureEnd(ret, 0, nil)
} else {
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.Config.Tracer.CaptureExit(ret, 0, nil)
@@ -199,9 +206,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if evm.Config.Debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
- defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
- evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
- }(gas, time.Now())
+ defer func(startGas uint64) { // Lazy evaluation of the parameters
+ evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err)
+ }(gas)
} else {
// Handle tracer events for entering and exiting a call frame
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
@@ -308,7 +315,11 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Debug {
- evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
+ // NOTE: caller must, at all times be a contract. It should never happen
+ // that caller is something other than a Contract.
+ parent := caller.(*Contract)
+ // DELEGATECALL inherits value from parent call
+ evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
@@ -448,8 +459,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
}
- start := time.Now()
-
ret, err := evm.interpreter.Run(contract, nil, false)
// Check whether the max code size has been exceeded, assign err if the case.
@@ -487,7 +496,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if evm.Config.Debug {
if evm.depth == 0 {
- evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
+ evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, err)
} else {
evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
}
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 4c2cb3e5cf..65d46b3436 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -117,20 +117,21 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
return params.SstoreResetGas, nil
}
}
+
// The new gas metering is based on net gas costs (EIP-1283):
//
- // 1. If current value equals new value (this is a no-op), 200 gas is deducted.
- // 2. If current value does not equal new value
- // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
- // 2.1.1. If original value is 0, 20000 gas is deducted.
- // 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
- // 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
- // 2.2.1. If original value is not 0
- // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
- // 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
- // 2.2.2. If original value equals new value (this storage slot is reset)
- // 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
- // 2.2.2.2. Otherwise, add 4800 gas to refund counter.
+ // (1.) If current value equals new value (this is a no-op), 200 gas is deducted.
+ // (2.) If current value does not equal new value
+ // (2.1.) If original value equals current value (this storage slot has not been changed by the current execution context)
+ // (2.1.1.) If original value is 0, 20000 gas is deducted.
+ // (2.1.2.) Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
+ // (2.2.) If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
+ // (2.2.1.) If original value is not 0
+ // (2.2.1.1.) If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
+ // (2.2.1.2.) If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
+ // (2.2.2.) If original value equals new value (this storage slot is reset)
+ // (2.2.2.1.) If original value is 0, add 19800 gas to refund counter.
+ // (2.2.2.2.) Otherwise, add 4800 gas to refund counter.
value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
@@ -162,19 +163,21 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
return params.NetSstoreDirtyGas, nil
}
-// 0. If *gasleft* is less than or equal to 2300, fail the current call.
-// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
-// 2. If current value does not equal new value:
-// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
-// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
-// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
-// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
-// 2.2.1. If original value is not 0:
-// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
-// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
-// 2.2.2. If original value equals new value (this storage slot is reset):
-// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
-// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
+// Here come the EIP220 rules:
+//
+// (0.) If *gasleft* is less than or equal to 2300, fail the current call.
+// (1.) If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
+// (2.) If current value does not equal new value:
+// (2.1.) If original value equals current value (this storage slot has not been changed by the current execution context):
+// (2.1.1.) If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
+// (2.1.2.) Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
+// (2.2.) If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
+// (2.2.1.) If original value is not 0:
+// (2.2.1.1.) If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
+// (2.2.1.2.) If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
+// (2.2.2.) If original value equals new value (this storage slot is reset):
+// (2.2.2.1.) If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
+// (2.2.2.2.) Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas <= params.SstoreSentryGasEIP2200 {
@@ -299,6 +302,39 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
return gas, nil
}
+func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ size, overflow := stack.Back(2).Uint64WithOverflow()
+ if overflow || size > params.MaxInitCodeSize {
+ return 0, ErrGasUintOverflow
+ }
+ // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
+ moreGas := params.InitCodeWordGas * ((size + 31) / 32)
+ if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ size, overflow := stack.Back(2).Uint64WithOverflow()
+ if overflow || size > params.MaxInitCodeSize {
+ return 0, ErrGasUintOverflow
+ }
+ // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
+ moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
+ if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go
index 6cd126c9b4..c3d06e515b 100644
--- a/core/vm/gas_table_test.go
+++ b/core/vm/gas_table_test.go
@@ -17,8 +17,10 @@
package vm
import (
+ "bytes"
"math"
"math/big"
+ "sort"
"testing"
"github.com/ethereum/go-ethereum/common"
@@ -105,3 +107,73 @@ func TestEIP2200(t *testing.T) {
}
}
}
+
+var createGasTests = []struct {
+ code string
+ eip3860 bool
+ gasUsed uint64
+ minimumGas uint64
+}{
+ // legacy create(0, 0, 0xc000) without 3860 used
+ {"0x61C00060006000f0" + "600052" + "60206000F3", false, 41237, 41237},
+ // legacy create(0, 0, 0xc000) _with_ 3860
+ {"0x61C00060006000f0" + "600052" + "60206000F3", true, 44309, 44309},
+ // create2(0, 0, 0xc001, 0) without 3860
+ {"0x600061C00160006000f5" + "600052" + "60206000F3", false, 50471, 50471},
+ // create2(0, 0, 0xc001, 0) (too large), with 3860
+ {"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32012, 100_000},
+ // create2(0, 0, 0xc000, 0)
+ // This case is trying to deploy code at (within) the limit
+ {"0x600061C00060006000f5" + "600052" + "60206000F3", true, 53528, 53528},
+ // create2(0, 0, 0xc001, 0)
+ // This case is trying to deploy code exceeding the limit
+ {"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32024, 100000},
+}
+
+func TestCreateGas(t *testing.T) {
+ for i, tt := range createGasTests {
+ var gasUsed = uint64(0)
+ doCheck := func(testGas int) bool {
+ address := common.BytesToAddress([]byte("contract"))
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
+ statedb.CreateAccount(address)
+ statedb.SetCode(address, hexutil.MustDecode(tt.code))
+ statedb.Finalise(true)
+ vmctx := BlockContext{
+ CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
+ Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
+ BlockNumber: big.NewInt(0),
+ }
+ config := Config{}
+ if tt.eip3860 {
+ config.ExtraEips = []int{3860}
+ }
+
+ vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config)
+ var startGas = uint64(testGas)
+ ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int))
+ if err != nil {
+ return false
+ }
+ gasUsed = startGas - gas
+ if len(ret) != 32 {
+ t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret))
+ }
+ if bytes.Equal(ret, make([]byte, 32)) {
+ // Failure
+ return false
+ }
+ return true
+ }
+ minGas := sort.Search(100_000, doCheck)
+ if uint64(minGas) != tt.minimumGas {
+ t.Fatalf("test %d: min gas error, want %d, have %d", i, tt.minimumGas, minGas)
+ }
+ // If the deployment succeeded, we also check the gas used
+ if minGas < 100_000 {
+ if gasUsed != tt.gasUsed {
+ t.Errorf("test %d: gas used mismatch: have %v, want %v", i, gasUsed, tt.gasUsed)
+ }
+ }
+ }
+}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 13c86b4837..d1d8152fab 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -21,9 +21,9 @@ import (
"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/holiman/uint256"
- "golang.org/x/crypto/sha3"
)
func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
@@ -238,7 +238,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
if interpreter.hasher == nil {
- interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
+ interpreter.hasher = crypto.NewKeccakState()
} else {
interpreter.hasher.Reset()
}
@@ -392,29 +392,29 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
// opExtCodeHash returns the code hash of a specified account.
// There are several cases when the function is called, while we can relay everything
// to `state.GetCodeHash` function to ensure the correctness.
-// (1) Caller tries to get the code hash of a normal contract account, state
-// should return the relative code hash and set it as the result.
//
-// (2) Caller tries to get the code hash of a non-existent account, state should
-// return common.Hash{} and zero will be set as the result.
+// 1. Caller tries to get the code hash of a normal contract account, state
+// should return the relative code hash and set it as the result.
//
-// (3) Caller tries to get the code hash for an account without contract code,
-// state should return emptyCodeHash(0xc5d246...) as the result.
+// 2. Caller tries to get the code hash of a non-existent account, state should
+// return common.Hash{} and zero will be set as the result.
//
-// (4) Caller tries to get the code hash of a precompiled account, the result
-// should be zero or emptyCodeHash.
+// 3. Caller tries to get the code hash for an account without contract code, state
+// should return emptyCodeHash(0xc5d246...) as the result.
//
-// It is worth noting that in order to avoid unnecessary create and clean,
-// all precompile accounts on mainnet have been transferred 1 wei, so the return
-// here should be emptyCodeHash.
-// If the precompile account is not transferred any amount on a private or
+// 4. Caller tries to get the code hash of a precompiled account, the result should be
+// zero or emptyCodeHash.
+//
+// It is worth noting that in order to avoid unnecessary create and clean, all precompile
+// accounts on mainnet have been transferred 1 wei, so the return here should be
+// emptyCodeHash. If the precompile account is not transferred any amount on a private or
// customized chain, the return value will be zero.
//
-// (5) Caller tries to get the code hash for an account which is marked as suicided
-// in the current transaction, the code hash of this account should be returned.
+// 5. Caller tries to get the code hash for an account which is marked as suicided
+// in the current transaction, the code hash of this account should be returned.
//
-// (6) Caller tries to get the code hash for an account which is marked as deleted,
-// this account should be regarded as a non-existent account and zero should be returned.
+// 6. Caller tries to get the code hash for an account which is marked as deleted, this
+// account should be regarded as a non-existent account and zero should be returned.
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek()
address := common.Address(slot.Bytes20())
@@ -466,8 +466,7 @@ func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
}
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- v, _ := uint256.FromBig(interpreter.evm.Context.Time)
- scope.Stack.push(v)
+ scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.Time))
return nil, nil
}
@@ -533,8 +532,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
}
loc := scope.Stack.pop()
val := scope.Stack.pop()
- interpreter.evm.StateDB.SetState(scope.Contract.Address(),
- loc.Bytes32(), val.Bytes32())
+ interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
return nil, nil
}
@@ -640,7 +638,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = scope.Contract.Gas
)
-
// Apply EIP150
gas -= gas / 64
scope.Contract.UseGas(gas)
@@ -703,7 +700,6 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- ret = common.CopyBytes(ret)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
@@ -739,7 +735,6 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- ret = common.CopyBytes(ret)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
@@ -768,7 +763,6 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- ret = common.CopyBytes(ret)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
@@ -797,7 +791,6 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- ret = common.CopyBytes(ret)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
@@ -837,9 +830,9 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
interpreter.evm.StateDB.Suicide(scope.Contract.Address())
- if interpreter.cfg.Debug {
- interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
- interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
+ if interpreter.evm.Config.Debug {
+ interpreter.evm.Config.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
+ interpreter.evm.Config.Tracer.CaptureExit([]byte{}, 0, nil)
}
return nil, errStopToken
}
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 602cde5101..61f001a692 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -25,6 +25,8 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
@@ -45,6 +47,14 @@ var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffff
var commonParams []*twoOperandParams
var twoOpMethods map[string]executionFunc
+type contractRef struct {
+ addr common.Address
+}
+
+func (c contractRef) Address() common.Address {
+ return c.addr
+}
+
func init() {
// Params is a list of common edgecases that should be used for some common tests
params := []string{
@@ -193,7 +203,7 @@ func TestAddMod(t *testing.T) {
var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
- evmInterpreter = NewEVMInterpreter(env, env.Config)
+ evmInterpreter = NewEVMInterpreter(env)
pc = uint64(0)
)
tests := []struct {
@@ -283,7 +293,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
scope = &ScopeContext{nil, stack, nil}
- evmInterpreter = NewEVMInterpreter(env, env.Config)
+ evmInterpreter = NewEVMInterpreter(env)
)
env.interpreter = evmInterpreter
@@ -524,7 +534,7 @@ func TestOpMstore(t *testing.T) {
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
- evmInterpreter = NewEVMInterpreter(env, env.Config)
+ evmInterpreter = NewEVMInterpreter(env)
)
env.interpreter = evmInterpreter
@@ -550,7 +560,7 @@ func BenchmarkOpMstore(bench *testing.B) {
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
- evmInterpreter = NewEVMInterpreter(env, env.Config)
+ evmInterpreter = NewEVMInterpreter(env)
)
env.interpreter = evmInterpreter
@@ -567,12 +577,55 @@ func BenchmarkOpMstore(bench *testing.B) {
}
}
+func TestOpTstore(t *testing.T) {
+ var (
+ statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
+ env = NewEVM(BlockContext{}, TxContext{}, statedb, params.TestChainConfig, Config{})
+ stack = newstack()
+ mem = NewMemory()
+ evmInterpreter = NewEVMInterpreter(env)
+ caller = common.Address{}
+ to = common.Address{1}
+ contractRef = contractRef{caller}
+ contract = NewContract(contractRef, AccountRef(to), new(big.Int), 0)
+ scopeContext = ScopeContext{mem, stack, contract}
+ value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
+ )
+
+ // Add a stateObject for the caller and the contract being called
+ statedb.CreateAccount(caller)
+ statedb.CreateAccount(to)
+
+ env.interpreter = evmInterpreter
+ pc := uint64(0)
+ // push the value to the stack
+ stack.push(new(uint256.Int).SetBytes(value))
+ // push the location to the stack
+ stack.push(new(uint256.Int))
+ opTstore(&pc, evmInterpreter, &scopeContext)
+ // there should be no elements on the stack after TSTORE
+ if stack.len() != 0 {
+ t.Fatal("stack wrong size")
+ }
+ // push the location to the stack
+ stack.push(new(uint256.Int))
+ opTload(&pc, evmInterpreter, &scopeContext)
+ // there should be one element on the stack after TLOAD
+ if stack.len() != 1 {
+ t.Fatal("stack wrong size")
+ }
+ val := stack.peek()
+ if !bytes.Equal(val.Bytes(), value) {
+ t.Fatal("incorrect element read from transient storage")
+ }
+}
+
func BenchmarkOpKeccak256(bench *testing.B) {
var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
- evmInterpreter = NewEVMInterpreter(env, env.Config)
+ evmInterpreter = NewEVMInterpreter(env)
)
env.interpreter = evmInterpreter
mem.Resize(32)
diff --git a/core/vm/interface.go b/core/vm/interface.go
index f255725cfb..c644ea0896 100644
--- a/core/vm/interface.go
+++ b/core/vm/interface.go
@@ -21,6 +21,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/params"
)
// StateDB is an EVM database for full state querying.
@@ -47,6 +48,9 @@ type StateDB interface {
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
+ GetTransientState(addr common.Address, key common.Hash) common.Hash
+ SetTransientState(addr common.Address, key, value common.Hash)
+
Suicide(common.Address) bool
HasSuicided(common.Address) bool
@@ -57,7 +61,6 @@ type StateDB interface {
// is defined according to EIP161 (balance = nonce = code = 0).
Empty(common.Address) bool
- PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
AddressInAccessList(addr common.Address) bool
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
@@ -66,6 +69,7 @@ type StateDB interface {
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
AddSlotToAccessList(addr common.Address, slot common.Hash)
+ Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
RevertToSnapshot(int)
Snapshot() int
@@ -80,12 +84,12 @@ type StateDB interface {
// CallContext provides a basic interface for the EVM calling conventions. The EVM
// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
type CallContext interface {
- // Call another contract
+ // Call calls another contract.
Call(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
- // Take another's contract code and execute within our own context
+ // CallCode takes another contracts code and execute within our own context
CallCode(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
- // Same as CallCode except sender and value is propagated from parent to child scope
+ // DelegateCall is same as CallCode except sender and value is propagated from parent to child scope
DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
- // Create a new contract
+ // Create creates a new contract
Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 40fe23dc51..0ab520b90f 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -17,10 +17,9 @@
package vm
import (
- "hash"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
)
@@ -30,10 +29,7 @@ type Config struct {
Tracer EVMLogger // Opcode logger
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
-
- JumpTable *JumpTable // EVM instruction table, automatically populated if unset
-
- ExtraEips []int // Additional EIPS that are to be enabled
+ ExtraEips []int // Additional EIPS that are to be enabled
}
// ScopeContext contains the things that are per-call, such as stack and memory,
@@ -44,67 +40,61 @@ type ScopeContext struct {
Contract *Contract
}
-// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
-// Read to get a variable amount of data from the hash state. Read is faster than Sum
-// because it doesn't copy the internal state, but also modifies the internal state.
-type keccakState interface {
- hash.Hash
- Read([]byte) (int, error)
-}
-
// EVMInterpreter represents an EVM interpreter
type EVMInterpreter struct {
- evm *EVM
- cfg Config
+ evm *EVM
+ table *JumpTable
- hasher keccakState // Keccak256 hasher instance shared across opcodes
- hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
+ hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes
+ hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
readOnly bool // Whether to throw on stateful modifications
returnData []byte // Last CALL's return data for subsequent reuse
}
// NewEVMInterpreter returns a new instance of the Interpreter.
-func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
+func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
// If jump table was not initialised we set the default one.
- if cfg.JumpTable == nil {
- switch {
- case evm.chainRules.IsMerge:
- cfg.JumpTable = &mergeInstructionSet
- case evm.chainRules.IsLondon:
- cfg.JumpTable = &londonInstructionSet
- case evm.chainRules.IsBerlin:
- cfg.JumpTable = &berlinInstructionSet
- case evm.chainRules.IsIstanbul:
- cfg.JumpTable = &istanbulInstructionSet
- case evm.chainRules.IsConstantinople:
- cfg.JumpTable = &constantinopleInstructionSet
- case evm.chainRules.IsByzantium:
- cfg.JumpTable = &byzantiumInstructionSet
- case evm.chainRules.IsEIP158:
- cfg.JumpTable = &spuriousDragonInstructionSet
- case evm.chainRules.IsEIP150:
- cfg.JumpTable = &tangerineWhistleInstructionSet
- case evm.chainRules.IsHomestead:
- cfg.JumpTable = &homesteadInstructionSet
- default:
- cfg.JumpTable = &frontierInstructionSet
- }
- for i, eip := range cfg.ExtraEips {
- copy := *cfg.JumpTable
- if err := EnableEIP(eip, ©); err != nil {
- // Disable it, so caller can check if it's activated or not
- cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...)
- log.Error("EIP activation failed", "eip", eip, "error", err)
- }
- cfg.JumpTable = ©
- }
+ var table *JumpTable
+ switch {
+ case evm.chainRules.IsShanghai:
+ table = &shanghaiInstructionSet
+ case evm.chainRules.IsMerge:
+ table = &mergeInstructionSet
+ case evm.chainRules.IsLondon:
+ table = &londonInstructionSet
+ case evm.chainRules.IsBerlin:
+ table = &berlinInstructionSet
+ case evm.chainRules.IsIstanbul:
+ table = &istanbulInstructionSet
+ case evm.chainRules.IsConstantinople:
+ table = &constantinopleInstructionSet
+ case evm.chainRules.IsByzantium:
+ table = &byzantiumInstructionSet
+ case evm.chainRules.IsEIP158:
+ table = &spuriousDragonInstructionSet
+ case evm.chainRules.IsEIP150:
+ table = &tangerineWhistleInstructionSet
+ case evm.chainRules.IsHomestead:
+ table = &homesteadInstructionSet
+ default:
+ table = &frontierInstructionSet
}
-
- return &EVMInterpreter{
- evm: evm,
- cfg: cfg,
+ var extraEips []int
+ if len(evm.Config.ExtraEips) > 0 {
+ // Deep-copy jumptable to prevent modification of opcodes in other tables
+ table = copyJumpTable(table)
+ }
+ for _, eip := range evm.Config.ExtraEips {
+ if err := EnableEIP(eip, table); err != nil {
+ // Disable it, so caller can check if it's activated or not
+ log.Error("EIP activation failed", "eip", eip, "error", err)
+ } else {
+ extraEips = append(extraEips, eip)
+ }
}
+ evm.Config.ExtraEips = extraEips
+ return &EVMInterpreter{evm: evm, table: table}
}
// Run loops and evaluates the contract's code with the given input data and returns
@@ -162,13 +152,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}()
contract.Input = input
- if in.cfg.Debug {
+ if in.evm.Config.Debug {
defer func() {
if err != nil {
if !logged {
- in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ in.evm.Config.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
} else {
- in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
+ in.evm.Config.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
}
}
}()
@@ -178,14 +168,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// the execution of one of the operations or until the done flag is set by the
// parent context.
for {
- if in.cfg.Debug {
+ if in.evm.Config.Debug {
// Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, pc, contract.Gas
}
// Get the operation from the jump table and validate the stack to ensure there are
// enough stack items available to perform the operation.
op = contract.GetOp(pc)
- operation := in.cfg.JumpTable[op]
+ operation := in.table[op]
cost = operation.constantGas // For tracing
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
@@ -223,15 +213,15 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
return nil, ErrOutOfGas
}
// Do tracing before memory expansion
- if in.cfg.Debug {
- in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ if in.evm.Config.Debug {
+ in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
logged = true
}
if memorySize > 0 {
mem.Resize(memorySize)
}
- } else if in.cfg.Debug {
- in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ } else if in.evm.Config.Debug {
+ in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
logged = true
}
// execute the operation
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 707b52e79d..91f1be669a 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -55,6 +55,7 @@ var (
berlinInstructionSet = newBerlinInstructionSet()
londonInstructionSet = newLondonInstructionSet()
mergeInstructionSet = newMergeInstructionSet()
+ shanghaiInstructionSet = newShanghaiInstructionSet()
)
// JumpTable contains the EVM opcodes supported at a given fork.
@@ -78,9 +79,16 @@ func validate(jt JumpTable) JumpTable {
return jt
}
+func newShanghaiInstructionSet() JumpTable {
+ instructionSet := newMergeInstructionSet()
+ enable3855(&instructionSet) // PUSH0 instruction
+ enable3860(&instructionSet) // Limit and meter initcode
+ return validate(instructionSet)
+}
+
func newMergeInstructionSet() JumpTable {
instructionSet := newLondonInstructionSet()
- instructionSet[RANDOM] = &operation{
+ instructionSet[PREVRANDAO] = &operation{
execute: opRandom,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
@@ -1043,3 +1051,14 @@ func newFrontierInstructionSet() JumpTable {
return validate(tbl)
}
+
+func copyJumpTable(source *JumpTable) *JumpTable {
+ dest := *source
+ for i, op := range source {
+ if op != nil {
+ opCopy := *op
+ dest[i] = &opCopy
+ }
+ }
+ return &dest
+}
diff --git a/mobile/geth_android.go b/core/vm/jump_table_test.go
similarity index 57%
rename from mobile/geth_android.go
rename to core/vm/jump_table_test.go
index cfdf1c28c9..f67915fff3 100644
--- a/mobile/geth_android.go
+++ b/core/vm/jump_table_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// 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
@@ -14,10 +14,22 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-//go:build android
-// +build android
+package vm
-package geth
+import (
+ "testing"
-// clientIdentifier is a hard coded identifier to report into the network.
-var clientIdentifier = "GethDroid"
+ "github.com/stretchr/testify/require"
+)
+
+// TestJumpTableCopy tests that deep copy is necessery to prevent modify shared jump table
+func TestJumpTableCopy(t *testing.T) {
+ tbl := newMergeInstructionSet()
+ require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
+
+ // a deep copy won't modify the shared jump table
+ deepCopy := copyJumpTable(&tbl)
+ deepCopy[SLOAD].constantGas = 100
+ require.Equal(t, uint64(100), deepCopy[SLOAD].constantGas)
+ require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
+}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 50fccafcf5..2667908a84 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -18,7 +18,6 @@ package vm
import (
"math/big"
- "time"
"github.com/ethereum/go-ethereum/common"
)
@@ -34,7 +33,7 @@ type EVMLogger interface {
CaptureTxEnd(restGas uint64)
// Top call frame
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
- CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
+ CaptureEnd(output []byte, gasUsed uint64, err error)
// Rest of call frames
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
CaptureExit(output []byte, gasUsed uint64, err error)
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index 77d619abb9..9f199eb8f6 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -25,11 +25,7 @@ type OpCode byte
// IsPush specifies if an opcode is a PUSH opcode.
func (op OpCode) IsPush() bool {
- switch op {
- case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
- return true
- }
- return false
+ return PUSH1 <= op && op <= PUSH32
}
// 0x0 range - arithmetic ops.
@@ -99,6 +95,7 @@ const (
NUMBER OpCode = 0x43
DIFFICULTY OpCode = 0x44
RANDOM OpCode = 0x44 // Same as DIFFICULTY
+ PREVRANDAO OpCode = 0x44 // Same as DIFFICULTY
GASLIMIT OpCode = 0x45
CHAINID OpCode = 0x46
SELFBALANCE OpCode = 0x47
@@ -222,6 +219,12 @@ const (
SELFDESTRUCT OpCode = 0xff
)
+// 0xb0 range.
+const (
+ TLOAD OpCode = 0xb3
+ TSTORE OpCode = 0xb4
+)
+
// Since the opcodes aren't all in order we can't use a regular slice.
var opCodeToString = map[OpCode]string{
// 0x0 range - arithmetic ops.
@@ -280,7 +283,7 @@ var opCodeToString = map[OpCode]string{
COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP",
NUMBER: "NUMBER",
- DIFFICULTY: "DIFFICULTY", // TODO (MariusVanDerWijden) rename to RANDOM post merge
+ DIFFICULTY: "DIFFICULTY", // TODO (MariusVanDerWijden) rename to PREVRANDAO post merge
GASLIMIT: "GASLIMIT",
CHAINID: "CHAINID",
SELFBALANCE: "SELFBALANCE",
@@ -376,6 +379,10 @@ var opCodeToString = map[OpCode]string{
LOG3: "LOG3",
LOG4: "LOG4",
+ // 0xb0 range.
+ TLOAD: "TLOAD",
+ TSTORE: "TSTORE",
+
// 0xf0 range.
CREATE: "CREATE",
CALL: "CALL",
@@ -466,6 +473,8 @@ var stringToOp = map[string]OpCode{
"GAS": GAS,
"JUMPDEST": JUMPDEST,
"PUSH0": PUSH0,
+ "TLOAD": TLOAD,
+ "TSTORE": TSTORE,
"PUSH1": PUSH1,
"PUSH2": PUSH2,
"PUSH3": PUSH3,
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index 7861fb92db..56ff5eeabe 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -19,7 +19,6 @@ package runtime
import (
"math"
"math/big"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -37,7 +36,7 @@ type Config struct {
Origin common.Address
Coinbase common.Address
BlockNumber *big.Int
- Time *big.Int
+ Time uint64
GasLimit uint64
GasPrice *big.Int
Value *big.Int
@@ -74,9 +73,6 @@ func setDefaults(cfg *Config) {
if cfg.Difficulty == nil {
cfg.Difficulty = new(big.Int)
}
- if cfg.Time == nil {
- cfg.Time = big.NewInt(time.Now().Unix())
- }
if cfg.GasLimit == 0 {
cfg.GasLimit = math.MaxUint64
}
@@ -117,10 +113,12 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
address = common.BytesToAddress([]byte("contract"))
vmenv = NewEnv(cfg)
sender = vm.AccountRef(cfg.Origin)
+ rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
)
- if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil); rules.IsBerlin {
- cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
- }
+ // Execute the preparatory steps for state transition which includes:
+ // - prepare accessList(post-berlin)
+ // - reset transient storage(eip 1153)
+ cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
cfg.State.CreateAccount(address)
// set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(address, code)
@@ -132,7 +130,6 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
cfg.GasLimit,
cfg.Value,
)
-
return ret, cfg.State, err
}
@@ -149,10 +146,12 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
var (
vmenv = NewEnv(cfg)
sender = vm.AccountRef(cfg.Origin)
+ rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
)
- if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil); rules.IsBerlin {
- cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil)
- }
+ // Execute the preparatory steps for state transition which includes:
+ // - prepare accessList(post-berlin)
+ // - reset transient storage(eip 1153)
+ cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
// Call the code with the given configuration.
code, address, leftOverGas, err := vmenv.Create(
sender,
@@ -171,14 +170,17 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) {
setDefaults(cfg)
- vmenv := NewEnv(cfg)
-
- sender := cfg.State.GetOrNewStateObject(cfg.Origin)
- statedb := cfg.State
+ var (
+ vmenv = NewEnv(cfg)
+ sender = cfg.State.GetOrNewStateObject(cfg.Origin)
+ statedb = cfg.State
+ rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
+ )
+ // Execute the preparatory steps for state transition which includes:
+ // - prepare accessList(post-berlin)
+ // - reset transient storage(eip 1153)
+ statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
- if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil); rules.IsBerlin {
- statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
- }
// Call the code with the given configuration.
ret, leftOverGas, err := vmenv.Call(
sender,
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index ab77e284df..6072591065 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -48,9 +48,6 @@ func TestDefaults(t *testing.T) {
t.Error("expected difficulty to be non nil")
}
- if cfg.Time == nil {
- t.Error("expected time to be non nil")
- }
if cfg.GasLimit == 0 {
t.Error("didn't expect gaslimit to be zero")
}
@@ -174,7 +171,7 @@ func benchmarkEVM_Create(bench *testing.B, code string) {
State: statedb,
GasLimit: 10000000,
Difficulty: big.NewInt(0x200000),
- Time: new(big.Int).SetUint64(0),
+ Time: 0,
Coinbase: common.Address{},
BlockNumber: new(big.Int).SetUint64(1),
ChainConfig: ¶ms.ChainConfig{
@@ -333,7 +330,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
cfg.GasLimit = gas
if len(tracerCode) > 0 {
- tracer, err := tracers.New(tracerCode, new(tracers.Context), nil)
+ tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil)
if err != nil {
b.Fatal(err)
}
@@ -832,7 +829,7 @@ func TestRuntimeJSTracer(t *testing.T) {
statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
statedb.SetCode(common.HexToAddress("0xff"), depressedCode)
- tracer, err := tracers.New(jsTracer, new(tracers.Context), nil)
+ tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
if err != nil {
t.Fatal(err)
}
@@ -868,7 +865,7 @@ func TestJSTracerCreateTx(t *testing.T) {
code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)}
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- tracer, err := tracers.New(jsTracer, new(tracers.Context), nil)
+ tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
if err != nil {
t.Fatal(err)
}
diff --git a/core/vm/testdata/precompiles/bn256ScalarMul.json b/core/vm/testdata/precompiles/bn256ScalarMul.json
index 2a28f6304b..b0427fcc05 100644
--- a/core/vm/testdata/precompiles/bn256ScalarMul.json
+++ b/core/vm/testdata/precompiles/bn256ScalarMul.json
@@ -124,5 +124,12 @@
"Name": "cdetrio15",
"Gas": 6000,
"NoBenchmark": true
+ },
+ {
+ "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000000000000000",
+ "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "Name": "zeroScalar",
+ "Gas": 6000,
+ "NoBenchmark": true
}
]
\ No newline at end of file
diff --git a/crypto/bls12381/g1.go b/crypto/bls12381/g1.go
index d853823cd2..bcb898027a 100644
--- a/crypto/bls12381/g1.go
+++ b/crypto/bls12381/g1.go
@@ -228,7 +228,7 @@ func (g *G1) IsAffine(p *PointG1) bool {
return p[2].isOne()
}
-// Add adds two G1 points p1, p2 and assigns the result to point at first argument.
+// Affine calculates affine form of given G1 point.
func (g *G1) Affine(p *PointG1) *PointG1 {
if g.IsZero(p) {
return p
@@ -247,7 +247,7 @@ func (g *G1) Affine(p *PointG1) *PointG1 {
// Add adds two G1 points p1, p2 and assigns the result to point at first argument.
func (g *G1) Add(r, p1, p2 *PointG1) *PointG1 {
- // http://www.hyperelliptic.org/EFD/gp/auto-shortw-jacobian-0.html#addition-add-2007-bl
+ // www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
if g.IsZero(p1) {
return r.Set(p2)
}
@@ -295,7 +295,7 @@ func (g *G1) Add(r, p1, p2 *PointG1) *PointG1 {
// Double doubles a G1 point p and assigns the result to the point at first argument.
func (g *G1) Double(r, p *PointG1) *PointG1 {
- // http://www.hyperelliptic.org/EFD/gp/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
+ // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
if g.IsZero(p) {
return r.Set(p)
}
diff --git a/crypto/bls12381/g1_test.go b/crypto/bls12381/g1_test.go
index eef9f45055..87140459fb 100644
--- a/crypto/bls12381/g1_test.go
+++ b/crypto/bls12381/g1_test.go
@@ -262,8 +262,9 @@ func BenchmarkG1Add(t *testing.B) {
}
func BenchmarkG1Mul(t *testing.B) {
+ worstCaseScalar, _ := new(big.Int).SetString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)
g1 := NewG1()
- a, e, c := g1.rand(), q, PointG1{}
+ a, e, c := g1.rand(), worstCaseScalar, PointG1{}
t.ResetTimer()
for i := 0; i < t.N; i++ {
g1.MulScalar(&c, a, e)
diff --git a/crypto/bls12381/g2.go b/crypto/bls12381/g2.go
index c2ca959bcc..4d6f1ff11d 100644
--- a/crypto/bls12381/g2.go
+++ b/crypto/bls12381/g2.go
@@ -267,7 +267,7 @@ func (g *G2) Affine(p *PointG2) *PointG2 {
// Add adds two G2 points p1, p2 and assigns the result to point at first argument.
func (g *G2) Add(r, p1, p2 *PointG2) *PointG2 {
- // http://www.hyperelliptic.org/EFD/gp/auto-shortw-jacobian-0.html#addition-add-2007-bl
+ // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
if g.IsZero(p1) {
return r.Set(p2)
}
@@ -315,7 +315,7 @@ func (g *G2) Add(r, p1, p2 *PointG2) *PointG2 {
// Double doubles a G2 point p and assigns the result to the point at first argument.
func (g *G2) Double(r, p *PointG2) *PointG2 {
- // http://www.hyperelliptic.org/EFD/gp/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
+ // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
if g.IsZero(p) {
return r.Set(p)
}
diff --git a/crypto/bls12381/g2_test.go b/crypto/bls12381/g2_test.go
index f16f0e5eea..4d1f3a19ac 100644
--- a/crypto/bls12381/g2_test.go
+++ b/crypto/bls12381/g2_test.go
@@ -265,8 +265,9 @@ func BenchmarkG2Add(t *testing.B) {
}
func BenchmarkG2Mul(t *testing.B) {
+ worstCaseScalar, _ := new(big.Int).SetString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)
g2 := NewG2()
- a, e, c := g2.rand(), q, PointG2{}
+ a, e, c := g2.rand(), worstCaseScalar, PointG2{}
t.ResetTimer()
for i := 0; i < t.N; i++ {
g2.MulScalar(&c, a, e)
diff --git a/crypto/crypto.go b/crypto/crypto.go
index 45ea72747e..e51b63beca 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -35,7 +35,7 @@ import (
"golang.org/x/crypto/sha3"
)
-//SignatureLength indicates the byte length required to carry a signature with recovery id.
+// SignatureLength indicates the byte length required to carry a signature with recovery id.
const SignatureLength = 64 + 1 // 64 bytes ECDSA signature + 1 byte recovery id
// RecoveryIDOffset points to the byte offset within the signature that contains the recovery id.
diff --git a/crypto/secp256k1/curve.go b/crypto/secp256k1/curve.go
index fa1b199a34..9b26ab2928 100644
--- a/crypto/secp256k1/curve.go
+++ b/crypto/secp256k1/curve.go
@@ -105,7 +105,6 @@ func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
return x3.Cmp(y2) == 0
}
-//TODO: double check if the function is okay
// affineFromJacobian reverses the Jacobian transform. See the comment at the
// top of the file.
func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
diff --git a/crypto/secp256k1/libsecp256k1/contrib/dummy.go b/crypto/secp256k1/libsecp256k1/contrib/dummy.go
index fda594be99..2c946210c5 100644
--- a/crypto/secp256k1/libsecp256k1/contrib/dummy.go
+++ b/crypto/secp256k1/libsecp256k1/contrib/dummy.go
@@ -1,3 +1,4 @@
+//go:build dummy
// +build dummy
// Package c contains only a C file.
diff --git a/crypto/secp256k1/libsecp256k1/dummy.go b/crypto/secp256k1/libsecp256k1/dummy.go
index 379b16992f..04bbe3d76e 100644
--- a/crypto/secp256k1/libsecp256k1/dummy.go
+++ b/crypto/secp256k1/libsecp256k1/dummy.go
@@ -1,3 +1,4 @@
+//go:build dummy
// +build dummy
// Package c contains only a C file.
diff --git a/crypto/secp256k1/libsecp256k1/include/dummy.go b/crypto/secp256k1/libsecp256k1/include/dummy.go
index 5af540c73c..64c71b8451 100644
--- a/crypto/secp256k1/libsecp256k1/include/dummy.go
+++ b/crypto/secp256k1/libsecp256k1/include/dummy.go
@@ -1,3 +1,4 @@
+//go:build dummy
// +build dummy
// Package c contains only a C file.
diff --git a/crypto/secp256k1/libsecp256k1/src/dummy.go b/crypto/secp256k1/libsecp256k1/src/dummy.go
index 65868f38a8..2df270adc3 100644
--- a/crypto/secp256k1/libsecp256k1/src/dummy.go
+++ b/crypto/secp256k1/libsecp256k1/src/dummy.go
@@ -1,3 +1,4 @@
+//go:build dummy
// +build dummy
// Package c contains only a C file.
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/dummy.go b/crypto/secp256k1/libsecp256k1/src/modules/dummy.go
index 3c7a696439..99c538db51 100644
--- a/crypto/secp256k1/libsecp256k1/src/modules/dummy.go
+++ b/crypto/secp256k1/libsecp256k1/src/modules/dummy.go
@@ -1,3 +1,4 @@
+//go:build dummy
// +build dummy
// Package c contains only a C file.
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go b/crypto/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go
index b6fc38327e..48c2e0aa54 100644
--- a/crypto/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go
+++ b/crypto/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go
@@ -1,3 +1,4 @@
+//go:build dummy
// +build dummy
// Package c contains only a C file.
diff --git a/crypto/secp256k1/libsecp256k1/src/modules/recovery/dummy.go b/crypto/secp256k1/libsecp256k1/src/modules/recovery/dummy.go
index b9491f0cb9..8efbd7abe7 100644
--- a/crypto/secp256k1/libsecp256k1/src/modules/recovery/dummy.go
+++ b/crypto/secp256k1/libsecp256k1/src/modules/recovery/dummy.go
@@ -1,3 +1,4 @@
+//go:build dummy
// +build dummy
// Package c contains only a C file.
diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go
index bd72d97d3b..3a32755f5e 100644
--- a/crypto/signature_cgo.go
+++ b/crypto/signature_cgo.go
@@ -48,7 +48,7 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
//
// This function is susceptible to chosen plaintext attacks that can leak
// information about the private key that is used for signing. Callers must
-// be aware that the given digest cannot be chosen by an adversery. Common
+// be aware that the given digest cannot be chosen by an adversary. Common
// solution is to hash any input before calculating the signature.
//
// The produced signature is in the [R || S || V] format where V is 0 or 1.
diff --git a/crypto/signify/signify_test.go b/crypto/signify/signify_test.go
index ba85d2fc43..9bac2c825f 100644
--- a/crypto/signify/signify_test.go
+++ b/crypto/signify/signify_test.go
@@ -20,10 +20,9 @@
package signify
import (
- "math/rand"
+ "crypto/rand"
"os"
"testing"
- "time"
"github.com/jedisct1/go-minisign"
)
@@ -41,8 +40,6 @@ func TestSignify(t *testing.T) {
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
- rand.Seed(time.Now().UnixNano())
-
data := make([]byte, 1024)
rand.Read(data)
tmpFile.Write(data)
@@ -85,8 +82,6 @@ func TestSignifyTrustedCommentTooManyLines(t *testing.T) {
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
- rand.Seed(time.Now().UnixNano())
-
data := make([]byte, 1024)
rand.Read(data)
tmpFile.Write(data)
@@ -110,8 +105,6 @@ func TestSignifyTrustedCommentTooManyLinesLF(t *testing.T) {
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
- rand.Seed(time.Now().UnixNano())
-
data := make([]byte, 1024)
rand.Read(data)
tmpFile.Write(data)
@@ -135,8 +128,6 @@ func TestSignifyTrustedCommentEmpty(t *testing.T) {
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
- rand.Seed(time.Now().UnixNano())
-
data := make([]byte, 1024)
rand.Read(data)
tmpFile.Write(data)
diff --git a/docs/postmortems/2021-08-22-split-postmortem.md b/docs/postmortems/2021-08-22-split-postmortem.md
index 2004f0f287..962aa51f64 100644
--- a/docs/postmortems/2021-08-22-split-postmortem.md
+++ b/docs/postmortems/2021-08-22-split-postmortem.md
@@ -68,7 +68,7 @@ Since we had merged the removal of `ETH65`, if the entire network were to upgrad
## Exploit
-At block [13107518](https://etherscan.io/block/13107518), mined at (Aug-27-2021 12:50:07 PM +UTC), a minority chain split occurred. The discord user @AlexSSD7 notified the allcoredevs-channel on the Eth R&D discord, on Aug 27 13:09 UTC.
+At block [13107518](https://etherscan.io/block/13107518), mined at Aug-27-2021 12:50:07 PM +UTC, a minority chain split occurred. The discord user @AlexSSD7 notified the allcoredevs-channel on the Eth R&D discord, on Aug 27 13:09 UTC.
At 14:09 UTC, it was confirmed that the transaction `0x1cb6fb36633d270edefc04d048145b4298e67b8aa82a9e5ec4aa1435dd770ce4` had triggered the bug, leading to a minority-split of the chain. The term 'minority split' means that the majority of miners continued to mine on the correct chain.
diff --git a/eth/api.go b/eth/api.go
index 5642ef4c3f..ceed85ef57 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -405,17 +405,22 @@ type storageEntry struct {
}
// StorageRangeAt returns the storage at the given block height and transaction index.
-func (api *DebugAPI) StorageRangeAt(blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
+func (api *DebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
// Retrieve the block
block := api.eth.blockchain.GetBlockByHash(blockHash)
if block == nil {
return StorageRangeResult{}, fmt.Errorf("block %#x not found", blockHash)
}
- _, _, statedb, err := api.eth.stateAtTransaction(block, txIndex, 0)
+ _, _, statedb, release, err := api.eth.stateAtTransaction(ctx, block, txIndex, 0)
+ if err != nil {
+ return StorageRangeResult{}, err
+ }
+ defer release()
+
+ st, err := statedb.StorageTrie(contractAddress)
if err != nil {
return StorageRangeResult{}, err
}
- st := statedb.StorageTrie(contractAddress)
if st == nil {
return StorageRangeResult{}, fmt.Errorf("account %x doesn't exist", contractAddress)
}
@@ -506,11 +511,11 @@ func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]c
}
triedb := api.eth.BlockChain().StateCache().TrieDB()
- oldTrie, err := trie.NewStateTrie(common.Hash{}, startBlock.Root(), triedb)
+ oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startBlock.Root()), triedb)
if err != nil {
return nil, err
}
- newTrie, err := trie.NewStateTrie(common.Hash{}, endBlock.Root(), triedb)
+ newTrie, err := trie.NewStateTrie(trie.StateTrieID(endBlock.Root()), triedb)
if err != nil {
return nil, err
}
@@ -588,3 +593,14 @@ func (api *DebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64, error
}
return 0, errors.New("no state found")
}
+
+// SetTrieFlushInterval configures how often in-memory tries are persisted
+// to disk. The value is in terms of block processing time, not wall clock.
+func (api *DebugAPI) SetTrieFlushInterval(interval string) error {
+ t, err := time.ParseDuration(interval)
+ if err != nil {
+ return err
+ }
+ api.eth.blockchain.SetTrieFlushInterval(t)
+ return nil
+}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 0958c8bb3b..d43e6c1a61 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -31,9 +31,11 @@ import (
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/gasprice"
+ "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
@@ -135,6 +137,17 @@ func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ
return b.eth.blockchain.GetBlockByHash(hash), nil
}
+// GetBody returns body of a block. It does not resolve special block numbers.
+func (b *EthAPIBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
+ if number < 0 || hash == (common.Hash{}) {
+ return nil, errors.New("invalid arguments; expect hash and no special block numbers")
+ }
+ if body := b.eth.blockchain.GetBody(hash); body != nil {
+ return body, nil
+ }
+ return nil, errors.New("block body not found")
+}
+
func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
if blockNr, ok := blockNrOrHash.Number(); ok {
return b.BlockByNumber(ctx, blockNr)
@@ -215,13 +228,12 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
}
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
- vmError := func() error { return nil }
if vmConfig == nil {
vmConfig = b.eth.blockchain.GetVMConfig()
}
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
- return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), vmError, nil
+ return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), state.Error, nil
}
func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
@@ -286,7 +298,7 @@ func (b *EthAPIBackend) TxPoolContentFrom(addr common.Address) (types.Transactio
return b.eth.TxPool().ContentFrom(addr)
}
-func (b *EthAPIBackend) TxPool() *core.TxPool {
+func (b *EthAPIBackend) TxPool() *txpool.TxPool {
return b.eth.TxPool()
}
@@ -365,12 +377,12 @@ func (b *EthAPIBackend) StartMining(threads int) error {
return b.eth.StartMining(threads)
}
-func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive, preferDisk bool) (*state.StateDB, error) {
- return b.eth.StateAtBlock(block, reexec, base, checkLive, preferDisk)
+func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, tracers.StateReleaseFunc, error) {
+ return b.eth.StateAtBlock(ctx, block, reexec, base, readOnly, preferDisk)
}
-func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
- return b.eth.stateAtTransaction(block, txIndex, reexec)
+func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
+ return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
}
// SetHistoricalBlocksSynced returns a bool for BSP replica config (Historical mode :0 , Live mode: 1)
diff --git a/eth/api_test.go b/eth/api_test.go
index 250591c107..fca17f1217 100644
--- a/eth/api_test.go
+++ b/eth/api_test.go
@@ -209,7 +209,11 @@ func TestStorageRangeAt(t *testing.T) {
},
}
for _, test := range tests {
- result, err := storageRangeAt(state.StorageTrie(addr), test.start, test.limit)
+ tr, err := state.StorageTrie(addr)
+ if err != nil {
+ t.Error(err)
+ }
+ result, err := storageRangeAt(tr, test.start, test.limit)
if err != nil {
t.Error(err)
}
diff --git a/eth/backend.go b/eth/backend.go
index a05e61ad09..014ed58131 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -22,7 +22,6 @@ import (
"fmt"
"math/big"
"runtime"
- "strings"
"sync"
"sync/atomic"
@@ -36,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/pruner"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
@@ -67,7 +67,7 @@ type Ethereum struct {
config *ethconfig.Config
// Handlers
- txPool *core.TxPool
+ txPool *txpool.TxPool
blockchain *core.BlockChain
handler *handler
ethDialCandidates enode.Iterator
@@ -128,38 +128,30 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
}
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
- // Transfer mining-related config to the ethash config.
- ethashConfig := config.Ethash
- ethashConfig.NotifyFull = config.Miner.NotifyFull
-
// Assemble the Ethereum object
chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/", false)
if err != nil {
return nil, err
}
- chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideTerminalTotalDifficulty, config.OverrideTerminalTotalDifficultyPassed)
- if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
- return nil, genesisErr
- }
- log.Info("")
- log.Info(strings.Repeat("-", 153))
- for _, line := range strings.Split(chainConfig.String(), "\n") {
- log.Info(line)
- }
- log.Info(strings.Repeat("-", 153))
- log.Info("")
-
if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil {
log.Error("Failed to recover state", "error", err)
}
- merger := consensus.NewMerger(chainDb)
+ // Transfer mining-related config to the ethash config.
+ ethashConfig := config.Ethash
+ ethashConfig.NotifyFull = config.Miner.NotifyFull
+ cliqueConfig, err := core.LoadCliqueConfig(chainDb, config.Genesis)
+ if err != nil {
+ return nil, err
+ }
+ engine := ethconfig.CreateConsensusEngine(stack, ðashConfig, cliqueConfig, config.Miner.Notify, config.Miner.Noverify, chainDb)
+
eth := &Ethereum{
config: config,
- merger: merger,
+ merger: consensus.NewMerger(chainDb),
chainDb: chainDb,
eventMux: stack.EventMux(),
accountManager: stack.AccountManager(),
- engine: ethconfig.CreateConsensusEngine(stack, chainConfig, ðashConfig, config.Miner.Notify, config.Miner.Noverify, chainDb),
+ engine: engine,
closeBloomHandler: make(chan struct{}),
networkID: config.NetworkId,
gasPrice: config.Miner.GasPrice,
@@ -218,16 +210,15 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
Preimages: config.Preimages,
}
)
- eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit)
+ // Override the chain config with provided settings.
+ var overrides core.ChainOverrides
+ if config.OverrideShanghai != nil {
+ overrides.OverrideShanghai = config.OverrideShanghai
+ }
+ eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit)
if err != nil {
return nil, err
}
- // Rewind the chain in case of an incompatible config upgrade.
- if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
- log.Warn("Rewinding chain to upgrade configuration", "err", compat)
- eth.blockchain.SetHead(compat.RewindTo)
- rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
- }
eth.bloomIndexer.Start(eth.blockchain)
for _, bRRepl := range eth.blockReplicators {
@@ -237,19 +228,19 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if config.TxPool.Journal != "" {
config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal)
}
- eth.txPool = core.NewTxPool(config.TxPool, chainConfig, eth.blockchain)
+ eth.txPool = txpool.NewTxPool(config.TxPool, eth.blockchain.Config(), eth.blockchain)
// Permit the downloader to use the trie cache allowance during fast sync
cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit
checkpoint := config.Checkpoint
if checkpoint == nil {
- checkpoint = params.TrustedCheckpoints[genesisHash]
+ checkpoint = params.TrustedCheckpoints[eth.blockchain.Genesis().Hash()]
}
if eth.handler, err = newHandler(&handlerConfig{
Database: chainDb,
Chain: eth.blockchain,
TxPool: eth.txPool,
- Merger: merger,
+ Merger: eth.merger,
Network: config.NetworkId,
Sync: config.SyncMode,
BloomCache: uint64(cacheLimit),
@@ -260,7 +251,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
return nil, err
}
- eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
+ eth.miner = miner.New(eth, &config.Miner, eth.blockchain.Config(), eth.EventMux(), eth.engine, eth.isLocalBlock)
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil}
@@ -359,18 +350,6 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
if etherbase != (common.Address{}) {
return etherbase, nil
}
- if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
- if accounts := wallets[0].Accounts(); len(accounts) > 0 {
- etherbase := accounts[0].Address
-
- s.lock.Lock()
- s.etherbase = etherbase
- s.lock.Unlock()
-
- log.Info("Etherbase automatically configured", "address", etherbase)
- return etherbase, nil
- }
- }
return common.Address{}, fmt.Errorf("etherbase must be explicitly specified")
}
@@ -486,7 +465,7 @@ func (s *Ethereum) StartMining(threads int) error {
// introduced to speed sync times.
atomic.StoreUint32(&s.handler.acceptTxs, 1)
- go s.miner.Start(eb)
+ go s.miner.Start()
}
return nil
}
@@ -510,7 +489,7 @@ func (s *Ethereum) Miner() *miner.Miner { return s.miner }
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
-func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
+func (s *Ethereum) TxPool() *txpool.TxPool { return s.txPool }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
func (s *Ethereum) Engine() consensus.Engine { return s.engine }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go
index b159f34e64..95eed408f0 100644
--- a/eth/catalyst/api.go
+++ b/eth/catalyst/api.go
@@ -18,22 +18,21 @@
package catalyst
import (
- "crypto/sha256"
- "encoding/binary"
"errors"
"fmt"
"math/big"
"sync"
"time"
+ "github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/core/beacon"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -80,6 +79,19 @@ const (
beaconUpdateWarnFrequency = 5 * time.Minute
)
+// All methods provided over the engine endpoint.
+var caps = []string{
+ "engine_forkchoiceUpdatedV1",
+ "engine_forkchoiceUpdatedV2",
+ "engine_exchangeTransitionConfigurationV1",
+ "engine_getPayloadV1",
+ "engine_getPayloadV2",
+ "engine_newPayloadV1",
+ "engine_newPayloadV2",
+ "engine_getPayloadBodiesByHashV1",
+ "engine_getPayloadBodiesByRangeV1",
+}
+
type ConsensusAPI struct {
eth *eth.Ethereum
@@ -105,7 +117,7 @@ type ConsensusAPI struct {
// problematic, so we will only track the head chain segment of a bad
// chain to allow discarding progressing bad chains and side chains,
// without tracking too much bad data.
- invalidBlocksHits map[common.Hash]int // Emhemeral cache to track invalid blocks and their hit count
+ invalidBlocksHits map[common.Hash]int // Ephemeral cache to track invalid blocks and their hit count
invalidTipsets map[common.Hash]*types.Header // Ephemeral cache to track invalid tipsets and their bad ancestor
invalidLock sync.Mutex // Protects the invalid maps from concurrent access
@@ -120,6 +132,7 @@ type ConsensusAPI struct {
lastNewPayloadLock sync.Mutex
forkchoiceLock sync.Mutex // Lock for the forkChoiceUpdated method
+ newPayloadLock sync.Mutex // Lock for the NewPayload method
}
// NewConsensusAPI creates a new consensus api for the given backend.
@@ -142,23 +155,64 @@ func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI {
}
// ForkchoiceUpdatedV1 has several responsibilities:
-// If the method is called with an empty head block:
-// we return success, which can be used to check if the engine API is enabled
-// If the total difficulty was not reached:
-// we return INVALID
-// If the finalizedBlockHash is set:
-// we check if we have the finalizedBlockHash in our db, if not we start a sync
-// We try to set our blockchain to the headBlock
-// If there are payloadAttributes:
-// we try to assemble a block with the payloadAttributes and return its payloadID
-func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) {
+//
+// We try to set our blockchain to the headBlock.
+//
+// If the method is called with an empty head block: we return success, which can be used
+// to check if the engine API is enabled.
+//
+// If the total difficulty was not reached: we return INVALID.
+//
+// If the finalizedBlockHash is set: we check if we have the finalizedBlockHash in our db,
+// if not we start a sync.
+//
+// If there are payloadAttributes: we try to assemble a block with the payloadAttributes
+// and return its payloadID.
+func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
+ if payloadAttributes != nil {
+ if payloadAttributes.Withdrawals != nil {
+ return engine.STATUS_INVALID, engine.InvalidParams.With(fmt.Errorf("withdrawals not supported in V1"))
+ }
+ if api.eth.BlockChain().Config().IsShanghai(payloadAttributes.Timestamp) {
+ return engine.STATUS_INVALID, engine.InvalidParams.With(fmt.Errorf("forkChoiceUpdateV1 called post-shanghai"))
+ }
+ }
+ return api.forkchoiceUpdated(update, payloadAttributes)
+}
+
+// ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload attributes.
+func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
+ if payloadAttributes != nil {
+ if err := api.verifyPayloadAttributes(payloadAttributes); err != nil {
+ return engine.STATUS_INVALID, engine.InvalidParams.With(err)
+ }
+ }
+ return api.forkchoiceUpdated(update, payloadAttributes)
+}
+
+func (api *ConsensusAPI) verifyPayloadAttributes(attr *engine.PayloadAttributes) error {
+ if !api.eth.BlockChain().Config().IsShanghai(attr.Timestamp) {
+ // Reject payload attributes with withdrawals before shanghai
+ if attr.Withdrawals != nil {
+ return errors.New("withdrawals before shanghai")
+ }
+ } else {
+ // Reject payload attributes with nil withdrawals after shanghai
+ if attr.Withdrawals == nil {
+ return errors.New("missing withdrawals list")
+ }
+ }
+ return nil
+}
+
+func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
api.forkchoiceLock.Lock()
defer api.forkchoiceLock.Unlock()
log.Trace("Engine API request received", "method", "ForkchoiceUpdated", "head", update.HeadBlockHash, "finalized", update.FinalizedBlockHash, "safe", update.SafeBlockHash)
if update.HeadBlockHash == (common.Hash{}) {
log.Warn("Forkchoice requested update to zero hash")
- return beacon.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
+ return engine.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
}
// Stash away the last update to warn the user if the beacon client goes offline
api.lastForkchoiceLock.Lock()
@@ -172,7 +226,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
if block == nil {
// If this block was previously invalidated, keep rejecting it here too
if res := api.checkInvalidAncestor(update.HeadBlockHash, update.HeadBlockHash); res != nil {
- return beacon.ForkChoiceResponse{PayloadStatus: *res, PayloadID: nil}, nil
+ return engine.ForkChoiceResponse{PayloadStatus: *res, PayloadID: nil}, nil
}
// If the head hash is unknown (was not given to us in a newPayload request),
// we cannot resolve the header, so not much to do. This could be extended in
@@ -181,7 +235,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
header := api.remoteBlocks.get(update.HeadBlockHash)
if header == nil {
log.Warn("Forkchoice requested unknown head", "hash", update.HeadBlockHash)
- return beacon.STATUS_SYNCING, nil
+ return engine.STATUS_SYNCING, nil
}
// Header advertised via a past newPayload request. Start syncing to it.
// Before we do however, make sure any legacy sync in switched off so we
@@ -192,9 +246,9 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
}
log.Info("Forkchoice requested sync to new head", "number", header.Number, "hash", header.Hash())
if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), header); err != nil {
- return beacon.STATUS_SYNCING, err
+ return engine.STATUS_SYNCING, err
}
- return beacon.STATUS_SYNCING, nil
+ return engine.STATUS_SYNCING, nil
}
// Block is known locally, just sanity check that the beacon client does not
// attempt to push us back to before the merge.
@@ -206,27 +260,27 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
)
if td == nil || (block.NumberU64() > 0 && ptd == nil) {
log.Error("TDs unavailable for TTD check", "number", block.NumberU64(), "hash", update.HeadBlockHash, "td", td, "parent", block.ParentHash(), "ptd", ptd)
- return beacon.STATUS_INVALID, errors.New("TDs unavailable for TDD check")
+ return engine.STATUS_INVALID, errors.New("TDs unavailable for TDD check")
}
if td.Cmp(ttd) < 0 {
log.Error("Refusing beacon update to pre-merge", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
- return beacon.ForkChoiceResponse{PayloadStatus: beacon.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
+ return engine.ForkChoiceResponse{PayloadStatus: engine.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
}
if block.NumberU64() > 0 && ptd.Cmp(ttd) >= 0 {
log.Error("Parent block is already post-ttd", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
- return beacon.ForkChoiceResponse{PayloadStatus: beacon.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
+ return engine.ForkChoiceResponse{PayloadStatus: engine.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
}
}
- valid := func(id *beacon.PayloadID) beacon.ForkChoiceResponse {
- return beacon.ForkChoiceResponse{
- PayloadStatus: beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &update.HeadBlockHash},
+ valid := func(id *engine.PayloadID) engine.ForkChoiceResponse {
+ return engine.ForkChoiceResponse{
+ PayloadStatus: engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &update.HeadBlockHash},
PayloadID: id,
}
}
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), block.NumberU64()) != update.HeadBlockHash {
// Block is not canonical, set head.
if latestValid, err := api.eth.BlockChain().SetCanonical(block); err != nil {
- return beacon.ForkChoiceResponse{PayloadStatus: beacon.PayloadStatusV1{Status: beacon.INVALID, LatestValidHash: &latestValid}}, err
+ return engine.ForkChoiceResponse{PayloadStatus: engine.PayloadStatusV1{Status: engine.INVALID, LatestValidHash: &latestValid}}, err
}
} else if api.eth.BlockChain().CurrentBlock().Hash() == update.HeadBlockHash {
// If the specified head matches with our local head, do nothing and keep
@@ -250,10 +304,10 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
finalBlock := api.eth.BlockChain().GetBlockByHash(update.FinalizedBlockHash)
if finalBlock == nil {
log.Warn("Final block not available in database", "hash", update.FinalizedBlockHash)
- return beacon.STATUS_INVALID, beacon.InvalidForkChoiceState.With(errors.New("final block not available in database"))
+ return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("final block not available in database"))
} else if rawdb.ReadCanonicalHash(api.eth.ChainDb(), finalBlock.NumberU64()) != update.FinalizedBlockHash {
log.Warn("Final block not in canonical chain", "number", block.NumberU64(), "hash", update.HeadBlockHash)
- return beacon.STATUS_INVALID, beacon.InvalidForkChoiceState.With(errors.New("final block not in canonical chain"))
+ return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("final block not in canonical chain"))
}
// Set the finalized block
api.eth.BlockChain().SetFinalized(finalBlock)
@@ -263,34 +317,38 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
safeBlock := api.eth.BlockChain().GetBlockByHash(update.SafeBlockHash)
if safeBlock == nil {
log.Warn("Safe block not available in database")
- return beacon.STATUS_INVALID, beacon.InvalidForkChoiceState.With(errors.New("safe block not available in database"))
+ return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("safe block not available in database"))
}
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), safeBlock.NumberU64()) != update.SafeBlockHash {
log.Warn("Safe block not in canonical chain")
- return beacon.STATUS_INVALID, beacon.InvalidForkChoiceState.With(errors.New("safe block not in canonical chain"))
+ return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("safe block not in canonical chain"))
}
// Set the safe block
api.eth.BlockChain().SetSafe(safeBlock)
}
// If payload generation was requested, create a new block to be potentially
// sealed by the beacon client. The payload will be requested later, and we
- // might replace it arbitrarily many times in between.
+ // will replace it arbitrarily many times in between.
if payloadAttributes != nil {
- // Create an empty block first which can be used as a fallback
- empty, err := api.eth.Miner().GetSealingBlockSync(update.HeadBlockHash, payloadAttributes.Timestamp, payloadAttributes.SuggestedFeeRecipient, payloadAttributes.Random, true)
- if err != nil {
- log.Error("Failed to create empty sealing payload", "err", err)
- return valid(nil), beacon.InvalidPayloadAttributes.With(err)
+ args := &miner.BuildPayloadArgs{
+ Parent: update.HeadBlockHash,
+ Timestamp: payloadAttributes.Timestamp,
+ FeeRecipient: payloadAttributes.SuggestedFeeRecipient,
+ Random: payloadAttributes.Random,
+ Withdrawals: payloadAttributes.Withdrawals,
+ }
+ id := args.Id()
+ // If we already are busy generating this work, then we do not need
+ // to start a second process.
+ if api.localBlocks.has(id) {
+ return valid(&id), nil
}
- // Send a request to generate a full block in the background.
- // The result can be obtained via the returned channel.
- resCh, err := api.eth.Miner().GetSealingBlockAsync(update.HeadBlockHash, payloadAttributes.Timestamp, payloadAttributes.SuggestedFeeRecipient, payloadAttributes.Random, false)
+ payload, err := api.eth.Miner().BuildPayload(args)
if err != nil {
- log.Error("Failed to create async sealing payload", "err", err)
- return valid(nil), beacon.InvalidPayloadAttributes.With(err)
+ log.Error("Failed to build payload", "err", err)
+ return valid(nil), engine.InvalidPayloadAttributes.With(err)
}
- id := computePayloadId(update.HeadBlockHash, payloadAttributes)
- api.localBlocks.put(id, &payload{empty: empty, result: resCh})
+ api.localBlocks.put(id, payload)
return valid(&id), nil
}
return valid(nil), nil
@@ -298,7 +356,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
// ExchangeTransitionConfigurationV1 checks the given configuration against
// the configuration of the node.
-func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config beacon.TransitionConfigurationV1) (*beacon.TransitionConfigurationV1, error) {
+func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.TransitionConfigurationV1) (*engine.TransitionConfigurationV1, error) {
log.Trace("Engine API request received", "method", "ExchangeTransitionConfiguration", "ttd", config.TerminalTotalDifficulty)
if config.TerminalTotalDifficulty == nil {
return nil, errors.New("invalid terminal total difficulty")
@@ -315,7 +373,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config beacon.Transit
}
if config.TerminalBlockHash != (common.Hash{}) {
if hash := api.eth.BlockChain().GetCanonicalHash(uint64(config.TerminalBlockNumber)); hash == config.TerminalBlockHash {
- return &beacon.TransitionConfigurationV1{
+ return &engine.TransitionConfigurationV1{
TerminalTotalDifficulty: (*hexutil.Big)(ttd),
TerminalBlockHash: config.TerminalBlockHash,
TerminalBlockNumber: config.TerminalBlockNumber,
@@ -323,26 +381,74 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config beacon.Transit
}
return nil, fmt.Errorf("invalid terminal block hash")
}
- return &beacon.TransitionConfigurationV1{TerminalTotalDifficulty: (*hexutil.Big)(ttd)}, nil
+ return &engine.TransitionConfigurationV1{TerminalTotalDifficulty: (*hexutil.Big)(ttd)}, nil
}
// GetPayloadV1 returns a cached payload by id.
-func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableDataV1, error) {
+func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.ExecutableData, error) {
+ data, err := api.getPayload(payloadID)
+ if err != nil {
+ return nil, err
+ }
+ return data.ExecutionPayload, nil
+}
+
+// GetPayloadV2 returns a cached payload by id.
+func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
+ return api.getPayload(payloadID)
+}
+
+func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
data := api.localBlocks.get(payloadID)
if data == nil {
- return nil, beacon.UnknownPayload
+ return nil, engine.UnknownPayload
}
return data, nil
}
// NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
-func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.PayloadStatusV1, error) {
- log.Trace("Engine API request received", "method", "ExecutePayload", "number", params.Number, "hash", params.BlockHash)
- block, err := beacon.ExecutableDataToBlock(params)
+func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
+ if params.Withdrawals != nil {
+ return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(fmt.Errorf("withdrawals not supported in V1"))
+ }
+ return api.newPayload(params)
+}
+
+// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
+func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
+ if api.eth.BlockChain().Config().IsShanghai(params.Timestamp) {
+ if params.Withdrawals == nil {
+ return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(fmt.Errorf("nil withdrawals post-shanghai"))
+ }
+ } else if params.Withdrawals != nil {
+ return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(fmt.Errorf("non-nil withdrawals pre-shanghai"))
+ }
+ return api.newPayload(params)
+}
+
+func (api *ConsensusAPI) newPayload(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
+ // The locking here is, strictly, not required. Without these locks, this can happen:
+ //
+ // 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to
+ // api.eth.BlockChain().InsertBlockWithoutSetHead, where it is blocked on
+ // e.g database compaction.
+ // 2. The call times out on the CL layer, which issues another NewPayload (execdata-N) call.
+ // Similarly, this also get stuck on the same place. Importantly, since the
+ // first call has not gone through, the early checks for "do we already have this block"
+ // will all return false.
+ // 3. When the db compaction ends, then N calls inserting the same payload are processed
+ // sequentially.
+ // Hence, we use a lock here, to be sure that the previous call has finished before we
+ // check whether we already have the block locally.
+ api.newPayloadLock.Lock()
+ defer api.newPayloadLock.Unlock()
+
+ log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash)
+ block, err := engine.ExecutableDataToBlock(params)
if err != nil {
log.Debug("Invalid NewPayload params", "params", params, "error", err)
- return beacon.PayloadStatusV1{Status: beacon.INVALIDBLOCKHASH}, nil
+ return engine.PayloadStatusV1{Status: engine.INVALID}, nil
}
// Stash away the last update to warn the user if the beacon client goes offline
api.lastNewPayloadLock.Lock()
@@ -354,7 +460,7 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
if block := api.eth.BlockChain().GetBlockByHash(params.BlockHash); block != nil {
log.Warn("Ignoring already known beacon payload", "number", params.Number, "hash", params.BlockHash, "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
hash := block.Hash()
- return beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &hash}, nil
+ return engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &hash}, nil
}
// If this block was rejected previously, keep rejecting it
if res := api.checkInvalidAncestor(block.Hash(), block.Hash()); res != nil {
@@ -379,11 +485,11 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
)
if ptd.Cmp(ttd) < 0 {
log.Warn("Ignoring pre-merge payload", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd)
- return beacon.INVALID_TERMINAL_BLOCK, nil
+ return engine.INVALID_TERMINAL_BLOCK, nil
}
if parent.Difficulty().BitLen() > 0 && gptd != nil && gptd.Cmp(ttd) >= 0 {
log.Error("Ignoring pre-merge parent block", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd)
- return beacon.INVALID_TERMINAL_BLOCK, nil
+ return engine.INVALID_TERMINAL_BLOCK, nil
}
if block.Time() <= parent.Time() {
log.Warn("Invalid timestamp", "parent", block.Time(), "block", block.Time())
@@ -399,7 +505,7 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
if !api.eth.BlockChain().HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
api.remoteBlocks.put(block.Hash(), block.Header())
log.Warn("State not available, ignoring new payload")
- return beacon.PayloadStatusV1{Status: beacon.ACCEPTED}, nil
+ return engine.PayloadStatusV1{Status: engine.ACCEPTED}, nil
}
log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number)
if err := api.eth.BlockChain().InsertBlockWithoutSetHead(block); err != nil {
@@ -420,27 +526,14 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
api.eth.Downloader().Cancel()
}
hash := block.Hash()
- return beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &hash}, nil
-}
-
-// computePayloadId computes a pseudo-random payloadid, based on the parameters.
-func computePayloadId(headBlockHash common.Hash, params *beacon.PayloadAttributesV1) beacon.PayloadID {
- // Hash
- hasher := sha256.New()
- hasher.Write(headBlockHash[:])
- binary.Write(hasher, binary.BigEndian, params.Timestamp)
- hasher.Write(params.Random[:])
- hasher.Write(params.SuggestedFeeRecipient[:])
- var out beacon.PayloadID
- copy(out[:], hasher.Sum(nil)[:8])
- return out
+ return engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &hash}, nil
}
// delayPayloadImport stashes the given block away for import at a later time,
// either via a forkchoice update or a sync extension. This method is meant to
// be called by the newpayload command when the block seems to be ok, but some
// prerequisite prevents it from being processed (e.g. no parent, or snap sync).
-func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (beacon.PayloadStatusV1, error) {
+func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (engine.PayloadStatusV1, error) {
// Sanity check that this block's parent is not on a previously invalidated
// chain. If it is, mark the block as invalid too.
if res := api.checkInvalidAncestor(block.ParentHash(), block.Hash()); res != nil {
@@ -455,7 +548,7 @@ func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (beacon.PayloadS
// some strain from the forkchoice update.
if err := api.eth.Downloader().BeaconExtend(api.eth.SyncMode(), block.Header()); err == nil {
log.Debug("Payload accepted for sync extension", "number", block.NumberU64(), "hash", block.Hash())
- return beacon.PayloadStatusV1{Status: beacon.SYNCING}, nil
+ return engine.PayloadStatusV1{Status: engine.SYNCING}, nil
}
// Either no beacon sync was started yet, or it rejected the delivered
// payload as non-integratable on top of the existing sync. We'll just
@@ -472,7 +565,7 @@ func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (beacon.PayloadS
// and cannot afford concurrent out-if-band modifications via imports.
log.Warn("Ignoring payload while snap syncing", "number", block.NumberU64(), "hash", block.Hash())
}
- return beacon.PayloadStatusV1{Status: beacon.SYNCING}, nil
+ return engine.PayloadStatusV1{Status: engine.SYNCING}, nil
}
// setInvalidAncestor is a callback for the downloader to notify us if a bad block
@@ -487,7 +580,7 @@ func (api *ConsensusAPI) setInvalidAncestor(invalid *types.Header, origin *types
// checkInvalidAncestor checks whether the specified chain end links to a known
// bad ancestor. If yes, it constructs the payload failure response to return.
-func (api *ConsensusAPI) checkInvalidAncestor(check common.Hash, head common.Hash) *beacon.PayloadStatusV1 {
+func (api *ConsensusAPI) checkInvalidAncestor(check common.Hash, head common.Hash) *engine.PayloadStatusV1 {
api.invalidLock.Lock()
defer api.invalidLock.Unlock()
@@ -529,8 +622,8 @@ func (api *ConsensusAPI) checkInvalidAncestor(check common.Hash, head common.Has
lastValid = &common.Hash{}
}
failure := "links to previously rejected block"
- return &beacon.PayloadStatusV1{
- Status: beacon.INVALID,
+ return &engine.PayloadStatusV1{
+ Status: engine.INVALID,
LatestValidHash: lastValid,
ValidationError: &failure,
}
@@ -538,7 +631,7 @@ func (api *ConsensusAPI) checkInvalidAncestor(check common.Hash, head common.Has
// invalid returns a response "INVALID" with the latest valid hash supplied by latest or to the current head
// if no latestValid block was provided.
-func (api *ConsensusAPI) invalid(err error, latestValid *types.Header) beacon.PayloadStatusV1 {
+func (api *ConsensusAPI) invalid(err error, latestValid *types.Header) engine.PayloadStatusV1 {
currentHash := api.eth.BlockChain().CurrentBlock().Hash()
if latestValid != nil {
// Set latest valid hash to 0x0 if parent is PoW block
@@ -549,7 +642,7 @@ func (api *ConsensusAPI) invalid(err error, latestValid *types.Header) beacon.Pa
}
}
errorMsg := err.Error()
- return beacon.PayloadStatusV1{Status: beacon.INVALID, LatestValidHash: ¤tHash, ValidationError: &errorMsg}
+ return engine.PayloadStatusV1{Status: engine.INVALID, LatestValidHash: ¤tHash, ValidationError: &errorMsg}
}
// heartbeat loops indefinitely, and checks if there have been beacon client updates
@@ -564,16 +657,16 @@ func (api *ConsensusAPI) heartbeat() {
var (
offlineLogged time.Time
+ ttd = api.eth.BlockChain().Config().TerminalTotalDifficulty
)
+ // If the network is not yet merged/merging, don't bother continuing.
+ if ttd == nil {
+ return
+ }
for {
// Sleep a bit and retrieve the last known consensus updates
time.Sleep(5 * time.Second)
- // If the network is not yet merged/merging, don't bother scaring the user
- ttd := api.eth.BlockChain().Config().TerminalTotalDifficulty
- if ttd == nil {
- continue
- }
api.lastTransitionLock.Lock()
lastTransitionUpdate := api.lastTransitionUpdate
api.lastTransitionLock.Unlock()
@@ -589,89 +682,152 @@ func (api *ConsensusAPI) heartbeat() {
// If there have been no updates for the past while, warn the user
// that the beacon client is probably offline
if api.eth.BlockChain().Config().TerminalTotalDifficultyPassed || api.eth.Merger().TDDReached() {
- if time.Since(lastForkchoiceUpdate) > beaconUpdateConsensusTimeout && time.Since(lastNewPayloadUpdate) > beaconUpdateConsensusTimeout {
- if time.Since(lastTransitionUpdate) > beaconUpdateExchangeTimeout {
- if time.Since(offlineLogged) > beaconUpdateWarnFrequency {
- if lastTransitionUpdate.IsZero() {
- log.Warn("Post-merge network, but no beacon client seen. Please launch one to follow the chain!")
- } else {
- log.Warn("Previously seen beacon client is offline. Please ensure it is operational to follow the chain!")
- }
- offlineLogged = time.Now()
- }
- continue
- }
- if time.Since(offlineLogged) > beaconUpdateWarnFrequency {
- if lastForkchoiceUpdate.IsZero() && lastNewPayloadUpdate.IsZero() {
- log.Warn("Beacon client online, but never received consensus updates. Please ensure your beacon client is operational to follow the chain!")
- } else {
- log.Warn("Beacon client online, but no consensus updates received in a while. Please fix your beacon client to follow the chain!")
- }
- offlineLogged = time.Now()
- }
- continue
- } else {
+ if time.Since(lastForkchoiceUpdate) <= beaconUpdateConsensusTimeout || time.Since(lastNewPayloadUpdate) <= beaconUpdateConsensusTimeout {
offlineLogged = time.Time{}
+ continue
}
- } else {
if time.Since(lastTransitionUpdate) > beaconUpdateExchangeTimeout {
if time.Since(offlineLogged) > beaconUpdateWarnFrequency {
- // Retrieve the last few blocks and make a rough estimate as
- // to when the merge transition should happen
- var (
- chain = api.eth.BlockChain()
- head = chain.CurrentBlock()
- htd = chain.GetTd(head.Hash(), head.NumberU64())
- eta time.Duration
- )
- if head.NumberU64() > 0 && htd.Cmp(ttd) < 0 {
- // Accumulate the last 64 difficulties to estimate the growth
- var diff float64
-
- block := head
- for i := 0; i < 64; i++ {
- diff += float64(block.Difficulty().Uint64())
- if parent := chain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent == nil {
- break
- } else {
- block = parent
- }
- }
- // Estimate an ETA based on the block times and the difficulty growth
- growth := diff / float64(head.Time()-block.Time()+1) // +1 to avoid div by zero
- if growth > 0 {
- if left := new(big.Int).Sub(ttd, htd); left.IsUint64() {
- eta = time.Duration(float64(left.Uint64())/growth) * time.Second
- } else {
- eta = time.Duration(new(big.Int).Div(left, big.NewInt(int64(growth))).Uint64()) * time.Second
- }
- }
- }
- var message string
- if htd.Cmp(ttd) > 0 {
- if lastTransitionUpdate.IsZero() {
- message = "Merge already reached, but no beacon client seen. Please launch one to follow the chain!"
- } else {
- message = "Merge already reached, but previously seen beacon client is offline. Please ensure it is operational to follow the chain!"
- }
+ if lastTransitionUpdate.IsZero() {
+ log.Warn("Post-merge network, but no beacon client seen. Please launch one to follow the chain!")
} else {
- if lastTransitionUpdate.IsZero() {
- message = "Merge is configured, but no beacon client seen. Please ensure you have one available before the transition arrives!"
- } else {
- message = "Merge is configured, but previously seen beacon client is offline. Please ensure it is operational before the transition arrives!"
- }
- }
- if eta == 0 {
- log.Warn(message)
- } else {
- log.Warn(message, "eta", common.PrettyAge(time.Now().Add(-eta))) // weird hack, but duration formatted doesn't handle days
+ log.Warn("Previously seen beacon client is offline. Please ensure it is operational to follow the chain!")
}
offlineLogged = time.Now()
}
continue
+ }
+ if time.Since(offlineLogged) > beaconUpdateWarnFrequency {
+ if lastForkchoiceUpdate.IsZero() && lastNewPayloadUpdate.IsZero() {
+ log.Warn("Beacon client online, but never received consensus updates. Please ensure your beacon client is operational to follow the chain!")
+ } else {
+ log.Warn("Beacon client online, but no consensus updates received in a while. Please fix your beacon client to follow the chain!")
+ }
+ offlineLogged = time.Now()
+ }
+ continue
+ }
+ if time.Since(lastTransitionUpdate) <= beaconUpdateExchangeTimeout {
+ offlineLogged = time.Time{}
+ continue
+ }
+ if time.Since(offlineLogged) > beaconUpdateWarnFrequency {
+ // Retrieve the last few blocks and make a rough estimate as
+ // to when the merge transition should happen
+ var (
+ chain = api.eth.BlockChain()
+ head = chain.CurrentHeader()
+ htd = chain.GetTd(head.Hash(), head.Number.Uint64())
+ )
+ if htd.Cmp(ttd) >= 0 {
+ if lastTransitionUpdate.IsZero() {
+ log.Warn("Merge already reached, but no beacon client seen. Please launch one to follow the chain!")
+ } else {
+ log.Warn("Merge already reached, but previously seen beacon client is offline. Please ensure it is operational to follow the chain!")
+ }
+ offlineLogged = time.Now()
+ continue
+ }
+ var eta time.Duration
+ if head.Number.Uint64() > 0 {
+ // Accumulate the last 64 difficulties to estimate the growth
+ var (
+ deltaDiff uint64
+ deltaTime uint64
+ current = head
+ )
+ for i := 0; i < 64; i++ {
+ parent := chain.GetHeader(current.ParentHash, current.Number.Uint64()-1)
+ if parent == nil {
+ break
+ }
+ deltaDiff += current.Difficulty.Uint64()
+ deltaTime += current.Time - parent.Time
+ current = parent
+ }
+ // Estimate an ETA based on the block times and the difficulty growth
+ if deltaTime > 0 {
+ growth := deltaDiff / deltaTime
+ left := new(big.Int).Sub(ttd, htd)
+ eta = time.Duration(new(big.Int).Div(left, new(big.Int).SetUint64(growth+1)).Uint64()) * time.Second
+ }
+ }
+ message := "Merge is configured, but previously seen beacon client is offline. Please ensure it is operational before the transition arrives!"
+ if lastTransitionUpdate.IsZero() {
+ message = "Merge is configured, but no beacon client seen. Please ensure you have one available before the transition arrives!"
+ }
+ if eta < time.Second {
+ log.Warn(message)
} else {
- offlineLogged = time.Time{}
+ log.Warn(message, "eta", common.PrettyAge(time.Now().Add(-eta))) // weird hack, but duration formatted doesn't handle days
}
+ offlineLogged = time.Now()
}
}
}
+
+// ExchangeCapabilities returns the current methods provided by this node.
+func (api *ConsensusAPI) ExchangeCapabilities([]string) []string {
+ return caps
+}
+
+// GetPayloadBodiesV1 implements engine_getPayloadBodiesByHashV1 which allows for retrieval of a list
+// of block bodies by the engine api.
+func (api *ConsensusAPI) GetPayloadBodiesByHashV1(hashes []common.Hash) []*engine.ExecutionPayloadBodyV1 {
+ var bodies = make([]*engine.ExecutionPayloadBodyV1, len(hashes))
+ for i, hash := range hashes {
+ block := api.eth.BlockChain().GetBlockByHash(hash)
+ bodies[i] = getBody(block)
+ }
+ return bodies
+}
+
+// GetPayloadBodiesByRangeV1 implements engine_getPayloadBodiesByRangeV1 which allows for retrieval of a range
+// of block bodies by the engine api.
+func (api *ConsensusAPI) GetPayloadBodiesByRangeV1(start, count hexutil.Uint64) ([]*engine.ExecutionPayloadBodyV1, error) {
+ if start == 0 || count == 0 {
+ return nil, engine.InvalidParams.With(fmt.Errorf("invalid start or count, start: %v count: %v", start, count))
+ }
+ if count > 1024 {
+ return nil, engine.TooLargeRequest.With(fmt.Errorf("requested count too large: %v", count))
+ }
+ // limit count up until current
+ current := api.eth.BlockChain().CurrentBlock().NumberU64()
+ last := uint64(start) + uint64(count) - 1
+ if last > current {
+ last = current
+ }
+ bodies := make([]*engine.ExecutionPayloadBodyV1, 0, uint64(count))
+ for i := uint64(start); i <= last; i++ {
+ block := api.eth.BlockChain().GetBlockByNumber(i)
+ bodies = append(bodies, getBody(block))
+ }
+ return bodies, nil
+}
+
+func getBody(block *types.Block) *engine.ExecutionPayloadBodyV1 {
+ if block == nil {
+ return nil
+ }
+
+ var (
+ body = block.Body()
+ txs = make([]hexutil.Bytes, len(body.Transactions))
+ withdrawals = body.Withdrawals
+ )
+
+ for j, tx := range body.Transactions {
+ data, _ := tx.MarshalBinary()
+ txs[j] = hexutil.Bytes(data)
+ }
+
+ // Post-shanghai withdrawals MUST be set to empty slice instead of nil
+ if withdrawals == nil && block.Header().WithdrawalsHash != nil {
+ withdrawals = make([]*types.Withdrawal, 0)
+ }
+
+ return &engine.ExecutionPayloadBodyV1{
+ TransactionData: txs,
+ Withdrawals: withdrawals,
+ }
+}
diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go
index 0d945993eb..4dc0c0ea7e 100644
--- a/eth/catalyst/api_test.go
+++ b/eth/catalyst/api_test.go
@@ -18,25 +18,31 @@ package catalyst
import (
"bytes"
+ "context"
"fmt"
"math/big"
+ "sync"
"testing"
"time"
+ "github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/consensus"
+ beaconConsensus "github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/beacon"
- "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
+ "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
)
@@ -50,11 +56,16 @@ var (
testBalance = big.NewInt(2e18)
)
-func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) {
- db := rawdb.NewMemoryDatabase()
- config := params.AllEthashProtocolChanges
+func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
+ config := *params.AllEthashProtocolChanges
+ engine := consensus.Engine(beaconConsensus.New(ethash.NewFaker()))
+ if merged {
+ config.TerminalTotalDifficulty = common.Big0
+ config.TerminalTotalDifficultyPassed = true
+ engine = beaconConsensus.NewFaker()
+ }
genesis := &core.Genesis{
- Config: config,
+ Config: &config,
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
ExtraData: []byte("test genesis"),
Timestamp: 9000,
@@ -65,23 +76,25 @@ func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) {
generate := func(i int, g *core.BlockGen) {
g.OffsetTime(5)
g.SetExtra([]byte("test"))
- tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(config), testKey)
+ tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(&config), testKey)
g.AddTx(tx)
testNonce++
}
- gblock := genesis.MustCommit(db)
- engine := ethash.NewFaker()
- blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate)
- totalDifficulty := big.NewInt(0)
- for _, b := range blocks {
- totalDifficulty.Add(totalDifficulty, b.Difficulty())
+ _, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, n, generate)
+
+ if !merged {
+ totalDifficulty := big.NewInt(0)
+ for _, b := range blocks {
+ totalDifficulty.Add(totalDifficulty, b.Difficulty())
+ }
+ config.TerminalTotalDifficulty = totalDifficulty
}
- config.TerminalTotalDifficulty = totalDifficulty
+
return genesis, blocks
}
func TestEth2AssembleBlock(t *testing.T) {
- genesis, blocks := generatePreMergeChain(10)
+ genesis, blocks := generateMergeChain(10, false)
n, ethservice := startEthService(t, genesis, blocks)
defer n.Close()
@@ -92,20 +105,35 @@ func TestEth2AssembleBlock(t *testing.T) {
t.Fatalf("error signing transaction, err=%v", err)
}
ethservice.TxPool().AddLocal(tx)
- blockParams := beacon.PayloadAttributesV1{
+ blockParams := engine.PayloadAttributes{
Timestamp: blocks[9].Time() + 5,
}
- execData, err := assembleBlock(api, blocks[9].Hash(), &blockParams)
- if err != nil {
- t.Fatalf("error producing block, err=%v", err)
+ // The miner needs to pick up on the txs in the pool, so a few retries might be
+ // needed.
+ if _, testErr := assembleWithTransactions(api, blocks[9].Hash(), &blockParams, 1); testErr != nil {
+ t.Fatal(testErr)
}
- if len(execData.Transactions) != 1 {
- t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
+}
+
+// assembleWithTransactions tries to assemble a block, retrying until it has 'want',
+// number of transactions in it, or it has retried three times.
+func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params *engine.PayloadAttributes, want int) (execData *engine.ExecutableData, err error) {
+ for retries := 3; retries > 0; retries-- {
+ execData, err = assembleBlock(api, parentHash, params)
+ if err != nil {
+ return nil, err
+ }
+ if have, want := len(execData.Transactions), want; have != want {
+ err = fmt.Errorf("invalid number of transactions, have %d want %d", have, want)
+ continue
+ }
+ return execData, nil
}
+ return nil, err
}
func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
- genesis, blocks := generatePreMergeChain(10)
+ genesis, blocks := generateMergeChain(10, false)
n, ethservice := startEthService(t, genesis, blocks[:9])
defer n.Close()
@@ -113,38 +141,36 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
// Put the 10th block's tx in the pool and produce a new block
api.eth.TxPool().AddRemotesSync(blocks[9].Transactions())
- blockParams := beacon.PayloadAttributesV1{
+ blockParams := engine.PayloadAttributes{
Timestamp: blocks[8].Time() + 5,
}
- execData, err := assembleBlock(api, blocks[8].Hash(), &blockParams)
- if err != nil {
- t.Fatalf("error producing block, err=%v", err)
- }
- if len(execData.Transactions) != blocks[9].Transactions().Len() {
- t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
+ // The miner needs to pick up on the txs in the pool, so a few retries might be
+ // needed.
+ if _, err := assembleWithTransactions(api, blocks[8].Hash(), &blockParams, blocks[9].Transactions().Len()); err != nil {
+ t.Fatal(err)
}
}
func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
- genesis, blocks := generatePreMergeChain(10)
+ genesis, blocks := generateMergeChain(10, false)
n, ethservice := startEthService(t, genesis, blocks)
defer n.Close()
api := NewConsensusAPI(ethservice)
- fcState := beacon.ForkchoiceStateV1{
+ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: blocks[5].Hash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
}
if resp, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
t.Errorf("fork choice updated should not error: %v", err)
- } else if resp.PayloadStatus.Status != beacon.INVALID_TERMINAL_BLOCK.Status {
+ } else if resp.PayloadStatus.Status != engine.INVALID_TERMINAL_BLOCK.Status {
t.Errorf("fork choice updated before total terminal difficulty should be INVALID")
}
}
func TestEth2PrepareAndGetPayload(t *testing.T) {
- genesis, blocks := generatePreMergeChain(10)
+ genesis, blocks := generateMergeChain(10, false)
// We need to properly set the terminal total difficulty
genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty())
n, ethservice := startEthService(t, genesis, blocks[:9])
@@ -154,10 +180,10 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
// Put the 10th block's tx in the pool and produce a new block
ethservice.TxPool().AddLocals(blocks[9].Transactions())
- blockParams := beacon.PayloadAttributesV1{
+ blockParams := engine.PayloadAttributes{
Timestamp: blocks[8].Time() + 5,
}
- fcState := beacon.ForkchoiceStateV1{
+ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: blocks[8].Hash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
@@ -166,7 +192,14 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
if err != nil {
t.Fatalf("error preparing payload, err=%v", err)
}
- payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams)
+ // give the payload some time to be built
+ time.Sleep(100 * time.Millisecond)
+ payloadID := (&miner.BuildPayloadArgs{
+ Parent: fcState.HeadBlockHash,
+ Timestamp: blockParams.Timestamp,
+ FeeRecipient: blockParams.SuggestedFeeRecipient,
+ Random: blockParams.Random,
+ }).Id()
execData, err := api.GetPayloadV1(payloadID)
if err != nil {
t.Fatalf("error getting payload, err=%v", err)
@@ -175,7 +208,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
}
// Test invalid payloadID
- var invPayload beacon.PayloadID
+ var invPayload engine.PayloadID
copy(invPayload[:], payloadID[:])
invPayload[0] = ^invPayload[0]
_, err = api.GetPayloadV1(invPayload)
@@ -203,7 +236,7 @@ func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan co
}
func TestInvalidPayloadTimestamp(t *testing.T) {
- genesis, preMergeBlocks := generatePreMergeChain(10)
+ genesis, preMergeBlocks := generateMergeChain(10, false)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
defer n.Close()
var (
@@ -226,12 +259,12 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
for i, test := range tests {
t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
- params := beacon.PayloadAttributesV1{
+ params := engine.PayloadAttributes{
Timestamp: test.time,
Random: crypto.Keccak256Hash([]byte{byte(123)}),
SuggestedFeeRecipient: parent.Coinbase(),
}
- fcState := beacon.ForkchoiceStateV1{
+ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: parent.Hash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
@@ -247,7 +280,7 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
}
func TestEth2NewBlock(t *testing.T) {
- genesis, preMergeBlocks := generatePreMergeChain(10)
+ genesis, preMergeBlocks := generateMergeChain(10, false)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
defer n.Close()
@@ -270,25 +303,27 @@ func TestEth2NewBlock(t *testing.T) {
tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
ethservice.TxPool().AddLocal(tx)
- execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{
+ execData, err := assembleWithTransactions(api, parent.Hash(), &engine.PayloadAttributes{
Timestamp: parent.Time() + 5,
- })
+ }, 1)
if err != nil {
t.Fatalf("Failed to create the executable data %v", err)
}
- block, err := beacon.ExecutableDataToBlock(*execData)
+ block, err := engine.ExecutableDataToBlock(*execData)
if err != nil {
t.Fatalf("Failed to convert executable data to block %v", err)
}
newResp, err := api.NewPayloadV1(*execData)
- if err != nil || newResp.Status != "VALID" {
+ switch {
+ case err != nil:
t.Fatalf("Failed to insert block: %v", err)
- }
- if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 {
+ case newResp.Status != "VALID":
+ t.Fatalf("Failed to insert block: %v", newResp.Status)
+ case ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1:
t.Fatalf("Chain head shouldn't be updated")
}
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
- fcState := beacon.ForkchoiceStateV1{
+ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: block.Hash(),
SafeBlockHash: block.Hash(),
FinalizedBlockHash: block.Hash(),
@@ -296,8 +331,8 @@ func TestEth2NewBlock(t *testing.T) {
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
t.Fatalf("Failed to insert block: %v", err)
}
- if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
- t.Fatalf("Chain head should be updated")
+ if have, want := ethservice.BlockChain().CurrentBlock().NumberU64(), block.NumberU64(); have != want {
+ t.Fatalf("Chain head should be updated, have %d want %d", have, want)
}
checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
@@ -310,13 +345,13 @@ func TestEth2NewBlock(t *testing.T) {
)
parent = preMergeBlocks[len(preMergeBlocks)-1]
for i := 0; i < 10; i++ {
- execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{
+ execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{
Timestamp: parent.Time() + 6,
})
if err != nil {
t.Fatalf("Failed to create the executable data %v", err)
}
- block, err := beacon.ExecutableDataToBlock(*execData)
+ block, err := engine.ExecutableDataToBlock(*execData)
if err != nil {
t.Fatalf("Failed to convert executable data to block %v", err)
}
@@ -328,7 +363,7 @@ func TestEth2NewBlock(t *testing.T) {
t.Fatalf("Chain head shouldn't be updated")
}
- fcState := beacon.ForkchoiceStateV1{
+ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: block.Hash(),
SafeBlockHash: block.Hash(),
FinalizedBlockHash: block.Hash(),
@@ -347,7 +382,7 @@ func TestEth2DeepReorg(t *testing.T) {
// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
// before the totalTerminalDifficulty threshold
/*
- genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
+ genesis, preMergeBlocks := generateMergeChain(core.TriesInMemory * 2, false)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
defer n.Close()
@@ -415,7 +450,6 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block)
n.Close()
t.Fatal("can't import test blocks:", err)
}
- time.Sleep(500 * time.Millisecond) // give txpool enough time to consume head event
ethservice.SetEtherbase(testAddr)
ethservice.SetSynced()
@@ -423,7 +457,7 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block)
}
func TestFullAPI(t *testing.T) {
- genesis, preMergeBlocks := generatePreMergeChain(10)
+ genesis, preMergeBlocks := generateMergeChain(10, false)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
defer n.Close()
var (
@@ -442,8 +476,9 @@ func TestFullAPI(t *testing.T) {
setupBlocks(t, ethservice, 10, parent, callback)
}
-func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Block, callback func(parent *types.Block)) {
+func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Block, callback func(parent *types.Block)) []*types.Block {
api := NewConsensusAPI(ethservice)
+ var blocks []*types.Block
for i := 0; i < n; i++ {
callback(parent)
@@ -453,10 +488,10 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Bl
if err != nil {
t.Fatalf("can't execute payload: %v", err)
}
- if execResp.Status != beacon.VALID {
+ if execResp.Status != engine.VALID {
t.Fatalf("invalid status: %v", execResp.Status)
}
- fcState := beacon.ForkchoiceStateV1{
+ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: payload.BlockHash,
SafeBlockHash: payload.ParentHash,
FinalizedBlockHash: payload.ParentHash,
@@ -471,18 +506,19 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Bl
t.Fatal("Finalized block should be updated")
}
parent = ethservice.BlockChain().CurrentBlock()
+ blocks = append(blocks, parent)
}
+ return blocks
}
func TestExchangeTransitionConfig(t *testing.T) {
- genesis, preMergeBlocks := generatePreMergeChain(10)
+ genesis, preMergeBlocks := generateMergeChain(10, false)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
defer n.Close()
- var (
- api = NewConsensusAPI(ethservice)
- )
+
// invalid ttd
- config := beacon.TransitionConfigurationV1{
+ api := NewConsensusAPI(ethservice)
+ config := engine.TransitionConfigurationV1{
TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
TerminalBlockHash: common.Hash{},
TerminalBlockNumber: 0,
@@ -491,7 +527,7 @@ func TestExchangeTransitionConfig(t *testing.T) {
t.Fatal("expected error on invalid config, invalid ttd")
}
// invalid terminal block hash
- config = beacon.TransitionConfigurationV1{
+ config = engine.TransitionConfigurationV1{
TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
TerminalBlockHash: common.Hash{1},
TerminalBlockNumber: 0,
@@ -500,7 +536,7 @@ func TestExchangeTransitionConfig(t *testing.T) {
t.Fatal("expected error on invalid config, invalid hash")
}
// valid config
- config = beacon.TransitionConfigurationV1{
+ config = engine.TransitionConfigurationV1{
TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
TerminalBlockHash: common.Hash{},
TerminalBlockNumber: 0,
@@ -509,7 +545,7 @@ func TestExchangeTransitionConfig(t *testing.T) {
t.Fatalf("expected no error on valid config, got %v", err)
}
// valid config
- config = beacon.TransitionConfigurationV1{
+ config = engine.TransitionConfigurationV1{
TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
TerminalBlockHash: preMergeBlocks[5].Hash(),
TerminalBlockNumber: 6,
@@ -523,71 +559,85 @@ func TestExchangeTransitionConfig(t *testing.T) {
TestNewPayloadOnInvalidChain sets up a valid chain and tries to feed blocks
from an invalid chain to test if latestValidHash (LVH) works correctly.
-We set up the following chain where P1 ... Pn and P1'' are valid while
+We set up the following chain where P1 ... Pn and P1” are valid while
P1' is invalid.
We expect
(1) The LVH to point to the current inserted payload if it was valid.
(2) The LVH to point to the valid parent on an invalid payload (if the parent is available).
(3) If the parent is unavailable, the LVH should not be set.
-CommonAncestor◄─▲── P1 ◄── P2 ◄─ P3 ◄─ ... ◄─ Pn
- │
- └── P1' ◄─ P2' ◄─ P3' ◄─ ... ◄─ Pn'
- │
- └── P1''
+ CommonAncestor◄─▲── P1 ◄── P2 ◄─ P3 ◄─ ... ◄─ Pn
+ │
+ └── P1' ◄─ P2' ◄─ P3' ◄─ ... ◄─ Pn'
+ │
+ └── P1''
*/
func TestNewPayloadOnInvalidChain(t *testing.T) {
- genesis, preMergeBlocks := generatePreMergeChain(10)
+ genesis, preMergeBlocks := generateMergeChain(10, false)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
defer n.Close()
var (
api = NewConsensusAPI(ethservice)
parent = ethservice.BlockChain().CurrentBlock()
+ signer = types.LatestSigner(ethservice.BlockChain().Config())
// This EVM code generates a log when the contract is created.
logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
)
for i := 0; i < 10; i++ {
statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
- nonce := statedb.GetNonce(testAddr)
- tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
- ethservice.TxPool().AddLocal(tx)
-
- params := beacon.PayloadAttributesV1{
- Timestamp: parent.Time() + 1,
- Random: crypto.Keccak256Hash([]byte{byte(i)}),
- SuggestedFeeRecipient: parent.Coinbase(),
- }
-
- fcState := beacon.ForkchoiceStateV1{
- HeadBlockHash: parent.Hash(),
- SafeBlockHash: common.Hash{},
- FinalizedBlockHash: common.Hash{},
- }
- resp, err := api.ForkchoiceUpdatedV1(fcState, ¶ms)
- if err != nil {
- t.Fatalf("error preparing payload, err=%v", err)
- }
- if resp.PayloadStatus.Status != beacon.VALID {
- t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status)
- }
- payload, err := api.GetPayloadV1(*resp.PayloadID)
- if err != nil {
- t.Fatalf("can't get payload: %v", err)
- }
- // TODO(493456442, marius) this test can be flaky since we rely on a 100ms
- // allowance for block generation internally.
- if len(payload.Transactions) == 0 {
- t.Fatalf("payload should not be empty")
+ tx := types.MustSignNewTx(testKey, signer, &types.LegacyTx{
+ Nonce: statedb.GetNonce(testAddr),
+ Value: new(big.Int),
+ Gas: 1000000,
+ GasPrice: big.NewInt(2 * params.InitialBaseFee),
+ Data: logCode,
+ })
+ ethservice.TxPool().AddRemotesSync([]*types.Transaction{tx})
+ var (
+ params = engine.PayloadAttributes{
+ Timestamp: parent.Time() + 1,
+ Random: crypto.Keccak256Hash([]byte{byte(i)}),
+ SuggestedFeeRecipient: parent.Coinbase(),
+ }
+ fcState = engine.ForkchoiceStateV1{
+ HeadBlockHash: parent.Hash(),
+ SafeBlockHash: common.Hash{},
+ FinalizedBlockHash: common.Hash{},
+ }
+ payload *engine.ExecutableData
+ resp engine.ForkChoiceResponse
+ err error
+ )
+ for i := 0; ; i++ {
+ if resp, err = api.ForkchoiceUpdatedV1(fcState, ¶ms); err != nil {
+ t.Fatalf("error preparing payload, err=%v", err)
+ }
+ if resp.PayloadStatus.Status != engine.VALID {
+ t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status)
+ }
+ // give the payload some time to be built
+ time.Sleep(50 * time.Millisecond)
+ if payload, err = api.GetPayloadV1(*resp.PayloadID); err != nil {
+ t.Fatalf("can't get payload: %v", err)
+ }
+ if len(payload.Transactions) > 0 {
+ break
+ }
+ // No luck this time we need to update the params and try again.
+ params.Timestamp = params.Timestamp + 1
+ if i > 10 {
+ t.Fatalf("payload should not be empty")
+ }
}
execResp, err := api.NewPayloadV1(*payload)
if err != nil {
t.Fatalf("can't execute payload: %v", err)
}
- if execResp.Status != beacon.VALID {
+ if execResp.Status != engine.VALID {
t.Fatalf("invalid status: %v", execResp.Status)
}
- fcState = beacon.ForkchoiceStateV1{
+ fcState = engine.ForkchoiceStateV1{
HeadBlockHash: payload.BlockHash,
SafeBlockHash: payload.ParentHash,
FinalizedBlockHash: payload.ParentHash,
@@ -602,16 +652,23 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
}
}
-func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1) (*beacon.ExecutableDataV1, error) {
- block, err := api.eth.Miner().GetSealingBlockSync(parentHash, params.Timestamp, params.SuggestedFeeRecipient, params.Random, false)
+func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *engine.PayloadAttributes) (*engine.ExecutableData, error) {
+ args := &miner.BuildPayloadArgs{
+ Parent: parentHash,
+ Timestamp: params.Timestamp,
+ FeeRecipient: params.SuggestedFeeRecipient,
+ Random: params.Random,
+ Withdrawals: params.Withdrawals,
+ }
+ payload, err := api.eth.Miner().BuildPayload(args)
if err != nil {
return nil, err
}
- return beacon.BlockToExecutableData(block), nil
+ return payload.ResolveFull().ExecutionPayload, nil
}
func TestEmptyBlocks(t *testing.T) {
- genesis, preMergeBlocks := generatePreMergeChain(10)
+ genesis, preMergeBlocks := generateMergeChain(10, false)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
defer n.Close()
@@ -628,7 +685,7 @@ func TestEmptyBlocks(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if status.Status != beacon.VALID {
+ if status.Status != engine.VALID {
t.Errorf("invalid status: expected VALID got: %v", status.Status)
}
if !bytes.Equal(status.LatestValidHash[:], payload.BlockHash[:]) {
@@ -644,7 +701,7 @@ func TestEmptyBlocks(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if status.Status != beacon.INVALID {
+ if status.Status != engine.INVALID {
t.Errorf("invalid status: expected INVALID got: %v", status.Status)
}
// Expect 0x0 on INVALID block on top of PoW block
@@ -662,7 +719,7 @@ func TestEmptyBlocks(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if status.Status != beacon.SYNCING {
+ if status.Status != engine.SYNCING {
t.Errorf("invalid status: expected SYNCING got: %v", status.Status)
}
if status.LatestValidHash != nil {
@@ -670,8 +727,8 @@ func TestEmptyBlocks(t *testing.T) {
}
}
-func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableDataV1 {
- params := beacon.PayloadAttributesV1{
+func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *engine.ExecutableData {
+ params := engine.PayloadAttributes{
Timestamp: parent.Time() + 1,
Random: crypto.Keccak256Hash([]byte{byte(1)}),
SuggestedFeeRecipient: parent.Coinbase(),
@@ -686,7 +743,7 @@ func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon
// setBlockhash sets the blockhash of a modified ExecutableData.
// Can be used to make modified payloads look valid.
-func setBlockhash(data *beacon.ExecutableDataV1) *beacon.ExecutableDataV1 {
+func setBlockhash(data *engine.ExecutableData) *engine.ExecutableData {
txs, _ := decodeTransactions(data.Transactions)
number := big.NewInt(0)
number.SetUint64(data.Number)
@@ -726,7 +783,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
func TestTrickRemoteBlockCache(t *testing.T) {
// Setup two nodes
- genesis, preMergeBlocks := generatePreMergeChain(10)
+ genesis, preMergeBlocks := generateMergeChain(10, false)
nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks)
nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks)
defer nodeA.Close()
@@ -745,7 +802,7 @@ func TestTrickRemoteBlockCache(t *testing.T) {
setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {})
commonAncestor = ethserviceA.BlockChain().CurrentBlock()
- var invalidChain []*beacon.ExecutableDataV1
+ var invalidChain []*engine.ExecutableData
// create a valid payload (P1)
//payload1 := getNewPayload(t, apiA, commonAncestor)
//invalidChain = append(invalidChain, payload1)
@@ -773,15 +830,15 @@ func TestTrickRemoteBlockCache(t *testing.T) {
if err != nil {
panic(err)
}
- if status.Status == beacon.VALID {
+ if status.Status == engine.VALID {
t.Error("invalid status: VALID on an invalid chain")
}
// Now reorg to the head of the invalid chain
- resp, err := apiB.ForkchoiceUpdatedV1(beacon.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil)
+ resp, err := apiB.ForkchoiceUpdatedV1(engine.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil)
if err != nil {
t.Fatal(err)
}
- if resp.PayloadStatus.Status == beacon.VALID {
+ if resp.PayloadStatus.Status == engine.VALID {
t.Error("invalid status: VALID on an invalid chain")
}
time.Sleep(100 * time.Millisecond)
@@ -789,7 +846,7 @@ func TestTrickRemoteBlockCache(t *testing.T) {
}
func TestInvalidBloom(t *testing.T) {
- genesis, preMergeBlocks := generatePreMergeChain(10)
+ genesis, preMergeBlocks := generateMergeChain(10, false)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
ethservice.Merger().ReachTTD()
defer n.Close()
@@ -807,27 +864,25 @@ func TestInvalidBloom(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if status.Status != beacon.INVALIDBLOCKHASH {
- t.Errorf("invalid status: expected VALID got: %v", status.Status)
+ if status.Status != engine.INVALID {
+ t.Errorf("invalid status: expected INVALID got: %v", status.Status)
}
}
func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
- genesis, preMergeBlocks := generatePreMergeChain(100)
- fmt.Println(genesis.Config.TerminalTotalDifficulty)
- genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty())
-
- fmt.Println(genesis.Config.TerminalTotalDifficulty)
+ genesis, preMergeBlocks := generateMergeChain(100, false)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
defer n.Close()
+ genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty())
+
var (
api = NewConsensusAPI(ethservice)
parent = preMergeBlocks[len(preMergeBlocks)-1]
)
// Test parent already post TTD in FCU
- fcState := beacon.ForkchoiceStateV1{
+ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: parent.Hash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
@@ -836,26 +891,554 @@ func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
if err != nil {
t.Fatalf("error sending forkchoice, err=%v", err)
}
- if resp.PayloadStatus != beacon.INVALID_TERMINAL_BLOCK {
+ if resp.PayloadStatus != engine.INVALID_TERMINAL_BLOCK {
t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
}
// Test parent already post TTD in NewPayload
- params := beacon.PayloadAttributesV1{
- Timestamp: parent.Time() + 1,
- Random: crypto.Keccak256Hash([]byte{byte(1)}),
- SuggestedFeeRecipient: parent.Coinbase(),
+ args := &miner.BuildPayloadArgs{
+ Parent: parent.Hash(),
+ Timestamp: parent.Time() + 1,
+ Random: crypto.Keccak256Hash([]byte{byte(1)}),
+ FeeRecipient: parent.Coinbase(),
}
- empty, err := api.eth.Miner().GetSealingBlockSync(parent.Hash(), params.Timestamp, params.SuggestedFeeRecipient, params.Random, true)
+ payload, err := api.eth.Miner().BuildPayload(args)
if err != nil {
t.Fatalf("error preparing payload, err=%v", err)
}
- data := *beacon.BlockToExecutableData(empty)
+ data := *payload.Resolve().ExecutionPayload
resp2, err := api.NewPayloadV1(data)
if err != nil {
t.Fatalf("error sending NewPayload, err=%v", err)
}
- if resp2 != beacon.INVALID_TERMINAL_BLOCK {
+ if resp2 != engine.INVALID_TERMINAL_BLOCK {
t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
}
}
+
+// TestSimultaneousNewBlock does several parallel inserts, both as
+// newPayLoad and forkchoiceUpdate. This is to test that the api behaves
+// well even of the caller is not being 'serial'.
+func TestSimultaneousNewBlock(t *testing.T) {
+ genesis, preMergeBlocks := generateMergeChain(10, false)
+ n, ethservice := startEthService(t, genesis, preMergeBlocks)
+ defer n.Close()
+
+ var (
+ api = NewConsensusAPI(ethservice)
+ parent = preMergeBlocks[len(preMergeBlocks)-1]
+ )
+ for i := 0; i < 10; i++ {
+ execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{
+ Timestamp: parent.Time() + 5,
+ })
+ if err != nil {
+ t.Fatalf("Failed to create the executable data %v", err)
+ }
+ // Insert it 10 times in parallel. Should be ignored.
+ {
+ var (
+ wg sync.WaitGroup
+ testErr error
+ errMu sync.Mutex
+ )
+ wg.Add(10)
+ for ii := 0; ii < 10; ii++ {
+ go func() {
+ defer wg.Done()
+ if newResp, err := api.NewPayloadV1(*execData); err != nil {
+ errMu.Lock()
+ testErr = fmt.Errorf("Failed to insert block: %w", err)
+ errMu.Unlock()
+ } else if newResp.Status != "VALID" {
+ errMu.Lock()
+ testErr = fmt.Errorf("Failed to insert block: %v", newResp.Status)
+ errMu.Unlock()
+ }
+ }()
+ }
+ wg.Wait()
+ if testErr != nil {
+ t.Fatal(testErr)
+ }
+ }
+ block, err := engine.ExecutableDataToBlock(*execData)
+ if err != nil {
+ t.Fatalf("Failed to convert executable data to block %v", err)
+ }
+ if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 {
+ t.Fatalf("Chain head shouldn't be updated")
+ }
+ fcState := engine.ForkchoiceStateV1{
+ HeadBlockHash: block.Hash(),
+ SafeBlockHash: block.Hash(),
+ FinalizedBlockHash: block.Hash(),
+ }
+ {
+ var (
+ wg sync.WaitGroup
+ testErr error
+ errMu sync.Mutex
+ )
+ wg.Add(10)
+ // Do each FCU 10 times
+ for ii := 0; ii < 10; ii++ {
+ go func() {
+ defer wg.Done()
+ if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
+ errMu.Lock()
+ testErr = fmt.Errorf("Failed to insert block: %w", err)
+ errMu.Unlock()
+ }
+ }()
+ }
+ wg.Wait()
+ if testErr != nil {
+ t.Fatal(testErr)
+ }
+ }
+ if have, want := ethservice.BlockChain().CurrentBlock().NumberU64(), block.NumberU64(); have != want {
+ t.Fatalf("Chain head should be updated, have %d want %d", have, want)
+ }
+ parent = block
+ }
+}
+
+// TestWithdrawals creates and verifies two post-Shanghai blocks. The first
+// includes zero withdrawals and the second includes two.
+func TestWithdrawals(t *testing.T) {
+ genesis, blocks := generateMergeChain(10, true)
+ // Set shanghai time to last block + 5 seconds (first post-merge block)
+ time := blocks[len(blocks)-1].Time() + 5
+ genesis.Config.ShanghaiTime = &time
+
+ n, ethservice := startEthService(t, genesis, blocks)
+ ethservice.Merger().ReachTTD()
+ defer n.Close()
+
+ api := NewConsensusAPI(ethservice)
+
+ // 10: Build Shanghai block with no withdrawals.
+ parent := ethservice.BlockChain().CurrentHeader()
+ blockParams := engine.PayloadAttributes{
+ Timestamp: parent.Time + 5,
+ Withdrawals: make([]*types.Withdrawal, 0),
+ }
+ fcState := engine.ForkchoiceStateV1{
+ HeadBlockHash: parent.Hash(),
+ }
+ resp, err := api.ForkchoiceUpdatedV2(fcState, &blockParams)
+ if err != nil {
+ t.Fatalf("error preparing payload, err=%v", err)
+ }
+ if resp.PayloadStatus.Status != engine.VALID {
+ t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID)
+ }
+
+ // 10: verify state root is the same as parent
+ payloadID := (&miner.BuildPayloadArgs{
+ Parent: fcState.HeadBlockHash,
+ Timestamp: blockParams.Timestamp,
+ FeeRecipient: blockParams.SuggestedFeeRecipient,
+ Random: blockParams.Random,
+ Withdrawals: blockParams.Withdrawals,
+ }).Id()
+ execData, err := api.GetPayloadV2(payloadID)
+ if err != nil {
+ t.Fatalf("error getting payload, err=%v", err)
+ }
+ if execData.ExecutionPayload.StateRoot != parent.Root {
+ t.Fatalf("mismatch state roots (got: %s, want: %s)", execData.ExecutionPayload.StateRoot, blocks[8].Root())
+ }
+
+ // 10: verify locally built block
+ if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
+ t.Fatalf("error validating payload: %v", err)
+ } else if status.Status != engine.VALID {
+ t.Fatalf("invalid payload")
+ }
+
+ // 11: build shanghai block with withdrawal
+ aa := common.Address{0xaa}
+ bb := common.Address{0xbb}
+ blockParams = engine.PayloadAttributes{
+ Timestamp: execData.ExecutionPayload.Timestamp + 5,
+ Withdrawals: []*types.Withdrawal{
+ {
+ Index: 0,
+ Address: aa,
+ Amount: 32,
+ },
+ {
+ Index: 1,
+ Address: bb,
+ Amount: 33,
+ },
+ },
+ }
+ fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
+ _, err = api.ForkchoiceUpdatedV2(fcState, &blockParams)
+ if err != nil {
+ t.Fatalf("error preparing payload, err=%v", err)
+ }
+
+ // 11: verify locally build block.
+ payloadID = (&miner.BuildPayloadArgs{
+ Parent: fcState.HeadBlockHash,
+ Timestamp: blockParams.Timestamp,
+ FeeRecipient: blockParams.SuggestedFeeRecipient,
+ Random: blockParams.Random,
+ Withdrawals: blockParams.Withdrawals,
+ }).Id()
+ execData, err = api.GetPayloadV2(payloadID)
+ if err != nil {
+ t.Fatalf("error getting payload, err=%v", err)
+ }
+ if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
+ t.Fatalf("error validating payload: %v", err)
+ } else if status.Status != engine.VALID {
+ t.Fatalf("invalid payload")
+ }
+
+ // 11: set block as head.
+ fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
+ _, err = api.ForkchoiceUpdatedV2(fcState, nil)
+ if err != nil {
+ t.Fatalf("error preparing payload, err=%v", err)
+ }
+
+ // 11: verify withdrawals were processed.
+ db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number))
+ if err != nil {
+ t.Fatalf("unable to load db: %v", err)
+ }
+ for i, w := range blockParams.Withdrawals {
+ // w.Amount is in gwei, balance in wei
+ if db.GetBalance(w.Address).Uint64() != w.Amount*params.GWei {
+ t.Fatalf("failed to process withdrawal %d", i)
+ }
+ }
+}
+
+func TestNilWithdrawals(t *testing.T) {
+ genesis, blocks := generateMergeChain(10, true)
+ // Set shanghai time to last block + 4 seconds (first post-merge block)
+ time := blocks[len(blocks)-1].Time() + 4
+ genesis.Config.ShanghaiTime = &time
+
+ n, ethservice := startEthService(t, genesis, blocks)
+ ethservice.Merger().ReachTTD()
+ defer n.Close()
+
+ api := NewConsensusAPI(ethservice)
+ parent := ethservice.BlockChain().CurrentHeader()
+ aa := common.Address{0xaa}
+
+ type test struct {
+ blockParams engine.PayloadAttributes
+ wantErr bool
+ }
+ tests := []test{
+ // Before Shanghai
+ {
+ blockParams: engine.PayloadAttributes{
+ Timestamp: parent.Time + 2,
+ Withdrawals: nil,
+ },
+ wantErr: false,
+ },
+ {
+ blockParams: engine.PayloadAttributes{
+ Timestamp: parent.Time + 2,
+ Withdrawals: make([]*types.Withdrawal, 0),
+ },
+ wantErr: true,
+ },
+ {
+ blockParams: engine.PayloadAttributes{
+ Timestamp: parent.Time + 2,
+ Withdrawals: []*types.Withdrawal{
+ {
+ Index: 0,
+ Address: aa,
+ Amount: 32,
+ },
+ },
+ },
+ wantErr: true,
+ },
+ // After Shanghai
+ {
+ blockParams: engine.PayloadAttributes{
+ Timestamp: parent.Time + 5,
+ Withdrawals: nil,
+ },
+ wantErr: true,
+ },
+ {
+ blockParams: engine.PayloadAttributes{
+ Timestamp: parent.Time + 5,
+ Withdrawals: make([]*types.Withdrawal, 0),
+ },
+ wantErr: false,
+ },
+ {
+ blockParams: engine.PayloadAttributes{
+ Timestamp: parent.Time + 5,
+ Withdrawals: []*types.Withdrawal{
+ {
+ Index: 0,
+ Address: aa,
+ Amount: 32,
+ },
+ },
+ },
+ wantErr: false,
+ },
+ }
+
+ fcState := engine.ForkchoiceStateV1{
+ HeadBlockHash: parent.Hash(),
+ }
+
+ for _, test := range tests {
+ _, err := api.ForkchoiceUpdatedV2(fcState, &test.blockParams)
+ if test.wantErr {
+ if err == nil {
+ t.Fatal("wanted error on fcuv2 with invalid withdrawals")
+ }
+ continue
+ }
+ if err != nil {
+ t.Fatalf("error preparing payload, err=%v", err)
+ }
+
+ // 11: verify locally build block.
+ payloadID := (&miner.BuildPayloadArgs{
+ Parent: fcState.HeadBlockHash,
+ Timestamp: test.blockParams.Timestamp,
+ FeeRecipient: test.blockParams.SuggestedFeeRecipient,
+ Random: test.blockParams.Random,
+ }).Id()
+ execData, err := api.GetPayloadV2(payloadID)
+ if err != nil {
+ t.Fatalf("error getting payload, err=%v", err)
+ }
+ if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
+ t.Fatalf("error validating payload: %v", err)
+ } else if status.Status != engine.VALID {
+ t.Fatalf("invalid payload")
+ }
+ }
+}
+
+func setupBodies(t *testing.T) (*node.Node, *eth.Ethereum, []*types.Block) {
+ genesis, preMergeBlocks := generateMergeChain(10, false)
+ n, ethservice := startEthService(t, genesis, preMergeBlocks)
+
+ var (
+ parent = ethservice.BlockChain().CurrentBlock()
+ // This EVM code generates a log when the contract is created.
+ logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
+ )
+
+ callback := func(parent *types.Block) {
+ statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
+ nonce := statedb.GetNonce(testAddr)
+ tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
+ ethservice.TxPool().AddLocal(tx)
+ }
+
+ postMergeBlocks := setupBlocks(t, ethservice, 10, parent, callback)
+ return n, ethservice, append(preMergeBlocks, postMergeBlocks...)
+}
+
+func TestGetBlockBodiesByHash(t *testing.T) {
+ node, eth, blocks := setupBodies(t)
+ api := NewConsensusAPI(eth)
+ defer node.Close()
+
+ tests := []struct {
+ results []*types.Body
+ hashes []common.Hash
+ }{
+ // First pow block
+ {
+ results: []*types.Body{eth.BlockChain().GetBlockByNumber(0).Body()},
+ hashes: []common.Hash{eth.BlockChain().GetBlockByNumber(0).Hash()},
+ },
+ // Last pow block
+ {
+ results: []*types.Body{blocks[9].Body()},
+ hashes: []common.Hash{blocks[9].Hash()},
+ },
+ // First post-merge block
+ {
+ results: []*types.Body{blocks[10].Body()},
+ hashes: []common.Hash{blocks[10].Hash()},
+ },
+ // Pre & post merge blocks
+ {
+ results: []*types.Body{blocks[0].Body(), blocks[9].Body(), blocks[14].Body()},
+ hashes: []common.Hash{blocks[0].Hash(), blocks[9].Hash(), blocks[14].Hash()},
+ },
+ // unavailable block
+ {
+ results: []*types.Body{blocks[0].Body(), nil, blocks[14].Body()},
+ hashes: []common.Hash{blocks[0].Hash(), {1, 2}, blocks[14].Hash()},
+ },
+ // same block multiple times
+ {
+ results: []*types.Body{blocks[0].Body(), nil, blocks[0].Body(), blocks[0].Body()},
+ hashes: []common.Hash{blocks[0].Hash(), {1, 2}, blocks[0].Hash(), blocks[0].Hash()},
+ },
+ }
+
+ for k, test := range tests {
+ result := api.GetPayloadBodiesByHashV1(test.hashes)
+ for i, r := range result {
+ if !equalBody(test.results[i], r) {
+ t.Fatalf("test %v: invalid response: expected %+v got %+v", k, test.results[i], r)
+ }
+ }
+ }
+}
+
+func TestGetBlockBodiesByRange(t *testing.T) {
+ node, eth, blocks := setupBodies(t)
+ api := NewConsensusAPI(eth)
+ defer node.Close()
+
+ tests := []struct {
+ results []*types.Body
+ start hexutil.Uint64
+ count hexutil.Uint64
+ }{
+ {
+ results: []*types.Body{blocks[9].Body()},
+ start: 10,
+ count: 1,
+ },
+ // Genesis
+ {
+ results: []*types.Body{blocks[0].Body()},
+ start: 1,
+ count: 1,
+ },
+ // First post-merge block
+ {
+ results: []*types.Body{blocks[9].Body()},
+ start: 10,
+ count: 1,
+ },
+ // Pre & post merge blocks
+ {
+ results: []*types.Body{blocks[7].Body(), blocks[8].Body(), blocks[9].Body(), blocks[10].Body()},
+ start: 8,
+ count: 4,
+ },
+ // unavailable block
+ {
+ results: []*types.Body{blocks[18].Body(), blocks[19].Body()},
+ start: 19,
+ count: 3,
+ },
+ // unavailable block
+ {
+ results: []*types.Body{blocks[19].Body()},
+ start: 20,
+ count: 2,
+ },
+ {
+ results: []*types.Body{blocks[19].Body()},
+ start: 20,
+ count: 1,
+ },
+ // whole range unavailable
+ {
+ results: make([]*types.Body, 0),
+ start: 22,
+ count: 2,
+ },
+ }
+
+ for k, test := range tests {
+ result, err := api.GetPayloadBodiesByRangeV1(test.start, test.count)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(result) == len(test.results) {
+ for i, r := range result {
+ if !equalBody(test.results[i], r) {
+ t.Fatalf("test %d: invalid response: expected \n%+v\ngot\n%+v", k, test.results[i], r)
+ }
+ }
+ } else {
+ t.Fatalf("test %d: invalid length want %v got %v", k, len(test.results), len(result))
+ }
+ }
+}
+
+func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) {
+ node, eth, _ := setupBodies(t)
+ api := NewConsensusAPI(eth)
+ defer node.Close()
+ tests := []struct {
+ start hexutil.Uint64
+ count hexutil.Uint64
+ want *engine.EngineAPIError
+ }{
+ // Genesis
+ {
+ start: 0,
+ count: 1,
+ want: engine.InvalidParams,
+ },
+ // No block requested
+ {
+ start: 1,
+ count: 0,
+ want: engine.InvalidParams,
+ },
+ // Genesis & no block
+ {
+ start: 0,
+ count: 0,
+ want: engine.InvalidParams,
+ },
+ // More than 1024 blocks
+ {
+ start: 1,
+ count: 1025,
+ want: engine.TooLargeRequest,
+ },
+ }
+ for i, tc := range tests {
+ result, err := api.GetPayloadBodiesByRangeV1(tc.start, tc.count)
+ if err == nil {
+ t.Fatalf("test %d: expected error, got %v", i, result)
+ }
+ if have, want := err.Error(), tc.want.Error(); have != want {
+ t.Fatalf("test %d: have %s, want %s", i, have, want)
+ }
+ }
+}
+
+func equalBody(a *types.Body, b *engine.ExecutionPayloadBodyV1) bool {
+ if a == nil && b == nil {
+ return true
+ } else if a == nil || b == nil {
+ return false
+ }
+ var want []hexutil.Bytes
+ for _, tx := range a.Transactions {
+ data, _ := tx.MarshalBinary()
+ want = append(want, hexutil.Bytes(data))
+ }
+ aBytes, errA := rlp.EncodeToBytes(want)
+ bBytes, errB := rlp.EncodeToBytes(b.TransactionData)
+ if errA != errB {
+ return false
+ }
+ return bytes.Equal(aBytes, bBytes)
+}
diff --git a/eth/catalyst/queue.go b/eth/catalyst/queue.go
index ff8edc1201..b7213dd591 100644
--- a/eth/catalyst/queue.go
+++ b/eth/catalyst/queue.go
@@ -18,11 +18,11 @@ package catalyst
import (
"sync"
- "time"
+ "github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/beacon"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/miner"
)
// maxTrackedPayloads is the maximum number of prepared payloads the execution
@@ -35,52 +35,11 @@ const maxTrackedPayloads = 10
// latest one; but have a slight wiggle room for non-ideal conditions.
const maxTrackedHeaders = 10
-// payload wraps the miner's block production channel, allowing the mined block
-// to be retrieved later upon the GetPayload engine API call.
-type payload struct {
- lock sync.Mutex
- done bool
- empty *types.Block
- block *types.Block
- result chan *types.Block
-}
-
-// resolve extracts the generated full block from the given channel if possible
-// or fallback to empty block as an alternative.
-func (req *payload) resolve() *beacon.ExecutableDataV1 {
- // this function can be called concurrently, prevent any
- // concurrency issue in the first place.
- req.lock.Lock()
- defer req.lock.Unlock()
-
- // Try to resolve the full block first if it's not obtained
- // yet. The returned block can be nil if the generation fails.
-
- if !req.done {
- timeout := time.NewTimer(500 * time.Millisecond)
- defer timeout.Stop()
-
- select {
- case req.block = <-req.result:
- req.done = true
- case <-timeout.C:
- // TODO(rjl49345642, Marius), should we keep this
- // 100ms timeout allowance? Why not just use the
- // default and then fallback to empty directly?
- }
- }
-
- if req.block != nil {
- return beacon.BlockToExecutableData(req.block)
- }
- return beacon.BlockToExecutableData(req.empty)
-}
-
// payloadQueueItem represents an id->payload tuple to store until it's retrieved
// or evicted.
type payloadQueueItem struct {
- id beacon.PayloadID
- data *payload
+ id engine.PayloadID
+ payload *miner.Payload
}
// payloadQueue tracks the latest handful of constructed payloads to be retrieved
@@ -99,19 +58,19 @@ func newPayloadQueue() *payloadQueue {
}
// put inserts a new payload into the queue at the given id.
-func (q *payloadQueue) put(id beacon.PayloadID, data *payload) {
+func (q *payloadQueue) put(id engine.PayloadID, payload *miner.Payload) {
q.lock.Lock()
defer q.lock.Unlock()
copy(q.payloads[1:], q.payloads)
q.payloads[0] = &payloadQueueItem{
- id: id,
- data: data,
+ id: id,
+ payload: payload,
}
}
// get retrieves a previously stored payload item or nil if it does not exist.
-func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutableDataV1 {
+func (q *payloadQueue) get(id engine.PayloadID) *engine.ExecutionPayloadEnvelope {
q.lock.RLock()
defer q.lock.RUnlock()
@@ -120,12 +79,28 @@ func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutableDataV1 {
return nil // no more items
}
if item.id == id {
- return item.data.resolve()
+ return item.payload.Resolve()
}
}
return nil
}
+// has checks if a particular payload is already tracked.
+func (q *payloadQueue) has(id engine.PayloadID) bool {
+ q.lock.RLock()
+ defer q.lock.RUnlock()
+
+ for _, item := range q.payloads {
+ if item == nil {
+ return false
+ }
+ if item.id == id {
+ return true
+ }
+ }
+ return false
+}
+
// headerQueueItem represents an hash->header tuple to store until it's retrieved
// or evicted.
type headerQueueItem struct {
diff --git a/eth/catalyst/tester.go b/eth/catalyst/tester.go
new file mode 100644
index 0000000000..05511eaf7a
--- /dev/null
+++ b/eth/catalyst/tester.go
@@ -0,0 +1,97 @@
+// 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 catalyst
+
+import (
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/node"
+)
+
+// FullSyncTester is an auxiliary service that allows Geth to perform full sync
+// alone without consensus-layer attached. Users must specify a valid block as
+// the sync target. This tester can be applied to different networks, no matter
+// it's pre-merge or post-merge, but only for full-sync.
+type FullSyncTester struct {
+ api *ConsensusAPI
+ block *types.Block
+ closed chan struct{}
+ wg sync.WaitGroup
+}
+
+// RegisterFullSyncTester registers the full-sync tester service into the node
+// stack for launching and stopping the service controlled by node.
+func RegisterFullSyncTester(stack *node.Node, backend *eth.Ethereum, block *types.Block) (*FullSyncTester, error) {
+ cl := &FullSyncTester{
+ api: NewConsensusAPI(backend),
+ block: block,
+ closed: make(chan struct{}),
+ }
+ stack.RegisterLifecycle(cl)
+ return cl, nil
+}
+
+// Start launches the beacon sync with provided sync target.
+func (tester *FullSyncTester) Start() error {
+ tester.wg.Add(1)
+ go func() {
+ defer tester.wg.Done()
+
+ ticker := time.NewTicker(time.Second * 5)
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ // Don't bother downloader in case it's already syncing.
+ if tester.api.eth.Downloader().Synchronising() {
+ continue
+ }
+ // Short circuit in case the target block is already stored
+ // locally. TODO(somehow terminate the node stack if target
+ // is reached).
+ if tester.api.eth.BlockChain().HasBlock(tester.block.Hash(), tester.block.NumberU64()) {
+ log.Info("Full-sync target reached", "number", tester.block.NumberU64(), "hash", tester.block.Hash())
+ return
+ }
+ // Trigger beacon sync with the provided block header as
+ // trusted chain head.
+ err := tester.api.eth.Downloader().BeaconSync(downloader.FullSync, tester.block.Header())
+ if err != nil {
+ log.Info("Failed to beacon sync", "err", err)
+ }
+
+ case <-tester.closed:
+ return
+ }
+ }
+ }()
+ return nil
+}
+
+// Stop stops the full-sync tester to stop all background activities.
+// This function can only be called for one time.
+func (tester *FullSyncTester) Stop() error {
+ close(tester.closed)
+ tester.wg.Wait()
+ return nil
+}
diff --git a/eth/downloader/beaconsync.go b/eth/downloader/beaconsync.go
index 484a4e20de..65d9225f8b 100644
--- a/eth/downloader/beaconsync.go
+++ b/eth/downloader/beaconsync.go
@@ -23,6 +23,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
@@ -270,7 +271,8 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) {
// fetchBeaconHeaders feeds skeleton headers to the downloader queue for scheduling
// until sync errors or is finished.
func (d *Downloader) fetchBeaconHeaders(from uint64) error {
- head, tail, err := d.skeleton.Bounds()
+ var head *types.Header
+ _, tail, err := d.skeleton.Bounds()
if err != nil {
return err
}
@@ -288,6 +290,47 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error {
log.Warn("Retrieved beacon headers from local", "from", from, "count", count)
}
for {
+ // Some beacon headers might have appeared since the last cycle, make
+ // sure we're always syncing to all available ones
+ head, _, err = d.skeleton.Bounds()
+ if err != nil {
+ return err
+ }
+ // If the pivot became stale (older than 2*64-8 (bit of wiggle room)),
+ // move it ahead to HEAD-64
+ d.pivotLock.Lock()
+ if d.pivotHeader != nil {
+ if head.Number.Uint64() > d.pivotHeader.Number.Uint64()+2*uint64(fsMinFullBlocks)-8 {
+ // Retrieve the next pivot header, either from skeleton chain
+ // or the filled chain
+ number := head.Number.Uint64() - uint64(fsMinFullBlocks)
+
+ log.Warn("Pivot seemingly stale, moving", "old", d.pivotHeader.Number, "new", number)
+ if d.pivotHeader = d.skeleton.Header(number); d.pivotHeader == nil {
+ if number < tail.Number.Uint64() {
+ dist := tail.Number.Uint64() - number
+ if len(localHeaders) >= int(dist) {
+ d.pivotHeader = localHeaders[dist-1]
+ log.Warn("Retrieved pivot header from local", "number", d.pivotHeader.Number, "hash", d.pivotHeader.Hash(), "latest", head.Number, "oldest", tail.Number)
+ }
+ }
+ }
+ // Print an error log and return directly in case the pivot header
+ // is still not found. It means the skeleton chain is not linked
+ // correctly with local chain.
+ if d.pivotHeader == nil {
+ log.Error("Pivot header is not found", "number", number)
+ d.pivotLock.Unlock()
+ return errNoPivotHeader
+ }
+ // Write out the pivot into the database so a rollback beyond
+ // it will reenable snap sync and update the state root that
+ // the state syncer will be downloading
+ rawdb.WriteLastPivotNumber(d.stateDB, d.pivotHeader.Number.Uint64())
+ }
+ }
+ d.pivotLock.Unlock()
+
// Retrieve a batch of headers and feed it to the header processor
var (
headers = make([]*types.Header, 0, maxHeadersProcess)
@@ -343,9 +386,5 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error {
case <-d.cancelCh:
return errCanceled
}
- head, _, err = d.skeleton.Bounds()
- if err != nil {
- return err
- }
}
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index c04352f0aa..bb74efe754 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/trie"
)
var (
@@ -153,6 +154,11 @@ type Downloader struct {
bodyFetchHook func([]*types.Header) // Method to call upon starting a block body fetch
receiptFetchHook func([]*types.Header) // Method to call upon starting a receipt fetch
chainInsertHook func([]*fetchResult) // Method to call upon inserting a chain of blocks (possibly in multiple invocations)
+
+ // Progress reporting metrics
+ syncStartBlock uint64 // Head snap block when Geth was started
+ syncStartTime time.Time // Time instance when chain sync started
+ syncLogTime time.Time // Time instance when status was last reported
}
// LightChain encapsulates functions required to synchronise a light chain.
@@ -206,6 +212,10 @@ type BlockChain interface {
// Snapshots returns the blockchain snapshot tree to paused it during sync.
Snapshots() *snapshot.Tree
+
+ // TrieDB retrieves the low level trie database used for interacting
+ // with trie nodes.
+ TrieDB() *trie.Database
}
// New creates a new downloader to fetch hashes and blocks from remote peers.
@@ -224,9 +234,11 @@ func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain Bl
dropPeer: dropPeer,
headerProcCh: make(chan *headerTask, 1),
quitCh: make(chan struct{}),
- SnapSyncer: snap.NewSyncer(stateDb),
+ SnapSyncer: snap.NewSyncer(stateDb, chain.TrieDB().Scheme()),
stateSyncStart: make(chan *stateSync),
+ syncStartBlock: chain.CurrentFastBlock().NumberU64(),
}
+ // Create the post-merge skeleton syncer and start the process
dl.skeleton = newSkeleton(stateDb, dl.peers, dropPeer, newBeaconBackfiller(dl, success))
go dl.stateFetcher()
@@ -476,7 +488,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd *
return err
}
} else {
- // In beacon mode, user the skeleton chain to retrieve the headers from
+ // In beacon mode, use the skeleton chain to retrieve the headers from
latest, _, err = d.skeleton.Bounds()
if err != nil {
return err
@@ -741,9 +753,11 @@ func (d *Downloader) fetchHead(p *peerConnection) (head *types.Header, pivot *ty
// calculateRequestSpan calculates what headers to request from a peer when trying to determine the
// common ancestor.
// It returns parameters to be used for peer.RequestHeadersByNumber:
-// from - starting block number
-// count - number of headers to request
-// skip - number of headers to skip
+//
+// from - starting block number
+// count - number of headers to request
+// skip - number of headers to skip
+//
// and also returns 'max', the last block which is expected to be returned by the remote peers,
// given the (from,count,skip)
func calculateRequestSpan(remoteHeight, localHeight uint64) (int64, int, int, uint64) {
@@ -1167,7 +1181,7 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, head uint64) e
}
}
}
- // If no headers have bene delivered, or all of them have been delayed,
+ // If no headers have been delivered, or all of them have been delayed,
// sleep a bit and retry. Take care with headers already consumed during
// skeleton filling
if len(headers) == 0 && !progressed {
@@ -1541,7 +1555,7 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
)
blocks := make([]*types.Block, len(results))
for i, result := range results {
- blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
+ blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals)
}
// Downloaded blocks are always regarded as trusted after the
// transition. Because the downloaded chain is guided by the
@@ -1607,6 +1621,7 @@ func (d *Downloader) processSnapSyncContent() error {
if len(results) == 0 {
// If pivot sync is done, stop
if oldPivot == nil {
+ d.reportSnapSyncProgress(true)
return sync.Cancel()
}
// If sync failed, stop
@@ -1620,6 +1635,8 @@ func (d *Downloader) processSnapSyncContent() error {
if d.chainInsertHook != nil {
d.chainInsertHook(results)
}
+ d.reportSnapSyncProgress(false)
+
// If we haven't downloaded the pivot block yet, check pivot staleness
// notifications from the header downloader
d.pivotLock.RLock()
@@ -1732,7 +1749,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
}
default:
}
- // Retrieve the a batch of results to import
+ // Retrieve the batch of results to import
first, last := results[0].Header, results[len(results)-1].Header
log.Debug("Inserting snap-sync blocks", "items", len(results),
"firstnum", first.Number, "firsthash", first.Hash(),
@@ -1741,7 +1758,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
blocks := make([]*types.Block, len(results))
receipts := make([]types.Receipts, len(results))
for i, result := range results {
- blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
+ blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals)
receipts[i] = result.Receipts
}
if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil {
@@ -1752,7 +1769,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
}
func (d *Downloader) commitPivotBlock(result *fetchResult) error {
- block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
+ block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals)
log.Debug("Committing snap sync pivot as new head", "number", block.Number(), "hash", block.Hash())
// Commit the pivot block as the new head, will require full sync from here on
@@ -1813,3 +1830,56 @@ func (d *Downloader) readHeaderRange(last *types.Header, count int) []*types.Hea
}
return headers
}
+
+// reportSnapSyncProgress calculates various status reports and provides it to the user.
+func (d *Downloader) reportSnapSyncProgress(force bool) {
+ // Initialize the sync start time if it's the first time we're reporting
+ if d.syncStartTime.IsZero() {
+ d.syncStartTime = time.Now().Add(-time.Millisecond) // -1ms offset to avoid division by zero
+ }
+ // Don't report all the events, just occasionally
+ if !force && time.Since(d.syncLogTime) < 8*time.Second {
+ return
+ }
+ // Don't report anything until we have a meaningful progress
+ var (
+ headerBytes, _ = d.stateDB.AncientSize(rawdb.ChainFreezerHeaderTable)
+ bodyBytes, _ = d.stateDB.AncientSize(rawdb.ChainFreezerBodiesTable)
+ receiptBytes, _ = d.stateDB.AncientSize(rawdb.ChainFreezerReceiptTable)
+ )
+ syncedBytes := common.StorageSize(headerBytes + bodyBytes + receiptBytes)
+ if syncedBytes == 0 {
+ return
+ }
+ var (
+ header = d.blockchain.CurrentHeader()
+ block = d.blockchain.CurrentFastBlock()
+ )
+ syncedBlocks := block.NumberU64() - d.syncStartBlock
+ if syncedBlocks == 0 {
+ return
+ }
+ // Retrieve the current chain head and calculate the ETA
+ latest, _, err := d.skeleton.Bounds()
+ if err != nil {
+ // We're going to cheat for non-merged networks, but that's fine
+ latest = d.pivotHeader
+ }
+ if latest == nil {
+ // This should really never happen, but add some defensive code for now.
+ // TODO(karalabe): Remove it eventually if we don't see it blow.
+ log.Error("Nil latest block in sync progress report")
+ return
+ }
+ var (
+ left = latest.Number.Uint64() - block.NumberU64()
+ eta = time.Since(d.syncStartTime) / time.Duration(syncedBlocks) * time.Duration(left)
+
+ progress = fmt.Sprintf("%.2f%%", float64(block.NumberU64())*100/float64(latest.Number.Uint64()))
+ headers = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(header.Number.Uint64()), common.StorageSize(headerBytes).TerminalString())
+ bodies = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(block.NumberU64()), common.StorageSize(bodyBytes).TerminalString())
+ receipts = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(block.NumberU64()), common.StorageSize(receiptBytes).TerminalString())
+ )
+ log.Info("Syncing: chain download in progress", "synced", progress, "chain", syncedBytes, "headers", headers, "bodies", bodies, "receipts", receipts, "eta", common.PrettyDuration(eta))
+ d.syncLogTime = time.Now()
+}
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index 450ed61efc..2f0c4acf78 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -68,13 +68,12 @@ func newTesterWithNotification(t *testing.T, success func()) *downloadTester {
t.Cleanup(func() {
db.Close()
})
- gspec := core.Genesis{
+ gspec := &core.Genesis{
+ Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}},
BaseFee: big.NewInt(params.InitialBaseFee),
}
- gspec.MustCommit(db)
-
- chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+ chain, err := core.NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
if err != nil {
panic(err)
}
@@ -274,8 +273,9 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
rlp.DecodeBytes(blob, bodies[i])
}
var (
- txsHashes = make([]common.Hash, len(bodies))
- uncleHashes = make([]common.Hash, len(bodies))
+ txsHashes = make([]common.Hash, len(bodies))
+ uncleHashes = make([]common.Hash, len(bodies))
+ withdrawalHashes = make([]common.Hash, len(bodies))
)
hasher := trie.NewStackTrie(nil)
for i, body := range bodies {
@@ -288,7 +288,7 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
res := ð.Response{
Req: req,
Res: (*eth.BlockBodiesPacket)(&bodies),
- Meta: [][]common.Hash{txsHashes, uncleHashes},
+ Meta: [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes},
Time: 1,
Done: make(chan error, 1), // Ignore the returned status
}
diff --git a/eth/downloader/fetchers_concurrent.go b/eth/downloader/fetchers_concurrent.go
index 44e6aa8f8d..649aa27615 100644
--- a/eth/downloader/fetchers_concurrent.go
+++ b/eth/downloader/fetchers_concurrent.go
@@ -91,8 +91,8 @@ func (d *Downloader) concurrentFetch(queue typedQueue, beaconMode bool) error {
}
}()
ordering := make(map[*eth.Request]int)
- timeouts := prque.New(func(data interface{}, index int) {
- ordering[data.(*eth.Request)] = index
+ timeouts := prque.New[int64, *eth.Request](func(data *eth.Request, index int) {
+ ordering[data] = index
})
timeout := time.NewTimer(0)
@@ -268,27 +268,26 @@ func (d *Downloader) concurrentFetch(queue typedQueue, beaconMode bool) error {
// below is purely for to catch programming errors, given the correct
// code, there's no possible order of events that should result in a
// timeout firing for a non-existent event.
- item, exp := timeouts.Peek()
+ req, exp := timeouts.Peek()
if now, at := time.Now(), time.Unix(0, -exp); now.Before(at) {
log.Error("Timeout triggered but not reached", "left", at.Sub(now))
timeout.Reset(at.Sub(now))
continue
}
- req := item.(*eth.Request)
-
// Stop tracking the timed out request from a timing perspective,
// cancel it, so it's not considered in-flight anymore, but keep
// the peer marked busy to prevent assigning a second request and
// overloading it further.
delete(pending, req.Peer)
stales[req.Peer] = req
- delete(ordering, req)
- timeouts.Pop()
+ timeouts.Pop() // Popping an item will reorder indices in `ordering`, delete after, otherwise will resurrect!
if timeouts.Size() > 0 {
_, exp := timeouts.Peek()
timeout.Reset(time.Until(time.Unix(0, -exp)))
}
+ delete(ordering, req)
+
// New timeout potentially set if there are more requests pending,
// reschedule the failed one to a free peer
fails := queue.unreserve(req.Peer)
diff --git a/eth/downloader/fetchers_concurrent_bodies.go b/eth/downloader/fetchers_concurrent_bodies.go
index e84206fe99..9440972c6d 100644
--- a/eth/downloader/fetchers_concurrent_bodies.go
+++ b/eth/downloader/fetchers_concurrent_bodies.go
@@ -89,10 +89,10 @@ func (q *bodyQueue) request(peer *peerConnection, req *fetchRequest, resCh chan
// deliver is responsible for taking a generic response packet from the concurrent
// fetcher, unpacking the body data and delivering it to the downloader's queue.
func (q *bodyQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) {
- txs, uncles := packet.Res.(*eth.BlockBodiesPacket).Unpack()
- hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes}
+ txs, uncles, withdrawals := packet.Res.(*eth.BlockBodiesPacket).Unpack()
+ hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes, withdrawal hashes}
- accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1])
+ accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1], withdrawals, hashsets[2])
switch {
case err == nil && len(txs) == 0:
peer.log.Trace("Requested bodies delivered")
diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go
index a8d2ea83a9..5af5068c98 100644
--- a/eth/downloader/queue.go
+++ b/eth/downloader/queue.go
@@ -67,6 +67,7 @@ type fetchResult struct {
Uncles []*types.Header
Transactions types.Transactions
Receipts types.Receipts
+ Withdrawals types.Withdrawals
}
func newFetchResult(header *types.Header, fastSync bool) *fetchResult {
@@ -75,6 +76,8 @@ func newFetchResult(header *types.Header, fastSync bool) *fetchResult {
}
if !header.EmptyBody() {
item.pending |= (1 << bodyType)
+ } else if header.WithdrawalsHash != nil {
+ item.Withdrawals = make(types.Withdrawals, 0)
}
if fastSync && !header.EmptyReceipts() {
item.pending |= (1 << receiptType)
@@ -114,7 +117,7 @@ type queue struct {
// Headers are "special", they download in batches, supported by a skeleton chain
headerHead common.Hash // Hash of the last queued header to verify order
headerTaskPool map[uint64]*types.Header // Pending header retrieval tasks, mapping starting indexes to skeleton headers
- headerTaskQueue *prque.Prque // Priority queue of the skeleton indexes to fetch the filling headers for
+ headerTaskQueue *prque.Prque[int64, uint64] // Priority queue of the skeleton indexes to fetch the filling headers for
headerPeerMiss map[string]map[uint64]struct{} // Set of per-peer header batches known to be unavailable
headerPendPool map[string]*fetchRequest // Currently pending header retrieval operations
headerResults []*types.Header // Result cache accumulating the completed headers
@@ -124,15 +127,15 @@ type queue struct {
headerContCh chan bool // Channel to notify when header download finishes
// All data retrievals below are based on an already assembles header chain
- blockTaskPool map[common.Hash]*types.Header // Pending block (body) retrieval tasks, mapping hashes to headers
- blockTaskQueue *prque.Prque // Priority queue of the headers to fetch the blocks (bodies) for
- blockPendPool map[string]*fetchRequest // Currently pending block (body) retrieval operations
- blockWakeCh chan bool // Channel to notify the block fetcher of new tasks
+ blockTaskPool map[common.Hash]*types.Header // Pending block (body) retrieval tasks, mapping hashes to headers
+ blockTaskQueue *prque.Prque[int64, *types.Header] // Priority queue of the headers to fetch the blocks (bodies) for
+ blockPendPool map[string]*fetchRequest // Currently pending block (body) retrieval operations
+ blockWakeCh chan bool // Channel to notify the block fetcher of new tasks
- receiptTaskPool map[common.Hash]*types.Header // Pending receipt retrieval tasks, mapping hashes to headers
- receiptTaskQueue *prque.Prque // Priority queue of the headers to fetch the receipts for
- receiptPendPool map[string]*fetchRequest // Currently pending receipt retrieval operations
- receiptWakeCh chan bool // Channel to notify when receipt fetcher of new tasks
+ receiptTaskPool map[common.Hash]*types.Header // Pending receipt retrieval tasks, mapping hashes to headers
+ receiptTaskQueue *prque.Prque[int64, *types.Header] // Priority queue of the headers to fetch the receipts for
+ receiptPendPool map[string]*fetchRequest // Currently pending receipt retrieval operations
+ receiptWakeCh chan bool // Channel to notify when receipt fetcher of new tasks
resultCache *resultStore // Downloaded but not yet delivered fetch results
resultSize common.StorageSize // Approximate size of a block (exponential moving average)
@@ -141,7 +144,7 @@ type queue struct {
active *sync.Cond
closed bool
- lastStatLog time.Time
+ logTime time.Time // Time instance when status was last reported
}
// newQueue creates a new download queue for scheduling block retrieval.
@@ -149,9 +152,9 @@ func newQueue(blockCacheLimit int, thresholdInitialSize int) *queue {
lock := new(sync.RWMutex)
q := &queue{
headerContCh: make(chan bool, 1),
- blockTaskQueue: prque.New(nil),
+ blockTaskQueue: prque.New[int64, *types.Header](nil),
blockWakeCh: make(chan bool, 1),
- receiptTaskQueue: prque.New(nil),
+ receiptTaskQueue: prque.New[int64, *types.Header](nil),
receiptWakeCh: make(chan bool, 1),
active: sync.NewCond(lock),
lock: lock,
@@ -257,7 +260,7 @@ func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) {
}
// Schedule all the header retrieval tasks for the skeleton assembly
q.headerTaskPool = make(map[uint64]*types.Header)
- q.headerTaskQueue = prque.New(nil)
+ q.headerTaskQueue = prque.New[int64, uint64](nil)
q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains
q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch)
q.headerHashes = make([]common.Hash, len(skeleton)*MaxHeaderFetch)
@@ -369,7 +372,7 @@ func (q *queue) Results(block bool) []*fetchResult {
size += receipt.Size()
}
for _, tx := range result.Transactions {
- size += tx.Size()
+ size += common.StorageSize(tx.Size())
}
q.resultSize = common.StorageSize(blockCacheSizeWeight)*size +
(1-common.StorageSize(blockCacheSizeWeight))*q.resultSize
@@ -387,11 +390,12 @@ func (q *queue) Results(block bool) []*fetchResult {
}
}
// Log some info at certain times
- if time.Since(q.lastStatLog) > 60*time.Second {
- q.lastStatLog = time.Now()
+ if time.Since(q.logTime) >= 60*time.Second {
+ q.logTime = time.Now()
+
info := q.Stats()
info = append(info, "throttle", throttleThreshold)
- log.Info("Downloader queue stats", info...)
+ log.Debug("Downloader queue stats", info...)
}
return results
}
@@ -427,12 +431,12 @@ func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest {
for send == 0 && !q.headerTaskQueue.Empty() {
from, _ := q.headerTaskQueue.Pop()
if q.headerPeerMiss[p.id] != nil {
- if _, ok := q.headerPeerMiss[p.id][from.(uint64)]; ok {
- skip = append(skip, from.(uint64))
+ if _, ok := q.headerPeerMiss[p.id][from]; ok {
+ skip = append(skip, from)
continue
}
}
- send = from.(uint64)
+ send = from
}
// Merge all the skipped batches back
for _, from := range skip {
@@ -480,10 +484,11 @@ func (q *queue) ReserveReceipts(p *peerConnection, count int) (*fetchRequest, bo
// to access the queue, so they already need a lock anyway.
//
// Returns:
-// item - the fetchRequest
-// progress - whether any progress was made
-// throttle - if the caller should throttle for a while
-func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque,
+//
+// item - the fetchRequest
+// progress - whether any progress was made
+// throttle - if the caller should throttle for a while
+func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque[int64, *types.Header],
pendPool map[string]*fetchRequest, kind uint) (*fetchRequest, bool, bool) {
// Short circuit if the pool has been depleted, or if the peer's already
// downloading something (sanity check not to corrupt state)
@@ -501,8 +506,8 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
for proc := 0; len(send) < count && !taskQueue.Empty(); proc++ {
// the task queue will pop items in order, so the highest prio block
// is also the lowest block number.
- h, _ := taskQueue.Peek()
- header := h.(*types.Header)
+ header, _ := taskQueue.Peek()
+
// we can ask the resultcache if this header is within the
// "prioritized" segment of blocks. If it is not, we need to throttle
@@ -625,13 +630,15 @@ func (q *queue) ExpireReceipts(peer string) int {
}
// expire is the generic check that moves a specific expired task from a pending
-// pool back into a task pool.
+// pool back into a task pool. The syntax on the passed taskQueue is a bit weird
+// as we would need a generic expire method to handle both types, but that is not
+// supported at the moment at least (Go 1.19).
//
// Note, this method expects the queue lock to be already held. The reason the
// lock is not obtained in here is that the parameters already need to access
// the queue, so they already need a lock anyway.
-func (q *queue) expire(peer string, pendPool map[string]*fetchRequest, taskQueue *prque.Prque) int {
- // Retrieve the request being expired and log an error if it's non-existnet,
+func (q *queue) expire(peer string, pendPool map[string]*fetchRequest, taskQueue interface{}) int {
+ // Retrieve the request being expired and log an error if it's non-existent,
// as there's no order of events that should lead to such expirations.
req := pendPool[peer]
if req == nil {
@@ -642,10 +649,10 @@ func (q *queue) expire(peer string, pendPool map[string]*fetchRequest, taskQueue
// Return any non-satisfied requests to the pool
if req.From > 0 {
- taskQueue.Push(req.From, -int64(req.From))
+ taskQueue.(*prque.Prque[int64, uint64]).Push(req.From, -int64(req.From))
}
for _, header := range req.Headers {
- taskQueue.Push(header, -int64(header.Number.Uint64()))
+ taskQueue.(*prque.Prque[int64, *types.Header]).Push(header, -int64(header.Number.Uint64()))
}
return len(req.Headers)
}
@@ -763,7 +770,9 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, hashes []comm
// DeliverBodies injects a block body retrieval response into the results queue.
// The method returns the number of blocks bodies accepted from the delivery and
// also wakes any threads waiting for data delivery.
-func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListHashes []common.Hash, uncleLists [][]*types.Header, uncleListHashes []common.Hash) (int, error) {
+func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListHashes []common.Hash,
+ uncleLists [][]*types.Header, uncleListHashes []common.Hash,
+ withdrawalLists [][]*types.Withdrawal, withdrawalListHashes []common.Hash) (int, error) {
q.lock.Lock()
defer q.lock.Unlock()
@@ -774,12 +783,26 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
if uncleListHashes[index] != header.UncleHash {
return errInvalidBody
}
+ if header.WithdrawalsHash == nil {
+ // nil hash means that withdrawals should not be present in body
+ if withdrawalLists[index] != nil {
+ return errInvalidBody
+ }
+ } else { // non-nil hash: body must have withdrawals
+ if withdrawalLists[index] == nil {
+ return errInvalidBody
+ }
+ if withdrawalListHashes[index] != *header.WithdrawalsHash {
+ return errInvalidBody
+ }
+ }
return nil
}
reconstruct := func(index int, result *fetchResult) {
result.Transactions = txLists[index]
result.Uncles = uncleLists[index]
+ result.Withdrawals = withdrawalLists[index]
result.SetBodyDone()
}
return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool,
@@ -813,7 +836,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt, recei
// reason this lock is not obtained in here is because the parameters already need
// to access the queue, so they already need a lock anyway.
func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
- taskQueue *prque.Prque, pendPool map[string]*fetchRequest,
+ taskQueue *prque.Prque[int64, *types.Header], pendPool map[string]*fetchRequest,
reqTimer metrics.Timer, resInMeter metrics.Meter, resDropMeter metrics.Meter,
results int, validate func(index int, header *types.Header) error,
reconstruct func(index int, result *fetchResult)) (int, error) {
@@ -856,7 +879,7 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
}
for _, header := range request.Headers[:i] {
- if res, stale, err := q.resultCache.GetDeliverySlot(header.Number.Uint64()); err == nil {
+ if res, stale, err := q.resultCache.GetDeliverySlot(header.Number.Uint64()); err == nil && !stale {
reconstruct(accepted, res)
} else {
// else: between here and above, some other peer filled this result,
diff --git a/eth/downloader/queue_test.go b/eth/downloader/queue_test.go
index 8631b27c92..6babf94408 100644
--- a/eth/downloader/queue_test.go
+++ b/eth/downloader/queue_test.go
@@ -339,7 +339,7 @@ func XTestDelivery(t *testing.T) {
uncleHashes[i] = types.CalcUncleHash(uncles)
}
time.Sleep(100 * time.Millisecond)
- _, err := q.DeliverBodies(peer.id, txset, txsHashes, uncleset, uncleHashes)
+ _, err := q.DeliverBodies(peer.id, txset, txsHashes, uncleset, uncleHashes, nil, nil)
if err != nil {
fmt.Printf("delivered %d bodies %v\n", len(txset), err)
}
diff --git a/eth/downloader/resultstore.go b/eth/downloader/resultstore.go
index 3162cd6d5b..a550f8c109 100644
--- a/eth/downloader/resultstore.go
+++ b/eth/downloader/resultstore.go
@@ -71,10 +71,11 @@ func (r *resultStore) SetThrottleThreshold(threshold uint64) uint64 {
// wants to reserve headers for fetching.
//
// It returns the following:
-// stale - if true, this item is already passed, and should not be requested again
-// throttled - if true, the store is at capacity, this particular header is not prio now
-// item - the result to store data into
-// err - any error that occurred
+//
+// stale - if true, this item is already passed, and should not be requested again
+// throttled - if true, the store is at capacity, this particular header is not prio now
+// item - the result to store data into
+// err - any error that occurred
func (r *resultStore) AddFetch(header *types.Header, fastSync bool) (stale, throttled bool, item *fetchResult, err error) {
r.lock.Lock()
defer r.lock.Unlock()
@@ -123,7 +124,7 @@ func (r *resultStore) getFetchResult(headerNumber uint64) (item *fetchResult, in
return item, index, stale, throttle, nil
}
-// hasCompletedItems returns true if there are processable items available
+// HasCompletedItems returns true if there are processable items available
// this method is cheaper than countCompleted
func (r *resultStore) HasCompletedItems() bool {
r.lock.RLock()
diff --git a/eth/downloader/skeleton.go b/eth/downloader/skeleton.go
index 517b8378c5..142e9e5e6b 100644
--- a/eth/downloader/skeleton.go
+++ b/eth/downloader/skeleton.go
@@ -35,7 +35,7 @@ import (
// scratchHeaders is the number of headers to store in a scratch space to allow
// concurrent downloads. A header is about 0.5KB in size, so there is no worry
// about using too much memory. The only catch is that we can only validate gaps
-// afer they're linked to the head, so the bigger the scratch space, the larger
+// after they're linked to the head, so the bigger the scratch space, the larger
// potential for invalid headers.
//
// The current scratch space of 131072 headers is expected to use 64MB RAM.
@@ -520,7 +520,7 @@ func (s *skeleton) initSync(head *types.Header) {
}
break
}
- // If the last subchain can be extended, we're lucky. Otherwise create
+ // If the last subchain can be extended, we're lucky. Otherwise, create
// a new subchain sync task.
var extended bool
if n := len(s.progress.Subchains); n > 0 {
@@ -977,8 +977,14 @@ func (s *skeleton) processResponse(res *headerResponse) (linked bool, merged boo
// the expected new sync cycle after some propagated blocks. Log
// it for debugging purposes, explicitly clean and don't escalate.
case subchains == 2 && s.progress.Subchains[1].Head == s.progress.Subchains[1].Tail:
- log.Debug("Cleaning previous beacon sync state", "head", s.progress.Subchains[1].Head)
- rawdb.DeleteSkeletonHeader(batch, s.progress.Subchains[1].Head)
+ // Remove the leftover skeleton header associated with old
+ // skeleton chain only if it's not covered by the current
+ // skeleton range.
+ if s.progress.Subchains[1].Head < s.progress.Subchains[0].Tail {
+ log.Debug("Cleaning previous beacon sync state", "head", s.progress.Subchains[1].Head)
+ rawdb.DeleteSkeletonHeader(batch, s.progress.Subchains[1].Head)
+ }
+ // Drop the leftover skeleton chain since it's stale.
s.progress.Subchains = s.progress.Subchains[:1]
// If we have more than one header or more than one leftover chain,
@@ -1096,6 +1102,7 @@ func (s *skeleton) cleanStales(filled *types.Header) error {
var (
start = s.progress.Subchains[0].Tail // start deleting from the first known header
end = number // delete until the requested threshold
+ batch = s.db.NewBatch()
)
s.progress.Subchains[0].Tail = number
s.progress.Subchains[0].Next = filled.ParentHash
@@ -1105,16 +1112,13 @@ func (s *skeleton) cleanStales(filled *types.Header) error {
// subchain forward to keep tracking the node's block imports
end = s.progress.Subchains[0].Head + 1 // delete the entire original range, including the head
s.progress.Subchains[0].Head = number // assign a new head (tail is already assigned to this)
- }
- // Execute the trimming and the potential rewiring of the progress
- batch := s.db.NewBatch()
- if end != number {
// The entire original skeleton chain was deleted and a new one
// defined. Make sure the new single-header chain gets pushed to
// disk to keep internal state consistent.
rawdb.WriteSkeletonHeader(batch, filled)
}
+ // Execute the trimming and the potential rewiring of the progress
s.saveSyncStatus(batch)
for n := start; n < end; n++ {
// If the batch grew too big, flush it and continue with a new batch.
@@ -1170,8 +1174,13 @@ func (s *skeleton) Bounds() (head *types.Header, tail *types.Header, err error)
return nil, nil, err
}
head = rawdb.ReadSkeletonHeader(s.db, progress.Subchains[0].Head)
+ if head == nil {
+ return nil, nil, fmt.Errorf("head skeleton header %d is missing", progress.Subchains[0].Head)
+ }
tail = rawdb.ReadSkeletonHeader(s.db, progress.Subchains[0].Tail)
-
+ if tail == nil {
+ return nil, nil, fmt.Errorf("tail skeleton header %d is missing", progress.Subchains[0].Tail)
+ }
return head, tail, nil
}
diff --git a/eth/downloader/skeleton_test.go b/eth/downloader/skeleton_test.go
index 6bcbac3a89..3b8e627cba 100644
--- a/eth/downloader/skeleton_test.go
+++ b/eth/downloader/skeleton_test.go
@@ -37,7 +37,7 @@ import (
type hookedBackfiller struct {
// suspendHook is an optional hook to be called when the filler is requested
// to be suspended.
- suspendHook func()
+ suspendHook func() *types.Header
// resumeHook is an optional hook to be called when the filler is requested
// to be resumed.
@@ -56,7 +56,7 @@ func newHookedBackfiller() backfiller {
// on initial startup.
func (hf *hookedBackfiller) suspend() *types.Header {
if hf.suspendHook != nil {
- hf.suspendHook()
+ return hf.suspendHook()
}
return nil // we don't really care about header cleanups for now
}
@@ -525,9 +525,21 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
Number: big.NewInt(int64(i)),
})
}
+ // Some tests require a forking side chain to trigger cornercases.
+ var sidechain []*types.Header
+ for i := 0; i < len(chain)/2; i++ { // Fork at block #5000
+ sidechain = append(sidechain, chain[i])
+ }
+ for i := len(chain) / 2; i < len(chain); i++ {
+ sidechain = append(sidechain, &types.Header{
+ ParentHash: sidechain[i-1].Hash(),
+ Number: big.NewInt(int64(i)),
+ Extra: []byte("B"), // force a different hash
+ })
+ }
tests := []struct {
- headers []*types.Header // Database content (beside the genesis)
- oldstate []*subchain // Old sync state with various interrupted subchains
+ fill bool // Whether to run a real backfiller in this test case
+ unpredictable bool // Whether to ignore drops/serves due to uncertain packet assignments
head *types.Header // New head header to announce to reorg to
peers []*skeletonTestPeer // Initial peer set to start the sync with
@@ -760,11 +772,41 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
endstate: []*subchain{{Head: 2*requestHeaders + 2, Tail: 1}},
endserve: 4 * requestHeaders,
},
+ // This test reproduces a bug caught by (@rjl493456442) where a skeleton
+ // header goes missing, causing the sync to get stuck and/or panic.
+ //
+ // The setup requires a previously successfully synced chain up to a block
+ // height N. That results is a single skeleton header (block N) and a single
+ // subchain (head N, Tail N) being stored on disk.
+ //
+ // The following step requires a new sync cycle to a new side chain of a
+ // height higher than N, and an ancestor lower than N (e.g. N-2, N+2).
+ // In this scenario, when processing a batch of headers, a link point of
+ // N-2 will be found, meaning that N-1 and N have been overwritten.
+ //
+ // The link event triggers an early exit, noticing that the previous sub-
+ // chain is a leftover and deletes it (with it's skeleton header N). But
+ // since skeleton header N has been overwritten to the new side chain, we
+ // end up losing it and creating a gap.
+ {
+ fill: true,
+ unpredictable: true, // We have good and bad peer too, bad may be dropped, test too short for certainty
+
+ head: chain[len(chain)/2+1], // Sync up until the sidechain common ancestor + 2
+ peers: []*skeletonTestPeer{newSkeletonTestPeer("test-peer-oldchain", chain)},
+ midstate: []*subchain{{Head: uint64(len(chain)/2 + 1), Tail: 1}},
+
+ newHead: sidechain[len(sidechain)/2+3], // Sync up until the sidechain common ancestor + 4
+ newPeer: newSkeletonTestPeer("test-peer-newchain", sidechain),
+ endstate: []*subchain{{Head: uint64(len(sidechain)/2 + 3), Tail: uint64(len(chain) / 2)}},
+ },
}
for i, tt := range tests {
// Create a fresh database and initialize it with the starting state
db := rawdb.NewMemoryDatabase()
- rawdb.WriteHeader(db, chain[0])
+
+ rawdb.WriteBlock(db, types.NewBlockWithHeader(chain[0]))
+ rawdb.WriteReceipts(db, chain[0].Hash(), chain[0].Number.Uint64(), types.Receipts{})
// Create a peer set to feed headers through
peerset := newPeerSet()
@@ -780,8 +822,43 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
peerset.Unregister(peer)
dropped[peer]++
}
+ // Create a backfiller if we need to run more advanced tests
+ filler := newHookedBackfiller()
+ if tt.fill {
+ var filled *types.Header
+
+ filler = &hookedBackfiller{
+ resumeHook: func() {
+ var progress skeletonProgress
+ json.Unmarshal(rawdb.ReadSkeletonSyncStatus(db), &progress)
+
+ for progress.Subchains[0].Tail < progress.Subchains[0].Head {
+ header := rawdb.ReadSkeletonHeader(db, progress.Subchains[0].Tail)
+
+ rawdb.WriteBlock(db, types.NewBlockWithHeader(header))
+ rawdb.WriteReceipts(db, header.Hash(), header.Number.Uint64(), types.Receipts{})
+
+ rawdb.DeleteSkeletonHeader(db, header.Number.Uint64())
+
+ progress.Subchains[0].Tail++
+ progress.Subchains[0].Next = header.Hash()
+ }
+ filled = rawdb.ReadSkeletonHeader(db, progress.Subchains[0].Tail)
+
+ rawdb.WriteBlock(db, types.NewBlockWithHeader(filled))
+ rawdb.WriteReceipts(db, filled.Hash(), filled.Number.Uint64(), types.Receipts{})
+ },
+
+ suspendHook: func() *types.Header {
+ prev := filled
+ filled = nil
+
+ return prev
+ },
+ }
+ }
// Create a skeleton sync and run a cycle
- skeleton := newSkeleton(db, peerset, drop, newHookedBackfiller())
+ skeleton := newSkeleton(db, peerset, drop, filler)
skeleton.Sync(tt.head, true)
var progress skeletonProgress
@@ -803,7 +880,7 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
}
waitStart := time.Now()
- for waitTime := 20 * time.Millisecond; time.Since(waitStart) < time.Second; waitTime = waitTime * 2 {
+ for waitTime := 20 * time.Millisecond; time.Since(waitStart) < 2*time.Second; waitTime = waitTime * 2 {
time.Sleep(waitTime)
// Check the post-init end state if it matches the required results
json.Unmarshal(rawdb.ReadSkeletonSyncStatus(db), &progress)
@@ -815,19 +892,21 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
t.Error(err)
continue
}
- var served uint64
- for _, peer := range tt.peers {
- served += atomic.LoadUint64(&peer.served)
- }
- if served != tt.midserve {
- t.Errorf("test %d, mid state: served headers mismatch: have %d, want %d", i, served, tt.midserve)
- }
- var drops uint64
- for _, peer := range tt.peers {
- drops += atomic.LoadUint64(&peer.dropped)
- }
- if drops != tt.middrop {
- t.Errorf("test %d, mid state: dropped peers mismatch: have %d, want %d", i, drops, tt.middrop)
+ if !tt.unpredictable {
+ var served uint64
+ for _, peer := range tt.peers {
+ served += atomic.LoadUint64(&peer.served)
+ }
+ if served != tt.midserve {
+ t.Errorf("test %d, mid state: served headers mismatch: have %d, want %d", i, served, tt.midserve)
+ }
+ var drops uint64
+ for _, peer := range tt.peers {
+ drops += atomic.LoadUint64(&peer.dropped)
+ }
+ if drops != tt.middrop {
+ t.Errorf("test %d, mid state: dropped peers mismatch: have %d, want %d", i, drops, tt.middrop)
+ }
}
// Apply the post-init events if there's any
if tt.newHead != nil {
@@ -855,7 +934,7 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
return nil
}
waitStart = time.Now()
- for waitTime := 20 * time.Millisecond; time.Since(waitStart) < time.Second; waitTime = waitTime * 2 {
+ for waitTime := 20 * time.Millisecond; time.Since(waitStart) < 2*time.Second; waitTime = waitTime * 2 {
time.Sleep(waitTime)
// Check the post-init end state if it matches the required results
json.Unmarshal(rawdb.ReadSkeletonSyncStatus(db), &progress)
@@ -868,25 +947,27 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
continue
}
// Check that the peers served no more headers than we actually needed
- served = 0
- for _, peer := range tt.peers {
- served += atomic.LoadUint64(&peer.served)
- }
- if tt.newPeer != nil {
- served += atomic.LoadUint64(&tt.newPeer.served)
- }
- if served != tt.endserve {
- t.Errorf("test %d, end state: served headers mismatch: have %d, want %d", i, served, tt.endserve)
- }
- drops = 0
- for _, peer := range tt.peers {
- drops += atomic.LoadUint64(&peer.dropped)
- }
- if tt.newPeer != nil {
- drops += atomic.LoadUint64(&tt.newPeer.dropped)
- }
- if drops != tt.middrop {
- t.Errorf("test %d, end state: dropped peers mismatch: have %d, want %d", i, drops, tt.middrop)
+ if !tt.unpredictable {
+ served := uint64(0)
+ for _, peer := range tt.peers {
+ served += atomic.LoadUint64(&peer.served)
+ }
+ if tt.newPeer != nil {
+ served += atomic.LoadUint64(&tt.newPeer.served)
+ }
+ if served != tt.endserve {
+ t.Errorf("test %d, end state: served headers mismatch: have %d, want %d", i, served, tt.endserve)
+ }
+ drops := uint64(0)
+ for _, peer := range tt.peers {
+ drops += atomic.LoadUint64(&peer.dropped)
+ }
+ if tt.newPeer != nil {
+ drops += atomic.LoadUint64(&tt.newPeer.dropped)
+ }
+ if drops != tt.enddrop {
+ t.Errorf("test %d, end state: dropped peers mismatch: have %d, want %d", i, drops, tt.middrop)
+ }
}
// Clean up any leftover skeleton sync resources
skeleton.Terminate()
diff --git a/eth/downloader/testchain_test.go b/eth/downloader/testchain_test.go
index 785da40b59..01f81a7b1c 100644
--- a/eth/downloader/testchain_test.go
+++ b/eth/downloader/testchain_test.go
@@ -38,7 +38,8 @@ var (
testAddress = crypto.PubkeyToAddress(testKey.PublicKey)
testDB = rawdb.NewMemoryDatabase()
- testGspec = core.Genesis{
+ testGspec = &core.Genesis{
+ Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}},
BaseFee: big.NewInt(params.InitialBaseFee),
}
@@ -159,7 +160,7 @@ func (tc *testChain) copy(newlen int) *testChain {
// contains a transaction and every 5th an uncle to allow testing correct block
// reassembly.
func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool) {
- blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testDB, n, func(i int, block *core.BlockGen) {
+ blocks, _ := core.GenerateChain(testGspec.Config, parent, ethash.NewFaker(), testDB, n, func(i int, block *core.BlockGen) {
block.SetCoinbase(common.Address{seed})
// If a heavy chain is requested, delay blocks to raise difficulty
if heavy {
@@ -216,10 +217,7 @@ func newTestBlockchain(blocks []*types.Block) *core.BlockChain {
if pregenerated {
panic("Requested chain generation outside of init")
}
- db := rawdb.NewMemoryDatabase()
- testGspec.MustCommit(db)
-
- chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+ chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, testGspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
if err != nil {
panic(err)
}
diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go
index ea80c21a32..1b74d9c655 100644
--- a/eth/ethconfig/config.go
+++ b/eth/ethconfig/config.go
@@ -18,7 +18,6 @@
package ethconfig
import (
- "math/big"
"os"
"os/user"
"path/filepath"
@@ -31,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
@@ -84,16 +84,12 @@ var Defaults = Config{
TrieTimeout: 60 * time.Minute,
SnapshotCache: 102,
FilterLogCacheSize: 32,
- Miner: miner.Config{
- GasCeil: 30000000,
- GasPrice: big.NewInt(params.GWei),
- Recommit: 3 * time.Second,
- },
- TxPool: core.DefaultTxPoolConfig,
- RPCGasCap: 50000000,
- RPCEVMTimeout: 5 * time.Second,
- GPO: FullNodeGPO,
- RPCTxFeeCap: 1, // 1 ether
+ Miner: miner.DefaultConfig,
+ TxPool: txpool.DefaultConfig,
+ RPCGasCap: 50000000,
+ RPCEVMTimeout: 5 * time.Second,
+ GPO: FullNodeGPO,
+ RPCTxFeeCap: 1, // 1 ether
}
func init() {
@@ -182,7 +178,7 @@ type Config struct {
Ethash ethash.Config
// Transaction pool options
- TxPool core.TxPoolConfig
+ TxPool txpool.Config
// Gas Price Oracle options
GPO gasprice.Config
@@ -209,11 +205,6 @@ type Config struct {
// CheckpointOracle is the configuration for checkpoint oracle.
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
- // OverrideTerminalTotalDifficulty (TODO: remove after the fork)
- OverrideTerminalTotalDifficulty *big.Int `toml:",omitempty"`
- // Berlin block override (TODO: remove after the fork)
- OverrideLondon *big.Int `toml:",omitempty"`
-
// List of URIs to connect replication providers to
BlockReplicationTargets []string `toml:",omitempty"`
@@ -221,18 +212,18 @@ type Config struct {
ReplicaEnableResult bool
ReplicaEnableSpecimen bool
- // OverrideTerminalTotalDifficultyPassed (TODO: remove after the fork)
- OverrideTerminalTotalDifficultyPassed *bool `toml:",omitempty"`
+ // OverrideShanghai (TODO: remove after the fork)
+ OverrideShanghai *uint64 `toml:",omitempty"`
}
// CreateConsensusEngine creates a consensus engine for the given chain configuration.
-func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine {
+func CreateConsensusEngine(stack *node.Node, ethashConfig *ethash.Config, cliqueConfig *params.CliqueConfig, notify []string, noverify bool, db ethdb.Database) consensus.Engine {
// If proof-of-authority is requested, set it up
var engine consensus.Engine
- if chainConfig.Clique != nil {
- engine = clique.New(chainConfig.Clique, db)
+ if cliqueConfig != nil {
+ engine = clique.New(cliqueConfig, db)
} else {
- switch config.PowMode {
+ switch ethashConfig.PowMode {
case ethash.ModeFake:
log.Warn("Ethash used in fake mode")
case ethash.ModeTest:
@@ -241,16 +232,16 @@ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, co
log.Warn("Ethash used in shared mode")
}
engine = ethash.New(ethash.Config{
- PowMode: config.PowMode,
- CacheDir: stack.ResolvePath(config.CacheDir),
- CachesInMem: config.CachesInMem,
- CachesOnDisk: config.CachesOnDisk,
- CachesLockMmap: config.CachesLockMmap,
- DatasetDir: config.DatasetDir,
- DatasetsInMem: config.DatasetsInMem,
- DatasetsOnDisk: config.DatasetsOnDisk,
- DatasetsLockMmap: config.DatasetsLockMmap,
- NotifyFull: config.NotifyFull,
+ PowMode: ethashConfig.PowMode,
+ CacheDir: stack.ResolvePath(ethashConfig.CacheDir),
+ CachesInMem: ethashConfig.CachesInMem,
+ CachesOnDisk: ethashConfig.CachesOnDisk,
+ CachesLockMmap: ethashConfig.CachesLockMmap,
+ DatasetDir: ethashConfig.DatasetDir,
+ DatasetsInMem: ethashConfig.DatasetsInMem,
+ DatasetsOnDisk: ethashConfig.DatasetsOnDisk,
+ DatasetsLockMmap: ethashConfig.DatasetsLockMmap,
+ NotifyFull: ethashConfig.NotifyFull,
}, notify, noverify)
engine.(*ethash.Ethash).SetThreads(-1) // Disable CPU mining
}
diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go
index 9c7a04364d..b7255a242e 100644
--- a/eth/ethconfig/gen_config.go
+++ b/eth/ethconfig/gen_config.go
@@ -3,12 +3,12 @@
package ethconfig
import (
- "math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/miner"
@@ -18,50 +18,49 @@ import (
// MarshalTOML marshals as TOML.
func (c Config) MarshalTOML() (interface{}, error) {
type Config struct {
- Genesis *core.Genesis `toml:",omitempty"`
- NetworkId uint64
- SyncMode downloader.SyncMode
- EthDiscoveryURLs []string
- SnapDiscoveryURLs []string
- NoPruning bool
- NoPrefetch bool
- TxLookupLimit uint64 `toml:",omitempty"`
- RequiredBlocks map[uint64]common.Hash `toml:"-"`
- LightServ int `toml:",omitempty"`
- LightIngress int `toml:",omitempty"`
- LightEgress int `toml:",omitempty"`
- LightPeers int `toml:",omitempty"`
- LightNoPrune bool `toml:",omitempty"`
- LightNoSyncServe bool `toml:",omitempty"`
- SyncFromCheckpoint bool `toml:",omitempty"`
- UltraLightServers []string `toml:",omitempty"`
- UltraLightFraction int `toml:",omitempty"`
- UltraLightOnlyAnnounce bool `toml:",omitempty"`
- SkipBcVersionCheck bool `toml:"-"`
- DatabaseHandles int `toml:"-"`
- DatabaseCache int
- DatabaseFreezer string
- TrieCleanCache int
- TrieCleanCacheJournal string `toml:",omitempty"`
- TrieCleanCacheRejournal time.Duration `toml:",omitempty"`
- TrieDirtyCache int
- TrieTimeout time.Duration
- SnapshotCache int
- Preimages bool
- FilterLogCacheSize int
- Miner miner.Config
- Ethash ethash.Config
- TxPool core.TxPoolConfig
- GPO gasprice.Config
- EnablePreimageRecording bool
- DocRoot string `toml:"-"`
- RPCGasCap uint64
- RPCEVMTimeout time.Duration
- RPCTxFeeCap float64
- Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
- CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
- OverrideTerminalTotalDifficulty *big.Int `toml:",omitempty"`
- OverrideTerminalTotalDifficultyPassed *bool `toml:",omitempty"`
+ Genesis *core.Genesis `toml:",omitempty"`
+ NetworkId uint64
+ SyncMode downloader.SyncMode
+ EthDiscoveryURLs []string
+ SnapDiscoveryURLs []string
+ NoPruning bool
+ NoPrefetch bool
+ TxLookupLimit uint64 `toml:",omitempty"`
+ RequiredBlocks map[uint64]common.Hash `toml:"-"`
+ LightServ int `toml:",omitempty"`
+ LightIngress int `toml:",omitempty"`
+ LightEgress int `toml:",omitempty"`
+ LightPeers int `toml:",omitempty"`
+ LightNoPrune bool `toml:",omitempty"`
+ LightNoSyncServe bool `toml:",omitempty"`
+ SyncFromCheckpoint bool `toml:",omitempty"`
+ UltraLightServers []string `toml:",omitempty"`
+ UltraLightFraction int `toml:",omitempty"`
+ UltraLightOnlyAnnounce bool `toml:",omitempty"`
+ SkipBcVersionCheck bool `toml:"-"`
+ DatabaseHandles int `toml:"-"`
+ DatabaseCache int
+ DatabaseFreezer string
+ TrieCleanCache int
+ TrieCleanCacheJournal string `toml:",omitempty"`
+ TrieCleanCacheRejournal time.Duration `toml:",omitempty"`
+ TrieDirtyCache int
+ TrieTimeout time.Duration
+ SnapshotCache int
+ Preimages bool
+ FilterLogCacheSize int
+ Miner miner.Config
+ Ethash ethash.Config
+ TxPool txpool.Config
+ GPO gasprice.Config
+ EnablePreimageRecording bool
+ DocRoot string `toml:"-"`
+ RPCGasCap uint64
+ RPCEVMTimeout time.Duration
+ RPCTxFeeCap float64
+ Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
+ CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
+ OverrideShanghai *uint64 `toml:",omitempty"`
}
var enc Config
enc.Genesis = c.Genesis
@@ -106,58 +105,56 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.RPCTxFeeCap = c.RPCTxFeeCap
enc.Checkpoint = c.Checkpoint
enc.CheckpointOracle = c.CheckpointOracle
- enc.OverrideTerminalTotalDifficulty = c.OverrideTerminalTotalDifficulty
- enc.OverrideTerminalTotalDifficultyPassed = c.OverrideTerminalTotalDifficultyPassed
+ enc.OverrideShanghai = c.OverrideShanghai
return &enc, nil
}
// UnmarshalTOML unmarshals from TOML.
func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
type Config struct {
- Genesis *core.Genesis `toml:",omitempty"`
- NetworkId *uint64
- SyncMode *downloader.SyncMode
- EthDiscoveryURLs []string
- SnapDiscoveryURLs []string
- NoPruning *bool
- NoPrefetch *bool
- TxLookupLimit *uint64 `toml:",omitempty"`
- RequiredBlocks map[uint64]common.Hash `toml:"-"`
- LightServ *int `toml:",omitempty"`
- LightIngress *int `toml:",omitempty"`
- LightEgress *int `toml:",omitempty"`
- LightPeers *int `toml:",omitempty"`
- LightNoPrune *bool `toml:",omitempty"`
- LightNoSyncServe *bool `toml:",omitempty"`
- SyncFromCheckpoint *bool `toml:",omitempty"`
- UltraLightServers []string `toml:",omitempty"`
- UltraLightFraction *int `toml:",omitempty"`
- UltraLightOnlyAnnounce *bool `toml:",omitempty"`
- SkipBcVersionCheck *bool `toml:"-"`
- DatabaseHandles *int `toml:"-"`
- DatabaseCache *int
- DatabaseFreezer *string
- TrieCleanCache *int
- TrieCleanCacheJournal *string `toml:",omitempty"`
- TrieCleanCacheRejournal *time.Duration `toml:",omitempty"`
- TrieDirtyCache *int
- TrieTimeout *time.Duration
- SnapshotCache *int
- Preimages *bool
- FilterLogCacheSize *int
- Miner *miner.Config
- Ethash *ethash.Config
- TxPool *core.TxPoolConfig
- GPO *gasprice.Config
- EnablePreimageRecording *bool
- DocRoot *string `toml:"-"`
- RPCGasCap *uint64
- RPCEVMTimeout *time.Duration
- RPCTxFeeCap *float64
- Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
- CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
- OverrideTerminalTotalDifficulty *big.Int `toml:",omitempty"`
- OverrideTerminalTotalDifficultyPassed *bool `toml:",omitempty"`
+ Genesis *core.Genesis `toml:",omitempty"`
+ NetworkId *uint64
+ SyncMode *downloader.SyncMode
+ EthDiscoveryURLs []string
+ SnapDiscoveryURLs []string
+ NoPruning *bool
+ NoPrefetch *bool
+ TxLookupLimit *uint64 `toml:",omitempty"`
+ RequiredBlocks map[uint64]common.Hash `toml:"-"`
+ LightServ *int `toml:",omitempty"`
+ LightIngress *int `toml:",omitempty"`
+ LightEgress *int `toml:",omitempty"`
+ LightPeers *int `toml:",omitempty"`
+ LightNoPrune *bool `toml:",omitempty"`
+ LightNoSyncServe *bool `toml:",omitempty"`
+ SyncFromCheckpoint *bool `toml:",omitempty"`
+ UltraLightServers []string `toml:",omitempty"`
+ UltraLightFraction *int `toml:",omitempty"`
+ UltraLightOnlyAnnounce *bool `toml:",omitempty"`
+ SkipBcVersionCheck *bool `toml:"-"`
+ DatabaseHandles *int `toml:"-"`
+ DatabaseCache *int
+ DatabaseFreezer *string
+ TrieCleanCache *int
+ TrieCleanCacheJournal *string `toml:",omitempty"`
+ TrieCleanCacheRejournal *time.Duration `toml:",omitempty"`
+ TrieDirtyCache *int
+ TrieTimeout *time.Duration
+ SnapshotCache *int
+ Preimages *bool
+ FilterLogCacheSize *int
+ Miner *miner.Config
+ Ethash *ethash.Config
+ TxPool *txpool.Config
+ GPO *gasprice.Config
+ EnablePreimageRecording *bool
+ DocRoot *string `toml:"-"`
+ RPCGasCap *uint64
+ RPCEVMTimeout *time.Duration
+ RPCTxFeeCap *float64
+ Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
+ CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
+ OverrideShanghai *uint64 `toml:",omitempty"`
}
var dec Config
if err := unmarshal(&dec); err != nil {
@@ -289,11 +286,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.CheckpointOracle != nil {
c.CheckpointOracle = dec.CheckpointOracle
}
- if dec.OverrideTerminalTotalDifficulty != nil {
- c.OverrideTerminalTotalDifficulty = dec.OverrideTerminalTotalDifficulty
- }
- if dec.OverrideTerminalTotalDifficultyPassed != nil {
- c.OverrideTerminalTotalDifficultyPassed = dec.OverrideTerminalTotalDifficultyPassed
+ if dec.OverrideShanghai != nil {
+ c.OverrideShanghai = dec.OverrideShanghai
}
return nil
}
diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go
index bd1a34c83c..35608031d9 100644
--- a/eth/fetcher/block_fetcher.go
+++ b/eth/fetcher/block_fetcher.go
@@ -175,9 +175,9 @@ type BlockFetcher struct {
completing map[common.Hash]*blockAnnounce // Blocks with headers, currently body-completing
// Block cache
- queue *prque.Prque // Queue containing the import operations (block number sorted)
- queues map[string]int // Per peer block counts to prevent memory exhaustion
- queued map[common.Hash]*blockOrHeaderInject // Set of already queued blocks (to dedup imports)
+ queue *prque.Prque[int64, *blockOrHeaderInject] // Queue containing the import operations (block number sorted)
+ queues map[string]int // Per peer block counts to prevent memory exhaustion
+ queued map[common.Hash]*blockOrHeaderInject // Set of already queued blocks (to dedup imports)
// Callbacks
getHeader HeaderRetrievalFn // Retrieves a header from the local chain
@@ -212,7 +212,7 @@ func NewBlockFetcher(light bool, getHeader HeaderRetrievalFn, getBlock blockRetr
fetching: make(map[common.Hash]*blockAnnounce),
fetched: make(map[common.Hash][]*blockAnnounce),
completing: make(map[common.Hash]*blockAnnounce),
- queue: prque.New(nil),
+ queue: prque.New[int64, *blockOrHeaderInject](nil),
queues: make(map[string]int),
queued: make(map[common.Hash]*blockOrHeaderInject),
getHeader: getHeader,
@@ -351,7 +351,7 @@ func (f *BlockFetcher) loop() {
// Import any queued blocks that could potentially fit
height := f.chainHeight()
for !f.queue.Empty() {
- op := f.queue.PopItem().(*blockOrHeaderInject)
+ op := f.queue.PopItem()
hash := op.hash()
if f.queueChangeHook != nil {
f.queueChangeHook(hash, false)
@@ -540,8 +540,8 @@ func (f *BlockFetcher) loop() {
select {
case res := <-resCh:
res.Done <- nil
-
- txs, uncles := res.Res.(*eth.BlockBodiesPacket).Unpack()
+ // Ignoring withdrawals here, since the block fetcher is not used post-merge.
+ txs, uncles, _ := res.Res.(*eth.BlockBodiesPacket).Unpack()
f.FilterBodies(peer, txs, uncles, time.Now())
case <-timeout.C:
@@ -599,7 +599,7 @@ func (f *BlockFetcher) loop() {
announce.time = task.time
// If the block is empty (header only), short circuit into the final import queue
- if header.TxHash == types.EmptyRootHash && header.UncleHash == types.EmptyUncleHash {
+ if header.TxHash == types.EmptyTxsHash && header.UncleHash == types.EmptyUncleHash {
log.Trace("Block empty, skipping body retrieval", "peer", announce.origin, "number", header.Number, "hash", header.Hash())
block := types.NewBlockWithHeader(header)
diff --git a/eth/fetcher/block_fetcher_test.go b/eth/fetcher/block_fetcher_test.go
index bf7946952e..9e5693c02e 100644
--- a/eth/fetcher/block_fetcher_test.go
+++ b/eth/fetcher/block_fetcher_test.go
@@ -39,7 +39,8 @@ var (
testdb = rawdb.NewMemoryDatabase()
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testAddress = crypto.PubkeyToAddress(testKey.PublicKey)
- gspec = core.Genesis{
+ gspec = &core.Genesis{
+ Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}},
BaseFee: big.NewInt(params.InitialBaseFee),
}
@@ -52,7 +53,7 @@ var (
// contains a transaction and every 5th an uncle to allow testing correct block
// reassembly.
func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) {
- blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) {
+ blocks, _ := core.GenerateChain(gspec.Config, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) {
block.SetCoinbase(common.Address{seed})
// If the block number is multiple of 3, send a bonus transaction to the miner
diff --git a/eth/fetcher/tx_fetcher.go b/eth/fetcher/tx_fetcher.go
index 035e0c2ec7..39727e0079 100644
--- a/eth/fetcher/tx_fetcher.go
+++ b/eth/fetcher/tx_fetcher.go
@@ -24,10 +24,10 @@ import (
"sort"
"time"
- mapset "github.com/deckarep/golang-set"
+ mapset "github.com/deckarep/golang-set/v2"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
@@ -148,13 +148,13 @@ type TxFetcher struct {
drop chan *txDrop
quit chan struct{}
- underpriced mapset.Set // Transactions discarded as too cheap (don't re-fetch)
+ underpriced mapset.Set[common.Hash] // Transactions discarded as too cheap (don't re-fetch)
// Stage 1: Waiting lists for newly discovered transactions that might be
// broadcast without needing explicit request/reply round trips.
waitlist map[common.Hash]map[string]struct{} // Transactions waiting for an potential broadcast
waittime map[common.Hash]mclock.AbsTime // Timestamps when transactions were added to the waitlist
- waitslots map[string]map[common.Hash]struct{} // Waiting announcement sgroupped by peer (DoS protection)
+ waitslots map[string]map[common.Hash]struct{} // Waiting announcements grouped by peer (DoS protection)
// Stage 2: Queue of transactions that waiting to be allocated to some peer
// to be retrieved directly.
@@ -202,7 +202,7 @@ func NewTxFetcherForTests(
fetching: make(map[common.Hash]string),
requests: make(map[string]*txRequest),
alternates: make(map[common.Hash]map[string]struct{}),
- underpriced: mapset.NewSet(),
+ underpriced: mapset.NewSet[common.Hash](),
hasTx: hasTx,
addTxs: addTxs,
fetchTxs: fetchTxs,
@@ -218,7 +218,7 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error {
txAnnounceInMeter.Mark(int64(len(hashes)))
// Skip any transaction announcements that we already know of, or that we've
- // previously marked as cheap and discarded. This check is of course racey,
+ // previously marked as cheap and discarded. This check is of course racy,
// because multiple concurrent notifies will still manage to pass it, but it's
// still valuable to check here because it runs concurrent to the internal
// loop, so anything caught here is time saved internally.
@@ -262,54 +262,72 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error {
// direct request replies. The differentiation is important so the fetcher can
// re-schedule missing transactions as soon as possible.
func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool) error {
- // Keep track of all the propagated transactions
- if direct {
- txReplyInMeter.Mark(int64(len(txs)))
- } else {
- txBroadcastInMeter.Mark(int64(len(txs)))
+ var (
+ inMeter = txReplyInMeter
+ knownMeter = txReplyKnownMeter
+ underpricedMeter = txReplyUnderpricedMeter
+ otherRejectMeter = txReplyOtherRejectMeter
+ )
+ if !direct {
+ inMeter = txBroadcastInMeter
+ knownMeter = txBroadcastKnownMeter
+ underpricedMeter = txBroadcastUnderpricedMeter
+ otherRejectMeter = txBroadcastOtherRejectMeter
}
+ // Keep track of all the propagated transactions
+ inMeter.Mark(int64(len(txs)))
+
// Push all the transactions into the pool, tracking underpriced ones to avoid
// re-requesting them and dropping the peer in case of malicious transfers.
var (
- added = make([]common.Hash, 0, len(txs))
- duplicate int64
- underpriced int64
- otherreject int64
+ added = make([]common.Hash, 0, len(txs))
)
- errs := f.addTxs(txs)
- for i, err := range errs {
- // Track the transaction hash if the price is too low for us.
- // Avoid re-request this transaction when we receive another
- // announcement.
- if errors.Is(err, core.ErrUnderpriced) || errors.Is(err, core.ErrReplaceUnderpriced) {
- for f.underpriced.Cardinality() >= maxTxUnderpricedSetSize {
- f.underpriced.Pop()
- }
- f.underpriced.Add(txs[i].Hash())
+ // proceed in batches
+ for i := 0; i < len(txs); i += 128 {
+ end := i + 128
+ if end > len(txs) {
+ end = len(txs)
}
- // Track a few interesting failure types
- switch {
- case err == nil: // Noop, but need to handle to not count these
+ var (
+ duplicate int64
+ underpriced int64
+ otherreject int64
+ )
+ batch := txs[i:end]
+ for j, err := range f.addTxs(batch) {
+ // Track the transaction hash if the price is too low for us.
+ // Avoid re-request this transaction when we receive another
+ // announcement.
+ if errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) {
+ for f.underpriced.Cardinality() >= maxTxUnderpricedSetSize {
+ f.underpriced.Pop()
+ }
+ f.underpriced.Add(batch[j].Hash())
+ }
+ // Track a few interesting failure types
+ switch {
+ case err == nil: // Noop, but need to handle to not count these
- case errors.Is(err, core.ErrAlreadyKnown):
- duplicate++
+ case errors.Is(err, txpool.ErrAlreadyKnown):
+ duplicate++
- case errors.Is(err, core.ErrUnderpriced) || errors.Is(err, core.ErrReplaceUnderpriced):
- underpriced++
+ case errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced):
+ underpriced++
- default:
- otherreject++
+ default:
+ otherreject++
+ }
+ added = append(added, batch[j].Hash())
+ }
+ knownMeter.Mark(duplicate)
+ underpricedMeter.Mark(underpriced)
+ otherRejectMeter.Mark(otherreject)
+
+ // If 'other reject' is >25% of the deliveries in any batch, sleep a bit.
+ if otherreject > 128/4 {
+ time.Sleep(200 * time.Millisecond)
+ log.Warn("Peer delivering stale transactions", "peer", peer, "rejected", otherreject)
}
- added = append(added, txs[i].Hash())
- }
- if direct {
- txReplyKnownMeter.Mark(duplicate)
- txReplyUnderpricedMeter.Mark(underpriced)
- txReplyOtherRejectMeter.Mark(otherreject)
- } else {
- txBroadcastKnownMeter.Mark(duplicate)
- txBroadcastUnderpricedMeter.Mark(underpriced)
- txBroadcastOtherRejectMeter.Mark(otherreject)
}
select {
case f.cleanup <- &txDelivery{origin: peer, hashes: added, direct: direct}:
diff --git a/eth/fetcher/tx_fetcher_test.go b/eth/fetcher/tx_fetcher_test.go
index 4c06712f77..1715def99c 100644
--- a/eth/fetcher/tx_fetcher_test.go
+++ b/eth/fetcher/tx_fetcher_test.go
@@ -25,7 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
)
@@ -869,9 +869,9 @@ func TestTransactionFetcherUnderpricedDedup(t *testing.T) {
errs := make([]error, len(txs))
for i := 0; i < len(errs); i++ {
if i%2 == 0 {
- errs[i] = core.ErrUnderpriced
+ errs[i] = txpool.ErrUnderpriced
} else {
- errs[i] = core.ErrReplaceUnderpriced
+ errs[i] = txpool.ErrReplaceUnderpriced
}
}
return errs
@@ -941,7 +941,7 @@ func TestTransactionFetcherUnderpricedDoSProtection(t *testing.T) {
func(txs []*types.Transaction) []error {
errs := make([]error, len(txs))
for i := 0; i < len(errs); i++ {
- errs[i] = core.ErrUnderpriced
+ errs[i] = txpool.ErrUnderpriced
}
return errs
},
diff --git a/eth/filters/api.go b/eth/filters/api.go
index 43e63d5ba9..f9ae70eba7 100644
--- a/eth/filters/api.go
+++ b/eth/filters/api.go
@@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -38,13 +39,15 @@ type filter struct {
typ Type
deadline *time.Timer // filter is inactive when deadline triggers
hashes []common.Hash
+ fullTx bool
+ txs []*types.Transaction
crit FilterCriteria
logs []*types.Log
s *Subscription // associated subscription in event system
}
// FilterAPI offers support to create and manage filters. This will allow external clients to retrieve various
-// information related to the Ethereum protocol such als blocks, transactions and logs.
+// information related to the Ethereum protocol such as blocks, transactions and logs.
type FilterAPI struct {
sys *FilterSystem
events *EventSystem
@@ -96,28 +99,28 @@ func (api *FilterAPI) timeoutLoop(timeout time.Duration) {
}
}
-// NewPendingTransactionFilter creates a filter that fetches pending transaction hashes
+// NewPendingTransactionFilter creates a filter that fetches pending transactions
// as transactions enter the pending state.
//
// It is part of the filter package because this filter can be used through the
// `eth_getFilterChanges` polling method that is also used for log filters.
-func (api *FilterAPI) NewPendingTransactionFilter() rpc.ID {
+func (api *FilterAPI) NewPendingTransactionFilter(fullTx *bool) rpc.ID {
var (
- pendingTxs = make(chan []common.Hash)
+ pendingTxs = make(chan []*types.Transaction)
pendingTxSub = api.events.SubscribePendingTxs(pendingTxs)
)
api.filtersMu.Lock()
- api.filters[pendingTxSub.ID] = &filter{typ: PendingTransactionsSubscription, deadline: time.NewTimer(api.timeout), hashes: make([]common.Hash, 0), s: pendingTxSub}
+ api.filters[pendingTxSub.ID] = &filter{typ: PendingTransactionsSubscription, fullTx: fullTx != nil && *fullTx, deadline: time.NewTimer(api.timeout), txs: make([]*types.Transaction, 0), s: pendingTxSub}
api.filtersMu.Unlock()
go func() {
for {
select {
- case ph := <-pendingTxs:
+ case pTx := <-pendingTxs:
api.filtersMu.Lock()
if f, found := api.filters[pendingTxSub.ID]; found {
- f.hashes = append(f.hashes, ph...)
+ f.txs = append(f.txs, pTx...)
}
api.filtersMu.Unlock()
case <-pendingTxSub.Err():
@@ -132,9 +135,10 @@ func (api *FilterAPI) NewPendingTransactionFilter() rpc.ID {
return pendingTxSub.ID
}
-// NewPendingTransactions creates a subscription that is triggered each time a transaction
-// enters the transaction pool and was signed from one of the transactions this nodes manages.
-func (api *FilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Subscription, error) {
+// NewPendingTransactions creates a subscription that is triggered each time a
+// transaction enters the transaction pool. If fullTx is true the full tx is
+// sent to the client, otherwise the hash is sent.
+func (api *FilterAPI) NewPendingTransactions(ctx context.Context, fullTx *bool) (*rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
@@ -143,16 +147,23 @@ func (api *FilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Subscrip
rpcSub := notifier.CreateSubscription()
go func() {
- txHashes := make(chan []common.Hash, 128)
- pendingTxSub := api.events.SubscribePendingTxs(txHashes)
+ txs := make(chan []*types.Transaction, 128)
+ pendingTxSub := api.events.SubscribePendingTxs(txs)
+ chainConfig := api.sys.backend.ChainConfig()
for {
select {
- case hashes := <-txHashes:
+ case txs := <-txs:
// To keep the original behaviour, send a single tx hash in one notification.
// TODO(rjl493456442) Send a batch of tx hashes in one notification
- for _, h := range hashes {
- notifier.Notify(rpcSub.ID, h)
+ latest := api.sys.backend.CurrentHeader()
+ for _, tx := range txs {
+ if fullTx != nil && *fullTx {
+ rpcTx := ethapi.NewRPCPendingTransaction(tx, latest, chainConfig)
+ notifier.Notify(rpcSub.ID, rpcTx)
+ } else {
+ notifier.Notify(rpcSub.ID, tx.Hash())
+ }
}
case <-rpcSub.Err():
pendingTxSub.Unsubscribe()
@@ -402,6 +413,9 @@ func (api *FilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) {
api.filtersMu.Lock()
defer api.filtersMu.Unlock()
+ chainConfig := api.sys.backend.ChainConfig()
+ latest := api.sys.backend.CurrentHeader()
+
if f, found := api.filters[id]; found {
if !f.deadline.Stop() {
// timer expired but filter is not yet removed in timeout loop
@@ -411,10 +425,26 @@ func (api *FilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) {
f.deadline.Reset(api.timeout)
switch f.typ {
- case PendingTransactionsSubscription, BlocksSubscription:
+ case BlocksSubscription:
hashes := f.hashes
f.hashes = nil
return returnHashes(hashes), nil
+ case PendingTransactionsSubscription:
+ if f.fullTx {
+ txs := make([]*ethapi.RPCTransaction, 0, len(f.txs))
+ for _, tx := range f.txs {
+ txs = append(txs, ethapi.NewRPCPendingTransaction(tx, latest, chainConfig))
+ }
+ f.txs = nil
+ return txs, nil
+ } else {
+ hashes := make([]common.Hash, 0, len(f.txs))
+ for _, tx := range f.txs {
+ hashes = append(hashes, tx.Hash())
+ }
+ f.txs = nil
+ return hashes, nil
+ }
case LogsSubscription, MinedAndPendingLogsSubscription:
logs := f.logs
f.logs = nil
diff --git a/eth/filters/filter.go b/eth/filters/filter.go
index 8d80647fc2..8ba482817e 100644
--- a/eth/filters/filter.go
+++ b/eth/filters/filter.go
@@ -104,7 +104,7 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
if header == nil {
return nil, errors.New("unknown block")
}
- return f.blockLogs(ctx, header, false)
+ return f.blockLogs(ctx, header)
}
// Short-cut if all we care about is pending logs
if f.begin == rpc.PendingBlockNumber.Int64() {
@@ -119,20 +119,44 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
return nil, nil
}
var (
- head = header.Number.Uint64()
- end = uint64(f.end)
+ err error
+ head = header.Number.Int64()
pending = f.end == rpc.PendingBlockNumber.Int64()
)
- if f.begin == rpc.LatestBlockNumber.Int64() {
- f.begin = int64(head)
+ resolveSpecial := func(number int64) (int64, error) {
+ var hdr *types.Header
+ switch number {
+ case rpc.LatestBlockNumber.Int64():
+ return head, nil
+ case rpc.PendingBlockNumber.Int64():
+ // we should return head here since we've already captured
+ // that we need to get the pending logs in the pending boolean above
+ return head, nil
+ case rpc.FinalizedBlockNumber.Int64():
+ hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
+ if hdr == nil {
+ return 0, errors.New("finalized header not found")
+ }
+ case rpc.SafeBlockNumber.Int64():
+ hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.SafeBlockNumber)
+ if hdr == nil {
+ return 0, errors.New("safe header not found")
+ }
+ default:
+ return number, nil
+ }
+ return hdr.Number.Int64(), nil
}
- if f.end == rpc.LatestBlockNumber.Int64() || f.end == rpc.PendingBlockNumber.Int64() {
- end = head
+ if f.begin, err = resolveSpecial(f.begin); err != nil {
+ return nil, err
+ }
+ if f.end, err = resolveSpecial(f.end); err != nil {
+ return nil, err
}
// Gather all indexed logs, and finish with non indexed ones
var (
logs []*types.Log
- err error
+ end = uint64(f.end)
size, sections = f.sys.backend.BloomStatus()
)
if indexed := sections * size; indexed > uint64(f.begin) {
@@ -192,7 +216,7 @@ func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, err
if header == nil || err != nil {
return logs, err
}
- found, err := f.blockLogs(ctx, header, true)
+ found, err := f.checkMatches(ctx, header)
if err != nil {
return logs, err
}
@@ -210,11 +234,14 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, e
var logs []*types.Log
for ; f.begin <= int64(end); f.begin++ {
+ if f.begin%10 == 0 && ctx.Err() != nil {
+ return logs, ctx.Err()
+ }
header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin))
if header == nil || err != nil {
return logs, err
}
- found, err := f.blockLogs(ctx, header, false)
+ found, err := f.blockLogs(ctx, header)
if err != nil {
return logs, err
}
@@ -224,15 +251,8 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, e
}
// blockLogs returns the logs matching the filter criteria within a single block.
-func (f *Filter) blockLogs(ctx context.Context, header *types.Header, skipBloom bool) ([]*types.Log, error) {
- // Fast track: no filtering criteria
- if len(f.addresses) == 0 && len(f.topics) == 0 {
- list, err := f.sys.cachedGetLogs(ctx, header.Hash(), header.Number.Uint64())
- if err != nil {
- return nil, err
- }
- return flatten(list), nil
- } else if skipBloom || bloomFilter(header.Bloom, f.addresses, f.topics) {
+func (f *Filter) blockLogs(ctx context.Context, header *types.Header) ([]*types.Log, error) {
+ if bloomFilter(header.Bloom, f.addresses, f.topics) {
return f.checkMatches(ctx, header)
}
return nil, nil
@@ -240,30 +260,37 @@ func (f *Filter) blockLogs(ctx context.Context, header *types.Header, skipBloom
// checkMatches checks if the receipts belonging to the given header contain any log events that
// match the filter criteria. This function is called when the bloom filter signals a potential match.
+// skipFilter signals all logs of the given block are requested.
func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) {
- logsList, err := f.sys.cachedGetLogs(ctx, header.Hash(), header.Number.Uint64())
+ hash := header.Hash()
+ // Logs in cache are partially filled with context data
+ // such as tx index, block hash, etc.
+ // Notably tx hash is NOT filled in because it needs
+ // access to block body data.
+ cached, err := f.sys.cachedLogElem(ctx, hash, header.Number.Uint64())
if err != nil {
return nil, err
}
-
- unfiltered := flatten(logsList)
- logs := filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
- if len(logs) > 0 {
- // We have matching logs, check if we need to resolve full logs via the light client
- if logs[0].TxHash == (common.Hash{}) {
- receipts, err := f.sys.backend.GetReceipts(ctx, header.Hash())
- if err != nil {
- return nil, err
- }
- unfiltered = unfiltered[:0]
- for _, receipt := range receipts {
- unfiltered = append(unfiltered, receipt.Logs...)
- }
- logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
- }
+ logs := filterLogs(cached.logs, nil, nil, f.addresses, f.topics)
+ if len(logs) == 0 {
+ return nil, nil
+ }
+ // Most backends will deliver un-derived logs, but check nevertheless.
+ if len(logs) > 0 && logs[0].TxHash != (common.Hash{}) {
return logs, nil
}
- return nil, nil
+
+ body, err := f.sys.cachedGetBody(ctx, cached, hash, header.Number.Uint64())
+ if err != nil {
+ return nil, err
+ }
+ for i, log := range logs {
+ // Copy log not to modify cache elements
+ logcopy := *log
+ logcopy.TxHash = body.Transactions[logcopy.TxIndex].Hash()
+ logs[i] = &logcopy
+ }
+ return logs, nil
}
// pendingLogs returns the logs matching the filter criteria within the pending block.
@@ -353,11 +380,3 @@ func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]commo
}
return true
}
-
-func flatten(list [][]*types.Log) []*types.Log {
- var flat []*types.Log
- for _, logs := range list {
- flat = append(flat, logs...)
- }
- return flat
-}
diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go
index 79a9b089f4..9fc20f335b 100644
--- a/eth/filters/filter_system.go
+++ b/eth/filters/filter_system.go
@@ -22,10 +22,12 @@ import (
"context"
"fmt"
"sync"
+ "sync/atomic"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -33,8 +35,8 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
- lru "github.com/hashicorp/golang-lru"
)
// Config represents the configuration of the filter system.
@@ -57,10 +59,13 @@ type Backend interface {
ChainDb() ethdb.Database
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error)
+ GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error)
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error)
PendingBlockAndReceipts() (*types.Block, types.Receipts)
+ CurrentHeader() *types.Header
+ ChainConfig() *params.ChainConfig
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
@@ -74,30 +79,30 @@ type Backend interface {
// FilterSystem holds resources shared by all filters.
type FilterSystem struct {
backend Backend
- logsCache *lru.Cache
+ logsCache *lru.Cache[common.Hash, *logCacheElem]
cfg *Config
}
// NewFilterSystem creates a filter system.
func NewFilterSystem(backend Backend, config Config) *FilterSystem {
config = config.withDefaults()
-
- cache, err := lru.New(config.LogCacheSize)
- if err != nil {
- panic(err)
- }
return &FilterSystem{
backend: backend,
- logsCache: cache,
+ logsCache: lru.NewCache[common.Hash, *logCacheElem](config.LogCacheSize),
cfg: &config,
}
}
-// cachedGetLogs loads block logs from the backend and caches the result.
-func (sys *FilterSystem) cachedGetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
+type logCacheElem struct {
+ logs []*types.Log
+ body atomic.Value
+}
+
+// cachedLogElem loads block logs from the backend and caches the result.
+func (sys *FilterSystem) cachedLogElem(ctx context.Context, blockHash common.Hash, number uint64) (*logCacheElem, error) {
cached, ok := sys.logsCache.Get(blockHash)
if ok {
- return cached.([][]*types.Log), nil
+ return cached, nil
}
logs, err := sys.backend.GetLogs(ctx, blockHash, number)
@@ -107,8 +112,35 @@ func (sys *FilterSystem) cachedGetLogs(ctx context.Context, blockHash common.Has
if logs == nil {
return nil, fmt.Errorf("failed to get logs for block #%d (0x%s)", number, blockHash.TerminalString())
}
- sys.logsCache.Add(blockHash, logs)
- return logs, nil
+ // Database logs are un-derived.
+ // Fill in whatever we can (txHash is inaccessible at this point).
+ flattened := make([]*types.Log, 0)
+ var logIdx uint
+ for i, txLogs := range logs {
+ for _, log := range txLogs {
+ log.BlockHash = blockHash
+ log.BlockNumber = number
+ log.TxIndex = uint(i)
+ log.Index = logIdx
+ logIdx++
+ flattened = append(flattened, log)
+ }
+ }
+ elem := &logCacheElem{logs: flattened}
+ sys.logsCache.Add(blockHash, elem)
+ return elem, nil
+}
+
+func (sys *FilterSystem) cachedGetBody(ctx context.Context, elem *logCacheElem, hash common.Hash, number uint64) (*types.Body, error) {
+ if body := elem.body.Load(); body != nil {
+ return body.(*types.Body), nil
+ }
+ body, err := sys.backend.GetBody(ctx, hash, rpc.BlockNumber(number))
+ if err != nil {
+ return nil, err
+ }
+ elem.body.Store(body)
+ return body, nil
}
// Type determines the kind of filter and is used to put the filter in to
@@ -124,12 +156,12 @@ const (
PendingLogsSubscription
// MinedAndPendingLogsSubscription queries for logs in mined and pending blocks.
MinedAndPendingLogsSubscription
- // PendingTransactionsSubscription queries tx hashes for pending
- // transactions entering the pending state
+ // PendingTransactionsSubscription queries for pending transactions entering
+ // the pending state
PendingTransactionsSubscription
// BlocksSubscription queries hashes for blocks that are imported
BlocksSubscription
- // LastSubscription keeps track of the last index
+ // LastIndexSubscription keeps track of the last index
LastIndexSubscription
)
@@ -151,7 +183,7 @@ type subscription struct {
created time.Time
logsCrit ethereum.FilterQuery
logs chan []*types.Log
- hashes chan []common.Hash
+ txs chan []*types.Transaction
headers chan *types.Header
installed chan struct{} // closed when the filter is installed
err chan error // closed when the filter is uninstalled
@@ -244,7 +276,7 @@ func (sub *Subscription) Unsubscribe() {
case sub.es.uninstall <- sub.f:
break uninstallLoop
case <-sub.f.logs:
- case <-sub.f.hashes:
+ case <-sub.f.txs:
case <-sub.f.headers:
}
}
@@ -311,7 +343,7 @@ func (es *EventSystem) subscribeMinedPendingLogs(crit ethereum.FilterQuery, logs
logsCrit: crit,
created: time.Now(),
logs: logs,
- hashes: make(chan []common.Hash),
+ txs: make(chan []*types.Transaction),
headers: make(chan *types.Header),
installed: make(chan struct{}),
err: make(chan error),
@@ -328,7 +360,7 @@ func (es *EventSystem) subscribeLogs(crit ethereum.FilterQuery, logs chan []*typ
logsCrit: crit,
created: time.Now(),
logs: logs,
- hashes: make(chan []common.Hash),
+ txs: make(chan []*types.Transaction),
headers: make(chan *types.Header),
installed: make(chan struct{}),
err: make(chan error),
@@ -345,7 +377,7 @@ func (es *EventSystem) subscribePendingLogs(crit ethereum.FilterQuery, logs chan
logsCrit: crit,
created: time.Now(),
logs: logs,
- hashes: make(chan []common.Hash),
+ txs: make(chan []*types.Transaction),
headers: make(chan *types.Header),
installed: make(chan struct{}),
err: make(chan error),
@@ -361,7 +393,7 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti
typ: BlocksSubscription,
created: time.Now(),
logs: make(chan []*types.Log),
- hashes: make(chan []common.Hash),
+ txs: make(chan []*types.Transaction),
headers: headers,
installed: make(chan struct{}),
err: make(chan error),
@@ -369,15 +401,15 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti
return es.subscribe(sub)
}
-// SubscribePendingTxs creates a subscription that writes transaction hashes for
+// SubscribePendingTxs creates a subscription that writes transactions for
// transactions that enter the transaction pool.
-func (es *EventSystem) SubscribePendingTxs(hashes chan []common.Hash) *Subscription {
+func (es *EventSystem) SubscribePendingTxs(txs chan []*types.Transaction) *Subscription {
sub := &subscription{
id: rpc.NewID(),
typ: PendingTransactionsSubscription,
created: time.Now(),
logs: make(chan []*types.Log),
- hashes: hashes,
+ txs: txs,
headers: make(chan *types.Header),
installed: make(chan struct{}),
err: make(chan error),
@@ -421,12 +453,8 @@ func (es *EventSystem) handleRemovedLogs(filters filterIndex, ev core.RemovedLog
}
func (es *EventSystem) handleTxsEvent(filters filterIndex, ev core.NewTxsEvent) {
- hashes := make([]common.Hash, 0, len(ev.Txs))
- for _, tx := range ev.Txs {
- hashes = append(hashes, tx.Hash())
- }
for _, f := range filters[PendingTransactionsSubscription] {
- f.hashes <- hashes
+ f.txs <- ev.Txs
}
}
@@ -437,6 +465,12 @@ func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent)
if es.lightMode && len(filters[LogsSubscription]) > 0 {
es.lightFilterNewHead(ev.Block.Header(), func(header *types.Header, remove bool) {
for _, f := range filters[LogsSubscription] {
+ if f.logsCrit.FromBlock != nil && header.Number.Cmp(f.logsCrit.FromBlock) < 0 {
+ continue
+ }
+ if f.logsCrit.ToBlock != nil && header.Number.Cmp(f.logsCrit.ToBlock) > 0 {
+ continue
+ }
if matchedLogs := es.lightFilterLogs(header, f.logsCrit.Addresses, f.logsCrit.Topics, remove); len(matchedLogs) > 0 {
f.logs <- matchedLogs
}
@@ -480,42 +514,39 @@ func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func
// filter logs of a single header in light client mode
func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*types.Log {
- if bloomFilter(header.Bloom, addresses, topics) {
- // Get the logs of the block
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
- defer cancel()
- logsList, err := es.sys.cachedGetLogs(ctx, header.Hash(), header.Number.Uint64())
- if err != nil {
- return nil
- }
- var unfiltered []*types.Log
- for _, logs := range logsList {
- for _, log := range logs {
- logcopy := *log
- logcopy.Removed = remove
- unfiltered = append(unfiltered, &logcopy)
- }
- }
- logs := filterLogs(unfiltered, nil, nil, addresses, topics)
- if len(logs) > 0 && logs[0].TxHash == (common.Hash{}) {
- // We have matching but non-derived logs
- receipts, err := es.backend.GetReceipts(ctx, header.Hash())
- if err != nil {
- return nil
- }
- unfiltered = unfiltered[:0]
- for _, receipt := range receipts {
- for _, log := range receipt.Logs {
- logcopy := *log
- logcopy.Removed = remove
- unfiltered = append(unfiltered, &logcopy)
- }
- }
- logs = filterLogs(unfiltered, nil, nil, addresses, topics)
- }
+ if !bloomFilter(header.Bloom, addresses, topics) {
+ return nil
+ }
+ // Get the logs of the block
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
+ defer cancel()
+ cached, err := es.sys.cachedLogElem(ctx, header.Hash(), header.Number.Uint64())
+ if err != nil {
+ return nil
+ }
+ unfiltered := append([]*types.Log{}, cached.logs...)
+ for i, log := range unfiltered {
+ // Don't modify in-cache elements
+ logcopy := *log
+ logcopy.Removed = remove
+ // Swap copy in-place
+ unfiltered[i] = &logcopy
+ }
+ logs := filterLogs(unfiltered, nil, nil, addresses, topics)
+ // Txhash is already resolved
+ if len(logs) > 0 && logs[0].TxHash != (common.Hash{}) {
return logs
}
- return nil
+ // Resolve txhash
+ body, err := es.sys.cachedGetBody(ctx, cached, header.Hash(), header.Number.Uint64())
+ if err != nil {
+ return nil
+ }
+ for _, log := range logs {
+ // logs are already copied, safe to modify
+ log.TxHash = body.Transactions[log.TxIndex].Hash()
+ }
+ return logs
}
// eventLoop (un)installs filters and processes mux events.
diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go
index 51bda29b42..b70b0158ad 100644
--- a/eth/filters/filter_system_test.go
+++ b/eth/filters/filter_system_test.go
@@ -18,6 +18,7 @@ package filters
import (
"context"
+ "errors"
"fmt"
"math/big"
"math/rand"
@@ -33,8 +34,10 @@ import (
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -49,6 +52,15 @@ type testBackend struct {
chainFeed event.Feed
}
+func (b *testBackend) ChainConfig() *params.ChainConfig {
+ return params.TestChainConfig
+}
+
+func (b *testBackend) CurrentHeader() *types.Header {
+ hdr, _ := b.HeaderByNumber(context.TODO(), rpc.LatestBlockNumber)
+ return hdr
+}
+
func (b *testBackend) ChainDb() ethdb.Database {
return b.db
}
@@ -58,14 +70,24 @@ func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumbe
hash common.Hash
num uint64
)
- if blockNr == rpc.LatestBlockNumber {
+ switch blockNr {
+ case rpc.LatestBlockNumber:
hash = rawdb.ReadHeadBlockHash(b.db)
number := rawdb.ReadHeaderNumber(b.db, hash)
if number == nil {
return nil, nil
}
num = *number
- } else {
+ case rpc.FinalizedBlockNumber:
+ hash = rawdb.ReadFinalizedBlockHash(b.db)
+ number := rawdb.ReadHeaderNumber(b.db, hash)
+ if number == nil {
+ return nil, nil
+ }
+ num = *number
+ case rpc.SafeBlockNumber:
+ return nil, errors.New("safe block not found")
+ default:
num = uint64(blockNr)
hash = rawdb.ReadCanonicalHash(b.db, num)
}
@@ -80,6 +102,13 @@ func (b *testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*type
return rawdb.ReadHeader(b.db, hash, *number), nil
}
+func (b *testBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
+ if body := rawdb.ReadBody(b.db, hash, uint64(number)); body != nil {
+ return body, nil
+ }
+ return nil, errors.New("block body not found")
+}
+
func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil {
return rawdb.ReadReceipts(b.db, hash, *number, params.TestChainConfig), nil
@@ -165,9 +194,12 @@ func TestBlockSubscription(t *testing.T) {
db = rawdb.NewMemoryDatabase()
backend, sys = newTestFilterSystem(t, db, Config{})
api = NewFilterAPI(sys, false)
- genesis = (&core.Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
- chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {})
- chainEvents = []core.ChainEvent{}
+ genesis = &core.Genesis{
+ Config: params.TestChainConfig,
+ BaseFee: big.NewInt(params.InitialBaseFee),
+ }
+ _, chain, _ = core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 10, func(i int, gen *core.BlockGen) {})
+ chainEvents = []core.ChainEvent{}
)
for _, blk := range chain {
@@ -229,7 +261,7 @@ func TestPendingTxFilter(t *testing.T) {
hashes []common.Hash
)
- fid0 := api.NewPendingTransactionFilter()
+ fid0 := api.NewPendingTransactionFilter(nil)
time.Sleep(1 * time.Second)
backend.txFeed.Send(core.NewTxsEvent{Txs: transactions})
@@ -265,6 +297,63 @@ func TestPendingTxFilter(t *testing.T) {
}
}
+// TestPendingTxFilterFullTx tests whether pending tx filters retrieve all pending transactions that are posted to the event mux.
+func TestPendingTxFilterFullTx(t *testing.T) {
+ t.Parallel()
+
+ var (
+ db = rawdb.NewMemoryDatabase()
+ backend, sys = newTestFilterSystem(t, db, Config{})
+ api = NewFilterAPI(sys, false)
+
+ transactions = []*types.Transaction{
+ types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
+ types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
+ types.NewTransaction(2, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
+ types.NewTransaction(3, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
+ types.NewTransaction(4, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
+ }
+
+ txs []*ethapi.RPCTransaction
+ )
+
+ fullTx := true
+ fid0 := api.NewPendingTransactionFilter(&fullTx)
+
+ time.Sleep(1 * time.Second)
+ backend.txFeed.Send(core.NewTxsEvent{Txs: transactions})
+
+ timeout := time.Now().Add(1 * time.Second)
+ for {
+ results, err := api.GetFilterChanges(fid0)
+ if err != nil {
+ t.Fatalf("Unable to retrieve logs: %v", err)
+ }
+
+ tx := results.([]*ethapi.RPCTransaction)
+ txs = append(txs, tx...)
+ if len(txs) >= len(transactions) {
+ break
+ }
+ // check timeout
+ if time.Now().After(timeout) {
+ break
+ }
+
+ time.Sleep(100 * time.Millisecond)
+ }
+
+ if len(txs) != len(transactions) {
+ t.Errorf("invalid number of transactions, want %d transactions(s), got %d", len(transactions), len(txs))
+ return
+ }
+ for i := range txs {
+ if txs[i].Hash != transactions[i].Hash() {
+ t.Errorf("hashes[%d] invalid, want %x, got %x", i, transactions[i].Hash(), txs[i].Hash)
+ }
+ }
+}
+
// TestLogFilterCreation test whether a given filter criteria makes sense.
// If not it must return an error.
func TestLogFilterCreation(t *testing.T) {
@@ -653,6 +742,143 @@ func TestPendingLogsSubscription(t *testing.T) {
}
}
+func TestLightFilterLogs(t *testing.T) {
+ t.Parallel()
+
+ var (
+ db = rawdb.NewMemoryDatabase()
+ backend, sys = newTestFilterSystem(t, db, Config{})
+ api = NewFilterAPI(sys, true)
+ signer = types.HomesteadSigner{}
+
+ firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
+ secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
+ thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333")
+ notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999")
+ firstTopic = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")
+ secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222")
+
+ // posted twice, once as regular logs and once as pending logs.
+ allLogs = []*types.Log{
+ // Block 1
+ {Address: firstAddr, Topics: []common.Hash{}, Data: []byte{}, BlockNumber: 2, Index: 0},
+ // Block 2
+ {Address: firstAddr, Topics: []common.Hash{firstTopic}, Data: []byte{}, BlockNumber: 3, Index: 0},
+ {Address: secondAddr, Topics: []common.Hash{firstTopic}, Data: []byte{}, BlockNumber: 3, Index: 1},
+ {Address: thirdAddress, Topics: []common.Hash{secondTopic}, Data: []byte{}, BlockNumber: 3, Index: 2},
+ // Block 3
+ {Address: thirdAddress, Topics: []common.Hash{secondTopic}, Data: []byte{}, BlockNumber: 4, Index: 0},
+ }
+
+ testCases = []struct {
+ crit FilterCriteria
+ expected []*types.Log
+ id rpc.ID
+ }{
+ // match all
+ 0: {FilterCriteria{}, allLogs, ""},
+ // match none due to no matching addresses
+ 1: {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}}, []*types.Log{}, ""},
+ // match logs based on addresses, ignore topics
+ 2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""},
+ // match logs based on addresses and topics
+ 3: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[3:5], ""},
+ // all logs with block num >= 3
+ 4: {FilterCriteria{FromBlock: big.NewInt(3), ToBlock: big.NewInt(5)}, allLogs[1:], ""},
+ // all logs
+ 5: {FilterCriteria{FromBlock: big.NewInt(0), ToBlock: big.NewInt(5)}, allLogs, ""},
+ // all logs with 1>= block num <=2 and topic secondTopic
+ 6: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(3), Topics: [][]common.Hash{{secondTopic}}}, allLogs[3:4], ""},
+ }
+
+ key, _ = crypto.GenerateKey()
+ addr = crypto.PubkeyToAddress(key.PublicKey)
+ genesis = &core.Genesis{Config: params.TestChainConfig,
+ Alloc: core.GenesisAlloc{
+ addr: {Balance: big.NewInt(params.Ether)},
+ },
+ }
+ receipts = []*types.Receipt{{
+ Logs: []*types.Log{allLogs[0]},
+ }, {
+ Logs: []*types.Log{allLogs[1], allLogs[2], allLogs[3]},
+ }, {
+ Logs: []*types.Log{allLogs[4]},
+ }}
+ )
+
+ _, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 4, func(i int, b *core.BlockGen) {
+ if i == 0 {
+ return
+ }
+ receipts[i-1].Bloom = types.CreateBloom(types.Receipts{receipts[i-1]})
+ b.AddUncheckedReceipt(receipts[i-1])
+ tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i - 1), To: &common.Address{}, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, key)
+ b.AddTx(tx)
+ })
+ for i, block := range blocks {
+ rawdb.WriteBlock(db, block)
+ rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
+ rawdb.WriteHeadBlockHash(db, block.Hash())
+ if i > 0 {
+ rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), []*types.Receipt{receipts[i-1]})
+ }
+ }
+ // create all filters
+ for i := range testCases {
+ id, err := api.NewFilter(testCases[i].crit)
+ if err != nil {
+ t.Fatal(err)
+ }
+ testCases[i].id = id
+ }
+
+ // raise events
+ time.Sleep(1 * time.Second)
+ for _, block := range blocks {
+ backend.chainFeed.Send(core.ChainEvent{Block: block, Hash: common.Hash{}, Logs: allLogs})
+ }
+
+ for i, tt := range testCases {
+ var fetched []*types.Log
+ timeout := time.Now().Add(1 * time.Second)
+ for { // fetch all expected logs
+ results, err := api.GetFilterChanges(tt.id)
+ if err != nil {
+ t.Fatalf("Unable to fetch logs: %v", err)
+ }
+ fetched = append(fetched, results.([]*types.Log)...)
+ if len(fetched) >= len(tt.expected) {
+ break
+ }
+ // check timeout
+ if time.Now().After(timeout) {
+ break
+ }
+
+ time.Sleep(100 * time.Millisecond)
+ }
+
+ if len(fetched) != len(tt.expected) {
+ t.Errorf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched))
+ return
+ }
+
+ for l := range fetched {
+ if fetched[l].Removed {
+ t.Errorf("expected log not to be removed for log %d in case %d", l, i)
+ }
+ expected := *tt.expected[l]
+ blockNum := expected.BlockNumber - 1
+ expected.BlockHash = blocks[blockNum].Hash()
+ expected.TxHash = blocks[blockNum].Transactions()[0].Hash()
+ if !reflect.DeepEqual(fetched[l], &expected) {
+ t.Errorf("invalid log on index %d for case %d", l, i)
+ }
+ }
+ }
+}
+
// TestPendingTxFilterDeadlock tests if the event loop hangs when pending
// txes arrive at the same time that one of multiple filters is timing out.
// Please refer to #22131 for more details.
@@ -687,7 +913,7 @@ func TestPendingTxFilterDeadlock(t *testing.T) {
// timeout either in 100ms or 200ms
fids := make([]rpc.ID, 20)
for i := 0; i < len(fids); i++ {
- fid := api.NewPendingTransactionFilter()
+ fid := api.NewPendingTransactionFilter(nil)
fids[i] = fid
// Wait for at least one tx to arrive in filter
for {
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index 2c1f7cadf4..d10e0f1d94 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -19,6 +19,7 @@ package filters
import (
"context"
"math/big"
+ "reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
@@ -40,10 +41,8 @@ func makeReceipt(addr common.Address) *types.Receipt {
}
func BenchmarkFilters(b *testing.B) {
- dir := b.TempDir()
-
var (
- db, _ = rawdb.NewLevelDBDatabase(dir, 0, 0, "", false)
+ db, _ = rawdb.NewLevelDBDatabase(b.TempDir(), 0, 0, "", false)
_, sys = newTestFilterSystem(b, db, Config{})
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
@@ -51,17 +50,14 @@ func BenchmarkFilters(b *testing.B) {
addr3 = common.BytesToAddress([]byte("ethereum"))
addr4 = common.BytesToAddress([]byte("random addresses please"))
- gspec = core.Genesis{
+ gspec = &core.Genesis{
Alloc: core.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
BaseFee: big.NewInt(params.InitialBaseFee),
+ Config: params.TestChainConfig,
}
- genesis = gspec.ToBlock()
)
defer db.Close()
-
- gspec.MustCommit(db)
-
- chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 100010, func(i int, gen *core.BlockGen) {
+ _, chain, receipts := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 100010, func(i int, gen *core.BlockGen) {
switch i {
case 2403:
receipt := makeReceipt(addr1)
@@ -81,6 +77,11 @@ func BenchmarkFilters(b *testing.B) {
gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
}
})
+ // The test txs are not properly signed, can't simply create a chain
+ // and then import blocks. TODO(rjl493456442) try to get rid of the
+ // manual database writes.
+ gspec.MustCommit(db)
+
for i, block := range chain {
rawdb.WriteBlock(db, block)
rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
@@ -100,10 +101,8 @@ func BenchmarkFilters(b *testing.B) {
}
func TestFilters(t *testing.T) {
- dir := t.TempDir()
-
var (
- db, _ = rawdb.NewLevelDBDatabase(dir, 0, 0, "", false)
+ db, _ = rawdb.NewLevelDBDatabase(t.TempDir(), 0, 0, "", false)
_, sys = newTestFilterSystem(t, db, Config{})
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key1.PublicKey)
@@ -113,17 +112,15 @@ func TestFilters(t *testing.T) {
hash3 = common.BytesToHash([]byte("topic3"))
hash4 = common.BytesToHash([]byte("topic4"))
- gspec = core.Genesis{
+ gspec = &core.Genesis{
+ Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(1000000)}},
BaseFee: big.NewInt(params.InitialBaseFee),
}
- genesis = gspec.ToBlock()
)
defer db.Close()
- gspec.MustCommit(db)
-
- chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 1000, func(i int, gen *core.BlockGen) {
+ _, chain, receipts := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 1000, func(i int, gen *core.BlockGen) {
switch i {
case 1:
receipt := types.NewReceipt(nil, false, 0)
@@ -168,6 +165,10 @@ func TestFilters(t *testing.T) {
gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
}
})
+ // The test txs are not properly signed, can't simply create a chain
+ // and then import blocks. TODO(rjl493456442) try to get rid of the
+ // manual database writes.
+ gspec.MustCommit(db)
for i, block := range chain {
rawdb.WriteBlock(db, block)
rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
@@ -175,58 +176,66 @@ func TestFilters(t *testing.T) {
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i])
}
- filter := sys.NewRangeFilter(0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}})
+ // Set block 998 as Finalized (-3)
+ rawdb.WriteFinalizedBlockHash(db, chain[998].Hash())
+ filter := sys.NewRangeFilter(0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}})
logs, _ := filter.Logs(context.Background())
if len(logs) != 4 {
t.Error("expected 4 log, got", len(logs))
}
- filter = sys.NewRangeFilter(900, 999, []common.Address{addr}, [][]common.Hash{{hash3}})
- logs, _ = filter.Logs(context.Background())
- if len(logs) != 1 {
- t.Error("expected 1 log, got", len(logs))
- }
- if len(logs) > 0 && logs[0].Topics[0] != hash3 {
- t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
- }
-
- filter = sys.NewRangeFilter(990, -1, []common.Address{addr}, [][]common.Hash{{hash3}})
- logs, _ = filter.Logs(context.Background())
- if len(logs) != 1 {
- t.Error("expected 1 log, got", len(logs))
- }
- if len(logs) > 0 && logs[0].Topics[0] != hash3 {
- t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
- }
-
- filter = sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}})
-
- logs, _ = filter.Logs(context.Background())
- if len(logs) != 2 {
- t.Error("expected 2 log, got", len(logs))
- }
-
- failHash := common.BytesToHash([]byte("fail"))
- filter = sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{failHash}})
-
- logs, _ = filter.Logs(context.Background())
- if len(logs) != 0 {
- t.Error("expected 0 log, got", len(logs))
- }
-
- failAddr := common.BytesToAddress([]byte("failmenow"))
- filter = sys.NewRangeFilter(0, -1, []common.Address{failAddr}, nil)
-
- logs, _ = filter.Logs(context.Background())
- if len(logs) != 0 {
- t.Error("expected 0 log, got", len(logs))
- }
-
- filter = sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{failHash}, {hash1}})
-
- logs, _ = filter.Logs(context.Background())
- if len(logs) != 0 {
- t.Error("expected 0 log, got", len(logs))
+ for i, tc := range []struct {
+ f *Filter
+ wantHashes []common.Hash
+ }{
+ {
+ sys.NewRangeFilter(900, 999, []common.Address{addr}, [][]common.Hash{{hash3}}),
+ []common.Hash{hash3},
+ }, {
+ sys.NewRangeFilter(990, -1, []common.Address{addr}, [][]common.Hash{{hash3}}),
+ []common.Hash{hash3},
+ }, {
+ sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}}),
+ []common.Hash{hash1, hash2},
+ }, {
+ sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}}),
+ nil,
+ }, {
+ sys.NewRangeFilter(0, -1, []common.Address{common.BytesToAddress([]byte("failmenow"))}, nil),
+ nil,
+ }, {
+ sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}, {hash1}}),
+ nil,
+ }, {
+ sys.NewRangeFilter(-1, -1, nil, nil), []common.Hash{hash4},
+ }, {
+ sys.NewRangeFilter(-3, -1, nil, nil), []common.Hash{hash3, hash4},
+ }, {
+ sys.NewRangeFilter(-3, -3, nil, nil), []common.Hash{hash3},
+ }, {
+ sys.NewRangeFilter(-1, -3, nil, nil), nil,
+ }, {
+ sys.NewRangeFilter(-4, -1, nil, nil), nil,
+ }, {
+ sys.NewRangeFilter(-4, -4, nil, nil), nil,
+ }, {
+ sys.NewRangeFilter(-1, -4, nil, nil), nil,
+ },
+ } {
+ logs, _ := tc.f.Logs(context.Background())
+ var haveHashes []common.Hash
+ for _, l := range logs {
+ haveHashes = append(haveHashes, l.Topics[0])
+ }
+ if have, want := len(haveHashes), len(tc.wantHashes); have != want {
+ t.Fatalf("test %d, have %d logs, want %d", i, have, want)
+ }
+ if len(haveHashes) == 0 {
+ continue
+ }
+ if !reflect.DeepEqual(tc.wantHashes, haveHashes) {
+ t.Fatalf("test %d, have %v want %v", i, haveHashes, tc.wantHashes)
+ }
}
}
diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go
index 91835c1641..47cc31999e 100644
--- a/eth/gasprice/feehistory.go
+++ b/eth/gasprice/feehistory.go
@@ -56,7 +56,12 @@ type blockFees struct {
err error
}
-// processedFees contains the results of a processed block and is also used for caching
+type cacheKey struct {
+ number uint64
+ percentiles string
+}
+
+// processedFees contains the results of a processed block.
type processedFees struct {
reward []*big.Int
baseFee, nextBaseFee *big.Int
@@ -208,10 +213,11 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNum
// actually processed range is returned to avoid ambiguity when parts of the requested range
// are not available or when the head has changed during processing this request.
// Three arrays are returned based on the processed blocks:
-// - reward: the requested percentiles of effective priority fees per gas of transactions in each
-// block, sorted in ascending order and weighted by gas used.
-// - baseFee: base fee per gas in the given block
-// - gasUsedRatio: gasUsed/gasLimit in the given block
+// - reward: the requested percentiles of effective priority fees per gas of transactions in each
+// block, sorted in ascending order and weighted by gas used.
+// - baseFee: base fee per gas in the given block
+// - gasUsedRatio: gasUsed/gasLimit in the given block
+//
// Note: baseFee includes the next block after the newest of the returned range, because this
// value can be derived from the newest block.
func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
@@ -269,13 +275,10 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast
oracle.processBlock(fees, rewardPercentiles)
results <- fees
} else {
- cacheKey := struct {
- number uint64
- percentiles string
- }{blockNumber, string(percentileKey)}
+ cacheKey := cacheKey{number: blockNumber, percentiles: string(percentileKey)}
if p, ok := oracle.historyCache.Get(cacheKey); ok {
- fees.results = p.(processedFees)
+ fees.results = p
results <- fees
} else {
if len(rewardPercentiles) != 0 {
diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go
index deece7f461..b54874d688 100644
--- a/eth/gasprice/feehistory_test.go
+++ b/eth/gasprice/feehistory_test.go
@@ -62,7 +62,7 @@ func TestFeeHistory(t *testing.T) {
oracle := NewOracle(backend, config)
first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)
-
+ backend.teardown()
expReward := c.expCount
if len(c.percent) == 0 {
expReward = 0
diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go
index 00128a5dc8..604ad5e104 100644
--- a/eth/gasprice/gasprice.go
+++ b/eth/gasprice/gasprice.go
@@ -23,13 +23,13 @@ import (
"sync"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
- lru "github.com/hashicorp/golang-lru"
)
const sampleNumber = 3 // Number of transactions sampled in a block
@@ -72,7 +72,8 @@ type Oracle struct {
checkBlocks, percentile int
maxHeaderHistory, maxBlockHistory int
- historyCache *lru.Cache
+
+ historyCache *lru.Cache[cacheKey, processedFees]
}
// NewOracle returns a new gasprice oracle which can recommend suitable
@@ -114,7 +115,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory)
}
- cache, _ := lru.New(2048)
+ cache := lru.NewCache[cacheKey, processedFees](2048)
headEvent := make(chan core.ChainHeadEvent, 1)
backend.SubscribeChainHeadEvent(headEvent)
go func() {
diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go
index d405188f81..a987d46458 100644
--- a/eth/gasprice/gasprice_test.go
+++ b/eth/gasprice/gasprice_test.go
@@ -113,6 +113,12 @@ func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) eve
return nil
}
+func (b *testBackend) teardown() {
+ b.chain.Stop()
+}
+
+// newTestBackend creates a test backend. OBS: don't forget to invoke tearDown
+// after use, otherwise the blockchain instance will mem-leak via goroutines.
func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBackend {
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
@@ -129,11 +135,9 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
config.GrayGlacierBlock = londonBlock
config.TerminalTotalDifficulty = common.Big0
engine := ethash.NewFaker()
- db := rawdb.NewMemoryDatabase()
- genesis := gspec.MustCommit(db)
// Generate testing blocks
- blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, testHead+1, func(i int, b *core.BlockGen) {
+ _, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, testHead+1, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})
var txdata types.TxData
@@ -160,9 +164,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
b.AddTx(types.MustSignNewTx(key, signer, txdata))
})
// Construct testing chain
- diskdb := rawdb.NewMemoryDatabase()
- gspec.MustCommit(diskdb)
- chain, err := core.NewBlockChain(diskdb, &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec.Config, engine, vm.Config{}, nil, nil)
+ chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec, nil, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create local chain, %v", err)
}
@@ -202,6 +204,7 @@ func TestSuggestTipCap(t *testing.T) {
// The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G
got, err := oracle.SuggestTipCap(context.Background())
+ backend.teardown()
if err != nil {
t.Fatalf("Failed to retrieve recommended gas price: %v", err)
}
diff --git a/eth/handler.go b/eth/handler.go
index 143147b0c8..078133f059 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -331,7 +331,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
number = head.Number.Uint64()
td = h.chain.GetTd(hash, number)
)
- forkID := forkid.NewID(h.chain.Config(), h.chain.Genesis().Hash(), h.chain.CurrentHeader().Number.Uint64())
+ forkID := forkid.NewID(h.chain.Config(), genesis.Hash(), number, head.Time)
if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil {
peer.Log().Debug("Ethereum handshake failed", "err", err)
return err
diff --git a/eth/handler_eth.go b/eth/handler_eth.go
index 12e91ec7f5..4ed6335769 100644
--- a/eth/handler_eth.go
+++ b/eth/handler_eth.go
@@ -67,9 +67,12 @@ func (h *ethHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
case *eth.NewBlockPacket:
return h.handleBlockBroadcast(peer, packet.Block, packet.TD)
- case *eth.NewPooledTransactionHashesPacket:
+ case *eth.NewPooledTransactionHashesPacket66:
return h.txFetcher.Notify(peer.ID(), *packet)
+ case *eth.NewPooledTransactionHashesPacket68:
+ return h.txFetcher.Notify(peer.ID(), packet.Hashes)
+
case *eth.TransactionsPacket:
return h.txFetcher.Enqueue(peer.ID(), *packet, false)
diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go
index 453bc5e98e..502cc8e6a9 100644
--- a/eth/handler_eth_test.go
+++ b/eth/handler_eth_test.go
@@ -61,10 +61,14 @@ func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
h.blockBroadcasts.Send(packet.Block)
return nil
- case *eth.NewPooledTransactionHashesPacket:
+ case *eth.NewPooledTransactionHashesPacket66:
h.txAnnounces.Send(([]common.Hash)(*packet))
return nil
+ case *eth.NewPooledTransactionHashesPacket68:
+ h.txAnnounces.Send(packet.Hashes)
+ return nil
+
case *eth.TransactionsPacket:
h.txBroadcasts.Send(([]*types.Transaction)(*packet))
return nil
@@ -81,6 +85,8 @@ func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
// Tests that peers are correctly accepted (or rejected) based on the advertised
// fork IDs in the protocol handshake.
func TestForkIDSplit66(t *testing.T) { testForkIDSplit(t, eth.ETH66) }
+func TestForkIDSplit67(t *testing.T) { testForkIDSplit(t, eth.ETH67) }
+func TestForkIDSplit68(t *testing.T) { testForkIDSplit(t, eth.ETH68) }
func testForkIDSplit(t *testing.T, protocol uint) {
t.Parallel()
@@ -102,14 +108,11 @@ func testForkIDSplit(t *testing.T, protocol uint) {
gspecNoFork = &core.Genesis{Config: configNoFork}
gspecProFork = &core.Genesis{Config: configProFork}
- genesisNoFork = gspecNoFork.MustCommit(dbNoFork)
- genesisProFork = gspecProFork.MustCommit(dbProFork)
-
- chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil)
- chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil)
+ chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, gspecNoFork, nil, engine, vm.Config{}, nil, nil)
+ chainProFork, _ = core.NewBlockChain(dbProFork, nil, gspecProFork, nil, engine, vm.Config{}, nil, nil)
- blocksNoFork, _ = core.GenerateChain(configNoFork, genesisNoFork, engine, dbNoFork, 2, nil)
- blocksProFork, _ = core.GenerateChain(configProFork, genesisProFork, engine, dbProFork, 2, nil)
+ _, blocksNoFork, _ = core.GenerateChainWithGenesis(gspecNoFork, engine, 2, nil)
+ _, blocksProFork, _ = core.GenerateChainWithGenesis(gspecProFork, engine, 2, nil)
ethNoFork, _ = newHandler(&handlerConfig{
Database: dbNoFork,
@@ -238,6 +241,8 @@ func testForkIDSplit(t *testing.T, protocol uint) {
// Tests that received transactions are added to the local pool.
func TestRecvTransactions66(t *testing.T) { testRecvTransactions(t, eth.ETH66) }
+func TestRecvTransactions67(t *testing.T) { testRecvTransactions(t, eth.ETH67) }
+func TestRecvTransactions68(t *testing.T) { testRecvTransactions(t, eth.ETH68) }
func testRecvTransactions(t *testing.T, protocol uint) {
t.Parallel()
@@ -295,6 +300,8 @@ func testRecvTransactions(t *testing.T, protocol uint) {
// This test checks that pending transactions are sent.
func TestSendTransactions66(t *testing.T) { testSendTransactions(t, eth.ETH66) }
+func TestSendTransactions67(t *testing.T) { testSendTransactions(t, eth.ETH67) }
+func TestSendTransactions68(t *testing.T) { testSendTransactions(t, eth.ETH68) }
func testSendTransactions(t *testing.T, protocol uint) {
t.Parallel()
@@ -353,7 +360,7 @@ func testSendTransactions(t *testing.T, protocol uint) {
seen := make(map[common.Hash]struct{})
for len(seen) < len(insert) {
switch protocol {
- case 66:
+ case 66, 67, 68:
select {
case hashes := <-anns:
for _, hash := range hashes {
@@ -380,6 +387,8 @@ func testSendTransactions(t *testing.T, protocol uint) {
// Tests that transactions get propagated to all attached peers, either via direct
// broadcasts or via announcements/retrievals.
func TestTransactionPropagation66(t *testing.T) { testTransactionPropagation(t, eth.ETH66) }
+func TestTransactionPropagation67(t *testing.T) { testTransactionPropagation(t, eth.ETH67) }
+func TestTransactionPropagation68(t *testing.T) { testTransactionPropagation(t, eth.ETH68) }
func testTransactionPropagation(t *testing.T, protocol uint) {
t.Parallel()
@@ -442,7 +451,7 @@ func testTransactionPropagation(t *testing.T, protocol uint) {
select {
case event := <-txChs[i]:
arrived += len(event.Txs)
- case <-time.After(time.Second):
+ case <-time.After(2 * time.Second):
t.Errorf("sink %d: transaction propagation timed out: have %d, want %d", i, arrived, len(txs))
timeout = true
}
@@ -681,6 +690,8 @@ func testBroadcastBlock(t *testing.T, peers, bcasts int) {
// Tests that a propagated malformed block (uncles or transactions don't match
// with the hashes in the header) gets discarded and not broadcast forward.
func TestBroadcastMalformedBlock66(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH66) }
+func TestBroadcastMalformedBlock67(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH67) }
+func TestBroadcastMalformedBlock68(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH68) }
func testBroadcastMalformedBlock(t *testing.T, protocol uint) {
t.Parallel()
diff --git a/eth/handler_test.go b/eth/handler_test.go
index d967b6df93..8939e53a95 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -133,14 +133,13 @@ func newTestHandler() *testHandler {
func newTestHandlerWithBlocks(blocks int) *testHandler {
// Create a database pre-initialize with a genesis block
db := rawdb.NewMemoryDatabase()
- (&core.Genesis{
+ gspec := &core.Genesis{
Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}},
- }).MustCommit(db)
-
- chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+ }
+ chain, _ := core.NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
- bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, nil)
+ _, bs, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), blocks, nil)
if _, err := chain.InsertChain(bs); err != nil {
panic(err)
}
diff --git a/eth/protocols/eth/broadcast.go b/eth/protocols/eth/broadcast.go
index 6fc15f136b..3045303f22 100644
--- a/eth/protocols/eth/broadcast.go
+++ b/eth/protocols/eth/broadcast.go
@@ -82,7 +82,7 @@ func (p *Peer) broadcastTransactions() {
for i := 0; i < len(queue) && size < maxTxPacketSize; i++ {
if tx := p.txpool.Get(queue[i]); tx != nil {
txs = append(txs, tx)
- size += tx.Size()
+ size += common.StorageSize(tx.Size())
}
hashesCount++
}
@@ -142,13 +142,17 @@ func (p *Peer) announceTransactions() {
if done == nil && len(queue) > 0 {
// Pile transaction hashes until we reach our allowed network limit
var (
- count int
- pending []common.Hash
- size common.StorageSize
+ count int
+ pending []common.Hash
+ pendingTypes []byte
+ pendingSizes []uint32
+ size common.StorageSize
)
for count = 0; count < len(queue) && size < maxTxPacketSize; count++ {
- if p.txpool.Get(queue[count]) != nil {
+ if tx := p.txpool.Get(queue[count]); tx != nil {
pending = append(pending, queue[count])
+ pendingTypes = append(pendingTypes, tx.Type())
+ pendingSizes = append(pendingSizes, uint32(tx.Size()))
size += common.HashLength
}
}
@@ -159,9 +163,16 @@ func (p *Peer) announceTransactions() {
if len(pending) > 0 {
done = make(chan struct{})
go func() {
- if err := p.sendPooledTransactionHashes(pending); err != nil {
- fail <- err
- return
+ if p.version >= ETH68 {
+ if err := p.sendPooledTransactionHashes68(pending, pendingTypes, pendingSizes); err != nil {
+ fail <- err
+ return
+ }
+ } else {
+ if err := p.sendPooledTransactionHashes66(pending); err != nil {
+ fail <- err
+ return
+ }
}
close(done)
p.Log().Trace("Sent transaction announcements", "count", len(pending))
diff --git a/eth/protocols/eth/discovery.go b/eth/protocols/eth/discovery.go
index 03f2ea3cc2..87857244b5 100644
--- a/eth/protocols/eth/discovery.go
+++ b/eth/protocols/eth/discovery.go
@@ -59,7 +59,8 @@ func StartENRUpdater(chain *core.BlockChain, ln *enode.LocalNode) {
// currentENREntry constructs an `eth` ENR entry based on the current state of the chain.
func currentENREntry(chain *core.BlockChain) *enrEntry {
+ head := chain.CurrentHeader()
return &enrEntry{
- ForkID: forkid.NewID(chain.Config(), chain.Genesis().Hash(), chain.CurrentHeader().Number.Uint64()),
+ ForkID: forkid.NewID(chain.Config(), chain.Genesis().Hash(), head.Number.Uint64(), head.Time),
}
}
diff --git a/eth/protocols/eth/dispatcher.go b/eth/protocols/eth/dispatcher.go
index 65a935d555..3f81e045ba 100644
--- a/eth/protocols/eth/dispatcher.go
+++ b/eth/protocols/eth/dispatcher.go
@@ -203,7 +203,7 @@ func (p *Peer) dispatcher() {
}
case cancelOp := <-p.reqCancel:
- // Retrieve the pendign request to cancel and short circuit if it
+ // Retrieve the pending request to cancel and short circuit if it
// has already been serviced and is not available anymore
req := pending[cancelOp.id]
if req == nil {
diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go
index 3a0b21c30b..ae4ec9142e 100644
--- a/eth/protocols/eth/handler.go
+++ b/eth/protocols/eth/handler.go
@@ -127,7 +127,7 @@ func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2
// NodeInfo represents a short summary of the `eth` sub-protocol metadata
// known about the host peer.
type NodeInfo struct {
- Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3, Rinkeby=4)
+ Network uint64 `json:"network"` // Ethereum network ID (1=Mainnet, Rinkeby=4, Goerli=5)
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules
@@ -168,7 +168,7 @@ var eth66 = map[uint64]msgHandler{
NewBlockHashesMsg: handleNewBlockhashes,
NewBlockMsg: handleNewBlock,
TransactionsMsg: handleTransactions,
- NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
+ NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes66,
GetBlockHeadersMsg: handleGetBlockHeaders66,
BlockHeadersMsg: handleBlockHeaders66,
GetBlockBodiesMsg: handleGetBlockBodies66,
@@ -185,7 +185,22 @@ var eth67 = map[uint64]msgHandler{
NewBlockHashesMsg: handleNewBlockhashes,
NewBlockMsg: handleNewBlock,
TransactionsMsg: handleTransactions,
- NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
+ NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes66,
+ GetBlockHeadersMsg: handleGetBlockHeaders66,
+ BlockHeadersMsg: handleBlockHeaders66,
+ GetBlockBodiesMsg: handleGetBlockBodies66,
+ BlockBodiesMsg: handleBlockBodies66,
+ GetReceiptsMsg: handleGetReceipts66,
+ ReceiptsMsg: handleReceipts66,
+ GetPooledTransactionsMsg: handleGetPooledTransactions66,
+ PooledTransactionsMsg: handlePooledTransactions66,
+}
+
+var eth68 = map[uint64]msgHandler{
+ NewBlockHashesMsg: handleNewBlockhashes,
+ NewBlockMsg: handleNewBlock,
+ TransactionsMsg: handleTransactions,
+ NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes68,
GetBlockHeadersMsg: handleGetBlockHeaders66,
BlockHeadersMsg: handleBlockHeaders66,
GetBlockBodiesMsg: handleGetBlockBodies66,
@@ -210,9 +225,12 @@ func handleMessage(backend Backend, peer *Peer) error {
defer msg.Discard()
var handlers = eth66
- if peer.Version() >= ETH67 {
+ if peer.Version() == ETH67 {
handlers = eth67
}
+ if peer.Version() >= ETH68 {
+ handlers = eth68
+ }
// Track the amount of time it takes to serve the request and run the handler
if metrics.Enabled {
diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go
index 2707a420bc..76505ab8d3 100644
--- a/eth/protocols/eth/handler_test.go
+++ b/eth/protocols/eth/handler_test.go
@@ -23,10 +23,13 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
+ "github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
@@ -44,43 +47,79 @@ var (
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
)
+func u64(val uint64) *uint64 { return &val }
+
// testBackend is a mock implementation of the live Ethereum message handler. Its
// purpose is to allow testing the request/reply workflows and wire serialization
// in the `eth` protocol without actually doing any data processing.
type testBackend struct {
db ethdb.Database
chain *core.BlockChain
- txpool *core.TxPool
+ txpool *txpool.TxPool
}
// newTestBackend creates an empty chain and wraps it into a mock backend.
func newTestBackend(blocks int) *testBackend {
- return newTestBackendWithGenerator(blocks, nil)
+ return newTestBackendWithGenerator(blocks, false, nil)
}
// newTestBackend creates a chain with a number of explicitly defined blocks and
// wraps it into a mock backend.
-func newTestBackendWithGenerator(blocks int, generator func(int, *core.BlockGen)) *testBackend {
- // Create a database pre-initialize with a genesis block
- db := rawdb.NewMemoryDatabase()
- (&core.Genesis{
- Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}},
- }).MustCommit(db)
+func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, *core.BlockGen)) *testBackend {
+ var (
+ // Create a database pre-initialize with a genesis block
+ db = rawdb.NewMemoryDatabase()
+ config = params.TestChainConfig
+ engine consensus.Engine = ethash.NewFaker()
+ )
- chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+ if shanghai {
+ config = ¶ms.ChainConfig{
+ ChainID: big.NewInt(1),
+ HomesteadBlock: big.NewInt(0),
+ DAOForkBlock: nil,
+ DAOForkSupport: true,
+ 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: big.NewInt(0),
+ ShanghaiTime: u64(0),
+ TerminalTotalDifficulty: big.NewInt(0),
+ TerminalTotalDifficultyPassed: true,
+ Ethash: new(params.EthashConfig),
+ }
+ engine = beacon.NewFaker()
+ }
+
+ gspec := &core.Genesis{
+ Config: config,
+ Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}},
+ }
+ chain, _ := core.NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, nil)
- bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, generator)
+ _, bs, _ := core.GenerateChainWithGenesis(gspec, engine, blocks, generator)
if _, err := chain.InsertChain(bs); err != nil {
panic(err)
}
- txconfig := core.DefaultTxPoolConfig
+ for _, block := range bs {
+ chain.StateCache().TrieDB().Commit(block.Root(), false)
+ }
+ txconfig := txpool.DefaultConfig
txconfig.Journal = "" // Don't litter the disk with test journals
return &testBackend{
db: db,
chain: chain,
- txpool: core.NewTxPool(txconfig, params.TestChainConfig, chain),
+ txpool: txpool.NewTxPool(txconfig, params.TestChainConfig, chain),
}
}
@@ -109,6 +148,8 @@ func (b *testBackend) Handle(*Peer, Packet) error {
// Tests that block headers can be retrieved from a remote chain based on user queries.
func TestGetBlockHeaders66(t *testing.T) { testGetBlockHeaders(t, ETH66) }
+func TestGetBlockHeaders67(t *testing.T) { testGetBlockHeaders(t, ETH67) }
+func TestGetBlockHeaders68(t *testing.T) { testGetBlockHeaders(t, ETH68) }
func testGetBlockHeaders(t *testing.T, protocol uint) {
t.Parallel()
@@ -294,11 +335,23 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
// Tests that block contents can be retrieved from a remote chain based on their hashes.
func TestGetBlockBodies66(t *testing.T) { testGetBlockBodies(t, ETH66) }
+func TestGetBlockBodies67(t *testing.T) { testGetBlockBodies(t, ETH67) }
+func TestGetBlockBodies68(t *testing.T) { testGetBlockBodies(t, ETH68) }
func testGetBlockBodies(t *testing.T, protocol uint) {
t.Parallel()
- backend := newTestBackend(maxBodiesServe + 15)
+ gen := func(n int, g *core.BlockGen) {
+ if n%2 == 0 {
+ w := &types.Withdrawal{
+ Address: common.Address{0xaa},
+ Amount: 42,
+ }
+ g.AddWithdrawal(w)
+ }
+ }
+
+ backend := newTestBackendWithGenerator(maxBodiesServe+15, true, gen)
defer backend.close()
peer, _ := newTestPeer("peer", protocol, backend)
@@ -333,7 +386,7 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
}
// Run each of the tests and verify the results against the chain
for i, tt := range tests {
- // Collect the hashes to request, and the response to expectva
+ // Collect the hashes to request, and the response to expect
var (
hashes []common.Hash
bodies []*BlockBody
@@ -348,7 +401,7 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
block := backend.chain.GetBlockByNumber(uint64(num))
hashes = append(hashes, block.Hash())
if len(bodies) < tt.expected {
- bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
+ bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles(), Withdrawals: block.Withdrawals()})
}
break
}
@@ -358,9 +411,10 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
hashes = append(hashes, hash)
if tt.available[j] && len(bodies) < tt.expected {
block := backend.chain.GetBlockByHash(hash)
- bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
+ bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles(), Withdrawals: block.Withdrawals()})
}
}
+
// Send the hash request and verify the response
p2p.Send(peer.app, GetBlockBodiesMsg, &GetBlockBodiesPacket66{
RequestId: 123,
@@ -370,15 +424,17 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
RequestId: 123,
BlockBodiesPacket: bodies,
}); err != nil {
- t.Errorf("test %d: bodies mismatch: %v", i, err)
+ t.Fatalf("test %d: bodies mismatch: %v", i, err)
}
}
}
// Tests that the state trie nodes can be retrieved based on hashes.
-func TestGetNodeData66(t *testing.T) { testGetNodeData(t, ETH66) }
+func TestGetNodeData66(t *testing.T) { testGetNodeData(t, ETH66, false) }
+func TestGetNodeData67(t *testing.T) { testGetNodeData(t, ETH67, true) }
+func TestGetNodeData68(t *testing.T) { testGetNodeData(t, ETH68, true) }
-func testGetNodeData(t *testing.T, protocol uint) {
+func testGetNodeData(t *testing.T, protocol uint, drop bool) {
t.Parallel()
// Define three accounts to simulate transactions with
@@ -417,7 +473,7 @@ func testGetNodeData(t *testing.T, protocol uint) {
}
}
// Assemble the test environment
- backend := newTestBackendWithGenerator(4, generator)
+ backend := newTestBackendWithGenerator(4, false, generator)
defer backend.close()
peer, _ := newTestPeer("peer", protocol, backend)
@@ -439,8 +495,15 @@ func testGetNodeData(t *testing.T, protocol uint) {
GetNodeDataPacket: hashes,
})
msg, err := peer.app.ReadMsg()
- if err != nil {
- t.Fatalf("failed to read node data response: %v", err)
+ if !drop {
+ if err != nil {
+ t.Fatalf("failed to read node data response: %v", err)
+ }
+ } else {
+ if err != nil {
+ return
+ }
+ t.Fatalf("succeeded to read node data response on non-supporting protocol: %v", msg)
}
if msg.Code != NodeDataMsg {
t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, NodeDataMsg)
@@ -461,7 +524,7 @@ func testGetNodeData(t *testing.T, protocol uint) {
// Reconstruct state tree from the received data.
reconstructDB := rawdb.NewMemoryDatabase()
for i := 0; i < len(data); i++ {
- rawdb.WriteTrieNode(reconstructDB, hashes[i], data[i])
+ rawdb.WriteLegacyTrieNode(reconstructDB, hashes[i], data[i])
}
// Sanity check whether all state matches.
@@ -486,6 +549,8 @@ func testGetNodeData(t *testing.T, protocol uint) {
// Tests that the transaction receipts can be retrieved based on hashes.
func TestGetBlockReceipts66(t *testing.T) { testGetBlockReceipts(t, ETH66) }
+func TestGetBlockReceipts67(t *testing.T) { testGetBlockReceipts(t, ETH67) }
+func TestGetBlockReceipts68(t *testing.T) { testGetBlockReceipts(t, ETH68) }
func testGetBlockReceipts(t *testing.T, protocol uint) {
t.Parallel()
@@ -526,7 +591,7 @@ func testGetBlockReceipts(t *testing.T, protocol uint) {
}
}
// Assemble the test environment
- backend := newTestBackendWithGenerator(4, generator)
+ backend := newTestBackendWithGenerator(4, false, generator)
defer backend.close()
peer, _ := newTestPeer("peer", protocol, backend)
diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go
index c8585dfdf8..74e514b863 100644
--- a/eth/protocols/eth/handlers.go
+++ b/eth/protocols/eth/handlers.go
@@ -84,7 +84,7 @@ func serviceNonContiguousBlockHeaderQuery(chain *core.BlockChain, query *GetBloc
break
}
if rlpData, err := rlp.EncodeToBytes(origin); err != nil {
- log.Crit("Unable to decode our own headers", "err", err)
+ log.Crit("Unable to encode our own headers", "err", err)
} else {
headers = append(headers, rlp.RawValue(rlpData))
bytes += common.StorageSize(len(rlpData))
@@ -379,15 +379,19 @@ func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
}
metadata := func() interface{} {
var (
- txsHashes = make([]common.Hash, len(res.BlockBodiesPacket))
- uncleHashes = make([]common.Hash, len(res.BlockBodiesPacket))
+ txsHashes = make([]common.Hash, len(res.BlockBodiesPacket))
+ uncleHashes = make([]common.Hash, len(res.BlockBodiesPacket))
+ withdrawalHashes = make([]common.Hash, len(res.BlockBodiesPacket))
)
hasher := trie.NewStackTrie(nil)
for i, body := range res.BlockBodiesPacket {
txsHashes[i] = types.DeriveSha(types.Transactions(body.Transactions), hasher)
uncleHashes[i] = types.CalcUncleHash(body.Uncles)
+ if body.Withdrawals != nil {
+ withdrawalHashes[i] = types.DeriveSha(types.Withdrawals(body.Withdrawals), hasher)
+ }
}
- return [][]common.Hash{txsHashes, uncleHashes}
+ return [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes}
}
return peer.dispatchResponse(&Response{
id: res.RequestId,
@@ -430,13 +434,13 @@ func handleReceipts66(backend Backend, msg Decoder, peer *Peer) error {
}, metadata)
}
-func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer) error {
+func handleNewPooledTransactionHashes66(backend Backend, msg Decoder, peer *Peer) error {
// New transaction announcement arrived, make sure we have
// a valid and fresh chain to handle them
if !backend.AcceptTxs() {
return nil
}
- ann := new(NewPooledTransactionHashesPacket)
+ ann := new(NewPooledTransactionHashesPacket66)
if err := msg.Decode(ann); err != nil {
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
}
@@ -447,6 +451,26 @@ func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer)
return backend.Handle(peer, ann)
}
+func handleNewPooledTransactionHashes68(backend Backend, msg Decoder, peer *Peer) error {
+ // New transaction announcement arrived, make sure we have
+ // a valid and fresh chain to handle them
+ if !backend.AcceptTxs() {
+ return nil
+ }
+ ann := new(NewPooledTransactionHashesPacket68)
+ if err := msg.Decode(ann); err != nil {
+ return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
+ }
+ if len(ann.Hashes) != len(ann.Types) || len(ann.Hashes) != len(ann.Sizes) {
+ return fmt.Errorf("%w: message %v: invalid len of fields: %v %v %v", errDecode, msg, len(ann.Hashes), len(ann.Types), len(ann.Sizes))
+ }
+ // Schedule all the unknown hashes for retrieval
+ for _, hash := range ann.Hashes {
+ peer.markTransaction(hash)
+ }
+ return backend.Handle(peer, ann)
+}
+
func handleGetPooledTransactions66(backend Backend, msg Decoder, peer *Peer) error {
// Decode the pooled transactions retrieval message
var query GetPooledTransactionsPacket66
diff --git a/eth/protocols/eth/handshake_test.go b/eth/protocols/eth/handshake_test.go
index 8cf5216cf4..c768edaeac 100644
--- a/eth/protocols/eth/handshake_test.go
+++ b/eth/protocols/eth/handshake_test.go
@@ -40,7 +40,7 @@ func testHandshake(t *testing.T, protocol uint) {
genesis = backend.chain.Genesis()
head = backend.chain.CurrentBlock()
td = backend.chain.GetTd(head.Hash(), head.NumberU64())
- forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis().Hash(), backend.chain.CurrentHeader().Number.Uint64())
+ forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis().Hash(), backend.chain.CurrentHeader().Number.Uint64(), backend.chain.CurrentHeader().Time)
)
tests := []struct {
code uint64
diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go
index a23726384d..219f486c8e 100644
--- a/eth/protocols/eth/peer.go
+++ b/eth/protocols/eth/peer.go
@@ -21,7 +21,7 @@ import (
"math/rand"
"sync"
- mapset "github.com/deckarep/golang-set"
+ mapset "github.com/deckarep/golang-set/v2"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/p2p"
@@ -188,7 +188,7 @@ func (p *Peer) markTransaction(hash common.Hash) {
// not be managed directly.
//
// The reasons this is public is to allow packages using this protocol to write
-// tests that directly send messages without having to do the asyn queueing.
+// tests that directly send messages without having to do the async queueing.
func (p *Peer) SendTransactions(txs types.Transactions) error {
// Mark all the transactions as known, but ensure we don't overflow our limits
for _, tx := range txs {
@@ -210,16 +210,29 @@ func (p *Peer) AsyncSendTransactions(hashes []common.Hash) {
}
}
-// sendPooledTransactionHashes sends transaction hashes to the peer and includes
+// sendPooledTransactionHashes66 sends transaction hashes to the peer and includes
// them in its transaction hash set for future reference.
//
// This method is a helper used by the async transaction announcer. Don't call it
// directly as the queueing (memory) and transmission (bandwidth) costs should
// not be managed directly.
-func (p *Peer) sendPooledTransactionHashes(hashes []common.Hash) error {
+func (p *Peer) sendPooledTransactionHashes66(hashes []common.Hash) error {
// Mark all the transactions as known, but ensure we don't overflow our limits
p.knownTxs.Add(hashes...)
- return p2p.Send(p.rw, NewPooledTransactionHashesMsg, NewPooledTransactionHashesPacket(hashes))
+ return p2p.Send(p.rw, NewPooledTransactionHashesMsg, NewPooledTransactionHashesPacket66(hashes))
+}
+
+// sendPooledTransactionHashes68 sends transaction hashes (tagged with their type
+// and size) to the peer and includes them in its transaction hash set for future
+// reference.
+//
+// This method is a helper used by the async transaction announcer. Don't call it
+// directly as the queueing (memory) and transmission (bandwidth) costs should
+// not be managed directly.
+func (p *Peer) sendPooledTransactionHashes68(hashes []common.Hash, types []byte, sizes []uint32) error {
+ // Mark all the transactions as known, but ensure we don't overflow our limits
+ p.knownTxs.Add(hashes...)
+ return p2p.Send(p.rw, NewPooledTransactionHashesMsg, NewPooledTransactionHashesPacket68{Types: types, Sizes: sizes, Hashes: hashes})
}
// AsyncSendPooledTransactionHashes queues a list of transactions hashes to eventually
@@ -489,7 +502,7 @@ func (p *Peer) RequestTxs(hashes []common.Hash) error {
// knownCache is a cache for known hashes.
type knownCache struct {
- hashes mapset.Set
+ hashes mapset.Set[common.Hash]
max int
}
@@ -497,7 +510,7 @@ type knownCache struct {
func newKnownCache(max int) *knownCache {
return &knownCache{
max: max,
- hashes: mapset.NewSet(),
+ hashes: mapset.NewSet[common.Hash](),
}
}
diff --git a/eth/protocols/eth/peer_test.go b/eth/protocols/eth/peer_test.go
index 0916ebee5d..efbbbc6fff 100644
--- a/eth/protocols/eth/peer_test.go
+++ b/eth/protocols/eth/peer_test.go
@@ -48,6 +48,8 @@ func newTestPeer(name string, version uint, backend Backend) (*testPeer, <-chan
peer := NewPeer(version, p2p.NewPeer(id, name, nil), net, backend.TxPool())
errc := make(chan error, 1)
go func() {
+ defer app.Close()
+
errc <- backend.RunPeer(peer, func(peer *Peer) error {
return Handle(backend, peer)
})
diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go
index f6fac42780..0d4b368988 100644
--- a/eth/protocols/eth/protocol.go
+++ b/eth/protocols/eth/protocol.go
@@ -32,6 +32,7 @@ import (
const (
ETH66 = 66
ETH67 = 67
+ ETH68 = 68
)
// ProtocolName is the official short name of the `eth` protocol used during
@@ -40,11 +41,11 @@ const ProtocolName = "eth"
// ProtocolVersions are the supported versions of the `eth` protocol (first
// is primary).
-var ProtocolVersions = []uint{ETH67, ETH66}
+var ProtocolVersions = []uint{ETH68, ETH67, ETH66}
// protocolLengths are the number of implemented message corresponding to
// different protocol versions.
-var protocolLengths = map[uint]uint64{ETH67: 17, ETH66: 17}
+var protocolLengths = map[uint]uint64{ETH68: 17, ETH67: 17, ETH66: 17}
// maxMessageSize is the maximum cap on the size of a protocol message.
const maxMessageSize = 10 * 1024 * 1024
@@ -238,19 +239,22 @@ type BlockBodiesRLPPacket66 struct {
type BlockBody struct {
Transactions []*types.Transaction // Transactions contained within a block
Uncles []*types.Header // Uncles contained within a block
+ Withdrawals []*types.Withdrawal `rlp:"optional"` // Withdrawals contained within a block
}
// Unpack retrieves the transactions and uncles from the range packet and returns
// them in a split flat format that's more consistent with the internal data structures.
-func (p *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header) {
+func (p *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal) {
+ // TODO(matt): add support for withdrawals to fetchers
var (
- txset = make([][]*types.Transaction, len(*p))
- uncleset = make([][]*types.Header, len(*p))
+ txset = make([][]*types.Transaction, len(*p))
+ uncleset = make([][]*types.Header, len(*p))
+ withdrawalset = make([][]*types.Withdrawal, len(*p))
)
for i, body := range *p {
- txset[i], uncleset[i] = body.Transactions, body.Uncles
+ txset[i], uncleset[i], withdrawalset[i] = body.Transactions, body.Uncles, body.Withdrawals
}
- return txset, uncleset
+ return txset, uncleset, withdrawalset
}
// GetNodeDataPacket represents a trie node data query.
@@ -298,8 +302,15 @@ type ReceiptsRLPPacket66 struct {
ReceiptsRLPPacket
}
-// NewPooledTransactionHashesPacket represents a transaction announcement packet.
-type NewPooledTransactionHashesPacket []common.Hash
+// NewPooledTransactionHashesPacket66 represents a transaction announcement packet on eth/66 and eth/67.
+type NewPooledTransactionHashesPacket66 []common.Hash
+
+// NewPooledTransactionHashesPacket68 represents a transaction announcement packet on eth/68 and newer.
+type NewPooledTransactionHashesPacket68 struct {
+ Types []byte
+ Sizes []uint32
+ Hashes []common.Hash
+}
// GetPooledTransactionsPacket represents a transaction query.
type GetPooledTransactionsPacket []common.Hash
@@ -364,8 +375,11 @@ func (*GetReceiptsPacket) Kind() byte { return GetReceiptsMsg }
func (*ReceiptsPacket) Name() string { return "Receipts" }
func (*ReceiptsPacket) Kind() byte { return ReceiptsMsg }
-func (*NewPooledTransactionHashesPacket) Name() string { return "NewPooledTransactionHashes" }
-func (*NewPooledTransactionHashesPacket) Kind() byte { return NewPooledTransactionHashesMsg }
+func (*NewPooledTransactionHashesPacket66) Name() string { return "NewPooledTransactionHashes" }
+func (*NewPooledTransactionHashesPacket66) Kind() byte { return NewPooledTransactionHashesMsg }
+
+func (*NewPooledTransactionHashesPacket68) Name() string { return "NewPooledTransactionHashes" }
+func (*NewPooledTransactionHashesPacket68) Kind() byte { return NewPooledTransactionHashesMsg }
func (*GetPooledTransactionsPacket) Name() string { return "GetPooledTransactions" }
func (*GetPooledTransactionsPacket) Kind() byte { return GetPooledTransactionsMsg }
diff --git a/eth/protocols/snap/handler.go b/eth/protocols/snap/handler.go
index 41380d96f5..d7c9400440 100644
--- a/eth/protocols/snap/handler.go
+++ b/eth/protocols/snap/handler.go
@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
@@ -139,7 +140,7 @@ func HandleMessage(backend Backend, peer *Peer) error {
}
defer msg.Discard()
start := time.Now()
- // Track the emount of time it takes to serve the request and run the handler
+ // Track the amount of time it takes to serve the request and run the handler
if metrics.Enabled {
h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code)
defer func(start time.Time) {
@@ -283,7 +284,7 @@ func ServiceGetAccountRangeQuery(chain *core.BlockChain, req *GetAccountRangePac
req.Bytes = softResponseLimit
}
// Retrieve the requested state and bail out if non existent
- tr, err := trie.New(common.Hash{}, req.Root, chain.StateCache().TrieDB())
+ tr, err := trie.New(trie.StateTrieID(req.Root), chain.StateCache().TrieDB())
if err != nil {
return nil, nil
}
@@ -343,7 +344,7 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP
req.Bytes = softResponseLimit
}
// TODO(karalabe): Do we want to enforce > 0 accounts and 1 account if origin is set?
- // TODO(karalabe): - Logging locally is not ideal as remote faulst annoy the local user
+ // TODO(karalabe): - Logging locally is not ideal as remote faults annoy the local user
// TODO(karalabe): - Dropping the remote peer is less flexible wrt client bugs (slow is better than non-functional)
// Calculate the hard limit at which to abort, even if mid storage trie
@@ -413,15 +414,16 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP
if origin != (common.Hash{}) || (abort && len(storage) > 0) {
// Request started at a non-zero hash or was capped prematurely, add
// the endpoint Merkle proofs
- accTrie, err := trie.NewStateTrie(common.Hash{}, req.Root, chain.StateCache().TrieDB())
+ accTrie, err := trie.NewStateTrie(trie.StateTrieID(req.Root), chain.StateCache().TrieDB())
if err != nil {
return nil, nil
}
- acc, err := accTrie.TryGetAccountWithPreHashedKey(account[:])
+ acc, err := accTrie.TryGetAccountByHash(account)
if err != nil || acc == nil {
return nil, nil
}
- stTrie, err := trie.NewStateTrie(account, acc.Root, chain.StateCache().TrieDB())
+ id := trie.StorageTrieID(req.Root, account, acc.Root)
+ stTrie, err := trie.NewStateTrie(id, chain.StateCache().TrieDB())
if err != nil {
return nil, nil
}
@@ -463,7 +465,7 @@ func ServiceGetByteCodesQuery(chain *core.BlockChain, req *GetByteCodesPacket) [
bytes uint64
)
for _, hash := range req.Hashes {
- if hash == emptyCode {
+ if hash == types.EmptyCodeHash {
// Peers should not request the empty code, but if they do, at
// least sent them back a correct response without db lookups
codes = append(codes, []byte{})
@@ -487,19 +489,13 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s
// Make sure we have the state associated with the request
triedb := chain.StateCache().TrieDB()
- accTrie, err := trie.NewStateTrie(common.Hash{}, req.Root, triedb)
+ accTrie, err := trie.NewStateTrie(trie.StateTrieID(req.Root), triedb)
if err != nil {
// We don't have the requested state available, bail out
return nil, nil
}
+ // The 'snap' might be nil, in which case we cannot serve storage slots.
snap := chain.Snapshots().Snapshot(req.Root)
- if snap == nil {
- // We don't have the requested state snapshotted yet, bail out.
- // In reality we could still serve using the account and storage
- // tries only, but let's protect the node a bit while it's doing
- // snapshot generation.
- return nil, nil
- }
// Retrieve trie nodes until the packet size limit is reached
var (
nodes [][]byte
@@ -523,13 +519,27 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s
bytes += uint64(len(blob))
default:
+ var stRoot common.Hash
// Storage slots requested, open the storage trie and retrieve from there
- account, err := snap.Account(common.BytesToHash(pathset[0]))
- loads++ // always account database reads, even for failures
- if err != nil || account == nil {
- break
+ if snap == nil {
+ // We don't have the requested state snapshotted yet (or it is stale),
+ // but can look up the account via the trie instead.
+ account, err := accTrie.TryGetAccountByHash(common.BytesToHash(pathset[0]))
+ loads += 8 // We don't know the exact cost of lookup, this is an estimate
+ if err != nil || account == nil {
+ break
+ }
+ stRoot = account.Root
+ } else {
+ account, err := snap.Account(common.BytesToHash(pathset[0]))
+ loads++ // always account database reads, even for failures
+ if err != nil || account == nil {
+ break
+ }
+ stRoot = common.BytesToHash(account.Root)
}
- stTrie, err := trie.NewStateTrie(common.BytesToHash(pathset[0]), common.BytesToHash(account.Root), triedb)
+ id := trie.StorageTrieID(req.Root, common.BytesToHash(pathset[0]), stRoot)
+ stTrie, err := trie.NewStateTrie(id, triedb)
loads++ // always account database reads, even for failures
if err != nil {
break
diff --git a/eth/protocols/snap/peer.go b/eth/protocols/snap/peer.go
index 235d499ffd..3db6e22cbd 100644
--- a/eth/protocols/snap/peer.go
+++ b/eth/protocols/snap/peer.go
@@ -86,7 +86,7 @@ func (p *Peer) RequestAccountRange(id uint64, root common.Hash, origin, limit co
})
}
-// RequestStorageRange fetches a batch of storage slots belonging to one or more
+// RequestStorageRanges fetches a batch of storage slots belonging to one or more
// accounts. If slots from only one account is requested, an origin marker may also
// be used to retrieve from there.
func (p *Peer) RequestStorageRanges(id uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, bytes uint64) error {
diff --git a/eth/protocols/snap/range_test.go b/eth/protocols/snap/range_test.go
index c6dc8fb718..3461439e54 100644
--- a/eth/protocols/snap/range_test.go
+++ b/eth/protocols/snap/range_test.go
@@ -95,7 +95,7 @@ func TestHashRanges(t *testing.T) {
// meaningful space size for manual verification.
// - The head being 0xff...f0, we have 14 hashes left in the space
// - Chunking up 14 into 3 pieces is 4.(6), but we need the ceil of 5 to avoid a micro-last-chunk
- // - Since the range is not divisible, the last interval will be shrter, capped at 0xff...f
+ // - Since the range is not divisible, the last interval will be shorter, capped at 0xff...f
// - The chunk ranges thus needs to be [..0, ..5], [..6, ..b], [..c, ..f]
{
head: common.HexToHash("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0"),
diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go
index eb8260bf7c..1a94e32ecc 100644
--- a/eth/protocols/snap/sync.go
+++ b/eth/protocols/snap/sync.go
@@ -46,14 +46,6 @@ import (
"golang.org/x/crypto/sha3"
)
-var (
- // emptyRoot is the known root hash of an empty trie.
- emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
-
- // emptyCode is the known hash of the empty EVM bytecode.
- emptyCode = crypto.Keccak256Hash(nil)
-)
-
const (
// minRequestSize is the minimum number of bytes to request from a remote peer.
// This number is used as the low cap for account and storage range requests.
@@ -70,7 +62,7 @@ const (
// and waste round trip times. If it's too high, we're capping responses and
// waste bandwidth.
//
- // Depoyed bytecodes are currently capped at 24KB, so the minimum request
+ // Deployed bytecodes are currently capped at 24KB, so the minimum request
// size should be maxRequestSize / 24K. Assuming that most contracts do not
// come close to that, requesting 4x should be a good approximation.
maxCodeRequestCount = maxRequestSize / (24 * 1024) * 4
@@ -87,8 +79,8 @@ const (
trienodeHealRateMeasurementImpact = 0.005
// minTrienodeHealThrottle is the minimum divisor for throttling trie node
- // heal requests to avoid overloading the local node and exessively expanding
- // the state trie bedth wise.
+ // heal requests to avoid overloading the local node and excessively expanding
+ // the state trie breadth wise.
minTrienodeHealThrottle = 1
// maxTrienodeHealThrottle is the maximum divisor for throttling trie node
@@ -417,7 +409,8 @@ type SyncPeer interface {
// - The peer delivers a stale response after a previous timeout
// - The peer delivers a refusal to serve the requested state
type Syncer struct {
- db ethdb.KeyValueStore // Database to store the trie nodes into (and dedup)
+ db ethdb.KeyValueStore // Database to store the trie nodes into (and dedup)
+ scheme string // Node scheme used in node database
root common.Hash // Current state trie root being synced
tasks []*accountTask // Current account task set being synced
@@ -485,9 +478,10 @@ type Syncer struct {
// NewSyncer creates a new snapshot syncer to download the Ethereum state over the
// snap protocol.
-func NewSyncer(db ethdb.KeyValueStore) *Syncer {
+func NewSyncer(db ethdb.KeyValueStore, scheme string) *Syncer {
return &Syncer{
- db: db,
+ db: db,
+ scheme: scheme,
peers: make(map[string]SyncPeer),
peerJoin: new(event.Feed),
@@ -581,7 +575,7 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error {
s.lock.Lock()
s.root = root
s.healer = &healTask{
- scheduler: state.NewStateSync(root, s.db, s.onHealState),
+ scheduler: state.NewStateSync(root, s.db, s.onHealState, s.scheme),
trieTasks: make(map[string]common.Hash),
codeTasks: make(map[common.Hash]struct{}),
}
@@ -615,6 +609,8 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error {
}
}()
defer s.report(true)
+ // commit any trie- and bytecode-healing data.
+ defer s.commitHealer(true)
// Whether sync completed or not, disregard any future packets
defer func() {
@@ -741,8 +737,9 @@ func (s *Syncer) loadSyncStatus() {
s.accountBytes += common.StorageSize(len(key) + len(value))
},
}
- task.genTrie = trie.NewStackTrie(task.genBatch)
-
+ task.genTrie = trie.NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, val []byte) {
+ rawdb.WriteTrieNode(task.genBatch, owner, path, hash, val, s.scheme)
+ })
for accountHash, subtasks := range task.SubTasks {
for _, subtask := range subtasks {
subtask.genBatch = ethdb.HookedBatch{
@@ -751,7 +748,9 @@ func (s *Syncer) loadSyncStatus() {
s.storageBytes += common.StorageSize(len(key) + len(value))
},
}
- subtask.genTrie = trie.NewStackTrieWithOwner(subtask.genBatch, accountHash)
+ subtask.genTrie = trie.NewStackTrieWithOwner(func(owner common.Hash, path []byte, hash common.Hash, val []byte) {
+ rawdb.WriteTrieNode(subtask.genBatch, owner, path, hash, val, s.scheme)
+ }, accountHash)
}
}
}
@@ -808,7 +807,9 @@ func (s *Syncer) loadSyncStatus() {
Last: last,
SubTasks: make(map[common.Hash][]*storageTask),
genBatch: batch,
- genTrie: trie.NewStackTrie(batch),
+ genTrie: trie.NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, val []byte) {
+ rawdb.WriteTrieNode(batch, owner, path, hash, val, s.scheme)
+ }),
})
log.Debug("Created account sync task", "from", next, "last", last)
next = common.BigToHash(new(big.Int).Add(last.Big(), common.Big1))
@@ -1824,7 +1825,7 @@ func (s *Syncer) processAccountResponse(res *accountResponse) {
res.task.pend = 0
for i, account := range res.accounts {
// Check if the account is a contract with an unknown code
- if !bytes.Equal(account.CodeHash, emptyCode[:]) {
+ if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
if !rawdb.HasCodeWithPrefix(s.db, common.BytesToHash(account.CodeHash)) {
res.task.codeTasks[common.BytesToHash(account.CodeHash)] = struct{}{}
res.task.needCode[i] = true
@@ -1832,8 +1833,8 @@ func (s *Syncer) processAccountResponse(res *accountResponse) {
}
}
// Check if the account is a contract with an unknown storage trie
- if account.Root != emptyRoot {
- if ok, err := s.db.Has(account.Root[:]); err != nil || !ok {
+ if account.Root != types.EmptyRootHash {
+ if !rawdb.HasTrieNode(s.db, res.hashes[i], nil, account.Root, s.scheme) {
// If there was a previous large state retrieval in progress,
// don't restart it from scratch. This happens if a sync cycle
// is interrupted and resumed later. However, *do* update the
@@ -2005,7 +2006,9 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
Last: r.End(),
root: acc.Root,
genBatch: batch,
- genTrie: trie.NewStackTrieWithOwner(batch, account),
+ genTrie: trie.NewStackTrieWithOwner(func(owner common.Hash, path []byte, hash common.Hash, val []byte) {
+ rawdb.WriteTrieNode(batch, owner, path, hash, val, s.scheme)
+ }, account),
})
for r.Next() {
batch := ethdb.HookedBatch{
@@ -2019,7 +2022,9 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
Last: r.End(),
root: acc.Root,
genBatch: batch,
- genTrie: trie.NewStackTrieWithOwner(batch, account),
+ genTrie: trie.NewStackTrieWithOwner(func(owner common.Hash, path []byte, hash common.Hash, val []byte) {
+ rawdb.WriteTrieNode(batch, owner, path, hash, val, s.scheme)
+ }, account),
})
}
for _, task := range tasks {
@@ -2064,7 +2069,9 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
slots += len(res.hashes[i])
if i < len(res.hashes)-1 || res.subTask == nil {
- tr := trie.NewStackTrieWithOwner(batch, account)
+ tr := trie.NewStackTrieWithOwner(func(owner common.Hash, path []byte, hash common.Hash, val []byte) {
+ rawdb.WriteTrieNode(batch, owner, path, hash, val, s.scheme)
+ }, account)
for j := 0; j < len(res.hashes[i]); j++ {
tr.Update(res.hashes[i][j][:], res.slots[i][j])
}
@@ -2154,6 +2161,57 @@ func (s *Syncer) processTrienodeHealResponse(res *trienodeHealResponse) {
log.Error("Invalid trienode processed", "hash", hash, "err", err)
}
}
+ s.commitHealer(false)
+
+ // Calculate the processing rate of one filled trie node
+ rate := float64(fills) / (float64(time.Since(start)) / float64(time.Second))
+
+ // Update the currently measured trienode queueing and processing throughput.
+ //
+ // The processing rate needs to be updated uniformly independent if we've
+ // processed 1x100 trie nodes or 100x1 to keep the rate consistent even in
+ // the face of varying network packets. As such, we cannot just measure the
+ // time it took to process N trie nodes and update once, we need one update
+ // per trie node.
+ //
+ // Naively, that would be:
+ //
+ // for i:=0; i time.Second {
+ // Periodically adjust the trie node throttler
+ if float64(pending) > 2*s.trienodeHealRate {
+ s.trienodeHealThrottle *= trienodeHealThrottleIncrease
+ } else {
+ s.trienodeHealThrottle /= trienodeHealThrottleDecrease
+ }
+ if s.trienodeHealThrottle > maxTrienodeHealThrottle {
+ s.trienodeHealThrottle = maxTrienodeHealThrottle
+ } else if s.trienodeHealThrottle < minTrienodeHealThrottle {
+ s.trienodeHealThrottle = minTrienodeHealThrottle
+ }
+ s.trienodeHealThrottled = time.Now()
+
+ log.Debug("Updated trie node heal throttler", "rate", s.trienodeHealRate, "pending", pending, "throttle", s.trienodeHealThrottle)
+ }
+}
+
+func (s *Syncer) commitHealer(force bool) {
+ if !force && s.healer.scheduler.MemSize() < ethdb.IdealBatchSize {
+ return
+ }
batch := s.db.NewBatch()
if err := s.healer.scheduler.Commit(batch); err != nil {
log.Error("Failed to commit healing data", "err", err)
@@ -2234,14 +2292,7 @@ func (s *Syncer) processBytecodeHealResponse(res *bytecodeHealResponse) {
log.Error("Invalid bytecode processed", "hash", hash, "err", err)
}
}
- batch := s.db.NewBatch()
- if err := s.healer.scheduler.Commit(batch); err != nil {
- log.Error("Failed to commit healing data", "err", err)
- }
- if err := batch.Write(); err != nil {
- log.Crit("Failed to persist healing data", "err", err)
- }
- log.Debug("Persisted set of healing data", "type", "bytecode", "bytes", common.StorageSize(batch.ValueSize()))
+ s.commitHealer(false)
}
// forwardAccountTask takes a filled account task and persists anything available
@@ -2962,7 +3013,7 @@ func (s *Syncer) reportSyncProgress(force bool) {
storage = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.storageSynced), s.storageBytes.TerminalString())
bytecode = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.bytecodeSynced), s.bytecodeBytes.TerminalString())
)
- log.Info("State sync in progress", "synced", progress, "state", synced,
+ log.Info("Syncing: state download in progress", "synced", progress, "state", synced,
"accounts", accounts, "slots", storage, "codes", bytecode, "eta", common.PrettyDuration(estTime-elapsed))
}
@@ -2981,7 +3032,7 @@ func (s *Syncer) reportHealProgress(force bool) {
accounts = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.accountHealed), s.accountHealedBytes.TerminalString())
storage = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.storageHealed), s.storageHealedBytes.TerminalString())
)
- log.Info("State heal in progress", "accounts", accounts, "slots", storage,
+ log.Info("Syncing: state healing in progress", "accounts", accounts, "slots", storage,
"codes", bytecode, "nodes", trienode, "pending", s.healer.scheduler.Pending())
}
@@ -3068,7 +3119,7 @@ func (t *healRequestSort) Swap(i, j int) {
func (t *healRequestSort) Merge() []TrieNodePathSet {
var result []TrieNodePathSet
for _, path := range t.syncPaths {
- pathset := TrieNodePathSet([][]byte(path))
+ pathset := TrieNodePathSet(path)
if len(path) == 1 {
// It's an account reference.
result = append(result, pathset)
diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go
index 4d9f631b5a..0a61179729 100644
--- a/eth/protocols/snap/sync_test.go
+++ b/eth/protocols/snap/sync_test.go
@@ -159,6 +159,13 @@ func newTestPeer(id string, t *testing.T, term func()) *testPeer {
return peer
}
+func (t *testPeer) setStorageTries(tries map[common.Hash]*trie.Trie) {
+ t.storageTries = make(map[common.Hash]*trie.Trie)
+ for root, trie := range tries {
+ t.storageTries[root] = trie.Copy()
+ }
+}
+
func (t *testPeer) ID() string { return t.id }
func (t *testPeer) Log() log.Logger { return t.logger }
@@ -368,8 +375,8 @@ func createStorageRequestResponse(t *testPeer, root common.Hash, accounts []comm
return hashes, slots, proofs
}
-// the createStorageRequestResponseAlwaysProve tests a cornercase, where it always
-// supplies the proof for the last account, even if it is 'complete'.h
+// createStorageRequestResponseAlwaysProve tests a cornercase, where the peer always
+// supplies the proof for the last account, even if it is 'complete'.
func createStorageRequestResponseAlwaysProve(t *testPeer, root common.Hash, accounts []common.Hash, bOrigin, bLimit []byte, max uint64) (hashes [][]common.Hash, slots [][][]byte, proofs [][]byte) {
var size uint64
max = max * 3 / 4
@@ -562,9 +569,9 @@ func TestSyncBloatedProof(t *testing.T) {
})
}
)
- sourceAccountTrie, elems := makeAccountTrieNoStorage(100)
+ nodeScheme, sourceAccountTrie, elems := makeAccountTrieNoStorage(100)
source := newTestPeer("source", t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
source.accountRequestHandler = func(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error {
@@ -610,15 +617,15 @@ func TestSyncBloatedProof(t *testing.T) {
}
return nil
}
- syncer := setupSyncer(source)
+ syncer := setupSyncer(nodeScheme, source)
if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err == nil {
t.Fatal("No error returned from incomplete/cancelled sync")
}
}
-func setupSyncer(peers ...*testPeer) *Syncer {
+func setupSyncer(scheme string, peers ...*testPeer) *Syncer {
stateDb := rawdb.NewMemoryDatabase()
- syncer := NewSyncer(stateDb)
+ syncer := NewSyncer(stateDb, scheme)
for _, peer := range peers {
syncer.Register(peer)
peer.remote = syncer
@@ -639,15 +646,15 @@ func TestSync(t *testing.T) {
})
}
)
- sourceAccountTrie, elems := makeAccountTrieNoStorage(100)
+ nodeScheme, sourceAccountTrie, elems := makeAccountTrieNoStorage(100)
mkSource := func(name string) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
return source
}
- syncer := setupSyncer(mkSource("source"))
+ syncer := setupSyncer(nodeScheme, mkSource("source"))
if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil {
t.Fatalf("sync failed: %v", err)
}
@@ -668,15 +675,15 @@ func TestSyncTinyTriePanic(t *testing.T) {
})
}
)
- sourceAccountTrie, elems := makeAccountTrieNoStorage(1)
+ nodeScheme, sourceAccountTrie, elems := makeAccountTrieNoStorage(1)
mkSource := func(name string) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
return source
}
- syncer := setupSyncer(mkSource("source"))
+ syncer := setupSyncer(nodeScheme, mkSource("source"))
done := checkStall(t, term)
if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil {
t.Fatalf("sync failed: %v", err)
@@ -698,15 +705,15 @@ func TestMultiSync(t *testing.T) {
})
}
)
- sourceAccountTrie, elems := makeAccountTrieNoStorage(100)
+ nodeScheme, sourceAccountTrie, elems := makeAccountTrieNoStorage(100)
mkSource := func(name string) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
return source
}
- syncer := setupSyncer(mkSource("sourceA"), mkSource("sourceB"))
+ syncer := setupSyncer(nodeScheme, mkSource("sourceA"), mkSource("sourceB"))
done := checkStall(t, term)
if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil {
t.Fatalf("sync failed: %v", err)
@@ -728,17 +735,17 @@ func TestSyncWithStorage(t *testing.T) {
})
}
)
- sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(3, 3000, true, false)
+ nodeScheme, sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(3, 3000, true, false)
mkSource := func(name string) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
- source.storageTries = storageTries
+ source.setStorageTries(storageTries)
source.storageValues = storageElems
return source
}
- syncer := setupSyncer(mkSource("sourceA"))
+ syncer := setupSyncer(nodeScheme, mkSource("sourceA"))
done := checkStall(t, term)
if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil {
t.Fatalf("sync failed: %v", err)
@@ -760,13 +767,13 @@ func TestMultiSyncManyUseless(t *testing.T) {
})
}
)
- sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false)
+ nodeScheme, sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false)
mkSource := func(name string, noAccount, noStorage, noTrieNode bool) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
- source.storageTries = storageTries
+ source.setStorageTries(storageTries)
source.storageValues = storageElems
if !noAccount {
@@ -782,6 +789,7 @@ func TestMultiSyncManyUseless(t *testing.T) {
}
syncer := setupSyncer(
+ nodeScheme,
mkSource("full", true, true, true),
mkSource("noAccounts", false, true, true),
mkSource("noStorage", true, false, true),
@@ -806,13 +814,13 @@ func TestMultiSyncManyUselessWithLowTimeout(t *testing.T) {
})
}
)
- sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false)
+ nodeScheme, sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false)
mkSource := func(name string, noAccount, noStorage, noTrieNode bool) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
- source.storageTries = storageTries
+ source.setStorageTries(storageTries)
source.storageValues = storageElems
if !noAccount {
@@ -828,6 +836,7 @@ func TestMultiSyncManyUselessWithLowTimeout(t *testing.T) {
}
syncer := setupSyncer(
+ nodeScheme,
mkSource("full", true, true, true),
mkSource("noAccounts", false, true, true),
mkSource("noStorage", true, false, true),
@@ -857,13 +866,13 @@ func TestMultiSyncManyUnresponsive(t *testing.T) {
})
}
)
- sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false)
+ nodeScheme, sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false)
mkSource := func(name string, noAccount, noStorage, noTrieNode bool) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
- source.storageTries = storageTries
+ source.setStorageTries(storageTries)
source.storageValues = storageElems
if !noAccount {
@@ -879,6 +888,7 @@ func TestMultiSyncManyUnresponsive(t *testing.T) {
}
syncer := setupSyncer(
+ nodeScheme,
mkSource("full", true, true, true),
mkSource("noAccounts", false, true, true),
mkSource("noStorage", true, false, true),
@@ -923,15 +933,16 @@ func TestSyncBoundaryAccountTrie(t *testing.T) {
})
}
)
- sourceAccountTrie, elems := makeBoundaryAccountTrie(3000)
+ nodeScheme, sourceAccountTrie, elems := makeBoundaryAccountTrie(3000)
mkSource := func(name string) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
return source
}
syncer := setupSyncer(
+ nodeScheme,
mkSource("peer-a"),
mkSource("peer-b"),
)
@@ -957,11 +968,11 @@ func TestSyncNoStorageAndOneCappedPeer(t *testing.T) {
})
}
)
- sourceAccountTrie, elems := makeAccountTrieNoStorage(3000)
+ nodeScheme, sourceAccountTrie, elems := makeAccountTrieNoStorage(3000)
mkSource := func(name string, slow bool) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
if slow {
@@ -971,6 +982,7 @@ func TestSyncNoStorageAndOneCappedPeer(t *testing.T) {
}
syncer := setupSyncer(
+ nodeScheme,
mkSource("nice-a", false),
mkSource("nice-b", false),
mkSource("nice-c", false),
@@ -998,11 +1010,11 @@ func TestSyncNoStorageAndOneCodeCorruptPeer(t *testing.T) {
})
}
)
- sourceAccountTrie, elems := makeAccountTrieNoStorage(3000)
+ nodeScheme, sourceAccountTrie, elems := makeAccountTrieNoStorage(3000)
mkSource := func(name string, codeFn codeHandlerFunc) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
source.codeRequestHandler = codeFn
return source
@@ -1012,6 +1024,7 @@ func TestSyncNoStorageAndOneCodeCorruptPeer(t *testing.T) {
// non-corrupt peer, which delivers everything in one go, and makes the
// test moot
syncer := setupSyncer(
+ nodeScheme,
mkSource("capped", cappedCodeRequestHandler),
mkSource("corrupt", corruptCodeRequestHandler),
)
@@ -1035,11 +1048,11 @@ func TestSyncNoStorageAndOneAccountCorruptPeer(t *testing.T) {
})
}
)
- sourceAccountTrie, elems := makeAccountTrieNoStorage(3000)
+ nodeScheme, sourceAccountTrie, elems := makeAccountTrieNoStorage(3000)
mkSource := func(name string, accFn accountHandlerFunc) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
source.accountRequestHandler = accFn
return source
@@ -1049,6 +1062,7 @@ func TestSyncNoStorageAndOneAccountCorruptPeer(t *testing.T) {
// non-corrupt peer, which delivers everything in one go, and makes the
// test moot
syncer := setupSyncer(
+ nodeScheme,
mkSource("capped", defaultAccountRequestHandler),
mkSource("corrupt", corruptAccountRequestHandler),
)
@@ -1074,11 +1088,11 @@ func TestSyncNoStorageAndOneCodeCappedPeer(t *testing.T) {
})
}
)
- sourceAccountTrie, elems := makeAccountTrieNoStorage(3000)
+ nodeScheme, sourceAccountTrie, elems := makeAccountTrieNoStorage(3000)
mkSource := func(name string, codeFn codeHandlerFunc) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
source.codeRequestHandler = codeFn
return source
@@ -1087,6 +1101,7 @@ func TestSyncNoStorageAndOneCodeCappedPeer(t *testing.T) {
// so it shouldn't be more than that
var counter int
syncer := setupSyncer(
+ nodeScheme,
mkSource("capped", func(t *testPeer, id uint64, hashes []common.Hash, max uint64) error {
counter++
return cappedCodeRequestHandler(t, id, hashes, max)
@@ -1124,17 +1139,18 @@ func TestSyncBoundaryStorageTrie(t *testing.T) {
})
}
)
- sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(10, 1000, false, true)
+ nodeScheme, sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(10, 1000, false, true)
mkSource := func(name string) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
- source.storageTries = storageTries
+ source.setStorageTries(storageTries)
source.storageValues = storageElems
return source
}
syncer := setupSyncer(
+ nodeScheme,
mkSource("peer-a"),
mkSource("peer-b"),
)
@@ -1160,13 +1176,13 @@ func TestSyncWithStorageAndOneCappedPeer(t *testing.T) {
})
}
)
- sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(300, 1000, false, false)
+ nodeScheme, sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(300, 1000, false, false)
mkSource := func(name string, slow bool) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
- source.storageTries = storageTries
+ source.setStorageTries(storageTries)
source.storageValues = storageElems
if slow {
@@ -1176,6 +1192,7 @@ func TestSyncWithStorageAndOneCappedPeer(t *testing.T) {
}
syncer := setupSyncer(
+ nodeScheme,
mkSource("nice-a", false),
mkSource("slow", true),
)
@@ -1201,19 +1218,20 @@ func TestSyncWithStorageAndCorruptPeer(t *testing.T) {
})
}
)
- sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false)
+ nodeScheme, sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false)
mkSource := func(name string, handler storageHandlerFunc) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
- source.storageTries = storageTries
+ source.setStorageTries(storageTries)
source.storageValues = storageElems
source.storageRequestHandler = handler
return source
}
syncer := setupSyncer(
+ nodeScheme,
mkSource("nice-a", defaultStorageRequestHandler),
mkSource("nice-b", defaultStorageRequestHandler),
mkSource("nice-c", defaultStorageRequestHandler),
@@ -1239,18 +1257,19 @@ func TestSyncWithStorageAndNonProvingPeer(t *testing.T) {
})
}
)
- sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false)
+ nodeScheme, sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorage(100, 3000, true, false)
mkSource := func(name string, handler storageHandlerFunc) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
- source.storageTries = storageTries
+ source.setStorageTries(storageTries)
source.storageValues = storageElems
source.storageRequestHandler = handler
return source
}
syncer := setupSyncer(
+ nodeScheme,
mkSource("nice-a", defaultStorageRequestHandler),
mkSource("nice-b", defaultStorageRequestHandler),
mkSource("nice-c", defaultStorageRequestHandler),
@@ -1279,18 +1298,18 @@ func TestSyncWithStorageMisbehavingProve(t *testing.T) {
})
}
)
- sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorageWithUniqueStorage(10, 30, false)
+ nodeScheme, sourceAccountTrie, elems, storageTries, storageElems := makeAccountTrieWithStorageWithUniqueStorage(10, 30, false)
mkSource := func(name string) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
- source.storageTries = storageTries
+ source.setStorageTries(storageTries)
source.storageValues = storageElems
source.storageRequestHandler = proofHappyStorageRequestHandler
return source
}
- syncer := setupSyncer(mkSource("sourceA"))
+ syncer := setupSyncer(nodeScheme, mkSource("sourceA"))
if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil {
t.Fatalf("sync failed: %v", err)
}
@@ -1335,7 +1354,7 @@ func getCodeHash(i uint64) []byte {
// getCodeByHash convenience function to lookup the code from the code hash
func getCodeByHash(hash common.Hash) []byte {
- if hash == emptyCode {
+ if hash == types.EmptyCodeHash {
return nil
}
for i, h := range codehashes {
@@ -1347,7 +1366,7 @@ func getCodeByHash(hash common.Hash) []byte {
}
// makeAccountTrieNoStorage spits out a trie, along with the leafs
-func makeAccountTrieNoStorage(n int) (*trie.Trie, entrySlice) {
+func makeAccountTrieNoStorage(n int) (string, *trie.Trie, entrySlice) {
var (
db = trie.NewDatabase(rawdb.NewMemoryDatabase())
accTrie = trie.NewEmpty(db)
@@ -1357,7 +1376,7 @@ func makeAccountTrieNoStorage(n int) (*trie.Trie, entrySlice) {
value, _ := rlp.EncodeToBytes(&types.StateAccount{
Nonce: i,
Balance: big.NewInt(int64(i)),
- Root: emptyRoot,
+ Root: types.EmptyRootHash,
CodeHash: getCodeHash(i),
})
key := key32(i)
@@ -1369,17 +1388,17 @@ func makeAccountTrieNoStorage(n int) (*trie.Trie, entrySlice) {
// Commit the state changes into db and re-create the trie
// for accessing later.
- root, nodes, _ := accTrie.Commit(false)
+ root, nodes := accTrie.Commit(false)
db.Update(trie.NewWithNodeSet(nodes))
- accTrie, _ = trie.New(common.Hash{}, root, db)
- return accTrie, entries
+ accTrie, _ = trie.New(trie.StateTrieID(root), db)
+ return db.Scheme(), accTrie, entries
}
// makeBoundaryAccountTrie constructs an account trie. Instead of filling
// accounts normally, this function will fill a few accounts which have
// boundary hash.
-func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
+func makeBoundaryAccountTrie(n int) (string, *trie.Trie, entrySlice) {
var (
entries entrySlice
boundaries []common.Hash
@@ -1408,7 +1427,7 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
value, _ := rlp.EncodeToBytes(&types.StateAccount{
Nonce: uint64(0),
Balance: big.NewInt(int64(i)),
- Root: emptyRoot,
+ Root: types.EmptyRootHash,
CodeHash: getCodeHash(uint64(i)),
})
elem := &kv{boundaries[i].Bytes(), value}
@@ -1420,7 +1439,7 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
value, _ := rlp.EncodeToBytes(&types.StateAccount{
Nonce: i,
Balance: big.NewInt(int64(i)),
- Root: emptyRoot,
+ Root: types.EmptyRootHash,
CodeHash: getCodeHash(i),
})
elem := &kv{key32(i), value}
@@ -1431,16 +1450,16 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
// Commit the state changes into db and re-create the trie
// for accessing later.
- root, nodes, _ := accTrie.Commit(false)
+ root, nodes := accTrie.Commit(false)
db.Update(trie.NewWithNodeSet(nodes))
- accTrie, _ = trie.New(common.Hash{}, root, db)
- return accTrie, entries
+ accTrie, _ = trie.New(trie.StateTrieID(root), db)
+ return db.Scheme(), accTrie, entries
}
// makeAccountTrieWithStorageWithUniqueStorage creates an account trie where each accounts
// has a unique storage set.
-func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) (*trie.Trie, entrySlice, map[common.Hash]*trie.Trie, map[common.Hash]entrySlice) {
+func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) (string, *trie.Trie, entrySlice, map[common.Hash]*trie.Trie, map[common.Hash]entrySlice) {
var (
db = trie.NewDatabase(rawdb.NewMemoryDatabase())
accTrie = trie.NewEmpty(db)
@@ -1453,7 +1472,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool)
// Create n accounts in the trie
for i := uint64(1); i <= uint64(accounts); i++ {
key := key32(i)
- codehash := emptyCode[:]
+ codehash := types.EmptyCodeHash.Bytes()
if code {
codehash = getCodeHash(i)
}
@@ -1477,24 +1496,25 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool)
sort.Sort(entries)
// Commit account trie
- root, set, _ := accTrie.Commit(true)
+ root, set := accTrie.Commit(true)
nodes.Merge(set)
// Commit gathered dirty nodes into database
db.Update(nodes)
// Re-create tries with new root
- accTrie, _ = trie.New(common.Hash{}, root, db)
+ accTrie, _ = trie.New(trie.StateTrieID(root), db)
for i := uint64(1); i <= uint64(accounts); i++ {
key := key32(i)
- trie, _ := trie.New(common.BytesToHash(key), storageRoots[common.BytesToHash(key)], db)
+ id := trie.StorageTrieID(root, common.BytesToHash(key), storageRoots[common.BytesToHash(key)])
+ trie, _ := trie.New(id, db)
storageTries[common.BytesToHash(key)] = trie
}
- return accTrie, entries, storageTries, storageEntries
+ return db.Scheme(), accTrie, entries, storageTries, storageEntries
}
// makeAccountTrieWithStorage spits out a trie, along with the leafs
-func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie.Trie, entrySlice, map[common.Hash]*trie.Trie, map[common.Hash]entrySlice) {
+func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (string, *trie.Trie, entrySlice, map[common.Hash]*trie.Trie, map[common.Hash]entrySlice) {
var (
db = trie.NewDatabase(rawdb.NewMemoryDatabase())
accTrie = trie.NewEmpty(db)
@@ -1507,7 +1527,7 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie
// Create n accounts in the trie
for i := uint64(1); i <= uint64(accounts); i++ {
key := key32(i)
- codehash := emptyCode[:]
+ codehash := types.EmptyCodeHash.Bytes()
if code {
codehash = getCodeHash(i)
}
@@ -1541,33 +1561,34 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie
sort.Sort(entries)
// Commit account trie
- root, set, _ := accTrie.Commit(true)
+ root, set := accTrie.Commit(true)
nodes.Merge(set)
// Commit gathered dirty nodes into database
db.Update(nodes)
// Re-create tries with new root
- accTrie, err := trie.New(common.Hash{}, root, db)
+ accTrie, err := trie.New(trie.StateTrieID(root), db)
if err != nil {
panic(err)
}
for i := uint64(1); i <= uint64(accounts); i++ {
key := key32(i)
- trie, err := trie.New(common.BytesToHash(key), storageRoots[common.BytesToHash(key)], db)
+ id := trie.StorageTrieID(root, common.BytesToHash(key), storageRoots[common.BytesToHash(key)])
+ trie, err := trie.New(id, db)
if err != nil {
panic(err)
}
storageTries[common.BytesToHash(key)] = trie
}
- return accTrie, entries, storageTries, storageEntries
+ return db.Scheme(), accTrie, entries, storageTries, storageEntries
}
// makeStorageTrieWithSeed fills a storage trie with n items, returning the
// not-yet-committed trie and the sorted entries. The seeds can be used to ensure
// that tries are unique.
func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *trie.Database) (common.Hash, *trie.NodeSet, entrySlice) {
- trie, _ := trie.New(owner, common.Hash{}, db)
+ trie, _ := trie.New(trie.StorageTrieID(common.Hash{}, owner, common.Hash{}), db)
var entries entrySlice
for i := uint64(1); i <= n; i++ {
// store 'x' at slot 'x'
@@ -1582,7 +1603,7 @@ func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *trie.Databas
entries = append(entries, elem)
}
sort.Sort(entries)
- root, nodes, _ := trie.Commit(false)
+ root, nodes := trie.Commit(false)
return root, nodes, entries
}
@@ -1593,7 +1614,7 @@ func makeBoundaryStorageTrie(owner common.Hash, n int, db *trie.Database) (commo
var (
entries entrySlice
boundaries []common.Hash
- trie, _ = trie.New(owner, common.Hash{}, db)
+ trie, _ = trie.New(trie.StorageTrieID(common.Hash{}, owner, common.Hash{}), db)
)
// Initialize boundaries
var next common.Hash
@@ -1633,14 +1654,14 @@ func makeBoundaryStorageTrie(owner common.Hash, n int, db *trie.Database) (commo
entries = append(entries, elem)
}
sort.Sort(entries)
- root, nodes, _ := trie.Commit(false)
+ root, nodes := trie.Commit(false)
return root, nodes, entries
}
func verifyTrie(db ethdb.KeyValueStore, root common.Hash, t *testing.T) {
t.Helper()
- triedb := trie.NewDatabase(db)
- accTrie, err := trie.New(common.Hash{}, root, triedb)
+ triedb := trie.NewDatabase(rawdb.NewDatabase(db))
+ accTrie, err := trie.New(trie.StateTrieID(root), triedb)
if err != nil {
t.Fatal(err)
}
@@ -1657,8 +1678,9 @@ func verifyTrie(db ethdb.KeyValueStore, root common.Hash, t *testing.T) {
log.Crit("Invalid account encountered during snapshot creation", "err", err)
}
accounts++
- if acc.Root != emptyRoot {
- storeTrie, err := trie.NewStateTrie(common.BytesToHash(accIt.Key), acc.Root, triedb)
+ if acc.Root != types.EmptyRootHash {
+ id := trie.StorageTrieID(root, common.BytesToHash(accIt.Key), acc.Root)
+ storeTrie, err := trie.NewStateTrie(id, triedb)
if err != nil {
t.Fatal(err)
}
@@ -1694,16 +1716,16 @@ func TestSyncAccountPerformance(t *testing.T) {
})
}
)
- sourceAccountTrie, elems := makeAccountTrieNoStorage(100)
+ nodeScheme, sourceAccountTrie, elems := makeAccountTrieNoStorage(100)
mkSource := func(name string) *testPeer {
source := newTestPeer(name, t, term)
- source.accountTrie = sourceAccountTrie
+ source.accountTrie = sourceAccountTrie.Copy()
source.accountValues = elems
return source
}
src := mkSource("source")
- syncer := setupSyncer(src)
+ syncer := setupSyncer(nodeScheme, src)
if err := syncer.Sync(sourceAccountTrie.Hash(), cancel); err != nil {
t.Fatalf("sync failed: %v", err)
}
diff --git a/eth/state_accessor.go b/eth/state_accessor.go
index 12dba8a0a9..3bb1464952 100644
--- a/eth/state_accessor.go
+++ b/eth/state_accessor.go
@@ -17,6 +17,7 @@
package eth
import (
+ "context"
"errors"
"fmt"
"time"
@@ -26,39 +27,59 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie"
)
+// noopReleaser is returned in case there is no operation expected
+// for releasing state.
+var noopReleaser = tracers.StateReleaseFunc(func() {})
+
// StateAtBlock retrieves the state database associated with a certain block.
// If no state is locally available for the given block, a number of blocks
// are attempted to be reexecuted to generate the desired state. The optional
-// base layer statedb can be passed then it's regarded as the statedb of the
+// base layer statedb can be provided which is regarded as the statedb of the
// parent block.
+//
+// An additional release function will be returned if the requested state is
+// available. Release is expected to be invoked when the returned state is no longer needed.
+// Its purpose is to prevent resource leaking. Though it can be noop in some cases.
+//
// Parameters:
-// - block: The block for which we want the state (== state at the stateRoot of the parent)
-// - reexec: The maximum number of blocks to reprocess trying to obtain the desired state
-// - base: If the caller is tracing multiple blocks, the caller can provide the parent state
-// continuously from the callsite.
-// - checklive: if true, then the live 'blockchain' state database is used. If the caller want to
-// perform Commit or other 'save-to-disk' changes, this should be set to false to avoid
-// storing trash persistently
-// - preferDisk: this arg can be used by the caller to signal that even though the 'base' is provided,
-// it would be preferable to start from a fresh state, if we have it on disk.
-func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
+// - block: The block for which we want the state(state = block.Root)
+// - reexec: The maximum number of blocks to reprocess trying to obtain the desired state
+// - base: If the caller is tracing multiple blocks, the caller can provide the parent
+// state continuously from the callsite.
+// - readOnly: If true, then the live 'blockchain' state database is used. No mutation should
+// be made from caller, e.g. perform Commit or other 'save-to-disk' changes.
+// Otherwise, the trash generated by caller may be persisted permanently.
+// - preferDisk: this arg can be used by the caller to signal that even though the 'base' is
+// provided, it would be preferable to start from a fresh state, if we have it
+// on disk.
+func (eth *Ethereum) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (statedb *state.StateDB, release tracers.StateReleaseFunc, err error) {
var (
current *types.Block
database state.Database
report = true
origin = block.NumberU64()
)
- // Check the live database first if we have the state fully available, use that.
- if checkLive {
- statedb, err = eth.blockchain.StateAt(block.Root())
- if err == nil {
- return statedb, nil
+ // The state is only for reading purposes, check the state presence in
+ // live database.
+ if readOnly {
+ // The state is available in live database, create a reference
+ // on top to prevent garbage collection and return a release
+ // function to deref it.
+ if statedb, err = eth.blockchain.StateAt(block.Root()); err == nil {
+ statedb.Database().TrieDB().Reference(block.Root(), common.Hash{})
+ return statedb, func() {
+ statedb.Database().TrieDB().Dereference(block.Root())
+ }, nil
}
}
+ // The state is both for reading and writing, or it's unavailable in disk,
+ // try to construct/recover the state over an ephemeral trie.Database for
+ // isolating the live one.
if base != nil {
if preferDisk {
// Create an ephemeral trie.Database for isolating the live one. Otherwise
@@ -66,37 +87,40 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state
database = state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16})
if statedb, err = state.New(block.Root(), database, nil); err == nil {
log.Info("Found disk backend for state trie", "root", block.Root(), "number", block.Number())
- return statedb, nil
+ return statedb, noopReleaser, nil
}
}
// The optional base statedb is given, mark the start point as parent block
statedb, database, report = base, base.Database(), false
current = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
} else {
- // Otherwise try to reexec blocks until we find a state or reach our limit
+ // Otherwise, try to reexec blocks until we find a state or reach our limit
current = block
// Create an ephemeral trie.Database for isolating the live one. Otherwise
// the internal junks created by tracing will be persisted into the disk.
database = state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16})
- // If we didn't check the dirty database, do check the clean one, otherwise
- // we would rewind past a persisted block (specific corner case is chain
- // tracing from the genesis).
- if !checkLive {
+ // If we didn't check the live database, do check state over ephemeral database,
+ // otherwise we would rewind past a persisted block (specific corner case is
+ // chain tracing from the genesis).
+ if !readOnly {
statedb, err = state.New(current.Root(), database, nil)
if err == nil {
- return statedb, nil
+ return statedb, noopReleaser, nil
}
}
// Database does not have the state for the given block, try to regenerate
for i := uint64(0); i < reexec; i++ {
+ if err := ctx.Err(); err != nil {
+ return nil, nil, err
+ }
if current.NumberU64() == 0 {
- return nil, errors.New("genesis state is missing")
+ return nil, nil, errors.New("genesis state is missing")
}
parent := eth.blockchain.GetBlock(current.ParentHash(), current.NumberU64()-1)
if parent == nil {
- return nil, fmt.Errorf("missing block %v %d", current.ParentHash(), current.NumberU64()-1)
+ return nil, nil, fmt.Errorf("missing block %v %d", current.ParentHash(), current.NumberU64()-1)
}
current = parent
@@ -108,19 +132,23 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state
if err != nil {
switch err.(type) {
case *trie.MissingNodeError:
- return nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec)
+ return nil, nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec)
default:
- return nil, err
+ return nil, nil, err
}
}
}
- // State was available at historical point, regenerate
+ // State is available at historical point, re-execute the blocks on top for
+ // the desired state.
var (
start = time.Now()
logged time.Time
parent common.Hash
)
for current.NumberU64() < origin {
+ if err := ctx.Err(); err != nil {
+ return nil, nil, err
+ }
// Print progress logs if long enough time elapsed
if time.Since(logged) > 8*time.Second && report {
log.Info("Regenerating historical state", "block", current.NumberU64()+1, "target", origin, "remaining", origin-current.NumberU64()-1, "elapsed", time.Since(start))
@@ -129,22 +157,24 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state
// Retrieve the next block to regenerate and process it
next := current.NumberU64() + 1
if current = eth.blockchain.GetBlockByNumber(next); current == nil {
- return nil, fmt.Errorf("block #%d not found", next)
+ return nil, nil, fmt.Errorf("block #%d not found", next)
}
_, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{})
if err != nil {
- return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
+ return nil, nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
}
// Finalize the state so any modifications are written to the trie
root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(current.Number()))
if err != nil {
- return nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w",
+ return nil, nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w",
current.NumberU64(), current.Root().Hex(), err)
}
statedb, err = state.New(root, database, nil)
if err != nil {
- return nil, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(), err)
+ return nil, nil, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(), err)
}
+ // Hold the state reference and also drop the parent state
+ // to prevent accumulating too many nodes in memory.
database.TrieDB().Reference(root, common.Hash{})
if parent != (common.Hash{}) {
database.TrieDB().Dereference(parent)
@@ -155,28 +185,28 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state
nodes, imgs := database.TrieDB().Size()
log.Info("Historical state regenerated", "block", current.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs)
}
- return statedb, nil
+ return statedb, func() { database.TrieDB().Dereference(block.Root()) }, nil
}
// stateAtTransaction returns the execution environment of a certain transaction.
-func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
+func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
// Short circuit if it's genesis block.
if block.NumberU64() == 0 {
- return nil, vm.BlockContext{}, nil, errors.New("no transaction in genesis")
+ return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
}
// Create the parent state database
parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
- return nil, vm.BlockContext{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
+ return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
}
// Lookup the statedb of parent block from the live database,
// otherwise regenerate it on the flight.
- statedb, err := eth.StateAtBlock(parent, reexec, nil, true, false)
+ statedb, release, err := eth.StateAtBlock(ctx, parent, reexec, nil, true, false)
if err != nil {
- return nil, vm.BlockContext{}, nil, err
+ return nil, vm.BlockContext{}, nil, nil, err
}
if txIndex == 0 && len(block.Transactions()) == 0 {
- return nil, vm.BlockContext{}, statedb, nil
+ return nil, vm.BlockContext{}, statedb, release, nil
}
// Recompute transactions up to the target index.
signer := types.MakeSigner(eth.blockchain.Config(), block.Number())
@@ -186,17 +216,17 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
if idx == txIndex {
- return msg, context, statedb, nil
+ return msg, context, statedb, release, nil
}
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
- statedb.Prepare(tx.Hash(), idx)
+ statedb.SetTxContext(tx.Hash(), idx)
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
- return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
+ return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
}
- return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
+ return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
}
diff --git a/eth/tracers/api.go b/eth/tracers/api.go
index 092950e78f..55c56b40c9 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/api.go
@@ -61,8 +61,19 @@ const (
// For non-archive nodes, this limit _will_ be overblown, as disk-backed tries
// will only be found every ~15K blocks or so.
defaultTracechainMemLimit = common.StorageSize(500 * 1024 * 1024)
+
+ // maximumPendingTraceStates is the maximum number of states allowed waiting
+ // for tracing. The creation of trace state will be paused if the unused
+ // trace states exceed this limit.
+ maximumPendingTraceStates = 128
)
+var errTxNotFound = errors.New("transaction not found")
+
+// StateReleaseFunc is used to deallocate resources held by constructing a
+// historical state for tracing purposes.
+type StateReleaseFunc func()
+
// Backend interface provides the common API services (that are provided by
// both full and light clients) with access to necessary functions.
type Backend interface {
@@ -75,11 +86,8 @@ type Backend interface {
ChainConfig() *params.ChainConfig
Engine() consensus.Engine
ChainDb() ethdb.Database
- // StateAtBlock returns the state corresponding to the stateroot of the block.
- // N.B: For executing transactions on block N, the required stateRoot is block N-1,
- // so this method should be called with the parent.
- StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive, preferDisk bool) (*state.StateDB, error)
- StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error)
+ StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error)
+ StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, StateReleaseFunc, error)
}
// API is the collection of tracing APIs exposed over the private debugging endpoint.
@@ -201,7 +209,7 @@ type txTraceResult struct {
type blockTraceTask struct {
statedb *state.StateDB // Intermediate state prepped for tracing
block *types.Block // Block to trace the transactions from
- rootref common.Hash // Trie root reference held for this task
+ release StateReleaseFunc // The function to release the held resource for this task
results []*txTraceResult // Trace results produced by the task
}
@@ -234,13 +242,6 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf
if from.Number().Cmp(to.Number()) >= 0 {
return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start)
}
- return api.traceChain(ctx, from, to, config)
-}
-
-// traceChain configures a new tracer according to the provided configuration, and
-// executes all the transactions contained within. The return value will be one item
-// per transaction, dependent on the requested tracer.
-func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) {
// Tracing a chain is a **long** operation, only do with subscriptions
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
@@ -248,8 +249,21 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
}
sub := notifier.CreateSubscription()
- // Prepare all the states for tracing. Note this procedure can take very
- // long time. Timeout mechanism is necessary.
+ resCh := api.traceChain(from, to, config, notifier.Closed())
+ go func() {
+ for result := range resCh {
+ notifier.Notify(sub.ID, result)
+ }
+ }()
+ return sub, nil
+}
+
+// traceChain configures a new tracer according to the provided configuration, and
+// executes all the transactions contained within. The tracing chain range includes
+// the end block but excludes the start one. The return value will be one item per
+// transaction, dependent on the requested tracer.
+// The tracing procedure should be aborted in case the closed signal is received.
+func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan interface{}) chan *blockTraceResult {
reexec := defaultTraceReexec
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
@@ -260,20 +274,23 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
threads = blocks
}
var (
- pend = new(sync.WaitGroup)
- tasks = make(chan *blockTraceTask, threads)
- results = make(chan *blockTraceTask, threads)
- localctx = context.Background()
+ pend = new(sync.WaitGroup)
+ ctx = context.Background()
+ taskCh = make(chan *blockTraceTask, threads)
+ resCh = make(chan *blockTraceTask, threads)
+ tracker = newStateTracker(maximumPendingTraceStates, start.NumberU64())
)
for th := 0; th < threads; th++ {
pend.Add(1)
go func() {
defer pend.Done()
- // Fetch and execute the next block trace tasks
- for task := range tasks {
- signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number())
- blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil)
+ // Fetch and execute the block trace taskCh
+ for task := range taskCh {
+ var (
+ signer = types.MakeSigner(api.backend.ChainConfig(), task.block.Number())
+ blockCtx = core.NewEVMBlockContext(task.block.Header(), api.chainContext(ctx), nil)
+ )
// Trace all the transactions contained within
for i, tx := range task.block.Transactions() {
msg, _ := tx.AsMessage(signer, task.block.BaseFee())
@@ -282,7 +299,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
TxIndex: i,
TxHash: tx.Hash(),
}
- res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
+ res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
if err != nil {
task.results[i] = &txTraceResult{Error: err.Error()}
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
@@ -292,36 +309,40 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number()))
task.results[i] = &txTraceResult{Result: res}
}
- // Stream the result back to the user or abort on teardown
+ // Tracing state is used up, queue it for de-referencing. Note the
+ // state is the parent state of trace block, use block.number-1 as
+ // the state number.
+ tracker.releaseState(task.block.NumberU64()-1, task.release)
+
+ // Stream the result back to the result catcher or abort on teardown
select {
- case results <- task:
- case <-notifier.Closed():
+ case resCh <- task:
+ case <-closed:
return
}
}
}()
}
// Start a goroutine to feed all the blocks into the tracers
- var (
- begin = time.Now()
- derefTodo []common.Hash // list of hashes to dereference from the db
- derefsMu sync.Mutex // mutex for the derefs
- )
-
go func() {
var (
logged time.Time
+ begin = time.Now()
number uint64
traced uint64
failed error
- parent common.Hash
statedb *state.StateDB
+ release StateReleaseFunc
)
// Ensure everything is properly cleaned up on any exit path
defer func() {
- close(tasks)
+ close(taskCh)
pend.Wait()
+ // Clean out any pending release functions of trace states.
+ tracker.callReleases()
+
+ // Log the chain result
switch {
case failed != nil:
log.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed)
@@ -330,105 +351,104 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
default:
log.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin))
}
- close(results)
+ close(resCh)
}()
- var preferDisk bool
// Feed all the blocks both into the tracer, as well as fast process concurrently
for number = start.NumberU64(); number < end.NumberU64(); number++ {
// Stop tracing if interruption was requested
select {
- case <-notifier.Closed():
+ case <-closed:
return
default:
}
- // clean out any derefs
- derefsMu.Lock()
- for _, h := range derefTodo {
- statedb.Database().TrieDB().Dereference(h)
- }
- derefTodo = derefTodo[:0]
- derefsMu.Unlock()
-
// Print progress logs if long enough time elapsed
if time.Since(logged) > 8*time.Second {
logged = time.Now()
log.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin))
}
- // Retrieve the parent state to trace on top
- block, err := api.blockByNumber(localctx, rpc.BlockNumber(number))
+ // Retrieve the parent block and target block for tracing.
+ block, err := api.blockByNumber(ctx, rpc.BlockNumber(number))
if err != nil {
failed = err
break
}
- // Prepare the statedb for tracing. Don't use the live database for
- // tracing to avoid persisting state junks into the database.
- statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false, preferDisk)
+ next, err := api.blockByNumber(ctx, rpc.BlockNumber(number+1))
if err != nil {
failed = err
break
}
- if trieDb := statedb.Database().TrieDB(); trieDb != nil {
- // Hold the reference for tracer, will be released at the final stage
- trieDb.Reference(block.Root(), common.Hash{})
-
- // Release the parent state because it's already held by the tracer
- if parent != (common.Hash{}) {
- trieDb.Dereference(parent)
- }
- // Prefer disk if the trie db memory grows too much
- s1, s2 := trieDb.Size()
- if !preferDisk && (s1+s2) > defaultTracechainMemLimit {
- log.Info("Switching to prefer-disk mode for tracing", "size", s1+s2)
- preferDisk = true
- }
+ // Make sure the state creator doesn't go too far. Too many unprocessed
+ // trace state may cause the oldest state to become stale(e.g. in
+ // path-based scheme).
+ if err = tracker.wait(number); err != nil {
+ failed = err
+ break
}
- parent = block.Root()
-
- next, err := api.blockByNumber(localctx, rpc.BlockNumber(number+1))
+ // Prepare the statedb for tracing. Don't use the live database for
+ // tracing to avoid persisting state junks into the database. Switch
+ // over to `preferDisk` mode only if the memory usage exceeds the
+ // limit, the trie database will be reconstructed from scratch only
+ // if the relevant state is available in disk.
+ var preferDisk bool
+ if statedb != nil {
+ s1, s2 := statedb.Database().TrieDB().Size()
+ preferDisk = s1+s2 > defaultTracechainMemLimit
+ }
+ statedb, release, err = api.backend.StateAtBlock(ctx, block, reexec, statedb, false, preferDisk)
if err != nil {
failed = err
break
}
+ // Clean out any pending release functions of trace state. Note this
+ // step must be done after constructing tracing state, because the
+ // tracing state of block next depends on the parent state and construction
+ // may fail if we release too early.
+ tracker.callReleases()
+
// Send the block over to the concurrent tracers (if not in the fast-forward phase)
txs := next.Transactions()
select {
- case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}:
- case <-notifier.Closed():
+ case taskCh <- &blockTraceTask{statedb: statedb.Copy(), block: next, release: release, results: make([]*txTraceResult, len(txs))}:
+ case <-closed:
+ tracker.releaseState(number, release)
return
}
traced += uint64(len(txs))
}
}()
- // Keep reading the trace results and stream the to the user
+ // Keep reading the trace results and stream them to result channel.
+ retCh := make(chan *blockTraceResult)
go func() {
+ defer close(retCh)
var (
- done = make(map[uint64]*blockTraceResult)
next = start.NumberU64() + 1
+ done = make(map[uint64]*blockTraceResult)
)
- for res := range results {
+ for res := range resCh {
// Queue up next received result
result := &blockTraceResult{
Block: hexutil.Uint64(res.block.NumberU64()),
Hash: res.block.Hash(),
Traces: res.results,
}
- // Schedule any parent tries held in memory by this task for dereferencing
done[uint64(result.Block)] = result
- derefsMu.Lock()
- derefTodo = append(derefTodo, res.rootref)
- derefsMu.Unlock()
- // Stream completed traces to the user, aborting on the first error
+
+ // Stream completed traces to the result channel
for result, ok := done[next]; ok; result, ok = done[next] {
if len(result.Traces) > 0 || next == end.NumberU64() {
- notifier.Notify(sub.ID, result)
+ // It will be blocked in case the channel consumer doesn't take the
+ // tracing result in time(e.g. the websocket connect is not stable)
+ // which will eventually block the entire chain tracer. It's the
+ // expected behavior to not waste node resources for a non-active user.
+ retCh <- result
}
delete(done, next)
next++
}
}
}()
- return sub, nil
+ return retCh
}
// TraceBlockByNumber returns the structured logs created during the execution of
@@ -515,10 +535,12 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
- statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
+ statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
if err != nil {
return nil, err
}
+ defer release()
+
var (
roots []common.Hash
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
@@ -527,12 +549,15 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
)
for i, tx := range block.Transactions() {
+ if err := ctx.Err(); err != nil {
+ return nil, err
+ }
var (
msg, _ = tx.AsMessage(signer, block.BaseFee())
txContext = core.NewEVMTxContext(msg)
vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
)
- statedb.Prepare(tx.Hash(), i)
+ statedb.SetTxContext(tx.Hash(), i)
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
// We intentionally don't return the error here: if we do, then the RPC server will not
@@ -568,6 +593,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
if block.NumberU64() == 0 {
return nil, errors.New("genesis is not traceable")
}
+ // Prepare base state
parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
if err != nil {
return nil, err
@@ -576,28 +602,70 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
- statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
+ statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
if err != nil {
return nil, err
}
- // Execute all the transaction contained within the block concurrently
+ defer release()
+
+ // JS tracers have high overhead. In this case run a parallel
+ // process that generates states in one thread and traces txes
+ // in separate worker threads.
+ if config != nil && config.Tracer != nil && *config.Tracer != "" {
+ if isJS := DefaultDirectory.IsJS(*config.Tracer); isJS {
+ return api.traceBlockParallel(ctx, block, statedb, config)
+ }
+ }
+ // Native tracers have low overhead
var (
- signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
- txs = block.Transactions()
- results = make([]*txTraceResult, len(txs))
+ txs = block.Transactions()
+ blockHash = block.Hash()
+ is158 = api.backend.ChainConfig().IsEIP158(block.Number())
+ blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
+ signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
+ results = make([]*txTraceResult, len(txs))
+ )
+ for i, tx := range txs {
+ // Generate the next state snapshot fast without tracing
+ msg, _ := tx.AsMessage(signer, block.BaseFee())
+ txctx := &Context{
+ BlockHash: blockHash,
+ TxIndex: i,
+ TxHash: tx.Hash(),
+ }
+ res, err := api.traceTx(ctx, msg, txctx, blockCtx, statedb, config)
+ if err != nil {
+ return nil, err
+ }
+ results[i] = &txTraceResult{Result: res}
+ // Finalize the state so any modifications are written to the trie
+ // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
+ statedb.Finalise(is158)
+ }
+ return results, nil
+}
- pend = new(sync.WaitGroup)
- jobs = make(chan *txTraceTask, len(txs))
+// traceBlockParallel is for tracers that have a high overhead (read JS tracers). One thread
+// runs along and executes txes without tracing enabled to generate their prestate.
+// Worker threads take the tasks and the prestate and trace them.
+func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, statedb *state.StateDB, config *TraceConfig) ([]*txTraceResult, error) {
+ // Execute all the transaction contained within the block concurrently
+ var (
+ txs = block.Transactions()
+ blockHash = block.Hash()
+ blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
+ signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
+ results = make([]*txTraceResult, len(txs))
+ pend sync.WaitGroup
)
threads := runtime.NumCPU()
if threads > len(txs) {
threads = len(txs)
}
- blockHash := block.Hash()
+ jobs := make(chan *txTraceTask, threads)
for th := 0; th < threads; th++ {
pend.Add(1)
go func() {
- blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
defer pend.Done()
// Fetch and execute the next transaction trace tasks
for task := range jobs {
@@ -616,25 +684,33 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
}
}()
}
+
// Feed the transactions into the tracers and return
var failed error
- blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
+txloop:
for i, tx := range txs {
// Send the trace task over for execution
- jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
+ task := &txTraceTask{statedb: statedb.Copy(), index: i}
+ select {
+ case <-ctx.Done():
+ failed = ctx.Err()
+ break txloop
+ case jobs <- task:
+ }
// Generate the next state snapshot fast without tracing
msg, _ := tx.AsMessage(signer, block.BaseFee())
- statedb.Prepare(tx.Hash(), i)
+ statedb.SetTxContext(tx.Hash(), i)
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
failed = err
- break
+ break txloop
}
// Finalize the state so any modifications are written to the trie
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
}
+
close(jobs)
pend.Wait()
@@ -666,10 +742,12 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
- statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
+ statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
if err != nil {
return nil, err
}
+ defer release()
+
// Retrieve the tracing configurations, or use default values
var (
logConfig logger.Config
@@ -694,17 +772,9 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
// actual specified block, not any preceding blocks that we have to go through
// in order to obtain the state.
// Therefore, it's perfectly valid to specify `"futureForkBlock": 0`, to enable `futureFork`
-
if config != nil && config.Overrides != nil {
- // Copy the config, to not screw up the main config
- // Note: the Clique-part is _not_ deep copied
- chainConfigCopy := new(params.ChainConfig)
- *chainConfigCopy = *chainConfig
- chainConfig = chainConfigCopy
- if berlin := config.Config.Overrides.BerlinBlock; berlin != nil {
- chainConfig.BerlinBlock = berlin
- canon = false
- }
+ // Note: This copies the config, to not screw up the main config
+ chainConfig, canon = overrideConfig(chainConfig, config.Overrides)
}
for i, tx := range block.Transactions() {
// Prepare the transaction for un-traced execution
@@ -739,7 +809,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
}
// Execute the transaction and flush any traces to disk
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
- statedb.Prepare(tx.Hash(), i)
+ statedb.SetTxContext(tx.Hash(), i)
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
if writer != nil {
writer.Flush()
@@ -777,10 +847,14 @@ func containsTx(block *types.Block, hash common.Hash) bool {
// TraceTransaction returns the structured logs created during the execution of EVM
// and returns them as a JSON object.
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
- _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
+ tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
if err != nil {
return nil, err
}
+ // Only mined txes are supported
+ if tx == nil {
+ return nil, errTxNotFound
+ }
// It shouldn't happen in practice.
if blockNumber == 0 {
return nil, errors.New("genesis is not traceable")
@@ -793,10 +867,12 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
if err != nil {
return nil, err
}
- msg, vmctx, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
+ msg, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
if err != nil {
return nil, err
}
+ defer release()
+
txctx := &Context{
BlockHash: blockHash,
TxIndex: int(index),
@@ -837,10 +913,12 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
- statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false)
+ statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false)
if err != nil {
return nil, err
}
+ defer release()
+
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
// Apply the customization rules if required.
if config != nil {
@@ -857,12 +935,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
var traceConfig *TraceConfig
if config != nil {
- traceConfig = &TraceConfig{
- Config: config.Config,
- Tracer: config.Tracer,
- Timeout: config.Timeout,
- Reexec: config.Reexec,
- }
+ traceConfig = &config.TraceConfig
}
return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
}
@@ -883,11 +956,13 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
// Default tracer is the struct logger
tracer = logger.NewStructLogger(config.Config)
if config.Tracer != nil {
- tracer, err = New(*config.Tracer, txctx, config.TracerConfig)
+ tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
if err != nil {
return nil, err
}
}
+ vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
+
// Define a meaningful timeout of a single transaction trace
if config.Timeout != nil {
if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
@@ -899,14 +974,14 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
<-deadlineCtx.Done()
if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
tracer.Stop(errors.New("execution timeout"))
+ // Stop evm execution. Note cancellation is not necessarily immediate.
+ vmenv.Cancel()
}
}()
defer cancel()
- // Run the transaction with tracing enabled.
- vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
// Call Prepare to clear out the statedb access list
- statedb.Prepare(txctx.TxHash, txctx.TxIndex)
+ statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())); err != nil {
return nil, fmt.Errorf("tracing failed: %w", err)
}
@@ -923,3 +998,48 @@ func APIs(backend Backend) []rpc.API {
},
}
}
+
+// overrideConfig returns a copy of original with forks enabled by override enabled,
+// along with a boolean that indicates whether the copy is canonical (equivalent to the original).
+// Note: the Clique-part is _not_ deep copied
+func overrideConfig(original *params.ChainConfig, override *params.ChainConfig) (*params.ChainConfig, bool) {
+ copy := new(params.ChainConfig)
+ *copy = *original
+ canon := true
+
+ // Apply forks (after Berlin) to the copy.
+ if block := override.BerlinBlock; block != nil {
+ copy.BerlinBlock = block
+ canon = false
+ }
+ if block := override.LondonBlock; block != nil {
+ copy.LondonBlock = block
+ canon = false
+ }
+ if block := override.ArrowGlacierBlock; block != nil {
+ copy.ArrowGlacierBlock = block
+ canon = false
+ }
+ if block := override.GrayGlacierBlock; block != nil {
+ copy.GrayGlacierBlock = block
+ canon = false
+ }
+ if block := override.MergeNetsplitBlock; block != nil {
+ copy.MergeNetsplitBlock = block
+ canon = false
+ }
+ if timestamp := override.ShanghaiTime; timestamp != nil {
+ copy.ShanghaiTime = timestamp
+ canon = false
+ }
+ if timestamp := override.CancunTime; timestamp != nil {
+ copy.CancunTime = timestamp
+ canon = false
+ }
+ if timestamp := override.PragueTime; timestamp != nil {
+ copy.PragueTime = timestamp
+ canon = false
+ }
+
+ return copy, canon
+}
diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go
index bc12b92751..29ec808685 100644
--- a/eth/tracers/api_test.go
+++ b/eth/tracers/api_test.go
@@ -26,6 +26,7 @@ import (
"math/big"
"reflect"
"sort"
+ "sync/atomic"
"testing"
"time"
@@ -47,9 +48,8 @@ import (
)
var (
- errStateNotFound = errors.New("state not found")
- errBlockNotFound = errors.New("block not found")
- errTransactionNotFound = errors.New("transaction not found")
+ errStateNotFound = errors.New("state not found")
+ errBlockNotFound = errors.New("block not found")
)
type testBackend struct {
@@ -57,24 +57,23 @@ type testBackend struct {
engine consensus.Engine
chaindb ethdb.Database
chain *core.BlockChain
+
+ refHook func() // Hook is invoked when the requested state is referenced
+ relHook func() // Hook is invoked when the requested state is released
}
+// testBackend creates a new test backend. OBS: After test is done, teardown must be
+// invoked in order to release associated resources.
func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend {
backend := &testBackend{
- chainConfig: params.TestChainConfig,
+ chainConfig: gspec.Config,
engine: ethash.NewFaker(),
chaindb: rawdb.NewMemoryDatabase(),
}
// Generate blocks for testing
- gspec.Config = backend.chainConfig
- var (
- gendb = rawdb.NewMemoryDatabase()
- genesis = gspec.MustCommit(gendb)
- )
- blocks, _ := core.GenerateChain(backend.chainConfig, genesis, backend.engine, gendb, n, generator)
+ _, blocks, _ := core.GenerateChainWithGenesis(gspec, backend.engine, n, generator)
// Import the canonical chain
- gspec.MustCommit(backend.chaindb)
cacheConfig := &core.CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
@@ -82,7 +81,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i i
SnapshotLimit: 0,
TrieDirtyDisabled: true, // Archive mode
}
- chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, backend.chainConfig, backend.engine, vm.Config{}, nil, nil)
+ chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, gspec, nil, backend.engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
@@ -117,9 +116,6 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash)
- if tx == nil {
- return nil, common.Hash{}, 0, 0, errTransactionNotFound
- }
return tx, hash, blockNumber, index, nil
}
@@ -139,25 +135,38 @@ func (b *testBackend) ChainDb() ethdb.Database {
return b.chaindb
}
-func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (*state.StateDB, error) {
+// teardown releases the associated resources.
+func (b *testBackend) teardown() {
+ b.chain.Stop()
+}
+
+func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error) {
statedb, err := b.chain.StateAt(block.Root())
if err != nil {
- return nil, errStateNotFound
+ return nil, nil, errStateNotFound
+ }
+ if b.refHook != nil {
+ b.refHook()
}
- return statedb, nil
+ release := func() {
+ if b.relHook != nil {
+ b.relHook()
+ }
+ }
+ return statedb, release, nil
}
-func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
+func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) {
parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
- return nil, vm.BlockContext{}, nil, errBlockNotFound
+ return nil, vm.BlockContext{}, nil, nil, errBlockNotFound
}
- statedb, err := b.chain.StateAt(parent.Root())
+ statedb, release, err := b.StateAtBlock(ctx, parent, reexec, nil, true, false)
if err != nil {
- return nil, vm.BlockContext{}, nil, errStateNotFound
+ return nil, vm.BlockContext{}, nil, nil, errStateNotFound
}
if txIndex == 0 && len(block.Transactions()) == 0 {
- return nil, vm.BlockContext{}, statedb, nil
+ return nil, vm.BlockContext{}, statedb, release, nil
}
// Recompute transactions up to the target index.
signer := types.MakeSigner(b.chainConfig, block.Number())
@@ -166,15 +175,15 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), b.chain, nil)
if idx == txIndex {
- return msg, context, statedb, nil
+ return msg, context, statedb, release, nil
}
vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
- return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
+ return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
}
- return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
+ return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
}
func TestTraceCall(t *testing.T) {
@@ -182,20 +191,25 @@ func TestTraceCall(t *testing.T) {
// Initialize test accounts
accounts := newAccounts(3)
- genesis := &core.Genesis{Alloc: core.GenesisAlloc{
- accounts[0].addr: {Balance: big.NewInt(params.Ether)},
- accounts[1].addr: {Balance: big.NewInt(params.Ether)},
- accounts[2].addr: {Balance: big.NewInt(params.Ether)},
- }}
+ genesis := &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: core.GenesisAlloc{
+ accounts[0].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[1].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[2].addr: {Balance: big.NewInt(params.Ether)},
+ },
+ }
genBlocks := 10
signer := types.HomesteadSigner{}
- api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
+ backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
b.AddTx(tx)
- }))
+ })
+ defer backend.teardown()
+ api := NewAPI(backend)
var testSuite = []struct {
blockNumber rpc.BlockNumber
call ethapi.TransactionArgs
@@ -312,20 +326,25 @@ func TestTraceTransaction(t *testing.T) {
// Initialize test accounts
accounts := newAccounts(2)
- genesis := &core.Genesis{Alloc: core.GenesisAlloc{
- accounts[0].addr: {Balance: big.NewInt(params.Ether)},
- accounts[1].addr: {Balance: big.NewInt(params.Ether)},
- }}
+ genesis := &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: core.GenesisAlloc{
+ accounts[0].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[1].addr: {Balance: big.NewInt(params.Ether)},
+ },
+ }
target := common.Hash{}
signer := types.HomesteadSigner{}
- api := NewAPI(newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
+ backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
b.AddTx(tx)
target = tx.Hash()
- }))
+ })
+ defer backend.chain.Stop()
+ api := NewAPI(backend)
result, err := api.TraceTransaction(context.Background(), target, nil)
if err != nil {
t.Errorf("Failed to trace transaction %v", err)
@@ -342,6 +361,12 @@ func TestTraceTransaction(t *testing.T) {
}) {
t.Error("Transaction tracing result is different")
}
+
+ // Test non-existent transaction
+ _, err = api.TraceTransaction(context.Background(), common.Hash{42}, nil)
+ if !errors.Is(err, errTxNotFound) {
+ t.Fatalf("want %v, have %v", errTxNotFound, err)
+ }
}
func TestTraceBlock(t *testing.T) {
@@ -349,20 +374,25 @@ func TestTraceBlock(t *testing.T) {
// Initialize test accounts
accounts := newAccounts(3)
- genesis := &core.Genesis{Alloc: core.GenesisAlloc{
- accounts[0].addr: {Balance: big.NewInt(params.Ether)},
- accounts[1].addr: {Balance: big.NewInt(params.Ether)},
- accounts[2].addr: {Balance: big.NewInt(params.Ether)},
- }}
+ genesis := &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: core.GenesisAlloc{
+ accounts[0].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[1].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[2].addr: {Balance: big.NewInt(params.Ether)},
+ },
+ }
genBlocks := 10
signer := types.HomesteadSigner{}
- api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
+ backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
b.AddTx(tx)
- }))
+ })
+ defer backend.chain.Stop()
+ api := NewAPI(backend)
var testSuite = []struct {
blockNumber rpc.BlockNumber
@@ -424,20 +454,34 @@ func TestTracingWithOverrides(t *testing.T) {
t.Parallel()
// Initialize test accounts
accounts := newAccounts(3)
- genesis := &core.Genesis{Alloc: core.GenesisAlloc{
- accounts[0].addr: {Balance: big.NewInt(params.Ether)},
- accounts[1].addr: {Balance: big.NewInt(params.Ether)},
- accounts[2].addr: {Balance: big.NewInt(params.Ether)},
- }}
+ storageAccount := common.Address{0x13, 37}
+ genesis := &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: core.GenesisAlloc{
+ accounts[0].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[1].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[2].addr: {Balance: big.NewInt(params.Ether)},
+ // An account with existing storage
+ storageAccount: {
+ Balance: new(big.Int),
+ Storage: map[common.Hash]common.Hash{
+ common.HexToHash("0x03"): common.HexToHash("0x33"),
+ common.HexToHash("0x04"): common.HexToHash("0x44"),
+ },
+ },
+ },
+ }
genBlocks := 10
signer := types.HomesteadSigner{}
- api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
+ backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
b.AddTx(tx)
- }))
+ })
+ defer backend.chain.Stop()
+ api := NewAPI(backend)
randomAccounts := newAccounts(3)
type res struct {
Gas int
@@ -544,6 +588,164 @@ func TestTracingWithOverrides(t *testing.T) {
},
want: `{"gas":72666,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`,
},
+ /*
+ pragma solidity =0.8.12;
+
+ contract Test {
+ uint private x;
+
+ function test2() external {
+ x = 1337;
+ revert();
+ }
+
+ function test() external returns (uint) {
+ x = 1;
+ try this.test2() {} catch (bytes memory) {}
+ return x;
+ }
+ }
+ */
+ { // First with only code override, not storage override
+ blockNumber: rpc.LatestBlockNumber,
+ call: ethapi.TransactionArgs{
+ From: &randomAccounts[0].addr,
+ To: &randomAccounts[2].addr,
+ Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
+ },
+ config: &TraceCallConfig{
+ StateOverrides: ðapi.StateOverride{
+ randomAccounts[2].addr: ethapi.OverrideAccount{
+ Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
+ },
+ },
+ },
+ want: `{"gas":44100,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000001"}`,
+ },
+ { // Same again, this time with storage override
+ blockNumber: rpc.LatestBlockNumber,
+ call: ethapi.TransactionArgs{
+ From: &randomAccounts[0].addr,
+ To: &randomAccounts[2].addr,
+ Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
+ },
+ config: &TraceCallConfig{
+ StateOverrides: ðapi.StateOverride{
+ randomAccounts[2].addr: ethapi.OverrideAccount{
+ Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
+ State: newStates([]common.Hash{{}}, []common.Hash{{}}),
+ },
+ },
+ },
+ //want: `{"gas":46900,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000539"}`,
+ want: `{"gas":44100,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000001"}`,
+ },
+ { // No state override
+ blockNumber: rpc.LatestBlockNumber,
+ call: ethapi.TransactionArgs{
+ From: &randomAccounts[0].addr,
+ To: &storageAccount,
+ Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
+ },
+ config: &TraceCallConfig{
+ StateOverrides: ðapi.StateOverride{
+ storageAccount: ethapi.OverrideAccount{
+ Code: newRPCBytes([]byte{
+ // SLOAD(3) + SLOAD(4) (which is 0x77)
+ byte(vm.PUSH1), 0x04,
+ byte(vm.SLOAD),
+ byte(vm.PUSH1), 0x03,
+ byte(vm.SLOAD),
+ byte(vm.ADD),
+ // 0x77 -> MSTORE(0)
+ byte(vm.PUSH1), 0x00,
+ byte(vm.MSTORE),
+ // RETURN (0, 32)
+ byte(vm.PUSH1), 32,
+ byte(vm.PUSH1), 00,
+ byte(vm.RETURN),
+ }),
+ },
+ },
+ },
+ want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000077"}`,
+ },
+ { // Full state override
+ // The original storage is
+ // 3: 0x33
+ // 4: 0x44
+ // With a full override, where we set 3:0x11, the slot 4 should be
+ // removed. So SLOT(3)+SLOT(4) should be 0x11.
+ blockNumber: rpc.LatestBlockNumber,
+ call: ethapi.TransactionArgs{
+ From: &randomAccounts[0].addr,
+ To: &storageAccount,
+ Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
+ },
+ config: &TraceCallConfig{
+ StateOverrides: ðapi.StateOverride{
+ storageAccount: ethapi.OverrideAccount{
+ Code: newRPCBytes([]byte{
+ // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x00)
+ byte(vm.PUSH1), 0x04,
+ byte(vm.SLOAD),
+ byte(vm.PUSH1), 0x03,
+ byte(vm.SLOAD),
+ byte(vm.ADD),
+ // 0x11 -> MSTORE(0)
+ byte(vm.PUSH1), 0x00,
+ byte(vm.MSTORE),
+ // RETURN (0, 32)
+ byte(vm.PUSH1), 32,
+ byte(vm.PUSH1), 00,
+ byte(vm.RETURN),
+ }),
+ State: newStates(
+ []common.Hash{common.HexToHash("0x03")},
+ []common.Hash{common.HexToHash("0x11")}),
+ },
+ },
+ },
+ want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000011"}`,
+ },
+ { // Partial state override
+ // The original storage is
+ // 3: 0x33
+ // 4: 0x44
+ // With a partial override, where we set 3:0x11, the slot 4 as before.
+ // So SLOT(3)+SLOT(4) should be 0x55.
+ blockNumber: rpc.LatestBlockNumber,
+ call: ethapi.TransactionArgs{
+ From: &randomAccounts[0].addr,
+ To: &storageAccount,
+ Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
+ },
+ config: &TraceCallConfig{
+ StateOverrides: ðapi.StateOverride{
+ storageAccount: ethapi.OverrideAccount{
+ Code: newRPCBytes([]byte{
+ // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x44)
+ byte(vm.PUSH1), 0x04,
+ byte(vm.SLOAD),
+ byte(vm.PUSH1), 0x03,
+ byte(vm.SLOAD),
+ byte(vm.ADD),
+ // 0x55 -> MSTORE(0)
+ byte(vm.PUSH1), 0x00,
+ byte(vm.MSTORE),
+ // RETURN (0, 32)
+ byte(vm.PUSH1), 32,
+ byte(vm.PUSH1), 00,
+ byte(vm.RETURN),
+ }),
+ StateDiff: &map[common.Hash]common.Hash{
+ common.HexToHash("0x03"): common.HexToHash("0x11"),
+ },
+ },
+ },
+ },
+ want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000055"}`,
+ },
}
for i, tc := range testSuite {
result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config)
@@ -570,7 +772,8 @@ func TestTracingWithOverrides(t *testing.T) {
json.Unmarshal(resBytes, &have)
json.Unmarshal([]byte(tc.want), &want)
if !reflect.DeepEqual(have, want) {
- t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(resBytes), want)
+ t.Logf("result: %v\n", string(resBytes))
+ t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, have, want)
}
}
}
@@ -616,3 +819,77 @@ func newStates(keys []common.Hash, vals []common.Hash) *map[common.Hash]common.H
}
return &m
}
+
+func TestTraceChain(t *testing.T) {
+ // Initialize test accounts
+ accounts := newAccounts(3)
+ genesis := &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: core.GenesisAlloc{
+ accounts[0].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[1].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[2].addr: {Balance: big.NewInt(params.Ether)},
+ },
+ }
+ genBlocks := 50
+ signer := types.HomesteadSigner{}
+
+ var (
+ ref uint32 // total refs has made
+ rel uint32 // total rels has made
+ nonce uint64
+ )
+ backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
+ // Transfer from account[0] to account[1]
+ // value: 1000 wei
+ // fee: 0 wei
+ for j := 0; j < i+1; j++ {
+ tx, _ := types.SignTx(types.NewTransaction(nonce, accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
+ b.AddTx(tx)
+ nonce += 1
+ }
+ })
+ backend.refHook = func() { atomic.AddUint32(&ref, 1) }
+ backend.relHook = func() { atomic.AddUint32(&rel, 1) }
+ api := NewAPI(backend)
+
+ single := `{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}`
+ var cases = []struct {
+ start uint64
+ end uint64
+ config *TraceConfig
+ }{
+ {0, 50, nil}, // the entire chain range, blocks [1, 50]
+ {10, 20, nil}, // the middle chain range, blocks [11, 20]
+ }
+ for _, c := range cases {
+ ref, rel = 0, 0 // clean up the counters
+
+ from, _ := api.blockByNumber(context.Background(), rpc.BlockNumber(c.start))
+ to, _ := api.blockByNumber(context.Background(), rpc.BlockNumber(c.end))
+ resCh := api.traceChain(from, to, c.config, nil)
+
+ next := c.start + 1
+ for result := range resCh {
+ if next != uint64(result.Block) {
+ t.Error("Unexpected tracing block")
+ }
+ if len(result.Traces) != int(next) {
+ t.Error("Unexpected tracing result")
+ }
+ for _, trace := range result.Traces {
+ blob, _ := json.Marshal(trace)
+ if string(blob) != single {
+ t.Error("Unexpected tracing result")
+ }
+ }
+ next += 1
+ }
+ if next != c.end+1 {
+ t.Error("Missing tracing block")
+ }
+ if ref != rel {
+ t.Errorf("Ref and deref actions are not equal, ref %d rel %d", ref, rel)
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go
index d2c50656d9..808841ade1 100644
--- a/eth/tracers/internal/tracetest/calltrace_test.go
+++ b/eth/tracers/internal/tracetest/calltrace_test.go
@@ -21,10 +21,8 @@ import (
"math/big"
"os"
"path/filepath"
- "reflect"
"strings"
"testing"
- "unicode"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -38,62 +36,8 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests"
-
- // Force-load native and js packages, to trigger registration
- _ "github.com/ethereum/go-ethereum/eth/tracers/js"
- _ "github.com/ethereum/go-ethereum/eth/tracers/native"
)
-// To generate a new callTracer test, copy paste the makeTest method below into
-// a Geth console and call it with a transaction hash you which to export.
-
-/*
-// makeTest generates a callTracer test by running a prestate reassembled and a
-// call trace run, assembling all the gathered information into a test case.
-var makeTest = function(tx, rewind) {
- // Generate the genesis block from the block, transaction and prestate data
- var block = eth.getBlock(eth.getTransaction(tx).blockHash);
- var genesis = eth.getBlock(block.parentHash);
-
- delete genesis.gasUsed;
- delete genesis.logsBloom;
- delete genesis.parentHash;
- delete genesis.receiptsRoot;
- delete genesis.sha3Uncles;
- delete genesis.size;
- delete genesis.transactions;
- delete genesis.transactionsRoot;
- delete genesis.uncles;
-
- genesis.gasLimit = genesis.gasLimit.toString();
- genesis.number = genesis.number.toString();
- genesis.timestamp = genesis.timestamp.toString();
-
- genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind});
- for (var key in genesis.alloc) {
- genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString();
- }
- genesis.config = admin.nodeInfo.protocols.eth.config;
-
- // Generate the call trace and produce the test input
- var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind});
- delete result.time;
-
- console.log(JSON.stringify({
- genesis: genesis,
- context: {
- number: block.number.toString(),
- difficulty: block.difficulty,
- timestamp: block.timestamp.toString(),
- gasLimit: block.gasLimit.toString(),
- miner: block.miner,
- },
- input: eth.getRawTransaction(tx),
- result: result,
- }, null, 2));
-}
-*/
-
type callContext struct {
Number math.HexOrDecimal64 `json:"number"`
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
@@ -102,18 +46,28 @@ type callContext struct {
Miner common.Address `json:"miner"`
}
+// callLog is the result of LOG opCode
+type callLog struct {
+ Address common.Address `json:"address"`
+ Topics []common.Hash `json:"topics"`
+ Data hexutil.Bytes `json:"data"`
+}
+
// callTrace is the result of a callTracer run.
type callTrace struct {
- Type string `json:"type"`
- From common.Address `json:"from"`
- To common.Address `json:"to"`
- Input hexutil.Bytes `json:"input"`
- Output hexutil.Bytes `json:"output"`
- Gas *hexutil.Uint64 `json:"gas,omitempty"`
- GasUsed *hexutil.Uint64 `json:"gasUsed,omitempty"`
- Value *hexutil.Big `json:"value,omitempty"`
- Error string `json:"error,omitempty"`
- Calls []callTrace `json:"calls,omitempty"`
+ From common.Address `json:"from"`
+ Gas *hexutil.Uint64 `json:"gas"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed"`
+ To common.Address `json:"to,omitempty"`
+ Input hexutil.Bytes `json:"input"`
+ Output hexutil.Bytes `json:"output,omitempty"`
+ Error string `json:"error,omitempty"`
+ RevertReason string `json:"revertReason,omitempty"`
+ Calls []callTrace `json:"calls,omitempty"`
+ Logs []callLog `json:"logs,omitempty"`
+ Value *hexutil.Big `json:"value,omitempty"`
+ // Gencodec adds overridden fields at the end
+ Type string `json:"type"`
}
// callTracerTest defines a single test to check the call tracer against.
@@ -135,7 +89,12 @@ func TestCallTracerNative(t *testing.T) {
testCallTracer("callTracer", "call_tracer", t)
}
+func TestCallTracerNativeWithLog(t *testing.T) {
+ testCallTracer("callTracer", "call_tracer_withLog", t)
+}
+
func testCallTracer(tracerName string, dirPath string, t *testing.T) {
+ isLegacy := strings.HasSuffix(dirPath, "_legacy")
files, err := os.ReadDir(filepath.Join("testdata", dirPath))
if err != nil {
t.Fatalf("failed to retrieve tracer test suite: %v", err)
@@ -158,7 +117,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
} else if err := json.Unmarshal(blob, test); err != nil {
t.Fatalf("failed to parse testcase: %v", err)
}
- if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil {
+ if err := tx.UnmarshalBinary(common.FromHex(test.Input)); err != nil {
t.Fatalf("failed to parse testcase input: %v", err)
}
// Configure a blockchain with the given prestate
@@ -174,13 +133,14 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
Transfer: core.Transfer,
Coinbase: test.Context.Miner,
BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)),
- Time: new(big.Int).SetUint64(uint64(test.Context.Time)),
+ Time: uint64(test.Context.Time),
Difficulty: (*big.Int)(test.Context.Difficulty),
GasLimit: uint64(test.Context.GasLimit),
+ BaseFee: test.Genesis.BaseFee,
}
_, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
)
- tracer, err := tracers.New(tracerName, new(tracers.Context), test.TracerConfig)
+ tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
@@ -189,57 +149,46 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
- st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
- if _, err = st.TransitionDb(); err != nil {
+ vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
+ if err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
- // Retrieve the trace result and compare against the etalon
+ // Retrieve the trace result and compare against the expected.
res, err := tracer.GetResult()
if err != nil {
t.Fatalf("failed to retrieve trace result: %v", err)
}
- ret := new(callTrace)
- if err := json.Unmarshal(res, ret); err != nil {
- t.Fatalf("failed to unmarshal trace result: %v", err)
+ // The legacy javascript calltracer marshals json in js, which
+ // is not deterministic (as opposed to the golang json encoder).
+ if isLegacy {
+ // This is a tweak to make it deterministic. Can be removed when
+ // we remove the legacy tracer.
+ var x callTrace
+ json.Unmarshal(res, &x)
+ res, _ = json.Marshal(x)
}
-
- if !jsonEqual(ret, test.Result) {
- // uncomment this for easier debugging
- //have, _ := json.MarshalIndent(ret, "", " ")
- //want, _ := json.MarshalIndent(test.Result, "", " ")
- //t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", string(have), string(want))
- t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", ret, test.Result)
+ want, err := json.Marshal(test.Result)
+ if err != nil {
+ t.Fatalf("failed to marshal test: %v", err)
+ }
+ if string(want) != string(res) {
+ t.Fatalf("trace mismatch\n have: %v\n want: %v\n", string(res), string(want))
+ }
+ // Sanity check: compare top call's gas used against vm result
+ type simpleResult struct {
+ GasUsed hexutil.Uint64
+ }
+ var topCall simpleResult
+ if err := json.Unmarshal(res, &topCall); err != nil {
+ t.Fatalf("failed to unmarshal top calls gasUsed: %v", err)
+ }
+ if uint64(topCall.GasUsed) != vmRet.UsedGas {
+ t.Fatalf("top call has invalid gasUsed. have: %d want: %d", topCall.GasUsed, vmRet.UsedGas)
}
})
}
}
-// jsonEqual is similar to reflect.DeepEqual, but does a 'bounce' via json prior to
-// comparison
-func jsonEqual(x, y interface{}) bool {
- xTrace := new(callTrace)
- yTrace := new(callTrace)
- if xj, err := json.Marshal(x); err == nil {
- json.Unmarshal(xj, xTrace)
- } else {
- return false
- }
- if yj, err := json.Marshal(y); err == nil {
- json.Unmarshal(yj, yTrace)
- } else {
- return false
- }
- return reflect.DeepEqual(xTrace, yTrace)
-}
-
-// camel converts a snake cased input string into a camel cased output.
-func camel(str string) string {
- pieces := strings.Split(str, "_")
- for i := 1; i < len(pieces); i++ {
- pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
- }
- return strings.Join(pieces, "")
-}
func BenchmarkTracers(b *testing.B) {
files, err := os.ReadDir(filepath.Join("testdata", "call_tracer"))
if err != nil {
@@ -285,7 +234,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
Transfer: core.Transfer,
Coinbase: test.Context.Miner,
BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)),
- Time: new(big.Int).SetUint64(uint64(test.Context.Time)),
+ Time: uint64(test.Context.Time),
Difficulty: (*big.Int)(test.Context.Difficulty),
GasLimit: uint64(test.Context.GasLimit),
}
@@ -294,7 +243,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
- tracer, err := tracers.New(tracerName, new(tracers.Context), nil)
+ tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil)
if err != nil {
b.Fatalf("failed to create call tracer: %v", err)
}
@@ -339,7 +288,7 @@ func TestZeroValueToNotExitCall(t *testing.T) {
Transfer: core.Transfer,
Coinbase: common.Address{},
BlockNumber: new(big.Int).SetUint64(8000000),
- Time: new(big.Int).SetUint64(5),
+ Time: 5,
Difficulty: big.NewInt(0x30000),
GasLimit: uint64(6000000),
}
@@ -360,7 +309,7 @@ func TestZeroValueToNotExitCall(t *testing.T) {
}
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
// Create the tracer, the EVM environment and run it
- tracer, err := tracers.New("callTracer", nil, nil)
+ tracer, err := tracers.DefaultDirectory.New("callTracer", nil, nil)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
@@ -378,14 +327,8 @@ func TestZeroValueToNotExitCall(t *testing.T) {
if err != nil {
t.Fatalf("failed to retrieve trace result: %v", err)
}
- have := new(callTrace)
- if err := json.Unmarshal(res, have); err != nil {
- t.Fatalf("failed to unmarshal trace result: %v", err)
- }
- wantStr := `{"type":"CALL","from":"0x682a80a6f560eec50d54e63cbeda1c324c5f8d1b","to":"0x00000000000000000000000000000000deadbeef","value":"0x0","gas":"0x7148","gasUsed":"0x2d0","input":"0x","output":"0x","calls":[{"type":"CALL","from":"0x00000000000000000000000000000000deadbeef","to":"0x00000000000000000000000000000000000000ff","value":"0x0","gas":"0x6cbf","gasUsed":"0x0","input":"0x","output":"0x"}]}`
- want := new(callTrace)
- json.Unmarshal([]byte(wantStr), want)
- if !jsonEqual(have, want) {
- t.Error("have != want")
+ wantStr := `{"from":"0x682a80a6f560eec50d54e63cbeda1c324c5f8d1b","gas":"0x7148","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[{"from":"0x00000000000000000000000000000000deadbeef","gas":"0x6cbf","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"}`
+ if string(res) != wantStr {
+ t.Fatalf("trace mismatch\n have: %v\n want: %v\n", string(res), wantStr)
}
}
diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go
new file mode 100644
index 0000000000..03f06311a3
--- /dev/null
+++ b/eth/tracers/internal/tracetest/prestate_test.go
@@ -0,0 +1,149 @@
+// 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 tracetest
+
+import (
+ "encoding/json"
+ "math/big"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/tests"
+)
+
+// prestateTrace is the result of a prestateTrace run.
+type prestateTrace = map[common.Address]*account
+
+type account struct {
+ Balance string `json:"balance"`
+ Code string `json:"code"`
+ Nonce uint64 `json:"nonce"`
+ Storage map[common.Hash]common.Hash `json:"storage"`
+}
+
+// testcase defines a single test to check the stateDiff tracer against.
+type testcase struct {
+ Genesis *core.Genesis `json:"genesis"`
+ Context *callContext `json:"context"`
+ Input string `json:"input"`
+ TracerConfig json.RawMessage `json:"tracerConfig"`
+ Result interface{} `json:"result"`
+}
+
+func TestPrestateTracerLegacy(t *testing.T) {
+ testPrestateDiffTracer("prestateTracerLegacy", "prestate_tracer_legacy", t)
+}
+
+func TestPrestateTracer(t *testing.T) {
+ testPrestateDiffTracer("prestateTracer", "prestate_tracer", t)
+}
+
+func TestPrestateWithDiffModeTracer(t *testing.T) {
+ testPrestateDiffTracer("prestateTracer", "prestate_tracer_with_diff_mode", t)
+}
+
+func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
+ files, err := os.ReadDir(filepath.Join("testdata", dirPath))
+ if err != nil {
+ t.Fatalf("failed to retrieve tracer test suite: %v", err)
+ }
+ for _, file := range files {
+ if !strings.HasSuffix(file.Name(), ".json") {
+ continue
+ }
+ file := file // capture range variable
+ t.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(t *testing.T) {
+ t.Parallel()
+
+ var (
+ test = new(testcase)
+ tx = new(types.Transaction)
+ )
+ // Call tracer test found, read if from disk
+ if blob, err := os.ReadFile(filepath.Join("testdata", dirPath, file.Name())); err != nil {
+ t.Fatalf("failed to read testcase: %v", err)
+ } else if err := json.Unmarshal(blob, test); err != nil {
+ t.Fatalf("failed to parse testcase: %v", err)
+ }
+ if err := tx.UnmarshalBinary(common.FromHex(test.Input)); err != nil {
+ t.Fatalf("failed to parse testcase input: %v", err)
+ }
+ // Configure a blockchain with the given prestate
+ var (
+ signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
+ origin, _ = signer.Sender(tx)
+ txContext = vm.TxContext{
+ Origin: origin,
+ GasPrice: tx.GasPrice(),
+ }
+ context = vm.BlockContext{
+ CanTransfer: core.CanTransfer,
+ Transfer: core.Transfer,
+ Coinbase: test.Context.Miner,
+ BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)),
+ Time: uint64(test.Context.Time),
+ Difficulty: (*big.Int)(test.Context.Difficulty),
+ GasLimit: uint64(test.Context.GasLimit),
+ BaseFee: test.Genesis.BaseFee,
+ }
+ _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
+ )
+ tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
+ if err != nil {
+ t.Fatalf("failed to create call tracer: %v", err)
+ }
+ evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
+ msg, err := tx.AsMessage(signer, nil)
+ if err != nil {
+ t.Fatalf("failed to prepare transaction for tracing: %v", err)
+ }
+ st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
+ if _, err = st.TransitionDb(); err != nil {
+ t.Fatalf("failed to execute transaction: %v", err)
+ }
+ // Retrieve the trace result and compare against the expected
+ res, err := tracer.GetResult()
+ if err != nil {
+ t.Fatalf("failed to retrieve trace result: %v", err)
+ }
+ // The legacy javascript calltracer marshals json in js, which
+ // is not deterministic (as opposed to the golang json encoder).
+ if strings.HasSuffix(dirPath, "_legacy") {
+ // This is a tweak to make it deterministic. Can be removed when
+ // we remove the legacy tracer.
+ var x prestateTrace
+ json.Unmarshal(res, &x)
+ res, _ = json.Marshal(x)
+ }
+ want, err := json.Marshal(test.Result)
+ if err != nil {
+ t.Fatalf("failed to marshal test: %v", err)
+ }
+ if string(want) != string(res) {
+ t.Fatalf("trace mismatch\n have: %v\n want: %v\n", string(res), string(want))
+ }
+ })
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/create.json b/eth/tracers/internal/tracetest/testdata/call_tracer/create.json
index 8699bf3e7e..8557f8efd6 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/create.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/create.json
@@ -48,7 +48,7 @@
"result": {
"from": "0x13e4acefe6a6700604929946e70e6443e4e73447",
"gas": "0x5e106",
- "gasUsed": "0x5e106",
+ "gasUsed": "0x897be",
"input": "0x606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a11",
"output": "0x606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029",
"to": "0x7dc9c9730689ff0b0fd506c67db815f12d90a448",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/deep_calls.json b/eth/tracers/internal/tracetest/testdata/call_tracer/deep_calls.json
index 0353d4cfa9..174f23fc45 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/deep_calls.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/deep_calls.json
@@ -263,7 +263,6 @@
"gas": "0x20ee1",
"gasUsed": "0x5374",
"input": "0x581d5d60000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b0000000000000000000000000000000000000000000000280faf689c35ac0000",
- "output": "0x",
"to": "0xcf00ffd997ad14939736f026006498e3f099baaf",
"type": "CALL",
"value": "0x0"
@@ -305,7 +304,6 @@
"gas": "0x1a91d",
"gasUsed": "0x12fa",
"input": "0x0accce0600000000000000000000000000000000000000000000000000000000000000025842545553440000000000000000000000000000000000000000000000000000000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
- "output": "0x",
"to": "0x2a98c5f40bfa3dee83431103c535f6fae9a8ad38",
"type": "CALL",
"value": "0x0"
@@ -377,7 +375,6 @@
"gas": "0x16e62",
"gasUsed": "0xebb",
"input": "0x645a3b72584254555344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002816d180e30c390000",
- "output": "0x",
"to": "0x2a98c5f40bfa3dee83431103c535f6fae9a8ad38",
"type": "CALL",
"value": "0x0"
@@ -387,7 +384,6 @@
"gas": "0x283b9",
"gasUsed": "0xc51c",
"input": "0x949ae479000000000000000000000000c212e03b9e060e36facad5fd8f4435412ca22e6b0000000000000000000000000000000000000000000000280faf689c35ac0000",
- "output": "0x",
"to": "0x3e9286eafa2db8101246c2131c09b49080d00690",
"type": "CALL",
"value": "0x0"
@@ -397,7 +393,6 @@
"gas": "0x30b4a",
"gasUsed": "0xedb7",
"input": "0x51a34eb80000000000000000000000000000000000000000000000280faf689c35ac0000",
- "output": "0x",
"to": "0xb4fe7aa695b326c9d219158d2ca50db77b39f99f",
"type": "CALL",
"value": "0x0"
@@ -405,9 +400,8 @@
],
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
"gas": "0x37b38",
- "gasUsed": "0x12bb3",
+ "gasUsed": "0x1810b",
"input": "0x51a34eb80000000000000000000000000000000000000000000000280faf689c35ac0000",
- "output": "0x",
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
"type": "CALL",
"value": "0x0"
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/delegatecall.json b/eth/tracers/internal/tracetest/testdata/call_tracer/delegatecall.json
index f7ad6df5f5..5fd946f734 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/delegatecall.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/delegatecall.json
@@ -72,7 +72,8 @@
"input": "0x7d65837a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a529806c67cc6486d4d62024471772f47f6fd672",
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
"to": "0x42b02b5deeb78f34cd5ac896473b63e6c99a71a2",
- "type": "DELEGATECALL"
+ "type": "DELEGATECALL",
+ "value": "0x0"
}
],
"from": "0x269296dddce321a6bcbaa2f0181127593d732cba",
@@ -87,9 +88,8 @@
],
"from": "0xa529806c67cc6486d4d62024471772f47f6fd672",
"gas": "0x2d6e28",
- "gasUsed": "0x64bd",
+ "gasUsed": "0xbd55",
"input": "0x7065cb480000000000000000000000001523e55a1ca4efbae03355775ae89f8d7699ad9e",
- "output": "0x",
"to": "0x269296dddce321a6bcbaa2f0181127593d732cba",
"type": "CALL",
"value": "0x0"
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/inner_create_oog_outer_throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_create_oog_outer_throw.json
index 9395eb401c..40d240e4b8 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/inner_create_oog_outer_throw.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_create_oog_outer_throw.json
@@ -58,6 +58,7 @@
{
"error": "contract creation code storage out of gas",
"from": "0x1d3ddf7caf024f253487e18bc4a15b1a360c170a",
+ "to": "0x0000000000000000000000000000000000000000",
"gas": "0x39ff0",
"gasUsed": "0x39ff0",
"input": "0x606060405234620000005760405160208062001fd283398101604052515b805b600a8054600160a060020a031916600160a060020a0383161790555b506001600d819055600e81905560408051808201909152600c8082527f566f74696e672053746f636b00000000000000000000000000000000000000006020928301908152600b805460008290528251601860ff1990911617825590947f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9600291831615610100026000190190921604601f0193909304830192906200010c565b828001600101855582156200010c579182015b828111156200010c578251825591602001919060010190620000ef565b5b50620001309291505b808211156200012c576000815560010162000116565b5090565b50506040805180820190915260038082527f43565300000000000000000000000000000000000000000000000000000000006020928301908152600c805460008290528251600660ff1990911617825590937fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c760026001841615610100026000190190931692909204601f010481019291620001f7565b82800160010185558215620001f7579182015b82811115620001f7578251825591602001919060010190620001da565b5b506200021b9291505b808211156200012c576000815560010162000116565b5090565b50505b505b611da280620002306000396000f3006060604052361561019a5763ffffffff60e060020a600035041662e1986d811461019f57806302a72a4c146101d657806306eb4e421461020157806306fdde0314610220578063095ea7b3146102ad578063158ccb99146102dd57806318160ddd146102f85780631cf65a781461031757806323b872dd146103365780632c71e60a1461036c57806333148fd6146103ca578063435ebc2c146103f55780635eeb6e451461041e578063600e85b71461043c5780636103d70b146104a157806362c1e46a146104b05780636c182e99146104ba578063706dc87c146104f057806370a082311461052557806377174f851461055057806395d89b411461056f578063a7771ee3146105fc578063a9059cbb14610629578063ab377daa14610659578063b25dbb5e14610685578063b89a73cb14610699578063ca5eb5e1146106c6578063cbcf2e5a146106e1578063d21f05ba1461070e578063d347c2051461072d578063d96831e114610765578063dd62ed3e14610777578063df3c211b146107a8578063e2982c21146107d6578063eb944e4c14610801575b610000565b34610000576101d4600160a060020a036004351660243567ffffffffffffffff6044358116906064358116906084351661081f565b005b34610000576101ef600160a060020a0360043516610a30565b60408051918252519081900360200190f35b34610000576101ef610a4f565b60408051918252519081900360200190f35b346100005761022d610a55565b604080516020808252835181830152835191928392908301918501908083838215610273575b80518252602083111561027357601f199092019160209182019101610253565b505050905090810190601f16801561029f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576102c9600160a060020a0360043516602435610ae3565b604080519115158252519081900360200190f35b34610000576101d4600160a060020a0360043516610b4e565b005b34610000576101ef610b89565b60408051918252519081900360200190f35b34610000576101ef610b8f565b60408051918252519081900360200190f35b34610000576102c9600160a060020a0360043581169060243516604435610b95565b604080519115158252519081900360200190f35b3461000057610388600160a060020a0360043516602435610bb7565b60408051600160a060020a039096168652602086019490945267ffffffffffffffff928316858501529082166060850152166080830152519081900360a00190f35b34610000576101ef600160a060020a0360043516610c21565b60408051918252519081900360200190f35b3461000057610402610c40565b60408051600160a060020a039092168252519081900360200190f35b34610000576101d4600160a060020a0360043516602435610c4f565b005b3461000057610458600160a060020a0360043516602435610cc9565b60408051600160a060020a03909716875260208701959095528585019390935267ffffffffffffffff9182166060860152811660808501521660a0830152519081900360c00190f35b34610000576101d4610d9e565b005b6101d4610e1e565b005b34610000576104d3600160a060020a0360043516610e21565b6040805167ffffffffffffffff9092168252519081900360200190f35b3461000057610402600160a060020a0360043516610ead565b60408051600160a060020a039092168252519081900360200190f35b34610000576101ef600160a060020a0360043516610ef9565b60408051918252519081900360200190f35b34610000576101ef610f18565b60408051918252519081900360200190f35b346100005761022d610f1e565b604080516020808252835181830152835191928392908301918501908083838215610273575b80518252602083111561027357601f199092019160209182019101610253565b505050905090810190601f16801561029f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576102c9600160a060020a0360043516610fac565b604080519115158252519081900360200190f35b34610000576102c9600160a060020a0360043516602435610fc2565b604080519115158252519081900360200190f35b3461000057610402600435610fe2565b60408051600160a060020a039092168252519081900360200190f35b34610000576101d46004351515610ffd565b005b34610000576102c9600160a060020a036004351661104c565b604080519115158252519081900360200190f35b34610000576101d4600160a060020a0360043516611062565b005b34610000576102c9600160a060020a0360043516611070565b604080519115158252519081900360200190f35b34610000576101ef6110f4565b60408051918252519081900360200190f35b34610000576101ef600160a060020a036004351667ffffffffffffffff602435166110fa565b60408051918252519081900360200190f35b34610000576101d4600435611121565b005b34610000576101ef600160a060020a03600435811690602435166111c6565b60408051918252519081900360200190f35b34610000576101ef6004356024356044356064356084356111f3565b60408051918252519081900360200190f35b34610000576101ef600160a060020a036004351661128c565b60408051918252519081900360200190f35b34610000576101d4600160a060020a036004351660243561129e565b005b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff848116908416101561086457610000565b8367ffffffffffffffff168267ffffffffffffffff16101561088557610000565b8267ffffffffffffffff168267ffffffffffffffff1610156108a657610000565b506040805160a081018252600160a060020a033381168252602080830188905267ffffffffffffffff80871684860152858116606085015287166080840152908816600090815260039091529190912080546001810180835582818380158290116109615760030281600302836000526020600020918201910161096191905b8082111561095d578054600160a060020a031916815560006001820155600281018054600160c060020a0319169055600301610926565b5090565b5b505050916000526020600020906003020160005b5082518154600160a060020a031916600160a060020a03909116178155602083015160018201556040830151600290910180546060850151608086015167ffffffffffffffff1990921667ffffffffffffffff948516176fffffffffffffffff00000000000000001916604060020a918516919091021777ffffffffffffffff000000000000000000000000000000001916608060020a939091169290920291909117905550610a268686610fc2565b505b505050505050565b600160a060020a0381166000908152600360205260409020545b919050565b60055481565b600b805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610adb5780601f10610ab057610100808354040283529160200191610adb565b820191906000526020600020905b815481529060010190602001808311610abe57829003601f168201915b505050505081565b600160a060020a03338116600081815260026020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b600a5433600160a060020a03908116911614610b6957610000565b600a8054600160a060020a031916600160a060020a0383161790555b5b50565b60005481565b60005b90565b6000610ba2848484611600565b610bad8484846116e2565b90505b9392505050565b600360205281600052604060002081815481101561000057906000526020600020906003020160005b5080546001820154600290920154600160a060020a03909116935090915067ffffffffffffffff80821691604060020a8104821691608060020a9091041685565b600160a060020a0381166000908152600860205260409020545b919050565b600a54600160a060020a031681565b600a5433600160a060020a03908116911614610c6a57610000565b610c7660005482611714565b6000908155600160a060020a038316815260016020526040902054610c9b9082611714565b600160a060020a038316600090815260016020526040812091909155610cc390839083611600565b5b5b5050565b6000600060006000600060006000600360008a600160a060020a0316600160a060020a0316815260200190815260200160002088815481101561000057906000526020600020906003020160005b508054600182015460028301546040805160a081018252600160a060020a039094168085526020850184905267ffffffffffffffff808416928601839052604060020a8404811660608701819052608060020a9094041660808601819052909c50929a509197509095509350909150610d90904261172d565b94505b509295509295509295565b33600160a060020a038116600090815260066020526040902054801515610dc457610000565b8030600160a060020a0316311015610ddb57610000565b600160a060020a0382166000818152600660205260408082208290555183156108fc0291849190818181858888f193505050501515610cc357610000565b5b5050565b5b565b600160a060020a03811660009081526003602052604081205442915b81811015610ea557600160a060020a03841660009081526003602052604090208054610e9a9190839081101561000057906000526020600020906003020160005b5060020154604060020a900467ffffffffffffffff168461177d565b92505b600101610e3d565b5b5050919050565b600160a060020a0380821660009081526007602052604081205490911615610eef57600160a060020a0380831660009081526007602052604090205416610ef1565b815b90505b919050565b600160a060020a0381166000908152600160205260409020545b919050565b600d5481565b600c805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610adb5780601f10610ab057610100808354040283529160200191610adb565b820191906000526020600020905b815481529060010190602001808311610abe57829003601f168201915b505050505081565b60006000610fb983610c21565b1190505b919050565b6000610fcf338484611600565b610fd983836117ac565b90505b92915050565b600460205260009081526040902054600160a060020a031681565b8015801561101a575061100f33610ef9565b61101833610c21565b115b1561102457610000565b33600160a060020a03166000908152600960205260409020805460ff19168215151790555b50565b60006000610fb983610ef9565b1190505b919050565b610b8533826117dc565b5b50565b600a54604080516000602091820181905282517fcbcf2e5a000000000000000000000000000000000000000000000000000000008152600160a060020a03868116600483015293519194939093169263cbcf2e5a92602480830193919282900301818787803b156100005760325a03f115610000575050604051519150505b919050565b600e5481565b6000610fd961110984846118b2565b61111385856119b6565b611a05565b90505b92915050565b600a5433600160a060020a0390811691161461113c57610000565b61114860005482611a1f565b600055600554600190101561116c57600a5461116c90600160a060020a0316611a47565b5b600a54600160a060020a03166000908152600160205260409020546111929082611a1f565b600a8054600160a060020a039081166000908152600160205260408120939093559054610b8592911683611600565b5b5b50565b600160a060020a038083166000908152600260209081526040808320938516835292905220545b92915050565b6000600060008487101561120a5760009250611281565b8387111561121a57879250611281565b61123f6112308961122b888a611714565b611a90565b61123a8689611714565b611abc565b915081925061124e8883611714565b905061127e8361127961126a8461122b8c8b611714565b611a90565b61123a888b611714565b611abc565b611a1f565b92505b505095945050505050565b60066020526000908152604090205481565b600160a060020a03821660009081526003602052604081208054829190849081101561000057906000526020600020906003020160005b50805490925033600160a060020a039081169116146112f357610000565b6040805160a0810182528354600160a060020a0316815260018401546020820152600284015467ffffffffffffffff80821693830193909352604060020a810483166060830152608060020a900490911660808201526113539042611af9565b600160a060020a0385166000908152600360205260409020805491925090849081101561000057906000526020600020906003020160005b508054600160a060020a031916815560006001820181905560029091018054600160c060020a0319169055600160a060020a0385168152600360205260409020805460001981019081101561000057906000526020600020906003020160005b50600160a060020a03851660009081526003602052604090208054859081101561000057906000526020600020906003020160005b5081548154600160a060020a031916600160a060020a03918216178255600180840154908301556002928301805493909201805467ffffffffffffffff191667ffffffffffffffff948516178082558354604060020a908190048616026fffffffffffffffff000000000000000019909116178082559254608060020a9081900490941690930277ffffffffffffffff00000000000000000000000000000000199092169190911790915584166000908152600360205260409020805460001981018083559190829080158290116115485760030281600302836000526020600020918201910161154891905b8082111561095d578054600160a060020a031916815560006001820155600281018054600160c060020a0319169055600301610926565b5090565b5b505050600160a060020a033316600090815260016020526040902054611570915082611a1f565b600160a060020a03338116600090815260016020526040808220939093559086168152205461159f9082611714565b600160a060020a038086166000818152600160209081526040918290209490945580518581529051339093169391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35b50505050565b600160a060020a0383161561166e576116466008600061161f86610ead565b600160a060020a0316600160a060020a031681526020019081526020016000205482611714565b6008600061165386610ead565b600160a060020a031681526020810191909152604001600020555b600160a060020a038216156116dc576116b46008600061168d85610ead565b600160a060020a0316600160a060020a031681526020019081526020016000205482611a1f565b600860006116c185610ead565b600160a060020a031681526020810191909152604001600020555b5b505050565b600083826116f082426110fa565b8111156116fc57610000565b611707868686611b1b565b92505b5b50509392505050565b600061172283831115611b4d565b508082035b92915050565b6000610fd983602001518367ffffffffffffffff16856080015167ffffffffffffffff16866040015167ffffffffffffffff16876060015167ffffffffffffffff166111f3565b90505b92915050565b60008167ffffffffffffffff168367ffffffffffffffff1610156117a15781610fd9565b825b90505b92915050565b600033826117ba82426110fa565b8111156117c657610000565b6117d08585611b5d565b92505b5b505092915050565b6117e582610ef9565b6117ee83610c21565b11156117f957610000565b600160a060020a03811660009081526009602052604090205460ff16158015611834575081600160a060020a031681600160a060020a031614155b1561183e57610000565b61184782611070565b1561185157610000565b611864828261185f85610ef9565b611600565b600160a060020a0382811660009081526007602052604090208054600160a060020a031916918316918217905561189a82610ead565b600160a060020a031614610cc357610000565b5b5050565b600160a060020a038216600090815260036020526040812054815b818110156119885761197d836112796003600089600160a060020a0316600160a060020a0316815260200190815260200160002084815481101561000057906000526020600020906003020160005b506040805160a0810182528254600160a060020a031681526001830154602082015260029092015467ffffffffffffffff80821692840192909252604060020a810482166060840152608060020a900416608082015287611af9565b611a1f565b92505b6001016118cd565b600160a060020a0385166000908152600160205260409020546117d09084611714565b92505b505092915050565b600060006119c384611070565b80156119d157506000600d54115b90506119fb816119e9576119e485610ef9565b6119ec565b60005b6111138686611b7b565b611a05565b91505b5092915050565b60008183106117a15781610fd9565b825b90505b92915050565b6000828201611a3c848210801590611a375750838210155b611b4d565b8091505b5092915050565b611a508161104c565b15611a5a57610b85565b6005805460009081526004602052604090208054600160a060020a031916600160a060020a038416179055805460010190555b50565b6000828202611a3c841580611a37575083858381156100005704145b611b4d565b8091505b5092915050565b60006000611acc60008411611b4d565b8284811561000057049050611a3c838581156100005706828502018514611b4d565b8091505b5092915050565b6000610fd98360200151611b0d858561172d565b611714565b90505b92915050565b60008382611b2982426110fa565b811115611b3557610000565b611707868686611b8f565b92505b5b50509392505050565b801515610b8557610000565b5b50565b6000611b6883611a47565b610fd98383611c92565b90505b92915050565b6000610fd983610ef9565b90505b92915050565b600160a060020a038084166000908152600260209081526040808320338516845282528083205493861683526001909152812054909190611bd09084611a1f565b600160a060020a038086166000908152600160205260408082209390935590871681522054611bff9084611714565b600160a060020a038616600090815260016020526040902055611c228184611714565b600160a060020a038087166000818152600260209081526040808320338616845282529182902094909455805187815290519288169391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3600191505b509392505050565b60003382611ca082426110fa565b811115611cac57610000565b6117d08585611cc2565b92505b5b505092915050565b600160a060020a033316600090815260016020526040812054611ce59083611714565b600160a060020a033381166000908152600160205260408082209390935590851681522054611d149083611a1f565b600160a060020a038085166000818152600160209081526040918290209490945580518681529051919333909316927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a35060015b929150505600a165627a7a72305820bfa5ddd3fecf3f43aed25385ec7ec3ef79638c2e58d99f85d9a3cc494183bf160029000000000000000000000000a14bdd7e5666d784dcce98ad24d383a6b1cd4182",
@@ -68,7 +69,7 @@
"error": "invalid jump destination",
"from": "0xe4a13bc304682a903e9472f469c33801dd18d9e8",
"gas": "0x435c8",
- "gasUsed": "0x435c8",
+ "gasUsed": "0x493e0",
"input": "0x3b91f506000000000000000000000000a14bdd7e5666d784dcce98ad24d383a6b1cd4182000000000000000000000000e4a13bc304682a903e9472f469c33801dd18d9e8",
"to": "0x1d3ddf7caf024f253487e18bc4a15b1a360c170a",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json
index 6e221b3c07..4d7305a154 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json
@@ -55,9 +55,7 @@
"to": "0x6c06b16512b332e6cd8293a2974872674716ce18",
"value": "0x0",
"gas": "0x1a466",
- "gasUsed": "0x1dc6",
- "input": "0x2e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b160000",
- "output": "0x",
- "calls": []
+ "gasUsed": "0x72de",
+ "input": "0x2e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b160000"
}
}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/inner_revert_reason.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_revert_reason.json
new file mode 100644
index 0000000000..ec10902b28
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_revert_reason.json
@@ -0,0 +1,86 @@
+{
+ "genesis": {
+ "baseFeePerGas": "1000000000",
+ "difficulty": "1",
+ "extraData": "0x00000000000000000000000000000000000000000000000000000000000000003623191d4ccfbbdf09e8ebf6382a1f8257417bc10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "gasLimit": "11500000",
+ "hash": "0x2af138b8a06e65b8dd0999df70b9e87609e9fc91ea201f08b1cc4f25ef01fcf6",
+ "miner": "0x0000000000000000000000000000000000000000",
+ "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "nonce": "0x0000000000000000",
+ "number": "0",
+ "stateRoot": "0xa775801d572e9b79585eb131d18d79f8a0f71895455ab9a5b656911428e11708",
+ "timestamp": "0",
+ "totalDifficulty": "1",
+ "alloc": {
+ "0x3623191d4ccfbbdf09e8ebf6382a1f8257417bc1": {
+ "balance": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7"
+ },
+ "0xd15abca351f79181dedfb6d019e382db90f3628a": {
+ "balance": "0x0"
+ }
+ },
+ "config": {
+ "chainId": 1337,
+ "homesteadBlock": 0,
+ "eip150Block": 0,
+ "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "eip155Block": 0,
+ "eip158Block": 0,
+ "byzantiumBlock": 0,
+ "constantinopleBlock": 0,
+ "petersburgBlock": 0,
+ "istanbulBlock": 0,
+ "muirGlacierBlock": 0,
+ "berlinBlock": 0,
+ "londonBlock": 0,
+ "clique": {
+ "period": 0,
+ "epoch": 30000
+ }
+ }
+ },
+ "context": {
+ "number": "1",
+ "difficulty": "2",
+ "timestamp": "1665537018",
+ "gasLimit": "11511229",
+ "miner": "0x0000000000000000000000000000000000000000"
+ },
+ "input": "0x02f9029d82053980849502f90085010c388d00832dc6c08080b90241608060405234801561001057600080fd5b50600060405161001f906100a2565b604051809103906000f08015801561003b573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663c04062266040518163ffffffff1660e01b815260040160006040518083038186803b15801561008457600080fd5b505afa158015610098573d6000803e3d6000fd5b50505050506100af565b610145806100fc83390190565b603f806100bd6000396000f3fe6080604052600080fdfea264697066735822122077f7dbd3450d6e817079cf3fe27107de5768bb3163a402b94e2206b468eb025664736f6c63430008070033608060405234801561001057600080fd5b50610125806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c040622614602d575b600080fd5b60336035565b005b60036002116076576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401606d906097565b60405180910390fd5b565b6000608360128360b5565b9150608c8260c6565b602082019050919050565b6000602082019050818103600083015260ae816078565b9050919050565b600082825260208201905092915050565b7f546869732063616c6c6564206661696c6564000000000000000000000000000060008201525056fea264697066735822122033f8d92e29d467e5ea08d0024eab0b36b86b8cdb3542c6e89dbaabeb8ffaa42064736f6c63430008070033c001a07566181071cabaf58b70fc41557eb813bfc7a24f5c58554e7fed0bf7c031f169a0420af50b5fe791a4d839e181a676db5250b415dfb35cb85d544db7a1475ae2cc",
+ "result": {
+ "from": "0x3623191d4ccfbbdf09e8ebf6382a1f8257417bc1",
+ "to": "0x0000000000000000000000000000000000000000",
+ "gas": "0x2cd774",
+ "gasUsed": "0x25590",
+ "input": "0x608060405234801561001057600080fd5b50600060405161001f906100a2565b604051809103906000f08015801561003b573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663c04062266040518163ffffffff1660e01b815260040160006040518083038186803b15801561008457600080fd5b505afa158015610098573d6000803e3d6000fd5b50505050506100af565b610145806100fc83390190565b603f806100bd6000396000f3fe6080604052600080fdfea264697066735822122077f7dbd3450d6e817079cf3fe27107de5768bb3163a402b94e2206b468eb025664736f6c63430008070033608060405234801561001057600080fd5b50610125806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c040622614602d575b600080fd5b60336035565b005b60036002116076576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401606d906097565b60405180910390fd5b565b6000608360128360b5565b9150608c8260c6565b602082019050919050565b6000602082019050818103600083015260ae816078565b9050919050565b600082825260208201905092915050565b7f546869732063616c6c6564206661696c6564000000000000000000000000000060008201525056fea264697066735822122033f8d92e29d467e5ea08d0024eab0b36b86b8cdb3542c6e89dbaabeb8ffaa42064736f6c63430008070033",
+ "output": "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012546869732063616c6c6564206661696c65640000000000000000000000000000",
+ "error": "execution reverted",
+ "revertReason": "This called failed",
+ "calls": [
+ {
+ "from": "0xdebfb4b387033eac57af7b3de5116dd60056803b",
+ "gas": "0x2ba851",
+ "gasUsed": "0xe557",
+ "to": "0xd15abca351f79181dedfb6d019e382db90f3628a",
+ "input": "0x608060405234801561001057600080fd5b50610125806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c040622614602d575b600080fd5b60336035565b005b60036002116076576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401606d906097565b60405180910390fd5b565b6000608360128360b5565b9150608c8260c6565b602082019050919050565b6000602082019050818103600083015260ae816078565b9050919050565b600082825260208201905092915050565b7f546869732063616c6c6564206661696c6564000000000000000000000000000060008201525056fea264697066735822122033f8d92e29d467e5ea08d0024eab0b36b86b8cdb3542c6e89dbaabeb8ffaa42064736f6c63430008070033",
+ "output": "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063c040622614602d575b600080fd5b60336035565b005b60036002116076576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401606d906097565b60405180910390fd5b565b6000608360128360b5565b9150608c8260c6565b602082019050919050565b6000602082019050818103600083015260ae816078565b9050919050565b600082825260208201905092915050565b7f546869732063616c6c6564206661696c6564000000000000000000000000000060008201525056fea264697066735822122033f8d92e29d467e5ea08d0024eab0b36b86b8cdb3542c6e89dbaabeb8ffaa42064736f6c63430008070033",
+ "value": "0x0",
+ "type": "CREATE"
+ },
+ {
+ "from": "0xdebfb4b387033eac57af7b3de5116dd60056803b",
+ "gas": "0x2ac548",
+ "gasUsed": "0x1b2",
+ "to": "0xd15abca351f79181dedfb6d019e382db90f3628a",
+ "input": "0xc0406226",
+ "output": "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012546869732063616c6c6564206661696c65640000000000000000000000000000",
+ "error": "execution reverted",
+ "revertReason": "This called failed",
+ "type": "STATICCALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CREATE"
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.json
index ec2ceb426f..2be2dee23f 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.json
@@ -72,7 +72,7 @@
"error": "execution reverted",
"from": "0xd4fcab9f0a6dc0493af47c864f6f17a8a5e2e826",
"gas": "0x78d9e",
- "gasUsed": "0x76fc0",
+ "gasUsed": "0x7c1c8",
"input": "0x",
"to": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.md b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.md
new file mode 100644
index 0000000000..2700578bd0
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.md
@@ -0,0 +1,19 @@
+This test tests out the trace generated by the deployment of this contract:
+
+```solidity
+contract Revertor {
+ function run() public pure {
+ require(2 > 3, "This called failed");
+ }
+}
+
+contract Contract {
+ constructor() {
+ Revertor r = new Revertor();
+ r.run();
+ }
+}
+```
+
+The trace should show a revert, with the revert reason for both the top-call as well
+as the inner call.
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/oog.json b/eth/tracers/internal/tracetest/testdata/call_tracer/oog.json
index de4fed6ab1..8022f53a99 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/oog.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/oog.json
@@ -51,7 +51,7 @@
"error": "out of gas",
"from": "0x94194bc2aaf494501d7880b61274a169f6502a54",
"gas": "0x7045",
- "gasUsed": "0x7045",
+ "gasUsed": "0xca1d",
"input": "0xa9059cbb000000000000000000000000e77b1ac803616503510bed0086e3a7be2627a69900000000000000000000000000000000000000000000000000000009502f9000",
"to": "0x43064693d3d38ad6a7cb579e0d6d9718c8aa6b62",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer/revert.json
index 059040a1c8..aee894d11f 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/revert.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/revert.json
@@ -49,7 +49,7 @@
"error": "execution reverted",
"from": "0x0f6cef2b7fbb504782e35aa82a2207e816a2b7a9",
"gas": "0x2d55e8",
- "gasUsed": "0xc3",
+ "gasUsed": "0x719b",
"input": "0x73b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a98800000000000000000000000000000000000000000000000000000000000000000000000000000000",
"to": "0xabbcd5b340c80b5f1c0545c04c987b87310296ae",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/revert_reason.json b/eth/tracers/internal/tracetest/testdata/call_tracer/revert_reason.json
index 094b044677..8c8abd4d6d 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/revert_reason.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/revert_reason.json
@@ -54,11 +54,12 @@
"error": "execution reverted",
"from": "0xf7579c3d8a669c89d5ed246a22eb6db8f6fedbf1",
"gas": "0x2d7308",
- "gasUsed": "0x588",
+ "gasUsed": "0x5940",
"input": "0x5c19a95c000000000000000000000000f7579c3d8a669c89d5ed246a22eb6db8f6fedbf1",
"to": "0xf58833cf0c791881b494eb79d461e08a1f043f52",
"type": "CALL",
"value": "0x0",
- "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001e53656c662d64656c65676174696f6e20697320646973616c6c6f7765642e0000"
+ "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001e53656c662d64656c65676174696f6e20697320646973616c6c6f7765642e0000",
+ "revertReason": "Self-delegation is disallowed."
}
}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/selfdestruct.json b/eth/tracers/internal/tracetest/testdata/call_tracer/selfdestruct.json
index dd717906bc..a89d4dc745 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/selfdestruct.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/selfdestruct.json
@@ -58,16 +58,15 @@
"gas": "0x0",
"gasUsed": "0x0",
"input": "0x",
- "to": "0x000000000000000000000000000000000000dEaD",
+ "to": "0x000000000000000000000000000000000000dead",
"type": "SELFDESTRUCT",
"value": "0x4d87094125a369d9bd5"
}
],
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
"gas": "0x10738",
- "gasUsed": "0x7533",
+ "gasUsed": "0x6fcb",
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
- "output": "0x",
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
"type": "CALL",
"value": "0x0"
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/simple.json b/eth/tracers/internal/tracetest/testdata/call_tracer/simple.json
index 08cb7b2d00..0a6d66a5c4 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/simple.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/simple.json
@@ -70,7 +70,7 @@
],
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
"gas": "0x10738",
- "gasUsed": "0x3ef9",
+ "gasUsed": "0x9751",
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/simple_onlytop.json b/eth/tracers/internal/tracetest/testdata/call_tracer/simple_onlytop.json
index ac1fef4409..5e25a01cef 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/simple_onlytop.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/simple_onlytop.json
@@ -62,7 +62,7 @@
"result": {
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
"gas": "0x10738",
- "gasUsed": "0x3ef9",
+ "gasUsed": "0x9751",
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer/throw.json
index 09cf449776..76fae3c392 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer/throw.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer/throw.json
@@ -53,7 +53,7 @@
"error": "invalid jump destination",
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
"gas": "0x37b38",
- "gasUsed": "0x37b38",
+ "gasUsed": "0x3d090",
"input": "0x51a34eb8000000000000000000000000000000000000000000000027fad02094277c0000",
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/create.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/create.json
index 8699bf3e7e..8557f8efd6 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/create.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/create.json
@@ -48,7 +48,7 @@
"result": {
"from": "0x13e4acefe6a6700604929946e70e6443e4e73447",
"gas": "0x5e106",
- "gasUsed": "0x5e106",
+ "gasUsed": "0x897be",
"input": "0x606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a11",
"output": "0x606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029",
"to": "0x7dc9c9730689ff0b0fd506c67db815f12d90a448",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/deep_calls.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/deep_calls.json
index 0353d4cfa9..ef28a930b3 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/deep_calls.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/deep_calls.json
@@ -405,7 +405,7 @@
],
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
"gas": "0x37b38",
- "gasUsed": "0x12bb3",
+ "gasUsed": "0x1810b",
"input": "0x51a34eb80000000000000000000000000000000000000000000000280faf689c35ac0000",
"output": "0x",
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/delegatecall.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/delegatecall.json
index f7ad6df5f5..c4c1390fa2 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/delegatecall.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/delegatecall.json
@@ -87,7 +87,7 @@
],
"from": "0xa529806c67cc6486d4d62024471772f47f6fd672",
"gas": "0x2d6e28",
- "gasUsed": "0x64bd",
+ "gasUsed": "0xbd55",
"input": "0x7065cb480000000000000000000000001523e55a1ca4efbae03355775ae89f8d7699ad9e",
"output": "0x",
"to": "0x269296dddce321a6bcbaa2f0181127593d732cba",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json
index 72152e27e7..0b60e34d0e 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json
@@ -68,7 +68,7 @@
"error": "invalid jump destination",
"from": "0xe4a13bc304682a903e9472f469c33801dd18d9e8",
"gas": "0x435c8",
- "gasUsed": "0x435c8",
+ "gasUsed": "0x493e0",
"input": "0x3b91f506000000000000000000000000a14bdd7e5666d784dcce98ad24d383a6b1cd4182000000000000000000000000e4a13bc304682a903e9472f469c33801dd18d9e8",
"to": "0x1d3ddf7caf024f253487e18bc4a15b1a360c170a",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_instafail.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_instafail.json
index 86070d1308..c1ed766ef9 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_instafail.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_instafail.json
@@ -55,7 +55,7 @@
"to": "0x6c06b16512b332e6cd8293a2974872674716ce18",
"value": "0x0",
"gas": "0x1a466",
- "gasUsed": "0x1dc6",
+ "gasUsed": "0x72de",
"input": "0x2e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b160000",
"output": "0x",
"calls": [
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_throw_outer_revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_throw_outer_revert.json
index ec2ceb426f..2be2dee23f 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_throw_outer_revert.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_throw_outer_revert.json
@@ -72,7 +72,7 @@
"error": "execution reverted",
"from": "0xd4fcab9f0a6dc0493af47c864f6f17a8a5e2e826",
"gas": "0x78d9e",
- "gasUsed": "0x76fc0",
+ "gasUsed": "0x7c1c8",
"input": "0x",
"to": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/oog.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/oog.json
index de4fed6ab1..8022f53a99 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/oog.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/oog.json
@@ -51,7 +51,7 @@
"error": "out of gas",
"from": "0x94194bc2aaf494501d7880b61274a169f6502a54",
"gas": "0x7045",
- "gasUsed": "0x7045",
+ "gasUsed": "0xca1d",
"input": "0xa9059cbb000000000000000000000000e77b1ac803616503510bed0086e3a7be2627a69900000000000000000000000000000000000000000000000000000009502f9000",
"to": "0x43064693d3d38ad6a7cb579e0d6d9718c8aa6b62",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert.json
index 059040a1c8..aee894d11f 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert.json
@@ -49,7 +49,7 @@
"error": "execution reverted",
"from": "0x0f6cef2b7fbb504782e35aa82a2207e816a2b7a9",
"gas": "0x2d55e8",
- "gasUsed": "0xc3",
+ "gasUsed": "0x719b",
"input": "0x73b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a98800000000000000000000000000000000000000000000000000000000000000000000000000000000",
"to": "0xabbcd5b340c80b5f1c0545c04c987b87310296ae",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert_reason.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert_reason.json
index 094b044677..4f7fee97d9 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert_reason.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert_reason.json
@@ -54,7 +54,7 @@
"error": "execution reverted",
"from": "0xf7579c3d8a669c89d5ed246a22eb6db8f6fedbf1",
"gas": "0x2d7308",
- "gasUsed": "0x588",
+ "gasUsed": "0x5940",
"input": "0x5c19a95c000000000000000000000000f7579c3d8a669c89d5ed246a22eb6db8f6fedbf1",
"to": "0xf58833cf0c791881b494eb79d461e08a1f043f52",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/selfdestruct.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/selfdestruct.json
index 132cefa168..55b63dbdb6 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/selfdestruct.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/selfdestruct.json
@@ -63,7 +63,7 @@
],
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
"gas": "0x10738",
- "gasUsed": "0x7533",
+ "gasUsed": "0x6fcb",
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
"output": "0x",
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/simple.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/simple.json
index b46432122d..c9192a19f9 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/simple.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/simple.json
@@ -68,7 +68,7 @@
],
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
"gas": "0x10738",
- "gasUsed": "0x3ef9",
+ "gasUsed": "0x9751",
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/throw.json
index 09cf449776..76fae3c392 100644
--- a/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/throw.json
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/throw.json
@@ -53,7 +53,7 @@
"error": "invalid jump destination",
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
"gas": "0x37b38",
- "gasUsed": "0x37b38",
+ "gasUsed": "0x3d090",
"input": "0x51a34eb8000000000000000000000000000000000000000000000027fad02094277c0000",
"to": "0xc212e03b9e060e36facad5fd8f4435412ca22e6b",
"type": "CALL",
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json
new file mode 100644
index 0000000000..b18c80e58e
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json
@@ -0,0 +1,115 @@
+{
+ "genesis": {
+ "difficulty": "11934798510088",
+ "extraData": "0xd983010302844765746887676f312e342e328777696e646f7773",
+ "gasLimit": "3141592",
+ "hash": "0xfc543a4a551afbd4a6c5d6d49041371e6bb58b1108c12aaec7f487ce656bb97f",
+ "miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069",
+ "mixHash": "0xa6a1e67fc68da76b8d9cc3ce1c45d5e1f4bbd96b5dcfddbe0017d7fa99903ead",
+ "nonce": "0x5f00c600268b4659",
+ "number": "995200",
+ "stateRoot": "0x3579328470dd2aef5b9da69f5480cbe0d375e653b530ab3c1aee0da5e1ff4c94",
+ "timestamp": "1455322761",
+ "totalDifficulty": "7077231809278509672",
+ "alloc": {
+ "0x200edd17f30485a8735878661960cd7a9a95733f": {
+ "balance": "0x0",
+ "code": "0x3660008037602060003660003473273930d21e01ee25e4c219b63259d214872220a261235a5a03f21560015760206000f3",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000002",
+ "0x0000000000000000000000000000000000000000000000000000000000000104": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x4c0be60200faa20559308cb7b5a1bb3255c16cb1cab91f525b5ae7a03d02fabe": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8ba1097eb3abe3dc1b51faa48445d593bf968f722e20b67bb62a87495836bf04": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8ba1097eb3abe3dc1b51faa48445d593bf968f722e20b67bb62a87495836bf05": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8ba1097eb3abe3dc1b51faa48445d593bf968f722e20b67bb62a87495836bf06": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xa611e7c895a426c0477bc9e280db9c3b1e456dc6310ffcf23926ef5186c1facc": "0x0000000000000000000000000000000000000000000000000000000000000002",
+ "0xac682d343707aadf06c2c4c3692831d9e7ba711099ef36f9efb8bb29be8c410e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xac682d343707aadf06c2c4c3692831d9e7ba711099ef36f9efb8bb29be8c410f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xac682d343707aadf06c2c4c3692831d9e7ba711099ef36f9efb8bb29be8c4110": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x273930d21e01ee25e4c219b63259d214872220a2": {
+ "balance": "0x0",
+ "code": "0x606060405236156100da5760e060020a6000350463173825d9811461012c5780632f54bf6e146101875780634123cb6b146101af57806352375093146101b857806354fd4d50146101c25780635c52c2f5146101cc578063659010e7146101fd5780637065cb4814610207578063746c91711461023b578063797af62714610244578063b20d30a914610257578063b61d27f61461028b578063b75c7dc6146102ac578063ba51a6df146102db578063c2cf73261461030f578063cbf0b0c01461034d578063f00d4b5d14610381578063f1736d86146103ba575b6103c4600034111561012a5760408051600160a060020a033216815234602082015281517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c929181900390910190a15b565b6103c46004356000600036436040518084848082843750505090910190815260405190819003602001902090506106c9815b600160a060020a03321660009081526101026020526040812054818082811415610c3f57610d97565b6103c66004355b600160a060020a03811660009081526101026020526040812054115b919050565b6103c660015481565b6103c66101075481565b6103c66101085481565b6103c46000364360405180848480828437505050909101908152604051908190036020019020905061081a8161015e565b6103c66101065481565b6103c4600435600036436040518084848082843750505090910190815260405190819003602001902090506106418161015e565b6103c660005481565b6103c66004355b600081610a7d8161015e565b6103c46004356000364360405180848480828437505050909101908152604051908190036020019020905061080e8161015e565b6103c66004803590602480359160443591820191013560006108393261018e565b6103c4600435600160a060020a033216600090815261010260205260408120549080828114156103d857610457565b6103c4600435600036436040518084848082843750505090910190815260405190819003602001902090506107888161015e565b6103c6600435602435600082815261010360209081526040808320600160a060020a038516845261010290925282205482818114156107e157610805565b6103c4600435600036436040518084848082843750505090910190815260405190819003602001902090506108288161015e565b6103c46004356024356000600036436040518084848082843750505090910190815260405190819003602001902090506104e28161015e565b6103c66101055481565b005b60408051918252519081900360200190f35b50506000828152610103602052604081206001810154600284900a9290831611156104575780546001828101805492909101835590839003905560408051600160a060020a03321681526020810186905281517fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b929181900390910190a15b50505050565b600160a060020a03831660028361010081101561000257508301819055600160a060020a03851660008181526101026020908152604080832083905584835291829020869055815192835282019290925281517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c929181900390910190a1505b505050565b15610457576104f08361018e565b156104fb57506104dd565b600160a060020a03841660009081526101026020526040812054925082141561052457506104dd565b61045d5b6101045460005b81811015610ee457610104805461010991600091849081101561000257600080516020610f9f83398151915201548252506020918252604081208054600160a060020a0319168155600181018290556002810180548382559083528383209193610f6992601f9290920104810190610a65565b60018054810190819055600160a060020a038316906002906101008110156100025790900160005081905550600160005054610102600050600084600160a060020a03168152602001908152602001600020600050819055507f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c3826040518082600160a060020a0316815260200191505060405180910390a15b505b50565b1561063c5761064f8261018e565b1561065a575061063e565b610662610528565b60015460fa90106106775761067561068c565b505b60015460fa90106105a2575061063e565b6107465b600060015b600154811015610a79575b600154811080156106bc5750600281610100811015610002570154600014155b15610d9f5760010161069c565b156104dd57600160a060020a0383166000908152610102602052604081205492508214156106f7575061063c565b6001600160005054036000600050541115610712575061063c565b600060028361010081101561000257508301819055600160a060020a03841681526101026020526040812055610688610528565b5060408051600160a060020a038516815290517f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da9181900360200190a1505050565b1561063c5760015482111561079d575061063e565b60008290556107aa610528565b6040805183815290517facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da9181900360200190a15050565b506001820154600282900a908116600014156108005760009350610805565b600193505b50505092915050565b1561063c575061010555565b1561063e5760006101065550565b1561063c5781600160a060020a0316ff5b15610a555761084d846000610e793261018e565b15610909577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd00432858786866040518086600160a060020a0316815260200185815260200184600160a060020a031681526020018060200182810382528484828181526020019250808284378201915050965050505050505060405180910390a184600160a060020a03168484846040518083838082843750505090810191506000908083038185876185025a03f15060009350610a5592505050565b6000364360405180848480828437505050909101908152604051908190036020019020915061093990508161024b565b15801561095c575060008181526101096020526040812054600160a060020a0316145b15610a555760008181526101096020908152604082208054600160a060020a03191688178155600181018790556002018054858255818452928290209092601f01919091048101908490868215610a5d579182015b82811115610a5d5782358260005055916020019190600101906109b1565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf328132868887876040518087815260200186600160a060020a0316815260200185815260200184600160a060020a03168152602001806020018281038252848482818152602001925080828437820191505097505050505050505060405180910390a15b949350505050565b506109cf9291505b80821115610a795760008155600101610a65565b5090565b15610c2c5760008381526101096020526040812054600160a060020a031614610c2c5760408051600091909120805460018201546002929092018054600160a060020a0392909216939091819083908015610afd57820191906000526020600020905b815481529060010190602001808311610ae057829003601f168201915b505091505060006040518083038185876185025a03f150505060008481526101096020908152604080519281902080546001820154600160a060020a033281811688529587018b905293860181905292166060850181905260a06080860181815260029390930180549187018290527fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a975094958a959293909160c083019084908015610bcf57820191906000526020600020905b815481529060010190602001808311610bb257829003601f168201915b5050965050505050505060405180910390a160008381526101096020908152604082208054600160a060020a031916815560018101839055600281018054848255908452828420919392610c3292601f9290920104810190610a65565b50919050565b50505060019150506101aa565b60008581526101036020526040812080549093501415610cc7576000805483556001838101919091556101048054918201808255828015829011610c9657818360005260206000209182019101610c969190610a65565b50505060028301819055610104805487929081101561000257600091909152600080516020610f9f83398151915201555b506001810154600283900a90811660001415610d975760408051600160a060020a03321681526020810187905281517fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda929181900390910190a1815460019011610d84576000858152610103602052604090206002015461010480549091908110156100025760406000908120600080516020610f9f8339815191529290920181905580825560018083018290556002909201559450610d979050565b8154600019018255600182018054821790555b505050919050565b5b60018054118015610dc257506001546002906101008110156100025701546000145b15610dd65760018054600019019055610da0565b60015481108015610df95750600154600290610100811015610002570154600014155b8015610e1357506002816101008110156100025701546000145b15610e7457600154600290610100811015610002578101549082610100811015610002578101919091558190610102906000908361010081101561000257810154825260209290925260408120929092556001546101008110156100025701555b610691565b156101aa5761010754610e8f5b62015180420490565b1115610ea857600061010655610ea3610e86565b610107555b6101065480830110801590610ec65750610106546101055490830111155b15610edc575061010680548201905560016101aa565b5060006101aa565b61063c6101045460005b81811015610f745761010480548290811015610002576000918252600080516020610f9f833981519152015414610f6157610104805461010391600091849081101561000257600080516020610f9f83398151915201548252506020919091526040812081815560018101829055600201555b600101610eee565b50505060010161052f565b61010480546000808355919091526104dd90600080516020610f9f83398151915290810190610a6556004c0be60200faa20559308cb7b5a1bb3255c16cb1cab91f525b5ae7a03d02fabe"
+ },
+ "0x4f5777744b500616697cb655dcb02ee6cd51deb5": {
+ "balance": "0xb0983f1b83eec290",
+ "nonce": "2"
+ },
+ "0xf8b483dba2c3b7176a3da549ad41a48bb3121069": {
+ "balance": "0x16969a0ba2c2d384d07",
+ "nonce": "67521"
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "995201",
+ "difficulty": "11940626048551",
+ "timestamp": "1455322773",
+ "gasLimit": "3141592",
+ "miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069"
+ },
+ "input": "0xf89102850a954d522e8303308594200edd17f30485a8735878661960cd7a9a95733f888ac7230489e80000a4ba51a6df00000000000000000000000000000000000000000000000000000000000000001ca04f2cc45b96f965296382b2e9b657e90808301d5179035a5d91a2de7b912def20a056e19271ea4e19e4e034f38e925e312beed4d300c267160eeb2f565c42deb578",
+ "tracerConfig": {
+ "withLog": true
+ },
+ "result": {
+ "from": "0x4f5777744b500616697cb655dcb02ee6cd51deb5",
+ "gas": "0x2dced",
+ "gasUsed": "0x1a9e5",
+ "to": "0x200edd17f30485a8735878661960cd7a9a95733f",
+ "input": "0xba51a6df0000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0xba51a6df00000000000000000000000000000000000000000000000000000000",
+ "calls": [
+ {
+ "from": "0x200edd17f30485a8735878661960cd7a9a95733f",
+ "gas": "0x2c263",
+ "gasUsed": "0x1b0e4",
+ "to": "0x273930d21e01ee25e4c219b63259d214872220a2",
+ "input": "0xba51a6df0000000000000000000000000000000000000000000000000000000000000000",
+ "logs": [
+ {
+ "address": "0x200edd17f30485a8735878661960cd7a9a95733f",
+ "topics": [
+ "0xe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda"
+ ],
+ "data": "0x0000000000000000000000004f5777744b500616697cb655dcb02ee6cd51deb5be96016bb57376da7a6d296e0a405ee1501778227dfa604df0a81cb1ae018598"
+ },
+ {
+ "address": "0x200edd17f30485a8735878661960cd7a9a95733f",
+ "topics": [
+ "0xacbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x8ac7230489e80000",
+ "type": "CALLCODE"
+ }
+ ],
+ "value": "0x8ac7230489e80000",
+ "type": "CALL"
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json
new file mode 100644
index 0000000000..2c82138022
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json
@@ -0,0 +1,408 @@
+{
+ "genesis": {
+ "difficulty": "80344740444880",
+ "extraData": "0x7777772e62772e636f6d",
+ "gasLimit": "1498600",
+ "hash": "0xf5d85a80bdbc5d28a16b8eb0d1b9dd18316ddc3655c7d5c901b67acdb7700037",
+ "miner": "0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1",
+ "mixHash": "0x433ae590edf0e7ba9aac698bb7d3be2300e3e79d175db13528ff3e79a3f93910",
+ "nonce": "0x084adce0020c6fd8",
+ "number": "2340152",
+ "stateRoot": "0x38295a2634c9c62d48bcbf2ef2ae83768b9055c1f5e6469d17a5d1bcb052072e",
+ "timestamp": "1475034708",
+ "totalDifficulty": "66488249547380413902",
+ "alloc": {
+ "0x01e60b511fced1eb2b5b40991eb1dfd171a6df42": {
+ "balance": "0x0",
+ "code": "0x6060604052361561008d5760e060020a600035046306fdde03811461008f578063095ea7b3146100a557806318160ddd1461012457806323b872dd1461012f578063313ce567146101dc578063475a9fa9146101f057806370a0823114610215578063721a37d21461024357806395d89b411461008f578063a9059cbb14610268578063dd62ed3e146102e7575b005b61031d6040805160208101909152600081525b90565b61038b60043560243560007319ee743d2e356d5f0e4d97cc09b96d06e933d0db63c6605267600160005085856040518460e060020a0281526004018084815260200183600160a060020a0316815260200182815260200193505050506020604051808303818660325a03f4156100025750506040515191506103179050565b6102316003546100a2565b61038b60043560243560443560008054604080517fa00bfa1100000000000000000000000000000000000000000000000000000000815260016004820152600160a060020a038781166024830152868116604483015260648201869052929092166084830152517319ee743d2e356d5f0e4d97cc09b96d06e933d0db9163a00bfa119160a482810192602092919082900301818660325a03f4156100025750506040515195945050505050565b604080516000815290519081900360200190f35b61038b6004356024356000805433600160a060020a0390811691161461039f57610002565b600160a060020a03600435166000908152600160205260409020545b60408051918252519081900360200190f35b61038b6004356024356000805433600160a060020a039081169116146103ce57610002565b61038b60043560243560007319ee743d2e356d5f0e4d97cc09b96d06e933d0db6388d5fecb600160005085856040518460e060020a0281526004018084815260200183600160a060020a0316815260200182815260200193505050506020604051808303818660325a03f4156100025750506040515191506103179050565b610231600435602435600160a060020a038281166000908152600260209081526040808320938516835292905220545b92915050565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561037d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080519115158252519081900360200190f35b50600160a060020a03821660009081526001602081905260409091208054830190556003805483019055610317565b600160a060020a038316600090815260016020526040902054821161040a57506040600020805482900390556003805482900390556001610317565b50600061031756",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000005aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000012098a4651fb262f7",
+ "0xfae22198212900725daa5db635d1fda7b0fa195adaabdc806a7267959c3d8ae4": "0x00000000000000000000000000000000000000000000000026cbcbc35aaa62f7"
+ }
+ },
+ "0x19ee743d2e356d5f0e4d97cc09b96d06e933d0db": {
+ "balance": "0x0",
+ "code": "0x6503060000000050606060405260e060020a600035046388d5fecb811461003c578063a00bfa11146100e3578063c6605267146102dc575b610007565b610356600435602435604435600160a060020a0333166000908152602084905260408120548290108015906100715750600082115b1561036a57600160a060020a0333811660008181526020878152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a350600161034f565b610356600435602435604435606435608435600160a060020a03841660009081526020869052604081205483901080159061011e5750600083115b80156101bb5750600160a060020a0385811660009081526001880160209081526040808320339094168352929052205483901015806101bb575081600160a060020a0316631934d55a86336040518360e060020a0281526004018083600160a060020a0316815260200182600160a060020a03168152602001925050506020604051808303816000876161da5a03f1156100075750506040515190505b1561037257600160a060020a038481166000908152602088815260408083208054880190558884168084528184208054899003905581517f1934d55a00000000000000000000000000000000000000000000000000000000815260048101919091523385166024820152905193861693631934d55a936044838101949383900301908290876161da5a03f115610007575050604051511515905061028957600160a060020a038581166000908152600188016020908152604080832033909416835292905220805484900390555b83600160a060020a031685600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3506001610376565b610356600435602435604435600160a060020a033381166000818152600186016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b9392505050565b604080519115158252519081900360200190f35b50600061034f565b5060005b9594505050505056"
+ },
+ "0x3de712784baf97260455ae25fb74f574ec9c1add": {
+ "balance": "0x23c8352f33854625",
+ "nonce": "80"
+ },
+ "0x5aae5c59d642e5fd45b427df6ed478b49d55fefd": {
+ "balance": "0x0",
+ "nonce": "29",
+ "code": "0x606060405236156100cf5760e060020a600035046307d5b82681146100d157806315e812ad146101775780631934d55a1461018d5780631d007f5f146101c65780631f0c1e0c146101ee5780633983d5c41461022b5780634025b29314610243578063428d64bd1461030f578063481b659d146104b557806357bcccb6146104f45780638c172fa21461052f5780639ba5b4e9146105ea578063a4a7cf5c146106ca578063b11e3b82146106ed578063c51cf179146107a6578063d6911046146107c2578063eff6be2f146109cb575b005b6109f2600435602435600082815260016020908152604080832060049081015482517f23b872dd00000000000000000000000000000000000000000000000000000000815233600160a060020a0390811693820193909352308316602482015260448101879052925185948594859493909316926323b872dd9260648281019392829003018187876161da5a03f1156100025750506040515115159050610a6d57610002565b6004545b60408051918252519081900360200190f35b6109f2600435602435600160a060020a0382811660009081526003602090815260408083209385168352929052205460ff165b92915050565b6109f2600435600080546101009004600160a060020a039081163390911614610be757610002565b610a066004356024356000828152600160205260408120600901805483908110156100025750815260209020810154600160a060020a03166101c0565b61017b6004355b600454620f4240908202045b919050565b6109f26004356024356000805b600084815260016020526040902060090154811015610c13576040600090812090859052600160205260090180548290811015610002576000918252604080516020808520909301547f721a37d2000000000000000000000000000000000000000000000000000000008252600160a060020a03338116600484015260248301899052925192169363721a37d293604483810194919391929183900301908290876161da5a03f1156100025750506040515115159050610c8d57610002565b604080516024803560048181013560208181028087018201909752818652610a2396833596939560449501929182919085019084908082843750949650505050505050604080516020818101835260008083528351918201909352828152909190819081905b8551831015610c9f57600091505b600160005060008785815181101561000257602090810290910181015182528101919091526040016000206009015460ff831610156104a957600060016000506000888681518110156100025760209081029091018101518252810191909152604001600020600901805460ff85169081101561000257906000526020600020900160009054906101000a9004600160a060020a0316600160a060020a03166370a08231896040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515191909111159050610f59576001600050600087858151811015610002576020908102909101810151825281019190915260400160002060090154909301600201925b60019290920191610375565b6109f260043533600160a060020a039081166000908152600360209081526040808320938516835292905220805460ff1916600190811790915561023e565b6109f260043533600160a060020a039081166000908152600360209081526040808320938516835292905220805460ff19169055600161023e565b60048035600090815260016020818152604092839020600981015481548551968301546002840154600385015460088601546005870154600688015499880154600790980154958c52600160a060020a03888116998d019990995260a060020a90970460ff90811615158c8c015260608c019390935260808b019190915260a08a019490945290851660c08901529290931660e087015261010086019390935216151561012084015261014083015251908190036101600190f35b60408051600480358082013560208181028086018201909652818552610a23959394602494909385019291829190850190849080828437509496505050505050506040805160208181018352600080835283519182019093528281529091908190815b8551831015610f93576000600260005060008886815181101561000257602090810290910181015182528101919091526040016000205411156106be576002600050600087858151811015610002576020908102909101810151825281019190915260400160002054909301600201925b6001929092019161064d565b61017b6004356000805481908190819081908190819060ff161561115757610002565b6040805160e4356004818101356020818102808601820190965281855261017b95833595602480359660443596606435966084359660a4359660c4359693956101049501929182919085019084908082843750949650505050505050600080808080808d81148061076657508c801561076657508a8c12155b80610774575060028a60ff16105b80610788575087600160a060020a03166000145b8061079c575088600160a060020a03166000145b1561177157611760565b61017b600435600454620f42409081039082020481900361023e565b60408051600480358082013560208181028086018201909652818552610a23959394602494909385019291829190850190849080828437509496505093359350506044359150506064356040805160208181018352600080835283519182019093528281529091908190815b8851831015611cee576000600102600160005060008b8681518110156100025760209081029091018101518252810191909152604001600020541180156108c7575087600160a060020a0316600014806108c7575087600160a060020a0316600160005060008b868151811015610002576020908102909101810151825281019190915260400160002060050154600160a060020a0316145b8015610925575086600160a060020a031660001480610925575086600160a060020a0316600160005060008b868151811015610002576020908102909101810151825281019190915260400160002060040154600160a060020a0316145b8015610983575085600160a060020a031660001480610983575085600160a060020a0316600160005060008b868151811015610002576020908102909101810151825281019190915260400160002060010154600160a060020a0316145b156109bf57600160005060008a858151811015610002576020908102909101810151825281019190915260400160002060090154909301600c01925b6001929092019161082e565b6109f26004356000805433600160a060020a03908116610100909204161461234c57610002565b604080519115158252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019250505060405180910390f35b610a7685610232565b92508285039150600083118015610b00575060008681526001602090815260408083206004908101548251855460e060020a63a9059cbb0282526101009004600160a060020a039081169382019390935260248101899052925191169363a9059cbb936044848101949193929183900301908290876161da5a03f115610002575050604051511590505b15610b0a57610002565b5060005b60008681526001602052604090206009015460ff82161015610bd35760406000908120908790526001602052600901805460ff831690811015610002576000918252604080516020808520909301547f475a9fa9000000000000000000000000000000000000000000000000000000008252600160a060020a03338116600484015260248301889052925192169363475a9fa993604483810194919391929183900301908290876161da5a03f1156100025750506040515115159050610bdf57610002565b50600195945050505050565b600101610b0e565b506000805474ffffffffffffffffffffffffffffffffffffffff0019166101008302179055600161023e565b6000848152600160209081526040808320600490810154825160e060020a63a9059cbb028152600160a060020a033381169382019390935260248101899052925191169363a9059cbb936044848101949193929183900301908290876161da5a03f1156100025750506040515115159050610c9557610002565b600101610250565b5060019392505050565b83604051805910610cad5750595b908082528060200260200182016040528015610cc4575b506000945084935090505b8551831015610f6557600091505b600160005060008785815181101561000257602090810290910181015182528101919091526040016000206009015460ff83161015610f7b57600060016000506000888681518110156100025760209081029091018101518252810191909152604001600020600901805460ff85169081101561000257906000526020600020900160009054906101000a9004600160a060020a0316600160a060020a03166370a08231896040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515191909111159050610f8757858381518110156100025790602001906020020151600190048185815181101561000257602090810290910101528551600190600090889086908110156100025760209081029091018101518252810191909152604001600020600901548151829060018701908110156100025760209081029091010152600091505b600160005060008785815181101561000257602090810290910181015182528101919091526040016000206009015460ff83161015610f6f5760016000506000878581518110156100025760209081029091018101518252810191909152604001600020600901805460ff84169081101561000257906000526020600020900160009054906101000a9004600160a060020a0316600160a060020a03166370a08231886040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f115610002575050604051518251909150829060ff8516870160020190811015610002576020908102909101015260019190910190610e49565b60019190910190610383565b9695505050505050565b6002820160ff16909301925b60019290920191610ccf565b60019190910190610cdd565b83604051805910610fa15750595b908082528060200260200182016040528015610fb8575b506000945084935091505b85518310156111125760006002600050600088868151811015610002576020908102909101810151825281019190915260400160002054111561114b578583815181101561000257906020019060200201516001900482858151811015610002576020908102909101015285516002906000908890869081101561000257602090810290910181015182528101919091526040016000205482518390600187019081101561000257602090810290910101525060005b600260005060008785815181101561000257602090810290910181015182528101919091526040016000205481101561111b5760026000506000878581518110156100025760209081029091018101518252810191909152604001600020805482908110156100025760009182526020909120015482518390868401600201908110156100025760209081029091010152600101611079565b50949350505050565b60026000506000878581518110156100025750506020858102890181015182528290526040902054909401909301925b60019290920191610fc3565b6000805460ff191660019081178255898252602052604090206007015460ff1615156112e85760406000818120600581015483516006909201547f5101770200000000000000000000000000000000000000000000000000000000835260048301529251600160a060020a0393909316926351017702926024838101936020939290839003909101908290876161da5a03f115610002575050604051511515905061120457611338611347565b6000888152600160209081526040808320815160058201546006909201547f5d1a3b8200000000000000000000000000000000000000000000000000000000825260048201529151600160a060020a039190911693635d1a3b82936024808501949193929183900301908290876161da5a03f1156100025750505060405180519060200150600160005060008a600019168152602001908152602001600020600050600801600050819055506001600160005060008a60001916815260200190815260200160002060005060070160006101000a81548160ff021916908302179055505b6000888152600160208190526040909120015460a060020a900460ff16156113535760406000908120908990526001602052600281015460089091015412156115435760009450611598565b8596505b505050505050919050565b6113345b6000805460ff19169055565b6000888152600160205260409020600981018054600890920154909181101561000257600091825260208083206040805193909101547f70a08231000000000000000000000000000000000000000000000000000000008452600160a060020a03338116600486015291519116936370a082319360248181019493929183900301908290876161da5a03f115610002575050604051519650505b600091505b60008881526001602052604090206009015460ff831610156116d65760406000908120908990526001602052600901805460ff84169081101561000257906000526020600020900160009054906101000a9004600160a060020a0316600160a060020a03166370a08231336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f115610002575050604080515160008b81526001602052919091206009018054919350915060ff84169081101561000257906000526020600020900160009054906101000a9004600160a060020a0316600160a060020a031663721a37d233836040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506020604051808303816000876161da5a03f115610002575050604051511515905061175057610002565b60008881526001602052604090206003810154600890910154131561156c576127109450611598565b600088815260016020526040902060028101546003820154600890920154918190039103612710020594505b6000888152600160208190526040909120600901805461271088810361ffff16975087810396509286929181101561000257906000526020600020900160009054906101000a9004600160a060020a0316600160a060020a03166370a08231336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f115610002575050604080515160008d815260016020529182206009018054919094029389935091908110156100025790815260208120909054906101000a9004600160a060020a0316600160a060020a03166370a08231336040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750505060405180519060200150020104955085506113ed565b6000888152600160209081526040808320600490810154825160e060020a63a9059cbb028152600160a060020a0333811693820193909352602481018c9052925191169363a9059cbb936044848101949193929183900301908290876161da5a03f115610002575050604051511515905061134357610002565b600191909101906113f2565b8495505b505050505098975050505050505050565b8d8d8d8d8d8d8d8d604051808960001916815260200188151560f860020a0281526001018781526020018681526020018560ff1660f860020a02815260010184600160a060020a03166c0100000000000000000000000002815260140183600160a060020a03166c010000000000000000000000000281526014018280519060200190602002808383829060006004602084601f0104600302600f01f1509050019850505050505050505060405180910390209450600060010260016000506000876000191681526020019081526020016000206000506000016000505460001916111561185e57611760565b87600160a060020a031663c91d7e9c886040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f150905001925050506040604051808303816000876161da5a03f1156100025750506040518051602091909101519095509350506000841180156119bd575082600160a060020a03166323b872dd3330876040518460e060020a0281526004018084600160a060020a0316815260200183600160a060020a0316815260200182815260200193505050506020604051808303816000876161da5a03f11561000257505060405151159050806119bd575082600160a060020a031663095ea7b389866040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506020604051808303816000876161da5a03f115610002575050604051511590505b156119c757610002565b87600160a060020a031663c1b06513886040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f150905001925050506020604051808303816000876161da5a03f115610002575050604051519250506000821415611a5257610002565b60008e81526002602052604090208054600181018083558281838015829011611a9e57818360005260206000209182019101611a9e91905b80821115611c975760008155600101611a8a565b50505091909060005260206000209001600087909190915055508d60016000506000876000191681526020019081526020016000206000506000016000508190555087600160005060008760001916815260200190815260200160002060005060050160006101000a815481600160a060020a0302191690830217905550816001600050600087600019168152602001908152602001600020600050600601600050819055508c600160005060008760001916815260200190815260200160002060005060010160146101000a81548160ff021916908302179055508b6001600050600087600019168152602001908152602001600020600050600201600050819055508a60016000506000876000191681526020019081526020016000206000506003016000508190555088600160005060008760001916815260200190815260200160002060005060040160006101000a815481600160a060020a030219169083021790555033600160005060008760001916815260200190815260200160002060005060010160006101000a815481600160a060020a0302191690830217905550600090505b8960ff168160ff16101561175c57600085815260016020819052604090912060090180549182018082559091908281838015829011611c9b57600083815260209020611c9b918101908301611a8a565b5090565b5050509190906000526020600020900160006040516104368061236c833901809050604051809103906000f0825473ffffffffffffffffffffffffffffffffffffffff1916179091555050600101611c47565b83604051805910611cfc5750595b908082528060200260200182016040528015611d13575b506000945084935091505b8851831015611fa2576000600102600160005060008b868151811015610002576020908102909101810151825281019190915260400160002054118015611db7575087600160a060020a031660001480611db7575087600160a060020a0316600160005060008b868151811015610002576020908102909101810151825281019190915260400160002060050154600160a060020a0316145b8015611e15575086600160a060020a031660001480611e15575086600160a060020a0316600160005060008b868151811015610002576020908102909101810151825281019190915260400160002060040154600160a060020a0316145b8015611e73575085600160a060020a031660001480611e73575085600160a060020a0316600160005060008b868151811015610002576020908102909101810151825281019190915260400160002060010154600160a060020a0316145b15611fe5578883815181101561000257906020019060200201516001900482858151811015610002576020908102909101015288516001906000908b908690811015610002576020908102909101810151825281019190915260400160002054825183906001870190811015610002576020908102909101015288516001906000908b9086908110156100025760209081029091018101518252810191909152604001600020600101548251600160a060020a03919091169083906002870190811015610002576020908102909101015288516001906000908b90869081101561000257602090810290910181015182528101919091526040016000206001015460a060020a900460ff1615611ff157600182856003018151811015610002576020908102909101015261200c565b50979650505050505050565b600160005060008a858151811015610002576020908102909101810151825281019190915260400160002060090154909301600c01925b60019290920191611d1e565b60008285600301815181101561000257602090810290910101525b600160005060008a858151811015610002576020908102909101810151825281019190915260400160002060020154825183906004870190811015610002576020908102909101015288516001906000908b908690811015610002576020908102909101810151825281019190915260400160002060030154825183906005870190811015610002576020908102909101015288516001906000908b9086908110156100025760209081029091018101518252810191909152604001600020600401548251600160a060020a03919091169083906006870190811015610002576020908102909101015288516001906000908b9086908110156100025760209081029091018101518252810191909152604001600020600501548251600160a060020a03919091169083906007870190811015610002576020908102909101015288516001906000908b908690811015610002576020908102909101810151825281019190915260400160002060060154825183906008870190811015610002576020908102909101015288516001906000908b90869081101561000257602090810290910181015182528101919091526040016000206007015460ff16156121ee576001828560090181518110156100025760209081029091010152612209565b60008285600901815181101561000257602090810290910101525b600160005060008a85815181101561000257602090810290910181015182528101919091526040016000206008015482518390600a870190811015610002576020908102909101015288516001906000908b90869081101561000257602090810290910181015182528101919091526040016000206009015482518390600b87019081101561000257602090810290910101525060005b600160005060008a858151811015610002576020908102909101810151825281019190915260400160002060090154811015611fae57600160005060008a858151811015610002576020908102909101810151825281019190915260400160002060090180548290811015610002576000918252602090912001548251600160a060020a0391909116908390868401600c019081101561000257602090810290910101526001016122a0565b620f424082101561236457506004819055600161023e565b50600061023e56606060405260008054600160a060020a03191633179055610412806100246000396000f36060604052361561008d5760e060020a600035046306fdde03811461008f578063095ea7b3146100a557806318160ddd1461012457806323b872dd1461012f578063313ce567146101dc578063475a9fa9146101f057806370a0823114610215578063721a37d21461024357806395d89b411461008f578063a9059cbb14610268578063dd62ed3e146102e7575b005b61031d6040805160208101909152600081525b90565b61038b60043560243560007319ee743d2e356d5f0e4d97cc09b96d06e933d0db63c6605267600160005085856040518460e060020a0281526004018084815260200183600160a060020a0316815260200182815260200193505050506020604051808303818660325a03f4156100025750506040515191506103179050565b6102316003546100a2565b61038b60043560243560443560008054604080517fa00bfa1100000000000000000000000000000000000000000000000000000000815260016004820152600160a060020a038781166024830152868116604483015260648201869052929092166084830152517319ee743d2e356d5f0e4d97cc09b96d06e933d0db9163a00bfa119160a482810192602092919082900301818660325a03f4156100025750506040515195945050505050565b604080516000815290519081900360200190f35b61038b6004356024356000805433600160a060020a0390811691161461039f57610002565b600160a060020a03600435166000908152600160205260409020545b60408051918252519081900360200190f35b61038b6004356024356000805433600160a060020a039081169116146103ce57610002565b61038b60043560243560007319ee743d2e356d5f0e4d97cc09b96d06e933d0db6388d5fecb600160005085856040518460e060020a0281526004018084815260200183600160a060020a0316815260200182815260200193505050506020604051808303818660325a03f4156100025750506040515191506103179050565b610231600435602435600160a060020a038281166000908152600260209081526040808320938516835292905220545b92915050565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561037d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080519115158252519081900360200190f35b50600160a060020a03821660009081526001602081905260409091208054830190556003805483019055610317565b600160a060020a038316600090815260016020526040902054821161040a57506040600020805482900390556003805482900390556001610317565b50600061031756",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000950ca4a06c78934a148b7a3ff3ea8fc366f77a0600",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x00000000000000000000000000000000000000000000000000000000000007d0",
+ "0x6b8ad191d0fa8204d4eafca22ce4ec42425fde2eecf25ce484ecc76765b9a937": "0x00000000000000000000000001e60b511fced1eb2b5b40991eb1dfd171a6df42",
+ "0x6b8ad191d0fa8204d4eafca22ce4ec42425fde2eecf25ce484ecc76765b9a938": "0x000000000000000000000000f4cbd7e037b80c2e67b80512d482685f15b1fb28",
+ "0x71dbd1e5cfc57324881ede454ea48ef3502c5c0b0454ccd622624a7061c2e854": "0x446374989d279847d0dbc6708a9c76a419fe9831d42c78bc89473f559a00d915",
+ "0x71dbd1e5cfc57324881ede454ea48ef3502c5c0b0454ccd622624a7061c2e855": "0x00000000000000000000000061d76c05cd2aa9ed5135e21e52fff188b02089d4",
+ "0x71dbd1e5cfc57324881ede454ea48ef3502c5c0b0454ccd622624a7061c2e856": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x71dbd1e5cfc57324881ede454ea48ef3502c5c0b0454ccd622624a7061c2e857": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x71dbd1e5cfc57324881ede454ea48ef3502c5c0b0454ccd622624a7061c2e858": "0x00000000000000000000000092f1dbea03ce08225e31e95cc926ddbe0198e6f2",
+ "0x71dbd1e5cfc57324881ede454ea48ef3502c5c0b0454ccd622624a7061c2e859": "0x000000000000000000000000529c4cb814029b8bb32acb516ea3a4b07fdae350",
+ "0x71dbd1e5cfc57324881ede454ea48ef3502c5c0b0454ccd622624a7061c2e85a": "0x846fd373887ade3ab7703750294876afa61cf56303f5f014a4d80d04f508a1f1",
+ "0x71dbd1e5cfc57324881ede454ea48ef3502c5c0b0454ccd622624a7061c2e85b": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x71dbd1e5cfc57324881ede454ea48ef3502c5c0b0454ccd622624a7061c2e85c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x71dbd1e5cfc57324881ede454ea48ef3502c5c0b0454ccd622624a7061c2e85d": "0x0000000000000000000000000000000000000000000000000000000000000002"
+ }
+ },
+ "0x61c808d82a3ac53231750dadc13c777b59310bd9": {
+ "balance": "0x90a7af5d4755984561",
+ "nonce": "197408"
+ },
+ "0x6ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5": {
+ "balance": "0x0",
+ "code": "0x606060405236156100a35760e060020a6000350463031d973e81146100a557806316181bb7146100da5780635aa97eeb146103b1578063674cc1f5146104f75780636da84ec0146105d7578063929e626e146105fe578063a0bde7e8146106ac578063bbd4f8541461078b578063c1fd43391461098e578063c3c95c7b14610a88578063db833e3a14610afe578063df6c13c314610cfe578063ebb7119414610d13575b005b610d4960043560008181526020819052604081206004015481908390600160a060020a039081163390911614610dcb57610002565b610d0160043560243560443560643560008481526020819052604080822054815160e160020a63460b97d1028152600481018290529151909183918291829182916000805160206123dd83398151915291638c172fa29160248181019261016092909190829003018187876161da5a03f1156100025750506040805160a081015160c08201517fc51cf179000000000000000000000000000000000000000000000000000000008352600483018d90529251909750919550600160a060020a038616926323b872dd92339230929163c51cf1799160248181019260209290919082900301818b876161da5a03f11561000257505060408051805160e060020a6323b872dd028252600160a060020a039586166004830152939094166024850152918d01604484015250516064828101926020929190829003018187876161da5a03f11561000257505060405151159050806102e8575082600160a060020a031663095ea7b36000805160206123dd8339815191526000805160206123dd833981519152600160a060020a031663c51cf1798c6040518260e060020a028152600401808281526020019150506020604051808303816000876161da5a03f11561000257505060408051805160e060020a63095ea7b30282526004820193909352918d0160248301525160448281019350602092829003018187876161da5a03f115610002575050604051511590505b806103a757506000805160206123dd833981519152600160a060020a03166307d5b826866000805160206123dd833981519152600160a060020a031663c51cf1798c6040518260e060020a028152600401808281526020019150506020604051808303816000876161da5a03f11561000257505060408051805160e160020a6303eadc130282526004820194909452928d016024840152516044838101936020935082900301816000876161da5a03f115610002575050604051511590505b15610fcd57610002565b60408051600480358082013560208181028086018201909652818552610d5d9593946024949093850192918291908501908490808284375094965050933593505050506040805160208181018352600080835283519182019093528281529091908190815b86518310156112c757600060010260006000506000898681518110156100025760209081029091018101518252810191909152604001600020541180156104af575085600160a060020a0316600014806104af575085600160a060020a03166000600050600089868151811015610002576020908102909101810151825281019190915260400160002060040154600160a060020a0316145b156104eb576000600050600088858151811015610002576020908102909101810151825281019190915260400160002060070154909301600901925b60019290920191610416565b60408051600480358082013560208181028086018201909652818552610d5d959394602494909385019291829190850190849080828437509496505050505050506040805160208181018352600080835283519182019093528281529091908190815b8551831015611713576000600160005060008886815181101561000257602090810290910181015182528101919091526040016000205411156105cb576001600050600087858151811015610002576020908102909101810151825281019190915260400160002054909301600201925b6001929092019161055a565b610d016004356024355b60009182526020829052604090912060010154620f424091020490565b610da760043561200060405190810160405280610100905b6000815260200190600190039081610616575050604080516120008101909152610100815b600081526020019060019003908161063b5750600090505b60008481526020819052604090206007015460ff821610156118d8576040600020600701805460ff8316908110156100025760009182526020909120810154908390610100811015610002576020020152600101610653565b610d5d600435604080516020818101835260008083528351808301855281815285825291819052835193812060070154929391929091600191909101908059106106f35750595b90808252806020026020018201604052801561070a575b509150428260008151811015610002576020919091019190915290505b60008481526020819052604090206007015460ff821610156118d8576040600020600701805460ff83169081101561000257906000526020600020900160005054828260010160ff1681518110156100025760209081029091010152600101610727565b610d0160043560243560443560643560008481526020819052604080822054815160e160020a63460b97d102815260048101919091529051829182918291829182916000805160206123dd83398151915291638c172fa29160248181019261016092909190829003018187876161da5a03f1156100025750505060405180519060200180519060200180519060200180519060200180519060200180519060200180519060200180519060200180519060200180519060200180519060200150505050509a50505050505050600060005060008b60001916815260200190815260200160002060005060050160009054906101000a9004600160a060020a0316600160a060020a0316630439978d8b600060005060008e60001916815260200190815260200160002060005060030160005054600060005060008f6000191681526020019081526020016000206000506007016000508d8d6040518660e060020a0281526004018086600019168152602001858152602001806020018460ff168152602001838152602001828103825285818154815260200191508054801561095657602002820191906000526020600020905b81600050548152602001906001019080831161093f575b505096505050505050506020604051808303816000876161da5a03f1156100025750506040515194505060008414156118e357610fc0565b610d01600435602435604435606435600060006000600060006000805160206123dd833981519152600160a060020a0316638c172fa28a6040518260e060020a0281526004018082600019168152602001915050610160604051808303816000876161da5a03f1156100025750506040805160a081015160c08201518d83526c01000000000000000000000000600160a060020a033381168202602086810191909152908d16909102603485015284516048948190039490940190932080875292869052928520600301549097509195509350821415905080610a7357506207a12088115b80610a7e5750836000145b15611cc857611cbc565b60048035600090815260208181526040918290206002810154815484516001840154600385015497850154600586015460069096015493835295820152808601929092526060820195909552600160a060020a039283166080820152911660a082015260c0810192909252519081900360e00190f35b610d0160043560243560443560643560008481526020819052604080822054815160e160020a63460b97d10281526004810191909152905182918291829182916000805160206123dd83398151915291638c172fa291602482810192610160929190829003018187876161da5a03f1156100025750505060405180519060200180519060200180519060200180519060200180519060200180519060200180519060200180519060200180519060200180519060200180519060200150505050509950505050505050600060005060008a60001916815260200190815260200160002060005060050160009054906101000a9004600160a060020a0316600160a060020a031663f47cd6718a600060005060008d60001916815260200190815260200160002060005060030160005054600060005060008e6000191681526020019081526020016000206000506007016000508c8c6040518660e060020a0281526004018086600019168152602001858152602001806020018460ff1681526020018381526020018281038252858181548152602001915080548015610cc657602002820191906000526020600020905b816000505481526020019060010190808311610caf575b505096505050505050506020604051808303816000876161da5a03f11561000257505060405151935050600083141561201957611cbc565b60005b60408051918252519081900360200190f35b610d0160043560008181526020819052604081206004015481908190849033600160a060020a039081169116146122df57610002565b604080519115158252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019250505060405180910390f35b6040518082612000808381846000600461030ff15090500191505060405180910390f35b600091505b60008481526020819052604090206007015460ff83161015610eed576040600081812086825260208281528351915460e260020a6307c30783028352600483015260ff8616602483015292516000805160206123dd83398151915293631f0c1e0c9360448481019492939283900301908290876161da5a03f115610002575050604080515160008781526020819052919091206007018054600160a060020a0392909216925063a9059cbb9133919060ff871690811015610002579060005260206000209001600050546040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506020604051808303816000876161da5a03f1156100025750506040515115159050610f7357610002565b60406000908120602082815282825560018201839055600282018390556003820183905560048201805473ffffffffffffffffffffffffffffffffffffffff19908116909155600583018054909116905560068201839055600782018054848255908452908320919291610fa7918101905b80821115610fb45760008155600101610f5f565b6000848152602081905260408120600701805460ff8516908110156100025790825260208220015560019190910190610dd0565b5060019695505050505050565b5090565b818385010195505b5050505050949350505050565b6040805160e260020a6307c307830281526004810187905260ff8b16602482015290516000805160206123dd83398151915291631f0c1e0c91604482810192602092919082900301816000876161da5a03f11561000257505060408051805160e060020a63095ea7b302825230600160a060020a039081166004840152602483018d905292519216925063095ea7b391604482810192602092919082900301816000876161da5a03f115610002575050604051511515905061108e57610002565b604080517fdb833e3a000000000000000000000000000000000000000000000000000000008152600481018c905260ff8b166024820152604481018a905260648101899052905130600160a060020a03169163db833e3a91608482810192602092919082900301816000876161da5a03f11561000257505060405151925050600082141561111b57610002565b5060005b838160ff1610156111f75760ff808a169082161461125b576040805160e260020a6307c307830281526004810187905260ff8316602482015290516000805160206123dd83398151915291631f0c1e0c91604482810192602092919082900301816000876161da5a03f11561000257505060408051805160e060020a63a9059cbb028252600160a060020a033381166004840152602483018d905292519216925063a9059cbb91604482810192602092919082900301816000876161da5a03f115610002575050604051511515905061125b57610002565b82600160a060020a031663a9059cbb33846040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506020604051808303816000876161da5a03f115610002575050604051511515905061126357610002565b60010161111f565b816000805160206123dd833981519152600160a060020a031663c51cf1798a6040518260e060020a028152600401808281526020019150506020604051808303816000876161da5a03f115610002575050604051518a01919091039650610fc09050565b836040518059106112d55750595b9080825280602002602001820160405280156112ec575b506000945084935091505b86518310156116c65760006001026000600050600089868151811015610002576020908102909101810151825281019190915260400160002054118015611390575085600160a060020a031660001480611390575085600160a060020a03166000600050600089868151811015610002576020908102909101810151825281019190915260400160002060040154600160a060020a0316145b1561170757868381518110156100025790602001906020020151600190048285815181101561000257602090810290910101528651600090819089908690811015610002576020908102909101810151825281019190915260400160002054825183906001870190811015610002576020908102909101015286516000908190899086908110156100025760209081029091018101518252810191909152604001600020600101548251839060028701908110156100025760209081029091010152865160009081908990869081101561000257602090810290910181015182528101919091526040016000206002015482518390600387019081101561000257602090810290910101528651600090819089908690811015610002576020908102909101810151825281019190915260400160002060030154825183906004870190811015610002576020908102909101015286516000908190899086908110156100025760209081029091018101518252810191909152604001600020600401548251600160a060020a03919091169083906005870190811015610002576020908102909101015286516000908190899086908110156100025760209081029091018101518252810191909152604001600020600501548251600160a060020a03919091169083906006870190811015610002576020908102909101015286516000908190899086908110156100025760209081029091018101518252810191909152604001600020600601548251839060078701908110156100025760209081029091010152865160009081908990869081101561000257602090810290910181015182528101919091526040016000206007015482518390600887019081101561000257602090810290910101525060005b60006000506000888581518110156100025760209081029091018101518252810191909152604001600020600701548110156116d0576000600050600088858151811015610002576020908102909101810151825281019190915260400160002060070180548290811015610002579060005260206000209001600050548282866009010181518110156100025760209081029091010152600101611626565b5095945050505050565b6000600050600088858151811015610002576020908102909101810151825281019190915260400160002060070154909301600901925b600192909201916112f7565b836040518059106117215750595b908082528060200260200182016040528015611738575b506000945084935091505b8551831015611892576000600160005060008886815181101561000257602090810290910181015182528101919091526040016000205411156118cc578583815181101561000257906020019060200201516001900482858151811015610002576020908102909101015285516001906000908890869081101561000257602090810290910181015182528101919091526040016000205482518390600187019081101561000257602090810290910101525060005b600160005060008785815181101561000257602090810290910181015182528101919091526040016000205481101561189b57600160005060008785815181101561000257602090810290910181015182528101919091526040016000208054829081101561000257600091825260209091200154825183908684016002019081101561000257602090810290910101526001016117f9565b50949350505050565b6001600050600087858151811015610002575050602085810289018101518252919091526040902054909301600201925b60019290920191611743565b8192505b5050919050565b6118ed8a856105e1565b92506000805160206123dd833981519152600160a060020a031663c51cf179896040518260e060020a028152600401808281526020019150506020604051808303816000876161da5a03f1156100025750506040515192505083830182018790111561195857610fc0565b84600160a060020a03166323b872dd333085878901016040518460e060020a0281526004018084600160a060020a0316815260200183600160a060020a0316815260200182815260200193505050506020604051808303816000876161da5a03f1156100025750506040515115905080611a3557506040805160e060020a63095ea7b30281526000805160206123dd833981519152600482015285840160248201529051600160a060020a0387169163095ea7b391604482810192602092919082900301816000876161da5a03f115610002575050604051511590505b80611aa9575060008a81526020818152604080832054815160e160020a6303eadc130281526004810191909152878601602482015290516000805160206123dd833981519152936307d5b826936044848101949193929183900301908290876161da5a03f115610002575050604051511590505b15611ab357610002565b5060005b60008a81526020819052604090206007015460ff82161015611b06576040600020600701805485919060ff84169081101561000257600091825260209091200180549091019055600101611ab7565b604060009081208b8252602091909152600701805460ff8b169081101561000257600091825260209091200154881115611b3f57610002565b60008a815260208190526040902060028101805485019055600701805489919060ff8c1690811015610002579060005260206000209001600050805491909103905560008a81526020818152604080832054815160e260020a6307c30783028152600481019190915260ff8d16602482015290516000805160206123dd83398151915293631f0c1e0c936044848101949193929183900301908290876161da5a03f11561000257505060408051805160e060020a63a9059cbb028252600160a060020a033381166004840152602483018d905292519216925063a9059cbb91604482810192602092919082900301816000876161da5a03f1156100025750506040515115159050610fb857610002565b505050600092835250602080832090910184905583825281905260409020600181018990556003810188905589815560048101805473ffffffffffffffffffffffffffffffffffffffff199081163317909155600582018054909116881790554360069091015590935083905b50505050949350505050565b82600160a060020a03166323b872dd33306000805160206123dd833981519152600160a060020a031663c51cf1798c6040518260e060020a028152600401808281526020019150506020604051808303816000876161da5a03f11561000257505060408051805160e060020a6323b872dd028252600160a060020a039586166004830152939094166024850152918c0160448401525051606482810192602092919082900301816000876161da5a03f1156100025750506040515115905080611e45575082600160a060020a031663095ea7b36000805160206123dd8339815191526000805160206123dd833981519152600160a060020a031663c51cf1798b6040518260e060020a028152600401808281526020019150506020604051808303816000876161da5a03f11561000257505060408051805160e060020a63095ea7b30282526004820193909352918c016024830152516044828101935060209282900301816000876161da5a03f115610002575050604051511590505b80611f0457506000805160206123dd833981519152600160a060020a03166307d5b8268a6000805160206123dd833981519152600160a060020a031663c51cf1798b6040518260e060020a028152600401808281526020019150506020604051808303816000876161da5a03f11561000257505060408051805160e160020a6303eadc130282526004820194909452928c016024840152516044838101936020935082900301816000876161da5a03f115610002575050604051511590505b15611f0e57610002565b83604051805910611f1c5750595b908082528060200260200182016040528015611f33575b506000838152602081815260408220600701805484518083558285529383902091949082019392018215611f86579160200282015b82811115611f86578251826000505591602001919060010190611f68565b50611f92929150610f5f565b5050600090505b838160ff161015611fda576000828152602081905260409020600701805488919060ff84169081101561000257600091825260209091200155600101611f99565b600089815260016020819052604090912080549182018082559091908281838015829011611c4f57600083815260209020611c4f918101908301610f5f565b61202389846105e1565b915085828403101561203457611cbc565b60008981526020818152604080832054815160e260020a6307c30783028152600481019190915260ff8c16602482015290516000805160206123dd83398151915293631f0c1e0c936044848101949193929183900301908290876161da5a03f11561000257505060408051805160e060020a6323b872dd028252600160a060020a0333811660048401523081166024840152604483018c90529251921692506323b872dd91606482810192602092919082900301816000876161da5a03f115610002575050604051511590508061218d57506000805160206123dd833981519152600160a060020a0316634025b293600060005060008c60001916815260200190815260200160002060005060000160005054856040518360e060020a0281526004018083600019168152602001828152602001925050506020604051808303816000876161da5a03f115610002575050604051511590505b1561219757610002565b6000898152602081905260409020600701805488919060ff8b16908110156100025760009182526020822001805490920190915590505b60008981526020819052604090206007015460ff82161015612254576040600020600701805484919060ff84169081101561000257600091825260209091200154106122d0576000898152602081905260409020600701805484919060ff84169081101561000257906000526020600020900160005080549190910390556001016121ce565b600089815260208181526040808320600201805486019055805160e060020a63a9059cbb028152600160a060020a033381166004830152868803602483015291519188169363a9059cbb93604483810194919391929183900301908290876161da5a03f11561000257505060405151151590506122d557610002565b610002565b8183039450611cbc565b60008581526020819052604080822054815160e160020a63460b97d1028152600481019190915290516000805160206123dd83398151915292638c172fa292602481810193610160939092839003909101908290876161da5a03f1156100025750506040805160c001516000888152602081905291822060020180549083905590955093508311905080156123ca575082600160a060020a031663a9059cbb33846040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506020604051808303816000876161da5a03f115610002575050604051511590505b156123d457610002565b819350506118dc560000000000000000000000005aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "storage": {
+ "0x50ff25f5e9a51687bca1c50f3544d5eef8202f228d3de791691a137aecb6b360": "0x00000000000000000000000000000000000000000000000026566ea1ec2f6a9b",
+ "0x50ff25f5e9a51687bca1c50f3544d5eef8202f228d3de791691a137aecb6b361": "0x00000000000000000000000000000000000000000000000072aa5b7e04d56a9b",
+ "0x642f3c12d3cd25d9b946d8c2ec97f080f4efcff18301a6dcade5a6be0c5ed86c": "0xd9a4ffe21d19763887176173d08241e8393c1dfd208f29193dfecdf854b664ac",
+ "0x642f3c12d3cd25d9b946d8c2ec97f080f4efcff18301a6dcade5a6be0c5ed86d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x642f3c12d3cd25d9b946d8c2ec97f080f4efcff18301a6dcade5a6be0c5ed86e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x642f3c12d3cd25d9b946d8c2ec97f080f4efcff18301a6dcade5a6be0c5ed86f": "0x0000000000000000000000000000000000000000000000004563918244f40000",
+ "0x642f3c12d3cd25d9b946d8c2ec97f080f4efcff18301a6dcade5a6be0c5ed871": "0x0000000000000000000000008695e5e79dab06fbbb05f445316fa4edb0da30f0",
+ "0x642f3c12d3cd25d9b946d8c2ec97f080f4efcff18301a6dcade5a6be0c5ed873": "0x0000000000000000000000000000000000000000000000000000000000000002"
+ }
+ },
+ "0x8695e5e79dab06fbbb05f445316fa4edb0da30f0": {
+ "balance": "0x0",
+ "code": "0x606060405260e060020a60003504630439978d811461003157806308f028d514610115578063f47cd6711461026e575b005b60408051604435600481810135602081810285810182019096528185526103509583359560248035966064959294910192829185019084908082843750949650509335935050608435915050600060006040604051908101604052806002905b60008152602001906001900390816100915790505060006000600061271073ef3487d24a0702703e04a26cef479e313c8fc7ae6324d4e90a604060020a8c51026040518260e060020a028152600401808281526020019150506020604051808303818660325a03f41561000257505060405151919091049550610398905089610157565b60408051600480358082013560208181028086018201909652818552610362959394602494909385019291829190850190849080828437509496505050505050505b6040604051908101604052806002905b6000815260200190600190039081610167575050604080518082019091526002815b600081526020019060019003908161018957905050600083600081518110156100025760209081029091010151825283518490600090811015610002576020908102909101810151908301525060005b83518160ff1610156104a7578351825190859060ff8416908110156100025790602001906020020151101561022357838160ff168151811015610002576020908102909101015182525b60208201518451859060ff8416908110156100025790602001906020020151111561026657838160ff168151811015610002576020908102909101810151908301525b6001016101d9565b60408051604435600481810135602081810285810182019096528185526103509583359560248035966064959294910192829185019084908082843750949650509335935050608435915050600060006040604051908101604052806002905b60008152602001906001900390816102ce579050506000600061271073ef3487d24a0702703e04a26cef479e313c8fc7ae6324d4e90a604060020a8b51026040518260e060020a028152600401808281526020019150506020604051808303818660325a03f415610002575050604051519190910494506104ae905088610157565b60408051918252519081900360200190f35b60408051908190839080838184600060046015f15090500191505060405180910390f35b8095505b505050505095945050505050565b935061044d85858b8d5b6000806127108304815b85518160ff16101561051d5773ef3487d24a0702703e04a26cef479e313c8fc7ae63872fb2b589848a6000909060200201518a8660ff1681518110156100025760209081029091010151038b600060200201518c600190906020020151030304026040518260e060020a028152600401808281526020019150506020604051808303818660325a03f4156100025750506040515190930192506001016103ac565b925086898960ff16815181101561000257602090810290910101805191909103905261047b85858b8d6103a2565b915050604060020a620186a0620186a28484036127108d0402020404868111156103865786955061038a565b5092915050565b6020810180518801905292506104c684848a8c6103a2565b915085888860ff16815181101561000257602090810290910101805190910190526104f384848a8c6103a2565b9050604060020a620186a06127108b04838503026201869e02040494505050505095945050505050565b87604060020a73ef3487d24a0702703e04a26cef479e313c8fc7ae6324d4e90a866040518260e060020a028152600401808281526020019150506020604051808303818660325a03f4156100025750506040515190910291909104999850505050505050505056"
+ },
+ "0x92f1dbea03ce08225e31e95cc926ddbe0198e6f2": {
+ "balance": "0xa6e361612cc228000",
+ "code": "0x6060604052361561008d5760e060020a600035046306fdde03811461008f578063095ea7b3146100ed57806318160ddd1461016257806323b872dd1461016b578063313ce567146102565780636c11bcd31461026257806370a08231146102d057806395d89b41146102f5578063a9059cbb14610353578063d0febe4c146103f8578063dd62ed3e14610439575b005b6040805160038054602060026001831615610100026000190190921691909104601f810182900482028401820190945283835261046d939083018282801561052e5780601f106105035761010080835404028352916020019161052e565b61042560043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6104db60025481565b610425600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101be575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101ca5750600082115b1561053657600160a060020a0383811660008181526020818152604080832080548801905588851680845281842080548990039055600183528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a350600161053a565b6104ed60055460ff1681565b61042560043533600160a060020a0316600090815260208190526040812054821161054157604081208054839003905560028054839003905580821180156102c6575060405133600160a060020a0316908290849082818181858883f19350505050155b1561054957610002565b6104db600435600160a060020a0381166000908152602081905260409020545b919050565b61046d6004805460408051602060026000196101006001871615020190941693909304601f8101849004840282018401909252818152929183018282801561052e5780601f106105035761010080835404028352916020019161052e565b61042560043560243533600160a060020a03166000908152602081905260408120548290108015906103855750600082115b156105515733600160a060020a0390811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a350600161015c565b33600160a060020a0316600090815260208190526040902080543490810190915560028054909101905560015b604080519115158252519081900360200190f35b6104db600435602435600160a060020a0382811660009081526001602090815260408083209385168352929052205461015c565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156104cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051918252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b81548152906001019060200180831161051157829003601f168201915b505050505081565b5060005b9392505050565b5060006102f0565b5060016102f0565b50600061015c56",
+ "storage": {
+ "0x3830062b39ca7888048a385f112e36aef7258a27d84eb6e31312c298e5954da3": "0x0000000000000000000000000000000000000000000000035fe3763f1973ab3b",
+ "0x527b1dd758d53f706730e0fb37a8de5c38d8b4cd17fbe1cfa285480a00f55bf4": "0x000000000000000000000000000000000000000000000003ab97b2fc29ad66c6",
+ "0x52cb6de4baff82acfb6977b64d52b9ac011f8af34631d933997b7649a84d716f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8f0cfa08792bcd3de052a3bb7bd54f8a62c44b02ba16ff336e9a881c348cca21": "0x000000000000000000000000000000000000000000046ba103abb9d1301f1b2e",
+ "0xa29249eda6f9f8d0c67b7a4f954f6ba7a9f1bb3f216b2fedc6db8def03c47746": "0x00000000000000000000000000000000000000000000000007a93ebd870d6684",
+ "0xbe1e23f4b08159a01ee61379749e9b484f5947aaeeb008ce7c97d1c56d3eeb8b": "0x0000000000000000000000000000000000000000000000000dfecc50c6f7d5cd"
+ }
+ },
+ "0xef3487d24a0702703e04a26cef479e313c8fc7ae": {
+ "balance": "0x0",
+ "code": "0x6503060000000050606060405260e060020a600035046324d4e90a8114610031578063872fb2b514610078575b610007565b61013c6004356000680171547652b82fe177818080808061014e886000604060020a82048160bf605f5b6001830182146103c4578060020a8410156103ce579050806103d2565b61013c600435604060020a67b17217f7d1cf79ac81830281900482810460020a680100000016aee6e8ef67b172182739bc0e46858406908102869004673d7f78a624cfb9b582800288900490810288900491909101670e359bcfeb6e45319183028890049182028890040167027601df2fc048dc91830288900491820288900401665808a728816ee89183028890049182028890040166095dedef350bc991830288900491820297909704969096019190910182810295905b505050505050919050565b60408051918252519081900360200190f35b94508460020a88049350604060020a9250604060020a600a029150819050604060020a83680443b9c5adb08cc45f0204810390508050604060020a8484020492508250604060020a83680f0a52590f17c71a3f0204810190508050604060020a8484020492508250604060020a83682478f22e787502b0230204810390508050604060020a8484020492508250604060020a836848c6de1480526b8d4c0204810190508050604060020a8484020492508250604060020a836870c18cae824656408c0204810390508050604060020a8484020492508250604060020a8368883c81ec0ce7abebb20204810190508050604060020a8484020492508250604060020a836881814da94fe52ca9f50204810390508050604060020a8484020492508250604060020a8368616361924625d1acf50204810190508050604060020a8484020492508250604060020a836839f9a16fb9292a608d0204810390508050604060020a8484020492508250604060020a83681b3049a5740b21d65f0204810190508050604060020a8484020492508250604060020a836809ee1408bd5ad96f3e0204810390508050604060020a8484020492508250604060020a836802c465c91703b7a7f40204810190508050604060020a8484020492508250604060020a8367918d2d5f045a4d630204810390508050604060020a8484020492508250604060020a836714ca095145f44f780204810190508050604060020a8484020492508250604060020a836701d806fc412c1b990204810390508050604060020a8484020492508250604060020a836613950b4e1e89cc020481019050805085604060020a8383604060020a8902010302049650610131565b5090949350505050565b9150815b5060028282010461005b56"
+ },
+ "0xf4cbd7e037b80c2e67b80512d482685f15b1fb28": {
+ "balance": "0x0",
+ "code": "0x6060604052361561008d5760e060020a600035046306fdde03811461008f578063095ea7b3146100a557806318160ddd1461012457806323b872dd1461012f578063313ce567146101dc578063475a9fa9146101f057806370a0823114610215578063721a37d21461024357806395d89b411461008f578063a9059cbb14610268578063dd62ed3e146102e7575b005b61031d6040805160208101909152600081525b90565b61038b60043560243560007319ee743d2e356d5f0e4d97cc09b96d06e933d0db63c6605267600160005085856040518460e060020a0281526004018084815260200183600160a060020a0316815260200182815260200193505050506020604051808303818660325a03f4156100025750506040515191506103179050565b6102316003546100a2565b61038b60043560243560443560008054604080517fa00bfa1100000000000000000000000000000000000000000000000000000000815260016004820152600160a060020a038781166024830152868116604483015260648201869052929092166084830152517319ee743d2e356d5f0e4d97cc09b96d06e933d0db9163a00bfa119160a482810192602092919082900301818660325a03f4156100025750506040515195945050505050565b604080516000815290519081900360200190f35b61038b6004356024356000805433600160a060020a0390811691161461039f57610002565b600160a060020a03600435166000908152600160205260409020545b60408051918252519081900360200190f35b61038b6004356024356000805433600160a060020a039081169116146103ce57610002565b61038b60043560243560007319ee743d2e356d5f0e4d97cc09b96d06e933d0db6388d5fecb600160005085856040518460e060020a0281526004018084815260200183600160a060020a0316815260200182815260200193505050506020604051808303818660325a03f4156100025750506040515191506103179050565b610231600435602435600160a060020a038281166000908152600260209081526040808320938516835292905220545b92915050565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561037d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080519115158252519081900360200190f35b50600160a060020a03821660009081526001602081905260409091208054830190556003805483019055610317565b600160a060020a038316600090815260016020526040902054821161040a57506040600020805482900390556003805482900390556001610317565b50600061031756",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000005aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000012098a4651fb262f7",
+ "0xfae22198212900725daa5db635d1fda7b0fa195adaabdc806a7267959c3d8ae4": "0x000000000000000000000000000000000000000000000000731fb89f735062f7",
+ "0xfd73dc2251dc113619c6fcc1c142e797f06e77a178cc37fe300a56823b741ef7": "0x0000000000000000000000000000000000000000000000008ac7230489e80000"
+ }
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "2340153",
+ "difficulty": "80383973372327",
+ "timestamp": "1475034716",
+ "gasLimit": "1500062",
+ "miner": "0x61c808d82a3ac53231750dadc13c777b59310bd9"
+ },
+ "input": "0xf8ea508504a817c80083084398946ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba580b884bbd4f854e9efd3ab89acad6a3edf9828c3b00ed1c4a74e974d05d32d3b2fb15aa16fc3770000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000080d29fa5cccfadac1ba0690ce7a4cf8590c636a1799ebf2cc52229714c47da72ee406fb9bd7d29e52440a017b6ce39e8876965afa2a1c579a592eb1af146506ccdbfc2c9ea422b13dca438",
+ "tracerConfig": {
+ "withLog": true
+ },
+ "result": {
+ "from": "0x3de712784baf97260455ae25fb74f574ec9c1add",
+ "gas": "0x7e2c0",
+ "gasUsed": "0x27ec3",
+ "to": "0x6ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "input": "0xbbd4f854e9efd3ab89acad6a3edf9828c3b00ed1c4a74e974d05d32d3b2fb15aa16fc3770000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000080d29fa5cccfadac",
+ "output": "0x00000000000000000000000000000000000000000000000080d29fa5cccfadac",
+ "calls": [
+ {
+ "from": "0x6ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "gas": "0x77e82",
+ "gasUsed": "0x54c",
+ "to": "0x5aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "input": "0x8c172fa2d9a4ffe21d19763887176173d08241e8393c1dfd208f29193dfecdf854b664ac",
+ "output": "0x446374989d279847d0dbc6708a9c76a419fe9831d42c78bc89473f559a00d91500000000000000000000000061d76c05cd2aa9ed5135e21e52fff188b02089d4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000092f1dbea03ce08225e31e95cc926ddbe0198e6f2000000000000000000000000529c4cb814029b8bb32acb516ea3a4b07fdae350846fd373887ade3ab7703750294876afa61cf56303f5f014a4d80d04f508a1f100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "gas": "0x7737b",
+ "gasUsed": "0x3fe1",
+ "to": "0x8695e5e79dab06fbbb05f445316fa4edb0da30f0",
+ "input": "0x0439978de9efd3ab89acad6a3edf9828c3b00ed1c4a74e974d05d32d3b2fb15aa16fc3770000000000000000000000000000000000000000000000004563918244f4000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000026566ea1ec2f6a9b00000000000000000000000000000000000000000000000072aa5b7e04d56a9b",
+ "output": "0x0000000000000000000000000000000000000000000000008060b57e2e0c99aa",
+ "calls": [
+ {
+ "from": "0x8695e5e79dab06fbbb05f445316fa4edb0da30f0",
+ "gas": "0x770ef",
+ "gasUsed": "0xc24",
+ "to": "0xef3487d24a0702703e04a26cef479e313c8fc7ae",
+ "input": "0x24d4e90a0000000000000000000000000000000000000000000000020000000000000000",
+ "output": "0x000000000000000000000000000000000000000000000000b17217f7d1cf79ab",
+ "type": "DELEGATECALL",
+ "value": "0x0"
+ },
+ {
+ "from": "0x8695e5e79dab06fbbb05f445316fa4edb0da30f0",
+ "gas": "0x75eb2",
+ "gasUsed": "0x265",
+ "to": "0xef3487d24a0702703e04a26cef479e313c8fc7ae",
+ "input": "0x872fb2b5000000000000000000000000000000000000000000000000c330b3f7006420b8",
+ "output": "0x00000000000000000000000000000000000000000000000224bf7df2c80f0878",
+ "type": "DELEGATECALL",
+ "value": "0x0"
+ },
+ {
+ "from": "0x8695e5e79dab06fbbb05f445316fa4edb0da30f0",
+ "gas": "0x75aad",
+ "gasUsed": "0x25b",
+ "to": "0xef3487d24a0702703e04a26cef479e313c8fc7ae",
+ "input": "0x872fb2b50000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x00000000000000000000000000000000000000000000000100000016aee6e8ef",
+ "type": "DELEGATECALL",
+ "value": "0x0"
+ },
+ {
+ "from": "0x8695e5e79dab06fbbb05f445316fa4edb0da30f0",
+ "gas": "0x75737",
+ "gasUsed": "0xc24",
+ "to": "0xef3487d24a0702703e04a26cef479e313c8fc7ae",
+ "input": "0x24d4e90a00000000000000000000000000000000000000000000000324bf7e0976f5f167",
+ "output": "0x0000000000000000000000000000000000000000000000012535c5e5f87ee0d2",
+ "type": "DELEGATECALL",
+ "value": "0x0"
+ },
+ {
+ "from": "0x8695e5e79dab06fbbb05f445316fa4edb0da30f0",
+ "gas": "0x748c7",
+ "gasUsed": "0x265",
+ "to": "0xef3487d24a0702703e04a26cef479e313c8fc7ae",
+ "input": "0x872fb2b5000000000000000000000000000000000000000000000000c330b3f7006420b8",
+ "output": "0x00000000000000000000000000000000000000000000000224bf7df2c80f0878",
+ "type": "DELEGATECALL",
+ "value": "0x0"
+ },
+ {
+ "from": "0x8695e5e79dab06fbbb05f445316fa4edb0da30f0",
+ "gas": "0x744c2",
+ "gasUsed": "0x265",
+ "to": "0xef3487d24a0702703e04a26cef479e313c8fc7ae",
+ "input": "0x872fb2b500000000000000000000000000000000000000000000000237d37fe5d297a500",
+ "output": "0x0000000000000000000000000000000000000000000000093088c407fcbbce38",
+ "type": "DELEGATECALL",
+ "value": "0x0"
+ },
+ {
+ "from": "0x8695e5e79dab06fbbb05f445316fa4edb0da30f0",
+ "gas": "0x74142",
+ "gasUsed": "0xc99",
+ "to": "0xef3487d24a0702703e04a26cef479e313c8fc7ae",
+ "input": "0x24d4e90a00000000000000000000000000000000000000000000000b554841fac4cad6b0",
+ "output": "0x0000000000000000000000000000000000000000000000026d7fc130d6a74cbe",
+ "type": "DELEGATECALL",
+ "value": "0x0"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "gas": "0x731be",
+ "gasUsed": "0x241",
+ "to": "0x5aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "input": "0xc51cf179000000000000000000000000000000000000000000000000de0b6b3a76400000",
+ "output": "0x0000000000000000000000000000000000000000000000000071ea279ec31402",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "gas": "0x72df4",
+ "gasUsed": "0x468b",
+ "to": "0x92f1dbea03ce08225e31e95cc926ddbe0198e6f2",
+ "input": "0x23b872dd0000000000000000000000003de712784baf97260455ae25fb74f574ec9c1add0000000000000000000000006ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba500000000000000000000000000000000000000000000000080d29fa5cccfadac",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x92f1dbea03ce08225e31e95cc926ddbe0198e6f2",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000003de712784baf97260455ae25fb74f574ec9c1add",
+ "0x0000000000000000000000006ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000080d29fa5cccfadac"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "gas": "0x6e627",
+ "gasUsed": "0x56d6",
+ "to": "0x92f1dbea03ce08225e31e95cc926ddbe0198e6f2",
+ "input": "0x095ea7b30000000000000000000000005aae5c59d642e5fd45b427df6ed478b49d55fefd00000000000000000000000000000000000000000000000080d29fa5cccfadac",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x92f1dbea03ce08225e31e95cc926ddbe0198e6f2",
+ "topics": [
+ "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
+ "0x0000000000000000000000006ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "0x0000000000000000000000005aae5c59d642e5fd45b427df6ed478b49d55fefd"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000080d29fa5cccfadac"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "gas": "0x68dae",
+ "gasUsed": "0xd6f0",
+ "to": "0x5aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "input": "0x07d5b826d9a4ffe21d19763887176173d08241e8393c1dfd208f29193dfecdf854b664ac00000000000000000000000000000000000000000000000080d29fa5cccfadac",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0x5aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "gas": "0x629ff",
+ "gasUsed": "0x468b",
+ "to": "0x92f1dbea03ce08225e31e95cc926ddbe0198e6f2",
+ "input": "0x23b872dd0000000000000000000000006ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba50000000000000000000000005aae5c59d642e5fd45b427df6ed478b49d55fefd00000000000000000000000000000000000000000000000080d29fa5cccfadac",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x92f1dbea03ce08225e31e95cc926ddbe0198e6f2",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000006ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "0x0000000000000000000000005aae5c59d642e5fd45b427df6ed478b49d55fefd"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000080d29fa5cccfadac"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x5aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "gas": "0x5e0df",
+ "gasUsed": "0x31af",
+ "to": "0x92f1dbea03ce08225e31e95cc926ddbe0198e6f2",
+ "input": "0xa9059cbb000000000000000000000000950ca4a06c78934a148b7a3ff3ea8fc366f77a060000000000000000000000000000000000000000000000000041f50e27d56848",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x92f1dbea03ce08225e31e95cc926ddbe0198e6f2",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000005aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "0x000000000000000000000000950ca4a06c78934a148b7a3ff3ea8fc366f77a06"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000041f50e27d56848"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x5aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "gas": "0x5ac6b",
+ "gasUsed": "0x29ae",
+ "to": "0x01e60b511fced1eb2b5b40991eb1dfd171a6df42",
+ "input": "0x475a9fa90000000000000000000000006ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba50000000000000000000000000000000000000000000000008090aa97a4fa4564",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x5aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "gas": "0x57fed",
+ "gasUsed": "0x29ae",
+ "to": "0xf4cbd7e037b80c2e67b80512d482685f15b1fb28",
+ "input": "0x475a9fa90000000000000000000000006ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba50000000000000000000000000000000000000000000000008090aa97a4fa4564",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "gas": "0x56030",
+ "gasUsed": "0x265",
+ "to": "0x5aae5c59d642e5fd45b427df6ed478b49d55fefd",
+ "input": "0x1f0c1e0cd9a4ffe21d19763887176173d08241e8393c1dfd208f29193dfecdf854b664ac0000000000000000000000000000000000000000000000000000000000000001",
+ "output": "0x000000000000000000000000f4cbd7e037b80c2e67b80512d482685f15b1fb28",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "gas": "0x55cc3",
+ "gasUsed": "0x339f",
+ "to": "0xf4cbd7e037b80c2e67b80512d482685f15b1fb28",
+ "input": "0xa9059cbb0000000000000000000000003de712784baf97260455ae25fb74f574ec9c1add000000000000000000000000000000000000000000000000de0b6b3a76400000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0xf4cbd7e037b80c2e67b80512d482685f15b1fb28",
+ "gas": "0x55a8a",
+ "gasUsed": "0x30f7",
+ "to": "0x19ee743d2e356d5f0e4d97cc09b96d06e933d0db",
+ "input": "0x88d5fecb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000003de712784baf97260455ae25fb74f574ec9c1add000000000000000000000000000000000000000000000000de0b6b3a76400000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0xf4cbd7e037b80c2e67b80512d482685f15b1fb28",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000006ca7f214ab2ddbb9a8e1a1e2c8550e3164e9dba5",
+ "0x0000000000000000000000003de712784baf97260455ae25fb74f574ec9c1add"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000de0b6b3a76400000"
+ }
+ ],
+ "type": "DELEGATECALL",
+ "value": "0x0"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json
new file mode 100644
index 0000000000..649a5b1b56
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json
@@ -0,0 +1,2295 @@
+{
+ "genesis": {
+ "difficulty": "59917798787272",
+ "extraData": "0xe4b883e5bda9e7a59ee4bb99e9b1bc",
+ "gasLimit": "4712380",
+ "hash": "0xae82afe3630b001a34ad4c51695dacb17872ebee4dadd2de88b1a16671871da4",
+ "miner": "0x61c808d82a3ac53231750dadc13c777b59310bd9",
+ "mixHash": "0x23c2289cdee8a397cf36db9ffa3419503bed54eb09e988b3c7a3587a090e6fc1",
+ "nonce": "0x94dc83e0044f49c8",
+ "number": "1881283",
+ "stateRoot": "0x6e3832bc2e4e66170a1e716449083e08fbb70e7b2a9f1f34e0c57e66ce40c50f",
+ "timestamp": "1468467284",
+ "totalDifficulty": "37186898441932102239",
+ "alloc": {
+ "0x0000000000000000000000000000000000000004": {
+ "balance": "0x0"
+ },
+ "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e": {
+ "balance": "0x0",
+ "code": "0x606060405236156100f05760e060020a600035046303afc23581146100f257806313af4035146101145780631838e26614610136578063186ef9621461014d57806327df8c501461016f578063295d5866146101915780634162169f146101b35780634dfc3db6146101c55780636637b882146101e8578063839d3f7f1461020a57806386c9b5361461021d5780638da5cb5b1461022f5780639093a5e714610241578063b199efb514610263578063b262b9ae14610275578063b9f34aa114610297578063be9a6555146102a9578063d1c3c84a146102c7578063e26fc92b146102d9578063e8d9f074146102eb575b005b6100f0600435600054600160a060020a03908116339091161461034057610002565b6100f0600435600054600160a060020a03908116339091161461035557610002565b6102fd60006000600060006000600061036a6101c9565b6100f0600435600054600160a060020a0390811633909116146108a157610002565b6100f0600435600054600160a060020a0390811633909116146108b657610002565b6100f0600435600054600160a060020a0390811633909116146108cb57610002565b61030f600154600160a060020a031681565b6102fd5b60008054600160a060020a0390811633909116146108e0575060015b90565b6100f0600435600054600160a060020a03908116339091161461098857610002565b61032c60075460a060020a900460ff1681565b61030f600454600160a060020a031681565b61030f600054600160a060020a031681565b6100f0600435600054600160a060020a03908116339091161461099d57610002565b61030f600254600160a060020a031681565b6100f0600435600054600160a060020a0390811633909116146109b257610002565b61030f600754600160a060020a031681565b6100f060005433600160a060020a03908116911614610a1a57610002565b61030f600354600160a060020a031681565b61030f600554600160a060020a031681565b61030f600654600160a060020a031681565b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b60048054600160a060020a0319168217905550565b60008054600160a060020a0319168217905550565b945060008514610380578495505b505050505090565b6002546040805160015460e060020a634162169f0282529151600160a060020a039283169390921691634162169f9160048181019260209290919082900301816000876161da5a03f11561000257505060405151600160a060020a031690911490506103ef5760649550610378565b600260009054906101000a9004600160a060020a0316600160a060020a0316634dfc3db66040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604051519450506000841461045857836064019550610378565b6040805160015460035460e060020a634162169f0283529251600160a060020a039182169390911691634162169f91600482810192602092919082900301816000876161da5a03f11561000257505060405151600160a060020a031690911490506104c65760c89550610378565b600360009054906101000a9004600160a060020a0316600160a060020a0316634dfc3db66040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604051519350506000831461052f578260c8019550610378565b604080516001546004805460e060020a634162169f0284529351600160a060020a039283169490921692634162169f928183019260209282900301816000876161da5a03f11561000257505060405151600160a060020a0316909114905061059b5761012c9550610378565b60408051600480547f4dfc3db60000000000000000000000000000000000000000000000000000000083529251600160a060020a039390931692634dfc3db692808301926020929182900301816000876161da5a03f1156100025750506040515192505060008214610613578161012c019550610378565b6040805160015460055460e060020a634162169f0283529251600160a060020a039182169390911691634162169f91600482810192602092919082900301816000876161da5a03f11561000257505060405151600160a060020a03169091149050610682576101909550610378565b600560009054906101000a9004600160a060020a0316600160a060020a0316634dfc3db66040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060405151915050600081146106ec5780610190019550610378565b6040805160015460065460e060020a634162169f0283529251600160a060020a039182169390911691634162169f91600482810192602092919082900301816000876161da5a03f11561000257505060405151600160a060020a0316909114905061075b576101f49550610378565b6040805160065460e060020a638da5cb5b028252915130600160a060020a03908116931691638da5cb5b91600482810192602092919082900301816000876161da5a03f11561000257505060405151600160a060020a031690911490506107c6576101f59550610378565b6040805160075460015460e060020a634162169f0283529251600160a060020a03938416939190911691634162169f91600482810192602092919082900301816000876161da5a03f11561000257505060405151600160a060020a03169091149050610836576102589550610378565b6040805160075460e060020a638da5cb5b028252915130600160a060020a03908116931691638da5cb5b91600482810192602092919082900301816000876161da5a03f11561000257505060405151600160a060020a03169091149050610378576102599550610378565b60038054600160a060020a0319168217905550565b60058054600160a060020a0319168217905550565b60068054600160a060020a0319168217905550565b600154600160a060020a0316600014156108fc575060026101e5565b600654600160a060020a031660001415610918575060036101e5565b600754600160a060020a031660001415610934575060046101e5565b600254600160a060020a031660001415610950575060056101e5565b600354600160a060020a03166000141561096c575060066101e5565b600454600160a060020a0316600014156101e5575060076101e5565b60018054600160a060020a0319168217905550565b60078054600160a060020a0319168217905550565b60028054600160a060020a0319168217905550565b600260009054906101000a9004600160a060020a0316600160a060020a031663975057e76040518160e060020a0281526004018090506000604051808303816000876161da5a03f115610002575050505b565b610a8a600154604080517f4b6753bc0000000000000000000000000000000000000000000000000000000081529051600092600160a060020a031691634b6753bc916004828101926020929190829003018187876161da5a03f11561000257505060405151421191506101e59050565b1515610a9557610a18565b60075460a060020a900460ff1615610e93576111556040805160015460065460e060020a6370a08231028352600160a060020a039081166004840152925160009384939216916370a08231916024808301926020929190829003018187876161da5a03f1156100025750506040515191909111159050610c61576040805160065460025460015460e060020a6370a08231028452600160a060020a0392831660048501819052945163a9059cbb949284169391909116916370a0823191602482810192602092919082900301818a876161da5a03f11561000257505060408051805160e060020a63a9059cbb028252600482019490945260248101939093525160448084019360209350829003018187876161da5a03f1156100025750506040805160025460e060020a63a8618f71028252600160a060020a031660048201819052915191925063a8618f71916024828101926020929190829003018187876161da5a03f11561000257505060405151159050610c6157600260009054906101000a9004600160a060020a0316600160a060020a031663975057e76040518160e060020a0281526004018090506000604051808303816000876161da5a03f11561000257506001925050505b6001546007546040805160e060020a6370a08231028152600160a060020a0392831660048201529051600093909216916370a0823191602481810192602092909190829003018187876161da5a03f1156100025750506040515191909111159050610e1b576040805160075460025460015460e060020a6370a08231028452600160a060020a0392831660048501819052945163a9059cbb949284169391909116916370a0823191602482810192602092919082900301816000876161da5a03f11561000257505060408051805160e060020a63a9059cbb02825260048201949094526024810193909352516044838101936020935082900301816000876161da5a03f1156100025750506040805160025460e060020a63a8618f71028252600160a060020a031660048201819052915191925063a8618f7191602482810192602092919082900301816000876161da5a03f11561000257505060405151159050610e1b57600260009054906101000a9004600160a060020a0316600160a060020a031663975057e76040518160e060020a0281526004018090506000604051808303816000876161da5a03f11561000257506001925050505b8015610e7257600260009054906101000a9004600160a060020a0316600160a060020a0316632e64cec16040518160e060020a0281526004018090506000604051808303816000876161da5a03f115610002575050505b6007805474ff00000000000000000000000000000000000000001916905550565b600260009054906101000a9004600160a060020a0316600160a060020a0316632e64cec16040518160e060020a0281526004018090506000604051808303816000876161da5a03f115610002575050505b60048054604080517ffc3407160000000000000000000000000000000000000000000000000000000081529051600160a060020a03929092169263fc340716928282019260009290829003018183876161da5a03f115610002575050600354604080517fd95f98ce0000000000000000000000000000000000000000000000000000000081529051600160a060020a0392909216925063d95f98ce916004828101926000929190829003018183876161da5a03f11561000257505050620f42405a11156109c7576109c76001546005546040805160e060020a6370a08231028152600160a060020a039283166004820152905192909116916370a082319160248181019260209290919082900301816000876161da5a03f1156100025750506040515160001415905061108357604080516002546005547fd0679d34000000000000000000000000000000000000000000000000000000008352600160a060020a03908116600484015260016024840152925192169163d0679d349160448181019260209290919082900301816000876161da5a03f115610002575050505b5b600554604080517f400e39490000000000000000000000000000000000000000000000000000000081529051600a92600160a060020a03169163400e394991600482810192602092919082900301816000876161da5a03f1156100025750506040515191909110905080156110fb5750620aae605a115b15610a1857600560009054906101000a9004600160a060020a0316600160a060020a031663ff2f4bd26040518160e060020a0281526004018090506000604051808303816000876161da5a03f11561000257505050611084565b610ee456",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000304a554a310c7e546dfe434669c62820b7d83490",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "0x0000000000000000000000000000000000000000000000000000000000000006": "0x000000000000000000000000c0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89",
+ "0x0000000000000000000000000000000000000000000000000000000000000007": "0x000000000000000000000001f835a0247b0063c04ef22006ebe57c5f11977cc4"
+ }
+ },
+ "0x304a554a310c7e546dfe434669c62820b7d83490": {
+ "balance": "0x3034f5ca7d45e17df1d83",
+ "nonce": "3",
+ "code": "0x6060604052361561020e5760e060020a6000350463013cf08b8114610247578063095ea7b3146102d05780630c3b7b96146103455780630e7082031461034e578063149acf9a1461036057806318160ddd146103725780631f2dc5ef1461037b57806321b5b8dd1461039b578063237e9492146103ad57806323b872dd1461040e5780632632bf2014610441578063341458081461047257806339d1f9081461047b5780634b6753bc146104935780634df6d6cc1461049c5780634e10c3ee146104b7578063590e1ae3146104ca578063612e45a3146104db578063643f7cdd1461057a578063674ed066146105925780636837ff1e1461059b57806370a08231146105e5578063749f98891461060b57806378524b2e1461062457806381f03fcb1461067e57806382661dc41461069657806382bf6464146106b75780638b15a605146106c95780638d7af473146106d257806396d7f3f5146106e1578063a1da2fb9146106ea578063a3912ec814610704578063a9059cbb1461070f578063b7bc2c841461073f578063baac53001461074b578063be7c29c1146107b1578063c9d27afe14610817578063cc9ae3f61461082d578063cdef91d014610841578063dbde198814610859578063dd62ed3e1461087e578063e33734fd146108b2578063e5962195146108c6578063e66f53b7146108de578063eceb2945146108f0578063f8c80d261461094f575b610966600f546000906234bc000142108015610239575060125433600160a060020a03908116911614155b156109785761098033610752565b6109866004356000805482908110156100025750808052600e8202600080516020612a3683398151915201905060038101546004820154600683015460018401548454600786015460058701546009880154600a890154600d8a0154600160a060020a039586169b509599600201989760ff81811698610100909204811697949691951693168c565b61096660043560243533600160a060020a03908116600081815260156020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b61096660105481565b610a7d600754600160a060020a031681565b610a7d600e54600160a060020a031681565b61096660165481565b6109665b60004262127500600f60005054031115610de557506014610983565b610a7d601254600160a060020a031681565b60408051602060248035600481810135601f810185900485028601850190965285855261096695813595919460449492939092019181908401838280828437509496505050505050506000600060006000600060003411156116a857610002565b6109666004356024356044355b60115460009060ff1680156104315750600f5442115b80156124e957506124e78461044b565b6109666000610980335b600160a060020a0381166000908152600b602052604081205481908114156129cb57610b99565b61096660065481565b6109665b600d5430600160a060020a03163103610983565b610966600f5481565b61096660043560046020526000908152604090205460ff1681565b61096660043560243560006124cb610831565b610a9a6000341115610ba457610002565b604080516020604435600481810135601f8101849004840285018401909552848452610966948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897608497919650602491909101945090925082915084018382808284375094965050933593505060a435915050600060006110c1336105ec565b61096660043560096020526000908152604090205481565b61096660015481565b610a9a60043530600160a060020a031633600160a060020a03161415806105db5750600160a060020a03811660009081526004602052604090205460ff16155b156121cb576121c8565b6109666004355b600160a060020a0381166000908152601460205260409020545b919050565b6109666004356024356000600034111561259957610002565b610966600062e6b680420360026000505410806106505750600354600160a060020a0390811633909116145b80156106645750600254621274ff19420190105b156126145750426002908155600180549091028155610983565b610966600435600a6020526000908152604090205481565b610966600435602435600060006000600060006000341115611ba157610002565b610a7d600854600160a060020a031681565b610966600c5481565b61096660005460001901610983565b61096660025481565b61096660043560006000600060003411156121fc57610002565b6109665b6001610983565b6109666004356024355b60115460009060ff16801561072f5750600f5442115b801561248757506124853361044b565b61096660115460ff1681565b6109666004355b60006000600f600050544210801561076a5750600034115b80156107a457506011546101009004600160a060020a0316600014806107a457506011546101009004600160a060020a0390811633909116145b15610b9f57610a9c61037f565b610a7d600435600060006000508281548110156100025750508080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56b600e83020180548290811015610002575081526020902060030154600160a060020a0316610606565b61096660043560243560006000610e1b336105ec565b6109665b6000600034111561247c57610002565b61096660043560056020526000908152604090205481565b610966600435602435604435600061252f845b6000600060003411156127ac57610002565b610966600435602435600160a060020a0382811660009081526015602090815260408083209385168352929052205461033f565b610a9a600435600034111561254557610002565b610966600435600b6020526000908152604090205481565b610a7d600354600160a060020a031681565b604080516020606435600481810135601f81018490048402850184019095528484526109669481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600034111561103257610002565b610a7d6011546101009004600160a060020a031681565b60408051918252519081900360200190f35b610980610708565b90505b90565b604051808d600160a060020a031681526020018c8152602001806020018b81526020018a815260200189815260200188815260200187815260200186815260200185815260200184815260200183600160a060020a0316815260200182810382528c818154600181600116156101000203166002900481526020019150805460018160011615610100020316600290048015610a635780601f10610a3857610100808354040283529160200191610a63565b820191906000526020600020905b815481529060010190602001808311610a4657829003601f168201915b50509d505050505050505050505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b005b604051601254601434908102939093049350600160a060020a03169183900390600081818185876185025a03f150505050600160a060020a038316600081815260146020908152604080832080548601905560168054860190556013825291829020805434019055815184815291517fdbccb92686efceafb9bb7e0394df7f58f71b954061b81afb57109bf247d3d75a9281900390910190a260105460165410801590610b4c575060115460ff16155b15610b94576011805460ff1916600117905560165460408051918252517ff381a3e2428fdda36615919e8d9c35878d9eb0cf85ac6edf575088e80e4c147e9181900360200190a15b600191505b50919050565b610002565b600f5442118015610bb8575060115460ff16155b15610de357601260009054906101000a9004600160a060020a0316600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040516012549051600160a060020a039190911631109050610cc9576040805160125460e060020a63d2cc718f0282529151600160a060020a039290921691630221038a913091849163d2cc718f91600482810192602092919082900301816000876161da5a03f11561000257505060408051805160e160020a63011081c5028252600160a060020a039490941660048201526024810193909352516044838101936020935082900301816000876161da5a03f115610002575050505b33600160a060020a0316600081815260136020526040808220549051909181818185876185025a03f19250505015610de35733600160a060020a03167fbb28353e4598c3b9199101a66e0989549b659a59a54d2c27fbb183f1932c8e6d6013600050600033600160a060020a03168152602001908152602001600020600050546040518082815260200191505060405180910390a26014600050600033600160a060020a0316815260200190815260200160002060005054601660008282825054039250508190555060006014600050600033600160a060020a031681526020019081526020016000206000508190555060006013600050600033600160a060020a03168152602001908152602001600020600050819055505b565b4262054600600f60005054031115610e13576201518062127500600f60005054034203046014019050610983565b50601e610983565b60001415610e2857610002565b6000341115610e3657610002565b6000805485908110156100025750600160a060020a03331681527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56e600e8602908101602052604090912054600080516020612a3683398151915291909101915060ff1680610eb05750600c810160205260406000205460ff165b80610ebf575060038101544210155b15610ec957610002565b8215610f0f5733600160a060020a03166000908152601460209081526040808320546009850180549091019055600b84019091529020805460ff19166001179055610f4b565b33600160a060020a0316600090815260146020908152604080832054600a850180549091019055600c84019091529020805460ff191660011790555b33600160a060020a03166000908152600b60205260408120541415610f77576040600020849055610feb565b33600160a060020a03166000908152600b60205260408120548154811015610002579080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566600e909102015460038201541115610feb5733600160a060020a03166000908152600b602052604090208490555b60408051848152905133600160a060020a03169186917f86abfce99b7dd908bec0169288797f85049ec73cbe046ed9de818fab3a497ae09181900360200190a35092915050565b6000805487908110156100025750808052600e8702600080516020612a3683398151915201905090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816005016000505414915050949350505050565b600014156110ce57610002565b82801561111857508660001415806110e857508451600014155b806111005750600354600160a060020a038981169116145b8061110b5750600034115b80611118575062093a8084105b1561112257610002565b8215801561114257506111348861115c565b158061114257506212750084105b156111fe57610002565b83546118e590600160a060020a03165b600160a060020a03811660009081526004602052604081205460ff16806111f15750601254600160a060020a039081169083161480156111f15750601260009054906101000a9004600160a060020a0316600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604051516006541190505b156129a157506001610606565b6249d40084111561120e57610002565b60115460ff1615806112215750600f5442105b806112365750600c5434108015611236575082155b1561124057610002565b42844201101561124f57610002565b30600160a060020a031633600160a060020a0316141561126e57610002565b60008054600181018083559091908280158290116112a557600e0281600e0283600052602060002091820191016112a5919061136a565b505060008054929450918491508110156100025750808052600e8302600080516020612a368339815191520190508054600160a060020a031916891781556001818101899055875160028084018054600082815260209081902096975091959481161561010002600019011691909104601f908101829004840193918b019083901061146257805160ff19168380011785555b5061149292915061144a565b5050600060098201819055600a820155600d81018054600160a060020a03191690556001015b8082111561145e578054600160a060020a03191681556000600182810182905560028084018054848255909281161561010002600019011604601f81901061143057505b506000600383018190556004808401805461ffff19169055600584018290556006840182905560078401805460ff191690556008840180548382559083526020909220611344929091028101905b8082111561145e57600080825560018201818155600283019190915560039091018054600160a060020a03191690556113fc565b601f0160209004906000526020600020908101906113ae91905b8082111561145e576000815560010161144a565b5090565b82800160010185558215611338579182015b82811115611338578251826000505591602001919060010190611474565b50508787866040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160050160005081905550834201816003016000508190555060018160040160006101000a81548160ff02191690830217905550828160070160006101000a81548160ff02191690830217905550821561157857600881018054600181018083559091908280158290116115735760040281600402836000526020600020918201910161157391906113fc565b505050505b600d8082018054600160a060020a031916331790553460068301819055815401905560408051600160a060020a038a16815260208181018a9052918101859052608060608201818152895191830191909152885185937f5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f938d938d938a938e93929160a084019185810191908190849082908590600090600490601f850104600f02600301f150905090810190601f1680156116485780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a2509695505050505050565b6040805186815260208101839052815189927fdfc78bdca8e3e0b18c16c5c99323c6cb9eb5e00afde190b4e7273f5158702b07928290030190a25b5050505092915050565b6000805488908110156100025750808052600e8802600080516020612a36833981519152019050600781015490945060ff166116e757620d2f006116ec565b622398805b600485015490935060ff16801561170857506003840154830142115b15611716576117b887611890565b600384015442108061172d5750600484015460ff16155b806117ae57508360000160009054906101000a9004600160a060020a03168460010160005054876040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020846005016000505414155b1561114c57610002565b61169e565b60048401805461ff001916610100179055835460019550600160a060020a03908116309091161480159061180157508354600754600160a060020a03908116911614155b801561181d57506008548454600160a060020a03908116911614155b801561183957508354601254600160a060020a03908116911614155b801561185557506003548454600160a060020a03908116911614155b1561188b5760018401805430600160a060020a031660009081526005602052604090208054919091019055546006805490910190555b611663875b6000600060005082815481101561000257908052600e02600080516020612a36833981519152018150600481015490915060ff16156118d757600d80546006830154900390555b600401805460ff1916905550565b15156118f45761190087611890565b6001915061193161047f565b604051600d8501546006860154600160a060020a0391909116916000919082818181858883f193505050505061169e565b6001850154111561194157600091505b50600a8301546009840154865191019060049010801590611986575085600081518110156100025790602001015160f860020a900460f860020a02606860f860020a02145b80156119b6575085600181518110156100025790602001015160f860020a900460f860020a02603760f860020a02145b80156119e6575085600281518110156100025790602001015160f860020a900460f860020a0260ff60f860020a02145b8015611a16575085600381518110156100025790602001015160f860020a900460f860020a02601e60f860020a02145b8015611a45575030600160a060020a0316600090815260056020526040902054611a4290611a5d61047f565b81105b15611a4f57600091505b6001840154611a8090611a5f565b015b30600160a060020a03166000908152600560205260408120546129a961047f565b8110611ad457604051600d8501546006860154600160a060020a0391909116916000919082818181858883f193505050501515611abc57610002565b4260025560165460059004811115611ad45760056001555b6001840154611ae290611a5f565b8110158015611af85750600a8401546009850154115b8015611b015750815b1561188b578360000160009054906101000a9004600160a060020a0316600160a060020a0316846001016000505487604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611b7d5780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f19250505015156117bd57610002565b611baa336105ec565b60001415611bb757610002565b60008054889081101561000257508052600e87027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566810154600080516020612a36833981519152919091019450421080611c1957506003840154622398800142115b80611c3257508354600160a060020a0390811690871614155b80611c425750600784015460ff16155b80611c68575033600160a060020a03166000908152600b8501602052604090205460ff16155b80611c9c575033600160a060020a03166000908152600b60205260409020548714801590611c9c5750604060009081205414155b15611ca657610002565b600884018054600090811015610002579081526020812060030154600160a060020a03161415611e1257611efc86604051600090600160a060020a038316907f9046fefd66f538ab35263248a44217dcb70e2eb2cd136629e141b8b8f9f03b60908390a260408051600e547fe2faf044000000000000000000000000000000000000000000000000000000008252600160a060020a03858116600484015260248301859052604483018590526223988042016064840152925192169163e2faf04491608480820192602092909190829003018187876161da5a03f1156100025750506040515191506106069050565b6008850180546000908110156100025781815260208082209390935530600160a060020a031681526005909252604082205481549092908110156100025790815260208120905060020155601654600885018054600090811015610002579081526020812090506001015560048401805461ff0019166101001790555b6008840180546000908110156100025781548282526020822060010154929190811015610002579081526020812090505433600160a060020a031660009081526014602052604081205460088801805493909102939093049550908110156100025790815260208120905060030154604080517fbaac530000000000000000000000000000000000000000000000000000000000815233600160a060020a0390811660048301529151929091169163baac53009186916024808301926020929190829003018185886185025a03f11561000257505060405151600014159150611f78905057610002565b60088501805460009081101561000257818152602081206003018054600160a060020a03191690931790925580549091908110156100025790815260208120905060030154600160a060020a031660001415611f5757610002565b600d5430600160a060020a0316311015611f7057610002565b611d9561047f565b6008840180546000908110156100025781548282526020822060010154929190811015610002579081526020812090506002015433600160a060020a0390811660009081526014602090815260408083205430909416835260058083528184205460099093529083205460088b018054969095029690960497509487020494508593929091908290811015610002575260208120815060030154600160a060020a0390811682526020828101939093526040918201600090812080549095019094553016835260059091529020548290101561205357610002565b30600160a060020a031660009081526005602052604081208054849003905560088501805483926009929091829081101561000257508152602080822060030154600160a060020a039081168352929052604080822080549094019093553090911681522054819010156120c657610002565b30600160a060020a0390811660009081526009602090815260408083208054869003905533909316808352601482528383205484519081529351929390927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a36121383361086c565b5033600160a060020a03166000908152601460209081526040808320805460168054919091039055839055600a9091528120556001945061169e565b30600160a060020a0390811660008181526005602090815260408083208054958716808552828520805490970190965584845283905560099091528082208054948352908220805490940190935590815290555b50565b604051600160a060020a0382811691309091163190600081818185876185025a03f192505050151561217457610002565b33600160a060020a03818116600090815260096020908152604080832054815160065460085460e060020a63d2cc718f028352935197995091969195929092169363d2cc718f936004848101949193929183900301908290876161da5a03f11561000257505050604051805190602001506005600050600033600160a060020a03168152602001908152602001600020600050540204101561229d57610002565b600160a060020a03338116600090815260096020908152604080832054815160065460085460e060020a63d2cc718f02835293519296909593169363d2cc718f93600483810194929383900301908290876161da5a03f11561000257505050604051805190602001506005600050600033600160a060020a0316815260200190815260200160002060005054020403905083156123ec57600860009054906101000a9004600160a060020a0316600160a060020a0316630221038a83600160a060020a0316630e7082036040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060408051805160e160020a63011081c5028252600160a060020a031660048201526024810186905290516044808301935060209282900301816000876161da5a03f115610002575050604051511515905061245457610002565b6040805160085460e160020a63011081c5028252600160a060020a038581166004840152602483018590529251921691630221038a9160448082019260209290919082900301816000876161da5a03f115610002575050604051511515905061245457610002565b600160a060020a03331660009081526009602052604090208054909101905550600192915050565b6109803361086c565b155b80156124a257506124a23384845b6000600061293a856105ec565b80156124be57506124be83836000600034111561261c57610002565b15610b9f5750600161033f565b15156124d657610002565b6124e08383610719565b905061033f565b155b80156124fb57506124fb848484612495565b80156125185750612518848484600060003411156126c157610002565b15610b9f57506001612528565b90505b9392505050565b151561253a57610002565b61252584848461041b565b30600160a060020a031633600160a060020a031614158061258a575030600160a060020a031660009081526005602052604090205460649061258561047f565b010481115b1561259457610002565b600c55565b600354600160a060020a0390811633909116146125b557610002565b600160a060020a038316600081815260046020908152604091829020805460ff191686179055815185815291517f73ad2a153c8b67991df9459024950b318a609782cee8c7eeda47b905f9baa91f9281900390910190a250600161033f565b506000610983565b33600160a060020a03166000908152601460205260409020548290108015906126455750600082115b156126b957600160a060020a03338116600081815260146020908152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a350600161033f565b50600061033f565b600160a060020a03841660009081526014602052604090205482901080159061270a5750601560209081526040600081812033600160a060020a03168252909252902054829010155b80156127165750600082115b156127a457600160a060020a03838116600081815260146020908152604080832080548801905588851680845281842080548990039055601583528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a3506001612528565b506000612528565b600160a060020a038381166000908152600a6020908152604080832054601654600754835160e060020a63d2cc718f02815293519296919591169363d2cc718f9360048181019492939183900301908290876161da5a03f11561000257505060405151905061281a866105ec565b0204101561282757610002565b600160a060020a038381166000908152600a6020908152604080832054601654600754835160e060020a63d2cc718f02815293519296919591169363d2cc718f9360048181019492939183900301908290876161da5a03f115610002575050604051519050612895866105ec565b0204039050600760009054906101000a9004600160a060020a0316600160a060020a0316630221038a84836040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506020604051808303816000876161da5a03f115610002575050604051511515905061291357610002565b600160a060020a0383166000908152600a6020526040902080548201905560019150610b99565b600160a060020a0386166000908152600a602052604090205480850291909104915081111561296857610002565b600160a060020a038581166000908152600a60205260408082208054859003905591861681522080548201905560019150509392505050565b506000610606565b0160030260166000505483020460016000505460166000505404019050610606565b600160a060020a0383166000908152600b6020526040812054815481101561000257818052600e02600080516020612a368339815191520190506003810154909150421115610b9457600160a060020a0383166000908152600b602052604081208190559150610b9956290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000b656b2a9c3b2416437a811e07466ca712f5a5b5a",
+ "0x0000000000000000000000000000000000000000000000000000000000000007": "0x000000000000000000000000ad3ecf23c0c8983b07163708be6d763b5f056193",
+ "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000000000d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000000000f": "0x0000000000000000000000000000000000000000000000000000000057870858",
+ "0x0000000000000000000000000000000000000000000000000000000000000011": "0x0000000000000000000000bb9bc244d798123fde783fcc1c72d3bb8c18941301",
+ "0x0000000000000000000000000000000000000000000000000000000000000016": "0x00000000000000000000000000000000000000000003034f5ca7d45e17df199b",
+ "0x0421a2c4dbea98e8df669bb77238b62677daa210c5fbc46600627f90c03d0f08": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e571": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e572": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e573": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e574": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e575": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e576": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e577": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e578": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e579": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e57e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e57f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e580": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e581": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e582": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e583": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e584": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e585": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e586": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e587": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e58c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e58d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e58e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e58f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e590": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e591": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e592": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e593": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e594": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e595": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e59a": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e59b": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e59c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e59d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e59e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e59f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5a0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5a1": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5a2": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5a3": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5a8": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5a9": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5aa": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5ab": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5ac": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5ad": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5ae": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5af": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5b0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5b1": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5b6": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5b7": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5b8": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5b9": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5ba": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5bb": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5bc": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5bd": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5be": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5bf": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5c4": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5c5": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5c6": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5c7": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5c8": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5c9": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5ca": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5cb": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5cc": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5cd": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5d2": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5d3": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5d4": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5d5": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5d6": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5d7": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5d8": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5d9": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5da": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5db": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5e0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5e1": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5e2": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5e3": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5e4": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5e5": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5e6": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5e7": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5e8": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5e9": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5ee": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5ef": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5f0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5f1": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5f2": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5f3": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5f4": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5f5": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5f6": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5f7": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5fc": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x330b9432081afd3b64172d5df1f72ca72fc17e7e729ceb8b7529f91eee8b3f23": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x33f9bdb745e7edb1789dd1d68f40f693940aa8313b4f6bdc543be443dbc85e63": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x4830270ad35536baba417a92ea24656430586a37c90999b53c4d72ef1090cc9d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x4b16ba88f291613070529c10c8bdc41e973e2e2aa412ed92254cdca71ccfbc89": "0x00000000000000000000000000000000000000000001819451f999d617dafa76",
+ "0x6546a4760869a51d07a75a31f00531836c32152c06dc88ac342da52fac5d939e": "0x000000000000000000000000000000000000000000000026b8b4a0b1e8292492",
+ "0x6796d25b854f17a11b940a9ff2822335f7d9bd1b85fbcd9d1f5cf742099d477a": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x711886c99bc7a6e316551823dca012bd5b4381b57cec388f72c4b8105c1ed4ad": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x74024021ec74dc59b0fa1b66e9f430163a5e1128785ec9495f9686628ca7cc2b": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x79a0e9ff42282e7cbcb539e164f024ab90021633de05f600fff6d16305888d26": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x81ffe0a69ee20c37e3f3ba834da8b20475846fcde1f4a39fdfc628a2560076aa": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8f85b96a91f601f62149f5dd6a35d6168f6de5bc047a18e3cf7b97a3843c6ffd": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x946f68a04a200ebe87f2f896f7f6c08f4e22813db910c8a6d6abf17611ce3ffb": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x9c1ad2f16775f94ffd360e8bc716f86016a3fcf90992b5b4f3312353efd1bd61": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xa66ae63934365a757bf33d70ca0a28352da4c2fe6cb147bf29d69fbea3a706e0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xa7653edcf1403f7ce2f75784d5f34ca5f57ff110bd0c3abbdcc5a84f101dc83a": "0x00000000000000000000000000000000000000000001819451f999d617dafa93",
+ "0xa87317e3ffd5ed16f357bd31427bd97cbb35fc51ad1e00feec89bdfe82c5dba4": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xaa535eb427f7963e49601f9032ee6b62a9f72b6b3c610a5f11faf8dc68a97e2a": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xaade287f2b81ac58dcc6ee0c381cde85b6aa7a9a769be73605f1af9453a340a0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xb56a086d82c3921c13c13d1d53f18bbbe36d9d1c4862be8339a5171feb94c164": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xb6ab9f1541f42dc4feba15ccd18bc3af7c8f99cafb184ab65539883a68c7a1a9": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xbad9e5f7dc3001078ea6433993a2f145c2ef9af1c5137a35e9c173c208990249": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xc319152db8781ef1f12090aad94325d650e39c8a20285c7e02959817118f3f28": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xde65b6d76ea4a5547af9707e6e099bba6f16dbc7b5cf97fb8fedc82583b38de0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xdf71c8506c3cf85e2e677b60ec28fe60eb820775001bdce289e3253f304f22e8": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x4fd27b205895e698fa350f7ea57cec8a21927fcd": {
+ "balance": "0x0",
+ "nonce": "11",
+ "code": "0x606060405236156100f05760e060020a600035046303afc23581146100f257806313af403514610114578063186ef962146101365780632e64cec11461015857806331962cdc146101d2578063365a86fc146101f45780634162169f146102065780634dfc3db61461021857806365f13792146102585780636637b88214610441578063715d832f146104625780637452c2e61461048457806386c9b536146105005780638da5cb5b14610512578063975057e714610524578063a8618f711461059f578063d0679d341461061b578063d1c3c84a14610698578063d9d35966146106aa578063f3273907146106c9575b005b6100f0600435600054600160a060020a03908116339091161461072e57610002565b6100f0600435600054600160a060020a03908116339091161461074457610002565b6100f0600435600054600160a060020a03908116339091161461075957610002565b6100f06000805481908190600160a060020a0390811633909116148015906101905750600154600160a060020a039081163390911614155b80156101ac5750600254600160a060020a039081163390911614155b80156101c85750600354600160a060020a039081163390911614155b1561076e57610002565b6100f0600435600054600160a060020a039081163390911614610a5d57610002565b6106eb600154600160a060020a031681565b6106eb600454600160a060020a031681565b61070860008054600160a060020a03908116339091161480159061024c5750600154600160a060020a039081163390911614155b15610a72575060015b90565b6107086004355b600060006000600060006000600460009054906101000a9004600160a060020a0316600160a060020a0316630e7082036040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150945084600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604080518051600480547f81f03fcb000000000000000000000000000000000000000000000000000000008452600160a060020a038d81169285019290925293519198509290921692506381f03fcb916024828101926020929190829003018187876161da5a03f115610002575050604080518051600480547f18160ddd0000000000000000000000000000000000000000000000000000000084529351919750600160a060020a039390931693506318160ddd92828101926020929190829003018187876161da5a03f1156100025750506040805180516004805460e060020a6370a08231028452600160a060020a038d81169285019290925293519196509290921692506370a08231916024828101926020929190829003018187876161da5a03f11561000257505060405151909402919091049695505050505050565b6100f060043560005433600160a060020a03908116911614610ad957610002565b6100f06004356000805433600160a060020a03908116911614610aff57610002565b61071a600435600080548190819033600160a060020a039081169116148015906104be5750600154600160a060020a039081163390911614155b80156104da5750600254600160a060020a039081163390911614155b80156104f65750600354600160a060020a039081163390911614155b15610be657610002565b6106eb600354600160a060020a031681565b6106eb600054600160a060020a031681565b6100f06000805481908190819033600160a060020a0390811691161480159061055d5750600154600160a060020a039081163390911614155b80156105795750600254600160a060020a039081163390911614155b80156105955750600354600160a060020a039081163390911614155b15610c4457610002565b61071a6004355b60006000600460009054906101000a9004600160a060020a0316600160a060020a03166381f03fcb846040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f115610002575050604051519150610da490508361025f565b61071a60043560243560008054819033600160a060020a039081169116148015906106565750600154600160a060020a039081163390911614155b80156106725750600254600160a060020a039081163390911614155b801561068e5750600354600160a060020a039081163390911614155b15610dac57610002565b6106eb600254600160a060020a031681565b6100f06000805433600160a060020a03908116911614610ec457610002565b6106eb6004356000805433600160a060020a03908116911614610fd357610002565b60408051600160a060020a03929092168252519081900360200190f35b60408051918252519081900360200190f35b604080519115158252519081900360200190f35b60038054600160a060020a031916821790555b50565b60008054600160a060020a0319168217905550565b60028054600160a060020a0319168217905550565b30925061077a836105a6565b1561082557600480546006546040805160e060020a6370a08231028152600160a060020a038881169582019590955290519284169363a9059cbb9392169184916370a0823191602482810192602092919082900301816000876161da5a03f11561000257505060408051805160e060020a63a9059cbb02825260048201949094526024810193909352516044838101936020935082900301816000876161da5a03f115610002575050505b600091505b6005548210156108f45760058054600454600160a060020a0316916370a0823191859081101561000257600091825260408051600080516020611089833981519152929092015460e060020a6370a08231028352600160a060020a0316600483015251602482810193602093839003909101908290876161da5a03f115610002575050604051519150506000811115610a51576108f96005600050838154811015610002576000919091526000805160206110898339815191520154600160a060020a03166105a6565b505050565b15156109c25760058054600454600160a060020a0316916323b872dd918590811015610002575060009081526040805160008051602061108983398151915287015460e060020a6323b872dd028252600160a060020a03908116600483015230166024820152604481018690529051606482810193602093839003909101908290876161da5a03f1156100025750506040805183815290517f92da44f6982cd1ca7a9c851f8c39b26c80c235d7bb9fd59bce334fa634a1728b92509081900360200190a1610a51565b60058054600454600160a060020a0316916323b872dd918590811015610002575060009081526006546040805160008051602061108983398151915288015460e060020a6323b872dd028252600160a060020a0390811660048301529290921660248301526044820186905251606482810193602093839003909101908290876161da5a03f115610002575050505b6001919091019061082a565b60018054600160a060020a0319168217905550565b600454600160a060020a031660001415610a8e57506002610255565b600354600160a060020a031660001415610aaa57506003610255565b600254600160a060020a031660001415610ac657506004610255565b6005546000141561025557506005610255565b6005546000901115610aea57610002565b60048054600160a060020a0319168217905550565b5060005b81811015610b5a57600580546001818101808455930192909190828015829011610b5e576000839052610b5e906000805160206110898339815191529081019083015b80821115610bd65760008155600101610b46565b5050565b5050604051600454600160a060020a0316925090506082806110078339018082600160a060020a03168152602001915050604051809103906000f06005805460001981019081101561000257600091909152600080516020611089833981519152018054600160a060020a0319169091179055610b03565b5090565b600092505b5050919050565b600091505b600554821015610bda57600580548390811015610002576000919091526000805160206110898339815191520154600160a060020a0390811691508416811415610c385760019250610bdf565b60019190910190610beb565b600460009054906101000a9004600160a060020a0316600160a060020a03166370a08231306040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f115610002575050604051519450506000841415610cbb575b50505050565b600554600093506004900460010191505b81831015610cb557506005805460045442850182900692600160a060020a03919091169163a9059cbb9190849081101561000257600091825260408051600080516020611089833981519152929092015460e060020a63a9059cbb028352600160a060020a03166004830152868904602483015251604482810193602093839003909101908290876161da5a03f11561000257505060408051848704815290517fc6d8c0af6d21f291e7c359603aa97e0ed500f04db6e983b9fce75a91c6b8da6b92509081900360200190a160019290920191610ccc565b901192915050565b600460009054906101000a9004600160a060020a0316600160a060020a03166370a08231306040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f1156100025750506040515191505082811015610e5f57604080516020810185905281517f703690365b2d63b5b9ec4471a919cdd5924f745170399a5d24927fd07d81a04d929181900390910190a1600091505b5092915050565b604080516004805460e060020a63a9059cbb028352600160a060020a038881169284019290925260248301879052925192169163a9059cbb9160448082019260209290919082900301816000876161da5a03f115610002575060019350610e58915050565b600480546006546040805160e060020a6370a08231028152600160a060020a0392831694810194909452519116916370a0823191602482810192602092919082900301816000876161da5a03f11561000257505060405151915050600081111561074157604080516004805460065460e060020a6323b872dd028452600160a060020a039081169284019290925230821660248401526044830185905292519216916323b872dd9160648181019260209290919082900301816000876161da5a03f1156100025750505060405180519060200150507f32e95f921f72e9e736ccad1cc1c0ef6e3c3c08204eb74e9ee4ae8f98e195e3f0816040518082815260200191505060405180910390a150565b600580548390811015610002576000919091526000805160206110898339815191520154600160a060020a03169291505056006060604052604051602080608283396080604081905291517f095ea7b3000000000000000000000000000000000000000000000000000000008352600160a060020a0333811660845260001960a4819052919384939184169163095ea7b39160c491906044816000876161da5a03f115600257505033600160a060020a03169050ff036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000003e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x000000000000000000000000304a554a310c7e546dfe434669c62820b7d83490",
+ "0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000000000000000000000000000000000000000000a",
+ "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db6": "0x0000000000000000000000007ccbc69292c7a6d7b538c91f3b283de97906cf30",
+ "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db7": "0x0000000000000000000000001b9ec8ba24630b75a7a958153ffff56dd6d4b6a2",
+ "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db8": "0x000000000000000000000000c3a2c744ad1f5253c736875b93bacce5b01b060b"
+ }
+ },
+ "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f": {
+ "balance": "0x0",
+ "code": "0x606060405236156100da5760e060020a600035046303afc23581146100dc57806309f180f9146100fe5780630a39ce021461016c57806310aa1caa146101a057806313af40351461021e57806331962cdc14610240578063365a86fc146102625780634162169f146102745780634dfc3db6146102865780636637b882146102c65780636de45dee146102e85780638da5cb5b146103285780639137c1a71461033a578063b199efb51461035c578063b3a69f861461036e578063d5d7ff3c1461040b578063d95f98ce1461044b578063fe39084c146104b5575b005b6100da600435600054600160a060020a0390811633909116146104f657610002565b6104c76004355b6002546040805160e060020a6381f03fcb028152600160a060020a0384811660048301529151600093849384939116916381f03fcb91602481810192602092909190829003018187876161da5a03f1156100025750506040515192506105199050846101a7565b6104d960043560068054829081101561000257506000526000805160206110cf8339815191520154600160a060020a031681565b6104c76004355b604080516003547f65f13792000000000000000000000000000000000000000000000000000000008252600160a060020a038481166004840152925160009391909116916365f13792916024828101926020929190829003018187876161da5a03f115610002575050604051516001019392505050565b6100da600435600054600160a060020a03908116339091161461052c57610002565b6100da600435600054600160a060020a03908116339091161461054157610002565b6104d9600154600160a060020a031681565b6104d9600254600160a060020a031681565b6104c760008054600160a060020a0390811633909116148015906102ba5750600154600160a060020a039081163390911614155b15610556575060015b90565b6100da600435600054600160a060020a03908116339091161461063c57610002565b6100da600435600054600160a060020a03908116339091161480159061031e5750600154600160a060020a039081163390911614155b1561065157610002565b6104d9600054600160a060020a031681565b6100da600435600054600160a060020a03908116339091161461081b57610002565b6104d9600354600160a060020a031681565b6104c75b6000805b6006548110156108175760068054600254600160a060020a0316916370a08231918490811015610002576000918252604080516000805160206110cf833981519152929092015460e060020a6370a08231028352600160a060020a0316600483015251602482810193602093839003909101908290876161da5a03f11561000257505060405151929092019150600101610376565b6100da6004356000805433600160a060020a039081169116148015906104415750600154600160a060020a039081163390911614155b1561083057610002565b6100da60006000600060006000600060006000600060006000600060009054906101000a9004600160a060020a0316600160a060020a031633600160a060020a0316141580156104ab5750600154600160a060020a039081163390911614155b1561092d57610002565b6104d9600454600160a060020a031681565b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b60048054600160a060020a031916821790555b50565b81810392505b5050919050565b90508082111561050c5760009250610512565b60008054600160a060020a0319168217905550565b60018054600160a060020a0319168217905550565b600254600160a060020a031660001415610572575060026102c3565b600354600160a060020a03166000141561058e575060036102c3565b600354604080517fd1c3c84a000000000000000000000000000000000000000000000000000000008152905130600160a060020a0390811693169163d1c3c84a91600482810192602092919082900301816000876161da5a03f11561000257505060405151600160a060020a0316909114905061060d575060046102c3565b60065460001415610620575060056102c3565b600454600160a060020a0316600014156102c3575060066102c3565b60028054600160a060020a0319168217905550565b6106ab816000805b6006548110156110bc5782600160a060020a03166006600050828154811015610002576000919091526000805160206110cf8339815191520154600160a060020a031614156110c757600191506110c1565b156106b557610509565b600354604080517f7452c2e6000000000000000000000000000000000000000000000000000000008152600160a060020a03848116600483015291519290911691637452c2e69160248181019260209290919082900301816000876161da5a03f1156100025750506040515115905061072d57610509565b30600160a060020a031681600160a060020a0316148061075b5750600354600160a060020a03908116908216145b806107745750600154600160a060020a03908116908216145b1561077e57610509565b60068054600181018083559091908280158290116107bf578183600052602060002091820191016107bf91905b8082111561081757600081556001016107ab565b505060068054849350909150600019810190811015610002575080546000919091527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3e018054600160a060020a031916909117905550565b5090565b60038054600160a060020a0319168217905550565b5060005b6006548110156109215781600160a060020a03166006600050828154811015610002576000919091526000805160206110cf8339815191520154600160a060020a0316141561092557600680546000198101908110156100025760009182526000805160206110cf83398151915201909054906101000a9004600160a060020a03166006600050828154811015610002576000805160206110cf833981519152018054600160a060020a0319169092179091558054600019810180835590919082801582901161091c5761091c906000805160206110cf8339815191529081019083016107ab565b505050505b5050565b600101610834565b6002546040805160e060020a6381f03fcb02815230600160a060020a0381811660048401529251909e5092909116916381f03fcb9160248181019260209290919082900301816000876161da5a03f115610002575050604051519a505060008a14156109c1576040517f044c61dab36644651a1f82d87d6494a3a6450a6edde20b9baf45e374fb2d0bb990600090a1610e04565b6109c9610372565b6040805160025460e060020a6370a08231028252600160a060020a038f811660048401529251939c50909116916370a082319160248181019260209290919082900301816000876161da5a03f115610002575050604051519850606497505086881015610ade57604080516003547fd0679d34000000000000000000000000000000000000000000000000000000008252600160a060020a038e811660048401528b8b036024840152925192169163d0679d349160448082019260209290919082900301816000876161da5a03f1156100025750506040515115159050610ad8576040517f044c61dab36644651a1f82d87d6494a3a6450a6edde20b9baf45e374fb2d0bb990600090a1610e04565b86975087505b600095505b600654861015610b2457610d8e6006600050878154811015610002576000919091526000805160206110cf8339815191520154600160a060020a0316610105565b6040805160025460e060020a6381f03fcb028252600160a060020a038e8116600484015292519216916381f03fcb9160248181019260209290919082900301816000876161da5a03f11561000257505050604051805190602001509250600260009054906101000a9004600160a060020a0316600160a060020a03166370a082318c6040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f11561000257505060405151600097509250505b600654861015610e045760068054600254600160a060020a0316916370a082319189908110156100025760009182526000805160206110cf83398151915201546040805160e060020a6370a08231028152600160a060020a0392909216600483015251602482810193602093839003909101908290876161da5a03f115610002575050604051518084028b900495509150506000841115610d82577ff340c079d598119636d42046c6a2d2faf7a68c04aecee516f0e0b8a9e79b86666006600050878154811015610002576000919091526000805160206110cf833981519152015460408051600160a060020a03929092168252602082018790528386048c900482820152519081900360600190a160025460068054600160a060020a03929092169163a9059cbb919089908110156100025760009182526000805160206110cf83398151915201546040805160e060020a63a9059cbb028152600160a060020a039290921660048301526024820189905251604482810193602093839003909101908290876161da5a03f115610002575050505b60019590950194610bed565b9450898589020493508760001415610e11577fdb0f19c627ca59a2db73b1e1e8c4853f34a58afa92b29331e56c75144fa0c84c6006600050878154811015610002576000919091526000805160206110cf833981519152015460408051600160a060020a03929092168252519081900360200190a15b5050505050505050505050565b87841115610e84577f211d59fc569e166e12f7ca82135d85b1f178f636fefe40d168f0113cf07f818f6006600050878154811015610002576000919091526000805160206110cf833981519152015460408051600160a060020a03929092168252519081900360200190a1879350610ee8565b7f4b0bc4f25f8d0b92d2e12b686ba96cd75e4e69325e6cf7b1f3119d14eaf2cbdf6006600050878154811015610002576000919091526000805160206110cf833981519152015460408051600160a060020a03929092168252519081900360200190a15b60008411156110b05760068054998501997ff340c079d598119636d42046c6a2d2faf7a68c04aecee516f0e0b8a9e79b86669190889081101561000257600091909152604080516000805160206110cf8339815191529290920154600160a060020a0316825260208201879052818101889052519081900360600190a160025460068054600160a060020a03929092169163a9059cbb91908990811015610002576000918252604080516000805160206110cf833981519152929092015460e060020a63a9059cbb028352600160a060020a031660048301526024820189905251604482810193602093839003909101908290876161da5a03f1156100025750506040805160025460e060020a6370a08231028252600160a060020a038f811660048401529251921692506370a0823191602482810192602092919082900301816000876161da5a03f115610002575050506040518051906020015097508750600260009054906101000a9004600160a060020a0316600160a060020a03166381f03fcb8c6040518260e060020a0281526004018082600160a060020a031681526020019150506020604051808303816000876161da5a03f115610002575050604051519a50505b60019590950194610ae3565b600091505b50919050565b60010161065956f652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000003e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000304a554a310c7e546dfe434669c62820b7d83490",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f": "0x000000000000000000000000da4a4626d3e16e094de3225a751aab7128e96526"
+ }
+ },
+ "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc": {
+ "balance": "0x4563918244f400000",
+ "code": "0x606060405236156100da5760e060020a600035046313af40358114610145578063186ef9621461016757806331962cdc14610189578063365a86fc146101ab5780634162169f146101bd57806348c981e2146101cf5780634dfc3db61461020f57806361bc221a146102505780636637b882146102595780636c0e29601461027b5780638da5cb5b146104795780638f2b29a71461048b5780639137c1a714610602578063b199efb514610624578063b826c4fd14610636578063d1c3c84a1461063f578063d2f0ad9214610651578063fc340716146106bf575b6107236002546040805160e060020a630e7082030281529051600092600160a060020a031691630e708203916004828101926020929190829003018187876161da5a03f1156100025750506040515133600160a060020a039081169116149050610737575060015b90565b610861600435600054600160a060020a03908116339091161461089257610002565b610861600435600054600160a060020a0390811633909116146108a757610002565b610861600435600054600160a060020a0390811633909116146108bc57610002565b610863600154600160a060020a031681565b610863600254600160a060020a031681565b610861600435600054600160a060020a0390811633909116148015906102055750600154600160a060020a039081163390911614155b156108d157610002565b61088060008054600160a060020a0390811633909116148015906102435750600154600160a060020a039081163390911614155b156108fa57506001610142565b61088060055481565b610861600435600054600160a060020a0390811633909116146109e857610002565b6108805b6000600060006000600060006000600060006000600260009054906101000a9004600160a060020a0316600160a060020a0316630e7082036040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150985088600160a060020a03163130600160a060020a03163101975088600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060408051805160025460035460e060020a6381f03fcb028452600160a060020a0390811660048501529351919b5090921692506381f03fcb916024828101926020929190829003018187876161da5a03f11561000257505060408051805160025460e060020a6318160ddd0283529251909950600160a060020a039290921692506318160ddd916004828101926020929190829003018187876161da5a03f11561000257505060408051805160025460035460e060020a6370a08231028452600160a060020a039081166004850152935191995090921692506370a08231916024828101926020929190829003018187876161da5a03f115610002575050506040518051906020015093508784860202925088600160a060020a03163188880103840291508585029050808210156109fd57610a05565b610863600054600160a060020a031681565b61088060043560006000600060006000600260009054906101000a9004600160a060020a0316600160a060020a0316630e7082036040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150935083600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060408051600254815160035460e060020a6381f03fcb028452600160a060020a0390811660048501529351909750921692506381f03fcb916024808301926020929190829003018187876161da5a03f11561000257505060408051805160025460e060020a6318160ddd0283529251909550600160a060020a039290921692506318160ddd916004828101926020929190829003018187876161da5a03f115610002575050506040518051906020015090508581038183020486820387850204039450845084945050505050919050565b610861600435600054600160a060020a039081163390911614610a1157610002565b610863600354600160a060020a031681565b61088060065481565b610863600454600160a060020a031681565b6108806004355b6040805160025460035460e060020a6370a08231028352600160a060020a039081166004840152925160009384939216916370a08231916024828101926020929190829003018187876161da5a03f115610002575050604051519093046001019392505050565b61086160006000600060006000600060006000600060009054906101000a9004600160a060020a0316600160a060020a031633600160a060020a0316141580156107195750600154600160a060020a039081163390911614155b15610cbc57610002565b604080519115158252519081900360200190f35b600654600554600019909101901115610803576040805160025460035460e060020a6370a0823102835230600160a060020a03908116600485015293519184169363a9059cbb9391169160019185916370a082319160248082019260209290919082900301816000876161da5a03f11561000257505060408051805160e060020a63a9059cbb028252600482019590955260001994909401602485015251604480850194602094509192509082900301816000876161da5a03f115610002575060019250610142915050565b6005805460010190556040805160025460e160020a63664d71fb0282529151600160a060020a03929092169163cc9ae3f69160048181019260209290919082900301816000876161da5a03f115610002575060019250610142915050565b005b60408051600160a060020a03929092168252519081900360200190f35b60408051918252519081900360200190f35b60008054600160a060020a0319168217905550565b60048054600160a060020a0319168217905550565b60018054600160a060020a0319168217905550565b604051600160a060020a0382811691309091163190600081818185876185025a03f15050505050565b600254600160a060020a03166000141561091657506002610142565b600354600160a060020a03166000141561093257506003610142565b600454600160a060020a03166000141561094e57506004610142565b600354604080517f86c9b536000000000000000000000000000000000000000000000000000000008152905130600160a060020a039081169316916386c9b53691600482810192602092919082900301816000876161da5a03f11561000257505060405151600160a060020a031690911490506109cd57506005610142565b30600160a060020a0316316000141561014257506006610142565b60028054600160a060020a0319168217905550565b808203830499505b50505050505050505090565b60038054600160a060020a0319168217905550565b6006819055600354604080517fd0679d34000000000000000000000000000000000000000000000000000000008152600160a060020a038981166004830152938b04602482018190529151919750919092169163d0679d349160448181019260209290919082900301816000876161da5a03f11561000257505060408051600160055530600160a060020a031631815290517f7027eecbd2a688fc1fa281702b311ed7168571514adfd17014a55d828cb4338292509081900360200190a1604051600160a060020a0389811691309091163190600081818185876185025a03f15050604080517fd2cc718f000000000000000000000000000000000000000000000000000000008152905163d2cc718f9250600482810192602092919082900301816000876161da5a03f11561000257505060408051805160025460e060020a6370a08231028352600160a060020a038a81166004850152935191975090921692506370a0823191602482810192602092919082900301816000876161da5a03f11561000257505060408051805160025460e060020a6381f03fcb028352600160a060020a038a81166004850152935191965090921692506381f03fcb91602482810192602092919082900301816000876161da5a03f11561000257505060408051805160025460e160020a63664d71fb0283529251909450600160a060020a0392909216925063cc9ae3f691600482810192602092919082900301816000876161da5a03f115610002575050604080516002546004805460e060020a63a9059cbb028452600160a060020a03908116918401919091526001602484015292519216925063a9059cbb91604482810192602092919082900301816000876161da5a03f115610002575050505b5050505050505050565b6002546040805160e060020a630e7082030281529051600160a060020a0390921691630e7082039160048181019260209290919082900301816000876161da5a03f115610002575050604051519850610d15905061027f565b60408051600160a060020a038b1631815290519198507f07cf7e805770612a8b2ee8e0bcbba8aa908df5f85fbc4f9e2ef384cf75315038919081900360200190a187600160a060020a03163130600160a060020a0316310195508660001480610d7e5750856000145b15610db1576040517f30090d86c52e12fbc1213c1ecf7e193d6ce4a5c838c8c41d06c1a9daea8a2cec90600090a1610cb2565b309450610a268761065856",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000003e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000304a554a310c7e546dfe434669c62820b7d83490",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b": {
+ "balance": "0x0",
+ "code": "0x606060405236156100985760e060020a6000350463013cf08b811461009a57806313af4035146100d757806331962cdc146100f9578063365a86fc1461011b578063400e39491461012d5780634162169f146101375780634dfc3db6146101495780636637b8821461018a5780638da5cb5b146101ac578063e66f53b7146101be578063e90956cf146101d0578063ff2f4bd2146101f2575b005b61024460043560048054829081101561000257506000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b015481565b610098600435600054600160a060020a03908116339091161461026157610002565b610098600435600054600160a060020a03908116339091161461027657610002565b61024e600154600160a060020a031681565b6102446004545b90565b61024e600254600160a060020a031681565b61024460008054600160a060020a03908116339091161480159061017d5750600154600160a060020a039081163390911614155b1561028b57506001610134565b610098600435600054600160a060020a0390811633909116146102c157610002565b61024e600054600160a060020a031681565b61024e600354600160a060020a031681565b610098600435600054600160a060020a0390811633909116146102d657610002565b6100986000606081815260a06040526080828152825491929091819033600160a060020a0390811691161480159061023a5750600154600160a060020a039081163390911614155b1561031857610002565b6060908152602090f35b600160a060020a03166060908152602090f35b60008054600160a060020a0319168217905550565b60018054600160a060020a0319168217905550565b600254600160a060020a03168114156102a657506002610134565b600354600160a060020a031681141561013457506003610134565b60028054600160a060020a0319168217905550565b60038054600160a060020a0319168217905550565b50508054839250600019810190811015610002579060005260206000209001600050819055505b50505050565b6002547f70a082310000000000000000000000000000000000000000000000000000000060a090815230600160a060020a0390811660a45291909116906370a082319060c49060209060248187876161da5a03f11561000257505060405151821415905061038557610312565b60006040518059106103945750595b9080825280602002602001820160405250935062093a809150600260009054906101000a9004600160a060020a0316600160a060020a031663612e45a3600360009054906101000a9004600160a060020a0316600086888760016040518760e060020a0281526004018087600160a060020a03168152602001868152602001806020018060200185815260200184151581526020018381038352878181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156104815780820380516001836020036101000a031916815260200191505b508381038252868181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156104da5780820380516001836020036101000a031916815260200191505b50985050505050505050506020604051808303816000876161da5a03f1156100025750506040515160048054600181018083559294509250908280158290116102eb578183600052602060002091820191016102eb91905b808211156105465760008155600101610532565b509056",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000003e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000304a554a310c7e546dfe434669c62820b7d83490",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd1a0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd1a1": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd1a2": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd1a3": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd1a4": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0xad3ecf23c0c8983b07163708be6d763b5f056193": {
+ "balance": "0x0",
+ "code": "0x606060405236156100405760e060020a60003504630221038a811461004d57806318bdc79a146100aa5780638da5cb5b146100be578063d2cc718f146100d0575b6100d96001805434019055565b6100db6004356024356000805433600160a060020a0390811691161415806100755750600034115b806100a05750805460a060020a900460ff1680156100a057508054600160a060020a03848116911614155b156100f757610002565b6100db60005460ff60a060020a9091041681565b6100ed600054600160a060020a031681565b6100db60015481565b005b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a0383168260608381818185876185025a03f1925050501561015c57604080518381529051600160a060020a038516917f9735b0cb909f3d21d5c16bbcccd272d85fa11446f6d679f6ecb170d2dabfecfc919081900360200190a25060015b9291505056",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000304a554a310c7e546dfe434669c62820b7d83490",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0xbe3ae5cb97c253dda67181c6e34e43f5c275e08b": {
+ "balance": "0x167d285b38143c04f",
+ "nonce": "68"
+ },
+ "0xc0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89": {
+ "balance": "0x9651c71936",
+ "code": "0x606060405236156100b95760e060020a600035046313af4035811461019e57806326f5a8c9146101c1578063371fa854146101ca5780634162169f146101d35780634c8fe526146101e55780635970c915146101f757806361bc221a14610209578063625e847d146102125780636637b882146102325780637f9f519f146102555780638da5cb5b14610278578063a9059cbb1461028a578063c4463c80146102b0578063c9d27afe146102df578063e66f53b714610305575b6103176002547f0e708203000000000000000000000000000000000000000000000000000000006060908152600091600160a060020a031690630e7082039060649060209060048187876161da5a03f1156100025750506040515133600160a060020a039081169116149050610329576040805133600160a060020a03166020820152818152600f818301527f636f6e73747563746f72206661696c0000000000000000000000000000000000606082015290517fa6af7265d7ede5fbf0ee375956b52b362800d4f92e268809bef5fdf2a57924b89181900360800190a15060015b90565b61031760043560008054600160a060020a03908116339091161461049257610002565b61047560055481565b61047560045481565b61047f600254600160a060020a031681565b61047f600654600160a060020a031681565b61047f600754600160a060020a031681565b61047560035481565b61031760008054600160a060020a0390811633909116146104ef57610002565b61031760043560008054600160a060020a03908116339091161461057a57610002565b61031760043560008054600160a060020a0390811633909116146105d757610002565b61047f600054600160a060020a031681565b61031760043560243560008054600160a060020a03908116339091161461060f57610002565b61031760043560243560443560643560843560008054600160a060020a0390811633909116146106a657610002565b61031760043560243560008054600160a060020a0390811633909116146107bb57610002565b61047f600154600160a060020a031681565b60408051918252519081900360200190f35b60055460035460001990910190111561040257604080516002546006547f70a0823100000000000000000000000000000000000000000000000000000000835230600160a060020a03908116600485015293519184169363a9059cbb9391169184916370a0823191602480830192602092919082900301818a876161da5a03f11561000257505060408051805160e060020a63a9059cbb028252600482019490945260248101939093525160448084019360209350829003018187876161da5a03f11561000257505060016003819055915061019b9050565b6040805160038054600190810190915560025460048054925460e260020a632099877102855290840192909252600160a060020a03918216602484015292519216916382661dc491604480820192602092909190829003018187876161da5a03f11561000257506001925061019b915050565b6060908152602090f35b600160a060020a03166060908152602090f35b600160a060020a03821660609081527f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc90602090a181600060006101000a815481600160a060020a0302191690830217905550600190505b919050565b6001600355600754600160a060020a03908116908290301631606082818181858883f15050604080516002546001546004805460e260020a632099877102855290840152600160a060020a0390811660248401529251921694506382661dc493506044808201935060209291829003018187876161da5a03f11561000257506001925061019b915050565b6002805473ffffffffffffffffffffffffffffffffffffffff1916831790819055600160a060020a031660609081527fce6a5015a40a2ec38ce912a63bca374d85386207c6927d284292449f1431082290602090a15060016104ea565b600582905560608281527fbab6859bc098da798dbdc4860f0fee7467d703dadd975799e8c258b46a37d3de90602090a15060016104ea565b60025460e060020a63a9059cbb026060908152600160a060020a0385811660645260848590529091169063a9059cbb9060a49060209060448187876161da5a03f11561000257505060408051600160a060020a03861681526020810185905281517f69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de293509081900390910190a15060015b92915050565b6006805473ffffffffffffffffffffffffffffffffffffffff1990811686179091556001600381905580548216871790556004879055600584905560078054909116831790819055600160a060020a03908116908290301631606082818181858883f15050604080516002546004805460015460e260020a632099877102855291840152600160a060020a0390811660248401529251921694506382661dc493506044808201935060209291829003018187876161da5a03f11561000257505060408051600454600654908252600160a060020a0316602082015281517fa1ab731770d71027cd294cc0af5c8f5ec3c2ff5dbe6b75d68963d17192f8377b93509081900390910190a150600195945050505050565b6002547fc9d27afe0000000000000000000000000000000000000000000000000000000060609081526064859052831515608452600160a060020a039091169063c9d27afe9060a49060209060448187876161da5a03f11561000257505060408051858152841515602082015281517f8bfa1f40665434b48e7becc865cc0586ce3d6d2388521c05d4db87536ac8279993509081900390910190a15060016106a056",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x00000000000000000000000003e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000304a554a310c7e546dfe434669c62820b7d83490"
+ }
+ },
+ "0xea674fdde714fd979de3edf0f56aa9716b898ec8": {
+ "balance": "0x4ab3566739e7b24371",
+ "nonce": "286339"
+ },
+ "0xf835a0247b0063c04ef22006ebe57c5f11977cc4": {
+ "balance": "0x9645db5736",
+ "code": "0x606060405236156100b95760e060020a600035046313af4035811461019e57806326f5a8c9146101c1578063371fa854146101ca5780634162169f146101d35780634c8fe526146101e55780635970c915146101f757806361bc221a14610209578063625e847d146102125780636637b882146102325780637f9f519f146102555780638da5cb5b14610278578063a9059cbb1461028a578063c4463c80146102b0578063c9d27afe146102df578063e66f53b714610305575b6103176002547f0e708203000000000000000000000000000000000000000000000000000000006060908152600091600160a060020a031690630e7082039060649060209060048187876161da5a03f1156100025750506040515133600160a060020a039081169116149050610329576040805133600160a060020a03166020820152818152600f818301527f636f6e73747563746f72206661696c0000000000000000000000000000000000606082015290517fa6af7265d7ede5fbf0ee375956b52b362800d4f92e268809bef5fdf2a57924b89181900360800190a15060015b90565b61031760043560008054600160a060020a03908116339091161461049257610002565b61047560055481565b61047560045481565b61047f600254600160a060020a031681565b61047f600654600160a060020a031681565b61047f600754600160a060020a031681565b61047560035481565b61031760008054600160a060020a0390811633909116146104ef57610002565b61031760043560008054600160a060020a03908116339091161461057a57610002565b61031760043560008054600160a060020a0390811633909116146105d757610002565b61047f600054600160a060020a031681565b61031760043560243560008054600160a060020a03908116339091161461060f57610002565b61031760043560243560443560643560843560008054600160a060020a0390811633909116146106a657610002565b61031760043560243560008054600160a060020a0390811633909116146107bb57610002565b61047f600154600160a060020a031681565b60408051918252519081900360200190f35b60055460035460001990910190111561040257604080516002546006547f70a0823100000000000000000000000000000000000000000000000000000000835230600160a060020a03908116600485015293519184169363a9059cbb9391169184916370a0823191602480830192602092919082900301818a876161da5a03f11561000257505060408051805160e060020a63a9059cbb028252600482019490945260248101939093525160448084019360209350829003018187876161da5a03f11561000257505060016003819055915061019b9050565b6040805160038054600190810190915560025460048054925460e260020a632099877102855290840192909252600160a060020a03918216602484015292519216916382661dc491604480820192602092909190829003018187876161da5a03f11561000257506001925061019b915050565b6060908152602090f35b600160a060020a03166060908152602090f35b600160a060020a03821660609081527f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc90602090a181600060006101000a815481600160a060020a0302191690830217905550600190505b919050565b6001600355600754600160a060020a03908116908290301631606082818181858883f15050604080516002546001546004805460e260020a632099877102855290840152600160a060020a0390811660248401529251921694506382661dc493506044808201935060209291829003018187876161da5a03f11561000257506001925061019b915050565b6002805473ffffffffffffffffffffffffffffffffffffffff1916831790819055600160a060020a031660609081527fce6a5015a40a2ec38ce912a63bca374d85386207c6927d284292449f1431082290602090a15060016104ea565b600582905560608281527fbab6859bc098da798dbdc4860f0fee7467d703dadd975799e8c258b46a37d3de90602090a15060016104ea565b60025460e060020a63a9059cbb026060908152600160a060020a0385811660645260848590529091169063a9059cbb9060a49060209060448187876161da5a03f11561000257505060408051600160a060020a03861681526020810185905281517f69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de293509081900390910190a15060015b92915050565b6006805473ffffffffffffffffffffffffffffffffffffffff1990811686179091556001600381905580548216871790556004879055600584905560078054909116831790819055600160a060020a03908116908290301631606082818181858883f15050604080516002546004805460015460e260020a632099877102855291840152600160a060020a0390811660248401529251921694506382661dc493506044808201935060209291829003018187876161da5a03f11561000257505060408051600454600654908252600160a060020a0316602082015281517fa1ab731770d71027cd294cc0af5c8f5ec3c2ff5dbe6b75d68963d17192f8377b93509081900390910190a150600195945050505050565b6002547fc9d27afe0000000000000000000000000000000000000000000000000000000060609081526064859052831515608452600160a060020a039091169063c9d27afe9060a49060209060448187876161da5a03f11561000257505060408051858152841515602082015281517f8bfa1f40665434b48e7becc865cc0586ce3d6d2388521c05d4db87536ac8279993509081900390910190a15060016106a056",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x00000000000000000000000003e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000304a554a310c7e546dfe434669c62820b7d83490"
+ }
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "1881284",
+ "difficulty": "59917798852808",
+ "timestamp": "1468467296",
+ "gasLimit": "4712388",
+ "miner": "0xea674fdde714fd979de3edf0f56aa9716b898ec8"
+ },
+ "input": "0xf869448505d21dba00833567e09403e3d4561a8f8e975fdcd798d32857a20cf25e7e8084be9a65551ba0d4dd5fff30e83fbe630bb0fd67eeefe9e3aad0c3ee870a2b6e80fc40191bc7d4a074f93b546bfad60f3cae8e4aafef835237095d6618334154a24df4b4d49d9359",
+ "tracerConfig": {
+ "withLog": true
+ },
+ "result": {
+ "from": "0xbe3ae5cb97c253dda67181c6e34e43f5c275e08b",
+ "gas": "0x3514c8",
+ "gasUsed": "0x26e1ef",
+ "to": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "input": "0xbe9a6555",
+ "calls": [
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x34affa",
+ "gasUsed": "0x1ef",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x4b6753bc",
+ "output": "0x0000000000000000000000000000000000000000000000000000000057870858",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x34abef",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a08231000000000000000000000000c0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89",
+ "output": "0x00000000000000000000000000000000000000000001819451f999d617dafa93",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x34a705",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a08231000000000000000000000000c0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89",
+ "output": "0x00000000000000000000000000000000000000000001819451f999d617dafa93",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x34a31a",
+ "gasUsed": "0xa2f3",
+ "to": "0xc0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89",
+ "input": "0xa9059cbb0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd00000000000000000000000000000000000000000001819451f999d617dafa93",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0xc0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89",
+ "gas": "0x343e8c",
+ "gasUsed": "0x9a62",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd00000000000000000000000000000000000000000001819451f999d617dafa93",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x000000000000000000000000c0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89",
+ "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd"
+ ],
+ "data": "0x00000000000000000000000000000000000000000001819451f999d617dafa93"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0xc0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89",
+ "topics": [
+ "0x69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2"
+ ],
+ "data": "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd00000000000000000000000000000000000000000001819451f999d617dafa93"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x33ff04",
+ "gasUsed": "0x168e",
+ "to": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "input": "0xa8618f710000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "calls": [
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x339a3b",
+ "gasUsed": "0x329",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x81f03fcb0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x3395a4",
+ "gasUsed": "0x15f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x0e708203",
+ "output": "0x000000000000000000000000ad3ecf23c0c8983b07163708be6d763b5f056193",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x339363",
+ "gasUsed": "0x113",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0xd2cc718f",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x339129",
+ "gasUsed": "0x329",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x81f03fcb0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x338cfa",
+ "gasUsed": "0x13f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x18160ddd",
+ "output": "0x00000000000000000000000000000000000000000003034f5ca7d45e17df199b",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x338a75",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x00000000000000000000000000000000000000000001819451f999d617dafa93",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x33e6f2",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a08231000000000000000000000000f835a0247b0063c04ef22006ebe57c5f11977cc4",
+ "output": "0x00000000000000000000000000000000000000000001819451f999d617dafa76",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x33e208",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a08231000000000000000000000000f835a0247b0063c04ef22006ebe57c5f11977cc4",
+ "output": "0x00000000000000000000000000000000000000000001819451f999d617dafa76",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x33de20",
+ "gasUsed": "0x685b",
+ "to": "0xf835a0247b0063c04ef22006ebe57c5f11977cc4",
+ "input": "0xa9059cbb0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd00000000000000000000000000000000000000000001819451f999d617dafa76",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0xf835a0247b0063c04ef22006ebe57c5f11977cc4",
+ "gas": "0x337992",
+ "gasUsed": "0x5fca",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd00000000000000000000000000000000000000000001819451f999d617dafa76",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x000000000000000000000000f835a0247b0063c04ef22006ebe57c5f11977cc4",
+ "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd"
+ ],
+ "data": "0x00000000000000000000000000000000000000000001819451f999d617dafa76"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0xf835a0247b0063c04ef22006ebe57c5f11977cc4",
+ "topics": [
+ "0x69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2"
+ ],
+ "data": "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd00000000000000000000000000000000000000000001819451f999d617dafa76"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x3374a2",
+ "gasUsed": "0x168e",
+ "to": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "input": "0xa8618f710000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "calls": [
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x330fd9",
+ "gasUsed": "0x329",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x81f03fcb0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x330b42",
+ "gasUsed": "0x15f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x0e708203",
+ "output": "0x000000000000000000000000ad3ecf23c0c8983b07163708be6d763b5f056193",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x330901",
+ "gasUsed": "0x113",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0xd2cc718f",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x3306c7",
+ "gasUsed": "0x329",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x81f03fcb0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x330298",
+ "gasUsed": "0x13f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x18160ddd",
+ "output": "0x00000000000000000000000000000000000000000003034f5ca7d45e17df199b",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x330013",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x000000000000000000000000000000000000000000030328a3f333ac2fb5f509",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x33490b",
+ "gasUsed": "0x3f781",
+ "to": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "input": "0xfc340716",
+ "calls": [
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x32e30d",
+ "gasUsed": "0x15f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x0e708203",
+ "output": "0x000000000000000000000000ad3ecf23c0c8983b07163708be6d763b5f056193",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x32e037",
+ "gasUsed": "0x15f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x0e708203",
+ "output": "0x000000000000000000000000ad3ecf23c0c8983b07163708be6d763b5f056193",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x32dd7b",
+ "gasUsed": "0x113",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0xd2cc718f",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x32daf9",
+ "gasUsed": "0x329",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x81f03fcb0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x32d6ab",
+ "gasUsed": "0x13f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x18160ddd",
+ "output": "0x00000000000000000000000000000000000000000003034f5ca7d45e17df199b",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x32d400",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x000000000000000000000000000000000000000000030328a3f333ac2fb5f509",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x32c975",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x000000000000000000000000000000000000000000030328a3f333ac2fb5f509",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x3276d3",
+ "gasUsed": "0xa49d",
+ "to": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "input": "0xd0679d340000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc0000000000000000000000000000000000000000000181a7ae53ea2f0bef8ccd",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x320fe1",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x000000000000000000000000000000000000000000030328a3f333ac2fb5f509",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x320b5b",
+ "gasUsed": "0x9a62",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc0000000000000000000000000000000000000000000181a7ae53ea2f0bef8ccd",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "0x0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000181a7ae53ea2f0bef8ccd"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x3164e1",
+ "gasUsed": "0x4e91",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0x",
+ "value": "0x4563918244f400000",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x3115cc",
+ "gasUsed": "0x113",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0xd2cc718f",
+ "output": "0x000000000000000000000000000000000000000000000004563918244f400000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x311382",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "output": "0x0000000000000000000000000000000000000000000181a7ae53ea2f0bef8ccd",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x310f37",
+ "gasUsed": "0x329",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x81f03fcb0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x310ae9",
+ "gasUsed": "0x1446e",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xcc9ae3f6",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x30a397",
+ "gasUsed": "0x113",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0xd2cc718f",
+ "output": "0x000000000000000000000000000000000000000000000004563918244f400000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x309fc1",
+ "gasUsed": "0x113",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0xd2cc718f",
+ "output": "0x000000000000000000000000000000000000000000000004563918244f400000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x309c45",
+ "gasUsed": "0x122af",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0x0221038a0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc0000000000000000000000000000000000000000000000022b1c8c12279fffff",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "gas": "0x301e6f",
+ "gasUsed": "0x10068",
+ "to": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "input": "0x",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x2fbb97",
+ "gasUsed": "0x15f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x0e708203",
+ "output": "0x000000000000000000000000ad3ecf23c0c8983b07163708be6d763b5f056193",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x2fa477",
+ "gasUsed": "0xe7b6",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xcc9ae3f6",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x2f3d25",
+ "gasUsed": "0x113",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0xd2cc718f",
+ "output": "0x000000000000000000000000000000000000000000000004563918244f400000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x2f394f",
+ "gasUsed": "0x113",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0xd2cc718f",
+ "output": "0x000000000000000000000000000000000000000000000004563918244f400000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x2f35d3",
+ "gasUsed": "0x8b5f",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0x0221038a0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc0000000000000000000000000000000000000000000000022b1c8c12279fffff",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "gas": "0x2eb7fd",
+ "gasUsed": "0x6918",
+ "to": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "input": "0x",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x2e5525",
+ "gasUsed": "0x15f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x0e708203",
+ "output": "0x000000000000000000000000ad3ecf23c0c8983b07163708be6d763b5f056193",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x2e5168",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "output": "0x0000000000000000000000000000000000000000000181a7ae53ea2f0bef8ccd",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x2e4d69",
+ "gasUsed": "0x5fca",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd0000000000000000000000000000000000000000000181a7ae53ea2f0bef8ccc",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000181a7ae53ea2f0bef8ccc"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x22b1c8c12279fffff",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "topics": [
+ "0x9735b0cb909f3d21d5c16bbcccd272d85fa11446f6d679f6ecb170d2dabfecfc",
+ "0x0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000022b1c8c12279fffff"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x22b1c8c12279fffff",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "topics": [
+ "0x9735b0cb909f3d21d5c16bbcccd272d85fa11446f6d679f6ecb170d2dabfecfc",
+ "0x0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000022b1c8c12279fffff"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "gas": "0x2fc505",
+ "gasUsed": "0xd4fa",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb0000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f0000000000000000000000000000000000000000000000000000000000000001",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000006e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "0x0000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000001"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "topics": [
+ "0x07cf7e805770612a8b2ee8e0bcbba8aa908df5f85fbc4f9e2ef384cf75315038"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ },
+ {
+ "address": "0x6e715ab4f598eacf0016b9b35ef33e4141844ccc",
+ "topics": [
+ "0x7027eecbd2a688fc1fa281702b311ed7168571514adfd17014a55d828cb43382"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000004563918244f400000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x2f5092",
+ "gasUsed": "0x14e37",
+ "to": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "input": "0xd95f98ce",
+ "calls": [
+ {
+ "from": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "gas": "0x2eea7c",
+ "gasUsed": "0x329",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x81f03fcb0000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "output": "0x000000000000000000000000000000000000000000000004563918244f3ffffe",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "gas": "0x2ee4cb",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a08231000000000000000000000000da4a4626d3e16e094de3225a751aab7128e96526",
+ "output": "0x000000000000000000000000000000000000000000000026b8b4a0b1e8292492",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "gas": "0x2edfff",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "gas": "0x2edb9a",
+ "gasUsed": "0x6994",
+ "to": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "input": "0xd0679d340000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f0000000000000000000000000000000000000000000000000000000000000063",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x2e7519",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x000000000000000000000000000000000000000000030328a3f333ac2fb5f508",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x2e7093",
+ "gasUsed": "0x5fca",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb0000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f0000000000000000000000000000000000000000000000000000000000000063",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "0x0000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000063"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "gas": "0x2e6f59",
+ "gasUsed": "0x329",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x81f03fcb000000000000000000000000da4a4626d3e16e094de3225a751aab7128e96526",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "gas": "0x2e6afa",
+ "gasUsed": "0x1113",
+ "to": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "input": "0x65f13792000000000000000000000000da4a4626d3e16e094de3225a751aab7128e96526",
+ "output": "0x0000000000000000000000000000000000000000000000000037bc5737aa7ba8",
+ "calls": [
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x2e06f9",
+ "gasUsed": "0x15f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x0e708203",
+ "output": "0x000000000000000000000000ad3ecf23c0c8983b07163708be6d763b5f056193",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x2e04b8",
+ "gasUsed": "0x113",
+ "to": "0xad3ecf23c0c8983b07163708be6d763b5f056193",
+ "input": "0xd2cc718f",
+ "output": "0x000000000000000000000000000000000000000000000004563918244f400000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x2e027b",
+ "gasUsed": "0x329",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x81f03fcb000000000000000000000000da4a4626d3e16e094de3225a751aab7128e96526",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x2dfe4c",
+ "gasUsed": "0x13f",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x18160ddd",
+ "output": "0x00000000000000000000000000000000000000000003034f5ca7d45e17df199b",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x2dfbc7",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a08231000000000000000000000000da4a4626d3e16e094de3225a751aab7128e96526",
+ "output": "0x000000000000000000000000000000000000000000000026b8b4a0b1e8292492",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "gas": "0x2e5281",
+ "gasUsed": "0x329",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x81f03fcb0000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "output": "0x000000000000000000000000000000000000000000000004563918244f3ffffe",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "gas": "0x2e4dcc",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000064",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "gas": "0x2e4857",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a08231000000000000000000000000da4a4626d3e16e094de3225a751aab7128e96526",
+ "output": "0x000000000000000000000000000000000000000000000026b8b4a0b1e8292492",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "gas": "0x2e3bae",
+ "gasUsed": "0x9a62",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb000000000000000000000000da4a4626d3e16e094de3225a751aab7128e965260000000000000000000000000000000000000000000000000000000000000064",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000006dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "0x000000000000000000000000da4a4626d3e16e094de3225a751aab7128e96526"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000064"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "topics": [
+ "0x4b0bc4f25f8d0b92d2e12b686ba96cd75e4e69325e6cf7b1f3119d14eaf2cbdf"
+ ],
+ "data": "0x000000000000000000000000da4a4626d3e16e094de3225a751aab7128e96526"
+ },
+ {
+ "address": "0x6dbfc63479ffc031f23e94dc91befa38bec2c25f",
+ "topics": [
+ "0xf340c079d598119636d42046c6a2d2faf7a68c04aecee516f0e0b8a9e79b8666"
+ ],
+ "data": "0x000000000000000000000000da4a4626d3e16e094de3225a751aab7128e9652600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x2e00dc",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x2dfc58",
+ "gasUsed": "0xa3bb",
+ "to": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "input": "0xd0679d340000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b0000000000000000000000000000000000000000000000000000000000000001",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x2d9648",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x000000000000000000000000000000000000000000030328a3f333ac2fb5f4a5",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0x2d91c2",
+ "gasUsed": "0x9a62",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb0000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b0000000000000000000000000000000000000000000000000000000000000001",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "0x0000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000001"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x2d57a6",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x2d5515",
+ "gasUsed": "0x3478d",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0xff2f4bd2",
+ "calls": [
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x2ceffb",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x2ce86e",
+ "gasUsed": "0x29e8d",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x612e45a3000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0x5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f",
+ "0x0000000000000000000000000000000000000000000000000000000000000001"
+ ],
+ "data": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x2a0c87",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x2a09f6",
+ "gasUsed": "0x30cf5",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0xff2f4bd2",
+ "calls": [
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x29a4dc",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x299d4f",
+ "gasUsed": "0x29e8d",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x612e45a3000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000002",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0x5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f",
+ "0x0000000000000000000000000000000000000000000000000000000000000002"
+ ],
+ "data": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x26fc00",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000002",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x26f96f",
+ "gasUsed": "0x30cf5",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0xff2f4bd2",
+ "calls": [
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x269455",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x268cc8",
+ "gasUsed": "0x29e8d",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x612e45a3000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000003",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0x5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f",
+ "0x0000000000000000000000000000000000000000000000000000000000000003"
+ ],
+ "data": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x23eb79",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000003",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x23e8e8",
+ "gasUsed": "0x30cf5",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0xff2f4bd2",
+ "calls": [
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x2383ce",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x237c41",
+ "gasUsed": "0x29e8d",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x612e45a3000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000004",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0x5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f",
+ "0x0000000000000000000000000000000000000000000000000000000000000004"
+ ],
+ "data": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x20daf2",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000004",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x20d861",
+ "gasUsed": "0x30cf5",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0xff2f4bd2",
+ "calls": [
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x207347",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x206bba",
+ "gasUsed": "0x29e8d",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x612e45a3000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000005",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0x5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f",
+ "0x0000000000000000000000000000000000000000000000000000000000000005"
+ ],
+ "data": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x1dca6b",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000005",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x1dc7da",
+ "gasUsed": "0x30cf5",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0xff2f4bd2",
+ "calls": [
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x1d62c0",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x1d5b33",
+ "gasUsed": "0x29e8d",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x612e45a3000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000006",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0x5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f",
+ "0x0000000000000000000000000000000000000000000000000000000000000006"
+ ],
+ "data": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x1ab9e4",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000006",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x1ab753",
+ "gasUsed": "0x30cf5",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0xff2f4bd2",
+ "calls": [
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x1a5239",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x1a4aac",
+ "gasUsed": "0x29e8d",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x612e45a3000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000007",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0x5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f",
+ "0x0000000000000000000000000000000000000000000000000000000000000007"
+ ],
+ "data": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x17a95d",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000007",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x17a6cc",
+ "gasUsed": "0x30cf5",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0xff2f4bd2",
+ "calls": [
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x1741b2",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x173a25",
+ "gasUsed": "0x29e8d",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x612e45a3000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000008",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0x5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f",
+ "0x0000000000000000000000000000000000000000000000000000000000000008"
+ ],
+ "data": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x1498d6",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000008",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x149645",
+ "gasUsed": "0x30cf5",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0xff2f4bd2",
+ "calls": [
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x14312b",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x14299e",
+ "gasUsed": "0x29e8d",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x612e45a3000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000009",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0x5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f",
+ "0x0000000000000000000000000000000000000000000000000000000000000009"
+ ],
+ "data": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x11884f",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000009",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0x1185be",
+ "gasUsed": "0x30cf5",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0xff2f4bd2",
+ "calls": [
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x1120a4",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000007498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "gas": "0x111917",
+ "gasUsed": "0x29e8d",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x612e45a3000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0x000000000000000000000000000000000000000000000000000000000000000a",
+ "calls": [
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "gas": "0x3",
+ "gasUsed": "0x3",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x",
+ "error": "out of gas",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0x5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f",
+ "0x000000000000000000000000000000000000000000000000000000000000000a"
+ ],
+ "data": "0x000000000000000000000000be3ae5cb97c253dda67181c6e34e43f5c275e08b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0xe77c8",
+ "gasUsed": "0x112",
+ "to": "0x7498bb5749c9801f1f7e490baf5f966dbfe4e97b",
+ "input": "0x400e3949",
+ "output": "0x000000000000000000000000000000000000000000000000000000000000000a",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x03e3d4561a8f8e975fdcd798d32857a20cf25e7e",
+ "gas": "0xe7537",
+ "gasUsed": "0x1eafd",
+ "to": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "input": "0x975057e7",
+ "calls": [
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0xe0f53",
+ "gasUsed": "0x314",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0x70a082310000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "output": "0x000000000000000000000000000000000000000000030328a3f333ac2fb5f4a4",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0xe096d",
+ "gasUsed": "0x9a62",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb0000000000000000000000007ccbc69292c7a6d7b538c91f3b283de97906cf3000000000000000000000000000000000000000000001010d8bfbbbe40fe7518c",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "0x0000000000000000000000007ccbc69292c7a6d7b538c91f3b283de97906cf30"
+ ],
+ "data": "0x00000000000000000000000000000000000000000001010d8bfbbbe40fe7518c"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0xd6871",
+ "gasUsed": "0x9a62",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb0000000000000000000000001b9ec8ba24630b75a7a958153ffff56dd6d4b6a200000000000000000000000000000000000000000001010d8bfbbbe40fe7518c",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "0x0000000000000000000000001b9ec8ba24630b75a7a958153ffff56dd6d4b6a2"
+ ],
+ "data": "0x00000000000000000000000000000000000000000001010d8bfbbbe40fe7518c"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "gas": "0xcc775",
+ "gasUsed": "0x9a62",
+ "to": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "input": "0xa9059cbb000000000000000000000000c3a2c744ad1f5253c736875b93bacce5b01b060b00000000000000000000000000000000000000000001010d8bfbbbe40fe7518c",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x304a554a310c7e546dfe434669c62820b7d83490",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x0000000000000000000000004fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "0x000000000000000000000000c3a2c744ad1f5253c736875b93bacce5b01b060b"
+ ],
+ "data": "0x00000000000000000000000000000000000000000001010d8bfbbbe40fe7518c"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "topics": [
+ "0xc6d8c0af6d21f291e7c359603aa97e0ed500f04db6e983b9fce75a91c6b8da6b"
+ ],
+ "data": "0x00000000000000000000000000000000000000000001010d8bfbbbe40fe7518c"
+ },
+ {
+ "address": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "topics": [
+ "0xc6d8c0af6d21f291e7c359603aa97e0ed500f04db6e983b9fce75a91c6b8da6b"
+ ],
+ "data": "0x00000000000000000000000000000000000000000001010d8bfbbbe40fe7518c"
+ },
+ {
+ "address": "0x4fd27b205895e698fa350f7ea57cec8a21927fcd",
+ "topics": [
+ "0xc6d8c0af6d21f291e7c359603aa97e0ed500f04db6e983b9fce75a91c6b8da6b"
+ ],
+ "data": "0x00000000000000000000000000000000000000000001010d8bfbbbe40fe7518c"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json
new file mode 100644
index 0000000000..858931558a
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json
@@ -0,0 +1,530 @@
+{
+ "genesis": {
+ "difficulty": "7507253814130",
+ "extraData": "0xd783010400844765746887676f312e352e31856c696e7578",
+ "gasLimit": "3141592",
+ "hash": "0x3d9d19618f67bbb7708403fe9bda131fbade0449d2ac12bf3b140b4269112826",
+ "miner": "0x63a9975ba31b0b9626b34300f7f627147df1f526",
+ "mixHash": "0x50aaa8973eadd4bbfc7f5b59d5be52f6a1be2d38f40b5a0786a24b90257520da",
+ "nonce": "0x3547956c62c256b9",
+ "number": "595531",
+ "stateRoot": "0x79d00dd270bffc48d89fa55842f63f840981121378da8c6de4d479535f25ed6a",
+ "timestamp": "1448471472",
+ "totalDifficulty": "3448100174991667199",
+ "alloc": {
+ "0x2a65aca4d5fc5b5c859090a6c34d164135398226": {
+ "balance": "0x44dc051cccdfd2e132",
+ "nonce": "39602"
+ },
+ "0x350e0ffc780a6a75b44cc52e1ff9092870668945": {
+ "balance": "0xe37111b7c79406c0",
+ "code": "0x606060405236156100f05760e060020a60003504631ff6c70581146100f257806347980c0d146100fd57806353ba9c2f146101085780635ea8cd12146101c957806369d640fd146101f05780637ce3489b146102405780637d1bb97a1461026b5780637fd6f15c146103e55780638bf50628146103f057806390a248f814610411578063a8f37bb214610438578063b019e0171461046a578063b4c70cea1461059b578063cf955f34146106a1578063d229b54b146106bd578063d54b4a04146106e4578063e021fadb146106f1578063e45be8eb14610858578063eddfa7c814610863578063f2a75fe41461095d575b005b6109a5621e84845481565b6109a5621e84865481565b6109b76004356024356000808080806003876103e881101561000257506107d0880201866103e8811015610002579090600202016000505461ffff168152602081019190915260400160002054600160a060020a031692506003856103e881101561000257506107d0860201846103e88110156100025790906002020160005054620100009004600390810b9250856103e881101561000257506107d0860201846103e8811015610002579090600202016000506001015490509250925092565b6100f0600435621e848354600160a060020a0390811633909116141561026857621e848755565b6109e36004356024356003826103e881101561000257506107d0830201816103e88110156100025790906002020160005080546001919091015461ffff821693506201000090910460030b915083565b6100f0600435621e848354600160a060020a0390811633909116141561026857621e84858190555b50565b610a0a600435617d00604051908101604052806103e8905b600081526020019060019003908161028357505060408051617d0081019091526103e8815b60008152602001906001900390816102a857505060408051617d0081019091526103e8815b60008152602001906001900390816102cd5750600090505b6103e861ffff82161015610d09576000806003836103e8811015610002576107d002018150876103e881101561000257600202016000505461ffff168152602081019190915260400160002054600160a060020a031684826103e8811015610002575050602082028501526003816103e8811015610002576107d00201600050856103e8811015610002579090600202016000505462010000900460030b83826103e8811015610002575050600390810b60208302850152816103e8811015610002576107d00201600050856103e8811015610002579090600202016000506001015482826103e8811015610002575050602082028301526001016102e5565b6109a5621e84855481565b610a59600435600060208190529081526040902054600160a060020a031681565b6100f0600435621e848354600160a060020a0390811633909116141561026857621e848655565b6100f060043560243560443560643560843560a435610a8e868684866101000288620100000286607f02010101610870565b604080516004803580820135602081810280860182019096528185526100f09593946024949093850192918291908501908490808284375050604080518735808a013560208181028085018201909552818452989a996044999398509190910195509350839250850190849080828437505060408051606435808a013560208181028085018201909552818452989a9935999860849850929650602491909101945092508291908501908490808284375050604080519635808901356020818102808b018201909452818a5297999860a4989097506024929092019550935083925085019084908082843750949650505050505050621e848354600090600160a060020a03908116339091161415610a8e575b8551811015610a8e57606060405190810160405280610d1186610ebc565b60408051602060248035600481810135601f81018590048502860185019096528585526109a5958135959194604494929390920191819084018382808284375094965050933593505050505b6000831515610699577ffd33e90d0eac940755277aa91045b95664988beeeafc4ed7d1281a6d83afbc003384846040518084600160a060020a03168152602001806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156106895780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a15b509192915050565b610a7660043560016020526000908152604090205461ffff1681565b6100f0600435621e848354600160a060020a0390811633909116141561026857621e848455565b610a7660025461ffff1681565b604080516004803580820135602081810280860182019096528185526109a59593946024949093850192918291908501908490808284375050604080518735808a013560208181028085018201909552818452989a9960449993985091909101955093508392508501908490808284375050604080519635808901356020818102808b018201909452818a529799986064989097506024929092019550935083925085019084908082843750506040805196358089013560208181028a81018201909452818a5297999860849890975060249290920195509350839250850190849080828437509496505050505050506000600060006000610ad68751895114606060405190810160405280602381526020017f446966666572656e74206e756d626572206f66207856616c732061732079566181526020017f6c732e00000000000000000000000000000000000000000000000000000000008152602001508a516105e7565b6109a5621e84875481565b6100f06004356024356044355b6000610a96848484345b6000808080808060038a6103e8811015610002576107d00201896103e88110156100025760020201805461ffff16825260208290526040822054621e8484546001830154621e848654939850600160a060020a03928316975060649181028290049650929092029190910492503316841415610f4d57610fbc82341015606060405190810160405280602e81526020017f4368616e67696e6720796f7572206f776e20706978656c20636f73747320313081526020017f25206f66206974732076616c7565000000000000000000000000000000000000815260200150846105e7565b6100f0621e848354600160a060020a039081163390911614156109a357604051621e848354600160a060020a03908116916000913016319082818181858883f150505050505b565b60408051918252519081900360200190f35b60408051600160a060020a0394909416845260039290920b602084015282820152519081900360600190f35b6040805161ffff94909416845260039290920b602084015282820152519081900360600190f35b6040518084617d008083818460006004610bc7f150918201918591508083818460006004610bc7f15061fa00840192508491508083818460006004610bc7f15062017700965092945050505050f35b60408051600160a060020a03929092168252519081900360200190f35b6040805161ffff929092168252519081900360200190f35b505050505050565b90506000811115610ac25760405133600160a060020a031690600090839082818181858883f150505050505b50505050565b93505b505050949350505050565b1580610b4e5750610b4c8651895114606060405190810160405280602481526020017f446966666572656e74206e756d626572206f66207856616c7320617320636f6c81526020017f6f72732e000000000000000000000000000000000000000000000000000000008152602001508a516105e7565b155b80610bc55750610bc38551895114606060405190810160405280602481526020017f446966666572656e74206e756d626572206f66207856616c732061732070726981526020017f6365732e000000000000000000000000000000000000000000000000000000008152602001508a516105e7565b155b15610bd35760009350610acb565b5034915060009050805b8751811015610c63578481815181101561000257602090810290910101519092039160008310610d0157610cfb88828151811015610002579060200190602002015188838151811015610002579060200190602002015188848151811015610002579060200190602002015188858151811015610002579060200190602002015161087a565b6000821115610c8d5760405133600160a060020a031690600090849082818181858883f150505050505b610ac86000841015606060405190810160405280602181526020017f56616c756520776173206c657373207468616e2073756d206f6620707269636581526020017f7300000000000000000000000000000000000000000000000000000000000000815260200150856105e7565b91909101905b600101610bdd565b509193909250565b8152602001848381518110156100025790602001906020020151815260200183838151811015610002579060200190602002015181526020015060036000508783815181101561000257906020019060200201516103e8811015610002576107d002016000508683815181101561000257906020019060200201516103e8811015610002576002020160005081518154602084015160e060020a90810204620100000261ffff199190911690911765ffffffff00001916178155604091909101516001919091015560010161057d565b8454600061ffff919091161115610e225750604051621e84855460649088020490600160a060020a03851690600090838a039082818181858883f150505050505b845460018601546040805161ffff8e811682528d166020820152600160a060020a038881168284015262010000909404600390810b810b606083015260808201939093523390931660a0840152908a900b60c083015260e08201899052517fcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42918190036101000190a1606060405190810160405280611143335b600160a060020a03811660009081526001602052604081205461ffff1690811415610f485750604060008181206002805461ffff1981811661ffff928316600190810191821790945591821685526020858152958520805473ffffffffffffffffffffffffffffffffffffffff191688179055600160a060020a03871690945293528054909116821790555b919050565b60408051621e848754606082018352602182527f4d696e696d756d20706978656c2070726963652069732035302066696e6e657960208301527f2e00000000000000000000000000000000000000000000000000000000000000928201929092526110c29134101590896105e7565b1515610fca578695506110b5565b33600160a060020a031684600160a060020a03161415610de157604080518654600188015461ffff8e811684528d166020840152600160a060020a03881683850181905262010000909204600390810b810b60608501526080840182905260a0840192909252908b900b60c083015260e082015290517fcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42918190036101000190a18760038b6103e8811015610002576107d002018a6103e88110156100025760020201805465ffffffff0000191660e060020a92830292909204620100000291909117905581870395505b5050505050949350505050565b15806111365750610fbc83341015606060405190810160405280603281526020017f56616c7565206d7573742062652031302520686967686572207468616e20637581526020017f7272656e7420706978656c2070726963652e0000000000000000000000000000815260200150856105e7565b15610fca578695506110b5565b8152602081018a905260400188905260038b6103e8811015610002576107d002018a6103e8811015610002576002020160005081518154602084015160e060020a90810204620100000261ffff199190911690911765ffffffff000019161781556040919091015160019190910155600095506110b556",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000175901": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000175902": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000175903": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000175904": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760c7": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760c8": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760c9": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760ca": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760cb": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760cc": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760cd": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760ce": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760cf": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760d0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760d1": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760d2": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760d3": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760d4": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760d5": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760d6": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760d7": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760d8": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760d9": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760da": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760db": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760dc": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760dd": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760de": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760df": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001760e0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000176897": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000176898": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000176899": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000017689a": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000017689b": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000017689c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000017689d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000017689e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000017689f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768a0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768a7": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768a8": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768a9": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768aa": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768ab": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768ac": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768ad": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768ae": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768af": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001768b0": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c37": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c38": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c39": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c3a": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c3b": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c3c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c3d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c3e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c3f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c40": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c45": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c46": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c47": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c48": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c49": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c4a": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c4b": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c4c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c4d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c4e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c4f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000196c50": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197407": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197408": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197409": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019740a": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019740b": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019740c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019740d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019740e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019740f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197410": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197411": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197412": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197413": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197414": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197415": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197416": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197417": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197418": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197419": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019741a": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019741b": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019741c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019741d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019741e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000019741f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197420": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197be3": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000197be4": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000001e8484": "0x000000000000000000000000000000000000000000000000000000000000006e",
+ "0x00000000000000000000000000000000000000000000000000000000001e8486": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0x00000000000000000000000000000000000000000000000000000000001e8487": "0x0000000000000000000000000000000000000000000000000011c37937e08000",
+ "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xe1723559c995b1804c0512df6fe6d061eeb47aff37a3ced3b93f0c1bef247540": "0x0000000000000000000000000000000000000000000000000000000000000007"
+ }
+ },
+ "0x3fcb0342353c541e210013aaddc2e740b9a33d08": {
+ "balance": "0x6a0e4be198f18400",
+ "nonce": "17"
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "595532",
+ "difficulty": "7503588162862",
+ "timestamp": "1448471495",
+ "gasLimit": "3141592",
+ "miner": "0x2a65aca4d5fc5b5c859090a6c34d164135398226"
+ },
+ "input": "0xf91a7311850ba43b7400832dc6c094350e0ffc780a6a75b44cc52e1ff90928706689458803782dace9d90000b91a04e021fadb000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000006e00000000000000000000000000000000000000000000000000000000000000d4000000000000000000000000000000000000000000000000000000000000013a00000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fd000000000000000000000000000000000000000000000000000000000000034300000000000000000000000000000000000000000000000000000000000002fd0000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003900000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003b00000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003d00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003700000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003900000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003b00000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003d00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003e0000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000032fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffebebebffffffffffffffffffffffffffffffffffffffffffffffffffffffffff888888ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb3b3b3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe3e3e3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3e3e3effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdbfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f4f4fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0b0b0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0a0a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b5b5bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbababaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeaeaeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa9a9a9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb9b9b9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfbfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbababaffffffffffffffffffffffffffffffffffffffffffffffffffffffffff636363fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9f9f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeaeaeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c9c9cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8f8f8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4d4e53ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f494b00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080001ca0e8a879dd98a39d735b866ff64d84e9c144a17bcab106cf2f1327b1272db06aaca02ab279a2459b5e30dfea0bc8a888c7d2a190740090352b4a7aded30c45490af9",
+ "tracerConfig": {
+ "withLog": true
+ },
+ "result": {
+ "from": "0x3fcb0342353c541e210013aaddc2e740b9a33d08",
+ "gas": "0x2b0868",
+ "gasUsed": "0x2570bf",
+ "to": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "input": "0xe021fadb000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000006e00000000000000000000000000000000000000000000000000000000000000d4000000000000000000000000000000000000000000000000000000000000013a00000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000002fd000000000000000000000000000000000000000000000000000000000000034300000000000000000000000000000000000000000000000000000000000002fd0000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003900000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003b00000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003d00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003700000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003900000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003b00000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003d00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003e0000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000032fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffebebebffffffffffffffffffffffffffffffffffffffffffffffffffffffffff888888ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb3b3b3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe3e3e3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3e3e3effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdbfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f4f4fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0b0b0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0a0a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b5b5bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbababaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeaeaeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa9a9a9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb9b9b9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfbfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbababaffffffffffffffffffffffffffffffffffffffffffffffffffffffffff636363fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9f9f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeaeaeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c9c9cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8f8f8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4d4e53ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f494b00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000011c37937e08000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "logs": [
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000000390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefe0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002ff00000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfd0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffebebeb0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8888880000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000341000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb3b3b30000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002ff00000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfc0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000003b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe3e3e30000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000341000000000000000000000000000000000000000000000000000000000000003b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3e3e3e0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002ff00000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000341000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdb0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002ff00000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f4f40000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000003d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000341000000000000000000000000000000000000000000000000000000000000003d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002ff00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfb0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002ff000000000000000000000000000000000000000000000000000000000000003e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000341000000000000000000000000000000000000000000000000000000000000003e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0b0b00000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefe0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0a0a00000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b5b5b0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000000390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbababa0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000000390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeaeaea0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa9a9a90000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb9b9b90000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000342000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfb0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefe0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefe0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000003b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbababa0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000342000000000000000000000000000000000000000000000000000000000000003b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6363630000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9f9f90000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeaeaea0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000342000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c9c9c0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8f8f80000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000003d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfd0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000342000000000000000000000000000000000000000000000000000000000000003d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034200000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfc0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fe000000000000000000000000000000000000000000000000000000000000003e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfd0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000000342000000000000000000000000000000000000000000000000000000000000003e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4d4e530000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x000000000000000000000000000000000000000000000000000000000000034300000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000011c37937e08000"
+ },
+ {
+ "address": "0x350e0ffc780a6a75b44cc52e1ff9092870668945",
+ "topics": [
+ "0xcacb62d8acea4678658eb5dc4aaa889b34d893b967c96a5f8c066e6549fa3f42"
+ ],
+ "data": "0x00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fcb0342353c541e210013aaddc2e740b9a33d08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f494b0000000000000000000000000000000000000000000000000011c37937e08000"
+ }
+ ],
+ "value": "0x3782dace9d90000",
+ "type": "CALL"
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json
new file mode 100644
index 0000000000..09aa7af461
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json
@@ -0,0 +1,286 @@
+{
+ "genesis": {
+ "difficulty": "45944156141275",
+ "extraData": "0xd783010406844765746887676f312e342e32856c696e7578",
+ "gasLimit": "4714680",
+ "hash": "0x3c41811ab60f232565db6cfafb939d96255b9f678a203181c6f537d6c22d7e6f",
+ "miner": "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5",
+ "mixHash": "0x8b736c63e05d381ae593d584b63fef5c31b04a3cea72bd5a3c92f95f4f7040e8",
+ "nonce": "0xce8ffb5c1ad942ec",
+ "number": "1725115",
+ "stateRoot": "0xca08a341c1f95fcba0821c4a27662ef162d39e1f9f5722717531f510d54112b0",
+ "timestamp": "1466232982",
+ "totalDifficulty": "28554024908214037524",
+ "alloc": {
+ "0x0000000000000000000000000000000000000004": {
+ "balance": "0x0"
+ },
+ "0x1d3b2638a7cc9f2cb3d298a3da7a90b67e5506ed": {
+ "balance": "0x0",
+ "code": "0x606060405260e060020a600035046338cc483181146038578063767800de14604f578063a6f9dae1146060578063d1d80fdf14607e575b005b600054600160a060020a03165b6060908152602090f35b6045600054600160a060020a031681565b603660043560015433600160a060020a03908116911614609c576002565b603660043560015433600160a060020a0390811691161460be576002565b6001805473ffffffffffffffffffffffffffffffffffffffff19168217905550565b6000805473ffffffffffffffffffffffffffffffffffffffff1916821790555056",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x00000000000000000000000088e1315687aec48a72786c6b3b3f075208b62713"
+ }
+ },
+ "0x50739060a2c32dc076e507ae1a893aab28ecfe68": {
+ "balance": "0x6a8ecefb09f7c4141",
+ "code": "0x606060405236156101745760e060020a6000350463058aace1811461017f578063061e494f146101905780630d1fce421461021e57806311610c251461029157806312253a6c146102b5578063132ae5e9146102d357806316d190e3146102dc57806329e206bd146102e5578063337b68ba1461030a57806338bbfa50146103135780633f683b6a146104115780634dc6b523146104245780634e69d5601461042d57806366d16cc31461044a578063724ae9d014610453578063758971e81461046f5780637cf0ffcb146104965780638ca17995146104a35780639619367d146104b7578063a96a5a5b146104c0578063adc2c98a146104c9578063b70d0b3b146104d2578063bc99cc37146104db578063c4bc5da5146104e4578063cafb220214610502578063d28442ef1461050b578063d4c80edf14610514578063df06f9061461051d578063e8b5e51f14610527578063f738e5ca14610546578063f8b2cb4f14610553578063fa968eea14610594575b610661610663610295565b6106616000341115610eab57610002565b61066560043560006000600060006000600f6000508054905086101561021657600f8054879081101561000257505050507f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80284015490819052600e602052604090912080546001820154600283015460039390930154600160a060020a03929092169450925b509193509193565b6106965b601254601354601154600c5460009391019091010330600160a060020a0316318190101561028957604080517f62616e6b726f6c6c5f6d69736d61746368000000000000000000000000000000815290519081900360110190a05030600160a060020a0316315b8091505b5090565b6106615b600060006000600d60149054906101000a900460ff16156106ef57610002565b610661600d5433600160a060020a03908116911614610fd657610002565b610696600a5481565b61069660045481565b6106616004355b600d5460009033600160a060020a0390811691161461101c57610002565b61069660125481565b60408051602060248035600481810135601f81018590048502860185019096528585526106619581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a0190935282825296989760649791965060249190910194509092508291508401838280828437509496505050505050506000600060006000610a18600080546040805160e060020a6338cc483102815290518392600160a060020a0316916338cc4831916004828101926020929190829003018187876161da5a03f11561000257505060405151915050600160a060020a0381168214156114635761140b6000610939565b610696600d5460a060020a900460ff1681565b61069660085481565b6106a8600060006000600060006000600060006000610f8d610222565b61069660115481565b6106965b600a54600654600091829182911015610ef857610f33565b6106616004355b600d54600090819033600160a060020a0390811691161461108657610002565b61066161066360006102ec565b6106616004356000341115610e7957610002565b61069660055481565b61069660025481565b61069660035481565b61069660075481565b61069660065481565b610661600d5433600160a060020a03908116911614610ffc57610002565b610696600c5481565b61069660135481565b61069660105481565b610696600f545b90565b610661600d54600090819060a060020a900460ff1615610c8057610002565b6106616106636000610476565b6106966004355b600160a060020a0381166000908152600b602052604081205481901180156105845750600c548190115b15610ebd57600c54610ec6610222565b610696600080546040805160e060020a6338cc483102815290518392600160a060020a0316916338cc4831916004828101926020929190829003018187876161da5a03f11561000257505060408051805160e260020a630bbceb33028252620249f06024830152600482018390526003604483015260ea60020a621554930260648301529151600160a060020a03929092169250632ef3accc916084828101926020929190829003018187876161da5a03f1156100025750506040515160055481019350915061028d9050565b005b565b60408051600160a060020a039590951685526020850193909352838301919091526060830152519081900360800190f35b60408051918252519081900360200190f35b60408051998a5260208a0198909852888801969096526060880194909452608087019290925260a086015260c085015260e084015261010083015251908190036101200190f35b600060009054906101000a9004600160a060020a0316600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060408051805160e260020a630bbceb33028252620249f06024830152600482018390526003604483015260ea60020a621554930260648301529151600160a060020a03929092169250632ef3accc91608480830192602092919082900301816000876161da5a03f1156100025750506040515193505034839010156107c257610002565b82340391506127106107d2610222565b600460005054020460026000505460026000505460036000505461271003038402041115801561080457506005548210155b1561095a576040805180820182526003815260ea60020a62155493026020828101919091528251608081018452604381527f6a736f6e2868747470733a2f2f6170692e72616e646f6d2e6f72672f6a736f6e818301527f2d7270632f312f696e766f6b65292e726573756c742e72616e646f6d2e646174818501527f612e30000000000000000000000000000000000000000000000000000000000060608201528351610160810190945261012c80855261095f94919261175690830139620249f0600060006000600060009054906101000a9004600160a060020a0316600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060405151915050600160a060020a03811682141561118c5761113460005b600060006115ef731d3b2638a7cc9f2cb3d298a3da7a90b67e5506ed5b3b90565b610002565b6040805160808101825233815260208181018681526000838501818152606085018a8152878352600e90945293519490208054600160a060020a031916909417845551600184810191909155915160028401555160039290920191909155600f8054918201808255929350918281838015829011610a0057818360005260206000209182019101610a0091905b8082111561028d57600081556001016109ec565b5050506000928352506020909120018190555b505050565b600160a060020a031633600160a060020a0316141515610a3757610002565b6000878152600e6020526040812060018101549095501115610c5257600d5460a060020a900460ff166000148015610aa15750612710610a75610222565b600460005054020460026000505460026000505460036000505461271003038660010160005054020411155b15610b4957610b968660006114e28260006040805160208101909152600090819052828180805b8351811015610b3e57603060f860020a02848281518110156100025790602001015160f860020a900460f860020a0210158015610b295750603960f860020a02848281518110156100025790602001015160f860020a900460f860020a0211155b156116cb578115611722578560001415611719575b509095945050505050565b60018401548454610c5291600160a060020a0391909116905b604051600160a060020a038316906161a89083906000818181858888f193505050501515610d005760138054820190555050565b92506001831080610ba8575061271083115b15610bc75783546001850154610c5291600160a060020a031690610b62565b6000878152600e6020526040902060029081018490555460001984011015610c5b57506002546003546001850154855461271092909203029190910490610c7190600160a060020a031682610b62565b60018401546000190191505b601380546007546127109085020590810190915560118054918403909101905560018401546010805490910190555b50505050505050565b8354610c1790600160a060020a03166001610b62565b60018401548190039150610c23565b33600160a060020a03166000908152600b60205260408120541115610ca757610cc5610cab565b610d045b6011546012546000918291829114610a13576114e9610222565b33600160a060020a03166000908152600b6020908152604080832054835260099091529020600101805434908101909155600c805490910190555b5050565b600a5460065460009350901015610d6557600a80546001019081905591505b600082111561095a576000828152600960205260408120600101541115610deb576040600020805460019190910154610dc591600160a060020a031690610e7f565b5060015b600a548111610d23576000818152600960205260409020600101543490108015610db457508160001480610db457506040600081812060019081015485835292822001549083905290105b15610dbd579050805b600101610d69565b600082815260096020908152604080832054600160a060020a03168352600b9091528120555b600082815260096020526040812060010154148015610e2357506040600081812054600160a060020a03168152600b60205290812054145b1561095a5760008281526009602090815260408083208054600160a060020a03191633908117825534600192909201829055600c8054909201909155600160a060020a03168352600b9091529020829055610d00565b610ea833825b600160a060020a0382166000908152600b602052604081205481901115610a1357611578610cab565b50565b61066333610eb83361055a565b610e7f565b5060005b919050565b600160a060020a0384166000908152600b60209081526040808320548352600990915290206001015402049050610ec1565b5060015b600a548111610f38578160001480610f5b5750600082815260096020526040902054610f6c90600160a060020a031661055a565b92505b505090565b600082815260096020526040902054610f3090600160a060020a031661055a565b105b15610f64579050805b600101610efc565b600082815260096020526040902054610f5990600160a060020a031661055a565b601154600254600354600454600554601054939492939192909190610fb0610457565b600f60005080549050985098509850985098509850985098509850909192939495969798565b600d805474ff0000000000000000000000000000000000000000191660a060020a179055565b600d805474ff000000000000000000000000000000000000000019169055565b5060015b600a54811161104e5760008181526009602052604090205461107e90600160a060020a0316610eb88161055a565b8115610d0057604051600d54600160a060020a03908116916000913016319082818181858883f150505050505050565b600101611020565b82156110bb57506000905060015b600a5481116110e55760008181526009602052604090206001015490910190600101611094565b601354604051600d54600160a060020a03169160009182818181858883f150505060135550505050565b816000148015611101575060135430600160a060020a03163114155b1561112f57604051600d54600160a060020a03908116916000913016319082818181858883f1505050601355505b610a13565b50600060009054906101000a9004600160a060020a0316600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604051519150505b60018054600160a060020a0319168217908190556040805160e260020a630bbceb330281526024810187905260048181019283528a5160448301528a51600160a060020a039490941693632ef3accc938c938a939192839260649290920191602087810192918291859183918691600091601f850104600f02600301f150905090810190601f1680156112335780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f115610002575050604051519250503a8402670de0b6b3a76400000182111561127c5750600091505b50949350505050565b600160009054906101000a9004600160a060020a0316600160a060020a03166385dee34c8360008a8a8a8a6040518760e060020a028152600401808681526020018060200180602001806020018581526020018481038452888181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156113275780820380516001836020036101000a031916815260200191505b508481038352878181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156113805780820380516001836020036101000a031916815260200191505b508481038252868181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156113d95780820380516001836020036101000a031916815260200191505b509850505050505050505060206040518083038185886185025a03f11561000257505060405151945061127392505050565b50600060009054906101000a9004600160a060020a0316600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604051519150505b60018054600160a060020a031916821790819055604080517fc281d19e0000000000000000000000000000000000000000000000000000000081529051600160a060020a03929092169163c281d19e9160048181019260209290919082900301816000876161da5a03f115610002575050604051519250610524915050565b9050610ec1565b9150600190505b600a54811161151a5760008181526009602052604090205461155990600160a060020a031661055a565b600c8390558282148015906115325750600a54600090115b1561154e5760138054848403908101909155600c805490910190555b601154601255505050565b60008281526009602052604090206001908101829055930192016114f0565b6115818361055a565b821115611594576115918361055a565b91505b50600160a060020a0382166000908152600b602090815260408083205483526009909152902060010180548290039055600c8054829003905560085460138054612710928402929092049182019055610a1383828403610b62565b1115611623575060008054600160a060020a031916731d3b2638a7cc9f2cb3d298a3da7a90b67e5506ed1790556001610ec1565b6000611642739efbea6358bed926b293d2ce63a730d6d98d43dd610956565b1115611678575060008054739efbea6358bed926b293d2ce63a730d6d98d43dd600160a060020a03199091161790556001610ec1565b60006116977320e12a1f859b3feae5fb2a0a32c18f5a65555bbf610956565b1115610ebd575060008054600160a060020a0319167320e12a1f859b3feae5fb2a0a32c18f5a65555bbf1790556001610ec1565b8381815181101561000257016020015160f860020a90819004027f2e00000000000000000000000000000000000000000000000000000000000000141561171157600191505b600101610ac8565b60001995909501945b600a83029250825060308482815181101561000257016020015160f860020a90819004810204909301602f19019250611711564244584a68725670424a35336f3243786c4a526c51745a4a4b5a714c5974354951652b37335944533448744e6a5335486f64624942337476666f773755717579416b303835566b4c6e4c3945704b67777157517a375a4c64477673516c526432734b78496f6c4e673944626e6650737047714c684c62625953566e4e38437776736a7041586353536f33632b34634e774339307946346f4e69626b764433797461706f5a37676f5453796f5559546677536a6e773374692b484a5648374e332b633069774f43715a6a4464734751556358336d33532f494857624f4f5151356f734f344c626a33476730783155644e7466557a5943465937396e7a596757495145464375524249306e364e42764251573732372b4f73445259304a2f392f676a74387563696248576963303d",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000001d3b2638a7cc9f2cb3d298a3da7a90b67e5506ed",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000088e1315687aec48a72786c6b3b3f075208b62713",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x00000000000000000000000000000000000000000000000000000000000009c4",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000000000000000000000000000000000000000000000000000be",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000064",
+ "0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000000000000000000000000000002c68af0bb140000",
+ "0x000000000000000000000000000000000000000000000000000000000000000c": "0x000000000000000000000000000000000000000000000006ad2ff8ba84afdcdc",
+ "0x000000000000000000000000000000000000000000000000000000000000000d": "0x000000000000000000000000a1b5f95be71ffa2f86adefcaa0028c46fe825161",
+ "0x000000000000000000000000000000000000000000000000000000000000000f": "0x0000000000000000000000000000000000000000000000000000000000000022",
+ "0x0000000000000000000000000000000000000000000000000000000000000011": "0xffffffffffffffffffffffffffffffffffffffffffffffffd14ae0a37b4cc1d4",
+ "0x0000000000000000000000000000000000000000000000000000000000000012": "0xffffffffffffffffffffffffffffffffffffffffffffffffd5ab72be30cb5f50",
+ "0x0000000000000000000000000000000000000000000000000000000000000013": "0xffffffffffffffffd5bbd8ce9d1eb44232ca20eb5b4319ac5e1982d2c94bc3cb",
+ "0x8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac824": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xe950f1be9a49788ef79ea4e854ed56155a7f60661724f41e3af5f799203a1eb9": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xe950f1be9a49788ef79ea4e854ed56155a7f60661724f41e3af5f799203a1eba": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xe950f1be9a49788ef79ea4e854ed56155a7f60661724f41e3af5f799203a1ebb": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xe950f1be9a49788ef79ea4e854ed56155a7f60661724f41e3af5f799203a1ebc": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x61c808d82a3ac53231750dadc13c777b59310bd9": {
+ "balance": "0x12f621ea72fef44f848",
+ "nonce": "51830"
+ },
+ "0x6412becf35cc7e2a9e7e47966e443f295e1e4f4a": {
+ "balance": "0xfb5dbfc0d448e70",
+ "nonce": "6"
+ },
+ "0x88e1315687aec48a72786c6b3b3f075208b62713": {
+ "balance": "0x24b9f2c5dc266dc6",
+ "code": "0x606060405236156101535760e060020a60003504630f825673811461018f57806323dc42e7146102135780632ef3accc146102ad578063453629781461033b578063480a434d146103d5578063524f3889146103de5780635c242c591461043f57806360f66701146104de57806362b3b8331461056757806368742da6146105eb578063688dcfd71461062b578063757004371461065857806377228659146106f25780637d242ae5146107cd5780637e1c42051461085357806381ade3071461033b57806385dee34c14610932578063a2ec191a14610a0c578063adf59f9914610213578063ae81584314610658578063b5bfdd7314610a64578063bf1fe42014610af2578063c281d19e14610b32578063c51be90f14610b44578063ca6ad1e414610bdd578063d959701614610bff578063db37e42f14610cb6578063de4b326214610d6d578063e839e65e14610daf575b61065660025433600160a060020a039081169116148015906101855750600154600160a060020a039081163390911614155b15610e8a57610002565b6106566004808035906020019082018035906020019191908080601f01602080910402602001604051908101604052809392919081815260200183838082843750949650505050505050600254600160a060020a0390811633909116148015906102095750600154600160a060020a039081163390911614155b15610ebb57610002565b60408051602060248035600481810135601f8101859004850286018501909652858552610e8c9581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a0190935282825296989760649791965060249190910194509092508291508401838280828437509496505050505050506000610f2084848462030d406104cb565b610e8c6004808035906020019082018035906020019191908080601f0160208091040260200160405190810160405280939291908181526020018383808284375094965050933593505050506000610f288383335b6000600062030d40841115801561032d5750600160a060020a03831681526020819052604081205481145b1561184e5760009150611846565b610e8c6004808035906020019082018035906020019191908080601f01602080910402602001604051908101604052809392919081815260200183838082843750506040805160208835808b0135601f81018390048302840183019094528383529799986044989297509190910194509092508291508401838280828437509496505050505050506000610f286000848462030d406104cb565b610e8c60085481565b610e8c6004808035906020019082018035906020019191908080601f016020809104026020016040519081016040528093929190818152602001838380828437509496505050505050506000610f2f82336000610f288362030d4084610302565b60408051602060248035600481810135601f8101859004850286018501909652858552610e8c9581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897606497919650602491909101945090925082915084018382808284375094965050933593505050505b600083826000600061113d848433610302565b6106566004808035906020019082018035906020019191908080601f0160208091040260200160405190810160405280939291908181526020018383808284375094965050505050505080604051808280519060200190808383829060006004602084601f0104600f02600301f150905001915050604051809103902060046000508190555050565b6106566004808035906020019082018035906020019191908080601f01602080910402602001604051908101604052809392919081815260200183838082843750949650505050505050600254600160a060020a0390811633909116148015906105e15750600154600160a060020a039081163390911614155b1561119757610002565b610656600435600254600160a060020a0390811633909116148015906106215750600154600160a060020a039081163390911614155b156111f957610002565b600160a060020a0333166000908152600660205260409020805460ff191660f860020a600435041790555b005b60408051602060248035600481810135601f8101859004850286018501909652858552610e8c9581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897606497919650602491909101945090925082915084018382808284375094965050933593505050505b6000610f1d858585856104cb565b60408051602060248035600481810135601f8101859004850286018501909652858552610e8c9581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a0190935282825296989760849791965060249190910194509092508291508401838280828437509496505050505050506000610f1d8585858562030d4061091f565b60408051602060248035600481810135601f81018590048502860185019096528585526106569581359591946044949293909201918190840183828082843750949650505050505050600254600090600160a060020a0390811633909116148015906108495750600154600160a060020a039081163390911614155b1561121f57610002565b60408051602060248035600481810135601f8101859004850286018501909652858552610e8c9581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897608497919650602491909101945090925082915084018382808284375094965050933593505050505b6000848260006000611516848433610302565b60408051602060248035600481810135601f8101859004850286018501909652858552610e8c9581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a0190935282825296989760849791965060249190910194509092508291508401838280828437509496505093359350505050600061156b868686868661091f565b6106566004808035906020019082018035906020019191908080601f01602080910402602001604051908101604052809392919081815260200183838082843750949650509335935050505061157282600083610ab5565b6106566004808035906020019082018035906020019191908080601f016020809104026020016040519081016040528093929190818152602001838380828437509496505093359350506044359150505b600254600090600160a060020a039081163390911614801590610ae85750600154600160a060020a039081163390911614155b1561157657610002565b610656600435600254600160a060020a039081163390911614801590610b285750600154600160a060020a039081163390911614155b1561162f57610002565b610e9e600154600160a060020a031681565b60408051602060248035600481810135601f8101859004850286018501909652858552610e8c9581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897606497919650602491909101945090925082915084018382808284375094965050933593505050506000610f1d858585856106e4565b600160a060020a03331660009081526007602052604090206004359055610656565b604080516004803580820135602081810285810182019096528185526106569593946024949093850192918291908501908490808284375050604080518735808a013560208181028085018201909552818452989a99604499939850919091019550935083925085019084908082843750949650505050505050600254600090600160a060020a039081163390911614801590610cac5750600154600160a060020a039081163390911614155b1561163457610002565b604080516004803580820135602081810285810182019096528185526106569593946024949093850192918291908501908490808284375050604080518735808a013560208181028085018201909552818452989a99604499939850919091019550935083925085019084908082843750949650505050505050600254600090600160a060020a039081163390911614801590610d635750600154600160a060020a039081163390911614155b1561168f57610002565b61065660043560025460009033600160a060020a03908116911614801590610da55750600154600160a060020a039081163390911614155b1561170557610002565b610e8c6004808035906020019082018035906020019191908080601f01602080910402602001604051908101604052809392919081815260200183838082843750506040805160208835808b0135601f8101839004830284018301909452838352979998604498929750919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a0190935282825296989760649791965060249190910194509092508291508401838280828437509496505050505050506000610f20600085858562030d4061091f565b565b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b60006003600050600083604051808280519060200190808383829060006004602084601f0104600f02600301f1509050019150506040518091039020815260200190815260200160002060006101000a81548160ff0219169083021790555050565b90505b949350505050565b9392505050565b92915050565b6000600050600033600160a060020a031681526020019081526020016000206000505433600160a060020a031630600160a060020a03160101604051808281526020019150506040518091039020945084506000600050600033600160a060020a031681526020019081526020016000206000818150548092919060010191905055507fb76d0edd90c6a07aa3ff7a222d7f5933e29c6acc660c059c97837f05c4ca1a8433868b8b8b8b6006600050600033600160a060020a0316815260200190815260200160002060009054906101000a900460f860020a026007600050600033600160a060020a03168152602001908152602001600020600050546040518089600160a060020a0316815260200188815260200187815260200180602001806020018681526020018581526020018481526020018381038352888181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156110c35780820380516001836020036101000a031916815260200191505b508381038252878181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561111c5780820380516001836020036101000a031916815260200191505b509a505050505050505050505060405180910390a150505050949350505050565b91503482901061119257813403905060008111156111765760405133600160a060020a031690600090839082818181858883f150505050505b42624f1a000189118061118857504586115b15610f3557610002565b610002565b60016003600050600083604051808280519060200190808383829060006004602084601f0104600f02600301f1509050019150506040518091039020815260200190815260200160002060006101000a81548160ff0219169083021790555050565b604051600160a060020a03828116916000913016319082818181858883f1505050505050565b50600882905560005b600b548110156112a757600b8054600a91600091849081101561000257508054600080516020611883833981519152850154835260209390935260408220548602926009929190859081101561000257908252600080516020611883833981519152018150548152602081019190915260400160002055600101611228565b505050565b6000600050600033600160a060020a031681526020019081526020016000206000505433600160a060020a031630600160a060020a03160101604051808281526020019150506040518091039020945084506000600050600033600160a060020a031681526020019081526020016000206000818150548092919060010191905055507faf30e4d66b2f1f23e63ef4591058a897f67e6867233e33ca3508b982dcc4129b33868c8c8c8c8c6006600050600033600160a060020a0316815260200190815260200160002060009054906101000a900460f860020a026007600050600033600160a060020a0316815260200190815260200160002060005054604051808a600160a060020a0316815260200189815260200188815260200180602001806020018060200187815260200186815260200185815260200184810384528a8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561143f5780820380516001836020036101000a031916815260200191505b508481038352898181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156114985780820380516001836020036101000a031916815260200191505b508481038252888181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156114f15780820380516001836020036101000a031916815260200191505b509c5050505050505050505050505060405180910390a1505050505b95945050505050565b915034829010611192578134039050600081111561154f5760405133600160a060020a031690600090839082818181858883f150505050505b42624f1a00018a118061156157504586115b156112ac57610002565b905061150d565b5050565b8383604051808380519060200190808383829060006004602084601f0104600f02600301f150905001828152600101925050506040518091039020905080600b600050600b600050805480919060010190908154818355818115116115fe578183600052602060002091820191016115fe91905b8082111561162b57600081556001016115ea565b5050508154811015610002576000918252602080832090910192909255918252600a905260409020555050565b5090565b600555565b5060005b81518110156112a7578281815181101561000257906020019060200201516007600050600084848151811015610002576020908102909101810151600160a060020a03168252919091526040902055600101611638565b5060005b81518110156112a75782818151811015610002579060200190602002015160f860020a026006600050600084848151811015610002576020908102909101810151600160a060020a031682529190915260409020805460f860020a90920460ff19909216919091179055600101611693565b50600881905560005b600b5481101561157257600b8054600a91600091849081101561000257600080516020611883833981519152015482526020929092526040812054825490850292600992918590811015610002576000805160206118838339815191520154825250602091909152604090205560010161170e565b60096000506000866006600050600087600160a060020a0316815260200190815260200160002060009054906101000a900460f860020a02604051808380519060200190808383829060006004602084601f0104600f02600301f150905001828152600101925050506040518091039020815260200190815260200160002060005054915081506007600050600084600160a060020a03168152602001908152602001600020600050549050806000141561183d57506005545b83810291909101905b509392505050565b600454600014801590611875575060045460009081526003602052604090205460ff166001145b156117835760009150611846560175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0xbb130806898f085471286ecb4f3966fcbe090ba29e4f9d194ee9e9062f6b61ae",
+ "0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000000000000000000000000000000000004a817c800",
+ "0x797fdd0f6c82412493cfa2aacdc9999c10e5d0c9aa3f05a8a289b1b3918c6db8": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8d90a37db271d62339ebfe84641d1ebdaf56fd5d50861d795eacb410dbb57630": "0x000000000000000000000000000000000000000000000000000cf4e712e8d654",
+ "0x9864048b6d6c99ecd7fcaecf663fbe1036a6e1fc00cec0a3eb25684dd08184c2": "0x0000000000000000000000000000000000000000000000000000000000000011",
+ "0xca9ea8077ddc97a21c029df4b19819e51903e11d4bfc7564a622a192cefd6356": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xf34e44a0672ef76b852374cc47d9772eb4e5e41fa79fba61dcfc9cf7d50418d5": "0x0000000000000000000000000000000000000000000000000000000000000022"
+ }
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "1725116",
+ "difficulty": "45966589844033",
+ "timestamp": "1466232988",
+ "gasLimit": "4716972",
+ "miner": "0x61c808d82a3ac53231750dadc13c777b59310bd9"
+ },
+ "input": "0xf86d068504e3b2920083030d409450739060a2c32dc076e507ae1a893aab28ecfe68880429d069189e0000801ca04e403b46022c2098e41d3a0e561881ac368cd330637239da85759c1b4f44ab24a072a88235d98959283c00af411bd663b0da8703e05a94d3673aca37d0a39b7e07",
+ "tracerConfig": {
+ "withLog": true
+ },
+ "result": {
+ "from": "0x6412becf35cc7e2a9e7e47966e443f295e1e4f4a",
+ "gas": "0x2bb38",
+ "gasUsed": "0x249eb",
+ "to": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "input": "0x",
+ "calls": [
+ {
+ "from": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "gas": "0x257af",
+ "gasUsed": "0xbc",
+ "to": "0x1d3b2638a7cc9f2cb3d298a3da7a90b67e5506ed",
+ "input": "0x38cc4831",
+ "output": "0x00000000000000000000000088e1315687aec48a72786c6b3b3f075208b62713",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "gas": "0x255a1",
+ "gasUsed": "0x73a",
+ "to": "0x88e1315687aec48a72786c6b3b3f075208b62713",
+ "input": "0x2ef3accc000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000249f0000000000000000000000000000000000000000000000000000000000000000355524c0000000000000000000000000000000000000000000000000000000000",
+ "output": "0x00000000000000000000000000000000000000000000000000179d63013c5654",
+ "calls": [
+ {
+ "from": "0x88e1315687aec48a72786c6b3b3f075208b62713",
+ "gas": "0x12",
+ "gasUsed": "0x12",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x55524c",
+ "output": "0x55524c",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "gas": "0x24680",
+ "gasUsed": "0xbc",
+ "to": "0x1d3b2638a7cc9f2cb3d298a3da7a90b67e5506ed",
+ "input": "0x38cc4831",
+ "output": "0x00000000000000000000000088e1315687aec48a72786c6b3b3f075208b62713",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "gas": "0x12",
+ "gasUsed": "0x12",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x55524c",
+ "output": "0x55524c",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "gas": "0x22f3b",
+ "gasUsed": "0x73a",
+ "to": "0x88e1315687aec48a72786c6b3b3f075208b62713",
+ "input": "0x2ef3accc000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000249f0000000000000000000000000000000000000000000000000000000000000000355524c0000000000000000000000000000000000000000000000000000000000",
+ "output": "0x00000000000000000000000000000000000000000000000000179d63013c5654",
+ "calls": [
+ {
+ "from": "0x88e1315687aec48a72786c6b3b3f075208b62713",
+ "gas": "0x12",
+ "gasUsed": "0x12",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x55524c",
+ "output": "0x55524c",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "gas": "0x12",
+ "gasUsed": "0x12",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x55524c",
+ "output": "0x55524c",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "gas": "0x30",
+ "gasUsed": "0x18",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x6a736f6e2868747470733a2f2f6170692e72616e646f6d2e6f72672f6a736f6e2d7270632f312f696e766f6b65292e726573756c742e72616e646f6d2e646174612e30",
+ "output": "0x6a736f6e2868747470733a2f2f6170692e72616e646f6d2e6f72672f6a736f6e2d7270632f312f696e766f6b65292e726573756c742e72616e646f6d2e646174612e30",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "gas": "0x99",
+ "gasUsed": "0x2d",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x4244584a68725670424a35336f3243786c4a526c51745a4a4b5a714c5974354951652b37335944533448744e6a5335486f64624942337476666f773755717579416b303835566b4c6e4c3945704b67777157517a375a4c64477673516c526432734b78496f6c4e673944626e6650737047714c684c62625953566e4e38437776736a7041586353536f33632b34634e774339307946346f4e69626b764433797461706f5a37676f5453796f5559546677536a6e773374692b484a5648374e332b633069774f43715a6a4464734751556358336d33532f494857624f4f5151356f734f344c626a33476730783155644e7466557a5943465937396e7a596757495145464375524249306e364e42764251573732372b4f73445259304a2f392f676a74387563696248576963303d",
+ "output": "0x4244584a68725670424a35336f3243786c4a526c51745a4a4b5a714c5974354951652b37335944533448744e6a5335486f64624942337476666f773755717579416b303835566b4c6e4c3945704b67777157517a375a4c64477673516c526432734b78496f6c4e673944626e6650737047714c684c62625953566e4e38437776736a7041586353536f33632b34634e774339307946346f4e69626b764433797461706f5a37676f5453796f5559546677536a6e773374692b484a5648374e332b633069774f43715a6a4464734751556358336d33532f494857624f4f5151356f734f344c626a33476730783155644e7466557a5943465937396e7a596757495145464375524249306e364e42764251573732372b4f73445259304a2f392f676a74387563696248576963303d",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "gas": "0x2083e",
+ "gasUsed": "0x4417",
+ "to": "0x88e1315687aec48a72786c6b3b3f075208b62713",
+ "input": "0x85dee34c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000249f0000000000000000000000000000000000000000000000000000000000000000355524c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000436a736f6e2868747470733a2f2f6170692e72616e646f6d2e6f72672f6a736f6e2d7270632f312f696e766f6b65292e726573756c742e72616e646f6d2e646174612e300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c4244584a68725670424a35336f3243786c4a526c51745a4a4b5a714c5974354951652b37335944533448744e6a5335486f64624942337476666f773755717579416b303835566b4c6e4c3945704b67777157517a375a4c64477673516c526432734b78496f6c4e673944626e6650737047714c684c62625953566e4e38437776736a7041586353536f33632b34634e774339307946346f4e69626b764433797461706f5a37676f5453796f5559546677536a6e773374692b484a5648374e332b633069774f43715a6a4464734751556358336d33532f494857624f4f5151356f734f344c626a33476730783155644e7466557a5943465937396e7a596757495145464375524249306e364e42764251573732372b4f73445259304a2f392f676a74387563696248576963303d0000000000000000000000000000000000000000",
+ "output": "0xd1b13c1538a940417bf0e73b2498634436753c854c7fb971224d971bd2ae3e88",
+ "calls": [
+ {
+ "from": "0x88e1315687aec48a72786c6b3b3f075208b62713",
+ "gas": "0x12",
+ "gasUsed": "0x12",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x55524c",
+ "output": "0x55524c",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x88e1315687aec48a72786c6b3b3f075208b62713",
+ "gas": "0x12",
+ "gasUsed": "0x12",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x55524c",
+ "output": "0x55524c",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x88e1315687aec48a72786c6b3b3f075208b62713",
+ "gas": "0x30",
+ "gasUsed": "0x18",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x6a736f6e2868747470733a2f2f6170692e72616e646f6d2e6f72672f6a736f6e2d7270632f312f696e766f6b65292e726573756c742e72616e646f6d2e646174612e30",
+ "output": "0x6a736f6e2868747470733a2f2f6170692e72616e646f6d2e6f72672f6a736f6e2d7270632f312f696e766f6b65292e726573756c742e72616e646f6d2e646174612e30",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x88e1315687aec48a72786c6b3b3f075208b62713",
+ "gas": "0x99",
+ "gasUsed": "0x2d",
+ "to": "0x0000000000000000000000000000000000000004",
+ "input": "0x4244584a68725670424a35336f3243786c4a526c51745a4a4b5a714c5974354951652b37335944533448744e6a5335486f64624942337476666f773755717579416b303835566b4c6e4c3945704b67777157517a375a4c64477673516c526432734b78496f6c4e673944626e6650737047714c684c62625953566e4e38437776736a7041586353536f33632b34634e774339307946346f4e69626b764433797461706f5a37676f5453796f5559546677536a6e773374692b484a5648374e332b633069774f43715a6a4464734751556358336d33532f494857624f4f5151356f734f344c626a33476730783155644e7466557a5943465937396e7a596757495145464375524249306e364e42764251573732372b4f73445259304a2f392f676a74387563696248576963303d",
+ "output": "0x4244584a68725670424a35336f3243786c4a526c51745a4a4b5a714c5974354951652b37335944533448744e6a5335486f64624942337476666f773755717579416b303835566b4c6e4c3945704b67777157517a375a4c64477673516c526432734b78496f6c4e673944626e6650737047714c684c62625953566e4e38437776736a7041586353536f33632b34634e774339307946346f4e69626b764433797461706f5a37676f5453796f5559546677536a6e773374692b484a5648374e332b633069774f43715a6a4464734751556358336d33532f494857624f4f5151356f734f344c626a33476730783155644e7466557a5943465937396e7a596757495145464375524249306e364e42764251573732372b4f73445259304a2f392f676a74387563696248576963303d",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x88e1315687aec48a72786c6b3b3f075208b62713",
+ "topics": [
+ "0xaf30e4d66b2f1f23e63ef4591058a897f67e6867233e33ca3508b982dcc4129b"
+ ],
+ "data": "0x00000000000000000000000050739060a2c32dc076e507ae1a893aab28ecfe68d1b13c1538a940417bf0e73b2498634436753c854c7fb971224d971bd2ae3e8800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000249f011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000355524c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000436a736f6e2868747470733a2f2f6170692e72616e646f6d2e6f72672f6a736f6e2d7270632f312f696e766f6b65292e726573756c742e72616e646f6d2e646174612e300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c4244584a68725670424a35336f3243786c4a526c51745a4a4b5a714c5974354951652b37335944533448744e6a5335486f64624942337476666f773755717579416b303835566b4c6e4c3945704b67777157517a375a4c64477673516c526432734b78496f6c4e673944626e6650737047714c684c62625953566e4e38437776736a7041586353536f33632b34634e774339307946346f4e69626b764433797461706f5a37676f5453796f5559546677536a6e773374692b484a5648374e332b633069774f43715a6a4464734751556358336d33532f494857624f4f5151356f734f344c626a33476730783155644e7466557a5943465937396e7a596757495145464375524249306e364e42764251573732372b4f73445259304a2f392f676a74387563696248576963303d0000000000000000000000000000000000000000"
+ }
+ ],
+ "value": "0x179d63013c5654",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0x50739060a2c32dc076e507ae1a893aab28ecfe68",
+ "topics": [],
+ "data": "0x62616e6b726f6c6c5f6d69736d61746368"
+ }
+ ],
+ "value": "0x429d069189e0000",
+ "type": "CALL"
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json
new file mode 100644
index 0000000000..1a03f0e7fb
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json
@@ -0,0 +1,84 @@
+{
+ "genesis": {
+ "difficulty": "8430028481555",
+ "extraData": "0xd783010302844765746887676f312e352e31856c696e7578",
+ "gasLimit": "3141592",
+ "hash": "0xde66937783697293f2e529d2034887c531535d78afa8c9051511ae12ba48fbea",
+ "miner": "0x2a65aca4d5fc5b5c859090a6c34d164135398226",
+ "mixHash": "0xba28a43bfbca4a2effbb76bb70d03482a8a0c92e2883ff36cbac3d7c6dbb7df5",
+ "nonce": "0xa3827ec0a82fe823",
+ "number": "765824",
+ "stateRoot": "0x8d96cb027a29f8ca0ccd6d31f9ea0656136ec8030ecda70bb9231849ed6f41a2",
+ "timestamp": "1451389443",
+ "totalDifficulty": "4838314986494741271",
+ "alloc": {
+ "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb": {
+ "balance": "0x14203bee2ea6fbe8c",
+ "nonce": "34"
+ },
+ "0xe2fe6b13287f28e193333fdfe7fedf2f6df6124a": {
+ "balance": "0x2717a9c870a286f4350"
+ },
+ "0xf4eced2f682ce333f96f2d8966c613ded8fc95dd": {
+ "balance": "0x0",
+ "code": "0x606060405260e060020a600035046306fdde038114610047578063313ce567146100a457806370a08231146100b057806395d89b41146100c8578063a9059cbb14610123575b005b61015260008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156101f55780601f106101ca576101008083540402835291602001916101f5565b6101c060025460ff1681565b6101c060043560036020526000908152604090205481565b610152600180546020601f6002600019610100858716150201909316929092049182018190040260809081016040526060828152929190828280156101f55780601f106101ca576101008083540402835291602001916101f5565b610045600435602435600160a060020a033316600090815260036020526040902054819010156101fd57610002565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156101b25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116101d857829003601f168201915b505050505081565b600160a060020a03821660009081526040902054808201101561021f57610002565b806003600050600033600160a060020a03168152602001908152602001600020600082828250540392505081905550806003600050600084600160a060020a0316815260200190815260200160002060008282825054019250508190555081600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505056",
+ "storage": {
+ "0x1dae8253445d3a5edbe8200da9fc39bc4f11db9362181dc1b640d08c3c2fb4d6": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8ba52aac7f255d80a49abcf003d6af4752aba5a9531cae94fde7ac8d72191d67": "0x000000000000000000000000000000000000000000000000000000000178e460"
+ }
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "765825",
+ "difficulty": "8425912256743",
+ "timestamp": "1451389488",
+ "gasLimit": "3141592",
+ "miner": "0xe2fe6b13287f28e193333fdfe7fedf2f6df6124a"
+ },
+ "input": "0xf8aa22850ba43b740083024d4594f4eced2f682ce333f96f2d8966c613ded8fc95dd80b844a9059cbb000000000000000000000000dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb00000000000000000000000000000000000000000000000000000000009896801ca067da548a2e0f381a957b9b51f086073375d6bfc7312cbc9540b3647ccab7db11a042c6e5b34bc7ba821e9c25b166fa13d82ad4b0d044d16174d5587d4f04ecfcd1",
+ "tracerConfig": {
+ "withLog": true
+ },
+ "result": {
+ "from": "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb",
+ "gas": "0x1f36d",
+ "gasUsed": "0xc6a5",
+ "to": "0xf4eced2f682ce333f96f2d8966c613ded8fc95dd",
+ "input": "0xa9059cbb000000000000000000000000dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb0000000000000000000000000000000000000000000000000000000000989680",
+ "logs": [
+ {
+ "address": "0xf4eced2f682ce333f96f2d8966c613ded8fc95dd",
+ "topics": [
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+ "0x000000000000000000000000d1220a0cf47c7b9be7a2e6ba89f429762e7b9adb",
+ "0x000000000000000000000000dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb"
+ ],
+ "data": "0x0000000000000000000000000000000000000000000000000000000000989680"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json
new file mode 100644
index 0000000000..4e0aec529f
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json
@@ -0,0 +1,244 @@
+{
+ "genesis": {
+ "difficulty": "56311715121637",
+ "extraData": "0x7777772e62772e636f6d",
+ "gasLimit": "4712388",
+ "hash": "0x20d3b8daa046f2f10564d84ccbe6d0a8842d8d52bc6d623e23c38050a8f73776",
+ "miner": "0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1",
+ "mixHash": "0x75029f90d7de3f9e3d5eac4a25019f9ac5d0041641d1ef17e7759e45699d4224",
+ "nonce": "0x54ff3b34fa1d9c97",
+ "number": "1968179",
+ "stateRoot": "0x6420003b1779cca3bcdab698c239bbc63623c0a7e4deeedbdb8190b9e7fd7520",
+ "timestamp": "1469713675",
+ "totalDifficulty": "42284028928878034360",
+ "alloc": {
+ "0x10abb5efecdc09581f8b7cb95791fe2936790b4e": {
+ "balance": "0x81f158e2814b4ab624c",
+ "code": "0x6060604052361561020e5760e060020a6000350463013cf08b8114610247578063095ea7b3146102d05780630c3b7b96146103455780630e7082031461034e578063149acf9a1461036057806318160ddd146103725780631f2dc5ef1461037b57806321b5b8dd1461039b578063237e9492146103ad57806323b872dd1461040e5780632632bf2014610441578063341458081461047257806339d1f9081461047b5780634b6753bc146104935780634df6d6cc1461049c5780634e10c3ee146104b7578063590e1ae3146104ca578063612e45a3146104db578063643f7cdd1461057a578063674ed066146105925780636837ff1e1461059b57806370a08231146105e5578063749f98891461060b57806378524b2e1461062457806381f03fcb1461067e57806382661dc41461069657806382bf6464146106b75780638b15a605146106c95780638d7af473146106d257806396d7f3f5146106e1578063a1da2fb9146106ea578063a3912ec814610704578063a9059cbb1461070f578063b7bc2c841461073f578063baac53001461074b578063be7c29c1146107b1578063c9d27afe14610817578063cc9ae3f61461082d578063cdef91d014610841578063dbde198814610859578063dd62ed3e1461087e578063e33734fd146108b2578063e5962195146108c6578063e66f53b7146108de578063eceb2945146108f0578063f8c80d261461094f575b610966600f546000906234bc000142108015610239575060125433600160a060020a03908116911614155b156109785761098033610752565b6109866004356000805482908110156100025750808052600e8202600080516020612a3683398151915201905060038101546004820154600683015460018401548454600786015460058701546009880154600a890154600d8a0154600160a060020a039586169b509599600201989760ff81811698610100909204811697949691951693168c565b61096660043560243533600160a060020a03908116600081815260156020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b61096660105481565b610a7d600754600160a060020a031681565b610a7d600e54600160a060020a031681565b61096660165481565b6109665b60004262127500600f60005054031115610de557506014610983565b610a7d601254600160a060020a031681565b60408051602060248035600481810135601f810185900485028601850190965285855261096695813595919460449492939092019181908401838280828437509496505050505050506000600060006000600060003411156116a857610002565b6109666004356024356044355b60115460009060ff1680156104315750600f5442115b80156124e957506124e78461044b565b6109666000610980335b600160a060020a0381166000908152600b602052604081205481908114156129cb57610b99565b61096660065481565b6109665b600d5430600160a060020a03163103610983565b610966600f5481565b61096660043560046020526000908152604090205460ff1681565b61096660043560243560006124cb610831565b610a9a6000341115610ba457610002565b604080516020604435600481810135601f8101849004840285018401909552848452610966948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897608497919650602491909101945090925082915084018382808284375094965050933593505060a435915050600060006110c1336105ec565b61096660043560096020526000908152604090205481565b61096660015481565b610a9a60043530600160a060020a031633600160a060020a03161415806105db5750600160a060020a03811660009081526004602052604090205460ff16155b156121cb576121c8565b6109666004355b600160a060020a0381166000908152601460205260409020545b919050565b6109666004356024356000600034111561259957610002565b610966600062e6b680420360026000505410806106505750600354600160a060020a0390811633909116145b80156106645750600254621274ff19420190105b156126145750426002908155600180549091028155610983565b610966600435600a6020526000908152604090205481565b610966600435602435600060006000600060006000341115611ba157610002565b610a7d600854600160a060020a031681565b610966600c5481565b61096660005460001901610983565b61096660025481565b61096660043560006000600060003411156121fc57610002565b6109665b6001610983565b6109666004356024355b60115460009060ff16801561072f5750600f5442115b801561248757506124853361044b565b61096660115460ff1681565b6109666004355b60006000600f600050544210801561076a5750600034115b80156107a457506011546101009004600160a060020a0316600014806107a457506011546101009004600160a060020a0390811633909116145b15610b9f57610a9c61037f565b610a7d600435600060006000508281548110156100025750508080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56b600e83020180548290811015610002575081526020902060030154600160a060020a0316610606565b61096660043560243560006000610e1b336105ec565b6109665b6000600034111561247c57610002565b61096660043560056020526000908152604090205481565b610966600435602435604435600061252f845b6000600060003411156127ac57610002565b610966600435602435600160a060020a0382811660009081526015602090815260408083209385168352929052205461033f565b610a9a600435600034111561254557610002565b610966600435600b6020526000908152604090205481565b610a7d600354600160a060020a031681565b604080516020606435600481810135601f81018490048402850184019095528484526109669481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600034111561103257610002565b610a7d6011546101009004600160a060020a031681565b60408051918252519081900360200190f35b610980610708565b90505b90565b604051808d600160a060020a031681526020018c8152602001806020018b81526020018a815260200189815260200188815260200187815260200186815260200185815260200184815260200183600160a060020a0316815260200182810382528c818154600181600116156101000203166002900481526020019150805460018160011615610100020316600290048015610a635780601f10610a3857610100808354040283529160200191610a63565b820191906000526020600020905b815481529060010190602001808311610a4657829003601f168201915b50509d505050505050505050505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b005b604051601254601434908102939093049350600160a060020a03169183900390600081818185876185025a03f150505050600160a060020a038316600081815260146020908152604080832080548601905560168054860190556013825291829020805434019055815184815291517fdbccb92686efceafb9bb7e0394df7f58f71b954061b81afb57109bf247d3d75a9281900390910190a260105460165410801590610b4c575060115460ff16155b15610b94576011805460ff1916600117905560165460408051918252517ff381a3e2428fdda36615919e8d9c35878d9eb0cf85ac6edf575088e80e4c147e9181900360200190a15b600191505b50919050565b610002565b600f5442118015610bb8575060115460ff16155b15610de357601260009054906101000a9004600160a060020a0316600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040516012549051600160a060020a039190911631109050610cc9576040805160125460e060020a63d2cc718f0282529151600160a060020a039290921691630221038a913091849163d2cc718f91600482810192602092919082900301816000876161da5a03f11561000257505060408051805160e160020a63011081c5028252600160a060020a039490941660048201526024810193909352516044838101936020935082900301816000876161da5a03f115610002575050505b33600160a060020a0316600081815260136020526040808220549051909181818185876185025a03f19250505015610de35733600160a060020a03167fbb28353e4598c3b9199101a66e0989549b659a59a54d2c27fbb183f1932c8e6d6013600050600033600160a060020a03168152602001908152602001600020600050546040518082815260200191505060405180910390a26014600050600033600160a060020a0316815260200190815260200160002060005054601660008282825054039250508190555060006014600050600033600160a060020a031681526020019081526020016000206000508190555060006013600050600033600160a060020a03168152602001908152602001600020600050819055505b565b4262054600600f60005054031115610e13576201518062127500600f60005054034203046014019050610983565b50601e610983565b60001415610e2857610002565b6000341115610e3657610002565b6000805485908110156100025750600160a060020a03331681527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56e600e8602908101602052604090912054600080516020612a3683398151915291909101915060ff1680610eb05750600c810160205260406000205460ff165b80610ebf575060038101544210155b15610ec957610002565b8215610f0f5733600160a060020a03166000908152601460209081526040808320546009850180549091019055600b84019091529020805460ff19166001179055610f4b565b33600160a060020a0316600090815260146020908152604080832054600a850180549091019055600c84019091529020805460ff191660011790555b33600160a060020a03166000908152600b60205260408120541415610f77576040600020849055610feb565b33600160a060020a03166000908152600b60205260408120548154811015610002579080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566600e909102015460038201541115610feb5733600160a060020a03166000908152600b602052604090208490555b60408051848152905133600160a060020a03169186917f86abfce99b7dd908bec0169288797f85049ec73cbe046ed9de818fab3a497ae09181900360200190a35092915050565b6000805487908110156100025750808052600e8702600080516020612a3683398151915201905090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816005016000505414915050949350505050565b600014156110ce57610002565b82801561111857508660001415806110e857508451600014155b806111005750600354600160a060020a038981169116145b8061110b5750600034115b80611118575062093a8084105b1561112257610002565b8215801561114257506111348861115c565b158061114257506212750084105b156111fe57610002565b83546118e590600160a060020a03165b600160a060020a03811660009081526004602052604081205460ff16806111f15750601254600160a060020a039081169083161480156111f15750601260009054906101000a9004600160a060020a0316600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604051516006541190505b156129a157506001610606565b6249d40084111561120e57610002565b60115460ff1615806112215750600f5442105b806112365750600c5434108015611236575082155b1561124057610002565b42844201101561124f57610002565b30600160a060020a031633600160a060020a0316141561126e57610002565b60008054600181018083559091908280158290116112a557600e0281600e0283600052602060002091820191016112a5919061136a565b505060008054929450918491508110156100025750808052600e8302600080516020612a368339815191520190508054600160a060020a031916891781556001818101899055875160028084018054600082815260209081902096975091959481161561010002600019011691909104601f908101829004840193918b019083901061146257805160ff19168380011785555b5061149292915061144a565b5050600060098201819055600a820155600d81018054600160a060020a03191690556001015b8082111561145e578054600160a060020a03191681556000600182810182905560028084018054848255909281161561010002600019011604601f81901061143057505b506000600383018190556004808401805461ffff19169055600584018290556006840182905560078401805460ff191690556008840180548382559083526020909220611344929091028101905b8082111561145e57600080825560018201818155600283019190915560039091018054600160a060020a03191690556113fc565b601f0160209004906000526020600020908101906113ae91905b8082111561145e576000815560010161144a565b5090565b82800160010185558215611338579182015b82811115611338578251826000505591602001919060010190611474565b50508787866040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160050160005081905550834201816003016000508190555060018160040160006101000a81548160ff02191690830217905550828160070160006101000a81548160ff02191690830217905550821561157857600881018054600181018083559091908280158290116115735760040281600402836000526020600020918201910161157391906113fc565b505050505b600d8082018054600160a060020a031916331790553460068301819055815401905560408051600160a060020a038a16815260208181018a9052918101859052608060608201818152895191830191909152885185937f5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f938d938d938a938e93929160a084019185810191908190849082908590600090600490601f850104600f02600301f150905090810190601f1680156116485780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a2509695505050505050565b6040805186815260208101839052815189927fdfc78bdca8e3e0b18c16c5c99323c6cb9eb5e00afde190b4e7273f5158702b07928290030190a25b5050505092915050565b6000805488908110156100025750808052600e8802600080516020612a36833981519152019050600781015490945060ff166116e757620d2f006116ec565b622398805b600485015490935060ff16801561170857506003840154830142115b15611716576117b887611890565b600384015442108061172d5750600484015460ff16155b806117ae57508360000160009054906101000a9004600160a060020a03168460010160005054876040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020846005016000505414155b1561114c57610002565b61169e565b60048401805461ff001916610100179055835460019550600160a060020a03908116309091161480159061180157508354600754600160a060020a03908116911614155b801561181d57506008548454600160a060020a03908116911614155b801561183957508354601254600160a060020a03908116911614155b801561185557506003548454600160a060020a03908116911614155b1561188b5760018401805430600160a060020a031660009081526005602052604090208054919091019055546006805490910190555b611663875b6000600060005082815481101561000257908052600e02600080516020612a36833981519152018150600481015490915060ff16156118d757600d80546006830154900390555b600401805460ff1916905550565b15156118f45761190087611890565b6001915061193161047f565b604051600d8501546006860154600160a060020a0391909116916000919082818181858883f193505050505061169e565b6001850154111561194157600091505b50600a8301546009840154865191019060049010801590611986575085600081518110156100025790602001015160f860020a900460f860020a02606860f860020a02145b80156119b6575085600181518110156100025790602001015160f860020a900460f860020a02603760f860020a02145b80156119e6575085600281518110156100025790602001015160f860020a900460f860020a0260ff60f860020a02145b8015611a16575085600381518110156100025790602001015160f860020a900460f860020a02601e60f860020a02145b8015611a45575030600160a060020a0316600090815260056020526040902054611a4290611a5d61047f565b81105b15611a4f57600091505b6001840154611a8090611a5f565b015b30600160a060020a03166000908152600560205260408120546129a961047f565b8110611ad457604051600d8501546006860154600160a060020a0391909116916000919082818181858883f193505050501515611abc57610002565b4260025560165460059004811115611ad45760056001555b6001840154611ae290611a5f565b8110158015611af85750600a8401546009850154115b8015611b015750815b1561188b578360000160009054906101000a9004600160a060020a0316600160a060020a0316846001016000505487604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611b7d5780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f19250505015156117bd57610002565b611baa336105ec565b60001415611bb757610002565b60008054889081101561000257508052600e87027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566810154600080516020612a36833981519152919091019450421080611c1957506003840154622398800142115b80611c3257508354600160a060020a0390811690871614155b80611c425750600784015460ff16155b80611c68575033600160a060020a03166000908152600b8501602052604090205460ff16155b80611c9c575033600160a060020a03166000908152600b60205260409020548714801590611c9c5750604060009081205414155b15611ca657610002565b600884018054600090811015610002579081526020812060030154600160a060020a03161415611e1257611efc86604051600090600160a060020a038316907f9046fefd66f538ab35263248a44217dcb70e2eb2cd136629e141b8b8f9f03b60908390a260408051600e547fe2faf044000000000000000000000000000000000000000000000000000000008252600160a060020a03858116600484015260248301859052604483018590526223988042016064840152925192169163e2faf04491608480820192602092909190829003018187876161da5a03f1156100025750506040515191506106069050565b6008850180546000908110156100025781815260208082209390935530600160a060020a031681526005909252604082205481549092908110156100025790815260208120905060020155601654600885018054600090811015610002579081526020812090506001015560048401805461ff0019166101001790555b6008840180546000908110156100025781548282526020822060010154929190811015610002579081526020812090505433600160a060020a031660009081526014602052604081205460088801805493909102939093049550908110156100025790815260208120905060030154604080517fbaac530000000000000000000000000000000000000000000000000000000000815233600160a060020a0390811660048301529151929091169163baac53009186916024808301926020929190829003018185886185025a03f11561000257505060405151600014159150611f78905057610002565b60088501805460009081101561000257818152602081206003018054600160a060020a03191690931790925580549091908110156100025790815260208120905060030154600160a060020a031660001415611f5757610002565b600d5430600160a060020a0316311015611f7057610002565b611d9561047f565b6008840180546000908110156100025781548282526020822060010154929190811015610002579081526020812090506002015433600160a060020a0390811660009081526014602090815260408083205430909416835260058083528184205460099093529083205460088b018054969095029690960497509487020494508593929091908290811015610002575260208120815060030154600160a060020a0390811682526020828101939093526040918201600090812080549095019094553016835260059091529020548290101561205357610002565b30600160a060020a031660009081526005602052604081208054849003905560088501805483926009929091829081101561000257508152602080822060030154600160a060020a039081168352929052604080822080549094019093553090911681522054819010156120c657610002565b30600160a060020a0390811660009081526009602090815260408083208054869003905533909316808352601482528383205484519081529351929390927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a36121383361086c565b5033600160a060020a03166000908152601460209081526040808320805460168054919091039055839055600a9091528120556001945061169e565b30600160a060020a0390811660008181526005602090815260408083208054958716808552828520805490970190965584845283905560099091528082208054948352908220805490940190935590815290555b50565b604051600160a060020a0382811691309091163190600081818185876185025a03f192505050151561217457610002565b33600160a060020a03818116600090815260096020908152604080832054815160065460085460e060020a63d2cc718f028352935197995091969195929092169363d2cc718f936004848101949193929183900301908290876161da5a03f11561000257505050604051805190602001506005600050600033600160a060020a03168152602001908152602001600020600050540204101561229d57610002565b600160a060020a03338116600090815260096020908152604080832054815160065460085460e060020a63d2cc718f02835293519296909593169363d2cc718f93600483810194929383900301908290876161da5a03f11561000257505050604051805190602001506005600050600033600160a060020a0316815260200190815260200160002060005054020403905083156123ec57600860009054906101000a9004600160a060020a0316600160a060020a0316630221038a83600160a060020a0316630e7082036040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060408051805160e160020a63011081c5028252600160a060020a031660048201526024810186905290516044808301935060209282900301816000876161da5a03f115610002575050604051511515905061245457610002565b6040805160085460e160020a63011081c5028252600160a060020a038581166004840152602483018590529251921691630221038a9160448082019260209290919082900301816000876161da5a03f115610002575050604051511515905061245457610002565b600160a060020a03331660009081526009602052604090208054909101905550600192915050565b6109803361086c565b155b80156124a257506124a23384845b6000600061293a856105ec565b80156124be57506124be83836000600034111561261c57610002565b15610b9f5750600161033f565b15156124d657610002565b6124e08383610719565b905061033f565b155b80156124fb57506124fb848484612495565b80156125185750612518848484600060003411156126c157610002565b15610b9f57506001612528565b90505b9392505050565b151561253a57610002565b61252584848461041b565b30600160a060020a031633600160a060020a031614158061258a575030600160a060020a031660009081526005602052604090205460649061258561047f565b010481115b1561259457610002565b600c55565b600354600160a060020a0390811633909116146125b557610002565b600160a060020a038316600081815260046020908152604091829020805460ff191686179055815185815291517f73ad2a153c8b67991df9459024950b318a609782cee8c7eeda47b905f9baa91f9281900390910190a250600161033f565b506000610983565b33600160a060020a03166000908152601460205260409020548290108015906126455750600082115b156126b957600160a060020a03338116600081815260146020908152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a350600161033f565b50600061033f565b600160a060020a03841660009081526014602052604090205482901080159061270a5750601560209081526040600081812033600160a060020a03168252909252902054829010155b80156127165750600082115b156127a457600160a060020a03838116600081815260146020908152604080832080548801905588851680845281842080548990039055601583528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a3506001612528565b506000612528565b600160a060020a038381166000908152600a6020908152604080832054601654600754835160e060020a63d2cc718f02815293519296919591169363d2cc718f9360048181019492939183900301908290876161da5a03f11561000257505060405151905061281a866105ec565b0204101561282757610002565b600160a060020a038381166000908152600a6020908152604080832054601654600754835160e060020a63d2cc718f02815293519296919591169363d2cc718f9360048181019492939183900301908290876161da5a03f115610002575050604051519050612895866105ec565b0204039050600760009054906101000a9004600160a060020a0316600160a060020a0316630221038a84836040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506020604051808303816000876161da5a03f115610002575050604051511515905061291357610002565b600160a060020a0383166000908152600a6020526040902080548201905560019150610b99565b600160a060020a0386166000908152600a602052604090205480850291909104915081111561296857610002565b600160a060020a038581166000908152600a60205260408082208054859003905591861681522080548201905560019150509392505050565b506000610606565b0160030260166000505483020460016000505460166000505404019050610606565b600160a060020a0383166000908152600b6020526040812054815481101561000257818052600e02600080516020612a368339815191520190506003810154909150421115610b9457600160a060020a0383166000908152600b602052604081208190559150610b9956290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563",
+ "nonce": "3",
+ "storage": {
+ "0x000000000000000000000000000000000000000000000000000000000000000f": "0x0000000000000000000000000000000000000000000000000000000057bda071",
+ "0x0000000000000000000000000000000000000000000000000000000000000010": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000011": "0x0000000000000000000000bb9bc244d798123fde783fcc1c72d3bb8c18941301",
+ "0x0000000000000000000000000000000000000000000000000000000000000012": "0x000000000000000000000000fde8d5f77ef48bb7bf5766c7404691b9ee1dfca7",
+ "0x0000000000000000000000000000000000000000000000000000000000000016": "0x00000000000000000000000000000000000000000000081f158e2814b4ab624c",
+ "0x7ffc832d0c7f56b16d03bf3ff14bc4dd6a6cb1ec75841f7397362f4a9be4d392": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xaccfa2662c944e8eae80b7720d9d232eb6809c18f6c8da65189acbb38069d869": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x630a0cd35d5bd57e61410fda76fea850225cda18": {
+ "balance": "0x0",
+ "code": "0x6060604052361561006c5760e060020a60003504630121b93f81146100e15780636637b882146101615780636dbf2fa0146101935780638da5cb5b1461026a578063a6f9dae11461027c578063beabacc8146102ae578063d979f5aa14610322578063e1fa763814610354575b61050b600060006000600460005054111561051d576004805460001901905560015460035460055460e260020a6320998771026060908152606492909252600160a060020a03908116608452909116906382661dc49060a49060209060448187876161da5a03f11561000257506105c3915050565b6105cb60043560005433600160a060020a039081169116141561015e57600180547fc9d27afe0000000000000000000000000000000000000000000000000000000060609081526064849052608492909252600160a060020a03169063c9d27afe9060a4906020906044816000876161da5a03f115610002575050505b50565b6105cb60043560005433600160a060020a039081169116141561015e5760018054600160a060020a0319168217905550565b60806020604435600481810135601f8101849004909302840160405260608381526105cb9482359460248035956064949391019190819083828082843750949650505050505050600054600160a060020a039081163390911614156102655782600160a060020a03168282604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561024b5780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f1505050505b505050565b6105cd600054600160a060020a031681565b6105cb60043560005433600160a060020a039081169116141561015e5760008054600160a060020a0319168217905550565b6105cb6004356024356044356000805433600160a060020a039081169116141561031c5760e060020a63a9059cbb026060908152600160a060020a03848116606452608484905285929083169163a9059cbb9160a4916020916044908290876161da5a03f115610002575050505b50505050565b6105cb60043560005433600160a060020a039081169116141561015e5760028054600160a060020a0319168217905550565b6105cb60043560243560005433600160a060020a03908116911614156105075760015460e060020a6370a0823102606090815230600160a060020a0390811660645291909116906370a08231906084906020906024816000876161da5a03f1156100025750506040805180516006556002546001547f1a695230000000000000000000000000000000000000000000000000000000008352600160a060020a039081166004840152925192169250631a695230916024828101926000929190829003018183876161da5a03f1156100025750505060048181556003839055600154604080517f013cf08b00000000000000000000000000000000000000000000000000000000815292830185905251600160a060020a03919091169163013cf08b91602482810192602092919082900301816000876161da5a03f11561000257505060408051805160058054600160a060020a0319169091179081905560015460035460e260020a63209987710284526004840152600160a060020a0391821660248401529251921692506382661dc491604482810192602092919082900301816000876161da5a03f115610002575050505b5050565b60408051918252519081900360200190f35b60015460e060020a6370a0823102606090815230600160a060020a0390811660645291909116906370a082319060849060209060248187876161da5a03f11561000257505060408051805160015460025460e060020a63a9059cbb028452600160a060020a039081166004850152602484018390529351919550909216925063a9059cbb916044828101926020929190829003018188876161da5a03f115610002575050505b600191505090565b005b6060908152602090f3",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000e6002189a74b43e6868b20c1311bc108e38aac57",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000bb9bc244d798123fde783fcc1c72d3bb8c189413",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000006e073c0e1bd5af550239025dffcfb37175acedd3",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x6e073c0e1bd5af550239025dffcfb37175acedd3": {
+ "balance": "0x0",
+ "code": "0x606060405260e060020a60003504631a69523081146100475780636dbf2fa01461006d5780638da5cb5b14610144578063a6f9dae114610156578063beabacc814610196575b005b610045600435600080548190819032600160a060020a0390811691161461022957610002565b60806020604435600481810135601f8101849004909302840160405260608381526100459482359460248035956064949391019190819083828082843750949650505050505050600054600160a060020a0390811633909116141561013f5782600160a060020a03168282604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156101255780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f1505050505b505050565b61021f600054600160a060020a031681565b61004560043560005433600160a060020a0390811691161415610193576000805473ffffffffffffffffffffffffffffffffffffffff1916821790555b50565b6100456004356024356044356000805433600160a060020a0390811691161415610343577fa9059cbb000000000000000000000000000000000000000000000000000000006060908152600160a060020a03808516606452608484905285929083169163a9059cbb9160a4916020916044908290876161da5a03f1156100025750505050505050565b6060908152602090f35b7f70a0823100000000000000000000000000000000000000000000000000000000606090815230600160a060020a039081166064528594508416906370a082319060849060209060248187876161da5a03f1156100025750506040805180517f18160ddd00000000000000000000000000000000000000000000000000000000825291519194506318160ddd916004828101926020929190829003018187876161da5a03f11561000257505050604051805190602001509050808211156102ee579050805b82600160a060020a031663a9059cbb33846040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506020604051808303816000876161da5a03f115610002575050505b5050505056",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000e6002189a74b43e6868b20c1311bc108e38aac57"
+ }
+ },
+ "0xbb9bc244d798123fde783fcc1c72d3bb8c189413": {
+ "balance": "0x53d2c8df046dd3db5",
+ "code": "0x6060604052361561020e5760e060020a6000350463013cf08b8114610247578063095ea7b3146102d05780630c3b7b96146103455780630e7082031461034e578063149acf9a1461036057806318160ddd146103725780631f2dc5ef1461037b57806321b5b8dd1461039b578063237e9492146103ad57806323b872dd1461040e5780632632bf2014610441578063341458081461047257806339d1f9081461047b5780634b6753bc146104935780634df6d6cc1461049c5780634e10c3ee146104b7578063590e1ae3146104ca578063612e45a3146104db578063643f7cdd1461057a578063674ed066146105925780636837ff1e1461059b57806370a08231146105e5578063749f98891461060b57806378524b2e1461062457806381f03fcb1461067e57806382661dc41461069657806382bf6464146106b75780638b15a605146106c95780638d7af473146106d257806396d7f3f5146106e1578063a1da2fb9146106ea578063a3912ec814610704578063a9059cbb1461070f578063b7bc2c841461073f578063baac53001461074b578063be7c29c1146107b1578063c9d27afe14610817578063cc9ae3f61461082d578063cdef91d014610841578063dbde198814610859578063dd62ed3e1461087e578063e33734fd146108b2578063e5962195146108c6578063e66f53b7146108de578063eceb2945146108f0578063f8c80d261461094f575b610966600f546000906234bc000142108015610239575060125433600160a060020a03908116911614155b156109785761098033610752565b6109866004356000805482908110156100025750808052600e8202600080516020612a3683398151915201905060038101546004820154600683015460018401548454600786015460058701546009880154600a890154600d8a0154600160a060020a039586169b509599600201989760ff81811698610100909204811697949691951693168c565b61096660043560243533600160a060020a03908116600081815260156020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b61096660105481565b610a7d600754600160a060020a031681565b610a7d600e54600160a060020a031681565b61096660165481565b6109665b60004262127500600f60005054031115610de557506014610983565b610a7d601254600160a060020a031681565b60408051602060248035600481810135601f810185900485028601850190965285855261096695813595919460449492939092019181908401838280828437509496505050505050506000600060006000600060003411156116a857610002565b6109666004356024356044355b60115460009060ff1680156104315750600f5442115b80156124e957506124e78461044b565b6109666000610980335b600160a060020a0381166000908152600b602052604081205481908114156129cb57610b99565b61096660065481565b6109665b600d5430600160a060020a03163103610983565b610966600f5481565b61096660043560046020526000908152604090205460ff1681565b61096660043560243560006124cb610831565b610a9a6000341115610ba457610002565b604080516020604435600481810135601f8101849004840285018401909552848452610966948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897608497919650602491909101945090925082915084018382808284375094965050933593505060a435915050600060006110c1336105ec565b61096660043560096020526000908152604090205481565b61096660015481565b610a9a60043530600160a060020a031633600160a060020a03161415806105db5750600160a060020a03811660009081526004602052604090205460ff16155b156121cb576121c8565b6109666004355b600160a060020a0381166000908152601460205260409020545b919050565b6109666004356024356000600034111561259957610002565b610966600062e6b680420360026000505410806106505750600354600160a060020a0390811633909116145b80156106645750600254621274ff19420190105b156126145750426002908155600180549091028155610983565b610966600435600a6020526000908152604090205481565b610966600435602435600060006000600060006000341115611ba157610002565b610a7d600854600160a060020a031681565b610966600c5481565b61096660005460001901610983565b61096660025481565b61096660043560006000600060003411156121fc57610002565b6109665b6001610983565b6109666004356024355b60115460009060ff16801561072f5750600f5442115b801561248757506124853361044b565b61096660115460ff1681565b6109666004355b60006000600f600050544210801561076a5750600034115b80156107a457506011546101009004600160a060020a0316600014806107a457506011546101009004600160a060020a0390811633909116145b15610b9f57610a9c61037f565b610a7d600435600060006000508281548110156100025750508080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56b600e83020180548290811015610002575081526020902060030154600160a060020a0316610606565b61096660043560243560006000610e1b336105ec565b6109665b6000600034111561247c57610002565b61096660043560056020526000908152604090205481565b610966600435602435604435600061252f845b6000600060003411156127ac57610002565b610966600435602435600160a060020a0382811660009081526015602090815260408083209385168352929052205461033f565b610a9a600435600034111561254557610002565b610966600435600b6020526000908152604090205481565b610a7d600354600160a060020a031681565b604080516020606435600481810135601f81018490048402850184019095528484526109669481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600034111561103257610002565b610a7d6011546101009004600160a060020a031681565b60408051918252519081900360200190f35b610980610708565b90505b90565b604051808d600160a060020a031681526020018c8152602001806020018b81526020018a815260200189815260200188815260200187815260200186815260200185815260200184815260200183600160a060020a0316815260200182810382528c818154600181600116156101000203166002900481526020019150805460018160011615610100020316600290048015610a635780601f10610a3857610100808354040283529160200191610a63565b820191906000526020600020905b815481529060010190602001808311610a4657829003601f168201915b50509d505050505050505050505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b005b604051601254601434908102939093049350600160a060020a03169183900390600081818185876185025a03f150505050600160a060020a038316600081815260146020908152604080832080548601905560168054860190556013825291829020805434019055815184815291517fdbccb92686efceafb9bb7e0394df7f58f71b954061b81afb57109bf247d3d75a9281900390910190a260105460165410801590610b4c575060115460ff16155b15610b94576011805460ff1916600117905560165460408051918252517ff381a3e2428fdda36615919e8d9c35878d9eb0cf85ac6edf575088e80e4c147e9181900360200190a15b600191505b50919050565b610002565b600f5442118015610bb8575060115460ff16155b15610de357601260009054906101000a9004600160a060020a0316600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040516012549051600160a060020a039190911631109050610cc9576040805160125460e060020a63d2cc718f0282529151600160a060020a039290921691630221038a913091849163d2cc718f91600482810192602092919082900301816000876161da5a03f11561000257505060408051805160e160020a63011081c5028252600160a060020a039490941660048201526024810193909352516044838101936020935082900301816000876161da5a03f115610002575050505b33600160a060020a0316600081815260136020526040808220549051909181818185876185025a03f19250505015610de35733600160a060020a03167fbb28353e4598c3b9199101a66e0989549b659a59a54d2c27fbb183f1932c8e6d6013600050600033600160a060020a03168152602001908152602001600020600050546040518082815260200191505060405180910390a26014600050600033600160a060020a0316815260200190815260200160002060005054601660008282825054039250508190555060006014600050600033600160a060020a031681526020019081526020016000206000508190555060006013600050600033600160a060020a03168152602001908152602001600020600050819055505b565b4262054600600f60005054031115610e13576201518062127500600f60005054034203046014019050610983565b50601e610983565b60001415610e2857610002565b6000341115610e3657610002565b6000805485908110156100025750600160a060020a03331681527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56e600e8602908101602052604090912054600080516020612a3683398151915291909101915060ff1680610eb05750600c810160205260406000205460ff165b80610ebf575060038101544210155b15610ec957610002565b8215610f0f5733600160a060020a03166000908152601460209081526040808320546009850180549091019055600b84019091529020805460ff19166001179055610f4b565b33600160a060020a0316600090815260146020908152604080832054600a850180549091019055600c84019091529020805460ff191660011790555b33600160a060020a03166000908152600b60205260408120541415610f77576040600020849055610feb565b33600160a060020a03166000908152600b60205260408120548154811015610002579080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566600e909102015460038201541115610feb5733600160a060020a03166000908152600b602052604090208490555b60408051848152905133600160a060020a03169186917f86abfce99b7dd908bec0169288797f85049ec73cbe046ed9de818fab3a497ae09181900360200190a35092915050565b6000805487908110156100025750808052600e8702600080516020612a3683398151915201905090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816005016000505414915050949350505050565b600014156110ce57610002565b82801561111857508660001415806110e857508451600014155b806111005750600354600160a060020a038981169116145b8061110b5750600034115b80611118575062093a8084105b1561112257610002565b8215801561114257506111348861115c565b158061114257506212750084105b156111fe57610002565b83546118e590600160a060020a03165b600160a060020a03811660009081526004602052604081205460ff16806111f15750601254600160a060020a039081169083161480156111f15750601260009054906101000a9004600160a060020a0316600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604051516006541190505b156129a157506001610606565b6249d40084111561120e57610002565b60115460ff1615806112215750600f5442105b806112365750600c5434108015611236575082155b1561124057610002565b42844201101561124f57610002565b30600160a060020a031633600160a060020a0316141561126e57610002565b60008054600181018083559091908280158290116112a557600e0281600e0283600052602060002091820191016112a5919061136a565b505060008054929450918491508110156100025750808052600e8302600080516020612a368339815191520190508054600160a060020a031916891781556001818101899055875160028084018054600082815260209081902096975091959481161561010002600019011691909104601f908101829004840193918b019083901061146257805160ff19168380011785555b5061149292915061144a565b5050600060098201819055600a820155600d81018054600160a060020a03191690556001015b8082111561145e578054600160a060020a03191681556000600182810182905560028084018054848255909281161561010002600019011604601f81901061143057505b506000600383018190556004808401805461ffff19169055600584018290556006840182905560078401805460ff191690556008840180548382559083526020909220611344929091028101905b8082111561145e57600080825560018201818155600283019190915560039091018054600160a060020a03191690556113fc565b601f0160209004906000526020600020908101906113ae91905b8082111561145e576000815560010161144a565b5090565b82800160010185558215611338579182015b82811115611338578251826000505591602001919060010190611474565b50508787866040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160050160005081905550834201816003016000508190555060018160040160006101000a81548160ff02191690830217905550828160070160006101000a81548160ff02191690830217905550821561157857600881018054600181018083559091908280158290116115735760040281600402836000526020600020918201910161157391906113fc565b505050505b600d8082018054600160a060020a031916331790553460068301819055815401905560408051600160a060020a038a16815260208181018a9052918101859052608060608201818152895191830191909152885185937f5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f938d938d938a938e93929160a084019185810191908190849082908590600090600490601f850104600f02600301f150905090810190601f1680156116485780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a2509695505050505050565b6040805186815260208101839052815189927fdfc78bdca8e3e0b18c16c5c99323c6cb9eb5e00afde190b4e7273f5158702b07928290030190a25b5050505092915050565b6000805488908110156100025750808052600e8802600080516020612a36833981519152019050600781015490945060ff166116e757620d2f006116ec565b622398805b600485015490935060ff16801561170857506003840154830142115b15611716576117b887611890565b600384015442108061172d5750600484015460ff16155b806117ae57508360000160009054906101000a9004600160a060020a03168460010160005054876040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020846005016000505414155b1561114c57610002565b61169e565b60048401805461ff001916610100179055835460019550600160a060020a03908116309091161480159061180157508354600754600160a060020a03908116911614155b801561181d57506008548454600160a060020a03908116911614155b801561183957508354601254600160a060020a03908116911614155b801561185557506003548454600160a060020a03908116911614155b1561188b5760018401805430600160a060020a031660009081526005602052604090208054919091019055546006805490910190555b611663875b6000600060005082815481101561000257908052600e02600080516020612a36833981519152018150600481015490915060ff16156118d757600d80546006830154900390555b600401805460ff1916905550565b15156118f45761190087611890565b6001915061193161047f565b604051600d8501546006860154600160a060020a0391909116916000919082818181858883f193505050505061169e565b6001850154111561194157600091505b50600a8301546009840154865191019060049010801590611986575085600081518110156100025790602001015160f860020a900460f860020a02606860f860020a02145b80156119b6575085600181518110156100025790602001015160f860020a900460f860020a02603760f860020a02145b80156119e6575085600281518110156100025790602001015160f860020a900460f860020a0260ff60f860020a02145b8015611a16575085600381518110156100025790602001015160f860020a900460f860020a02601e60f860020a02145b8015611a45575030600160a060020a0316600090815260056020526040902054611a4290611a5d61047f565b81105b15611a4f57600091505b6001840154611a8090611a5f565b015b30600160a060020a03166000908152600560205260408120546129a961047f565b8110611ad457604051600d8501546006860154600160a060020a0391909116916000919082818181858883f193505050501515611abc57610002565b4260025560165460059004811115611ad45760056001555b6001840154611ae290611a5f565b8110158015611af85750600a8401546009850154115b8015611b015750815b1561188b578360000160009054906101000a9004600160a060020a0316600160a060020a0316846001016000505487604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611b7d5780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f19250505015156117bd57610002565b611baa336105ec565b60001415611bb757610002565b60008054889081101561000257508052600e87027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566810154600080516020612a36833981519152919091019450421080611c1957506003840154622398800142115b80611c3257508354600160a060020a0390811690871614155b80611c425750600784015460ff16155b80611c68575033600160a060020a03166000908152600b8501602052604090205460ff16155b80611c9c575033600160a060020a03166000908152600b60205260409020548714801590611c9c5750604060009081205414155b15611ca657610002565b600884018054600090811015610002579081526020812060030154600160a060020a03161415611e1257611efc86604051600090600160a060020a038316907f9046fefd66f538ab35263248a44217dcb70e2eb2cd136629e141b8b8f9f03b60908390a260408051600e547fe2faf044000000000000000000000000000000000000000000000000000000008252600160a060020a03858116600484015260248301859052604483018590526223988042016064840152925192169163e2faf04491608480820192602092909190829003018187876161da5a03f1156100025750506040515191506106069050565b6008850180546000908110156100025781815260208082209390935530600160a060020a031681526005909252604082205481549092908110156100025790815260208120905060020155601654600885018054600090811015610002579081526020812090506001015560048401805461ff0019166101001790555b6008840180546000908110156100025781548282526020822060010154929190811015610002579081526020812090505433600160a060020a031660009081526014602052604081205460088801805493909102939093049550908110156100025790815260208120905060030154604080517fbaac530000000000000000000000000000000000000000000000000000000000815233600160a060020a0390811660048301529151929091169163baac53009186916024808301926020929190829003018185886185025a03f11561000257505060405151600014159150611f78905057610002565b60088501805460009081101561000257818152602081206003018054600160a060020a03191690931790925580549091908110156100025790815260208120905060030154600160a060020a031660001415611f5757610002565b600d5430600160a060020a0316311015611f7057610002565b611d9561047f565b6008840180546000908110156100025781548282526020822060010154929190811015610002579081526020812090506002015433600160a060020a0390811660009081526014602090815260408083205430909416835260058083528184205460099093529083205460088b018054969095029690960497509487020494508593929091908290811015610002575260208120815060030154600160a060020a0390811682526020828101939093526040918201600090812080549095019094553016835260059091529020548290101561205357610002565b30600160a060020a031660009081526005602052604081208054849003905560088501805483926009929091829081101561000257508152602080822060030154600160a060020a039081168352929052604080822080549094019093553090911681522054819010156120c657610002565b30600160a060020a0390811660009081526009602090815260408083208054869003905533909316808352601482528383205484519081529351929390927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a36121383361086c565b5033600160a060020a03166000908152601460209081526040808320805460168054919091039055839055600a9091528120556001945061169e565b30600160a060020a0390811660008181526005602090815260408083208054958716808552828520805490970190965584845283905560099091528082208054948352908220805490940190935590815290555b50565b604051600160a060020a0382811691309091163190600081818185876185025a03f192505050151561217457610002565b33600160a060020a03818116600090815260096020908152604080832054815160065460085460e060020a63d2cc718f028352935197995091969195929092169363d2cc718f936004848101949193929183900301908290876161da5a03f11561000257505050604051805190602001506005600050600033600160a060020a03168152602001908152602001600020600050540204101561229d57610002565b600160a060020a03338116600090815260096020908152604080832054815160065460085460e060020a63d2cc718f02835293519296909593169363d2cc718f93600483810194929383900301908290876161da5a03f11561000257505050604051805190602001506005600050600033600160a060020a0316815260200190815260200160002060005054020403905083156123ec57600860009054906101000a9004600160a060020a0316600160a060020a0316630221038a83600160a060020a0316630e7082036040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060408051805160e160020a63011081c5028252600160a060020a031660048201526024810186905290516044808301935060209282900301816000876161da5a03f115610002575050604051511515905061245457610002565b6040805160085460e160020a63011081c5028252600160a060020a038581166004840152602483018590529251921691630221038a9160448082019260209290919082900301816000876161da5a03f115610002575050604051511515905061245457610002565b600160a060020a03331660009081526009602052604090208054909101905550600192915050565b6109803361086c565b155b80156124a257506124a23384845b6000600061293a856105ec565b80156124be57506124be83836000600034111561261c57610002565b15610b9f5750600161033f565b15156124d657610002565b6124e08383610719565b905061033f565b155b80156124fb57506124fb848484612495565b80156125185750612518848484600060003411156126c157610002565b15610b9f57506001612528565b90505b9392505050565b151561253a57610002565b61252584848461041b565b30600160a060020a031633600160a060020a031614158061258a575030600160a060020a031660009081526005602052604090205460649061258561047f565b010481115b1561259457610002565b600c55565b600354600160a060020a0390811633909116146125b557610002565b600160a060020a038316600081815260046020908152604091829020805460ff191686179055815185815291517f73ad2a153c8b67991df9459024950b318a609782cee8c7eeda47b905f9baa91f9281900390910190a250600161033f565b506000610983565b33600160a060020a03166000908152601460205260409020548290108015906126455750600082115b156126b957600160a060020a03338116600081815260146020908152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a350600161033f565b50600061033f565b600160a060020a03841660009081526014602052604090205482901080159061270a5750601560209081526040600081812033600160a060020a03168252909252902054829010155b80156127165750600082115b156127a457600160a060020a03838116600081815260146020908152604080832080548801905588851680845281842080548990039055601583528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a3506001612528565b506000612528565b600160a060020a038381166000908152600a6020908152604080832054601654600754835160e060020a63d2cc718f02815293519296919591169363d2cc718f9360048181019492939183900301908290876161da5a03f11561000257505060405151905061281a866105ec565b0204101561282757610002565b600160a060020a038381166000908152600a6020908152604080832054601654600754835160e060020a63d2cc718f02815293519296919591169363d2cc718f9360048181019492939183900301908290876161da5a03f115610002575050604051519050612895866105ec565b0204039050600760009054906101000a9004600160a060020a0316600160a060020a0316630221038a84836040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506020604051808303816000876161da5a03f115610002575050604051511515905061291357610002565b600160a060020a0383166000908152600a6020526040902080548201905560019150610b99565b600160a060020a0386166000908152600a602052604090205480850291909104915081111561296857610002565b600160a060020a038581166000908152600a60205260408082208054859003905591861681522080548201905560019150509392505050565b506000610606565b0160030260166000505483020460016000505460166000505404019050610606565b600160a060020a0383166000908152600b6020526040812054815481101561000257818052600e02600080516020612a368339815191520190506003810154909150421115610b9457600160a060020a0383166000908152600b602052604081208190559150610b9956290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563",
+ "nonce": "3",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000120",
+ "0x000000000000000000000000000000000000000000000000000000000000000f": "0x0000000000000000000000000000000000000000000000000000000057495e10",
+ "0x0000000000000000000000000000000000000000000000000000000000000011": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0x0000000000000000000000000000000000000000000000000000000000000016": "0x000000000000000000000000000000000000000000098b4d3b425f8c368391b2",
+ "0x29066f14bd0b438bb3db8771a65febf0be7574be7528f87e7ae11aafc2b2c3ac": "0x000000000000000000000000000000000000000000000025d57ab057892050fc",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f443": "0x000000000000000000000000b3b10eff47b9c0b3e5579bf1c25872111667e650",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f444": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f445": "0x0000000000000000000000000000000000000000000000000000000000000093",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f446": "0x00000000000000000000000000000000000000000000000000000000579a07ea",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f447": "0x0000000000000000000000000000000000000000000000000000000000000101",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f448": "0x63c103e1feea47a9bf6c0dce1349da1a95b96532661d43063ab8e52b3e2a844b",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f449": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f44a": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f44b": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f44c": "0x00000000000000000000000000000000000000000000000001620725a3de2009",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f44d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3f450": "0x000000000000000000000000b3b10eff47b9c0b3e5579bf1c25872111667e650",
+ "0x3987ba2457a57cc6778cce06d8c58970029977d834f0de345c7a495612cbb060": "0x00000000000000000000000000000000000000000000081f2acc2a62590de041",
+ "0x3987ba2457a57cc6778cce06d8c58970029977d834f0de345c7a495612cbb061": "0x000000000000000000000000000000000000000000098b4d3b425f8c368391b2",
+ "0x3987ba2457a57cc6778cce06d8c58970029977d834f0de345c7a495612cbb062": "0x00000000000000000000000000000000000000000000003635c9adc5dea00000",
+ "0x3987ba2457a57cc6778cce06d8c58970029977d834f0de345c7a495612cbb063": "0x00000000000000000000000010abb5efecdc09581f8b7cb95791fe2936790b4e",
+ "0x6f125332c6f598e8798f0c277f4b1052ac80cd02ff2eebe0c7f362d63b6959ef": "0x000000000000000000000000000000000000000000000000008dc9007b27b5a9",
+ "0x793bebaf0ea12c858c08547e9aa88b849bba94bb6933c7bdb0fecbb707ecf5c7": "0x00000000000000000000000000000000000000000000076d52eebfbfbfc172e5",
+ "0xaccfa2662c944e8eae80b7720d9d232eb6809c18f6c8da65189acbb38069d869": "0x000000000000000000000000000000000000000000000000000289739e60e3e2",
+ "0xb6e4d5c52e0c64fb49c5a97cacdbcf8bd94b5bd4d490590326a19d27eaf543ae": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xbe273e24e8bd646e29d1fb5a924a12a8585095b9f45a317fc708165a127fdd70": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xc34fc4bc1a730d3f836c9ac5124865056e88f3776b63662e34976bdb47549077": "0x000000000000000000000000000000000000000000000036353be4c563784a57",
+ "0xe2112d92b8a1d00a569b85fbe7a384a5c9f74f5ff8478647397cb58dde254ffa": "0x53706c697420666f722070656f706c652077686f2073656e74206d6f6e657920",
+ "0xe2112d92b8a1d00a569b85fbe7a384a5c9f74f5ff8478647397cb58dde254ffb": "0x746f207468652044414f20616674657220746865204861726420466f726b2062",
+ "0xe2112d92b8a1d00a569b85fbe7a384a5c9f74f5ff8478647397cb58dde254ffc": "0x79206d697374616b650000000000000000000000000000000000000000000000",
+ "0xf60322aa1a2e769d412b36e4a9def4300f7540bf1bc9e0f4691786a9100145fa": "0x0000000000000000000000000000000000000000000000000000000062188dd2",
+ "0xf735edeea40e4ec771f49da7f7b854b398a1ad43f8a9617d43e53d3093e9fdc0": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0xf7905fa5d54027d5d59f4678dda481331babad2d3d0fdefd552afbce2e74c07e": "0x0000000000000000000000000000000000000000000000000000000000000110"
+ }
+ },
+ "0xe6002189a74b43e6868b20c1311bc108e38aac57": {
+ "balance": "0x29129264d1ae4848b",
+ "nonce": "45"
+ },
+ "0xea674fdde714fd979de3edf0f56aa9716b898ec8": {
+ "balance": "0x1601bbe4c58ec73210",
+ "nonce": "337736"
+ },
+ "0xfde8d5f77ef48bb7bf5766c7404691b9ee1dfca7": {
+ "balance": "0x0",
+ "code": "0x606060405236156100405760e060020a60003504630221038a811461004d57806318bdc79a146100aa5780638da5cb5b146100be578063d2cc718f146100d0575b6100d96001805434019055565b6100db6004356024356000805433600160a060020a0390811691161415806100755750600034115b806100a05750805460a060020a900460ff1680156100a057508054600160a060020a03848116911614155b156100f757610002565b6100db60005460ff60a060020a9091041681565b6100ed600054600160a060020a031681565b6100db60015481565b005b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a0383168260608381818185876185025a03f1925050501561015c57604080518381529051600160a060020a038516917f9735b0cb909f3d21d5c16bbcccd272d85fa11446f6d679f6ecb170d2dabfecfc919081900360200190a25060015b9291505056",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "1968180",
+ "difficulty": "56311715252709",
+ "timestamp": "1469713694",
+ "gasLimit": "4712388",
+ "miner": "0xea674fdde714fd979de3edf0f56aa9716b898ec8"
+ },
+ "input": "0xf8aa2d850c2b6f9f7e830aae6094630a0cd35d5bd57e61410fda76fea850225cda1880b844e1fa7638000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000001ba0563f81ca66b2c618bf4be9470fab88fff1b44eb5c33a9c73a68e8b26fbaa7c8da041464789c49fee77d2e053ff0705bc845fe2a78a35e478132371f294bb594021",
+ "tracerConfig": {
+ "withLog": true
+ },
+ "result": {
+ "from": "0xe6002189a74b43e6868b20c1311bc108e38aac57",
+ "gas": "0xa59c8",
+ "gasUsed": "0xaae60",
+ "to": "0x630a0cd35d5bd57e61410fda76fea850225cda18",
+ "input": "0xe1fa763800000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000000",
+ "error": "invalid jump destination",
+ "calls": [
+ {
+ "from": "0x630a0cd35d5bd57e61410fda76fea850225cda18",
+ "gas": "0x9f5a0",
+ "gasUsed": "0x314",
+ "to": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
+ "input": "0x70a08231000000000000000000000000630a0cd35d5bd57e61410fda76fea850225cda18",
+ "output": "0x000000000000000000000000000000000000000000000000000289739e60e3e2",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x630a0cd35d5bd57e61410fda76fea850225cda18",
+ "gas": "0x9a327",
+ "gasUsed": "0x67b0",
+ "to": "0x6e073c0e1bd5af550239025dffcfb37175acedd3",
+ "input": "0x1a695230000000000000000000000000bb9bc244d798123fde783fcc1c72d3bb8c189413",
+ "calls": [
+ {
+ "from": "0x6e073c0e1bd5af550239025dffcfb37175acedd3",
+ "gas": "0x93ff6",
+ "gasUsed": "0x314",
+ "to": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
+ "input": "0x70a082310000000000000000000000006e073c0e1bd5af550239025dffcfb37175acedd3",
+ "output": "0x000000000000000000000000000000000000000000000025d57ab057892050fc",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e073c0e1bd5af550239025dffcfb37175acedd3",
+ "gas": "0x93c42",
+ "gasUsed": "0x13f",
+ "to": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
+ "input": "0x18160ddd",
+ "output": "0x000000000000000000000000000000000000000000098b4d3b425f8c368391b2",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x6e073c0e1bd5af550239025dffcfb37175acedd3",
+ "gas": "0x939ba",
+ "gasUsed": "0x5fca",
+ "to": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
+ "input": "0xa9059cbb000000000000000000000000630a0cd35d5bd57e61410fda76fea850225cda18000000000000000000000000000000000000000000000025d57ab057892050fc",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x630a0cd35d5bd57e61410fda76fea850225cda18",
+ "gas": "0x8d8b6",
+ "gasUsed": "0x7be",
+ "to": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
+ "input": "0x013cf08b0000000000000000000000000000000000000000000000000000000000000110",
+ "output": "0x000000000000000000000000b3b10eff47b9c0b3e5579bf1c25872111667e6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000579a07ea0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000163c103e1feea47a9bf6c0dce1349da1a95b96532661d43063ab8e52b3e2a844b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000001620725a3de20090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b3b10eff47b9c0b3e5579bf1c25872111667e650000000000000000000000000000000000000000000000000000000000000004953706c697420666f722070656f706c652077686f2073656e74206d6f6e657920746f207468652044414f20616674657220746865204861726420466f726b206279206d697374616b650000000000000000000000000000000000000000000000",
+ "value": "0x0",
+ "type": "CALL"
+ },
+ {
+ "from": "0x630a0cd35d5bd57e61410fda76fea850225cda18",
+ "gas": "0x880f8",
+ "gasUsed": "0x880f8",
+ "to": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
+ "input": "0x82661dc40000000000000000000000000000000000000000000000000000000000000110000000000000000000000000b3b10eff47b9c0b3e5579bf1c25872111667e650",
+ "error": "invalid jump destination",
+ "calls": [
+ {
+ "from": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
+ "gas": "0x7f910",
+ "gasUsed": "0xd20f",
+ "to": "0x10abb5efecdc09581f8b7cb95791fe2936790b4e",
+ "input": "0xbaac5300000000000000000000000000630a0cd35d5bd57e61410fda76fea850225cda18",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "calls": [
+ {
+ "from": "0x10abb5efecdc09581f8b7cb95791fe2936790b4e",
+ "gas": "0x76e12",
+ "gasUsed": "0x13f9",
+ "to": "0xfde8d5f77ef48bb7bf5766c7404691b9ee1dfca7",
+ "input": "0x",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x20320625e3126cb0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json
new file mode 100644
index 0000000000..8df52db23c
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json
@@ -0,0 +1,107 @@
+{
+ "genesis": {
+ "difficulty": "45372803248884",
+ "extraData": "0x65746865726d696e652e6f7267202855533129",
+ "gasLimit": "4712388",
+ "hash": "0xa2b18cc64ec062676680f2bb2d880205dcd372f4396722f2294d3fceece96193",
+ "miner": "0xea674fdde714fd979de3edf0f56aa9716b898ec8",
+ "mixHash": "0xce7c26a9238b249edcdcd51f0ea1ad0e632e872daf9a09f039d918bcaeb7194f",
+ "nonce": "0x849d49e634e93bb5",
+ "number": "1646451",
+ "stateRoot": "0x2bd193b9911caf43204960cc7661ce864bf0bac7f9b60191aa02bbff24f061fb",
+ "timestamp": "1465103859",
+ "totalDifficulty": "24813742796574158431",
+ "alloc": {
+ "0x01115b41bd2731353dd3e6abf44818fdc035aaf1": {
+ "balance": "0x16d99e16e809000",
+ "nonce": "23"
+ },
+ "0x61c808d82a3ac53231750dadc13c777b59310bd9": {
+ "balance": "0x6a636960e34bd696f4",
+ "nonce": "36888"
+ },
+ "0xbb9bc244d798123fde783fcc1c72d3bb8c189413": {
+ "balance": "0x9b37460cdbcba74181f81",
+ "code": "0x6060604052361561020e5760e060020a6000350463013cf08b8114610247578063095ea7b3146102d05780630c3b7b96146103455780630e7082031461034e578063149acf9a1461036057806318160ddd146103725780631f2dc5ef1461037b57806321b5b8dd1461039b578063237e9492146103ad57806323b872dd1461040e5780632632bf2014610441578063341458081461047257806339d1f9081461047b5780634b6753bc146104935780634df6d6cc1461049c5780634e10c3ee146104b7578063590e1ae3146104ca578063612e45a3146104db578063643f7cdd1461057a578063674ed066146105925780636837ff1e1461059b57806370a08231146105e5578063749f98891461060b57806378524b2e1461062457806381f03fcb1461067e57806382661dc41461069657806382bf6464146106b75780638b15a605146106c95780638d7af473146106d257806396d7f3f5146106e1578063a1da2fb9146106ea578063a3912ec814610704578063a9059cbb1461070f578063b7bc2c841461073f578063baac53001461074b578063be7c29c1146107b1578063c9d27afe14610817578063cc9ae3f61461082d578063cdef91d014610841578063dbde198814610859578063dd62ed3e1461087e578063e33734fd146108b2578063e5962195146108c6578063e66f53b7146108de578063eceb2945146108f0578063f8c80d261461094f575b610966600f546000906234bc000142108015610239575060125433600160a060020a03908116911614155b156109785761098033610752565b6109866004356000805482908110156100025750808052600e8202600080516020612a3683398151915201905060038101546004820154600683015460018401548454600786015460058701546009880154600a890154600d8a0154600160a060020a039586169b509599600201989760ff81811698610100909204811697949691951693168c565b61096660043560243533600160a060020a03908116600081815260156020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b61096660105481565b610a7d600754600160a060020a031681565b610a7d600e54600160a060020a031681565b61096660165481565b6109665b60004262127500600f60005054031115610de557506014610983565b610a7d601254600160a060020a031681565b60408051602060248035600481810135601f810185900485028601850190965285855261096695813595919460449492939092019181908401838280828437509496505050505050506000600060006000600060003411156116a857610002565b6109666004356024356044355b60115460009060ff1680156104315750600f5442115b80156124e957506124e78461044b565b6109666000610980335b600160a060020a0381166000908152600b602052604081205481908114156129cb57610b99565b61096660065481565b6109665b600d5430600160a060020a03163103610983565b610966600f5481565b61096660043560046020526000908152604090205460ff1681565b61096660043560243560006124cb610831565b610a9a6000341115610ba457610002565b604080516020604435600481810135601f8101849004840285018401909552848452610966948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897608497919650602491909101945090925082915084018382808284375094965050933593505060a435915050600060006110c1336105ec565b61096660043560096020526000908152604090205481565b61096660015481565b610a9a60043530600160a060020a031633600160a060020a03161415806105db5750600160a060020a03811660009081526004602052604090205460ff16155b156121cb576121c8565b6109666004355b600160a060020a0381166000908152601460205260409020545b919050565b6109666004356024356000600034111561259957610002565b610966600062e6b680420360026000505410806106505750600354600160a060020a0390811633909116145b80156106645750600254621274ff19420190105b156126145750426002908155600180549091028155610983565b610966600435600a6020526000908152604090205481565b610966600435602435600060006000600060006000341115611ba157610002565b610a7d600854600160a060020a031681565b610966600c5481565b61096660005460001901610983565b61096660025481565b61096660043560006000600060003411156121fc57610002565b6109665b6001610983565b6109666004356024355b60115460009060ff16801561072f5750600f5442115b801561248757506124853361044b565b61096660115460ff1681565b6109666004355b60006000600f600050544210801561076a5750600034115b80156107a457506011546101009004600160a060020a0316600014806107a457506011546101009004600160a060020a0390811633909116145b15610b9f57610a9c61037f565b610a7d600435600060006000508281548110156100025750508080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56b600e83020180548290811015610002575081526020902060030154600160a060020a0316610606565b61096660043560243560006000610e1b336105ec565b6109665b6000600034111561247c57610002565b61096660043560056020526000908152604090205481565b610966600435602435604435600061252f845b6000600060003411156127ac57610002565b610966600435602435600160a060020a0382811660009081526015602090815260408083209385168352929052205461033f565b610a9a600435600034111561254557610002565b610966600435600b6020526000908152604090205481565b610a7d600354600160a060020a031681565b604080516020606435600481810135601f81018490048402850184019095528484526109669481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600034111561103257610002565b610a7d6011546101009004600160a060020a031681565b60408051918252519081900360200190f35b610980610708565b90505b90565b604051808d600160a060020a031681526020018c8152602001806020018b81526020018a815260200189815260200188815260200187815260200186815260200185815260200184815260200183600160a060020a0316815260200182810382528c818154600181600116156101000203166002900481526020019150805460018160011615610100020316600290048015610a635780601f10610a3857610100808354040283529160200191610a63565b820191906000526020600020905b815481529060010190602001808311610a4657829003601f168201915b50509d505050505050505050505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b005b604051601254601434908102939093049350600160a060020a03169183900390600081818185876185025a03f150505050600160a060020a038316600081815260146020908152604080832080548601905560168054860190556013825291829020805434019055815184815291517fdbccb92686efceafb9bb7e0394df7f58f71b954061b81afb57109bf247d3d75a9281900390910190a260105460165410801590610b4c575060115460ff16155b15610b94576011805460ff1916600117905560165460408051918252517ff381a3e2428fdda36615919e8d9c35878d9eb0cf85ac6edf575088e80e4c147e9181900360200190a15b600191505b50919050565b610002565b600f5442118015610bb8575060115460ff16155b15610de357601260009054906101000a9004600160a060020a0316600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040516012549051600160a060020a039190911631109050610cc9576040805160125460e060020a63d2cc718f0282529151600160a060020a039290921691630221038a913091849163d2cc718f91600482810192602092919082900301816000876161da5a03f11561000257505060408051805160e160020a63011081c5028252600160a060020a039490941660048201526024810193909352516044838101936020935082900301816000876161da5a03f115610002575050505b33600160a060020a0316600081815260136020526040808220549051909181818185876185025a03f19250505015610de35733600160a060020a03167fbb28353e4598c3b9199101a66e0989549b659a59a54d2c27fbb183f1932c8e6d6013600050600033600160a060020a03168152602001908152602001600020600050546040518082815260200191505060405180910390a26014600050600033600160a060020a0316815260200190815260200160002060005054601660008282825054039250508190555060006014600050600033600160a060020a031681526020019081526020016000206000508190555060006013600050600033600160a060020a03168152602001908152602001600020600050819055505b565b4262054600600f60005054031115610e13576201518062127500600f60005054034203046014019050610983565b50601e610983565b60001415610e2857610002565b6000341115610e3657610002565b6000805485908110156100025750600160a060020a03331681527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56e600e8602908101602052604090912054600080516020612a3683398151915291909101915060ff1680610eb05750600c810160205260406000205460ff165b80610ebf575060038101544210155b15610ec957610002565b8215610f0f5733600160a060020a03166000908152601460209081526040808320546009850180549091019055600b84019091529020805460ff19166001179055610f4b565b33600160a060020a0316600090815260146020908152604080832054600a850180549091019055600c84019091529020805460ff191660011790555b33600160a060020a03166000908152600b60205260408120541415610f77576040600020849055610feb565b33600160a060020a03166000908152600b60205260408120548154811015610002579080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566600e909102015460038201541115610feb5733600160a060020a03166000908152600b602052604090208490555b60408051848152905133600160a060020a03169186917f86abfce99b7dd908bec0169288797f85049ec73cbe046ed9de818fab3a497ae09181900360200190a35092915050565b6000805487908110156100025750808052600e8702600080516020612a3683398151915201905090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816005016000505414915050949350505050565b600014156110ce57610002565b82801561111857508660001415806110e857508451600014155b806111005750600354600160a060020a038981169116145b8061110b5750600034115b80611118575062093a8084105b1561112257610002565b8215801561114257506111348861115c565b158061114257506212750084105b156111fe57610002565b83546118e590600160a060020a03165b600160a060020a03811660009081526004602052604081205460ff16806111f15750601254600160a060020a039081169083161480156111f15750601260009054906101000a9004600160a060020a0316600160a060020a031663d2cc718f6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604051516006541190505b156129a157506001610606565b6249d40084111561120e57610002565b60115460ff1615806112215750600f5442105b806112365750600c5434108015611236575082155b1561124057610002565b42844201101561124f57610002565b30600160a060020a031633600160a060020a0316141561126e57610002565b60008054600181018083559091908280158290116112a557600e0281600e0283600052602060002091820191016112a5919061136a565b505060008054929450918491508110156100025750808052600e8302600080516020612a368339815191520190508054600160a060020a031916891781556001818101899055875160028084018054600082815260209081902096975091959481161561010002600019011691909104601f908101829004840193918b019083901061146257805160ff19168380011785555b5061149292915061144a565b5050600060098201819055600a820155600d81018054600160a060020a03191690556001015b8082111561145e578054600160a060020a03191681556000600182810182905560028084018054848255909281161561010002600019011604601f81901061143057505b506000600383018190556004808401805461ffff19169055600584018290556006840182905560078401805460ff191690556008840180548382559083526020909220611344929091028101905b8082111561145e57600080825560018201818155600283019190915560039091018054600160a060020a03191690556113fc565b601f0160209004906000526020600020908101906113ae91905b8082111561145e576000815560010161144a565b5090565b82800160010185558215611338579182015b82811115611338578251826000505591602001919060010190611474565b50508787866040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160050160005081905550834201816003016000508190555060018160040160006101000a81548160ff02191690830217905550828160070160006101000a81548160ff02191690830217905550821561157857600881018054600181018083559091908280158290116115735760040281600402836000526020600020918201910161157391906113fc565b505050505b600d8082018054600160a060020a031916331790553460068301819055815401905560408051600160a060020a038a16815260208181018a9052918101859052608060608201818152895191830191909152885185937f5790de2c279e58269b93b12828f56fd5f2bc8ad15e61ce08572585c81a38756f938d938d938a938e93929160a084019185810191908190849082908590600090600490601f850104600f02600301f150905090810190601f1680156116485780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a2509695505050505050565b6040805186815260208101839052815189927fdfc78bdca8e3e0b18c16c5c99323c6cb9eb5e00afde190b4e7273f5158702b07928290030190a25b5050505092915050565b6000805488908110156100025750808052600e8802600080516020612a36833981519152019050600781015490945060ff166116e757620d2f006116ec565b622398805b600485015490935060ff16801561170857506003840154830142115b15611716576117b887611890565b600384015442108061172d5750600484015460ff16155b806117ae57508360000160009054906101000a9004600160a060020a03168460010160005054876040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020846005016000505414155b1561114c57610002565b61169e565b60048401805461ff001916610100179055835460019550600160a060020a03908116309091161480159061180157508354600754600160a060020a03908116911614155b801561181d57506008548454600160a060020a03908116911614155b801561183957508354601254600160a060020a03908116911614155b801561185557506003548454600160a060020a03908116911614155b1561188b5760018401805430600160a060020a031660009081526005602052604090208054919091019055546006805490910190555b611663875b6000600060005082815481101561000257908052600e02600080516020612a36833981519152018150600481015490915060ff16156118d757600d80546006830154900390555b600401805460ff1916905550565b15156118f45761190087611890565b6001915061193161047f565b604051600d8501546006860154600160a060020a0391909116916000919082818181858883f193505050505061169e565b6001850154111561194157600091505b50600a8301546009840154865191019060049010801590611986575085600081518110156100025790602001015160f860020a900460f860020a02606860f860020a02145b80156119b6575085600181518110156100025790602001015160f860020a900460f860020a02603760f860020a02145b80156119e6575085600281518110156100025790602001015160f860020a900460f860020a0260ff60f860020a02145b8015611a16575085600381518110156100025790602001015160f860020a900460f860020a02601e60f860020a02145b8015611a45575030600160a060020a0316600090815260056020526040902054611a4290611a5d61047f565b81105b15611a4f57600091505b6001840154611a8090611a5f565b015b30600160a060020a03166000908152600560205260408120546129a961047f565b8110611ad457604051600d8501546006860154600160a060020a0391909116916000919082818181858883f193505050501515611abc57610002565b4260025560165460059004811115611ad45760056001555b6001840154611ae290611a5f565b8110158015611af85750600a8401546009850154115b8015611b015750815b1561188b578360000160009054906101000a9004600160a060020a0316600160a060020a0316846001016000505487604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611b7d5780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f19250505015156117bd57610002565b611baa336105ec565b60001415611bb757610002565b60008054889081101561000257508052600e87027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566810154600080516020612a36833981519152919091019450421080611c1957506003840154622398800142115b80611c3257508354600160a060020a0390811690871614155b80611c425750600784015460ff16155b80611c68575033600160a060020a03166000908152600b8501602052604090205460ff16155b80611c9c575033600160a060020a03166000908152600b60205260409020548714801590611c9c5750604060009081205414155b15611ca657610002565b600884018054600090811015610002579081526020812060030154600160a060020a03161415611e1257611efc86604051600090600160a060020a038316907f9046fefd66f538ab35263248a44217dcb70e2eb2cd136629e141b8b8f9f03b60908390a260408051600e547fe2faf044000000000000000000000000000000000000000000000000000000008252600160a060020a03858116600484015260248301859052604483018590526223988042016064840152925192169163e2faf04491608480820192602092909190829003018187876161da5a03f1156100025750506040515191506106069050565b6008850180546000908110156100025781815260208082209390935530600160a060020a031681526005909252604082205481549092908110156100025790815260208120905060020155601654600885018054600090811015610002579081526020812090506001015560048401805461ff0019166101001790555b6008840180546000908110156100025781548282526020822060010154929190811015610002579081526020812090505433600160a060020a031660009081526014602052604081205460088801805493909102939093049550908110156100025790815260208120905060030154604080517fbaac530000000000000000000000000000000000000000000000000000000000815233600160a060020a0390811660048301529151929091169163baac53009186916024808301926020929190829003018185886185025a03f11561000257505060405151600014159150611f78905057610002565b60088501805460009081101561000257818152602081206003018054600160a060020a03191690931790925580549091908110156100025790815260208120905060030154600160a060020a031660001415611f5757610002565b600d5430600160a060020a0316311015611f7057610002565b611d9561047f565b6008840180546000908110156100025781548282526020822060010154929190811015610002579081526020812090506002015433600160a060020a0390811660009081526014602090815260408083205430909416835260058083528184205460099093529083205460088b018054969095029690960497509487020494508593929091908290811015610002575260208120815060030154600160a060020a0390811682526020828101939093526040918201600090812080549095019094553016835260059091529020548290101561205357610002565b30600160a060020a031660009081526005602052604081208054849003905560088501805483926009929091829081101561000257508152602080822060030154600160a060020a039081168352929052604080822080549094019093553090911681522054819010156120c657610002565b30600160a060020a0390811660009081526009602090815260408083208054869003905533909316808352601482528383205484519081529351929390927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a36121383361086c565b5033600160a060020a03166000908152601460209081526040808320805460168054919091039055839055600a9091528120556001945061169e565b30600160a060020a0390811660008181526005602090815260408083208054958716808552828520805490970190965584845283905560099091528082208054948352908220805490940190935590815290555b50565b604051600160a060020a0382811691309091163190600081818185876185025a03f192505050151561217457610002565b33600160a060020a03818116600090815260096020908152604080832054815160065460085460e060020a63d2cc718f028352935197995091969195929092169363d2cc718f936004848101949193929183900301908290876161da5a03f11561000257505050604051805190602001506005600050600033600160a060020a03168152602001908152602001600020600050540204101561229d57610002565b600160a060020a03338116600090815260096020908152604080832054815160065460085460e060020a63d2cc718f02835293519296909593169363d2cc718f93600483810194929383900301908290876161da5a03f11561000257505050604051805190602001506005600050600033600160a060020a0316815260200190815260200160002060005054020403905083156123ec57600860009054906101000a9004600160a060020a0316600160a060020a0316630221038a83600160a060020a0316630e7082036040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060408051805160e160020a63011081c5028252600160a060020a031660048201526024810186905290516044808301935060209282900301816000876161da5a03f115610002575050604051511515905061245457610002565b6040805160085460e160020a63011081c5028252600160a060020a038581166004840152602483018590529251921691630221038a9160448082019260209290919082900301816000876161da5a03f115610002575050604051511515905061245457610002565b600160a060020a03331660009081526009602052604090208054909101905550600192915050565b6109803361086c565b155b80156124a257506124a23384845b6000600061293a856105ec565b80156124be57506124be83836000600034111561261c57610002565b15610b9f5750600161033f565b15156124d657610002565b6124e08383610719565b905061033f565b155b80156124fb57506124fb848484612495565b80156125185750612518848484600060003411156126c157610002565b15610b9f57506001612528565b90505b9392505050565b151561253a57610002565b61252584848461041b565b30600160a060020a031633600160a060020a031614158061258a575030600160a060020a031660009081526005602052604090205460649061258561047f565b010481115b1561259457610002565b600c55565b600354600160a060020a0390811633909116146125b557610002565b600160a060020a038316600081815260046020908152604091829020805460ff191686179055815185815291517f73ad2a153c8b67991df9459024950b318a609782cee8c7eeda47b905f9baa91f9281900390910190a250600161033f565b506000610983565b33600160a060020a03166000908152601460205260409020548290108015906126455750600082115b156126b957600160a060020a03338116600081815260146020908152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a350600161033f565b50600061033f565b600160a060020a03841660009081526014602052604090205482901080159061270a5750601560209081526040600081812033600160a060020a03168252909252902054829010155b80156127165750600082115b156127a457600160a060020a03838116600081815260146020908152604080832080548801905588851680845281842080548990039055601583528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a3506001612528565b506000612528565b600160a060020a038381166000908152600a6020908152604080832054601654600754835160e060020a63d2cc718f02815293519296919591169363d2cc718f9360048181019492939183900301908290876161da5a03f11561000257505060405151905061281a866105ec565b0204101561282757610002565b600160a060020a038381166000908152600a6020908152604080832054601654600754835160e060020a63d2cc718f02815293519296919591169363d2cc718f9360048181019492939183900301908290876161da5a03f115610002575050604051519050612895866105ec565b0204039050600760009054906101000a9004600160a060020a0316600160a060020a0316630221038a84836040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506020604051808303816000876161da5a03f115610002575050604051511515905061291357610002565b600160a060020a0383166000908152600a6020526040902080548201905560019150610b99565b600160a060020a0386166000908152600a602052604090205480850291909104915081111561296857610002565b600160a060020a038581166000908152600a60205260408082208054859003905591861681522080548201905560019150509392505050565b506000610606565b0160030260166000505483020460016000505460166000505404019050610606565b600160a060020a0383166000908152600b6020526040812054815481101561000257818052600e02600080516020612a368339815191520190506003810154909150421115610b9457600160a060020a0383166000908152600b602052604081208190559150610b9956290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563",
+ "nonce": "3",
+ "storage": {
+ "0x000000000000000000000000000000000000000000000000000000000000000f": "0x0000000000000000000000000000000000000000000000000000000057495e10",
+ "0x0000000000000000000000000000000000000000000000000000000000000012": "0x000000000000000000000000807640a13483f8ac783c557fcdf27be11ea4ac7a"
+ }
+ },
+ "0xcf1476387d780169410d4e936d75a206fda2a68c": {
+ "balance": "0x15fd0ad66ea7000",
+ "code": "0x606060405236156100b95760e060020a6000350463173825d9811461010b5780632f54bf6e1461015f5780634123cb6b146101875780635c52c2f5146101905780637065cb48146101ba578063746c9171146101e7578063797af627146101f0578063b20d30a914610203578063b61d27f614610230578063b75c7dc614610251578063ba51a6df14610280578063c2cf7326146102ad578063cbf0b0c0146102eb578063f00d4b5d14610318578063f1736d861461034a575b61035460003411156101095760408051600160a060020a033316815234602082015281517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c929181900390910190a15b565b610354600435600060003660405180838380828437820191505092505050604051809103902061064a815b600160a060020a03331660009081526101026020526040812054818082811415610c6657610dbf565b6103566004355b600160a060020a03811660009081526101026020526040812054115b919050565b61035660015481565b61035460003660405180838380828437820191505092505050604051809103902061078b81610136565b6103546004356000366040518083838082843782019150509250505060405180910390206105c681610136565b61035660005481565b6103566004355b600081610a2781610136565b61035460043560003660405180838380828437820191505092505050604051809103902061077f81610136565b6103566004803590602480359160443591820191013560006107aa33610166565b610354600435600160a060020a03331660009081526101026020526040812054908082811415610368576103e7565b61035460043560003660405180838380828437820191505092505050604051809103902061070881610136565b610356600435602435600082815261010360209081526040808320600160a060020a0385168452610102909252822054828181141561076157610776565b61035460043560003660405180838380828437820191505092505050604051809103902061079981610136565b610354600435602435600060003660405180838380828437820191505092505050604051809103902061047281610136565b6103566101055481565b005b60408051918252519081900360200190f35b50506000828152610103602052604081206001810154600284900a9290831611156103e75780546001828101805492909101835590839003905560408051600160a060020a03331681526020810186905281517fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b929181900390910190a15b50505050565b600160a060020a03831660028361010081101561000257508301819055600160a060020a03851660008181526101026020908152604080832083905584835291829020869055815192835282019290925281517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c929181900390910190a1505b505050565b156103e75761048083610166565b1561048b575061046d565b600160a060020a0384166000908152610102602052604081205492508214156104b4575061046d565b6103ed5b6101045460005b81811015610f0b57610104805461010891600091849081101561000257600080516020610fd88339815191520154825250602091909152604081208054600160a060020a0319168155600181810183905560028281018054858255939493909281161561010002600019011604601f819010610f9057505b5050506001016104bf565b60018054810190819055600160a060020a038316906002906101008110156100025790900160005055600154600160a060020a03831660008181526101026020908152604091829020939093558051918252517f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c3929181900390910190a15b505b50565b156105c1576105d482610166565b156105df57506105c3565b6105e76104b8565b60015460fa90106105fa576105fa61060f565b60015460fa901061054257506105c3565b6106c75b60015b6001548110156105c3575b6001548110801561063d5750600281610100811015610002570154600014155b15610dc75760010161061d565b1561046d57600160a060020a03831660009081526101026020526040812054925082141561067857506105c1565b600160016000505403600060005054111561069357506105c1565b600060028361010081101561000257508301819055600160a060020a0384168152610102602052604081205561060b6104b8565b60408051600160a060020a038516815290517f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da9181900360200190a1505050565b156105c15760015482111561071d57506105c3565b600082905561072a6104b8565b6040805183815290517facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da9181900360200190a15050565b506001820154600282900a9081166000141593505b50505092915050565b156105c1575061010555565b156105c35760006101065550565b156105c15781600160a060020a0316ff5b156109eb576107be846000610ea133610166565b1561087d577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd00433858786866040518086600160a060020a0316815260200185815260200184600160a060020a031681526020018060200182810382528484828181526020019250808284378201915050965050505050505060405180910390a184600160a060020a03168484846040518083838082843782019150509250505060006040518083038185876185025a03f150600093506109eb92505050565b6000364360405180848480828437820191505082815260200193505050506040518091039020905080506108b0816101f7565b1580156108d3575060008181526101086020526040812054600160a060020a0316145b156109eb5760008181526101086020908152604082208054600160a060020a0319168817815560018181018890556002918201805481865294849020909491821615610100026000190190911691909104601f9081019290920481019185919087908390106109f35760ff198135168380011785555b506109659291505b80821115610a235760008155600101610951565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf328133868887876040518087815260200186600160a060020a0316815260200185815260200184600160a060020a03168152602001806020018281038252848482818152602001925080828437820191505097505050505050505060405180910390a15b949350505050565b82800160010185558215610949579182015b82811115610949578235826000505591602001919060010190610a05565b5090565b15610aaa5760008381526101086020526040812054600160a060020a031614610aaa5760408051600091909120805460018281015460029384018054600160a060020a0394909416959194909391928392859291811615610100026000190116048015610adb5780601f10610ab057610100808354040283529160200191610adb565b50919050565b820191906000526020600020905b815481529060010190602001808311610abe57829003601f168201915b505091505060006040518083038185876185025a03f1505050600084815261010860209081526040918290208054600180830154855133600160a060020a0381811683529682018c9052968101829052929094166060830181905260a06080840181815260029586018054948516156101000260001901909416959095049084018190527fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a97508a95949193919060c083019084908015610bdd5780601f10610bb257610100808354040283529160200191610bdd565b820191906000526020600020905b815481529060010190602001808311610bc057829003601f168201915b5050965050505050505060405180910390a16000838152610108602052604081208054600160a060020a0319168155600181810183905560028281018054858255939493909281161561010002600019011604601f819010610c4857505b5050506001915050610182565b601f016020900490600052602060002090810190610c3b9190610951565b60008581526101036020526040812080549093501415610cee576000805483556001838101919091556101048054918201808255828015829011610cbd57818360005260206000209182019101610cbd9190610951565b50505060028301819055610104805487929081101561000257600091909152600080516020610fd883398151915201555b506001810154600283900a90811660001415610dbf5760408051600160a060020a03331681526020810187905281517fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda929181900390910190a1815460019011610dac576000858152610103602052604090206002015461010480549091908110156100025760406000908120600080516020610fd8833981519152929092018190558082556001828101829055600292909201559450610dbf9050565b8154600019018255600182018054821790555b505050919050565b5b60018054118015610dea57506001546002906101008110156100025701546000145b15610dfe5760018054600019019055610dc8565b60015481108015610e215750600154600290610100811015610002570154600014155b8015610e3b57506002816101008110156100025701546000145b15610e9c57600154600290610100811015610002578101549082610100811015610002579090016000505580610102600060028361010081101561000257810154825260209290925260408120929092556001546101008110156100025701555b610612565b156101825761010754610eb75b62015180420490565b1115610ed057600061010655610ecb610eae565b610107555b6101065480830110801590610eed57506101055461010654830111155b15610f0357506101068054820190556001610182565b506000610182565b6105c16101045460005b81811015610fae5761010480548290811015610002576000918252600080516020610fd8833981519152015414610f8857610104805461010391600091849081101561000257600080516020610fd883398151915201548252506020919091526040812081815560018101829055600201555b600101610f15565b601f0160209004906000526020600020908101906105379190610951565b610104805460008083559190915261046d90600080516020610fd883398151915290810190610951564c0be60200faa20559308cb7b5a1bb3255c16cb1cab91f525b5ae7a03d02fabe",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000105": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000106": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000107": "0x000000000000000000000000000000000000000000000000000000000000423d",
+ "0xcabd288dcb1ace4f49c34e8ac2d843772952b4226b3c832bdb4ac1ddca0f7c05": "0x0000000000000000000000000000000000000000000000000000000000000002"
+ }
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "1646452",
+ "difficulty": "45328493887096",
+ "timestamp": "1465103894",
+ "gasLimit": "4712388",
+ "miner": "0x61c808d82a3ac53231750dadc13c777b59310bd9"
+ },
+ "input": "0xf9018b178504a817c80083030d4094cf1476387d780169410d4e936d75a206fda2a68c80b90124b61d27f6000000000000000000000000bb9bc244d798123fde783fcc1c72d3bb8c189413000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000088613930353963626230303030303030303030303030303030303030303030303039306433633138313264653236363962663830376264373735386365623165333439376163376534303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316336626635323633343030300000000000000000000000000000000000000000000000001ca0f1ae5ea07b1d00eb5e06fc854124ee0234ec61c8b393147f9d030804a75c98daa01d045d7633012cca74e30e975c3d00d11b4243dd8648f2e78d652f3a8aaafceb",
+ "tracerConfig": {
+ "withLog": true
+ },
+ "result": {
+ "from": "0x01115b41bd2731353dd3e6abf44818fdc035aaf1",
+ "gas": "0x28e28",
+ "gasUsed": "0x288c9",
+ "to": "0xcf1476387d780169410d4e936d75a206fda2a68c",
+ "input": "0xb61d27f6000000000000000000000000bb9bc244d798123fde783fcc1c72d3bb8c18941300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008861393035396362623030303030303030303030303030303030303030303030303930643363313831326465323636396266383037626437373538636562316533343937616337653430303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031633662663532363334303030000000000000000000000000000000000000000000000000",
+ "output": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "calls": [
+ {
+ "from": "0xcf1476387d780169410d4e936d75a206fda2a68c",
+ "gas": "0x1e30b",
+ "gasUsed": "0x1e30b",
+ "to": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
+ "input": "0x61393035396362623030303030303030303030303030303030303030303030303930643363313831326465323636396266383037626437373538636562316533343937616337653430303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031633662663532363334303030",
+ "error": "invalid jump destination",
+ "value": "0x0",
+ "type": "CALL"
+ }
+ ],
+ "logs": [
+ {
+ "address": "0xcf1476387d780169410d4e936d75a206fda2a68c",
+ "topics": [
+ "0x92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd004"
+ ],
+ "data": "0x00000000000000000000000001115b41bd2731353dd3e6abf44818fdc035aaf10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb9bc244d798123fde783fcc1c72d3bb8c1894130000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008861393035396362623030303030303030303030303030303030303030303030303930643363313831326465323636396266383037626437373538636562316533343937616337653430303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031633662663532363334303030"
+ }
+ ],
+ "value": "0x0",
+ "type": "CALL"
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json
new file mode 100644
index 0000000000..c805296adb
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json
@@ -0,0 +1,89 @@
+{
+ "genesis": {
+ "difficulty": "11934798510088",
+ "extraData": "0xd983010302844765746887676f312e342e328777696e646f7773",
+ "gasLimit": "3141592",
+ "hash": "0xfc543a4a551afbd4a6c5d6d49041371e6bb58b1108c12aaec7f487ce656bb97f",
+ "miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069",
+ "mixHash": "0xa6a1e67fc68da76b8d9cc3ce1c45d5e1f4bbd96b5dcfddbe0017d7fa99903ead",
+ "nonce": "0x5f00c600268b4659",
+ "number": "995200",
+ "stateRoot": "0x3579328470dd2aef5b9da69f5480cbe0d375e653b530ab3c1aee0da5e1ff4c94",
+ "timestamp": "1455322761",
+ "totalDifficulty": "7077231809278509672",
+ "alloc": {
+ "0x200edd17f30485a8735878661960cd7a9a95733f": {
+ "balance": "0x0",
+ "code": "0x3660008037602060003660003473273930d21e01ee25e4c219b63259d214872220a261235a5a03f21560015760206000f3",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000002",
+ "0x0000000000000000000000000000000000000000000000000000000000000104": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x4c0be60200faa20559308cb7b5a1bb3255c16cb1cab91f525b5ae7a03d02fabe": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8ba1097eb3abe3dc1b51faa48445d593bf968f722e20b67bb62a87495836bf04": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8ba1097eb3abe3dc1b51faa48445d593bf968f722e20b67bb62a87495836bf05": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x8ba1097eb3abe3dc1b51faa48445d593bf968f722e20b67bb62a87495836bf06": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xa611e7c895a426c0477bc9e280db9c3b1e456dc6310ffcf23926ef5186c1facc": "0x0000000000000000000000000000000000000000000000000000000000000002",
+ "0xac682d343707aadf06c2c4c3692831d9e7ba711099ef36f9efb8bb29be8c410e": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xac682d343707aadf06c2c4c3692831d9e7ba711099ef36f9efb8bb29be8c410f": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xac682d343707aadf06c2c4c3692831d9e7ba711099ef36f9efb8bb29be8c4110": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x273930d21e01ee25e4c219b63259d214872220a2": {
+ "balance": "0x0",
+ "code": "0x606060405236156100da5760e060020a6000350463173825d9811461012c5780632f54bf6e146101875780634123cb6b146101af57806352375093146101b857806354fd4d50146101c25780635c52c2f5146101cc578063659010e7146101fd5780637065cb4814610207578063746c91711461023b578063797af62714610244578063b20d30a914610257578063b61d27f61461028b578063b75c7dc6146102ac578063ba51a6df146102db578063c2cf73261461030f578063cbf0b0c01461034d578063f00d4b5d14610381578063f1736d86146103ba575b6103c4600034111561012a5760408051600160a060020a033216815234602082015281517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c929181900390910190a15b565b6103c46004356000600036436040518084848082843750505090910190815260405190819003602001902090506106c9815b600160a060020a03321660009081526101026020526040812054818082811415610c3f57610d97565b6103c66004355b600160a060020a03811660009081526101026020526040812054115b919050565b6103c660015481565b6103c66101075481565b6103c66101085481565b6103c46000364360405180848480828437505050909101908152604051908190036020019020905061081a8161015e565b6103c66101065481565b6103c4600435600036436040518084848082843750505090910190815260405190819003602001902090506106418161015e565b6103c660005481565b6103c66004355b600081610a7d8161015e565b6103c46004356000364360405180848480828437505050909101908152604051908190036020019020905061080e8161015e565b6103c66004803590602480359160443591820191013560006108393261018e565b6103c4600435600160a060020a033216600090815261010260205260408120549080828114156103d857610457565b6103c4600435600036436040518084848082843750505090910190815260405190819003602001902090506107888161015e565b6103c6600435602435600082815261010360209081526040808320600160a060020a038516845261010290925282205482818114156107e157610805565b6103c4600435600036436040518084848082843750505090910190815260405190819003602001902090506108288161015e565b6103c46004356024356000600036436040518084848082843750505090910190815260405190819003602001902090506104e28161015e565b6103c66101055481565b005b60408051918252519081900360200190f35b50506000828152610103602052604081206001810154600284900a9290831611156104575780546001828101805492909101835590839003905560408051600160a060020a03321681526020810186905281517fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b929181900390910190a15b50505050565b600160a060020a03831660028361010081101561000257508301819055600160a060020a03851660008181526101026020908152604080832083905584835291829020869055815192835282019290925281517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c929181900390910190a1505b505050565b15610457576104f08361018e565b156104fb57506104dd565b600160a060020a03841660009081526101026020526040812054925082141561052457506104dd565b61045d5b6101045460005b81811015610ee457610104805461010991600091849081101561000257600080516020610f9f83398151915201548252506020918252604081208054600160a060020a0319168155600181018290556002810180548382559083528383209193610f6992601f9290920104810190610a65565b60018054810190819055600160a060020a038316906002906101008110156100025790900160005081905550600160005054610102600050600084600160a060020a03168152602001908152602001600020600050819055507f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c3826040518082600160a060020a0316815260200191505060405180910390a15b505b50565b1561063c5761064f8261018e565b1561065a575061063e565b610662610528565b60015460fa90106106775761067561068c565b505b60015460fa90106105a2575061063e565b6107465b600060015b600154811015610a79575b600154811080156106bc5750600281610100811015610002570154600014155b15610d9f5760010161069c565b156104dd57600160a060020a0383166000908152610102602052604081205492508214156106f7575061063c565b6001600160005054036000600050541115610712575061063c565b600060028361010081101561000257508301819055600160a060020a03841681526101026020526040812055610688610528565b5060408051600160a060020a038516815290517f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da9181900360200190a1505050565b1561063c5760015482111561079d575061063e565b60008290556107aa610528565b6040805183815290517facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da9181900360200190a15050565b506001820154600282900a908116600014156108005760009350610805565b600193505b50505092915050565b1561063c575061010555565b1561063e5760006101065550565b1561063c5781600160a060020a0316ff5b15610a555761084d846000610e793261018e565b15610909577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd00432858786866040518086600160a060020a0316815260200185815260200184600160a060020a031681526020018060200182810382528484828181526020019250808284378201915050965050505050505060405180910390a184600160a060020a03168484846040518083838082843750505090810191506000908083038185876185025a03f15060009350610a5592505050565b6000364360405180848480828437505050909101908152604051908190036020019020915061093990508161024b565b15801561095c575060008181526101096020526040812054600160a060020a0316145b15610a555760008181526101096020908152604082208054600160a060020a03191688178155600181018790556002018054858255818452928290209092601f01919091048101908490868215610a5d579182015b82811115610a5d5782358260005055916020019190600101906109b1565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf328132868887876040518087815260200186600160a060020a0316815260200185815260200184600160a060020a03168152602001806020018281038252848482818152602001925080828437820191505097505050505050505060405180910390a15b949350505050565b506109cf9291505b80821115610a795760008155600101610a65565b5090565b15610c2c5760008381526101096020526040812054600160a060020a031614610c2c5760408051600091909120805460018201546002929092018054600160a060020a0392909216939091819083908015610afd57820191906000526020600020905b815481529060010190602001808311610ae057829003601f168201915b505091505060006040518083038185876185025a03f150505060008481526101096020908152604080519281902080546001820154600160a060020a033281811688529587018b905293860181905292166060850181905260a06080860181815260029390930180549187018290527fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a975094958a959293909160c083019084908015610bcf57820191906000526020600020905b815481529060010190602001808311610bb257829003601f168201915b5050965050505050505060405180910390a160008381526101096020908152604082208054600160a060020a031916815560018101839055600281018054848255908452828420919392610c3292601f9290920104810190610a65565b50919050565b50505060019150506101aa565b60008581526101036020526040812080549093501415610cc7576000805483556001838101919091556101048054918201808255828015829011610c9657818360005260206000209182019101610c969190610a65565b50505060028301819055610104805487929081101561000257600091909152600080516020610f9f83398151915201555b506001810154600283900a90811660001415610d975760408051600160a060020a03321681526020810187905281517fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda929181900390910190a1815460019011610d84576000858152610103602052604090206002015461010480549091908110156100025760406000908120600080516020610f9f8339815191529290920181905580825560018083018290556002909201559450610d979050565b8154600019018255600182018054821790555b505050919050565b5b60018054118015610dc257506001546002906101008110156100025701546000145b15610dd65760018054600019019055610da0565b60015481108015610df95750600154600290610100811015610002570154600014155b8015610e1357506002816101008110156100025701546000145b15610e7457600154600290610100811015610002578101549082610100811015610002578101919091558190610102906000908361010081101561000257810154825260209290925260408120929092556001546101008110156100025701555b610691565b156101aa5761010754610e8f5b62015180420490565b1115610ea857600061010655610ea3610e86565b610107555b6101065480830110801590610ec65750610106546101055490830111155b15610edc575061010680548201905560016101aa565b5060006101aa565b61063c6101045460005b81811015610f745761010480548290811015610002576000918252600080516020610f9f833981519152015414610f6157610104805461010391600091849081101561000257600080516020610f9f83398151915201548252506020919091526040812081815560018101829055600201555b600101610eee565b50505060010161052f565b61010480546000808355919091526104dd90600080516020610f9f83398151915290810190610a6556004c0be60200faa20559308cb7b5a1bb3255c16cb1cab91f525b5ae7a03d02fabe"
+ },
+ "0x4f5777744b500616697cb655dcb02ee6cd51deb5": {
+ "balance": "0xb0983f1b83eec290",
+ "nonce": "2"
+ },
+ "0xf8b483dba2c3b7176a3da549ad41a48bb3121069": {
+ "balance": "0x16969a0ba2c2d384d07",
+ "nonce": "67521"
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "995201",
+ "difficulty": "11940626048551",
+ "timestamp": "1455322773",
+ "gasLimit": "3141592",
+ "miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069"
+ },
+ "input": "0xf89102850a954d522e8303308594200edd17f30485a8735878661960cd7a9a95733f888ac7230489e80000a4ba51a6df00000000000000000000000000000000000000000000000000000000000000001ca04f2cc45b96f965296382b2e9b657e90808301d5179035a5d91a2de7b912def20a056e19271ea4e19e4e034f38e925e312beed4d300c267160eeb2f565c42deb578",
+ "tracerConfig": {
+ "withLog": true,
+ "onlyTopCall": true
+ },
+ "result": {
+ "from": "0x4f5777744b500616697cb655dcb02ee6cd51deb5",
+ "gas": "0x2dced",
+ "gasUsed": "0x1a9e5",
+ "to": "0x200edd17f30485a8735878661960cd7a9a95733f",
+ "input": "0xba51a6df0000000000000000000000000000000000000000000000000000000000000000",
+ "output": "0xba51a6df00000000000000000000000000000000000000000000000000000000",
+ "value": "0x8ac7230489e80000",
+ "type": "CALL"
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json
new file mode 100644
index 0000000000..a34d3b759e
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json
@@ -0,0 +1,85 @@
+{
+ "genesis": {
+ "difficulty": "6217248151198",
+ "extraData": "0xd783010103844765746887676f312e342e32856c696e7578",
+ "gasLimit": "3141592",
+ "hash": "0xe8bff55fe3e61936ef321cf3afaeb1ba2f7234e1e89535fa8ae39963caebe9c3",
+ "miner": "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5",
+ "mixHash": "0x03da00d5a15a064e5ebddf53cd0aaeb9a8aff0f40c0fb031a74f463d11ec83b8",
+ "nonce": "0x6575fe08c4167044",
+ "number": "243825",
+ "stateRoot": "0x47182fe2e6e740b8a76f82fe5c527d6ad548f805274f21792cf4047235b24fbf",
+ "timestamp": "1442424328",
+ "totalDifficulty": "1035061827427752845",
+ "alloc": {
+ "0x082d4cdf07f386ffa9258f52a5c49db4ac321ec6": {
+ "balance": "0xc820f93200f4000",
+ "nonce": "0x5E",
+ "code": "0x"
+ },
+ "0x332b656504f4eabb44c8617a42af37461a34e9dc": {
+ "balance": "0x11faea4f35e5af80000",
+ "code": "0x"
+ },
+ "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5": {
+ "balance": "0xbf681825be002ac452",
+ "nonce": "0x70FA",
+ "code": "0x"
+ },
+ "0x82effbaaaf28614e55b2ba440fb198e0e5789b0f": {
+ "balance": "0xb3d0ac5cb94df6f6b0",
+ "nonce": "0x1",
+ "code": "0x"
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "243826",
+ "difficulty": "6214212385501",
+ "timestamp": "1442424353",
+ "gasLimit": "3141592",
+ "miner": "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5"
+ },
+ "input": "0xf8e85e850ba43b7400830f42408080b89660606040527382effbaaaf28614e55b2ba440fb198e0e5789b0f600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055505b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b600a80608c6000396000f30060606040526008565b001ca0340b21661e5bb85a46319a15f33a362e5c0f02faa7cdbf9c5808b2134da968eaa0226e6788f8c20e211d436ab7f6298ef32fa4c23a509eeeaac0880d115c17bc3f",
+ "result": {
+ "0x082d4cdf07f386ffa9258f52a5c49db4ac321ec6": {
+ "balance": "0xc820f93200f4000",
+ "nonce": 94
+ },
+ "0x332b656504f4eabb44c8617a42af37461a34e9dc": {
+ "balance": "0x11faea4f35e5af80000",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5": {
+ "balance": "0xbf681825be002ac452",
+ "nonce": 28922
+ },
+ "0x82effbaaaf28614e55b2ba440fb198e0e5789b0f": {
+ "balance": "0xb3d0ac5cb94df6f6b0",
+ "nonce": 1
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/simple.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/simple.json
new file mode 100644
index 0000000000..7204bfcbfe
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/simple.json
@@ -0,0 +1,83 @@
+{
+ "context": {
+ "difficulty": "3502894804",
+ "gasLimit": "4722976",
+ "miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724",
+ "number": "2289806",
+ "timestamp": "1513601314"
+ },
+ "genesis": {
+ "alloc": {
+ "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
+ "balance": "0x0",
+ "code": "0x",
+ "nonce": "22",
+ "storage": {}
+ },
+ "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
+ "balance": "0x4d87094125a369d9bd5",
+ "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
+ "nonce": "1",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
+ }
+ },
+ "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
+ "balance": "0x1780d77678137ac1b775",
+ "code": "0x",
+ "nonce": "29072",
+ "storage": {}
+ }
+ },
+ "config": {
+ "byzantiumBlock": 1700000,
+ "chainId": 3,
+ "daoForkSupport": true,
+ "eip150Block": 0,
+ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
+ "eip155Block": 10,
+ "eip158Block": 10,
+ "ethash": {},
+ "homesteadBlock": 0
+ },
+ "difficulty": "3509749784",
+ "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
+ "gasLimit": "4727564",
+ "hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440",
+ "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
+ "mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada",
+ "nonce": "0x4eb12e19c16d43da",
+ "number": "2289805",
+ "stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
+ "timestamp": "1513601261",
+ "totalDifficulty": "7143276353481064"
+ },
+ "input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
+ "result": {
+ "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
+ "balance": "0x0",
+ "nonce": 22
+ },
+ "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
+ "balance": "0x4d87094125a369d9bd5",
+ "nonce": 1,
+ "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
+ }
+ },
+ "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
+ "balance": "0x1780d77678137ac1b775",
+ "nonce": 29072
+ },
+ "0x1585936b53834b021f68cc13eeefdec2efc8e724": {
+ "balance": "0x0"
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_legacy/simple.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_legacy/simple.json
new file mode 100644
index 0000000000..44b1f08dd3
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_legacy/simple.json
@@ -0,0 +1,84 @@
+{
+ "context": {
+ "difficulty": "3502894804",
+ "gasLimit": "4722976",
+ "miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724",
+ "number": "2289806",
+ "timestamp": "1513601314"
+ },
+ "genesis": {
+ "alloc": {
+ "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
+ "balance": "0x0",
+ "code": "0x",
+ "nonce": "22",
+ "storage": {}
+ },
+ "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
+ "balance": "0x4d87094125a369d9bd5",
+ "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
+ "nonce": "1",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
+ }
+ },
+ "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
+ "balance": "0x1780d77678137ac1b775",
+ "code": "0x",
+ "nonce": "29072",
+ "storage": {}
+ }
+ },
+ "config": {
+ "byzantiumBlock": 1700000,
+ "chainId": 3,
+ "daoForkSupport": true,
+ "eip150Block": 0,
+ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
+ "eip155Block": 10,
+ "eip158Block": 10,
+ "ethash": {},
+ "homesteadBlock": 0
+ },
+ "difficulty": "3509749784",
+ "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
+ "gasLimit": "4727564",
+ "hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440",
+ "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
+ "mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada",
+ "nonce": "0x4eb12e19c16d43da",
+ "number": "2289805",
+ "stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
+ "timestamp": "1513601261",
+ "totalDifficulty": "7143276353481064"
+ },
+ "input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
+ "result": {
+ "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
+ "balance": "0x0",
+ "code": "0x",
+ "nonce": 22,
+ "storage": {}
+ },
+ "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
+ "balance": "0x4d87094125a369d9bd5",
+ "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
+ "nonce": 1,
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
+ }
+ },
+ "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
+ "balance": "0x1780d77678137ac1b775",
+ "code": "0x",
+ "nonce": 29072,
+ "storage": {}
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create.json
new file mode 100644
index 0000000000..1b09622474
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create.json
@@ -0,0 +1,102 @@
+{
+ "genesis": {
+ "difficulty": "13756228101629",
+ "extraData": "0xd983010302844765746887676f312e342e328777696e646f7773",
+ "gasLimit": "3141592",
+ "hash": "0x58b7a87b6ba10b46b4e251d64ebc3d9822dd82218eaf24dff6796f6f1f687251",
+ "miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069",
+ "mixHash": "0x5984b9a316116bd890e6e5f4c52d655184b0d7aa74821e1382d7760f9803c1dd",
+ "nonce": "0xea4bb4997242c681",
+ "number": "1061221",
+ "stateRoot": "0x5402c04d481414248d824c3b61e924e0c9307adbc9fbaae774a74cce30a4163d",
+ "timestamp": "1456458069",
+ "totalDifficulty": "7930751135586064334",
+ "alloc": {
+ "0x2a65aca4d5fc5b5c859090a6c34d164135398226": {
+ "balance": "0x9fb6b81e112638b886",
+ "nonce": "217865",
+ "code": "0x"
+ },
+ "0xf0c5cef39b17c213cfe090a46b8c7760ffb7928a": {
+ "balance": "0x15b6828e22bb12188",
+ "nonce": "747",
+ "code": "0x"
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "1061222",
+ "difficulty": "13749511193633",
+ "timestamp": "1456458097",
+ "gasLimit": "3141592",
+ "miner": "0x2a65aca4d5fc5b5c859090a6c34d164135398226"
+ },
+ "input": "0xf905498202eb850ba43b7400830f42408080b904f460606040526040516102b43803806102b48339016040526060805160600190602001505b5b33600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055505b806001600050908051906020019082805482825590600052602060002090601f01602090048101928215609e579182015b82811115609d5782518260005055916020019190600101906081565b5b50905060c5919060a9565b8082111560c1576000818150600090555060010160a9565b5090565b50505b506101dc806100d86000396000f30060606040526000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b514610044578063cfae32171461005157610042565b005b61004f6004506100ca565b005b61005c60045061015e565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156100bc5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561015b57600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b565b60206040519081016040528060008152602001506001600050805480601f016020809104026020016040519081016040528092919081815260200182805480156101cd57820191906000526020600020905b8154815290600101906020018083116101b057829003601f168201915b505050505090506101d9565b9056000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001ee7b225f6964223a225a473466784a7245323639384866623839222c22666f726d5f736f75726365223a22434c54523031222c22636f6d6d69746d656e745f64617465223a22222c22626f72726f7765725f6e616d65223a22222c22626f72726f7765725f616464726573735f6c696e6531223a22222c22626f72726f7765725f616464726573735f6c696e6532223a22222c22626f72726f7765725f636f6e74616374223a22222c22626f72726f7765725f7374617465223a22222c22626f72726f7765725f74797065223a22222c2270726f70657274795f61646472657373223a22222c226c6f616e5f616d6f756e745f7772697474656e223a22222c226c6f616e5f616d6f756e74223a22222c224c54565f7772697474656e223a22222c224c5456223a22222c2244534352223a22222c2270726f70657274795f74797065223a22222c2270726f70657274795f6465736372697074696f6e223a22222c226c656e646572223a22222c2267756172616e746f7273223a22222c226c696d69746564223a22222c226361705f616d6f756e74223a22222c226361705f70657263656e745f7772697474656e223a22222c226361705f70657263656e74616765223a22222c227465726d5f7772697474656e223a22222c227465726d223a22222c22657874656e64223a22227d0000000000000000000000000000000000001ba027d54712289af34f0ec0f06092745104d68e5801cd17097bc1104111f855258da070ec9f1c942d9bedf89f9660a684d3bb8cd9c2ac7f6dd883cb3e26a193180244",
+ "tracerConfig": {
+ "diffMode": true
+ },
+ "result": {
+ "pre": {
+ "0x2a65aca4d5fc5b5c859090a6c34d164135398226": {
+ "balance": "0x9fb6b81e112638b886",
+ "nonce": 217865
+ },
+ "0xf0c5cef39b17c213cfe090a46b8c7760ffb7928a": {
+ "balance": "0x15b6828e22bb12188",
+ "nonce": 747
+ }
+ },
+ "post": {
+ "0x2a65aca4d5fc5b5c859090a6c34d164135398226": {
+ "balance": "0x9fb71abdd2621d8886"
+ },
+ "0x40f2f445da6c9047554683fb382fba6769717116": {
+ "code": "0x60606040526000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b514610044578063cfae32171461005157610042565b005b61004f6004506100ca565b005b61005c60045061015e565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156100bc5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561015b57600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b565b60206040519081016040528060008152602001506001600050805480601f016020809104026020016040519081016040528092919081815260200182805480156101cd57820191906000526020600020905b8154815290600101906020018083116101b057829003601f168201915b505050505090506101d9565b9056",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000f0c5cef39b17c213cfe090a46b8c7760ffb7928a",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000001ee",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": "0x7b225f6964223a225a473466784a7245323639384866623839222c22666f726d",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf7": "0x5f736f75726365223a22434c54523031222c22636f6d6d69746d656e745f6461",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf8": "0x7465223a22222c22626f72726f7765725f6e616d65223a22222c22626f72726f",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf9": "0x7765725f616464726573735f6c696e6531223a22222c22626f72726f7765725f",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cfa": "0x616464726573735f6c696e6532223a22222c22626f72726f7765725f636f6e74",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cfb": "0x616374223a22222c22626f72726f7765725f7374617465223a22222c22626f72",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cfc": "0x726f7765725f74797065223a22222c2270726f70657274795f61646472657373",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cfd": "0x223a22222c226c6f616e5f616d6f756e745f7772697474656e223a22222c226c",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cfe": "0x6f616e5f616d6f756e74223a22222c224c54565f7772697474656e223a22222c",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cff": "0x224c5456223a22222c2244534352223a22222c2270726f70657274795f747970",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d00": "0x65223a22222c2270726f70657274795f6465736372697074696f6e223a22222c",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d01": "0x226c656e646572223a22222c2267756172616e746f7273223a22222c226c696d",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d02": "0x69746564223a22222c226361705f616d6f756e74223a22222c226361705f7065",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d03": "0x7263656e745f7772697474656e223a22222c226361705f70657263656e746167",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d04": "0x65223a22222c227465726d5f7772697474656e223a22222c227465726d223a22",
+ "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d05": "0x222c22657874656e64223a22227d000000000000000000000000000000000000"
+ }
+ },
+ "0xf0c5cef39b17c213cfe090a46b8c7760ffb7928a": {
+ "balance": "0x15b058920efcc5188",
+ "nonce": 748
+ }
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json
new file mode 100644
index 0000000000..e80dad5667
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json
@@ -0,0 +1,94 @@
+{
+ "genesis": {
+ "baseFeePerGas": "51088069741",
+ "difficulty": "14315558652874667",
+ "extraData": "0xd883010a10846765746888676f312e31362e35856c696e7578",
+ "gasLimit": "30058590",
+ "hash": "0xdf6b95183f99054fb6541e3b482c0109c9f6be40553cff24efa3ac76736adbf5",
+ "miner": "0xb7e390864a90b7b923c9f9310c6f98aafe43f707",
+ "mixHash": "0x8d76b0d32e42ab277dbf00836eabef76674cd70ae2bb53718175069ad6b6147e",
+ "nonce": "0x8d3a1c010ad2c687",
+ "number": "14707767",
+ "stateRoot": "0x8a50c896a6f7eb1f3479337db981fa10ce316281cb4dd2f07487be9ca27dae6b",
+ "timestamp": "1651623275",
+ "alloc": {
+ "0x0000000000000000000000000000000000000000": {
+ "balance": "0x268fd0b894b8c4f6d1f"
+ },
+ "0x13b152c9f50878ffaf3de41e192653bda545d889": {
+ "balance": "0x0",
+ "nonce": "1",
+ "code": "0x363d3d373d3d3d363d73059ffafdc6ef594230de44f824e2bd0a51ca5ded5af43d82803e903d91602b57fd5bf3"
+ },
+ "0x808b4da0be6c9512e948521452227efc619bea52": {
+ "balance": "0x2cdb96c56db040b43",
+ "nonce": "1223932"
+ },
+ "0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7": {
+ "balance": "0x38079b28689d40240e",
+ "nonce": "44"
+ },
+ "0xffa397285ce46fb78c588a9e993286aac68c37cd": {
+ "balance": "0x0",
+ "nonce": "747319",
+ "code": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063b97a23191461003b578063fb90b3201461006f575b600080fd5b6100436100bd565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100bb6004803603604081101561008557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506100e1565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008282604051602001808373ffffffffffffffffffffffffffffffffffffffff1660601b815260140182815260200192505050604051602081830303815290604052805190602001209050600061015960008054906101000a900473ffffffffffffffffffffffffffffffffffffffff168361024d565b90508073ffffffffffffffffffffffffffffffffffffffff166319ab453c856040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff168152602001915050600060405180830381600087803b1580156101c457600080fd5b505af11580156101d8573d6000803e3d6000fd5b505050507fa35ea2cc726861482a50a162c72aad60965cc64641d419cd4d675036238b52048185604051808373ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a150505050565b6000808360601b90506040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528160148201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006028820152836037826000f5925050509291505056fea2646970667358221220c87b2492828fdd7dad3175a32a98ff07fc0eedf106536f2eddd9a016971c56a764736f6c63430007050033",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000059ffafdc6ef594230de44f824e2bd0a51ca5ded"
+ }
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "terminalTotalDifficultyPassed": true,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "14707768",
+ "difficulty": "14322823549655084",
+ "timestamp": "1651623279",
+ "gasLimit": "30029237",
+ "miner": "0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7"
+ },
+ "input": "0x02f8b4018312acfc8459682f00851a46bcf47a8302b1a194ffa397285ce46fb78c588a9e993286aac68c37cd80b844fb90b3200000000000000000000000002a549b4af9ec39b03142da6dc32221fc390b553300000000000000000000000000000000000000000000000000000000000cb3d5c001a03002079d2873f7963c4278200c43aa71efad262b2150bc8524480acfc38b5faaa077d44aa09d56b9cf99443c7f55aaad1bbae9cfb5bbb9de31eaf7a8f9e623e980",
+ "tracerConfig": {
+ "diffMode": true
+ },
+ "result": {
+ "pre": {
+ "0x808b4da0be6c9512e948521452227efc619bea52": {
+ "balance": "0x2cdb96c56db040b43",
+ "nonce": 1223932
+ },
+ "0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7": {
+ "balance": "0x38079b28689d40240e",
+ "nonce": 44
+ }
+ },
+ "post": {
+ "0x808b4da0be6c9512e948521452227efc619bea52": {
+ "balance": "0x2cd72a36dd031f089",
+ "nonce": 1223933
+ },
+ "0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7": {
+ "balance": "0x38079c19423e44b30e"
+ }
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_suicide.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_suicide.json
new file mode 100644
index 0000000000..fdeb0e5067
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_suicide.json
@@ -0,0 +1,104 @@
+{
+ "genesis": {
+ "difficulty": "6217248151198",
+ "extraData": "0xd783010103844765746887676f312e342e32856c696e7578",
+ "gasLimit": "3141592",
+ "hash": "0xe8bff55fe3e61936ef321cf3afaeb1ba2f7234e1e89535fa8ae39963caebe9c3",
+ "miner": "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5",
+ "mixHash": "0x03da00d5a15a064e5ebddf53cd0aaeb9a8aff0f40c0fb031a74f463d11ec83b8",
+ "nonce": "0x6575fe08c4167044",
+ "number": "243825",
+ "stateRoot": "0x47182fe2e6e740b8a76f82fe5c527d6ad548f805274f21792cf4047235b24fbf",
+ "timestamp": "1442424328",
+ "totalDifficulty": "1035061827427752845",
+ "alloc": {
+ "0x082d4cdf07f386ffa9258f52a5c49db4ac321ec6": {
+ "balance": "0xc820f93200f4000",
+ "nonce": "0x5E",
+ "code": "0x"
+ },
+ "0x332b656504f4eabb44c8617a42af37461a34e9dc": {
+ "balance": "0x11faea4f35e5af80000",
+ "code": "0x",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5": {
+ "balance": "0xbf681825be002ac452",
+ "nonce": "0x70FA",
+ "code": "0x"
+ },
+ "0x82effbaaaf28614e55b2ba440fb198e0e5789b0f": {
+ "balance": "0xb3d0ac5cb94df6f6b0",
+ "nonce": "0x1",
+ "code": "0x"
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "243826",
+ "difficulty": "6214212385501",
+ "timestamp": "1442424353",
+ "gasLimit": "3141592",
+ "miner": "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5"
+ },
+ "input": "0xf8e85e850ba43b7400830f42408080b89660606040527382effbaaaf28614e55b2ba440fb198e0e5789b0f600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055505b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b600a80608c6000396000f30060606040526008565b001ca0340b21661e5bb85a46319a15f33a362e5c0f02faa7cdbf9c5808b2134da968eaa0226e6788f8c20e211d436ab7f6298ef32fa4c23a509eeeaac0880d115c17bc3f",
+ "tracerConfig": {
+ "diffMode": true
+ },
+ "result": {
+ "pre": {
+ "0x082d4cdf07f386ffa9258f52a5c49db4ac321ec6": {
+ "balance": "0xc820f93200f4000",
+ "nonce": 94
+ },
+ "0x332b656504f4eabb44c8617a42af37461a34e9dc": {
+ "balance": "0x11faea4f35e5af80000",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5": {
+ "balance": "0xbf681825be002ac452",
+ "nonce": 28922
+ },
+ "0x82effbaaaf28614e55b2ba440fb198e0e5789b0f": {
+ "balance": "0xb3d0ac5cb94df6f6b0",
+ "nonce": 1
+ }
+ },
+ "post": {
+ "0x082d4cdf07f386ffa9258f52a5c49db4ac321ec6": {
+ "balance": "0xc7d4d88af8b4c00",
+ "nonce": 95
+ },
+ "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5": {
+ "balance": "0xbf681ce7c870aeb852"
+ },
+ "0x82effbaaaf28614e55b2ba440fb198e0e5789b0f": {
+ "balance": "0x1d37f515017a8eef6b0"
+ }
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/inner_create.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/inner_create.json
new file mode 100644
index 0000000000..9c0030a0a8
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/inner_create.json
@@ -0,0 +1,312 @@
+{
+ "genesis": {
+ "difficulty": "13707196986889",
+ "extraData": "0xd983010302844765746887676f312e342e328777696e646f7773",
+ "gasLimit": "3141592",
+ "hash": "0x607b38fe7e94427ee8f3b9a62375c67f953f8d49e05dbfd0145f9d3bac142193",
+ "miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069",
+ "mixHash": "0x98c74c9e76fd0078157e1696e4334a7e787396459693a84536d8b96414dafd5d",
+ "nonce": "0x77a5a0a73ad8745e",
+ "number": "1062502",
+ "stateRoot": "0x1df615df5fdbc8d5397bf3574f462f6d9696428eb8796d8e9252bccc8e3a8996",
+ "timestamp": "1456480432",
+ "totalDifficulty": "7948153536501153741",
+ "alloc": {
+ "0x0000000000000000000000000000000000000004": {
+ "balance": "0x0",
+ "code": "0x"
+ },
+ "0x1deeda36e15ec9e80f3d7414d67a4803ae45fc80": {
+ "balance": "0x0",
+ "code": "0x650200d2f18c7350606060405236156100c15760e060020a60003504630bd295e681146100c65780630fd1f94e1461017d5780630fee183d1461018c578063349501b7146101ad5780635054d98a146101c75780637c0278fc146101ef5780637e92656214610287578063a0943154146102f6578063a1873db61461030e578063a9d2293d14610355578063b5d0f16e146103ad578063c17e6817146103ce578063cc3471af1461046a578063da46be0a1461047a578063f55627531461052a575b610007565b6105d36004356024356044355b60006000600030915081600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040515191505080841080610173575081600160a060020a031663a06db7dc6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050506040518051906020015060ff16810184115b1561100d57610007565b6105d3600060f0610f6d61046e565b6105d3600435602435604435606435600081600202831015610ff657610fee565b6105d36004355b600081600014156109115750600161098f565b6105d36004355b6008810154600090819062010000900460ff16156105f257600691506105ec565b60408051602060248035600481810135601f81018590048502860185019096528585526105e5958135959194604494929390920191819084018382808284375094965050505050505060006004825103836001016000508181546001816001161561010002031660029004825481601f106108005782601f1061083a575b826008026101000360020a80910402828001178355610851565b6105e5600435602435604051600090600160a060020a038316907f398bd6b21ae4164ec322fb0eb8c2eb6277f36fd41903fbbed594dfe125591281908390a26007830154819010610e415760078301546005840154610e3f9162010000909104600160a060020a0316906103d8565b6105d3600435602435600060006000611064856101ce565b6105d36004356024356044356004830154600090819030908410156110e4577f4e4f545f454e4f5547485f47415300000000000000000000000000000000000091506112dd565b6105d35b60006000309050600a81600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040515160091901935050505b5090565b6105d36004356024355b60008282111561099e578183606402049050610998565b6105d36004356024355b600030600160a060020a0316318211156103fa57600160a060020a0330163191505b6000821115610994577389efe605e9ecbe22849cd85d5449cc946c26f8f36312c82bcc84846040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060206040518083038160008760325a03f2156100075750839250610998915050565b6105d35b6000600f610f6d610359565b6105e560043560243560443560643560843560088501805461ff00191661010017905584543090600090819081908190819060a060020a900460e060020a02811480156104db575060018b8101546002918116156101000260001901160481145b156109b3578a5460028c0154600160a060020a039190911690895a60405191900391906000818181858888f193505050508b60080160006101000a81548160ff02191690830217905550610bfa565b6105d36004355b6000600060006000309250600a83600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000757505060405151600919019350505081851115610eb05782600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050604051519450610ea89050565b60408051918252519081900360200190f35b005b600291505b50919050565b6008830154610100900460ff161561060d57600591506105ec565b30905080600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040515143610109011015905061066457600091506105ec565b80600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040515143600a01101590506106d3576005830154620100009004600160a060020a0316600014156105e757600191506105ec565b80600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000757505060405151431015905061072357600391506105ec565b80600160a060020a031663a06db7dc6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050506040518051906020015060ff1681600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050604051519190910143101590506107bf57600491506105ec565b600791506105ec565b5081800160010183558181151161085157601f016020900481601f016020900483600052602060002091820191016108519190610826565b82601f106107c8575082600052602060002080549082601f016020900481019061090691905b808211156103a95760008155600101610826565b60ff19168360005260206000205581800160010183555b5050505060048251111561090c575060005b6001838101546002918116156101000260001901160481101561090c57818160040181518110156100075790602001015160f860020a900460f860020a02836001016000508281546001816001161561010002031660029004811015610007578154600116156108e25790600052602060002090602091828204019190065b601f036101000a81548160ff0219169060f860020a84040217905550600101610863565b5061026d565b505050565b604080517f5f5f6469672875696e74323536290000000000000000000000000000000000008152815190819003600e01812060e060020a9081900481028190049081028252600019850160048301529151600160a060020a03301692916102bc86029160248281019260009291908290030181838887f19450505050505b919050565b5060005b92915050565b818360020203836064020460c8039050610998565b8a5460a060020a900460e060020a0260001415610a23578a5460028c0154600160a060020a039190911690895a03908d6001016000506040518082805460018160011615610100020316600290048015610ae55780601f10610aba57610100808354040283529160200191610ae5565b60018b8101546002918116156101000260001901160460001415610b1a578a5460028c0154600160a060020a039190911690895a03908d60000160149054906101000a900460e060020a0260e060020a900491906040518360e060020a028152600401809050600060405180830381858988f19450505050508b60080160006101000a81548160ff02191690830217905550610bfa565b820191906000526020600020905b815481529060010190602001808311610ac857829003601f168201915b5050915050600060405180830381858888f193505050508b60080160006101000a81548160ff02191690830217905550610bfa565b8a5460028c0154600160a060020a039190911690895a03908d60000160149054906101000a900460e060020a0260e060020a900491908e6001016000506040518460e060020a0281526004018082805460018160011615610100020316600290048015610bc85780601f10610b9d57610100808354040283529160200191610bc8565b820191906000526020600020905b815481529060010190602001808311610bab57829003601f168201915b5050915050600060405180830381858988f19450505050508b60080160006101000a81548160ff021916908302179055505b85600160a060020a031663938b5f326040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750505060405180519060200150600160a060020a031660405180807f75706461746544656661756c745061796d656e742829000000000000000000008152602001506016019050604051809103902060e060020a8091040260e060020a90046040518160e060020a0281526004018090506000604051808303816000876161da5a03f15050505060038b0154610cc8903a6103b7565b60058c0154909550620100009004600160a060020a03908116908a161415610cf65760068b01549350610d38565b85600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050604051519450505b6064858502048b6007016000505401925060648587600160a060020a031663625cc4656040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000757505050604051805190602001500204915060008b60070160005081905550865a8b03013a029050610db7898285016103d8565b9250610dd773d3cda913deb6f67967b99d67acdfa1712c293601836103d8565b6040805160088e01548482526020820187905281830184905260ff1660608201529051919350600160a060020a038b16917f4538b7ec91dae8fada01e66a052482086d3e690c3db5a80457fbcd55457b4ae19181900360800190a25050505050505050505050565b505b309050610e8c81600160a060020a031663ae45850b6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040515190316103d8565b505050600801805462ff0000191662010000179055565b600093505b505050919050565b600e19919091019081851115610f075782600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050604051519450610ea89050565b60ef19919091019081851115610ea357818503905060f08184600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050506040518051906020015002049350610ea8565b03905090565b6006860181905560058601805475ffffffffffffffffffffffffffffffffffffffff000019166201000087021790556007860184905560408051600160a060020a0387168152602081019290925280517fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a9281900390910190a15b949350505050565b610f7343610531565b600192505b50509392505050565b60108185031015610fff576005860154620100009004600160a060020a03166000148061105057506005860154620100009004600160a060020a03908116908616145b9250611004565b600092505b505092915050565b91503090506000821480156110c4575080600160a060020a031663ae45850b6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000757505060405151600160a060020a039081169086161490505b156110d2576001925061105c565b6007821415611057576001925061105c565b6008860154610100900460ff161561111e577f414c52454144595f43414c4c454400000000000000000000000000000000000091506112dd565b80600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050604051514310905080611206575080600160a060020a031663a06db7dc6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040805180517f0a16697a000000000000000000000000000000000000000000000000000000008252915160ff9092169291630a16697a9160048181019260209290919082900301816000876161da5a03f1156100075750506040515191909101431190505b15611233577f4e4f545f494e5f43414c4c5f57494e444f57000000000000000000000000000091506112dd565b61123e8686436100d3565b151561126c577f4e4f545f415554484f52495a454400000000000000000000000000000000000091506112dd565b6005860154600061ffff91909116118015611299575032600160a060020a031685600160a060020a031614155b80156112b4575060058601546112b29061ffff166101b4565b155b156112dd577f535441434b5f544f4f5f4445455000000000000000000000000000000000000091505b60008214610fff5760408051600160a060020a03871681526020810184905281517fdcb278834ca505ad219cf8e4b5d11f026080abef6ec68e249ea5e4d9bb3dc7b2929181900390910190a16000925061100456"
+ },
+ "0x2a65aca4d5fc5b5c859090a6c34d164135398226": {
+ "balance": "0x98e1c608601c2496b2",
+ "nonce": "218916",
+ "code": "0x"
+ },
+ "0x651913977e8140c323997fce5e03c19e0015eebf": {
+ "balance": "0x0",
+ "code": "0x",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000007": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000008": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000000000d": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000000000e": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b": {
+ "balance": "0x0",
+ "nonce": "237",
+ "code": "0x6060604052361561027c5760e060020a600035046301991313811461027e57806303d22885146102ca5780630450991814610323578063049ae734146103705780630ce46c43146103c35780630e85023914610602578063112e39a8146106755780631b4fa6ab146106c25780631e74a2d3146106d057806326a7985a146106fd5780633017fe2414610753578063346cabbc1461075c578063373a1bc3146107d55780633a9e74331461081e5780633c2c21a01461086e5780633d9ce89b146108ba578063480b70bd1461092f578063481078431461097e57806348f0518714610a0e5780634c471cde14610a865780634db3da8314610b09578063523ccfa814610b4f578063586a69fa14610be05780635a9f2def14610c3657806364ee49fe14610caf57806367beaccb14610d055780636840246014610d74578063795b9a6f14610dca5780637b55c8b514610e415780637c73f84614610ee15780638c0e156d14610f145780638c1d01c814610f605780638e46afa914610f69578063938c430714610fc0578063971c803f146111555780639772c982146111ac57806398c9cdf41461122857806398e00e541461127f5780639f927be7146112d5578063a00aede914611383578063a1c0539d146113d3578063aff21c6514611449578063b152f19e14611474578063b549793d146114cb578063b5b33eda1461154b578063bbc6eb1f1461159b578063c0f68859146115ab578063c3a2c0c314611601578063c43d05751461164b578063d8e5c04814611694578063dbfef71014611228578063e29fb547146116e7578063e6470fbe1461173a578063ea27a8811461174c578063ee77fe86146117d1578063f158458c14611851575b005b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503387876020604051908101604052806000815260200150612225610f6d565b61188260043560243560443560643560843560a43560c435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050338b8a6020604051908101604052806000815260200150896125196106c6565b611882600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503385600060e060020a026020604051908101604052806000815260200150611e4a610f6d565b611882600435602435604435606435608435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503389896020604051908101604052806000815260200150886124e86106c6565b604080516020604435600481810135601f8101849004840285018401909552848452611882948135946024803595939460649492939101918190840183828082843750506040805160a08082019092529597963596608435969095506101449450925060a491506005908390839080828437509095505050505050604080518082018252600160a060020a03338116825288166020820152815160c0810190925260009173e54d323f9ef17c1f0dede47ecc86a9718fe5ea349163e3042c0f91600191908a908a9089908b90808b8b9090602002015181526020018b60016005811015610002579090602002015181526020018b60026005811015610002579090602002015181526020018b60036005811015610002579090602002015181526020018b6004600581101561000257909060200201518152602001348152602001506040518860e060020a02815260040180888152602001876002602002808383829060006004602084601f0104600f02600301f150905001868152602001806020018560ff1681526020018461ffff168152602001836006602002808383829060006004602084601f0104600f02600301f1509050018281038252868181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d25780820380516001836020036101000a031916815260200191505b509850505050505050505060206040518083038160008760325a03f2156100025750506040515191506124cd9050565b60408051602060248035600481810135601f81018590048502860185019096528585526118829581359591946044949293909201918190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808787611e64610f6d565b611882600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333600060e060020a026020604051908101604052806000815260200150611d28610f6d565b61189f5b6000611bf8611159565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a881600060005054611a9561159f565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346326a7985a6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b6118b760075b90565b604080516020606435600481810135601f8101849004840285018401909552848452611882948135946024803595604435956084949201919081908401838280828437509496505093359350505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160013389898861224b610f6d565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503386866020604051908101604052806000815260200150611e64610f6d565b611882600435602435604435606435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333896020604051908101604052806000815260200150886123bc6106c6565b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503387866020604051908101604052806000815260200150611f8d610f6d565b60408051602060248035600481810135601f810185900485028601850190965285855261188295813595919460449492939092019181908401838280828437509496505093359350505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808888612225610f6d565b611882600435602435604435606435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503388886020604051908101604052806000815260200150612388610f6d565b611882600435604080517fc4144b2600000000000000000000000000000000000000000000000000000000815260016004820152600160a060020a03831660248201529051600091737c1eb207c07e7ab13cf245585bd03d0fa478d0349163c4144b26916044818101926020929091908290030181878760325a03f215610002575050604051519150611b409050565b604080516020604435600481810135601f81018490048402850184019095528484526118829481359460248035959394606494929391019181908401838280828437509496505093359350505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133888888612238610f6d565b604080516020604435600481810135601f810184900484028501840190955284845261188294813594602480359593946064949293910191819084018382808284375094965050933593505060843591505060a43560c435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338b8b8b896126536106c6565b611882600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333866020604051908101604052806000815260200150611e4a610f6d565b6118b76004355b604080517fed5bd7ea00000000000000000000000000000000000000000000000000000000815260016004820152600160a060020a03831660248201529051600091737c1eb207c07e7ab13cf245585bd03d0fa478d0349163ed5bd7ea916044818101926020929091908290030181878760325a03f215610002575050604051519150611b409050565b61189f600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463586a69fa6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b60408051602060248035600481810135601f81018590048502860185019096528585526118829581359591946044949293909201918190840183828082843750949650509335935050606435915050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808989612388610f6d565b61188260043560243560443560643560843560a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050338a896020604051908101604052806000815260200150886124d76106c6565b6040805160206004803580820135601f8101849004840285018401909552848452611882949193602493909291840191908190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808587611e4a610f6d565b61188260043560243560443560643560843560a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050338a8a60206040519081016040528060008152602001508961262d6106c6565b604080516020606435600481810135601f810184900484028501840190955284845261188294813594602480359560443595608494920191908190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338888876120c7610f6d565b604080516020604435600481810135601f81018490048402850184019095528484526118829481359460248035959394606494929391019181908401838280828437505060408051608080820190925295979635969561010495509350608492508591508390839080828437509095505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338989898961263a6106c6565b6118b7600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a881858585611ba361122c565b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050333388602060405190810160405280600081526020015061236e610f6d565b6118b760005481565b6118c95b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea34638e46afa96040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b60408051602060248035600481810135601f8101859004850286018501909652858552611882958135959194604494929390920191819084018382808284375094965050933593505060643591505060843560a43560c43560e43561010435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600160005033338e8e8d8f8e8e8e8e8e346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156111195780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519b9a5050505050505050505050565b61189f5b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463971c803f6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b604080516020604435600481810135601f8101849004840285018401909552848452611882948135946024803595939460649492939101918190840183828082843750949650509335935050608435915050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338989896123a2610f6d565b6118b75b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346398c9cdf46040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346398e00e546040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b611882600435604080517fe6ce3a6a000000000000000000000000000000000000000000000000000000008152600160048201527f3e3d0000000000000000000000000000000000000000000000000000000000006024820152604481018390529051600091737c1eb207c07e7ab13cf245585bd03d0fa478d0349163e6ce3a6a916064818101926020929091908290030181878760325a03f215610002575050604051519150611b409050565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503385600060e060020a0260206040519081016040528060008152602001506121ef610f6d565b604080516020604435600481810135601f8101849004840285018401909552848452611882948135946024803595939460649492939101918190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338787876120b5610f6d565b6118b7600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a88183611b4561159f565b6118b75b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463b152f19e6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b60408051602060248035600481810135601f8101859004850286018501909652858552611882958135959194604494929390920191819084018382808284375094965050933593505060643591505060843560a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808b8b8961262d6106c6565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503386600060e060020a026020604051908101604052806000815260200150612200610f6d565b6118b75b60005460649004610759565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463c0f688596040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b611882600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333600060e060020a026020604051908101604052806000815260200150611bff610f6d565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333876020604051908101604052806000815260200150612200610f6d565b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503387600060e060020a026020604051908101604052806000815260200150612213610f6d565b611882600435602435604435606435608435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600160005033338a60206040519081016040528060008152602001508961250c6106c6565b61027c6000600060006118e033610b56565b6118b7600435602435604435606435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a881868686866040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b949350505050565b604080516020604435600481810135601f810184900484028501840190955284845261188294813594602480359593946064949293910191819084018382808284375094965050933593505060843591505060a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338a8a8a886124fa6106c6565b6118b7600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a88184846000611b4f61122c565b60408051600160a060020a03929092168252519081900360200190f35b6040805161ffff929092168252519081900360200190f35b60408051918252519081900360200190f35b6040805160ff929092168252519081900360200190f35b15611a905733925082600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040805180517fc6803622000000000000000000000000000000000000000000000000000000008252915191945063c680362291600482810192602092919082900301816000876161da5a03f11561000257505060405151905080156119d1575082600160a060020a031663d379be236040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060405151600160a060020a03166000141590505b80156119dd5750600082115b80156119ec5750600054600190115b15611a90578183600160a060020a031663830953ab6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040515160640291909104915050604281118015611a4d5750600054829011155b15611a675760008054612710612711909102049055611a90565b602181108015611a7a5750600054829010155b15611a90576000805461271061270f9091020490555b505050565b6000611a9f61122c565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f2156100025750506040515191506107599050565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b919050565b6000611af261122c565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b92915050565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b9392505050565b9050610759565b611c076106c6565b6000611c11611478565b611c1961122c565b600054611c2461159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611cf25780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f2156100025750506040515191506107599050565b611d306106c6565b60008b611d3b61122c565b600054611d4661159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611e145780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150611b409050565b611e526106c6565b6000611e5c611478565b611d3b61122c565b611e6c6106c6565b6000611e76611478565b611e7e61122c565b600054611e8961159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611f575780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150611b9d9050565b611f956106c6565b8b611f9e611478565b611fa661122c565b600054611fb161159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561207f5780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150611bf19050565b6120bd6106c6565b6000611f9e611478565b6120cf6106c6565b8b6120d8611478565b6120e061122c565b6000546120eb61159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156121b95780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f2156100025750506040515191506117c99050565b6121f76106c6565b8b611e76611478565b6122086106c6565b60008b611e7e61122c565b61221b6106c6565b8a8c611fa661122c565b61222d6106c6565b60008b611fa661122c565b6122406106c6565b60008b6120e061122c565b6122536106c6565b8c8b61225d61122c565b60005461226861159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156123365780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f21561000257505060405151979650505050505050565b6123766106c6565b60008c8c600060005054611fb161159f565b6123906106c6565b60008c8c6000600050546120eb61159f565b6123aa6106c6565b60008c8c60006000505461226861159f565b60008d8d6000600050546120eb61159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561249c5780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150505b9695505050505050565b8e8d8d6000600050546123ce61159f565b60008d8d60006000505461226861159f565b60008d8d6000600050546123ce61159f565b60008e8e8d61226861159f565b8f8e8e8d61252561159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156125f35780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519998505050505050505050565b60008e8e8d6123ce61159f565b8a5160208c015160408d015160608e015161226861159f565b60008e8e8d61252561159f56",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000011f8119429ed3a",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000f5d861791e76fa01433e0d7421aee565290e4afe",
+ "0x031b9ec274101cc3ccff4d6d98ef4513742dadbaadba538bff48b88403253234": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0x20ef51bb8ea9e8e8d5e2c17d28e47285698893c1017db4b4e40b792358a3dbc7": "0x0000000000000000000000000000000000000000000000000000000000000004",
+ "0x26cba0705aade77fa0f9275b68d01fb71206a44abd3a4f5a838f7241efbc8abd": "0x000000000000000000000000c9a2bfd279fe57e7651e5d9f29bb1793c9a1cf01",
+ "0x26cba0705aade77fa0f9275b68d01fb71206a44abd3a4f5a838f7241efbc8abf": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230",
+ "0x26cba0705aade77fa0f9275b68d01fb71206a44abd3a4f5a838f7241efbc8ac2": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794dfb": "0x000000000000000000000000f5d861791e76fa01433e0d7421aee565290e4afe",
+ "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794dfc": "0x00000000000000000000000000000000000000000000000000000000000f6897",
+ "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794dfd": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794dfe": "0x0000000000000000000000002859ddf2877c46d54e67b6becdb1cafb8ef4a458",
+ "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794dff": "0x000000000000000000000000b7df3c43a8b13ecf45777c267404e15c7cdb04c9",
+ "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794e00": "0x0000000000000000000000000000000000000000000000000000000000000008",
+ "0x3b20a4b931bc4ae9450774ee52b8f5da1b248d23e61cd20c09b25662f73894fd": "0x0000000000000000000000000000000000000000000000000000000000000006",
+ "0x3b99aee1e3090227401ac2055c861246ca6ec62f426b4b4d74df88510f841b89": "0x0000000000000000000000000000000000000000000000000000000000000007",
+ "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef711": "0x000000000000000000000000a4d91b341f0e9a7000be916a668408b463f4c38c",
+ "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef712": "0x0000000000000000000000000000000000000000000000000000000000102ce9",
+ "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef713": "0x000000000000000000000000fd97a0d81cc92eecd52452831930b27889925ef0",
+ "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef714": "0x00000000000000000000000016917c151bb1399852a0741eb7b317b443e2cfa3",
+ "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef715": "0x000000000000000000000000d7b0e93fa8386b17fb5d1cf934076203dcc122f3",
+ "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef716": "0x0000000000000000000000000000000000000000000000000000000000000004",
+ "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a3fe": "0x000000000000000000000000c5ef24ec3bf0e3522cfc8e53f3e076b043547ce1",
+ "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a3ff": "0x00000000000000000000000000000000000000000000000000000000000fff67",
+ "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a400": "0x000000000000000000000000b7df3c43a8b13ecf45777c267404e15c7cdb04c9",
+ "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a401": "0x00000000000000000000000010fc2e8ba5f40336c3576ffaa25177f1cdedf836",
+ "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a402": "0x000000000000000000000000fd97a0d81cc92eecd52452831930b27889925ef0",
+ "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a403": "0x0000000000000000000000000000000000000000000000000000000000000006",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5ba": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bb": "0x000000000000000000000000000000000000000000000000000000000010347b",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bc": "0x000000000000000000000000d7b0e93fa8386b17fb5d1cf934076203dcc122f3",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bd": "0x000000000000000000000000c9a2bfd279fe57e7651e5d9f29bb1793c9a1cf01",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5be": "0x000000000000000000000000741467b251fca923d6229c4b439078b55dca233b",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bf": "0x0000000000000000000000000000000000000000000000000000000000000002",
+ "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2751": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2752": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2753": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2754": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2755": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2756": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826a7": "0x000000000000000000000000b7df3c43a8b13ecf45777c267404e15c7cdb04c9",
+ "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826a8": "0x00000000000000000000000000000000000000000000000000000000000fe13d",
+ "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826a9": "0x000000000000000000000000f5d861791e76fa01433e0d7421aee565290e4afe",
+ "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826aa": "0x00000000000000000000000063110531142fb314118164ff579ba52746504408",
+ "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826ab": "0x000000000000000000000000c5ef24ec3bf0e3522cfc8e53f3e076b043547ce1",
+ "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826ac": "0x0000000000000000000000000000000000000000000000000000000000000007",
+ "0xac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c890780": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xccd2cbc946692be8ade97db99353304e3af0fa6202f93649d4e185ad8b1f385c": "0x0000000000000000000000000000000000000000000000000000000000000004",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4ef": "0x000000000000000000000000d7b0e93fa8386b17fb5d1cf934076203dcc122f3",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f0": "0x00000000000000000000000000000000000000000000000000000000001030b3",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f1": "0x000000000000000000000000a4d91b341f0e9a7000be916a668408b463f4c38c",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f2": "0x000000000000000000000000dd87a67740c2acf48a31829783a095a81c3628d9",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f3": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f4": "0x0000000000000000000000000000000000000000000000000000000000000003",
+ "0xdabde47554d6a6cfcff3c968abb145f298585fafa9e24c10fc526269794bd626": "0x0000000000000000000000000000000000000000000000000000000000000003",
+ "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64db7": "0x000000000000000000000000741467b251fca923d6229c4b439078b55dca233b",
+ "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64db8": "0x000000000000000000000000000000000000000000000000000000000010365c",
+ "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64db9": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230",
+ "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64dba": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64dbb": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64dbc": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bdec": "0x000000000000000000000000fd97a0d81cc92eecd52452831930b27889925ef0",
+ "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bded": "0x0000000000000000000000000000000000000000000000000000000000101dc2",
+ "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bdee": "0x000000000000000000000000c5ef24ec3bf0e3522cfc8e53f3e076b043547ce1",
+ "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bdef": "0x000000000000000000000000173243e117a6382211b1ac91eeb262f4a7021c16",
+ "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bdf0": "0x000000000000000000000000a4d91b341f0e9a7000be916a668408b463f4c38c",
+ "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bdf1": "0x0000000000000000000000000000000000000000000000000000000000000005"
+ }
+ },
+ "0x741467b251fca923d6229c4b439078b55dca233b": {
+ "balance": "0x29c613529e8218f8",
+ "code": "0x606060405236156101a05760e060020a60003504630924120081146101c25780630a16697a146101cf5780630fd1f94e146101d8578063137c638b1461022e57806321835af61461023b57806324032866146102545780632f95b833146102d65780633017fe24146102e55780633233c686146102ef57806337f4c00e146102fa5780634500054f146103055780634e417a98146103785780634e71d92d146103e15780634f059a43146103f35780636146195414610451578063625cc4651461046157806367ce940d1461046a5780637d298ee314610477578063830953ab146104f9578063938b5f321461050457806395ee122114610516578063974654f41461052a578063a06db7dc14610535578063a9d2293d14610541578063ae45850b14610597578063b0f07e44146105a9578063c19d93fb146105cb578063c6502da81461062e578063c680362214610637578063ca94692d1461064a578063cc3471af14610673578063d379be23146106c9578063d62457f6146106e3578063ea8a1af0146106ee578063f5562753146107f3578063f6b4dfb414610854575b610868600080548190600160a060020a03908116339091161461087a57610994565b610868600b5460ff165b90565b610868600d5481565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630fd1f94e6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6108685b62012cc86101cc565b61086860043560008160001415610dc65750600161084f565b6108686004356024356000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630bd295e6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f215610002575050604051519150505b92915050565b61099860085461ffff166101cc565b61086860026101cc565b610868600a546101cc565b6108686006546101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a09431546003600050336040518360e060020a0281526004018083815260200182600160a060020a031681526020019250505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6109af60408051602081810183526000825282516004805460026001821615610100026000190190911604601f81018490048402830184019095528482529293909291830182828015610a7d5780601f10610a5257610100808354040283529160200191610a7d565b61086860006000600180610b7b6105cf565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753436040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1d6000600480610c986105cf565b61086860025481565b6108685b620186a06101cc565b6108686004356024355b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a1873db6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f2156100025750506040515191506102d09050565b6108686009546101cc565b610a1f600c54600160a060020a031681565b610868600b5462010000900460ff166101cc565b6108686007546101cc565b610a3c600e5460ff1681565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a9d2293d6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600054600160a060020a031681565b610868600080548190600160a060020a039081163390911614610a8957610994565b6108685b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc80635054d98a60036000506040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b61086860015481565b610868600b54610100900460ff166101cc565b61086860035474010000000000000000000000000000000000000000900460e060020a026101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063cc3471af6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600854620100009004600160a060020a03166101cc565b6108686005546101cc565b610a1d604080517fa09431540000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc809163a0943154916044808301926020929190829003018160008760325a03f215610002575050604051511590506107f157604080517f7e9265620000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091637e9265629160448083019260009291908290030181838760325a03f215610002575050505b565b6108686004356000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753836040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f215610002575050604051519150505b919050565b610a1f600354600160a060020a03166101cc565b60408051918252519081900360200190f35b60045460006002600183161561010002600019019092169190910411156108a45760009150610994565b6108ac6105cf565b9050600081141580156108c0575060018114155b80156108cd575060028114155b156108db5760009150610994565b600480546000828152602060026001841615610100026000190190931692909204601f908101929092047f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b9081019236929083901061095d5782800160ff198235161785555b5061098d9291505b808211156109945760008155600101610949565b82800160010185558215610941579182015b8281111561094157823582600050559160200191906001019061096f565b5050600191505b5090565b6040805161ffff9092168252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610a0f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b005b60408051600160a060020a03929092168252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b815481529060010190602001808311610a6057829003601f168201915b505050505090506101cc565b6004546000600260018316156101000260001901909216919091041115610ab35760009150610994565b610abb6105cf565b905060008114158015610acf575060018114155b8015610adc575060028114155b15610aea5760009150610994565b604080517f7c0278fc000000000000000000000000000000000000000000000000000000008152600360048201818152602483019384523660448401819052731deeda36e15ec9e80f3d7414d67a4803ae45fc8094637c0278fc946000939190606401848480828437820191505094505050505060006040518083038160008760325a03f215610002575050505090565b1415610c8557604080516001547f0fee183d0000000000000000000000000000000000000000000000000000000082526003600483015233600160a060020a0316602483015234604483015260648201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091630fee183d916084828101926020929190829003018160008760325a03f21561000257505060405151925050811515610c8a577389efe605e9ecbe22849cd85d5449cc946c26f8f36312c82bcc33346040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515115159050610c8a57610002565b505090565b81925050610994565b505b50565b1415610c93575a9150610cab3383610481565b1515610cb75750610c95565b731deeda36e15ec9e80f3d7414d67a4803ae45fc8063da46be0a60038433610cdd61046e565b610ce5610232565b6040518660e060020a0281526004018086815260200185815260200184600160a060020a031681526020018381526020018281526020019550505050505060006040518083038160008760325a03f21561000257505050610c933360408051600080547fc17e6817000000000000000000000000000000000000000000000000000000008352600160a060020a03908116600484015230163160248301529151731deeda36e15ec9e80f3d7414d67a4803ae45fc809263c17e68179260448082019360209390928390039091019082908760325a03f2156100025750505050565b30600160a060020a031660405180807f5f5f6469672875696e7432353629000000000000000000000000000000000000815260200150600e019050604051809103902060e060020a8091040260e060020a9004600184036040518260e060020a0281526004018082815260200191505060006040518083038160008760325a03f292505050151561084f5761000256",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000007dd677b54fc954824a7bc49bd26cbdfa12c75adf",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000011f79bd42b0c7c",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x00000000000000000000000000000000000000000000000000002dfeff8fca5d",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000003defb9627dd677b54fc954824a7bc49bd26cbdfa12c75adf",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000ba43b7400",
+ "0x0000000000000000000000000000000000000000000000000000000000000007": "0x00000000000000000000000000000000000000000000000000000000001e8480",
+ "0x0000000000000000000000000000000000000000000000000000000000000008": "0x000000000000000000000000000000000000000000000000000000000000000a",
+ "0x000000000000000000000000000000000000000000000000000000000000000a": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000000000b": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0000000000000000000000006c8f2a135f6ed072de4503bd7c4999a1a17f824b",
+ "0x000000000000000000000000000000000000000000000000000000000000000d": "0x000000000000000000000000000000000000000000000000000000000010365c",
+ "0x000000000000000000000000000000000000000000000000000000000000000e": "0x00000000000000000000000000000000000000000000000000000000000000ff"
+ }
+ },
+ "0x7c1eb207c07e7ab13cf245585bd03d0fa478d034": {
+ "balance": "0x0",
+ "code": "0x650200d2f18c7350606060405236156100a05760e060020a60003504630e9f1a3c81146100a55780632b4096b4146100c95780636ec13982146100eb578063a3119e571461010d578063a749f19b1461012f578063ab7366f714610151578063bacd69581461017f578063bfdf87c0146101c2578063c4144b26146101e1578063caa46c9c1461023c578063e6ce3a6a14610297578063ed5bd7ea146102b6575b610007565b6102d960043560243560008181526001830160205260409020600401545b92915050565b6102d960043560243560008181526001830160205260409020600301546100c3565b6102d960043560243560008181526001830160205260409020600201546100c3565b6102d960043560243560008181526001838101602052604090912001546100c3565b6102d960043560243560008181526001830160205260409020600501546100c3565b6102eb6004356024355b600081815260018301602052604081208054829182918291908614610790576101b9565b6102eb600435602435604435600082815260018401602052604081205481908190819086141561068a576040812060010154851415610680575b50505050505050565b6102d960043560243560008181526001830160205260409020546100c3565b6102d96004356024355b6040805160c08101825260008082526020828101829052828401829052606083018290526080830182905260a08301829052848252600186019052918220805490919083908114156102fb576102f2565b6102d96004356024355b6040805160c08101825260008082526020828101829052828401829052606083018290526080830182905260a08301829052848252600186019052918220805490919083908114156104c0576102f2565b6102d960043560243560443582546000908181811415610a6557610a8c565b6102d96004356024356000818152600183016020526040812060050154116100c3565b60408051918252519081900360200190f35b005b815193505b50505092915050565b60048301546000146103d257600483810154600090815260018881016020908152604092839020835160c081018552815481529281015491830191909152600281015492820192909252600382015460608201529181015460808301526005015460a082015291505b60608201516000146102ed57606091820151600090815260018781016020908152604092839020835160c081018552815481529281015491830191909152600281015492820192909252600382015493810193909352600481015460808401526005015460a0830152610364565b600283015460001461045b5750506002810154600081815260018681016020908152604092839020835160c081018552865481529286015491830191909152918101929092526003830154606083015260048301546080830152600583015460a08301525b81516003820154141561044d57805493506102f2565b600281015460001415610464575b600093506102f2565b6040805160c08101825282548152600183810154602083810191909152600285015483850181905260038601546060850152600486015460808501526005959095015460a0840152600094855290890190529120909150610437565b600383015460001461059757600383810154600090815260018881016020908152604092839020835160c081018552815481529281015491830191909152600281015492820192909252918101546060830152600481015460808301526005015460a082015291505b60808201516000146102ed57608091820151600090815260018781016020908152604092839020835160c081018552815481529281015491830191909152600281015492820192909252600382015460608201526004820154938101939093526005015460a0830152610529565b600283015460001461045b5750506002810154600081815260018681016020908152604092839020835160c081018552865481529286015491830191909152918101929092526003830154606083015260048301546080830152600583015460a08301525b81516004820154141561061257805493506102f2565b6002810154600014156106245761045b565b6040805160c08101825282548152600183810154602083810191909152600285015483850181905260038601546060850152600486015460808501526005959095015460a08401526000948552908901905291209091506105fc565b61068a878761015b565b86546000925082141561069b578587555b508554600090815260018701602052604090205b8054600014156107255785815560028101829055600181018590556101b987875b60008181526001830160205260408120905b8154610d8e9085905b60008181526001830160205260408082206004810154835281832060059081015460038301548552929093209092015403905b5092915050565b60018101548154925085126107625760048101546000141561074957600481018690555b60040154600090815260018701602052604090206106af565b60038101546000141561077757600381018690555b60030154600090815260018701602052604090206106af565b600381015460001415806107a957506004810154600014155b156107cf576003810154600014610826578054600188019060009061083b908a90610246565b6002810154600014610a285760028101546000908152600188016020526040902060038101548254919550141561080857600060038501555b60048401548154141561081d57600060048501555b83549150610a2d565b80546001880190600090610852908a906101eb565b815260208101919091526040016000209450610865565b8152602081019190915260400160002094505b600285015460009081526001880160205260409020600381015486549195509092508214156108b9576004850154600385018190556000146108b95760048501546000908152604090208454600282015592505b60048401548554141561091357600385015460048501819055600014610913578660010160005060008660030160005054815260200190815260200160002060005092508250836000016000505483600201600050819055505b60028082015490860181905560001461098457866001016000506000826002016000505481526020019081526020016000206000509350835080600001600050548460030160005054141561096a57845460038501555b60048401548154141561097f57845460048501555b610989565b845487555b6003818101549086018190556000146109d6578660010160005060008260030160005054815260200190815260200160002060005092508250846000016000505483600201600050819055505b600481810154908601819055600014610a23578660010160005060008260040160005054815260200190815260200160002060005092508250846000016000505483600201600050819055505b610a2d565b600087555b6000808255600182018190556002820181905560038201819055600482018190556005820181905582146101b9576101b987836106d0565b50600081815260018601602052604090205b6001810154610a95908686610ad4565b805492505b50509392505050565b15610b915760fa60020a600f02851480610ab6575060f060020a613c3d0285145b15610af157600481015460001415610b3a5780549250610a8c565b86865b600060f960020a601f02831415610ce357508083135b9392505050565b60f960020a601f02851480610b0d575060f060020a613e3d0285145b80610b1f575060f060020a613d3d0285145b15610b9157600381015460001415610bc85780549250610a8c565b610b73610ad1878360040160005054600081815260018301602052604081205b600381015460001415610d61576001810154915061071e565b15610a87576004015460009081526001860160205260409020610a77565b60fa60020a600f02851480610bad575060f060020a613c3d0285145b15610c1f57600381015460001415610c565760009250610a8c565b610c01610ad1878360030160005054600081815260018301602052604081205b600481015460001415610d48576001810154915061071e565b15610a87576003015460009081526001860160205260409020610a77565b60f960020a601f02851480610c3b575060f060020a613e3d0285145b15610c6f57600481015460001415610ca25760009250610a8c565b6003015460009081526001860160205260409020610a77565b60f060020a613d3d02851415610cde57600181015484901215610cbb57600481015460001415610ca25760009250610a8c565b6004015460009081526001860160205260409020610a77565b600181015484901315610cde57600381015460001415610c565760009250610a8c565b610a77565b60fa60020a600f02831415610cfb5750808312610aea565b60f060020a613e3d02831415610d15575080831215610aea565b60f060020a613c3d02831415610d2f575080831315610aea565b60f060020a613d3d028314156100a05750828114610aea565b6004015460009081526001840160205260409020610be8565b6003015460009081526001840160205260409020610b5a565b600282015460001415610fbd575b50505050565b90508060021415610e2657610daa8483600301600050546106eb565b6000191415610dc457610dc4848360030160005054610dfe565b8154610e269085905b60008181526001830160205260408120600381015490919081908190811415610ffb57610007565b8154610e5a9085905b60008181526001830160205260408120600481015490919081908190811415610e7f57610007565b806001191415610e5a57610e418483600401600050546106eb565b60011415610df557610df5848360040160005054610dcd565b8060001913158015610e6d575060018113155b15610d7a578154610d7a908590610f7a565b6004840180546000908152600188016020526040812060028088015490820181905592829055945014610f0f57856001016000506000856002016000505481526020019081526020016000206000509150836000016000505482600301600050541415610efa57826000016000505482600301600050819055505b835460048301541415610f0f57825460048301555b6003830154600014610f40575060038201546000908152600186016020526040902080546004850155835460028201555b82546002808601919091558454600385015583015460001415610f7157826000016000505486600001600050819055505b8354610fe69087905b6000818152600183016020526040808220600381015483528183206005908101546004830154855292842001549092610fd99291908183106110fa5750816100c3565b60029091015460009081526001840160205260409020906106e2565b6001016005820155505050565b8254610ff3908790610f7a565b505050505050565b600384018054600090815260018801602052604081206002808801549082018190559282905594501461108b5785600101600050600085600201600050548152602001908152602001600020600050915083600001600050548260030160005054141561107657826000016000505482600301600050819055505b83546004830154141561108b57825460048301555b60048301546000146110bd57506004820154600081815260018701602052604090206003850191909155835460028201555b82546002808601919091558454600485015583015460001415610f7157826000016000505486600001600050819055508354610fe6908790610f7a565b50806100c356"
+ },
+ "0x7dd677b54fc954824a7bc49bd26cbdfa12c75adf": {
+ "balance": "0xd7a58f5b73b4b6c4",
+ "code": "0x606060405236156100985760e060020a60003504633896002781146100e15780633defb962146100ea5780633f4be8891461010c5780634136aa351461011f5780634a420138146101a057806369c1a7121461028c5780638129fc1c146102955780638da5cb5b146102a6578063ae45850b146102b8578063af3309d8146102cc578063ea8a1af0146102d5578063ead50da3146102f4575b610308671bc16d674ec8000030600160a060020a03163110156100df57600554604051600160a060020a03918216916000913091909116319082818181858883f150505050505b565b61030a60005481565b610308671bc16d674ec8000030600160a060020a031631101561040f576100df565b61031c600454600160a060020a03165b90565b61030a5b600080548190118015610199575060408051600480547f0a16697a0000000000000000000000000000000000000000000000000000000083529251600160a060020a039390931692630a16697a928083019260209291829003018187876161da5a03f1156100025750506040515160ff01431090505b905061011c565b6103085b600354600554604080517f8c0e156d0000000000000000000000000000000000000000000000000000000081527f3defb96200000000000000000000000000000000000000000000000000000000600482015260a060020a90920461ffff1643016024830152621e8480604483015251600092600160a060020a031691638c0e156d916729a2241af62c000091606481810192602092909190829003018185886185025a03f1156100025750506040515192600160a060020a0384161491506102899050576004805473ffffffffffffffffffffffffffffffffffffffff1916821790555b50565b61030a60015481565b61030860008054146103f2576100df565b61031c600554600160a060020a031681565b61031c600354600160a060020a031661011c565b61030a60025481565b610308600554600160a060020a03908116339091161461035157610002565b61033960055460a060020a900461ffff1681565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b6040805161ffff929092168252519081900360200190f35b6004546000600160a060020a03919091163111156103c75760408051600480547fea8a1af00000000000000000000000000000000000000000000000000000000083529251600160a060020a03939093169263ea8a1af0928083019260009291829003018183876161da5a03f115610002575050505b600554604051600160a060020a03918216916000913091909116319082818181858883f15050505050565b426000556100df6101a4565b600280546001908101909155429055565b600454600160a060020a03908116339091161461042b576100df565b610433610123565b151561043e576100df565b6103fe6101a456",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000056be5b99",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000056d0009b",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000008b",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000006c8f2a135f6ed072de4503bd7c4999a1a17f824b",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x000000000000000000000000741467b251fca923d6229c4b439078b55dca233b",
+ "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000001e0d3cda913deb6f67967b99d67acdfa1712c293601"
+ }
+ },
+ "0x89efe605e9ecbe22849cd85d5449cc946c26f8f3": {
+ "balance": "0x0",
+ "code": "0x650200d2f18c73506060604052361561007f5760e060020a600035046312c82bcc81146100845780635548c837146100a55780635c54305e146101015780636b103966146101555780637fcf532c14610189578063b1df3d80146101d5578063b5bc6dbb146101ee578063c6ab451414610225578063e62af6c114610293575b610007565b6102c56004356024356000620186a05a10156103855761030083835a610232565b6102d760043560243560443581600160a060020a031683600160a060020a03167f47a08955ce2b7f21ea62ff0024e1ea0ad87430953554a87e6bc65d777f18e639836040518082815260200191505060405180910390a3505050565b6102d760043560243560443560408051838152602081018390528151600160a060020a038616927f9b24879829bed3003de08d5c5d7e18dcbb8dc76faebd95cafc5d4dec8c61a3a5928290030190a2505050565b6102d76004356024356044355b600160a060020a03821660009081526020849052604090205480820110156102d957610007565b6102d7600435602435604080518281529051600160a060020a038416917fd0c5cf41ee8ebf084ad0bce53de7cbc6e4693d9b53a4019ca36a2f91cdc20b3a919081900360200190a25050565b6102c560043560243560443560006102fc848484610162565b6102c5600435602435604435600160a060020a03821660009081526020849052604081205482901061032b576103338484846102a0565b6102c56004356024356044355b60006000831180156102605750604051600160a060020a038516908290859082818181858883f19350505050155b156102fc57604051600160a060020a03851690839085906000818181858888f1935050505015156102fc57506000610300565b6102d76004356024356044355b600160a060020a03821660009081526020849052604090205481111561030757610007565b60408051918252519081900360200190f35b005b600160a060020a0382166000908152602084905260409020805482019055505050565b5060015b9392505050565b600160a060020a038216600090815260208490526040902080548290039055505050565b506000610300565b604051600160a060020a03841690600090849082818181858883f1935050505015156102fc57604051600160a060020a038416908390600081818185876185025a03f19250505015156102fc57610007565b6103008383620186a061023256"
+ },
+ "0xb834e3edfc1a927bdcecb67a9d0eccbd752a5bb3": {
+ "balance": "0xffe9b09a5c474dca",
+ "nonce": "975",
+ "code": "0x"
+ },
+ "0xd3cda913deb6f67967b99d67acdfa1712c293601": {
+ "balance": "0x4f5807198e238f13e",
+ "nonce": "283",
+ "code": "0x"
+ },
+ "0xe54d323f9ef17c1f0dede47ecc86a9718fe5ea34": {
+ "balance": "0x0",
+ "code": "0x650200d2f18c7350606060405236156100ab5760e060020a600035046326a7985a81146100b057806350d4e411146100be57806354fd4d501461023d578063586a69fa1461025d5780638e46afa91461026857806396cff3df14610272578063971c803f1461029657806398c9cdf4146102a157806398e00e54146102ae578063b152f19e146102b8578063c0f68859146102c4578063e3042c0f146102cf578063ea27a88114610461575b610007565b6102845b60006104cb6102a5565b604080516020601f60843560048181013592830184900484028501840190955281845261047f948035946024803595604435956064359560a494930191819084018382808284375094965050933593505060c43591505060e435610104356101243561014435610164356101843560006101806040519081016040528060008152602001600081526020016000815260200160206040519081016040528060008152602001508152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200150610180604051908101604052808f81526020018e81526020018d81526020018c81526020018981526020018b81526020018a81526020018881526020018781526020018681526020018581526020018481526020015090506104d48f825b600060006000600a43018460e0015110156105de577f544f4f5f534f4f4e0000000000000000000000000000000000000000000000009150610524565b604080516000808252600760208301528183015290519081900360600190f35b61049c5b6103e85b90565b6104b460ff610265565b62030d403a0260026024356004350102015b60408051918252519081900360200190f35b61049c5b600a610265565b6102845b62030d40610265565b6102846010610265565b61028443600a01610265565b6102845b6020610265565b60408051808201825261047f916004803592909160649190602490600290839083908082843780516020601f608435808c01359182018390048302840183019094528083529499983598975060a49650909450910191908190840183828082843750506040805160c0818101909252959796359660c435969095506101a49450925060e491506006908390839080828437509095505050505050604080516101808181018352600080835260208381018290528385018290528451908101855281815260608401526080830181905260a0830181905260c0830181905260e0830181905261010083018190526101208301819052610140830181905261016083018190528351918201909352808984505181526020018960015060209081015182528101899052604081018890526060018484505181526020810187905260408101869052606001846001506020908101518252018460025060400151815260200184600350606001518152602001846004506080015181526020018460055060a00151905290506104e78982610200565b6102846004356024356044356064355b3a0291909201600202010190565b60408051600160a060020a03929092168252519081900360200190f35b6040805161ffff929092168252519081900360200190f35b6040805160ff929092168252519081900360200190f35b45039050610265565b9f9e505050505050505050505050505050565b9998505050505050505050565b8461016001511015610524577f494e53554646494349454e545f46554e4453000000000000000000000000000091505b600082146106ed576040805185518482529151600160a060020a0392909216917f513485fc54ef019ef1bc1ea683ef7d5d522f2865224ae10871ff992749c0ba4f9181900360200190a27389efe605e9ecbe22849cd85d5449cc946c26f8f36312c82bcc85600001518661016001516040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060206040518083038160008760325a03f215610007575050505b505092915050565b8360c0015161ffff166105ef61029a565b61ffff1611806106115750610602610261565b61ffff168460c0015161ffff16115b1561063e577f535441434b5f434845434b5f4f55545f4f465f52414e474500000000000000009150610524565b6106466102c8565b8460a0015160ff16101561067c577f47524143455f544f4f5f53484f525400000000000000000000000000000000009150610524565b6106846102a5565b84610100015110806106a157506106996100b4565b846101000151115b156106ce577f52455155495245445f4741535f4f55545f4f465f52414e4745000000000000009150610524565b6104f48461012001518561014001518660800151876101000151610471565b83610160015184600001518560e001518660a001518760200151886040015189606001518a608001518b61010001518c60c001518d61012001518e6101400151604051611078806108fa833901808c600160a060020a031681526020018b81526020018a60ff16815260200189600160a060020a03168152602001888152602001806020018781526020018681526020018561ffff1681526020018481526020018381526020018281038252888181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156107ec5780820380516001836020036101000a031916815260200191505b509c505050505050505050505050506040518091039082f090509050737c1eb207c07e7ab13cf245585bd03d0fa478d03463bacd69588683600160a060020a031660010284600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000757505050604051805190602001506040518460e060020a02815260040180848152602001838152602001828152602001935050505060006040518083038160008760325a03f21561000757505060408051600160a060020a038416815290517f2b05d346f0b0b9fd470024751c52d3b5dac5c37796f077c1a66241f2eada44b792509081900360200190a18092506105d656606060405260405161107838038061107883398101604052805160805160a05160c05160e05161010051610120516101405161016051610180516101a051999a98999798969795969490940194929391929091908a84848a8a8a8a88886101008051600c8054600160a060020a031990811633179091556000805482168d1781556001868155600286815560078e90556008805461ffff19168e1790553a600655600380547c01000000000000000000000000000000000000000000000000000000008d04740100000000000000000000000000000000000000000260a060020a63ffffffff0219919096168e17169490941790935588516004805493819052956020601f9385161590910260001901909316939093048101919091047f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b908101939091608091909101908390106101ee57805160ff19168380011785555b5061017a9291505b8082111561021e5760008155600101610166565b5050826003600050600201600050819055505050505050505050508a600060006101000a815481600160a060020a030219169083021790555089600d6000508190555088600e60006101000a81548160ff021916908302179055505050505050505050505050610e56806102226000396000f35b8280016001018555821561015e579182015b8281111561015e578251826000505591602001919060010190610200565b509056606060405236156101a05760e060020a60003504630924120081146101c25780630a16697a146101cf5780630fd1f94e146101d8578063137c638b1461022e57806321835af61461023b57806324032866146102545780632f95b833146102d65780633017fe24146102e55780633233c686146102ef57806337f4c00e146102fa5780634500054f146103055780634e417a98146103785780634e71d92d146103e15780634f059a43146103f35780636146195414610451578063625cc4651461046157806367ce940d1461046a5780637d298ee314610477578063830953ab146104f9578063938b5f321461050457806395ee122114610516578063974654f41461052a578063a06db7dc14610535578063a9d2293d14610541578063ae45850b14610597578063b0f07e44146105a9578063c19d93fb146105cb578063c6502da81461062e578063c680362214610637578063ca94692d1461064a578063cc3471af14610673578063d379be23146106c9578063d62457f6146106e3578063ea8a1af0146106ee578063f5562753146107f3578063f6b4dfb414610854575b610868600080548190600160a060020a03908116339091161461087a57610994565b610868600b5460ff165b90565b610868600d5481565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630fd1f94e6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6108685b62012cc86101cc565b61086860043560008160001415610dc65750600161084f565b6108686004356024356000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630bd295e6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f215610002575050604051519150505b92915050565b61099860085461ffff166101cc565b61086860026101cc565b610868600a546101cc565b6108686006546101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a09431546003600050336040518360e060020a0281526004018083815260200182600160a060020a031681526020019250505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6109af60408051602081810183526000825282516004805460026001821615610100026000190190911604601f81018490048402830184019095528482529293909291830182828015610a7d5780601f10610a5257610100808354040283529160200191610a7d565b61086860006000600180610b7b6105cf565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753436040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1d6000600480610c986105cf565b61086860025481565b6108685b620186a06101cc565b6108686004356024355b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a1873db6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f2156100025750506040515191506102d09050565b6108686009546101cc565b610a1f600c54600160a060020a031681565b610868600b5462010000900460ff166101cc565b6108686007546101cc565b610a3c600e5460ff1681565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a9d2293d6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600054600160a060020a031681565b610868600080548190600160a060020a039081163390911614610a8957610994565b6108685b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc80635054d98a60036000506040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b61086860015481565b610868600b54610100900460ff166101cc565b61086860035474010000000000000000000000000000000000000000900460e060020a026101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063cc3471af6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600854620100009004600160a060020a03166101cc565b6108686005546101cc565b610a1d604080517fa09431540000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc809163a0943154916044808301926020929190829003018160008760325a03f215610002575050604051511590506107f157604080517f7e9265620000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091637e9265629160448083019260009291908290030181838760325a03f215610002575050505b565b6108686004356000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753836040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f215610002575050604051519150505b919050565b610a1f600354600160a060020a03166101cc565b60408051918252519081900360200190f35b60045460006002600183161561010002600019019092169190910411156108a45760009150610994565b6108ac6105cf565b9050600081141580156108c0575060018114155b80156108cd575060028114155b156108db5760009150610994565b600480546000828152602060026001841615610100026000190190931692909204601f908101929092047f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b9081019236929083901061095d5782800160ff198235161785555b5061098d9291505b808211156109945760008155600101610949565b82800160010185558215610941579182015b8281111561094157823582600050559160200191906001019061096f565b5050600191505b5090565b6040805161ffff9092168252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610a0f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b005b60408051600160a060020a03929092168252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b815481529060010190602001808311610a6057829003601f168201915b505050505090506101cc565b6004546000600260018316156101000260001901909216919091041115610ab35760009150610994565b610abb6105cf565b905060008114158015610acf575060018114155b8015610adc575060028114155b15610aea5760009150610994565b604080517f7c0278fc000000000000000000000000000000000000000000000000000000008152600360048201818152602483019384523660448401819052731deeda36e15ec9e80f3d7414d67a4803ae45fc8094637c0278fc946000939190606401848480828437820191505094505050505060006040518083038160008760325a03f215610002575050505090565b1415610c8557604080516001547f0fee183d0000000000000000000000000000000000000000000000000000000082526003600483015233600160a060020a0316602483015234604483015260648201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091630fee183d916084828101926020929190829003018160008760325a03f21561000257505060405151925050811515610c8a577389efe605e9ecbe22849cd85d5449cc946c26f8f36312c82bcc33346040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515115159050610c8a57610002565b505090565b81925050610994565b505b50565b1415610c93575a9150610cab3383610481565b1515610cb75750610c95565b731deeda36e15ec9e80f3d7414d67a4803ae45fc8063da46be0a60038433610cdd61046e565b610ce5610232565b6040518660e060020a0281526004018086815260200185815260200184600160a060020a031681526020018381526020018281526020019550505050505060006040518083038160008760325a03f21561000257505050610c933360408051600080547fc17e6817000000000000000000000000000000000000000000000000000000008352600160a060020a03908116600484015230163160248301529151731deeda36e15ec9e80f3d7414d67a4803ae45fc809263c17e68179260448082019360209390928390039091019082908760325a03f2156100025750505050565b30600160a060020a031660405180807f5f5f6469672875696e7432353629000000000000000000000000000000000000815260200150600e019050604051809103902060e060020a8091040260e060020a9004600184036040518260e060020a0281526004018082815260200191505060006040518083038160008760325a03f292505050151561084f5761000256"
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "1062503",
+ "difficulty": "13700504019867",
+ "timestamp": "1456480446",
+ "gasLimit": "3141592",
+ "miner": "0x2a65aca4d5fc5b5c859090a6c34d164135398226"
+ },
+ "input": "0xf86b8203cf850ba43b740083200b2094741467b251fca923d6229c4b439078b55dca233b8084614619541ca078293714f69a810356f1ee29dc686ec2ca3a0e5448e1ef6322c77369ebdd26c2a01c3836fa363548959554ee5360361be9db4aea9eb7c31f61550f0e9a10138adf",
+ "tracerConfig": {
+ "diffMode": true
+ },
+ "result": {
+ "pre": {
+ "0x2a65aca4d5fc5b5c859090a6c34d164135398226": {
+ "balance": "0x98e1c608601c2496b2",
+ "nonce": 218916
+ },
+ "0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b": {
+ "balance": "0x0",
+ "nonce": 237,
+ "code": "0x6060604052361561027c5760e060020a600035046301991313811461027e57806303d22885146102ca5780630450991814610323578063049ae734146103705780630ce46c43146103c35780630e85023914610602578063112e39a8146106755780631b4fa6ab146106c25780631e74a2d3146106d057806326a7985a146106fd5780633017fe2414610753578063346cabbc1461075c578063373a1bc3146107d55780633a9e74331461081e5780633c2c21a01461086e5780633d9ce89b146108ba578063480b70bd1461092f578063481078431461097e57806348f0518714610a0e5780634c471cde14610a865780634db3da8314610b09578063523ccfa814610b4f578063586a69fa14610be05780635a9f2def14610c3657806364ee49fe14610caf57806367beaccb14610d055780636840246014610d74578063795b9a6f14610dca5780637b55c8b514610e415780637c73f84614610ee15780638c0e156d14610f145780638c1d01c814610f605780638e46afa914610f69578063938c430714610fc0578063971c803f146111555780639772c982146111ac57806398c9cdf41461122857806398e00e541461127f5780639f927be7146112d5578063a00aede914611383578063a1c0539d146113d3578063aff21c6514611449578063b152f19e14611474578063b549793d146114cb578063b5b33eda1461154b578063bbc6eb1f1461159b578063c0f68859146115ab578063c3a2c0c314611601578063c43d05751461164b578063d8e5c04814611694578063dbfef71014611228578063e29fb547146116e7578063e6470fbe1461173a578063ea27a8811461174c578063ee77fe86146117d1578063f158458c14611851575b005b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503387876020604051908101604052806000815260200150612225610f6d565b61188260043560243560443560643560843560a43560c435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050338b8a6020604051908101604052806000815260200150896125196106c6565b611882600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503385600060e060020a026020604051908101604052806000815260200150611e4a610f6d565b611882600435602435604435606435608435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503389896020604051908101604052806000815260200150886124e86106c6565b604080516020604435600481810135601f8101849004840285018401909552848452611882948135946024803595939460649492939101918190840183828082843750506040805160a08082019092529597963596608435969095506101449450925060a491506005908390839080828437509095505050505050604080518082018252600160a060020a03338116825288166020820152815160c0810190925260009173e54d323f9ef17c1f0dede47ecc86a9718fe5ea349163e3042c0f91600191908a908a9089908b90808b8b9090602002015181526020018b60016005811015610002579090602002015181526020018b60026005811015610002579090602002015181526020018b60036005811015610002579090602002015181526020018b6004600581101561000257909060200201518152602001348152602001506040518860e060020a02815260040180888152602001876002602002808383829060006004602084601f0104600f02600301f150905001868152602001806020018560ff1681526020018461ffff168152602001836006602002808383829060006004602084601f0104600f02600301f1509050018281038252868181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d25780820380516001836020036101000a031916815260200191505b509850505050505050505060206040518083038160008760325a03f2156100025750506040515191506124cd9050565b60408051602060248035600481810135601f81018590048502860185019096528585526118829581359591946044949293909201918190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808787611e64610f6d565b611882600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333600060e060020a026020604051908101604052806000815260200150611d28610f6d565b61189f5b6000611bf8611159565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a881600060005054611a9561159f565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346326a7985a6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b6118b760075b90565b604080516020606435600481810135601f8101849004840285018401909552848452611882948135946024803595604435956084949201919081908401838280828437509496505093359350505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160013389898861224b610f6d565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503386866020604051908101604052806000815260200150611e64610f6d565b611882600435602435604435606435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333896020604051908101604052806000815260200150886123bc6106c6565b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503387866020604051908101604052806000815260200150611f8d610f6d565b60408051602060248035600481810135601f810185900485028601850190965285855261188295813595919460449492939092019181908401838280828437509496505093359350505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808888612225610f6d565b611882600435602435604435606435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503388886020604051908101604052806000815260200150612388610f6d565b611882600435604080517fc4144b2600000000000000000000000000000000000000000000000000000000815260016004820152600160a060020a03831660248201529051600091737c1eb207c07e7ab13cf245585bd03d0fa478d0349163c4144b26916044818101926020929091908290030181878760325a03f215610002575050604051519150611b409050565b604080516020604435600481810135601f81018490048402850184019095528484526118829481359460248035959394606494929391019181908401838280828437509496505093359350505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133888888612238610f6d565b604080516020604435600481810135601f810184900484028501840190955284845261188294813594602480359593946064949293910191819084018382808284375094965050933593505060843591505060a43560c435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338b8b8b896126536106c6565b611882600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333866020604051908101604052806000815260200150611e4a610f6d565b6118b76004355b604080517fed5bd7ea00000000000000000000000000000000000000000000000000000000815260016004820152600160a060020a03831660248201529051600091737c1eb207c07e7ab13cf245585bd03d0fa478d0349163ed5bd7ea916044818101926020929091908290030181878760325a03f215610002575050604051519150611b409050565b61189f600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463586a69fa6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b60408051602060248035600481810135601f81018590048502860185019096528585526118829581359591946044949293909201918190840183828082843750949650509335935050606435915050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808989612388610f6d565b61188260043560243560443560643560843560a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050338a896020604051908101604052806000815260200150886124d76106c6565b6040805160206004803580820135601f8101849004840285018401909552848452611882949193602493909291840191908190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808587611e4a610f6d565b61188260043560243560443560643560843560a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050338a8a60206040519081016040528060008152602001508961262d6106c6565b604080516020606435600481810135601f810184900484028501840190955284845261188294813594602480359560443595608494920191908190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338888876120c7610f6d565b604080516020604435600481810135601f81018490048402850184019095528484526118829481359460248035959394606494929391019181908401838280828437505060408051608080820190925295979635969561010495509350608492508591508390839080828437509095505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338989898961263a6106c6565b6118b7600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a881858585611ba361122c565b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050333388602060405190810160405280600081526020015061236e610f6d565b6118b760005481565b6118c95b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea34638e46afa96040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b60408051602060248035600481810135601f8101859004850286018501909652858552611882958135959194604494929390920191819084018382808284375094965050933593505060643591505060843560a43560c43560e43561010435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600160005033338e8e8d8f8e8e8e8e8e346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156111195780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519b9a5050505050505050505050565b61189f5b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463971c803f6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b604080516020604435600481810135601f8101849004840285018401909552848452611882948135946024803595939460649492939101918190840183828082843750949650509335935050608435915050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338989896123a2610f6d565b6118b75b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346398c9cdf46040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346398e00e546040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b611882600435604080517fe6ce3a6a000000000000000000000000000000000000000000000000000000008152600160048201527f3e3d0000000000000000000000000000000000000000000000000000000000006024820152604481018390529051600091737c1eb207c07e7ab13cf245585bd03d0fa478d0349163e6ce3a6a916064818101926020929091908290030181878760325a03f215610002575050604051519150611b409050565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503385600060e060020a0260206040519081016040528060008152602001506121ef610f6d565b604080516020604435600481810135601f8101849004840285018401909552848452611882948135946024803595939460649492939101918190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338787876120b5610f6d565b6118b7600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a88183611b4561159f565b6118b75b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463b152f19e6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b60408051602060248035600481810135601f8101859004850286018501909652858552611882958135959194604494929390920191819084018382808284375094965050933593505060643591505060843560a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808b8b8961262d6106c6565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503386600060e060020a026020604051908101604052806000815260200150612200610f6d565b6118b75b60005460649004610759565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463c0f688596040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b611882600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333600060e060020a026020604051908101604052806000815260200150611bff610f6d565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333876020604051908101604052806000815260200150612200610f6d565b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503387600060e060020a026020604051908101604052806000815260200150612213610f6d565b611882600435602435604435606435608435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600160005033338a60206040519081016040528060008152602001508961250c6106c6565b61027c6000600060006118e033610b56565b6118b7600435602435604435606435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a881868686866040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b949350505050565b604080516020604435600481810135601f810184900484028501840190955284845261188294813594602480359593946064949293910191819084018382808284375094965050933593505060843591505060a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338a8a8a886124fa6106c6565b6118b7600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a88184846000611b4f61122c565b60408051600160a060020a03929092168252519081900360200190f35b6040805161ffff929092168252519081900360200190f35b60408051918252519081900360200190f35b6040805160ff929092168252519081900360200190f35b15611a905733925082600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040805180517fc6803622000000000000000000000000000000000000000000000000000000008252915191945063c680362291600482810192602092919082900301816000876161da5a03f11561000257505060405151905080156119d1575082600160a060020a031663d379be236040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060405151600160a060020a03166000141590505b80156119dd5750600082115b80156119ec5750600054600190115b15611a90578183600160a060020a031663830953ab6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040515160640291909104915050604281118015611a4d5750600054829011155b15611a675760008054612710612711909102049055611a90565b602181108015611a7a5750600054829010155b15611a90576000805461271061270f9091020490555b505050565b6000611a9f61122c565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f2156100025750506040515191506107599050565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b919050565b6000611af261122c565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b92915050565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b9392505050565b9050610759565b611c076106c6565b6000611c11611478565b611c1961122c565b600054611c2461159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611cf25780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f2156100025750506040515191506107599050565b611d306106c6565b60008b611d3b61122c565b600054611d4661159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611e145780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150611b409050565b611e526106c6565b6000611e5c611478565b611d3b61122c565b611e6c6106c6565b6000611e76611478565b611e7e61122c565b600054611e8961159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611f575780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150611b9d9050565b611f956106c6565b8b611f9e611478565b611fa661122c565b600054611fb161159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561207f5780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150611bf19050565b6120bd6106c6565b6000611f9e611478565b6120cf6106c6565b8b6120d8611478565b6120e061122c565b6000546120eb61159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156121b95780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f2156100025750506040515191506117c99050565b6121f76106c6565b8b611e76611478565b6122086106c6565b60008b611e7e61122c565b61221b6106c6565b8a8c611fa661122c565b61222d6106c6565b60008b611fa661122c565b6122406106c6565b60008b6120e061122c565b6122536106c6565b8c8b61225d61122c565b60005461226861159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156123365780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f21561000257505060405151979650505050505050565b6123766106c6565b60008c8c600060005054611fb161159f565b6123906106c6565b60008c8c6000600050546120eb61159f565b6123aa6106c6565b60008c8c60006000505461226861159f565b60008d8d6000600050546120eb61159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561249c5780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150505b9695505050505050565b8e8d8d6000600050546123ce61159f565b60008d8d60006000505461226861159f565b60008d8d6000600050546123ce61159f565b60008e8e8d61226861159f565b8f8e8e8d61252561159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156125f35780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519998505050505050505050565b60008e8e8d6123ce61159f565b8a5160208c015160408d015160608e015161226861159f565b60008e8e8d61252561159f56",
+ "storage": {
+ "0x26cba0705aade77fa0f9275b68d01fb71206a44abd3a4f5a838f7241efbc8abf": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230",
+ "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef715": "0x000000000000000000000000d7b0e93fa8386b17fb5d1cf934076203dcc122f3",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bc": "0x000000000000000000000000d7b0e93fa8386b17fb5d1cf934076203dcc122f3",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bd": "0x000000000000000000000000c9a2bfd279fe57e7651e5d9f29bb1793c9a1cf01",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bf": "0x0000000000000000000000000000000000000000000000000000000000000002",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f1": "0x000000000000000000000000a4d91b341f0e9a7000be916a668408b463f4c38c",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f3": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f4": "0x0000000000000000000000000000000000000000000000000000000000000003",
+ "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64dbc": "0x0000000000000000000000000000000000000000000000000000000000000001"
+ }
+ },
+ "0x741467b251fca923d6229c4b439078b55dca233b": {
+ "balance": "0x29c613529e8218f8",
+ "code": "0x606060405236156101a05760e060020a60003504630924120081146101c25780630a16697a146101cf5780630fd1f94e146101d8578063137c638b1461022e57806321835af61461023b57806324032866146102545780632f95b833146102d65780633017fe24146102e55780633233c686146102ef57806337f4c00e146102fa5780634500054f146103055780634e417a98146103785780634e71d92d146103e15780634f059a43146103f35780636146195414610451578063625cc4651461046157806367ce940d1461046a5780637d298ee314610477578063830953ab146104f9578063938b5f321461050457806395ee122114610516578063974654f41461052a578063a06db7dc14610535578063a9d2293d14610541578063ae45850b14610597578063b0f07e44146105a9578063c19d93fb146105cb578063c6502da81461062e578063c680362214610637578063ca94692d1461064a578063cc3471af14610673578063d379be23146106c9578063d62457f6146106e3578063ea8a1af0146106ee578063f5562753146107f3578063f6b4dfb414610854575b610868600080548190600160a060020a03908116339091161461087a57610994565b610868600b5460ff165b90565b610868600d5481565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630fd1f94e6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6108685b62012cc86101cc565b61086860043560008160001415610dc65750600161084f565b6108686004356024356000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630bd295e6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f215610002575050604051519150505b92915050565b61099860085461ffff166101cc565b61086860026101cc565b610868600a546101cc565b6108686006546101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a09431546003600050336040518360e060020a0281526004018083815260200182600160a060020a031681526020019250505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6109af60408051602081810183526000825282516004805460026001821615610100026000190190911604601f81018490048402830184019095528482529293909291830182828015610a7d5780601f10610a5257610100808354040283529160200191610a7d565b61086860006000600180610b7b6105cf565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753436040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1d6000600480610c986105cf565b61086860025481565b6108685b620186a06101cc565b6108686004356024355b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a1873db6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f2156100025750506040515191506102d09050565b6108686009546101cc565b610a1f600c54600160a060020a031681565b610868600b5462010000900460ff166101cc565b6108686007546101cc565b610a3c600e5460ff1681565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a9d2293d6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600054600160a060020a031681565b610868600080548190600160a060020a039081163390911614610a8957610994565b6108685b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc80635054d98a60036000506040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b61086860015481565b610868600b54610100900460ff166101cc565b61086860035474010000000000000000000000000000000000000000900460e060020a026101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063cc3471af6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600854620100009004600160a060020a03166101cc565b6108686005546101cc565b610a1d604080517fa09431540000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc809163a0943154916044808301926020929190829003018160008760325a03f215610002575050604051511590506107f157604080517f7e9265620000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091637e9265629160448083019260009291908290030181838760325a03f215610002575050505b565b6108686004356000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753836040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f215610002575050604051519150505b919050565b610a1f600354600160a060020a03166101cc565b60408051918252519081900360200190f35b60045460006002600183161561010002600019019092169190910411156108a45760009150610994565b6108ac6105cf565b9050600081141580156108c0575060018114155b80156108cd575060028114155b156108db5760009150610994565b600480546000828152602060026001841615610100026000190190931692909204601f908101929092047f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b9081019236929083901061095d5782800160ff198235161785555b5061098d9291505b808211156109945760008155600101610949565b82800160010185558215610941579182015b8281111561094157823582600050559160200191906001019061096f565b5050600191505b5090565b6040805161ffff9092168252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610a0f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b005b60408051600160a060020a03929092168252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b815481529060010190602001808311610a6057829003601f168201915b505050505090506101cc565b6004546000600260018316156101000260001901909216919091041115610ab35760009150610994565b610abb6105cf565b905060008114158015610acf575060018114155b8015610adc575060028114155b15610aea5760009150610994565b604080517f7c0278fc000000000000000000000000000000000000000000000000000000008152600360048201818152602483019384523660448401819052731deeda36e15ec9e80f3d7414d67a4803ae45fc8094637c0278fc946000939190606401848480828437820191505094505050505060006040518083038160008760325a03f215610002575050505090565b1415610c8557604080516001547f0fee183d0000000000000000000000000000000000000000000000000000000082526003600483015233600160a060020a0316602483015234604483015260648201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091630fee183d916084828101926020929190829003018160008760325a03f21561000257505060405151925050811515610c8a577389efe605e9ecbe22849cd85d5449cc946c26f8f36312c82bcc33346040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515115159050610c8a57610002565b505090565b81925050610994565b505b50565b1415610c93575a9150610cab3383610481565b1515610cb75750610c95565b731deeda36e15ec9e80f3d7414d67a4803ae45fc8063da46be0a60038433610cdd61046e565b610ce5610232565b6040518660e060020a0281526004018086815260200185815260200184600160a060020a031681526020018381526020018281526020019550505050505060006040518083038160008760325a03f21561000257505050610c933360408051600080547fc17e6817000000000000000000000000000000000000000000000000000000008352600160a060020a03908116600484015230163160248301529151731deeda36e15ec9e80f3d7414d67a4803ae45fc809263c17e68179260448082019360209390928390039091019082908760325a03f2156100025750505050565b30600160a060020a031660405180807f5f5f6469672875696e7432353629000000000000000000000000000000000000815260200150600e019050604051809103902060e060020a8091040260e060020a9004600184036040518260e060020a0281526004018082815260200191505060006040518083038160008760325a03f292505050151561084f5761000256"
+ },
+ "0x7dd677b54fc954824a7bc49bd26cbdfa12c75adf": {
+ "balance": "0xd7a58f5b73b4b6c4",
+ "code": "0x606060405236156100985760e060020a60003504633896002781146100e15780633defb962146100ea5780633f4be8891461010c5780634136aa351461011f5780634a420138146101a057806369c1a7121461028c5780638129fc1c146102955780638da5cb5b146102a6578063ae45850b146102b8578063af3309d8146102cc578063ea8a1af0146102d5578063ead50da3146102f4575b610308671bc16d674ec8000030600160a060020a03163110156100df57600554604051600160a060020a03918216916000913091909116319082818181858883f150505050505b565b61030a60005481565b610308671bc16d674ec8000030600160a060020a031631101561040f576100df565b61031c600454600160a060020a03165b90565b61030a5b600080548190118015610199575060408051600480547f0a16697a0000000000000000000000000000000000000000000000000000000083529251600160a060020a039390931692630a16697a928083019260209291829003018187876161da5a03f1156100025750506040515160ff01431090505b905061011c565b6103085b600354600554604080517f8c0e156d0000000000000000000000000000000000000000000000000000000081527f3defb96200000000000000000000000000000000000000000000000000000000600482015260a060020a90920461ffff1643016024830152621e8480604483015251600092600160a060020a031691638c0e156d916729a2241af62c000091606481810192602092909190829003018185886185025a03f1156100025750506040515192600160a060020a0384161491506102899050576004805473ffffffffffffffffffffffffffffffffffffffff1916821790555b50565b61030a60015481565b61030860008054146103f2576100df565b61031c600554600160a060020a031681565b61031c600354600160a060020a031661011c565b61030a60025481565b610308600554600160a060020a03908116339091161461035157610002565b61033960055460a060020a900461ffff1681565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b6040805161ffff929092168252519081900360200190f35b6004546000600160a060020a03919091163111156103c75760408051600480547fea8a1af00000000000000000000000000000000000000000000000000000000083529251600160a060020a03939093169263ea8a1af0928083019260009291829003018183876161da5a03f115610002575050505b600554604051600160a060020a03918216916000913091909116319082818181858883f15050505050565b426000556100df6101a4565b600280546001908101909155429055565b600454600160a060020a03908116339091161461042b576100df565b610433610123565b151561043e576100df565b6103fe6101a456",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000056d0009b",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000008b",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x000000000000000000000000741467b251fca923d6229c4b439078b55dca233b"
+ }
+ },
+ "0xb834e3edfc1a927bdcecb67a9d0eccbd752a5bb3": {
+ "balance": "0xffe9b09a5c474dca",
+ "nonce": 975
+ },
+ "0xd3cda913deb6f67967b99d67acdfa1712c293601": {
+ "balance": "0x4f5807198e238f13e",
+ "nonce": 283
+ }
+ },
+ "post": {
+ "0x2a65aca4d5fc5b5c859090a6c34d164135398226": {
+ "balance": "0x98e2b02f14529b1eb2"
+ },
+ "0x651913977e8140c323997fce5e03c19e0015eebf": {
+ "balance": "0x29a2241af62c0000",
+ "code": "0x606060405236156101a05760e060020a60003504630924120081146101c25780630a16697a146101cf5780630fd1f94e146101d8578063137c638b1461022e57806321835af61461023b57806324032866146102545780632f95b833146102d65780633017fe24146102e55780633233c686146102ef57806337f4c00e146102fa5780634500054f146103055780634e417a98146103785780634e71d92d146103e15780634f059a43146103f35780636146195414610451578063625cc4651461046157806367ce940d1461046a5780637d298ee314610477578063830953ab146104f9578063938b5f321461050457806395ee122114610516578063974654f41461052a578063a06db7dc14610535578063a9d2293d14610541578063ae45850b14610597578063b0f07e44146105a9578063c19d93fb146105cb578063c6502da81461062e578063c680362214610637578063ca94692d1461064a578063cc3471af14610673578063d379be23146106c9578063d62457f6146106e3578063ea8a1af0146106ee578063f5562753146107f3578063f6b4dfb414610854575b610868600080548190600160a060020a03908116339091161461087a57610994565b610868600b5460ff165b90565b610868600d5481565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630fd1f94e6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6108685b62012cc86101cc565b61086860043560008160001415610dc65750600161084f565b6108686004356024356000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630bd295e6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f215610002575050604051519150505b92915050565b61099860085461ffff166101cc565b61086860026101cc565b610868600a546101cc565b6108686006546101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a09431546003600050336040518360e060020a0281526004018083815260200182600160a060020a031681526020019250505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6109af60408051602081810183526000825282516004805460026001821615610100026000190190911604601f81018490048402830184019095528482529293909291830182828015610a7d5780601f10610a5257610100808354040283529160200191610a7d565b61086860006000600180610b7b6105cf565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753436040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1d6000600480610c986105cf565b61086860025481565b6108685b620186a06101cc565b6108686004356024355b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a1873db6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f2156100025750506040515191506102d09050565b6108686009546101cc565b610a1f600c54600160a060020a031681565b610868600b5462010000900460ff166101cc565b6108686007546101cc565b610a3c600e5460ff1681565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a9d2293d6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600054600160a060020a031681565b610868600080548190600160a060020a039081163390911614610a8957610994565b6108685b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc80635054d98a60036000506040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b61086860015481565b610868600b54610100900460ff166101cc565b61086860035474010000000000000000000000000000000000000000900460e060020a026101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063cc3471af6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600854620100009004600160a060020a03166101cc565b6108686005546101cc565b610a1d604080517fa09431540000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc809163a0943154916044808301926020929190829003018160008760325a03f215610002575050604051511590506107f157604080517f7e9265620000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091637e9265629160448083019260009291908290030181838760325a03f215610002575050505b565b6108686004356000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753836040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f215610002575050604051519150505b919050565b610a1f600354600160a060020a03166101cc565b60408051918252519081900360200190f35b60045460006002600183161561010002600019019092169190910411156108a45760009150610994565b6108ac6105cf565b9050600081141580156108c0575060018114155b80156108cd575060028114155b156108db5760009150610994565b600480546000828152602060026001841615610100026000190190931692909204601f908101929092047f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b9081019236929083901061095d5782800160ff198235161785555b5061098d9291505b808211156109945760008155600101610949565b82800160010185558215610941579182015b8281111561094157823582600050559160200191906001019061096f565b5050600191505b5090565b6040805161ffff9092168252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610a0f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b005b60408051600160a060020a03929092168252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b815481529060010190602001808311610a6057829003601f168201915b505050505090506101cc565b6004546000600260018316156101000260001901909216919091041115610ab35760009150610994565b610abb6105cf565b905060008114158015610acf575060018114155b8015610adc575060028114155b15610aea5760009150610994565b604080517f7c0278fc000000000000000000000000000000000000000000000000000000008152600360048201818152602483019384523660448401819052731deeda36e15ec9e80f3d7414d67a4803ae45fc8094637c0278fc946000939190606401848480828437820191505094505050505060006040518083038160008760325a03f215610002575050505090565b1415610c8557604080516001547f0fee183d0000000000000000000000000000000000000000000000000000000082526003600483015233600160a060020a0316602483015234604483015260648201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091630fee183d916084828101926020929190829003018160008760325a03f21561000257505060405151925050811515610c8a577389efe605e9ecbe22849cd85d5449cc946c26f8f36312c82bcc33346040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515115159050610c8a57610002565b505090565b81925050610994565b505b50565b1415610c93575a9150610cab3383610481565b1515610cb75750610c95565b731deeda36e15ec9e80f3d7414d67a4803ae45fc8063da46be0a60038433610cdd61046e565b610ce5610232565b6040518660e060020a0281526004018086815260200185815260200184600160a060020a031681526020018381526020018281526020019550505050505060006040518083038160008760325a03f21561000257505050610c933360408051600080547fc17e6817000000000000000000000000000000000000000000000000000000008352600160a060020a03908116600484015230163160248301529151731deeda36e15ec9e80f3d7414d67a4803ae45fc809263c17e68179260448082019360209390928390039091019082908760325a03f2156100025750505050565b30600160a060020a031660405180807f5f5f6469672875696e7432353629000000000000000000000000000000000000815260200150600e019050604051809103902060e060020a8091040260e060020a9004600184036040518260e060020a0281526004018082815260200191505060006040518083038160008760325a03f292505050151561084f5761000256",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000007dd677b54fc954824a7bc49bd26cbdfa12c75adf",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000011f8119429ed3a",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x00000000000000000000000000000000000000000000000000002e002d006b55",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000003defb9627dd677b54fc954824a7bc49bd26cbdfa12c75adf",
+ "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000ba43b7400",
+ "0x0000000000000000000000000000000000000000000000000000000000000007": "0x00000000000000000000000000000000000000000000000000000000001e8480",
+ "0x0000000000000000000000000000000000000000000000000000000000000008": "0x000000000000000000000000000000000000000000000000000000000000000a",
+ "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0000000000000000000000006c8f2a135f6ed072de4503bd7c4999a1a17f824b",
+ "0x000000000000000000000000000000000000000000000000000000000000000d": "0x0000000000000000000000000000000000000000000000000000000000103847",
+ "0x000000000000000000000000000000000000000000000000000000000000000e": "0x00000000000000000000000000000000000000000000000000000000000000ff"
+ }
+ },
+ "0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b": {
+ "nonce": 238,
+ "storage": {
+ "0x26cba0705aade77fa0f9275b68d01fb71206a44abd3a4f5a838f7241efbc8abf": "0x000000000000000000000000d7b0e93fa8386b17fb5d1cf934076203dcc122f3",
+ "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef715": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bc": "0x000000000000000000000000a4d91b341f0e9a7000be916a668408b463f4c38c",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bd": "0x000000000000000000000000d7b0e93fa8386b17fb5d1cf934076203dcc122f3",
+ "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bf": "0x0000000000000000000000000000000000000000000000000000000000000003",
+ "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2751": "0x000000000000000000000000651913977e8140c323997fce5e03c19e0015eebf",
+ "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2752": "0x0000000000000000000000000000000000000000000000000000000000103847",
+ "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2753": "0x000000000000000000000000741467b251fca923d6229c4b439078b55dca233b",
+ "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2756": "0x0000000000000000000000000000000000000000000000000000000000000001",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f1": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f3": "0x000000000000000000000000c9a2bfd279fe57e7651e5d9f29bb1793c9a1cf01",
+ "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f4": "0x0000000000000000000000000000000000000000000000000000000000000002",
+ "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64dbb": "0x000000000000000000000000651913977e8140c323997fce5e03c19e0015eebf",
+ "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64dbc": "0x0000000000000000000000000000000000000000000000000000000000000002"
+ }
+ },
+ "0x741467b251fca923d6229c4b439078b55dca233b": {
+ "balance": "0x0",
+ "storage": {
+ "0x000000000000000000000000000000000000000000000000000000000000000b": "0x0000000000000000000000000000000000000000000000000000000000000101"
+ }
+ },
+ "0x7dd677b54fc954824a7bc49bd26cbdfa12c75adf": {
+ "balance": "0xd6c5f42b8502a0e3",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000056d020be",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000008c",
+ "0x0000000000000000000000000000000000000000000000000000000000000004": "0x000000000000000000000000651913977e8140c323997fce5e03c19e0015eebf"
+ }
+ },
+ "0xb834e3edfc1a927bdcecb67a9d0eccbd752a5bb3": {
+ "balance": "0x10002e64ebd492a46",
+ "nonce": 976
+ },
+ "0xd3cda913deb6f67967b99d67acdfa1712c293601": {
+ "balance": "0x4f5809f97e1c8bb9b"
+ }
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/simple.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/simple.json
new file mode 100644
index 0000000000..01cc3c5058
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/simple.json
@@ -0,0 +1,103 @@
+{
+ "context": {
+ "difficulty": "3502894804",
+ "gasLimit": "4722976",
+ "miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724",
+ "number": "2289806",
+ "timestamp": "1513601314"
+ },
+ "genesis": {
+ "alloc": {
+ "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
+ "balance": "0x0",
+ "code": "0x",
+ "nonce": "22",
+ "storage": {}
+ },
+ "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
+ "balance": "0x4d87094125a369d9bd5",
+ "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
+ "nonce": "1",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000",
+ "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c",
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
+ }
+ },
+ "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
+ "balance": "0x1780d77678137ac1b775",
+ "code": "0x",
+ "nonce": "29072",
+ "storage": {}
+ }
+ },
+ "config": {
+ "byzantiumBlock": 1700000,
+ "chainId": 3,
+ "daoForkSupport": true,
+ "eip150Block": 0,
+ "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
+ "eip155Block": 10,
+ "eip158Block": 10,
+ "ethash": {},
+ "homesteadBlock": 0
+ },
+ "difficulty": "3509749784",
+ "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
+ "gasLimit": "4727564",
+ "hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440",
+ "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
+ "mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada",
+ "nonce": "0x4eb12e19c16d43da",
+ "number": "2289805",
+ "stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
+ "timestamp": "1513601261",
+ "totalDifficulty": "7143276353481064"
+ },
+ "input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
+ "tracerConfig": {
+ "diffMode": true
+ },
+ "result": {
+ "pre": {
+ "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
+ "balance": "0x0",
+ "nonce": 22
+ },
+ "0x1585936b53834b021f68cc13eeefdec2efc8e724": {
+ "balance": "0x0"
+ },
+ "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
+ "balance": "0x4d87094125a369d9bd5",
+ "nonce": 1,
+ "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
+ }
+ },
+ "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
+ "balance": "0x1780d77678137ac1b775",
+ "nonce": 29072
+ }
+ },
+ "post": {
+ "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
+ "balance": "0x6f05b59d3b20000"
+ },
+ "0x1585936b53834b021f68cc13eeefdec2efc8e724": {
+ "balance": "0x420eed1bd6c00"
+ },
+ "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
+ "balance": "0x4d869a3b70062eb9bd5",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b95e"
+ }
+ },
+ "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
+ "balance": "0x1780d7725724a9044b75",
+ "nonce": 29073
+ }
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/suicide.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/suicide.json
new file mode 100644
index 0000000000..5021bda192
--- /dev/null
+++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/suicide.json
@@ -0,0 +1,107 @@
+{
+ "genesis": {
+ "difficulty": "5697691613344",
+ "extraData": "0xd783010202844765746887676f312e342e32856c696e7578",
+ "gasLimit": "3141592",
+ "hash": "0x2004021ae3545cf8abba1ec97a7e401157cee9e847131e2f4c75ce38610040cc",
+ "miner": "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5",
+ "mixHash": "0x651f01d13fb801c602e1544ab80b3bc32888ea40ef298efa52ec3df983b558ee",
+ "nonce": "0xdf23f0da925518a6",
+ "number": "422908",
+ "stateRoot": "0xd914c6440edf9f4a6f997a9b3ecb6e1a9ca2310f74b0b6890c0d0d4a3c28e4d3",
+ "timestamp": "1445530335",
+ "totalDifficulty": "2148894717741690476",
+ "alloc": {
+ "0x2861bf89b6c640c79040d357c1e9513693ef5d3f": {
+ "balance": "0x0",
+ "code": "0x606060405236156100825760e060020a600035046312055e8f8114610084578063185061da146100b157806322beb9b9146100d5578063245a03ec146101865780633fa4f245146102a657806341c0e1b5146102af578063890eba68146102cb578063b29f0835146102de578063d6b4485914610308578063dd012a15146103b9575b005b6001805474ff0000000000000000000000000000000000000000191660a060020a60043502179055610082565b6100826001805475ff00000000000000000000000000000000000000000019169055565b61008260043560015460e060020a6352afbc3302606090815230600160a060020a039081166064527fb29f0835000000000000000000000000000000000000000000000000000000006084527fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060a45243840160c490815260ff60a060020a85041660e452600061010481905291909316926352afbc339261012492918183876161da5a03f1156100025750505050565b6100826004356024356001547fb0f07e440000000000000000000000000000000000000000000000000000000060609081526064839052600160a060020a039091169063b0f07e449060849060009060248183876161da5a03f150604080516001547f73657449742875696e74323536290000000000000000000000000000000000008252825191829003600e018220878352835192839003602001832060e060020a6352afbc33028452600160a060020a03308116600486015260e060020a9283900490920260248501526044840152438901606484015260a060020a820460ff1694830194909452600060a483018190529251931694506352afbc33935060c48181019391829003018183876161da5a03f115610002575050505050565b6103c460025481565b61008260005433600160a060020a039081169116146103ce575b565b6103c460015460a860020a900460ff1681565b6100826001805475ff000000000000000000000000000000000000000000191660a860020a179055565b61008260043560015460e060020a6352afbc3302606090815230600160a060020a039081166064527f185061da000000000000000000000000000000000000000000000000000000006084527fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060a45243840160c490815260ff60a060020a85041660e452600061010481905291909316926352afbc339261012492918183876161da5a03f1156100025750505050565b600435600255610082565b6060908152602090f35b6001547f6ff96d17000000000000000000000000000000000000000000000000000000006060908152600160a060020a0330811660645290911690632e1a7d4d908290636ff96d17906084906020906024816000876161da5a03f1156100025750506040805180517f2e1a7d4d0000000000000000000000000000000000000000000000000000000082526004820152905160248281019350600092829003018183876161da5a03f115610002575050600054600160a060020a03169050ff",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000d3cda913deb6f67967b99d67acdfa1712c293601",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000ff30c9e568f133adce1f1ea91e189613223fc461b9"
+ }
+ },
+ "0x2a65aca4d5fc5b5c859090a6c34d164135398226": {
+ "balance": "0x326601cc6cf364f6b9",
+ "nonce": "12122",
+ "code": "0x"
+ },
+ "0x30c9e568f133adce1f1ea91e189613223fc461b9": {
+ "balance": "0x8b83c417dd78000",
+ "nonce": "2",
+ "code": "0x606060405236156102ea5760e060020a6000350463022bc71f81146102f757806303d6d7b61461037f578063086ae9e4146103ec57806309c975df146104595780631145a20f146104c657806312d67c5f146104e75780631302188c146104f15780631ae460e5146104fc57806323306ed614610573578063234917d4146105ca57806329917954146106375780632a472ae81461071d5780632e1a7d4d1461078a578063306b031d1461087f57806333613cbe1461089d57806334c19b93146108c257806335b281531461092f5780633664a0ea146109b85780633c941423146109c35780633cbfed7414610a3b57806350a3bd3914610a4957806352afbc3314610a735780635539d40014610c2a5780635a5383ac14610c3e57806360b831e514610cb55780636164947214610d7f578063685c234a14610d8a5780636ffc089614610de0578063741b3c3914610e4d5780637542861514610ed25780637772a38014610f5557806377b19cd514610ff057806378bc64601461105d5780638b37e656146110ca5780638baced64146111375780638dd5e298146111b157806393423e9c146111de57806394d2b21b1461120257806394f3f81d1461121657806398e00e54146112665780639f927be7146112bc578063a502aae81461136a578063a6c01cfd146113e8578063a9743c68146113fa578063aa4cc01f14611467578063b010d94a146114d4578063b0171fa41461154e578063b0ac4c8c146115cc578063b0f07e4414611635578063b35594601461171c578063c0f6885914611739578063c3daab961461178f578063c630f92b146117bb578063c831391d146117e5578063cd062734146117f0578063d0e30db01461185d578063db681e5414611865578063e40986551461190c578063e850f3ae14611979578063ed2b8e0b146119e6578063f340fa01146119f1578063f828c3fa14611ae8578063f8b1185314611b07578063f9f447eb14611b24578063fc30052214611b91578063fcf3691814611bfe575b6112645b611c86336119f8565b611c88600435604080517fc4144b260000000000000000000000000000000000000000000000000000000081526010600482015260248101839052905160009173ce642b6a82e72147ceade0e72c786ba8eaeb31d79163c4144b26916044818101926020929091908290030181878760325a03f2156100025750506040515191506108989050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b637d613b346000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63da40fd616000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611c9a60043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63c68efc486000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b61126460043560243560443560643560843561200185858585856000610a89565b611c886004545b90565b611c886005546104ee565b611c886040805160e160020a6333f8a36702815260066004820152600160a060020a0333166024820152905160009173c895c144d0b0f88417cf9e14e03e6abc82c0af3f916367f146ce916044818101926020929091908290030181878760325a03f2156100025750506040515191506104ee9050565b611c885b60007327b1b436e4699a012cc8698e33c8f3e1c035c28b6323306ed66040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506104ee9050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63e99a66856000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611264604080517f317c152d00000000000000000000000000000000000000000000000000000000815260066004820152600160a060020a0333166024820152905160009173c895c144d0b0f88417cf9e14e03e6abc82c0af3f9163317c152d916044818101926020929091908290030181878760325a03f2156100025750506040805180517ff1173928000000000000000000000000000000000000000000000000000000008252600160a060020a0333166004830152602482018190529151919363f1173928926044838101938290030181838760325a03f2156100025750505050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63707378396000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611264600435604080517fb5bc6dbb00000000000000000000000000000000000000000000000000000000815260126004820152600160a060020a033316602482015260448101839052905173d3cb18959b0435864ff33010fa83be60afc04b229163b5bc6dbb916064828101926020929190829003018160008760325a03f21561000257505060405151159050611d255773d3cb18959b0435864ff33010fa83be60afc04b22637fcf532c33836040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060006040518083038160008760325a03f21561000257505050611ae5565b611c886004356000818152600e60205260409020600201545b919050565b611c886004355b600160a060020a0381166000908152600f6020526040902054610898565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63fc4730126000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611264600435604080517fa95d3e76000000000000000000000000000000000000000000000000000000008152600060048201819052600160a060020a0384811660248401523316604483015291517327b1b436e4699a012cc8698e33c8f3e1c035c28b9263a95d3e7692606481810193918290030181838760325a03f2156100025750505050565b611c886002546104ee565b611c9a60043560243560007327b1b436e4699a012cc8698e33c8f3e1c035c28b6398213db6600060005085856040518460e060020a02815260040180848152602001838152602001828152602001935050505060206040518083038160008760325a03f215610002575050604051519150610dda9050565b611c886000611e0a336108a4565b611264600073c895c144d0b0f88417cf9e14e03e6abc82c0af3f635748147e600633611ec2610577565b61126460043560243560443560643560843560a4355b604080517ff1924efb000000000000000000000000000000000000000000000000000000008152600060048201819052600160a060020a03338116602484015289166044830152606482018890526084820187905260a4820186905260ff851660c483015260e48201849052915182917327b1b436e4699a012cc8698e33c8f3e1c035c28b9163f1924efb91610104818101926020929091908290030181878760325a03f2156100025750506040805180517f5a1230bf000000000000000000000000000000000000000000000000000000008252600160a060020a0333811660048401528c166024830152604482018b9052606482018a90526084820189905260ff881660a483015260c482018790529151919450635a1230bf9160e48083019260209291908290030181878760325a03f215610002575050604051519183149050612008577327b1b436e4699a012cc8698e33c8f3e1c035c28b6318b753ab82846040518360e060020a028152600401808381526020018281526020019250505060006040518083038160008760325a03f21561000257505050612056565b611c9a600154600160a060020a03166104ee565b611c886040805160e560020a6304b47bb902815260066004820152600160a060020a0333166024820152905160009173c895c144d0b0f88417cf9e14e03e6abc82c0af3f9163968f7720916044818101926020929091908290030181878760325a03f2156100025750506040515191506104ee9050565b6112646004357327b1b436e4699a012cc8698e33c8f3e1c035c28b637e853f3d600060005083336040518460e060020a0281526004018084815260200183815260200182600160a060020a03168152602001935050505060206040518083038160008760325a03f21561000257505060405151159050611ae5577327b1b436e4699a012cc8698e33c8f3e1c035c28b63ab2af349826040518260e060020a0281526004018082815260200191505060006040518083038160008760325a03f2156100025750505050565b611c886008546104ee565b611c88600435602435604080516c01000000000000000000000000600160a060020a03858116820283528416026014820152815160289181900391909101902060009081526015602052205460ff165b92915050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63b506054f6000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611264604080517f068e3ef100000000000000000000000000000000000000000000000000000000815260066004820152600160a060020a0333166024820152346044820152905173c895c144d0b0f88417cf9e14e03e6abc82c0af3f9163068e3ef19160648281019260009291908290030181838760325a03f21561000257505050565b611cb76004356040805160208181018352600080835284815260138252838120600d0154815260148252835190849020805460026001821615610100026000190190911604601f81018490048402830184019095528482529293909291830182828015611fb85780601f10611f8d57610100808354040283529160200191611fb8565b611c886004356024355b604080517fa163a32500000000000000000000000000000000000000000000000000000000815260066004820152600160a060020a038416602482015260448101839052905160009173c895c144d0b0f88417cf9e14e03e6abc82c0af3f9163a163a325916064818101926020929091908290030181878760325a03f215610002575050604051519150610dda9050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63775f20f96000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b637517a7c96000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611c9a60043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63250687836000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611c886004356040805160e160020a6333f8a36702815260066004820152600160a060020a0383166024820152905160009173c895c144d0b0f88417cf9e14e03e6abc82c0af3f916367f146ce916044818101926020929091908290030181878760325a03f2156100025750506040515191506108989050565b611c88600435600073c895c144d0b0f88417cf9e14e03e6abc82c0af3f6354e37911600684611e6d610577565b611c88600435600160a060020a038116600090815260126020526040902054610898565b611c9a600054600160a060020a03166104ee565b604080516c01000000000000000000000000600435600160a060020a0390811682028352331602601482015281516028918190039190910190206000908152601560205220805460ff191690555b005b611c8860007327b1b436e4699a012cc8698e33c8f3e1c035c28b6398e00e546040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506104ee9050565b611c88600435604080517fe6ce3a6a000000000000000000000000000000000000000000000000000000008152601060048201527f3e3d000000000000000000000000000000000000000000000000000000000000602482015260448101839052905160009173ce642b6a82e72147ceade0e72c786ba8eaeb31d79163e6ce3a6a916064818101926020929091908290030181878760325a03f2156100025750506040515191506108989050565b611c88604080517f8f00e61a00000000000000000000000000000000000000000000000000000000815260066004820152905160009173c895c144d0b0f88417cf9e14e03e6abc82c0af3f91638f00e61a916024818101926020929091908290030181878760325a03f2156100025750506040515191506104ee9050565b611c886004356000611e113383610f5f565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63dd382dd36000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63aebd65476000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611c886004356040805160e560020a6304b47bb902815260066004820152600160a060020a0383166024820152905160009173c895c144d0b0f88417cf9e14e03e6abc82c0af3f9163968f7720916044818101926020929091908290030181878760325a03f2156100025750506040515191506108989050565b611c88604080517fc75e8f8800000000000000000000000000000000000000000000000000000000815260066004820152905160009173c895c144d0b0f88417cf9e14e03e6abc82c0af3f9163c75e8f88916024818101926020929091908290030181878760325a03f2156100025750506040515191506104ee9050565b611cb760408051602081810183526000825282516003805460026000196001831615610100020190911604601f81018490048402830184019095528482529293909291830182828015611fef5780601f10611fc457610100808354040283529160200191611fef565b611264604080517fa89713750000000000000000000000000000000000000000000000000000000081526000600482018181526024830193845236604484018190527327b1b436e4699a012cc8698e33c8f3e1c035c28b9463a89713759484939190606401848480828437820191505094505050505060006040518083038160008760325a03f215610002575050604080516005547f321f45840000000000000000000000000000000000000000000000000000000082526004820152905163321f4584916024818101926000929091908290030181838760325a03f21561000257505050565b611c886004356000818152600e6020526040902060030154610898565b611c8860007327b1b436e4699a012cc8698e33c8f3e1c035c28b63c0f688596040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506104ee9050565b61126460043573c895c144d0b0f88417cf9e14e03e6abc82c0af3f63dd8abb6c60063384611db7610577565b611c88600073c895c144d0b0f88417cf9e14e03e6abc82c0af3f6354e37911600633611e18610577565b611c886007546104ee565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63125935846000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b6112646102ee565b611c886004356000818152601360209081526040805181842060038101546004828101547f38f4c9eb0000000000000000000000000000000000000000000000000000000085526006918501919091526024840182905260ff160160448301529151919273c895c144d0b0f88417cf9e14e03e6abc82c0af3f926338f4c9eb9260648181019392918290030181888760325a03f21561000257505060405151949350505050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63fae644646000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63b3a5e2556000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611c886006546104ee565b6112646004355b604080517fb1df3d8000000000000000000000000000000000000000000000000000000000815260126004820152600160a060020a0383166024820152346044820152905173d3cb18959b0435864ff33010fa83be60afc04b229163b1df3d80916064828101926020929190829003018160008760325a03f215610002575050604080517f5548c837000000000000000000000000000000000000000000000000000000008152600160a060020a033381166004830152841660248201523460448201529051635548c837916064818101926000929091908290030181838760325a03f215610002575050505b50565b611264600435602435604435606435611ffb8484848460ff6000610a89565b611c886004356000818152600e6020526040902060010154610898565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b63c9abdb7c6000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611c8860043560007327b1b436e4699a012cc8698e33c8f3e1c035c28b6386b0aac96000600050846040518360e060020a028152600401808381526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515191506108989050565b611264600435604080517f25fea09900000000000000000000000000000000000000000000000000000000815260006004820181905260248201849052600160a060020a033316604483015291517327b1b436e4699a012cc8698e33c8f3e1c035c28b926325fea09992606481810193918290030181838760325a03f2156100025750505050565b565b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015611d175780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600160a060020a0333166000818152601260205260408051818320547f5c54305e00000000000000000000000000000000000000000000000000000000825260048201949094526024810185905260448101939093525173d3cb18959b0435864ff33010fa83be60afc04b2292635c54305e9260648281019391928290030181838760325a03f2156100025750505050565b6040518560e060020a0281526004018085815260200184600160a060020a0316815260200183815260200182815260200194505050505060006040518083038160008760325a03f2156100025750505050565b90506104ee565b9050610898565b6040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f2156100025750506040515191506104ee9050565b6040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f2156100025750506040515191506108989050565b6040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f2156100025750506040805180517f6a704d7b000000000000000000000000000000000000000000000000000000008252600160a060020a033316600483015260248201819052915191935073c895c144d0b0f88417cf9e14e03e6abc82c0af3f9250636a704d7b9160448281019260009291908290030181838760325a03f2156100025750505050565b820191906000526020600020905b815481529060010190602001808311611f9b57829003601f168201915b50505050509050610898565b820191906000526020600020905b815481529060010190602001808311611fd257829003601f168201915b505050505090506104ee565b50505050565b5050505050565b7327b1b436e4699a012cc8698e33c8f3e1c035c28b635ca1bad5826040518260e060020a0281526004018082815260200191505060006040518083038160008760325a03f215610002575050505b505050505050505056",
+ "storage": {
+ "0x18b039f13c5f33908f0960616cb3e44029c716366508c54d555096d6e1fa5145": "0x00000000000000000000000000000000000000000000000008b83c417dd78000"
+ }
+ },
+ "0xd3cb18959b0435864ff33010fa83be60afc04b22": {
+ "balance": "0x0",
+ "code": "0x650105e11e10f850606060405236156100695760e060020a60003504635548c837811461006e5780635c54305e146100ca5780636b1039661461011e5780637fcf532c14610152578063b1df3d801461019e578063b5bc6dbb146101b7578063e62af6c1146101ee575b610007565b61022060043560243560443581600160a060020a031683600160a060020a03167f47a08955ce2b7f21ea62ff0024e1ea0ad87430953554a87e6bc65d777f18e639836040518082815260200191505060405180910390a3505050565b61022060043560243560443560408051838152602081018390528151600160a060020a038616927f9b24879829bed3003de08d5c5d7e18dcbb8dc76faebd95cafc5d4dec8c61a3a5928290030190a2505050565b6102206004356024356044355b600160a060020a038216600090815260208490526040902054808201101561023457610007565b610220600435602435604080518281529051600160a060020a038416917fd0c5cf41ee8ebf084ad0bce53de7cbc6e4693d9b53a4019ca36a2f91cdc20b3a919081900360200190a25050565b610222600435602435604435600061025784848461012b565b610222600435602435604435600160a060020a0382166000908152602084905260408120548290106102865761028e8484846101fb565b6102206004356024356044355b600160a060020a03821660009081526020849052604090205481111561026257610007565b005b60408051918252519081900360200190f35b600160a060020a0382166000908152602084905260409020805482019055505050565b5060015b9392505050565b600160a060020a038216600090815260208490526040902080548290039055505050565b50600061025b565b604051600160a060020a03841690600090849082818181858883f19350505050151561025757604051600160a060020a038416908390600081818185876185025a03f19250505015156102575761000756"
+ },
+ "0xd3cda913deb6f67967b99d67acdfa1712c293601": {
+ "balance": "0x1ff0509d9d6821e26",
+ "nonce": "138",
+ "code": "0x"
+ }
+ },
+ "config": {
+ "chainId": 1,
+ "homesteadBlock": 1150000,
+ "daoForkBlock": 1920000,
+ "daoForkSupport": true,
+ "eip150Block": 2463000,
+ "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
+ "eip155Block": 2675000,
+ "eip158Block": 2675000,
+ "byzantiumBlock": 4370000,
+ "constantinopleBlock": 7280000,
+ "petersburgBlock": 7280000,
+ "istanbulBlock": 9069000,
+ "muirGlacierBlock": 9200000,
+ "berlinBlock": 12244000,
+ "londonBlock": 12965000,
+ "arrowGlacierBlock": 13773000,
+ "grayGlacierBlock": 15050000,
+ "ethash": {}
+ }
+ },
+ "context": {
+ "number": "422909",
+ "difficulty": "5694909537365",
+ "timestamp": "1445530357",
+ "gasLimit": "3141592",
+ "miner": "0x2a65aca4d5fc5b5c859090a6c34d164135398226"
+ },
+ "input": "0xf86a818a850ba43b7400832d8a40942861bf89b6c640c79040d357c1e9513693ef5d3f808441c0e1b51ca0b8de64a9a04d699f5938efa5431ca7c80500f6accb329da43aadabd4eab84f17a035b969c198f694be991a2a5b287250e19e852efd0ccba30bd50707277bfbc9aa",
+ "tracerConfig": {
+ "diffMode": true
+ },
+ "result": {
+ "pre": {
+ "0x2861bf89b6c640c79040d357c1e9513693ef5d3f": {
+ "balance": "0x0",
+ "code": "0x606060405236156100825760e060020a600035046312055e8f8114610084578063185061da146100b157806322beb9b9146100d5578063245a03ec146101865780633fa4f245146102a657806341c0e1b5146102af578063890eba68146102cb578063b29f0835146102de578063d6b4485914610308578063dd012a15146103b9575b005b6001805474ff0000000000000000000000000000000000000000191660a060020a60043502179055610082565b6100826001805475ff00000000000000000000000000000000000000000019169055565b61008260043560015460e060020a6352afbc3302606090815230600160a060020a039081166064527fb29f0835000000000000000000000000000000000000000000000000000000006084527fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060a45243840160c490815260ff60a060020a85041660e452600061010481905291909316926352afbc339261012492918183876161da5a03f1156100025750505050565b6100826004356024356001547fb0f07e440000000000000000000000000000000000000000000000000000000060609081526064839052600160a060020a039091169063b0f07e449060849060009060248183876161da5a03f150604080516001547f73657449742875696e74323536290000000000000000000000000000000000008252825191829003600e018220878352835192839003602001832060e060020a6352afbc33028452600160a060020a03308116600486015260e060020a9283900490920260248501526044840152438901606484015260a060020a820460ff1694830194909452600060a483018190529251931694506352afbc33935060c48181019391829003018183876161da5a03f115610002575050505050565b6103c460025481565b61008260005433600160a060020a039081169116146103ce575b565b6103c460015460a860020a900460ff1681565b6100826001805475ff000000000000000000000000000000000000000000191660a860020a179055565b61008260043560015460e060020a6352afbc3302606090815230600160a060020a039081166064527f185061da000000000000000000000000000000000000000000000000000000006084527fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060a45243840160c490815260ff60a060020a85041660e452600061010481905291909316926352afbc339261012492918183876161da5a03f1156100025750505050565b600435600255610082565b6060908152602090f35b6001547f6ff96d17000000000000000000000000000000000000000000000000000000006060908152600160a060020a0330811660645290911690632e1a7d4d908290636ff96d17906084906020906024816000876161da5a03f1156100025750506040805180517f2e1a7d4d0000000000000000000000000000000000000000000000000000000082526004820152905160248281019350600092829003018183876161da5a03f115610002575050600054600160a060020a03169050ff",
+ "storage": {
+ "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000d3cda913deb6f67967b99d67acdfa1712c293601",
+ "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000ff30c9e568f133adce1f1ea91e189613223fc461b9"
+ }
+ },
+ "0x2a65aca4d5fc5b5c859090a6c34d164135398226": {
+ "balance": "0x326601cc6cf364f6b9",
+ "nonce": 12122
+ },
+ "0xd3cda913deb6f67967b99d67acdfa1712c293601": {
+ "balance": "0x1ff0509d9d6821e26",
+ "nonce": 138
+ }
+ },
+ "post": {
+ "0x2a65aca4d5fc5b5c859090a6c34d164135398226": {
+ "balance": "0x326604ee5f5eecd2b9"
+ },
+ "0xd3cda913deb6f67967b99d67acdfa1712c293601": {
+ "balance": "0x1ff01e7e76afa4226",
+ "nonce": 139
+ }
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/util.go b/eth/tracers/internal/tracetest/util.go
new file mode 100644
index 0000000000..95d292c924
--- /dev/null
+++ b/eth/tracers/internal/tracetest/util.go
@@ -0,0 +1,72 @@
+package tracetest
+
+import (
+ "strings"
+ "unicode"
+
+ // Force-load native and js packages, to trigger registration
+ _ "github.com/ethereum/go-ethereum/eth/tracers/js"
+ _ "github.com/ethereum/go-ethereum/eth/tracers/native"
+)
+
+// To generate a new callTracer test, copy paste the makeTest method below into
+// a Geth console and call it with a transaction hash you which to export.
+
+/*
+// makeTest generates a callTracer test by running a prestate reassembled and a
+// call trace run, assembling all the gathered information into a test case.
+var makeTest = function(tx, rewind) {
+ // Generate the genesis block from the block, transaction and prestate data
+ var block = eth.getBlock(eth.getTransaction(tx).blockHash);
+ var genesis = eth.getBlock(block.parentHash);
+
+ delete genesis.gasUsed;
+ delete genesis.logsBloom;
+ delete genesis.parentHash;
+ delete genesis.receiptsRoot;
+ delete genesis.sha3Uncles;
+ delete genesis.size;
+ delete genesis.transactions;
+ delete genesis.transactionsRoot;
+ delete genesis.uncles;
+
+ genesis.gasLimit = genesis.gasLimit.toString();
+ genesis.number = genesis.number.toString();
+ genesis.timestamp = genesis.timestamp.toString();
+
+ genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind});
+ for (var key in genesis.alloc) {
+ var nonce = genesis.alloc[key].nonce;
+ if (nonce) {
+ genesis.alloc[key].nonce = nonce.toString();
+ }
+ }
+ genesis.config = admin.nodeInfo.protocols.eth.config;
+
+ // Generate the call trace and produce the test input
+ var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind});
+ delete result.time;
+
+ console.log(JSON.stringify({
+ genesis: genesis,
+ context: {
+ number: block.number.toString(),
+ difficulty: block.difficulty,
+ timestamp: block.timestamp.toString(),
+ gasLimit: block.gasLimit.toString(),
+ miner: block.miner,
+ },
+ input: eth.getRawTransaction(tx),
+ result: result,
+ }, null, 2));
+}
+*/
+
+// camel converts a snake cased input string into a camel cased output.
+func camel(str string) string {
+ pieces := strings.Split(str, "_")
+ for i := 1; i < len(pieces); i++ {
+ pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
+ }
+ return strings.Join(pieces, "")
+}
diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go
index 8238bb603d..8e52f5b210 100644
--- a/eth/tracers/js/goja.go
+++ b/eth/tracers/js/goja.go
@@ -21,7 +21,6 @@ import (
"errors"
"fmt"
"math/big"
- "time"
"github.com/dop251/goja"
@@ -33,6 +32,10 @@ import (
jsassets "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers"
)
+const (
+ memoryPadLimit = 1024 * 1024
+)
+
var assetTracers = make(map[string]string)
// init retrieves the JavaScript transaction tracers included in go-ethereum.
@@ -42,7 +45,16 @@ func init() {
if err != nil {
panic(err)
}
- tracers.RegisterLookup(true, newJsTracer)
+ type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
+ lookup := func(code string) ctorFn {
+ return func(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+ return newJsTracer(code, ctx, cfg)
+ }
+ }
+ for name, code := range assetTracers {
+ tracers.DefaultDirectory.Register(name, lookup(code), true)
+ }
+ tracers.DefaultDirectory.RegisterJSEval(newJsTracer)
}
// bigIntProgram is compiled once and the exported function mostly invoked to convert
@@ -119,16 +131,14 @@ type jsTracer struct {
frameResultValue goja.Value
}
-// newJsTracer instantiates a new JS tracer instance. code is either
-// the name of a built-in JS tracer or a Javascript snippet which
-// evaluates to an expression returning an object with certain methods.
+// newJsTracer instantiates a new JS tracer instance. code is a
+// Javascript snippet which evaluates to an expression returning
+// an object with certain methods:
+//
// The methods `result` and `fault` are required to be present.
// The methods `step`, `enter`, and `exit` are optional, but note that
// `enter` and `exit` always go together.
func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
- if c, ok := assetTracers[code]; ok {
- code = c
- }
vm := goja.New()
// By default field names are exported to JS as is, i.e. capitalized.
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
@@ -210,9 +220,11 @@ func (t *jsTracer) CaptureTxStart(gasLimit uint64) {
t.gasLimit = gasLimit
}
-// CaptureTxStart implements the Tracer interface and is invoked at the end of
+// CaptureTxEnd implements the Tracer interface and is invoked at the end of
// transaction processing.
-func (t *jsTracer) CaptureTxEnd(restGas uint64) {}
+func (t *jsTracer) CaptureTxEnd(restGas uint64) {
+ t.ctx["gasUsed"] = t.vm.ToValue(t.gasLimit - restGas)
+}
// CaptureStart implements the Tracer interface to initialize the tracing operation.
func (t *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
@@ -237,9 +249,8 @@ func (t *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Addr
t.ctx["value"] = valueBig
t.ctx["block"] = t.vm.ToValue(env.Context.BlockNumber.Uint64())
// Update list of precompiles based on current block
- rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil)
+ rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time)
t.activePrecompiles = vm.ActivePrecompiles(rules)
- t.ctx["intrinsicGas"] = t.vm.ToValue(t.gasLimit - gas)
}
// CaptureState implements the Tracer interface to trace a single step of VM execution.
@@ -256,10 +267,11 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
log.memory.memory = scope.Memory
log.stack.stack = scope.Stack
log.contract.contract = scope.Contract
- log.pc = uint(pc)
- log.gas = uint(gas)
- log.cost = uint(cost)
- log.depth = uint(depth)
+ log.pc = pc
+ log.gas = gas
+ log.cost = cost
+ log.refund = t.env.StateDB.GetRefund()
+ log.depth = depth
log.err = err
if _, err := t.step(t.obj, t.logValue, t.dbValue); err != nil {
t.onError("step", err)
@@ -279,10 +291,8 @@ func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
}
// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, duration time.Duration, err error) {
+func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
t.ctx["output"] = t.vm.ToValue(output)
- t.ctx["time"] = t.vm.ToValue(duration.String())
- t.ctx["gasUsed"] = t.vm.ToValue(gasUsed)
if err != nil {
t.ctx["error"] = t.vm.ToValue(err.Error())
}
@@ -561,10 +571,15 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
if end < begin || begin < 0 {
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
}
- if mo.memory.Len() < int(end) {
- return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), begin, end-begin)
+ mlen := mo.memory.Len()
+ if end-int64(mlen) > memoryPadLimit {
+ return nil, fmt.Errorf("tracer reached limit for padding memory slice: end %d, memorySize %d", end, mlen)
}
- return mo.memory.GetCopy(begin, end-begin), nil
+ slice := make([]byte, end-begin)
+ end = min(end, int64(mo.memory.Len()))
+ ptr := mo.memory.GetPtr(begin, end-begin)
+ copy(slice[:], ptr[:])
+ return slice, nil
}
func (mo *memoryObj) GetUint(addr int64) goja.Value {
@@ -907,33 +922,19 @@ type steplog struct {
stack *stackObj
contract *contractObj
- pc uint
- gas uint
- cost uint
- depth uint
- refund uint
+ pc uint64
+ gas uint64
+ cost uint64
+ depth int
+ refund uint64
err error
}
-func (l *steplog) GetPC() uint {
- return l.pc
-}
-
-func (l *steplog) GetGas() uint {
- return l.gas
-}
-
-func (l *steplog) GetCost() uint {
- return l.cost
-}
-
-func (l *steplog) GetDepth() uint {
- return l.depth
-}
-
-func (l *steplog) GetRefund() uint {
- return l.refund
-}
+func (l *steplog) GetPC() uint64 { return l.pc }
+func (l *steplog) GetGas() uint64 { return l.gas }
+func (l *steplog) GetCost() uint64 { return l.cost }
+func (l *steplog) GetDepth() int { return l.depth }
+func (l *steplog) GetRefund() uint64 { return l.refund }
func (l *steplog) GetError() goja.Value {
if l.err != nil {
@@ -958,3 +959,10 @@ func (l *steplog) setupObject() *goja.Object {
o.Set("contract", l.contract.setupObject())
return o
}
+
+func min(a, b int64) int64 {
+ if a < b {
+ return a
+ }
+ return b
+}
diff --git a/eth/tracers/js/internal/tracers/call_tracer_legacy.js b/eth/tracers/js/internal/tracers/call_tracer_legacy.js
index 3ca7377738..451a644b91 100644
--- a/eth/tracers/js/internal/tracers/call_tracer_legacy.js
+++ b/eth/tracers/js/internal/tracers/call_tracer_legacy.js
@@ -204,7 +204,6 @@
gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16),
input: toHex(ctx.input),
output: toHex(ctx.output),
- time: ctx.time,
};
if (this.callstack[0].calls !== undefined) {
result.calls = this.callstack[0].calls;
@@ -234,7 +233,6 @@
input: call.input,
output: call.output,
error: call.error,
- time: call.time,
calls: call.calls,
}
for (var key in sorted) {
diff --git a/eth/tracers/js/internal/tracers/prestate_tracer_legacy.js b/eth/tracers/js/internal/tracers/prestate_tracer_legacy.js
index 77f25209cd..2757b8b141 100644
--- a/eth/tracers/js/internal/tracers/prestate_tracer_legacy.js
+++ b/eth/tracers/js/internal/tracers/prestate_tracer_legacy.js
@@ -62,7 +62,7 @@
var toBal = bigInt(this.prestate[toHex(ctx.to)].balance.slice(2), 16);
this.prestate[toHex(ctx.to)].balance = '0x'+toBal.subtract(ctx.value).toString(16);
- this.prestate[toHex(ctx.from)].balance = '0x'+fromBal.add(ctx.value).add((ctx.gasUsed + ctx.intrinsicGas) * ctx.gasPrice).toString(16);
+ this.prestate[toHex(ctx.from)].balance = '0x'+fromBal.add(ctx.value).add(ctx.gasUsed * ctx.gasPrice).toString(16);
// Decrement the caller's nonce, and remove empty create targets
this.prestate[toHex(ctx.from)].nonce--;
diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go
index 80a002d5af..524d174749 100644
--- a/eth/tracers/js/tracer_test.go
+++ b/eth/tracers/js/tracer_test.go
@@ -60,7 +60,7 @@ func testCtx() *vmContext {
return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
}
-func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) {
+func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
var (
env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer})
gasLimit uint64 = 31000
@@ -69,13 +69,16 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
contract = vm.NewContract(account{}, account{}, value, startGas)
)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
+ if contractCode != nil {
+ contract.Code = contractCode
+ }
tracer.CaptureTxStart(gasLimit)
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
ret, err := env.Interpreter().Run(contract, []byte{}, false)
- tracer.CaptureEnd(ret, startGas-contract.Gas, 1, err)
+ tracer.CaptureEnd(ret, startGas-contract.Gas, err)
// Rest gas assumes no refund
- tracer.CaptureTxEnd(startGas - contract.Gas)
+ tracer.CaptureTxEnd(contract.Gas)
if err != nil {
return nil, err
}
@@ -83,35 +86,36 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
}
func TestTracer(t *testing.T) {
- execTracer := func(code string) ([]byte, string) {
+ execTracer := func(code string, contract []byte) ([]byte, string) {
t.Helper()
tracer, err := newJsTracer(code, nil, nil)
if err != nil {
t.Fatal(err)
}
- ret, err := runTrace(tracer, testCtx(), params.TestChainConfig)
+ ret, err := runTrace(tracer, testCtx(), params.TestChainConfig, contract)
if err != nil {
return nil, err.Error() // Stringify to allow comparison without nil checks
}
return ret, ""
}
for i, tt := range []struct {
- code string
- want string
- fail string
+ code string
+ want string
+ fail string
+ contract []byte
}{
{ // tests that we don't panic on bad arguments to memory access
code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}",
want: ``,
- fail: "tracer accessed out of bound memory: offset -1, end -2 at step (:1:53(15)) in server-side tracer function 'step'",
+ fail: "tracer accessed out of bound memory: offset -1, end -2 at step (:1:53(13)) in server-side tracer function 'step'",
}, { // tests that we don't panic on bad arguments to stack peeks
code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}",
want: ``,
- fail: "tracer accessed out of bound stack: size 0, index -1 at step (:1:53(13)) in server-side tracer function 'step'",
+ fail: "tracer accessed out of bound stack: size 0, index -1 at step (:1:53(11)) in server-side tracer function 'step'",
}, { // tests that we don't panic on bad arguments to memory getUint
code: "{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}",
want: ``,
- fail: "tracer accessed out of bound memory: available 0, offset -64, size 32 at step (:1:58(13)) in server-side tracer function 'step'",
+ fail: "tracer accessed out of bound memory: available 0, offset -64, size 32 at step (:1:58(11)) in server-side tracer function 'step'",
}, { // tests some general counting
code: "{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}",
want: `3`,
@@ -124,9 +128,9 @@ func TestTracer(t *testing.T) {
}, { // tests to-string of opcodes
code: "{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}",
want: `["PUSH1","PUSH1","STOP"]`,
- }, { // tests intrinsic gas
- code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed+'.'+ctx.intrinsicGas; }}",
- want: `"100000.6.21000"`,
+ }, { // tests gasUsed
+ code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed; }}",
+ want: `"100000.21006"`,
}, {
code: "{res: null, step: function(log) {}, fault: function() {}, result: function() { return toWord('0xffaa') }}",
want: `{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":255,"31":170}`,
@@ -139,9 +143,18 @@ func TestTracer(t *testing.T) {
}, {
code: "{res: null, step: function(log) { var address = Array.prototype.slice.call(log.contract.getAddress()); this.res = toAddress(address); }, fault: function() {}, result: function() { return this.res }}",
want: `{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0}`,
+ }, {
+ code: "{res: [], step: function(log) { var op = log.op.toString(); if (op === 'MSTORE8' || op === 'STOP') { this.res.push(log.memory.slice(0, 2)) } }, fault: function() {}, result: function() { return this.res }}",
+ want: `[{"0":0,"1":0},{"0":255,"1":0}]`,
+ contract: []byte{byte(vm.PUSH1), byte(0xff), byte(vm.PUSH1), byte(0x00), byte(vm.MSTORE8), byte(vm.STOP)},
+ }, {
+ code: "{res: [], step: function(log) { if (log.op.toString() === 'STOP') { this.res.push(log.memory.slice(5, 1025 * 1024)) } }, fault: function() {}, result: function() { return this.res }}",
+ want: "",
+ fail: "tracer reached limit for padding memory slice: end 1049600, memorySize 32 at step (:1:83(20)) in server-side tracer function 'step'",
+ contract: []byte{byte(vm.PUSH1), byte(0xff), byte(vm.PUSH1), byte(0x00), byte(vm.MSTORE8), byte(vm.STOP)},
},
} {
- if have, err := execTracer(tt.code); tt.want != string(have) || tt.fail != err {
+ if have, err := execTracer(tt.code, tt.contract); tt.want != string(have) || tt.fail != err {
t.Errorf("testcase %d: expected return value to be '%s' got '%s', error to be '%s' got '%s'\n\tcode: %v", i, tt.want, string(have), tt.fail, err, tt.code)
}
}
@@ -157,7 +170,7 @@ func TestHalt(t *testing.T) {
time.Sleep(1 * time.Second)
tracer.Stop(timeout)
}()
- if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); !strings.Contains(err.Error(), "stahp") {
+ if _, err = runTrace(tracer, testCtx(), params.TestChainConfig, nil); !strings.Contains(err.Error(), "stahp") {
t.Errorf("Expected timeout error, got %v", err)
}
}
@@ -193,7 +206,7 @@ func TestNoStepExec(t *testing.T) {
}
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0))
- tracer.CaptureEnd(nil, 0, 1, nil)
+ tracer.CaptureEnd(nil, 0, nil)
ret, err := tracer.GetResult()
if err != nil {
t.Fatal(err)
@@ -227,7 +240,7 @@ func TestIsPrecompile(t *testing.T) {
}
blockCtx := vm.BlockContext{BlockNumber: big.NewInt(150)}
- res, err := runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
+ res, err := runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg, nil)
if err != nil {
t.Error(err)
}
@@ -237,7 +250,7 @@ func TestIsPrecompile(t *testing.T) {
tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil)
blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)}
- res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
+ res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg, nil)
if err != nil {
t.Error(err)
}
diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go
index a8908094eb..766ee4e4b9 100644
--- a/eth/tracers/logger/access_list_tracer.go
+++ b/eth/tracers/logger/access_list_tracer.go
@@ -18,7 +18,6 @@ package logger
import (
"math/big"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
@@ -162,7 +161,7 @@ func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
}
-func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
+func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {}
func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}
diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go
index 07aa2f2b43..5e75318b9a 100644
--- a/eth/tracers/logger/logger.go
+++ b/eth/tracers/logger/logger.go
@@ -24,7 +24,6 @@ import (
"math/big"
"strings"
"sync/atomic"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -151,7 +150,6 @@ func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common.
func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
// If tracing was interrupted, set the error and stop
if atomic.LoadUint32(&l.interrupt) > 0 {
- l.env.Cancel()
return
}
// check if already accumulated the specified number of logs
@@ -220,7 +218,7 @@ func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, s
}
// CaptureEnd is called after the call finishes to finalize the tracing.
-func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
+func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
l.output = output
l.err = err
if l.cfg.Debug {
@@ -386,7 +384,7 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
}
-func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) {
+func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}
diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go
index 838d5017b8..a2cb4cd9fc 100644
--- a/eth/tracers/logger/logger_json.go
+++ b/eth/tracers/logger/logger_json.go
@@ -20,7 +20,6 @@ import (
"encoding/json"
"io"
"math/big"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
@@ -80,18 +79,17 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
}
// CaptureEnd is triggered at end of execution.
-func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
+func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
type endLog struct {
Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
- Time time.Duration `json:"time"`
Err string `json:"error,omitempty"`
}
var errMsg string
if err != nil {
errMsg = err.Error()
}
- l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg})
+ l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg})
}
func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go
index 34e608bfd6..1b4649baa3 100644
--- a/eth/tracers/native/4byte.go
+++ b/eth/tracers/native/4byte.go
@@ -21,7 +21,6 @@ import (
"math/big"
"strconv"
"sync/atomic"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
@@ -29,7 +28,7 @@ import (
)
func init() {
- register("4byteTracer", newFourByteTracer)
+ tracers.DefaultDirectory.Register("4byteTracer", newFourByteTracer, false)
}
// fourByteTracer searches for 4byte-identifiers, and collects them for post-processing.
@@ -37,16 +36,17 @@ func init() {
// a reversed signature can be matched against the size of the data.
//
// Example:
-// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
-// {
-// 0x27dc297e-128: 1,
-// 0x38cc4831-0: 2,
-// 0x524f3889-96: 1,
-// 0xadf59f99-288: 1,
-// 0xc281d19e-0: 1
-// }
+//
+// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
+// {
+// 0x27dc297e-128: 1,
+// 0x38cc4831-0: 2,
+// 0x524f3889-96: 1,
+// 0xadf59f99-288: 1,
+// 0xc281d19e-0: 1
+// }
type fourByteTracer struct {
- env *vm.EVM
+ noopTracer
ids map[string]int // ids aggregates the 4byte ids found
interrupt uint32 // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption
@@ -80,10 +80,8 @@ func (t *fourByteTracer) store(id []byte, size int) {
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
- t.env = env
-
// Update list of precompiles based on current block
- rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil)
+ rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time)
t.activePrecompiles = vm.ActivePrecompiles(rules)
// Save the outer calldata also
@@ -92,15 +90,10 @@ func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to commo
}
}
-// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
-func (t *fourByteTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
-}
-
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
// Skip if tracing was interrupted
if atomic.LoadUint32(&t.interrupt) > 0 {
- t.env.Cancel()
return
}
if len(input) < 4 {
@@ -118,23 +111,6 @@ func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to comm
t.store(input[0:4], len(input)-4)
}
-// CaptureExit is called when EVM exits a scope, even if the scope didn't
-// execute any code.
-func (t *fourByteTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
-}
-
-// CaptureFault implements the EVMLogger interface to trace an execution fault.
-func (t *fourByteTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
-}
-
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *fourByteTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
-}
-
-func (*fourByteTracer) CaptureTxStart(gasLimit uint64) {}
-
-func (*fourByteTracer) CaptureTxEnd(restGas uint64) {}
-
// GetResult returns the json-encoded nested list of call traces, and any
// error arising from the encoding or forceful termination (via `Stop`).
func (t *fourByteTracer) GetResult() (json.RawMessage, error) {
@@ -150,3 +126,7 @@ func (t *fourByteTracer) Stop(err error) {
t.reason = err
atomic.StoreUint32(&t.interrupt, 1)
}
+
+func bytesToHex(s []byte) string {
+ return "0x" + common.Bytes2Hex(s)
+}
diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go
index 7af0e658a8..75103f64cc 100644
--- a/eth/tracers/native/call.go
+++ b/eth/tracers/native/call.go
@@ -20,43 +20,95 @@ import (
"encoding/json"
"errors"
"math/big"
- "strconv"
- "strings"
"sync/atomic"
- "time"
+ "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
)
+//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
+
func init() {
- register("callTracer", newCallTracer)
+ tracers.DefaultDirectory.Register("callTracer", newCallTracer, false)
+}
+
+type callLog struct {
+ Address common.Address `json:"address"`
+ Topics []common.Hash `json:"topics"`
+ Data hexutil.Bytes `json:"data"`
}
type callFrame struct {
- Type string `json:"type"`
- From string `json:"from"`
- To string `json:"to,omitempty"`
- Value string `json:"value,omitempty"`
- Gas string `json:"gas"`
- GasUsed string `json:"gasUsed"`
- Input string `json:"input"`
- Output string `json:"output,omitempty"`
- Error string `json:"error,omitempty"`
- Calls []callFrame `json:"calls,omitempty"`
+ Type vm.OpCode `json:"-"`
+ From common.Address `json:"from"`
+ Gas uint64 `json:"gas"`
+ GasUsed uint64 `json:"gasUsed"`
+ To common.Address `json:"to,omitempty" rlp:"optional"`
+ Input []byte `json:"input" rlp:"optional"`
+ Output []byte `json:"output,omitempty" rlp:"optional"`
+ Error string `json:"error,omitempty" rlp:"optional"`
+ RevertReason string `json:"revertReason,omitempty"`
+ Calls []callFrame `json:"calls,omitempty" rlp:"optional"`
+ Logs []callLog `json:"logs,omitempty" rlp:"optional"`
+ // Placed at end on purpose. The RLP will be decoded to 0 instead of
+ // nil if there are non-empty elements after in the struct.
+ Value *big.Int `json:"value,omitempty" rlp:"optional"`
+}
+
+func (f callFrame) TypeString() string {
+ return f.Type.String()
+}
+
+func (f callFrame) failed() bool {
+ return len(f.Error) > 0
+}
+
+func (f *callFrame) processOutput(output []byte, err error) {
+ output = common.CopyBytes(output)
+ if err == nil {
+ f.Output = output
+ return
+ }
+ f.Error = err.Error()
+ if f.Type == vm.CREATE || f.Type == vm.CREATE2 {
+ f.To = common.Address{}
+ }
+ if !errors.Is(err, vm.ErrExecutionReverted) || len(output) == 0 {
+ return
+ }
+ f.Output = output
+ if len(output) < 4 {
+ return
+ }
+ if unpacked, err := abi.UnpackRevert(output); err == nil {
+ f.RevertReason = unpacked
+ }
+}
+
+type callFrameMarshaling struct {
+ TypeString string `json:"type"`
+ Gas hexutil.Uint64
+ GasUsed hexutil.Uint64
+ Value *hexutil.Big
+ Input hexutil.Bytes
+ Output hexutil.Bytes
}
type callTracer struct {
- env *vm.EVM
+ noopTracer
callstack []callFrame
config callTracerConfig
+ gasLimit uint64
interrupt uint32 // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption
}
type callTracerConfig struct {
OnlyTopCall bool `json:"onlyTopCall"` // If true, call tracer won't collect any subcalls
+ WithLog bool `json:"withLog"` // If true, call tracer will collect event logs
}
// newCallTracer returns a native go tracer which tracks
@@ -75,39 +127,58 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
- t.env = env
t.callstack[0] = callFrame{
- Type: "CALL",
- From: addrToHex(from),
- To: addrToHex(to),
- Input: bytesToHex(input),
- Gas: uintToHex(gas),
- Value: bigToHex(value),
+ Type: vm.CALL,
+ From: from,
+ To: to,
+ Input: common.CopyBytes(input),
+ Gas: gas,
+ Value: value,
}
if create {
- t.callstack[0].Type = "CREATE"
+ t.callstack[0].Type = vm.CREATE
}
}
// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
- t.callstack[0].GasUsed = uintToHex(gasUsed)
- if err != nil {
- t.callstack[0].Error = err.Error()
- if err.Error() == "execution reverted" && len(output) > 0 {
- t.callstack[0].Output = bytesToHex(output)
- }
- } else {
- t.callstack[0].Output = bytesToHex(output)
- }
+func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ t.callstack[0].processOutput(output, err)
}
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
-}
+ // Only logs need to be captured via opcode processing
+ if !t.config.WithLog {
+ return
+ }
+ // Avoid processing nested calls when only caring about top call
+ if t.config.OnlyTopCall && depth > 0 {
+ return
+ }
+ // Skip if tracing was interrupted
+ if atomic.LoadUint32(&t.interrupt) > 0 {
+ return
+ }
+ switch op {
+ case vm.LOG0, vm.LOG1, vm.LOG2, vm.LOG3, vm.LOG4:
+ size := int(op - vm.LOG0)
+
+ stack := scope.Stack
+ stackData := stack.Data()
+
+ // Don't modify the stack
+ mStart := stackData[len(stackData)-1]
+ mSize := stackData[len(stackData)-2]
+ topics := make([]common.Hash, size)
+ for i := 0; i < size; i++ {
+ topic := stackData[len(stackData)-2-(i+1)]
+ topics[i] = common.Hash(topic.Bytes32())
+ }
-// CaptureFault implements the EVMLogger interface to trace an execution fault.
-func (t *callTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
+ data := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
+ log := callLog{Address: scope.Contract.Address(), Topics: topics, Data: hexutil.Bytes(data)}
+ t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, log)
+ }
}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
@@ -117,17 +188,16 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
}
// Skip if tracing was interrupted
if atomic.LoadUint32(&t.interrupt) > 0 {
- t.env.Cancel()
return
}
call := callFrame{
- Type: typ.String(),
- From: addrToHex(from),
- To: addrToHex(to),
- Input: bytesToHex(input),
- Gas: uintToHex(gas),
- Value: bigToHex(value),
+ Type: typ,
+ From: from,
+ To: to,
+ Input: common.CopyBytes(input),
+ Gas: gas,
+ Value: value,
}
t.callstack = append(t.callstack, call)
}
@@ -147,21 +217,22 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
t.callstack = t.callstack[:size-1]
size -= 1
- call.GasUsed = uintToHex(gasUsed)
- if err == nil {
- call.Output = bytesToHex(output)
- } else {
- call.Error = err.Error()
- if call.Type == "CREATE" || call.Type == "CREATE2" {
- call.To = ""
- }
- }
+ call.GasUsed = gasUsed
+ call.processOutput(output, err)
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
}
-func (*callTracer) CaptureTxStart(gasLimit uint64) {}
+func (t *callTracer) CaptureTxStart(gasLimit uint64) {
+ t.gasLimit = gasLimit
+}
-func (*callTracer) CaptureTxEnd(restGas uint64) {}
+func (t *callTracer) CaptureTxEnd(restGas uint64) {
+ t.callstack[0].GasUsed = t.gasLimit - restGas
+ if t.config.WithLog {
+ // Logs are not emitted when the call fails
+ clearFailedLogs(&t.callstack[0], false)
+ }
+}
// GetResult returns the json-encoded nested list of call traces, and any
// error arising from the encoding or forceful termination (via `Stop`).
@@ -182,21 +253,15 @@ func (t *callTracer) Stop(err error) {
atomic.StoreUint32(&t.interrupt, 1)
}
-func bytesToHex(s []byte) string {
- return "0x" + common.Bytes2Hex(s)
-}
-
-func bigToHex(n *big.Int) string {
- if n == nil {
- return ""
+// clearFailedLogs clears the logs of a callframe and all its children
+// in case of execution failure.
+func clearFailedLogs(cf *callFrame, parentFailed bool) {
+ failed := cf.failed() || parentFailed
+ // Clear own logs
+ if failed {
+ cf.Logs = nil
+ }
+ for i := range cf.Calls {
+ clearFailedLogs(&cf.Calls[i], failed)
}
- return "0x" + n.Text(16)
-}
-
-func uintToHex(n uint64) string {
- return "0x" + strconv.FormatUint(n, 16)
-}
-
-func addrToHex(a common.Address) string {
- return strings.ToLower(a.Hex())
}
diff --git a/eth/tracers/native/gen_account_json.go b/eth/tracers/native/gen_account_json.go
new file mode 100644
index 0000000000..4c39cbc38c
--- /dev/null
+++ b/eth/tracers/native/gen_account_json.go
@@ -0,0 +1,56 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package native
+
+import (
+ "encoding/json"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*accountMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (a account) MarshalJSON() ([]byte, error) {
+ type account struct {
+ Balance *hexutil.Big `json:"balance,omitempty"`
+ Code hexutil.Bytes `json:"code,omitempty"`
+ Nonce uint64 `json:"nonce,omitempty"`
+ Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
+ }
+ var enc account
+ enc.Balance = (*hexutil.Big)(a.Balance)
+ enc.Code = a.Code
+ enc.Nonce = a.Nonce
+ enc.Storage = a.Storage
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (a *account) UnmarshalJSON(input []byte) error {
+ type account struct {
+ Balance *hexutil.Big `json:"balance,omitempty"`
+ Code *hexutil.Bytes `json:"code,omitempty"`
+ Nonce *uint64 `json:"nonce,omitempty"`
+ Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
+ }
+ var dec account
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Balance != nil {
+ a.Balance = (*big.Int)(dec.Balance)
+ }
+ if dec.Code != nil {
+ a.Code = *dec.Code
+ }
+ if dec.Nonce != nil {
+ a.Nonce = *dec.Nonce
+ }
+ if dec.Storage != nil {
+ a.Storage = dec.Storage
+ }
+ return nil
+}
diff --git a/eth/tracers/native/gen_callframe_json.go b/eth/tracers/native/gen_callframe_json.go
new file mode 100644
index 0000000000..21fc9e2b31
--- /dev/null
+++ b/eth/tracers/native/gen_callframe_json.go
@@ -0,0 +1,107 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package native
+
+import (
+ "encoding/json"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+var _ = (*callFrameMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (c callFrame) MarshalJSON() ([]byte, error) {
+ type callFrame0 struct {
+ Type vm.OpCode `json:"-"`
+ From common.Address `json:"from"`
+ Gas hexutil.Uint64 `json:"gas"`
+ GasUsed hexutil.Uint64 `json:"gasUsed"`
+ To common.Address `json:"to,omitempty" rlp:"optional"`
+ Input hexutil.Bytes `json:"input" rlp:"optional"`
+ Output hexutil.Bytes `json:"output,omitempty" rlp:"optional"`
+ Error string `json:"error,omitempty" rlp:"optional"`
+ RevertReason string `json:"revertReason,omitempty"`
+ Calls []callFrame `json:"calls,omitempty" rlp:"optional"`
+ Logs []callLog `json:"logs,omitempty" rlp:"optional"`
+ Value *hexutil.Big `json:"value,omitempty" rlp:"optional"`
+ TypeString string `json:"type"`
+ }
+ var enc callFrame0
+ enc.Type = c.Type
+ enc.From = c.From
+ enc.Gas = hexutil.Uint64(c.Gas)
+ enc.GasUsed = hexutil.Uint64(c.GasUsed)
+ enc.To = c.To
+ enc.Input = c.Input
+ enc.Output = c.Output
+ enc.Error = c.Error
+ enc.RevertReason = c.RevertReason
+ enc.Calls = c.Calls
+ enc.Logs = c.Logs
+ enc.Value = (*hexutil.Big)(c.Value)
+ enc.TypeString = c.TypeString()
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (c *callFrame) UnmarshalJSON(input []byte) error {
+ type callFrame0 struct {
+ Type *vm.OpCode `json:"-"`
+ From *common.Address `json:"from"`
+ Gas *hexutil.Uint64 `json:"gas"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed"`
+ To *common.Address `json:"to,omitempty" rlp:"optional"`
+ Input *hexutil.Bytes `json:"input" rlp:"optional"`
+ Output *hexutil.Bytes `json:"output,omitempty" rlp:"optional"`
+ Error *string `json:"error,omitempty" rlp:"optional"`
+ RevertReason *string `json:"revertReason,omitempty"`
+ Calls []callFrame `json:"calls,omitempty" rlp:"optional"`
+ Logs []callLog `json:"logs,omitempty" rlp:"optional"`
+ Value *hexutil.Big `json:"value,omitempty" rlp:"optional"`
+ }
+ var dec callFrame0
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Type != nil {
+ c.Type = *dec.Type
+ }
+ if dec.From != nil {
+ c.From = *dec.From
+ }
+ if dec.Gas != nil {
+ c.Gas = uint64(*dec.Gas)
+ }
+ if dec.GasUsed != nil {
+ c.GasUsed = uint64(*dec.GasUsed)
+ }
+ if dec.To != nil {
+ c.To = *dec.To
+ }
+ if dec.Input != nil {
+ c.Input = *dec.Input
+ }
+ if dec.Output != nil {
+ c.Output = *dec.Output
+ }
+ if dec.Error != nil {
+ c.Error = *dec.Error
+ }
+ if dec.RevertReason != nil {
+ c.RevertReason = *dec.RevertReason
+ }
+ if dec.Calls != nil {
+ c.Calls = dec.Calls
+ }
+ if dec.Logs != nil {
+ c.Logs = dec.Logs
+ }
+ if dec.Value != nil {
+ c.Value = (*big.Int)(dec.Value)
+ }
+ return nil
+}
diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go
new file mode 100644
index 0000000000..db8ddd6438
--- /dev/null
+++ b/eth/tracers/native/mux.go
@@ -0,0 +1,138 @@
+// 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 native
+
+import (
+ "encoding/json"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers"
+)
+
+func init() {
+ tracers.DefaultDirectory.Register("muxTracer", newMuxTracer, false)
+}
+
+// muxTracer is a go implementation of the Tracer interface which
+// runs multiple tracers in one go.
+type muxTracer struct {
+ names []string
+ tracers []tracers.Tracer
+}
+
+// newMuxTracer returns a new mux tracer.
+func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+ var config map[string]json.RawMessage
+ if cfg != nil {
+ if err := json.Unmarshal(cfg, &config); err != nil {
+ return nil, err
+ }
+ }
+ objects := make([]tracers.Tracer, 0, len(config))
+ names := make([]string, 0, len(config))
+ for k, v := range config {
+ t, err := tracers.DefaultDirectory.New(k, ctx, v)
+ if err != nil {
+ return nil, err
+ }
+ objects = append(objects, t)
+ names = append(names, k)
+ }
+
+ return &muxTracer{names: names, tracers: objects}, nil
+}
+
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
+func (t *muxTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ for _, t := range t.tracers {
+ t.CaptureStart(env, from, to, create, input, gas, value)
+ }
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ for _, t := range t.tracers {
+ t.CaptureEnd(output, gasUsed, err)
+ }
+}
+
+// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
+func (t *muxTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+ for _, t := range t.tracers {
+ t.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
+ }
+}
+
+// CaptureFault implements the EVMLogger interface to trace an execution fault.
+func (t *muxTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
+ for _, t := range t.tracers {
+ t.CaptureFault(pc, op, gas, cost, scope, depth, err)
+ }
+}
+
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ for _, t := range t.tracers {
+ t.CaptureEnter(typ, from, to, input, gas, value)
+ }
+}
+
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
+func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
+ for _, t := range t.tracers {
+ t.CaptureExit(output, gasUsed, err)
+ }
+}
+
+func (t *muxTracer) CaptureTxStart(gasLimit uint64) {
+ for _, t := range t.tracers {
+ t.CaptureTxStart(gasLimit)
+ }
+}
+
+func (t *muxTracer) CaptureTxEnd(restGas uint64) {
+ for _, t := range t.tracers {
+ t.CaptureTxEnd(restGas)
+ }
+}
+
+// GetResult returns an empty json object.
+func (t *muxTracer) GetResult() (json.RawMessage, error) {
+ resObject := make(map[string]json.RawMessage)
+ for i, tt := range t.tracers {
+ r, err := tt.GetResult()
+ if err != nil {
+ return nil, err
+ }
+ resObject[t.names[i]] = r
+ }
+ res, err := json.Marshal(resObject)
+ if err != nil {
+ return nil, err
+ }
+ return res, nil
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (t *muxTracer) Stop(err error) {
+ for _, t := range t.tracers {
+ t.Stop(err)
+ }
+}
diff --git a/eth/tracers/native/noop.go b/eth/tracers/native/noop.go
index c252b2408f..3beecd8abf 100644
--- a/eth/tracers/native/noop.go
+++ b/eth/tracers/native/noop.go
@@ -19,7 +19,6 @@ package native
import (
"encoding/json"
"math/big"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
@@ -27,7 +26,7 @@ import (
)
func init() {
- register("noopTracer", newNoopTracer)
+ tracers.DefaultDirectory.Register("noopTracer", newNoopTracer, false)
}
// noopTracer is a go implementation of the Tracer interface which
@@ -44,7 +43,7 @@ func (t *noopTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad
}
// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
+func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
}
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go
index b513f383b9..948d09ef76 100644
--- a/eth/tracers/native/prestate.go
+++ b/eth/tracers/native/prestate.go
@@ -17,10 +17,10 @@
package native
import (
+ "bytes"
"encoding/json"
"math/big"
"sync/atomic"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -29,32 +29,63 @@ import (
"github.com/ethereum/go-ethereum/eth/tracers"
)
+//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
+
func init() {
- register("prestateTracer", newPrestateTracer)
+ tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
}
-type prestate = map[common.Address]*account
+type state = map[common.Address]*account
+
type account struct {
- Balance string `json:"balance"`
- Nonce uint64 `json:"nonce"`
- Code string `json:"code"`
- Storage map[common.Hash]common.Hash `json:"storage"`
+ Balance *big.Int `json:"balance,omitempty"`
+ Code []byte `json:"code,omitempty"`
+ Nonce uint64 `json:"nonce,omitempty"`
+ Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
+}
+
+func (a *account) exists() bool {
+ return a.Nonce > 0 || len(a.Code) > 0 || len(a.Storage) > 0 || (a.Balance != nil && a.Balance.Sign() != 0)
+}
+
+type accountMarshaling struct {
+ Balance *hexutil.Big
+ Code hexutil.Bytes
}
type prestateTracer struct {
+ noopTracer
env *vm.EVM
- prestate prestate
+ pre state
+ post state
create bool
to common.Address
gasLimit uint64 // Amount of gas bought for the whole tx
+ config prestateTracerConfig
interrupt uint32 // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption
+ created map[common.Address]bool
+ deleted map[common.Address]bool
+}
+
+type prestateTracerConfig struct {
+ DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
}
-func newPrestateTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
- // First callframe contains tx context info
- // and is populated on start and end.
- return &prestateTracer{prestate: prestate{}}, nil
+func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+ var config prestateTracerConfig
+ if cfg != nil {
+ if err := json.Unmarshal(cfg, &config); err != nil {
+ return nil, err
+ }
+ }
+ return &prestateTracer{
+ pre: state{},
+ post: state{},
+ config: config,
+ created: make(map[common.Address]bool),
+ deleted: make(map[common.Address]bool),
+ }, nil
}
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
@@ -65,27 +96,38 @@ func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to commo
t.lookupAccount(from)
t.lookupAccount(to)
+ t.lookupAccount(env.Context.Coinbase)
// The recipient balance includes the value transferred.
- toBal := hexutil.MustDecodeBig(t.prestate[to].Balance)
- toBal = new(big.Int).Sub(toBal, value)
- t.prestate[to].Balance = hexutil.EncodeBig(toBal)
+ toBal := new(big.Int).Sub(t.pre[to].Balance, value)
+ t.pre[to].Balance = toBal
// The sender balance is after reducing: value and gasLimit.
// We need to re-add them to get the pre-tx balance.
- fromBal := hexutil.MustDecodeBig(t.prestate[from].Balance)
+ fromBal := new(big.Int).Set(t.pre[from].Balance)
gasPrice := env.TxContext.GasPrice
consumedGas := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(t.gasLimit))
fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas))
- t.prestate[from].Balance = hexutil.EncodeBig(fromBal)
- t.prestate[from].Nonce--
+ t.pre[from].Balance = fromBal
+ t.pre[from].Nonce--
+
+ if create && t.config.DiffMode {
+ t.created[to] = true
+ }
}
// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
+func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ if t.config.DiffMode {
+ return
+ }
+
if t.create {
- // Exclude created contract.
- delete(t.prestate, t.to)
+ // Keep existing account prior to contract creation at that address
+ if s := t.pre[t.to]; s != nil && !s.exists() {
+ // Exclude newly created contract.
+ delete(t.pre, t.to)
+ }
}
}
@@ -94,53 +136,117 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
stack := scope.Stack
stackData := stack.Data()
stackLen := len(stackData)
+ caller := scope.Contract.Address()
switch {
case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE):
slot := common.Hash(stackData[stackLen-1].Bytes32())
- t.lookupStorage(scope.Contract.Address(), slot)
+ t.lookupStorage(caller, slot)
case stackLen >= 1 && (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT):
addr := common.Address(stackData[stackLen-1].Bytes20())
t.lookupAccount(addr)
+ if op == vm.SELFDESTRUCT {
+ t.deleted[caller] = true
+ }
case stackLen >= 5 && (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE):
addr := common.Address(stackData[stackLen-2].Bytes20())
t.lookupAccount(addr)
case op == vm.CREATE:
- addr := scope.Contract.Address()
- nonce := t.env.StateDB.GetNonce(addr)
- t.lookupAccount(crypto.CreateAddress(addr, nonce))
+ nonce := t.env.StateDB.GetNonce(caller)
+ addr := crypto.CreateAddress(caller, nonce)
+ t.lookupAccount(addr)
+ t.created[addr] = true
case stackLen >= 4 && op == vm.CREATE2:
offset := stackData[stackLen-2]
size := stackData[stackLen-3]
init := scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
inithash := crypto.Keccak256(init)
salt := stackData[stackLen-4]
- t.lookupAccount(crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), inithash))
+ addr := crypto.CreateAddress2(caller, salt.Bytes32(), inithash)
+ t.lookupAccount(addr)
+ t.created[addr] = true
}
}
-// CaptureFault implements the EVMLogger interface to trace an execution fault.
-func (t *prestateTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
+func (t *prestateTracer) CaptureTxStart(gasLimit uint64) {
+ t.gasLimit = gasLimit
}
-// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
-func (t *prestateTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
-}
+func (t *prestateTracer) CaptureTxEnd(restGas uint64) {
+ if !t.config.DiffMode {
+ return
+ }
-// CaptureExit is called when EVM exits a scope, even if the scope didn't
-// execute any code.
-func (t *prestateTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
-}
+ for addr, state := range t.pre {
+ // The deleted account's state is pruned from `post` but kept in `pre`
+ if _, ok := t.deleted[addr]; ok {
+ continue
+ }
+ modified := false
+ postAccount := &account{Storage: make(map[common.Hash]common.Hash)}
+ newBalance := t.env.StateDB.GetBalance(addr)
+ newNonce := t.env.StateDB.GetNonce(addr)
+ newCode := t.env.StateDB.GetCode(addr)
-func (t *prestateTracer) CaptureTxStart(gasLimit uint64) {
- t.gasLimit = gasLimit
-}
+ if newBalance.Cmp(t.pre[addr].Balance) != 0 {
+ modified = true
+ postAccount.Balance = newBalance
+ }
+ if newNonce != t.pre[addr].Nonce {
+ modified = true
+ postAccount.Nonce = newNonce
+ }
+ if !bytes.Equal(newCode, t.pre[addr].Code) {
+ modified = true
+ postAccount.Code = newCode
+ }
+
+ for key, val := range state.Storage {
+ // don't include the empty slot
+ if val == (common.Hash{}) {
+ delete(t.pre[addr].Storage, key)
+ }
-func (t *prestateTracer) CaptureTxEnd(restGas uint64) {}
+ newVal := t.env.StateDB.GetState(addr, key)
+ if val == newVal {
+ // Omit unchanged slots
+ delete(t.pre[addr].Storage, key)
+ } else {
+ modified = true
+ if newVal != (common.Hash{}) {
+ postAccount.Storage[key] = newVal
+ }
+ }
+ }
+
+ if modified {
+ t.post[addr] = postAccount
+ } else {
+ // if state is not modified, then no need to include into the pre state
+ delete(t.pre, addr)
+ }
+ }
+ // the new created contracts' prestate were empty, so delete them
+ for a := range t.created {
+ // the created contract maybe exists in statedb before the creating tx
+ if s := t.pre[a]; s != nil && !s.exists() {
+ delete(t.pre, a)
+ }
+ }
+}
// GetResult returns the json-encoded nested list of call traces, and any
// error arising from the encoding or forceful termination (via `Stop`).
func (t *prestateTracer) GetResult() (json.RawMessage, error) {
- res, err := json.Marshal(t.prestate)
+ var res []byte
+ var err error
+ if t.config.DiffMode {
+ res, err = json.Marshal(struct {
+ Post state `json:"post"`
+ Pre state `json:"pre"`
+ }{t.post, t.pre})
+ } else {
+ res, err = json.Marshal(t.pre)
+ }
if err != nil {
return nil, err
}
@@ -156,13 +262,14 @@ func (t *prestateTracer) Stop(err error) {
// lookupAccount fetches details of an account and adds it to the prestate
// if it doesn't exist there.
func (t *prestateTracer) lookupAccount(addr common.Address) {
- if _, ok := t.prestate[addr]; ok {
+ if _, ok := t.pre[addr]; ok {
return
}
- t.prestate[addr] = &account{
- Balance: bigToHex(t.env.StateDB.GetBalance(addr)),
+
+ t.pre[addr] = &account{
+ Balance: t.env.StateDB.GetBalance(addr),
Nonce: t.env.StateDB.GetNonce(addr),
- Code: bytesToHex(t.env.StateDB.GetCode(addr)),
+ Code: t.env.StateDB.GetCode(addr),
Storage: make(map[common.Hash]common.Hash),
}
}
@@ -171,8 +278,8 @@ func (t *prestateTracer) lookupAccount(addr common.Address) {
// it to the prestate of the given contract. It assumes `lookupAccount`
// has been performed on the contract before.
func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) {
- if _, ok := t.prestate[addr].Storage[key]; ok {
+ if _, ok := t.pre[addr].Storage[key]; ok {
return
}
- t.prestate[addr].Storage[key] = t.env.StateDB.GetState(addr, key)
+ t.pre[addr].Storage[key] = t.env.StateDB.GetState(addr, key)
}
diff --git a/eth/tracers/native/revertreason.go b/eth/tracers/native/revertreason.go
deleted file mode 100644
index d09b861009..0000000000
--- a/eth/tracers/native/revertreason.go
+++ /dev/null
@@ -1,108 +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 native
-
-import (
- "bytes"
- "encoding/json"
- "math/big"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/accounts/abi"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/eth/tracers"
-)
-
-func init() {
- register("revertReasonTracer", newRevertReasonTracer)
-}
-
-var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
-
-// revertReasonTracer is a go implementation of the Tracer interface which
-// track the error message or revert reason return by the contract.
-type revertReasonTracer struct {
- env *vm.EVM
- revertReason string // The revert reason return from the tx, if tx success, empty string return
- interrupt uint32 // Atomic flag to signal execution interruption
- reason error // Textual reason for the interruption
-}
-
-// newRevertReasonTracer returns a new revert reason tracer.
-func newRevertReasonTracer(_ *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
- return &revertReasonTracer{}, nil
-}
-
-// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
-func (t *revertReasonTracer) CaptureStart(env *vm.EVM, _ common.Address, _ common.Address, _ bool, _ []byte, _ uint64, _ *big.Int) {
- t.env = env
-}
-
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *revertReasonTracer) CaptureEnd(output []byte, _ uint64, _ time.Duration, err error) {
- if err != nil {
- if err == vm.ErrExecutionReverted && len(output) > 4 && bytes.Equal(output[:4], revertSelector) {
- errMsg, _ := abi.UnpackRevert(output)
- t.revertReason = err.Error() + ": " + errMsg
- } else {
- t.revertReason = err.Error()
- }
- }
-}
-
-// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
-func (t *revertReasonTracer) CaptureState(_ uint64, _ vm.OpCode, _, _ uint64, _ *vm.ScopeContext, _ []byte, _ int, _ error) {
-}
-
-// CaptureFault implements the EVMLogger interface to trace an execution fault.
-func (t *revertReasonTracer) CaptureFault(_ uint64, _ vm.OpCode, _, _ uint64, _ *vm.ScopeContext, _ int, _ error) {
-}
-
-// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
-func (t *revertReasonTracer) CaptureEnter(_ vm.OpCode, _ common.Address, _ common.Address, _ []byte, _ uint64, _ *big.Int) {
- // Skip if tracing was interrupted
- if atomic.LoadUint32(&t.interrupt) > 0 {
- t.env.Cancel()
- return
- }
-}
-
-// CaptureExit is called when EVM exits a scope, even if the scope didn't
-// execute any code.
-func (t *revertReasonTracer) CaptureExit(_ []byte, _ uint64, _ error) {}
-
-func (t *revertReasonTracer) CaptureTxStart(_ uint64) {}
-
-func (t *revertReasonTracer) CaptureTxEnd(_ uint64) {}
-
-// GetResult returns an error message json object.
-func (t *revertReasonTracer) GetResult() (json.RawMessage, error) {
- res, err := json.Marshal(t.revertReason)
- if err != nil {
- return nil, err
- }
- return res, t.reason
-}
-
-// Stop terminates execution of the tracer at the first opportune moment.
-func (t *revertReasonTracer) Stop(err error) {
- t.reason = err
- atomic.StoreUint32(&t.interrupt, 1)
-}
diff --git a/eth/tracers/native/tracer.go b/eth/tracers/native/tracer.go
deleted file mode 100644
index 9587caf19f..0000000000
--- a/eth/tracers/native/tracer.go
+++ /dev/null
@@ -1,83 +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 native is a collection of tracers written in go.
-
-In order to add a native tracer and have it compiled into the binary, a new
-file needs to be added to this folder, containing an implementation of the
-`eth.tracers.Tracer` interface.
-
-Aside from implementing the tracer, it also needs to register itself, using the
-`register` method -- and this needs to be done in the package initialization.
-
-Example:
-
-```golang
-func init() {
- register("noopTracerNative", newNoopTracer)
-}
-```
-*/
-package native
-
-import (
- "encoding/json"
- "errors"
-
- "github.com/ethereum/go-ethereum/eth/tracers"
-)
-
-// init registers itself this packages as a lookup for tracers.
-func init() {
- tracers.RegisterLookup(false, lookup)
-}
-
-// ctorFn is the constructor signature of a native tracer.
-type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
-
-/*
-ctors is a map of package-local tracer constructors.
-
-We cannot be certain about the order of init-functions within a package,
-The go spec (https://golang.org/ref/spec#Package_initialization) says
-
-> To ensure reproducible initialization behavior, build systems
-> are encouraged to present multiple files belonging to the same
-> package in lexical file name order to a compiler.
-
-Hence, we cannot make the map in init, but must make it upon first use.
-*/
-var ctors map[string]ctorFn
-
-// register is used by native tracers to register their presence.
-func register(name string, ctor ctorFn) {
- if ctors == nil {
- ctors = make(map[string]ctorFn)
- }
- ctors[name] = ctor
-}
-
-// lookup returns a tracer, if one can be matched to the given name.
-func lookup(name string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
- if ctors == nil {
- ctors = make(map[string]ctorFn)
- }
- if ctor, ok := ctors[name]; ok {
- return ctor(ctx, cfg)
- }
- return nil, errors.New("no tracer found")
-}
diff --git a/eth/tracers/tracers.go b/eth/tracers/tracers.go
index 3d2d1256c0..b93f7db6f5 100644
--- a/eth/tracers/tracers.go
+++ b/eth/tracers/tracers.go
@@ -19,7 +19,6 @@ package tracers
import (
"encoding/json"
- "errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
@@ -42,31 +41,55 @@ type Tracer interface {
Stop(err error)
}
-type lookupFunc func(string, *Context, json.RawMessage) (Tracer, error)
+type ctorFn func(*Context, json.RawMessage) (Tracer, error)
+type jsCtorFn func(string, *Context, json.RawMessage) (Tracer, error)
-var (
- lookups []lookupFunc
-)
+type elem struct {
+ ctor ctorFn
+ isJS bool
+}
-// RegisterLookup registers a method as a lookup for tracers, meaning that
-// users can invoke a named tracer through that lookup. If 'wildcard' is true,
-// then the lookup will be placed last. This is typically meant for interpreted
-// engines (js) which can evaluate dynamic user-supplied code.
-func RegisterLookup(wildcard bool, lookup lookupFunc) {
- if wildcard {
- lookups = append(lookups, lookup)
- } else {
- lookups = append([]lookupFunc{lookup}, lookups...)
- }
+// DefaultDirectory is the collection of tracers bundled by default.
+var DefaultDirectory = directory{elems: make(map[string]elem)}
+
+// directory provides functionality to lookup a tracer by name
+// and a function to instantiate it. It falls back to a JS code evaluator
+// if no tracer of the given name exists.
+type directory struct {
+ elems map[string]elem
+ jsEval jsCtorFn
+}
+
+// Register registers a method as a lookup for tracers, meaning that
+// users can invoke a named tracer through that lookup.
+func (d *directory) Register(name string, f ctorFn, isJS bool) {
+ d.elems[name] = elem{ctor: f, isJS: isJS}
+}
+
+// RegisterJSEval registers a tracer that is able to parse
+// dynamic user-provided JS code.
+func (d *directory) RegisterJSEval(f jsCtorFn) {
+ d.jsEval = f
}
// New returns a new instance of a tracer, by iterating through the
-// registered lookups.
-func New(code string, ctx *Context, cfg json.RawMessage) (Tracer, error) {
- for _, lookup := range lookups {
- if tracer, err := lookup(code, ctx, cfg); err == nil {
- return tracer, nil
- }
+// registered lookups. Name is either name of an existing tracer
+// or an arbitrary JS code.
+func (d *directory) New(name string, ctx *Context, cfg json.RawMessage) (Tracer, error) {
+ if elem, ok := d.elems[name]; ok {
+ return elem.ctor(ctx, cfg)
+ }
+ // Assume JS code
+ return d.jsEval(name, ctx, cfg)
+}
+
+// IsJS will return true if the given tracer will evaluate
+// JS code. Because code evaluation has high overhead, this
+// info will be used in determining fast and slow code paths.
+func (d *directory) IsJS(name string) bool {
+ if elem, ok := d.elems[name]; ok {
+ return elem.isJS
}
- return nil, errors.New("tracer not found")
+ // JS eval will execute JS code
+ return true
}
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index 12e01abae4..6cd5a022b1 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -56,7 +56,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
Transfer: core.Transfer,
Coinbase: common.Address{},
BlockNumber: new(big.Int).SetUint64(uint64(5)),
- Time: new(big.Int).SetUint64(uint64(5)),
+ Time: 5,
Difficulty: big.NewInt(0xffffffff),
GasLimit: gas,
BaseFee: big.NewInt(8),
diff --git a/eth/tracers/tracker.go b/eth/tracers/tracker.go
new file mode 100644
index 0000000000..136be37f5c
--- /dev/null
+++ b/eth/tracers/tracker.go
@@ -0,0 +1,109 @@
+// 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 tracers
+
+import (
+ "fmt"
+ "sync"
+)
+
+// stateTracker is an auxiliary tool used to cache the release functions of all
+// used trace states, and to determine whether the creation of trace state needs
+// to be paused in case there are too many states waiting for tracing.
+type stateTracker struct {
+ limit int // Maximum number of states allowed waiting for tracing
+ oldest uint64 // The number of the oldest state which is still using for trace
+ used []bool // List of flags indicating whether the trace state has been used up
+ releases []StateReleaseFunc // List of trace state release functions waiting to be called
+ cond *sync.Cond
+ lock *sync.RWMutex
+}
+
+// newStateTracker initializes the tracker with provided state limits and
+// the number of the first state that will be used.
+func newStateTracker(limit int, oldest uint64) *stateTracker {
+ lock := new(sync.RWMutex)
+ return &stateTracker{
+ limit: limit,
+ oldest: oldest,
+ used: make([]bool, limit),
+ cond: sync.NewCond(lock),
+ lock: lock,
+ }
+}
+
+// releaseState marks the state specified by the number as released and caches
+// the corresponding release functions internally.
+func (t *stateTracker) releaseState(number uint64, release StateReleaseFunc) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ // Set the state as used, the corresponding flag is indexed by
+ // the distance between the specified state and the oldest state
+ // which is still using for trace.
+ t.used[int(number-t.oldest)] = true
+
+ // If the oldest state is used up, update the oldest marker by moving
+ // it to the next state which is not used up.
+ if number == t.oldest {
+ var count int
+ for _, used := range t.used {
+ if !used {
+ break
+ }
+ count += 1
+ }
+ t.oldest += uint64(count)
+ copy(t.used, t.used[count:])
+
+ // Clean up the array tail since they are useless now.
+ for i := t.limit - count; i < t.limit; i++ {
+ t.used[i] = false
+ }
+ // Fire the signal to all waiters that oldest marker is updated.
+ t.cond.Broadcast()
+ }
+ t.releases = append(t.releases, release)
+}
+
+// callReleases invokes all cached release functions.
+func (t *stateTracker) callReleases() {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ for _, release := range t.releases {
+ release()
+ }
+ t.releases = t.releases[:0]
+}
+
+// wait blocks until the accumulated trace states are less than the limit.
+func (t *stateTracker) wait(number uint64) error {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ for {
+ if number < t.oldest {
+ return fmt.Errorf("invalid state number %d head %d", number, t.oldest)
+ }
+ if number < t.oldest+uint64(t.limit) {
+ // number is now within limit, wait over
+ return nil
+ }
+ t.cond.Wait()
+ }
+}
diff --git a/eth/tracers/tracker_test.go b/eth/tracers/tracker_test.go
new file mode 100644
index 0000000000..46f6ac8e51
--- /dev/null
+++ b/eth/tracers/tracker_test.go
@@ -0,0 +1,171 @@
+// 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 tracers
+
+import (
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestTracker(t *testing.T) {
+ var cases = []struct {
+ limit int
+ calls []uint64
+ expHead uint64
+ }{
+ // Release in order
+ {
+ limit: 3,
+ calls: []uint64{0, 1, 2},
+ expHead: 3,
+ },
+ {
+ limit: 3,
+ calls: []uint64{0, 1, 2, 3, 4, 5},
+ expHead: 6,
+ },
+
+ // Release out of order
+ {
+ limit: 3,
+ calls: []uint64{1, 2, 0},
+ expHead: 3,
+ },
+ {
+ limit: 3,
+ calls: []uint64{1, 2, 0, 5, 4, 3},
+ expHead: 6,
+ },
+ }
+ for _, c := range cases {
+ tracker := newStateTracker(c.limit, 0)
+ for _, call := range c.calls {
+ tracker.releaseState(call, func() {})
+ }
+ tracker.lock.RLock()
+ head := tracker.oldest
+ tracker.lock.RUnlock()
+
+ if head != c.expHead {
+ t.Fatalf("Unexpected head want %d got %d", c.expHead, head)
+ }
+ }
+
+ var calls = []struct {
+ number uint64
+ expUsed []bool
+ expHead uint64
+ }{
+ // Release the first one, update the oldest flag
+ {
+ number: 0,
+ expUsed: []bool{false, false, false, false, false},
+ expHead: 1,
+ },
+ // Release the second one, oldest shouldn't be updated
+ {
+ number: 2,
+ expUsed: []bool{false, true, false, false, false},
+ expHead: 1,
+ },
+ // Release the forth one, oldest shouldn't be updated
+ {
+ number: 4,
+ expUsed: []bool{false, true, false, true, false},
+ expHead: 1,
+ },
+ // Release the first one, the first two should all be cleaned,
+ // and the remaining flags should all be left-shifted.
+ {
+ number: 1,
+ expUsed: []bool{false, true, false, false, false},
+ expHead: 3,
+ },
+ // Release the first one, the first two should all be cleaned
+ {
+ number: 3,
+ expUsed: []bool{false, false, false, false, false},
+ expHead: 5,
+ },
+ }
+ tracker := newStateTracker(5, 0) // limit = 5, oldest = 0
+ for _, call := range calls {
+ tracker.releaseState(call.number, nil)
+ tracker.lock.RLock()
+ if !reflect.DeepEqual(tracker.used, call.expUsed) {
+ t.Fatalf("Unexpected used array")
+ }
+ if tracker.oldest != call.expHead {
+ t.Fatalf("Unexpected head")
+ }
+ tracker.lock.RUnlock()
+ }
+}
+
+func TestTrackerWait(t *testing.T) {
+ var (
+ tracker = newStateTracker(5, 0) // limit = 5, oldest = 0
+ result = make(chan error, 1)
+ doCall = func(number uint64) {
+ go func() {
+ result <- tracker.wait(number)
+ }()
+ }
+ checkNoWait = func() {
+ select {
+ case <-result:
+ return
+ case <-time.NewTimer(time.Second).C:
+ t.Fatal("No signal fired")
+ }
+ }
+ checkWait = func() {
+ select {
+ case <-result:
+ t.Fatal("Unexpected signal")
+ case <-time.NewTimer(time.Millisecond * 100).C:
+ }
+ }
+ )
+ // States [0, 5) should all be available
+ doCall(0)
+ checkNoWait()
+
+ doCall(4)
+ checkNoWait()
+
+ // State 5 is not available
+ doCall(5)
+ checkWait()
+
+ // States [1, 6) are available
+ tracker.releaseState(0, nil)
+ checkNoWait()
+
+ // States [1, 6) are available
+ doCall(7)
+ checkWait()
+
+ // States [2, 7) are available
+ tracker.releaseState(1, nil)
+ checkWait()
+
+ // States [3, 8) are available
+ tracker.releaseState(2, nil)
+ checkNoWait()
+}
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 8a00184318..460035f36a 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -131,10 +131,10 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 {
return nil, fmt.Errorf("server returned empty uncle list but block header indicates uncles")
}
- if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 {
+ if head.TxHash == types.EmptyTxsHash && len(body.Transactions) > 0 {
return nil, fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions")
}
- if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 {
+ if head.TxHash != types.EmptyTxsHash && len(body.Transactions) == 0 {
return nil, fmt.Errorf("server returned empty transaction list but block header indicates transactions")
}
// Load uncles because they are not included in the block response.
@@ -320,7 +320,7 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header)
// State Access
-// NetworkID returns the network ID (also known as the chain ID) for this chain.
+// NetworkID returns the network ID for this client.
func (ec *Client) NetworkID(ctx context.Context) (*big.Int, error) {
version := new(big.Int)
var ver string
@@ -570,6 +570,14 @@ func toBlockNumArg(number *big.Int) string {
if number.Cmp(pending) == 0 {
return "pending"
}
+ finalized := big.NewInt(int64(rpc.FinalizedBlockNumber))
+ if number.Cmp(finalized) == 0 {
+ return "finalized"
+ }
+ safe := big.NewInt(int64(rpc.SafeBlockNumber))
+ if number.Cmp(safe) == 0 {
+ return "safe"
+ }
return hexutil.EncodeBig(number)
}
diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go
index f2f4a5765d..8bd8b0614c 100644
--- a/ethclient/ethclient_test.go
+++ b/ethclient/ethclient_test.go
@@ -30,7 +30,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
@@ -238,7 +237,6 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
}
func generateTestChain() []*types.Block {
- db := rawdb.NewMemoryDatabase()
generate := func(i int, g *core.BlockGen) {
g.OffsetTime(5)
g.SetExtra([]byte("test"))
@@ -248,11 +246,8 @@ func generateTestChain() []*types.Block {
g.AddTx(testTx2)
}
}
- gblock := genesis.MustCommit(db)
- engine := ethash.NewFaker()
- blocks, _ := core.GenerateChain(genesis.Config, gblock, engine, db, 2, generate)
- blocks = append([]*types.Block{gblock}, blocks...)
- return blocks
+ _, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 2, generate)
+ return append([]*types.Block{genesis.ToBlock()}, blocks...)
}
func TestEthClient(t *testing.T) {
@@ -397,7 +392,7 @@ func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) {
t.Fatalf("unexpected error: %v", err)
}
- // Test tx in block interupted.
+ // Test tx in block interrupted.
ctx, cancel := context.WithCancel(context.Background())
cancel()
tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0)
diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go
index a86f4339f4..fdcfb9a0ac 100644
--- a/ethclient/gethclient/gethclient.go
+++ b/ethclient/gethclient/gethclient.go
@@ -19,6 +19,7 @@ package gethclient
import (
"context"
+ "encoding/json"
"math/big"
"runtime"
"runtime/debug"
@@ -95,6 +96,11 @@ func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []s
StorageProof []storageResult `json:"storageProof"`
}
+ // Avoid keys being 'null'.
+ if keys == nil {
+ keys = []string{}
+ }
+
var res accountResult
err := ec.c.CallContext(ctx, &res, "eth_getProof", account, keys, toBlockNumArg(blockNumber))
// Turn hexutils back to normal datatypes
@@ -118,15 +124,6 @@ func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []s
return &result, err
}
-// OverrideAccount specifies the state of an account to be overridden.
-type OverrideAccount struct {
- Nonce uint64 `json:"nonce"`
- Code []byte `json:"code"`
- Balance *big.Int `json:"balance"`
- State map[common.Hash]common.Hash `json:"state"`
- StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
-}
-
// CallContract executes a message call transaction, which is directly executed in the VM
// of the node, but never mined into the blockchain.
//
@@ -141,7 +138,7 @@ func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockN
var hex hexutil.Bytes
err := ec.c.CallContext(
ctx, &hex, "eth_call", toCallArg(msg),
- toBlockNumArg(blockNumber), toOverrideMap(overrides),
+ toBlockNumArg(blockNumber), overrides,
)
return hex, err
}
@@ -174,7 +171,12 @@ func (ec *Client) GetNodeInfo(ctx context.Context) (*p2p.NodeInfo, error) {
return &result, err
}
-// SubscribePendingTransactions subscribes to new pending transactions.
+// SubscribeFullPendingTransactions subscribes to new pending transactions.
+func (ec *Client) SubscribeFullPendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (*rpc.ClientSubscription, error) {
+ return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions", true)
+}
+
+// SubscribePendingTransactions subscribes to new pending transaction hashes.
func (ec *Client) SubscribePendingTransactions(ctx context.Context, ch chan<- common.Hash) (*rpc.ClientSubscription, error) {
return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions")
}
@@ -187,6 +189,14 @@ func toBlockNumArg(number *big.Int) string {
if number.Cmp(pending) == 0 {
return "pending"
}
+ finalized := big.NewInt(int64(rpc.FinalizedBlockNumber))
+ if number.Cmp(finalized) == 0 {
+ return "finalized"
+ }
+ safe := big.NewInt(int64(rpc.SafeBlockNumber))
+ if number.Cmp(safe) == 0 {
+ return "safe"
+ }
return hexutil.EncodeBig(number)
}
@@ -210,26 +220,48 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
return arg
}
-func toOverrideMap(overrides *map[common.Address]OverrideAccount) interface{} {
- if overrides == nil {
- return nil
- }
- type overrideAccount struct {
- Nonce hexutil.Uint64 `json:"nonce"`
- Code hexutil.Bytes `json:"code"`
- Balance *hexutil.Big `json:"balance"`
- State map[common.Hash]common.Hash `json:"state"`
- StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
- }
- result := make(map[common.Address]overrideAccount)
- for addr, override := range *overrides {
- result[addr] = overrideAccount{
- Nonce: hexutil.Uint64(override.Nonce),
- Code: override.Code,
- Balance: (*hexutil.Big)(override.Balance),
- State: override.State,
- StateDiff: override.StateDiff,
- }
- }
- return &result
+// OverrideAccount specifies the state of an account to be overridden.
+type OverrideAccount struct {
+ // Nonce sets nonce of the account. Note: the nonce override will only
+ // be applied when it is set to a non-zero value.
+ Nonce uint64
+
+ // Code sets the contract code. The override will be applied
+ // when the code is non-nil, i.e. setting empty code is possible
+ // using an empty slice.
+ Code []byte
+
+ // Balance sets the account balance.
+ Balance *big.Int
+
+ // State sets the complete storage. The override will be applied
+ // when the given map is non-nil. Using an empty map wipes the
+ // entire contract storage during the call.
+ State map[common.Hash]common.Hash
+
+ // StateDiff allows overriding individual storage slots.
+ StateDiff map[common.Hash]common.Hash
+}
+
+func (a OverrideAccount) MarshalJSON() ([]byte, error) {
+ type acc struct {
+ Nonce hexutil.Uint64 `json:"nonce,omitempty"`
+ Code string `json:"code,omitempty"`
+ Balance *hexutil.Big `json:"balance,omitempty"`
+ State interface{} `json:"state,omitempty"`
+ StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"`
+ }
+
+ output := acc{
+ Nonce: hexutil.Uint64(a.Nonce),
+ Balance: (*hexutil.Big)(a.Balance),
+ StateDiff: a.StateDiff,
+ }
+ if a.Code != nil {
+ output.Code = hexutil.Encode(a.Code)
+ }
+ if a.State != nil {
+ output.State = a.State
+ }
+ return json.Marshal(output)
}
diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go
index a0f4eaaf5d..a9637d182f 100644
--- a/ethclient/gethclient/gethclient_test.go
+++ b/ethclient/gethclient/gethclient_test.go
@@ -19,6 +19,7 @@ package gethclient
import (
"bytes"
"context"
+ "encoding/json"
"math/big"
"testing"
@@ -26,7 +27,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
@@ -78,10 +78,8 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
}
func generateTestChain() (*core.Genesis, []*types.Block) {
- db := rawdb.NewMemoryDatabase()
- config := params.AllEthashProtocolChanges
genesis := &core.Genesis{
- Config: config,
+ Config: params.AllEthashProtocolChanges,
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance, Storage: map[common.Hash]common.Hash{testSlot: testValue}}},
ExtraData: []byte("test genesis"),
Timestamp: 9000,
@@ -90,10 +88,8 @@ func generateTestChain() (*core.Genesis, []*types.Block) {
g.OffsetTime(5)
g.SetExtra([]byte("test"))
}
- gblock := genesis.MustCommit(db)
- engine := ethash.NewFaker()
- blocks, _ := core.GenerateChain(config, gblock, engine, db, 1, generate)
- blocks = append([]*types.Block{gblock}, blocks...)
+ _, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 1, generate)
+ blocks = append([]*types.Block{genesis.ToBlock()}, blocks...)
return genesis, blocks
}
@@ -110,10 +106,6 @@ func TestGethClient(t *testing.T) {
name string
test func(t *testing.T)
}{
- {
- "TestAccessList",
- func(t *testing.T) { testAccessList(t, client) },
- },
{
"TestGetProof",
func(t *testing.T) { testGetProof(t, client) },
@@ -127,17 +119,27 @@ func TestGethClient(t *testing.T) {
"TestGetNodeInfo",
func(t *testing.T) { testGetNodeInfo(t, client) },
}, {
- "TestSetHead",
- func(t *testing.T) { testSetHead(t, client) },
+ "TestSubscribePendingTxHashes",
+ func(t *testing.T) { testSubscribePendingTransactions(t, client) },
}, {
"TestSubscribePendingTxs",
- func(t *testing.T) { testSubscribePendingTransactions(t, client) },
+ func(t *testing.T) { testSubscribeFullPendingTransactions(t, client) },
}, {
"TestCallContract",
func(t *testing.T) { testCallContract(t, client) },
},
+ // The testaccesslist is a bit time-sensitive: the newTestBackend imports
+ // one block. The `testAcessList` fails if the miner has not yet created a
+ // new pending-block after the import event.
+ // Hence: this test should be last, execute the tests serially.
+ {
+ "TestAccessList",
+ func(t *testing.T) { testAccessList(t, client) },
+ }, {
+ "TestSetHead",
+ func(t *testing.T) { testSetHead(t, client) },
+ },
}
- t.Parallel()
for _, tt := range tests {
t.Run(tt.name, tt.test)
}
@@ -297,13 +299,47 @@ func testSubscribePendingTransactions(t *testing.T, client *rpc.Client) {
if err != nil {
t.Fatal(err)
}
- // Check that the transaction was send over the channel
+ // Check that the transaction was sent over the channel
hash := <-ch
if hash != signedTx.Hash() {
t.Fatalf("Invalid tx hash received, got %v, want %v", hash, signedTx.Hash())
}
}
+func testSubscribeFullPendingTransactions(t *testing.T, client *rpc.Client) {
+ ec := New(client)
+ ethcl := ethclient.NewClient(client)
+ // Subscribe to Transactions
+ ch := make(chan *types.Transaction)
+ ec.SubscribeFullPendingTransactions(context.Background(), ch)
+ // Send a transaction
+ chainID, err := ethcl.ChainID(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Create transaction
+ tx := types.NewTransaction(1, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil)
+ signer := types.LatestSignerForChainID(chainID)
+ signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
+ if err != nil {
+ t.Fatal(err)
+ }
+ signedTx, err := tx.WithSignature(signer, signature)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Send transaction
+ err = ethcl.SendTransaction(context.Background(), signedTx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Check that the transaction was sent over the channel
+ tx = <-ch
+ if tx.Hash() != signedTx.Hash() {
+ t.Fatalf("Invalid tx hash received, got %v, want %v", tx.Hash(), signedTx.Hash())
+ }
+}
+
func testCallContract(t *testing.T, client *rpc.Client) {
ec := New(client)
msg := ethereum.CallMsg{
@@ -327,3 +363,53 @@ func testCallContract(t *testing.T, client *rpc.Client) {
t.Fatalf("unexpected error: %v", err)
}
}
+
+func TestOverrideAccountMarshal(t *testing.T) {
+ om := map[common.Address]OverrideAccount{
+ common.Address{0x11}: OverrideAccount{
+ // Zero-valued nonce is not overriddden, but simply dropped by the encoder.
+ Nonce: 0,
+ },
+ common.Address{0xaa}: OverrideAccount{
+ Nonce: 5,
+ },
+ common.Address{0xbb}: OverrideAccount{
+ Code: []byte{1},
+ },
+ common.Address{0xcc}: OverrideAccount{
+ // 'code', 'balance', 'state' should be set when input is
+ // a non-nil but empty value.
+ Code: []byte{},
+ Balance: big.NewInt(0),
+ State: map[common.Hash]common.Hash{},
+ // For 'stateDiff' the behavior is different, empty map
+ // is ignored because it makes no difference.
+ StateDiff: map[common.Hash]common.Hash{},
+ },
+ }
+
+ marshalled, err := json.MarshalIndent(&om, "", " ")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ expected := `{
+ "0x1100000000000000000000000000000000000000": {},
+ "0xaa00000000000000000000000000000000000000": {
+ "nonce": "0x5"
+ },
+ "0xbb00000000000000000000000000000000000000": {
+ "code": "0x01"
+ },
+ "0xcc00000000000000000000000000000000000000": {
+ "code": "0x",
+ "balance": "0x0",
+ "state": {}
+ }
+}`
+
+ if string(marshalled) != expected {
+ t.Error("wrong output:", string(marshalled))
+ t.Error("want:", expected)
+ }
+}
diff --git a/ethdb/dbtest/testsuite.go b/ethdb/dbtest/testsuite.go
index 6b206af48d..e455215cb0 100644
--- a/ethdb/dbtest/testsuite.go
+++ b/ethdb/dbtest/testsuite.go
@@ -18,6 +18,7 @@ package dbtest
import (
"bytes"
+ "crypto/rand"
"reflect"
"sort"
"testing"
@@ -377,6 +378,101 @@ func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) {
})
}
+// BenchDatabaseSuite runs a suite of benchmarks against a KeyValueStore database
+// implementation.
+func BenchDatabaseSuite(b *testing.B, New func() ethdb.KeyValueStore) {
+ var (
+ keys, vals = makeDataset(1_000_000, 32, 32, false)
+ sKeys, sVals = makeDataset(1_000_000, 32, 32, true)
+ )
+ // Run benchmarks sequentially
+ b.Run("Write", func(b *testing.B) {
+ benchWrite := func(b *testing.B, keys, vals [][]byte) {
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ db := New()
+ defer db.Close()
+
+ for i := 0; i < len(keys); i++ {
+ db.Put(keys[i], vals[i])
+ }
+ }
+ b.Run("WriteSorted", func(b *testing.B) {
+ benchWrite(b, sKeys, sVals)
+ })
+ b.Run("WriteRandom", func(b *testing.B) {
+ benchWrite(b, keys, vals)
+ })
+ })
+ b.Run("Read", func(b *testing.B) {
+ benchRead := func(b *testing.B, keys, vals [][]byte) {
+ db := New()
+ defer db.Close()
+
+ for i := 0; i < len(keys); i++ {
+ db.Put(keys[i], vals[i])
+ }
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ for i := 0; i < len(keys); i++ {
+ db.Get(keys[i])
+ }
+ }
+ b.Run("ReadSorted", func(b *testing.B) {
+ benchRead(b, sKeys, sVals)
+ })
+ b.Run("ReadRandom", func(b *testing.B) {
+ benchRead(b, keys, vals)
+ })
+ })
+ b.Run("Iteration", func(b *testing.B) {
+ benchIteration := func(b *testing.B, keys, vals [][]byte) {
+ db := New()
+ defer db.Close()
+
+ for i := 0; i < len(keys); i++ {
+ db.Put(keys[i], vals[i])
+ }
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ it := db.NewIterator(nil, nil)
+ for it.Next() {
+ }
+ it.Release()
+ }
+ b.Run("IterationSorted", func(b *testing.B) {
+ benchIteration(b, sKeys, sVals)
+ })
+ b.Run("IterationRandom", func(b *testing.B) {
+ benchIteration(b, keys, vals)
+ })
+ })
+ b.Run("BatchWrite", func(b *testing.B) {
+ benchBatchWrite := func(b *testing.B, keys, vals [][]byte) {
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ db := New()
+ defer db.Close()
+
+ batch := db.NewBatch()
+ for i := 0; i < len(keys); i++ {
+ batch.Put(keys[i], vals[i])
+ }
+ batch.Write()
+ }
+ b.Run("BenchWriteSorted", func(b *testing.B) {
+ benchBatchWrite(b, sKeys, sVals)
+ })
+ b.Run("BenchWriteRandom", func(b *testing.B) {
+ benchBatchWrite(b, keys, vals)
+ })
+ })
+}
+
func iterateKeys(it ethdb.Iterator) []string {
keys := []string{}
for it.Next() {
@@ -386,3 +482,25 @@ func iterateKeys(it ethdb.Iterator) []string {
it.Release()
return keys
}
+
+// randomHash generates a random blob of data and returns it as a hash.
+func randBytes(len int) []byte {
+ buf := make([]byte, len)
+ if n, err := rand.Read(buf); n != len || err != nil {
+ panic(err)
+ }
+ return buf
+}
+
+func makeDataset(size, ksize, vsize int, order bool) ([][]byte, [][]byte) {
+ var keys [][]byte
+ var vals [][]byte
+ for i := 0; i < size; i += 1 {
+ keys = append(keys, randBytes(ksize))
+ vals = append(vals, randBytes(vsize))
+ }
+ if order {
+ sort.Slice(keys, func(i, j int) bool { return bytes.Compare(keys[i], keys[j]) < 0 })
+ }
+ return keys, vals
+}
diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go
index 15bd4e6eb3..ce13659d9d 100644
--- a/ethdb/leveldb/leveldb.go
+++ b/ethdb/leveldb/leveldb.go
@@ -63,18 +63,19 @@ type Database struct {
fn string // filename for reporting
db *leveldb.DB // LevelDB instance
- compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction
- compReadMeter metrics.Meter // Meter for measuring the data read during compaction
- compWriteMeter metrics.Meter // Meter for measuring the data written during compaction
- writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction
- writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction
- diskSizeGauge metrics.Gauge // Gauge for tracking the size of all the levels in the database
- diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read
- diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written
- memCompGauge metrics.Gauge // Gauge for tracking the number of memory compaction
- level0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in level0
- nonlevel0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in non0 level
- seekCompGauge metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt
+ compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction
+ compReadMeter metrics.Meter // Meter for measuring the data read during compaction
+ compWriteMeter metrics.Meter // Meter for measuring the data written during compaction
+ writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction
+ writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction
+ diskSizeGauge metrics.Gauge // Gauge for tracking the size of all the levels in the database
+ diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read
+ diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written
+ memCompGauge metrics.Gauge // Gauge for tracking the number of memory compaction
+ level0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in level0
+ nonlevel0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in non0 level
+ seekCompGauge metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt
+ manualMemAllocGauge metrics.Gauge // Gauge to track the amount of memory that has been manually allocated (not a part of runtime/GC)
quitLock sync.Mutex // Mutex protecting the quit channel access
quitChan chan chan error // Quit channel to stop the metrics collection before closing the database
@@ -143,6 +144,7 @@ func NewCustom(file string, namespace string, customize func(options *opt.Option
ldb.level0CompGauge = metrics.NewRegisteredGauge(namespace+"compact/level0", nil)
ldb.nonlevel0CompGauge = metrics.NewRegisteredGauge(namespace+"compact/nonlevel0", nil)
ldb.seekCompGauge = metrics.NewRegisteredGauge(namespace+"compact/seek", nil)
+ ldb.manualMemAllocGauge = metrics.NewRegisteredGauge(namespace+"memory/manualalloc", nil)
// Start up the metrics gathering and return
go ldb.meter(metricsGatheringInterval)
@@ -266,13 +268,14 @@ func (db *Database) Path() string {
// the metrics subsystem.
//
// This is how a LevelDB stats table looks like (currently):
-// Compactions
-// Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)
-// -------+------------+---------------+---------------+---------------+---------------
-// 0 | 0 | 0.00000 | 1.27969 | 0.00000 | 12.31098
-// 1 | 85 | 109.27913 | 28.09293 | 213.92493 | 214.26294
-// 2 | 523 | 1000.37159 | 7.26059 | 66.86342 | 66.77884
-// 3 | 570 | 1113.18458 | 0.00000 | 0.00000 | 0.00000
+//
+// Compactions
+// Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)
+// -------+------------+---------------+---------------+---------------+---------------
+// 0 | 0 | 0.00000 | 1.27969 | 0.00000 | 12.31098
+// 1 | 85 | 109.27913 | 28.09293 | 213.92493 | 214.26294
+// 2 | 523 | 1000.37159 | 7.26059 | 66.86342 | 66.77884
+// 3 | 570 | 1113.18458 | 0.00000 | 0.00000 | 0.00000
//
// This is how the write delay look like (currently):
// DelayN:5 Delay:406.604657ms Paused: false
diff --git a/ethdb/leveldb/leveldb_test.go b/ethdb/leveldb/leveldb_test.go
index 421d9b4693..d8c6386016 100644
--- a/ethdb/leveldb/leveldb_test.go
+++ b/ethdb/leveldb/leveldb_test.go
@@ -38,3 +38,15 @@ func TestLevelDB(t *testing.T) {
})
})
}
+
+func BenchmarkLevelDB(b *testing.B) {
+ dbtest.BenchDatabaseSuite(b, func() ethdb.KeyValueStore {
+ db, err := leveldb.Open(storage.NewMemStorage(), nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ return &Database{
+ db: db,
+ }
+ })
+}
diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go
new file mode 100644
index 0000000000..fdad13b392
--- /dev/null
+++ b/ethdb/pebble/pebble.go
@@ -0,0 +1,574 @@
+// Copyright 2023 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 arm64 || amd64
+
+// Package pebble implements the key-value database layer based on pebble.
+package pebble
+
+import (
+ "fmt"
+ "runtime"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/cockroachdb/pebble"
+ "github.com/cockroachdb/pebble/bloom"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
+)
+
+const (
+ // minCache is the minimum amount of memory in megabytes to allocate to pebble
+ // read and write caching, split half and half.
+ minCache = 16
+
+ // minHandles is the minimum number of files handles to allocate to the open
+ // database files.
+ minHandles = 16
+
+ // metricsGatheringInterval specifies the interval to retrieve pebble database
+ // compaction, io and pause stats to report to the user.
+ metricsGatheringInterval = 3 * time.Second
+)
+
+// Database is a persistent key-value store based on the pebble storage engine.
+// Apart from basic data storage functionality it also supports batch writes and
+// iterating over the keyspace in binary-alphabetical order.
+type Database struct {
+ fn string // filename for reporting
+ db *pebble.DB // Underlying pebble storage engine
+
+ compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction
+ compReadMeter metrics.Meter // Meter for measuring the data read during compaction
+ compWriteMeter metrics.Meter // Meter for measuring the data written during compaction
+ writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction
+ writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction
+ diskSizeGauge metrics.Gauge // Gauge for tracking the size of all the levels in the database
+ diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read
+ diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written
+ memCompGauge metrics.Gauge // Gauge for tracking the number of memory compaction
+ level0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in level0
+ nonlevel0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in non0 level
+ seekCompGauge metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt
+ manualMemAllocGauge metrics.Gauge // Gauge for tracking amount of non-managed memory currently allocated
+
+ quitLock sync.Mutex // Mutex protecting the quit channel access
+ quitChan chan chan error // Quit channel to stop the metrics collection before closing the database
+
+ log log.Logger // Contextual logger tracking the database path
+
+ activeComp int // Current number of active compactions
+ compStartTime time.Time // The start time of the earliest currently-active compaction
+ compTime int64 // Total time spent in compaction in ns
+ level0Comp uint32 // Total number of level-zero compactions
+ nonLevel0Comp uint32 // Total number of non level-zero compactions
+ writeDelayStartTime time.Time // The start time of the latest write stall
+ writeDelayCount int64 // Total number of write stall counts
+ writeDelayTime int64 // Total time spent in write stalls
+}
+
+func (d *Database) onCompactionBegin(info pebble.CompactionInfo) {
+ if d.activeComp == 0 {
+ d.compStartTime = time.Now()
+ }
+ l0 := info.Input[0]
+ if l0.Level == 0 {
+ atomic.AddUint32(&d.level0Comp, 1)
+ } else {
+ atomic.AddUint32(&d.nonLevel0Comp, 1)
+ }
+ d.activeComp++
+}
+
+func (d *Database) onCompactionEnd(info pebble.CompactionInfo) {
+ if d.activeComp == 1 {
+ atomic.AddInt64(&d.compTime, int64(time.Since(d.compStartTime)))
+ } else if d.activeComp == 0 {
+ panic("should not happen")
+ }
+ d.activeComp--
+}
+
+func (d *Database) onWriteStallBegin(b pebble.WriteStallBeginInfo) {
+ d.writeDelayStartTime = time.Now()
+}
+
+func (d *Database) onWriteStallEnd() {
+ atomic.AddInt64(&d.writeDelayTime, int64(time.Since(d.writeDelayStartTime)))
+}
+
+// New returns a wrapped pebble DB object. The namespace is the prefix that the
+// metrics reporting should use for surfacing internal stats.
+func New(file string, cache int, handles int, namespace string, readonly bool) (*Database, error) {
+ // Ensure we have some minimal caching and file guarantees
+ if cache < minCache {
+ cache = minCache
+ }
+ if handles < minHandles {
+ handles = minHandles
+ }
+ logger := log.New("database", file)
+ logger.Info("Allocated cache and file handles", "cache", common.StorageSize(cache*1024*1024), "handles", handles)
+
+ // The max memtable size is limited by the uint32 offsets stored in
+ // internal/arenaskl.node, DeferredBatchOp, and flushableBatchEntry.
+ // Taken from https://github.com/cockroachdb/pebble/blob/master/open.go#L38
+ maxMemTableSize := 4 << 30 // 4 GB
+
+ // Two memory tables is configured which is identical to leveldb,
+ // including a frozen memory table and another live one.
+ memTableLimit := 2
+ memTableSize := cache * 1024 * 1024 / 2 / memTableLimit
+ if memTableSize > maxMemTableSize {
+ memTableSize = maxMemTableSize
+ }
+ db := &Database{
+ fn: file,
+ log: logger,
+ quitChan: make(chan chan error),
+ }
+ opt := &pebble.Options{
+ // Pebble has a single combined cache area and the write
+ // buffers are taken from this too. Assign all available
+ // memory allowance for cache.
+ Cache: pebble.NewCache(int64(cache * 1024 * 1024)),
+ MaxOpenFiles: handles,
+
+ // The size of memory table(as well as the write buffer).
+ // Note, there may have more than two memory tables in the system.
+ MemTableSize: memTableSize,
+
+ // MemTableStopWritesThreshold places a hard limit on the size
+ // of the existent MemTables(including the frozen one).
+ // Note, this must be the number of tables not the size of all memtables
+ // according to https://github.com/cockroachdb/pebble/blob/master/options.go#L738-L742
+ // and to https://github.com/cockroachdb/pebble/blob/master/db.go#L1892-L1903.
+ MemTableStopWritesThreshold: memTableLimit,
+
+ // The default compaction concurrency(1 thread),
+ // Here use all available CPUs for faster compaction.
+ MaxConcurrentCompactions: func() int { return runtime.NumCPU() },
+
+ // Per-level options. Options for at least one level must be specified. The
+ // options for the last level are used for all subsequent levels.
+ Levels: []pebble.LevelOptions{
+ {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)},
+ {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)},
+ {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)},
+ {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)},
+ {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)},
+ {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)},
+ {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)},
+ },
+ ReadOnly: readonly,
+ EventListener: &pebble.EventListener{
+ CompactionBegin: db.onCompactionBegin,
+ CompactionEnd: db.onCompactionEnd,
+ WriteStallBegin: db.onWriteStallBegin,
+ WriteStallEnd: db.onWriteStallEnd,
+ },
+ }
+ // Disable seek compaction explicitly. Check https://github.com/ethereum/go-ethereum/pull/20130
+ // for more details.
+ opt.Experimental.ReadSamplingMultiplier = -1
+
+ // Open the db and recover any potential corruptions
+ innerDB, err := pebble.Open(file, opt)
+ if err != nil {
+ return nil, err
+ }
+ db.db = innerDB
+
+ db.compTimeMeter = metrics.NewRegisteredMeter(namespace+"compact/time", nil)
+ db.compReadMeter = metrics.NewRegisteredMeter(namespace+"compact/input", nil)
+ db.compWriteMeter = metrics.NewRegisteredMeter(namespace+"compact/output", nil)
+ db.diskSizeGauge = metrics.NewRegisteredGauge(namespace+"disk/size", nil)
+ db.diskReadMeter = metrics.NewRegisteredMeter(namespace+"disk/read", nil)
+ db.diskWriteMeter = metrics.NewRegisteredMeter(namespace+"disk/write", nil)
+ db.writeDelayMeter = metrics.NewRegisteredMeter(namespace+"compact/writedelay/duration", nil)
+ db.writeDelayNMeter = metrics.NewRegisteredMeter(namespace+"compact/writedelay/counter", nil)
+ db.memCompGauge = metrics.NewRegisteredGauge(namespace+"compact/memory", nil)
+ db.level0CompGauge = metrics.NewRegisteredGauge(namespace+"compact/level0", nil)
+ db.nonlevel0CompGauge = metrics.NewRegisteredGauge(namespace+"compact/nonlevel0", nil)
+ db.seekCompGauge = metrics.NewRegisteredGauge(namespace+"compact/seek", nil)
+ db.manualMemAllocGauge = metrics.NewRegisteredGauge(namespace+"memory/manualalloc", nil)
+
+ // Start up the metrics gathering and return
+ go db.meter(metricsGatheringInterval)
+ return db, nil
+}
+
+// Close stops the metrics collection, flushes any pending data to disk and closes
+// all io accesses to the underlying key-value store.
+func (d *Database) Close() error {
+ d.quitLock.Lock()
+ defer d.quitLock.Unlock()
+
+ if d.quitChan != nil {
+ errc := make(chan error)
+ d.quitChan <- errc
+ if err := <-errc; err != nil {
+ d.log.Error("Metrics collection failed", "err", err)
+ }
+ d.quitChan = nil
+ }
+ return d.db.Close()
+}
+
+// Has retrieves if a key is present in the key-value store.
+func (d *Database) Has(key []byte) (bool, error) {
+ _, closer, err := d.db.Get(key)
+ if err == pebble.ErrNotFound {
+ return false, nil
+ } else if err != nil {
+ return false, err
+ }
+ closer.Close()
+ return true, nil
+}
+
+// Get retrieves the given key if it's present in the key-value store.
+func (d *Database) Get(key []byte) ([]byte, error) {
+ dat, closer, err := d.db.Get(key)
+ if err != nil {
+ return nil, err
+ }
+ ret := make([]byte, len(dat))
+ copy(ret, dat)
+ closer.Close()
+ return ret, nil
+}
+
+// Put inserts the given value into the key-value store.
+func (d *Database) Put(key []byte, value []byte) error {
+ return d.db.Set(key, value, pebble.NoSync)
+}
+
+// Delete removes the key from the key-value store.
+func (d *Database) Delete(key []byte) error {
+ return d.db.Delete(key, nil)
+}
+
+// NewBatch creates a write-only key-value store that buffers changes to its host
+// database until a final write is called.
+func (d *Database) NewBatch() ethdb.Batch {
+ return &batch{
+ b: d.db.NewBatch(),
+ }
+}
+
+// NewBatchWithSize creates a write-only database batch with pre-allocated buffer.
+// It's not supported by pebble, but pebble has better memory allocation strategy
+// which turns out a lot faster than leveldb. It's performant enough to construct
+// batch object without any pre-allocated space.
+func (d *Database) NewBatchWithSize(_ int) ethdb.Batch {
+ return &batch{
+ b: d.db.NewBatch(),
+ }
+}
+
+// snapshot wraps a pebble snapshot for implementing the Snapshot interface.
+type snapshot struct {
+ db *pebble.Snapshot
+}
+
+// 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.
+// Note don't forget to release the snapshot once it's used up, otherwise
+// the stale data will never be cleaned up by the underlying compactor.
+func (d *Database) NewSnapshot() (ethdb.Snapshot, error) {
+ snap := d.db.NewSnapshot()
+ return &snapshot{db: snap}, nil
+}
+
+// Has retrieves if a key is present in the snapshot backing by a key-value
+// data store.
+func (snap *snapshot) Has(key []byte) (bool, error) {
+ _, closer, err := snap.db.Get(key)
+ if err != nil {
+ if err != pebble.ErrNotFound {
+ return false, err
+ } else {
+ return false, nil
+ }
+ }
+ closer.Close()
+ return true, nil
+}
+
+// Get retrieves the given key if it's present in the snapshot backing by
+// key-value data store.
+func (snap *snapshot) Get(key []byte) ([]byte, error) {
+ dat, closer, err := snap.db.Get(key)
+ if err != nil {
+ return nil, err
+ }
+ ret := make([]byte, len(dat))
+ copy(ret, dat)
+ closer.Close()
+ return ret, nil
+}
+
+// Release releases associated resources. Release should always succeed and can
+// be called multiple times without causing error.
+func (snap *snapshot) Release() {
+ snap.db.Close()
+}
+
+// upperBound returns the upper bound for the given prefix
+func upperBound(prefix []byte) (limit []byte) {
+ for i := len(prefix) - 1; i >= 0; i-- {
+ c := prefix[i]
+ if c == 0xff {
+ continue
+ }
+ limit = make([]byte, i+1)
+ copy(limit, prefix)
+ limit[i] = c + 1
+ break
+ }
+ return limit
+}
+
+// Stat returns a particular internal stat of the database.
+func (d *Database) Stat(property string) (string, error) {
+ return "", nil
+}
+
+// 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 (d *Database) Compact(start []byte, limit []byte) error {
+ return d.db.Compact(start, limit, true) // Parallelization is preferred
+}
+
+// Path returns the path to the database directory.
+func (d *Database) Path() string {
+ return d.fn
+}
+
+// meter periodically retrieves internal pebble counters and reports them to
+// the metrics subsystem.
+func (d *Database) meter(refresh time.Duration) {
+ var errc chan error
+ timer := time.NewTimer(refresh)
+ defer timer.Stop()
+
+ // Create storage and warning log tracer for write delay.
+ var (
+ compTimes [2]int64
+ writeDelayTimes [2]int64
+ writeDelayCounts [2]int64
+ compWrites [2]int64
+ compReads [2]int64
+
+ nWrites [2]int64
+ )
+
+ // Iterate ad infinitum and collect the stats
+ for i := 1; errc == nil; i++ {
+ var (
+ compWrite int64
+ compRead int64
+ nWrite int64
+
+ metrics = d.db.Metrics()
+ compTime = atomic.LoadInt64(&d.compTime)
+ writeDelayCount = atomic.LoadInt64(&d.writeDelayCount)
+ writeDelayTime = atomic.LoadInt64(&d.writeDelayTime)
+ nonLevel0CompCount = int64(atomic.LoadUint32(&d.nonLevel0Comp))
+ level0CompCount = int64(atomic.LoadUint32(&d.level0Comp))
+ )
+ writeDelayTimes[i%2] = writeDelayTime
+ writeDelayCounts[i%2] = writeDelayCount
+ compTimes[i%2] = compTime
+
+ for _, levelMetrics := range metrics.Levels {
+ nWrite += int64(levelMetrics.BytesCompacted)
+ nWrite += int64(levelMetrics.BytesFlushed)
+ compWrite += int64(levelMetrics.BytesCompacted)
+ compRead += int64(levelMetrics.BytesRead)
+ }
+
+ nWrite += int64(metrics.WAL.BytesWritten)
+
+ compWrites[i%2] = compWrite
+ compReads[i%2] = compRead
+ nWrites[i%2] = nWrite
+
+ if d.writeDelayNMeter != nil {
+ d.writeDelayNMeter.Mark(writeDelayCounts[i%2] - writeDelayCounts[(i-1)%2])
+ }
+ if d.writeDelayMeter != nil {
+ d.writeDelayMeter.Mark(writeDelayTimes[i%2] - writeDelayTimes[(i-1)%2])
+ }
+ if d.compTimeMeter != nil {
+ d.compTimeMeter.Mark(compTimes[i%2] - compTimes[(i-1)%2])
+ }
+ if d.compReadMeter != nil {
+ d.compReadMeter.Mark(compReads[i%2] - compReads[(i-1)%2])
+ }
+ if d.compWriteMeter != nil {
+ d.compWriteMeter.Mark(compWrites[i%2] - compWrites[(i-1)%2])
+ }
+ if d.diskSizeGauge != nil {
+ d.diskSizeGauge.Update(int64(metrics.DiskSpaceUsage()))
+ }
+ if d.diskReadMeter != nil {
+ d.diskReadMeter.Mark(0) // pebble doesn't track non-compaction reads
+ }
+ if d.diskWriteMeter != nil {
+ d.diskWriteMeter.Mark(nWrites[i%2] - nWrites[(i-1)%2])
+ }
+ // See https://github.com/cockroachdb/pebble/pull/1628#pullrequestreview-1026664054
+ manuallyAllocated := metrics.BlockCache.Size + int64(metrics.MemTable.Size) + int64(metrics.MemTable.ZombieSize)
+ d.manualMemAllocGauge.Update(manuallyAllocated)
+ d.memCompGauge.Update(metrics.Flush.Count)
+ d.nonlevel0CompGauge.Update(nonLevel0CompCount)
+ d.level0CompGauge.Update(level0CompCount)
+ d.seekCompGauge.Update(metrics.Compact.ReadCount)
+
+ // Sleep a bit, then repeat the stats collection
+ select {
+ case errc = <-d.quitChan:
+ // Quit requesting, stop hammering the database
+ case <-timer.C:
+ timer.Reset(refresh)
+ // Timeout, gather a new set of stats
+ }
+ }
+ errc <- nil
+}
+
+// batch is a write-only batch that commits changes to its host database
+// when Write is called. A batch cannot be used concurrently.
+type batch struct {
+ b *pebble.Batch
+ size int
+}
+
+// Put inserts the given value into the batch for later committing.
+func (b *batch) Put(key, value []byte) error {
+ b.b.Set(key, value, nil)
+ b.size += len(key) + len(value)
+ return nil
+}
+
+// Delete inserts the a key removal into the batch for later committing.
+func (b *batch) Delete(key []byte) error {
+ b.b.Delete(key, nil)
+ b.size += len(key)
+ return nil
+}
+
+// ValueSize retrieves the amount of data queued up for writing.
+func (b *batch) ValueSize() int {
+ return b.size
+}
+
+// Write flushes any accumulated data to disk.
+func (b *batch) Write() error {
+ return b.b.Commit(pebble.NoSync)
+}
+
+// Reset resets the batch for reuse.
+func (b *batch) Reset() {
+ b.b.Reset()
+ b.size = 0
+}
+
+// Replay replays the batch contents.
+func (b *batch) Replay(w ethdb.KeyValueWriter) error {
+ reader := b.b.Reader()
+ for {
+ kind, k, v, ok := reader.Next()
+ if !ok {
+ break
+ }
+ // The (k,v) slices might be overwritten if the batch is reset/reused,
+ // and the receiver should copy them if they are to be retained long-term.
+ if kind == pebble.InternalKeyKindSet {
+ w.Put(k, v)
+ } else if kind == pebble.InternalKeyKindDelete {
+ w.Delete(k)
+ } else {
+ return fmt.Errorf("unhandled operation, keytype: %v", kind)
+ }
+ }
+ return nil
+}
+
+// pebbleIterator is a wrapper of underlying iterator in storage engine.
+// The purpose of this structure is to implement the missing APIs.
+type pebbleIterator struct {
+ iter *pebble.Iterator
+ moved bool
+}
+
+// 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 (d *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
+ iter := d.db.NewIter(&pebble.IterOptions{
+ LowerBound: append(prefix, start...),
+ UpperBound: upperBound(prefix),
+ })
+ iter.First()
+ return &pebbleIterator{iter: iter, moved: true}
+}
+
+// Next moves the iterator to the next key/value pair. It returns whether the
+// iterator is exhausted.
+func (iter *pebbleIterator) Next() bool {
+ if iter.moved {
+ iter.moved = false
+ return iter.iter.Valid()
+ }
+ return iter.iter.Next()
+}
+
+// Error returns any accumulated error. Exhausting all the key/value pairs
+// is not considered to be an error.
+func (iter *pebbleIterator) 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 *pebbleIterator) Key() []byte {
+ return iter.iter.Key()
+}
+
+// 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 *pebbleIterator) 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 *pebbleIterator) Release() { iter.iter.Close() }
diff --git a/ethdb/pebble/pebble_test.go b/ethdb/pebble/pebble_test.go
new file mode 100644
index 0000000000..c773967dc6
--- /dev/null
+++ b/ethdb/pebble/pebble_test.go
@@ -0,0 +1,58 @@
+// Copyright 2023 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 arm64 || amd64
+
+package pebble
+
+import (
+ "testing"
+
+ "github.com/cockroachdb/pebble"
+ "github.com/cockroachdb/pebble/vfs"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/ethdb/dbtest"
+)
+
+func TestPebbleDB(t *testing.T) {
+ t.Run("DatabaseSuite", func(t *testing.T) {
+ dbtest.TestDatabaseSuite(t, func() ethdb.KeyValueStore {
+ db, err := pebble.Open("", &pebble.Options{
+ FS: vfs.NewMem(),
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ return &Database{
+ db: db,
+ }
+ })
+ })
+}
+
+func BenchmarkPebbleDB(b *testing.B) {
+ dbtest.BenchDatabaseSuite(b, func() ethdb.KeyValueStore {
+ db, err := pebble.Open("", &pebble.Options{
+ FS: vfs.NewMem(),
+ })
+ if err != nil {
+ b.Fatal(err)
+ }
+ return &Database{
+ db: db,
+ }
+ })
+}
diff --git a/ethdb/remotedb/remotedb.go b/ethdb/remotedb/remotedb.go
index 59a570bb5e..9ce657d780 100644
--- a/ethdb/remotedb/remotedb.go
+++ b/ethdb/remotedb/remotedb.go
@@ -22,9 +22,6 @@
package remotedb
import (
- "errors"
- "strings"
-
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rpc"
@@ -150,24 +147,8 @@ func (db *Database) Close() error {
return nil
}
-func dialRPC(endpoint string) (*rpc.Client, error) {
- if endpoint == "" {
- return nil, errors.New("endpoint must be specified")
- }
- if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") {
- // Backwards compatibility with geth < 1.5 which required
- // these prefixes.
- endpoint = endpoint[4:]
- }
- return rpc.Dial(endpoint)
-}
-
-func New(endpoint string) (ethdb.Database, error) {
- client, err := dialRPC(endpoint)
- if err != nil {
- return nil, err
- }
+func New(client *rpc.Client) ethdb.Database {
return &Database{
remote: client,
- }, nil
+ }
}
diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go
index 5d60efab2e..e059844a17 100644
--- a/ethstats/ethstats.go
+++ b/ethstats/ethstats.go
@@ -57,6 +57,8 @@ const (
txChanSize = 4096
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
chainHeadChanSize = 10
+
+ messageSizeLimit = 15 * 1024 * 1024
)
// backend encompasses the bare-minimum functionality needed for ethstats reporting
@@ -102,13 +104,17 @@ type Service struct {
// websocket.
//
// From Gorilla websocket docs:
-// Connections support one concurrent reader and one concurrent writer.
-// Applications are responsible for ensuring that no more than one goroutine calls the write methods
-// - NextWriter, SetWriteDeadline, WriteMessage, WriteJSON, EnableWriteCompression, SetCompressionLevel
-// concurrently and that no more than one goroutine calls the read methods
-// - NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler
-// concurrently.
-// The Close and WriteControl methods can be called concurrently with all other methods.
+//
+// Connections support one concurrent reader and one concurrent writer. Applications are
+// responsible for ensuring that
+// - no more than one goroutine calls the write methods
+// NextWriter, SetWriteDeadline, WriteMessage, WriteJSON, EnableWriteCompression,
+// SetCompressionLevel concurrently; and
+// - that no more than one goroutine calls the
+// read methods NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler,
+// SetPingHandler concurrently.
+//
+// The Close and WriteControl methods can be called concurrently with all other methods.
type connWrapper struct {
conn *websocket.Conn
@@ -117,6 +123,7 @@ type connWrapper struct {
}
func newConnectionWrapper(conn *websocket.Conn) *connWrapper {
+ conn.SetReadLimit(messageSizeLimit)
return &connWrapper{conn: conn}
}
diff --git a/event/event_test.go b/event/event_test.go
index bdad11f13d..84b37eca3b 100644
--- a/event/event_test.go
+++ b/event/event_test.go
@@ -100,7 +100,6 @@ func TestSubscribeDuplicateType(t *testing.T) {
}
func TestMuxConcurrent(t *testing.T) {
- rand.Seed(time.Now().Unix())
mux := new(TypeMux)
defer mux.Stop()
diff --git a/event/feedof.go b/event/feedof.go
new file mode 100644
index 0000000000..598038a19e
--- /dev/null
+++ b/event/feedof.go
@@ -0,0 +1,167 @@
+// 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 .
+
+//go:build go1.18
+// +build go1.18
+
+package event
+
+import (
+ "reflect"
+ "sync"
+)
+
+// FeedOf implements one-to-many subscriptions where the carrier of events is a channel.
+// Values sent to a Feed are delivered to all subscribed channels simultaneously.
+//
+// The zero value is ready to use.
+type FeedOf[T any] struct {
+ once sync.Once // ensures that init only runs once
+ sendLock chan struct{} // sendLock has a one-element buffer and is empty when held.It protects sendCases.
+ removeSub chan chan<- T // interrupts Send
+ sendCases caseList // the active set of select cases used by Send
+
+ // The inbox holds newly subscribed channels until they are added to sendCases.
+ mu sync.Mutex
+ inbox caseList
+}
+
+func (f *FeedOf[T]) init() {
+ f.removeSub = make(chan chan<- T)
+ f.sendLock = make(chan struct{}, 1)
+ f.sendLock <- struct{}{}
+ f.sendCases = caseList{{Chan: reflect.ValueOf(f.removeSub), Dir: reflect.SelectRecv}}
+}
+
+// Subscribe adds a channel to the feed. Future sends will be delivered on the channel
+// until the subscription is canceled.
+//
+// The channel should have ample buffer space to avoid blocking other subscribers. Slow
+// subscribers are not dropped.
+func (f *FeedOf[T]) Subscribe(channel chan<- T) Subscription {
+ f.once.Do(f.init)
+
+ chanval := reflect.ValueOf(channel)
+ sub := &feedOfSub[T]{feed: f, channel: channel, err: make(chan error, 1)}
+
+ // Add the select case to the inbox.
+ // The next Send will add it to f.sendCases.
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ cas := reflect.SelectCase{Dir: reflect.SelectSend, Chan: chanval}
+ f.inbox = append(f.inbox, cas)
+ return sub
+}
+
+func (f *FeedOf[T]) remove(sub *feedOfSub[T]) {
+ // Delete from inbox first, which covers channels
+ // that have not been added to f.sendCases yet.
+ f.mu.Lock()
+ index := f.inbox.find(sub.channel)
+ if index != -1 {
+ f.inbox = f.inbox.delete(index)
+ f.mu.Unlock()
+ return
+ }
+ f.mu.Unlock()
+
+ select {
+ case f.removeSub <- sub.channel:
+ // Send will remove the channel from f.sendCases.
+ case <-f.sendLock:
+ // No Send is in progress, delete the channel now that we have the send lock.
+ f.sendCases = f.sendCases.delete(f.sendCases.find(sub.channel))
+ f.sendLock <- struct{}{}
+ }
+}
+
+// Send delivers to all subscribed channels simultaneously.
+// It returns the number of subscribers that the value was sent to.
+func (f *FeedOf[T]) Send(value T) (nsent int) {
+ rvalue := reflect.ValueOf(value)
+
+ f.once.Do(f.init)
+ <-f.sendLock
+
+ // Add new cases from the inbox after taking the send lock.
+ f.mu.Lock()
+ f.sendCases = append(f.sendCases, f.inbox...)
+ f.inbox = nil
+ f.mu.Unlock()
+
+ // Set the sent value on all channels.
+ for i := firstSubSendCase; i < len(f.sendCases); i++ {
+ f.sendCases[i].Send = rvalue
+ }
+
+ // Send until all channels except removeSub have been chosen. 'cases' tracks a prefix
+ // of sendCases. When a send succeeds, the corresponding case moves to the end of
+ // 'cases' and it shrinks by one element.
+ cases := f.sendCases
+ for {
+ // Fast path: try sending without blocking before adding to the select set.
+ // This should usually succeed if subscribers are fast enough and have free
+ // buffer space.
+ for i := firstSubSendCase; i < len(cases); i++ {
+ if cases[i].Chan.TrySend(rvalue) {
+ nsent++
+ cases = cases.deactivate(i)
+ i--
+ }
+ }
+ if len(cases) == firstSubSendCase {
+ break
+ }
+ // Select on all the receivers, waiting for them to unblock.
+ chosen, recv, _ := reflect.Select(cases)
+ if chosen == 0 /* <-f.removeSub */ {
+ index := f.sendCases.find(recv.Interface())
+ f.sendCases = f.sendCases.delete(index)
+ if index >= 0 && index < len(cases) {
+ // Shrink 'cases' too because the removed case was still active.
+ cases = f.sendCases[:len(cases)-1]
+ }
+ } else {
+ cases = cases.deactivate(chosen)
+ nsent++
+ }
+ }
+
+ // Forget about the sent value and hand off the send lock.
+ for i := firstSubSendCase; i < len(f.sendCases); i++ {
+ f.sendCases[i].Send = reflect.Value{}
+ }
+ f.sendLock <- struct{}{}
+ return nsent
+}
+
+type feedOfSub[T any] struct {
+ feed *FeedOf[T]
+ channel chan<- T
+ errOnce sync.Once
+ err chan error
+}
+
+func (sub *feedOfSub[T]) Unsubscribe() {
+ sub.errOnce.Do(func() {
+ sub.feed.remove(sub)
+ close(sub.err)
+ })
+}
+
+func (sub *feedOfSub[T]) Err() <-chan error {
+ return sub.err
+}
diff --git a/event/feedof_test.go b/event/feedof_test.go
new file mode 100644
index 0000000000..8478eeffb1
--- /dev/null
+++ b/event/feedof_test.go
@@ -0,0 +1,282 @@
+// Copyright 2016 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 go1.18
+// +build go1.18
+
+package event
+
+import (
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestFeedOf(t *testing.T) {
+ var feed FeedOf[int]
+ var done, subscribed sync.WaitGroup
+ subscriber := func(i int) {
+ defer done.Done()
+
+ subchan := make(chan int)
+ sub := feed.Subscribe(subchan)
+ timeout := time.NewTimer(2 * time.Second)
+ defer timeout.Stop()
+ subscribed.Done()
+
+ select {
+ case v := <-subchan:
+ if v != 1 {
+ t.Errorf("%d: received value %d, want 1", i, v)
+ }
+ case <-timeout.C:
+ t.Errorf("%d: receive timeout", i)
+ }
+
+ sub.Unsubscribe()
+ select {
+ case _, ok := <-sub.Err():
+ if ok {
+ t.Errorf("%d: error channel not closed after unsubscribe", i)
+ }
+ case <-timeout.C:
+ t.Errorf("%d: unsubscribe timeout", i)
+ }
+ }
+
+ const n = 1000
+ done.Add(n)
+ subscribed.Add(n)
+ for i := 0; i < n; i++ {
+ go subscriber(i)
+ }
+ subscribed.Wait()
+ if nsent := feed.Send(1); nsent != n {
+ t.Errorf("first send delivered %d times, want %d", nsent, n)
+ }
+ if nsent := feed.Send(2); nsent != 0 {
+ t.Errorf("second send delivered %d times, want 0", nsent)
+ }
+ done.Wait()
+}
+
+func TestFeedOfSubscribeSameChannel(t *testing.T) {
+ var (
+ feed FeedOf[int]
+ done sync.WaitGroup
+ ch = make(chan int)
+ sub1 = feed.Subscribe(ch)
+ sub2 = feed.Subscribe(ch)
+ _ = feed.Subscribe(ch)
+ )
+ expectSends := func(value, n int) {
+ if nsent := feed.Send(value); nsent != n {
+ t.Errorf("send delivered %d times, want %d", nsent, n)
+ }
+ done.Done()
+ }
+ expectRecv := func(wantValue, n int) {
+ for i := 0; i < n; i++ {
+ if v := <-ch; v != wantValue {
+ t.Errorf("received %d, want %d", v, wantValue)
+ }
+ }
+ }
+
+ done.Add(1)
+ go expectSends(1, 3)
+ expectRecv(1, 3)
+ done.Wait()
+
+ sub1.Unsubscribe()
+
+ done.Add(1)
+ go expectSends(2, 2)
+ expectRecv(2, 2)
+ done.Wait()
+
+ sub2.Unsubscribe()
+
+ done.Add(1)
+ go expectSends(3, 1)
+ expectRecv(3, 1)
+ done.Wait()
+}
+
+func TestFeedOfSubscribeBlockedPost(t *testing.T) {
+ var (
+ feed FeedOf[int]
+ nsends = 2000
+ ch1 = make(chan int)
+ ch2 = make(chan int)
+ wg sync.WaitGroup
+ )
+ defer wg.Wait()
+
+ feed.Subscribe(ch1)
+ wg.Add(nsends)
+ for i := 0; i < nsends; i++ {
+ go func() {
+ feed.Send(99)
+ wg.Done()
+ }()
+ }
+
+ sub2 := feed.Subscribe(ch2)
+ defer sub2.Unsubscribe()
+
+ // We're done when ch1 has received N times.
+ // The number of receives on ch2 depends on scheduling.
+ for i := 0; i < nsends; {
+ select {
+ case <-ch1:
+ i++
+ case <-ch2:
+ }
+ }
+}
+
+func TestFeedOfUnsubscribeBlockedPost(t *testing.T) {
+ var (
+ feed FeedOf[int]
+ nsends = 200
+ chans = make([]chan int, 2000)
+ subs = make([]Subscription, len(chans))
+ bchan = make(chan int)
+ bsub = feed.Subscribe(bchan)
+ wg sync.WaitGroup
+ )
+ for i := range chans {
+ chans[i] = make(chan int, nsends)
+ }
+
+ // Queue up some Sends. None of these can make progress while bchan isn't read.
+ wg.Add(nsends)
+ for i := 0; i < nsends; i++ {
+ go func() {
+ feed.Send(99)
+ wg.Done()
+ }()
+ }
+ // Subscribe the other channels.
+ for i, ch := range chans {
+ subs[i] = feed.Subscribe(ch)
+ }
+ // Unsubscribe them again.
+ for _, sub := range subs {
+ sub.Unsubscribe()
+ }
+ // Unblock the Sends.
+ bsub.Unsubscribe()
+ wg.Wait()
+}
+
+// Checks that unsubscribing a channel during Send works even if that
+// channel has already been sent on.
+func TestFeedOfUnsubscribeSentChan(t *testing.T) {
+ var (
+ feed FeedOf[int]
+ ch1 = make(chan int)
+ ch2 = make(chan int)
+ sub1 = feed.Subscribe(ch1)
+ sub2 = feed.Subscribe(ch2)
+ wg sync.WaitGroup
+ )
+ defer sub2.Unsubscribe()
+
+ wg.Add(1)
+ go func() {
+ feed.Send(0)
+ wg.Done()
+ }()
+
+ // Wait for the value on ch1.
+ <-ch1
+ // Unsubscribe ch1, removing it from the send cases.
+ sub1.Unsubscribe()
+
+ // Receive ch2, finishing Send.
+ <-ch2
+ wg.Wait()
+
+ // Send again. This should send to ch2 only, so the wait group will unblock
+ // as soon as a value is received on ch2.
+ wg.Add(1)
+ go func() {
+ feed.Send(0)
+ wg.Done()
+ }()
+ <-ch2
+ wg.Wait()
+}
+
+func TestFeedOfUnsubscribeFromInbox(t *testing.T) {
+ var (
+ feed FeedOf[int]
+ ch1 = make(chan int)
+ ch2 = make(chan int)
+ sub1 = feed.Subscribe(ch1)
+ sub2 = feed.Subscribe(ch1)
+ sub3 = feed.Subscribe(ch2)
+ )
+ if len(feed.inbox) != 3 {
+ t.Errorf("inbox length != 3 after subscribe")
+ }
+ if len(feed.sendCases) != 1 {
+ t.Errorf("sendCases is non-empty after unsubscribe")
+ }
+
+ sub1.Unsubscribe()
+ sub2.Unsubscribe()
+ sub3.Unsubscribe()
+ if len(feed.inbox) != 0 {
+ t.Errorf("inbox is non-empty after unsubscribe")
+ }
+ if len(feed.sendCases) != 1 {
+ t.Errorf("sendCases is non-empty after unsubscribe")
+ }
+}
+
+func BenchmarkFeedOfSend1000(b *testing.B) {
+ var (
+ done sync.WaitGroup
+ feed FeedOf[int]
+ nsubs = 1000
+ )
+ subscriber := func(ch <-chan int) {
+ for i := 0; i < b.N; i++ {
+ <-ch
+ }
+ done.Done()
+ }
+ done.Add(nsubs)
+ for i := 0; i < nsubs; i++ {
+ ch := make(chan int, 200)
+ feed.Subscribe(ch)
+ go subscriber(ch)
+ }
+
+ // The actual benchmark.
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ if feed.Send(i) != nsubs {
+ panic("wrong number of sends")
+ }
+ }
+
+ b.StopTimer()
+ done.Wait()
+}
diff --git a/go.mod b/go.mod
index 474bbbd6a3..a450a76aa3 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/ethereum/go-ethereum
-go 1.17
+go 1.18
require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0
@@ -12,27 +12,30 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.2.0
github.com/cespare/cp v0.1.0
github.com/cloudflare/cloudflare-go v0.14.0
- github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f
+ github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811
+ github.com/consensys/gnark-crypto v0.9.1-0.20230105202408-1a7a29904a7c
github.com/davecgh/go-spew v1.1.1
- github.com/deckarep/golang-set v1.8.0
+ github.com/deckarep/golang-set/v2 v2.1.0
github.com/docker/docker v1.6.2
- github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf
+ github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7
github.com/edsrzf/mmap-go v1.0.0
github.com/fatih/color v1.7.0
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
+ github.com/fsnotify/fsnotify v1.6.0
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
+ github.com/gballet/go-verkle v0.0.0-20220902153445-097bd83b7732
github.com/go-redis/redis/v7 v7.4.1
github.com/go-stack/stack v1.8.1
- github.com/golang-jwt/jwt/v4 v4.4.2
+ github.com/golang-jwt/jwt/v4 v4.3.0
github.com/golang/protobuf v1.5.2
github.com/golang/snappy v0.0.4
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa
- github.com/google/uuid v1.2.0
- github.com/gorilla/websocket v1.5.0
+ github.com/google/uuid v1.3.0
+ github.com/gorilla/websocket v1.4.2
github.com/graph-gophers/graphql-go v1.3.0
- github.com/hashicorp/go-bexpr v0.1.11
- github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
+ github.com/hashicorp/go-bexpr v0.1.10
+ github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e
github.com/holiman/bloomfilter/v2 v2.0.3
github.com/holiman/uint256 v1.2.0
github.com/huin/goupnp v1.0.3
@@ -40,31 +43,29 @@ require (
github.com/influxdata/influxdb-client-go/v2 v2.9.1
github.com/jackpal/go-nat-pmp v1.0.2
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e
- github.com/julienschmidt/httprouter v1.2.0
+ github.com/julienschmidt/httprouter v1.3.0
github.com/karalabe/usb v0.0.2
- github.com/mattn/go-colorable v0.1.12
- github.com/mattn/go-isatty v0.0.14
+ github.com/mattn/go-colorable v0.1.13
+ github.com/mattn/go-isatty v0.0.16
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
github.com/olekukonko/tablewriter v0.0.5
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
- github.com/prometheus/tsdb v0.10.0
- github.com/rjeczalik/notify v0.9.1
- github.com/rs/cors v1.8.3
+ github.com/prometheus/tsdb v0.7.1
+ github.com/rs/cors v1.7.0
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
- github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
- github.com/stretchr/testify v1.8.2
+ github.com/status-im/keycard-go v0.2.0
+ github.com/stretchr/testify v1.8.0
github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
- github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef
- github.com/urfave/cli/v2 v2.24.4
+ github.com/tyler-smith/go-bip39 v1.1.0
+ github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa
golang.org/x/crypto v0.1.0
- golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028
- golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
+ golang.org/x/exp v0.0.0-20230206171751-46f607a40771
+ golang.org/x/sync v0.1.0
golang.org/x/sys v0.5.0
- golang.org/x/term v0.5.0
golang.org/x/text v0.7.0
- golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
- golang.org/x/tools v0.1.12
+ golang.org/x/time v0.0.0-20220922220347-f3bd1da661af
+ golang.org/x/tools v0.2.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
)
@@ -72,6 +73,7 @@ require (
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 // indirect
+ github.com/DataDog/zstd v1.5.2 // indirect
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.13 // indirect
@@ -81,31 +83,50 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.11.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.16.8 // indirect
github.com/aws/smithy-go v1.12.0 // indirect
- github.com/cespare/xxhash/v2 v2.1.2 // indirect
+ github.com/beorn7/perks v1.0.1 // indirect
+ github.com/cespare/xxhash/v2 v2.2.0 // indirect
+ github.com/cockroachdb/errors v1.9.1 // indirect
+ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
+ github.com/cockroachdb/redact v1.1.3 // indirect
+ github.com/consensys/bavard v0.1.13 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
+ github.com/crate-crypto/go-ipa v0.0.0-20220523130400-f11357ae11c7 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/deepmap/oapi-codegen v1.8.2 // indirect
- github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect
+ github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect
+ github.com/getsentry/sentry-go v0.18.0 // indirect
github.com/go-ole/go-ole v1.2.1 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect
+ github.com/klauspost/compress v1.15.15 // indirect
+ github.com/kr/pretty v0.3.1 // indirect
+ github.com/kr/text v0.2.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
+ github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/mitchellh/pointerstructure v1.2.1 // indirect
+ github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/opentracing/opentracing-go v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/prometheus/client_golang v1.14.0 // indirect
+ github.com/prometheus/client_model v0.3.0 // indirect
+ github.com/prometheus/common v0.39.0 // indirect
+ github.com/prometheus/procfs v0.9.0 // indirect
+ github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/tklauser/go-sysconf v0.3.5 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
- golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
+ golang.org/x/mod v0.6.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
- google.golang.org/protobuf v1.26.0 // indirect
+ google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+ rsc.io/tmplfunc v0.0.3 // indirect
)
diff --git a/go.sum b/go.sum
index 757e62700d..5fd0123034 100644
--- a/go.sum
+++ b/go.sum
@@ -18,6 +18,7 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 h1:qoVeMsc9/fh/yhxVaA0obYjVH/oI/ihrOoMwsLS9KSA=
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM=
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 h1:E+m3SkZCN0Bf5q7YdTs5lSm2CYY3CK4spn5OmUIiQtk=
@@ -25,15 +26,21 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSu
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 h1:Px2UA+2RvSSvv+RvJNuUB6n7rs5Wsel4dXLe90Um2n4=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
-github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
+github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
+github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
+github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
+github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY=
github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8=
+github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -41,6 +48,7 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo=
github.com/aws/aws-sdk-go-v2 v1.16.6 h1:kzafGZYwkwVgLZ2zEX7P+vTwLli6uIMXF8aGjunN6UI=
github.com/aws/aws-sdk-go-v2 v1.16.6/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw=
@@ -67,92 +75,139 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.16.8/go.mod h1:50YdFq1WIuxA0AGrygvYG
github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw=
github.com/aws/smithy-go v1.12.0 h1:gXpeZel/jPoWQ7OEmLIgCUnhkFftqNfwWUwAHSlp1v0=
github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
+github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
-github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA=
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
-github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
-github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f h1:C43yEtQ6NIf4ftFXD/V55gnGFgPbMQobd//YlnLjUJ8=
-github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
+github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
+github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8=
+github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk=
+github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
+github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk=
+github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM=
+github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
+github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
+github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
+github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
+github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
+github.com/consensys/gnark-crypto v0.9.1-0.20230105202408-1a7a29904a7c h1:llSLg4o9EgH3SrXky+Q5BqEYqV76NGKo07K5Ps2pIKo=
+github.com/consensys/gnark-crypto v0.9.1-0.20230105202408-1a7a29904a7c/go.mod h1:CkbdF9hbRidRJYMRzmfX8TMOr95I2pYXRHF18MzRrvA=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/crate-crypto/go-ipa v0.0.0-20220523130400-f11357ae11c7 h1:6IrxszG5G+O7zhtkWxq6+unVvnrm1fqV2Pe+T95DUzw=
+github.com/crate-crypto/go-ipa v0.0.0-20220523130400-f11357ae11c7/go.mod h1:gFnFS95y8HstDP6P9pPwzrxOOC5TRDkwbM+ao15ChAI=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
-github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
+github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
+github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU=
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
+github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
+github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
+github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/docker v1.6.2 h1:HlFGsy+9/xrgMmhmN+NGhCc5SHGJ7I+kHosRR1xc/aI=
github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf h1:Yt+4K30SdjOkRoRRm3vYNQgR+/ZIy0RmeUDZo7Y8zeQ=
-github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
+github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
+github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7 h1:kgvzE5wLsLa7XKfV85VZl40QXaMCaeFtHpPwJ8fhotY=
+github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
+github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
+github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c h1:CndMRAH4JIwxbW8KYq6Q+cGWcGHz0FjGR3QqcInWcW0=
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc=
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
+github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
+github.com/gballet/go-verkle v0.0.0-20220902153445-097bd83b7732 h1:AB7YjNrzlVHsYz06zCULVV2zYCEft82P86dSmtwxKL0=
+github.com/gballet/go-verkle v0.0.0-20220902153445-097bd83b7732/go.mod h1:o/XfIXWi4/GqbQirfRm5uTbXMG5NpqxkxblnbZ+QM9I=
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
+github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
+github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0=
+github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
+github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
+github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
+github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -165,11 +220,21 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
+github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
+github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
+github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
-github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
+github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
+github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
+github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -181,20 +246,22 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
+github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
@@ -203,10 +270,13 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -214,22 +284,27 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
-github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
-github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0=
github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
-github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY=
-github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE=
+github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
+github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
+github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
-github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw=
+github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
@@ -238,7 +313,9 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=
github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
+github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY=
github.com/influxdata/influxdb v1.8.3 h1:WEypI1BQFTT4teLM+1qkEcvUi0dAvopAI/ir0vAiBg8=
@@ -247,13 +324,17 @@ github.com/influxdata/influxdb-client-go/v2 v2.9.1 h1:5kbH226fmmiV0MMTs7a8L7/ECC
github.com/influxdata/influxdb-client-go/v2 v2.9.1/go.mod h1:x7Jo5UHHl+w8wu8UnGiNobDDHygojXwJX4mx7rXGKMk=
github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk=
github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE=
-github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 h1:vilfsDSy7TDxedi9gyBkMvAirat/oRcL0lFdJBf6tdM=
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8=
github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE=
github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0=
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
+github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
+github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
+github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
+github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
+github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e h1:UvSe12bq+Uj2hWd8aOlwPmoZ+CITRFrdit+sDGfAg8U=
@@ -261,40 +342,56 @@ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1C
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
+github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/karalabe/usb v0.0.2 h1:M6QQBNxF+CQ8OFvxrT90BA0qBOXymndZnk5q235mFc4=
github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
+github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
+github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
+github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
+github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
+github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
+github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
+github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
@@ -302,33 +399,54 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
-github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
+github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
+github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
+github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw=
github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
+github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
+github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
+github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
+github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
@@ -349,11 +467,15 @@ github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mo
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
+github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -363,24 +485,38 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
+github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
+github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI=
+github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
-github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
+github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
+github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
+github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
-github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE=
-github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo=
-github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
+github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@@ -391,15 +527,18 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
-github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
+github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -407,9 +546,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344 h1:m+8fKfQwCAy1QjzINvKe/pYtLjo2dl59x2w9YSEJxuY=
github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
@@ -419,21 +557,36 @@ github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
-github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=
-github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
+github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
+github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
-github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU=
-github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
+github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q=
+github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
+github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
+github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
+github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
+github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
+github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
+github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -441,11 +594,14 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
@@ -463,7 +619,8 @@ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm0
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
+golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg=
+golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -475,25 +632,26 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -501,8 +659,10 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -511,11 +671,10 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
+golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -531,12 +690,14 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -545,6 +706,7 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -563,27 +725,25 @@ golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -592,23 +752,25 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
+golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -623,14 +785,15 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
-golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -655,6 +818,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -669,20 +833,30 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
+google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
+google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -691,6 +865,10 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
+gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
@@ -705,6 +883,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
@@ -714,6 +893,7 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
+rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
diff --git a/graphql/graphiql.go b/graphql/graphiql.go
index 864ebf57df..576a0cbe95 100644
--- a/graphql/graphiql.go
+++ b/graphql/graphiql.go
@@ -48,7 +48,7 @@ func errorJSON(msg string) []byte {
}
func (h GraphiQL) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if r.Method != "GET" {
+ if r.Method != http.MethodGet {
respond(w, errorJSON("only GET requests are supported"), http.StatusMethodNotAllowed)
return
}
diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go
index 491c731521..46acd15293 100644
--- a/graphql/graphql_test.go
+++ b/graphql/graphql_test.go
@@ -321,10 +321,11 @@ func TestGraphQLTransactionLogs(t *testing.T) {
func createNode(t *testing.T) *node.Node {
stack, err := node.New(&node.Config{
- HTTPHost: "127.0.0.1",
- HTTPPort: 0,
- WSHost: "127.0.0.1",
- WSPort: 0,
+ HTTPHost: "127.0.0.1",
+ HTTPPort: 0,
+ WSHost: "127.0.0.1",
+ WSPort: 0,
+ HTTPTimeouts: node.DefaultConfig.HTTPTimeouts,
})
if err != nil {
t.Fatalf("could not create node: %v", err)
diff --git a/graphql/service.go b/graphql/service.go
index 6f6e583359..4392dd83e6 100644
--- a/graphql/service.go
+++ b/graphql/service.go
@@ -17,13 +17,19 @@
package graphql
import (
+ "context"
"encoding/json"
"net/http"
+ "strconv"
+ "sync"
+ "time"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/rpc"
"github.com/graph-gophers/graphql-go"
+ gqlErrors "github.com/graph-gophers/graphql-go/errors"
)
type handler struct {
@@ -41,18 +47,60 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
- response := h.Schema.Exec(r.Context(), params.Query, params.OperationName, params.Variables)
- responseJSON, err := json.Marshal(response)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- if len(response.Errors) > 0 {
- w.WriteHeader(http.StatusBadRequest)
+ var (
+ ctx = r.Context()
+ responded sync.Once
+ timer *time.Timer
+ cancel context.CancelFunc
+ )
+ ctx, cancel = context.WithCancel(ctx)
+ defer cancel()
+
+ if timeout, ok := rpc.ContextRequestTimeout(ctx); ok {
+ timer = time.AfterFunc(timeout, func() {
+ responded.Do(func() {
+ // Cancel request handling.
+ cancel()
+
+ // Create the timeout response.
+ response := &graphql.Response{
+ Errors: []*gqlErrors.QueryError{{Message: "request timed out"}},
+ }
+ responseJSON, err := json.Marshal(response)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // Setting this disables gzip compression in package node.
+ w.Header().Set("transfer-encoding", "identity")
+
+ // Flush the response. Since we are writing close to the response timeout,
+ // chunked transfer encoding must be disabled by setting content-length.
+ w.Header().Set("content-type", "application/json")
+ w.Header().Set("content-length", strconv.Itoa(len(responseJSON)))
+ w.Write(responseJSON)
+ if flush, ok := w.(http.Flusher); ok {
+ flush.Flush()
+ }
+ })
+ })
}
- w.Header().Set("Content-Type", "application/json")
- w.Write(responseJSON)
+ response := h.Schema.Exec(ctx, params.Query, params.OperationName, params.Variables)
+ timer.Stop()
+ responded.Do(func() {
+ responseJSON, err := json.Marshal(response)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if len(response.Errors) > 0 {
+ w.WriteHeader(http.StatusBadRequest)
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.Write(responseJSON)
+ })
}
// New constructs a new GraphQL service instance.
diff --git a/internal/cmdtest/test_cmd.go b/internal/cmdtest/test_cmd.go
index b837c9c399..fd7a4a8b7f 100644
--- a/internal/cmdtest/test_cmd.go
+++ b/internal/cmdtest/test_cmd.go
@@ -83,7 +83,7 @@ func (tt *TestCmd) Run(name string, args ...string) {
// InputLine writes the given text to the child's stdin.
// This method can also be called from an expect template, e.g.:
//
-// geth.expect(`Passphrase: {{.InputLine "password"}}`)
+// geth.expect(`Passphrase: {{.InputLine "password"}}`)
func (tt *TestCmd) InputLine(s string) string {
io.WriteString(tt.stdin, s+"\n")
return ""
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index e6740942d8..58f65f86d7 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -18,6 +18,7 @@ package ethapi
import (
"context"
+ "encoding/hex"
"errors"
"fmt"
"math/big"
@@ -87,7 +88,7 @@ type feeHistoryResult struct {
}
// FeeHistory returns the fee market history.
-func (s *EthereumAPI) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) {
+func (s *EthereumAPI) FeeHistory(ctx context.Context, blockCount math.HexOrDecimal64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) {
oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles)
if err != nil {
return nil, err
@@ -170,7 +171,7 @@ func (s *TxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction {
for account, txs := range pending {
dump := make(map[string]*RPCTransaction)
for _, tx := range txs {
- dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
+ dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
}
content["pending"][account.Hex()] = dump
}
@@ -178,7 +179,7 @@ func (s *TxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction {
for account, txs := range queue {
dump := make(map[string]*RPCTransaction)
for _, tx := range txs {
- dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
+ dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
}
content["queued"][account.Hex()] = dump
}
@@ -194,14 +195,14 @@ func (s *TxPoolAPI) ContentFrom(addr common.Address) map[string]map[string]*RPCT
// Build the pending transactions
dump := make(map[string]*RPCTransaction, len(pending))
for _, tx := range pending {
- dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
+ dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
}
content["pending"] = dump
// Build the queued transactions
dump = make(map[string]*RPCTransaction, len(queue))
for _, tx := range queue {
- dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
+ dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
}
content["queued"] = dump
@@ -455,7 +456,7 @@ func (s *PersonalAccountAPI) signTransaction(ctx context.Context, args *Transact
// passwd isn't able to decrypt the key it fails.
func (s *PersonalAccountAPI) SendTransaction(ctx context.Context, args TransactionArgs, passwd string) (common.Hash, error) {
if args.Nonce == nil {
- // Hold the addresse's mutex around signing to prevent concurrent assignment of
+ // Hold the mutex around signing to prevent concurrent assignment of
// the same nonce to multiple accounts.
s.nonceLock.LockAddr(args.from())
defer s.nonceLock.UnlockAddr(args.from())
@@ -659,8 +660,10 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
if state == nil || err != nil {
return nil, err
}
-
- storageTrie := state.StorageTrie(address)
+ storageTrie, err := state.StorageTrie(address)
+ if err != nil {
+ return nil, err
+ }
storageHash := types.EmptyRootHash
codeHash := state.GetCodeHash(address)
storageProof := make([]StorageResult, len(storageKeys))
@@ -674,15 +677,19 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
}
// create the proof for the storageKeys
- for i, key := range storageKeys {
+ for i, hexKey := range storageKeys {
+ key, err := decodeHash(hexKey)
+ if err != nil {
+ return nil, err
+ }
if storageTrie != nil {
- proof, storageError := state.GetStorageProof(address, common.HexToHash(key))
+ proof, storageError := state.GetStorageProof(address, key)
if storageError != nil {
return nil, storageError
}
- storageProof[i] = StorageResult{key, (*hexutil.Big)(state.GetState(address, common.HexToHash(key)).Big()), toHexSlice(proof)}
+ storageProof[i] = StorageResult{hexKey, (*hexutil.Big)(state.GetState(address, key).Big()), toHexSlice(proof)}
} else {
- storageProof[i] = StorageResult{key, &hexutil.Big{}, []string{}}
+ storageProof[i] = StorageResult{hexKey, &hexutil.Big{}, []string{}}
}
}
@@ -703,6 +710,25 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
}, state.Error()
}
+// decodeHash parses a hex-encoded 32-byte hash. The input may optionally
+// be prefixed by 0x and can have an byte length up to 32.
+func decodeHash(s string) (common.Hash, error) {
+ if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") {
+ s = s[2:]
+ }
+ if (len(s) & 1) > 0 {
+ s = "0" + s
+ }
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ return common.Hash{}, fmt.Errorf("hex string invalid")
+ }
+ if len(b) > 32 {
+ return common.Hash{}, fmt.Errorf("hex string too long, want at most 32 bytes")
+ }
+ return common.BytesToHash(b), nil
+}
+
// GetHeaderByNumber returns the requested canonical block header.
// * When blockNr is -1 the chain head is returned.
// * When blockNr is -2 the pending chain head is returned.
@@ -731,10 +757,10 @@ func (s *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.Hash) m
}
// GetBlockByNumber returns the requested canonical block.
-// * When blockNr is -1 the chain head is returned.
-// * When blockNr is -2 the pending chain head is returned.
-// * When fullTx is true all transactions in the block are returned, otherwise
-// only the transaction hash is returned.
+// - When blockNr is -1 the chain head is returned.
+// - When blockNr is -2 the pending chain head is returned.
+// - When fullTx is true all transactions in the block are returned, otherwise
+// only the transaction hash is returned.
func (s *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
block, err := s.b.BlockByNumber(ctx, number)
if block != nil && err == nil {
@@ -821,12 +847,16 @@ func (s *BlockChainAPI) GetCode(ctx context.Context, address common.Address, blo
// GetStorageAt returns the storage from the state at the given address, key and
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
// numbers are also allowed.
-func (s *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
+func (s *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, hexKey string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil {
return nil, err
}
- res := state.GetState(address, common.HexToHash(key))
+ key, err := decodeHash(hexKey)
+ if err != nil {
+ return nil, fmt.Errorf("unable to decode storage key: %s", err)
+ }
+ res := state.GetState(address, key)
return res[:], state.Error()
}
@@ -879,6 +909,10 @@ func (diff *StateOverride) Apply(state *state.StateDB) error {
}
}
}
+ // Now finalize the changes. Finalize is normally performed between transactions.
+ // By using finalize, the overrides are semantically behaving as
+ // if they were created in a transaction just before the tracing occur.
+ state.Finalise(false)
return nil
}
@@ -886,7 +920,7 @@ func (diff *StateOverride) Apply(state *state.StateDB) error {
type BlockOverrides struct {
Number *hexutil.Big
Difficulty *hexutil.Big
- Time *hexutil.Big
+ Time *hexutil.Uint64
GasLimit *hexutil.Uint64
Coinbase *common.Address
Random *common.Hash
@@ -905,7 +939,7 @@ func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
blockCtx.Difficulty = diff.Difficulty.ToInt()
}
if diff.Time != nil {
- blockCtx.Time = diff.Time.ToInt()
+ blockCtx.Time = uint64(*diff.Time)
}
if diff.GasLimit != nil {
blockCtx.GasLimit = uint64(*diff.GasLimit)
@@ -1070,7 +1104,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
available := new(big.Int).Set(balance)
if args.Value != nil {
if args.Value.ToInt().Cmp(available) >= 0 {
- return 0, errors.New("insufficient funds for transfer")
+ return 0, core.ErrInsufficientFundsForTransfer
}
available.Sub(available, args.Value.ToInt())
}
@@ -1180,6 +1214,10 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee)
}
+ if head.WithdrawalsHash != nil {
+ result["withdrawalsRoot"] = head.WithdrawalsHash
+ }
+
return result
}
@@ -1215,7 +1253,9 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param
uncleHashes[i] = uncle.Hash()
}
fields["uncles"] = uncleHashes
-
+ if block.Header().WithdrawalsHash != nil {
+ fields["withdrawals"] = block.Withdrawals()
+ }
return fields, nil
}
@@ -1316,8 +1356,8 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
return result
}
-// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
-func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction {
+// NewRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
+func NewRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction {
var baseFee *big.Int
blockNumber := uint64(0)
if current != nil {
@@ -1410,7 +1450,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
}
isPostMerge := header.Difficulty.Cmp(common.Big0) == 0
// Retrieve the precompiles since they don't need to be added to the access list
- precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number, isPostMerge))
+ precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number, isPostMerge, header.Time))
// Create an initial tracer
prevTracer := logger.NewAccessListTracer(nil, args.from(), to, precompiles)
@@ -1549,7 +1589,7 @@ func (s *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.H
}
// No finalized transaction, try to retrieve it from the pool
if tx := s.b.GetPoolTransaction(hash); tx != nil {
- return newRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil
+ return NewRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil
}
// Transaction unknown, return as such
@@ -1691,7 +1731,7 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr
}
if args.Nonce == nil {
- // Hold the addresse's mutex around signing to prevent concurrent assignment of
+ // Hold the mutex around signing to prevent concurrent assignment of
// the same nonce to multiple accounts.
s.nonceLock.LockAddr(args.from())
defer s.nonceLock.UnlockAddr(args.from())
@@ -1819,7 +1859,7 @@ func (s *TransactionAPI) PendingTransactions() ([]*RPCTransaction, error) {
for _, tx := range pending {
from, _ := types.Sender(s.signer, tx)
if _, exists := accounts[from]; exists {
- transactions = append(transactions, newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()))
+ transactions = append(transactions, NewRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()))
}
}
return transactions, nil
@@ -1888,25 +1928,45 @@ func NewDebugAPI(b Backend) *DebugAPI {
return &DebugAPI{b: b}
}
-// GetHeaderRlp retrieves the RLP encoded for of a single header.
-func (api *DebugAPI) GetHeaderRlp(ctx context.Context, number uint64) (hexutil.Bytes, error) {
- header, _ := api.b.HeaderByNumber(ctx, rpc.BlockNumber(number))
+// GetRawHeader retrieves the RLP encoding for a single header.
+func (api *DebugAPI) GetRawHeader(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
+ var hash common.Hash
+ if h, ok := blockNrOrHash.Hash(); ok {
+ hash = h
+ } else {
+ block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash)
+ if err != nil {
+ return nil, err
+ }
+ hash = block.Hash()
+ }
+ header, _ := api.b.HeaderByHash(ctx, hash)
if header == nil {
- return nil, fmt.Errorf("header #%d not found", number)
+ return nil, fmt.Errorf("header #%d not found", hash)
}
return rlp.EncodeToBytes(header)
}
-// GetBlockRlp retrieves the RLP encoded for of a single block.
-func (api *DebugAPI) GetBlockRlp(ctx context.Context, number uint64) (hexutil.Bytes, error) {
- block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number))
+// GetRawBlock retrieves the RLP encoded for a single block.
+func (api *DebugAPI) GetRawBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
+ var hash common.Hash
+ if h, ok := blockNrOrHash.Hash(); ok {
+ hash = h
+ } else {
+ block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash)
+ if err != nil {
+ return nil, err
+ }
+ hash = block.Hash()
+ }
+ block, _ := api.b.BlockByHash(ctx, hash)
if block == nil {
- return nil, fmt.Errorf("block #%d not found", number)
+ return nil, fmt.Errorf("block #%d not found", hash)
}
return rlp.EncodeToBytes(block)
}
-// GetRawReceipts retrieves the binary-encoded raw receipts of a single block.
+// GetRawReceipts retrieves the binary-encoded receipts of a single block.
func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]hexutil.Bytes, error) {
var hash common.Hash
if h, ok := blockNrOrHash.Hash(); ok {
@@ -1933,6 +1993,22 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block
return result, nil
}
+// GetRawTransaction returns the bytes of the transaction for the given hash.
+func (s *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
+ // Retrieve a finalized transaction, or a pooled otherwise
+ tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
+ if err != nil {
+ return nil, err
+ }
+ if tx == nil {
+ if tx = s.b.GetPoolTransaction(hash); tx == nil {
+ // Transaction not found anywhere, abort
+ return nil, nil
+ }
+ }
+ return tx.MarshalBinary()
+}
+
// PrintBlock retrieves a block and returns its pretty printed form.
func (api *DebugAPI) PrintBlock(ctx context.Context, number uint64) (string, error) {
block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number))
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index 4a142e6876..bf51cba272 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -27,10 +27,10 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
@@ -88,9 +88,16 @@ type Backend interface {
Engine() consensus.Engine
SetHistoricalBlocksSynced() bool
+ // This is copied from filters.Backend
// eth/filters needs to be initialized from this backend type, so methods needed by
// it must also be included here.
- filters.Backend
+ GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error)
+ GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error)
+ SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
+ SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
+ SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription
+ BloomStatus() (uint64, uint64)
+ ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
}
func GetAPIs(apiBackend Backend) []rpc.API {
diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go
index 5cd82fa8dd..4b05d611c0 100644
--- a/internal/ethapi/transaction_args_test.go
+++ b/internal/ethapi/transaction_args_test.go
@@ -289,6 +289,9 @@ func (b *backendMock) BlockByHash(ctx context.Context, hash common.Hash) (*types
func (b *backendMock) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
return nil, nil
}
+func (b *backendMock) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
+ return nil, nil
+}
func (b *backendMock) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
return nil, nil, nil
}
diff --git a/internal/flags/customflags.go b/internal/flags/customflags.go
index ac807f7c03..3d6e7acd9f 100644
--- a/internal/flags/customflags.go
+++ b/internal/flags/customflags.go
@@ -7,7 +7,7 @@ import (
"math/big"
"os"
"os/user"
- "path"
+ "path/filepath"
"strings"
"github.com/ethereum/go-ethereum/common/math"
@@ -87,30 +87,6 @@ func (f *DirectoryFlag) GetDefaultText() string {
return f.GetValue()
}
-// Expands a file path
-// 1. replace tilde with users home dir
-// 2. expands embedded environment variables
-// 3. cleans the path, e.g. /a/b/../c -> /a/c
-// Note, it has limitations, e.g. ~someuser/tmp will not be expanded
-func expandPath(p string) string {
- if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
- if home := HomeDir(); home != "" {
- p = home + p[1:]
- }
- }
- return path.Clean(os.ExpandEnv(p))
-}
-
-func HomeDir() string {
- if home := os.Getenv("HOME"); home != "" {
- return home
- }
- if usr, err := user.Current(); err == nil {
- return usr.HomeDir
- }
- return ""
-}
-
type TextMarshaler interface {
encoding.TextMarshaler
encoding.TextUnmarshaler
@@ -292,6 +268,34 @@ func GlobalBig(ctx *cli.Context, name string) *big.Int {
return (*big.Int)(val.(*bigValue))
}
+// Expands a file path
+// 1. replace tilde with users home dir
+// 2. expands embedded environment variables
+// 3. cleans the path, e.g. /a/b/../c -> /a/c
+// Note, it has limitations, e.g. ~someuser/tmp will not be expanded
+func expandPath(p string) string {
+ // Named pipes are not file paths on windows, ignore
+ if strings.HasPrefix(p, `\\.\pipe`) {
+ return p
+ }
+ if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
+ if home := HomeDir(); home != "" {
+ p = home + p[1:]
+ }
+ }
+ return filepath.Clean(os.ExpandEnv(p))
+}
+
+func HomeDir() string {
+ if home := os.Getenv("HOME"); home != "" {
+ return home
+ }
+ if usr, err := user.Current(); err == nil {
+ return usr.HomeDir
+ }
+ return ""
+}
+
func eachName(f cli.Flag, fn func(string)) {
for _, name := range f.Names() {
name = strings.Trim(name, " ")
diff --git a/internal/flags/customflags_test.go b/internal/flags/customflags_test.go
index a0d4af7ca3..681586b46c 100644
--- a/internal/flags/customflags_test.go
+++ b/internal/flags/customflags_test.go
@@ -19,23 +19,43 @@ package flags
import (
"os"
"os/user"
+ "runtime"
"testing"
)
func TestPathExpansion(t *testing.T) {
user, _ := user.Current()
- tests := map[string]string{
- "/home/someuser/tmp": "/home/someuser/tmp",
- "~/tmp": user.HomeDir + "/tmp",
- "~thisOtherUser/b/": "~thisOtherUser/b",
- "$DDDXXX/a/b": "/tmp/a/b",
- "/a/b/": "/a/b",
+ var tests map[string]string
+
+ if runtime.GOOS == "windows" {
+ tests = map[string]string{
+ `/home/someuser/tmp`: `\home\someuser\tmp`,
+ `~/tmp`: user.HomeDir + `\tmp`,
+ `~thisOtherUser/b/`: `~thisOtherUser\b`,
+ `$DDDXXX/a/b`: `\tmp\a\b`,
+ `/a/b/`: `\a\b`,
+ `C:\Documents\Newsletters\`: `C:\Documents\Newsletters`,
+ `C:\`: `C:\`,
+ `\\.\pipe\\pipe\geth621383`: `\\.\pipe\\pipe\geth621383`,
+ }
+ } else {
+ tests = map[string]string{
+ `/home/someuser/tmp`: `/home/someuser/tmp`,
+ `~/tmp`: user.HomeDir + `/tmp`,
+ `~thisOtherUser/b/`: `~thisOtherUser/b`,
+ `$DDDXXX/a/b`: `/tmp/a/b`,
+ `/a/b/`: `/a/b`,
+ `C:\Documents\Newsletters\`: `C:\Documents\Newsletters\`,
+ `C:\`: `C:\`,
+ `\\.\pipe\\pipe\geth621383`: `\\.\pipe\\pipe\geth621383`,
+ }
}
- os.Setenv("DDDXXX", "/tmp")
+
+ os.Setenv(`DDDXXX`, `/tmp`)
for test, expected := range tests {
got := expandPath(test)
if got != expected {
- t.Errorf("test %s, got %s, expected %s\n", test, got, expected)
+ t.Errorf(`test %s, got %s, expected %s\n`, test, got, expected)
}
}
}
diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go
index 4bcdc816fe..f210e729dd 100644
--- a/internal/flags/helpers.go
+++ b/internal/flags/helpers.go
@@ -20,17 +20,19 @@ import (
"fmt"
"strings"
+ "github.com/ethereum/go-ethereum/internal/version"
"github.com/ethereum/go-ethereum/params"
"github.com/urfave/cli/v2"
)
// NewApp creates an app with sane defaults.
-func NewApp(gitCommit, gitDate, usage string) *cli.App {
+func NewApp(usage string) *cli.App {
+ git, _ := version.VCS()
app := cli.NewApp()
app.EnableBashCompletion = true
- app.Version = params.VersionWithCommit(gitCommit, gitDate)
+ app.Version = params.VersionWithCommit(git.Commit, git.Date)
app.Usage = usage
- app.Copyright = "Copyright 2013-2022 The go-ethereum Authors"
+ app.Copyright = "Copyright 2013-2023 The go-ethereum Authors"
app.Before = func(ctx *cli.Context) error {
MigrateGlobalFlags(ctx)
return nil
@@ -54,11 +56,11 @@ var migrationApplied = map[*cli.Command]struct{}{}
//
// Example:
//
-// geth account new --keystore /tmp/mykeystore --lightkdf
+// geth account new --keystore /tmp/mykeystore --lightkdf
//
// is equivalent after calling this method with:
//
-// geth --keystore /tmp/mykeystore --lightkdf account new
+// geth --keystore /tmp/mykeystore --lightkdf account new
//
// i.e. in the subcommand Action function of 'account new', ctx.Bool("lightkdf)
// will return true even if --lightkdf is set as a global option.
@@ -92,10 +94,34 @@ func MigrateGlobalFlags(ctx *cli.Context) {
}
func doMigrateFlags(ctx *cli.Context) {
+ // Figure out if there are any aliases of commands. If there are, we want
+ // to ignore them when iterating over the flags.
+ var aliases = make(map[string]bool)
+ for _, fl := range ctx.Command.Flags {
+ for _, alias := range fl.Names()[1:] {
+ aliases[alias] = true
+ }
+ }
for _, name := range ctx.FlagNames() {
for _, parent := range ctx.Lineage()[1:] {
if parent.IsSet(name) {
- ctx.Set(name, parent.String(name))
+ // When iterating across the lineage, we will be served both
+ // the 'canon' and alias formats of all commmands. In most cases,
+ // it's fine to set it in the ctx multiple times (one for each
+ // name), however, the Slice-flags are not fine.
+ // The slice-flags accumulate, so if we set it once as
+ // "foo" and once as alias "F", then both will be present in the slice.
+ if _, isAlias := aliases[name]; isAlias {
+ continue
+ }
+ // If it is a string-slice, we need to set it as
+ // "alfa, beta, gamma" instead of "[alfa beta gamma]", in order
+ // for the backing StringSlice to parse it properly.
+ if result := parent.StringSlice(name); len(result) > 0 {
+ ctx.Set(name, strings.Join(result, ","))
+ } else {
+ ctx.Set(name, parent.String(name))
+ }
break
}
}
diff --git a/internal/guide/guide_test.go b/internal/guide/guide_test.go
index cdf0ec4d26..f682daac91 100644
--- a/internal/guide/guide_test.go
+++ b/internal/guide/guide_test.go
@@ -38,8 +38,8 @@ func TestAccountManagement(t *testing.T) {
// Create a temporary folder to work with
workdir := t.TempDir()
- // Create an encrypted keystore with standard crypto parameters
- ks := keystore.NewKeyStore(filepath.Join(workdir, "keystore"), keystore.StandardScryptN, keystore.StandardScryptP)
+ // Create an encrypted keystore (using light scrypt parameters)
+ ks := keystore.NewKeyStore(filepath.Join(workdir, "keystore"), keystore.LightScryptN, keystore.LightScryptP)
// Create a new account with the specified encryption passphrase
newAcc, err := ks.NewAccount("Creation password")
diff --git a/internal/jsre/completion.go b/internal/jsre/completion.go
index 538fca298d..844a0532fd 100644
--- a/internal/jsre/completion.go
+++ b/internal/jsre/completion.go
@@ -17,12 +17,16 @@
package jsre
import (
+ "regexp"
"sort"
"strings"
"github.com/dop251/goja"
)
+// JS numerical token
+var numerical = regexp.MustCompile(`^(NaN|-?((\d*\.\d+|\d+)([Ee][+-]?\d+)?|Infinity))$`)
+
// CompleteKeywords returns potential continuations for the given line. Since line is
// evaluated, callers need to make sure that evaluating line does not have side effects.
func (jsre *JSRE) CompleteKeywords(line string) []string {
@@ -43,6 +47,9 @@ func getCompletions(vm *goja.Runtime, line string) (results []string) {
// and "x.y" is an object, obj will reference "x.y".
obj := vm.GlobalObject()
for i := 0; i < len(parts)-1; i++ {
+ if numerical.MatchString(parts[i]) {
+ return nil
+ }
v := obj.Get(parts[i])
if v == nil || goja.IsNull(v) || goja.IsUndefined(v) {
return nil // No object was found
@@ -67,7 +74,11 @@ func getCompletions(vm *goja.Runtime, line string) (results []string) {
// Append opening parenthesis (for functions) or dot (for objects)
// if the line itself is the only completion.
if len(results) == 1 && results[0] == line {
- obj := obj.Get(parts[len(parts)-1])
+ // Accessing the property will cause it to be evaluated.
+ // This can cause an error, e.g. in case of web3.eth.protocolVersion
+ // which has been dropped from geth. Ignore the error for autocompletion
+ // purposes.
+ obj := SafeGet(obj, parts[len(parts)-1])
if obj != nil {
if _, isfunc := goja.AssertFunction(obj); isfunc {
results[0] += "("
diff --git a/mobile/geth_ios.go b/internal/version/vcs_fallback.go
similarity index 73%
rename from mobile/geth_ios.go
rename to internal/version/vcs_fallback.go
index aab839727f..f792c68cdb 100644
--- a/mobile/geth_ios.go
+++ b/internal/version/vcs_fallback.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// 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
@@ -14,10 +14,15 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-//go:build ios
-// +build ios
+//go:build !go1.18
+// +build !go1.18
-package geth
+package version
-// clientIdentifier is a hard coded identifier to report into the network.
-var clientIdentifier = "iGeth"
+import "runtime/debug"
+
+// In Go versions before 1.18, VCS information is not available.
+
+func buildInfoVCS(info *debug.BuildInfo) (VCSInfo, bool) {
+ return VCSInfo{}, false
+}
diff --git a/internal/version/vcs_go1.18.go b/internal/version/vcs_go1.18.go
new file mode 100644
index 0000000000..53cd41fb30
--- /dev/null
+++ b/internal/version/vcs_go1.18.go
@@ -0,0 +1,55 @@
+// 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 .
+
+//go:build go1.18
+// +build go1.18
+
+package version
+
+import (
+ "runtime/debug"
+ "time"
+)
+
+// In go 1.18 and beyond, the go tool embeds VCS information into the build.
+
+const (
+ govcsTimeLayout = "2006-01-02T15:04:05Z"
+ ourTimeLayout = "20060102"
+)
+
+// buildInfoVCS returns VCS information of the build.
+func buildInfoVCS(info *debug.BuildInfo) (s VCSInfo, ok bool) {
+ for _, v := range info.Settings {
+ switch v.Key {
+ case "vcs.revision":
+ s.Commit = v.Value
+ case "vcs.modified":
+ if v.Value == "true" {
+ s.Dirty = true
+ }
+ case "vcs.time":
+ t, err := time.Parse(govcsTimeLayout, v.Value)
+ if err == nil {
+ s.Date = t.Format(ourTimeLayout)
+ }
+ }
+ }
+ if s.Commit != "" && s.Date != "" {
+ ok = true
+ }
+ return
+}
diff --git a/internal/version/version.go b/internal/version/version.go
new file mode 100644
index 0000000000..0daea02b57
--- /dev/null
+++ b/internal/version/version.go
@@ -0,0 +1,141 @@
+// 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 version implements reading of build version information.
+package version
+
+import (
+ "fmt"
+ "runtime"
+ "runtime/debug"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/params"
+)
+
+const ourPath = "github.com/ethereum/go-ethereum" // Path to our module
+
+// These variables are set at build-time by the linker when the build is
+// done by build/ci.go.
+var gitCommit, gitDate string
+
+// VCSInfo represents the git repository state.
+type VCSInfo struct {
+ Commit string // head commit hash
+ Date string // commit time in YYYYMMDD format
+ Dirty bool
+}
+
+// VCS returns version control information of the current executable.
+func VCS() (VCSInfo, bool) {
+ if gitCommit != "" {
+ // Use information set by the build script if present.
+ return VCSInfo{Commit: gitCommit, Date: gitDate}, true
+ }
+ if buildInfo, ok := debug.ReadBuildInfo(); ok {
+ if buildInfo.Main.Path == ourPath {
+ return buildInfoVCS(buildInfo)
+ }
+ }
+ return VCSInfo{}, false
+}
+
+// ClientName creates a software name/version identifier according to common
+// conventions in the Ethereum p2p network.
+func ClientName(clientIdentifier string) string {
+ git, _ := VCS()
+ return fmt.Sprintf("%s/v%v/%v-%v/%v",
+ strings.Title(clientIdentifier),
+ params.VersionWithCommit(git.Commit, git.Date),
+ runtime.GOOS, runtime.GOARCH,
+ runtime.Version(),
+ )
+}
+
+// runtimeInfo returns build and platform information about the current binary.
+//
+// If the package that is currently executing is a prefixed by our go-ethereum
+// module path, it will print out commit and date VCS information. Otherwise,
+// it will assume it's imported by a third-party and will return the imported
+// version and whether it was replaced by another module.
+func Info() (version, vcs string) {
+ version = params.VersionWithMeta
+ buildInfo, ok := debug.ReadBuildInfo()
+ if !ok {
+ return version, ""
+ }
+ version = versionInfo(buildInfo)
+ if status, ok := VCS(); ok {
+ modified := ""
+ if status.Dirty {
+ modified = " (dirty)"
+ }
+ commit := status.Commit
+ if len(commit) > 8 {
+ commit = commit[:8]
+ }
+ vcs = commit + "-" + status.Date + modified
+ }
+ return version, vcs
+}
+
+// versionInfo returns version information for the currently executing
+// implementation.
+//
+// Depending on how the code is instantiated, it returns different amounts of
+// information. If it is unable to determine which module is related to our
+// package it falls back to the hardcoded values in the params package.
+func versionInfo(info *debug.BuildInfo) string {
+ // If the main package is from our repo, prefix version with "geth".
+ if strings.HasPrefix(info.Path, ourPath) {
+ return fmt.Sprintf("geth %s", info.Main.Version)
+ }
+ // Not our main package, so explicitly print out the module path and
+ // version.
+ var version string
+ if info.Main.Path != "" && info.Main.Version != "" {
+ // These can be empty when invoked with "go run".
+ version = fmt.Sprintf("%s@%s ", info.Main.Path, info.Main.Version)
+ }
+ mod := findModule(info, ourPath)
+ if mod == nil {
+ // If our module path wasn't imported, it's unclear which
+ // version of our code they are running. Fallback to hardcoded
+ // version.
+ return version + fmt.Sprintf("geth %s", params.VersionWithMeta)
+ }
+ // Our package is a dependency for the main module. Return path and
+ // version data for both.
+ version += fmt.Sprintf("%s@%s", mod.Path, mod.Version)
+ if mod.Replace != nil {
+ // If our package was replaced by something else, also note that.
+ version += fmt.Sprintf(" (replaced by %s@%s)", mod.Replace.Path, mod.Replace.Version)
+ }
+ return version
+}
+
+// findModule returns the module at path.
+func findModule(info *debug.BuildInfo, path string) *debug.Module {
+ if info.Path == ourPath {
+ return &info.Main
+ }
+ for _, mod := range info.Deps {
+ if mod.Path == path {
+ return mod
+ }
+ }
+ return nil
+}
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index 754ebe74d4..ea65f6e874 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -224,13 +224,13 @@ web3._extend({
outputFormatter: console.log
}),
new web3._extend.Method({
- name: 'getHeaderRlp',
- call: 'debug_getHeaderRlp',
+ name: 'getRawHeader',
+ call: 'debug_getRawHeader',
params: 1
}),
new web3._extend.Method({
- name: 'getBlockRlp',
- call: 'debug_getBlockRlp',
+ name: 'getRawBlock',
+ call: 'debug_getRawBlock',
params: 1
}),
new web3._extend.Method({
@@ -238,6 +238,11 @@ web3._extend({
call: 'debug_getRawReceipts',
params: 1
}),
+ new web3._extend.Method({
+ name: 'getRawTransaction',
+ call: 'debug_getRawTransaction',
+ params: 1
+ }),
new web3._extend.Method({
name: 'setHead',
call: 'debug_setHead',
@@ -485,6 +490,11 @@ web3._extend({
call: 'debug_dbAncients',
params: 0
}),
+ new web3._extend.Method({
+ name: 'setTrieFlushInterval',
+ call: 'debug_setTrieFlushInterval',
+ params: 1
+ }),
],
properties: []
});
@@ -601,6 +611,12 @@ web3._extend({
call: 'eth_getLogs',
params: 1,
}),
+ new web3._extend.Method({
+ name: 'call',
+ call: 'eth_call',
+ params: 3,
+ inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputDefaultBlockNumberFormatter, null],
+ }),
],
properties: [
new web3._extend.Property({
diff --git a/les/api.go b/les/api.go
index dc86393817..3b21b635ac 100644
--- a/les/api.go
+++ b/les/api.go
@@ -366,10 +366,11 @@ func NewLightAPI(backend *lesCommons) *LightAPI {
// LatestCheckpoint returns the latest local checkpoint package.
//
// The checkpoint package consists of 4 strings:
-// result[0], hex encoded latest section index
-// result[1], 32 bytes hex encoded latest section head hash
-// result[2], 32 bytes hex encoded latest section canonical hash trie root hash
-// result[3], 32 bytes hex encoded latest section bloom trie root hash
+//
+// result[0], hex encoded latest section index
+// result[1], 32 bytes hex encoded latest section head hash
+// result[2], 32 bytes hex encoded latest section canonical hash trie root hash
+// result[3], 32 bytes hex encoded latest section bloom trie root hash
func (api *LightAPI) LatestCheckpoint() ([4]string, error) {
var res [4]string
cp := api.backend.latestLocalCheckpoint()
@@ -381,12 +382,13 @@ func (api *LightAPI) LatestCheckpoint() ([4]string, error) {
return res, nil
}
-// GetLocalCheckpoint returns the specific local checkpoint package.
+// GetCheckpoint returns the specific local checkpoint package.
//
// The checkpoint package consists of 3 strings:
-// result[0], 32 bytes hex encoded latest section head hash
-// result[1], 32 bytes hex encoded latest section canonical hash trie root hash
-// result[2], 32 bytes hex encoded latest section bloom trie root hash
+//
+// result[0], 32 bytes hex encoded latest section head hash
+// result[1], 32 bytes hex encoded latest section canonical hash trie root hash
+// result[2], 32 bytes hex encoded latest section bloom trie root hash
func (api *LightAPI) GetCheckpoint(index uint64) ([3]string, error) {
var res [3]string
cp := api.backend.localCheckpoint(index)
diff --git a/les/api_backend.go b/les/api_backend.go
index 5c7c7050a4..40801d6ea9 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/gasprice"
+ "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/light"
@@ -130,6 +131,10 @@ func (b *LesApiBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r
return nil, errors.New("invalid arguments; neither block nor hash specified")
}
+func (b *LesApiBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
+ return light.GetBody(ctx, b.eth.odr, hash, uint64(number))
+}
+
func (b *LesApiBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
return nil, nil
}
@@ -322,11 +327,11 @@ func (b *LesApiBackend) CurrentHeader() *types.Header {
return b.eth.blockchain.CurrentHeader()
}
-func (b *LesApiBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (*state.StateDB, error) {
+func (b *LesApiBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, tracers.StateReleaseFunc, error) {
return b.eth.stateAtBlock(ctx, block, reexec)
}
-func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
+func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
}
diff --git a/les/api_test.go b/les/api_test.go
index 3db1c5fd5e..db680da0bf 100644
--- a/les/api_test.go
+++ b/les/api_test.go
@@ -18,6 +18,7 @@ package les
import (
"context"
+ crand "crypto/rand"
"errors"
"flag"
"math/rand"
@@ -326,7 +327,7 @@ func getHead(ctx context.Context, t *testing.T, client *rpc.Client) (uint64, com
func testRequest(ctx context.Context, t *testing.T, client *rpc.Client) bool {
var res string
var addr common.Address
- rand.Read(addr[:])
+ crand.Read(addr[:])
c, cancel := context.WithTimeout(ctx, time.Second*12)
defer cancel()
err := client.CallContext(c, &res, "eth_getBalance", addr, "latest")
diff --git a/les/benchmark.go b/les/benchmark.go
index 757822a6b3..95563a21aa 100644
--- a/les/benchmark.go
+++ b/les/benchmark.go
@@ -17,6 +17,7 @@
package les
import (
+ crand "crypto/rand"
"encoding/binary"
"fmt"
"math/big"
@@ -114,7 +115,7 @@ func (b *benchmarkProofsOrCode) init(h *serverHandler, count int) error {
func (b *benchmarkProofsOrCode) request(peer *serverPeer, index int) error {
key := make([]byte, 32)
- rand.Read(key)
+ crand.Read(key)
if b.code {
return peer.requestCode(0, []CodeReq{{BHash: b.headHash, AccKey: key}})
}
@@ -176,7 +177,7 @@ func (b *benchmarkTxSend) init(h *serverHandler, count int) error {
for i := range b.txs {
data := make([]byte, txSizeCostLimit)
- rand.Read(data)
+ crand.Read(data)
tx, err := types.SignTx(types.NewTransaction(0, addr, new(big.Int), 0, new(big.Int), data), signer, key)
if err != nil {
panic(err)
@@ -200,7 +201,7 @@ func (b *benchmarkTxStatus) init(h *serverHandler, count int) error {
func (b *benchmarkTxStatus) request(peer *serverPeer, index int) error {
var hash common.Hash
- rand.Read(hash[:])
+ crand.Read(hash[:])
return peer.requestTxStatus(0, []common.Hash{hash})
}
@@ -278,7 +279,7 @@ func (h *serverHandler) measure(setup *benchmarkSetup, count int) error {
clientMeteredPipe := &meteredPipe{rw: clientPipe}
serverMeteredPipe := &meteredPipe{rw: serverPipe}
var id enode.ID
- rand.Read(id[:])
+ crand.Read(id[:])
peer1 := newServerPeer(lpv2, NetworkId, false, p2p.NewPeer(id, "client", nil), clientMeteredPipe)
peer2 := newClientPeer(lpv2, NetworkId, p2p.NewPeer(id, "server", nil), serverMeteredPipe)
diff --git a/les/catalyst/api.go b/les/catalyst/api.go
index 983fc7bff0..c9db514fcc 100644
--- a/les/catalyst/api.go
+++ b/les/catalyst/api.go
@@ -21,8 +21,9 @@ import (
"errors"
"fmt"
+ "github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/beacon"
+ "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
@@ -56,52 +57,56 @@ func NewConsensusAPI(les *les.LightEthereum) *ConsensusAPI {
}
// ForkchoiceUpdatedV1 has several responsibilities:
-// If the method is called with an empty head block:
-// we return success, which can be used to check if the catalyst mode is enabled
-// If the total difficulty was not reached:
-// we return INVALID
-// If the finalizedBlockHash is set:
-// we check if we have the finalizedBlockHash in our db, if not we start a sync
-// We try to set our blockchain to the headBlock
-// If there are payloadAttributes:
-// we return an error since block creation is not supported in les mode
-func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) {
+//
+// We try to set our blockchain to the headBlock.
+//
+// If the method is called with an empty head block: we return success, which can be used
+// to check if the catalyst mode is enabled.
+//
+// If the total difficulty was not reached: we return INVALID.
+//
+// If the finalizedBlockHash is set: we check if we have the finalizedBlockHash in our db,
+// if not we start a sync.
+//
+// If there are payloadAttributes: we return an error since block creation is not
+// supported in les mode.
+func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
if heads.HeadBlockHash == (common.Hash{}) {
log.Warn("Forkchoice requested update to zero hash")
- return beacon.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
+ return engine.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
}
if err := api.checkTerminalTotalDifficulty(heads.HeadBlockHash); err != nil {
if header := api.les.BlockChain().GetHeaderByHash(heads.HeadBlockHash); header == nil {
// TODO (MariusVanDerWijden) trigger sync
- return beacon.STATUS_SYNCING, nil
+ return engine.STATUS_SYNCING, nil
}
- return beacon.STATUS_INVALID, err
+ return engine.STATUS_INVALID, err
}
// If the finalized block is set, check if it is in our blockchain
if heads.FinalizedBlockHash != (common.Hash{}) {
if header := api.les.BlockChain().GetHeaderByHash(heads.FinalizedBlockHash); header == nil {
// TODO (MariusVanDerWijden) trigger sync
- return beacon.STATUS_SYNCING, nil
+ return engine.STATUS_SYNCING, nil
}
}
// SetHead
if err := api.setCanonical(heads.HeadBlockHash); err != nil {
- return beacon.STATUS_INVALID, err
+ return engine.STATUS_INVALID, err
}
if payloadAttributes != nil {
- return beacon.STATUS_INVALID, errors.New("not supported")
+ return engine.STATUS_INVALID, errors.New("not supported")
}
return api.validForkChoiceResponse(), nil
}
// GetPayloadV1 returns a cached payload by id. It's not supported in les mode.
-func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableDataV1, error) {
- return nil, beacon.GenericServerError.With(errors.New("not supported in light client mode"))
+func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.ExecutableData, error) {
+ return nil, engine.GenericServerError.With(errors.New("not supported in light client mode"))
}
// ExecutePayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
-func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beacon.PayloadStatusV1, error) {
- block, err := beacon.ExecutableDataToBlock(params)
+func (api *ConsensusAPI) ExecutePayloadV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
+ block, err := engine.ExecutableDataToBlock(params)
if err != nil {
return api.invalid(), err
}
@@ -113,7 +118,7 @@ func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beaco
}
*/
// TODO (MariusVanDerWijden) we should return nil here not empty hash
- return beacon.PayloadStatusV1{Status: beacon.SYNCING, LatestValidHash: nil}, nil
+ return engine.PayloadStatusV1{Status: engine.SYNCING, LatestValidHash: nil}, nil
}
parent := api.les.BlockChain().GetHeaderByHash(params.ParentHash)
if parent == nil {
@@ -131,20 +136,20 @@ func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beaco
merger.ReachTTD()
}
hash := block.Hash()
- return beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &hash}, nil
+ return engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &hash}, nil
}
-func (api *ConsensusAPI) validForkChoiceResponse() beacon.ForkChoiceResponse {
+func (api *ConsensusAPI) validForkChoiceResponse() engine.ForkChoiceResponse {
currentHash := api.les.BlockChain().CurrentHeader().Hash()
- return beacon.ForkChoiceResponse{
- PayloadStatus: beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: ¤tHash},
+ return engine.ForkChoiceResponse{
+ PayloadStatus: engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: ¤tHash},
}
}
// invalid returns a response "INVALID" with the latest valid hash set to the current head.
-func (api *ConsensusAPI) invalid() beacon.PayloadStatusV1 {
+func (api *ConsensusAPI) invalid() engine.PayloadStatusV1 {
currentHash := api.les.BlockChain().CurrentHeader().Hash()
- return beacon.PayloadStatusV1{Status: beacon.INVALID, LatestValidHash: ¤tHash}
+ return engine.PayloadStatusV1{Status: engine.INVALID, LatestValidHash: ¤tHash}
}
func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error {
@@ -185,3 +190,31 @@ func (api *ConsensusAPI) setCanonical(newHead common.Hash) error {
}
return nil
}
+
+// ExchangeTransitionConfigurationV1 checks the given configuration against
+// the configuration of the node.
+func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.TransitionConfigurationV1) (*engine.TransitionConfigurationV1, error) {
+ log.Trace("Engine API request received", "method", "ExchangeTransitionConfiguration", "ttd", config.TerminalTotalDifficulty)
+ if config.TerminalTotalDifficulty == nil {
+ return nil, errors.New("invalid terminal total difficulty")
+ }
+
+ ttd := api.les.BlockChain().Config().TerminalTotalDifficulty
+ if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
+ log.Warn("Invalid TTD configured", "geth", ttd, "beacon", config.TerminalTotalDifficulty)
+ return nil, fmt.Errorf("invalid ttd: execution %v consensus %v", ttd, config.TerminalTotalDifficulty)
+ }
+
+ if config.TerminalBlockHash != (common.Hash{}) {
+ if hash := api.les.BlockChain().GetCanonicalHash(uint64(config.TerminalBlockNumber)); hash == config.TerminalBlockHash {
+ return &engine.TransitionConfigurationV1{
+ TerminalTotalDifficulty: (*hexutil.Big)(ttd),
+ TerminalBlockHash: config.TerminalBlockHash,
+ TerminalBlockNumber: config.TerminalBlockNumber,
+ }, nil
+ }
+ return nil, fmt.Errorf("invalid terminal block hash")
+ }
+
+ return &engine.TransitionConfigurationV1{TerminalTotalDifficulty: (*hexutil.Big)(ttd)}, nil
+}
diff --git a/les/catalyst/api_test.go b/les/catalyst/api_test.go
index 26c49d6ef9..54757f61da 100644
--- a/les/catalyst/api_test.go
+++ b/les/catalyst/api_test.go
@@ -20,11 +20,10 @@ import (
"math/big"
"testing"
+ "github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/beacon"
- "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/downloader"
@@ -45,38 +44,47 @@ var (
testBalance = big.NewInt(2e18)
)
-func generatePreMergeChain(n int) (*core.Genesis, []*types.Header, []*types.Block) {
- db := rawdb.NewMemoryDatabase()
- config := params.AllEthashProtocolChanges
+func generatePreMergeChain(pre, post int) (*core.Genesis, []*types.Header, []*types.Block, []*types.Header, []*types.Block) {
+ config := *params.AllEthashProtocolChanges
genesis := &core.Genesis{
- Config: config,
+ Config: &config,
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
ExtraData: []byte("test genesis"),
Timestamp: 9000,
BaseFee: big.NewInt(params.InitialBaseFee),
}
- gblock := genesis.MustCommit(db)
- engine := ethash.NewFaker()
- blocks, _ := core.GenerateChain(config, gblock, engine, db, n, nil)
- totalDifficulty := big.NewInt(0)
+ // Pre-merge blocks
+ db, preBLocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), pre, nil)
+ totalDifficulty := new(big.Int).Set(params.GenesisDifficulty)
- var headers []*types.Header
- for _, b := range blocks {
+ var preHeaders []*types.Header
+ for _, b := range preBLocks {
totalDifficulty.Add(totalDifficulty, b.Difficulty())
- headers = append(headers, b.Header())
+ preHeaders = append(preHeaders, b.Header())
}
config.TerminalTotalDifficulty = totalDifficulty
+ // Post-merge blocks
+ postBlocks, _ := core.GenerateChain(genesis.Config,
+ preBLocks[len(preBLocks)-1], ethash.NewFaker(), db, post,
+ func(i int, b *core.BlockGen) {
+ b.SetPoS()
+ })
- return genesis, headers, blocks
+ var postHeaders []*types.Header
+ for _, b := range postBlocks {
+ postHeaders = append(postHeaders, b.Header())
+ }
+
+ return genesis, preHeaders, preBLocks, postHeaders, postBlocks
}
func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
- genesis, headers, blocks := generatePreMergeChain(10)
+ genesis, headers, blocks, _, _ := generatePreMergeChain(10, 0)
n, lesService := startLesService(t, genesis, headers)
defer n.Close()
api := NewConsensusAPI(lesService)
- fcState := beacon.ForkchoiceStateV1{
+ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: blocks[5].Hash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
@@ -87,21 +95,21 @@ func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
}
func TestExecutePayloadV1(t *testing.T) {
- genesis, headers, blocks := generatePreMergeChain(10)
- n, lesService := startLesService(t, genesis, headers[:9])
+ genesis, headers, _, _, postBlocks := generatePreMergeChain(10, 2)
+ n, lesService := startLesService(t, genesis, headers)
lesService.Merger().ReachTTD()
defer n.Close()
api := NewConsensusAPI(lesService)
- fcState := beacon.ForkchoiceStateV1{
- HeadBlockHash: blocks[8].Hash(),
+ fcState := engine.ForkchoiceStateV1{
+ HeadBlockHash: postBlocks[0].Hash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
}
if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
t.Errorf("Failed to update head %v", err)
}
- block := blocks[9]
+ block := postBlocks[0]
fakeBlock := types.NewBlock(&types.Header{
ParentHash: block.ParentHash(),
@@ -122,7 +130,7 @@ func TestExecutePayloadV1(t *testing.T) {
BaseFee: block.BaseFee(),
}, nil, nil, nil, trie.NewStackTrie(nil))
- _, err := api.ExecutePayloadV1(beacon.ExecutableDataV1{
+ _, err := api.ExecutePayloadV1(engine.ExecutableData{
ParentHash: fakeBlock.ParentHash(),
FeeRecipient: fakeBlock.Coinbase(),
StateRoot: fakeBlock.Root(),
@@ -145,7 +153,7 @@ func TestExecutePayloadV1(t *testing.T) {
if headHeader.Number.Uint64() != fakeBlock.NumberU64()-1 {
t.Fatal("Unexpected chain head update")
}
- fcState = beacon.ForkchoiceStateV1{
+ fcState = engine.ForkchoiceStateV1{
HeadBlockHash: fakeBlock.Hash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
diff --git a/les/client.go b/les/client.go
index 6504fe2af8..9ac85ecdac 100644
--- a/les/client.go
+++ b/les/client.go
@@ -48,6 +48,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
+ "github.com/ethereum/go-ethereum/trie"
)
type LightEthereum struct {
@@ -92,13 +93,17 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
if err != nil {
return nil, err
}
- chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideTerminalTotalDifficulty, config.OverrideTerminalTotalDifficultyPassed)
+ var overrides core.ChainOverrides
+ if config.OverrideShanghai != nil {
+ overrides.OverrideShanghai = config.OverrideShanghai
+ }
+ chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, trie.NewDatabase(chainDb), config.Genesis, &overrides)
if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat {
return nil, genesisErr
}
log.Info("")
log.Info(strings.Repeat("-", 153))
- for _, line := range strings.Split(chainConfig.String(), "\n") {
+ for _, line := range strings.Split(chainConfig.Description(), "\n") {
log.Info(line)
}
log.Info(strings.Repeat("-", 153))
@@ -121,7 +126,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
reqDist: newRequestDistributor(peers, &mclock.System{}),
accountManager: stack.AccountManager(),
merger: merger,
- engine: ethconfig.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb),
+ engine: ethconfig.CreateConsensusEngine(stack, &config.Ethash, chainConfig.Clique, nil, false, chainDb),
bloomRequests: make(chan chan *bloombits.Retrieval),
bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations),
p2pServer: stack.Server(),
@@ -171,7 +176,11 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
// Rewind the chain in case of an incompatible config upgrade.
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
- leth.blockchain.SetHead(compat.RewindTo)
+ if compat.RewindToTime > 0 {
+ leth.blockchain.SetHeadWithTimestamp(compat.RewindToTime)
+ } else {
+ leth.blockchain.SetHead(compat.RewindToBlock)
+ }
rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
}
diff --git a/les/client_handler.go b/les/client_handler.go
index e416f92e29..cce99d41dc 100644
--- a/les/client_handler.go
+++ b/les/client_handler.go
@@ -111,7 +111,7 @@ func (h *clientHandler) handle(p *serverPeer, noInitAnnounce bool) error {
p.Log().Debug("Light Ethereum peer connected", "name", p.Name())
// Execute the LES handshake
- forkid := forkid.NewID(h.backend.blockchain.Config(), h.backend.genesis, h.backend.blockchain.CurrentHeader().Number.Uint64())
+ forkid := forkid.NewID(h.backend.blockchain.Config(), h.backend.genesis, h.backend.blockchain.CurrentHeader().Number.Uint64(), h.backend.blockchain.CurrentHeader().Time)
if err := p.Handshake(h.backend.blockchain.Genesis().Hash(), forkid, h.forkFilter); err != nil {
p.Log().Debug("Light Ethereum handshake failed", "err", err)
return err
diff --git a/les/commons.go b/les/commons.go
index d090fc21fc..860175c8ca 100644
--- a/les/commons.go
+++ b/les/commons.go
@@ -63,7 +63,7 @@ type lesCommons struct {
// NodeInfo represents a short summary of the Ethereum sub-protocol metadata
// known about the host peer.
type NodeInfo struct {
- Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3, Rinkeby=4)
+ Network uint64 `json:"network"` // Ethereum network ID (1=Mainnet, Rinkeby=4, Goerli=5)
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules
diff --git a/les/downloader/downloader.go b/les/downloader/downloader.go
index 6352b1c217..a6ebf1d2a5 100644
--- a/les/downloader/downloader.go
+++ b/les/downloader/downloader.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// This is a temporary package whilst working on the eth/66 blocking refactors.
+// Package downloader is a temporary package whilst working on the eth/66 blocking refactors.
// After that work is done, les needs to be refactored to use the new package,
// or alternatively use a stripped down version of it. Either way, we need to
// keep the changes scoped so duplicating temporarily seems the sanest.
@@ -226,7 +226,7 @@ func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain Bl
headerProcCh: make(chan []*types.Header, 1),
quitCh: make(chan struct{}),
stateCh: make(chan dataPack),
- SnapSyncer: snap.NewSyncer(stateDb),
+ SnapSyncer: snap.NewSyncer(stateDb, rawdb.HashScheme),
stateSyncStart: make(chan *stateSync),
//syncStatsState: stateSyncStats{
// processed: rawdb.ReadFastTrieProgress(stateDb),
@@ -693,9 +693,11 @@ func (d *Downloader) fetchHead(p *peerConnection) (head *types.Header, pivot *ty
// calculateRequestSpan calculates what headers to request from a peer when trying to determine the
// common ancestor.
// It returns parameters to be used for peer.RequestHeadersByNumber:
-// from - starting block number
-// count - number of headers to request
-// skip - number of headers to skip
+//
+// from - starting block number
+// count - number of headers to request
+// skip - number of headers to skip
+//
// and also returns 'max', the last block which is expected to be returned by the remote peers,
// given the (from,count,skip)
func calculateRequestSpan(remoteHeight, localHeight uint64) (int64, int, int, uint64) {
@@ -1310,22 +1312,22 @@ func (d *Downloader) fetchReceipts(from uint64) error {
// various callbacks to handle the slight differences between processing them.
//
// The instrumentation parameters:
-// - errCancel: error type to return if the fetch operation is cancelled (mostly makes logging nicer)
-// - deliveryCh: channel from which to retrieve downloaded data packets (merged from all concurrent peers)
-// - deliver: processing callback to deliver data packets into type specific download queues (usually within `queue`)
-// - wakeCh: notification channel for waking the fetcher when new tasks are available (or sync completed)
-// - expire: task callback method to abort requests that took too long and return the faulty peers (traffic shaping)
-// - pending: task callback for the number of requests still needing download (detect completion/non-completability)
-// - inFlight: task callback for the number of in-progress requests (wait for all active downloads to finish)
-// - throttle: task callback to check if the processing queue is full and activate throttling (bound memory use)
-// - reserve: task callback to reserve new download tasks to a particular peer (also signals partial completions)
-// - fetchHook: tester callback to notify of new tasks being initiated (allows testing the scheduling logic)
-// - fetch: network callback to actually send a particular download request to a physical remote peer
-// - cancel: task callback to abort an in-flight download request and allow rescheduling it (in case of lost peer)
-// - capacity: network callback to retrieve the estimated type-specific bandwidth capacity of a peer (traffic shaping)
-// - idle: network callback to retrieve the currently (type specific) idle peers that can be assigned tasks
-// - setIdle: network callback to set a peer back to idle and update its estimated capacity (traffic shaping)
-// - kind: textual label of the type being downloaded to display in log messages
+// - errCancel: error type to return if the fetch operation is cancelled (mostly makes logging nicer)
+// - deliveryCh: channel from which to retrieve downloaded data packets (merged from all concurrent peers)
+// - deliver: processing callback to deliver data packets into type specific download queues (usually within `queue`)
+// - wakeCh: notification channel for waking the fetcher when new tasks are available (or sync completed)
+// - expire: task callback method to abort requests that took too long and return the faulty peers (traffic shaping)
+// - pending: task callback for the number of requests still needing download (detect completion/non-completability)
+// - inFlight: task callback for the number of in-progress requests (wait for all active downloads to finish)
+// - throttle: task callback to check if the processing queue is full and activate throttling (bound memory use)
+// - reserve: task callback to reserve new download tasks to a particular peer (also signals partial completions)
+// - fetchHook: tester callback to notify of new tasks being initiated (allows testing the scheduling logic)
+// - fetch: network callback to actually send a particular download request to a physical remote peer
+// - cancel: task callback to abort an in-flight download request and allow rescheduling it (in case of lost peer)
+// - capacity: network callback to retrieve the estimated type-specific bandwidth capacity of a peer (traffic shaping)
+// - idle: network callback to retrieve the currently (type specific) idle peers that can be assigned tasks
+// - setIdle: network callback to set a peer back to idle and update its estimated capacity (traffic shaping)
+// - kind: textual label of the type being downloaded to display in log messages
func (d *Downloader) fetchParts(deliveryCh chan dataPack, deliver func(dataPack) (int, error), wakeCh chan bool,
expire func() map[string]int, pending func() int, inFlight func() bool, reserve func(*peerConnection, int) (*fetchRequest, bool, bool),
fetchHook func([]*types.Header), fetch func(*peerConnection, *fetchRequest) error, cancel func(*fetchRequest), capacity func(*peerConnection) int,
diff --git a/les/downloader/downloader_test.go b/les/downloader/downloader_test.go
index c56870ff17..1704d3e743 100644
--- a/les/downloader/downloader_test.go
+++ b/les/downloader/downloader_test.go
@@ -229,7 +229,7 @@ func (dl *downloadTester) CurrentFastBlock() *types.Block {
func (dl *downloadTester) FastSyncCommitHead(hash common.Hash) error {
// For now only check that the state trie is correct
if block := dl.GetBlockByHash(hash); block != nil {
- _, err := trie.NewStateTrie(common.Hash{}, block.Root(), trie.NewDatabase(dl.stateDb))
+ _, err := trie.NewStateTrie(trie.StateTrieID(block.Root()), trie.NewDatabase(dl.stateDb))
return err
}
return fmt.Errorf("non existent block: %x", hash[:4])
diff --git a/les/downloader/queue.go b/les/downloader/queue.go
index b165b6b5c1..6896b09b38 100644
--- a/les/downloader/queue.go
+++ b/les/downloader/queue.go
@@ -115,7 +115,7 @@ type queue struct {
// Headers are "special", they download in batches, supported by a skeleton chain
headerHead common.Hash // Hash of the last queued header to verify order
headerTaskPool map[uint64]*types.Header // Pending header retrieval tasks, mapping starting indexes to skeleton headers
- headerTaskQueue *prque.Prque // Priority queue of the skeleton indexes to fetch the filling headers for
+ headerTaskQueue *prque.Prque[int64, uint64] // Priority queue of the skeleton indexes to fetch the filling headers for
headerPeerMiss map[string]map[uint64]struct{} // Set of per-peer header batches known to be unavailable
headerPendPool map[string]*fetchRequest // Currently pending header retrieval operations
headerResults []*types.Header // Result cache accumulating the completed headers
@@ -124,13 +124,13 @@ type queue struct {
headerContCh chan bool // Channel to notify when header download finishes
// All data retrievals below are based on an already assembles header chain
- blockTaskPool map[common.Hash]*types.Header // Pending block (body) retrieval tasks, mapping hashes to headers
- blockTaskQueue *prque.Prque // Priority queue of the headers to fetch the blocks (bodies) for
- blockPendPool map[string]*fetchRequest // Currently pending block (body) retrieval operations
+ blockTaskPool map[common.Hash]*types.Header // Pending block (body) retrieval tasks, mapping hashes to headers
+ blockTaskQueue *prque.Prque[int64, *types.Header] // Priority queue of the headers to fetch the blocks (bodies) for
+ blockPendPool map[string]*fetchRequest // Currently pending block (body) retrieval operations
- receiptTaskPool map[common.Hash]*types.Header // Pending receipt retrieval tasks, mapping hashes to headers
- receiptTaskQueue *prque.Prque // Priority queue of the headers to fetch the receipts for
- receiptPendPool map[string]*fetchRequest // Currently pending receipt retrieval operations
+ receiptTaskPool map[common.Hash]*types.Header // Pending receipt retrieval tasks, mapping hashes to headers
+ receiptTaskQueue *prque.Prque[int64, *types.Header] // Priority queue of the headers to fetch the receipts for
+ receiptPendPool map[string]*fetchRequest // Currently pending receipt retrieval operations
resultCache *resultStore // Downloaded but not yet delivered fetch results
resultSize common.StorageSize // Approximate size of a block (exponential moving average)
@@ -147,8 +147,8 @@ func newQueue(blockCacheLimit int, thresholdInitialSize int) *queue {
lock := new(sync.RWMutex)
q := &queue{
headerContCh: make(chan bool),
- blockTaskQueue: prque.New(nil),
- receiptTaskQueue: prque.New(nil),
+ blockTaskQueue: prque.New[int64, *types.Header](nil),
+ receiptTaskQueue: prque.New[int64, *types.Header](nil),
active: sync.NewCond(lock),
lock: lock,
}
@@ -262,7 +262,7 @@ func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) {
}
// Schedule all the header retrieval tasks for the skeleton assembly
q.headerTaskPool = make(map[uint64]*types.Header)
- q.headerTaskQueue = prque.New(nil)
+ q.headerTaskQueue = prque.New[int64, uint64](nil)
q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains
q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch)
q.headerProced = 0
@@ -373,7 +373,7 @@ func (q *queue) Results(block bool) []*fetchResult {
size += receipt.Size()
}
for _, tx := range result.Transactions {
- size += tx.Size()
+ size += common.StorageSize(tx.Size())
}
q.resultSize = common.StorageSize(blockCacheSizeWeight)*size +
(1-common.StorageSize(blockCacheSizeWeight))*q.resultSize
@@ -424,12 +424,12 @@ func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest {
for send == 0 && !q.headerTaskQueue.Empty() {
from, _ := q.headerTaskQueue.Pop()
if q.headerPeerMiss[p.id] != nil {
- if _, ok := q.headerPeerMiss[p.id][from.(uint64)]; ok {
- skip = append(skip, from.(uint64))
+ if _, ok := q.headerPeerMiss[p.id][from]; ok {
+ skip = append(skip, from)
continue
}
}
- send = from.(uint64)
+ send = from
}
// Merge all the skipped batches back
for _, from := range skip {
@@ -477,10 +477,11 @@ func (q *queue) ReserveReceipts(p *peerConnection, count int) (*fetchRequest, bo
// to access the queue, so they already need a lock anyway.
//
// Returns:
-// item - the fetchRequest
-// progress - whether any progress was made
-// throttle - if the caller should throttle for a while
-func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque,
+//
+// item - the fetchRequest
+// progress - whether any progress was made
+// throttle - if the caller should throttle for a while
+func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque[int64, *types.Header],
pendPool map[string]*fetchRequest, kind uint) (*fetchRequest, bool, bool) {
// Short circuit if the pool has been depleted, or if the peer's already
// downloading something (sanity check not to corrupt state)
@@ -498,8 +499,8 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
for proc := 0; len(send) < count && !taskQueue.Empty(); proc++ {
// the task queue will pop items in order, so the highest prio block
// is also the lowest block number.
- h, _ := taskQueue.Peek()
- header := h.(*types.Header)
+ header, _ := taskQueue.Peek()
+
// we can ask the resultcache if this header is within the
// "prioritized" segment of blocks. If it is not, we need to throttle
@@ -590,12 +591,12 @@ func (q *queue) CancelReceipts(request *fetchRequest) {
}
// Cancel aborts a fetch request, returning all pending hashes to the task queue.
-func (q *queue) cancel(request *fetchRequest, taskQueue *prque.Prque, pendPool map[string]*fetchRequest) {
+func (q *queue) cancel(request *fetchRequest, taskQueue interface{}, pendPool map[string]*fetchRequest) {
if request.From > 0 {
- taskQueue.Push(request.From, -int64(request.From))
+ taskQueue.(*prque.Prque[int64, uint64]).Push(request.From, -int64(request.From))
}
for _, header := range request.Headers {
- taskQueue.Push(header, -int64(header.Number.Uint64()))
+ taskQueue.(*prque.Prque[int64, *types.Header]).Push(header, -int64(header.Number.Uint64()))
}
delete(pendPool, request.Peer.id)
}
@@ -654,7 +655,7 @@ func (q *queue) ExpireReceipts(timeout time.Duration) map[string]int {
// Note, this method expects the queue lock to be already held. The
// reason the lock is not obtained in here is because the parameters already need
// to access the queue, so they already need a lock anyway.
-func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter metrics.Meter) map[string]int {
+func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue interface{}, timeoutMeter metrics.Meter) map[string]int {
// Iterate over the expired requests and return each to the queue
expiries := make(map[string]int)
for id, request := range pendPool {
@@ -664,10 +665,10 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest,
// Return any non satisfied requests to the pool
if request.From > 0 {
- taskQueue.Push(request.From, -int64(request.From))
+ taskQueue.(*prque.Prque[int64, uint64]).Push(request.From, -int64(request.From))
}
for _, header := range request.Headers {
- taskQueue.Push(header, -int64(header.Number.Uint64()))
+ taskQueue.(*prque.Prque[int64, *types.Header]).Push(header, -int64(header.Number.Uint64()))
}
// Add the peer to the expiry report along the number of failed requests
expiries[id] = len(request.Headers)
@@ -830,7 +831,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int,
// reason this lock is not obtained in here is because the parameters already need
// to access the queue, so they already need a lock anyway.
func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
- taskQueue *prque.Prque, pendPool map[string]*fetchRequest, reqTimer metrics.Timer,
+ taskQueue *prque.Prque[int64, *types.Header], pendPool map[string]*fetchRequest, reqTimer metrics.Timer,
results int, validate func(index int, header *types.Header) error,
reconstruct func(index int, result *fetchResult)) (int, error) {
// Short circuit if the data was never requested
@@ -869,7 +870,7 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
}
for _, header := range request.Headers[:i] {
- if res, stale, err := q.resultCache.GetDeliverySlot(header.Number.Uint64()); err == nil {
+ if res, stale, err := q.resultCache.GetDeliverySlot(header.Number.Uint64()); err == nil && !stale {
reconstruct(accepted, res)
} else {
// else: between here and above, some other peer filled this result,
diff --git a/les/downloader/resultstore.go b/les/downloader/resultstore.go
index 3162cd6d5b..7fcade2946 100644
--- a/les/downloader/resultstore.go
+++ b/les/downloader/resultstore.go
@@ -71,10 +71,11 @@ func (r *resultStore) SetThrottleThreshold(threshold uint64) uint64 {
// wants to reserve headers for fetching.
//
// It returns the following:
-// stale - if true, this item is already passed, and should not be requested again
-// throttled - if true, the store is at capacity, this particular header is not prio now
-// item - the result to store data into
-// err - any error that occurred
+//
+// stale - if true, this item is already passed, and should not be requested again
+// throttled - if true, the store is at capacity, this particular header is not prio now
+// item - the result to store data into
+// err - any error that occurred
func (r *resultStore) AddFetch(header *types.Header, fastSync bool) (stale, throttled bool, item *fetchResult, err error) {
r.lock.Lock()
defer r.lock.Unlock()
@@ -123,7 +124,7 @@ func (r *resultStore) getFetchResult(headerNumber uint64) (item *fetchResult, in
return item, index, stale, throttle, nil
}
-// hasCompletedItems returns true if there are processable items available
+// HasCompletedItems returns true if there are processable items available
// this method is cheaper than countCompleted
func (r *resultStore) HasCompletedItems() bool {
r.lock.RLock()
@@ -141,7 +142,7 @@ func (r *resultStore) HasCompletedItems() bool {
// countCompleted returns the number of items ready for delivery, stopping at
// the first non-complete item.
//
-// The mthod assumes (at least) rlock is held.
+// The method assumes (at least) rlock is held.
func (r *resultStore) countCompleted() int {
// We iterate from the already known complete point, and see
// if any more has completed since last count
diff --git a/les/downloader/statesync.go b/les/downloader/statesync.go
index 22f952155f..4dacade3fa 100644
--- a/les/downloader/statesync.go
+++ b/les/downloader/statesync.go
@@ -22,6 +22,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
@@ -295,10 +296,12 @@ type codeTask struct {
// newStateSync creates a new state trie download scheduler. This method does not
// yet start the sync. The user needs to call run to initiate.
func newStateSync(d *Downloader, root common.Hash) *stateSync {
+ // Hack the node scheme here. It's a dead code is not used
+ // by light client at all. Just aim for passing tests.
return &stateSync{
d: d,
root: root,
- sched: state.NewStateSync(root, d.stateDB, nil),
+ sched: state.NewStateSync(root, d.stateDB, nil, rawdb.HashScheme),
keccak: sha3.NewLegacyKeccak256().(crypto.KeccakState),
trieTasks: make(map[string]*trieTask),
codeTasks: make(map[common.Hash]*codeTask),
diff --git a/les/fetcher.go b/les/fetcher.go
index 6861eebcf5..c7a55b193d 100644
--- a/les/fetcher.go
+++ b/les/fetcher.go
@@ -242,18 +242,20 @@ func (f *lightFetcher) forEachPeer(check func(id enode.ID, p *fetcherPeer) bool)
}
// mainloop is the main event loop of the light fetcher, which is responsible for
-// - announcement maintenance(ulc)
-// If we are running in ultra light client mode, then all announcements from
-// the trusted servers are maintained. If the same announcements from trusted
-// servers reach the threshold, then the relevant header is requested for retrieval.
//
-// - block header retrieval
-// Whenever we receive announce with higher td compared with local chain, the
-// request will be made for header retrieval.
+// - announcement maintenance(ulc)
//
-// - re-sync trigger
-// If the local chain lags too much, then the fetcher will enter "synchronise"
-// mode to retrieve missing headers in batch.
+// If we are running in ultra light client mode, then all announcements from
+// the trusted servers are maintained. If the same announcements from trusted
+// servers reach the threshold, then the relevant header is requested for retrieval.
+//
+// - block header retrieval
+// Whenever we receive announce with higher td compared with local chain, the
+// request will be made for header retrieval.
+//
+// - re-sync trigger
+// If the local chain lags too much, then the fetcher will enter "synchronise"
+// mode to retrieve missing headers in batch.
func (f *lightFetcher) mainloop() {
defer f.wg.Done()
@@ -270,6 +272,7 @@ func (f *lightFetcher) mainloop() {
localHead = f.chain.CurrentHeader()
localTd = f.chain.GetTd(localHead.Hash(), localHead.Number.Uint64())
)
+ defer requestTimer.Stop()
sub := f.chain.SubscribeChainHeadEvent(headCh)
defer sub.Unsubscribe()
diff --git a/les/fetcher/block_fetcher.go b/les/fetcher/block_fetcher.go
index 86b3c552ce..085ecb2d66 100644
--- a/les/fetcher/block_fetcher.go
+++ b/les/fetcher/block_fetcher.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// This is a temporary package whilst working on the eth/66 blocking refactors.
+// Package fetcher is a temporary package whilst working on the eth/66 blocking refactors.
// After that work is done, les needs to be refactored to use the new package,
// or alternatively use a stripped down version of it. Either way, we need to
// keep the changes scoped so duplicating temporarily seems the sanest.
@@ -177,9 +177,9 @@ type BlockFetcher struct {
completing map[common.Hash]*blockAnnounce // Blocks with headers, currently body-completing
// Block cache
- queue *prque.Prque // Queue containing the import operations (block number sorted)
- queues map[string]int // Per peer block counts to prevent memory exhaustion
- queued map[common.Hash]*blockOrHeaderInject // Set of already queued blocks (to dedup imports)
+ queue *prque.Prque[int64, *blockOrHeaderInject] // Queue containing the import operations (block number sorted)
+ queues map[string]int // Per peer block counts to prevent memory exhaustion
+ queued map[common.Hash]*blockOrHeaderInject // Set of already queued blocks (to dedup imports)
// Callbacks
getHeader HeaderRetrievalFn // Retrieves a header from the local chain
@@ -214,7 +214,7 @@ func NewBlockFetcher(light bool, getHeader HeaderRetrievalFn, getBlock blockRetr
fetching: make(map[common.Hash]*blockAnnounce),
fetched: make(map[common.Hash][]*blockAnnounce),
completing: make(map[common.Hash]*blockAnnounce),
- queue: prque.New(nil),
+ queue: prque.New[int64, *blockOrHeaderInject](nil),
queues: make(map[string]int),
queued: make(map[common.Hash]*blockOrHeaderInject),
getHeader: getHeader,
@@ -353,7 +353,7 @@ func (f *BlockFetcher) loop() {
// Import any queued blocks that could potentially fit
height := f.chainHeight()
for !f.queue.Empty() {
- op := f.queue.PopItem().(*blockOrHeaderInject)
+ op := f.queue.PopItem()
hash := op.hash()
if f.queueChangeHook != nil {
f.queueChangeHook(hash, false)
@@ -548,7 +548,7 @@ func (f *BlockFetcher) loop() {
announce.time = task.time
// If the block is empty (header only), short circuit into the final import queue
- if header.TxHash == types.EmptyRootHash && header.UncleHash == types.EmptyUncleHash {
+ if header.TxHash == types.EmptyTxsHash && header.UncleHash == types.EmptyUncleHash {
log.Trace("Block empty, skipping body retrieval", "peer", announce.origin, "number", header.Number, "hash", header.Hash())
block := types.NewBlockWithHeader(header)
diff --git a/les/fetcher_test.go b/les/fetcher_test.go
index 6a17e73757..2f3a80aa5b 100644
--- a/les/fetcher_test.go
+++ b/les/fetcher_test.go
@@ -282,7 +282,7 @@ func testInvalidAnnounces(t *testing.T, protocol int) {
peer.cpeer.sendAnnounce(announce)
<-done // Wait syncing
- // Ensure the bad peer is evicited
+ // Ensure the bad peer is evicted
if c.handler.backend.peers.len() != 0 {
t.Fatalf("Failed to evict invalid peer")
}
diff --git a/les/flowcontrol/manager.go b/les/flowcontrol/manager.go
index 4367974d63..b7cc9bd903 100644
--- a/les/flowcontrol/manager.go
+++ b/les/flowcontrol/manager.go
@@ -75,10 +75,11 @@ type ClientManager struct {
// (totalRecharge / sumRecharge)*FixedPointMultiplier or 0 if sumRecharge==0
rcLastUpdate mclock.AbsTime // last time the recharge integrator was updated
rcLastIntValue int64 // last updated value of the recharge integrator
+ priorityOffset int64 // offset for prque priority values ensures that all priorities stay in the int64 range
// recharge queue is a priority queue with currently recharging client nodes
// as elements. The priority value is rcFullIntValue which allows to quickly
// determine which client will first finish recharge.
- rcQueue *prque.Prque
+ rcQueue *prque.Prque[int64, *ClientNode]
}
// NewClientManager returns a new client manager.
@@ -107,7 +108,7 @@ type ClientManager struct {
func NewClientManager(curve PieceWiseLinear, clock mclock.Clock) *ClientManager {
cm := &ClientManager{
clock: clock,
- rcQueue: prque.NewWrapAround(func(a interface{}, i int) { a.(*ClientNode).queueIndex = i }),
+ rcQueue: prque.New[int64, *ClientNode](func(a *ClientNode, i int) { a.queueIndex = i }),
capLastUpdate: clock.Now(),
stop: make(chan chan struct{}),
}
@@ -153,7 +154,7 @@ func (cm *ClientManager) SetRechargeCurve(curve PieceWiseLinear) {
}
}
-// SetCapacityRaiseThreshold sets a threshold value used for raising capFactor.
+// SetCapacityLimits sets a threshold value used for raising capFactor.
// Either if the difference between total allowed and connected capacity is less
// than this threshold or if their ratio is less than capacityRaiseThresholdRatio
// then capFactor is allowed to slowly raise.
@@ -223,7 +224,7 @@ func (cm *ClientManager) processed(node *ClientNode, maxCost, realCost uint64, n
cm.updateBuffer(node, int64(maxCost-realCost), now)
}
-// updateBuffer recalulates the corrected buffer value, adds the given value to it
+// updateBuffer recalculates the corrected buffer value, adds the given value to it
// and updates the node's actual buffer value if possible
func (cm *ClientManager) updateBuffer(node *ClientNode, add int64, now mclock.AbsTime) {
cm.lock.Lock()
@@ -288,13 +289,13 @@ func (cm *ClientManager) updateRecharge(now mclock.AbsTime) {
}
dt := now - lastUpdate
// fetch the client that finishes first
- rcqNode := cm.rcQueue.PopItem().(*ClientNode) // if sumRecharge > 0 then the queue cannot be empty
+ rcqNode := cm.rcQueue.PopItem() // if sumRecharge > 0 then the queue cannot be empty
// check whether it has already finished
dtNext := mclock.AbsTime(float64(rcqNode.rcFullIntValue-cm.rcLastIntValue) / bonusRatio)
if dt < dtNext {
// not finished yet, put it back, update integrator according
// to current bonusRatio and return
- cm.rcQueue.Push(rcqNode, -rcqNode.rcFullIntValue)
+ cm.addToQueue(rcqNode)
cm.rcLastIntValue += int64(bonusRatio * float64(dt))
return
}
@@ -308,6 +309,20 @@ func (cm *ClientManager) updateRecharge(now mclock.AbsTime) {
}
}
+func (cm *ClientManager) addToQueue(node *ClientNode) {
+ if cm.priorityOffset-node.rcFullIntValue < -0x4000000000000000 {
+ cm.priorityOffset += 0x4000000000000000
+ // recreate priority queue with new offset to avoid overflow; should happen very rarely
+ newRcQueue := prque.New[int64, *ClientNode](func(a *ClientNode, i int) { a.queueIndex = i })
+ for cm.rcQueue.Size() > 0 {
+ n := cm.rcQueue.PopItem()
+ newRcQueue.Push(n, cm.priorityOffset-n.rcFullIntValue)
+ }
+ cm.rcQueue = newRcQueue
+ }
+ cm.rcQueue.Push(node, cm.priorityOffset-node.rcFullIntValue)
+}
+
// updateNodeRc updates a node's corrBufValue and adds an external correction value.
// It also adds or removes the rcQueue entry and updates ServerParams and sumRecharge if necessary.
func (cm *ClientManager) updateNodeRc(node *ClientNode, bvc int64, params *ServerParams, now mclock.AbsTime) {
@@ -344,7 +359,7 @@ func (cm *ClientManager) updateNodeRc(node *ClientNode, bvc int64, params *Serve
}
node.rcLastIntValue = cm.rcLastIntValue
node.rcFullIntValue = cm.rcLastIntValue + (int64(node.params.BufLimit)-node.corrBufValue)*FixedPointMultiplier/int64(node.params.MinRecharge)
- cm.rcQueue.Push(node, -node.rcFullIntValue)
+ cm.addToQueue(node)
}
}
diff --git a/les/flowcontrol/manager_test.go b/les/flowcontrol/manager_test.go
index 564d813f15..3afc31272f 100644
--- a/les/flowcontrol/manager_test.go
+++ b/les/flowcontrol/manager_test.go
@@ -17,6 +17,7 @@
package flowcontrol
import (
+ "math"
"math/rand"
"testing"
"time"
@@ -44,16 +45,17 @@ const (
// maximum permitted rate. The max capacity nodes are changed multiple times during
// a single test.
func TestConstantTotalCapacity(t *testing.T) {
- testConstantTotalCapacity(t, 10, 1, 0)
- testConstantTotalCapacity(t, 10, 1, 1)
- testConstantTotalCapacity(t, 30, 1, 0)
- testConstantTotalCapacity(t, 30, 2, 3)
- testConstantTotalCapacity(t, 100, 1, 0)
- testConstantTotalCapacity(t, 100, 3, 5)
- testConstantTotalCapacity(t, 100, 5, 10)
+ testConstantTotalCapacity(t, 10, 1, 0, false)
+ testConstantTotalCapacity(t, 10, 1, 1, false)
+ testConstantTotalCapacity(t, 30, 1, 0, false)
+ testConstantTotalCapacity(t, 30, 2, 3, false)
+ testConstantTotalCapacity(t, 100, 1, 0, false)
+ testConstantTotalCapacity(t, 100, 3, 5, false)
+ testConstantTotalCapacity(t, 100, 5, 10, false)
+ testConstantTotalCapacity(t, 100, 3, 5, true)
}
-func testConstantTotalCapacity(t *testing.T, nodeCount, maxCapacityNodes, randomSend int) {
+func testConstantTotalCapacity(t *testing.T, nodeCount, maxCapacityNodes, randomSend int, priorityOverflow bool) {
clock := &mclock.Simulated{}
nodes := make([]*testNode, nodeCount)
var totalCapacity uint64
@@ -62,6 +64,10 @@ func testConstantTotalCapacity(t *testing.T, nodeCount, maxCapacityNodes, random
totalCapacity += nodes[i].capacity
}
m := NewClientManager(PieceWiseLinear{{0, totalCapacity}}, clock)
+ if priorityOverflow {
+ // provoke a situation where rcLastUpdate overflow needs to be handled
+ m.rcLastIntValue = math.MaxInt64 - 10000000000
+ }
for _, n := range nodes {
n.bufLimit = n.capacity * 6000
n.node = NewClientNode(m, ServerParams{BufLimit: n.bufLimit, MinRecharge: n.capacity})
diff --git a/les/handler_test.go b/les/handler_test.go
index 56d7d55b5a..b7be29b862 100644
--- a/les/handler_test.go
+++ b/les/handler_test.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/les/downloader"
@@ -405,7 +406,7 @@ func testGetProofs(t *testing.T, protocol int) {
accounts := []common.Address{bankAddr, userAddr1, userAddr2, signerAddr, {}}
for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ {
header := bc.GetHeaderByNumber(i)
- trie, _ := trie.New(common.Hash{}, header.Root, trie.NewDatabase(server.db))
+ trie, _ := trie.New(trie.StateTrieID(header.Root), trie.NewDatabase(server.db))
for _, acc := range accounts {
req := ProofReq{
@@ -456,7 +457,7 @@ func testGetStaleProof(t *testing.T, protocol int) {
var expected []rlp.RawValue
if wantOK {
proofsV2 := light.NewNodeSet()
- t, _ := trie.New(common.Hash{}, header.Root, trie.NewDatabase(server.db))
+ t, _ := trie.New(trie.StateTrieID(header.Root), trie.NewDatabase(server.db))
t.Prove(account, 0, proofsV2)
expected = proofsV2.NodeList()
}
@@ -512,7 +513,7 @@ func testGetCHTProofs(t *testing.T, protocol int) {
AuxData: [][]byte{rlp},
}
root := light.GetChtRoot(server.db, 0, bc.GetHeaderByNumber(config.ChtSize-1).Hash())
- trie, _ := trie.New(common.Hash{}, root, trie.NewDatabase(rawdb.NewTable(server.db, light.ChtTablePrefix)))
+ trie, _ := trie.New(trie.TrieID(root), trie.NewDatabase(rawdb.NewTable(server.db, string(rawdb.ChtTablePrefix))))
trie.Prove(key, 0, &proofsV2.Proofs)
// Assemble the requests for the different protocols
requestsV2 := []HelperTrieReq{{
@@ -577,7 +578,7 @@ func testGetBloombitsProofs(t *testing.T, protocol int) {
var proofs HelperTrieResps
root := light.GetBloomTrieRoot(server.db, 0, bc.GetHeaderByNumber(config.BloomTrieSize-1).Hash())
- trie, _ := trie.New(common.Hash{}, root, trie.NewDatabase(rawdb.NewTable(server.db, light.BloomTrieTablePrefix)))
+ trie, _ := trie.New(trie.TrieID(root), trie.NewDatabase(rawdb.NewTable(server.db, string(rawdb.BloomTrieTablePrefix))))
trie.Prove(key, 0, &proofs.Proofs)
// Send the proof request and verify the response
@@ -624,20 +625,20 @@ func testTransactionStatus(t *testing.T, protocol int) {
// test error status by sending an underpriced transaction
tx0, _ := types.SignTx(types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, nil, nil), signer, bankKey)
- test(tx0, true, light.TxStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced.Error()})
+ test(tx0, true, light.TxStatus{Status: txpool.TxStatusUnknown, Error: txpool.ErrUnderpriced.Error()})
tx1, _ := types.SignTx(types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey)
- test(tx1, false, light.TxStatus{Status: core.TxStatusUnknown}) // query before sending, should be unknown
- test(tx1, true, light.TxStatus{Status: core.TxStatusPending}) // send valid processable tx, should return pending
- test(tx1, true, light.TxStatus{Status: core.TxStatusPending}) // adding it again should not return an error
+ test(tx1, false, light.TxStatus{Status: txpool.TxStatusUnknown}) // query before sending, should be unknown
+ test(tx1, true, light.TxStatus{Status: txpool.TxStatusPending}) // send valid processable tx, should return pending
+ test(tx1, true, light.TxStatus{Status: txpool.TxStatusPending}) // adding it again should not return an error
tx2, _ := types.SignTx(types.NewTransaction(1, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey)
tx3, _ := types.SignTx(types.NewTransaction(2, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey)
// send transactions in the wrong order, tx3 should be queued
- test(tx3, true, light.TxStatus{Status: core.TxStatusQueued})
- test(tx2, true, light.TxStatus{Status: core.TxStatusPending})
+ test(tx3, true, light.TxStatus{Status: txpool.TxStatusQueued})
+ test(tx2, true, light.TxStatus{Status: txpool.TxStatusPending})
// query again, now tx3 should be pending too
- test(tx3, false, light.TxStatus{Status: core.TxStatusPending})
+ test(tx3, false, light.TxStatus{Status: txpool.TxStatusPending})
// generate and add a block with tx1 and tx2 included
gchain, _ := core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), server.db, 1, func(i int, block *core.BlockGen) {
@@ -663,9 +664,9 @@ func testTransactionStatus(t *testing.T, protocol int) {
// check if their status is included now
block1hash := rawdb.ReadCanonicalHash(server.db, 1)
- test(tx1, false, light.TxStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}})
+ test(tx1, false, light.TxStatus{Status: txpool.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}})
- test(tx2, false, light.TxStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}})
+ test(tx2, false, light.TxStatus{Status: txpool.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}})
// create a reorg that rolls them back
gchain, _ = core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), server.db, 2, func(i int, block *core.BlockGen) {})
@@ -687,8 +688,8 @@ func testTransactionStatus(t *testing.T, protocol int) {
msg.Discard()
// check if their status is pending again
- test(tx1, false, light.TxStatus{Status: core.TxStatusPending})
- test(tx2, false, light.TxStatus{Status: core.TxStatusPending})
+ test(tx1, false, light.TxStatus{Status: txpool.TxStatusPending})
+ test(tx2, false, light.TxStatus{Status: txpool.TxStatusPending})
}
func TestStopResumeLES3(t *testing.T) { testStopResume(t, lpv3) }
diff --git a/les/odr.go b/les/odr.go
index 2643a53478..943b05fdfc 100644
--- a/les/odr.go
+++ b/les/odr.go
@@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/light"
)
@@ -119,7 +120,7 @@ func (h peerByTxHistory) Less(i, j int) bool {
func (h peerByTxHistory) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
const (
- maxTxStatusRetry = 3 // The maximum retrys will be made for tx status request.
+ maxTxStatusRetry = 3 // The maximum retries will be made for tx status request.
maxTxStatusCandidates = 5 // The maximum les servers the tx status requests will be sent to.
)
@@ -176,10 +177,10 @@ func (odr *LesOdr) RetrieveTxStatus(ctx context.Context, req *light.TxStatusRequ
// All the response is not verifiable, so always pick the first
// one we get.
for index, status := range req.Status {
- if result[index].Status != core.TxStatusUnknown {
+ if result[index].Status != txpool.TxStatusUnknown {
continue
}
- if status.Status == core.TxStatusUnknown {
+ if status.Status == txpool.TxStatusUnknown {
continue
}
result[index], missing = status, missing-1
diff --git a/les/odr_requests.go b/les/odr_requests.go
index d548fb1ee0..d8b094b727 100644
--- a/les/odr_requests.go
+++ b/les/odr_requests.go
@@ -93,7 +93,7 @@ func (r *BlockRequest) Request(reqID uint64, peer *serverPeer) error {
return peer.requestBodies(reqID, []common.Hash{r.Hash})
}
-// Valid processes an ODR request reply message from the LES network
+// Validate processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of LesOdrRequest)
func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error {
@@ -151,7 +151,7 @@ func (r *ReceiptsRequest) Request(reqID uint64, peer *serverPeer) error {
return peer.requestReceipts(reqID, []common.Hash{r.Hash})
}
-// Valid processes an ODR request reply message from the LES network
+// Validate processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of LesOdrRequest)
func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error {
@@ -213,7 +213,7 @@ func (r *TrieRequest) Request(reqID uint64, peer *serverPeer) error {
return peer.requestProofs(reqID, []ProofReq{req})
}
-// Valid processes an ODR request reply message from the LES network
+// Validate processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of LesOdrRequest)
func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error {
@@ -242,7 +242,7 @@ type CodeReq struct {
AccKey []byte
}
-// ODR request type for node data (used for retrieving contract code), see LesOdrRequest interface
+// CodeRequest is the ODR request type for node data (used for retrieving contract code), see LesOdrRequest interface
type CodeRequest light.CodeRequest
// GetCost returns the cost of the given ODR request according to the serving
@@ -266,7 +266,7 @@ func (r *CodeRequest) Request(reqID uint64, peer *serverPeer) error {
return peer.requestCode(reqID, []CodeReq{req})
}
-// Valid processes an ODR request reply message from the LES network
+// Validate processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of LesOdrRequest)
func (r *CodeRequest) Validate(db ethdb.Database, msg *Msg) error {
@@ -312,7 +312,7 @@ type HelperTrieResps struct { // describes all responses, not just a single one
AuxData [][]byte
}
-// ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
+// ChtRequest is the ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
type ChtRequest light.ChtRequest
// GetCost returns the cost of the given ODR request according to the serving
@@ -343,7 +343,7 @@ func (r *ChtRequest) Request(reqID uint64, peer *serverPeer) error {
return peer.requestHelperTrieProofs(reqID, []HelperTrieReq{req})
}
-// Valid processes an ODR request reply message from the LES network
+// Validate processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of LesOdrRequest)
func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
@@ -400,7 +400,7 @@ type BloomReq struct {
BloomTrieNum, BitIdx, SectionIndex, FromLevel uint64
}
-// ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
+// BloomRequest is the ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
type BloomRequest light.BloomRequest
// GetCost returns the cost of the given ODR request according to the serving
@@ -439,7 +439,7 @@ func (r *BloomRequest) Request(reqID uint64, peer *serverPeer) error {
return peer.requestHelperTrieProofs(reqID, reqs)
}
-// Valid processes an ODR request reply message from the LES network
+// Validate processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of LesOdrRequest)
func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error {
diff --git a/les/odr_test.go b/les/odr_test.go
index 24b8e2ae31..e028d35e63 100644
--- a/les/odr_test.go
+++ b/les/odr_test.go
@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
@@ -129,7 +130,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
data[35] = byte(i)
if bc != nil {
header := bc.GetHeaderByHash(bhash)
- statedb, err := state.New(header.Root, state.NewDatabase(db), nil)
+ statedb, err := state.New(header.Root, bc.StateCache(), nil)
if err == nil {
from := statedb.GetOrNewStateObject(bankAddr)
@@ -294,7 +295,7 @@ func testGetTxStatusFromUnindexedPeers(t *testing.T, protocol int) {
if testHash == (common.Hash{}) {
testHash = tx.Hash()
testStatus = light.TxStatus{
- Status: core.TxStatusIncluded,
+ Status: txpool.TxStatusIncluded,
Lookup: &rawdb.LegacyTxLookupEntry{
BlockHash: block.Hash(),
BlockIndex: block.NumberU64(),
@@ -327,7 +328,7 @@ func testGetTxStatusFromUnindexedPeers(t *testing.T, protocol int) {
if txLookup != txIndexUnlimited && (txLookup == txIndexDisabled || number < min) {
continue // Filter out unindexed transactions
}
- stats[i].Status = core.TxStatusIncluded
+ stats[i].Status = txpool.TxStatusIncluded
stats[i].Lookup = &rawdb.LegacyTxLookupEntry{
BlockHash: blockHashes[hash],
BlockIndex: number,
diff --git a/les/peer_test.go b/les/peer_test.go
index b8a1482a04..021d5cb594 100644
--- a/les/peer_test.go
+++ b/les/peer_test.go
@@ -124,8 +124,8 @@ func TestHandshake(t *testing.T) {
genesis = common.HexToHash("cafebabe")
chain1, chain2 = &fakeChain{}, &fakeChain{}
- forkID1 = forkid.NewID(chain1.Config(), chain1.Genesis().Hash(), chain1.CurrentHeader().Number.Uint64())
- forkID2 = forkid.NewID(chain2.Config(), chain2.Genesis().Hash(), chain2.CurrentHeader().Number.Uint64())
+ forkID1 = forkid.NewID(chain1.Config(), chain1.Genesis().Hash(), chain1.CurrentHeader().Number.Uint64(), chain1.CurrentHeader().Time)
+ forkID2 = forkid.NewID(chain2.Config(), chain2.Genesis().Hash(), chain2.CurrentHeader().Number.Uint64(), chain2.CurrentHeader().Time)
filter1, filter2 = forkid.NewFilter(chain1), forkid.NewFilter(chain2)
)
diff --git a/les/protocol.go b/les/protocol.go
index 06db9024eb..dced7039e4 100644
--- a/les/protocol.go
+++ b/les/protocol.go
@@ -45,7 +45,7 @@ var (
AdvertiseProtocolVersions = []uint{lpv2} // clients are searching for the first advertised protocol in the list
)
-// Number of implemented message corresponding to different protocol versions.
+// ProtocolLengths is the number of implemented message corresponding to different protocol versions.
var ProtocolLengths = map[uint]uint64{lpv2: 22, lpv3: 24, lpv4: 24}
const (
diff --git a/les/request_test.go b/les/request_test.go
index c65405e375..9b52e6bd86 100644
--- a/les/request_test.go
+++ b/les/request_test.go
@@ -104,6 +104,7 @@ func testAccess(t *testing.T, protocol int, fn accessTestFn) {
bhash := rawdb.ReadCanonicalHash(server.db, i)
if req := fn(client.db, bhash, i); req != nil {
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
+
err := client.handler.backend.odr.Retrieve(ctx, req)
cancel()
diff --git a/les/server.go b/les/server.go
index df453b4819..06bbc30fb0 100644
--- a/les/server.go
+++ b/les/server.go
@@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/les/flowcontrol"
@@ -49,7 +50,7 @@ type ethBackend interface {
BloomIndexer() *core.ChainIndexer
ChainDb() ethdb.Database
Synced() bool
- TxPool() *core.TxPool
+ TxPool() *txpool.TxPool
}
type LesServer struct {
diff --git a/les/server_handler.go b/les/server_handler.go
index a199a34a72..2ea496ac2c 100644
--- a/les/server_handler.go
+++ b/les/server_handler.go
@@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/les/flowcontrol"
@@ -62,7 +63,7 @@ type serverHandler struct {
forkFilter forkid.Filter
blockchain *core.BlockChain
chainDb ethdb.Database
- txpool *core.TxPool
+ txpool *txpool.TxPool
server *LesServer
closeCh chan struct{} // Channel used to exit all background routines of handler.
@@ -73,7 +74,7 @@ type serverHandler struct {
addTxsSync bool
}
-func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb ethdb.Database, txpool *core.TxPool, synced func() bool) *serverHandler {
+func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb ethdb.Database, txpool *txpool.TxPool, synced func() bool) *serverHandler {
handler := &serverHandler{
forkFilter: forkid.NewFilter(blockchain),
server: server,
@@ -116,7 +117,7 @@ func (h *serverHandler) handle(p *clientPeer) error {
hash = head.Hash()
number = head.Number.Uint64()
td = h.blockchain.GetTd(hash, number)
- forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis().Hash(), h.blockchain.CurrentBlock().NumberU64())
+ forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis().Hash(), number, head.Time)
)
if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), forkID, h.forkFilter, h.server); err != nil {
p.Log().Debug("Light Ethereum handshake failed", "err", err)
@@ -343,7 +344,7 @@ func (h *serverHandler) BlockChain() *core.BlockChain {
}
// TxPool implements serverBackend
-func (h *serverHandler) TxPool() *core.TxPool {
+func (h *serverHandler) TxPool() *txpool.TxPool {
return h.txpool
}
@@ -359,7 +360,7 @@ func (h *serverHandler) AddTxsSync() bool {
// getAccount retrieves an account from the state based on root.
func getAccount(triedb *trie.Database, root, hash common.Hash) (types.StateAccount, error) {
- trie, err := trie.New(common.Hash{}, root, triedb)
+ trie, err := trie.New(trie.StateTrieID(root), triedb)
if err != nil {
return types.StateAccount{}, err
}
@@ -383,15 +384,15 @@ func (h *serverHandler) GetHelperTrie(typ uint, index uint64) *trie.Trie {
switch typ {
case htCanonical:
sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.ChtSize-1)
- root, prefix = light.GetChtRoot(h.chainDb, index, sectionHead), light.ChtTablePrefix
+ root, prefix = light.GetChtRoot(h.chainDb, index, sectionHead), string(rawdb.ChtTablePrefix)
case htBloomBits:
sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.BloomTrieSize-1)
- root, prefix = light.GetBloomTrieRoot(h.chainDb, index, sectionHead), light.BloomTrieTablePrefix
+ root, prefix = light.GetBloomTrieRoot(h.chainDb, index, sectionHead), string(rawdb.BloomTrieTablePrefix)
}
if root == (common.Hash{}) {
return nil
}
- trie, _ := trie.New(common.Hash{}, root, trie.NewDatabase(rawdb.NewTable(h.chainDb, prefix)))
+ trie, _ := trie.New(trie.TrieID(root), trie.NewDatabase(rawdb.NewTable(h.chainDb, prefix)))
return trie
}
diff --git a/les/server_requests.go b/les/server_requests.go
index bab5f733d5..033b11d793 100644
--- a/les/server_requests.go
+++ b/les/server_requests.go
@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/log"
@@ -36,7 +37,7 @@ type serverBackend interface {
ArchiveMode() bool
AddTxsSync() bool
BlockChain() *core.BlockChain
- TxPool() *core.TxPool
+ TxPool() *txpool.TxPool
GetHelperTrie(typ uint, index uint64) *trie.Trie
}
@@ -347,7 +348,7 @@ func handleGetReceipts(msg Decoder) (serveRequestFn, uint64, uint64, error) {
// Retrieve the requested block's receipts, skipping if unknown to us
results := bc.GetReceiptsByHash(hash)
if results == nil {
- if header := bc.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
+ if header := bc.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyReceiptsHash {
p.bumpInvalid()
continue
}
@@ -428,13 +429,13 @@ func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
p.bumpInvalid()
continue
}
- trie, err = statedb.OpenStorageTrie(common.BytesToHash(request.AccKey), account.Root)
+ trie, err = statedb.OpenStorageTrie(root, common.BytesToHash(request.AccKey), account.Root)
if trie == nil || err != nil {
p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "root", account.Root, "err", err)
continue
}
}
- // Prove the user's request from the account or stroage trie
+ // Prove the user's request from the account or storage trie
if err := trie.Prove(request.Key, request.FromLevel, nodes); err != nil {
p.Log().Warn("Failed to prove state request", "block", header.Number, "hash", header.Hash(), "err", err)
continue
@@ -516,7 +517,7 @@ func handleSendTx(msg Decoder) (serveRequestFn, uint64, uint64, error) {
}
hash := tx.Hash()
stats[i] = txStatus(backend, hash)
- if stats[i].Status == core.TxStatusUnknown {
+ if stats[i].Status == txpool.TxStatusUnknown {
addFn := backend.TxPool().AddRemotes
// Add txs synchronously for testing purpose
if backend.AddTxsSync() {
@@ -558,10 +559,10 @@ func txStatus(b serverBackend, hash common.Hash) light.TxStatus {
stat.Status = b.TxPool().Status([]common.Hash{hash})[0]
// If the transaction is unknown to the pool, try looking it up locally.
- if stat.Status == core.TxStatusUnknown {
+ if stat.Status == txpool.TxStatusUnknown {
lookup := b.BlockChain().GetTransactionLookup(hash)
if lookup != nil {
- stat.Status = core.TxStatusIncluded
+ stat.Status = txpool.TxStatusIncluded
stat.Lookup = lookup
}
}
diff --git a/les/servingqueue.go b/les/servingqueue.go
index 10c7e6f48c..b4b53d8df5 100644
--- a/les/servingqueue.go
+++ b/les/servingqueue.go
@@ -38,10 +38,10 @@ type servingQueue struct {
setThreadsCh chan int
wg sync.WaitGroup
- threadCount int // number of currently running threads
- queue *prque.Prque // priority queue for waiting or suspended tasks
- best *servingTask // the highest priority task (not included in the queue)
- suspendBias int64 // priority bias against suspending an already running task
+ threadCount int // number of currently running threads
+ queue *prque.Prque[int64, *servingTask] // priority queue for waiting or suspended tasks
+ best *servingTask // the highest priority task (not included in the queue)
+ suspendBias int64 // priority bias against suspending an already running task
}
// servingTask represents a request serving task. Tasks can be implemented to
@@ -123,7 +123,7 @@ func (t *servingTask) waitOrStop() bool {
// newServingQueue returns a new servingQueue
func newServingQueue(suspendBias int64, utilTarget float64) *servingQueue {
sq := &servingQueue{
- queue: prque.NewWrapAround(nil),
+ queue: prque.New[int64, *servingTask](nil),
suspendBias: suspendBias,
queueAddCh: make(chan *servingTask, 100),
queueBestCh: make(chan *servingTask),
@@ -214,7 +214,7 @@ func (sq *servingQueue) freezePeers() {
}
sq.best = nil
for sq.queue.Size() > 0 {
- task := sq.queue.PopItem().(*servingTask)
+ task := sq.queue.PopItem()
tasks := peerMap[task.peer]
if tasks == nil {
bufValue, bufLimit := task.peer.fcClient.BufferStatus()
@@ -251,7 +251,7 @@ func (sq *servingQueue) freezePeers() {
}
}
if sq.queue.Size() > 0 {
- sq.best = sq.queue.PopItem().(*servingTask)
+ sq.best = sq.queue.PopItem()
}
}
@@ -310,7 +310,7 @@ func (sq *servingQueue) queueLoop() {
if sq.queue.Size() == 0 {
sq.best = nil
} else {
- sq.best, _ = sq.queue.PopItem().(*servingTask)
+ sq.best = sq.queue.PopItem()
}
case <-sq.quit:
return
diff --git a/les/state_accessor.go b/les/state_accessor.go
index 112e6fd44d..091ec8871e 100644
--- a/les/state_accessor.go
+++ b/les/state_accessor.go
@@ -25,31 +25,36 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/light"
)
+// noopReleaser is returned in case there is no operation expected
+// for releasing state.
+var noopReleaser = tracers.StateReleaseFunc(func() {})
+
// stateAtBlock retrieves the state database associated with a certain block.
-func (leth *LightEthereum) stateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, error) {
- return light.NewState(ctx, block.Header(), leth.odr), nil
+func (leth *LightEthereum) stateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, tracers.StateReleaseFunc, error) {
+ return light.NewState(ctx, block.Header(), leth.odr), noopReleaser, nil
}
// stateAtTransaction returns the execution environment of a certain transaction.
-func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
+func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
// Short circuit if it's genesis block.
if block.NumberU64() == 0 {
- return nil, vm.BlockContext{}, nil, errors.New("no transaction in genesis")
+ return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
}
// Create the parent state database
parent, err := leth.blockchain.GetBlock(ctx, block.ParentHash(), block.NumberU64()-1)
if err != nil {
- return nil, vm.BlockContext{}, nil, err
+ return nil, vm.BlockContext{}, nil, nil, err
}
- statedb, err := leth.stateAtBlock(ctx, parent, reexec)
+ statedb, release, err := leth.stateAtBlock(ctx, parent, reexec)
if err != nil {
- return nil, vm.BlockContext{}, nil, err
+ return nil, vm.BlockContext{}, nil, nil, err
}
if txIndex == 0 && len(block.Transactions()) == 0 {
- return nil, vm.BlockContext{}, statedb, nil
+ return nil, vm.BlockContext{}, statedb, release, nil
}
// Recompute transactions up to the target index.
signer := types.MakeSigner(leth.blockchain.Config(), block.Number())
@@ -58,18 +63,18 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.
msg, _ := tx.AsMessage(signer, block.BaseFee())
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil)
- statedb.Prepare(tx.Hash(), idx)
+ statedb.SetTxContext(tx.Hash(), idx)
if idx == txIndex {
- return msg, context, statedb, nil
+ return msg, context, statedb, release, nil
}
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
- return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
+ return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
}
- return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
+ return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
}
diff --git a/les/test_helper.go b/les/test_helper.go
index 8335e2c39a..714bc7b3f6 100644
--- a/les/test_helper.go
+++ b/les/test_helper.go
@@ -39,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/ethconfig"
@@ -269,9 +270,9 @@ func newTestServerHandler(blocks int, indexers []*core.ChainIndexer, db ethdb.Da
simulation := backends.NewSimulatedBackendWithDatabase(db, gspec.Alloc, 100000000)
prepare(blocks, simulation)
- txpoolConfig := core.DefaultTxPoolConfig
+ txpoolConfig := txpool.DefaultConfig
txpoolConfig.Journal = ""
- txpool := core.NewTxPool(txpoolConfig, gspec.Config, simulation.Blockchain())
+ txpool := txpool.NewTxPool(txpoolConfig, gspec.Config, simulation.Blockchain())
if indexers != nil {
checkpointConfig := ¶ms.CheckpointOracleConfig{
Address: crypto.CreateAddress(bankAddr, 0),
@@ -488,7 +489,7 @@ func (client *testClient) newRawPeer(t *testing.T, name string, version int, rec
head = client.handler.backend.blockchain.CurrentHeader()
td = client.handler.backend.blockchain.GetTd(head.Hash(), head.Number.Uint64())
)
- forkID := forkid.NewID(client.handler.backend.blockchain.Config(), genesis.Hash(), head.Number.Uint64())
+ forkID := forkid.NewID(client.handler.backend.blockchain.Config(), genesis.Hash(), head.Number.Uint64(), head.Time)
tp.handshakeWithClient(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID, testCostList(0), recentTxLookup) // disable flow control by default
// Ensure the connection is established or exits when any error occurs
@@ -552,7 +553,7 @@ func (server *testServer) newRawPeer(t *testing.T, name string, version int) (*t
head = server.handler.blockchain.CurrentHeader()
td = server.handler.blockchain.GetTd(head.Hash(), head.Number.Uint64())
)
- forkID := forkid.NewID(server.handler.blockchain.Config(), genesis.Hash(), head.Number.Uint64())
+ forkID := forkid.NewID(server.handler.blockchain.Config(), genesis.Hash(), head.Number.Uint64(), head.Time)
tp.handshakeWithServer(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID)
// Ensure the connection is established or exits when any error occurs
diff --git a/les/utils/expiredvalue.go b/les/utils/expiredvalue.go
index 3fd52616fa..099b61d053 100644
--- a/les/utils/expiredvalue.go
+++ b/les/utils/expiredvalue.go
@@ -67,13 +67,13 @@ func (e ExpirationFactor) Value(base float64, exp uint64) float64 {
return base / e.Factor * math.Pow(2, float64(int64(exp-e.Exp)))
}
-// value calculates the value at the given moment.
+// Value calculates the value at the given moment.
func (e ExpiredValue) Value(logOffset Fixed64) uint64 {
offset := Uint64ToFixed64(e.Exp) - logOffset
return uint64(float64(e.Base) * offset.Pow2())
}
-// add adds a signed value at the given moment
+// Add adds a signed value at the given moment
func (e *ExpiredValue) Add(amount int64, logOffset Fixed64) int64 {
integer, frac := logOffset.ToUint64(), logOffset.Fraction()
factor := frac.Pow2()
@@ -102,7 +102,7 @@ func (e *ExpiredValue) Add(amount int64, logOffset Fixed64) int64 {
return net
}
-// addExp adds another ExpiredValue
+// AddExp adds another ExpiredValue
func (e *ExpiredValue) AddExp(a ExpiredValue) {
if e.Exp > a.Exp {
a.Base >>= (e.Exp - a.Exp)
@@ -114,7 +114,7 @@ func (e *ExpiredValue) AddExp(a ExpiredValue) {
e.Base += a.Base
}
-// subExp subtracts another ExpiredValue
+// SubExp subtracts another ExpiredValue
func (e *ExpiredValue) SubExp(a ExpiredValue) {
if e.Exp > a.Exp {
a.Base >>= (e.Exp - a.Exp)
@@ -143,7 +143,7 @@ type LinearExpiredValue struct {
Rate mclock.AbsTime `rlp:"-"` // Expiration rate(by nanosecond), will ignored by RLP
}
-// value calculates the value at the given moment. This function always has the
+// Value calculates the value at the given moment. This function always has the
// assumption that the given timestamp shouldn't less than the recorded one.
func (e LinearExpiredValue) Value(now mclock.AbsTime) uint64 {
offset := uint64(now / e.Rate)
@@ -158,7 +158,7 @@ func (e LinearExpiredValue) Value(now mclock.AbsTime) uint64 {
return e.Val
}
-// add adds a signed value at the given moment. This function always has the
+// Add adds a signed value at the given moment. This function always has the
// assumption that the given timestamp shouldn't less than the recorded one.
func (e *LinearExpiredValue) Add(amount int64, now mclock.AbsTime) uint64 {
offset := uint64(now / e.Rate)
@@ -244,17 +244,17 @@ func Uint64ToFixed64(f uint64) Fixed64 {
return Fixed64(f * fixedFactor)
}
-// float64ToFixed64 converts float64 to Fixed64 format.
+// Float64ToFixed64 converts float64 to Fixed64 format.
func Float64ToFixed64(f float64) Fixed64 {
return Fixed64(f * fixedFactor)
}
-// toUint64 converts Fixed64 format to uint64.
+// ToUint64 converts Fixed64 format to uint64.
func (f64 Fixed64) ToUint64() uint64 {
return uint64(f64) / fixedFactor
}
-// fraction returns the fractional part of a Fixed64 value.
+// Fraction returns the fractional part of a Fixed64 value.
func (f64 Fixed64) Fraction() Fixed64 {
return f64 % fixedFactor
}
@@ -264,7 +264,7 @@ var (
fixedToLogFactor = math.Log(2) / float64(fixedFactor)
)
-// pow2Fixed returns the base 2 power of the fixed point value.
+// Pow2 returns the base 2 power of the fixed point value.
func (f64 Fixed64) Pow2() float64 {
return math.Exp(float64(f64) * fixedToLogFactor)
}
diff --git a/les/utils/limiter_test.go b/les/utils/limiter_test.go
index 3fbdc60d7c..c031b21de5 100644
--- a/les/utils/limiter_test.go
+++ b/les/utils/limiter_test.go
@@ -17,7 +17,7 @@
package utils
import (
- "math/rand"
+ "crypto/rand"
"testing"
"github.com/ethereum/go-ethereum/p2p/enode"
diff --git a/les/vflux/client/fillset_test.go b/les/vflux/client/fillset_test.go
index ddb12a82f9..652dcf9f62 100644
--- a/les/vflux/client/fillset_test.go
+++ b/les/vflux/client/fillset_test.go
@@ -17,7 +17,7 @@
package client
import (
- "math/rand"
+ "crypto/rand"
"testing"
"time"
diff --git a/les/vflux/client/serverpool.go b/les/vflux/client/serverpool.go
index cf96f0ee3a..271d6e0224 100644
--- a/les/vflux/client/serverpool.go
+++ b/les/vflux/client/serverpool.go
@@ -89,7 +89,7 @@ type nodeHistoryEnc struct {
RedialWaitStart, RedialWaitEnd uint64
}
-// queryFunc sends a pre-negotiation query and blocks until a response arrives or timeout occurs.
+// QueryFunc sends a pre-negotiation query and blocks until a response arrives or timeout occurs.
// It returns 1 if the remote node has confirmed that connection is possible, 0 if not
// possible and -1 if no response arrived (timeout).
type QueryFunc func(*enode.Node) int
@@ -302,7 +302,7 @@ func (s *ServerPool) addPreNegFilter(input enode.Iterator, query QueryFunc) enod
})
}
-// start starts the server pool. Note that NodeStateMachine should be started first.
+// Start starts the server pool. Note that NodeStateMachine should be started first.
func (s *ServerPool) Start() {
s.ns.Start()
for _, iter := range s.mixSources {
@@ -336,7 +336,7 @@ func (s *ServerPool) Start() {
atomic.StoreUint32(&s.started, 1)
}
-// stop stops the server pool
+// Stop stops the server pool
func (s *ServerPool) Stop() {
if s.fillSet != nil {
s.fillSet.Close()
diff --git a/les/vflux/client/valuetracker.go b/les/vflux/client/valuetracker.go
index dcd2fcdfd9..806d0c7d75 100644
--- a/les/vflux/client/valuetracker.go
+++ b/les/vflux/client/valuetracker.go
@@ -233,7 +233,7 @@ func (vt *ValueTracker) StatsExpirer() *utils.Expirer {
return &vt.statsExpirer
}
-// StatsExpirer returns the current expiration factor so that other values can be expired
+// StatsExpFactor returns the current expiration factor so that other values can be expired
// with the same rate as the service value statistics.
func (vt *ValueTracker) StatsExpFactor() utils.ExpirationFactor {
vt.statsExpLock.RLock()
diff --git a/les/vflux/requests.go b/les/vflux/requests.go
index 7d4bafc188..5abae2f537 100644
--- a/les/vflux/requests.go
+++ b/les/vflux/requests.go
@@ -50,7 +50,7 @@ type (
Bias uint64 // seconds
AddTokens []IntOrInf
}
- // CapacityQueryReq is the encoding format of the response to the capacity query
+ // CapacityQueryReply is the encoding format of the response to the capacity query
CapacityQueryReply []uint64
)
diff --git a/les/vflux/server/clientdb.go b/les/vflux/server/clientdb.go
index 30cd9a6528..a39cbec36a 100644
--- a/les/vflux/server/clientdb.go
+++ b/les/vflux/server/clientdb.go
@@ -22,13 +22,13 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/les/utils"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/rlp"
- lru "github.com/hashicorp/golang-lru"
)
const (
@@ -57,7 +57,7 @@ var (
type nodeDB struct {
db ethdb.KeyValueStore
- cache *lru.Cache
+ cache *lru.Cache[string, utils.ExpiredValue]
auxbuf []byte // 37-byte auxiliary buffer for key encoding
verbuf [2]byte // 2-byte auxiliary buffer for db version
evictCallBack func(mclock.AbsTime, bool, utils.ExpiredValue) bool // Callback to determine whether the balance can be evicted.
@@ -67,10 +67,9 @@ type nodeDB struct {
}
func newNodeDB(db ethdb.KeyValueStore, clock mclock.Clock) *nodeDB {
- cache, _ := lru.New(balanceCacheLimit)
ndb := &nodeDB{
db: db,
- cache: cache,
+ cache: lru.NewCache[string, utils.ExpiredValue](balanceCacheLimit),
auxbuf: make([]byte, 37),
clock: clock,
closeCh: make(chan struct{}),
@@ -125,8 +124,9 @@ func (db *nodeDB) getOrNewBalance(id []byte, neg bool) utils.ExpiredValue {
key := db.key(id, neg)
item, exist := db.cache.Get(string(key))
if exist {
- return item.(utils.ExpiredValue)
+ return item
}
+
var b utils.ExpiredValue
enc, err := db.db.Get(key)
if err != nil || len(enc) == 0 {
diff --git a/les/vflux/server/clientpool.go b/les/vflux/server/clientpool.go
index 734d74f453..a525f86368 100644
--- a/les/vflux/server/clientpool.go
+++ b/les/vflux/server/clientpool.go
@@ -53,7 +53,7 @@ var (
// each client can have several minutes of connection time.
//
// Balances of disconnected clients are stored in nodeDB including positive balance
-// and negative banalce. Boeth positive balance and negative balance will decrease
+// and negative balance. Both positive balance and negative balance will decrease
// exponentially. If the balance is low enough, then the record will be dropped.
type ClientPool struct {
*priorityPool
diff --git a/les/vflux/server/clientpool_test.go b/les/vflux/server/clientpool_test.go
index 790ec51360..f75c70afca 100644
--- a/les/vflux/server/clientpool_test.go
+++ b/les/vflux/server/clientpool_test.go
@@ -135,7 +135,6 @@ func alwaysTrueFn() bool {
}
func testClientPool(t *testing.T, activeLimit, clientCount, paidCount int, randomDisconnect bool) {
- rand.Seed(time.Now().UnixNano())
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
diff --git a/les/vflux/server/prioritypool.go b/les/vflux/server/prioritypool.go
index 059dac0d46..766026a808 100644
--- a/les/vflux/server/prioritypool.go
+++ b/les/vflux/server/prioritypool.go
@@ -77,8 +77,8 @@ type priorityPool struct {
// temporary state if tempState is not empty
tempState []*ppNodeInfo
activeCount, activeCap uint64
- activeQueue *prque.LazyQueue
- inactiveQueue *prque.Prque
+ activeQueue *prque.LazyQueue[int64, *ppNodeInfo]
+ inactiveQueue *prque.Prque[int64, *ppNodeInfo]
}
// ppNodeInfo is the internal node descriptor of priorityPool
@@ -104,7 +104,7 @@ func newPriorityPool(ns *nodestate.NodeStateMachine, setup *serverSetup, clock m
setup: setup,
ns: ns,
clock: clock,
- inactiveQueue: prque.New(inactiveSetIndex),
+ inactiveQueue: prque.New[int64, *ppNodeInfo](inactiveSetIndex),
minCap: minCap,
activeBias: activeBias,
capacityStepDiv: capacityStepDiv,
@@ -183,8 +183,7 @@ func (pp *priorityPool) requestCapacity(node *enode.Node, minTarget, maxTarget u
}
pp.setTempCapacity(c, maxTarget)
c.minTarget = minTarget
- pp.activeQueue.Remove(c.activeIndex)
- pp.inactiveQueue.Remove(c.inactiveIndex)
+ pp.removeFromQueues(c)
pp.activeQueue.Push(c)
pp.enforceLimits()
updates := pp.finalizeChanges(c.tempCapacity >= minTarget && c.tempCapacity <= maxTarget && c.tempCapacity != c.capacity)
@@ -250,13 +249,13 @@ func (pp *priorityPool) Limits() (uint64, uint64) {
}
// inactiveSetIndex callback updates ppNodeInfo item index in inactiveQueue
-func inactiveSetIndex(a interface{}, index int) {
- a.(*ppNodeInfo).inactiveIndex = index
+func inactiveSetIndex(a *ppNodeInfo, index int) {
+ a.inactiveIndex = index
}
// activeSetIndex callback updates ppNodeInfo item index in activeQueue
-func activeSetIndex(a interface{}, index int) {
- a.(*ppNodeInfo).activeIndex = index
+func activeSetIndex(a *ppNodeInfo, index int) {
+ a.activeIndex = index
}
// invertPriority inverts a priority value. The active queue uses inverted priorities
@@ -269,8 +268,7 @@ func invertPriority(p int64) int64 {
}
// activePriority callback returns actual priority of ppNodeInfo item in activeQueue
-func activePriority(a interface{}) int64 {
- c := a.(*ppNodeInfo)
+func activePriority(c *ppNodeInfo) int64 {
if c.bias == 0 {
return invertPriority(c.nodePriority.priority(c.tempCapacity))
} else {
@@ -279,8 +277,7 @@ func activePriority(a interface{}) int64 {
}
// activeMaxPriority callback returns estimated maximum priority of ppNodeInfo item in activeQueue
-func (pp *priorityPool) activeMaxPriority(a interface{}, until mclock.AbsTime) int64 {
- c := a.(*ppNodeInfo)
+func (pp *priorityPool) activeMaxPriority(c *ppNodeInfo, until mclock.AbsTime) int64 {
future := time.Duration(until - pp.clock.Now())
if future < 0 {
future = 0
@@ -293,6 +290,16 @@ func (pp *priorityPool) inactivePriority(p *ppNodeInfo) int64 {
return p.nodePriority.priority(pp.minCap)
}
+// removeFromQueues removes the node from the active/inactive queues
+func (pp *priorityPool) removeFromQueues(c *ppNodeInfo) {
+ if c.activeIndex >= 0 {
+ pp.activeQueue.Remove(c.activeIndex)
+ }
+ if c.inactiveIndex >= 0 {
+ pp.inactiveQueue.Remove(c.inactiveIndex)
+ }
+}
+
// connectNode is called when a new node has been added to the pool (inactiveFlag set)
// Note: this function should run inside a NodeStateMachine operation
func (pp *priorityPool) connectNode(c *ppNodeInfo) {
@@ -320,8 +327,7 @@ func (pp *priorityPool) disconnectNode(c *ppNodeInfo) {
return
}
c.connected = false
- pp.activeQueue.Remove(c.activeIndex)
- pp.inactiveQueue.Remove(c.inactiveIndex)
+ pp.removeFromQueues(c)
var updates []capUpdate
if c.capacity != 0 {
@@ -411,11 +417,11 @@ func (pp *priorityPool) enforceLimits() (*ppNodeInfo, int64) {
return nil, math.MinInt64
}
var (
- c *ppNodeInfo
+ lastNode *ppNodeInfo
maxActivePriority int64
)
- pp.activeQueue.MultiPop(func(data interface{}, priority int64) bool {
- c = data.(*ppNodeInfo)
+ pp.activeQueue.MultiPop(func(c *ppNodeInfo, priority int64) bool {
+ lastNode = c
pp.setTempState(c)
maxActivePriority = priority
if c.tempCapacity == c.minTarget || pp.activeCount > pp.maxCount {
@@ -433,7 +439,7 @@ func (pp *priorityPool) enforceLimits() (*ppNodeInfo, int64) {
}
return pp.activeCap > pp.maxCap || pp.activeCount > pp.maxCount
})
- return c, invertPriority(maxActivePriority)
+ return lastNode, invertPriority(maxActivePriority)
}
// finalizeChanges either commits or reverts temporary changes. The necessary capacity
@@ -442,8 +448,7 @@ func (pp *priorityPool) enforceLimits() (*ppNodeInfo, int64) {
func (pp *priorityPool) finalizeChanges(commit bool) (updates []capUpdate) {
for _, c := range pp.tempState {
// always remove and push back in order to update biased priority
- pp.activeQueue.Remove(c.activeIndex)
- pp.inactiveQueue.Remove(c.inactiveIndex)
+ pp.removeFromQueues(c)
oldCapacity := c.capacity
if commit {
c.capacity = c.tempCapacity
@@ -496,7 +501,7 @@ func (pp *priorityPool) updateFlags(updates []capUpdate) {
// tryActivate tries to activate inactive nodes if possible
func (pp *priorityPool) tryActivate(commit bool) []capUpdate {
for pp.inactiveQueue.Size() > 0 {
- c := pp.inactiveQueue.PopItem().(*ppNodeInfo)
+ c := pp.inactiveQueue.PopItem()
pp.setTempState(c)
pp.setTempBias(c, pp.activeBias)
pp.setTempCapacity(c, pp.minCap)
@@ -524,8 +529,7 @@ func (pp *priorityPool) updatePriority(node *enode.Node) {
pp.lock.Unlock()
return
}
- pp.activeQueue.Remove(c.activeIndex)
- pp.inactiveQueue.Remove(c.inactiveIndex)
+ pp.removeFromQueues(c)
if c.capacity != 0 {
pp.activeQueue.Push(c)
} else {
diff --git a/light/lightchain.go b/light/lightchain.go
index dca97ce45c..f42c904f57 100644
--- a/light/lightchain.go
+++ b/light/lightchain.go
@@ -27,6 +27,7 @@ import (
"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"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -37,7 +38,6 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
- lru "github.com/hashicorp/golang-lru"
)
var (
@@ -61,9 +61,9 @@ type LightChain struct {
genesisBlock *types.Block
forker *core.ForkChoice
- bodyCache *lru.Cache // Cache for the most recent block bodies
- bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
- blockCache *lru.Cache // Cache for the most recent entire blocks
+ bodyCache *lru.Cache[common.Hash, *types.Body]
+ bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
+ blockCache *lru.Cache[common.Hash, *types.Block]
chainmu sync.RWMutex // protects header inserts
quit chan struct{}
@@ -79,18 +79,14 @@ type LightChain struct {
// available in the database. It initialises the default Ethereum header
// validator.
func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine, checkpoint *params.TrustedCheckpoint) (*LightChain, error) {
- bodyCache, _ := lru.New(bodyCacheLimit)
- bodyRLPCache, _ := lru.New(bodyCacheLimit)
- blockCache, _ := lru.New(blockCacheLimit)
-
bc := &LightChain{
chainDb: odr.Database(),
indexerConfig: odr.IndexerConfig(),
odr: odr,
quit: make(chan struct{}),
- bodyCache: bodyCache,
- bodyRLPCache: bodyRLPCache,
- blockCache: blockCache,
+ bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
+ bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
+ blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
engine: engine,
}
bc.forker = core.NewForkChoice(bc, nil)
@@ -182,6 +178,17 @@ func (lc *LightChain) SetHead(head uint64) error {
return lc.loadLastState()
}
+// SetHeadWithTimestamp rewinds the local chain to a new head that has at max
+// the given timestamp. Everything above the new head will be deleted and the
+// new one set.
+func (lc *LightChain) SetHeadWithTimestamp(timestamp uint64) error {
+ lc.chainmu.Lock()
+ defer lc.chainmu.Unlock()
+
+ lc.hc.SetHeadWithTimestamp(timestamp, nil, nil)
+ return lc.loadLastState()
+}
+
// GasLimit returns the gas limit of the current HEAD block.
func (lc *LightChain) GasLimit() uint64 {
return lc.hc.CurrentHeader().GasLimit
@@ -233,8 +240,7 @@ func (lc *LightChain) StateCache() state.Database {
func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) {
// Short circuit if the body's already in the cache, retrieve otherwise
if cached, ok := lc.bodyCache.Get(hash); ok {
- body := cached.(*types.Body)
- return body, nil
+ return cached, nil
}
number := lc.hc.GetBlockNumber(hash)
if number == nil {
@@ -254,7 +260,7 @@ func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Bod
func (lc *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) {
// Short circuit if the body's already in the cache, retrieve otherwise
if cached, ok := lc.bodyRLPCache.Get(hash); ok {
- return cached.(rlp.RawValue), nil
+ return cached, nil
}
number := lc.hc.GetBlockNumber(hash)
if number == nil {
@@ -281,7 +287,7 @@ func (lc *LightChain) HasBlock(hash common.Hash, number uint64) bool {
func (lc *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) {
// Short circuit if the block's already in the cache, retrieve otherwise
if block, ok := lc.blockCache.Get(hash); ok {
- return block.(*types.Block), nil
+ return block, nil
}
block, err := GetBlock(ctx, lc.odr, hash, number)
if err != nil {
@@ -453,7 +459,7 @@ func (lc *LightChain) GetTd(hash common.Hash, number uint64) *big.Int {
return lc.hc.GetTd(hash, number)
}
-// GetHeaderByNumberOdr retrieves the total difficult from the database or
+// GetTdOdr retrieves the total difficult from the database or
// network by hash and number, caching it (associated with its hash) if found.
func (lc *LightChain) GetTdOdr(ctx context.Context, hash common.Hash, number uint64) *big.Int {
td := lc.GetTd(hash, number)
diff --git a/light/lightchain_test.go b/light/lightchain_test.go
index 8600e56345..d19713dc2f 100644
--- a/light/lightchain_test.go
+++ b/light/lightchain_test.go
@@ -253,8 +253,8 @@ func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.
Number: big.NewInt(int64(i + 1)),
Difficulty: big.NewInt(int64(difficulty)),
UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyRootHash,
- ReceiptHash: types.EmptyRootHash,
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
}
if i == 0 {
header.ParentHash = genesis.Hash()
diff --git a/light/odr.go b/light/odr.go
index 9521dd53e8..f998dbe584 100644
--- a/light/odr.go
+++ b/light/odr.go
@@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
)
@@ -53,9 +54,11 @@ type OdrRequest interface {
// TrieID identifies a state or account storage trie
type TrieID struct {
- BlockHash, Root common.Hash
- BlockNumber uint64
- AccKey []byte
+ BlockHash common.Hash
+ BlockNumber uint64
+ StateRoot common.Hash
+ Root common.Hash
+ AccKey []byte
}
// StateTrieID returns a TrieID for a state trie belonging to a certain block
@@ -64,8 +67,9 @@ func StateTrieID(header *types.Header) *TrieID {
return &TrieID{
BlockHash: header.Hash(),
BlockNumber: header.Number.Uint64(),
- AccKey: nil,
+ StateRoot: header.Root,
Root: header.Root,
+ AccKey: nil,
}
}
@@ -76,6 +80,7 @@ func StorageTrieID(state *TrieID, addrHash, root common.Hash) *TrieID {
return &TrieID{
BlockHash: state.BlockHash,
BlockNumber: state.BlockNumber,
+ StateRoot: state.StateRoot,
AccKey: addrHash[:],
Root: root,
}
@@ -178,7 +183,7 @@ func (req *BloomRequest) StoreResult(db ethdb.Database) {
// TxStatus describes the status of a transaction
type TxStatus struct {
- Status core.TxStatus
+ Status txpool.TxStatus
Lookup *rawdb.LegacyTxLookupEntry `rlp:"nil"`
Error string
}
diff --git a/light/odr_test.go b/light/odr_test.go
index ec109f61f2..903c7f6f90 100644
--- a/light/odr_test.go
+++ b/light/odr_test.go
@@ -36,7 +36,6 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
)
var (
@@ -57,6 +56,7 @@ type testOdr struct {
OdrBackend
indexerConfig *IndexerConfig
sdb, ldb ethdb.Database
+ serverState state.Database
disable bool
}
@@ -82,7 +82,18 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
req.Receipts = rawdb.ReadRawReceipts(odr.sdb, req.Hash, *number)
}
case *TrieRequest:
- t, _ := trie.New(common.BytesToHash(req.Id.AccKey), req.Id.Root, trie.NewDatabase(odr.sdb))
+ var (
+ err error
+ t state.Trie
+ )
+ if len(req.Id.AccKey) > 0 {
+ t, err = odr.serverState.OpenStorageTrie(req.Id.StateRoot, common.BytesToHash(req.Id.AccKey), req.Id.Root)
+ } else {
+ t, err = odr.serverState.OpenTrie(req.Id.Root)
+ }
+ if err != nil {
+ panic(err)
+ }
nodes := NewNodeSet()
t.Prove(req.Key, 0, nodes)
req.Proof = nodes
@@ -149,7 +160,7 @@ func odrAccounts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc
st = NewState(ctx, header, lc.Odr())
} else {
header := bc.GetHeaderByHash(bhash)
- st, _ = state.New(header.Root, state.NewDatabase(db), nil)
+ st, _ = state.New(header.Root, bc.StateCache(), nil)
}
var res []byte
@@ -189,7 +200,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
} else {
chain = bc
header = bc.GetHeaderByHash(bhash)
- st, _ = state.New(header.Root, state.NewDatabase(db), nil)
+ st, _ = state.New(header.Root, bc.StateCache(), nil)
}
// Perform read-only call.
@@ -253,22 +264,22 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
var (
sdb = rawdb.NewMemoryDatabase()
ldb = rawdb.NewMemoryDatabase()
- gspec = core.Genesis{
+ gspec = &core.Genesis{
+ Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
BaseFee: big.NewInt(params.InitialBaseFee),
}
- genesis = gspec.MustCommit(sdb)
)
- gspec.MustCommit(ldb)
// Assemble the test environment
- blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil)
- gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen)
+ blockchain, _ := core.NewBlockChain(sdb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
+ _, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, testChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil {
t.Fatal(err)
}
- odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig}
- lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil)
+ gspec.MustCommit(ldb)
+ odr := &testOdr{sdb: sdb, ldb: ldb, serverState: blockchain.StateCache(), indexerConfig: TestClientIndexerConfig}
+ lightchain, err := NewLightChain(odr, gspec.Config, ethash.NewFullFaker(), nil)
if err != nil {
t.Fatal(err)
}
diff --git a/light/odr_util.go b/light/odr_util.go
index 48631139b4..c49af3a1fb 100644
--- a/light/odr_util.go
+++ b/light/odr_util.go
@@ -23,8 +23,8 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -148,7 +148,7 @@ func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint
}
// GetBlockReceipts retrieves the receipts generated by the transactions included
-// in a block given by its hash.
+// in a block given by its hash. Receipts will be filled in with context data.
func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (types.Receipts, error) {
// Assume receipts are already stored locally and attempt to retrieve.
receipts := rawdb.ReadRawReceipts(odr.Database(), hash, number)
@@ -184,9 +184,8 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
}
// GetBlockLogs retrieves the logs generated by the transactions included in a
-// block given by its hash.
+// block given by its hash. Logs will be filled in with context data.
func GetBlockLogs(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) ([][]*types.Log, error) {
- // Retrieve the potentially incomplete receipts from disk or network
receipts, err := GetBlockReceipts(ctx, odr, hash, number)
if err != nil {
return nil, err
@@ -277,7 +276,7 @@ func GetBloomBits(ctx context.Context, odr OdrBackend, bit uint, sections []uint
// number of retries, thus giving a weak guarantee.
func GetTransaction(ctx context.Context, odr OdrBackend, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
r := &TxStatusRequest{Hashes: []common.Hash{txHash}}
- if err := odr.RetrieveTxStatus(ctx, r); err != nil || r.Status[0].Status != core.TxStatusIncluded {
+ if err := odr.RetrieveTxStatus(ctx, r); err != nil || r.Status[0].Status != txpool.TxStatusIncluded {
return nil, common.Hash{}, 0, 0, err
}
pos := r.Status[0].Lookup
diff --git a/light/postprocess.go b/light/postprocess.go
index 3f9da65933..e800a1f0f7 100644
--- a/light/postprocess.go
+++ b/light/postprocess.go
@@ -25,7 +25,6 @@ import (
"math/big"
"time"
- mapset "github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/bitutil"
"github.com/ethereum/go-ethereum/core"
@@ -103,8 +102,6 @@ var (
errNoTrustedCht = errors.New("no trusted canonical hash trie")
errNoTrustedBloomTrie = errors.New("no trusted bloom trie")
errNoHeader = errors.New("header not found")
- chtPrefix = []byte("chtRootV2-") // chtPrefix + chtNum (uint64 big endian) -> trie root hash
- ChtTablePrefix = "cht-"
)
// ChtNode structures are stored in the Canonical Hash Trie in an RLP encoded format
@@ -117,7 +114,7 @@ type ChtNode struct {
func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash {
var encNumber [8]byte
binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- data, _ := db.Get(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...))
+ data, _ := db.Get(append(append(rawdb.ChtPrefix, encNumber[:]...), sectionHead.Bytes()...))
return common.BytesToHash(data)
}
@@ -125,7 +122,7 @@ func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) c
func StoreChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) {
var encNumber [8]byte
binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- db.Put(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes())
+ db.Put(append(append(rawdb.ChtPrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes())
}
// ChtIndexerBackend implements core.ChainIndexerBackend.
@@ -134,7 +131,6 @@ type ChtIndexerBackend struct {
diskdb, trieTable ethdb.Database
odr OdrBackend
triedb *trie.Database
- trieset mapset.Set
section, sectionSize uint64
lastHash common.Hash
trie *trie.Trie
@@ -142,17 +138,16 @@ type ChtIndexerBackend struct {
// NewChtIndexer creates a Cht chain indexer
func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64, disablePruning bool) *core.ChainIndexer {
- trieTable := rawdb.NewTable(db, ChtTablePrefix)
+ trieTable := rawdb.NewTable(db, string(rawdb.ChtTablePrefix))
backend := &ChtIndexerBackend{
diskdb: db,
odr: odr,
trieTable: trieTable,
triedb: trie.NewDatabaseWithConfig(trieTable, &trie.Config{Cache: 1}), // Use a tiny cache only to keep memory down
- trieset: mapset.NewSet(),
sectionSize: size,
disablePruning: disablePruning,
}
- return core.NewChainIndexer(db, rawdb.NewTable(db, "chtIndexV2-"), backend, size, confirms, time.Millisecond*100, "cht")
+ return core.NewChainIndexer(db, rawdb.NewTable(db, string(rawdb.ChtIndexTablePrefix)), backend, size, confirms, time.Millisecond*100, "cht")
}
// fetchMissingNodes tries to retrieve the last entry of the latest trusted CHT from the
@@ -187,12 +182,12 @@ func (c *ChtIndexerBackend) Reset(ctx context.Context, section uint64, lastSecti
root = GetChtRoot(c.diskdb, section-1, lastSectionHead)
}
var err error
- c.trie, err = trie.New(common.Hash{}, root, c.triedb)
+ c.trie, err = trie.New(trie.TrieID(root), c.triedb)
if err != nil && c.odr != nil {
err = c.fetchMissingNodes(ctx, section, root)
if err == nil {
- c.trie, err = trie.New(common.Hash{}, root, c.triedb)
+ c.trie, err = trie.New(trie.TrieID(root), c.triedb)
}
}
c.section = section
@@ -217,47 +212,51 @@ func (c *ChtIndexerBackend) Process(ctx context.Context, header *types.Header) e
// Commit implements core.ChainIndexerBackend
func (c *ChtIndexerBackend) Commit() error {
- root, nodes, err := c.trie.Commit(false)
- if err != nil {
- return err
- }
+ root, nodes := c.trie.Commit(false)
// Commit trie changes into trie database in case it's not nil.
if nodes != nil {
if err := c.triedb.Update(trie.NewWithNodeSet(nodes)); err != nil {
return err
}
+ if err := c.triedb.Commit(root, false); err != nil {
+ return err
+ }
}
// Re-create trie with newly generated root and updated database.
- c.trie, err = trie.New(common.Hash{}, root, c.triedb)
+ var err error
+ c.trie, err = trie.New(trie.TrieID(root), c.triedb)
if err != nil {
return err
}
// Pruning historical trie nodes if necessary.
if !c.disablePruning {
- // Flush the triedb and track the latest trie nodes.
- c.trieset.Clear()
- c.triedb.Commit(root, false, func(hash common.Hash) { c.trieset.Add(hash) })
-
it := c.trieTable.NewIterator(nil, nil)
defer it.Release()
var (
- deleted int
- remaining int
- t = time.Now()
+ deleted int
+ batch = c.trieTable.NewBatch()
+ t = time.Now()
)
+ hashes := make(map[common.Hash]struct{})
+ if nodes != nil {
+ for _, hash := range nodes.Hashes() {
+ hashes[hash] = struct{}{}
+ }
+ }
for it.Next() {
- trimmed := bytes.TrimPrefix(it.Key(), []byte(ChtTablePrefix))
- if !c.trieset.Contains(common.BytesToHash(trimmed)) {
- c.trieTable.Delete(trimmed)
- deleted += 1
- } else {
- remaining += 1
+ trimmed := bytes.TrimPrefix(it.Key(), rawdb.ChtTablePrefix)
+ if len(trimmed) == common.HashLength {
+ if _, ok := hashes[common.BytesToHash(trimmed)]; !ok {
+ batch.Delete(trimmed)
+ deleted += 1
+ }
}
}
- log.Debug("Prune historical CHT trie nodes", "deleted", deleted, "remaining", remaining, "elapsed", common.PrettyDuration(time.Since(t)))
- } else {
- c.triedb.Commit(root, false, nil)
+ if err := batch.Write(); err != nil {
+ return err
+ }
+ log.Debug("Prune historical CHT trie nodes", "deleted", deleted, "remaining", len(hashes), "elapsed", common.PrettyDuration(time.Since(t)))
}
log.Info("Storing CHT", "section", c.section, "head", fmt.Sprintf("%064x", c.lastHash), "root", fmt.Sprintf("%064x", root))
StoreChtRoot(c.diskdb, c.section, c.lastHash, root)
@@ -308,16 +307,11 @@ func (c *ChtIndexerBackend) Prune(threshold uint64) error {
return nil
}
-var (
- bloomTriePrefix = []byte("bltRoot-") // bloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash
- BloomTrieTablePrefix = "blt-"
-)
-
// GetBloomTrieRoot reads the BloomTrie root associated to the given section from the database
func GetBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash {
var encNumber [8]byte
binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- data, _ := db.Get(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...))
+ data, _ := db.Get(append(append(rawdb.BloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...))
return common.BytesToHash(data)
}
@@ -325,7 +319,7 @@ func GetBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.H
func StoreBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) {
var encNumber [8]byte
binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- db.Put(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes())
+ db.Put(append(append(rawdb.BloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes())
}
// BloomTrieIndexerBackend implements core.ChainIndexerBackend
@@ -333,7 +327,6 @@ type BloomTrieIndexerBackend struct {
disablePruning bool
diskdb, trieTable ethdb.Database
triedb *trie.Database
- trieset mapset.Set
odr OdrBackend
section uint64
parentSize uint64
@@ -345,20 +338,19 @@ type BloomTrieIndexerBackend struct {
// NewBloomTrieIndexer creates a BloomTrie chain indexer
func NewBloomTrieIndexer(db ethdb.Database, odr OdrBackend, parentSize, size uint64, disablePruning bool) *core.ChainIndexer {
- trieTable := rawdb.NewTable(db, BloomTrieTablePrefix)
+ trieTable := rawdb.NewTable(db, string(rawdb.BloomTrieTablePrefix))
backend := &BloomTrieIndexerBackend{
diskdb: db,
odr: odr,
trieTable: trieTable,
triedb: trie.NewDatabaseWithConfig(trieTable, &trie.Config{Cache: 1}), // Use a tiny cache only to keep memory down
- trieset: mapset.NewSet(),
parentSize: parentSize,
size: size,
disablePruning: disablePruning,
}
backend.bloomTrieRatio = size / parentSize
backend.sectionHeads = make([]common.Hash, backend.bloomTrieRatio)
- return core.NewChainIndexer(db, rawdb.NewTable(db, "bltIndex-"), backend, size, 0, time.Millisecond*100, "bloomtrie")
+ return core.NewChainIndexer(db, rawdb.NewTable(db, string(rawdb.BloomTrieIndexPrefix)), backend, size, 0, time.Millisecond*100, "bloomtrie")
}
// fetchMissingNodes tries to retrieve the last entries of the latest trusted bloom trie from the
@@ -414,11 +406,11 @@ func (b *BloomTrieIndexerBackend) Reset(ctx context.Context, section uint64, las
root = GetBloomTrieRoot(b.diskdb, section-1, lastSectionHead)
}
var err error
- b.trie, err = trie.New(common.Hash{}, root, b.triedb)
+ b.trie, err = trie.New(trie.TrieID(root), b.triedb)
if err != nil && b.odr != nil {
err = b.fetchMissingNodes(ctx, section, root)
if err == nil {
- b.trie, err = trie.New(common.Hash{}, root, b.triedb)
+ b.trie, err = trie.New(trie.TrieID(root), b.triedb)
}
}
b.section = section
@@ -464,47 +456,51 @@ func (b *BloomTrieIndexerBackend) Commit() error {
b.trie.Delete(encKey[:])
}
}
- root, nodes, err := b.trie.Commit(false)
- if err != nil {
- return err
- }
+ root, nodes := b.trie.Commit(false)
// Commit trie changes into trie database in case it's not nil.
if nodes != nil {
if err := b.triedb.Update(trie.NewWithNodeSet(nodes)); err != nil {
return err
}
+ if err := b.triedb.Commit(root, false); err != nil {
+ return err
+ }
}
// Re-create trie with newly generated root and updated database.
- b.trie, err = trie.New(common.Hash{}, root, b.triedb)
+ var err error
+ b.trie, err = trie.New(trie.TrieID(root), b.triedb)
if err != nil {
return err
}
// Pruning historical trie nodes if necessary.
if !b.disablePruning {
- // Flush the triedb and track the latest trie nodes.
- b.trieset.Clear()
- b.triedb.Commit(root, false, func(hash common.Hash) { b.trieset.Add(hash) })
-
it := b.trieTable.NewIterator(nil, nil)
defer it.Release()
var (
- deleted int
- remaining int
- t = time.Now()
+ deleted int
+ batch = b.trieTable.NewBatch()
+ t = time.Now()
)
+ hashes := make(map[common.Hash]struct{})
+ if nodes != nil {
+ for _, hash := range nodes.Hashes() {
+ hashes[hash] = struct{}{}
+ }
+ }
for it.Next() {
- trimmed := bytes.TrimPrefix(it.Key(), []byte(BloomTrieTablePrefix))
- if !b.trieset.Contains(common.BytesToHash(trimmed)) {
- b.trieTable.Delete(trimmed)
- deleted += 1
- } else {
- remaining += 1
+ trimmed := bytes.TrimPrefix(it.Key(), rawdb.BloomTrieTablePrefix)
+ if len(trimmed) == common.HashLength {
+ if _, ok := hashes[common.BytesToHash(trimmed)]; !ok {
+ batch.Delete(trimmed)
+ deleted += 1
+ }
}
}
- log.Debug("Prune historical bloom trie nodes", "deleted", deleted, "remaining", remaining, "elapsed", common.PrettyDuration(time.Since(t)))
- } else {
- b.triedb.Commit(root, false, nil)
+ if err := batch.Write(); err != nil {
+ return err
+ }
+ log.Debug("Prune historical bloom trie nodes", "deleted", deleted, "remaining", len(hashes), "elapsed", common.PrettyDuration(time.Since(t)))
}
sectionHead := b.sectionHeads[b.bloomTrieRatio-1]
StoreBloomTrieRoot(b.diskdb, b.section, sectionHead, root)
diff --git a/light/trie.go b/light/trie.go
index b88265e87d..0ccab1588d 100644
--- a/light/trie.go
+++ b/light/trie.go
@@ -54,7 +54,7 @@ func (db *odrDatabase) OpenTrie(root common.Hash) (state.Trie, error) {
return &odrTrie{db: db, id: db.id}, nil
}
-func (db *odrDatabase) OpenStorageTrie(addrHash, root common.Hash) (state.Trie, error) {
+func (db *odrDatabase) OpenStorageTrie(state, addrHash, root common.Hash) (state.Trie, error) {
return &odrTrie{db: db, id: StorageTrieID(db.id, addrHash, root)}, nil
}
@@ -63,8 +63,7 @@ func (db *odrDatabase) CopyTrie(t state.Trie) state.Trie {
case *odrTrie:
cpy := &odrTrie{db: t.db, id: t.id}
if t.trie != nil {
- cpytrie := *t.trie
- cpy.trie = &cpytrie
+ cpy.trie = t.trie.Copy()
}
return cpy
default:
@@ -96,6 +95,10 @@ func (db *odrDatabase) TrieDB() *trie.Database {
return nil
}
+func (db *odrDatabase) DiskDB() ethdb.KeyValueStore {
+ panic("not implemented")
+}
+
type odrTrie struct {
db *odrDatabase
id *TrieID
@@ -112,9 +115,9 @@ func (t *odrTrie) TryGet(key []byte) ([]byte, error) {
return res, err
}
-func (t *odrTrie) TryGetAccount(key []byte) (*types.StateAccount, error) {
- key = crypto.Keccak256(key)
+func (t *odrTrie) TryGetAccount(address common.Address) (*types.StateAccount, error) {
var res types.StateAccount
+ key := crypto.Keccak256(address.Bytes())
err := t.do(key, func() (err error) {
value, err := t.trie.TryGet(key)
if err != nil {
@@ -128,8 +131,8 @@ func (t *odrTrie) TryGetAccount(key []byte) (*types.StateAccount, error) {
return &res, err
}
-func (t *odrTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
- key = crypto.Keccak256(key)
+func (t *odrTrie) TryUpdateAccount(address common.Address, acc *types.StateAccount) error {
+ key := crypto.Keccak256(address.Bytes())
value, err := rlp.EncodeToBytes(acc)
if err != nil {
return fmt.Errorf("decoding error in account update: %w", err)
@@ -154,16 +157,16 @@ func (t *odrTrie) TryDelete(key []byte) error {
}
// TryDeleteAccount abstracts an account deletion from the trie.
-func (t *odrTrie) TryDeleteAccount(key []byte) error {
- key = crypto.Keccak256(key)
+func (t *odrTrie) TryDeleteAccount(address common.Address) error {
+ key := crypto.Keccak256(address.Bytes())
return t.do(key, func() error {
return t.trie.TryDelete(key)
})
}
-func (t *odrTrie) Commit(collectLeaf bool) (common.Hash, *trie.NodeSet, error) {
+func (t *odrTrie) Commit(collectLeaf bool) (common.Hash, *trie.NodeSet) {
if t.trie == nil {
- return t.id.Root, nil, nil
+ return t.id.Root, nil
}
return t.trie.Commit(collectLeaf)
}
@@ -193,11 +196,13 @@ func (t *odrTrie) do(key []byte, fn func() error) error {
for {
var err error
if t.trie == nil {
- var owner common.Hash
+ var id *trie.ID
if len(t.id.AccKey) > 0 {
- owner = common.BytesToHash(t.id.AccKey)
+ id = trie.StorageTrieID(t.id.StateRoot, common.BytesToHash(t.id.AccKey), t.id.Root)
+ } else {
+ id = trie.StateTrieID(t.id.StateRoot)
}
- t.trie, err = trie.New(owner, t.id.Root, trie.NewDatabase(t.db.backend.Database()))
+ t.trie, err = trie.New(id, trie.NewDatabase(t.db.backend.Database()))
}
if err == nil {
err = fn()
@@ -223,11 +228,13 @@ func newNodeIterator(t *odrTrie, startkey []byte) trie.NodeIterator {
// Open the actual non-ODR trie if that hasn't happened yet.
if t.trie == nil {
it.do(func() error {
- var owner common.Hash
+ var id *trie.ID
if len(t.id.AccKey) > 0 {
- owner = common.BytesToHash(t.id.AccKey)
+ id = trie.StorageTrieID(t.id.StateRoot, common.BytesToHash(t.id.AccKey), t.id.Root)
+ } else {
+ id = trie.StateTrieID(t.id.StateRoot)
}
- t, err := trie.New(owner, t.id.Root, trie.NewDatabase(t.db.backend.Database()))
+ t, err := trie.New(id, trie.NewDatabase(t.db.backend.Database()))
if err == nil {
it.t.trie = t
}
diff --git a/light/trie_test.go b/light/trie_test.go
index 63dd9020f2..0ab3eb02a0 100644
--- a/light/trie_test.go
+++ b/light/trie_test.go
@@ -37,24 +37,24 @@ func TestNodeIterator(t *testing.T) {
var (
fulldb = rawdb.NewMemoryDatabase()
lightdb = rawdb.NewMemoryDatabase()
- gspec = core.Genesis{
+ gspec = &core.Genesis{
+ Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
BaseFee: big.NewInt(params.InitialBaseFee),
}
- genesis = gspec.MustCommit(fulldb)
)
- gspec.MustCommit(lightdb)
- blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil)
- gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), fulldb, 4, testChainGen)
+ blockchain, _ := core.NewBlockChain(fulldb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
+ _, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, testChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err)
}
+ gspec.MustCommit(lightdb)
ctx := context.Background()
- odr := &testOdr{sdb: fulldb, ldb: lightdb, indexerConfig: TestClientIndexerConfig}
+ odr := &testOdr{sdb: fulldb, ldb: lightdb, serverState: blockchain.StateCache(), indexerConfig: TestClientIndexerConfig}
head := blockchain.CurrentHeader()
lightTrie, _ := NewStateDatabase(ctx, head, odr).OpenTrie(head.Root)
- fullTrie, _ := state.NewDatabase(fulldb).OpenTrie(head.Root)
+ fullTrie, _ := blockchain.StateCache().OpenTrie(head.Root)
if err := diffTries(fullTrie, lightTrie); err != nil {
t.Fatal(err)
}
diff --git a/light/txpool.go b/light/txpool.go
index b3e1a62e18..e59dc3e774 100644
--- a/light/txpool.go
+++ b/light/txpool.go
@@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
@@ -69,17 +70,19 @@ type TxPool struct {
istanbul bool // Fork indicator whether we are in the istanbul stage.
eip2718 bool // Fork indicator whether we are in the eip2718 stage.
+ shanghai bool // Fork indicator whether we are in the shanghai stage.
}
-// TxRelayBackend provides an interface to the mechanism that forwards transactions
-// to the ETH network. The implementations of the functions should be non-blocking.
+// TxRelayBackend provides an interface to the mechanism that forwards transactions to the
+// ETH network. The implementations of the functions should be non-blocking.
//
-// Send instructs backend to forward new transactions
-// NewHead notifies backend about a new head after processed by the tx pool,
-// including mined and rolled back transactions since the last event
-// Discard notifies backend about transactions that should be discarded either
-// because they have been replaced by a re-send or because they have been mined
-// long ago and no rollback is expected
+// Send instructs backend to forward new transactions NewHead notifies backend about a new
+// head after processed by the tx pool, including mined and rolled back transactions since
+// the last event.
+//
+// Discard notifies backend about transactions that should be discarded either because
+// they have been replaced by a re-send or because they have been mined long ago and no
+// rollback is expected.
type TxRelayBackend interface {
Send(txs types.Transactions)
NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash)
@@ -181,7 +184,7 @@ func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number
}
// If some transactions have been mined, write the needed data to disk and update
if list != nil {
- // Retrieve all the receipts belonging to this block and write the loopup table
+ // Retrieve all the receipts belonging to this block and write the lookup table
if _, err := GetBlockReceipts(ctx, pool.odr, hash, number); err != nil { // ODR caches, ignore results
return err
}
@@ -315,6 +318,7 @@ func (pool *TxPool) setNewHead(head *types.Header) {
next := new(big.Int).Add(head.Number, big.NewInt(1))
pool.istanbul = pool.config.IsIstanbul(next)
pool.eip2718 = pool.config.IsBerlin(next)
+ pool.shanghai = pool.config.IsShanghai(uint64(time.Now().Unix()))
}
// Stop stops the light transaction pool
@@ -353,7 +357,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
// Validate the transaction sender and it's sig. Throw
// if the from fields is invalid.
if from, err = types.Sender(pool.signer, tx); err != nil {
- return core.ErrInvalidSender
+ return txpool.ErrInvalidSender
}
// Last but not least check for nonce errors
currentState := pool.currentState(ctx)
@@ -365,14 +369,14 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
// block limit gas.
header := pool.chain.GetHeaderByHash(pool.head)
if header.GasLimit < tx.Gas() {
- return core.ErrGasLimit
+ return txpool.ErrGasLimit
}
// Transactions can't be negative. This may never happen
// using RLP decoded transactions but may occur if you create
// a transaction using the RPC for example.
if tx.Value().Sign() < 0 {
- return core.ErrNegativeValue
+ return txpool.ErrNegativeValue
}
// Transactor should have enough funds to cover the costs
@@ -382,7 +386,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
}
// Should supply enough intrinsic gas
- gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul)
+ gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
if err != nil {
return err
}
@@ -446,7 +450,7 @@ func (pool *TxPool) Add(ctx context.Context, tx *types.Transaction) error {
return nil
}
-// AddTransactions adds all valid transactions to the pool and passes them to
+// AddBatch adds all valid transactions to the pool and passes them to
// the tx relay backend
func (pool *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) {
pool.mu.Lock()
diff --git a/light/txpool_test.go b/light/txpool_test.go
index cc2651d29a..0770088063 100644
--- a/light/txpool_test.go
+++ b/light/txpool_test.go
@@ -36,19 +36,19 @@ type testTxRelay struct {
send, discard, mined chan int
}
-func (self *testTxRelay) Send(txs types.Transactions) {
- self.send <- len(txs)
+func (r *testTxRelay) Send(txs types.Transactions) {
+ r.send <- len(txs)
}
-func (self *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
+func (r *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
m := len(mined)
if m != 0 {
- self.mined <- m
+ r.mined <- m
}
}
-func (self *testTxRelay) Discard(hashes []common.Hash) {
- self.discard <- len(hashes)
+func (r *testTxRelay) Discard(hashes []common.Hash) {
+ r.discard <- len(hashes)
}
const poolTestTxs = 1000
@@ -83,21 +83,21 @@ func TestTxPool(t *testing.T) {
var (
sdb = rawdb.NewMemoryDatabase()
ldb = rawdb.NewMemoryDatabase()
- gspec = core.Genesis{
+ gspec = &core.Genesis{
+ Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
BaseFee: big.NewInt(params.InitialBaseFee),
}
- genesis = gspec.MustCommit(sdb)
)
- gspec.MustCommit(ldb)
// Assemble the test environment
- blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil)
- gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen)
+ blockchain, _ := core.NewBlockChain(sdb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
+ _, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), poolTestBlocks, txPoolTestChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err)
}
- odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig}
+ gspec.MustCommit(ldb)
+ odr := &testOdr{sdb: sdb, ldb: ldb, serverState: blockchain.StateCache(), indexerConfig: TestClientIndexerConfig}
relay := &testTxRelay{
send: make(chan int, 1),
discard: make(chan int, 1),
diff --git a/log/doc.go b/log/doc.go
index 993743c0fd..d2e15140e4 100644
--- a/log/doc.go
+++ b/log/doc.go
@@ -7,27 +7,25 @@ This package enforces you to only log key/value pairs. Keys must be strings. Val
any type that you like. The default output format is logfmt, but you may also choose to use
JSON instead if that suits you. Here's how you log:
- log.Info("page accessed", "path", r.URL.Path, "user_id", user.id)
+ log.Info("page accessed", "path", r.URL.Path, "user_id", user.id)
This will output a line that looks like:
- lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9
+ lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9
-Getting Started
+# Getting Started
To get started, you'll want to import the library:
- import log "github.com/inconshreveable/log15"
-
+ import log "github.com/inconshreveable/log15"
Now you're ready to start logging:
- func main() {
- log.Info("Program starting", "args", os.Args())
- }
-
+ func main() {
+ log.Info("Program starting", "args", os.Args())
+ }
-Convention
+# Convention
Because recording a human-meaningful message is common and good practice, the first argument to every
logging method is the value to the *implicit* key 'msg'.
@@ -40,38 +38,35 @@ you to favor terseness, ordering, and speed over safety. This is a reasonable tr
logging functions. You don't need to explicitly state keys/values, log15 understands that they alternate
in the variadic argument list:
- log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val)
+ log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val)
If you really do favor your type-safety, you may choose to pass a log.Ctx instead:
- log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val})
-
+ log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val})
-Context loggers
+# Context loggers
Frequently, you want to add context to a logger so that you can track actions associated with it. An http
request is a good example. You can easily create new loggers that have context that is automatically included
with each log line:
- requestlogger := log.New("path", r.URL.Path)
+ requestlogger := log.New("path", r.URL.Path)
- // later
- requestlogger.Debug("db txn commit", "duration", txnTimer.Finish())
+ // later
+ requestlogger.Debug("db txn commit", "duration", txnTimer.Finish())
This will output a log line that includes the path context that is attached to the logger:
- lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12
+ lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12
-
-Handlers
+# Handlers
The Handler interface defines where log lines are printed to and how they are formatted. Handler is a
single interface that is inspired by net/http's handler interface:
- type Handler interface {
- Log(r *Record) error
- }
-
+ type Handler interface {
+ Log(r *Record) error
+ }
Handlers can filter records, format them, or dispatch to multiple other Handlers.
This package implements a number of Handlers for common logging patterns that are
@@ -79,49 +74,49 @@ easily composed to create flexible, custom logging structures.
Here's an example handler that prints logfmt output to Stdout:
- handler := log.StreamHandler(os.Stdout, log.LogfmtFormat())
+ handler := log.StreamHandler(os.Stdout, log.LogfmtFormat())
Here's an example handler that defers to two other handlers. One handler only prints records
from the rpc package in logfmt to standard out. The other prints records at Error level
or above in JSON formatted output to the file /var/log/service.json
- handler := log.MultiHandler(
- log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JSONFormat())),
- log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler())
- )
+ handler := log.MultiHandler(
+ log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JSONFormat())),
+ log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler())
+ )
-Logging File Names and Line Numbers
+# Logging File Names and Line Numbers
This package implements three Handlers that add debugging information to the
context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's
an example that adds the source file and line number of each logging call to
the context.
- h := log.CallerFileHandler(log.StdoutHandler)
- log.Root().SetHandler(h)
- ...
- log.Error("open file", "err", err)
+ h := log.CallerFileHandler(log.StdoutHandler)
+ log.Root().SetHandler(h)
+ ...
+ log.Error("open file", "err", err)
This will output a line that looks like:
- lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42
+ lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42
Here's an example that logs the call stack rather than just the call site.
- h := log.CallerStackHandler("%+v", log.StdoutHandler)
- log.Root().SetHandler(h)
- ...
- log.Error("open file", "err", err)
+ h := log.CallerStackHandler("%+v", log.StdoutHandler)
+ log.Root().SetHandler(h)
+ ...
+ log.Error("open file", "err", err)
This will output a line that looks like:
- lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]"
+ lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]"
The "%+v" format instructs the handler to include the path of the source file
relative to the compile time GOPATH. The github.com/go-stack/stack package
documents the full list of formatting verbs and modifiers available.
-Custom Handlers
+# Custom Handlers
The Handler interface is so simple that it's also trivial to write your own. Let's create an
example handler which tries to write to one handler, but if that fails it falls back to
@@ -129,24 +124,24 @@ writing to another handler and includes the error that it encountered when tryin
to the primary. This might be useful when trying to log over a network socket, but if that
fails you want to log those records to a file on disk.
- type BackupHandler struct {
- Primary Handler
- Secondary Handler
- }
+ type BackupHandler struct {
+ Primary Handler
+ Secondary Handler
+ }
- func (h *BackupHandler) Log (r *Record) error {
- err := h.Primary.Log(r)
- if err != nil {
- r.Ctx = append(ctx, "primary_err", err)
- return h.Secondary.Log(r)
- }
- return nil
- }
+ func (h *BackupHandler) Log (r *Record) error {
+ err := h.Primary.Log(r)
+ if err != nil {
+ r.Ctx = append(ctx, "primary_err", err)
+ return h.Secondary.Log(r)
+ }
+ return nil
+ }
This pattern is so useful that a generic version that handles an arbitrary number of Handlers
is included as part of this library called FailoverHandler.
-Logging Expensive Operations
+# Logging Expensive Operations
Sometimes, you want to log values that are extremely expensive to compute, but you don't want to pay
the price of computing them if you haven't turned up your logging level to a high level of detail.
@@ -155,50 +150,50 @@ This package provides a simple type to annotate a logging operation that you wan
lazily, just when it is about to be logged, so that it would not be evaluated if an upstream Handler
filters it out. Just wrap any function which takes no arguments with the log.Lazy type. For example:
- func factorRSAKey() (factors []int) {
- // return the factors of a very large number
- }
+ func factorRSAKey() (factors []int) {
+ // return the factors of a very large number
+ }
- log.Debug("factors", log.Lazy{factorRSAKey})
+ log.Debug("factors", log.Lazy{factorRSAKey})
If this message is not logged for any reason (like logging at the Error level), then
factorRSAKey is never evaluated.
-Dynamic context values
+# Dynamic context values
The same log.Lazy mechanism can be used to attach context to a logger which you want to be
evaluated when the message is logged, but not when the logger is created. For example, let's imagine
a game where you have Player objects:
- type Player struct {
- name string
- alive bool
- log.Logger
- }
+ type Player struct {
+ name string
+ alive bool
+ log.Logger
+ }
You always want to log a player's name and whether they're alive or dead, so when you create the player
object, you might do:
- p := &Player{name: name, alive: true}
- p.Logger = log.New("name", p.name, "alive", p.alive)
+ p := &Player{name: name, alive: true}
+ p.Logger = log.New("name", p.name, "alive", p.alive)
Only now, even after a player has died, the logger will still report they are alive because the logging
context is evaluated when the logger was created. By using the Lazy wrapper, we can defer the evaluation
of whether the player is alive or not to each log message, so that the log records will reflect the player's
current state no matter when the log message is written:
- p := &Player{name: name, alive: true}
- isAlive := func() bool { return p.alive }
- player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive})
+ p := &Player{name: name, alive: true}
+ isAlive := func() bool { return p.alive }
+ player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive})
-Terminal Format
+# Terminal Format
If log15 detects that stdout is a terminal, it will configure the default
handler for it (which is log.StdoutHandler) to use TerminalFormat. This format
logs records nicely for your terminal, including color-coded output based
on log level.
-Error Handling
+# Error Handling
Becasuse log15 allows you to step around the type system, there are a few ways you can specify
invalid arguments to the logging functions. You could, for example, wrap something that is not
@@ -216,61 +211,61 @@ are encouraged to return errors only if they fail to write their log records out
syslog daemon is not responding. This allows the construction of useful handlers which cope with those failures
like the FailoverHandler.
-Library Use
+# Library Use
log15 is intended to be useful for library authors as a way to provide configurable logging to
users of their library. Best practice for use in a library is to always disable all output for your logger
by default and to provide a public Logger instance that consumers of your library can configure. Like so:
- package yourlib
+ package yourlib
- import "github.com/inconshreveable/log15"
+ import "github.com/inconshreveable/log15"
- var Log = log.New()
+ var Log = log.New()
- func init() {
- Log.SetHandler(log.DiscardHandler())
- }
+ func init() {
+ Log.SetHandler(log.DiscardHandler())
+ }
Users of your library may then enable it if they like:
- import "github.com/inconshreveable/log15"
- import "example.com/yourlib"
+ import "github.com/inconshreveable/log15"
+ import "example.com/yourlib"
- func main() {
- handler := // custom handler setup
- yourlib.Log.SetHandler(handler)
- }
+ func main() {
+ handler := // custom handler setup
+ yourlib.Log.SetHandler(handler)
+ }
-Best practices attaching logger context
+# Best practices attaching logger context
The ability to attach context to a logger is a powerful one. Where should you do it and why?
I favor embedding a Logger directly into any persistent object in my application and adding
unique, tracing context keys to it. For instance, imagine I am writing a web browser:
- type Tab struct {
- url string
- render *RenderingContext
- // ...
+ type Tab struct {
+ url string
+ render *RenderingContext
+ // ...
- Logger
- }
+ Logger
+ }
- func NewTab(url string) *Tab {
- return &Tab {
- // ...
- url: url,
+ func NewTab(url string) *Tab {
+ return &Tab {
+ // ...
+ url: url,
- Logger: log.New("url", url),
- }
- }
+ Logger: log.New("url", url),
+ }
+ }
When a new tab is created, I assign a logger to it with the url of
the tab as context so it can easily be traced through the logs.
Now, whenever we perform any operation with the tab, we'll log with its
embedded logger and it will include the tab title automatically:
- tab.Debug("moved position", "idx", tab.idx)
+ tab.Debug("moved position", "idx", tab.idx)
There's only one problem. What if the tab url changes? We could
use log.Lazy to make sure the current url is always written, but that
@@ -285,29 +280,29 @@ function to let you generate what you might call "surrogate keys"
They're just random hex identifiers to use for tracing. Back to our
Tab example, we would prefer to set up our Logger like so:
- import logext "github.com/inconshreveable/log15/ext"
+ import logext "github.com/inconshreveable/log15/ext"
- t := &Tab {
- // ...
- url: url,
- }
+ t := &Tab {
+ // ...
+ url: url,
+ }
- t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl})
- return t
+ t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl})
+ return t
Now we'll have a unique traceable identifier even across loading new urls, but
we'll still be able to see the tab's current url in the log messages.
-Must
+# Must
For all Handler functions which can return an error, there is a version of that
function which will return no error but panics on failure. They are all available
on the Must object. For example:
- log.Must.FileHandler("/path", log.JSONFormat)
- log.Must.NetHandler("tcp", ":1234", log.JSONFormat)
+ log.Must.FileHandler("/path", log.JSONFormat)
+ log.Must.NetHandler("tcp", ":1234", log.JSONFormat)
-Inspiration and Credit
+# Inspiration and Credit
All of the following excellent projects inspired the design of this library:
@@ -325,9 +320,8 @@ github.com/spacemonkeygo/spacelog
golang's stdlib, notably io and net/http
-The Name
+# The Name
https://xkcd.com/927/
-
*/
package log
diff --git a/log/format.go b/log/format.go
index baf8fddac0..d7e2f820af 100644
--- a/log/format.go
+++ b/log/format.go
@@ -79,14 +79,14 @@ type TerminalStringer interface {
// a terminal with color-coded level output and terser human friendly timestamp.
// This format should only be used for interactive programs or while developing.
//
-// [LEVEL] [TIME] MESSAGE key=value key=value ...
+// [LEVEL] [TIME] MESSAGE key=value key=value ...
//
// Example:
//
-// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002
-//
+// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002
func TerminalFormat(usecolor bool) Format {
return FormatFunc(func(r *Record) []byte {
+ msg := escapeMessage(r.Msg)
var color = 0
if usecolor {
switch r.Lvl {
@@ -123,19 +123,19 @@ func TerminalFormat(usecolor bool) Format {
// Assemble and print the log heading
if color > 0 {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg)
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, msg)
} else {
- fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg)
+ fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, msg)
}
} else {
if color > 0 {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg)
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), msg)
} else {
- fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg)
+ fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), msg)
}
}
// try to justify the log output for short messages
- length := utf8.RuneCountInString(r.Msg)
+ length := utf8.RuneCountInString(msg)
if len(r.Ctx) > 0 && length < termMsgJust {
b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length))
}
@@ -149,7 +149,6 @@ func TerminalFormat(usecolor bool) Format {
// format for key/value pairs.
//
// For more details see: http://godoc.org/github.com/kr/logfmt
-//
func LogfmtFormat() Format {
return FormatFunc(func(r *Record) []byte {
common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg}
@@ -169,6 +168,8 @@ func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) {
v := formatLogfmtValue(ctx[i+1], term)
if !ok {
k, v = errorKey, formatLogfmtValue(k, term)
+ } else {
+ k = escapeString(k)
}
// XXX: we should probably check that all of your key bytes aren't invalid
@@ -473,7 +474,7 @@ func formatLogfmtBigInt(n *big.Int) string {
func escapeString(s string) string {
needsQuoting := false
for _, r := range s {
- // We quote everything below " (0x34) and above~ (0x7E), plus equal-sign
+ // We quote everything below " (0x22) and above~ (0x7E), plus equal-sign
if r <= '"' || r > '~' || r == '=' {
needsQuoting = true
break
@@ -484,3 +485,26 @@ func escapeString(s string) string {
}
return strconv.Quote(s)
}
+
+// escapeMessage checks if the provided string needs escaping/quoting, similarly
+// to escapeString. The difference is that this method is more lenient: it allows
+// for spaces and linebreaks to occur without needing quoting.
+func escapeMessage(s string) string {
+ needsQuoting := false
+ for _, r := range s {
+ // Allow CR/LF/TAB. This is to make multi-line messages work.
+ if r == '\r' || r == '\n' || r == '\t' {
+ continue
+ }
+ // We quote everything below (0x20) and above~ (0x7E),
+ // plus equal-sign
+ if r < ' ' || r > '~' || r == '=' {
+ needsQuoting = true
+ break
+ }
+ }
+ if !needsQuoting {
+ return s
+ }
+ return strconv.Quote(s)
+}
diff --git a/log/format_test.go b/log/format_test.go
index d7e0a95768..cfcfe85802 100644
--- a/log/format_test.go
+++ b/log/format_test.go
@@ -1,9 +1,11 @@
package log
import (
+ "fmt"
"math"
"math/big"
"math/rand"
+ "strings"
"testing"
)
@@ -93,3 +95,47 @@ func BenchmarkPrettyUint64Logfmt(b *testing.B) {
sink = FormatLogfmtUint64(rand.Uint64())
}
}
+
+func TestSanitation(t *testing.T) {
+ msg := "\u001b[1G\u001b[K\u001b[1A"
+ msg2 := "\u001b \u0000"
+ msg3 := "NiceMessage"
+ msg4 := "Space Message"
+ msg5 := "Enter\nMessage"
+
+ for i, tt := range []struct {
+ msg string
+ want string
+ }{
+ {
+ msg: msg,
+ want: fmt.Sprintf("] %q %q=%q\n", msg, msg, msg),
+ },
+ {
+ msg: msg2,
+ want: fmt.Sprintf("] %q %q=%q\n", msg2, msg2, msg2),
+ },
+ {
+ msg: msg3,
+ want: fmt.Sprintf("] %s %s=%s\n", msg3, msg3, msg3),
+ },
+ {
+ msg: msg4,
+ want: fmt.Sprintf("] %s %q=%q\n", msg4, msg4, msg4),
+ },
+ {
+ msg: msg5,
+ want: fmt.Sprintf("] %s %q=%q\n", msg5, msg5, msg5),
+ },
+ } {
+ var (
+ logger = New()
+ out = new(strings.Builder)
+ )
+ logger.SetHandler(LvlFilterHandler(LvlInfo, StreamHandler(out, TerminalFormat(false))))
+ logger.Info(tt.msg, tt.msg, tt.msg)
+ if have := out.String()[24:]; tt.want != have {
+ t.Fatalf("test %d: want / have: \n%v\n%v", i, tt.want, have)
+ }
+ }
+}
diff --git a/log/handler.go b/log/handler.go
index 4b9515fa15..892cfcc3e1 100644
--- a/log/handler.go
+++ b/log/handler.go
@@ -136,15 +136,14 @@ func CallerStackHandler(format string, h Handler) Handler {
// wrapped Handler if the given function evaluates true. For example,
// to only log records where the 'err' key is not nil:
//
-// logger.SetHandler(FilterHandler(func(r *Record) bool {
-// for i := 0; i < len(r.Ctx); i += 2 {
-// if r.Ctx[i] == "err" {
-// return r.Ctx[i+1] != nil
-// }
-// }
-// return false
-// }, h))
-//
+// logger.SetHandler(FilterHandler(func(r *Record) bool {
+// for i := 0; i < len(r.Ctx); i += 2 {
+// if r.Ctx[i] == "err" {
+// return r.Ctx[i+1] != nil
+// }
+// }
+// return false
+// }, h))
func FilterHandler(fn func(r *Record) bool, h Handler) Handler {
return FuncHandler(func(r *Record) error {
if fn(r) {
@@ -159,8 +158,7 @@ func FilterHandler(fn func(r *Record) bool, h Handler) Handler {
// context matches the value. For example, to only log records
// from your ui package:
//
-// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler)
-//
+// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler)
func MatchFilterHandler(key string, value interface{}, h Handler) Handler {
return FilterHandler(func(r *Record) (pass bool) {
switch key {
@@ -186,8 +184,7 @@ func MatchFilterHandler(key string, value interface{}, h Handler) Handler {
// level to the wrapped Handler. For example, to only
// log Error/Crit records:
//
-// log.LvlFilterHandler(log.LvlError, log.StdoutHandler)
-//
+// log.LvlFilterHandler(log.LvlError, log.StdoutHandler)
func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
return FilterHandler(func(r *Record) (pass bool) {
return r.Lvl <= maxLvl
@@ -199,10 +196,9 @@ func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
// to different locations. For example, to log to a file and
// standard error:
//
-// log.MultiHandler(
-// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
-// log.StderrHandler)
-//
+// log.MultiHandler(
+// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
+// log.StderrHandler)
func MultiHandler(hs ...Handler) Handler {
return FuncHandler(func(r *Record) error {
for _, h := range hs {
@@ -220,10 +216,10 @@ func MultiHandler(hs ...Handler) Handler {
// to writing to a file if the network fails, and then to
// standard out if the file write fails:
//
-// log.FailoverHandler(
-// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()),
-// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
-// log.StdoutHandler)
+// log.FailoverHandler(
+// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()),
+// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
+// log.StdoutHandler)
//
// All writes that do not go to the first handler will add context with keys of
// the form "failover_err_{idx}" which explain the error encountered while
diff --git a/log/handler_glog.go b/log/handler_glog.go
index 9b1d4efaf4..b5186d4b27 100644
--- a/log/handler_glog.go
+++ b/log/handler_glog.go
@@ -82,14 +82,14 @@ func (h *GlogHandler) Verbosity(level Lvl) {
//
// For instance:
//
-// pattern="gopher.go=3"
-// sets the V level to 3 in all Go files named "gopher.go"
+// pattern="gopher.go=3"
+// sets the V level to 3 in all Go files named "gopher.go"
//
-// pattern="foo=3"
-// sets V to 3 in all files of any packages whose import path ends in "foo"
+// pattern="foo=3"
+// sets V to 3 in all files of any packages whose import path ends in "foo"
//
-// pattern="foo/*=3"
-// sets V to 3 in all files of any packages whose import path contains "foo"
+// pattern="foo/*=3"
+// sets V to 3 in all files of any packages whose import path contains "foo"
func (h *GlogHandler) Vmodule(ruleset string) error {
var filter []pattern
for _, rule := range strings.Split(ruleset, ",") {
diff --git a/log/logger.go b/log/logger.go
index 276d6969e2..1549e32854 100644
--- a/log/logger.go
+++ b/log/logger.go
@@ -46,7 +46,7 @@ func (l Lvl) AlignedString() string {
}
}
-// Strings returns the name of a Lvl.
+// String returns the name of a Lvl.
func (l Lvl) String() string {
switch l {
case LvlTrace:
diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go
index e99717aeeb..748c692e13 100644
--- a/metrics/influxdb/influxdb.go
+++ b/metrics/influxdb/influxdb.go
@@ -101,6 +101,9 @@ func (r *reporter) run() {
intervalTicker := time.NewTicker(r.interval)
pingTicker := time.NewTicker(time.Second * 5)
+ defer intervalTicker.Stop()
+ defer pingTicker.Stop()
+
for {
select {
case <-intervalTicker.C:
@@ -160,27 +163,28 @@ func (r *reporter) send() error {
})
case metrics.Histogram:
ms := metric.Snapshot()
-
if ms.Count() > 0 {
- ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
+ ps := ms.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
+ fields := map[string]interface{}{
+ "count": ms.Count(),
+ "max": ms.Max(),
+ "mean": ms.Mean(),
+ "min": ms.Min(),
+ "stddev": ms.StdDev(),
+ "variance": ms.Variance(),
+ "p25": ps[0],
+ "p50": ps[1],
+ "p75": ps[2],
+ "p95": ps[3],
+ "p99": ps[4],
+ "p999": ps[5],
+ "p9999": ps[6],
+ }
pts = append(pts, client.Point{
Measurement: fmt.Sprintf("%s%s.histogram", namespace, name),
Tags: r.tags,
- Fields: map[string]interface{}{
- "count": ms.Count(),
- "max": ms.Max(),
- "mean": ms.Mean(),
- "min": ms.Min(),
- "stddev": ms.StdDev(),
- "variance": ms.Variance(),
- "p50": ps[0],
- "p75": ps[1],
- "p95": ps[2],
- "p99": ps[3],
- "p999": ps[4],
- "p9999": ps[5],
- },
- Time: now,
+ Fields: fields,
+ Time: now,
})
}
case metrics.Meter:
diff --git a/metrics/influxdb/influxdbv2.go b/metrics/influxdb/influxdbv2.go
index c8eca41616..bfb762196c 100644
--- a/metrics/influxdb/influxdbv2.go
+++ b/metrics/influxdb/influxdbv2.go
@@ -1,11 +1,3 @@
-//
-// 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 influxdb
import (
@@ -70,6 +62,9 @@ func (r *v2Reporter) run() {
intervalTicker := time.NewTicker(r.interval)
pingTicker := time.NewTicker(time.Second * 5)
+ defer intervalTicker.Stop()
+ defer pingTicker.Stop()
+
for {
select {
case <-intervalTicker.C:
diff --git a/metrics/librato/client.go b/metrics/librato/client.go
index eebe20521b..729c2da9a9 100644
--- a/metrics/librato/client.go
+++ b/metrics/librato/client.go
@@ -80,7 +80,7 @@ func (c *LibratoClient) PostMetrics(batch Batch) (err error) {
return
}
- if req, err = http.NewRequest("POST", MetricsPostUrl, bytes.NewBuffer(js)); err != nil {
+ if req, err = http.NewRequest(http.MethodPost, MetricsPostUrl, bytes.NewBuffer(js)); err != nil {
return
}
diff --git a/metrics/metrics.go b/metrics/metrics.go
index 747d6471a7..2edf8e35f1 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -7,7 +7,8 @@ package metrics
import (
"os"
- "runtime"
+ "runtime/metrics"
+ "runtime/pprof"
"strings"
"time"
@@ -54,38 +55,106 @@ func init() {
}
}
-// CollectProcessMetrics periodically collects various metrics about the running
-// process.
+var threadCreateProfile = pprof.Lookup("threadcreate")
+
+type runtimeStats struct {
+ GCPauses *metrics.Float64Histogram
+ GCAllocBytes uint64
+ GCFreedBytes uint64
+
+ MemTotal uint64
+ HeapObjects uint64
+ HeapFree uint64
+ HeapReleased uint64
+ HeapUnused uint64
+
+ Goroutines uint64
+ SchedLatency *metrics.Float64Histogram
+}
+
+var runtimeSamples = []metrics.Sample{
+ {Name: "/gc/pauses:seconds"}, // histogram
+ {Name: "/gc/heap/allocs:bytes"},
+ {Name: "/gc/heap/frees:bytes"},
+ {Name: "/memory/classes/total:bytes"},
+ {Name: "/memory/classes/heap/objects:bytes"},
+ {Name: "/memory/classes/heap/free:bytes"},
+ {Name: "/memory/classes/heap/released:bytes"},
+ {Name: "/memory/classes/heap/unused:bytes"},
+ {Name: "/sched/goroutines:goroutines"},
+ {Name: "/sched/latencies:seconds"}, // histogram
+}
+
+func readRuntimeStats(v *runtimeStats) {
+ metrics.Read(runtimeSamples)
+ for _, s := range runtimeSamples {
+ // Skip invalid/unknown metrics. This is needed because some metrics
+ // are unavailable in older Go versions, and attempting to read a 'bad'
+ // metric panics.
+ if s.Value.Kind() == metrics.KindBad {
+ continue
+ }
+
+ switch s.Name {
+ case "/gc/pauses:seconds":
+ v.GCPauses = s.Value.Float64Histogram()
+ case "/gc/heap/allocs:bytes":
+ v.GCAllocBytes = s.Value.Uint64()
+ case "/gc/heap/frees:bytes":
+ v.GCFreedBytes = s.Value.Uint64()
+ case "/memory/classes/total:bytes":
+ v.MemTotal = s.Value.Uint64()
+ case "/memory/classes/heap/objects:bytes":
+ v.HeapObjects = s.Value.Uint64()
+ case "/memory/classes/heap/free:bytes":
+ v.HeapFree = s.Value.Uint64()
+ case "/memory/classes/heap/released:bytes":
+ v.HeapReleased = s.Value.Uint64()
+ case "/memory/classes/heap/unused:bytes":
+ v.HeapUnused = s.Value.Uint64()
+ case "/sched/goroutines:goroutines":
+ v.Goroutines = s.Value.Uint64()
+ case "/sched/latencies:seconds":
+ v.SchedLatency = s.Value.Float64Histogram()
+ }
+ }
+}
+
+// CollectProcessMetrics periodically collects various metrics about the running process.
func CollectProcessMetrics(refresh time.Duration) {
// Short circuit if the metrics system is disabled
if !Enabled {
return
}
+
refreshFreq := int64(refresh / time.Second)
// Create the various data collectors
- cpuStats := make([]*CPUStats, 2)
- memstats := make([]*runtime.MemStats, 2)
- diskstats := make([]*DiskStats, 2)
- for i := 0; i < len(memstats); i++ {
- cpuStats[i] = new(CPUStats)
- memstats[i] = new(runtime.MemStats)
- diskstats[i] = new(DiskStats)
- }
- // Define the various metrics to collect
var (
- cpuSysLoad = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry)
- cpuSysWait = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry)
- cpuProcLoad = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry)
- cpuThreads = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry)
- cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry)
-
- memPauses = GetOrRegisterMeter("system/memory/pauses", DefaultRegistry)
- memAllocs = GetOrRegisterMeter("system/memory/allocs", DefaultRegistry)
- memFrees = GetOrRegisterMeter("system/memory/frees", DefaultRegistry)
- memHeld = GetOrRegisterGauge("system/memory/held", DefaultRegistry)
- memUsed = GetOrRegisterGauge("system/memory/used", DefaultRegistry)
+ cpustats = make([]CPUStats, 2)
+ diskstats = make([]DiskStats, 2)
+ rstats = make([]runtimeStats, 2)
+ )
+
+ // This scale factor is used for the runtime's time metrics. It's useful to convert to
+ // ns here because the runtime gives times in float seconds, but runtimeHistogram can
+ // only provide integers for the minimum and maximum values.
+ const secondsToNs = float64(time.Second)
+ // Define the various metrics to collect
+ var (
+ cpuSysLoad = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry)
+ cpuSysWait = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry)
+ cpuProcLoad = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry)
+ cpuThreads = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry)
+ cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry)
+ cpuSchedLatency = getOrRegisterRuntimeHistogram("system/cpu/schedlatency", secondsToNs, nil)
+ memPauses = getOrRegisterRuntimeHistogram("system/memory/pauses", secondsToNs, nil)
+ memAllocs = GetOrRegisterMeter("system/memory/allocs", DefaultRegistry)
+ memFrees = GetOrRegisterMeter("system/memory/frees", DefaultRegistry)
+ memTotal = GetOrRegisterGauge("system/memory/held", DefaultRegistry)
+ heapUsed = GetOrRegisterGauge("system/memory/used", DefaultRegistry)
+ heapObjects = GetOrRegisterGauge("system/memory/objects", DefaultRegistry)
diskReads = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry)
diskReadBytes = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry)
diskReadBytesCounter = GetOrRegisterCounter("system/disk/readbytes", DefaultRegistry)
@@ -93,34 +162,43 @@ func CollectProcessMetrics(refresh time.Duration) {
diskWriteBytes = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry)
diskWriteBytesCounter = GetOrRegisterCounter("system/disk/writebytes", DefaultRegistry)
)
- // Iterate loading the different stats and updating the meters
- for i := 1; ; i++ {
- location1 := i % 2
- location2 := (i - 1) % 2
-
- ReadCPUStats(cpuStats[location1])
- cpuSysLoad.Update((cpuStats[location1].GlobalTime - cpuStats[location2].GlobalTime) / refreshFreq)
- cpuSysWait.Update((cpuStats[location1].GlobalWait - cpuStats[location2].GlobalWait) / refreshFreq)
- cpuProcLoad.Update((cpuStats[location1].LocalTime - cpuStats[location2].LocalTime) / refreshFreq)
+
+ // Iterate loading the different stats and updating the meters.
+ now, prev := 0, 1
+ for ; ; now, prev = prev, now {
+ // CPU
+ ReadCPUStats(&cpustats[now])
+ cpuSysLoad.Update((cpustats[now].GlobalTime - cpustats[prev].GlobalTime) / refreshFreq)
+ cpuSysWait.Update((cpustats[now].GlobalWait - cpustats[prev].GlobalWait) / refreshFreq)
+ cpuProcLoad.Update((cpustats[now].LocalTime - cpustats[prev].LocalTime) / refreshFreq)
+
+ // Threads
cpuThreads.Update(int64(threadCreateProfile.Count()))
- cpuGoroutines.Update(int64(runtime.NumGoroutine()))
-
- runtime.ReadMemStats(memstats[location1])
- memPauses.Mark(int64(memstats[location1].PauseTotalNs - memstats[location2].PauseTotalNs))
- memAllocs.Mark(int64(memstats[location1].Mallocs - memstats[location2].Mallocs))
- memFrees.Mark(int64(memstats[location1].Frees - memstats[location2].Frees))
- memHeld.Update(int64(memstats[location1].HeapSys - memstats[location1].HeapReleased))
- memUsed.Update(int64(memstats[location1].Alloc))
-
- if ReadDiskStats(diskstats[location1]) == nil {
- diskReads.Mark(diskstats[location1].ReadCount - diskstats[location2].ReadCount)
- diskReadBytes.Mark(diskstats[location1].ReadBytes - diskstats[location2].ReadBytes)
- diskWrites.Mark(diskstats[location1].WriteCount - diskstats[location2].WriteCount)
- diskWriteBytes.Mark(diskstats[location1].WriteBytes - diskstats[location2].WriteBytes)
-
- diskReadBytesCounter.Inc(diskstats[location1].ReadBytes - diskstats[location2].ReadBytes)
- diskWriteBytesCounter.Inc(diskstats[location1].WriteBytes - diskstats[location2].WriteBytes)
+
+ // Go runtime metrics
+ readRuntimeStats(&rstats[now])
+
+ cpuGoroutines.Update(int64(rstats[now].Goroutines))
+ cpuSchedLatency.update(rstats[now].SchedLatency)
+ memPauses.update(rstats[now].GCPauses)
+
+ memAllocs.Mark(int64(rstats[now].GCAllocBytes - rstats[prev].GCAllocBytes))
+ memFrees.Mark(int64(rstats[now].GCFreedBytes - rstats[prev].GCFreedBytes))
+
+ memTotal.Update(int64(rstats[now].MemTotal))
+ heapUsed.Update(int64(rstats[now].MemTotal - rstats[now].HeapUnused - rstats[now].HeapFree - rstats[now].HeapReleased))
+ heapObjects.Update(int64(rstats[now].HeapObjects))
+
+ // Disk
+ if ReadDiskStats(&diskstats[now]) == nil {
+ diskReads.Mark(diskstats[now].ReadCount - diskstats[prev].ReadCount)
+ diskReadBytes.Mark(diskstats[now].ReadBytes - diskstats[prev].ReadBytes)
+ diskWrites.Mark(diskstats[now].WriteCount - diskstats[prev].WriteCount)
+ diskWriteBytes.Mark(diskstats[now].WriteBytes - diskstats[prev].WriteBytes)
+ diskReadBytesCounter.Inc(diskstats[now].ReadBytes - diskstats[prev].ReadBytes)
+ diskWriteBytesCounter.Inc(diskstats[now].WriteBytes - diskstats[prev].WriteBytes)
}
+
time.Sleep(refresh)
}
}
diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go
index 029c99870e..e3fde1ea62 100644
--- a/metrics/metrics_test.go
+++ b/metrics/metrics_test.go
@@ -2,8 +2,6 @@ package metrics
import (
"fmt"
- "io"
- "log"
"sync"
"testing"
"time"
@@ -11,11 +9,11 @@ import (
const FANOUT = 128
-// Stop the compiler from complaining during debugging.
-var (
- _ = io.Discard
- _ = log.LstdFlags
-)
+func TestReadRuntimeValues(t *testing.T) {
+ var v runtimeStats
+ readRuntimeStats(&v)
+ t.Logf("%+v", v)
+}
func BenchmarkMetrics(b *testing.B) {
r := NewRegistry()
@@ -26,7 +24,6 @@ func BenchmarkMetrics(b *testing.B) {
m := NewRegisteredMeter("meter", r)
t := NewRegisteredTimer("timer", r)
RegisterDebugGCStats(r)
- RegisterRuntimeMemStats(r)
b.ResetTimer()
ch := make(chan bool)
@@ -48,24 +45,6 @@ func BenchmarkMetrics(b *testing.B) {
}()
//*/
- wgR := &sync.WaitGroup{}
- //*
- wgR.Add(1)
- go func() {
- defer wgR.Done()
- //log.Println("go CaptureRuntimeMemStats")
- for {
- select {
- case <-ch:
- //log.Println("done CaptureRuntimeMemStats")
- return
- default:
- CaptureRuntimeMemStatsOnce(r)
- }
- }
- }()
- //*/
-
wgW := &sync.WaitGroup{}
/*
wgW.Add(1)
@@ -104,7 +83,6 @@ func BenchmarkMetrics(b *testing.B) {
wg.Wait()
close(ch)
wgD.Wait()
- wgR.Wait()
wgW.Wait()
}
diff --git a/metrics/runtime.go b/metrics/runtime.go
deleted file mode 100644
index 9450c479ba..0000000000
--- a/metrics/runtime.go
+++ /dev/null
@@ -1,212 +0,0 @@
-package metrics
-
-import (
- "runtime"
- "runtime/pprof"
- "time"
-)
-
-var (
- memStats runtime.MemStats
- runtimeMetrics struct {
- MemStats struct {
- Alloc Gauge
- BuckHashSys Gauge
- DebugGC Gauge
- EnableGC Gauge
- Frees Gauge
- HeapAlloc Gauge
- HeapIdle Gauge
- HeapInuse Gauge
- HeapObjects Gauge
- HeapReleased Gauge
- HeapSys Gauge
- LastGC Gauge
- Lookups Gauge
- Mallocs Gauge
- MCacheInuse Gauge
- MCacheSys Gauge
- MSpanInuse Gauge
- MSpanSys Gauge
- NextGC Gauge
- NumGC Gauge
- GCCPUFraction GaugeFloat64
- PauseNs Histogram
- PauseTotalNs Gauge
- StackInuse Gauge
- StackSys Gauge
- Sys Gauge
- TotalAlloc Gauge
- }
- NumCgoCall Gauge
- NumGoroutine Gauge
- NumThread Gauge
- ReadMemStats Timer
- }
- frees uint64
- lookups uint64
- mallocs uint64
- numGC uint32
- numCgoCalls int64
-
- threadCreateProfile = pprof.Lookup("threadcreate")
-)
-
-// Capture new values for the Go runtime statistics exported in
-// runtime.MemStats. This is designed to be called as a goroutine.
-func CaptureRuntimeMemStats(r Registry, d time.Duration) {
- for range time.Tick(d) {
- CaptureRuntimeMemStatsOnce(r)
- }
-}
-
-// Capture new values for the Go runtime statistics exported in
-// runtime.MemStats. This is designed to be called in a background
-// goroutine. Giving a registry which has not been given to
-// RegisterRuntimeMemStats will panic.
-//
-// Be very careful with this because runtime.ReadMemStats calls the C
-// functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld()
-// and that last one does what it says on the tin.
-func CaptureRuntimeMemStatsOnce(r Registry) {
- t := time.Now()
- runtime.ReadMemStats(&memStats) // This takes 50-200us.
- runtimeMetrics.ReadMemStats.UpdateSince(t)
-
- runtimeMetrics.MemStats.Alloc.Update(int64(memStats.Alloc))
- runtimeMetrics.MemStats.BuckHashSys.Update(int64(memStats.BuckHashSys))
- if memStats.DebugGC {
- runtimeMetrics.MemStats.DebugGC.Update(1)
- } else {
- runtimeMetrics.MemStats.DebugGC.Update(0)
- }
- if memStats.EnableGC {
- runtimeMetrics.MemStats.EnableGC.Update(1)
- } else {
- runtimeMetrics.MemStats.EnableGC.Update(0)
- }
-
- runtimeMetrics.MemStats.Frees.Update(int64(memStats.Frees - frees))
- runtimeMetrics.MemStats.HeapAlloc.Update(int64(memStats.HeapAlloc))
- runtimeMetrics.MemStats.HeapIdle.Update(int64(memStats.HeapIdle))
- runtimeMetrics.MemStats.HeapInuse.Update(int64(memStats.HeapInuse))
- runtimeMetrics.MemStats.HeapObjects.Update(int64(memStats.HeapObjects))
- runtimeMetrics.MemStats.HeapReleased.Update(int64(memStats.HeapReleased))
- runtimeMetrics.MemStats.HeapSys.Update(int64(memStats.HeapSys))
- runtimeMetrics.MemStats.LastGC.Update(int64(memStats.LastGC))
- runtimeMetrics.MemStats.Lookups.Update(int64(memStats.Lookups - lookups))
- runtimeMetrics.MemStats.Mallocs.Update(int64(memStats.Mallocs - mallocs))
- runtimeMetrics.MemStats.MCacheInuse.Update(int64(memStats.MCacheInuse))
- runtimeMetrics.MemStats.MCacheSys.Update(int64(memStats.MCacheSys))
- runtimeMetrics.MemStats.MSpanInuse.Update(int64(memStats.MSpanInuse))
- runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys))
- runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC))
- runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC))
- runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats))
-
- //
- i := numGC % uint32(len(memStats.PauseNs))
- ii := memStats.NumGC % uint32(len(memStats.PauseNs))
- if memStats.NumGC-numGC >= uint32(len(memStats.PauseNs)) {
- for i = 0; i < uint32(len(memStats.PauseNs)); i++ {
- runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
- }
- } else {
- if i > ii {
- for ; i < uint32(len(memStats.PauseNs)); i++ {
- runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
- }
- i = 0
- }
- for ; i < ii; i++ {
- runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
- }
- }
- frees = memStats.Frees
- lookups = memStats.Lookups
- mallocs = memStats.Mallocs
- numGC = memStats.NumGC
-
- runtimeMetrics.MemStats.PauseTotalNs.Update(int64(memStats.PauseTotalNs))
- runtimeMetrics.MemStats.StackInuse.Update(int64(memStats.StackInuse))
- runtimeMetrics.MemStats.StackSys.Update(int64(memStats.StackSys))
- runtimeMetrics.MemStats.Sys.Update(int64(memStats.Sys))
- runtimeMetrics.MemStats.TotalAlloc.Update(int64(memStats.TotalAlloc))
-
- currentNumCgoCalls := numCgoCall()
- runtimeMetrics.NumCgoCall.Update(currentNumCgoCalls - numCgoCalls)
- numCgoCalls = currentNumCgoCalls
-
- runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine()))
-
- runtimeMetrics.NumThread.Update(int64(threadCreateProfile.Count()))
-}
-
-// Register runtimeMetrics for the Go runtime statistics exported in runtime and
-// specifically runtime.MemStats. The runtimeMetrics are named by their
-// fully-qualified Go symbols, i.e. runtime.MemStats.Alloc.
-func RegisterRuntimeMemStats(r Registry) {
- runtimeMetrics.MemStats.Alloc = NewGauge()
- runtimeMetrics.MemStats.BuckHashSys = NewGauge()
- runtimeMetrics.MemStats.DebugGC = NewGauge()
- runtimeMetrics.MemStats.EnableGC = NewGauge()
- runtimeMetrics.MemStats.Frees = NewGauge()
- runtimeMetrics.MemStats.HeapAlloc = NewGauge()
- runtimeMetrics.MemStats.HeapIdle = NewGauge()
- runtimeMetrics.MemStats.HeapInuse = NewGauge()
- runtimeMetrics.MemStats.HeapObjects = NewGauge()
- runtimeMetrics.MemStats.HeapReleased = NewGauge()
- runtimeMetrics.MemStats.HeapSys = NewGauge()
- runtimeMetrics.MemStats.LastGC = NewGauge()
- runtimeMetrics.MemStats.Lookups = NewGauge()
- runtimeMetrics.MemStats.Mallocs = NewGauge()
- runtimeMetrics.MemStats.MCacheInuse = NewGauge()
- runtimeMetrics.MemStats.MCacheSys = NewGauge()
- runtimeMetrics.MemStats.MSpanInuse = NewGauge()
- runtimeMetrics.MemStats.MSpanSys = NewGauge()
- runtimeMetrics.MemStats.NextGC = NewGauge()
- runtimeMetrics.MemStats.NumGC = NewGauge()
- runtimeMetrics.MemStats.GCCPUFraction = NewGaugeFloat64()
- runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015))
- runtimeMetrics.MemStats.PauseTotalNs = NewGauge()
- runtimeMetrics.MemStats.StackInuse = NewGauge()
- runtimeMetrics.MemStats.StackSys = NewGauge()
- runtimeMetrics.MemStats.Sys = NewGauge()
- runtimeMetrics.MemStats.TotalAlloc = NewGauge()
- runtimeMetrics.NumCgoCall = NewGauge()
- runtimeMetrics.NumGoroutine = NewGauge()
- runtimeMetrics.NumThread = NewGauge()
- runtimeMetrics.ReadMemStats = NewTimer()
-
- r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc)
- r.Register("runtime.MemStats.BuckHashSys", runtimeMetrics.MemStats.BuckHashSys)
- r.Register("runtime.MemStats.DebugGC", runtimeMetrics.MemStats.DebugGC)
- r.Register("runtime.MemStats.EnableGC", runtimeMetrics.MemStats.EnableGC)
- r.Register("runtime.MemStats.Frees", runtimeMetrics.MemStats.Frees)
- r.Register("runtime.MemStats.HeapAlloc", runtimeMetrics.MemStats.HeapAlloc)
- r.Register("runtime.MemStats.HeapIdle", runtimeMetrics.MemStats.HeapIdle)
- r.Register("runtime.MemStats.HeapInuse", runtimeMetrics.MemStats.HeapInuse)
- r.Register("runtime.MemStats.HeapObjects", runtimeMetrics.MemStats.HeapObjects)
- r.Register("runtime.MemStats.HeapReleased", runtimeMetrics.MemStats.HeapReleased)
- r.Register("runtime.MemStats.HeapSys", runtimeMetrics.MemStats.HeapSys)
- r.Register("runtime.MemStats.LastGC", runtimeMetrics.MemStats.LastGC)
- r.Register("runtime.MemStats.Lookups", runtimeMetrics.MemStats.Lookups)
- r.Register("runtime.MemStats.Mallocs", runtimeMetrics.MemStats.Mallocs)
- r.Register("runtime.MemStats.MCacheInuse", runtimeMetrics.MemStats.MCacheInuse)
- r.Register("runtime.MemStats.MCacheSys", runtimeMetrics.MemStats.MCacheSys)
- r.Register("runtime.MemStats.MSpanInuse", runtimeMetrics.MemStats.MSpanInuse)
- r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys)
- r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC)
- r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC)
- r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction)
- r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs)
- r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs)
- r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse)
- r.Register("runtime.MemStats.StackSys", runtimeMetrics.MemStats.StackSys)
- r.Register("runtime.MemStats.Sys", runtimeMetrics.MemStats.Sys)
- r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc)
- r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall)
- r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine)
- r.Register("runtime.NumThread", runtimeMetrics.NumThread)
- r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats)
-}
diff --git a/metrics/runtime_cgo.go b/metrics/runtime_cgo.go
deleted file mode 100644
index 4307ebdba6..0000000000
--- a/metrics/runtime_cgo.go
+++ /dev/null
@@ -1,10 +0,0 @@
-//go:build cgo && !appengine && !js
-// +build cgo,!appengine,!js
-
-package metrics
-
-import "runtime"
-
-func numCgoCall() int64 {
- return runtime.NumCgoCall()
-}
diff --git a/metrics/runtime_gccpufraction.go b/metrics/runtime_gccpufraction.go
deleted file mode 100644
index 28cd44752b..0000000000
--- a/metrics/runtime_gccpufraction.go
+++ /dev/null
@@ -1,10 +0,0 @@
-//go:build go1.5
-// +build go1.5
-
-package metrics
-
-import "runtime"
-
-func gcCPUFraction(memStats *runtime.MemStats) float64 {
- return memStats.GCCPUFraction
-}
diff --git a/metrics/runtime_no_cgo.go b/metrics/runtime_no_cgo.go
deleted file mode 100644
index 1799bef63b..0000000000
--- a/metrics/runtime_no_cgo.go
+++ /dev/null
@@ -1,8 +0,0 @@
-//go:build !cgo || appengine || js
-// +build !cgo appengine js
-
-package metrics
-
-func numCgoCall() int64 {
- return 0
-}
diff --git a/metrics/runtime_no_gccpufraction.go b/metrics/runtime_no_gccpufraction.go
deleted file mode 100644
index af1a4b63c8..0000000000
--- a/metrics/runtime_no_gccpufraction.go
+++ /dev/null
@@ -1,10 +0,0 @@
-//go:build !go1.5
-// +build !go1.5
-
-package metrics
-
-import "runtime"
-
-func gcCPUFraction(memStats *runtime.MemStats) float64 {
- return 0
-}
diff --git a/metrics/runtime_test.go b/metrics/runtime_test.go
deleted file mode 100644
index f85f7868f7..0000000000
--- a/metrics/runtime_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package metrics
-
-import (
- "runtime"
- "testing"
- "time"
-)
-
-func BenchmarkRuntimeMemStats(b *testing.B) {
- r := NewRegistry()
- RegisterRuntimeMemStats(r)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- CaptureRuntimeMemStatsOnce(r)
- }
-}
-
-func TestRuntimeMemStats(t *testing.T) {
- r := NewRegistry()
- RegisterRuntimeMemStats(r)
- CaptureRuntimeMemStatsOnce(r)
- zero := runtimeMetrics.MemStats.PauseNs.Count() // Get a "zero" since GC may have run before these tests.
- runtime.GC()
- CaptureRuntimeMemStatsOnce(r)
- if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 1 {
- t.Fatal(count - zero)
- }
- runtime.GC()
- runtime.GC()
- CaptureRuntimeMemStatsOnce(r)
- if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 3 {
- t.Fatal(count - zero)
- }
- for i := 0; i < 256; i++ {
- runtime.GC()
- }
- CaptureRuntimeMemStatsOnce(r)
- if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 259 {
- t.Fatal(count - zero)
- }
- for i := 0; i < 257; i++ {
- runtime.GC()
- }
- CaptureRuntimeMemStatsOnce(r)
- if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 515 { // We lost one because there were too many GCs between captures.
- t.Fatal(count - zero)
- }
-}
-
-func TestRuntimeMemStatsNumThread(t *testing.T) {
- r := NewRegistry()
- RegisterRuntimeMemStats(r)
- CaptureRuntimeMemStatsOnce(r)
-
- if value := runtimeMetrics.NumThread.Value(); value < 1 {
- t.Fatalf("got NumThread: %d, wanted at least 1", value)
- }
-}
-
-func TestRuntimeMemStatsBlocking(t *testing.T) {
- if g := runtime.GOMAXPROCS(0); g < 2 {
- t.Skipf("skipping TestRuntimeMemStatsBlocking with GOMAXPROCS=%d\n", g)
- }
- ch := make(chan int)
- go testRuntimeMemStatsBlocking(ch)
- var memStats runtime.MemStats
- t0 := time.Now()
- runtime.ReadMemStats(&memStats)
- t1 := time.Now()
- t.Log("i++ during runtime.ReadMemStats:", <-ch)
- go testRuntimeMemStatsBlocking(ch)
- d := t1.Sub(t0)
- t.Log(d)
- time.Sleep(d)
- t.Log("i++ during time.Sleep:", <-ch)
-}
-
-func testRuntimeMemStatsBlocking(ch chan int) {
- i := 0
- for {
- select {
- case ch <- i:
- return
- default:
- i++
- }
- }
-}
diff --git a/metrics/runtimehistogram.go b/metrics/runtimehistogram.go
new file mode 100644
index 0000000000..c68939af1e
--- /dev/null
+++ b/metrics/runtimehistogram.go
@@ -0,0 +1,319 @@
+package metrics
+
+import (
+ "math"
+ "runtime/metrics"
+ "sort"
+ "sync/atomic"
+)
+
+func getOrRegisterRuntimeHistogram(name string, scale float64, r Registry) *runtimeHistogram {
+ if r == nil {
+ r = DefaultRegistry
+ }
+ constructor := func() Histogram { return newRuntimeHistogram(scale) }
+ return r.GetOrRegister(name, constructor).(*runtimeHistogram)
+}
+
+// runtimeHistogram wraps a runtime/metrics histogram.
+type runtimeHistogram struct {
+ v atomic.Value
+ scaleFactor float64
+}
+
+func newRuntimeHistogram(scale float64) *runtimeHistogram {
+ h := &runtimeHistogram{scaleFactor: scale}
+ h.update(&metrics.Float64Histogram{})
+ return h
+}
+
+func (h *runtimeHistogram) update(mh *metrics.Float64Histogram) {
+ if mh == nil {
+ // The update value can be nil if the current Go version doesn't support a
+ // requested metric. It's just easier to handle nil here than putting
+ // conditionals everywhere.
+ return
+ }
+
+ s := runtimeHistogramSnapshot{
+ Counts: make([]uint64, len(mh.Counts)),
+ Buckets: make([]float64, len(mh.Buckets)),
+ }
+ copy(s.Counts, mh.Counts)
+ copy(s.Buckets, mh.Buckets)
+ for i, b := range s.Buckets {
+ s.Buckets[i] = b * h.scaleFactor
+ }
+ h.v.Store(&s)
+}
+
+func (h *runtimeHistogram) load() *runtimeHistogramSnapshot {
+ return h.v.Load().(*runtimeHistogramSnapshot)
+}
+
+func (h *runtimeHistogram) Clear() {
+ panic("runtimeHistogram does not support Clear")
+}
+func (h *runtimeHistogram) Update(int64) {
+ panic("runtimeHistogram does not support Update")
+}
+func (h *runtimeHistogram) Sample() Sample {
+ return NilSample{}
+}
+
+// Snapshot returns a non-changing cop of the histogram.
+func (h *runtimeHistogram) Snapshot() Histogram {
+ return h.load()
+}
+
+// Count returns the sample count.
+func (h *runtimeHistogram) Count() int64 {
+ return h.load().Count()
+}
+
+// Mean returns an approximation of the mean.
+func (h *runtimeHistogram) Mean() float64 {
+ return h.load().Mean()
+}
+
+// StdDev approximates the standard deviation of the histogram.
+func (h *runtimeHistogram) StdDev() float64 {
+ return h.load().StdDev()
+}
+
+// Variance approximates the variance of the histogram.
+func (h *runtimeHistogram) Variance() float64 {
+ return h.load().Variance()
+}
+
+// Percentile computes the p'th percentile value.
+func (h *runtimeHistogram) Percentile(p float64) float64 {
+ return h.load().Percentile(p)
+}
+
+// Percentiles computes all requested percentile values.
+func (h *runtimeHistogram) Percentiles(ps []float64) []float64 {
+ return h.load().Percentiles(ps)
+}
+
+// Max returns the highest sample value.
+func (h *runtimeHistogram) Max() int64 {
+ return h.load().Max()
+}
+
+// Min returns the lowest sample value.
+func (h *runtimeHistogram) Min() int64 {
+ return h.load().Min()
+}
+
+// Sum returns the sum of all sample values.
+func (h *runtimeHistogram) Sum() int64 {
+ return h.load().Sum()
+}
+
+type runtimeHistogramSnapshot metrics.Float64Histogram
+
+func (h *runtimeHistogramSnapshot) Clear() {
+ panic("runtimeHistogram does not support Clear")
+}
+func (h *runtimeHistogramSnapshot) Update(int64) {
+ panic("runtimeHistogram does not support Update")
+}
+func (h *runtimeHistogramSnapshot) Sample() Sample {
+ return NilSample{}
+}
+
+func (h *runtimeHistogramSnapshot) Snapshot() Histogram {
+ return h
+}
+
+// Count returns the sample count.
+func (h *runtimeHistogramSnapshot) Count() int64 {
+ var count int64
+ for _, c := range h.Counts {
+ count += int64(c)
+ }
+ return count
+}
+
+// Mean returns an approximation of the mean.
+func (h *runtimeHistogramSnapshot) Mean() float64 {
+ if len(h.Counts) == 0 {
+ return 0
+ }
+ mean, _ := h.mean()
+ return mean
+}
+
+// mean computes the mean and also the total sample count.
+func (h *runtimeHistogramSnapshot) mean() (mean, totalCount float64) {
+ var sum float64
+ for i, c := range h.Counts {
+ midpoint := h.midpoint(i)
+ sum += midpoint * float64(c)
+ totalCount += float64(c)
+ }
+ return sum / totalCount, totalCount
+}
+
+func (h *runtimeHistogramSnapshot) midpoint(bucket int) float64 {
+ high := h.Buckets[bucket+1]
+ low := h.Buckets[bucket]
+ if math.IsInf(high, 1) {
+ // The edge of the highest bucket can be +Inf, and it's supposed to mean that this
+ // bucket contains all remaining samples > low. We can't get the middle of an
+ // infinite range, so just return the lower bound of this bucket instead.
+ return low
+ }
+ if math.IsInf(low, -1) {
+ // Similarly, we can get -Inf in the left edge of the lowest bucket,
+ // and it means the bucket contains all remaining values < high.
+ return high
+ }
+ return (low + high) / 2
+}
+
+// StdDev approximates the standard deviation of the histogram.
+func (h *runtimeHistogramSnapshot) StdDev() float64 {
+ return math.Sqrt(h.Variance())
+}
+
+// Variance approximates the variance of the histogram.
+func (h *runtimeHistogramSnapshot) Variance() float64 {
+ if len(h.Counts) == 0 {
+ return 0
+ }
+
+ mean, totalCount := h.mean()
+ if totalCount <= 1 {
+ // There is no variance when there are zero or one items.
+ return 0
+ }
+
+ var sum float64
+ for i, c := range h.Counts {
+ midpoint := h.midpoint(i)
+ d := midpoint - mean
+ sum += float64(c) * (d * d)
+ }
+ return sum / (totalCount - 1)
+}
+
+// Percentile computes the p'th percentile value.
+func (h *runtimeHistogramSnapshot) Percentile(p float64) float64 {
+ threshold := float64(h.Count()) * p
+ values := [1]float64{threshold}
+ h.computePercentiles(values[:])
+ return values[0]
+}
+
+// Percentiles computes all requested percentile values.
+func (h *runtimeHistogramSnapshot) Percentiles(ps []float64) []float64 {
+ // Compute threshold values. We need these to be sorted
+ // for the percentile computation, but restore the original
+ // order later, so keep the indexes as well.
+ count := float64(h.Count())
+ thresholds := make([]float64, len(ps))
+ indexes := make([]int, len(ps))
+ for i, percentile := range ps {
+ thresholds[i] = count * math.Max(0, math.Min(1.0, percentile))
+ indexes[i] = i
+ }
+ sort.Sort(floatsAscendingKeepingIndex{thresholds, indexes})
+
+ // Now compute. The result is stored back into the thresholds slice.
+ h.computePercentiles(thresholds)
+
+ // Put the result back into the requested order.
+ sort.Sort(floatsByIndex{thresholds, indexes})
+ return thresholds
+}
+
+func (h *runtimeHistogramSnapshot) computePercentiles(thresh []float64) {
+ var totalCount float64
+ for i, count := range h.Counts {
+ totalCount += float64(count)
+
+ for len(thresh) > 0 && thresh[0] < totalCount {
+ thresh[0] = h.Buckets[i]
+ thresh = thresh[1:]
+ }
+ if len(thresh) == 0 {
+ return
+ }
+ }
+}
+
+// Note: runtime/metrics.Float64Histogram is a collection of float64s, but the methods
+// below need to return int64 to satisfy the interface. The histogram provided by runtime
+// also doesn't keep track of individual samples, so results are approximated.
+
+// Max returns the highest sample value.
+func (h *runtimeHistogramSnapshot) Max() int64 {
+ for i := len(h.Counts) - 1; i >= 0; i-- {
+ count := h.Counts[i]
+ if count > 0 {
+ edge := h.Buckets[i+1]
+ if math.IsInf(edge, 1) {
+ edge = h.Buckets[i]
+ }
+ return int64(math.Ceil(edge))
+ }
+ }
+ return 0
+}
+
+// Min returns the lowest sample value.
+func (h *runtimeHistogramSnapshot) Min() int64 {
+ for i, count := range h.Counts {
+ if count > 0 {
+ return int64(math.Floor(h.Buckets[i]))
+ }
+ }
+ return 0
+}
+
+// Sum returns the sum of all sample values.
+func (h *runtimeHistogramSnapshot) Sum() int64 {
+ var sum float64
+ for i := range h.Counts {
+ sum += h.Buckets[i] * float64(h.Counts[i])
+ }
+ return int64(math.Ceil(sum))
+}
+
+type floatsAscendingKeepingIndex struct {
+ values []float64
+ indexes []int
+}
+
+func (s floatsAscendingKeepingIndex) Len() int {
+ return len(s.values)
+}
+
+func (s floatsAscendingKeepingIndex) Less(i, j int) bool {
+ return s.values[i] < s.values[j]
+}
+
+func (s floatsAscendingKeepingIndex) Swap(i, j int) {
+ s.values[i], s.values[j] = s.values[j], s.values[i]
+ s.indexes[i], s.indexes[j] = s.indexes[j], s.indexes[i]
+}
+
+type floatsByIndex struct {
+ values []float64
+ indexes []int
+}
+
+func (s floatsByIndex) Len() int {
+ return len(s.values)
+}
+
+func (s floatsByIndex) Less(i, j int) bool {
+ return s.indexes[i] < s.indexes[j]
+}
+
+func (s floatsByIndex) Swap(i, j int) {
+ s.values[i], s.values[j] = s.values[j], s.values[i]
+ s.indexes[i], s.indexes[j] = s.indexes[j], s.indexes[i]
+}
diff --git a/metrics/runtimehistogram_test.go b/metrics/runtimehistogram_test.go
new file mode 100644
index 0000000000..d53a014383
--- /dev/null
+++ b/metrics/runtimehistogram_test.go
@@ -0,0 +1,133 @@
+package metrics
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "runtime/metrics"
+ "testing"
+)
+
+var _ Histogram = (*runtimeHistogram)(nil)
+
+type runtimeHistogramTest struct {
+ h metrics.Float64Histogram
+
+ Count int64
+ Min int64
+ Max int64
+ Sum int64
+ Mean float64
+ Variance float64
+ StdDev float64
+ Percentiles []float64 // .5 .8 .9 .99 .995
+}
+
+// This test checks the results of statistical functions implemented
+// by runtimeHistogramSnapshot.
+func TestRuntimeHistogramStats(t *testing.T) {
+ tests := []runtimeHistogramTest{
+ 0: {
+ h: metrics.Float64Histogram{
+ Counts: []uint64{},
+ Buckets: []float64{},
+ },
+ Count: 0,
+ Max: 0,
+ Min: 0,
+ Sum: 0,
+ Mean: 0,
+ Variance: 0,
+ StdDev: 0,
+ Percentiles: []float64{0, 0, 0, 0, 0},
+ },
+ 1: {
+ // This checks the case where the highest bucket is +Inf.
+ h: metrics.Float64Histogram{
+ Counts: []uint64{0, 1, 2},
+ Buckets: []float64{0, 0.5, 1, math.Inf(1)},
+ },
+ Count: 3,
+ Max: 1,
+ Min: 0,
+ Sum: 3,
+ Mean: 0.9166666,
+ Percentiles: []float64{1, 1, 1, 1, 1},
+ Variance: 0.020833,
+ StdDev: 0.144433,
+ },
+ 2: {
+ h: metrics.Float64Histogram{
+ Counts: []uint64{8, 6, 3, 1},
+ Buckets: []float64{12, 16, 18, 24, 25},
+ },
+ Count: 18,
+ Max: 25,
+ Min: 12,
+ Sum: 270,
+ Mean: 16.75,
+ Variance: 10.3015,
+ StdDev: 3.2096,
+ Percentiles: []float64{16, 18, 18, 24, 24},
+ },
+ }
+
+ for i, test := range tests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ s := runtimeHistogramSnapshot(test.h)
+
+ if v := s.Count(); v != test.Count {
+ t.Errorf("Count() = %v, want %v", v, test.Count)
+ }
+ if v := s.Min(); v != test.Min {
+ t.Errorf("Min() = %v, want %v", v, test.Min)
+ }
+ if v := s.Max(); v != test.Max {
+ t.Errorf("Max() = %v, want %v", v, test.Max)
+ }
+ if v := s.Sum(); v != test.Sum {
+ t.Errorf("Sum() = %v, want %v", v, test.Sum)
+ }
+ if v := s.Mean(); !approxEqual(v, test.Mean, 0.0001) {
+ t.Errorf("Mean() = %v, want %v", v, test.Mean)
+ }
+ if v := s.Variance(); !approxEqual(v, test.Variance, 0.0001) {
+ t.Errorf("Variance() = %v, want %v", v, test.Variance)
+ }
+ if v := s.StdDev(); !approxEqual(v, test.StdDev, 0.0001) {
+ t.Errorf("StdDev() = %v, want %v", v, test.StdDev)
+ }
+ ps := []float64{.5, .8, .9, .99, .995}
+ if v := s.Percentiles(ps); !reflect.DeepEqual(v, test.Percentiles) {
+ t.Errorf("Percentiles(%v) = %v, want %v", ps, v, test.Percentiles)
+ }
+ })
+ }
+}
+
+func approxEqual(x, y, ε float64) bool {
+ if math.IsInf(x, -1) && math.IsInf(y, -1) {
+ return true
+ }
+ if math.IsInf(x, 1) && math.IsInf(y, 1) {
+ return true
+ }
+ if math.IsNaN(x) && math.IsNaN(y) {
+ return true
+ }
+ return math.Abs(x-y) < ε
+}
+
+// This test verifies that requesting Percentiles in unsorted order
+// returns them in the requested order.
+func TestRuntimeHistogramStatsPercentileOrder(t *testing.T) {
+ p := runtimeHistogramSnapshot{
+ Counts: []uint64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+ Buckets: []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
+ }
+ result := p.Percentiles([]float64{1, 0.2, 0.5, 0.1, 0.2})
+ expected := []float64{10, 2, 5, 1, 2}
+ if !reflect.DeepEqual(result, expected) {
+ t.Fatal("wrong result:", result)
+ }
+}
diff --git a/metrics/sample.go b/metrics/sample.go
index fa2bfb274e..afcaa21184 100644
--- a/metrics/sample.go
+++ b/metrics/sample.go
@@ -41,6 +41,7 @@ type ExpDecaySample struct {
reservoirSize int
t0, t1 time.Time
values *expDecaySampleHeap
+ rand *rand.Rand
}
// NewExpDecaySample constructs a new exponentially-decaying sample with the
@@ -59,6 +60,12 @@ func NewExpDecaySample(reservoirSize int, alpha float64) Sample {
return s
}
+// SetRand sets the random source (useful in tests)
+func (s *ExpDecaySample) SetRand(prng *rand.Rand) Sample {
+ s.rand = prng
+ return s
+}
+
// Clear clears all samples.
func (s *ExpDecaySample) Clear() {
s.mutex.Lock()
@@ -168,8 +175,14 @@ func (s *ExpDecaySample) update(t time.Time, v int64) {
if s.values.Size() == s.reservoirSize {
s.values.Pop()
}
+ var f64 float64
+ if s.rand != nil {
+ f64 = s.rand.Float64()
+ } else {
+ f64 = rand.Float64()
+ }
s.values.Push(expDecaySample{
- k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / rand.Float64(),
+ k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / f64,
v: v,
})
if t.After(s.t1) {
@@ -402,6 +415,7 @@ type UniformSample struct {
mutex sync.Mutex
reservoirSize int
values []int64
+ rand *rand.Rand
}
// NewUniformSample constructs a new uniform sample with the given reservoir
@@ -416,6 +430,12 @@ func NewUniformSample(reservoirSize int) Sample {
}
}
+// SetRand sets the random source (useful in tests)
+func (s *UniformSample) SetRand(prng *rand.Rand) Sample {
+ s.rand = prng
+ return s
+}
+
// Clear clears all samples.
func (s *UniformSample) Clear() {
s.mutex.Lock()
@@ -511,7 +531,12 @@ func (s *UniformSample) Update(v int64) {
if len(s.values) < s.reservoirSize {
s.values = append(s.values, v)
} else {
- r := rand.Int63n(s.count)
+ var r int64
+ if s.rand != nil {
+ r = s.rand.Int63n(s.count)
+ } else {
+ r = rand.Int63n(s.count)
+ }
if r < int64(len(s.values)) {
s.values[int(r)] = v
}
diff --git a/metrics/sample_test.go b/metrics/sample_test.go
index c9168d3e82..3ae128d56f 100644
--- a/metrics/sample_test.go
+++ b/metrics/sample_test.go
@@ -80,7 +80,6 @@ func BenchmarkUniformSample1028(b *testing.B) {
}
func TestExpDecaySample10(t *testing.T) {
- rand.Seed(1)
s := NewExpDecaySample(100, 0.99)
for i := 0; i < 10; i++ {
s.Update(int64(i))
@@ -102,7 +101,6 @@ func TestExpDecaySample10(t *testing.T) {
}
func TestExpDecaySample100(t *testing.T) {
- rand.Seed(1)
s := NewExpDecaySample(1000, 0.01)
for i := 0; i < 100; i++ {
s.Update(int64(i))
@@ -124,7 +122,6 @@ func TestExpDecaySample100(t *testing.T) {
}
func TestExpDecaySample1000(t *testing.T) {
- rand.Seed(1)
s := NewExpDecaySample(100, 0.99)
for i := 0; i < 1000; i++ {
s.Update(int64(i))
@@ -150,7 +147,6 @@ func TestExpDecaySample1000(t *testing.T) {
// The priority becomes +Inf quickly after starting if this is done,
// effectively freezing the set of samples until a rescale step happens.
func TestExpDecaySampleNanosecondRegression(t *testing.T) {
- rand.Seed(1)
s := NewExpDecaySample(100, 0.99)
for i := 0; i < 100; i++ {
s.Update(10)
@@ -183,8 +179,7 @@ func TestExpDecaySampleRescale(t *testing.T) {
func TestExpDecaySampleSnapshot(t *testing.T) {
now := time.Now()
- rand.Seed(1)
- s := NewExpDecaySample(100, 0.99)
+ s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1)))
for i := 1; i <= 10000; i++ {
s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i))
}
@@ -195,8 +190,7 @@ func TestExpDecaySampleSnapshot(t *testing.T) {
func TestExpDecaySampleStatistics(t *testing.T) {
now := time.Now()
- rand.Seed(1)
- s := NewExpDecaySample(100, 0.99)
+ s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1)))
for i := 1; i <= 10000; i++ {
s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i))
}
@@ -204,7 +198,6 @@ func TestExpDecaySampleStatistics(t *testing.T) {
}
func TestUniformSample(t *testing.T) {
- rand.Seed(1)
s := NewUniformSample(100)
for i := 0; i < 1000; i++ {
s.Update(int64(i))
@@ -226,7 +219,6 @@ func TestUniformSample(t *testing.T) {
}
func TestUniformSampleIncludesTail(t *testing.T) {
- rand.Seed(1)
s := NewUniformSample(100)
max := 100
for i := 0; i < max; i++ {
@@ -244,7 +236,7 @@ func TestUniformSampleIncludesTail(t *testing.T) {
}
func TestUniformSampleSnapshot(t *testing.T) {
- s := NewUniformSample(100)
+ s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1)))
for i := 1; i <= 10000; i++ {
s.Update(int64(i))
}
@@ -254,8 +246,7 @@ func TestUniformSampleSnapshot(t *testing.T) {
}
func TestUniformSampleStatistics(t *testing.T) {
- rand.Seed(1)
- s := NewUniformSample(100)
+ s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1)))
for i := 1; i <= 10000; i++ {
s.Update(int64(i))
}
diff --git a/miner/miner.go b/miner/miner.go
index 1e9607a76a..c969aec735 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/event"
@@ -39,12 +40,12 @@ import (
// to offer all the functions here.
type Backend interface {
BlockChain() *core.BlockChain
- TxPool() *core.TxPool
+ TxPool() *txpool.TxPool
}
// Config is the configuration parameters of mining.
type Config struct {
- Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards (default = first account)
+ Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards
Notify []string `toml:",omitempty"` // HTTP URL list to be notified of new work packages (only useful in ethash).
NotifyFull bool `toml:",omitempty"` // Notify with pending block headers instead of work packages
ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner
@@ -53,29 +54,43 @@ type Config struct {
GasPrice *big.Int // Minimum gas price for mining a transaction
Recommit time.Duration // The time interval for miner to re-create mining work.
Noverify bool // Disable remote mining solution verification(only useful in ethash).
+
+ NewPayloadTimeout time.Duration // The maximum time allowance for creating a new payload
+}
+
+// DefaultConfig contains default settings for miner.
+var DefaultConfig = Config{
+ GasCeil: 30000000,
+ GasPrice: big.NewInt(params.GWei),
+
+ // The default recommit time is chosen as two seconds since
+ // consensus-layer usually will wait a half slot of time(6s)
+ // for payload generation. It should be enough for Geth to
+ // run 3 rounds.
+ Recommit: 2 * time.Second,
+ NewPayloadTimeout: 2 * time.Second,
}
// Miner creates blocks and searches for proof-of-work values.
type Miner struct {
- mux *event.TypeMux
- worker *worker
- coinbase common.Address
- eth Backend
- engine consensus.Engine
- exitCh chan struct{}
- startCh chan common.Address
- stopCh chan struct{}
+ mux *event.TypeMux
+ eth Backend
+ engine consensus.Engine
+ exitCh chan struct{}
+ startCh chan struct{}
+ stopCh chan struct{}
+ worker *worker
wg sync.WaitGroup
}
func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(header *types.Header) bool) *Miner {
miner := &Miner{
- eth: eth,
mux: mux,
+ eth: eth,
engine: engine,
exitCh: make(chan struct{}),
- startCh: make(chan common.Address),
+ startCh: make(chan struct{}),
stopCh: make(chan struct{}),
worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true),
}
@@ -122,20 +137,17 @@ func (miner *Miner) update() {
case downloader.FailedEvent:
canStart = true
if shouldStart {
- miner.SetEtherbase(miner.coinbase)
miner.worker.start()
}
case downloader.DoneEvent:
canStart = true
if shouldStart {
- miner.SetEtherbase(miner.coinbase)
miner.worker.start()
}
// Stop reacting to downloader events
events.Unsubscribe()
}
- case addr := <-miner.startCh:
- miner.SetEtherbase(addr)
+ case <-miner.startCh:
if canStart {
miner.worker.start()
}
@@ -150,8 +162,8 @@ func (miner *Miner) update() {
}
}
-func (miner *Miner) Start(coinbase common.Address) {
- miner.startCh <- coinbase
+func (miner *Miner) Start() {
+ miner.startCh <- struct{}{}
}
func (miner *Miner) Stop() {
@@ -207,7 +219,6 @@ func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
}
func (miner *Miner) SetEtherbase(addr common.Address) {
- miner.coinbase = addr
miner.worker.setEtherbase(addr)
}
@@ -240,26 +251,7 @@ func (miner *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscript
return miner.worker.pendingLogsFeed.Subscribe(ch)
}
-// GetSealingBlockAsync requests to generate a sealing block according to the
-// given parameters. Regardless of whether the generation is successful or not,
-// there is always a result that will be returned through the result channel.
-// The difference is that if the execution fails, the returned result is nil
-// and the concrete error is dropped silently.
-func (miner *Miner) GetSealingBlockAsync(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, noTxs bool) (chan *types.Block, error) {
- resCh, _, err := miner.worker.getSealingBlock(parent, timestamp, coinbase, random, noTxs)
- if err != nil {
- return nil, err
- }
- return resCh, nil
-}
-
-// GetSealingBlockSync creates a sealing block according to the given parameters.
-// If the generation is failed or the underlying work is already closed, an error
-// will be returned.
-func (miner *Miner) GetSealingBlockSync(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, noTxs bool) (*types.Block, error) {
- resCh, errCh, err := miner.worker.getSealingBlock(parent, timestamp, coinbase, random, noTxs)
- if err != nil {
- return nil, err
- }
- return <-resCh, <-errCh
+// BuildPayload builds the payload according to the provided parameters.
+func (miner *Miner) BuildPayload(args *BuildPayloadArgs) (*Payload, error) {
+ return miner.worker.buildPayload(args)
}
diff --git a/miner/miner_test.go b/miner/miner_test.go
index 5bf344fd70..2e7682acd3 100644
--- a/miner/miner_test.go
+++ b/miner/miner_test.go
@@ -27,20 +27,20 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/trie"
)
type mockBackend struct {
bc *core.BlockChain
- txPool *core.TxPool
+ txPool *txpool.TxPool
}
-func NewMockBackend(bc *core.BlockChain, txPool *core.TxPool) *mockBackend {
+func NewMockBackend(bc *core.BlockChain, txPool *txpool.TxPool) *mockBackend {
return &mockBackend{
bc: bc,
txPool: txPool,
@@ -51,7 +51,7 @@ func (m *mockBackend) BlockChain() *core.BlockChain {
return m.bc
}
-func (m *mockBackend) TxPool() *core.TxPool {
+func (m *mockBackend) TxPool() *txpool.TxPool {
return m.txPool
}
@@ -86,7 +86,8 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent)
func TestMiner(t *testing.T) {
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
- miner.Start(common.HexToAddress("0x12345"))
+
+ miner.Start()
waitForMiningState(t, miner, true)
// Start the downloader
mux.Post(downloader.StartEvent{})
@@ -114,7 +115,8 @@ func TestMiner(t *testing.T) {
func TestMinerDownloaderFirstFails(t *testing.T) {
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
- miner.Start(common.HexToAddress("0x12345"))
+
+ miner.Start()
waitForMiningState(t, miner, true)
// Start the downloader
mux.Post(downloader.StartEvent{})
@@ -146,7 +148,8 @@ func TestMinerDownloaderFirstFails(t *testing.T) {
func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
- miner.Start(common.HexToAddress("0x12345"))
+
+ miner.Start()
waitForMiningState(t, miner, true)
// Start the downloader
mux.Post(downloader.StartEvent{})
@@ -159,7 +162,7 @@ func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
miner.Stop()
waitForMiningState(t, miner, false)
- miner.Start(common.HexToAddress("0x678910"))
+ miner.Start()
waitForMiningState(t, miner, true)
miner.Stop()
@@ -170,13 +173,13 @@ func TestStartWhileDownload(t *testing.T) {
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
waitForMiningState(t, miner, false)
- miner.Start(common.HexToAddress("0x12345"))
+ miner.Start()
waitForMiningState(t, miner, true)
// Stop the downloader and wait for the update loop to run
mux.Post(downloader.StartEvent{})
waitForMiningState(t, miner, false)
// Starting the miner after the downloader should not work
- miner.Start(common.HexToAddress("0x12345"))
+ miner.Start()
waitForMiningState(t, miner, false)
}
@@ -184,7 +187,7 @@ func TestStartStopMiner(t *testing.T) {
miner, _, cleanup := createMiner(t)
defer cleanup(false)
waitForMiningState(t, miner, false)
- miner.Start(common.HexToAddress("0x12345"))
+ miner.Start()
waitForMiningState(t, miner, true)
miner.Stop()
waitForMiningState(t, miner, false)
@@ -194,7 +197,7 @@ func TestCloseMiner(t *testing.T) {
miner, _, cleanup := createMiner(t)
defer cleanup(true)
waitForMiningState(t, miner, false)
- miner.Start(common.HexToAddress("0x12345"))
+ miner.Start()
waitForMiningState(t, miner, true)
// Terminate the miner and wait for the update loop to run
miner.Close()
@@ -206,21 +209,21 @@ func TestCloseMiner(t *testing.T) {
func TestMinerSetEtherbase(t *testing.T) {
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
- // Start with a 'bad' mining address
- miner.Start(common.HexToAddress("0xdead"))
+ miner.Start()
waitForMiningState(t, miner, true)
// Start the downloader
mux.Post(downloader.StartEvent{})
waitForMiningState(t, miner, false)
// Now user tries to configure proper mining address
- miner.Start(common.HexToAddress("0x1337"))
+ miner.Start()
// Stop the downloader and wait for the update loop to run
mux.Post(downloader.DoneEvent{})
-
waitForMiningState(t, miner, true)
- // The miner should now be using the good address
- if got, exp := miner.coinbase, common.HexToAddress("0x1337"); got != exp {
- t.Fatalf("Wrong coinbase, got %x expected %x", got, exp)
+
+ coinbase := common.HexToAddress("0xdeedbeef")
+ miner.SetEtherbase(coinbase)
+ if addr := miner.worker.etherbase(); addr != coinbase {
+ t.Fatalf("Unexpected etherbase want %x got %x", coinbase, addr)
}
}
@@ -246,24 +249,23 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) {
Etherbase: common.HexToAddress("123456789"),
}
// Create chainConfig
- memdb := memorydb.New()
- chainDB := rawdb.NewDatabase(memdb)
+ chainDB := rawdb.NewMemoryDatabase()
genesis := core.DeveloperGenesisBlock(15, 11_500_000, common.HexToAddress("12345"))
- chainConfig, _, err := core.SetupGenesisBlock(chainDB, genesis)
+ chainConfig, _, err := core.SetupGenesisBlock(chainDB, trie.NewDatabase(chainDB), genesis)
if err != nil {
t.Fatalf("can't create new chain config: %v", err)
}
// Create consensus engine
engine := clique.New(chainConfig.Clique, chainDB)
// Create Ethereum backend
- bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil)
+ bc, err := core.NewBlockChain(chainDB, nil, genesis, nil, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("can't create new chain %v", err)
}
statedb, _ := state.New(common.Hash{}, state.NewDatabase(chainDB), nil)
blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)}
- pool := core.NewTxPool(testTxPoolConfig, chainConfig, blockchain)
+ pool := txpool.NewTxPool(testTxPoolConfig, chainConfig, blockchain)
backend := NewMockBackend(bc, pool)
// Create event Mux
mux := new(event.TypeMux)
diff --git a/miner/payload_building.go b/miner/payload_building.go
new file mode 100644
index 0000000000..f84d908e86
--- /dev/null
+++ b/miner/payload_building.go
@@ -0,0 +1,198 @@
+// 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 miner
+
+import (
+ "crypto/sha256"
+ "encoding/binary"
+ "math/big"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/beacon/engine"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// BuildPayloadArgs contains the provided parameters for building payload.
+// Check engine-api specification for more details.
+// https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#payloadattributesv1
+type BuildPayloadArgs struct {
+ Parent common.Hash // The parent block to build payload on top
+ Timestamp uint64 // The provided timestamp of generated payload
+ FeeRecipient common.Address // The provided recipient address for collecting transaction fee
+ Random common.Hash // The provided randomness value
+ Withdrawals types.Withdrawals // The provided withdrawals
+}
+
+// Id computes an 8-byte identifier by hashing the components of the payload arguments.
+func (args *BuildPayloadArgs) Id() engine.PayloadID {
+ // Hash
+ hasher := sha256.New()
+ hasher.Write(args.Parent[:])
+ binary.Write(hasher, binary.BigEndian, args.Timestamp)
+ hasher.Write(args.Random[:])
+ hasher.Write(args.FeeRecipient[:])
+ rlp.Encode(hasher, args.Withdrawals)
+ var out engine.PayloadID
+ copy(out[:], hasher.Sum(nil)[:8])
+ return out
+}
+
+// Payload wraps the built payload(block waiting for sealing). According to the
+// engine-api specification, EL should build the initial version of the payload
+// which has an empty transaction set and then keep update it in order to maximize
+// the revenue. Therefore, the empty-block here is always available and full-block
+// will be set/updated afterwards.
+type Payload struct {
+ id engine.PayloadID
+ empty *types.Block
+ full *types.Block
+ fullFees *big.Int
+ stop chan struct{}
+ lock sync.Mutex
+ cond *sync.Cond
+}
+
+// newPayload initializes the payload object.
+func newPayload(empty *types.Block, id engine.PayloadID) *Payload {
+ payload := &Payload{
+ id: id,
+ empty: empty,
+ stop: make(chan struct{}),
+ }
+ log.Info("Starting work on payload", "id", payload.id)
+ payload.cond = sync.NewCond(&payload.lock)
+ return payload
+}
+
+// update updates the full-block with latest built version.
+func (payload *Payload) update(block *types.Block, fees *big.Int, elapsed time.Duration) {
+ payload.lock.Lock()
+ defer payload.lock.Unlock()
+
+ select {
+ case <-payload.stop:
+ return // reject stale update
+ default:
+ }
+ // Ensure the newly provided full block has a higher transaction fee.
+ // In post-merge stage, there is no uncle reward anymore and transaction
+ // fee(apart from the mev revenue) is the only indicator for comparison.
+ if payload.full == nil || fees.Cmp(payload.fullFees) > 0 {
+ payload.full = block
+ payload.fullFees = fees
+
+ feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether))
+ log.Info("Updated payload", "id", payload.id, "number", block.NumberU64(), "hash", block.Hash(),
+ "txs", len(block.Transactions()), "gas", block.GasUsed(), "fees", feesInEther,
+ "root", block.Root(), "elapsed", common.PrettyDuration(elapsed))
+ }
+ payload.cond.Broadcast() // fire signal for notifying full block
+}
+
+// Resolve returns the latest built payload and also terminates the background
+// thread for updating payload. It's safe to be called multiple times.
+func (payload *Payload) Resolve() *engine.ExecutionPayloadEnvelope {
+ payload.lock.Lock()
+ defer payload.lock.Unlock()
+
+ select {
+ case <-payload.stop:
+ default:
+ close(payload.stop)
+ }
+ if payload.full != nil {
+ return engine.BlockToExecutableData(payload.full, payload.fullFees)
+ }
+ return engine.BlockToExecutableData(payload.empty, big.NewInt(0))
+}
+
+// ResolveEmpty is basically identical to Resolve, but it expects empty block only.
+// It's only used in tests.
+func (payload *Payload) ResolveEmpty() *engine.ExecutionPayloadEnvelope {
+ payload.lock.Lock()
+ defer payload.lock.Unlock()
+
+ return engine.BlockToExecutableData(payload.empty, big.NewInt(0))
+}
+
+// ResolveFull is basically identical to Resolve, but it expects full block only.
+// It's only used in tests.
+func (payload *Payload) ResolveFull() *engine.ExecutionPayloadEnvelope {
+ payload.lock.Lock()
+ defer payload.lock.Unlock()
+
+ if payload.full == nil {
+ select {
+ case <-payload.stop:
+ return nil
+ default:
+ }
+ payload.cond.Wait()
+ }
+ return engine.BlockToExecutableData(payload.full, payload.fullFees)
+}
+
+// buildPayload builds the payload according to the provided parameters.
+func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) {
+ // Build the initial version with no transaction included. It should be fast
+ // enough to run. The empty payload can at least make sure there is something
+ // to deliver for not missing slot.
+ empty, _, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, args.Withdrawals, true)
+ if err != nil {
+ return nil, err
+ }
+ // Construct a payload object for return.
+ payload := newPayload(empty, args.Id())
+
+ // Spin up a routine for updating the payload in background. This strategy
+ // can maximum the revenue for including transactions with highest fee.
+ go func() {
+ // Setup the timer for re-building the payload. The initial clock is kept
+ // for triggering process immediately.
+ timer := time.NewTimer(0)
+ defer timer.Stop()
+
+ // Setup the timer for terminating the process if SECONDS_PER_SLOT (12s in
+ // the Mainnet configuration) have passed since the point in time identified
+ // by the timestamp parameter.
+ endTimer := time.NewTimer(time.Second * 12)
+
+ for {
+ select {
+ case <-timer.C:
+ start := time.Now()
+ block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, args.Withdrawals, false)
+ if err == nil {
+ payload.update(block, fees, time.Since(start))
+ }
+ timer.Reset(w.recommit)
+ case <-payload.stop:
+ log.Info("Stopping work on payload", "id", payload.id, "reason", "delivery")
+ return
+ case <-endTimer.C:
+ log.Info("Stopping work on payload", "id", payload.id, "reason", "timeout")
+ return
+ }
+ }
+ }()
+ return payload, nil
+}
diff --git a/miner/payload_building_test.go b/miner/payload_building_test.go
new file mode 100644
index 0000000000..b2f1d68f3c
--- /dev/null
+++ b/miner/payload_building_test.go
@@ -0,0 +1,158 @@
+// 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 miner
+
+import (
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/beacon/engine"
+ "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/types"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+func TestBuildPayload(t *testing.T) {
+ var (
+ db = rawdb.NewMemoryDatabase()
+ recipient = common.HexToAddress("0xdeadbeef")
+ )
+ w, b := newTestWorker(t, params.TestChainConfig, ethash.NewFaker(), db, 0)
+ defer w.close()
+
+ timestamp := uint64(time.Now().Unix())
+ args := &BuildPayloadArgs{
+ Parent: b.chain.CurrentBlock().Hash(),
+ Timestamp: timestamp,
+ Random: common.Hash{},
+ FeeRecipient: recipient,
+ }
+ payload, err := w.buildPayload(args)
+ if err != nil {
+ t.Fatalf("Failed to build payload %v", err)
+ }
+ verify := func(outer *engine.ExecutionPayloadEnvelope, txs int) {
+ payload := outer.ExecutionPayload
+ if payload.ParentHash != b.chain.CurrentBlock().Hash() {
+ t.Fatal("Unexpect parent hash")
+ }
+ if payload.Random != (common.Hash{}) {
+ t.Fatal("Unexpect random value")
+ }
+ if payload.Timestamp != timestamp {
+ t.Fatal("Unexpect timestamp")
+ }
+ if payload.FeeRecipient != recipient {
+ t.Fatal("Unexpect fee recipient")
+ }
+ if len(payload.Transactions) != txs {
+ t.Fatal("Unexpect transaction set")
+ }
+ }
+ empty := payload.ResolveEmpty()
+ verify(empty, 0)
+
+ full := payload.ResolveFull()
+ verify(full, len(pendingTxs))
+
+ // Ensure resolve can be called multiple times and the
+ // result should be unchanged
+ dataOne := payload.Resolve()
+ dataTwo := payload.Resolve()
+ if !reflect.DeepEqual(dataOne, dataTwo) {
+ t.Fatal("Unexpected payload data")
+ }
+}
+
+func TestPayloadId(t *testing.T) {
+ ids := make(map[string]int)
+ for i, tt := range []*BuildPayloadArgs{
+ &BuildPayloadArgs{
+ Parent: common.Hash{1},
+ Timestamp: 1,
+ Random: common.Hash{0x1},
+ FeeRecipient: common.Address{0x1},
+ },
+ // Different parent
+ &BuildPayloadArgs{
+ Parent: common.Hash{2},
+ Timestamp: 1,
+ Random: common.Hash{0x1},
+ FeeRecipient: common.Address{0x1},
+ },
+ // Different timestamp
+ &BuildPayloadArgs{
+ Parent: common.Hash{2},
+ Timestamp: 2,
+ Random: common.Hash{0x1},
+ FeeRecipient: common.Address{0x1},
+ },
+ // Different Random
+ &BuildPayloadArgs{
+ Parent: common.Hash{2},
+ Timestamp: 2,
+ Random: common.Hash{0x2},
+ FeeRecipient: common.Address{0x1},
+ },
+ // Different fee-recipient
+ &BuildPayloadArgs{
+ Parent: common.Hash{2},
+ Timestamp: 2,
+ Random: common.Hash{0x2},
+ FeeRecipient: common.Address{0x2},
+ },
+ // Different withdrawals (non-empty)
+ &BuildPayloadArgs{
+ Parent: common.Hash{2},
+ Timestamp: 2,
+ Random: common.Hash{0x2},
+ FeeRecipient: common.Address{0x2},
+ Withdrawals: []*types.Withdrawal{
+ &types.Withdrawal{
+ Index: 0,
+ Validator: 0,
+ Address: common.Address{},
+ Amount: 0,
+ },
+ },
+ },
+ // Different withdrawals (non-empty)
+ &BuildPayloadArgs{
+ Parent: common.Hash{2},
+ Timestamp: 2,
+ Random: common.Hash{0x2},
+ FeeRecipient: common.Address{0x2},
+ Withdrawals: []*types.Withdrawal{
+ &types.Withdrawal{
+ Index: 2,
+ Validator: 0,
+ Address: common.Address{},
+ Amount: 0,
+ },
+ },
+ },
+ } {
+ id := tt.Id().String()
+ if prev, exists := ids[id]; exists {
+ t.Errorf("ID collision, case %d and case %d: id %v", prev, i, id)
+ }
+ ids[id] = i
+ }
+}
diff --git a/miner/stress/1559/main.go b/miner/stress/1559/main.go
index 9c1ab0f4a1..c27875000d 100644
--- a/miner/stress/1559/main.go
+++ b/miner/stress/1559/main.go
@@ -19,6 +19,7 @@ package main
import (
"crypto/ecdsa"
+ crand "crypto/rand"
"math/big"
"math/rand"
"os"
@@ -29,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
@@ -58,7 +60,7 @@ func main() {
// Pre-generate the ethash mining DAG so we don't race
ethash.MakeDataset(1, ethconfig.Defaults.Ethash.DatasetDir)
- // Create an Ethash network based off of the Ropsten config
+ // Create an Ethash network
genesis := makeGenesis(faucets)
// Handle interrupts.
@@ -161,7 +163,7 @@ func makeTransaction(nonce uint64, privKey *ecdsa.PrivateKey, signer types.Signe
// Feecap and feetip are limited to 32 bytes. Offer a sightly
// larger buffer for creating both valid and invalid transactions.
var buf = make([]byte, 32+5)
- rand.Read(buf)
+ crand.Read(buf)
gasTipCap := new(big.Int).SetBytes(buf)
// If the given base fee is nil(the 1559 is still not available),
@@ -169,10 +171,10 @@ func makeTransaction(nonce uint64, privKey *ecdsa.PrivateKey, signer types.Signe
if baseFee == nil {
baseFee = new(big.Int).SetInt64(int64(rand.Int31()))
}
- // Generate the feecap, 75% valid feecap and 25% unguaranted.
+ // Generate the feecap, 75% valid feecap and 25% unguaranteed.
var gasFeeCap *big.Int
if rand.Intn(4) == 0 {
- rand.Read(buf)
+ crand.Read(buf)
gasFeeCap = new(big.Int).SetBytes(buf)
} else {
gasFeeCap = new(big.Int).Add(baseFee, gasTipCap)
@@ -193,7 +195,7 @@ func makeTransaction(nonce uint64, privKey *ecdsa.PrivateKey, signer types.Signe
// makeGenesis creates a custom Ethash genesis block based on some pre-defined
// faucet accounts.
func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis {
- genesis := core.DefaultRopstenGenesisBlock()
+ genesis := core.DefaultGenesisBlock()
genesis.Config = params.AllEthashProtocolChanges
genesis.Config.LondonBlock = londonBlock
@@ -245,7 +247,7 @@ func makeMiner(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) {
SyncMode: downloader.FullSync,
DatabaseCache: 256,
DatabaseHandles: 256,
- TxPool: core.DefaultTxPoolConfig,
+ TxPool: txpool.DefaultConfig,
GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash,
Miner: miner.Config{
diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go
index 88af84c7fc..516862c9cb 100644
--- a/miner/stress/beacon/main.go
+++ b/miner/stress/beacon/main.go
@@ -27,11 +27,12 @@ import (
"time"
"github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/beacon"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
@@ -80,8 +81,8 @@ var (
transitionDifficulty = new(big.Int).Mul(big.NewInt(20), params.MinimumDifficulty)
// blockInterval is the time interval for creating a new eth2 block
- blockInterval = time.Second * 3
blockIntervalInt = 3
+ blockInterval = time.Second * time.Duration(blockIntervalInt)
// finalizationDist is the block distance for finalizing block
finalizationDist = 10
@@ -141,7 +142,7 @@ func newNode(typ nodetype, genesis *core.Genesis, enodes []*enode.Node) *ethNode
}
}
-func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) (*beacon.ExecutableDataV1, error) {
+func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) (*engine.ExecutableData, error) {
if n.typ != eth2MiningNode {
return nil, errors.New("invalid node type")
}
@@ -149,12 +150,12 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64)
if timestamp <= parentTimestamp {
timestamp = parentTimestamp + 1
}
- payloadAttribute := beacon.PayloadAttributesV1{
+ payloadAttribute := engine.PayloadAttributes{
Timestamp: timestamp,
Random: common.Hash{},
SuggestedFeeRecipient: common.HexToAddress("0xdeadbeef"),
}
- fcState := beacon.ForkchoiceStateV1{
+ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: parentHash,
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
@@ -163,10 +164,11 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64)
if err != nil {
return nil, err
}
+ time.Sleep(time.Second * 5) // give enough time for block creation
return n.api.GetPayloadV1(*payload.PayloadID)
}
-func (n *ethNode) insertBlock(eb beacon.ExecutableDataV1) error {
+func (n *ethNode) insertBlock(eb engine.ExecutableData) error {
if !eth2types(n.typ) {
return errors.New("invalid node type")
}
@@ -192,18 +194,18 @@ func (n *ethNode) insertBlock(eb beacon.ExecutableDataV1) error {
}
}
-func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed beacon.ExecutableDataV1) error {
+func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed engine.ExecutableData) error {
if !eth2types(n.typ) {
return errors.New("invalid node type")
}
if err := n.insertBlock(ed); err != nil {
return err
}
- block, err := beacon.ExecutableDataToBlock(ed)
+ block, err := engine.ExecutableDataToBlock(ed)
if err != nil {
return err
}
- fcState := beacon.ForkchoiceStateV1{
+ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: block.ParentHash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
@@ -315,17 +317,14 @@ func (mgr *nodeManager) run() {
}
nodes := mgr.getNodes(eth2MiningNode)
nodes = append(nodes, mgr.getNodes(eth2NormalNode)...)
- nodes = append(nodes, mgr.getNodes(eth2LightClient)...)
+ //nodes = append(nodes, mgr.getNodes(eth2LightClient)...)
for _, node := range nodes {
- fcState := beacon.ForkchoiceStateV1{
- HeadBlockHash: oldest.Hash(),
- SafeBlockHash: common.Hash{},
+ fcState := engine.ForkchoiceStateV1{
+ HeadBlockHash: parentBlock.Hash(),
+ SafeBlockHash: oldest.Hash(),
FinalizedBlockHash: oldest.Hash(),
}
- // TODO(rjl493456442) finalization doesn't work properly, FIX IT
- _ = fcState
- _ = node
- //node.api.ForkchoiceUpdatedV1(fcState, nil)
+ node.api.ForkchoiceUpdatedV1(fcState, nil)
}
log.Info("Finalised eth2 block", "number", oldest.NumberU64(), "hash", oldest.Hash())
waitFinalise = waitFinalise[1:]
@@ -363,7 +362,7 @@ func (mgr *nodeManager) run() {
log.Error("Failed to assemble the block", "err", err)
continue
}
- block, _ := beacon.ExecutableDataToBlock(*ed)
+ block, _ := engine.ExecutableDataToBlock(*ed)
nodes := mgr.getNodes(eth2MiningNode)
nodes = append(nodes, mgr.getNodes(eth2NormalNode)...)
@@ -393,7 +392,7 @@ func main() {
// Pre-generate the ethash mining DAG so we don't race
ethash.MakeDataset(1, filepath.Join(os.Getenv("HOME"), ".ethash"))
- // Create an Ethash network based off of the Ropsten config
+ // Create an Ethash network
genesis := makeGenesis(faucets)
manager := newNodeManager(genesis)
defer manager.shutdown()
@@ -422,7 +421,7 @@ func main() {
node := nodes[index%len(nodes)]
// Create a self transaction and inject into the pool
- tx, err := types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(100000000000+rand.Int63n(65536)), nil), types.HomesteadSigner{}, faucets[index])
+ tx, err := types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(10_000_000_000+rand.Int63n(6_553_600_000)), nil), types.HomesteadSigner{}, faucets[index])
if err != nil {
panic(err)
}
@@ -441,7 +440,7 @@ func main() {
// makeGenesis creates a custom Ethash genesis block based on some pre-defined
// faucet accounts.
func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis {
- genesis := core.DefaultRopstenGenesisBlock()
+ genesis := core.DefaultGenesisBlock()
genesis.Difficulty = params.MinimumDifficulty
genesis.GasLimit = 25000000
@@ -484,14 +483,14 @@ func makeFullNode(genesis *core.Genesis) (*node.Node, *eth.Ethereum, *ethcatalys
SyncMode: downloader.FullSync,
DatabaseCache: 256,
DatabaseHandles: 256,
- TxPool: core.DefaultTxPoolConfig,
+ TxPool: txpool.DefaultConfig,
GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash,
Miner: miner.Config{
GasFloor: genesis.GasLimit * 9 / 10,
GasCeil: genesis.GasLimit * 11 / 10,
GasPrice: big.NewInt(1),
- Recommit: 10 * time.Second, // Disable the recommit
+ Recommit: 1 * time.Second,
},
LightServ: 100,
LightPeers: 10,
@@ -535,7 +534,7 @@ func makeLightNode(genesis *core.Genesis) (*node.Node, *les.LightEthereum, *lesc
SyncMode: downloader.LightSync,
DatabaseCache: 256,
DatabaseHandles: 256,
- TxPool: core.DefaultTxPoolConfig,
+ TxPool: txpool.DefaultConfig,
GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash,
LightPeers: 10,
diff --git a/miner/stress/clique/main.go b/miner/stress/clique/main.go
index 070a6ed60e..688c2b6984 100644
--- a/miner/stress/clique/main.go
+++ b/miner/stress/clique/main.go
@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
@@ -206,7 +207,7 @@ func makeSealer(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) {
SyncMode: downloader.FullSync,
DatabaseCache: 256,
DatabaseHandles: 256,
- TxPool: core.DefaultTxPoolConfig,
+ TxPool: txpool.DefaultConfig,
GPO: ethconfig.Defaults.GPO,
Miner: miner.Config{
GasCeil: genesis.GasLimit * 11 / 10,
diff --git a/miner/stress/ethash/main.go b/miner/stress/ethash/main.go
index 56a6e58173..6b6e7059d3 100644
--- a/miner/stress/ethash/main.go
+++ b/miner/stress/ethash/main.go
@@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
@@ -54,7 +55,7 @@ func main() {
// Pre-generate the ethash mining DAG so we don't race
ethash.MakeDataset(1, ethconfig.Defaults.Ethash.DatasetDir)
- // Create an Ethash network based off of the Ropsten config
+ // Create an Ethash network
genesis := makeGenesis(faucets)
// Handle interrupts.
@@ -133,7 +134,7 @@ func main() {
// makeGenesis creates a custom Ethash genesis block based on some pre-defined
// faucet accounts.
func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis {
- genesis := core.DefaultRopstenGenesisBlock()
+ genesis := core.DefaultGenesisBlock()
genesis.Difficulty = params.MinimumDifficulty
genesis.GasLimit = 25000000
@@ -175,7 +176,7 @@ func makeMiner(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) {
SyncMode: downloader.FullSync,
DatabaseCache: 256,
DatabaseHandles: 256,
- TxPool: core.DefaultTxPoolConfig,
+ TxPool: txpool.DefaultConfig,
GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash,
Miner: miner.Config{
diff --git a/miner/worker.go b/miner/worker.go
index 93fb6288bb..49204f71a0 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -24,7 +24,7 @@ import (
"sync/atomic"
"time"
- mapset "github.com/deckarep/golang-set"
+ mapset "github.com/deckarep/golang-set/v2"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
@@ -80,6 +80,7 @@ const (
var (
errBlockInterruptedByNewHead = errors.New("new head arrived while building block")
errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block")
+ errBlockInterruptedByTimeout = errors.New("timeout while building block")
)
// environment is the worker's current environment and holds all
@@ -87,11 +88,11 @@ var (
type environment struct {
signer types.Signer
- state *state.StateDB // apply state changes here
- ancestors mapset.Set // ancestor set (used for checking uncle parent validity)
- family mapset.Set // family set (used for checking uncle invalidity)
- tcount int // tx count in cycle
- gasPool *core.GasPool // available gas used to pack transactions
+ state *state.StateDB // apply state changes here
+ ancestors mapset.Set[common.Hash] // ancestor set (used for checking uncle parent validity)
+ family mapset.Set[common.Hash] // family set (used for checking uncle invalidity)
+ tcount int // tx count in cycle
+ gasPool *core.GasPool // available gas used to pack transactions
coinbase common.Address
header *types.Header
@@ -158,6 +159,7 @@ const (
commitInterruptNone int32 = iota
commitInterruptNewHead
commitInterruptResubmit
+ commitInterruptTimeout
)
// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier.
@@ -167,11 +169,17 @@ type newWorkReq struct {
timestamp int64
}
+// newPayloadResult represents a result struct corresponds to payload generation.
+type newPayloadResult struct {
+ err error
+ block *types.Block
+ fees *big.Int
+}
+
// getWorkReq represents a request for getting a new sealing work with provided parameters.
type getWorkReq struct {
params *generateParams
- result chan *types.Block // non-blocking channel
- err chan error
+ result chan *newPayloadResult // non-blocking channel
}
// intervalAdjust represents a resubmitting interval adjustment.
@@ -241,6 +249,17 @@ type worker struct {
// non-stop and no real transaction will be included.
noempty uint32
+ // newpayloadTimeout is the maximum timeout allowance for creating payload.
+ // The default value is 2 seconds but node operator can set it to arbitrary
+ // large value. A large timeout allowance may cause Geth to fail creating
+ // a non-empty payload within the specified time and eventually miss the slot
+ // in case there are some computation expensive transactions in txpool.
+ newpayloadTimeout time.Duration
+
+ // recommit is the time interval to re-create sealing work or to re-build
+ // payload in proof-of-stake stage.
+ recommit time.Duration
+
// External functions
isLocalBlock func(header *types.Header) bool // Function used to determine whether the specified block is mined by local miner.
@@ -257,12 +276,14 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
chainConfig: chainConfig,
engine: engine,
eth: eth,
- mux: mux,
chain: eth.BlockChain(),
+ mux: mux,
isLocalBlock: isLocalBlock,
localUncles: make(map[common.Hash]*types.Block),
remoteUncles: make(map[common.Hash]*types.Block),
unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), sealingLogAtDepth),
+ coinbase: config.Etherbase,
+ extra: config.ExtraData,
pendingTasks: make(map[common.Hash]*task),
txsCh: make(chan core.NewTxsEvent, txChanSize),
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
@@ -271,8 +292,8 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
getWorkCh: make(chan *getWorkReq),
taskCh: make(chan *task),
resultCh: make(chan *types.Block, resultQueueSize),
- exitCh: make(chan struct{}),
startCh: make(chan struct{}, 1),
+ exitCh: make(chan struct{}),
resubmitIntervalCh: make(chan time.Duration),
resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize),
}
@@ -288,6 +309,18 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval)
recommit = minRecommitInterval
}
+ worker.recommit = recommit
+
+ // Sanitize the timeout config for creating payload.
+ newpayloadTimeout := worker.config.NewPayloadTimeout
+ if newpayloadTimeout == 0 {
+ log.Warn("Sanitizing new payload timeout to default", "provided", newpayloadTimeout, "updated", DefaultConfig.NewPayloadTimeout)
+ newpayloadTimeout = DefaultConfig.NewPayloadTimeout
+ }
+ if newpayloadTimeout < time.Millisecond*100 {
+ log.Warn("Low payload timeout may cause high amount of non-full blocks", "provided", newpayloadTimeout, "default", DefaultConfig.NewPayloadTimeout)
+ }
+ worker.newpayloadTimeout = newpayloadTimeout
worker.wg.Add(4)
go worker.mainLoop()
@@ -309,6 +342,13 @@ func (w *worker) setEtherbase(addr common.Address) {
w.coinbase = addr
}
+// etherbase retrieves the configured etherbase address.
+func (w *worker) etherbase() common.Address {
+ w.mu.RLock()
+ defer w.mu.RUnlock()
+ return w.coinbase
+}
+
func (w *worker) setGasCeil(ceil uint64) {
w.mu.Lock()
defer w.mu.Unlock()
@@ -534,13 +574,11 @@ func (w *worker) mainLoop() {
w.commitWork(req.interrupt, req.noempty, req.timestamp)
case req := <-w.getWorkCh:
- block, err := w.generateWork(req.params)
- if err != nil {
- req.err <- err
- req.result <- nil
- } else {
- req.err <- nil
- req.result <- block
+ block, fees, err := w.generateWork(req.params)
+ req.result <- &newPayloadResult{
+ err: err,
+ block: block,
+ fees: fees,
}
case ev := <-w.chainSideCh:
// Short circuit for duplicate side blocks
@@ -766,8 +804,8 @@ func (w *worker) makeEnv(parent *types.Block, header *types.Header, coinbase com
signer: types.MakeSigner(w.chainConfig, header.Number),
state: state,
coinbase: coinbase,
- ancestors: mapset.NewSet(),
- family: mapset.NewSet(),
+ ancestors: mapset.NewSet[common.Hash](),
+ family: mapset.NewSet[common.Hash](),
header: header,
uncles: make(map[common.Hash]*types.Header),
}
@@ -844,42 +882,26 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
var coalescedLogs []*types.Log
for {
- // In the following three cases, we will interrupt the execution of the transaction.
- // (1) new head block event arrival, the interrupt signal is 1
- // (2) worker start or restart, the interrupt signal is 1
- // (3) worker recreate the sealing block with any newly arrived transactions, the interrupt signal is 2.
- // For the first two cases, the semi-finished work will be discarded.
- // For the third case, the semi-finished work will be submitted to the consensus engine.
- if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone {
- // Notify resubmit loop to increase resubmitting interval due to too frequent commits.
- if atomic.LoadInt32(interrupt) == commitInterruptResubmit {
- ratio := float64(gasLimit-env.gasPool.Gas()) / float64(gasLimit)
- if ratio < 0.1 {
- ratio = 0.1
- }
- w.resubmitAdjustCh <- &intervalAdjust{
- ratio: ratio,
- inc: true,
- }
- return errBlockInterruptedByRecommit
+ // Check interruption signal and abort building if it's fired.
+ if interrupt != nil {
+ if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone {
+ return signalToErr(signal)
}
- return errBlockInterruptedByNewHead
}
- // If we don't have enough gas for any further transactions then we're done
+ // If we don't have enough gas for any further transactions then we're done.
if env.gasPool.Gas() < params.TxGas {
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
break
}
- // Retrieve the next transaction and abort if all done
+ // Retrieve the next transaction and abort if all done.
tx := txs.Peek()
if tx == nil {
break
}
// Error may be ignored here. The error has already been checked
// during transaction acceptance is the transaction pool.
- //
- // We use the eip155 signer regardless of the current hf.
from, _ := types.Sender(env.signer, tx)
+
// Check whether the tx is replay protected. If we're not in the EIP155 hf
// phase, start ignoring the sender until we do.
if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
@@ -889,7 +911,7 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
continue
}
// Start executing the transaction
- env.state.Prepare(tx.Hash(), env.tcount)
+ env.state.SetTxContext(tx.Hash(), env.tcount)
logs, err := w.commitTransaction(env, tx)
switch {
@@ -914,7 +936,7 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
env.tcount++
txs.Shift()
- case errors.Is(err, core.ErrTxTypeNotSupported):
+ case errors.Is(err, types.ErrTxTypeNotSupported):
// Pop the unsupported transaction without shifting in the next from the account
log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
txs.Pop()
@@ -926,7 +948,6 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
txs.Shift()
}
}
-
if !w.isRunning() && len(coalescedLogs) > 0 {
// We don't push the pendingLogsEvent while we are sealing. The reason is that
// when we are sealing, the worker will regenerate a sealing block every 3 seconds.
@@ -942,24 +963,19 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
}
w.pendingLogsFeed.Send(cpy)
}
- // Notify resubmit loop to decrease resubmitting interval if current interval is larger
- // than the user-specified one.
- if interrupt != nil {
- w.resubmitAdjustCh <- &intervalAdjust{inc: false}
- }
return nil
}
// generateParams wraps various of settings for generating sealing task.
type generateParams struct {
- timestamp uint64 // The timstamp for sealing task
- forceTime bool // Flag whether the given timestamp is immutable or not
- parentHash common.Hash // Parent block hash, empty means the latest chain head
- coinbase common.Address // The fee recipient address for including transaction
- random common.Hash // The randomness generated by beacon chain, empty before the merge
- noUncle bool // Flag whether the uncle block inclusion is allowed
- noExtra bool // Flag whether the extra field assignment is allowed
- noTxs bool // Flag whether an empty block without any transaction is expected
+ timestamp uint64 // The timstamp for sealing task
+ forceTime bool // Flag whether the given timestamp is immutable or not
+ parentHash common.Hash // Parent block hash, empty means the latest chain head
+ coinbase common.Address // The fee recipient address for including transaction
+ random common.Hash // The randomness generated by beacon chain, empty before the merge
+ withdrawals types.Withdrawals // List of withdrawals to include in block.
+ noUncle bool // Flag whether the uncle block inclusion is allowed
+ noTxs bool // Flag whether an empty block without any transaction is expected
}
// prepareWork constructs the sealing task according to the given parameters,
@@ -986,16 +1002,16 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
}
timestamp = parent.Time() + 1
}
- // Construct the sealing block header, set the extra field if it's allowed
- num := parent.Number()
+ // Construct the sealing block header.
header := &types.Header{
ParentHash: parent.Hash(),
- Number: num.Add(num, common.Big1),
+ Number: new(big.Int).Add(parent.Number(), common.Big1),
GasLimit: core.CalcGasLimit(parent.GasLimit(), w.config.GasCeil),
Time: timestamp,
Coinbase: genParams.coinbase,
}
- if !genParams.noExtra && len(w.extra) != 0 {
+ // Set the extra field.
+ if len(w.extra) != 0 {
header.Extra = w.extra
}
// Set the randomness field from the beacon chain if it's available.
@@ -1006,7 +1022,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
if w.chainConfig.IsLondon(header.Number) {
header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header())
if !w.chainConfig.IsLondon(parent.Number()) {
- parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier
+ parentGasLimit := parent.GasLimit() * w.chainConfig.ElasticityMultiplier()
header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil)
}
}
@@ -1074,17 +1090,30 @@ func (w *worker) fillTransactions(interrupt *int32, env *environment) error {
}
// generateWork generates a sealing block based on the given parameters.
-func (w *worker) generateWork(params *generateParams) (*types.Block, error) {
+func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, error) {
work, err := w.prepareWork(params)
if err != nil {
- return nil, err
+ return nil, nil, err
}
defer work.discard()
if !params.noTxs {
- w.fillTransactions(nil, work)
+ interrupt := new(int32)
+ timer := time.AfterFunc(w.newpayloadTimeout, func() {
+ atomic.StoreInt32(interrupt, commitInterruptTimeout)
+ })
+ defer timer.Stop()
+
+ err := w.fillTransactions(interrupt, work)
+ if errors.Is(err, errBlockInterruptedByTimeout) {
+ log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(w.newpayloadTimeout))
+ }
}
- return w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts)
+ block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts, params.withdrawals)
+ if err != nil {
+ return nil, nil, err
+ }
+ return block, totalFees(block, work.receipts), nil
}
// commitWork generates several new sealing tasks based on the parent block
@@ -1095,11 +1124,11 @@ func (w *worker) commitWork(interrupt *int32, noempty bool, timestamp int64) {
// Set the coinbase if the worker is running or it's required
var coinbase common.Address
if w.isRunning() {
- if w.coinbase == (common.Address{}) {
+ coinbase = w.etherbase()
+ if coinbase == (common.Address{}) {
log.Error("Refusing to mine without etherbase")
return
}
- coinbase = w.coinbase // Use the preset address as the fee recipient
}
work, err := w.prepareWork(&generateParams{
timestamp: uint64(timestamp),
@@ -1113,13 +1142,36 @@ func (w *worker) commitWork(interrupt *int32, noempty bool, timestamp int64) {
if !noempty && atomic.LoadUint32(&w.noempty) == 0 {
w.commit(work.copy(), nil, false, start)
}
-
- // Fill pending transactions from the txpool
+ // Fill pending transactions from the txpool into the block.
err = w.fillTransactions(interrupt, work)
- if errors.Is(err, errBlockInterruptedByNewHead) {
+ switch {
+ case err == nil:
+ // The entire block is filled, decrease resubmit interval in case
+ // of current interval is larger than the user-specified one.
+ w.resubmitAdjustCh <- &intervalAdjust{inc: false}
+
+ case errors.Is(err, errBlockInterruptedByRecommit):
+ // Notify resubmit loop to increase resubmitting interval if the
+ // interruption is due to frequent commits.
+ gaslimit := work.header.GasLimit
+ ratio := float64(gaslimit-work.gasPool.Gas()) / float64(gaslimit)
+ if ratio < 0.1 {
+ ratio = 0.1
+ }
+ w.resubmitAdjustCh <- &intervalAdjust{
+ ratio: ratio,
+ inc: true,
+ }
+
+ case errors.Is(err, errBlockInterruptedByNewHead):
+ // If the block building is interrupted by newhead event, discard it
+ // totally. Committing the interrupted block introduces unnecessary
+ // delay, and possibly causes miner to mine on the previous head,
+ // which could result in higher uncle rate.
work.discard()
return
}
+ // Submit the generated block for consensus sealing.
w.commit(work.copy(), w.fullTaskHook, true, start)
// Swap out the old work with the new one, terminating any leftover
@@ -1142,7 +1194,8 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
// Create a local environment copy, avoid the data race with snapshot state.
// https://github.com/ethereum/go-ethereum/issues/24299
env := env.copy()
- block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts)
+ // Withdrawals are set to nil here, because this is only called in PoW.
+ block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts, nil)
if err != nil {
return err
}
@@ -1151,9 +1204,12 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
select {
case w.taskCh <- &task{receipts: env.receipts, state: env.state, block: block, createdAt: time.Now()}:
w.unconfirmed.Shift(block.NumberU64() - 1)
+
+ fees := totalFees(block, env.receipts)
+ feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether))
log.Info("Commit new sealing work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
"uncles", len(env.uncles), "txs", env.tcount,
- "gas", block.GasUsed(), "fees", totalFees(block, env.receipts),
+ "gas", block.GasUsed(), "fees", feesInEther,
"elapsed", common.PrettyDuration(time.Since(start)))
case <-w.exitCh:
@@ -1170,28 +1226,27 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
// getSealingBlock generates the sealing block based on the given parameters.
// The generation result will be passed back via the given channel no matter
// the generation itself succeeds or not.
-func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, noTxs bool) (chan *types.Block, chan error, error) {
- var (
- resCh = make(chan *types.Block, 1)
- errCh = make(chan error, 1)
- )
+func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, withdrawals types.Withdrawals, noTxs bool) (*types.Block, *big.Int, error) {
req := &getWorkReq{
params: &generateParams{
- timestamp: timestamp,
- forceTime: true,
- parentHash: parent,
- coinbase: coinbase,
- random: random,
- noUncle: true,
- noExtra: true,
- noTxs: noTxs,
+ timestamp: timestamp,
+ forceTime: true,
+ parentHash: parent,
+ coinbase: coinbase,
+ random: random,
+ withdrawals: withdrawals,
+ noUncle: true,
+ noTxs: noTxs,
},
- result: resCh,
- err: errCh,
+ result: make(chan *newPayloadResult, 1),
}
select {
case w.getWorkCh <- req:
- return resCh, errCh, nil
+ result := <-req.result
+ if result.err != nil {
+ return nil, nil, result.err
+ }
+ return result.block, result.fees, nil
case <-w.exitCh:
return nil, nil, errors.New("miner closed")
}
@@ -1222,12 +1277,27 @@ func (w *worker) postSideBlock(event core.ChainSideEvent) {
}
}
-// totalFees computes total consumed miner fees in ETH. Block transactions and receipts have to have the same order.
-func totalFees(block *types.Block, receipts []*types.Receipt) *big.Float {
+// totalFees computes total consumed miner fees in Wei. Block transactions and receipts have to have the same order.
+func totalFees(block *types.Block, receipts []*types.Receipt) *big.Int {
feesWei := new(big.Int)
for i, tx := range block.Transactions() {
minerFee, _ := tx.EffectiveGasTip(block.BaseFee())
feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), minerFee))
}
- return new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether)))
+ return feesWei
+}
+
+// signalToErr converts the interruption signal to a concrete error type for return.
+// The given signal must be a valid interruption signal.
+func signalToErr(signal int32) error {
+ switch signal {
+ case commitInterruptNewHead:
+ return errBlockInterruptedByNewHead
+ case commitInterruptResubmit:
+ return errBlockInterruptedByRecommit
+ case commitInterruptTimeout:
+ return errBlockInterruptedByTimeout
+ default:
+ panic(fmt.Errorf("undefined signal %d", signal))
+ }
}
diff --git a/miner/worker_test.go b/miner/worker_test.go
index ec5ba67e1c..ba929d293d 100644
--- a/miner/worker_test.go
+++ b/miner/worker_test.go
@@ -17,9 +17,9 @@
package miner
import (
+ "crypto/rand"
"errors"
"math/big"
- "math/rand"
"sync/atomic"
"testing"
"time"
@@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
@@ -51,7 +52,7 @@ const (
var (
// Test chain configurations
- testTxPoolConfig core.TxPoolConfig
+ testTxPoolConfig txpool.Config
ethashChainConfig *params.ChainConfig
cliqueChainConfig *params.ChainConfig
@@ -74,7 +75,7 @@ var (
)
func init() {
- testTxPoolConfig = core.DefaultTxPoolConfig
+ testTxPoolConfig = txpool.DefaultConfig
testTxPoolConfig.Journal = ""
ethashChainConfig = new(params.ChainConfig)
*ethashChainConfig = *params.TestChainConfig
@@ -104,25 +105,22 @@ func init() {
GasPrice: big.NewInt(params.InitialBaseFee),
})
newTxs = append(newTxs, tx2)
-
- rand.Seed(time.Now().UnixNano())
}
// testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing.
type testWorkerBackend struct {
db ethdb.Database
- txPool *core.TxPool
+ txPool *txpool.TxPool
chain *core.BlockChain
genesis *core.Genesis
uncleBlock *types.Block
}
func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend {
- var gspec = core.Genesis{
+ var gspec = &core.Genesis{
Config: chainConfig,
Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
}
-
switch e := engine.(type) {
case *clique.Clique:
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
@@ -134,39 +132,43 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
default:
t.Fatalf("unexpected consensus engine type: %T", engine)
}
- genesis := gspec.MustCommit(db)
-
- chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil)
- txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain)
+ chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec, nil, engine, vm.Config{}, nil, nil)
+ if err != nil {
+ t.Fatalf("core.NewBlockChain failed: %v", err)
+ }
+ txpool := txpool.NewTxPool(testTxPoolConfig, chainConfig, chain)
// Generate a small n-block chain and an uncle block for it
+ var uncle *types.Block
if n > 0 {
- blocks, _ := core.GenerateChain(chainConfig, genesis, engine, db, n, func(i int, gen *core.BlockGen) {
+ genDb, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, func(i int, gen *core.BlockGen) {
gen.SetCoinbase(testBankAddress)
})
if _, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("failed to insert origin chain: %v", err)
}
+ parent := chain.GetBlockByHash(chain.CurrentBlock().ParentHash())
+ blocks, _ = core.GenerateChain(chainConfig, parent, engine, genDb, 1, func(i int, gen *core.BlockGen) {
+ gen.SetCoinbase(testUserAddress)
+ })
+ uncle = blocks[0]
+ } else {
+ _, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, 1, func(i int, gen *core.BlockGen) {
+ gen.SetCoinbase(testUserAddress)
+ })
+ uncle = blocks[0]
}
- parent := genesis
- if n > 0 {
- parent = chain.GetBlockByHash(chain.CurrentBlock().ParentHash())
- }
- blocks, _ := core.GenerateChain(chainConfig, parent, engine, db, 1, func(i int, gen *core.BlockGen) {
- gen.SetCoinbase(testUserAddress)
- })
-
return &testWorkerBackend{
db: db,
chain: chain,
txPool: txpool,
- genesis: &gspec,
- uncleBlock: blocks[0],
+ genesis: gspec,
+ uncleBlock: uncle,
}
}
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
-func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool }
+func (b *testWorkerBackend) TxPool() *txpool.TxPool { return b.txPool }
func (b *testWorkerBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
return nil, errors.New("not supported")
}
@@ -217,26 +219,22 @@ func TestGenerateBlockAndImportClique(t *testing.T) {
func testGenerateBlockAndImport(t *testing.T, isClique bool) {
var (
engine consensus.Engine
- chainConfig *params.ChainConfig
+ chainConfig params.ChainConfig
db = rawdb.NewMemoryDatabase()
)
if isClique {
- chainConfig = params.AllCliqueProtocolChanges
+ chainConfig = *params.AllCliqueProtocolChanges
chainConfig.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000}
engine = clique.New(chainConfig.Clique, db)
} else {
- chainConfig = params.AllEthashProtocolChanges
+ chainConfig = *params.AllEthashProtocolChanges
engine = ethash.NewFaker()
}
-
- chainConfig.LondonBlock = big.NewInt(0)
- w, b := newTestWorker(t, chainConfig, engine, db, 0)
+ w, b := newTestWorker(t, &chainConfig, engine, db, 0)
defer w.close()
// This test chain imports the mined blocks.
- db2 := rawdb.NewMemoryDatabase()
- b.genesis.MustCommit(db2)
- chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil)
+ chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, b.genesis, nil, engine, vm.Config{}, nil, nil)
defer chain.Stop()
// Ignore empty commit here for less noise.
@@ -328,7 +326,7 @@ func TestStreamUncleBlock(t *testing.T) {
w, b := newTestWorker(t, ethashChainConfig, ethash, rawdb.NewMemoryDatabase(), 1)
defer w.close()
- var taskCh = make(chan struct{})
+ var taskCh = make(chan struct{}, 3)
taskIndex := 0
w.newTaskHook = func(task *task) {
@@ -527,21 +525,21 @@ func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine co
}
func TestGetSealingWorkEthash(t *testing.T) {
- testGetSealingWork(t, ethashChainConfig, ethash.NewFaker(), false)
+ testGetSealingWork(t, ethashChainConfig, ethash.NewFaker())
}
func TestGetSealingWorkClique(t *testing.T) {
- testGetSealingWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()), false)
+ testGetSealingWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()))
}
func TestGetSealingWorkPostMerge(t *testing.T) {
local := new(params.ChainConfig)
*local = *ethashChainConfig
local.TerminalTotalDifficulty = big.NewInt(0)
- testGetSealingWork(t, local, ethash.NewFaker(), true)
+ testGetSealingWork(t, local, ethash.NewFaker())
}
-func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, postMerge bool) {
+func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
defer engine.Close()
w, b := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0)
@@ -568,7 +566,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
}
_, isClique := engine.(*clique.Clique)
if !isClique {
- if len(block.Extra()) != 0 {
+ if len(block.Extra()) != 2 {
t.Error("Unexpected extra field")
}
if block.Coinbase() != coinbase {
@@ -637,9 +635,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
// This API should work even when the automatic sealing is not enabled
for _, c := range cases {
- resChan, errChan, _ := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, false)
- block := <-resChan
- err := <-errChan
+ block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, nil, false)
if c.expectErr {
if err == nil {
t.Error("Expect error but get nil")
@@ -655,9 +651,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
// This API should work even when the automatic sealing is enabled
w.start()
for _, c := range cases {
- resChan, errChan, _ := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, false)
- block := <-resChan
- err := <-errChan
+ block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, nil, false)
if c.expectErr {
if err == nil {
t.Error("Expect error but get nil")
diff --git a/mobile/accounts.go b/mobile/accounts.go
deleted file mode 100644
index d9eab93a74..0000000000
--- a/mobile/accounts.go
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the accounts package to support client side key
-// management on mobile platforms.
-
-package geth
-
-import (
- "errors"
- "time"
-
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/accounts/keystore"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
-)
-
-const (
- // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
- // memory and taking approximately 1s CPU time on a modern processor.
- StandardScryptN = int(keystore.StandardScryptN)
-
- // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
- // memory and taking approximately 1s CPU time on a modern processor.
- StandardScryptP = int(keystore.StandardScryptP)
-
- // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
- // memory and taking approximately 100ms CPU time on a modern processor.
- LightScryptN = int(keystore.LightScryptN)
-
- // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
- // memory and taking approximately 100ms CPU time on a modern processor.
- LightScryptP = int(keystore.LightScryptP)
-)
-
-// Account represents a stored key.
-type Account struct{ account accounts.Account }
-
-// Accounts represents a slice of accounts.
-type Accounts struct{ accounts []accounts.Account }
-
-// Size returns the number of accounts in the slice.
-func (a *Accounts) Size() int {
- return len(a.accounts)
-}
-
-// Get returns the account at the given index from the slice.
-func (a *Accounts) Get(index int) (account *Account, _ error) {
- if index < 0 || index >= len(a.accounts) {
- return nil, errors.New("index out of bounds")
- }
- return &Account{a.accounts[index]}, nil
-}
-
-// Set sets the account at the given index in the slice.
-func (a *Accounts) Set(index int, account *Account) error {
- if index < 0 || index >= len(a.accounts) {
- return errors.New("index out of bounds")
- }
- a.accounts[index] = account.account
- return nil
-}
-
-// GetAddress retrieves the address associated with the account.
-func (a *Account) GetAddress() *Address {
- return &Address{a.account.Address}
-}
-
-// GetURL retrieves the canonical URL of the account.
-func (a *Account) GetURL() string {
- return a.account.URL.String()
-}
-
-// KeyStore manages a key storage directory on disk.
-type KeyStore struct{ keystore *keystore.KeyStore }
-
-// NewKeyStore creates a keystore for the given directory.
-func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
- return &KeyStore{keystore: keystore.NewKeyStore(keydir, scryptN, scryptP)}
-}
-
-// HasAddress reports whether a key with the given address is present.
-func (ks *KeyStore) HasAddress(address *Address) bool {
- return ks.keystore.HasAddress(address.address)
-}
-
-// GetAccounts returns all key files present in the directory.
-func (ks *KeyStore) GetAccounts() *Accounts {
- return &Accounts{ks.keystore.Accounts()}
-}
-
-// DeleteAccount deletes the key matched by account if the passphrase is correct.
-// If a contains no filename, the address must match a unique key.
-func (ks *KeyStore) DeleteAccount(account *Account, passphrase string) error {
- return ks.keystore.Delete(account.account, passphrase)
-}
-
-// SignHash calculates a ECDSA signature for the given hash. The produced signature
-// is in the [R || S || V] format where V is 0 or 1.
-func (ks *KeyStore) SignHash(address *Address, hash []byte) (signature []byte, _ error) {
- return ks.keystore.SignHash(accounts.Account{Address: address.address}, common.CopyBytes(hash))
-}
-
-// SignTx signs the given transaction with the requested account.
-func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (*Transaction, error) {
- if chainID == nil { // Null passed from mobile app
- chainID = new(BigInt)
- }
- signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint)
- if err != nil {
- return nil, err
- }
- return &Transaction{signed}, nil
-}
-
-// SignHashPassphrase signs hash if the private key matching the given address can
-// be decrypted with the given passphrase. The produced signature is in the
-// [R || S || V] format where V is 0 or 1.
-func (ks *KeyStore) SignHashPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) {
- return ks.keystore.SignHashWithPassphrase(account.account, passphrase, common.CopyBytes(hash))
-}
-
-// SignTxPassphrase signs the transaction if the private key matching the
-// given address can be decrypted with the given passphrase.
-func (ks *KeyStore) SignTxPassphrase(account *Account, passphrase string, tx *Transaction, chainID *BigInt) (*Transaction, error) {
- if chainID == nil { // Null passed from mobile app
- chainID = new(BigInt)
- }
- signed, err := ks.keystore.SignTxWithPassphrase(account.account, passphrase, tx.tx, chainID.bigint)
- if err != nil {
- return nil, err
- }
- return &Transaction{signed}, nil
-}
-
-// Unlock unlocks the given account indefinitely.
-func (ks *KeyStore) Unlock(account *Account, passphrase string) error {
- return ks.keystore.TimedUnlock(account.account, passphrase, 0)
-}
-
-// Lock removes the private key with the given address from memory.
-func (ks *KeyStore) Lock(address *Address) error {
- return ks.keystore.Lock(address.address)
-}
-
-// TimedUnlock unlocks the given account with the passphrase. The account stays
-// unlocked for the duration of timeout (nanoseconds). A timeout of 0 unlocks the
-// account until the program exits. The account must match a unique key file.
-//
-// If the account address is already unlocked for a duration, TimedUnlock extends or
-// shortens the active unlock timeout. If the address was previously unlocked
-// indefinitely the timeout is not altered.
-func (ks *KeyStore) TimedUnlock(account *Account, passphrase string, timeout int64) error {
- return ks.keystore.TimedUnlock(account.account, passphrase, time.Duration(timeout))
-}
-
-// NewAccount generates a new key and stores it into the key directory,
-// encrypting it with the passphrase.
-func (ks *KeyStore) NewAccount(passphrase string) (*Account, error) {
- account, err := ks.keystore.NewAccount(passphrase)
- if err != nil {
- return nil, err
- }
- return &Account{account}, nil
-}
-
-// UpdateAccount changes the passphrase of an existing account.
-func (ks *KeyStore) UpdateAccount(account *Account, passphrase, newPassphrase string) error {
- return ks.keystore.Update(account.account, passphrase, newPassphrase)
-}
-
-// ExportKey exports as a JSON key, encrypted with newPassphrase.
-func (ks *KeyStore) ExportKey(account *Account, passphrase, newPassphrase string) (key []byte, _ error) {
- return ks.keystore.Export(account.account, passphrase, newPassphrase)
-}
-
-// ImportKey stores the given encrypted JSON key into the key directory.
-func (ks *KeyStore) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) {
- acc, err := ks.keystore.Import(common.CopyBytes(keyJSON), passphrase, newPassphrase)
- if err != nil {
- return nil, err
- }
- return &Account{acc}, nil
-}
-
-// ImportECDSAKey stores the given encrypted JSON key into the key directory.
-func (ks *KeyStore) ImportECDSAKey(key []byte, passphrase string) (account *Account, _ error) {
- privkey, err := crypto.ToECDSA(common.CopyBytes(key))
- if err != nil {
- return nil, err
- }
- acc, err := ks.keystore.ImportECDSA(privkey, passphrase)
- if err != nil {
- return nil, err
- }
- return &Account{acc}, nil
-}
-
-// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
-// a key file in the key directory. The key file is encrypted with the same passphrase.
-func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (account *Account, _ error) {
- acc, err := ks.keystore.ImportPreSaleKey(common.CopyBytes(keyJSON), passphrase)
- if err != nil {
- return nil, err
- }
- return &Account{acc}, nil
-}
diff --git a/mobile/big.go b/mobile/big.go
deleted file mode 100644
index c08bcf93f2..0000000000
--- a/mobile/big.go
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the math/big package.
-
-package geth
-
-import (
- "errors"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-// A BigInt represents a signed multi-precision integer.
-type BigInt struct {
- bigint *big.Int
-}
-
-// NewBigInt allocates and returns a new BigInt set to x.
-func NewBigInt(x int64) *BigInt {
- return &BigInt{big.NewInt(x)}
-}
-
-// NewBigIntFromString allocates and returns a new BigInt set to x
-// interpreted in the provided base.
-func NewBigIntFromString(x string, base int) *BigInt {
- b, success := new(big.Int).SetString(x, base)
- if !success {
- return nil
- }
- return &BigInt{b}
-}
-
-// GetBytes returns the absolute value of x as a big-endian byte slice.
-func (bi *BigInt) GetBytes() []byte {
- return bi.bigint.Bytes()
-}
-
-// String returns the value of x as a formatted decimal string.
-func (bi *BigInt) String() string {
- return bi.bigint.String()
-}
-
-// GetInt64 returns the int64 representation of x. If x cannot be represented in
-// an int64, the result is undefined.
-func (bi *BigInt) GetInt64() int64 {
- return bi.bigint.Int64()
-}
-
-// SetBytes interprets buf as the bytes of a big-endian unsigned integer and sets
-// the big int to that value.
-func (bi *BigInt) SetBytes(buf []byte) {
- bi.bigint.SetBytes(common.CopyBytes(buf))
-}
-
-// SetInt64 sets the big int to x.
-func (bi *BigInt) SetInt64(x int64) {
- bi.bigint.SetInt64(x)
-}
-
-// Sign returns:
-//
-// -1 if x < 0
-// 0 if x == 0
-// +1 if x > 0
-//
-func (bi *BigInt) Sign() int {
- return bi.bigint.Sign()
-}
-
-// SetString sets the big int to x.
-//
-// The string prefix determines the actual conversion base. A prefix of "0x" or
-// "0X" selects base 16; the "0" prefix selects base 8, and a "0b" or "0B" prefix
-// selects base 2. Otherwise the selected base is 10.
-func (bi *BigInt) SetString(x string, base int) {
- bi.bigint.SetString(x, base)
-}
-
-// BigInts represents a slice of big ints.
-type BigInts struct{ bigints []*big.Int }
-
-// NewBigInts creates a slice of uninitialized big numbers.
-func NewBigInts(size int) *BigInts {
- return &BigInts{
- bigints: make([]*big.Int, size),
- }
-}
-
-// Size returns the number of big ints in the slice.
-func (bi *BigInts) Size() int {
- return len(bi.bigints)
-}
-
-// Get returns the bigint at the given index from the slice.
-func (bi *BigInts) Get(index int) (bigint *BigInt, _ error) {
- if index < 0 || index >= len(bi.bigints) {
- return nil, errors.New("index out of bounds")
- }
- return &BigInt{bi.bigints[index]}, nil
-}
-
-// Set sets the big int at the given index in the slice.
-func (bi *BigInts) Set(index int, bigint *BigInt) error {
- if index < 0 || index >= len(bi.bigints) {
- return errors.New("index out of bounds")
- }
- bi.bigints[index] = bigint.bigint
- return nil
-}
-
-// GetString returns the value of x as a formatted string in some number base.
-func (bi *BigInt) GetString(base int) string {
- return bi.bigint.Text(base)
-}
diff --git a/mobile/bind.go b/mobile/bind.go
deleted file mode 100644
index e32d864aa5..0000000000
--- a/mobile/bind.go
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the bind package.
-
-package geth
-
-import (
- "math/big"
- "strings"
-
- "github.com/ethereum/go-ethereum/accounts/abi"
- "github.com/ethereum/go-ethereum/accounts/abi/bind"
- "github.com/ethereum/go-ethereum/accounts/keystore"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
-)
-
-// Signer is an interface defining the callback when a contract requires a
-// method to sign the transaction before submission.
-type Signer interface {
- Sign(addr *Address, unsignedTx *Transaction) (tx *Transaction, _ error)
-}
-
-type MobileSigner struct {
- sign bind.SignerFn
-}
-
-func (s *MobileSigner) Sign(addr *Address, unsignedTx *Transaction) (signedTx *Transaction, _ error) {
- sig, err := s.sign(addr.address, unsignedTx.tx)
- if err != nil {
- return nil, err
- }
- return &Transaction{sig}, nil
-}
-
-// CallOpts is the collection of options to fine tune a contract call request.
-type CallOpts struct {
- opts bind.CallOpts
-}
-
-// NewCallOpts creates a new option set for contract calls.
-func NewCallOpts() *CallOpts {
- return new(CallOpts)
-}
-
-func (opts *CallOpts) IsPending() bool { return opts.opts.Pending }
-func (opts *CallOpts) GetGasLimit() int64 { return 0 /* TODO(karalabe) */ }
-
-// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876)
-// Even then it's awkward to unpack the subtleties of a Go context out to Java.
-// func (opts *CallOpts) GetContext() *Context { return &Context{opts.opts.Context} }
-
-func (opts *CallOpts) SetPending(pending bool) { opts.opts.Pending = pending }
-func (opts *CallOpts) SetGasLimit(limit int64) { /* TODO(karalabe) */ }
-func (opts *CallOpts) SetContext(context *Context) { opts.opts.Context = context.context }
-func (opts *CallOpts) SetFrom(addr *Address) { opts.opts.From = addr.address }
-
-// TransactOpts is the collection of authorization data required to create a
-// valid Ethereum transaction.
-type TransactOpts struct {
- opts bind.TransactOpts
-}
-
-// NewTransactOpts creates a new option set for contract transaction.
-func NewTransactOpts() *TransactOpts {
- return new(TransactOpts)
-}
-
-// NewKeyedTransactOpts is a utility method to easily create a transaction signer
-// from a single private key.
-func NewKeyedTransactOpts(keyJson []byte, passphrase string, chainID *big.Int) (*TransactOpts, error) {
- key, err := keystore.DecryptKey(keyJson, passphrase)
- if err != nil {
- return nil, err
- }
- auth, err := bind.NewKeyedTransactorWithChainID(key.PrivateKey, chainID)
- if err != nil {
- return nil, err
- }
- return &TransactOpts{*auth}, nil
-}
-
-func (opts *TransactOpts) GetFrom() *Address { return &Address{opts.opts.From} }
-func (opts *TransactOpts) GetNonce() int64 { return opts.opts.Nonce.Int64() }
-func (opts *TransactOpts) GetValue() *BigInt { return &BigInt{opts.opts.Value} }
-func (opts *TransactOpts) GetGasPrice() *BigInt { return &BigInt{opts.opts.GasPrice} }
-func (opts *TransactOpts) GetGasLimit() int64 { return int64(opts.opts.GasLimit) }
-
-// GetSigner cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876)
-// func (opts *TransactOpts) GetSigner() Signer { return &signer{opts.opts.Signer} }
-
-// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876)
-// Even then it's awkward to unpack the subtleties of a Go context out to Java.
-//func (opts *TransactOpts) GetContext() *Context { return &Context{opts.opts.Context} }
-
-func (opts *TransactOpts) SetFrom(from *Address) { opts.opts.From = from.address }
-func (opts *TransactOpts) SetNonce(nonce int64) { opts.opts.Nonce = big.NewInt(nonce) }
-func (opts *TransactOpts) SetSigner(s Signer) {
- opts.opts.Signer = func(addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
- sig, err := s.Sign(&Address{addr}, &Transaction{tx})
- if err != nil {
- return nil, err
- }
- return sig.tx, nil
- }
-}
-func (opts *TransactOpts) SetValue(value *BigInt) { opts.opts.Value = value.bigint }
-func (opts *TransactOpts) SetGasPrice(price *BigInt) { opts.opts.GasPrice = price.bigint }
-func (opts *TransactOpts) SetGasLimit(limit int64) { opts.opts.GasLimit = uint64(limit) }
-func (opts *TransactOpts) SetContext(context *Context) { opts.opts.Context = context.context }
-
-// BoundContract is the base wrapper object that reflects a contract on the
-// Ethereum network. It contains a collection of methods that are used by the
-// higher level contract bindings to operate.
-type BoundContract struct {
- contract *bind.BoundContract
- address common.Address
- deployer *types.Transaction
-}
-
-// DeployContract deploys a contract onto the Ethereum blockchain and binds the
-// deployment address with a wrapper.
-func DeployContract(opts *TransactOpts, abiJSON string, bytecode []byte, client *EthereumClient, args *Interfaces) (contract *BoundContract, _ error) {
- // Deploy the contract to the network
- parsed, err := abi.JSON(strings.NewReader(abiJSON))
- if err != nil {
- return nil, err
- }
- addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, common.CopyBytes(bytecode), client.client, args.objects...)
- if err != nil {
- return nil, err
- }
- return &BoundContract{
- contract: bound,
- address: addr,
- deployer: tx,
- }, nil
-}
-
-// BindContract creates a low level contract interface through which calls and
-// transactions may be made through.
-func BindContract(address *Address, abiJSON string, client *EthereumClient) (contract *BoundContract, _ error) {
- parsed, err := abi.JSON(strings.NewReader(abiJSON))
- if err != nil {
- return nil, err
- }
- return &BoundContract{
- contract: bind.NewBoundContract(address.address, parsed, client.client, client.client, client.client),
- address: address.address,
- }, nil
-}
-
-func (c *BoundContract) GetAddress() *Address { return &Address{c.address} }
-func (c *BoundContract) GetDeployer() *Transaction {
- if c.deployer == nil {
- return nil
- }
- return &Transaction{c.deployer}
-}
-
-// Call invokes the (constant) contract method with params as input values and
-// sets the output to result.
-func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error {
- results := make([]interface{}, len(out.objects))
- copy(results, out.objects)
- if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil {
- return err
- }
- copy(out.objects, results)
- return nil
-}
-
-// Transact invokes the (paid) contract method with params as input values.
-func (c *BoundContract) Transact(opts *TransactOpts, method string, args *Interfaces) (tx *Transaction, _ error) {
- rawTx, err := c.contract.Transact(&opts.opts, method, args.objects...)
- if err != nil {
- return nil, err
- }
- return &Transaction{rawTx}, nil
-}
-
-// RawTransact invokes the (paid) contract method with raw calldata as input values.
-func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (tx *Transaction, _ error) {
- rawTx, err := c.contract.RawTransact(&opts.opts, calldata)
- if err != nil {
- return nil, err
- }
- return &Transaction{rawTx}, nil
-}
-
-// Transfer initiates a plain transaction to move funds to the contract, calling
-// its default method if one is available.
-func (c *BoundContract) Transfer(opts *TransactOpts) (tx *Transaction, _ error) {
- rawTx, err := c.contract.Transfer(&opts.opts)
- if err != nil {
- return nil, err
- }
- return &Transaction{rawTx}, nil
-}
diff --git a/mobile/common.go b/mobile/common.go
deleted file mode 100644
index 124712b4b1..0000000000
--- a/mobile/common.go
+++ /dev/null
@@ -1,251 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the common package.
-
-package geth
-
-import (
- "encoding/hex"
- "errors"
- "fmt"
- "strings"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
-)
-
-// Hash represents the 32 byte Keccak256 hash of arbitrary data.
-type Hash struct {
- hash common.Hash
-}
-
-// NewHashFromBytes converts a slice of bytes to a hash value.
-func NewHashFromBytes(binary []byte) (hash *Hash, _ error) {
- h := new(Hash)
- if err := h.SetBytes(common.CopyBytes(binary)); err != nil {
- return nil, err
- }
- return h, nil
-}
-
-// NewHashFromHex converts a hex string to a hash value.
-func NewHashFromHex(hex string) (hash *Hash, _ error) {
- h := new(Hash)
- if err := h.SetHex(hex); err != nil {
- return nil, err
- }
- return h, nil
-}
-
-// SetBytes sets the specified slice of bytes as the hash value.
-func (h *Hash) SetBytes(hash []byte) error {
- if length := len(hash); length != common.HashLength {
- return fmt.Errorf("invalid hash length: %v != %v", length, common.HashLength)
- }
- copy(h.hash[:], hash)
- return nil
-}
-
-// GetBytes retrieves the byte representation of the hash.
-func (h *Hash) GetBytes() []byte {
- return h.hash[:]
-}
-
-// SetHex sets the specified hex string as the hash value.
-func (h *Hash) SetHex(hash string) error {
- hash = strings.ToLower(hash)
- if len(hash) >= 2 && hash[:2] == "0x" {
- hash = hash[2:]
- }
- if length := len(hash); length != 2*common.HashLength {
- return fmt.Errorf("invalid hash hex length: %v != %v", length, 2*common.HashLength)
- }
- bin, err := hex.DecodeString(hash)
- if err != nil {
- return err
- }
- copy(h.hash[:], bin)
- return nil
-}
-
-// GetHex retrieves the hex string representation of the hash.
-func (h *Hash) GetHex() string {
- return h.hash.Hex()
-}
-
-// String implements Stringer interface for printable representation of the hash.
-func (h *Hash) String() string {
- return h.GetHex()
-}
-
-// Hashes represents a slice of hashes.
-type Hashes struct{ hashes []common.Hash }
-
-// NewHashes creates a slice of uninitialized Hashes.
-func NewHashes(size int) *Hashes {
- return &Hashes{
- hashes: make([]common.Hash, size),
- }
-}
-
-// NewHashesEmpty creates an empty slice of Hashes values.
-func NewHashesEmpty() *Hashes {
- return NewHashes(0)
-}
-
-// Size returns the number of hashes in the slice.
-func (h *Hashes) Size() int {
- return len(h.hashes)
-}
-
-// Get returns the hash at the given index from the slice.
-func (h *Hashes) Get(index int) (hash *Hash, _ error) {
- if index < 0 || index >= len(h.hashes) {
- return nil, errors.New("index out of bounds")
- }
- return &Hash{h.hashes[index]}, nil
-}
-
-// Set sets the Hash at the given index in the slice.
-func (h *Hashes) Set(index int, hash *Hash) error {
- if index < 0 || index >= len(h.hashes) {
- return errors.New("index out of bounds")
- }
- h.hashes[index] = hash.hash
- return nil
-}
-
-// Append adds a new Hash element to the end of the slice.
-func (h *Hashes) Append(hash *Hash) {
- h.hashes = append(h.hashes, hash.hash)
-}
-
-// Address represents the 20 byte address of an Ethereum account.
-type Address struct {
- address common.Address
-}
-
-// NewAddressFromBytes converts a slice of bytes to a hash value.
-func NewAddressFromBytes(binary []byte) (address *Address, _ error) {
- a := new(Address)
- if err := a.SetBytes(common.CopyBytes(binary)); err != nil {
- return nil, err
- }
- return a, nil
-}
-
-// NewAddressFromHex converts a hex string to a address value.
-func NewAddressFromHex(hex string) (address *Address, _ error) {
- a := new(Address)
- if err := a.SetHex(hex); err != nil {
- return nil, err
- }
- return a, nil
-}
-
-// SetBytes sets the specified slice of bytes as the address value.
-func (a *Address) SetBytes(address []byte) error {
- if length := len(address); length != common.AddressLength {
- return fmt.Errorf("invalid address length: %v != %v", length, common.AddressLength)
- }
- copy(a.address[:], address)
- return nil
-}
-
-// GetBytes retrieves the byte representation of the address.
-func (a *Address) GetBytes() []byte {
- return a.address[:]
-}
-
-// SetHex sets the specified hex string as the address value.
-func (a *Address) SetHex(address string) error {
- address = strings.ToLower(address)
- if len(address) >= 2 && address[:2] == "0x" {
- address = address[2:]
- }
- if length := len(address); length != 2*common.AddressLength {
- return fmt.Errorf("invalid address hex length: %v != %v", length, 2*common.AddressLength)
- }
- bin, err := hex.DecodeString(address)
- if err != nil {
- return err
- }
- copy(a.address[:], bin)
- return nil
-}
-
-// GetHex retrieves the hex string representation of the address.
-func (a *Address) GetHex() string {
- return a.address.Hex()
-}
-
-// String returns a printable representation of the address.
-func (a *Address) String() string {
- return a.GetHex()
-}
-
-// Addresses represents a slice of addresses.
-type Addresses struct{ addresses []common.Address }
-
-// NewAddresses creates a slice of uninitialized addresses.
-func NewAddresses(size int) *Addresses {
- return &Addresses{
- addresses: make([]common.Address, size),
- }
-}
-
-// NewAddressesEmpty creates an empty slice of Addresses values.
-func NewAddressesEmpty() *Addresses {
- return NewAddresses(0)
-}
-
-// Size returns the number of addresses in the slice.
-func (a *Addresses) Size() int {
- return len(a.addresses)
-}
-
-// Get returns the address at the given index from the slice.
-func (a *Addresses) Get(index int) (address *Address, _ error) {
- if index < 0 || index >= len(a.addresses) {
- return nil, errors.New("index out of bounds")
- }
- return &Address{a.addresses[index]}, nil
-}
-
-// Set sets the address at the given index in the slice.
-func (a *Addresses) Set(index int, address *Address) error {
- if index < 0 || index >= len(a.addresses) {
- return errors.New("index out of bounds")
- }
- a.addresses[index] = address.address
- return nil
-}
-
-// Append adds a new address element to the end of the slice.
-func (a *Addresses) Append(address *Address) {
- a.addresses = append(a.addresses, address.address)
-}
-
-// EncodeToHex encodes b as a hex string with 0x prefix.
-func EncodeToHex(b []byte) string {
- return hexutil.Encode(b)
-}
-
-// DecodeFromHex decodes a hex string with 0x prefix.
-func DecodeFromHex(s string) ([]byte, error) {
- return hexutil.Decode(s)
-}
diff --git a/mobile/context.go b/mobile/context.go
deleted file mode 100644
index 76b4c54642..0000000000
--- a/mobile/context.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the golang.org/x/net/context package to support
-// client side context management on mobile platforms.
-
-package geth
-
-import (
- "context"
- "time"
-)
-
-// Context carries a deadline, a cancellation signal, and other values across API
-// boundaries.
-type Context struct {
- context context.Context
- cancel context.CancelFunc
-}
-
-// NewContext returns a non-nil, empty Context. It is never canceled, has no
-// values, and has no deadline. It is typically used by the main function,
-// initialization, and tests, and as the top-level Context for incoming requests.
-func NewContext() *Context {
- return &Context{
- context: context.Background(),
- }
-}
-
-// WithCancel returns a copy of the original context with cancellation mechanism
-// included.
-//
-// Canceling this context releases resources associated with it, so code should
-// call cancel as soon as the operations running in this Context complete.
-func (c *Context) WithCancel() *Context {
- child, cancel := context.WithCancel(c.context)
- return &Context{
- context: child,
- cancel: cancel,
- }
-}
-
-// WithDeadline returns a copy of the original context with the deadline adjusted
-// to be no later than the specified time.
-//
-// Canceling this context releases resources associated with it, so code should
-// call cancel as soon as the operations running in this Context complete.
-func (c *Context) WithDeadline(sec int64, nsec int64) *Context {
- child, cancel := context.WithDeadline(c.context, time.Unix(sec, nsec))
- return &Context{
- context: child,
- cancel: cancel,
- }
-}
-
-// WithTimeout returns a copy of the original context with the deadline adjusted
-// to be no later than now + the duration specified.
-//
-// Canceling this context releases resources associated with it, so code should
-// call cancel as soon as the operations running in this Context complete.
-func (c *Context) WithTimeout(nsec int64) *Context {
- child, cancel := context.WithTimeout(c.context, time.Duration(nsec))
- return &Context{
- context: child,
- cancel: cancel,
- }
-}
diff --git a/mobile/discover.go b/mobile/discover.go
deleted file mode 100644
index 2c699f08be..0000000000
--- a/mobile/discover.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the accounts package to support client side enode
-// management on mobile platforms.
-
-package geth
-
-import (
- "errors"
-
- "github.com/ethereum/go-ethereum/p2p/enode"
-)
-
-// Enode represents a host on the network.
-type Enode struct {
- node *enode.Node
-}
-
-// NewEnode parses a node designator.
-//
-// There are two basic forms of node designators
-// - incomplete nodes, which only have the public key (node ID)
-// - complete nodes, which contain the public key and IP/Port information
-//
-// For incomplete nodes, the designator must look like one of these
-//
-// enode://
-//
-//
-// For complete nodes, the node ID is encoded in the username portion
-// of the URL, separated from the host by an @ sign. The hostname can
-// only be given as an IP address, DNS domain names are not allowed.
-// The port in the host name section is the TCP listening port. If the
-// TCP and UDP (discovery) ports differ, the UDP port is specified as
-// query parameter "discport".
-//
-// In the following example, the node URL describes
-// a node with IP address 10.3.58.6, TCP listening port 30303
-// and UDP discovery port 30301.
-//
-// enode://@10.3.58.6:30303?discport=30301
-func NewEnode(rawurl string) (*Enode, error) {
- node, err := enode.Parse(enode.ValidSchemes, rawurl)
- if err != nil {
- return nil, err
- }
- return &Enode{node}, nil
-}
-
-// Enodes represents a slice of accounts.
-type Enodes struct{ nodes []*enode.Node }
-
-// NewEnodes creates a slice of uninitialized enodes.
-func NewEnodes(size int) *Enodes {
- return &Enodes{
- nodes: make([]*enode.Node, size),
- }
-}
-
-// NewEnodesEmpty creates an empty slice of Enode values.
-func NewEnodesEmpty() *Enodes {
- return NewEnodes(0)
-}
-
-// Size returns the number of enodes in the slice.
-func (e *Enodes) Size() int {
- return len(e.nodes)
-}
-
-// Get returns the enode at the given index from the slice.
-func (e *Enodes) Get(index int) (enode *Enode, _ error) {
- if index < 0 || index >= len(e.nodes) {
- return nil, errors.New("index out of bounds")
- }
- return &Enode{e.nodes[index]}, nil
-}
-
-// Set sets the enode at the given index in the slice.
-func (e *Enodes) Set(index int, enode *Enode) error {
- if index < 0 || index >= len(e.nodes) {
- return errors.New("index out of bounds")
- }
- e.nodes[index] = enode.node
- return nil
-}
-
-// Append adds a new enode element to the end of the slice.
-func (e *Enodes) Append(enode *Enode) {
- e.nodes = append(e.nodes, enode.node)
-}
diff --git a/mobile/doc.go b/mobile/doc.go
deleted file mode 100644
index 20131afc2e..0000000000
--- a/mobile/doc.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2016 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 geth contains the simplified mobile APIs to go-ethereum.
-//
-// The scope of this package is *not* to allow writing a custom Ethereum client
-// with pieces plucked from go-ethereum, rather to allow writing native dapps on
-// mobile platforms. Keep this in mind when using or extending this package!
-//
-// API limitations
-//
-// Since gomobile cannot bridge arbitrary types between Go and Android/iOS, the
-// exposed APIs need to be manually wrapped into simplified types, with custom
-// constructors and getters/setters to ensure that they can be meaningfully used
-// from Java/ObjC too.
-//
-// With this in mind, please try to limit the scope of this package and only add
-// essentials without which mobile support cannot work, especially since manually
-// syncing the code will be unwieldy otherwise. In the long term we might consider
-// writing custom library generators, but those are out of scope now.
-//
-// Content wise each file in this package corresponds to an entire Go package
-// from the go-ethereum repository. Please adhere to this scoping to prevent this
-// package getting unmaintainable.
-//
-// Wrapping guidelines:
-//
-// Every type that is to be exposed should be wrapped into its own plain struct,
-// which internally contains a single field: the original go-ethereum version.
-// This is needed because gomobile cannot expose named types for now.
-//
-// Whenever a method argument or a return type is a custom struct, the pointer
-// variant should always be used as value types crossing over between language
-// boundaries might have strange behaviors.
-//
-// Slices of types should be converted into a single multiplicative type wrapping
-// a go slice with the methods `Size`, `Get` and `Set`. Further slice operations
-// should not be provided to limit the remote code complexity. Arrays should be
-// avoided as much as possible since they complicate bounds checking.
-//
-// If a method has multiple return values (e.g. some return + an error), those
-// are generated as output arguments in ObjC. To avoid weird generated names like
-// ret_0 for them, please always assign names to output variables if tuples.
-//
-// Note, a panic *cannot* cross over language boundaries, instead will result in
-// an undebuggable SEGFAULT in the process. For error handling only ever use error
-// returns, which may be the only or the second return.
-package geth
diff --git a/mobile/ethclient.go b/mobile/ethclient.go
deleted file mode 100644
index 00bcb3a2b9..0000000000
--- a/mobile/ethclient.go
+++ /dev/null
@@ -1,315 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains a wrapper for the Ethereum client.
-
-package geth
-
-import (
- "math/big"
-
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethclient"
-)
-
-// EthereumClient provides access to the Ethereum APIs.
-type EthereumClient struct {
- client *ethclient.Client
-}
-
-// NewEthereumClient connects a client to the given URL.
-func NewEthereumClient(rawurl string) (client *EthereumClient, _ error) {
- rawClient, err := ethclient.Dial(rawurl)
- return &EthereumClient{rawClient}, err
-}
-
-// GetBlockByHash returns the given full block.
-func (ec *EthereumClient) GetBlockByHash(ctx *Context, hash *Hash) (block *Block, _ error) {
- rawBlock, err := ec.client.BlockByHash(ctx.context, hash.hash)
- return &Block{rawBlock}, err
-}
-
-// GetBlockByNumber returns a block from the current canonical chain. If number is <0, the
-// latest known block is returned.
-func (ec *EthereumClient) GetBlockByNumber(ctx *Context, number int64) (block *Block, _ error) {
- if number < 0 {
- rawBlock, err := ec.client.BlockByNumber(ctx.context, nil)
- return &Block{rawBlock}, err
- }
- rawBlock, err := ec.client.BlockByNumber(ctx.context, big.NewInt(number))
- return &Block{rawBlock}, err
-}
-
-// GetHeaderByHash returns the block header with the given hash.
-func (ec *EthereumClient) GetHeaderByHash(ctx *Context, hash *Hash) (header *Header, _ error) {
- rawHeader, err := ec.client.HeaderByHash(ctx.context, hash.hash)
- return &Header{rawHeader}, err
-}
-
-// GetHeaderByNumber returns a block header from the current canonical chain. If number is <0,
-// the latest known header is returned.
-func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (header *Header, _ error) {
- if number < 0 {
- rawHeader, err := ec.client.HeaderByNumber(ctx.context, nil)
- return &Header{rawHeader}, err
- }
- rawHeader, err := ec.client.HeaderByNumber(ctx.context, big.NewInt(number))
- return &Header{rawHeader}, err
-}
-
-// GetTransactionByHash returns the transaction with the given hash.
-func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (tx *Transaction, _ error) {
- // TODO(karalabe): handle isPending
- rawTx, _, err := ec.client.TransactionByHash(ctx.context, hash.hash)
- return &Transaction{rawTx}, err
-}
-
-// GetTransactionSender returns the sender address of a transaction. The transaction must
-// be included in blockchain at the given block and index.
-func (ec *EthereumClient) GetTransactionSender(ctx *Context, tx *Transaction, blockhash *Hash, index int) (sender *Address, _ error) {
- addr, err := ec.client.TransactionSender(ctx.context, tx.tx, blockhash.hash, uint(index))
- return &Address{addr}, err
-}
-
-// GetTransactionCount returns the total number of transactions in the given block.
-func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (count int, _ error) {
- rawCount, err := ec.client.TransactionCount(ctx.context, hash.hash)
- return int(rawCount), err
-}
-
-// GetTransactionInBlock returns a single transaction at index in the given block.
-func (ec *EthereumClient) GetTransactionInBlock(ctx *Context, hash *Hash, index int) (tx *Transaction, _ error) {
- rawTx, err := ec.client.TransactionInBlock(ctx.context, hash.hash, uint(index))
- return &Transaction{rawTx}, err
-}
-
-// GetTransactionReceipt returns the receipt of a transaction by transaction hash.
-// Note that the receipt is not available for pending transactions.
-func (ec *EthereumClient) GetTransactionReceipt(ctx *Context, hash *Hash) (receipt *Receipt, _ error) {
- rawReceipt, err := ec.client.TransactionReceipt(ctx.context, hash.hash)
- return &Receipt{rawReceipt}, err
-}
-
-// SyncProgress retrieves the current progress of the sync algorithm. If there's
-// no sync currently running, it returns nil.
-func (ec *EthereumClient) SyncProgress(ctx *Context) (progress *SyncProgress, _ error) {
- rawProgress, err := ec.client.SyncProgress(ctx.context)
- if rawProgress == nil {
- return nil, err
- }
- return &SyncProgress{*rawProgress}, err
-}
-
-// NewHeadHandler is a client-side subscription callback to invoke on events and
-// subscription failure.
-type NewHeadHandler interface {
- OnNewHead(header *Header)
- OnError(failure string)
-}
-
-// SubscribeNewHead subscribes to notifications about the current blockchain head
-// on the given channel.
-func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, buffer int) (sub *Subscription, _ error) {
- // Subscribe to the event internally
- ch := make(chan *types.Header, buffer)
- rawSub, err := ec.client.SubscribeNewHead(ctx.context, ch)
- if err != nil {
- return nil, err
- }
- // Start up a dispatcher to feed into the callback
- go func() {
- for {
- select {
- case header := <-ch:
- handler.OnNewHead(&Header{header})
-
- case err := <-rawSub.Err():
- if err != nil {
- handler.OnError(err.Error())
- }
- return
- }
- }
- }()
- return &Subscription{rawSub}, nil
-}
-
-// State Access
-
-// GetBalanceAt returns the wei balance of the given account.
-// The block number can be <0, in which case the balance is taken from the latest known block.
-func (ec *EthereumClient) GetBalanceAt(ctx *Context, account *Address, number int64) (balance *BigInt, _ error) {
- if number < 0 {
- rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, nil)
- return &BigInt{rawBalance}, err
- }
- rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, big.NewInt(number))
- return &BigInt{rawBalance}, err
-}
-
-// GetStorageAt returns the value of key in the contract storage of the given account.
-// The block number can be <0, in which case the value is taken from the latest known block.
-func (ec *EthereumClient) GetStorageAt(ctx *Context, account *Address, key *Hash, number int64) (storage []byte, _ error) {
- if number < 0 {
- return ec.client.StorageAt(ctx.context, account.address, key.hash, nil)
- }
- return ec.client.StorageAt(ctx.context, account.address, key.hash, big.NewInt(number))
-}
-
-// GetCodeAt returns the contract code of the given account.
-// The block number can be <0, in which case the code is taken from the latest known block.
-func (ec *EthereumClient) GetCodeAt(ctx *Context, account *Address, number int64) (code []byte, _ error) {
- if number < 0 {
- return ec.client.CodeAt(ctx.context, account.address, nil)
- }
- return ec.client.CodeAt(ctx.context, account.address, big.NewInt(number))
-}
-
-// GetNonceAt returns the account nonce of the given account.
-// The block number can be <0, in which case the nonce is taken from the latest known block.
-func (ec *EthereumClient) GetNonceAt(ctx *Context, account *Address, number int64) (nonce int64, _ error) {
- if number < 0 {
- rawNonce, err := ec.client.NonceAt(ctx.context, account.address, nil)
- return int64(rawNonce), err
- }
- rawNonce, err := ec.client.NonceAt(ctx.context, account.address, big.NewInt(number))
- return int64(rawNonce), err
-}
-
-// Filters
-
-// FilterLogs executes a filter query.
-func (ec *EthereumClient) FilterLogs(ctx *Context, query *FilterQuery) (logs *Logs, _ error) {
- rawLogs, err := ec.client.FilterLogs(ctx.context, query.query)
- if err != nil {
- return nil, err
- }
- // Temp hack due to vm.Logs being []*vm.Log
- res := make([]*types.Log, len(rawLogs))
- for i := range rawLogs {
- res[i] = &rawLogs[i]
- }
- return &Logs{res}, nil
-}
-
-// FilterLogsHandler is a client-side subscription callback to invoke on events and
-// subscription failure.
-type FilterLogsHandler interface {
- OnFilterLogs(log *Log)
- OnError(failure string)
-}
-
-// SubscribeFilterLogs subscribes to the results of a streaming filter query.
-func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler FilterLogsHandler, buffer int) (sub *Subscription, _ error) {
- // Subscribe to the event internally
- ch := make(chan types.Log, buffer)
- rawSub, err := ec.client.SubscribeFilterLogs(ctx.context, query.query, ch)
- if err != nil {
- return nil, err
- }
- // Start up a dispatcher to feed into the callback
- go func() {
- for {
- select {
- case log := <-ch:
- handler.OnFilterLogs(&Log{&log})
-
- case err := <-rawSub.Err():
- if err != nil {
- handler.OnError(err.Error())
- }
- return
- }
- }
- }()
- return &Subscription{rawSub}, nil
-}
-
-// Pending State
-
-// GetPendingBalanceAt returns the wei balance of the given account in the pending state.
-func (ec *EthereumClient) GetPendingBalanceAt(ctx *Context, account *Address) (balance *BigInt, _ error) {
- rawBalance, err := ec.client.PendingBalanceAt(ctx.context, account.address)
- return &BigInt{rawBalance}, err
-}
-
-// GetPendingStorageAt returns the value of key in the contract storage of the given account in the pending state.
-func (ec *EthereumClient) GetPendingStorageAt(ctx *Context, account *Address, key *Hash) (storage []byte, _ error) {
- return ec.client.PendingStorageAt(ctx.context, account.address, key.hash)
-}
-
-// GetPendingCodeAt returns the contract code of the given account in the pending state.
-func (ec *EthereumClient) GetPendingCodeAt(ctx *Context, account *Address) (code []byte, _ error) {
- return ec.client.PendingCodeAt(ctx.context, account.address)
-}
-
-// GetPendingNonceAt returns the account nonce of the given account in the pending state.
-// This is the nonce that should be used for the next transaction.
-func (ec *EthereumClient) GetPendingNonceAt(ctx *Context, account *Address) (nonce int64, _ error) {
- rawNonce, err := ec.client.PendingNonceAt(ctx.context, account.address)
- return int64(rawNonce), err
-}
-
-// GetPendingTransactionCount returns the total number of transactions in the pending state.
-func (ec *EthereumClient) GetPendingTransactionCount(ctx *Context) (count int, _ error) {
- rawCount, err := ec.client.PendingTransactionCount(ctx.context)
- return int(rawCount), err
-}
-
-// Contract Calling
-
-// CallContract executes a message call transaction, which is directly executed in the VM
-// of the node, but never mined into the blockchain.
-//
-// blockNumber selects the block height at which the call runs. It can be <0, in which
-// case the code is taken from the latest known block. Note that state from very old
-// blocks might not be available.
-func (ec *EthereumClient) CallContract(ctx *Context, msg *CallMsg, number int64) (output []byte, _ error) {
- if number < 0 {
- return ec.client.CallContract(ctx.context, msg.msg, nil)
- }
- return ec.client.CallContract(ctx.context, msg.msg, big.NewInt(number))
-}
-
-// PendingCallContract executes a message call transaction using the EVM.
-// The state seen by the contract call is the pending state.
-func (ec *EthereumClient) PendingCallContract(ctx *Context, msg *CallMsg) (output []byte, _ error) {
- return ec.client.PendingCallContract(ctx.context, msg.msg)
-}
-
-// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
-// execution of a transaction.
-func (ec *EthereumClient) SuggestGasPrice(ctx *Context) (price *BigInt, _ error) {
- rawPrice, err := ec.client.SuggestGasPrice(ctx.context)
- return &BigInt{rawPrice}, err
-}
-
-// EstimateGas tries to estimate the gas needed to execute a specific transaction based on
-// the current pending state of the backend blockchain. There is no guarantee that this is
-// the true gas limit requirement as other transactions may be added or removed by miners,
-// but it should provide a basis for setting a reasonable default.
-func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (gas int64, _ error) {
- rawGas, err := ec.client.EstimateGas(ctx.context, msg.msg)
- return int64(rawGas), err
-}
-
-// SendTransaction injects a signed transaction into the pending pool for execution.
-//
-// If the transaction was a contract creation use the TransactionReceipt method to get the
-// contract address after the transaction has been mined.
-func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error {
- return ec.client.SendTransaction(ctx.context, tx.tx)
-}
diff --git a/mobile/ethereum.go b/mobile/ethereum.go
deleted file mode 100644
index d5058e4e20..0000000000
--- a/mobile/ethereum.go
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the go-ethereum root package.
-
-package geth
-
-import (
- "errors"
-
- "github.com/ethereum/go-ethereum"
- "github.com/ethereum/go-ethereum/common"
-)
-
-// Subscription represents an event subscription where events are
-// delivered on a data channel.
-type Subscription struct {
- sub ethereum.Subscription
-}
-
-// Unsubscribe cancels the sending of events to the data channel
-// and closes the error channel.
-func (s *Subscription) Unsubscribe() {
- s.sub.Unsubscribe()
-}
-
-// CallMsg contains parameters for contract calls.
-type CallMsg struct {
- msg ethereum.CallMsg
-}
-
-// NewCallMsg creates an empty contract call parameter list.
-func NewCallMsg() *CallMsg {
- return new(CallMsg)
-}
-
-func (msg *CallMsg) GetFrom() *Address { return &Address{msg.msg.From} }
-func (msg *CallMsg) GetGas() int64 { return int64(msg.msg.Gas) }
-func (msg *CallMsg) GetGasPrice() *BigInt { return &BigInt{msg.msg.GasPrice} }
-func (msg *CallMsg) GetValue() *BigInt { return &BigInt{msg.msg.Value} }
-func (msg *CallMsg) GetData() []byte { return msg.msg.Data }
-func (msg *CallMsg) GetTo() *Address {
- if to := msg.msg.To; to != nil {
- return &Address{*msg.msg.To}
- }
- return nil
-}
-
-func (msg *CallMsg) SetFrom(address *Address) { msg.msg.From = address.address }
-func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = uint64(gas) }
-func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint }
-func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint }
-func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = common.CopyBytes(data) }
-func (msg *CallMsg) SetTo(address *Address) {
- if address == nil {
- msg.msg.To = nil
- return
- }
- msg.msg.To = &address.address
-}
-
-// SyncProgress gives progress indications when the node is synchronising with
-// the Ethereum network.
-type SyncProgress struct {
- progress ethereum.SyncProgress
-}
-
-func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) }
-func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) }
-func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) }
-func (p *SyncProgress) GetSyncedAccounts() int64 { return int64(p.progress.SyncedAccounts) }
-func (p *SyncProgress) GetSyncedAccountBytes() int64 { return int64(p.progress.SyncedAccountBytes) }
-func (p *SyncProgress) GetSyncedBytecodes() int64 { return int64(p.progress.SyncedBytecodes) }
-func (p *SyncProgress) GetSyncedBytecodeBytes() int64 { return int64(p.progress.SyncedBytecodeBytes) }
-func (p *SyncProgress) GetSyncedStorage() int64 { return int64(p.progress.SyncedStorage) }
-func (p *SyncProgress) GetSyncedStorageBytes() int64 { return int64(p.progress.SyncedStorageBytes) }
-func (p *SyncProgress) GetHealedTrienodes() int64 { return int64(p.progress.HealedTrienodes) }
-func (p *SyncProgress) GetHealedTrienodeBytes() int64 { return int64(p.progress.HealedTrienodeBytes) }
-func (p *SyncProgress) GetHealedBytecodes() int64 { return int64(p.progress.HealedBytecodes) }
-func (p *SyncProgress) GetHealedBytecodeBytes() int64 { return int64(p.progress.HealedBytecodeBytes) }
-func (p *SyncProgress) GetHealingTrienodes() int64 { return int64(p.progress.HealingTrienodes) }
-func (p *SyncProgress) GetHealingBytecode() int64 { return int64(p.progress.HealingBytecode) }
-
-// Topics is a set of topic lists to filter events with.
-type Topics struct{ topics [][]common.Hash }
-
-// NewTopics creates a slice of uninitialized Topics.
-func NewTopics(size int) *Topics {
- return &Topics{
- topics: make([][]common.Hash, size),
- }
-}
-
-// NewTopicsEmpty creates an empty slice of Topics values.
-func NewTopicsEmpty() *Topics {
- return NewTopics(0)
-}
-
-// Size returns the number of topic lists inside the set
-func (t *Topics) Size() int {
- return len(t.topics)
-}
-
-// Get returns the topic list at the given index from the slice.
-func (t *Topics) Get(index int) (hashes *Hashes, _ error) {
- if index < 0 || index >= len(t.topics) {
- return nil, errors.New("index out of bounds")
- }
- return &Hashes{t.topics[index]}, nil
-}
-
-// Set sets the topic list at the given index in the slice.
-func (t *Topics) Set(index int, topics *Hashes) error {
- if index < 0 || index >= len(t.topics) {
- return errors.New("index out of bounds")
- }
- t.topics[index] = topics.hashes
- return nil
-}
-
-// Append adds a new topic list to the end of the slice.
-func (t *Topics) Append(topics *Hashes) {
- t.topics = append(t.topics, topics.hashes)
-}
-
-// FilterQuery contains options for contract log filtering.
-type FilterQuery struct {
- query ethereum.FilterQuery
-}
-
-// NewFilterQuery creates an empty filter query for contract log filtering.
-func NewFilterQuery() *FilterQuery {
- return new(FilterQuery)
-}
-
-func (fq *FilterQuery) GetFromBlock() *BigInt { return &BigInt{fq.query.FromBlock} }
-func (fq *FilterQuery) GetToBlock() *BigInt { return &BigInt{fq.query.ToBlock} }
-func (fq *FilterQuery) GetAddresses() *Addresses { return &Addresses{fq.query.Addresses} }
-func (fq *FilterQuery) GetTopics() *Topics { return &Topics{fq.query.Topics} }
-
-func (fq *FilterQuery) SetFromBlock(fromBlock *BigInt) { fq.query.FromBlock = fromBlock.bigint }
-func (fq *FilterQuery) SetToBlock(toBlock *BigInt) { fq.query.ToBlock = toBlock.bigint }
-func (fq *FilterQuery) SetAddresses(addresses *Addresses) { fq.query.Addresses = addresses.addresses }
-func (fq *FilterQuery) SetTopics(topics *Topics) { fq.query.Topics = topics.topics }
diff --git a/mobile/geth.go b/mobile/geth.go
deleted file mode 100644
index 709b68cbde..0000000000
--- a/mobile/geth.go
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the node package to support client side node
-// management on mobile platforms.
-
-package geth
-
-import (
- "encoding/json"
- "fmt"
- "path/filepath"
-
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/eth/downloader"
- "github.com/ethereum/go-ethereum/eth/ethconfig"
- "github.com/ethereum/go-ethereum/ethclient"
- "github.com/ethereum/go-ethereum/ethstats"
- "github.com/ethereum/go-ethereum/internal/debug"
- "github.com/ethereum/go-ethereum/les"
- "github.com/ethereum/go-ethereum/node"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/nat"
- "github.com/ethereum/go-ethereum/params"
-)
-
-// NodeConfig represents the collection of configuration values to fine tune the Geth
-// node embedded into a mobile process. The available values are a subset of the
-// entire API provided by go-ethereum to reduce the maintenance surface and dev
-// complexity.
-type NodeConfig struct {
- // Bootstrap nodes used to establish connectivity with the rest of the network.
- BootstrapNodes *Enodes
-
- // MaxPeers is the maximum number of peers that can be connected. If this is
- // set to zero, then only the configured static and trusted peers can connect.
- MaxPeers int
-
- // EthereumEnabled specifies whether the node should run the Ethereum protocol.
- EthereumEnabled bool
-
- // EthereumNetworkID is the network identifier used by the Ethereum protocol to
- // decide if remote peers should be accepted or not.
- EthereumNetworkID int64 // uint64 in truth, but Java can't handle that...
-
- // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An
- // empty genesis state is equivalent to using the mainnet's state.
- EthereumGenesis string
-
- // EthereumDatabaseCache is the system memory in MB to allocate for database caching.
- // A minimum of 16MB is always reserved.
- EthereumDatabaseCache int
-
- // EthereumNetStats is a netstats connection string to use to report various
- // chain, transaction and node stats to a monitoring server.
- //
- // It has the form "nodename:secret@host:port"
- EthereumNetStats string
-
- // Listening address of pprof server.
- PprofAddress string
-}
-
-// defaultNodeConfig contains the default node configuration values to use if all
-// or some fields are missing from the user's specified list.
-var defaultNodeConfig = &NodeConfig{
- BootstrapNodes: FoundationBootnodes(),
- MaxPeers: 25,
- EthereumEnabled: true,
- EthereumNetworkID: 1,
- EthereumDatabaseCache: 16,
-}
-
-// NewNodeConfig creates a new node option set, initialized to the default values.
-func NewNodeConfig() *NodeConfig {
- config := *defaultNodeConfig
- return &config
-}
-
-// AddBootstrapNode adds an additional bootstrap node to the node config.
-func (conf *NodeConfig) AddBootstrapNode(node *Enode) {
- conf.BootstrapNodes.Append(node)
-}
-
-// EncodeJSON encodes a NodeConfig into a JSON data dump.
-func (conf *NodeConfig) EncodeJSON() (string, error) {
- data, err := json.Marshal(conf)
- return string(data), err
-}
-
-// String returns a printable representation of the node config.
-func (conf *NodeConfig) String() string {
- return encodeOrError(conf)
-}
-
-// Node represents a Geth Ethereum node instance.
-type Node struct {
- node *node.Node
-}
-
-// NewNode creates and configures a new Geth node.
-func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
- // If no or partial configurations were specified, use defaults
- if config == nil {
- config = NewNodeConfig()
- }
- if config.MaxPeers == 0 {
- config.MaxPeers = defaultNodeConfig.MaxPeers
- }
- if config.BootstrapNodes == nil || config.BootstrapNodes.Size() == 0 {
- config.BootstrapNodes = defaultNodeConfig.BootstrapNodes
- }
-
- if config.PprofAddress != "" {
- debug.StartPProf(config.PprofAddress, true)
- }
-
- // Create the empty networking stack
- nodeConf := &node.Config{
- Name: clientIdentifier,
- Version: params.VersionWithMeta,
- DataDir: datadir,
- KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores!
- P2P: p2p.Config{
- NoDiscovery: true,
- DiscoveryV5: true,
- BootstrapNodesV5: config.BootstrapNodes.nodes,
- ListenAddr: ":0",
- NAT: nat.Any(),
- MaxPeers: config.MaxPeers,
- },
- }
-
- rawStack, err := node.New(nodeConf)
- if err != nil {
- return nil, err
- }
-
- debug.Memsize.Add("node", rawStack)
-
- var genesis *core.Genesis
- if config.EthereumGenesis != "" {
- // Parse the user supplied genesis spec if not mainnet
- genesis = new(core.Genesis)
- if err := json.Unmarshal([]byte(config.EthereumGenesis), genesis); err != nil {
- return nil, fmt.Errorf("invalid genesis spec: %v", err)
- }
- // If we have the Ropsten testnet, hard code the chain configs too
- if config.EthereumGenesis == RopstenGenesis() {
- genesis.Config = params.RopstenChainConfig
- if config.EthereumNetworkID == 1 {
- config.EthereumNetworkID = 3
- }
- }
- // If we have the Sepolia testnet, hard code the chain configs too
- if config.EthereumGenesis == SepoliaGenesis() {
- genesis.Config = params.SepoliaChainConfig
- if config.EthereumNetworkID == 1 {
- config.EthereumNetworkID = 11155111
- }
- }
- // If we have the Rinkeby testnet, hard code the chain configs too
- if config.EthereumGenesis == RinkebyGenesis() {
- genesis.Config = params.RinkebyChainConfig
- if config.EthereumNetworkID == 1 {
- config.EthereumNetworkID = 4
- }
- }
- // If we have the Goerli testnet, hard code the chain configs too
- if config.EthereumGenesis == GoerliGenesis() {
- genesis.Config = params.GoerliChainConfig
- if config.EthereumNetworkID == 1 {
- config.EthereumNetworkID = 5
- }
- }
- }
- // Register the Ethereum protocol if requested
- if config.EthereumEnabled {
- ethConf := ethconfig.Defaults
- ethConf.Genesis = genesis
- ethConf.SyncMode = downloader.LightSync
- ethConf.NetworkId = uint64(config.EthereumNetworkID)
- ethConf.DatabaseCache = config.EthereumDatabaseCache
- lesBackend, err := les.New(rawStack, ðConf)
- if err != nil {
- return nil, fmt.Errorf("ethereum init: %v", err)
- }
- // If netstats reporting is requested, do it
- if config.EthereumNetStats != "" {
- if err := ethstats.New(rawStack, lesBackend.ApiBackend, lesBackend.Engine(), config.EthereumNetStats); err != nil {
- return nil, fmt.Errorf("netstats init: %v", err)
- }
- }
- }
- return &Node{rawStack}, nil
-}
-
-// Close terminates a running node along with all it's services, tearing internal state
-// down. It is not possible to restart a closed node.
-func (n *Node) Close() error {
- return n.node.Close()
-}
-
-// Start creates a live P2P node and starts running it.
-func (n *Node) Start() error {
- // TODO: recreate the node so it can be started multiple times
- return n.node.Start()
-}
-
-// GetEthereumClient retrieves a client to access the Ethereum subsystem.
-func (n *Node) GetEthereumClient() (client *EthereumClient, _ error) {
- rpc, err := n.node.Attach()
- if err != nil {
- return nil, err
- }
- return &EthereumClient{ethclient.NewClient(rpc)}, nil
-}
-
-// GetNodeInfo gathers and returns a collection of metadata known about the host.
-func (n *Node) GetNodeInfo() *NodeInfo {
- return &NodeInfo{n.node.Server().NodeInfo()}
-}
-
-// GetPeersInfo returns an array of metadata objects describing connected peers.
-func (n *Node) GetPeersInfo() *PeerInfos {
- return &PeerInfos{n.node.Server().PeersInfo()}
-}
diff --git a/mobile/interface.go b/mobile/interface.go
deleted file mode 100644
index d5200d5b1b..0000000000
--- a/mobile/interface.go
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains perverted wrappers to allow crossing over empty interfaces.
-
-package geth
-
-import (
- "errors"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-// Interface represents a wrapped version of Go's interface{}, with the capacity
-// to store arbitrary data types.
-//
-// Since it's impossible to get the arbitrary-ness converted between Go and mobile
-// platforms, we're using explicit getters and setters for the conversions. There
-// is of course no point in enumerating everything, just enough to support the
-// contract bindins requiring client side generated code.
-type Interface struct {
- object interface{}
-}
-
-// NewInterface creates a new empty interface that can be used to pass around
-// generic types.
-func NewInterface() *Interface {
- return new(Interface)
-}
-
-func (i *Interface) SetBool(b bool) { i.object = &b }
-func (i *Interface) SetBools(bs *Bools) { i.object = &bs.bools }
-func (i *Interface) SetString(str string) { i.object = &str }
-func (i *Interface) SetStrings(strs *Strings) { i.object = &strs.strs }
-func (i *Interface) SetBinary(binary []byte) { b := common.CopyBytes(binary); i.object = &b }
-func (i *Interface) SetBinaries(binaries *Binaries) { i.object = &binaries.binaries }
-func (i *Interface) SetAddress(address *Address) { i.object = &address.address }
-func (i *Interface) SetAddresses(addrs *Addresses) { i.object = &addrs.addresses }
-func (i *Interface) SetHash(hash *Hash) { i.object = &hash.hash }
-func (i *Interface) SetHashes(hashes *Hashes) { i.object = &hashes.hashes }
-func (i *Interface) SetInt8(n int8) { i.object = &n }
-func (i *Interface) SetInt16(n int16) { i.object = &n }
-func (i *Interface) SetInt32(n int32) { i.object = &n }
-func (i *Interface) SetInt64(n int64) { i.object = &n }
-func (i *Interface) SetInt8s(bigints *BigInts) {
- ints := make([]int8, 0, bigints.Size())
- for _, bi := range bigints.bigints {
- ints = append(ints, int8(bi.Int64()))
- }
- i.object = &ints
-}
-func (i *Interface) SetInt16s(bigints *BigInts) {
- ints := make([]int16, 0, bigints.Size())
- for _, bi := range bigints.bigints {
- ints = append(ints, int16(bi.Int64()))
- }
- i.object = &ints
-}
-func (i *Interface) SetInt32s(bigints *BigInts) {
- ints := make([]int32, 0, bigints.Size())
- for _, bi := range bigints.bigints {
- ints = append(ints, int32(bi.Int64()))
- }
- i.object = &ints
-}
-func (i *Interface) SetInt64s(bigints *BigInts) {
- ints := make([]int64, 0, bigints.Size())
- for _, bi := range bigints.bigints {
- ints = append(ints, bi.Int64())
- }
- i.object = &ints
-}
-func (i *Interface) SetUint8(bigint *BigInt) { n := uint8(bigint.bigint.Uint64()); i.object = &n }
-func (i *Interface) SetUint16(bigint *BigInt) { n := uint16(bigint.bigint.Uint64()); i.object = &n }
-func (i *Interface) SetUint32(bigint *BigInt) { n := uint32(bigint.bigint.Uint64()); i.object = &n }
-func (i *Interface) SetUint64(bigint *BigInt) { n := bigint.bigint.Uint64(); i.object = &n }
-func (i *Interface) SetUint8s(bigints *BigInts) {
- ints := make([]uint8, 0, bigints.Size())
- for _, bi := range bigints.bigints {
- ints = append(ints, uint8(bi.Uint64()))
- }
- i.object = &ints
-}
-func (i *Interface) SetUint16s(bigints *BigInts) {
- ints := make([]uint16, 0, bigints.Size())
- for _, bi := range bigints.bigints {
- ints = append(ints, uint16(bi.Uint64()))
- }
- i.object = &ints
-}
-func (i *Interface) SetUint32s(bigints *BigInts) {
- ints := make([]uint32, 0, bigints.Size())
- for _, bi := range bigints.bigints {
- ints = append(ints, uint32(bi.Uint64()))
- }
- i.object = &ints
-}
-func (i *Interface) SetUint64s(bigints *BigInts) {
- ints := make([]uint64, 0, bigints.Size())
- for _, bi := range bigints.bigints {
- ints = append(ints, bi.Uint64())
- }
- i.object = &ints
-}
-func (i *Interface) SetBigInt(bigint *BigInt) { i.object = &bigint.bigint }
-func (i *Interface) SetBigInts(bigints *BigInts) { i.object = &bigints.bigints }
-
-func (i *Interface) SetDefaultBool() { i.object = new(bool) }
-func (i *Interface) SetDefaultBools() { i.object = new([]bool) }
-func (i *Interface) SetDefaultString() { i.object = new(string) }
-func (i *Interface) SetDefaultStrings() { i.object = new([]string) }
-func (i *Interface) SetDefaultBinary() { i.object = new([]byte) }
-func (i *Interface) SetDefaultBinaries() { i.object = new([][]byte) }
-func (i *Interface) SetDefaultAddress() { i.object = new(common.Address) }
-func (i *Interface) SetDefaultAddresses() { i.object = new([]common.Address) }
-func (i *Interface) SetDefaultHash() { i.object = new(common.Hash) }
-func (i *Interface) SetDefaultHashes() { i.object = new([]common.Hash) }
-func (i *Interface) SetDefaultInt8() { i.object = new(int8) }
-func (i *Interface) SetDefaultInt8s() { i.object = new([]int8) }
-func (i *Interface) SetDefaultInt16() { i.object = new(int16) }
-func (i *Interface) SetDefaultInt16s() { i.object = new([]int16) }
-func (i *Interface) SetDefaultInt32() { i.object = new(int32) }
-func (i *Interface) SetDefaultInt32s() { i.object = new([]int32) }
-func (i *Interface) SetDefaultInt64() { i.object = new(int64) }
-func (i *Interface) SetDefaultInt64s() { i.object = new([]int64) }
-func (i *Interface) SetDefaultUint8() { i.object = new(uint8) }
-func (i *Interface) SetDefaultUint8s() { i.object = new([]uint8) }
-func (i *Interface) SetDefaultUint16() { i.object = new(uint16) }
-func (i *Interface) SetDefaultUint16s() { i.object = new([]uint16) }
-func (i *Interface) SetDefaultUint32() { i.object = new(uint32) }
-func (i *Interface) SetDefaultUint32s() { i.object = new([]uint32) }
-func (i *Interface) SetDefaultUint64() { i.object = new(uint64) }
-func (i *Interface) SetDefaultUint64s() { i.object = new([]uint64) }
-func (i *Interface) SetDefaultBigInt() { i.object = new(*big.Int) }
-func (i *Interface) SetDefaultBigInts() { i.object = new([]*big.Int) }
-
-func (i *Interface) GetBool() bool { return *i.object.(*bool) }
-func (i *Interface) GetBools() *Bools { return &Bools{*i.object.(*[]bool)} }
-func (i *Interface) GetString() string { return *i.object.(*string) }
-func (i *Interface) GetStrings() *Strings { return &Strings{*i.object.(*[]string)} }
-func (i *Interface) GetBinary() []byte { return *i.object.(*[]byte) }
-func (i *Interface) GetBinaries() *Binaries { return &Binaries{*i.object.(*[][]byte)} }
-func (i *Interface) GetAddress() *Address { return &Address{*i.object.(*common.Address)} }
-func (i *Interface) GetAddresses() *Addresses { return &Addresses{*i.object.(*[]common.Address)} }
-func (i *Interface) GetHash() *Hash { return &Hash{*i.object.(*common.Hash)} }
-func (i *Interface) GetHashes() *Hashes { return &Hashes{*i.object.(*[]common.Hash)} }
-func (i *Interface) GetInt8() int8 { return *i.object.(*int8) }
-func (i *Interface) GetInt16() int16 { return *i.object.(*int16) }
-func (i *Interface) GetInt32() int32 { return *i.object.(*int32) }
-func (i *Interface) GetInt64() int64 { return *i.object.(*int64) }
-func (i *Interface) GetInt8s() *BigInts {
- val := i.object.(*[]int8)
- bigints := NewBigInts(len(*val))
- for i, v := range *val {
- bigints.Set(i, &BigInt{new(big.Int).SetInt64(int64(v))})
- }
- return bigints
-}
-func (i *Interface) GetInt16s() *BigInts {
- val := i.object.(*[]int16)
- bigints := NewBigInts(len(*val))
- for i, v := range *val {
- bigints.Set(i, &BigInt{new(big.Int).SetInt64(int64(v))})
- }
- return bigints
-}
-func (i *Interface) GetInt32s() *BigInts {
- val := i.object.(*[]int32)
- bigints := NewBigInts(len(*val))
- for i, v := range *val {
- bigints.Set(i, &BigInt{new(big.Int).SetInt64(int64(v))})
- }
- return bigints
-}
-func (i *Interface) GetInt64s() *BigInts {
- val := i.object.(*[]int64)
- bigints := NewBigInts(len(*val))
- for i, v := range *val {
- bigints.Set(i, &BigInt{new(big.Int).SetInt64(v)})
- }
- return bigints
-}
-func (i *Interface) GetUint8() *BigInt {
- return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint8)))}
-}
-func (i *Interface) GetUint16() *BigInt {
- return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint16)))}
-}
-func (i *Interface) GetUint32() *BigInt {
- return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint32)))}
-}
-func (i *Interface) GetUint64() *BigInt {
- return &BigInt{new(big.Int).SetUint64(*i.object.(*uint64))}
-}
-func (i *Interface) GetUint8s() *BigInts {
- val := i.object.(*[]uint8)
- bigints := NewBigInts(len(*val))
- for i, v := range *val {
- bigints.Set(i, &BigInt{new(big.Int).SetUint64(uint64(v))})
- }
- return bigints
-}
-func (i *Interface) GetUint16s() *BigInts {
- val := i.object.(*[]uint16)
- bigints := NewBigInts(len(*val))
- for i, v := range *val {
- bigints.Set(i, &BigInt{new(big.Int).SetUint64(uint64(v))})
- }
- return bigints
-}
-func (i *Interface) GetUint32s() *BigInts {
- val := i.object.(*[]uint32)
- bigints := NewBigInts(len(*val))
- for i, v := range *val {
- bigints.Set(i, &BigInt{new(big.Int).SetUint64(uint64(v))})
- }
- return bigints
-}
-func (i *Interface) GetUint64s() *BigInts {
- val := i.object.(*[]uint64)
- bigints := NewBigInts(len(*val))
- for i, v := range *val {
- bigints.Set(i, &BigInt{new(big.Int).SetUint64(v)})
- }
- return bigints
-}
-func (i *Interface) GetBigInt() *BigInt { return &BigInt{*i.object.(**big.Int)} }
-func (i *Interface) GetBigInts() *BigInts { return &BigInts{*i.object.(*[]*big.Int)} }
-
-// Interfaces is a slices of wrapped generic objects.
-type Interfaces struct {
- objects []interface{}
-}
-
-// NewInterfaces creates a slice of uninitialized interfaces.
-func NewInterfaces(size int) *Interfaces {
- return &Interfaces{objects: make([]interface{}, size)}
-}
-
-// Size returns the number of interfaces in the slice.
-func (i *Interfaces) Size() int {
- return len(i.objects)
-}
-
-// Get returns the bigint at the given index from the slice.
-// Notably the returned value can be changed without affecting the
-// interfaces itself.
-func (i *Interfaces) Get(index int) (iface *Interface, _ error) {
- if index < 0 || index >= len(i.objects) {
- return nil, errors.New("index out of bounds")
- }
- return &Interface{object: i.objects[index]}, nil
-}
-
-// Set sets the big int at the given index in the slice.
-func (i *Interfaces) Set(index int, object *Interface) error {
- if index < 0 || index >= len(i.objects) {
- return errors.New("index out of bounds")
- }
- i.objects[index] = object.object
- return nil
-}
diff --git a/mobile/interface_test.go b/mobile/interface_test.go
deleted file mode 100644
index 4bd1af47aa..0000000000
--- a/mobile/interface_test.go
+++ /dev/null
@@ -1,90 +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 geth
-
-import (
- "fmt"
- "math/big"
- "reflect"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-func TestInterfaceGetSet(t *testing.T) {
- var tests = []struct {
- method string
- input interface{}
- expect interface{}
- }{
- {"Bool", true, true},
- {"Bool", false, false},
- {"Bools", &Bools{[]bool{false, true}}, &Bools{[]bool{false, true}}},
- {"String", "go-ethereum", "go-ethereum"},
- {"Strings", &Strings{strs: []string{"hello", "world"}}, &Strings{strs: []string{"hello", "world"}}},
- {"Binary", []byte{0x01, 0x02}, []byte{0x01, 0x02}},
- {"Binaries", &Binaries{[][]byte{{0x01, 0x02}, {0x03, 0x04}}}, &Binaries{[][]byte{{0x01, 0x02}, {0x03, 0x04}}}},
- {"Address", &Address{common.HexToAddress("deadbeef")}, &Address{common.HexToAddress("deadbeef")}},
- {"Addresses", &Addresses{[]common.Address{common.HexToAddress("deadbeef"), common.HexToAddress("cafebabe")}}, &Addresses{[]common.Address{common.HexToAddress("deadbeef"), common.HexToAddress("cafebabe")}}},
- {"Hash", &Hash{common.HexToHash("deadbeef")}, &Hash{common.HexToHash("deadbeef")}},
- {"Hashes", &Hashes{[]common.Hash{common.HexToHash("deadbeef"), common.HexToHash("cafebabe")}}, &Hashes{[]common.Hash{common.HexToHash("deadbeef"), common.HexToHash("cafebabe")}}},
- {"Int8", int8(1), int8(1)},
- {"Int16", int16(1), int16(1)},
- {"Int32", int32(1), int32(1)},
- {"Int64", int64(1), int64(1)},
- {"Int8s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}},
- {"Int16s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}},
- {"Int32s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}},
- {"Int64s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}},
- {"Uint8", NewBigInt(1), NewBigInt(1)},
- {"Uint16", NewBigInt(1), NewBigInt(1)},
- {"Uint32", NewBigInt(1), NewBigInt(1)},
- {"Uint64", NewBigInt(1), NewBigInt(1)},
- {"Uint8s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}},
- {"Uint16s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}},
- {"Uint32s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}},
- {"Uint64s", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}},
- {"BigInt", NewBigInt(1), NewBigInt(1)},
- {"BigInts", &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}, &BigInts{[]*big.Int{big.NewInt(1), big.NewInt(2)}}},
- }
-
- args := NewInterfaces(len(tests))
-
- callFn := func(receiver interface{}, method string, arg interface{}) interface{} {
- rval := reflect.ValueOf(receiver)
- rval.MethodByName(fmt.Sprintf("Set%s", method)).Call([]reflect.Value{reflect.ValueOf(arg)})
- res := rval.MethodByName(fmt.Sprintf("Get%s", method)).Call(nil)
- if len(res) > 0 {
- return res[0].Interface()
- }
- return nil
- }
-
- for index, c := range tests {
- // In theory the change of iface shouldn't effect the args value
- iface, _ := args.Get(index)
- result := callFn(iface, c.method, c.input)
- if !reflect.DeepEqual(result, c.expect) {
- t.Errorf("Interface get/set mismatch, want %v, got %v", c.expect, result)
- }
- // Check whether the underlying value in args is still zero
- iface, _ = args.Get(index)
- if iface.object != nil {
- t.Error("Get operation is not write safe")
- }
- }
-}
diff --git a/mobile/p2p.go b/mobile/p2p.go
deleted file mode 100644
index a80d9fff2e..0000000000
--- a/mobile/p2p.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains wrappers for the p2p package.
-
-package geth
-
-import (
- "errors"
-
- "github.com/ethereum/go-ethereum/p2p"
-)
-
-// NodeInfo represents pi short summary of the information known about the host.
-type NodeInfo struct {
- info *p2p.NodeInfo
-}
-
-func (ni *NodeInfo) GetID() string { return ni.info.ID }
-func (ni *NodeInfo) GetName() string { return ni.info.Name }
-func (ni *NodeInfo) GetEnode() string { return ni.info.Enode }
-func (ni *NodeInfo) GetIP() string { return ni.info.IP }
-func (ni *NodeInfo) GetDiscoveryPort() int { return ni.info.Ports.Discovery }
-func (ni *NodeInfo) GetListenerPort() int { return ni.info.Ports.Listener }
-func (ni *NodeInfo) GetListenerAddress() string { return ni.info.ListenAddr }
-func (ni *NodeInfo) GetProtocols() *Strings {
- protos := []string{}
- for proto := range ni.info.Protocols {
- protos = append(protos, proto)
- }
- return &Strings{protos}
-}
-
-// PeerInfo represents pi short summary of the information known about pi connected peer.
-type PeerInfo struct {
- info *p2p.PeerInfo
-}
-
-func (pi *PeerInfo) GetID() string { return pi.info.ID }
-func (pi *PeerInfo) GetName() string { return pi.info.Name }
-func (pi *PeerInfo) GetCaps() *Strings { return &Strings{pi.info.Caps} }
-func (pi *PeerInfo) GetLocalAddress() string { return pi.info.Network.LocalAddress }
-func (pi *PeerInfo) GetRemoteAddress() string { return pi.info.Network.RemoteAddress }
-
-// PeerInfos represents a slice of infos about remote peers.
-type PeerInfos struct {
- infos []*p2p.PeerInfo
-}
-
-// Size returns the number of peer info entries in the slice.
-func (pi *PeerInfos) Size() int {
- return len(pi.infos)
-}
-
-// Get returns the peer info at the given index from the slice.
-func (pi *PeerInfos) Get(index int) (info *PeerInfo, _ error) {
- if index < 0 || index >= len(pi.infos) {
- return nil, errors.New("index out of bounds")
- }
- return &PeerInfo{pi.infos[index]}, nil
-}
diff --git a/mobile/params.go b/mobile/params.go
deleted file mode 100644
index 2f4240b2e4..0000000000
--- a/mobile/params.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the params package.
-
-package geth
-
-import (
- "encoding/json"
-
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/params"
-)
-
-// MainnetGenesis returns the JSON spec to use for the main Ethereum network. It
-// is actually empty since that defaults to the hard coded binary genesis block.
-func MainnetGenesis() string {
- return ""
-}
-
-// RopstenGenesis returns the JSON spec to use for the Ropsten test network.
-func RopstenGenesis() string {
- enc, err := json.Marshal(core.DefaultRopstenGenesisBlock())
- if err != nil {
- panic(err)
- }
- return string(enc)
-}
-
-// SepoliaGenesis returns the JSON spec to use for the Sepolia test network.
-func SepoliaGenesis() string {
- enc, err := json.Marshal(core.DefaultSepoliaGenesisBlock())
- if err != nil {
- panic(err)
- }
- return string(enc)
-}
-
-// RinkebyGenesis returns the JSON spec to use for the Rinkeby test network
-func RinkebyGenesis() string {
- enc, err := json.Marshal(core.DefaultRinkebyGenesisBlock())
- if err != nil {
- panic(err)
- }
- return string(enc)
-}
-
-// GoerliGenesis returns the JSON spec to use for the Goerli test network
-func GoerliGenesis() string {
- enc, err := json.Marshal(core.DefaultGoerliGenesisBlock())
- if err != nil {
- panic(err)
- }
- return string(enc)
-}
-
-// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated
-// by the foundation running the V5 discovery protocol.
-func FoundationBootnodes() *Enodes {
- nodes := &Enodes{nodes: make([]*enode.Node, len(params.MainnetBootnodes))}
- for i, url := range params.MainnetBootnodes {
- var err error
- nodes.nodes[i], err = enode.Parse(enode.ValidSchemes, url)
- if err != nil {
- panic("invalid node URL: " + err.Error())
- }
- }
- return nodes
-}
diff --git a/mobile/primitives.go b/mobile/primitives.go
deleted file mode 100644
index 7e1ab26ef0..0000000000
--- a/mobile/primitives.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains various wrappers for primitive types.
-
-package geth
-
-import (
- "errors"
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-// Strings represents s slice of strs.
-type Strings struct{ strs []string }
-
-// Size returns the number of strs in the slice.
-func (s *Strings) Size() int {
- return len(s.strs)
-}
-
-// Get returns the string at the given index from the slice.
-func (s *Strings) Get(index int) (str string, _ error) {
- if index < 0 || index >= len(s.strs) {
- return "", errors.New("index out of bounds")
- }
- return s.strs[index], nil
-}
-
-// Set sets the string at the given index in the slice.
-func (s *Strings) Set(index int, str string) error {
- if index < 0 || index >= len(s.strs) {
- return errors.New("index out of bounds")
- }
- s.strs[index] = str
- return nil
-}
-
-// String implements the Stringer interface.
-func (s *Strings) String() string {
- return fmt.Sprintf("%v", s.strs)
-}
-
-// Bools represents a slice of bool.
-type Bools struct{ bools []bool }
-
-// Size returns the number of bool in the slice.
-func (bs *Bools) Size() int {
- return len(bs.bools)
-}
-
-// Get returns the bool at the given index from the slice.
-func (bs *Bools) Get(index int) (b bool, _ error) {
- if index < 0 || index >= len(bs.bools) {
- return false, errors.New("index out of bounds")
- }
- return bs.bools[index], nil
-}
-
-// Set sets the bool at the given index in the slice.
-func (bs *Bools) Set(index int, b bool) error {
- if index < 0 || index >= len(bs.bools) {
- return errors.New("index out of bounds")
- }
- bs.bools[index] = b
- return nil
-}
-
-// String implements the Stringer interface.
-func (bs *Bools) String() string {
- return fmt.Sprintf("%v", bs.bools)
-}
-
-// Binaries represents a slice of byte slice
-type Binaries struct{ binaries [][]byte }
-
-// Size returns the number of byte slice in the slice.
-func (bs *Binaries) Size() int {
- return len(bs.binaries)
-}
-
-// Get returns the byte slice at the given index from the slice.
-func (bs *Binaries) Get(index int) (binary []byte, _ error) {
- if index < 0 || index >= len(bs.binaries) {
- return nil, errors.New("index out of bounds")
- }
- return common.CopyBytes(bs.binaries[index]), nil
-}
-
-// Set sets the byte slice at the given index in the slice.
-func (bs *Binaries) Set(index int, binary []byte) error {
- if index < 0 || index >= len(bs.binaries) {
- return errors.New("index out of bounds")
- }
- bs.binaries[index] = common.CopyBytes(binary)
- return nil
-}
-
-// String implements the Stringer interface.
-func (bs *Binaries) String() string {
- return fmt.Sprintf("%v", bs.binaries)
-}
diff --git a/mobile/types.go b/mobile/types.go
deleted file mode 100644
index f3f92e4d4a..0000000000
--- a/mobile/types.go
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the core/types package.
-
-package geth
-
-import (
- "encoding/json"
- "errors"
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-type jsonEncoder interface {
- EncodeJSON() (string, error)
-}
-
-// encodeOrError tries to encode the object into json.
-// If the encoding fails the resulting error is returned.
-func encodeOrError(encoder jsonEncoder) string {
- enc, err := encoder.EncodeJSON()
- if err != nil {
- return err.Error()
- }
- return enc
-}
-
-// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that
-// a sufficient amount of computation has been carried out on a block.
-type Nonce struct {
- nonce types.BlockNonce
-}
-
-// GetBytes retrieves the byte representation of the block nonce.
-func (n *Nonce) GetBytes() []byte {
- return n.nonce[:]
-}
-
-// GetHex retrieves the hex string representation of the block nonce.
-func (n *Nonce) GetHex() string {
- return fmt.Sprintf("%#x", n.nonce[:])
-}
-
-// String returns a printable representation of the nonce.
-func (n *Nonce) String() string {
- return n.GetHex()
-}
-
-// Bloom represents a 256 bit bloom filter.
-type Bloom struct {
- bloom types.Bloom
-}
-
-// GetBytes retrieves the byte representation of the bloom filter.
-func (b *Bloom) GetBytes() []byte {
- return b.bloom[:]
-}
-
-// GetHex retrieves the hex string representation of the bloom filter.
-func (b *Bloom) GetHex() string {
- return fmt.Sprintf("%#x", b.bloom[:])
-}
-
-// String returns a printable representation of the bloom filter.
-func (b *Bloom) String() string {
- return b.GetHex()
-}
-
-// Header represents a block header in the Ethereum blockchain.
-type Header struct {
- header *types.Header
-}
-
-// NewHeaderFromRLP parses a header from an RLP data dump.
-func NewHeaderFromRLP(data []byte) (*Header, error) {
- h := &Header{
- header: new(types.Header),
- }
- if err := rlp.DecodeBytes(common.CopyBytes(data), h.header); err != nil {
- return nil, err
- }
- return h, nil
-}
-
-// EncodeRLP encodes a header into an RLP data dump.
-func (h *Header) EncodeRLP() ([]byte, error) {
- return rlp.EncodeToBytes(h.header)
-}
-
-// NewHeaderFromJSON parses a header from a JSON data dump.
-func NewHeaderFromJSON(data string) (*Header, error) {
- h := &Header{
- header: new(types.Header),
- }
- if err := json.Unmarshal([]byte(data), h.header); err != nil {
- return nil, err
- }
- return h, nil
-}
-
-// EncodeJSON encodes a header into a JSON data dump.
-func (h *Header) EncodeJSON() (string, error) {
- data, err := json.Marshal(h.header)
- return string(data), err
-}
-
-// String returns a printable representation of the header.
-func (h *Header) String() string {
- return encodeOrError(h)
-}
-
-func (h *Header) GetParentHash() *Hash { return &Hash{h.header.ParentHash} }
-func (h *Header) GetUncleHash() *Hash { return &Hash{h.header.UncleHash} }
-func (h *Header) GetCoinbase() *Address { return &Address{h.header.Coinbase} }
-func (h *Header) GetRoot() *Hash { return &Hash{h.header.Root} }
-func (h *Header) GetTxHash() *Hash { return &Hash{h.header.TxHash} }
-func (h *Header) GetReceiptHash() *Hash { return &Hash{h.header.ReceiptHash} }
-func (h *Header) GetBloom() *Bloom { return &Bloom{h.header.Bloom} }
-func (h *Header) GetDifficulty() *BigInt { return &BigInt{h.header.Difficulty} }
-func (h *Header) GetNumber() int64 { return h.header.Number.Int64() }
-func (h *Header) GetGasLimit() int64 { return int64(h.header.GasLimit) }
-func (h *Header) GetGasUsed() int64 { return int64(h.header.GasUsed) }
-func (h *Header) GetTime() int64 { return int64(h.header.Time) }
-func (h *Header) GetExtra() []byte { return h.header.Extra }
-func (h *Header) GetMixDigest() *Hash { return &Hash{h.header.MixDigest} }
-func (h *Header) GetNonce() *Nonce { return &Nonce{h.header.Nonce} }
-func (h *Header) GetHash() *Hash { return &Hash{h.header.Hash()} }
-
-// Headers represents a slice of headers.
-type Headers struct{ headers []*types.Header }
-
-// Size returns the number of headers in the slice.
-func (h *Headers) Size() int {
- return len(h.headers)
-}
-
-// Get returns the header at the given index from the slice.
-func (h *Headers) Get(index int) (header *Header, _ error) {
- if index < 0 || index >= len(h.headers) {
- return nil, errors.New("index out of bounds")
- }
- return &Header{h.headers[index]}, nil
-}
-
-// Block represents an entire block in the Ethereum blockchain.
-type Block struct {
- block *types.Block
-}
-
-// NewBlockFromRLP parses a block from an RLP data dump.
-func NewBlockFromRLP(data []byte) (*Block, error) {
- b := &Block{
- block: new(types.Block),
- }
- if err := rlp.DecodeBytes(common.CopyBytes(data), b.block); err != nil {
- return nil, err
- }
- return b, nil
-}
-
-// EncodeRLP encodes a block into an RLP data dump.
-func (b *Block) EncodeRLP() ([]byte, error) {
- return rlp.EncodeToBytes(b.block)
-}
-
-// NewBlockFromJSON parses a block from a JSON data dump.
-func NewBlockFromJSON(data string) (*Block, error) {
- b := &Block{
- block: new(types.Block),
- }
- if err := json.Unmarshal([]byte(data), b.block); err != nil {
- return nil, err
- }
- return b, nil
-}
-
-// EncodeJSON encodes a block into a JSON data dump.
-func (b *Block) EncodeJSON() (string, error) {
- data, err := json.Marshal(b.block)
- return string(data), err
-}
-
-// String returns a printable representation of the block.
-func (b *Block) String() string {
- return encodeOrError(b)
-}
-
-func (b *Block) GetParentHash() *Hash { return &Hash{b.block.ParentHash()} }
-func (b *Block) GetUncleHash() *Hash { return &Hash{b.block.UncleHash()} }
-func (b *Block) GetCoinbase() *Address { return &Address{b.block.Coinbase()} }
-func (b *Block) GetRoot() *Hash { return &Hash{b.block.Root()} }
-func (b *Block) GetTxHash() *Hash { return &Hash{b.block.TxHash()} }
-func (b *Block) GetReceiptHash() *Hash { return &Hash{b.block.ReceiptHash()} }
-func (b *Block) GetBloom() *Bloom { return &Bloom{b.block.Bloom()} }
-func (b *Block) GetDifficulty() *BigInt { return &BigInt{b.block.Difficulty()} }
-func (b *Block) GetNumber() int64 { return b.block.Number().Int64() }
-func (b *Block) GetGasLimit() int64 { return int64(b.block.GasLimit()) }
-func (b *Block) GetGasUsed() int64 { return int64(b.block.GasUsed()) }
-func (b *Block) GetTime() int64 { return int64(b.block.Time()) }
-func (b *Block) GetExtra() []byte { return b.block.Extra() }
-func (b *Block) GetMixDigest() *Hash { return &Hash{b.block.MixDigest()} }
-func (b *Block) GetNonce() int64 { return int64(b.block.Nonce()) }
-func (b *Block) GetHash() *Hash { return &Hash{b.block.Hash()} }
-func (b *Block) GetHeader() *Header { return &Header{b.block.Header()} }
-func (b *Block) GetUncles() *Headers { return &Headers{b.block.Uncles()} }
-func (b *Block) GetTransactions() *Transactions { return &Transactions{b.block.Transactions()} }
-func (b *Block) GetTransaction(hash *Hash) *Transaction {
- return &Transaction{b.block.Transaction(hash.hash)}
-}
-
-// Transaction represents a single Ethereum transaction.
-type Transaction struct {
- tx *types.Transaction
-}
-
-// NewContractCreation creates a new transaction for deploying a new contract with
-// the given properties.
-func NewContractCreation(nonce int64, amount *BigInt, gasLimit int64, gasPrice *BigInt, data []byte) *Transaction {
- return &Transaction{types.NewContractCreation(uint64(nonce), amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))}
-}
-
-// NewTransaction creates a new transaction with the given properties. Contracts
-// can be created by transacting with a nil recipient.
-func NewTransaction(nonce int64, to *Address, amount *BigInt, gasLimit int64, gasPrice *BigInt, data []byte) *Transaction {
- if to == nil {
- return &Transaction{types.NewContractCreation(uint64(nonce), amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))}
- }
- return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))}
-}
-
-// NewTransactionFromRLP parses a transaction from an RLP data dump.
-func NewTransactionFromRLP(data []byte) (*Transaction, error) {
- tx := &Transaction{
- tx: new(types.Transaction),
- }
- if err := rlp.DecodeBytes(common.CopyBytes(data), tx.tx); err != nil {
- return nil, err
- }
- return tx, nil
-}
-
-// EncodeRLP encodes a transaction into an RLP data dump.
-func (tx *Transaction) EncodeRLP() ([]byte, error) {
- return rlp.EncodeToBytes(tx.tx)
-}
-
-// NewTransactionFromJSON parses a transaction from a JSON data dump.
-func NewTransactionFromJSON(data string) (*Transaction, error) {
- tx := &Transaction{
- tx: new(types.Transaction),
- }
- if err := json.Unmarshal([]byte(data), tx.tx); err != nil {
- return nil, err
- }
- return tx, nil
-}
-
-// EncodeJSON encodes a transaction into a JSON data dump.
-func (tx *Transaction) EncodeJSON() (string, error) {
- data, err := json.Marshal(tx.tx)
- return string(data), err
-}
-
-// String returns a printable representation of the transaction.
-func (tx *Transaction) String() string {
- return encodeOrError(tx)
-}
-
-func (tx *Transaction) GetData() []byte { return tx.tx.Data() }
-func (tx *Transaction) GetGas() int64 { return int64(tx.tx.Gas()) }
-func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} }
-func (tx *Transaction) GetValue() *BigInt { return &BigInt{tx.tx.Value()} }
-func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) }
-
-func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} }
-func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} }
-
-func (tx *Transaction) GetTo() *Address {
- if to := tx.tx.To(); to != nil {
- return &Address{*to}
- }
- return nil
-}
-
-func (tx *Transaction) WithSignature(sig []byte, chainID *BigInt) (signedTx *Transaction, _ error) {
- var signer types.Signer = types.HomesteadSigner{}
- if chainID != nil {
- signer = types.NewEIP155Signer(chainID.bigint)
- }
- rawTx, err := tx.tx.WithSignature(signer, common.CopyBytes(sig))
- return &Transaction{rawTx}, err
-}
-
-// Transactions represents a slice of transactions.
-type Transactions struct{ txs types.Transactions }
-
-// Size returns the number of transactions in the slice.
-func (txs *Transactions) Size() int {
- return len(txs.txs)
-}
-
-// Get returns the transaction at the given index from the slice.
-func (txs *Transactions) Get(index int) (tx *Transaction, _ error) {
- if index < 0 || index >= len(txs.txs) {
- return nil, errors.New("index out of bounds")
- }
- return &Transaction{txs.txs[index]}, nil
-}
-
-// Receipt represents the results of a transaction.
-type Receipt struct {
- receipt *types.Receipt
-}
-
-// NewReceiptFromRLP parses a transaction receipt from an RLP data dump.
-func NewReceiptFromRLP(data []byte) (*Receipt, error) {
- r := &Receipt{
- receipt: new(types.Receipt),
- }
- if err := rlp.DecodeBytes(common.CopyBytes(data), r.receipt); err != nil {
- return nil, err
- }
- return r, nil
-}
-
-// EncodeRLP encodes a transaction receipt into an RLP data dump.
-func (r *Receipt) EncodeRLP() ([]byte, error) {
- return rlp.EncodeToBytes(r.receipt)
-}
-
-// NewReceiptFromJSON parses a transaction receipt from a JSON data dump.
-func NewReceiptFromJSON(data string) (*Receipt, error) {
- r := &Receipt{
- receipt: new(types.Receipt),
- }
- if err := json.Unmarshal([]byte(data), r.receipt); err != nil {
- return nil, err
- }
- return r, nil
-}
-
-// EncodeJSON encodes a transaction receipt into a JSON data dump.
-func (r *Receipt) EncodeJSON() (string, error) {
- data, err := json.Marshal(r.receipt)
- return string(data), err
-}
-
-// String returns a printable representation of the receipt.
-func (r *Receipt) String() string {
- return encodeOrError(r)
-}
-
-func (r *Receipt) GetStatus() int { return int(r.receipt.Status) }
-func (r *Receipt) GetPostState() []byte { return r.receipt.PostState }
-func (r *Receipt) GetCumulativeGasUsed() int64 { return int64(r.receipt.CumulativeGasUsed) }
-func (r *Receipt) GetBloom() *Bloom { return &Bloom{r.receipt.Bloom} }
-func (r *Receipt) GetLogs() *Logs { return &Logs{r.receipt.Logs} }
-func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} }
-func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} }
-func (r *Receipt) GetGasUsed() int64 { return int64(r.receipt.GasUsed) }
diff --git a/mobile/vm.go b/mobile/vm.go
deleted file mode 100644
index 72093e3d5b..0000000000
--- a/mobile/vm.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 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 .
-
-// Contains all the wrappers from the core/types package.
-
-package geth
-
-import (
- "errors"
-
- "github.com/ethereum/go-ethereum/core/types"
-)
-
-// Log represents a contract log event. These events are generated by the LOG
-// opcode and stored/indexed by the node.
-type Log struct {
- log *types.Log
-}
-
-func (l *Log) GetAddress() *Address { return &Address{l.log.Address} }
-func (l *Log) GetTopics() *Hashes { return &Hashes{l.log.Topics} }
-func (l *Log) GetData() []byte { return l.log.Data }
-func (l *Log) GetBlockNumber() int64 { return int64(l.log.BlockNumber) }
-func (l *Log) GetTxHash() *Hash { return &Hash{l.log.TxHash} }
-func (l *Log) GetTxIndex() int { return int(l.log.TxIndex) }
-func (l *Log) GetBlockHash() *Hash { return &Hash{l.log.BlockHash} }
-func (l *Log) GetIndex() int { return int(l.log.Index) }
-
-// Logs represents a slice of VM logs.
-type Logs struct{ logs []*types.Log }
-
-// Size returns the number of logs in the slice.
-func (l *Logs) Size() int {
- return len(l.logs)
-}
-
-// Get returns the log at the given index from the slice.
-func (l *Logs) Get(index int) (log *Log, _ error) {
- if index < 0 || index >= len(l.logs) {
- return nil, errors.New("index out of bounds")
- }
- return &Log{l.logs[index]}, nil
-}
diff --git a/node/api.go b/node/api.go
index 67953a812e..15892a270b 100644
--- a/node/api.go
+++ b/node/api.go
@@ -269,7 +269,7 @@ func (api *adminAPI) StartWS(host *string, port *int, allowedOrigins *string, ap
if err := server.setListenAddr(*host, *port); err != nil {
return false, err
}
- openApis, _ := api.node.GetAPIs()
+ openApis, _ := api.node.getAPIs()
if err := server.enableWS(openApis, config); err != nil {
return false, err
}
diff --git a/node/api_test.go b/node/api_test.go
index d76cb943e4..8761c4883e 100644
--- a/node/api_test.go
+++ b/node/api_test.go
@@ -252,6 +252,9 @@ func TestStartRPC(t *testing.T) {
config := test.cfg
// config.Logger = testlog.Logger(t, log.LvlDebug)
config.P2P.NoDiscovery = true
+ if config.HTTPTimeouts == (rpc.HTTPTimeouts{}) {
+ config.HTTPTimeouts = rpc.DefaultHTTPTimeouts
+ }
// Create Node.
stack, err := New(&config)
diff --git a/node/config.go b/node/config.go
index 2047299fb5..37a7d5837f 100644
--- a/node/config.go
+++ b/node/config.go
@@ -23,13 +23,11 @@ import (
"path/filepath"
"runtime"
"strings"
- "sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -194,15 +192,18 @@ type Config struct {
// Logger is a custom logger to use with the p2p.Server.
Logger log.Logger `toml:",omitempty"`
- staticNodesWarning bool
- trustedNodesWarning bool
oldGethResourceWarning bool
// AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC.
AllowUnprotectedTxs bool `toml:",omitempty"`
- // JWTSecret is the hex-encoded jwt secret.
+ // JWTSecret is the path to the hex-encoded jwt secret.
JWTSecret string `toml:",omitempty"`
+
+ // EnablePersonal enables the deprecated personal namespace.
+ EnablePersonal bool `toml:"-"`
+
+ DBEngine string `toml:",omitempty"`
}
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into
@@ -340,8 +341,9 @@ func (c *Config) ResolvePath(path string) string {
oldpath = filepath.Join(c.DataDir, path)
}
if oldpath != "" && common.FileExist(oldpath) {
- if warn {
- c.warnOnce(&c.oldGethResourceWarning, "Using deprecated resource file %s, please move this file to the 'geth' subdirectory of datadir.", oldpath)
+ if warn && !c.oldGethResourceWarning {
+ c.oldGethResourceWarning = true
+ log.Warn("Using deprecated resource file, please move this file to the 'geth' subdirectory of datadir.", "file", oldpath)
}
return oldpath
}
@@ -394,48 +396,35 @@ func (c *Config) NodeKey() *ecdsa.PrivateKey {
return key
}
-// StaticNodes returns a list of node enode URLs configured as static nodes.
-func (c *Config) StaticNodes() []*enode.Node {
- return c.parsePersistentNodes(&c.staticNodesWarning, c.ResolvePath(datadirStaticNodes))
+// CheckLegacyFiles inspects the datadir for signs of legacy static-nodes
+// and trusted-nodes files. If they exist it raises an error.
+func (c *Config) checkLegacyFiles() {
+ c.checkLegacyFile(c.ResolvePath(datadirStaticNodes))
+ c.checkLegacyFile(c.ResolvePath(datadirTrustedNodes))
}
-// TrustedNodes returns a list of node enode URLs configured as trusted nodes.
-func (c *Config) TrustedNodes() []*enode.Node {
- return c.parsePersistentNodes(&c.trustedNodesWarning, c.ResolvePath(datadirTrustedNodes))
-}
-
-// parsePersistentNodes parses a list of discovery node URLs loaded from a .json
-// file from within the data directory.
-func (c *Config) parsePersistentNodes(w *bool, path string) []*enode.Node {
+// checkLegacyFile will only raise an error if a file at the given path exists.
+func (c *Config) checkLegacyFile(path string) {
// Short circuit if no node config is present
if c.DataDir == "" {
- return nil
+ return
}
if _, err := os.Stat(path); err != nil {
- return nil
+ return
}
- c.warnOnce(w, "Found deprecated node list file %s, please use the TOML config file instead.", path)
-
- // Load the nodes from the config file.
- var nodelist []string
- if err := common.LoadJSON(path, &nodelist); err != nil {
- log.Error(fmt.Sprintf("Can't load node list file: %v", err))
- return nil
+ logger := c.Logger
+ if logger == nil {
+ logger = log.Root()
}
- // Interpret the list as a discovery node array
- var nodes []*enode.Node
- for _, url := range nodelist {
- if url == "" {
- continue
- }
- node, err := enode.Parse(enode.ValidSchemes, url)
- if err != nil {
- log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err))
- continue
- }
- nodes = append(nodes, node)
+ switch fname := filepath.Base(path); fname {
+ case "static-nodes.json":
+ logger.Error("The static-nodes.json file is deprecated and ignored. Use P2P.StaticNodes in config.toml instead.")
+ case "trusted-nodes.json":
+ logger.Error("The trusted-nodes.json file is deprecated and ignored. Use P2P.TrustedNodes in config.toml instead.")
+ default:
+ // We shouldn't wind up here, but better print something just in case.
+ logger.Error("Ignoring deprecated file.", "file", path)
}
- return nodes
}
// KeyDirConfig determines the settings for keydirectory
@@ -482,20 +471,3 @@ func getKeyStoreDir(conf *Config) (string, bool, error) {
return keydir, isEphemeral, nil
}
-
-var warnLock sync.Mutex
-
-func (c *Config) warnOnce(w *bool, format string, args ...interface{}) {
- warnLock.Lock()
- defer warnLock.Unlock()
-
- if *w {
- return
- }
- l := c.Logger
- if l == nil {
- l = log.Root()
- }
- l.Warn(fmt.Sprintf(format, args...))
- *w = true
-}
diff --git a/node/defaults.go b/node/defaults.go
index fd0277e29d..96ebed81c5 100644
--- a/node/defaults.go
+++ b/node/defaults.go
@@ -64,6 +64,7 @@ var DefaultConfig = Config{
MaxPeers: 50,
NAT: nat.Any(),
},
+ DBEngine: "",
}
// DefaultDataDir is the default data directory to use for the databases and other
diff --git a/node/doc.go b/node/doc.go
index b257f412fe..4474e43660 100644
--- a/node/doc.go
+++ b/node/doc.go
@@ -21,25 +21,22 @@ In the model exposed by this package, a node is a collection of services which u
resources to provide RPC APIs. Services can also offer devp2p protocols, which are wired
up to the devp2p network when the node instance is started.
-
-Node Lifecycle
+# Node Lifecycle
The Node object has a lifecycle consisting of three basic states, INITIALIZING, RUNNING
and CLOSED.
-
- ●───────┐
- New()
- │
- ▼
- INITIALIZING ────Start()─┐
- │ │
- │ ▼
- Close() RUNNING
- │ │
- ▼ │
- CLOSED ◀──────Close()─┘
-
+ ●───────┐
+ New()
+ │
+ ▼
+ INITIALIZING ────Start()─┐
+ │ │
+ │ ▼
+ Close() RUNNING
+ │ │
+ ▼ │
+ CLOSED ◀──────Close()─┘
Creating a Node allocates basic resources such as the data directory and returns the node
in its INITIALIZING state. Lifecycle objects, RPC APIs and peer-to-peer networking
@@ -58,8 +55,7 @@ objects and shuts down RPC and peer-to-peer networking.
You must always call Close on Node, even if the node was not started.
-
-Resources Managed By Node
+# Resources Managed By Node
All file-system resources used by a node instance are located in a directory called the
data directory. The location of each resource can be overridden through additional node
@@ -83,8 +79,7 @@ without a data directory, databases are opened in memory instead.
Node also creates the shared store of encrypted Ethereum account keys. Services can access
the account manager through the service context.
-
-Sharing Data Directory Among Instances
+# Sharing Data Directory Among Instances
Multiple node instances can share a single data directory if they have distinct instance
names (set through the Name config option). Sharing behaviour depends on the type of
@@ -102,26 +97,25 @@ create one database for each instance.
The account key store is shared among all node instances using the same data directory
unless its location is changed through the KeyStoreDir configuration option.
-
-Data Directory Sharing Example
+# Data Directory Sharing Example
In this example, two node instances named A and B are started with the same data
directory. Node instance A opens the database "db", node instance B opens the databases
"db" and "db-2". The following files will be created in the data directory:
- data-directory/
- A/
- nodekey -- devp2p node key of instance A
- nodes/ -- devp2p discovery knowledge database of instance A
- db/ -- LevelDB content for "db"
- A.ipc -- JSON-RPC UNIX domain socket endpoint of instance A
- B/
- nodekey -- devp2p node key of node B
- nodes/ -- devp2p discovery knowledge database of instance B
- static-nodes.json -- devp2p static node list of instance B
- db/ -- LevelDB content for "db"
- db-2/ -- LevelDB content for "db-2"
- B.ipc -- JSON-RPC UNIX domain socket endpoint of instance B
- keystore/ -- account key store, used by both instances
+ data-directory/
+ A/
+ nodekey -- devp2p node key of instance A
+ nodes/ -- devp2p discovery knowledge database of instance A
+ db/ -- LevelDB content for "db"
+ A.ipc -- JSON-RPC UNIX domain socket endpoint of instance A
+ B/
+ nodekey -- devp2p node key of node B
+ nodes/ -- devp2p discovery knowledge database of instance B
+ static-nodes.json -- devp2p static node list of instance B
+ db/ -- LevelDB content for "db"
+ db-2/ -- LevelDB content for "db-2"
+ B.ipc -- JSON-RPC UNIX domain socket endpoint of instance B
+ keystore/ -- account key store, used by both instances
*/
package node
diff --git a/node/jwt_auth.go b/node/jwt_auth.go
new file mode 100644
index 0000000000..d4f8193ca7
--- /dev/null
+++ b/node/jwt_auth.go
@@ -0,0 +1,45 @@
+// 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 node
+
+import (
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/golang-jwt/jwt/v4"
+)
+
+// NewJWTAuth creates an rpc client authentication provider that uses JWT. The
+// secret MUST be 32 bytes (256 bits) as defined by the Engine-API authentication spec.
+//
+// See https://github.com/ethereum/execution-apis/blob/main/src/engine/authentication.md
+// for more details about this authentication scheme.
+func NewJWTAuth(jwtsecret [32]byte) rpc.HTTPAuth {
+ return func(h http.Header) error {
+ token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
+ "iat": &jwt.NumericDate{Time: time.Now()},
+ })
+ s, err := token.SignedString(jwtsecret[:])
+ if err != nil {
+ return fmt.Errorf("failed to create JWT token: %w", err)
+ }
+ h.Set("Authorization", "Bearer "+s)
+ return nil
+ }
+}
diff --git a/node/jwt_handler.go b/node/jwt_handler.go
index 363f6b3aad..637ae19686 100644
--- a/node/jwt_handler.go
+++ b/node/jwt_handler.go
@@ -51,7 +51,7 @@ func (handler *jwtHandler) ServeHTTP(out http.ResponseWriter, r *http.Request) {
strToken = strings.TrimPrefix(auth, "Bearer ")
}
if len(strToken) == 0 {
- http.Error(out, "missing token", http.StatusForbidden)
+ http.Error(out, "missing token", http.StatusUnauthorized)
return
}
// We explicitly set only HS256 allowed, and also disables the
@@ -63,17 +63,17 @@ func (handler *jwtHandler) ServeHTTP(out http.ResponseWriter, r *http.Request) {
switch {
case err != nil:
- http.Error(out, err.Error(), http.StatusForbidden)
+ http.Error(out, err.Error(), http.StatusUnauthorized)
case !token.Valid:
- http.Error(out, "invalid token", http.StatusForbidden)
+ http.Error(out, "invalid token", http.StatusUnauthorized)
case !claims.VerifyExpiresAt(time.Now(), false): // optional
- http.Error(out, "token is expired", http.StatusForbidden)
+ http.Error(out, "token is expired", http.StatusUnauthorized)
case claims.IssuedAt == nil:
- http.Error(out, "missing issued-at", http.StatusForbidden)
+ http.Error(out, "missing issued-at", http.StatusUnauthorized)
case time.Since(claims.IssuedAt.Time) > jwtExpiryTimeout:
- http.Error(out, "stale token", http.StatusForbidden)
+ http.Error(out, "stale token", http.StatusUnauthorized)
case time.Until(claims.IssuedAt.Time) > jwtExpiryTimeout:
- http.Error(out, "future token", http.StatusForbidden)
+ http.Error(out, "future token", http.StatusUnauthorized)
default:
handler.next.ServeHTTP(out, r)
}
diff --git a/node/node.go b/node/node.go
index b60e32f22f..112a771ab0 100644
--- a/node/node.go
+++ b/node/node.go
@@ -133,12 +133,7 @@ func New(conf *Config) (*Node, error) {
node.server.Config.PrivateKey = node.config.NodeKey()
node.server.Config.Name = node.config.NodeName()
node.server.Config.Logger = node.log
- if node.server.Config.StaticNodes == nil {
- node.server.Config.StaticNodes = node.config.StaticNodes()
- }
- if node.server.Config.TrustedNodes == nil {
- node.server.Config.TrustedNodes = node.config.TrustedNodes()
- }
+ node.config.checkLegacyFiles()
if node.server.Config.NodeDatabase == "" {
node.server.Config.NodeDatabase = node.config.NodeDB()
}
@@ -381,26 +376,38 @@ func (n *Node) obtainJWTSecret(cliParam string) ([]byte, error) {
// startup. It's not meant to be called at any time afterwards as it makes certain
// assumptions about the state of the node.
func (n *Node) startRPC() error {
- if err := n.startInProc(); err != nil {
+ // Filter out personal api
+ var apis []rpc.API
+ for _, api := range n.rpcAPIs {
+ if api.Namespace == "personal" {
+ if n.config.EnablePersonal {
+ log.Warn("Deprecated personal namespace activated")
+ } else {
+ continue
+ }
+ }
+ apis = append(apis, api)
+ }
+ if err := n.startInProc(apis); err != nil {
return err
}
// Configure IPC.
if n.ipc.endpoint != "" {
- if err := n.ipc.start(n.rpcAPIs); err != nil {
+ if err := n.ipc.start(apis); err != nil {
return err
}
}
var (
- servers []*httpServer
- open, all = n.GetAPIs()
+ servers []*httpServer
+ openAPIs, allAPIs = n.getAPIs()
)
- initHttp := func(server *httpServer, apis []rpc.API, port int) error {
+ initHttp := func(server *httpServer, port int) error {
if err := server.setListenAddr(n.config.HTTPHost, port); err != nil {
return err
}
- if err := server.enableRPC(apis, httpConfig{
+ if err := server.enableRPC(openAPIs, httpConfig{
CorsAllowedOrigins: n.config.HTTPCors,
Vhosts: n.config.HTTPVirtualHosts,
Modules: n.config.HTTPModules,
@@ -412,12 +419,12 @@ func (n *Node) startRPC() error {
return nil
}
- initWS := func(apis []rpc.API, port int) error {
+ initWS := func(port int) error {
server := n.wsServerForPort(port, false)
if err := server.setListenAddr(n.config.WSHost, port); err != nil {
return err
}
- if err := server.enableWS(n.rpcAPIs, wsConfig{
+ if err := server.enableWS(openAPIs, wsConfig{
Modules: n.config.WSModules,
Origins: n.config.WSOrigins,
prefix: n.config.WSPathPrefix,
@@ -428,13 +435,13 @@ func (n *Node) startRPC() error {
return nil
}
- initAuth := func(apis []rpc.API, port int, secret []byte) error {
+ initAuth := func(port int, secret []byte) error {
// Enable auth via HTTP
server := n.httpAuth
if err := server.setListenAddr(n.config.AuthAddr, port); err != nil {
return err
}
- if err := server.enableRPC(apis, httpConfig{
+ if err := server.enableRPC(allAPIs, httpConfig{
CorsAllowedOrigins: DefaultAuthCors,
Vhosts: n.config.AuthVirtualHosts,
Modules: DefaultAuthModules,
@@ -449,7 +456,7 @@ func (n *Node) startRPC() error {
if err := server.setListenAddr(n.config.AuthAddr, port); err != nil {
return err
}
- if err := server.enableWS(apis, wsConfig{
+ if err := server.enableWS(allAPIs, wsConfig{
Modules: DefaultAuthModules,
Origins: DefaultAuthOrigins,
prefix: DefaultAuthPrefix,
@@ -464,24 +471,24 @@ func (n *Node) startRPC() error {
// Set up HTTP.
if n.config.HTTPHost != "" {
// Configure legacy unauthenticated HTTP.
- if err := initHttp(n.http, open, n.config.HTTPPort); err != nil {
+ if err := initHttp(n.http, n.config.HTTPPort); err != nil {
return err
}
}
// Configure WebSocket.
if n.config.WSHost != "" {
// legacy unauthenticated
- if err := initWS(open, n.config.WSPort); err != nil {
+ if err := initWS(n.config.WSPort); err != nil {
return err
}
}
// Configure authenticated API
- if len(open) != len(all) {
+ if len(openAPIs) != len(allAPIs) {
jwtSecret, err := n.obtainJWTSecret(n.config.JWTSecret)
if err != nil {
return err
}
- if err := initAuth(all, n.config.AuthPort, jwtSecret); err != nil {
+ if err := initAuth(n.config.AuthPort, jwtSecret); err != nil {
return err
}
}
@@ -515,8 +522,8 @@ func (n *Node) stopRPC() {
}
// startInProc registers all RPC APIs on the inproc server.
-func (n *Node) startInProc() error {
- for _, api := range n.rpcAPIs {
+func (n *Node) startInProc(apis []rpc.API) error {
+ for _, api := range apis {
if err := n.inprocHandler.RegisterName(api.Namespace, api.Service); err != nil {
return err
}
@@ -570,9 +577,9 @@ func (n *Node) RegisterAPIs(apis []rpc.API) {
n.rpcAPIs = append(n.rpcAPIs, apis...)
}
-// GetAPIs return two sets of APIs, both the ones that do not require
+// getAPIs return two sets of APIs, both the ones that do not require
// authentication, and the complete set
-func (n *Node) GetAPIs() (unauthenticated, all []rpc.API) {
+func (n *Node) getAPIs() (unauthenticated, all []rpc.API) {
for _, api := range n.rpcAPIs {
if !api.Authenticated {
unauthenticated = append(unauthenticated, api)
@@ -668,6 +675,19 @@ func (n *Node) WSEndpoint() string {
return "ws://" + n.ws.listenAddr() + n.ws.wsConfig.prefix
}
+// HTTPAuthEndpoint returns the URL of the authenticated HTTP server.
+func (n *Node) HTTPAuthEndpoint() string {
+ return "http://" + n.httpAuth.listenAddr()
+}
+
+// WSAuthEndpoint returns the current authenticated JSON-RPC over WebSocket endpoint.
+func (n *Node) WSAuthEndpoint() string {
+ if n.httpAuth.wsAllowed() {
+ return "ws://" + n.httpAuth.listenAddr() + n.httpAuth.wsConfig.prefix
+ }
+ return "ws://" + n.wsAuth.listenAddr() + n.wsAuth.wsConfig.prefix
+}
+
// EventMux retrieves the event multiplexer used by all the network services in
// the current protocol stack.
func (n *Node) EventMux() *event.TypeMux {
@@ -689,7 +709,14 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, r
if n.config.DataDir == "" {
db = rawdb.NewMemoryDatabase()
} else {
- db, err = rawdb.NewLevelDBDatabase(n.ResolvePath(name), cache, handles, namespace, readonly)
+ db, err = rawdb.Open(rawdb.OpenOptions{
+ Type: n.config.DBEngine,
+ Directory: n.ResolvePath(name),
+ Namespace: namespace,
+ Cache: cache,
+ Handles: handles,
+ ReadOnly: readonly,
+ })
}
if err == nil {
@@ -709,13 +736,20 @@ func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, ancient
if n.state == closedState {
return nil, ErrNodeStopped
}
-
var db ethdb.Database
var err error
if n.config.DataDir == "" {
db = rawdb.NewMemoryDatabase()
} else {
- db, err = rawdb.NewLevelDBDatabaseWithFreezer(n.ResolvePath(name), cache, handles, n.ResolveAncient(name, ancient), namespace, readonly)
+ db, err = rawdb.Open(rawdb.OpenOptions{
+ Type: n.config.DBEngine,
+ Directory: n.ResolvePath(name),
+ AncientsDirectory: n.ResolveAncient(name, ancient),
+ Namespace: namespace,
+ Cache: cache,
+ Handles: handles,
+ ReadOnly: readonly,
+ })
}
if err == nil {
diff --git a/node/node_auth_test.go b/node/node_auth_test.go
new file mode 100644
index 0000000000..597cd8531f
--- /dev/null
+++ b/node/node_auth_test.go
@@ -0,0 +1,237 @@
+// 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 node
+
+import (
+ "context"
+ crand "crypto/rand"
+ "fmt"
+ "net/http"
+ "os"
+ "path"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/golang-jwt/jwt/v4"
+)
+
+type helloRPC string
+
+func (ta helloRPC) HelloWorld() (string, error) {
+ return string(ta), nil
+}
+
+type authTest struct {
+ name string
+ endpoint string
+ prov rpc.HTTPAuth
+ expectDialFail bool
+ expectCall1Fail bool
+ expectCall2Fail bool
+}
+
+func (at *authTest) Run(t *testing.T) {
+ ctx := context.Background()
+ cl, err := rpc.DialOptions(ctx, at.endpoint, rpc.WithHTTPAuth(at.prov))
+ if at.expectDialFail {
+ if err == nil {
+ t.Fatal("expected initial dial to fail")
+ } else {
+ return
+ }
+ }
+ if err != nil {
+ t.Fatalf("failed to dial rpc endpoint: %v", err)
+ }
+
+ var x string
+ err = cl.CallContext(ctx, &x, "engine_helloWorld")
+ if at.expectCall1Fail {
+ if err == nil {
+ t.Fatal("expected call 1 to fail")
+ } else {
+ return
+ }
+ }
+ if err != nil {
+ t.Fatalf("failed to call rpc endpoint: %v", err)
+ }
+ if x != "hello engine" {
+ t.Fatalf("method was silent but did not return expected value: %q", x)
+ }
+
+ err = cl.CallContext(ctx, &x, "eth_helloWorld")
+ if at.expectCall2Fail {
+ if err == nil {
+ t.Fatal("expected call 2 to fail")
+ } else {
+ return
+ }
+ }
+ if err != nil {
+ t.Fatalf("failed to call rpc endpoint: %v", err)
+ }
+ if x != "hello eth" {
+ t.Fatalf("method was silent but did not return expected value: %q", x)
+ }
+}
+
+func TestAuthEndpoints(t *testing.T) {
+ var secret [32]byte
+ if _, err := crand.Read(secret[:]); err != nil {
+ t.Fatalf("failed to create jwt secret: %v", err)
+ }
+ // Geth must read it from a file, and does not support in-memory JWT secrets, so we create a temporary file.
+ jwtPath := path.Join(t.TempDir(), "jwt_secret")
+ if err := os.WriteFile(jwtPath, []byte(hexutil.Encode(secret[:])), 0600); err != nil {
+ t.Fatalf("failed to prepare jwt secret file: %v", err)
+ }
+ // We get ports assigned by the node automatically
+ conf := &Config{
+ HTTPHost: "127.0.0.1",
+ HTTPPort: 0,
+ WSHost: "127.0.0.1",
+ WSPort: 0,
+ AuthAddr: "127.0.0.1",
+ AuthPort: 0,
+ JWTSecret: jwtPath,
+
+ WSModules: []string{"eth", "engine"},
+ HTTPModules: []string{"eth", "engine"},
+ }
+ node, err := New(conf)
+ if err != nil {
+ t.Fatalf("could not create a new node: %v", err)
+ }
+ // register dummy apis so we can test the modules are available and reachable with authentication
+ node.RegisterAPIs([]rpc.API{
+ {
+ Namespace: "engine",
+ Version: "1.0",
+ Service: helloRPC("hello engine"),
+ Public: true,
+ Authenticated: true,
+ },
+ {
+ Namespace: "eth",
+ Version: "1.0",
+ Service: helloRPC("hello eth"),
+ Public: true,
+ Authenticated: true,
+ },
+ })
+ if err := node.Start(); err != nil {
+ t.Fatalf("failed to start test node: %v", err)
+ }
+ defer node.Close()
+
+ // sanity check we are running different endpoints
+ if a, b := node.WSEndpoint(), node.WSAuthEndpoint(); a == b {
+ t.Fatalf("expected ws and auth-ws endpoints to be different, got: %q and %q", a, b)
+ }
+ if a, b := node.HTTPEndpoint(), node.HTTPAuthEndpoint(); a == b {
+ t.Fatalf("expected http and auth-http endpoints to be different, got: %q and %q", a, b)
+ }
+
+ goodAuth := NewJWTAuth(secret)
+ var otherSecret [32]byte
+ if _, err := crand.Read(otherSecret[:]); err != nil {
+ t.Fatalf("failed to create jwt secret: %v", err)
+ }
+ badAuth := NewJWTAuth(otherSecret)
+
+ notTooLong := time.Second * 57
+ tooLong := time.Second * 60
+ requestDelay := time.Second
+
+ testCases := []authTest{
+ // Auth works
+ {name: "ws good", endpoint: node.WSAuthEndpoint(), prov: goodAuth, expectCall1Fail: false},
+ {name: "http good", endpoint: node.HTTPAuthEndpoint(), prov: goodAuth, expectCall1Fail: false},
+
+ // Try a bad auth
+ {name: "ws bad", endpoint: node.WSAuthEndpoint(), prov: badAuth, expectDialFail: true}, // ws auth is immediate
+ {name: "http bad", endpoint: node.HTTPAuthEndpoint(), prov: badAuth, expectCall1Fail: true}, // http auth is on first call
+
+ // A common mistake with JWT is to allow the "none" algorithm, which is a valid JWT but not secure.
+ {name: "ws none", endpoint: node.WSAuthEndpoint(), prov: noneAuth(secret), expectDialFail: true},
+ {name: "http none", endpoint: node.HTTPAuthEndpoint(), prov: noneAuth(secret), expectCall1Fail: true},
+
+ // claims of 5 seconds or more, older or newer, are not allowed
+ {name: "ws too old", endpoint: node.WSAuthEndpoint(), prov: offsetTimeAuth(secret, -tooLong), expectDialFail: true},
+ {name: "http too old", endpoint: node.HTTPAuthEndpoint(), prov: offsetTimeAuth(secret, -tooLong), expectCall1Fail: true},
+ // note: for it to be too long we need to add a delay, so that once we receive the request, the difference has not dipped below the "tooLong"
+ {name: "ws too new", endpoint: node.WSAuthEndpoint(), prov: offsetTimeAuth(secret, tooLong+requestDelay), expectDialFail: true},
+ {name: "http too new", endpoint: node.HTTPAuthEndpoint(), prov: offsetTimeAuth(secret, tooLong+requestDelay), expectCall1Fail: true},
+
+ // Try offset the time, but stay just within bounds
+ {name: "ws old", endpoint: node.WSAuthEndpoint(), prov: offsetTimeAuth(secret, -notTooLong)},
+ {name: "http old", endpoint: node.HTTPAuthEndpoint(), prov: offsetTimeAuth(secret, -notTooLong)},
+ {name: "ws new", endpoint: node.WSAuthEndpoint(), prov: offsetTimeAuth(secret, notTooLong)},
+ {name: "http new", endpoint: node.HTTPAuthEndpoint(), prov: offsetTimeAuth(secret, notTooLong)},
+
+ // ws only authenticates on initial dial, then continues communication
+ {name: "ws single auth", endpoint: node.WSAuthEndpoint(), prov: changingAuth(goodAuth, badAuth)},
+ {name: "http call fail auth", endpoint: node.HTTPAuthEndpoint(), prov: changingAuth(goodAuth, badAuth), expectCall2Fail: true},
+ {name: "http call fail time", endpoint: node.HTTPAuthEndpoint(), prov: changingAuth(goodAuth, offsetTimeAuth(secret, tooLong+requestDelay)), expectCall2Fail: true},
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, testCase.Run)
+ }
+}
+
+func noneAuth(secret [32]byte) rpc.HTTPAuth {
+ return func(header http.Header) error {
+ token := jwt.NewWithClaims(jwt.SigningMethodNone, jwt.MapClaims{
+ "iat": &jwt.NumericDate{Time: time.Now()},
+ })
+ s, err := token.SignedString(secret[:])
+ if err != nil {
+ return fmt.Errorf("failed to create JWT token: %w", err)
+ }
+ header.Set("Authorization", "Bearer "+s)
+ return nil
+ }
+}
+
+func changingAuth(provs ...rpc.HTTPAuth) rpc.HTTPAuth {
+ i := 0
+ return func(header http.Header) error {
+ i += 1
+ if i > len(provs) {
+ i = len(provs)
+ }
+ return provs[i-1](header)
+ }
+}
+
+func offsetTimeAuth(secret [32]byte, offset time.Duration) rpc.HTTPAuth {
+ return func(header http.Header) error {
+ token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
+ "iat": &jwt.NumericDate{Time: time.Now().Add(offset)},
+ })
+ s, err := token.SignedString(secret[:])
+ if err != nil {
+ return fmt.Errorf("failed to create JWT token: %w", err)
+ }
+ header.Set("Authorization", "Bearer "+s)
+ return nil
+ }
+}
diff --git a/node/node_example_test.go b/node/node_example_test.go
index d54fe03067..e45ee49a25 100644
--- a/node/node_example_test.go
+++ b/node/node_example_test.go
@@ -27,8 +27,8 @@ import (
// life cycle management.
//
// The following methods are needed to implement a node.Lifecycle:
-// - Start() error - method invoked when the node is ready to start the service
-// - Stop() error - method invoked when the node terminates the service
+// - Start() error - method invoked when the node is ready to start the service
+// - Stop() error - method invoked when the node terminates the service
type SampleLifecycle struct{}
func (s *SampleLifecycle) Start() error { fmt.Println("Service starting..."); return nil }
diff --git a/node/node_test.go b/node/node_test.go
index 7c76e21f6b..560d487fa8 100644
--- a/node/node_test.go
+++ b/node/node_test.go
@@ -559,13 +559,13 @@ func (test rpcPrefixTest) check(t *testing.T, node *Node) {
}
for _, path := range test.wantHTTP {
- resp := rpcRequest(t, httpBase+path)
+ resp := rpcRequest(t, httpBase+path, testMethod)
if resp.StatusCode != 200 {
t.Errorf("Error: %s: bad status code %d, want 200", path, resp.StatusCode)
}
}
for _, path := range test.wantNoHTTP {
- resp := rpcRequest(t, httpBase+path)
+ resp := rpcRequest(t, httpBase+path, testMethod)
if resp.StatusCode != 404 {
t.Errorf("Error: %s: bad status code %d, want 404", path, resp.StatusCode)
}
@@ -586,10 +586,11 @@ func (test rpcPrefixTest) check(t *testing.T, node *Node) {
func createNode(t *testing.T, httpPort, wsPort int) *Node {
conf := &Config{
- HTTPHost: "127.0.0.1",
- HTTPPort: httpPort,
- WSHost: "127.0.0.1",
- WSPort: wsPort,
+ HTTPHost: "127.0.0.1",
+ HTTPPort: httpPort,
+ WSHost: "127.0.0.1",
+ WSPort: wsPort,
+ HTTPTimeouts: rpc.DefaultHTTPTimeouts,
}
node, err := New(conf)
if err != nil {
diff --git a/node/rpcstack.go b/node/rpcstack.go
index 5d411fa61e..97d591642c 100644
--- a/node/rpcstack.go
+++ b/node/rpcstack.go
@@ -24,6 +24,7 @@ import (
"net"
"net/http"
"sort"
+ "strconv"
"strings"
"sync"
"sync/atomic"
@@ -196,6 +197,7 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
return
}
+
// if http-rpc is enabled, try to serve request
rpc := h.httpHandler.Load().(*rpcHandler)
if rpc != nil {
@@ -267,13 +269,15 @@ func (h *httpServer) doStop() {
h.wsHandler.Store((*rpcHandler)(nil))
wsHandler.server.Stop()
}
+
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()
err := h.server.Shutdown(ctx)
- if err == ctx.Err() {
+ if err != nil && err == ctx.Err() {
h.log.Warn("HTTP server graceful shutdown timed out")
h.server.Close()
}
+
h.listener.Close()
h.log.Info("HTTP server stopped", "endpoint", h.listener.Addr())
@@ -460,17 +464,94 @@ var gzPool = sync.Pool{
}
type gzipResponseWriter struct {
- io.Writer
- http.ResponseWriter
+ resp http.ResponseWriter
+
+ gz *gzip.Writer
+ contentLength uint64 // total length of the uncompressed response
+ written uint64 // amount of written bytes from the uncompressed response
+ hasLength bool // true if uncompressed response had Content-Length
+ inited bool // true after init was called for the first time
+}
+
+// init runs just before response headers are written. Among other things, this function
+// also decides whether compression will be applied at all.
+func (w *gzipResponseWriter) init() {
+ if w.inited {
+ return
+ }
+ w.inited = true
+
+ hdr := w.resp.Header()
+ length := hdr.Get("content-length")
+ if len(length) > 0 {
+ if n, err := strconv.ParseUint(length, 10, 64); err != nil {
+ w.hasLength = true
+ w.contentLength = n
+ }
+ }
+
+ // Setting Transfer-Encoding to "identity" explicitly disables compression. net/http
+ // also recognizes this header value and uses it to disable "chunked" transfer
+ // encoding, trimming the header from the response. This means downstream handlers can
+ // set this without harm, even if they aren't wrapped by newGzipHandler.
+ //
+ // In go-ethereum, we use this signal to disable compression for certain error
+ // responses which are flushed out close to the write deadline of the response. For
+ // these cases, we want to avoid chunked transfer encoding and compression because
+ // they require additional output that may not get written in time.
+ passthrough := hdr.Get("transfer-encoding") == "identity"
+ if !passthrough {
+ w.gz = gzPool.Get().(*gzip.Writer)
+ w.gz.Reset(w.resp)
+ hdr.Del("content-length")
+ hdr.Set("content-encoding", "gzip")
+ }
+}
+
+func (w *gzipResponseWriter) Header() http.Header {
+ return w.resp.Header()
}
func (w *gzipResponseWriter) WriteHeader(status int) {
- w.Header().Del("Content-Length")
- w.ResponseWriter.WriteHeader(status)
+ w.init()
+ w.resp.WriteHeader(status)
}
func (w *gzipResponseWriter) Write(b []byte) (int, error) {
- return w.Writer.Write(b)
+ w.init()
+
+ if w.gz == nil {
+ // Compression is disabled.
+ return w.resp.Write(b)
+ }
+
+ n, err := w.gz.Write(b)
+ w.written += uint64(n)
+ if w.hasLength && w.written >= w.contentLength {
+ // The HTTP handler has finished writing the entire uncompressed response. Close
+ // the gzip stream to ensure the footer will be seen by the client in case the
+ // response is flushed after this call to write.
+ err = w.gz.Close()
+ }
+ return n, err
+}
+
+func (w *gzipResponseWriter) Flush() {
+ if w.gz != nil {
+ w.gz.Flush()
+ }
+ if f, ok := w.resp.(http.Flusher); ok {
+ f.Flush()
+ }
+}
+
+func (w *gzipResponseWriter) close() {
+ if w.gz == nil {
+ return
+ }
+ w.gz.Close()
+ gzPool.Put(w.gz)
+ w.gz = nil
}
func newGzipHandler(next http.Handler) http.Handler {
@@ -480,15 +561,10 @@ func newGzipHandler(next http.Handler) http.Handler {
return
}
- w.Header().Set("Content-Encoding", "gzip")
-
- gz := gzPool.Get().(*gzip.Writer)
- defer gzPool.Put(gz)
-
- gz.Reset(w)
- defer gz.Close()
+ wrapper := &gzipResponseWriter{resp: w}
+ defer wrapper.close()
- next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r)
+ next.ServeHTTP(wrapper, r)
})
}
diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go
index 09acf7ea04..4d10e61e2d 100644
--- a/node/rpcstack_test.go
+++ b/node/rpcstack_test.go
@@ -19,7 +19,9 @@ package node
import (
"bytes"
"fmt"
+ "io"
"net/http"
+ "net/http/httptest"
"net/url"
"strconv"
"strings"
@@ -34,29 +36,31 @@ import (
"github.com/stretchr/testify/assert"
)
+const testMethod = "rpc_modules"
+
// TestCorsHandler makes sure CORS are properly handled on the http server.
func TestCorsHandler(t *testing.T) {
- srv := createAndStartServer(t, &httpConfig{CorsAllowedOrigins: []string{"test", "test.com"}}, false, &wsConfig{})
+ srv := createAndStartServer(t, &httpConfig{CorsAllowedOrigins: []string{"test", "test.com"}}, false, &wsConfig{}, nil)
defer srv.stop()
url := "http://" + srv.listenAddr()
- resp := rpcRequest(t, url, "origin", "test.com")
+ resp := rpcRequest(t, url, testMethod, "origin", "test.com")
assert.Equal(t, "test.com", resp.Header.Get("Access-Control-Allow-Origin"))
- resp2 := rpcRequest(t, url, "origin", "bad")
+ resp2 := rpcRequest(t, url, testMethod, "origin", "bad")
assert.Equal(t, "", resp2.Header.Get("Access-Control-Allow-Origin"))
}
// TestVhosts makes sure vhosts are properly handled on the http server.
func TestVhosts(t *testing.T) {
- srv := createAndStartServer(t, &httpConfig{Vhosts: []string{"test"}}, false, &wsConfig{})
+ srv := createAndStartServer(t, &httpConfig{Vhosts: []string{"test"}}, false, &wsConfig{}, nil)
defer srv.stop()
url := "http://" + srv.listenAddr()
- resp := rpcRequest(t, url, "host", "test")
+ resp := rpcRequest(t, url, testMethod, "host", "test")
assert.Equal(t, resp.StatusCode, http.StatusOK)
- resp2 := rpcRequest(t, url, "host", "bad")
+ resp2 := rpcRequest(t, url, testMethod, "host", "bad")
assert.Equal(t, resp2.StatusCode, http.StatusForbidden)
}
@@ -145,7 +149,7 @@ func TestWebsocketOrigins(t *testing.T) {
},
}
for _, tc := range tests {
- srv := createAndStartServer(t, &httpConfig{}, true, &wsConfig{Origins: splitAndTrim(tc.spec)})
+ srv := createAndStartServer(t, &httpConfig{}, true, &wsConfig{Origins: splitAndTrim(tc.spec)}, nil)
url := fmt.Sprintf("ws://%v", srv.listenAddr())
for _, origin := range tc.expOk {
if err := wsRequest(t, url, "Origin", origin); err != nil {
@@ -163,7 +167,7 @@ func TestWebsocketOrigins(t *testing.T) {
// TestIsWebsocket tests if an incoming websocket upgrade request is handled properly.
func TestIsWebsocket(t *testing.T) {
- r, _ := http.NewRequest("GET", "/", nil)
+ r, _ := http.NewRequest(http.MethodGet, "/", nil)
assert.False(t, isWebsocket(r))
r.Header.Set("upgrade", "websocket")
@@ -231,11 +235,14 @@ func Test_checkPath(t *testing.T) {
}
}
-func createAndStartServer(t *testing.T, conf *httpConfig, ws bool, wsConf *wsConfig) *httpServer {
+func createAndStartServer(t *testing.T, conf *httpConfig, ws bool, wsConf *wsConfig, timeouts *rpc.HTTPTimeouts) *httpServer {
t.Helper()
- srv := newHTTPServer(testlog.Logger(t, log.LvlDebug), rpc.DefaultHTTPTimeouts)
- assert.NoError(t, srv.enableRPC(nil, *conf))
+ if timeouts == nil {
+ timeouts = &rpc.DefaultHTTPTimeouts
+ }
+ srv := newHTTPServer(testlog.Logger(t, log.LvlDebug), *timeouts)
+ assert.NoError(t, srv.enableRPC(apis(), *conf))
if ws {
assert.NoError(t, srv.enableWS(nil, *wsConf))
}
@@ -266,16 +273,33 @@ func wsRequest(t *testing.T, url string, extraHeaders ...string) error {
}
// rpcRequest performs a JSON-RPC request to the given URL.
-func rpcRequest(t *testing.T, url string, extraHeaders ...string) *http.Response {
+func rpcRequest(t *testing.T, url, method string, extraHeaders ...string) *http.Response {
+ t.Helper()
+
+ body := fmt.Sprintf(`{"jsonrpc":"2.0","id":1,"method":"%s","params":[]}`, method)
+ return baseRpcRequest(t, url, body, extraHeaders...)
+}
+
+func batchRpcRequest(t *testing.T, url string, methods []string, extraHeaders ...string) *http.Response {
+ reqs := make([]string, len(methods))
+ for i, m := range methods {
+ reqs[i] = fmt.Sprintf(`{"jsonrpc":"2.0","id":1,"method":"%s","params":[]}`, m)
+ }
+ body := fmt.Sprintf(`[%s]`, strings.Join(reqs, ","))
+ return baseRpcRequest(t, url, body, extraHeaders...)
+}
+
+func baseRpcRequest(t *testing.T, url, bodyStr string, extraHeaders ...string) *http.Response {
t.Helper()
// Create the request.
- body := bytes.NewReader([]byte(`{"jsonrpc":"2.0","id":1,"method":"rpc_modules","params":[]}`))
- req, err := http.NewRequest("POST", url, body)
+ body := bytes.NewReader([]byte(bodyStr))
+ req, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
t.Fatal("could not create http request:", err)
}
req.Header.Set("content-type", "application/json")
+ req.Header.Set("accept-encoding", "identity")
// Apply extra headers.
if len(extraHeaders)%2 != 0 {
@@ -315,7 +339,7 @@ func TestJWT(t *testing.T) {
return ss
}
srv := createAndStartServer(t, &httpConfig{jwtSecret: []byte("secret")},
- true, &wsConfig{Origins: []string{"*"}, jwtSecret: []byte("secret")})
+ true, &wsConfig{Origins: []string{"*"}, jwtSecret: []byte("secret")}, nil)
wsUrl := fmt.Sprintf("ws://%v", srv.listenAddr())
htUrl := fmt.Sprintf("http://%v", srv.listenAddr())
@@ -348,7 +372,7 @@ func TestJWT(t *testing.T) {
t.Errorf("test %d-ws, token '%v': expected ok, got %v", i, token, err)
}
token = tokenFn()
- if resp := rpcRequest(t, htUrl, "Authorization", token); resp.StatusCode != 200 {
+ if resp := rpcRequest(t, htUrl, testMethod, "Authorization", token); resp.StatusCode != 200 {
t.Errorf("test %d-http, token '%v': expected ok, got %v", i, token, resp.StatusCode)
}
}
@@ -412,10 +436,178 @@ func TestJWT(t *testing.T) {
if err := wsRequest(t, wsUrl, "Authorization", token); err == nil {
t.Errorf("tc %d-ws, token '%v': expected not to allow, got ok", i, token)
}
+
token = tokenFn()
- if resp := rpcRequest(t, htUrl, "Authorization", token); resp.StatusCode != 403 {
+ resp := rpcRequest(t, htUrl, testMethod, "Authorization", token)
+ if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("tc %d-http, token '%v': expected not to allow, got %v", i, token, resp.StatusCode)
}
}
srv.stop()
}
+
+func TestGzipHandler(t *testing.T) {
+ type gzipTest struct {
+ name string
+ handler http.HandlerFunc
+ status int
+ isGzip bool
+ header map[string]string
+ }
+ tests := []gzipTest{
+ {
+ name: "Write",
+ handler: func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("response"))
+ },
+ isGzip: true,
+ status: 200,
+ },
+ {
+ name: "WriteHeader",
+ handler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("x-foo", "bar")
+ w.WriteHeader(205)
+ w.Write([]byte("response"))
+ },
+ isGzip: true,
+ status: 205,
+ header: map[string]string{"x-foo": "bar"},
+ },
+ {
+ name: "WriteContentLength",
+ handler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("content-length", "8")
+ w.Write([]byte("response"))
+ },
+ isGzip: true,
+ status: 200,
+ },
+ {
+ name: "Flush",
+ handler: func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("res"))
+ w.(http.Flusher).Flush()
+ w.Write([]byte("ponse"))
+ },
+ isGzip: true,
+ status: 200,
+ },
+ {
+ name: "disable",
+ handler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("transfer-encoding", "identity")
+ w.Header().Set("x-foo", "bar")
+ w.Write([]byte("response"))
+ },
+ isGzip: false,
+ status: 200,
+ header: map[string]string{"x-foo": "bar"},
+ },
+ {
+ name: "disable-WriteHeader",
+ handler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("transfer-encoding", "identity")
+ w.Header().Set("x-foo", "bar")
+ w.WriteHeader(205)
+ w.Write([]byte("response"))
+ },
+ isGzip: false,
+ status: 205,
+ header: map[string]string{"x-foo": "bar"},
+ },
+ }
+
+ for _, test := range tests {
+ test := test
+ t.Run(test.name, func(t *testing.T) {
+ srv := httptest.NewServer(newGzipHandler(test.handler))
+ defer srv.Close()
+
+ resp, err := http.Get(srv.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer resp.Body.Close()
+
+ content, err := io.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wasGzip := resp.Uncompressed
+
+ if string(content) != "response" {
+ t.Fatalf("wrong response content %q", content)
+ }
+ if wasGzip != test.isGzip {
+ t.Fatalf("response gzipped == %t, want %t", wasGzip, test.isGzip)
+ }
+ if resp.StatusCode != test.status {
+ t.Fatalf("response status == %d, want %d", resp.StatusCode, test.status)
+ }
+ for name, expectedValue := range test.header {
+ if v := resp.Header.Get(name); v != expectedValue {
+ t.Fatalf("response header %s == %s, want %s", name, v, expectedValue)
+ }
+ }
+ })
+ }
+}
+
+func TestHTTPWriteTimeout(t *testing.T) {
+ const (
+ timeoutRes = `{"jsonrpc":"2.0","id":1,"error":{"code":-32002,"message":"request timed out"}}`
+ greetRes = `{"jsonrpc":"2.0","id":1,"result":"Hello"}`
+ )
+ // Set-up server
+ timeouts := rpc.DefaultHTTPTimeouts
+ timeouts.WriteTimeout = time.Second
+ srv := createAndStartServer(t, &httpConfig{Modules: []string{"test"}}, false, &wsConfig{}, &timeouts)
+ url := fmt.Sprintf("http://%v", srv.listenAddr())
+
+ // Send normal request
+ t.Run("message", func(t *testing.T) {
+ resp := rpcRequest(t, url, "test_sleep")
+ defer resp.Body.Close()
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(body) != timeoutRes {
+ t.Errorf("wrong response. have %s, want %s", string(body), timeoutRes)
+ }
+ })
+
+ // Batch request
+ t.Run("batch", func(t *testing.T) {
+ want := fmt.Sprintf("[%s,%s,%s]", greetRes, timeoutRes, timeoutRes)
+ resp := batchRpcRequest(t, url, []string{"test_greet", "test_sleep", "test_greet"})
+ defer resp.Body.Close()
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(body) != want {
+ t.Errorf("wrong response. have %s, want %s", string(body), want)
+ }
+ })
+}
+
+func apis() []rpc.API {
+ return []rpc.API{
+ {
+ Namespace: "test",
+ Service: &testService{},
+ },
+ }
+}
+
+type testService struct{}
+
+func (s *testService) Greet() string {
+ return "Hello"
+}
+
+func (s *testService) Sleep() {
+ time.Sleep(1500 * time.Millisecond)
+}
diff --git a/oss-fuzz.sh b/oss-fuzz.sh
index 745a5ba7c7..7f454ff307 100644
--- a/oss-fuzz.sh
+++ b/oss-fuzz.sh
@@ -125,5 +125,7 @@ compile_fuzzer tests/fuzzers/snap FuzzSRange fuzz_storage_range
compile_fuzzer tests/fuzzers/snap FuzzByteCodes fuzz_byte_codes
compile_fuzzer tests/fuzzers/snap FuzzTrieNodes fuzz_trie_nodes
+compile_fuzzer tests/fuzzers/modexp Fuzz fuzzModexp
+
#TODO: move this to tests/fuzzers, if possible
compile_fuzzer crypto/blake2b Fuzz fuzzBlake2b
diff --git a/p2p/dial.go b/p2p/dial.go
index 0d70e6f4a3..134e6e2eae 100644
--- a/p2p/dial.go
+++ b/p2p/dial.go
@@ -84,13 +84,12 @@ var (
// dialer creates outbound connections and submits them into Server.
// Two types of peer connections can be created:
//
-// - static dials are pre-configured connections. The dialer attempts
-// keep these nodes connected at all times.
-//
-// - dynamic dials are created from node discovery results. The dialer
-// continuously reads candidate nodes from its input iterator and attempts
-// to create peer connections to nodes arriving through the iterator.
+// - static dials are pre-configured connections. The dialer attempts
+// keep these nodes connected at all times.
//
+// - dynamic dials are created from node discovery results. The dialer
+// continuously reads candidate nodes from its input iterator and attempts
+// to create peer connections to nodes arriving through the iterator.
type dialScheduler struct {
dialConfig
setupFunc dialSetupFunc
@@ -118,9 +117,8 @@ type dialScheduler struct {
staticPool []*dialTask
// The dial history keeps recently dialed nodes. Members of history are not dialed.
- history expHeap
- historyTimer mclock.Timer
- historyTimerTime mclock.AbsTime
+ history expHeap
+ historyTimer *mclock.Alarm
// for logStats
lastStatsLog mclock.AbsTime
@@ -161,18 +159,20 @@ func (cfg dialConfig) withDefaults() dialConfig {
}
func newDialScheduler(config dialConfig, it enode.Iterator, setupFunc dialSetupFunc) *dialScheduler {
+ cfg := config.withDefaults()
d := &dialScheduler{
- dialConfig: config.withDefaults(),
- setupFunc: setupFunc,
- dialing: make(map[enode.ID]*dialTask),
- static: make(map[enode.ID]*dialTask),
- peers: make(map[enode.ID]struct{}),
- doneCh: make(chan *dialTask),
- nodesIn: make(chan *enode.Node),
- addStaticCh: make(chan *enode.Node),
- remStaticCh: make(chan *enode.Node),
- addPeerCh: make(chan *conn),
- remPeerCh: make(chan *conn),
+ dialConfig: cfg,
+ historyTimer: mclock.NewAlarm(cfg.clock),
+ setupFunc: setupFunc,
+ dialing: make(map[enode.ID]*dialTask),
+ static: make(map[enode.ID]*dialTask),
+ peers: make(map[enode.ID]struct{}),
+ doneCh: make(chan *dialTask),
+ nodesIn: make(chan *enode.Node),
+ addStaticCh: make(chan *enode.Node),
+ remStaticCh: make(chan *enode.Node),
+ addPeerCh: make(chan *conn),
+ remPeerCh: make(chan *conn),
}
d.lastStatsLog = d.clock.Now()
d.ctx, d.cancel = context.WithCancel(context.Background())
@@ -223,8 +223,7 @@ func (d *dialScheduler) peerRemoved(c *conn) {
// loop is the main loop of the dialer.
func (d *dialScheduler) loop(it enode.Iterator) {
var (
- nodesCh chan *enode.Node
- historyExp = make(chan struct{}, 1)
+ nodesCh chan *enode.Node
)
loop:
@@ -237,7 +236,7 @@ loop:
} else {
nodesCh = nil
}
- d.rearmHistoryTimer(historyExp)
+ d.rearmHistoryTimer()
d.logStats()
select {
@@ -298,7 +297,7 @@ loop:
}
}
- case <-historyExp:
+ case <-d.historyTimer.C():
d.expireHistory()
case <-d.ctx.Done():
@@ -307,7 +306,7 @@ loop:
}
}
- d.stopHistoryTimer(historyExp)
+ d.historyTimer.Stop()
for range d.dialing {
<-d.doneCh
}
@@ -344,28 +343,15 @@ func (d *dialScheduler) logStats() {
// rearmHistoryTimer configures d.historyTimer to fire when the
// next item in d.history expires.
-func (d *dialScheduler) rearmHistoryTimer(ch chan struct{}) {
- if len(d.history) == 0 || d.historyTimerTime == d.history.nextExpiry() {
+func (d *dialScheduler) rearmHistoryTimer() {
+ if len(d.history) == 0 {
return
}
- d.stopHistoryTimer(ch)
- d.historyTimerTime = d.history.nextExpiry()
- timeout := time.Duration(d.historyTimerTime - d.clock.Now())
- d.historyTimer = d.clock.AfterFunc(timeout, func() { ch <- struct{}{} })
-}
-
-// stopHistoryTimer stops the timer and drains the channel it sends on.
-func (d *dialScheduler) stopHistoryTimer(ch chan struct{}) {
- if d.historyTimer != nil && !d.historyTimer.Stop() {
- <-ch
- }
+ d.historyTimer.Schedule(d.history.nextExpiry())
}
// expireHistory removes expired items from d.history.
func (d *dialScheduler) expireHistory() {
- d.historyTimer.Stop()
- d.historyTimer = nil
- d.historyTimerTime = 0
d.history.expire(d.clock.Now(), func(hkey string) {
var id enode.ID
copy(id[:], hkey)
diff --git a/p2p/discover/common.go b/p2p/discover/common.go
index e389821fda..c36e8dcc3a 100644
--- a/p2p/discover/common.go
+++ b/p2p/discover/common.go
@@ -35,16 +35,24 @@ type UDPConn interface {
LocalAddr() net.Addr
}
+type V5Config struct {
+ ProtocolID *[6]byte
+}
+
// Config holds settings for the discovery listener.
type Config struct {
// These settings are required and configure the UDP listener:
PrivateKey *ecdsa.PrivateKey
// These settings are optional:
- NetRestrict *netutil.Netlist // list of allowed IP networks
- Bootnodes []*enode.Node // list of bootstrap nodes
- Unhandled chan<- ReadPacket // unhandled packets are sent on this channel
- Log log.Logger // if set, log messages go here
+ NetRestrict *netutil.Netlist // list of allowed IP networks
+ Bootnodes []*enode.Node // list of bootstrap nodes
+ Unhandled chan<- ReadPacket // unhandled packets are sent on this channel
+ Log log.Logger // if set, log messages go here
+
+ // V5ProtocolID configures the discv5 protocol identifier.
+ V5ProtocolID *[6]byte
+
ValidSchemes enr.IdentityScheme // allowed identity schemes
Clock mclock.Clock
}
diff --git a/p2p/discover/table.go b/p2p/discover/table.go
index d08f8a6c69..41d5ac6e34 100644
--- a/p2p/discover/table.go
+++ b/p2p/discover/table.go
@@ -672,15 +672,14 @@ func (h *nodesByDistance) push(n *node, maxElems int) {
ix := sort.Search(len(h.entries), func(i int) bool {
return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0
})
+
+ end := len(h.entries)
if len(h.entries) < maxElems {
h.entries = append(h.entries, n)
}
- if ix == len(h.entries) {
- // farther away than all nodes we already have.
- // if there was room for it, the node is now the last element.
- } else {
- // slide existing entries down to make room
- // this will overwrite the entry we just appended.
+ if ix < end {
+ // Slide existing entries down to make room.
+ // This will overwrite the entry we just appended.
copy(h.entries[ix+1:], h.entries[ix:])
h.entries[ix] = n
}
diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go
index 5f40c967fd..1ef63fe010 100644
--- a/p2p/discover/table_test.go
+++ b/p2p/discover/table_test.go
@@ -396,6 +396,59 @@ func TestTable_revalidateSyncRecord(t *testing.T) {
}
}
+func TestNodesPush(t *testing.T) {
+ var target enode.ID
+ n1 := nodeAtDistance(target, 255, intIP(1))
+ n2 := nodeAtDistance(target, 254, intIP(2))
+ n3 := nodeAtDistance(target, 253, intIP(3))
+ perm := [][]*node{
+ {n3, n2, n1},
+ {n3, n1, n2},
+ {n2, n3, n1},
+ {n2, n1, n3},
+ {n1, n3, n2},
+ {n1, n2, n3},
+ }
+
+ // Insert all permutations into lists with size limit 3.
+ for _, nodes := range perm {
+ list := nodesByDistance{target: target}
+ for _, n := range nodes {
+ list.push(n, 3)
+ }
+ if !slicesEqual(list.entries, perm[0], nodeIDEqual) {
+ t.Fatal("not equal")
+ }
+ }
+
+ // Insert all permutations into lists with size limit 2.
+ for _, nodes := range perm {
+ list := nodesByDistance{target: target}
+ for _, n := range nodes {
+ list.push(n, 2)
+ }
+ if !slicesEqual(list.entries, perm[0][:2], nodeIDEqual) {
+ t.Fatal("not equal")
+ }
+ }
+}
+
+func nodeIDEqual(n1, n2 *node) bool {
+ return n1.ID() == n2.ID()
+}
+
+func slicesEqual[T any](s1, s2 []T, check func(e1, e2 T) bool) bool {
+ if len(s1) != len(s2) {
+ return false
+ }
+ for i := range s1 {
+ if !check(s1[i], s2[i]) {
+ return false
+ }
+ }
+ return true
+}
+
// gen wraps quick.Value so it's easier to use.
// it generates a random value of the given value's type.
func gen(typ interface{}, rand *rand.Rand) interface{} {
diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go
index e00bf2784c..21f0d75172 100644
--- a/p2p/discover/v4_udp_test.go
+++ b/p2p/discover/v4_udp_test.go
@@ -165,7 +165,6 @@ func TestUDPv4_responseTimeouts(t *testing.T) {
test := newUDPTest(t)
defer test.close()
- rand.Seed(time.Now().UnixNano())
randomDuration := func(max time.Duration) time.Duration {
return time.Duration(rand.Int63n(int64(max)))
}
@@ -313,7 +312,7 @@ func TestUDPv4_findnodeMultiReply(t *testing.T) {
test.table.db.UpdateLastPingReceived(rid, test.remoteaddr.IP, time.Now())
// queue a pending findnode request
- resultc, errc := make(chan []*node), make(chan error)
+ resultc, errc := make(chan []*node, 1), make(chan error, 1)
go func() {
rid := encodePubkey(&test.remotekey.PublicKey).id()
ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
diff --git a/p2p/discover/v4wire/v4wire.go b/p2p/discover/v4wire/v4wire.go
index b07a6e341c..3935068cd9 100644
--- a/p2p/discover/v4wire/v4wire.go
+++ b/p2p/discover/v4wire/v4wire.go
@@ -60,7 +60,7 @@ type (
Pong struct {
// This field should mirror the UDP envelope address
// of the ping packet, which provides a way to discover the
- // the external address (after NAT).
+ // external address (after NAT).
To Endpoint
ReplyTok []byte // This contains the hash of the ping packet.
Expiration uint64 // Absolute timestamp at which the packet becomes invalid.
@@ -102,7 +102,7 @@ type (
}
)
-// This number is the maximum number of neighbor nodes in a Neighbors packet.
+// MaxNeighbors is the maximum number of neighbor nodes in a Neighbors packet.
const MaxNeighbors = 12
// This code computes the MaxNeighbors constant value.
@@ -161,8 +161,9 @@ func NewEndpoint(addr *net.UDPAddr, tcpPort uint16) Endpoint {
}
type Packet interface {
- // packet name and type for logging purposes.
+ // Name is the name of the package, for logging purposes.
Name() string
+ // Kind is the packet type, for logging purposes.
Kind() byte
}
diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go
index 071ed65adc..53a1c6f767 100644
--- a/p2p/discover/v5_udp.go
+++ b/p2p/discover/v5_udp.go
@@ -24,7 +24,6 @@ import (
"errors"
"fmt"
"io"
- "math"
"net"
"sync"
"time"
@@ -41,7 +40,6 @@ const (
lookupRequestLimit = 3 // max requests against a single node during lookup
findnodeResultLimit = 16 // applies in FINDNODE handler
totalNodesResponseLimit = 5 // applies in waitForNodes
- nodesResponseItemLimit = 3 // applies in sendNodes
respTimeoutV5 = 700 * time.Millisecond
)
@@ -54,7 +52,7 @@ type codecV5 interface {
// Encode encodes a packet.
Encode(enode.ID, string, v5wire.Packet, *v5wire.Whoareyou) ([]byte, v5wire.Nonce, error)
- // decode decodes a packet. It returns a *v5wire.Unknown packet if decryption fails.
+ // Decode decodes a packet. It returns a *v5wire.Unknown packet if decryption fails.
// The *enode.Node return value is non-nil when the input contains a handshake response.
Decode([]byte, string) (enode.ID, *enode.Node, v5wire.Packet, error)
}
@@ -72,6 +70,9 @@ type UDPv5 struct {
clock mclock.Clock
validSchemes enr.IdentityScheme
+ // misc buffers used during message handling
+ logcontext []interface{}
+
// talkreq handler registry
trlock sync.Mutex
trhandlers map[string]TalkRequestHandler
@@ -156,10 +157,11 @@ func newUDPv5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) {
callDoneCh: make(chan *callV5),
respTimeoutCh: make(chan *callTimeout),
// state of dispatch
- codec: v5wire.NewCodec(ln, cfg.PrivateKey, cfg.Clock),
+ codec: v5wire.NewCodec(ln, cfg.PrivateKey, cfg.Clock, cfg.V5ProtocolID),
activeCallByNode: make(map[enode.ID]*callV5),
activeCallByAuth: make(map[v5wire.Nonce]*callV5),
callQueue: make(map[enode.ID][]*callV5),
+
// shutdown
closeCtx: closeCtx,
cancelCloseCtx: cancelCloseCtx,
@@ -323,7 +325,7 @@ func lookupDistances(target, dest enode.ID) (dists []uint) {
td := enode.LogDist(target, dest)
dists = append(dists, uint(td))
for i := 1; len(dists) < lookupRequestLimit; i++ {
- if td+i < 256 {
+ if td+i <= 256 {
dists = append(dists, uint(td+i))
}
if td-i > 0 {
@@ -387,7 +389,7 @@ func (t *UDPv5) waitForNodes(c *callV5, distances []uint) ([]*enode.Node, error)
nodes = append(nodes, node)
}
if total == -1 {
- total = min(int(response.Total), totalNodesResponseLimit)
+ total = min(int(response.RespCount), totalNodesResponseLimit)
}
if received++; received == total {
return nodes, nil
@@ -603,13 +605,18 @@ func (t *UDPv5) sendResponse(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.P
// send sends a packet to the given node.
func (t *UDPv5) send(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet, c *v5wire.Whoareyou) (v5wire.Nonce, error) {
addr := toAddr.String()
+ t.logcontext = append(t.logcontext[:0], "id", toID, "addr", addr)
+ t.logcontext = packet.AppendLogInfo(t.logcontext)
+
enc, nonce, err := t.codec.Encode(toID, addr, packet, c)
if err != nil {
- t.log.Warn(">> "+packet.Name(), "id", toID, "addr", addr, "err", err)
+ t.logcontext = append(t.logcontext, "err", err)
+ t.log.Warn(">> "+packet.Name(), t.logcontext...)
return nonce, err
}
+
_, err = t.conn.WriteToUDP(enc, toAddr)
- t.log.Trace(">> "+packet.Name(), "id", toID, "addr", addr)
+ t.log.Trace(">> "+packet.Name(), t.logcontext...)
return nonce, err
}
@@ -659,7 +666,9 @@ func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error {
}
if packet.Kind() != v5wire.WhoareyouPacket {
// WHOAREYOU logged separately to report errors.
- t.log.Trace("<< "+packet.Name(), "id", fromID, "addr", addr)
+ t.logcontext = append(t.logcontext[:0], "id", fromID, "addr", addr)
+ t.logcontext = packet.AppendLogInfo(t.logcontext)
+ t.log.Trace("<< "+packet.Name(), t.logcontext...)
}
t.handle(packet, fromID, fromAddr)
return nil
@@ -714,7 +723,7 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr)
case *v5wire.Nodes:
t.handleCallResponse(fromID, fromAddr, p)
case *v5wire.TalkRequest:
- t.handleTalkRequest(p, fromID, fromAddr)
+ t.handleTalkRequest(fromID, fromAddr, p)
case *v5wire.TalkResponse:
t.handleCallResponse(fromID, fromAddr, p)
}
@@ -829,25 +838,37 @@ func (t *UDPv5) collectTableNodes(rip net.IP, distances []uint, limit int) []*en
// packNodes creates NODES response packets for the given node list.
func packNodes(reqid []byte, nodes []*enode.Node) []*v5wire.Nodes {
if len(nodes) == 0 {
- return []*v5wire.Nodes{{ReqID: reqid, Total: 1}}
+ return []*v5wire.Nodes{{ReqID: reqid, RespCount: 1}}
}
- total := uint8(math.Ceil(float64(len(nodes)) / 3))
+ // This limit represents the available space for nodes in output packets. Maximum
+ // packet size is 1280, and out of this ~80 bytes will be taken up by the packet
+ // frame. So limiting to 1000 bytes here leaves 200 bytes for other fields of the
+ // NODES message, which is a lot.
+ const sizeLimit = 1000
+
var resp []*v5wire.Nodes
for len(nodes) > 0 {
- p := &v5wire.Nodes{ReqID: reqid, Total: total}
- items := min(nodesResponseItemLimit, len(nodes))
- for i := 0; i < items; i++ {
- p.Nodes = append(p.Nodes, nodes[i].Record())
+ p := &v5wire.Nodes{ReqID: reqid}
+ size := uint64(0)
+ for len(nodes) > 0 {
+ r := nodes[0].Record()
+ if size += r.Size(); size > sizeLimit {
+ break
+ }
+ p.Nodes = append(p.Nodes, r)
+ nodes = nodes[1:]
}
- nodes = nodes[items:]
resp = append(resp, p)
}
+ for _, msg := range resp {
+ msg.RespCount = uint8(len(resp))
+ }
return resp
}
// handleTalkRequest runs the talk request handler of the requested protocol.
-func (t *UDPv5) handleTalkRequest(p *v5wire.TalkRequest, fromID enode.ID, fromAddr *net.UDPAddr) {
+func (t *UDPv5) handleTalkRequest(fromID enode.ID, fromAddr *net.UDPAddr, p *v5wire.TalkRequest) {
t.trlock.Lock()
handler := t.trhandlers[p.Protocol]
t.trlock.Unlock()
diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go
index 30d610a4dd..481bb1cdc3 100644
--- a/p2p/discover/v5_udp_test.go
+++ b/p2p/discover/v5_udp_test.go
@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/stretchr/testify/require"
)
// Real sockets, real crypto: this test checks end-to-end connectivity for UDPv5.
@@ -160,7 +161,7 @@ func TestUDPv5_findnodeHandling(t *testing.T) {
defer test.close()
// Create test nodes and insert them into the table.
- nodes253 := nodesAtDistance(test.table.self().ID(), 253, 10)
+ nodes253 := nodesAtDistance(test.table.self().ID(), 253, 16)
nodes249 := nodesAtDistance(test.table.self().ID(), 249, 4)
nodes248 := nodesAtDistance(test.table.self().ID(), 248, 10)
fillTable(test.table, wrapNodes(nodes253))
@@ -185,7 +186,7 @@ func TestUDPv5_findnodeHandling(t *testing.T) {
// This request gets all the distance-253 nodes.
test.packetIn(&v5wire.Findnode{ReqID: []byte{4}, Distances: []uint{253}})
- test.expectNodes([]byte{4}, 4, nodes253)
+ test.expectNodes([]byte{4}, 1, nodes253)
// This request gets all the distance-249 nodes and some more at 248 because
// the bucket at 249 is not full.
@@ -193,7 +194,7 @@ func TestUDPv5_findnodeHandling(t *testing.T) {
var nodes []*enode.Node
nodes = append(nodes, nodes249...)
nodes = append(nodes, nodes248[:10]...)
- test.expectNodes([]byte{5}, 5, nodes)
+ test.expectNodes([]byte{5}, 1, nodes)
}
func (test *udpV5Test) expectNodes(wantReqID []byte, wantTotal uint8, wantNodes []*enode.Node) {
@@ -207,11 +208,8 @@ func (test *udpV5Test) expectNodes(wantReqID []byte, wantTotal uint8, wantNodes
if !bytes.Equal(p.ReqID, wantReqID) {
test.t.Fatalf("wrong request ID %v in response, want %v", p.ReqID, wantReqID)
}
- if len(p.Nodes) > 3 {
- test.t.Fatalf("too many nodes in response")
- }
- if p.Total != wantTotal {
- test.t.Fatalf("wrong total response count %d, want %d", p.Total, wantTotal)
+ if p.RespCount != wantTotal {
+ test.t.Fatalf("wrong total response count %d, want %d", p.RespCount, wantTotal)
}
for _, record := range p.Nodes {
n, _ := enode.New(enode.ValidSchemesForTesting, record)
@@ -303,14 +301,14 @@ func TestUDPv5_findnodeCall(t *testing.T) {
t.Fatalf("wrong distances in request: %v", p.Distances)
}
test.packetIn(&v5wire.Nodes{
- ReqID: p.ReqID,
- Total: 2,
- Nodes: nodesToRecords(nodes[:4]),
+ ReqID: p.ReqID,
+ RespCount: 2,
+ Nodes: nodesToRecords(nodes[:4]),
})
test.packetIn(&v5wire.Nodes{
- ReqID: p.ReqID,
- Total: 2,
- Nodes: nodesToRecords(nodes[4:]),
+ ReqID: p.ReqID,
+ RespCount: 2,
+ Nodes: nodesToRecords(nodes[4:]),
})
})
@@ -411,16 +409,16 @@ func TestUDPv5_callTimeoutReset(t *testing.T) {
test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) {
time.Sleep(respTimeout - 50*time.Millisecond)
test.packetIn(&v5wire.Nodes{
- ReqID: p.ReqID,
- Total: 2,
- Nodes: nodesToRecords(nodes[:4]),
+ ReqID: p.ReqID,
+ RespCount: 2,
+ Nodes: nodesToRecords(nodes[:4]),
})
time.Sleep(respTimeout - 50*time.Millisecond)
test.packetIn(&v5wire.Nodes{
- ReqID: p.ReqID,
- Total: 2,
- Nodes: nodesToRecords(nodes[4:]),
+ ReqID: p.ReqID,
+ RespCount: 2,
+ Nodes: nodesToRecords(nodes[4:]),
})
})
if err := <-done; err != nil {
@@ -519,6 +517,42 @@ func TestUDPv5_talkRequest(t *testing.T) {
}
}
+// This test checks that lookupDistances works.
+func TestUDPv5_lookupDistances(t *testing.T) {
+ test := newUDPV5Test(t)
+ lnID := test.table.self().ID()
+
+ t.Run("target distance of 1", func(t *testing.T) {
+ node := nodeAtDistance(lnID, 1, intIP(0))
+ dists := lookupDistances(lnID, node.ID())
+ require.Equal(t, []uint{1, 2, 3}, dists)
+ })
+
+ t.Run("target distance of 2", func(t *testing.T) {
+ node := nodeAtDistance(lnID, 2, intIP(0))
+ dists := lookupDistances(lnID, node.ID())
+ require.Equal(t, []uint{2, 3, 1}, dists)
+ })
+
+ t.Run("target distance of 128", func(t *testing.T) {
+ node := nodeAtDistance(lnID, 128, intIP(0))
+ dists := lookupDistances(lnID, node.ID())
+ require.Equal(t, []uint{128, 129, 127}, dists)
+ })
+
+ t.Run("target distance of 255", func(t *testing.T) {
+ node := nodeAtDistance(lnID, 255, intIP(0))
+ dists := lookupDistances(lnID, node.ID())
+ require.Equal(t, []uint{255, 256, 254}, dists)
+ })
+
+ t.Run("target distance of 256", func(t *testing.T) {
+ node := nodeAtDistance(lnID, 256, intIP(0))
+ dists := lookupDistances(lnID, node.ID())
+ require.Equal(t, []uint{256, 255, 254}, dists)
+ })
+}
+
// This test checks that lookup works.
func TestUDPv5_lookup(t *testing.T) {
t.Parallel()
diff --git a/p2p/discover/v5wire/encoding.go b/p2p/discover/v5wire/encoding.go
index 45f2f0883b..d979ab0f9c 100644
--- a/p2p/discover/v5wire/encoding.go
+++ b/p2p/discover/v5wire/encoding.go
@@ -65,7 +65,7 @@ type (
handshakeAuthData struct {
h struct {
SrcID enode.ID
- SigSize byte // ignature data
+ SigSize byte // signature data
PubkeySize byte // offset of
}
// Trailing variable-size data.
@@ -90,11 +90,15 @@ const (
minVersion = 1
sizeofMaskingIV = 16
+ // The minimum size of any Discovery v5 packet is 63 bytes.
+ // Should reject packets smaller than minPacketSize.
+ minPacketSize = 63
+
minMessageSize = 48 // this refers to data after static headers
randomPacketMsgSize = 20
)
-var protocolID = [6]byte{'d', 'i', 's', 'c', 'v', '5'}
+var DefaultProtocolID = [6]byte{'d', 'i', 's', 'c', 'v', '5'}
// Errors.
var (
@@ -114,6 +118,7 @@ var (
// Public errors.
var (
+ // ErrInvalidReqID represents error when the ID is invalid.
ErrInvalidReqID = errors.New("request ID larger than 8 bytes")
)
@@ -129,10 +134,11 @@ var (
// Codec encodes and decodes Discovery v5 packets.
// This type is not safe for concurrent use.
type Codec struct {
- sha256 hash.Hash
- localnode *enode.LocalNode
- privkey *ecdsa.PrivateKey
- sc *SessionCache
+ sha256 hash.Hash
+ localnode *enode.LocalNode
+ privkey *ecdsa.PrivateKey
+ sc *SessionCache
+ protocolID [6]byte
// encoder buffers
buf bytes.Buffer // whole packet
@@ -145,12 +151,16 @@ type Codec struct {
}
// NewCodec creates a wire codec.
-func NewCodec(ln *enode.LocalNode, key *ecdsa.PrivateKey, clock mclock.Clock) *Codec {
+func NewCodec(ln *enode.LocalNode, key *ecdsa.PrivateKey, clock mclock.Clock, protocolID *[6]byte) *Codec {
c := &Codec{
- sha256: sha256.New(),
- localnode: ln,
- privkey: key,
- sc: NewSessionCache(1024, clock),
+ sha256: sha256.New(),
+ localnode: ln,
+ privkey: key,
+ sc: NewSessionCache(1024, clock),
+ protocolID: DefaultProtocolID,
+ }
+ if protocolID != nil {
+ c.protocolID = *protocolID
}
return c
}
@@ -250,7 +260,7 @@ func (c *Codec) makeHeader(toID enode.ID, flag byte, authsizeExtra int) Header {
}
return Header{
StaticHeader: StaticHeader{
- ProtocolID: protocolID,
+ ProtocolID: c.protocolID,
Version: version,
Flag: flag,
AuthSize: uint16(authsize),
@@ -415,10 +425,10 @@ func (c *Codec) encryptMessage(s *session, p Packet, head *Header, headerData []
// Decode decodes a discovery packet.
func (c *Codec) Decode(input []byte, addr string) (src enode.ID, n *enode.Node, p Packet, err error) {
- // Unmask the static header.
- if len(input) < sizeofStaticPacketData {
+ if len(input) < minPacketSize {
return enode.ID{}, nil, nil, errTooShort
}
+ // Unmask the static header.
var head Header
copy(head.IV[:], input[:sizeofMaskingIV])
mask := head.mask(c.localnode.ID())
@@ -429,7 +439,7 @@ func (c *Codec) Decode(input []byte, addr string) (src enode.ID, n *enode.Node,
c.reader.Reset(staticHeader)
binary.Read(&c.reader, binary.BigEndian, &head.StaticHeader)
remainingInput := len(input) - sizeofStaticPacketData
- if err := head.checkValid(remainingInput); err != nil {
+ if err := head.checkValid(remainingInput, c.protocolID); err != nil {
return enode.ID{}, nil, nil, err
}
@@ -524,7 +534,7 @@ func (c *Codec) decodeHandshake(fromAddr string, head *Header) (n *enode.Node, a
if err != nil {
return nil, auth, nil, errInvalidAuthKey
}
- // Derive sesssion keys.
+ // Derive session keys.
session := deriveKeys(sha256.New, c.privkey, ephkey, auth.h.SrcID, c.localnode.ID(), cdata)
session = session.keysFlipped()
return n, auth, session, nil
@@ -616,7 +626,7 @@ func (c *Codec) decryptMessage(input, nonce, headerData, readKey []byte) (Packet
// checkValid performs some basic validity checks on the header.
// The packetLen here is the length remaining after the static header.
-func (h *StaticHeader) checkValid(packetLen int) error {
+func (h *StaticHeader) checkValid(packetLen int, protocolID [6]byte) error {
if h.ProtocolID != protocolID {
return errInvalidHeader
}
diff --git a/p2p/discover/v5wire/encoding_test.go b/p2p/discover/v5wire/encoding_test.go
index 18aa1db1a4..a5387311a5 100644
--- a/p2p/discover/v5wire/encoding_test.go
+++ b/p2p/discover/v5wire/encoding_test.go
@@ -38,8 +38,7 @@ import (
// To regenerate discv5 test vectors, run
//
-// go test -run TestVectors -write-test-vectors
-//
+// go test -run TestVectors -write-test-vectors
var writeTestVectorsFlag = flag.Bool("write-test-vectors", false, "Overwrite discv5 test vectors in testdata/")
var (
@@ -93,7 +92,7 @@ func TestHandshake(t *testing.T) {
}
// A <- B NODES
- nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{Total: 1})
+ nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{RespCount: 1})
net.nodeA.expectDecode(t, NodesMsg, nodes)
}
@@ -151,7 +150,7 @@ func TestHandshake_norecord(t *testing.T) {
net.nodeB.expectDecode(t, FindnodeMsg, findnode)
// A <- B NODES
- nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{Total: 1})
+ nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{RespCount: 1})
net.nodeA.expectDecode(t, NodesMsg, nodes)
}
@@ -191,7 +190,7 @@ func TestHandshake_rekey(t *testing.T) {
net.nodeB.expectDecode(t, FindnodeMsg, findnode)
// A <- B NODES
- nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{Total: 1})
+ nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{RespCount: 1})
net.nodeA.expectDecode(t, NodesMsg, nodes)
}
@@ -226,7 +225,7 @@ func TestHandshake_rekey2(t *testing.T) {
net.nodeB.expectDecode(t, FindnodeMsg, findnode)
// A <- B NODES
- nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{Total: 1})
+ nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{RespCount: 1})
net.nodeA.expectDecode(t, NodesMsg, nodes)
}
@@ -275,7 +274,15 @@ func TestDecodeErrorsV5(t *testing.T) {
net := newHandshakeTest()
defer net.close()
- net.nodeA.expectDecodeErr(t, errTooShort, []byte{})
+ b := make([]byte, 0)
+ net.nodeA.expectDecodeErr(t, errTooShort, b)
+
+ b = make([]byte, 62)
+ net.nodeA.expectDecodeErr(t, errTooShort, b)
+
+ b = make([]byte, 63)
+ net.nodeA.expectDecodeErr(t, errInvalidHeader, b)
+
// TODO some more tests would be nice :)
// - check invalid authdata sizes
// - check invalid handshake data sizes
@@ -497,8 +504,8 @@ type handshakeTestNode struct {
func newHandshakeTest() *handshakeTest {
t := new(handshakeTest)
- t.nodeA.init(testKeyA, net.IP{127, 0, 0, 1}, &t.clock)
- t.nodeB.init(testKeyB, net.IP{127, 0, 0, 1}, &t.clock)
+ t.nodeA.init(testKeyA, net.IP{127, 0, 0, 1}, &t.clock, DefaultProtocolID)
+ t.nodeB.init(testKeyB, net.IP{127, 0, 0, 1}, &t.clock, DefaultProtocolID)
return t
}
@@ -507,11 +514,11 @@ func (t *handshakeTest) close() {
t.nodeB.ln.Database().Close()
}
-func (n *handshakeTestNode) init(key *ecdsa.PrivateKey, ip net.IP, clock mclock.Clock) {
+func (n *handshakeTestNode) init(key *ecdsa.PrivateKey, ip net.IP, clock mclock.Clock, protocolID [6]byte) {
db, _ := enode.OpenDB("")
n.ln = enode.NewLocalNode(db, key)
n.ln.SetStaticIP(ip)
- n.c = NewCodec(n.ln, key, clock)
+ n.c = NewCodec(n.ln, key, clock, nil)
}
func (n *handshakeTestNode) encode(t testing.TB, to handshakeTestNode, p Packet) ([]byte, Nonce) {
diff --git a/p2p/discover/v5wire/msg.go b/p2p/discover/v5wire/msg.go
index 2f387b4025..fb8e1e12c2 100644
--- a/p2p/discover/v5wire/msg.go
+++ b/p2p/discover/v5wire/msg.go
@@ -20,6 +20,7 @@ import (
"fmt"
"net"
+ "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
@@ -32,6 +33,10 @@ type Packet interface {
Kind() byte // Kind returns the message type.
RequestID() []byte // Returns the request ID.
SetRequestID([]byte) // Sets the request ID.
+
+ // AppendLogInfo returns its argument 'ctx' with additional fields
+ // appended for logging purposes.
+ AppendLogInfo(ctx []interface{}) []interface{}
}
// Message types.
@@ -44,9 +49,6 @@ const (
TalkResponseMsg
RequestTicketMsg
TicketMsg
- RegtopicMsg
- RegconfirmationMsg
- TopicQueryMsg
UnknownPacket = byte(255) // any non-decryptable packet
WhoareyouPacket = byte(254) // the WHOAREYOU packet
@@ -91,13 +93,17 @@ type (
Findnode struct {
ReqID []byte
Distances []uint
+
+ // OpID is for debugging purposes and is not part of the packet encoding.
+ // It identifies the 'operation' on behalf of which the request was sent.
+ OpID uint64 `rlp:"-"`
}
- // NODES is the reply to FINDNODE and TOPICQUERY.
+ // NODES is a response to FINDNODE.
Nodes struct {
- ReqID []byte
- Total uint8
- Nodes []*enr.Record
+ ReqID []byte
+ RespCount uint8 // total number of responses to the request
+ Nodes []*enr.Record
}
// TALKREQ is an application-level request.
@@ -112,37 +118,6 @@ type (
ReqID []byte
Message []byte
}
-
- // REQUESTTICKET requests a ticket for a topic queue.
- RequestTicket struct {
- ReqID []byte
- Topic []byte
- }
-
- // TICKET is the response to REQUESTTICKET.
- Ticket struct {
- ReqID []byte
- Ticket []byte
- }
-
- // REGTOPIC registers the sender in a topic queue using a ticket.
- Regtopic struct {
- ReqID []byte
- Ticket []byte
- ENR *enr.Record
- }
-
- // REGCONFIRMATION is the reply to REGTOPIC.
- Regconfirmation struct {
- ReqID []byte
- Registered bool
- }
-
- // TOPICQUERY asks for nodes with the given topic.
- TopicQuery struct {
- ReqID []byte
- Topic []byte
- }
)
// DecodeMessage decodes the message body of a packet.
@@ -161,16 +136,6 @@ func DecodeMessage(ptype byte, body []byte) (Packet, error) {
dec = new(TalkRequest)
case TalkResponseMsg:
dec = new(TalkResponse)
- case RequestTicketMsg:
- dec = new(RequestTicket)
- case TicketMsg:
- dec = new(Ticket)
- case RegtopicMsg:
- dec = new(Regtopic)
- case RegconfirmationMsg:
- dec = new(Regconfirmation)
- case TopicQueryMsg:
- dec = new(TopicQuery)
default:
return nil, fmt.Errorf("unknown packet type %d", ptype)
}
@@ -188,62 +153,77 @@ func (*Whoareyou) Kind() byte { return WhoareyouPacket }
func (*Whoareyou) RequestID() []byte { return nil }
func (*Whoareyou) SetRequestID([]byte) {}
+func (*Whoareyou) AppendLogInfo(ctx []interface{}) []interface{} {
+ return ctx
+}
+
func (*Unknown) Name() string { return "UNKNOWN/v5" }
func (*Unknown) Kind() byte { return UnknownPacket }
func (*Unknown) RequestID() []byte { return nil }
func (*Unknown) SetRequestID([]byte) {}
+func (*Unknown) AppendLogInfo(ctx []interface{}) []interface{} {
+ return ctx
+}
+
func (*Ping) Name() string { return "PING/v5" }
func (*Ping) Kind() byte { return PingMsg }
func (p *Ping) RequestID() []byte { return p.ReqID }
func (p *Ping) SetRequestID(id []byte) { p.ReqID = id }
+func (p *Ping) AppendLogInfo(ctx []interface{}) []interface{} {
+ return append(ctx, "req", hexutil.Bytes(p.ReqID), "enrseq", p.ENRSeq)
+}
+
func (*Pong) Name() string { return "PONG/v5" }
func (*Pong) Kind() byte { return PongMsg }
func (p *Pong) RequestID() []byte { return p.ReqID }
func (p *Pong) SetRequestID(id []byte) { p.ReqID = id }
-func (*Findnode) Name() string { return "FINDNODE/v5" }
-func (*Findnode) Kind() byte { return FindnodeMsg }
+func (p *Pong) AppendLogInfo(ctx []interface{}) []interface{} {
+ return append(ctx, "req", hexutil.Bytes(p.ReqID), "enrseq", p.ENRSeq)
+}
+
+func (p *Findnode) Name() string { return "FINDNODE/v5" }
+func (p *Findnode) Kind() byte { return FindnodeMsg }
func (p *Findnode) RequestID() []byte { return p.ReqID }
func (p *Findnode) SetRequestID(id []byte) { p.ReqID = id }
+func (p *Findnode) AppendLogInfo(ctx []interface{}) []interface{} {
+ ctx = append(ctx, "req", hexutil.Bytes(p.ReqID))
+ if p.OpID != 0 {
+ ctx = append(ctx, "opid", p.OpID)
+ }
+ return ctx
+}
+
func (*Nodes) Name() string { return "NODES/v5" }
func (*Nodes) Kind() byte { return NodesMsg }
func (p *Nodes) RequestID() []byte { return p.ReqID }
func (p *Nodes) SetRequestID(id []byte) { p.ReqID = id }
+func (p *Nodes) AppendLogInfo(ctx []interface{}) []interface{} {
+ return append(ctx,
+ "req", hexutil.Bytes(p.ReqID),
+ "tot", p.RespCount,
+ "n", len(p.Nodes),
+ )
+}
+
func (*TalkRequest) Name() string { return "TALKREQ/v5" }
func (*TalkRequest) Kind() byte { return TalkRequestMsg }
func (p *TalkRequest) RequestID() []byte { return p.ReqID }
func (p *TalkRequest) SetRequestID(id []byte) { p.ReqID = id }
+func (p *TalkRequest) AppendLogInfo(ctx []interface{}) []interface{} {
+ return append(ctx, "proto", p.Protocol, "reqid", hexutil.Bytes(p.ReqID), "len", len(p.Message))
+}
+
func (*TalkResponse) Name() string { return "TALKRESP/v5" }
func (*TalkResponse) Kind() byte { return TalkResponseMsg }
func (p *TalkResponse) RequestID() []byte { return p.ReqID }
func (p *TalkResponse) SetRequestID(id []byte) { p.ReqID = id }
-func (*RequestTicket) Name() string { return "REQTICKET/v5" }
-func (*RequestTicket) Kind() byte { return RequestTicketMsg }
-func (p *RequestTicket) RequestID() []byte { return p.ReqID }
-func (p *RequestTicket) SetRequestID(id []byte) { p.ReqID = id }
-
-func (*Regtopic) Name() string { return "REGTOPIC/v5" }
-func (*Regtopic) Kind() byte { return RegtopicMsg }
-func (p *Regtopic) RequestID() []byte { return p.ReqID }
-func (p *Regtopic) SetRequestID(id []byte) { p.ReqID = id }
-
-func (*Ticket) Name() string { return "TICKET/v5" }
-func (*Ticket) Kind() byte { return TicketMsg }
-func (p *Ticket) RequestID() []byte { return p.ReqID }
-func (p *Ticket) SetRequestID(id []byte) { p.ReqID = id }
-
-func (*Regconfirmation) Name() string { return "REGCONFIRMATION/v5" }
-func (*Regconfirmation) Kind() byte { return RegconfirmationMsg }
-func (p *Regconfirmation) RequestID() []byte { return p.ReqID }
-func (p *Regconfirmation) SetRequestID(id []byte) { p.ReqID = id }
-
-func (*TopicQuery) Name() string { return "TOPICQUERY/v5" }
-func (*TopicQuery) Kind() byte { return TopicQueryMsg }
-func (p *TopicQuery) RequestID() []byte { return p.ReqID }
-func (p *TopicQuery) SetRequestID(id []byte) { p.ReqID = id }
+func (p *TalkResponse) AppendLogInfo(ctx []interface{}) []interface{} {
+ return append(ctx, "req", p.ReqID, "len", len(p.Message))
+}
diff --git a/p2p/discover/v5wire/session.go b/p2p/discover/v5wire/session.go
index d52b5c1181..862c21fcee 100644
--- a/p2p/discover/v5wire/session.go
+++ b/p2p/discover/v5wire/session.go
@@ -22,10 +22,10 @@ import (
"encoding/binary"
"time"
+ "github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/hashicorp/golang-lru/simplelru"
)
const handshakeTimeout = time.Second
@@ -33,7 +33,7 @@ const handshakeTimeout = time.Second
// The SessionCache keeps negotiated encryption keys and
// state for in-progress handshakes in the Discovery v5 wire protocol.
type SessionCache struct {
- sessions *simplelru.LRU
+ sessions lru.BasicLRU[sessionID, *session]
handshakes map[sessionID]*Whoareyou
clock mclock.Clock
@@ -62,12 +62,8 @@ func (s *session) keysFlipped() *session {
}
func NewSessionCache(maxItems int, clock mclock.Clock) *SessionCache {
- cache, err := simplelru.NewLRU(maxItems, nil)
- if err != nil {
- panic("can't create session cache")
- }
return &SessionCache{
- sessions: cache,
+ sessions: lru.NewBasicLRU[sessionID, *session](maxItems),
handshakes: make(map[sessionID]*Whoareyou),
clock: clock,
nonceGen: generateNonce,
@@ -95,11 +91,8 @@ func (sc *SessionCache) nextNonce(s *session) (Nonce, error) {
// session returns the current session for the given node, if any.
func (sc *SessionCache) session(id enode.ID, addr string) *session {
- item, ok := sc.sessions.Get(sessionID{id, addr})
- if !ok {
- return nil
- }
- return item.(*session)
+ item, _ := sc.sessions.Get(sessionID{id, addr})
+ return item
}
// readKey returns the current read key for the given node.
diff --git a/p2p/dnsdisc/client.go b/p2p/dnsdisc/client.go
index 3f914d6e94..8f1c221b80 100644
--- a/p2p/dnsdisc/client.go
+++ b/p2p/dnsdisc/client.go
@@ -27,12 +27,12 @@ import (
"sync"
"time"
+ "github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
- lru "github.com/hashicorp/golang-lru"
"golang.org/x/sync/singleflight"
"golang.org/x/time/rate"
)
@@ -41,7 +41,7 @@ import (
type Client struct {
cfg Config
clock mclock.Clock
- entries *lru.Cache
+ entries *lru.Cache[string, entry]
ratelimit *rate.Limiter
singleflight singleflight.Group
}
@@ -96,14 +96,10 @@ func (cfg Config) withDefaults() Config {
// NewClient creates a client.
func NewClient(cfg Config) *Client {
cfg = cfg.withDefaults()
- cache, err := lru.New(cfg.CacheLimit)
- if err != nil {
- panic(err)
- }
rlimit := rate.NewLimiter(rate.Limit(cfg.RateLimit), 10)
return &Client{
cfg: cfg,
- entries: cache,
+ entries: lru.NewCache[string, entry](cfg.CacheLimit),
clock: mclock.System{},
ratelimit: rlimit,
}
@@ -176,7 +172,7 @@ func (c *Client) resolveEntry(ctx context.Context, domain, hash string) (entry,
}
cacheKey := truncateHash(hash)
if e, ok := c.entries.Get(cacheKey); ok {
- return e.(entry), nil
+ return e, nil
}
ei, err, _ := c.singleflight.Do(cacheKey, func() (interface{}, error) {
diff --git a/p2p/dnsdisc/client_test.go b/p2p/dnsdisc/client_test.go
index 0a9a96e621..6de7fead22 100644
--- a/p2p/dnsdisc/client_test.go
+++ b/p2p/dnsdisc/client_test.go
@@ -20,12 +20,12 @@ import (
"context"
"crypto/ecdsa"
"errors"
- "math/rand"
"reflect"
"testing"
"time"
"github.com/davecgh/go-spew/spew"
+ "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/testlog"
@@ -34,23 +34,25 @@ import (
"github.com/ethereum/go-ethereum/p2p/enr"
)
-const (
- signingKeySeed = 0x111111
- nodesSeed1 = 0x2945237
- nodesSeed2 = 0x4567299
-)
+var signingKeyForTesting, _ = crypto.ToECDSA(hexutil.MustDecode("0xdc599867fc513f8f5e2c2c9c489cde5e71362d1d9ec6e693e0de063236ed1240"))
func TestClientSyncTree(t *testing.T) {
+ nodes := []string{
+ "enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA",
+ "enr:-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI",
+ "enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o",
+ }
+
r := mapResolver{
"n": "enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA",
"C7HRFPF3BLGF3YR4DY5KX3SMBE.n": "enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org",
"JWXYDBPXYWG6FX3GMDIBFA6CJ4.n": "enrtree-branch:2XS2367YHAXJFGLZHVAWLQD4ZY,H4FHT4B454P6UXFD7JCYQ5PWDY,MHTDO6TMUBRIA2XWG5LUDACK24",
- "2XS2367YHAXJFGLZHVAWLQD4ZY.n": "enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA",
- "H4FHT4B454P6UXFD7JCYQ5PWDY.n": "enr:-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI",
- "MHTDO6TMUBRIA2XWG5LUDACK24.n": "enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o",
+ "2XS2367YHAXJFGLZHVAWLQD4ZY.n": nodes[0],
+ "H4FHT4B454P6UXFD7JCYQ5PWDY.n": nodes[1],
+ "MHTDO6TMUBRIA2XWG5LUDACK24.n": nodes[2],
}
var (
- wantNodes = testNodes(0x29452, 3)
+ wantNodes = sortByID(parseNodes(nodes))
wantLinks = []string{"enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org"}
wantSeq = uint(1)
)
@@ -60,7 +62,7 @@ func TestClientSyncTree(t *testing.T) {
if err != nil {
t.Fatal("sync error:", err)
}
- if !reflect.DeepEqual(sortByID(stree.Nodes()), sortByID(wantNodes)) {
+ if !reflect.DeepEqual(sortByID(stree.Nodes()), wantNodes) {
t.Errorf("wrong nodes in synced tree:\nhave %v\nwant %v", spew.Sdump(stree.Nodes()), spew.Sdump(wantNodes))
}
if !reflect.DeepEqual(stree.Links(), wantLinks) {
@@ -80,7 +82,7 @@ func TestClientSyncTreeBadNode(t *testing.T) {
// tree, _ := MakeTree(3, nil, []string{"enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org"})
// tree.entries[badHash] = &b
// tree.root.eroot = badHash
- // url, _ := tree.Sign(testKey(signingKeySeed), "n")
+ // url, _ := tree.Sign(signingKeyForTesting, "n")
// fmt.Println(url)
// fmt.Printf("%#v\n", tree.ToTXT("n"))
@@ -99,9 +101,13 @@ func TestClientSyncTreeBadNode(t *testing.T) {
// This test checks that randomIterator finds all entries.
func TestIterator(t *testing.T) {
- nodes := testNodes(nodesSeed1, 30)
- tree, url := makeTestTree("n", nodes, nil)
- r := mapResolver(tree.ToTXT("n"))
+ var (
+ keys = testKeys(30)
+ nodes = testNodes(keys)
+ tree, url = makeTestTree("n", nodes, nil)
+ r = mapResolver(tree.ToTXT("n"))
+ )
+
c := NewClient(Config{
Resolver: r,
Logger: testlog.Logger(t, log.LvlTrace),
@@ -132,8 +138,12 @@ func TestIteratorCloseWithoutNext(t *testing.T) {
// This test checks if closing randomIterator races.
func TestIteratorClose(t *testing.T) {
- nodes := testNodes(nodesSeed1, 500)
- tree1, url1 := makeTestTree("t1", nodes, nil)
+ var (
+ keys = testKeys(500)
+ nodes = testNodes(keys)
+ tree1, url1 = makeTestTree("t1", nodes, nil)
+ )
+
c := NewClient(Config{Resolver: newMapResolver(tree1.ToTXT("t1"))})
it, err := c.NewIterator(url1)
if err != nil {
@@ -155,9 +165,13 @@ func TestIteratorClose(t *testing.T) {
// This test checks that randomIterator traverses linked trees as well as explicitly added trees.
func TestIteratorLinks(t *testing.T) {
- nodes := testNodes(nodesSeed1, 40)
- tree1, url1 := makeTestTree("t1", nodes[:10], nil)
- tree2, url2 := makeTestTree("t2", nodes[10:], []string{url1})
+ var (
+ keys = testKeys(40)
+ nodes = testNodes(keys)
+ tree1, url1 = makeTestTree("t1", nodes[:10], nil)
+ tree2, url2 = makeTestTree("t2", nodes[10:], []string{url1})
+ )
+
c := NewClient(Config{
Resolver: newMapResolver(tree1.ToTXT("t1"), tree2.ToTXT("t2")),
Logger: testlog.Logger(t, log.LvlTrace),
@@ -176,7 +190,8 @@ func TestIteratorLinks(t *testing.T) {
func TestIteratorNodeUpdates(t *testing.T) {
var (
clock = new(mclock.Simulated)
- nodes = testNodes(nodesSeed1, 30)
+ keys = testKeys(30)
+ nodes = testNodes(keys)
resolver = newMapResolver()
c = NewClient(Config{
Resolver: resolver,
@@ -197,7 +212,7 @@ func TestIteratorNodeUpdates(t *testing.T) {
checkIterator(t, it, nodes[:25])
// Ensure RandomNode returns the new nodes after the tree is updated.
- updateSomeNodes(nodesSeed1, nodes)
+ updateSomeNodes(keys, nodes)
tree2, _ := makeTestTree("n", nodes, nil)
resolver.clear()
resolver.add(tree2.ToTXT("n"))
@@ -213,7 +228,8 @@ func TestIteratorNodeUpdates(t *testing.T) {
func TestIteratorRootRecheckOnFail(t *testing.T) {
var (
clock = new(mclock.Simulated)
- nodes = testNodes(nodesSeed1, 30)
+ keys = testKeys(30)
+ nodes = testNodes(keys)
resolver = newMapResolver()
c = NewClient(Config{
Resolver: resolver,
@@ -237,7 +253,7 @@ func TestIteratorRootRecheckOnFail(t *testing.T) {
checkIterator(t, it, nodes[:25])
// Ensure RandomNode returns the new nodes after the tree is updated.
- updateSomeNodes(nodesSeed1, nodes)
+ updateSomeNodes(keys, nodes)
tree2, _ := makeTestTree("n", nodes, nil)
resolver.clear()
resolver.add(tree2.ToTXT("n"))
@@ -250,7 +266,8 @@ func TestIteratorRootRecheckOnFail(t *testing.T) {
func TestIteratorEmptyTree(t *testing.T) {
var (
clock = new(mclock.Simulated)
- nodes = testNodes(nodesSeed1, 1)
+ keys = testKeys(1)
+ nodes = testNodes(keys)
resolver = newMapResolver()
c = NewClient(Config{
Resolver: resolver,
@@ -265,7 +282,7 @@ func TestIteratorEmptyTree(t *testing.T) {
resolver.add(tree1.ToTXT("n"))
// Start the iterator.
- node := make(chan *enode.Node)
+ node := make(chan *enode.Node, 1)
it, err := c.NewIterator(url)
if err != nil {
t.Fatal(err)
@@ -294,8 +311,7 @@ func TestIteratorEmptyTree(t *testing.T) {
}
// updateSomeNodes applies ENR updates to some of the given nodes.
-func updateSomeNodes(keySeed int64, nodes []*enode.Node) {
- keys := testKeys(nodesSeed1, len(nodes))
+func updateSomeNodes(keys []*ecdsa.PrivateKey, nodes []*enode.Node) {
for i, n := range nodes[:len(nodes)/2] {
r := n.Record()
r.Set(enr.IP{127, 0, 0, 1})
@@ -311,7 +327,8 @@ func updateSomeNodes(keySeed int64, nodes []*enode.Node) {
func TestIteratorLinkUpdates(t *testing.T) {
var (
clock = new(mclock.Simulated)
- nodes = testNodes(nodesSeed1, 30)
+ keys = testKeys(30)
+ nodes = testNodes(keys)
resolver = newMapResolver()
c = NewClient(Config{
Resolver: resolver,
@@ -384,7 +401,7 @@ func makeTestTree(domain string, nodes []*enode.Node, links []string) (*Tree, st
if err != nil {
panic(err)
}
- url, err := tree.Sign(testKey(signingKeySeed), domain)
+ url, err := tree.Sign(signingKeyForTesting, domain)
if err != nil {
panic(err)
}
@@ -392,11 +409,10 @@ func makeTestTree(domain string, nodes []*enode.Node, links []string) (*Tree, st
}
// testKeys creates deterministic private keys for testing.
-func testKeys(seed int64, n int) []*ecdsa.PrivateKey {
- rand := rand.New(rand.NewSource(seed))
+func testKeys(n int) []*ecdsa.PrivateKey {
keys := make([]*ecdsa.PrivateKey, n)
for i := 0; i < n; i++ {
- key, err := ecdsa.GenerateKey(crypto.S256(), rand)
+ key, err := crypto.GenerateKey()
if err != nil {
panic("can't generate key: " + err.Error())
}
@@ -405,13 +421,8 @@ func testKeys(seed int64, n int) []*ecdsa.PrivateKey {
return keys
}
-func testKey(seed int64) *ecdsa.PrivateKey {
- return testKeys(seed, 1)[0]
-}
-
-func testNodes(seed int64, n int) []*enode.Node {
- keys := testKeys(seed, n)
- nodes := make([]*enode.Node, n)
+func testNodes(keys []*ecdsa.PrivateKey) []*enode.Node {
+ nodes := make([]*enode.Node, len(keys))
for i, key := range keys {
record := new(enr.Record)
record.SetSeq(uint64(i))
@@ -425,10 +436,6 @@ func testNodes(seed int64, n int) []*enode.Node {
return nodes
}
-func testNode(seed int64) *enode.Node {
- return testNodes(seed, 1)[0]
-}
-
type mapResolver map[string]string
func newMapResolver(maps ...map[string]string) mapResolver {
@@ -457,3 +464,15 @@ func (mr mapResolver) LookupTXT(ctx context.Context, name string) ([]string, err
}
return nil, errors.New("not found")
}
+
+func parseNodes(rec []string) []*enode.Node {
+ var ns []*enode.Node
+ for _, r := range rec {
+ var n enode.Node
+ if err := n.UnmarshalText([]byte(r)); err != nil {
+ panic(err)
+ }
+ ns = append(ns, &n)
+ }
+ return ns
+}
diff --git a/p2p/dnsdisc/tree.go b/p2p/dnsdisc/tree.go
index 7d11e07ef7..a3f426e428 100644
--- a/p2p/dnsdisc/tree.go
+++ b/p2p/dnsdisc/tree.go
@@ -117,32 +117,32 @@ func (t *Tree) Nodes() []*enode.Node {
We want to keep the UDP size below 512 bytes. The UDP size is roughly:
UDP length = 8 + UDP payload length ( 229 )
UPD Payload length:
- - dns.id 2
- - dns.flags 2
- - dns.count.queries 2
- - dns.count.answers 2
- - dns.count.auth_rr 2
- - dns.count.add_rr 2
- - queries (query-size + 6)
- - answers :
- - dns.resp.name 2
- - dns.resp.type 2
- - dns.resp.class 2
- - dns.resp.ttl 4
- - dns.resp.len 2
- - dns.txt.length 1
- - dns.txt resp_data_size
-
-So the total size is roughly a fixed overhead of `39`, and the size of the
-query (domain name) and response.
-The query size is, for example, FVY6INQ6LZ33WLCHO3BPR3FH6Y.snap.mainnet.ethdisco.net (52)
+ - dns.id 2
+ - dns.flags 2
+ - dns.count.queries 2
+ - dns.count.answers 2
+ - dns.count.auth_rr 2
+ - dns.count.add_rr 2
+ - queries (query-size + 6)
+ - answers :
+ - dns.resp.name 2
+ - dns.resp.type 2
+ - dns.resp.class 2
+ - dns.resp.ttl 4
+ - dns.resp.len 2
+ - dns.txt.length 1
+ - dns.txt resp_data_size
+
+So the total size is roughly a fixed overhead of `39`, and the size of the query (domain
+name) and response. The query size is, for example,
+FVY6INQ6LZ33WLCHO3BPR3FH6Y.snap.mainnet.ethdisco.net (52)
We also have some static data in the response, such as `enrtree-branch:`, and potentially
splitting the response up with `" "`, leaving us with a size of roughly `400` that we need
to stay below.
-The number `370` is used to have some margin for extra overhead (for example, the dns query
-may be larger - more subdomains).
+The number `370` is used to have some margin for extra overhead (for example, the dns
+query may be larger - more subdomains).
*/
const (
hashAbbrevSize = 1 + 16*13/8 // Size of an encoded hash (plus comma)
diff --git a/p2p/dnsdisc/tree_test.go b/p2p/dnsdisc/tree_test.go
index 461b9ec4fd..9ed17aa4b3 100644
--- a/p2p/dnsdisc/tree_test.go
+++ b/p2p/dnsdisc/tree_test.go
@@ -61,7 +61,9 @@ func TestParseRoot(t *testing.T) {
}
func TestParseEntry(t *testing.T) {
- testkey := testKey(signingKeySeed)
+ testENRs := []string{"enr:-HW4QES8QIeXTYlDzbfr1WEzE-XKY4f8gJFJzjJL-9D7TC9lJb4Z3JPRRz1lP4pL_N_QpT6rGQjAU9Apnc-C1iMP36OAgmlkgnY0iXNlY3AyNTZrMaED5IdwfMxdmR8W37HqSFdQLjDkIwBd4Q_MjxgZifgKSdM"}
+ testNodes := parseNodes(testENRs)
+
tests := []struct {
input string
e entry
@@ -91,7 +93,11 @@ func TestParseEntry(t *testing.T) {
// Links
{
input: "enrtree://AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@nodes.example.org",
- e: &linkEntry{"AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@nodes.example.org", "nodes.example.org", &testkey.PublicKey},
+ e: &linkEntry{
+ str: "AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@nodes.example.org",
+ domain: "nodes.example.org",
+ pubkey: &signingKeyForTesting.PublicKey,
+ },
},
{
input: "enrtree://nodes.example.org",
@@ -107,8 +113,8 @@ func TestParseEntry(t *testing.T) {
},
// ENRs
{
- input: "enr:-HW4QES8QIeXTYlDzbfr1WEzE-XKY4f8gJFJzjJL-9D7TC9lJb4Z3JPRRz1lP4pL_N_QpT6rGQjAU9Apnc-C1iMP36OAgmlkgnY0iXNlY3AyNTZrMaED5IdwfMxdmR8W37HqSFdQLjDkIwBd4Q_MjxgZifgKSdM",
- e: &enrEntry{node: testNode(nodesSeed1)},
+ input: testENRs[0],
+ e: &enrEntry{node: testNodes[0]},
},
{
input: "enr:-HW4QLZHjM4vZXkbp-5xJoHsKSbE7W39FPC8283X-y8oHcHPTnDDlIlzL5ArvDUlHZVDPgmFASrh7cWgLOLxj4wprRkHgmlkgnY0iXNlY3AyNTZrMaEC3t2jLMhDpCDX5mbSEwDn4L3iUfyXzoO8G28XvjGRkrAg=",
@@ -132,7 +138,8 @@ func TestParseEntry(t *testing.T) {
}
func TestMakeTree(t *testing.T) {
- nodes := testNodes(nodesSeed2, 50)
+ keys := testKeys(50)
+ nodes := testNodes(keys)
tree, err := MakeTree(2, nodes, nil)
if err != nil {
t.Fatal(err)
diff --git a/p2p/enode/idscheme.go b/p2p/enode/idscheme.go
index c1834f0699..fd5d868b76 100644
--- a/p2p/enode/idscheme.go
+++ b/p2p/enode/idscheme.go
@@ -28,17 +28,18 @@ import (
"golang.org/x/crypto/sha3"
)
-// List of known secure identity schemes.
+// ValidSchemes is a List of known secure identity schemes.
var ValidSchemes = enr.SchemeMap{
"v4": V4ID{},
}
+// ValidSchemesForTesting is a List of identity schemes for testing.
var ValidSchemesForTesting = enr.SchemeMap{
"v4": V4ID{},
"null": NullID{},
}
-// v4ID is the "v4" identity scheme.
+// V4ID is the "v4" identity scheme.
type V4ID struct{}
// SignV4 signs a record using the v4 scheme.
diff --git a/p2p/enode/iter.go b/p2p/enode/iter.go
index 664964f534..b8ab4a758a 100644
--- a/p2p/enode/iter.go
+++ b/p2p/enode/iter.go
@@ -203,27 +203,34 @@ func (m *FairMix) Close() {
func (m *FairMix) Next() bool {
m.cur = nil
- var timeout <-chan time.Time
- if m.timeout >= 0 {
- timer := time.NewTimer(m.timeout)
- timeout = timer.C
- defer timer.Stop()
- }
for {
source := m.pickSource()
if source == nil {
return m.nextFromAny()
}
+
+ var timeout <-chan time.Time
+ if source.timeout >= 0 {
+ timer := time.NewTimer(source.timeout)
+ timeout = timer.C
+ defer timer.Stop()
+ }
+
select {
case n, ok := <-source.next:
if ok {
- m.cur = n
+ // Here, the timeout is reset to the configured value
+ // because the source delivered a node.
source.timeout = m.timeout
+ m.cur = n
return true
}
// This source has ended.
m.deleteSource(source)
case <-timeout:
+ // The selected source did not deliver a node within the timeout, so the
+ // timeout duration is halved for next time. This is supposed to improve
+ // latency with stuck sources.
source.timeout /= 2
return m.nextFromAny()
}
diff --git a/p2p/enode/localnode_test.go b/p2p/enode/localnode_test.go
index 312df813bb..7f97ad392f 100644
--- a/p2p/enode/localnode_test.go
+++ b/p2p/enode/localnode_test.go
@@ -17,7 +17,7 @@
package enode
import (
- "math/rand"
+ "crypto/rand"
"net"
"testing"
diff --git a/p2p/enode/node.go b/p2p/enode/node.go
index d747ca3313..d7a1a9a156 100644
--- a/p2p/enode/node.go
+++ b/p2p/enode/node.go
@@ -199,7 +199,7 @@ func (n ID) String() string {
return fmt.Sprintf("%x", n[:])
}
-// The Go syntax representation of a ID is a call to HexID.
+// GoString returns the Go syntax representation of a ID is a call to HexID.
func (n ID) GoString() string {
return fmt.Sprintf("enode.HexID(\"%x\")", n[:])
}
diff --git a/p2p/enode/nodedb.go b/p2p/enode/nodedb.go
index d1712f7597..7e7fb69b29 100644
--- a/p2p/enode/nodedb.go
+++ b/p2p/enode/nodedb.go
@@ -494,7 +494,7 @@ func nextNode(it iterator.Iterator) *Node {
return nil
}
-// close flushes and closes the database files.
+// Close flushes and closes the database files.
func (db *DB) Close() {
close(db.quit)
db.lvl.Close()
diff --git a/p2p/enode/urlv4.go b/p2p/enode/urlv4.go
index c445049102..0272eee987 100644
--- a/p2p/enode/urlv4.go
+++ b/p2p/enode/urlv4.go
@@ -54,8 +54,8 @@ func MustParseV4(rawurl string) *Node {
//
// For incomplete nodes, the designator must look like one of these
//
-// enode://
-//
+// enode://
+//
//
// For complete nodes, the node ID is encoded in the username portion
// of the URL, separated from the host by an @ sign. The hostname can
@@ -68,7 +68,7 @@ func MustParseV4(rawurl string) *Node {
// a node with IP address 10.3.58.6, TCP listening port 30303
// and UDP discovery port 30301.
//
-// enode://@10.3.58.6:30303?discport=30301
+// enode://@10.3.58.6:30303?discport=30301
func ParseV4(rawurl string) (*Node, error) {
if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil {
id, err := parsePubkey(m[1])
diff --git a/p2p/enr/enr.go b/p2p/enr/enr.go
index 15891813b4..2b093b2f1a 100644
--- a/p2p/enr/enr.go
+++ b/p2p/enr/enr.go
@@ -19,7 +19,7 @@
// stored in key/value pairs. To store and retrieve key/values in a record, use the Entry
// interface.
//
-// Signature Handling
+// # Signature Handling
//
// Records must be signed before transmitting them to another node.
//
@@ -96,6 +96,24 @@ type pair struct {
v rlp.RawValue
}
+// Size returns the encoded size of the record.
+func (r *Record) Size() uint64 {
+ if r.raw != nil {
+ return uint64(len(r.raw))
+ }
+ return computeSize(r)
+}
+
+func computeSize(r *Record) uint64 {
+ size := uint64(rlp.IntSize(r.seq))
+ size += rlp.BytesSize(r.signature)
+ for _, p := range r.pairs {
+ size += rlp.StringSize(p.k)
+ size += uint64(len(p.v))
+ }
+ return rlp.ListSize(size)
+}
+
// Seq returns the sequence number.
func (r *Record) Seq() uint64 {
return r.seq
diff --git a/p2p/enr/enr_test.go b/p2p/enr/enr_test.go
index bf3f104744..b85ee209d5 100644
--- a/p2p/enr/enr_test.go
+++ b/p2p/enr/enr_test.go
@@ -169,6 +169,32 @@ func TestDirty(t *testing.T) {
}
}
+func TestSize(t *testing.T) {
+ var r Record
+
+ // Empty record size is 3 bytes.
+ // Unsigned records cannot be encoded, but they could, the encoding
+ // would be [ 0, 0 ] -> 0xC28080.
+ assert.Equal(t, uint64(3), r.Size())
+
+ // Add one attribute. The size increases to 5, the encoding
+ // would be [ 0, 0, "k", "v" ] -> 0xC58080C26B76.
+ r.Set(WithEntry("k", "v"))
+ assert.Equal(t, uint64(5), r.Size())
+
+ // Now add a signature.
+ nodeid := []byte{1, 2, 3, 4, 5, 6, 7, 8}
+ signTest(nodeid, &r)
+ assert.Equal(t, uint64(45), r.Size())
+ enc, _ := rlp.EncodeToBytes(&r)
+ if r.Size() != uint64(len(enc)) {
+ t.Error("Size() not equal encoded length", len(enc))
+ }
+ if r.Size() != computeSize(&r) {
+ t.Error("Size() not equal computed size", computeSize(&r))
+ }
+}
+
func TestSeq(t *testing.T) {
var r Record
@@ -268,8 +294,11 @@ func TestSignEncodeAndDecodeRandom(t *testing.T) {
}
require.NoError(t, signTest([]byte{5}, &r))
- _, err := rlp.EncodeToBytes(r)
+
+ enc, err := rlp.EncodeToBytes(r)
require.NoError(t, err)
+ require.Equal(t, uint64(len(enc)), r.Size())
+ require.Equal(t, uint64(len(enc)), computeSize(&r))
for k, v := range pairs {
desc := fmt.Sprintf("key %q", k)
diff --git a/p2p/enr/entries.go b/p2p/enr/entries.go
index a8b0a3839b..9945a436c9 100644
--- a/p2p/enr/entries.go
+++ b/p2p/enr/entries.go
@@ -61,7 +61,7 @@ type TCP uint16
func (v TCP) ENRKey() string { return "tcp" }
-// UDP is the "udp" key, which holds the IPv6-specific UDP port of the node.
+// TCP6 is the "tcp6" key, which holds the IPv6-specific tcp6 port of the node.
type TCP6 uint16
func (v TCP6) ENRKey() string { return "tcp6" }
@@ -71,7 +71,7 @@ type UDP uint16
func (v UDP) ENRKey() string { return "udp" }
-// UDP is the "udp" key, which holds the IPv6-specific UDP port of the node.
+// UDP6 is the "udp6" key, which holds the IPv6-specific UDP port of the node.
type UDP6 uint16
func (v UDP6) ENRKey() string { return "udp6" }
diff --git a/p2p/message.go b/p2p/message.go
index 7cbe0f1dc8..24f21456d8 100644
--- a/p2p/message.go
+++ b/p2p/message.go
@@ -107,12 +107,11 @@ func Send(w MsgWriter, msgcode uint64, data interface{}) error {
// SendItems writes an RLP with the given code and data elements.
// For a call such as:
//
-// SendItems(w, code, e1, e2, e3)
+// SendItems(w, code, e1, e2, e3)
//
// the message payload will be an RLP list containing the items:
//
-// [e1, e2, e3]
-//
+// [e1, e2, e3]
func SendItems(w MsgWriter, msgcode uint64, elems ...interface{}) error {
return Send(w, msgcode, elems)
}
diff --git a/p2p/msgrate/msgrate.go b/p2p/msgrate/msgrate.go
index d4e0eb8b5a..ff29c9620a 100644
--- a/p2p/msgrate/msgrate.go
+++ b/p2p/msgrate/msgrate.go
@@ -38,14 +38,6 @@ const measurementImpact = 0.1
// to fetch more than some local stable value.
const capacityOverestimation = 1.01
-// qosTuningPeers is the number of best peers to tune round trip times based on.
-// An Ethereum node doesn't need hundreds of connections to operate correctly,
-// so instead of lowering our download speed to the median of potentially many
-// bad nodes, we can target a smaller set of vey good nodes. At worse this will
-// result in less nodes to sync from, but that's still better than some hogging
-// the pipeline.
-const qosTuningPeers = 5
-
// rttMinEstimate is the minimal round trip time to target requests for. Since
// every request entails a 2 way latency + bandwidth + serving database lookups,
// it should be generous enough to permit meaningful work to be done on top of
@@ -81,7 +73,7 @@ const rttMinConfidence = 0.1
const ttlScaling = 3
// ttlLimit is the maximum timeout allowance to prevent reaching crazy numbers
-// if some unforeseen network events shappen. As much as we try to hone in on
+// if some unforeseen network events happen. As much as we try to hone in on
// the most optimal values, it doesn't make any sense to go above a threshold,
// even if everything is slow and screwy.
const ttlLimit = time.Minute
@@ -100,9 +92,9 @@ const tuningImpact = 0.25
// Tracker estimates the throughput capacity of a peer with regard to each data
// type it can deliver. The goal is to dynamically adjust request sizes to max
-// out network throughput without overloading either the peer or th elocal node.
+// out network throughput without overloading either the peer or the local node.
//
-// By tracking in real time the latencies and bandiwdths peers exhibit for each
+// By tracking in real time the latencies and bandwidths peers exhibit for each
// packet type, it's possible to prevent overloading by detecting a slowdown on
// one type when another type is pushed too hard.
//
@@ -222,7 +214,7 @@ type Trackers struct {
// confidence represents the probability that the estimated roundtrip value
// is the real one across all our peers. The confidence value is used as an
// impact factor of new measurements on old estimates. As our connectivity
- // stabilizes, this value gravitates towards 1, new measurements havinng
+ // stabilizes, this value gravitates towards 1, new measurements having
// almost no impact. If there's a large peer churn and few peers, then new
// measurements will impact it more. The confidence is increased with every
// packet and dropped with every new connection.
@@ -303,11 +295,15 @@ func (t *Trackers) medianRoundTrip() time.Duration {
}
sort.Float64s(rtts)
- median := rttMaxEstimate
- if qosTuningPeers <= len(rtts) {
- median = time.Duration(rtts[qosTuningPeers/2]) // Median of our best few peers
- } else if len(rtts) > 0 {
- median = time.Duration(rtts[len(rtts)/2]) // Median of all out connected peers
+ var median time.Duration
+ switch len(rtts) {
+ case 0:
+ median = rttMaxEstimate
+ case 1:
+ median = time.Duration(rtts[0])
+ default:
+ idx := int(math.Sqrt(float64(len(rtts))))
+ median = time.Duration(rtts[idx])
}
// Restrict the RTT into some QoS defaults, irrelevant of true RTT
if median < rttMinEstimate {
@@ -320,7 +316,7 @@ func (t *Trackers) medianRoundTrip() time.Duration {
}
// MeanCapacities returns the capacities averaged across all the added trackers.
-// The purpos of the mean capacities are to initialize a new peer with some sane
+// The purpose of the mean capacities are to initialize a new peer with some sane
// starting values that it will hopefully outperform. If the mean overshoots, the
// peer will be cut back to minimal capacity and given another chance.
func (t *Trackers) MeanCapacities() map[uint64]float64 {
diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go
index 9d5519b9c4..ad4c36582a 100644
--- a/p2p/nat/nat.go
+++ b/p2p/nat/nat.go
@@ -29,7 +29,7 @@ import (
natpmp "github.com/jackpal/go-nat-pmp"
)
-// An implementation of nat.Interface can map local ports to ports
+// Interface An implementation of nat.Interface can map local ports to ports
// accessible from the Internet.
type Interface interface {
// These methods manage a mapping between a port on the local
@@ -41,11 +41,11 @@ type Interface interface {
AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error
DeleteMapping(protocol string, extport, intport int) error
- // This method should return the external (Internet-facing)
+ // ExternalIP should return the external (Internet-facing)
// address of the gateway device.
ExternalIP() (net.IP, error)
- // Should return name of the method. This is used for logging.
+ // String should return name of the method. This is used for logging.
String() string
}
@@ -53,12 +53,12 @@ type Interface interface {
// The following formats are currently accepted.
// Note that mechanism names are not case-sensitive.
//
-// "" or "none" return nil
-// "extip:77.12.33.4" will assume the local machine is reachable on the given IP
-// "any" uses the first auto-detected mechanism
-// "upnp" uses the Universal Plug and Play protocol
-// "pmp" uses NAT-PMP with an auto-detected gateway address
-// "pmp:192.168.0.1" uses NAT-PMP with the given gateway address
+// "" or "none" return nil
+// "extip:77.12.33.4" will assume the local machine is reachable on the given IP
+// "any" uses the first auto-detected mechanism
+// "upnp" uses the Universal Plug and Play protocol
+// "pmp" uses NAT-PMP with an auto-detected gateway address
+// "pmp:192.168.0.1" uses NAT-PMP with the given gateway address
func Parse(spec string) (Interface, error) {
var (
parts = strings.SplitN(spec, ":", 2)
diff --git a/p2p/nat/natpmp.go b/p2p/nat/natpmp.go
index 7f85543f8e..40f2aff44e 100644
--- a/p2p/nat/natpmp.go
+++ b/p2p/nat/natpmp.go
@@ -50,8 +50,22 @@ func (n *pmp) AddMapping(protocol string, extport, intport int, name string, lif
}
// Note order of port arguments is switched between our
// AddMapping and the client's AddPortMapping.
- _, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second))
- return err
+ res, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second))
+ if err != nil {
+ return err
+ }
+
+ // NAT-PMP maps an alternative available port number if the requested
+ // port is already mapped to another address and returns success. In this
+ // case, we return an error because there is no way to return the new port
+ // to the caller.
+ if uint16(extport) != res.MappedExternalPort {
+ // Destroy the mapping in NAT device.
+ n.c.AddPortMapping(strings.ToLower(protocol), intport, 0, 0)
+ return fmt.Errorf("port %d already mapped to another address (%s)", extport, protocol)
+ }
+
+ return nil
}
func (n *pmp) DeleteMapping(protocol string, extport, intport int) (err error) {
@@ -95,13 +109,6 @@ func discoverPMP() Interface {
return nil
}
-var (
- // LAN IP ranges
- _, lan10, _ = net.ParseCIDR("10.0.0.0/8")
- _, lan176, _ = net.ParseCIDR("172.16.0.0/12")
- _, lan192, _ = net.ParseCIDR("192.168.0.0/16")
-)
-
// TODO: improve this. We currently assume that (on most networks)
// the router is X.X.X.1 in a local LAN range.
func potentialGateways() (gws []net.IP) {
@@ -116,7 +123,7 @@ func potentialGateways() (gws []net.IP) {
}
for _, addr := range ifaddrs {
if x, ok := addr.(*net.IPNet); ok {
- if lan10.Contains(x.IP) || lan176.Contains(x.IP) || lan192.Contains(x.IP) {
+ if x.IP.IsPrivate() {
ip := x.IP.Mask(x.Mask).To4()
if ip != nil {
ip[3] = ip[3] | 0x01
diff --git a/p2p/netutil/iptrack_test.go b/p2p/netutil/iptrack_test.go
index a9a2998a65..ee3bba861e 100644
--- a/p2p/netutil/iptrack_test.go
+++ b/p2p/netutil/iptrack_test.go
@@ -17,8 +17,8 @@
package netutil
import (
+ crand "crypto/rand"
"fmt"
- mrand "math/rand"
"testing"
"time"
@@ -123,8 +123,8 @@ func TestIPTrackerForceGC(t *testing.T) {
for i := 0; i < 5*max; i++ {
e1 := make([]byte, 4)
e2 := make([]byte, 4)
- mrand.Read(e1)
- mrand.Read(e2)
+ crand.Read(e1)
+ crand.Read(e2)
it.AddStatement(string(e1), string(e2))
it.AddContact(string(e1))
clock.Run(rate)
diff --git a/p2p/nodestate/nodestate.go b/p2p/nodestate/nodestate.go
index 2af0d0a6bd..3adcd6c463 100644
--- a/p2p/nodestate/nodestate.go
+++ b/p2p/nodestate/nodestate.go
@@ -117,7 +117,7 @@ type (
decode func([]byte) (interface{}, error)
}
- // stateSetup contains the list of flags and fields used by the application
+ // Setup contains the list of flags and fields used by the application
Setup struct {
Version uint
flags []flagDefinition
diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go
index 1cb26a8ea0..36b5286517 100644
--- a/p2p/simulations/adapters/inproc.go
+++ b/p2p/simulations/adapters/inproc.go
@@ -206,7 +206,7 @@ func (sn *SimNode) ServeRPC(conn *websocket.Conn) error {
if err != nil {
return err
}
- codec := rpc.NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON)
+ codec := rpc.NewFuncCodec(conn, func(v any, _ bool) error { return conn.WriteJSON(v) }, conn.ReadJSON)
handler.ServeCodec(codec, 0)
return nil
}
diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go
index aeb8ef7772..3b4e05a901 100644
--- a/p2p/simulations/adapters/types.go
+++ b/p2p/simulations/adapters/types.go
@@ -39,10 +39,9 @@ import (
// Node represents a node in a simulation network which is created by a
// NodeAdapter, for example:
//
-// * SimNode - An in-memory node
-// * ExecNode - A child process node
-// * DockerNode - A Docker container node
-//
+// - SimNode, an in-memory node in the same process
+// - ExecNode, a child process node
+// - DockerNode, a node running in a Docker container
type Node interface {
// Addr returns the node's address (e.g. an Enode URL)
Addr() []byte
diff --git a/p2p/simulations/examples/ping-pong.go b/p2p/simulations/examples/ping-pong.go
index 2f4c560548..d9b51dc09b 100644
--- a/p2p/simulations/examples/ping-pong.go
+++ b/p2p/simulations/examples/ping-pong.go
@@ -139,7 +139,7 @@ const (
func (p *pingPongService) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
log := p.log.New("peer.id", peer.ID())
- errC := make(chan error)
+ errC := make(chan error, 1)
go func() {
for range time.Tick(10 * time.Second) {
log.Info("sending ping")
diff --git a/p2p/simulations/http.go b/p2p/simulations/http.go
index b221a0597f..7a4f70e9b0 100644
--- a/p2p/simulations/http.go
+++ b/p2p/simulations/http.go
@@ -102,7 +102,7 @@ type SubscribeOpts struct {
// nodes and connections and filtering message events
func (c *Client) SubscribeNetwork(events chan *Event, opts SubscribeOpts) (event.Subscription, error) {
url := fmt.Sprintf("%s/events?current=%t&filter=%s", c.URL, opts.Current, opts.Filter)
- req, err := http.NewRequest("GET", url, nil)
+ req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
@@ -215,18 +215,18 @@ func (c *Client) RPCClient(ctx context.Context, nodeID string) (*rpc.Client, err
// Get performs a HTTP GET request decoding the resulting JSON response
// into "out"
func (c *Client) Get(path string, out interface{}) error {
- return c.Send("GET", path, nil, out)
+ return c.Send(http.MethodGet, path, nil, out)
}
// Post performs a HTTP POST request sending "in" as the JSON body and
// decoding the resulting JSON response into "out"
func (c *Client) Post(path string, in, out interface{}) error {
- return c.Send("POST", path, in, out)
+ return c.Send(http.MethodPost, path, in, out)
}
// Delete performs a HTTP DELETE request
func (c *Client) Delete(path string) error {
- return c.Send("DELETE", path, nil, nil)
+ return c.Send(http.MethodDelete, path, nil, nil)
}
// Send performs a HTTP request, sending "in" as the JSON request body and
@@ -365,7 +365,7 @@ func (s *Server) StopMocker(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
}
-// GetMockerList returns a list of available mockers
+// GetMockers returns a list of available mockers
func (s *Server) GetMockers(w http.ResponseWriter, req *http.Request) {
list := GetMockerList()
s.JSON(w, http.StatusOK, list)
diff --git a/p2p/simulations/http_test.go b/p2p/simulations/http_test.go
index 5775977a41..05e43238ab 100644
--- a/p2p/simulations/http_test.go
+++ b/p2p/simulations/http_test.go
@@ -596,7 +596,7 @@ func TestHTTPSnapshot(t *testing.T) {
network, s := testHTTPServer(t)
defer s.Close()
- var eventsDone = make(chan struct{})
+ var eventsDone = make(chan struct{}, 1)
count := 1
eventsDoneChan := make(chan *Event)
eventSub := network.Events().Subscribe(eventsDoneChan)
diff --git a/p2p/simulations/mocker.go b/p2p/simulations/mocker.go
index 5a74b02c43..0dc04e65f9 100644
--- a/p2p/simulations/mocker.go
+++ b/p2p/simulations/mocker.go
@@ -29,20 +29,20 @@ import (
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
)
-//a map of mocker names to its function
+// a map of mocker names to its function
var mockerList = map[string]func(net *Network, quit chan struct{}, nodeCount int){
"startStop": startStop,
"probabilistic": probabilistic,
"boot": boot,
}
-//Lookup a mocker by its name, returns the mockerFn
+// LookupMocker looks a mocker by its name, returns the mockerFn
func LookupMocker(mockerType string) func(net *Network, quit chan struct{}, nodeCount int) {
return mockerList[mockerType]
}
-//Get a list of mockers (keys of the map)
-//Useful for frontend to build available mocker selection
+// GetMockerList returns a list of mockers (keys of the map)
+// Useful for frontend to build available mocker selection
func GetMockerList() []string {
list := make([]string, 0, len(mockerList))
for k := range mockerList {
@@ -51,7 +51,7 @@ func GetMockerList() []string {
return list
}
-//The boot mockerFn only connects the node in a ring and doesn't do anything else
+// The boot mockerFn only connects the node in a ring and doesn't do anything else
func boot(net *Network, quit chan struct{}, nodeCount int) {
_, err := connectNodesInRing(net, nodeCount)
if err != nil {
@@ -59,7 +59,7 @@ func boot(net *Network, quit chan struct{}, nodeCount int) {
}
}
-//The startStop mockerFn stops and starts nodes in a defined period (ticker)
+// The startStop mockerFn stops and starts nodes in a defined period (ticker)
func startStop(net *Network, quit chan struct{}, nodeCount int) {
nodes, err := connectNodesInRing(net, nodeCount)
if err != nil {
@@ -96,10 +96,10 @@ func startStop(net *Network, quit chan struct{}, nodeCount int) {
}
}
-//The probabilistic mocker func has a more probabilistic pattern
-//(the implementation could probably be improved):
-//nodes are connected in a ring, then a varying number of random nodes is selected,
-//mocker then stops and starts them in random intervals, and continues the loop
+// The probabilistic mocker func has a more probabilistic pattern
+// (the implementation could probably be improved):
+// nodes are connected in a ring, then a varying number of random nodes is selected,
+// mocker then stops and starts them in random intervals, and continues the loop
func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
nodes, err := connectNodesInRing(net, nodeCount)
if err != nil {
@@ -159,7 +159,7 @@ func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
}
}
-//connect nodeCount number of nodes in a ring
+// connect nodeCount number of nodes in a ring
func connectNodesInRing(net *Network, nodeCount int) ([]enode.ID, error) {
ids := make([]enode.ID, nodeCount)
for i := 0; i < nodeCount; i++ {
diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go
index d6c5aca73c..4735e5cfa6 100644
--- a/p2p/simulations/network.go
+++ b/p2p/simulations/network.go
@@ -646,8 +646,8 @@ func (net *Network) getConn(oneID, otherID enode.ID) *Conn {
return net.Conns[i]
}
-// InitConn(one, other) retrieves the connection model for the connection between
-// peers one and other, or creates a new one if it does not exist
+// InitConn retrieves the connection model for the connection between
+// peers 'oneID' and 'otherID', or creates a new one if it does not exist
// the order of nodes does not matter, i.e., Conn(i,j) == Conn(j, i)
// it checks if the connection is already up, and if the nodes are running
// NOTE:
diff --git a/p2p/util.go b/p2p/util.go
index 3c5f6b8508..2c8f322a66 100644
--- a/p2p/util.go
+++ b/p2p/util.go
@@ -70,6 +70,7 @@ func (h *expHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
+ old[n-1] = expItem{}
*h = old[0 : n-1]
return x
}
diff --git a/params/bootnodes.go b/params/bootnodes.go
index b809977745..0a995bc3c4 100644
--- a/params/bootnodes.go
+++ b/params/bootnodes.go
@@ -32,15 +32,6 @@ var MainnetBootnodes = []string{
"enode://4aeb4ab6c14b23e2c4cfdce879c04b0748a20d8e9b59e25ded2a08143e265c6c25936e74cbc8e641e3312ca288673d91f2f93f8e277de3cfa444ecdaaf982052@157.90.35.166:30303", // bootnode-hetzner-fsn
}
-// RopstenBootnodes are the enode URLs of the P2P bootstrap nodes running on the
-// Ropsten test network.
-var RopstenBootnodes = []string{
- "enode://30b7ab30a01c124a6cceca36863ece12c4f5fa68e3ba9b0b51407ccc002eeed3b3102d20a88f1c1d3c3154e2449317b8ef95090e77b312d5cc39354f86d5d606@52.176.7.10:30303", // US-Azure geth
- "enode://865a63255b3bb68023b6bffd5095118fcc13e79dcf014fe4e47e065c350c7cc72af2e53eff895f11ba1bbb6a2b33271c1116ee870f266618eadfc2e78aa7349c@52.176.100.77:30303", // US-Azure parity
- "enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303", // Parity
- "enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303", // @gpip
-}
-
// SepoliaBootnodes are the enode URLs of the P2P bootstrap nodes running on the
// Sepolia test network.
var SepoliaBootnodes = []string{
@@ -54,7 +45,6 @@ var SepoliaBootnodes = []string{
// Rinkeby test network.
var RinkebyBootnodes = []string{
"enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303", // IE
- "enode://343149e4feefa15d882d9fe4ac7d88f885bd05ebb735e547f12e12080a9fa07c8014ca6fd7f373123488102fe5e34111f8509cf0b7de3f5b44339c9f25e87cb8@52.3.158.184:30303", // INFURA
"enode://b6b28890b006743680c52e64e0d16db57f28124885595fa03a562be1d2bf0f3a1da297d56b13da25fb992888fd556d4c1a27b1f39d531bde7de1921c90061cc6@159.89.28.211:30303", // AKASHA
}
@@ -75,13 +65,6 @@ var GoerliBootnodes = []string{
"enode://d2b720352e8216c9efc470091aa91ddafc53e222b32780f505c817ceef69e01d5b0b0797b69db254c586f493872352f5a022b4d8479a00fc92ec55f9ad46a27e@88.99.70.182:30303",
}
-var KilnBootnodes = []string{
- "enode://c354db99124f0faf677ff0e75c3cbbd568b2febc186af664e0c51ac435609badedc67a18a63adb64dacc1780a28dcefebfc29b83fd1a3f4aa3c0eb161364cf94@164.92.130.5:30303",
- "enode://d41af1662434cad0a88fe3c7c92375ec5719f4516ab6d8cb9695e0e2e815382c767038e72c224e04040885157da47422f756c040a9072676c6e35c5b1a383cce@138.68.66.103:30303",
- "enode://91a745c3fb069f6b99cad10b75c463d527711b106b622756e9ef9f12d2631b6cb885f831d1c8731b9bc7177cae5e1ea1f1be087f86d7d30b590a91f22bc041b0@165.232.180.230:30303",
- "enode://b74bd2e8a9f0c53f0c93bcce80818f2f19439fd807af5c7fbc3efb10130c6ee08be8f3aaec7dc0a057ad7b2a809c8f34dc62431e9b6954b07a6548cc59867884@164.92.140.200:30303",
-}
-
var V5Bootnodes = []string{
// Teku team's bootnode
"enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA",
@@ -110,8 +93,6 @@ func KnownDNSNetwork(genesis common.Hash, protocol string) string {
switch genesis {
case MainnetGenesisHash:
net = "mainnet"
- case RopstenGenesisHash:
- net = "ropsten"
case RinkebyGenesisHash:
net = "rinkeby"
case GoerliGenesisHash:
diff --git a/params/config.go b/params/config.go
index 80e671f9bf..816577a547 100644
--- a/params/config.go
+++ b/params/config.go
@@ -28,18 +28,15 @@ import (
// Genesis hashes to enforce below configs on.
var (
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
- RopstenGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9")
RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a")
- KilnGenesisHash = common.HexToHash("0x51c7fe41be669f69c45c33a56982cbde405313342d9e2b00d7c91a7b284dd4f8")
)
// TrustedCheckpoints associates each known checkpoint with the genesis hash of
// the chain it belongs to.
var TrustedCheckpoints = map[common.Hash]*TrustedCheckpoint{
MainnetGenesisHash: MainnetTrustedCheckpoint,
- RopstenGenesisHash: RopstenTrustedCheckpoint,
SepoliaGenesisHash: SepoliaTrustedCheckpoint,
RinkebyGenesisHash: RinkebyTrustedCheckpoint,
GoerliGenesisHash: GoerliTrustedCheckpoint,
@@ -49,11 +46,12 @@ var TrustedCheckpoints = map[common.Hash]*TrustedCheckpoint{
// the chain it belongs to.
var CheckpointOracles = map[common.Hash]*CheckpointOracleConfig{
MainnetGenesisHash: MainnetCheckpointOracle,
- RopstenGenesisHash: RopstenCheckpointOracle,
RinkebyGenesisHash: RinkebyCheckpointOracle,
GoerliGenesisHash: GoerliCheckpointOracle,
}
+func newUint64(val uint64) *uint64 { return &val }
+
var (
MainnetTerminalTotalDifficulty, _ = new(big.Int).SetString("58_750_000_000_000_000_000_000", 0)
@@ -83,10 +81,10 @@ var (
// MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network.
MainnetTrustedCheckpoint = &TrustedCheckpoint{
- SectionIndex: 451,
- SectionHead: common.HexToHash("0xe47f84b9967eb2ad2afff74d59901b63134660011822fdababaf8fdd18a75aa6"),
- CHTRoot: common.HexToHash("0xc31e0462ca3d39a46111bb6b63ac4e1cac84089472b7474a319d582f72b3f0c0"),
- BloomRoot: common.HexToHash("0x7c9f25ce3577a3ab330d52a7343f801899cf9d4980c69f81de31ccc1a055c809"),
+ SectionIndex: 506,
+ SectionHead: common.HexToHash("0x3d1a139a6fc7764211236ef7c64d9e8c1fe55b358d7414e25277bac1144486cd"),
+ CHTRoot: common.HexToHash("0xef7fc3321a239a54238593bdf68d82933d903cb533b0d03228a8d958cd35ea77"),
+ BloomRoot: common.HexToHash("0x51d7bfe7c6397b1caa8b1cb046de4aeaf7e7fbd3fb6c726b60bf750de78809e8"),
}
// MainnetCheckpointOracle contains a set of configs for the main network oracle.
@@ -102,49 +100,6 @@ var (
Threshold: 2,
}
- // RopstenChainConfig contains the chain parameters to run a node on the Ropsten test network.
- RopstenChainConfig = &ChainConfig{
- ChainID: big.NewInt(3),
- HomesteadBlock: big.NewInt(0),
- DAOForkBlock: nil,
- DAOForkSupport: true,
- EIP150Block: big.NewInt(0),
- EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"),
- EIP155Block: big.NewInt(10),
- EIP158Block: big.NewInt(10),
- ByzantiumBlock: big.NewInt(1_700_000),
- ConstantinopleBlock: big.NewInt(4_230_000),
- PetersburgBlock: big.NewInt(4_939_394),
- IstanbulBlock: big.NewInt(6_485_846),
- MuirGlacierBlock: big.NewInt(7_117_117),
- BerlinBlock: big.NewInt(9_812_189),
- LondonBlock: big.NewInt(10_499_401),
- TerminalTotalDifficulty: new(big.Int).SetUint64(50_000_000_000_000_000),
- TerminalTotalDifficultyPassed: true,
- Ethash: new(EthashConfig),
- }
-
- // RopstenTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network.
- RopstenTrustedCheckpoint = &TrustedCheckpoint{
- SectionIndex: 346,
- SectionHead: common.HexToHash("0xafa0384ebd13a751fb7475aaa7fc08ac308925c8b2e2195bca2d4ab1878a7a84"),
- CHTRoot: common.HexToHash("0x522ae1f334bfa36033b2315d0b9954052780700b69448ecea8d5877e0f7ee477"),
- BloomRoot: common.HexToHash("0x4093fd53b0d2cc50181dca353fe66f03ae113e7cb65f869a4dfb5905de6a0493"),
- }
-
- // RopstenCheckpointOracle contains a set of configs for the Ropsten test network oracle.
- RopstenCheckpointOracle = &CheckpointOracleConfig{
- Address: common.HexToAddress("0xEF79475013f154E6A65b54cB2742867791bf0B84"),
- Signers: []common.Address{
- common.HexToAddress("0x32162F3581E88a5f62e8A61892B42C46E2c18f7b"), // Peter
- common.HexToAddress("0x78d1aD571A1A09D60D9BBf25894b44e4C8859595"), // Martin
- common.HexToAddress("0x286834935f4A8Cfb4FF4C77D5770C2775aE2b0E7"), // Zsolt
- common.HexToAddress("0xb86e2B0Ab5A4B1373e40c51A7C712c70Ba2f9f8E"), // Gary
- common.HexToAddress("0x0DF8fa387C602AE62559cC4aFa4972A7045d6707"), // Guillaume
- },
- Threshold: 2,
- }
-
// SepoliaChainConfig contains the chain parameters to run a node on the Sepolia test network.
SepoliaChainConfig = &ChainConfig{
ChainID: big.NewInt(11155111),
@@ -164,15 +119,16 @@ var (
TerminalTotalDifficulty: big.NewInt(17_000_000_000_000_000),
TerminalTotalDifficultyPassed: true,
MergeNetsplitBlock: big.NewInt(1735371),
+ ShanghaiTime: newUint64(1677557088),
Ethash: new(EthashConfig),
}
// SepoliaTrustedCheckpoint contains the light client trusted checkpoint for the Sepolia test network.
SepoliaTrustedCheckpoint = &TrustedCheckpoint{
- SectionIndex: 34,
- SectionHead: common.HexToHash("0xe361400fcbc468d641e7bdd0b0946a3548e97c5d2703b124f04a3f1deccec244"),
- CHTRoot: common.HexToHash("0xea6768fd288dce7d84f590884908ec39e4de78e6e1a38de5c5419b0f49a42f91"),
- BloomRoot: common.HexToHash("0x06d32f35d5a611bfd0333ad44e39c619449824167d8ef2913edc48a8112be2cd"),
+ SectionIndex: 55,
+ SectionHead: common.HexToHash("0xb70ea113ab4db9d6e015c5b55d486713f60c40bda666121914a71ce3aec53a75"),
+ CHTRoot: common.HexToHash("0x206456d8847b66aaf427ed551f55e24cff90241bdb0a02583c761bf8164f78e4"),
+ BloomRoot: common.HexToHash("0x4369228d59a8fe285fee874c636531091e659b3b1294bb978eb159860a1cede2"),
}
// RinkebyChainConfig contains the chain parameters to run a node on the Rinkeby test network.
@@ -201,10 +157,10 @@ var (
// RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network.
RinkebyTrustedCheckpoint = &TrustedCheckpoint{
- SectionIndex: 326,
- SectionHead: common.HexToHash("0x941a41a153b0e36cb15d9d193d1d0f9715bdb2435efd1c95119b64168667ce00"),
- CHTRoot: common.HexToHash("0xe2331e00d579cf4093091dee35bef772e63c2341380c276041dc22563c8aba2e"),
- BloomRoot: common.HexToHash("0x595206febcf118958c2bc1218ea71d01fd04b8f97ad71813df4be0af5b36b0e5"),
+ SectionIndex: 373,
+ SectionHead: common.HexToHash("0x09f6d8f0d08d61025ccf4578dc214220b78013841470d445ed86faab4a5a885a"),
+ CHTRoot: common.HexToHash("0xef72902b944a111e9fdfee5fb69a5e46f68bf11a1f0bd430321f92d6b66987df"),
+ BloomRoot: common.HexToHash("0xd0120268729c51dd6fa2714f7f88527adfecbdb08592c671233ad2e0ad7cd835"),
}
// RinkebyCheckpointOracle contains a set of configs for the Rinkeby test network oracle.
@@ -246,10 +202,10 @@ var (
// GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network.
GoerliTrustedCheckpoint = &TrustedCheckpoint{
- SectionIndex: 210,
- SectionHead: common.HexToHash("0xbb11eaf551a6c06f74a6c7bbfe1699cbf64b8f248b64691da916dd443176db2f"),
- CHTRoot: common.HexToHash("0x9934ae326d00d9c7de2e074c0e51689efb7fa7fcba18929ff4279c27259c45e6"),
- BloomRoot: common.HexToHash("0x7fe3bd4fd45194aa8a5cfe5ac590edff1f870d3d98d3c310494e7f67613a87ff"),
+ SectionIndex: 229,
+ SectionHead: common.HexToHash("0xc5a7b57cb4af7b3d4cc251ac5f29acaac94e7464365358e7ad26129083b7729a"),
+ CHTRoot: common.HexToHash("0x54c0d5c756d9c48eda26ea13c2a49c2e31f1cb7dfb01514ddc49f3d24272c77e"),
+ BloomRoot: common.HexToHash("0xd681970a496f6187d089f8c8665a3587b5a78212d79b6ceef97c0dabd0188e56"),
}
// GoerliCheckpointOracle contains a set of configs for the Goerli test network oracle.
@@ -267,26 +223,129 @@ var (
// AllEthashProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Ethash consensus.
- //
- // This configuration is intentionally not using keyed fields to force anyone
- // adding flags to the config to also have to set these fields.
- AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, false, new(EthashConfig), nil}
+ AllEthashProtocolChanges = &ChainConfig{
+ ChainID: big.NewInt(1337),
+ HomesteadBlock: big.NewInt(0),
+ DAOForkBlock: nil,
+ DAOForkSupport: false,
+ EIP150Block: big.NewInt(0),
+ EIP150Hash: common.Hash{},
+ 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: nil,
+ CancunTime: nil,
+ PragueTime: nil,
+ TerminalTotalDifficulty: nil,
+ TerminalTotalDifficultyPassed: false,
+ Ethash: new(EthashConfig),
+ Clique: nil,
+ }
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Clique consensus.
- //
- // This configuration is intentionally not using keyed fields to force anyone
- // adding flags to the config to also have to set these fields.
- AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, false, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
+ AllCliqueProtocolChanges = &ChainConfig{
+ ChainID: big.NewInt(1337),
+ HomesteadBlock: big.NewInt(0),
+ DAOForkBlock: nil,
+ DAOForkSupport: false,
+ EIP150Block: big.NewInt(0),
+ EIP150Hash: common.Hash{},
+ 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: nil,
+ GrayGlacierBlock: nil,
+ MergeNetsplitBlock: nil,
+ ShanghaiTime: nil,
+ CancunTime: nil,
+ PragueTime: nil,
+ TerminalTotalDifficulty: nil,
+ TerminalTotalDifficultyPassed: false,
+ Ethash: nil,
+ Clique: &CliqueConfig{Period: 0, Epoch: 30000},
+ }
+
+ // TestChainConfig contains every protocol change (EIPs) introduced
+ // and accepted by the Ethereum core developers for testing proposes.
+ TestChainConfig = &ChainConfig{
+ ChainID: big.NewInt(1),
+ HomesteadBlock: big.NewInt(0),
+ DAOForkBlock: nil,
+ DAOForkSupport: false,
+ EIP150Block: big.NewInt(0),
+ EIP150Hash: common.Hash{},
+ 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: nil,
+ CancunTime: nil,
+ PragueTime: nil,
+ TerminalTotalDifficulty: nil,
+ TerminalTotalDifficultyPassed: false,
+ Ethash: new(EthashConfig),
+ Clique: nil,
+ }
- TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, false, new(EthashConfig), nil}
- TestRules = TestChainConfig.Rules(new(big.Int), false)
+ // NonActivatedConfig defines the chain configuration without activating
+ // any protocol change (EIPs).
+ NonActivatedConfig = &ChainConfig{
+ ChainID: big.NewInt(1),
+ HomesteadBlock: nil,
+ DAOForkBlock: nil,
+ DAOForkSupport: false,
+ EIP150Block: nil,
+ EIP150Hash: common.Hash{},
+ EIP155Block: nil,
+ EIP158Block: nil,
+ ByzantiumBlock: nil,
+ ConstantinopleBlock: nil,
+ PetersburgBlock: nil,
+ IstanbulBlock: nil,
+ MuirGlacierBlock: nil,
+ BerlinBlock: nil,
+ LondonBlock: nil,
+ ArrowGlacierBlock: nil,
+ GrayGlacierBlock: nil,
+ MergeNetsplitBlock: nil,
+ ShanghaiTime: nil,
+ CancunTime: nil,
+ PragueTime: nil,
+ TerminalTotalDifficulty: nil,
+ TerminalTotalDifficultyPassed: false,
+ Ethash: new(EthashConfig),
+ Clique: nil,
+ }
+ TestRules = TestChainConfig.Rules(new(big.Int), false, 0)
)
// NetworkNames are user friendly names to use in the chain spec banner.
var NetworkNames = map[string]string{
MainnetChainConfig.ChainID.String(): "mainnet",
- RopstenChainConfig.ChainID.String(): "ropsten",
RinkebyChainConfig.ChainID.String(): "rinkeby",
GoerliChainConfig.ChainID.String(): "goerli",
SepoliaChainConfig.ChainID.String(): "sepolia",
@@ -370,8 +429,12 @@ type ChainConfig struct {
ArrowGlacierBlock *big.Int `json:"arrowGlacierBlock,omitempty"` // Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated)
GrayGlacierBlock *big.Int `json:"grayGlacierBlock,omitempty"` // Eip-5133 (bomb delay) switch block (nil = no fork, 0 = already activated)
MergeNetsplitBlock *big.Int `json:"mergeNetsplitBlock,omitempty"` // Virtual fork after The Merge to use as a network splitter
- ShanghaiBlock *big.Int `json:"shanghaiBlock,omitempty"` // Shanghai switch block (nil = no fork, 0 = already on shanghai)
- CancunBlock *big.Int `json:"cancunBlock,omitempty"` // Cancun switch block (nil = no fork, 0 = already on cancun)
+
+ // Fork scheduling was switched from blocks to timestamps here
+
+ ShanghaiTime *uint64 `json:"shanghaiTime,omitempty"` // Shanghai switch time (nil = no fork, 0 = already on shanghai)
+ CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already on cancun)
+ PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague)
// TerminalTotalDifficulty is the amount of total difficulty reached by
// the network that triggers the consensus upgrade.
@@ -406,8 +469,8 @@ func (c *CliqueConfig) String() string {
return "clique"
}
-// String implements the fmt.Stringer interface.
-func (c *ChainConfig) String() string {
+// Description returns a human-readable description of ChainConfig.
+func (c *ChainConfig) Description() string {
var banner string
// Create some basinc network config output
@@ -441,121 +504,130 @@ func (c *ChainConfig) String() string {
// Create a list of forks with a short description of them. Forks that only
// makes sense for mainnet should be optional at printing to avoid bloating
// the output for testnets and private networks.
- banner += "Pre-Merge hard forks:\n"
- banner += fmt.Sprintf(" - Homestead: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/homestead.md)\n", c.HomesteadBlock)
+ banner += "Pre-Merge hard forks (block based):\n"
+ banner += fmt.Sprintf(" - Homestead: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/homestead.md)\n", c.HomesteadBlock)
if c.DAOForkBlock != nil {
- banner += fmt.Sprintf(" - DAO Fork: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/dao-fork.md)\n", c.DAOForkBlock)
- }
- banner += fmt.Sprintf(" - Tangerine Whistle (EIP 150): %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/tangerine-whistle.md)\n", c.EIP150Block)
- banner += fmt.Sprintf(" - Spurious Dragon/1 (EIP 155): %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md)\n", c.EIP155Block)
- banner += fmt.Sprintf(" - Spurious Dragon/2 (EIP 158): %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md)\n", c.EIP155Block)
- banner += fmt.Sprintf(" - Byzantium: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/byzantium.md)\n", c.ByzantiumBlock)
- banner += fmt.Sprintf(" - Constantinople: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/constantinople.md)\n", c.ConstantinopleBlock)
- banner += fmt.Sprintf(" - Petersburg: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/petersburg.md)\n", c.PetersburgBlock)
- banner += fmt.Sprintf(" - Istanbul: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/istanbul.md)\n", c.IstanbulBlock)
+ banner += fmt.Sprintf(" - DAO Fork: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/dao-fork.md)\n", c.DAOForkBlock)
+ }
+ banner += fmt.Sprintf(" - Tangerine Whistle (EIP 150): #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/tangerine-whistle.md)\n", c.EIP150Block)
+ banner += fmt.Sprintf(" - Spurious Dragon/1 (EIP 155): #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md)\n", c.EIP155Block)
+ banner += fmt.Sprintf(" - Spurious Dragon/2 (EIP 158): #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md)\n", c.EIP155Block)
+ banner += fmt.Sprintf(" - Byzantium: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/byzantium.md)\n", c.ByzantiumBlock)
+ banner += fmt.Sprintf(" - Constantinople: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/constantinople.md)\n", c.ConstantinopleBlock)
+ banner += fmt.Sprintf(" - Petersburg: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/petersburg.md)\n", c.PetersburgBlock)
+ banner += fmt.Sprintf(" - Istanbul: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/istanbul.md)\n", c.IstanbulBlock)
if c.MuirGlacierBlock != nil {
- banner += fmt.Sprintf(" - Muir Glacier: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/muir-glacier.md)\n", c.MuirGlacierBlock)
+ banner += fmt.Sprintf(" - Muir Glacier: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/muir-glacier.md)\n", c.MuirGlacierBlock)
}
- banner += fmt.Sprintf(" - Berlin: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md)\n", c.BerlinBlock)
- banner += fmt.Sprintf(" - London: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md)\n", c.LondonBlock)
+ banner += fmt.Sprintf(" - Berlin: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md)\n", c.BerlinBlock)
+ banner += fmt.Sprintf(" - London: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md)\n", c.LondonBlock)
if c.ArrowGlacierBlock != nil {
- banner += fmt.Sprintf(" - Arrow Glacier: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/arrow-glacier.md)\n", c.ArrowGlacierBlock)
+ banner += fmt.Sprintf(" - Arrow Glacier: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/arrow-glacier.md)\n", c.ArrowGlacierBlock)
}
if c.GrayGlacierBlock != nil {
- banner += fmt.Sprintf(" - Gray Glacier: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/gray-glacier.md)\n", c.GrayGlacierBlock)
- }
- if c.ShanghaiBlock != nil {
- banner += fmt.Sprintf(" - Shanghai: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md)\n", c.ShanghaiBlock)
- }
- if c.CancunBlock != nil {
- banner += fmt.Sprintf(" - Cancun: %-8v\n", c.CancunBlock)
+ banner += fmt.Sprintf(" - Gray Glacier: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/gray-glacier.md)\n", c.GrayGlacierBlock)
}
banner += "\n"
// Add a special section for the merge as it's non-obvious
if c.TerminalTotalDifficulty == nil {
banner += "The Merge is not yet available for this network!\n"
- banner += " - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md"
+ banner += " - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md\n"
} else {
banner += "Merge configured:\n"
banner += " - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md\n"
banner += fmt.Sprintf(" - Network known to be merged: %v\n", c.TerminalTotalDifficultyPassed)
banner += fmt.Sprintf(" - Total terminal difficulty: %v\n", c.TerminalTotalDifficulty)
- banner += fmt.Sprintf(" - Merge netsplit block: %-8v", c.MergeNetsplitBlock)
+ if c.MergeNetsplitBlock != nil {
+ banner += fmt.Sprintf(" - Merge netsplit block: #%-8v\n", c.MergeNetsplitBlock)
+ }
+ }
+ banner += "\n"
+
+ // Create a list of forks post-merge
+ banner += "Post-Merge hard forks (timestamp based):\n"
+ if c.ShanghaiTime != nil {
+ banner += fmt.Sprintf(" - Shanghai: @%-10v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md)\n", *c.ShanghaiTime)
+ }
+ if c.CancunTime != nil {
+ banner += fmt.Sprintf(" - Cancun: @%-10v\n", *c.CancunTime)
+ }
+ if c.PragueTime != nil {
+ banner += fmt.Sprintf(" - Prague: @%-10v\n", *c.PragueTime)
}
return banner
}
// IsHomestead returns whether num is either equal to the homestead block or greater.
func (c *ChainConfig) IsHomestead(num *big.Int) bool {
- return isForked(c.HomesteadBlock, num)
+ return isBlockForked(c.HomesteadBlock, num)
}
// IsDAOFork returns whether num is either equal to the DAO fork block or greater.
func (c *ChainConfig) IsDAOFork(num *big.Int) bool {
- return isForked(c.DAOForkBlock, num)
+ return isBlockForked(c.DAOForkBlock, num)
}
// IsEIP150 returns whether num is either equal to the EIP150 fork block or greater.
func (c *ChainConfig) IsEIP150(num *big.Int) bool {
- return isForked(c.EIP150Block, num)
+ return isBlockForked(c.EIP150Block, num)
}
// IsEIP155 returns whether num is either equal to the EIP155 fork block or greater.
func (c *ChainConfig) IsEIP155(num *big.Int) bool {
- return isForked(c.EIP155Block, num)
+ return isBlockForked(c.EIP155Block, num)
}
// IsEIP158 returns whether num is either equal to the EIP158 fork block or greater.
func (c *ChainConfig) IsEIP158(num *big.Int) bool {
- return isForked(c.EIP158Block, num)
+ return isBlockForked(c.EIP158Block, num)
}
// IsByzantium returns whether num is either equal to the Byzantium fork block or greater.
func (c *ChainConfig) IsByzantium(num *big.Int) bool {
- return isForked(c.ByzantiumBlock, num)
+ return isBlockForked(c.ByzantiumBlock, num)
}
// IsConstantinople returns whether num is either equal to the Constantinople fork block or greater.
func (c *ChainConfig) IsConstantinople(num *big.Int) bool {
- return isForked(c.ConstantinopleBlock, num)
+ return isBlockForked(c.ConstantinopleBlock, num)
}
// IsMuirGlacier returns whether num is either equal to the Muir Glacier (EIP-2384) fork block or greater.
func (c *ChainConfig) IsMuirGlacier(num *big.Int) bool {
- return isForked(c.MuirGlacierBlock, num)
+ return isBlockForked(c.MuirGlacierBlock, num)
}
// IsPetersburg returns whether num is either
// - equal to or greater than the PetersburgBlock fork block,
// - OR is nil, and Constantinople is active
func (c *ChainConfig) IsPetersburg(num *big.Int) bool {
- return isForked(c.PetersburgBlock, num) || c.PetersburgBlock == nil && isForked(c.ConstantinopleBlock, num)
+ return isBlockForked(c.PetersburgBlock, num) || c.PetersburgBlock == nil && isBlockForked(c.ConstantinopleBlock, num)
}
// IsIstanbul returns whether num is either equal to the Istanbul fork block or greater.
func (c *ChainConfig) IsIstanbul(num *big.Int) bool {
- return isForked(c.IstanbulBlock, num)
+ return isBlockForked(c.IstanbulBlock, num)
}
// IsBerlin returns whether num is either equal to the Berlin fork block or greater.
func (c *ChainConfig) IsBerlin(num *big.Int) bool {
- return isForked(c.BerlinBlock, num)
+ return isBlockForked(c.BerlinBlock, num)
}
// IsLondon returns whether num is either equal to the London fork block or greater.
func (c *ChainConfig) IsLondon(num *big.Int) bool {
- return isForked(c.LondonBlock, num)
+ return isBlockForked(c.LondonBlock, num)
}
// IsArrowGlacier returns whether num is either equal to the Arrow Glacier (EIP-4345) fork block or greater.
func (c *ChainConfig) IsArrowGlacier(num *big.Int) bool {
- return isForked(c.ArrowGlacierBlock, num)
+ return isBlockForked(c.ArrowGlacierBlock, num)
}
// IsGrayGlacier returns whether num is either equal to the Gray Glacier (EIP-5133) fork block or greater.
func (c *ChainConfig) IsGrayGlacier(num *big.Int) bool {
- return isForked(c.GrayGlacierBlock, num)
+ return isBlockForked(c.GrayGlacierBlock, num)
}
// IsTerminalPoWBlock returns whether the given block is the last block of PoW stage.
@@ -566,30 +638,42 @@ func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *bi
return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0
}
-// IsShanghai returns whether num is either equal to the Shanghai fork block or greater.
-func (c *ChainConfig) IsShanghai(num *big.Int) bool {
- return isForked(c.ShanghaiBlock, num)
+// IsShanghai returns whether time is either equal to the Shanghai fork time or greater.
+func (c *ChainConfig) IsShanghai(time uint64) bool {
+ return isTimestampForked(c.ShanghaiTime, time)
+}
+
+// IsCancun returns whether num is either equal to the Cancun fork time or greater.
+func (c *ChainConfig) IsCancun(time uint64) bool {
+ return isTimestampForked(c.CancunTime, time)
}
-// IsCancun returns whether num is either equal to the Cancun fork block or greater.
-func (c *ChainConfig) IsCancun(num *big.Int) bool {
- return isForked(c.CancunBlock, num)
+// IsPrague returns whether num is either equal to the Prague fork time or greater.
+func (c *ChainConfig) IsPrague(time uint64) bool {
+ return isTimestampForked(c.PragueTime, time)
}
// CheckCompatible checks whether scheduled fork transitions have been imported
// with a mismatching chain configuration.
-func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError {
- bhead := new(big.Int).SetUint64(height)
-
+func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError {
+ var (
+ bhead = new(big.Int).SetUint64(height)
+ btime = time
+ )
// Iterate checkCompatible to find the lowest conflict.
var lasterr *ConfigCompatError
for {
- err := c.checkCompatible(newcfg, bhead)
- if err == nil || (lasterr != nil && err.RewindTo == lasterr.RewindTo) {
+ err := c.checkCompatible(newcfg, bhead, btime)
+ if err == nil || (lasterr != nil && err.RewindToBlock == lasterr.RewindToBlock && err.RewindToTime == lasterr.RewindToTime) {
break
}
lasterr = err
- bhead.SetUint64(err.RewindTo)
+
+ if err.RewindToTime > 0 {
+ btime = err.RewindToTime
+ } else {
+ bhead.SetUint64(err.RewindToBlock)
+ }
}
return lasterr
}
@@ -598,9 +682,10 @@ func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *Confi
// to guarantee that forks can be implemented in a different order than on official networks
func (c *ChainConfig) CheckConfigForkOrder() error {
type fork struct {
- name string
- block *big.Int
- optional bool // if true, the fork may be nil and next fork is still allowed
+ name string
+ block *big.Int // forks up to - and including the merge - were defined with block numbers
+ timestamp *uint64 // forks after the merge are scheduled using timestamps
+ optional bool // if true, the fork may be nil and next fork is still allowed
}
var lastFork fork
for _, cur := range []fork{
@@ -619,110 +704,142 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
{name: "arrowGlacierBlock", block: c.ArrowGlacierBlock, optional: true},
{name: "grayGlacierBlock", block: c.GrayGlacierBlock, optional: true},
{name: "mergeNetsplitBlock", block: c.MergeNetsplitBlock, optional: true},
- {name: "shanghaiBlock", block: c.ShanghaiBlock, optional: true},
- {name: "cancunBlock", block: c.CancunBlock, optional: true},
+ {name: "shanghaiTime", timestamp: c.ShanghaiTime},
+ {name: "cancunTime", timestamp: c.CancunTime, optional: true},
+ {name: "pragueTime", timestamp: c.PragueTime, optional: true},
} {
if lastFork.name != "" {
- // Next one must be higher number
- if lastFork.block == nil && cur.block != nil {
- return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at %v",
- lastFork.name, cur.name, cur.block)
- }
- if lastFork.block != nil && cur.block != nil {
- if lastFork.block.Cmp(cur.block) > 0 {
- return fmt.Errorf("unsupported fork ordering: %v enabled at %v, but %v enabled at %v",
+ switch {
+ // Non-optional forks must all be present in the chain config up to the last defined fork
+ case lastFork.block == nil && lastFork.timestamp == nil && (cur.block != nil || cur.timestamp != nil):
+ if cur.block != nil {
+ return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at block %v",
+ lastFork.name, cur.name, cur.block)
+ } else {
+ return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at timestamp %v",
+ lastFork.name, cur.name, cur.timestamp)
+ }
+
+ // Fork (whether defined by block or timestamp) must follow the fork definition sequence
+ case (lastFork.block != nil && cur.block != nil) || (lastFork.timestamp != nil && cur.timestamp != nil):
+ if lastFork.block != nil && lastFork.block.Cmp(cur.block) > 0 {
+ return fmt.Errorf("unsupported fork ordering: %v enabled at block %v, but %v enabled at block %v",
lastFork.name, lastFork.block, cur.name, cur.block)
+ } else if lastFork.timestamp != nil && *lastFork.timestamp > *cur.timestamp {
+ return fmt.Errorf("unsupported fork ordering: %v enabled at timestamp %v, but %v enabled at timestamp %v",
+ lastFork.name, lastFork.timestamp, cur.name, cur.timestamp)
+ }
+
+ // Timestamp based forks can follow block based ones, but not the other way around
+ if lastFork.timestamp != nil && cur.block != nil {
+ return fmt.Errorf("unsupported fork ordering: %v used timestamp ordering, but %v reverted to block ordering",
+ lastFork.name, cur.name)
}
}
}
// If it was optional and not set, then ignore it
- if !cur.optional || cur.block != nil {
+ if !cur.optional || (cur.block != nil || cur.timestamp != nil) {
lastFork = cur
}
}
return nil
}
-func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError {
- if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, head) {
- return newCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock)
+func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int, headTimestamp uint64) *ConfigCompatError {
+ if isForkBlockIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, headNumber) {
+ return newBlockCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock)
}
- if isForkIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, head) {
- return newCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock)
+ if isForkBlockIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, headNumber) {
+ return newBlockCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock)
}
- if c.IsDAOFork(head) && c.DAOForkSupport != newcfg.DAOForkSupport {
- return newCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock)
+ if c.IsDAOFork(headNumber) && c.DAOForkSupport != newcfg.DAOForkSupport {
+ return newBlockCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock)
}
- if isForkIncompatible(c.EIP150Block, newcfg.EIP150Block, head) {
- return newCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block)
+ if isForkBlockIncompatible(c.EIP150Block, newcfg.EIP150Block, headNumber) {
+ return newBlockCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block)
}
- if isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, head) {
- return newCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block)
+ if isForkBlockIncompatible(c.EIP155Block, newcfg.EIP155Block, headNumber) {
+ return newBlockCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block)
}
- if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, head) {
- return newCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block)
+ if isForkBlockIncompatible(c.EIP158Block, newcfg.EIP158Block, headNumber) {
+ return newBlockCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block)
}
- if c.IsEIP158(head) && !configNumEqual(c.ChainID, newcfg.ChainID) {
- return newCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block)
+ if c.IsEIP158(headNumber) && !configBlockEqual(c.ChainID, newcfg.ChainID) {
+ return newBlockCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block)
}
- if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, head) {
- return newCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock)
+ if isForkBlockIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, headNumber) {
+ return newBlockCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock)
}
- if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) {
- return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock)
+ if isForkBlockIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, headNumber) {
+ return newBlockCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock)
}
- if isForkIncompatible(c.PetersburgBlock, newcfg.PetersburgBlock, head) {
+ if isForkBlockIncompatible(c.PetersburgBlock, newcfg.PetersburgBlock, headNumber) {
// the only case where we allow Petersburg to be set in the past is if it is equal to Constantinople
// mainly to satisfy fork ordering requirements which state that Petersburg fork be set if Constantinople fork is set
- if isForkIncompatible(c.ConstantinopleBlock, newcfg.PetersburgBlock, head) {
- return newCompatError("Petersburg fork block", c.PetersburgBlock, newcfg.PetersburgBlock)
+ if isForkBlockIncompatible(c.ConstantinopleBlock, newcfg.PetersburgBlock, headNumber) {
+ return newBlockCompatError("Petersburg fork block", c.PetersburgBlock, newcfg.PetersburgBlock)
}
}
- if isForkIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, head) {
- return newCompatError("Istanbul fork block", c.IstanbulBlock, newcfg.IstanbulBlock)
+ if isForkBlockIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, headNumber) {
+ return newBlockCompatError("Istanbul fork block", c.IstanbulBlock, newcfg.IstanbulBlock)
}
- if isForkIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, head) {
- return newCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock)
+ if isForkBlockIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, headNumber) {
+ return newBlockCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock)
}
- if isForkIncompatible(c.BerlinBlock, newcfg.BerlinBlock, head) {
- return newCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock)
+ if isForkBlockIncompatible(c.BerlinBlock, newcfg.BerlinBlock, headNumber) {
+ return newBlockCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock)
}
- if isForkIncompatible(c.LondonBlock, newcfg.LondonBlock, head) {
- return newCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock)
+ if isForkBlockIncompatible(c.LondonBlock, newcfg.LondonBlock, headNumber) {
+ return newBlockCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock)
}
- if isForkIncompatible(c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock, head) {
- return newCompatError("Arrow Glacier fork block", c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock)
+ if isForkBlockIncompatible(c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock, headNumber) {
+ return newBlockCompatError("Arrow Glacier fork block", c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock)
}
- if isForkIncompatible(c.GrayGlacierBlock, newcfg.GrayGlacierBlock, head) {
- return newCompatError("Gray Glacier fork block", c.GrayGlacierBlock, newcfg.GrayGlacierBlock)
+ if isForkBlockIncompatible(c.GrayGlacierBlock, newcfg.GrayGlacierBlock, headNumber) {
+ return newBlockCompatError("Gray Glacier fork block", c.GrayGlacierBlock, newcfg.GrayGlacierBlock)
}
- if isForkIncompatible(c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock, head) {
- return newCompatError("Merge netsplit fork block", c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock)
+ if isForkBlockIncompatible(c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock, headNumber) {
+ return newBlockCompatError("Merge netsplit fork block", c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock)
}
- if isForkIncompatible(c.ShanghaiBlock, newcfg.ShanghaiBlock, head) {
- return newCompatError("Shanghai fork block", c.ShanghaiBlock, newcfg.ShanghaiBlock)
+ if isForkTimestampIncompatible(c.ShanghaiTime, newcfg.ShanghaiTime, headTimestamp) {
+ return newTimestampCompatError("Shanghai fork timestamp", c.ShanghaiTime, newcfg.ShanghaiTime)
}
- if isForkIncompatible(c.CancunBlock, newcfg.CancunBlock, head) {
- return newCompatError("Cancun fork block", c.CancunBlock, newcfg.CancunBlock)
+ if isForkTimestampIncompatible(c.CancunTime, newcfg.CancunTime, headTimestamp) {
+ return newTimestampCompatError("Cancun fork timestamp", c.CancunTime, newcfg.CancunTime)
+ }
+ if isForkTimestampIncompatible(c.PragueTime, newcfg.PragueTime, headTimestamp) {
+ return newTimestampCompatError("Prague fork timestamp", c.PragueTime, newcfg.PragueTime)
}
return nil
}
-// isForkIncompatible returns true if a fork scheduled at s1 cannot be rescheduled to
-// block s2 because head is already past the fork.
-func isForkIncompatible(s1, s2, head *big.Int) bool {
- return (isForked(s1, head) || isForked(s2, head)) && !configNumEqual(s1, s2)
+// BaseFeeChangeDenominator bounds the amount the base fee can change between blocks.
+func (c *ChainConfig) BaseFeeChangeDenominator() uint64 {
+ return DefaultBaseFeeChangeDenominator
+}
+
+// ElasticityMultiplier bounds the maximum gas limit an EIP-1559 block may have.
+func (c *ChainConfig) ElasticityMultiplier() uint64 {
+ return DefaultElasticityMultiplier
}
-// isForked returns whether a fork scheduled at block s is active at the given head block.
-func isForked(s, head *big.Int) bool {
+// isForkBlockIncompatible returns true if a fork scheduled at block s1 cannot be
+// rescheduled to block s2 because head is already past the fork.
+func isForkBlockIncompatible(s1, s2, head *big.Int) bool {
+ return (isBlockForked(s1, head) || isBlockForked(s2, head)) && !configBlockEqual(s1, s2)
+}
+
+// isBlockForked returns whether a fork scheduled at block s is active at the
+// given head block. Whilst this method is the same as isTimestampForked, they
+// are explicitly separate for clearer reading.
+func isBlockForked(s, head *big.Int) bool {
if s == nil || head == nil {
return false
}
return s.Cmp(head) <= 0
}
-func configNumEqual(x, y *big.Int) bool {
+func configBlockEqual(x, y *big.Int) bool {
if x == nil {
return y == nil
}
@@ -732,17 +849,51 @@ func configNumEqual(x, y *big.Int) bool {
return x.Cmp(y) == 0
}
+// isForkTimestampIncompatible returns true if a fork scheduled at timestamp s1
+// cannot be rescheduled to timestamp s2 because head is already past the fork.
+func isForkTimestampIncompatible(s1, s2 *uint64, head uint64) bool {
+ return (isTimestampForked(s1, head) || isTimestampForked(s2, head)) && !configTimestampEqual(s1, s2)
+}
+
+// isTimestampForked returns whether a fork scheduled at timestamp s is active
+// at the given head timestamp. Whilst this method is the same as isBlockForked,
+// they are explicitly separate for clearer reading.
+func isTimestampForked(s *uint64, head uint64) bool {
+ if s == nil {
+ return false
+ }
+ return *s <= head
+}
+
+func configTimestampEqual(x, y *uint64) bool {
+ if x == nil {
+ return y == nil
+ }
+ if y == nil {
+ return x == nil
+ }
+ return *x == *y
+}
+
// ConfigCompatError is raised if the locally-stored blockchain is initialised with a
// ChainConfig that would alter the past.
type ConfigCompatError struct {
What string
- // block numbers of the stored and new configurations
- StoredConfig, NewConfig *big.Int
+
+ // block numbers of the stored and new configurations if block based forking
+ StoredBlock, NewBlock *big.Int
+
+ // timestamps of the stored and new configurations if time based forking
+ StoredTime, NewTime *uint64
+
// the block number to which the local chain must be rewound to correct the error
- RewindTo uint64
+ RewindToBlock uint64
+
+ // the timestamp to which the local chain must be rewound to correct the error
+ RewindToTime uint64
}
-func newCompatError(what string, storedblock, newblock *big.Int) *ConfigCompatError {
+func newBlockCompatError(what string, storedblock, newblock *big.Int) *ConfigCompatError {
var rew *big.Int
switch {
case storedblock == nil:
@@ -752,15 +903,45 @@ func newCompatError(what string, storedblock, newblock *big.Int) *ConfigCompatEr
default:
rew = newblock
}
- err := &ConfigCompatError{what, storedblock, newblock, 0}
+ err := &ConfigCompatError{
+ What: what,
+ StoredBlock: storedblock,
+ NewBlock: newblock,
+ RewindToBlock: 0,
+ }
if rew != nil && rew.Sign() > 0 {
- err.RewindTo = rew.Uint64() - 1
+ err.RewindToBlock = rew.Uint64() - 1
+ }
+ return err
+}
+
+func newTimestampCompatError(what string, storedtime, newtime *uint64) *ConfigCompatError {
+ var rew *uint64
+ switch {
+ case storedtime == nil:
+ rew = newtime
+ case newtime == nil || *storedtime < *newtime:
+ rew = storedtime
+ default:
+ rew = newtime
+ }
+ err := &ConfigCompatError{
+ What: what,
+ StoredTime: storedtime,
+ NewTime: newtime,
+ RewindToTime: 0,
+ }
+ if rew != nil {
+ err.RewindToTime = *rew - 1
}
return err
}
func (err *ConfigCompatError) Error() string {
- return fmt.Sprintf("mismatching %s in database (have %d, want %d, rewindto %d)", err.What, err.StoredConfig, err.NewConfig, err.RewindTo)
+ if err.StoredBlock != nil {
+ return fmt.Sprintf("mismatching %s in database (have block %d, want block %d, rewindto block %d)", err.What, err.StoredBlock, err.NewBlock, err.RewindToBlock)
+ }
+ return fmt.Sprintf("mismatching %s in database (have timestamp %d, want timestamp %d, rewindto timestamp %d)", err.What, err.StoredTime, err.NewTime, err.RewindToTime)
}
// Rules wraps ChainConfig and is merely syntactic sugar or can be used for functions
@@ -773,11 +954,11 @@ type Rules struct {
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon bool
- IsMerge, IsShanghai, isCancun bool
+ IsMerge, IsShanghai, isCancun, isPrague bool
}
// Rules ensures c's ChainID is not nil.
-func (c *ChainConfig) Rules(num *big.Int, isMerge bool) Rules {
+func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules {
chainID := c.ChainID
if chainID == nil {
chainID = new(big.Int)
@@ -795,7 +976,8 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool) Rules {
IsBerlin: c.IsBerlin(num),
IsLondon: c.IsLondon(num),
IsMerge: isMerge,
- IsShanghai: c.IsShanghai(num),
- isCancun: c.IsCancun(num),
+ IsShanghai: c.IsShanghai(timestamp),
+ isCancun: c.IsCancun(timestamp),
+ isPrague: c.IsPrague(timestamp),
}
}
diff --git a/params/config_test.go b/params/config_test.go
index 3c8ebaf4a5..5634569e29 100644
--- a/params/config_test.go
+++ b/params/config_test.go
@@ -20,79 +20,119 @@ import (
"math/big"
"reflect"
"testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common/math"
)
func TestCheckCompatible(t *testing.T) {
type test struct {
- stored, new *ChainConfig
- head uint64
- wantErr *ConfigCompatError
+ stored, new *ChainConfig
+ headBlock uint64
+ headTimestamp uint64
+ wantErr *ConfigCompatError
}
tests := []test{
- {stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, head: 0, wantErr: nil},
- {stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, head: 100, wantErr: nil},
+ {stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, headBlock: 0, headTimestamp: 0, wantErr: nil},
+ {stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, headBlock: 0, headTimestamp: uint64(time.Now().Unix()), wantErr: nil},
+ {stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, headBlock: 100, wantErr: nil},
{
- stored: &ChainConfig{EIP150Block: big.NewInt(10)},
- new: &ChainConfig{EIP150Block: big.NewInt(20)},
- head: 9,
- wantErr: nil,
+ stored: &ChainConfig{EIP150Block: big.NewInt(10)},
+ new: &ChainConfig{EIP150Block: big.NewInt(20)},
+ headBlock: 9,
+ wantErr: nil,
},
{
- stored: AllEthashProtocolChanges,
- new: &ChainConfig{HomesteadBlock: nil},
- head: 3,
+ stored: AllEthashProtocolChanges,
+ new: &ChainConfig{HomesteadBlock: nil},
+ headBlock: 3,
wantErr: &ConfigCompatError{
- What: "Homestead fork block",
- StoredConfig: big.NewInt(0),
- NewConfig: nil,
- RewindTo: 0,
+ What: "Homestead fork block",
+ StoredBlock: big.NewInt(0),
+ NewBlock: nil,
+ RewindToBlock: 0,
},
},
{
- stored: AllEthashProtocolChanges,
- new: &ChainConfig{HomesteadBlock: big.NewInt(1)},
- head: 3,
+ stored: AllEthashProtocolChanges,
+ new: &ChainConfig{HomesteadBlock: big.NewInt(1)},
+ headBlock: 3,
wantErr: &ConfigCompatError{
- What: "Homestead fork block",
- StoredConfig: big.NewInt(0),
- NewConfig: big.NewInt(1),
- RewindTo: 0,
+ What: "Homestead fork block",
+ StoredBlock: big.NewInt(0),
+ NewBlock: big.NewInt(1),
+ RewindToBlock: 0,
},
},
{
- stored: &ChainConfig{HomesteadBlock: big.NewInt(30), EIP150Block: big.NewInt(10)},
- new: &ChainConfig{HomesteadBlock: big.NewInt(25), EIP150Block: big.NewInt(20)},
- head: 25,
+ stored: &ChainConfig{HomesteadBlock: big.NewInt(30), EIP150Block: big.NewInt(10)},
+ new: &ChainConfig{HomesteadBlock: big.NewInt(25), EIP150Block: big.NewInt(20)},
+ headBlock: 25,
+ wantErr: &ConfigCompatError{
+ What: "EIP150 fork block",
+ StoredBlock: big.NewInt(10),
+ NewBlock: big.NewInt(20),
+ RewindToBlock: 9,
+ },
+ },
+ {
+ stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
+ new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(30)},
+ headBlock: 40,
+ wantErr: nil,
+ },
+ {
+ stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
+ new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(31)},
+ headBlock: 40,
wantErr: &ConfigCompatError{
- What: "EIP150 fork block",
- StoredConfig: big.NewInt(10),
- NewConfig: big.NewInt(20),
- RewindTo: 9,
+ What: "Petersburg fork block",
+ StoredBlock: nil,
+ NewBlock: big.NewInt(31),
+ RewindToBlock: 30,
},
},
{
- stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
- new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(30)},
- head: 40,
- wantErr: nil,
+ stored: &ChainConfig{ShanghaiTime: newUint64(10)},
+ new: &ChainConfig{ShanghaiTime: newUint64(20)},
+ headTimestamp: 9,
+ wantErr: nil,
},
{
- stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
- new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(31)},
- head: 40,
+ stored: &ChainConfig{ShanghaiTime: newUint64(10)},
+ new: &ChainConfig{ShanghaiTime: newUint64(20)},
+ headTimestamp: 25,
wantErr: &ConfigCompatError{
- What: "Petersburg fork block",
- StoredConfig: nil,
- NewConfig: big.NewInt(31),
- RewindTo: 30,
+ What: "Shanghai fork timestamp",
+ StoredTime: newUint64(10),
+ NewTime: newUint64(20),
+ RewindToTime: 9,
},
},
}
for _, test := range tests {
- err := test.stored.CheckCompatible(test.new, test.head)
+ err := test.stored.CheckCompatible(test.new, test.headBlock, test.headTimestamp)
if !reflect.DeepEqual(err, test.wantErr) {
- t.Errorf("error mismatch:\nstored: %v\nnew: %v\nhead: %v\nerr: %v\nwant: %v", test.stored, test.new, test.head, err, test.wantErr)
+ t.Errorf("error mismatch:\nstored: %v\nnew: %v\nheadBlock: %v\nheadTimestamp: %v\nerr: %v\nwant: %v", test.stored, test.new, test.headBlock, test.headTimestamp, err, test.wantErr)
}
}
}
+
+func TestConfigRules(t *testing.T) {
+ c := &ChainConfig{
+ ShanghaiTime: newUint64(500),
+ }
+ var stamp uint64
+ if r := c.Rules(big.NewInt(0), true, stamp); r.IsShanghai {
+ t.Errorf("expected %v to not be shanghai", stamp)
+ }
+ stamp = 500
+ if r := c.Rules(big.NewInt(0), true, stamp); !r.IsShanghai {
+ t.Errorf("expected %v to be shanghai", stamp)
+ }
+ stamp = math.MaxInt64
+ if r := c.Rules(big.NewInt(0), true, stamp); !r.IsShanghai {
+ t.Errorf("expected %v to be shanghai", stamp)
+ }
+}
diff --git a/params/denomination.go b/params/denomination.go
index fb4da7f412..bcedd271e0 100644
--- a/params/denomination.go
+++ b/params/denomination.go
@@ -19,8 +19,7 @@ package params
// These are the multipliers for ether denominations.
// Example: To get the wei value of an amount in 'gwei', use
//
-// new(big.Int).Mul(value, big.NewInt(params.GWei))
-//
+// new(big.Int).Mul(value, big.NewInt(params.GWei))
const (
Wei = 1
GWei = 1e9
diff --git a/params/protocol_params.go b/params/protocol_params.go
index 5f154597a7..bb703d0b74 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -38,6 +38,7 @@ const (
Keccak256Gas uint64 = 30 // Once per KECCAK256 operation.
Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data.
+ InitCodeWordGas uint64 = 2 // Once per word of the init code when creating a contract.
SstoreSetGas uint64 = 20000 // Once per SSTORE operation.
SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero.
@@ -119,11 +120,12 @@ const (
// Introduced in Tangerine Whistle (Eip 150)
CreateBySelfdestructGas uint64 = 25000
- BaseFeeChangeDenominator = 8 // Bounds the amount the base fee can change between blocks.
- ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
- InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.
+ DefaultBaseFeeChangeDenominator = 8 // Bounds the amount the base fee can change between blocks.
+ DefaultElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
+ InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.
- MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
+ MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
+ MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions
// Precompiled contract gas prices
diff --git a/params/version.go b/params/version.go
index f067adb1d1..de0789e883 100644
--- a/params/version.go
+++ b/params/version.go
@@ -22,8 +22,8 @@ import (
const (
VersionMajor = 1 // Major version component of the current release
- VersionMinor = 10 // Minor version component of the current release
- VersionPatch = 26 // Patch version component of the current release
+ VersionMinor = 11 // Minor version component of the current release
+ VersionPatch = 2 // Patch version component of the current release
VersionMeta = "stable" // Version metadata to append to the version string
)
diff --git a/rlp/decode.go b/rlp/decode.go
index 9214dbfb37..c9b2652414 100644
--- a/rlp/decode.go
+++ b/rlp/decode.go
@@ -76,7 +76,7 @@ type Decoder interface {
// Note that Decode does not set an input limit for all readers and may be vulnerable to
// panics cause by huge value sizes. If you need an input limit, use
//
-// NewStream(r, limit).Decode(val)
+// NewStream(r, limit).Decode(val)
func Decode(r io.Reader, val interface{}) error {
stream := streamPool.Get().(*Stream)
defer streamPool.Put(stream)
diff --git a/rlp/decode_test.go b/rlp/decode_test.go
index 00722f847b..dbcfcffed1 100644
--- a/rlp/decode_test.go
+++ b/rlp/decode_test.go
@@ -439,6 +439,16 @@ type optionalPtrField struct {
B *[3]byte `rlp:"optional"`
}
+type nonOptionalPtrField struct {
+ A uint
+ B *[3]byte
+}
+
+type multipleOptionalFields struct {
+ A *[3]byte `rlp:"optional"`
+ B *[3]byte `rlp:"optional"`
+}
+
type optionalPtrFieldNil struct {
A uint
B *[3]byte `rlp:"optional,nil"`
@@ -744,6 +754,30 @@ var decodeTests = []decodeTest{
ptr: new(optionalPtrField),
value: optionalPtrField{A: 1, B: &[3]byte{1, 2, 3}},
},
+ {
+ // all optional fields nil
+ input: "C0",
+ ptr: new(multipleOptionalFields),
+ value: multipleOptionalFields{A: nil, B: nil},
+ },
+ {
+ // all optional fields set
+ input: "C88301020383010203",
+ ptr: new(multipleOptionalFields),
+ value: multipleOptionalFields{A: &[3]byte{1, 2, 3}, B: &[3]byte{1, 2, 3}},
+ },
+ {
+ // nil optional field appears before a non-nil one
+ input: "C58083010203",
+ ptr: new(multipleOptionalFields),
+ error: "rlp: input string too short for [3]uint8, decoding into (rlp.multipleOptionalFields).A",
+ },
+ {
+ // decode a nil ptr into a ptr that is not nil or not optional
+ input: "C20180",
+ ptr: new(nonOptionalPtrField),
+ error: "rlp: input string too short for [3]uint8, decoding into (rlp.nonOptionalPtrField).B",
+ },
{
input: "C101",
ptr: new(optionalPtrFieldNil),
diff --git a/rlp/doc.go b/rlp/doc.go
index e4404c978d..eeeee9a43a 100644
--- a/rlp/doc.go
+++ b/rlp/doc.go
@@ -27,8 +27,7 @@ value zero equivalent to the empty string).
RLP values are distinguished by a type tag. The type tag precedes the value in the input
stream and defines the size and kind of the bytes that follow.
-
-Encoding Rules
+# Encoding Rules
Package rlp uses reflection and encodes RLP based on the Go type of the value.
@@ -58,8 +57,7 @@ An interface value encodes as the value contained in the interface.
Floating point numbers, maps, channels and functions are not supported.
-
-Decoding Rules
+# Decoding Rules
Decoding uses the following type-dependent rules:
@@ -93,30 +91,29 @@ or one (true).
To decode into an interface value, one of these types is stored in the value:
- []interface{}, for RLP lists
- []byte, for RLP strings
+ []interface{}, for RLP lists
+ []byte, for RLP strings
Non-empty interface types are not supported when decoding.
Signed integers, floating point numbers, maps, channels and functions cannot be decoded into.
-
-Struct Tags
+# Struct Tags
As with other encoding packages, the "-" tag ignores fields.
- type StructWithIgnoredField struct{
- Ignored uint `rlp:"-"`
- Field uint
- }
+ type StructWithIgnoredField struct{
+ Ignored uint `rlp:"-"`
+ Field uint
+ }
Go struct values encode/decode as RLP lists. There are two ways of influencing the mapping
of fields to list elements. The "tail" tag, which may only be used on the last exported
struct field, allows slurping up any excess list elements into a slice.
- type StructWithTail struct{
- Field uint
- Tail []string `rlp:"tail"`
- }
+ type StructWithTail struct{
+ Field uint
+ Tail []string `rlp:"tail"`
+ }
The "optional" tag says that the field may be omitted if it is zero-valued. If this tag is
used on a struct field, all subsequent public fields must also be declared optional.
@@ -128,11 +125,11 @@ When decoding into a struct, optional fields may be omitted from the end of the
list. For the example below, this means input lists of one, two, or three elements are
accepted.
- type StructWithOptionalFields struct{
- Required uint
- Optional1 uint `rlp:"optional"`
- Optional2 uint `rlp:"optional"`
- }
+ type StructWithOptionalFields struct{
+ Required uint
+ Optional1 uint `rlp:"optional"`
+ Optional2 uint `rlp:"optional"`
+ }
The "nil", "nilList" and "nilString" tags apply to pointer-typed fields only, and change
the decoding rules for the field type. For regular pointer fields without the "nil" tag,
@@ -140,9 +137,9 @@ input values must always match the required input length exactly and the decoder
produce nil values. When the "nil" tag is set, input values of size zero decode as a nil
pointer. This is especially useful for recursive types.
- type StructWithNilField struct {
- Field *[3]byte `rlp:"nil"`
- }
+ type StructWithNilField struct {
+ Field *[3]byte `rlp:"nil"`
+ }
In the example above, Field allows two possible input sizes. For input 0xC180 (a list
containing an empty string) Field is set to nil after decoding. For input 0xC483000000 (a
diff --git a/rlp/encbuffer.go b/rlp/encbuffer.go
index 687949c044..d2c6d93bca 100644
--- a/rlp/encbuffer.go
+++ b/rlp/encbuffer.go
@@ -381,7 +381,7 @@ func (w EncoderBuffer) WriteBytes(b []byte) {
w.buf.writeBytes(b)
}
-// WriteBytes encodes s as an RLP string.
+// WriteString encodes s as an RLP string.
func (w EncoderBuffer) WriteString(s string) {
w.buf.writeString(s)
}
diff --git a/rlp/encode.go b/rlp/encode.go
index b96505f56d..a377a1ef4c 100644
--- a/rlp/encode.go
+++ b/rlp/encode.go
@@ -29,8 +29,11 @@ import (
var (
// Common encoded values.
// These are useful when implementing EncodeRLP.
+
+ // EmptyString is the encoding of an empty string.
EmptyString = []byte{0x80}
- EmptyList = []byte{0xC0}
+ // EmptyList is the encoding of an empty list.
+ EmptyList = []byte{0xC0}
)
var ErrNegativeBigInt = errors.New("rlp: cannot encode negative big.Int")
diff --git a/rlp/encode_test.go b/rlp/encode_test.go
index 58ddc0d120..82c490a802 100644
--- a/rlp/encode_test.go
+++ b/rlp/encode_test.go
@@ -290,6 +290,10 @@ var encTests = []encTest{
{val: &optionalBigIntField{A: 1}, output: "C101"},
{val: &optionalPtrField{A: 1}, output: "C101"},
{val: &optionalPtrFieldNil{A: 1}, output: "C101"},
+ {val: &multipleOptionalFields{A: nil, B: nil}, output: "C0"},
+ {val: &multipleOptionalFields{A: &[3]byte{1, 2, 3}, B: &[3]byte{1, 2, 3}}, output: "C88301020383010203"},
+ {val: &multipleOptionalFields{A: nil, B: &[3]byte{1, 2, 3}}, output: "C58083010203"}, // encodes without error but decode will fail
+ {val: &nonOptionalPtrField{A: 1}, output: "C20180"}, // encodes without error but decode will fail
// nil
{val: (*uint)(nil), output: "80"},
diff --git a/rlp/internal/rlpstruct/rlpstruct.go b/rlp/internal/rlpstruct/rlpstruct.go
index 1edead96ce..2e3eeb6881 100644
--- a/rlp/internal/rlpstruct/rlpstruct.go
+++ b/rlp/internal/rlpstruct/rlpstruct.go
@@ -44,7 +44,7 @@ type Type struct {
Elem *Type // non-nil for Kind values of Ptr, Slice, Array
}
-// defaultNilValue determines whether a nil pointer to t encodes/decodes
+// DefaultNilValue determines whether a nil pointer to t encodes/decodes
// as an empty string or empty list.
func (t Type) DefaultNilValue() NilKind {
k := t.Kind
diff --git a/rlp/raw.go b/rlp/raw.go
index f355efc144..773aa7e614 100644
--- a/rlp/raw.go
+++ b/rlp/raw.go
@@ -28,13 +28,46 @@ type RawValue []byte
var rawValueType = reflect.TypeOf(RawValue{})
+// StringSize returns the encoded size of a string.
+func StringSize(s string) uint64 {
+ switch {
+ case len(s) == 0:
+ return 1
+ case len(s) == 1:
+ if s[0] <= 0x7f {
+ return 1
+ } else {
+ return 2
+ }
+ default:
+ return uint64(headsize(uint64(len(s))) + len(s))
+ }
+}
+
+// BytesSize returns the encoded size of a byte slice.
+func BytesSize(b []byte) uint64 {
+ switch {
+ case len(b) == 0:
+ return 1
+ case len(b) == 1:
+ if b[0] <= 0x7f {
+ return 1
+ } else {
+ return 2
+ }
+ default:
+ return uint64(headsize(uint64(len(b))) + len(b))
+ }
+}
+
// ListSize returns the encoded size of an RLP list with the given
// content size.
func ListSize(contentSize uint64) uint64 {
return uint64(headsize(contentSize)) + contentSize
}
-// IntSize returns the encoded size of the integer x.
+// IntSize returns the encoded size of the integer x. Note: The return type of this
+// function is 'int' for backwards-compatibility reasons. The result is always positive.
func IntSize(x uint64) int {
if x < 0x80 {
return 1
diff --git a/rlp/raw_test.go b/rlp/raw_test.go
index 46adff22c5..7b3255eca3 100644
--- a/rlp/raw_test.go
+++ b/rlp/raw_test.go
@@ -60,15 +60,35 @@ func TestCountValues(t *testing.T) {
}
}
-func TestSplitTypes(t *testing.T) {
- if _, _, err := SplitString(unhex("C100")); err != ErrExpectedString {
- t.Errorf("SplitString returned %q, want %q", err, ErrExpectedString)
- }
- if _, _, err := SplitList(unhex("01")); err != ErrExpectedList {
- t.Errorf("SplitString returned %q, want %q", err, ErrExpectedList)
+func TestSplitString(t *testing.T) {
+ for i, test := range []string{
+ "C0",
+ "C100",
+ "C3010203",
+ "C88363617483646F67",
+ "F8384C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E7365637465747572206164697069736963696E6720656C6974",
+ } {
+ if _, _, err := SplitString(unhex(test)); !errors.Is(err, ErrExpectedString) {
+ t.Errorf("test %d: error mismatch: have %q, want %q", i, err, ErrExpectedString)
+ }
}
- if _, _, err := SplitList(unhex("81FF")); err != ErrExpectedList {
- t.Errorf("SplitString returned %q, want %q", err, ErrExpectedList)
+}
+
+func TestSplitList(t *testing.T) {
+ for i, test := range []string{
+ "80",
+ "00",
+ "01",
+ "8180",
+ "81FF",
+ "820400",
+ "83636174",
+ "83646F67",
+ "B8384C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E7365637465747572206164697069736963696E6720656C6974",
+ } {
+ if _, _, err := SplitList(unhex(test)); !errors.Is(err, ErrExpectedList) {
+ t.Errorf("test %d: error mismatch: have %q, want %q", i, err, ErrExpectedList)
+ }
}
}
@@ -283,3 +303,36 @@ func TestAppendUint64Random(t *testing.T) {
t.Fatal(err)
}
}
+
+func TestBytesSize(t *testing.T) {
+ tests := []struct {
+ v []byte
+ size uint64
+ }{
+ {v: []byte{}, size: 1},
+ {v: []byte{0x1}, size: 1},
+ {v: []byte{0x7E}, size: 1},
+ {v: []byte{0x7F}, size: 1},
+ {v: []byte{0x80}, size: 2},
+ {v: []byte{0xFF}, size: 2},
+ {v: []byte{0xFF, 0xF0}, size: 3},
+ {v: make([]byte, 55), size: 56},
+ {v: make([]byte, 56), size: 58},
+ }
+
+ for _, test := range tests {
+ s := BytesSize(test.v)
+ if s != test.size {
+ t.Errorf("BytesSize(%#x) -> %d, want %d", test.v, s, test.size)
+ }
+ s = StringSize(string(test.v))
+ if s != test.size {
+ t.Errorf("StringSize(%#x) -> %d, want %d", test.v, s, test.size)
+ }
+ // Sanity check:
+ enc, _ := EncodeToBytes(test.v)
+ if uint64(len(enc)) != test.size {
+ t.Errorf("len(EncodeToBytes(%#x)) -> %d, test says %d", test.v, len(enc), test.size)
+ }
+ }
+}
diff --git a/rpc/client.go b/rpc/client.go
index fcd31319a3..69ff4851e3 100644
--- a/rpc/client.go
+++ b/rpc/client.go
@@ -22,6 +22,7 @@ import (
"errors"
"fmt"
"net/url"
+ "os"
"reflect"
"strconv"
"sync/atomic"
@@ -100,7 +101,7 @@ type Client struct {
reqTimeout chan *requestOp // removes response IDs when call timeout expires
}
-type reconnectFunc func(ctx context.Context) (ServerCodec, error)
+type reconnectFunc func(context.Context) (ServerCodec, error)
type clientContextKey struct{}
@@ -154,14 +155,16 @@ func (op *requestOp) wait(ctx context.Context, c *Client) (*jsonrpcMessage, erro
//
// The currently supported URL schemes are "http", "https", "ws" and "wss". If rawurl is a
// file name with no URL scheme, a local socket connection is established using UNIX
-// domain sockets on supported platforms and named pipes on Windows. If you want to
-// configure transport options, use DialHTTP, DialWebsocket or DialIPC instead.
+// domain sockets on supported platforms and named pipes on Windows.
+//
+// If you want to further configure the transport, use DialOptions instead of this
+// function.
//
// For websocket connections, the origin is set to the local host name.
//
-// The client reconnects automatically if the connection is lost.
+// The client reconnects automatically when the connection is lost.
func Dial(rawurl string) (*Client, error) {
- return DialContext(context.Background(), rawurl)
+ return DialOptions(context.Background(), rawurl)
}
// DialContext creates a new RPC client, just like Dial.
@@ -169,22 +172,46 @@ func Dial(rawurl string) (*Client, error) {
// The context is used to cancel or time out the initial connection establishment. It does
// not affect subsequent interactions with the client.
func DialContext(ctx context.Context, rawurl string) (*Client, error) {
+ return DialOptions(ctx, rawurl)
+}
+
+// DialOptions creates a new RPC client for the given URL. You can supply any of the
+// pre-defined client options to configure the underlying transport.
+//
+// The context is used to cancel or time out the initial connection establishment. It does
+// not affect subsequent interactions with the client.
+//
+// The client reconnects automatically when the connection is lost.
+func DialOptions(ctx context.Context, rawurl string, options ...ClientOption) (*Client, error) {
u, err := url.Parse(rawurl)
if err != nil {
return nil, err
}
+
+ cfg := new(clientConfig)
+ for _, opt := range options {
+ opt.applyOption(cfg)
+ }
+
+ var reconnect reconnectFunc
switch u.Scheme {
case "http", "https":
- return DialHTTP(rawurl)
+ reconnect = newClientTransportHTTP(rawurl, cfg)
case "ws", "wss":
- return DialWebsocket(ctx, rawurl, "")
+ rc, err := newClientTransportWS(rawurl, cfg)
+ if err != nil {
+ return nil, err
+ }
+ reconnect = rc
case "stdio":
- return DialStdIO(ctx)
+ reconnect = newClientTransportIO(os.Stdin, os.Stdout)
case "":
- return DialIPC(ctx, rawurl)
+ reconnect = newClientTransportIPC(rawurl)
default:
return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme)
}
+
+ return newClient(ctx, reconnect)
}
// ClientFromContext retrieves the client from the context, if any. This can be used to perform
@@ -318,7 +345,10 @@ func (c *Client) CallContext(ctx context.Context, result interface{}, method str
case len(resp.Result) == 0:
return ErrNoResult
default:
- return json.Unmarshal(resp.Result, &result)
+ if result == nil {
+ return nil
+ }
+ return json.Unmarshal(resp.Result, result)
}
}
@@ -500,7 +530,7 @@ func (c *Client) write(ctx context.Context, msg interface{}, retry bool) error {
return err
}
}
- err := c.writeConn.writeJSON(ctx, msg)
+ err := c.writeConn.writeJSON(ctx, msg, false)
if err != nil {
c.writeConn = nil
if !retry {
@@ -633,7 +663,8 @@ func (c *Client) read(codec ServerCodec) {
for {
msgs, batch, err := codec.readBatch()
if _, ok := err.(*json.SyntaxError); ok {
- codec.writeJSON(context.Background(), errorMessage(&parseError{err.Error()}))
+ msg := errorMessage(&parseError{err.Error()})
+ codec.writeJSON(context.Background(), msg, true)
}
if err != nil {
c.readErr <- err
diff --git a/rpc/client_opt.go b/rpc/client_opt.go
new file mode 100644
index 0000000000..5ad7c22b3c
--- /dev/null
+++ b/rpc/client_opt.go
@@ -0,0 +1,106 @@
+// 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 rpc
+
+import (
+ "net/http"
+
+ "github.com/gorilla/websocket"
+)
+
+// ClientOption is a configuration option for the RPC client.
+type ClientOption interface {
+ applyOption(*clientConfig)
+}
+
+type clientConfig struct {
+ httpClient *http.Client
+ httpHeaders http.Header
+ httpAuth HTTPAuth
+
+ wsDialer *websocket.Dialer
+}
+
+func (cfg *clientConfig) initHeaders() {
+ if cfg.httpHeaders == nil {
+ cfg.httpHeaders = make(http.Header)
+ }
+}
+
+func (cfg *clientConfig) setHeader(key, value string) {
+ cfg.initHeaders()
+ cfg.httpHeaders.Set(key, value)
+}
+
+type optionFunc func(*clientConfig)
+
+func (fn optionFunc) applyOption(opt *clientConfig) {
+ fn(opt)
+}
+
+// WithWebsocketDialer configures the websocket.Dialer used by the RPC client.
+func WithWebsocketDialer(dialer websocket.Dialer) ClientOption {
+ return optionFunc(func(cfg *clientConfig) {
+ cfg.wsDialer = &dialer
+ })
+}
+
+// WithHeader configures HTTP headers set by the RPC client. Headers set using this option
+// will be used for both HTTP and WebSocket connections.
+func WithHeader(key, value string) ClientOption {
+ return optionFunc(func(cfg *clientConfig) {
+ cfg.initHeaders()
+ cfg.httpHeaders.Set(key, value)
+ })
+}
+
+// WithHeaders configures HTTP headers set by the RPC client. Headers set using this
+// option will be used for both HTTP and WebSocket connections.
+func WithHeaders(headers http.Header) ClientOption {
+ return optionFunc(func(cfg *clientConfig) {
+ cfg.initHeaders()
+ for k, vs := range headers {
+ cfg.httpHeaders[k] = vs
+ }
+ })
+}
+
+// WithHTTPClient configures the http.Client used by the RPC client.
+func WithHTTPClient(c *http.Client) ClientOption {
+ return optionFunc(func(cfg *clientConfig) {
+ cfg.httpClient = c
+ })
+}
+
+// WithHTTPAuth configures HTTP request authentication. The given provider will be called
+// whenever a request is made. Note that only one authentication provider can be active at
+// any time.
+func WithHTTPAuth(a HTTPAuth) ClientOption {
+ if a == nil {
+ panic("nil auth")
+ }
+ return optionFunc(func(cfg *clientConfig) {
+ cfg.httpAuth = a
+ })
+}
+
+// A HTTPAuth function is called by the client whenever a HTTP request is sent.
+// The function must be safe for concurrent use.
+//
+// Usually, HTTPAuth functions will call h.Set("authorization", "...") to add
+// auth information to the request.
+type HTTPAuth func(h http.Header) error
diff --git a/rpc/client_opt_test.go b/rpc/client_opt_test.go
new file mode 100644
index 0000000000..d7cc2572a7
--- /dev/null
+++ b/rpc/client_opt_test.go
@@ -0,0 +1,25 @@
+package rpc_test
+
+import (
+ "context"
+ "net/http"
+ "time"
+
+ "github.com/ethereum/go-ethereum/rpc"
+)
+
+// This example configures a HTTP-based RPC client with two options - one setting the
+// overall request timeout, the other adding a custom HTTP header to all requests.
+func ExampleDialOptions() {
+ tokenHeader := rpc.WithHeader("x-token", "foo")
+ httpClient := rpc.WithHTTPClient(&http.Client{
+ Timeout: 10 * time.Second,
+ })
+
+ ctx := context.Background()
+ c, err := rpc.DialOptions(ctx, "http://rpc.example.com", httpClient, tokenHeader)
+ if err != nil {
+ panic(err)
+ }
+ c.Close()
+}
diff --git a/rpc/client_test.go b/rpc/client_test.go
index 5ae549d865..a94a54929b 100644
--- a/rpc/client_test.go
+++ b/rpc/client_test.go
@@ -69,6 +69,26 @@ func TestClientResponseType(t *testing.T) {
}
}
+// This test checks calling a method that returns 'null'.
+func TestClientNullResponse(t *testing.T) {
+ server := newTestServer()
+ defer server.Stop()
+
+ client := DialInProc(server)
+ defer client.Close()
+
+ var result json.RawMessage
+ if err := client.Call(&result, "test_null"); err != nil {
+ t.Fatal(err)
+ }
+ if result == nil {
+ t.Fatal("Expected non-nil result")
+ }
+ if !reflect.DeepEqual(result, json.RawMessage("null")) {
+ t.Errorf("Expected null, got %s", result)
+ }
+}
+
// This test checks that server-returned errors with code and data come out of Client.Call.
func TestClientErrorData(t *testing.T) {
server := newTestServer()
@@ -83,11 +103,15 @@ func TestClientErrorData(t *testing.T) {
}
// Check code.
+ // The method handler returns an error value which implements the rpc.Error
+ // interface, i.e. it has a custom error code. The server returns this error code.
+ expectedCode := testError{}.ErrorCode()
if e, ok := err.(Error); !ok {
t.Fatalf("client did not return rpc.Error, got %#v", e)
- } else if e.ErrorCode() != (testError{}.ErrorCode()) {
- t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), testError{}.ErrorCode())
+ } else if e.ErrorCode() != expectedCode {
+ t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), expectedCode)
}
+
// Check data.
if e, ok := err.(DataError); !ok {
t.Fatalf("client did not return rpc.DataError, got %#v", e)
diff --git a/rpc/context_headers.go b/rpc/context_headers.go
new file mode 100644
index 0000000000..29a58150e3
--- /dev/null
+++ b/rpc/context_headers.go
@@ -0,0 +1,56 @@
+// 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 rpc
+
+import (
+ "context"
+ "net/http"
+)
+
+type mdHeaderKey struct{}
+
+// NewContextWithHeaders wraps the given context, adding HTTP headers. These headers will
+// be applied by Client when making a request using the returned context.
+func NewContextWithHeaders(ctx context.Context, h http.Header) context.Context {
+ if len(h) == 0 {
+ // This check ensures the header map set in context will never be nil.
+ return ctx
+ }
+
+ var ctxh http.Header
+ prev, ok := ctx.Value(mdHeaderKey{}).(http.Header)
+ if ok {
+ ctxh = setHeaders(prev.Clone(), h)
+ } else {
+ ctxh = h.Clone()
+ }
+ return context.WithValue(ctx, mdHeaderKey{}, ctxh)
+}
+
+// headersFromContext is used to extract http.Header from context.
+func headersFromContext(ctx context.Context) http.Header {
+ source, _ := ctx.Value(mdHeaderKey{}).(http.Header)
+ return source
+}
+
+// setHeaders sets all headers from src in dst.
+func setHeaders(dst http.Header, src http.Header) http.Header {
+ for key, values := range src {
+ dst[http.CanonicalHeaderKey(key)] = values
+ }
+ return dst
+}
diff --git a/rpc/doc.go b/rpc/doc.go
index e0a6324675..7c87793dca 100644
--- a/rpc/doc.go
+++ b/rpc/doc.go
@@ -15,7 +15,6 @@
// along with the go-ethereum library. If not, see .
/*
-
Package rpc implements bi-directional JSON-RPC 2.0 on multiple transports.
It provides access to the exported methods of an object across a network or other I/O
@@ -23,16 +22,16 @@ connection. After creating a server or client instance, objects can be registere
them visible as 'services'. Exported methods that follow specific conventions can be
called remotely. It also has support for the publish/subscribe pattern.
-RPC Methods
+# RPC Methods
Methods that satisfy the following criteria are made available for remote access:
- - method must be exported
- - method returns 0, 1 (response or error) or 2 (response and error) values
+ - method must be exported
+ - method returns 0, 1 (response or error) or 2 (response and error) values
An example method:
- func (s *CalcService) Add(a, b int) (int, error)
+ func (s *CalcService) Add(a, b int) (int, error)
When the returned error isn't nil the returned integer is ignored and the error is sent
back to the client. Otherwise the returned integer is sent back to the client.
@@ -41,7 +40,7 @@ Optional arguments are supported by accepting pointer values as arguments. E.g.
to do the addition in an optional finite field we can accept a mod argument as pointer
value.
- func (s *CalcService) Add(a, b int, mod *int) (int, error)
+ func (s *CalcService) Add(a, b int, mod *int) (int, error)
This RPC method can be called with 2 integers and a null value as third argument. In that
case the mod argument will be nil. Or it can be called with 3 integers, in that case mod
@@ -56,40 +55,40 @@ to the client out of order.
An example server which uses the JSON codec:
- type CalculatorService struct {}
+ type CalculatorService struct {}
- func (s *CalculatorService) Add(a, b int) int {
- return a + b
- }
+ func (s *CalculatorService) Add(a, b int) int {
+ return a + b
+ }
- func (s *CalculatorService) Div(a, b int) (int, error) {
- if b == 0 {
- return 0, errors.New("divide by zero")
- }
- return a/b, nil
- }
+ func (s *CalculatorService) Div(a, b int) (int, error) {
+ if b == 0 {
+ return 0, errors.New("divide by zero")
+ }
+ return a/b, nil
+ }
- calculator := new(CalculatorService)
- server := NewServer()
- server.RegisterName("calculator", calculator)
- l, _ := net.ListenUnix("unix", &net.UnixAddr{Net: "unix", Name: "/tmp/calculator.sock"})
- server.ServeListener(l)
+ calculator := new(CalculatorService)
+ server := NewServer()
+ server.RegisterName("calculator", calculator)
+ l, _ := net.ListenUnix("unix", &net.UnixAddr{Net: "unix", Name: "/tmp/calculator.sock"})
+ server.ServeListener(l)
-Subscriptions
+# Subscriptions
The package also supports the publish subscribe pattern through the use of subscriptions.
A method that is considered eligible for notifications must satisfy the following
criteria:
- - method must be exported
- - first method argument type must be context.Context
- - method must have return types (rpc.Subscription, error)
+ - method must be exported
+ - first method argument type must be context.Context
+ - method must have return types (rpc.Subscription, error)
An example method:
- func (s *BlockChainService) NewBlocks(ctx context.Context) (rpc.Subscription, error) {
- ...
- }
+ func (s *BlockChainService) NewBlocks(ctx context.Context) (rpc.Subscription, error) {
+ ...
+ }
When the service containing the subscription method is registered to the server, for
example under the "blockchain" namespace, a subscription is created by calling the
@@ -101,7 +100,7 @@ the client and server. The server will close the connection for any write error.
For more information about subscriptions, see https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB.
-Reverse Calls
+# Reverse Calls
In any method handler, an instance of rpc.Client can be accessed through the
ClientFromContext method. Using this client instance, server-to-client method calls can be
diff --git a/rpc/errors.go b/rpc/errors.go
index 4c06a745fb..7188332d55 100644
--- a/rpc/errors.go
+++ b/rpc/errors.go
@@ -54,9 +54,20 @@ var (
_ Error = new(invalidRequestError)
_ Error = new(invalidMessageError)
_ Error = new(invalidParamsError)
+ _ Error = new(internalServerError)
)
-const defaultErrorCode = -32000
+const (
+ errcodeDefault = -32000
+ errcodeNotificationsUnsupported = -32001
+ errcodeTimeout = -32002
+ errcodePanic = -32603
+ errcodeMarshalError = -32603
+)
+
+const (
+ errMsgTimeout = "request timed out"
+)
type methodNotFoundError struct{ method string }
@@ -101,3 +112,13 @@ type invalidParamsError struct{ message string }
func (e *invalidParamsError) ErrorCode() int { return -32602 }
func (e *invalidParamsError) Error() string { return e.message }
+
+// internalServerError is used for server errors during request processing.
+type internalServerError struct {
+ code int
+ message string
+}
+
+func (e *internalServerError) ErrorCode() int { return e.code }
+
+func (e *internalServerError) Error() string { return e.message }
diff --git a/rpc/handler.go b/rpc/handler.go
index cd95a067f3..c2e7d7dc08 100644
--- a/rpc/handler.go
+++ b/rpc/handler.go
@@ -34,21 +34,20 @@ import (
//
// The entry points for incoming messages are:
//
-// h.handleMsg(message)
-// h.handleBatch(message)
+// h.handleMsg(message)
+// h.handleBatch(message)
//
// Outgoing calls use the requestOp struct. Register the request before sending it
// on the connection:
//
-// op := &requestOp{ids: ...}
-// h.addRequestOp(op)
+// op := &requestOp{ids: ...}
+// h.addRequestOp(op)
//
// Now send the request, then wait for the reply to be delivered through handleMsg:
//
-// if err := op.wait(...); err != nil {
-// h.removeRequestOp(op) // timeout, etc.
-// }
-//
+// if err := op.wait(...); err != nil {
+// h.removeRequestOp(op) // timeout, etc.
+// }
type handler struct {
reg *serviceRegistry
unsubscribeCb *callback
@@ -92,12 +91,83 @@ func newHandler(connCtx context.Context, conn jsonWriter, idgen func() ID, reg *
return h
}
+// batchCallBuffer manages in progress call messages and their responses during a batch
+// call. Calls need to be synchronized between the processing and timeout-triggering
+// goroutines.
+type batchCallBuffer struct {
+ mutex sync.Mutex
+ calls []*jsonrpcMessage
+ resp []*jsonrpcMessage
+ wrote bool
+}
+
+// nextCall returns the next unprocessed message.
+func (b *batchCallBuffer) nextCall() *jsonrpcMessage {
+ b.mutex.Lock()
+ defer b.mutex.Unlock()
+
+ if len(b.calls) == 0 {
+ return nil
+ }
+ // The popping happens in `pushAnswer`. The in progress call is kept
+ // so we can return an error for it in case of timeout.
+ msg := b.calls[0]
+ return msg
+}
+
+// pushResponse adds the response to last call returned by nextCall.
+func (b *batchCallBuffer) pushResponse(answer *jsonrpcMessage) {
+ b.mutex.Lock()
+ defer b.mutex.Unlock()
+
+ if answer != nil {
+ b.resp = append(b.resp, answer)
+ }
+ b.calls = b.calls[1:]
+}
+
+// write sends the responses.
+func (b *batchCallBuffer) write(ctx context.Context, conn jsonWriter) {
+ b.mutex.Lock()
+ defer b.mutex.Unlock()
+
+ b.doWrite(ctx, conn, false)
+}
+
+// timeout sends the responses added so far. For the remaining unanswered call
+// messages, it sends a timeout error response.
+func (b *batchCallBuffer) timeout(ctx context.Context, conn jsonWriter) {
+ b.mutex.Lock()
+ defer b.mutex.Unlock()
+
+ for _, msg := range b.calls {
+ if !msg.isNotification() {
+ resp := msg.errorResponse(&internalServerError{errcodeTimeout, errMsgTimeout})
+ b.resp = append(b.resp, resp)
+ }
+ }
+ b.doWrite(ctx, conn, true)
+}
+
+// doWrite actually writes the response.
+// This assumes b.mutex is held.
+func (b *batchCallBuffer) doWrite(ctx context.Context, conn jsonWriter, isErrorResponse bool) {
+ if b.wrote {
+ return
+ }
+ b.wrote = true // can only write once
+ if len(b.resp) > 0 {
+ conn.writeJSON(ctx, b.resp, isErrorResponse)
+ }
+}
+
// handleBatch executes all messages in a batch and returns the responses.
func (h *handler) handleBatch(msgs []*jsonrpcMessage) {
// Emit error response for empty batches:
if len(msgs) == 0 {
h.startCallProc(func(cp *callProc) {
- h.conn.writeJSON(cp.ctx, errorMessage(&invalidRequestError{"empty batch"}))
+ resp := errorMessage(&invalidRequestError{"empty batch"})
+ h.conn.writeJSON(cp.ctx, resp, true)
})
return
}
@@ -114,16 +184,42 @@ func (h *handler) handleBatch(msgs []*jsonrpcMessage) {
}
// Process calls on a goroutine because they may block indefinitely:
h.startCallProc(func(cp *callProc) {
- answers := make([]*jsonrpcMessage, 0, len(msgs))
- for _, msg := range calls {
- if answer := h.handleCallMsg(cp, msg); answer != nil {
- answers = append(answers, answer)
+ var (
+ timer *time.Timer
+ cancel context.CancelFunc
+ callBuffer = &batchCallBuffer{calls: calls, resp: make([]*jsonrpcMessage, 0, len(calls))}
+ )
+
+ cp.ctx, cancel = context.WithCancel(cp.ctx)
+ defer cancel()
+
+ // Cancel the request context after timeout and send an error response. Since the
+ // currently-running method might not return immediately on timeout, we must wait
+ // for the timeout concurrently with processing the request.
+ if timeout, ok := ContextRequestTimeout(cp.ctx); ok {
+ timer = time.AfterFunc(timeout, func() {
+ cancel()
+ callBuffer.timeout(cp.ctx, h.conn)
+ })
+ }
+
+ for {
+ // No need to handle rest of calls if timed out.
+ if cp.ctx.Err() != nil {
+ break
}
+ msg := callBuffer.nextCall()
+ if msg == nil {
+ break
+ }
+ resp := h.handleCallMsg(cp, msg)
+ callBuffer.pushResponse(resp)
}
- h.addSubscriptions(cp.notifiers)
- if len(answers) > 0 {
- h.conn.writeJSON(cp.ctx, answers)
+ if timer != nil {
+ timer.Stop()
}
+ callBuffer.write(cp.ctx, h.conn)
+ h.addSubscriptions(cp.notifiers)
for _, n := range cp.notifiers {
n.activate()
}
@@ -136,10 +232,36 @@ func (h *handler) handleMsg(msg *jsonrpcMessage) {
return
}
h.startCallProc(func(cp *callProc) {
+ var (
+ responded sync.Once
+ timer *time.Timer
+ cancel context.CancelFunc
+ )
+ cp.ctx, cancel = context.WithCancel(cp.ctx)
+ defer cancel()
+
+ // Cancel the request context after timeout and send an error response. Since the
+ // running method might not return immediately on timeout, we must wait for the
+ // timeout concurrently with processing the request.
+ if timeout, ok := ContextRequestTimeout(cp.ctx); ok {
+ timer = time.AfterFunc(timeout, func() {
+ cancel()
+ responded.Do(func() {
+ resp := msg.errorResponse(&internalServerError{errcodeTimeout, errMsgTimeout})
+ h.conn.writeJSON(cp.ctx, resp, true)
+ })
+ })
+ }
+
answer := h.handleCallMsg(cp, msg)
+ if timer != nil {
+ timer.Stop()
+ }
h.addSubscriptions(cp.notifiers)
if answer != nil {
- h.conn.writeJSON(cp.ctx, answer)
+ responded.Do(func() {
+ h.conn.writeJSON(cp.ctx, answer, false)
+ })
}
for _, n := range cp.notifiers {
n.activate()
@@ -335,7 +457,6 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage
}
start := time.Now()
answer := h.runMethod(cp.ctx, msg, callb, args)
-
// Collect the statistics for RPC calls if metrics is enabled.
// We only care about pure rpc call. Filter out subscription.
if callb != h.unsubscribeCb {
@@ -354,7 +475,10 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage
// handleSubscribe processes *_subscribe method calls.
func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
if !h.allowSubscribe {
- return msg.errorResponse(ErrNotificationsUnsupported)
+ return msg.errorResponse(&internalServerError{
+ code: errcodeNotificationsUnsupported,
+ message: ErrNotificationsUnsupported.Error(),
+ })
}
// Subscription method name is first argument.
diff --git a/rpc/http.go b/rpc/http.go
index b82ea2707a..8712f99610 100644
--- a/rpc/http.go
+++ b/rpc/http.go
@@ -23,9 +23,11 @@ import (
"errors"
"fmt"
"io"
+ "math"
"mime"
"net/http"
"net/url"
+ "strconv"
"sync"
"time"
)
@@ -45,13 +47,14 @@ type httpConn struct {
closeCh chan interface{}
mu sync.Mutex // protects headers
headers http.Header
+ auth HTTPAuth
}
// httpConn implements ServerCodec, but it is treated specially by Client
// and some methods don't work. The panic() stubs here exist to ensure
// this special treatment is correct.
-func (hc *httpConn) writeJSON(context.Context, interface{}) error {
+func (hc *httpConn) writeJSON(context.Context, interface{}, bool) error {
panic("writeJSON called on httpConn")
}
@@ -117,8 +120,15 @@ var DefaultHTTPTimeouts = HTTPTimeouts{
IdleTimeout: 120 * time.Second,
}
+// DialHTTP creates a new RPC client that connects to an RPC server over HTTP.
+func DialHTTP(endpoint string) (*Client, error) {
+ return DialHTTPWithClient(endpoint, new(http.Client))
+}
+
// DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP
// using the provided HTTP Client.
+//
+// Deprecated: use DialOptions and the WithHTTPClient option.
func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) {
// Sanity check URL so we don't end up with a client that will fail every request.
_, err := url.Parse(endpoint)
@@ -126,24 +136,36 @@ func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) {
return nil, err
}
- initctx := context.Background()
- headers := make(http.Header, 2)
+ var cfg clientConfig
+ cfg.httpClient = client
+ fn := newClientTransportHTTP(endpoint, &cfg)
+ return newClient(context.Background(), fn)
+}
+
+func newClientTransportHTTP(endpoint string, cfg *clientConfig) reconnectFunc {
+ headers := make(http.Header, 2+len(cfg.httpHeaders))
headers.Set("accept", contentType)
headers.Set("content-type", contentType)
- return newClient(initctx, func(context.Context) (ServerCodec, error) {
- hc := &httpConn{
- client: client,
- headers: headers,
- url: endpoint,
- closeCh: make(chan interface{}),
- }
- return hc, nil
- })
-}
+ for key, values := range cfg.httpHeaders {
+ headers[key] = values
+ }
-// DialHTTP creates a new RPC client that connects to an RPC server over HTTP.
-func DialHTTP(endpoint string) (*Client, error) {
- return DialHTTPWithClient(endpoint, new(http.Client))
+ client := cfg.httpClient
+ if client == nil {
+ client = new(http.Client)
+ }
+
+ hc := &httpConn{
+ client: client,
+ headers: headers,
+ url: endpoint,
+ auth: cfg.httpAuth,
+ closeCh: make(chan interface{}),
+ }
+
+ return func(ctx context.Context) (ServerCodec, error) {
+ return hc, nil
+ }
}
func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error {
@@ -187,7 +209,7 @@ func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadClos
if err != nil {
return nil, err
}
- req, err := http.NewRequestWithContext(ctx, "POST", hc.url, io.NopCloser(bytes.NewReader(body)))
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, hc.url, io.NopCloser(bytes.NewReader(body)))
if err != nil {
return nil, err
}
@@ -198,6 +220,13 @@ func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadClos
hc.mu.Lock()
req.Header = hc.headers.Clone()
hc.mu.Unlock()
+ setHeaders(req.Header, headersFromContext(ctx))
+
+ if hc.auth != nil {
+ if err := hc.auth(req.Header); err != nil {
+ return nil, err
+ }
+ }
// do request
resp, err := hc.client.Do(req)
@@ -230,7 +259,42 @@ type httpServerConn struct {
func newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec {
body := io.LimitReader(r.Body, maxRequestContentLength)
conn := &httpServerConn{Reader: body, Writer: w, r: r}
- return NewCodec(conn)
+
+ encoder := func(v any, isErrorResponse bool) error {
+ if !isErrorResponse {
+ return json.NewEncoder(conn).Encode(v)
+ }
+
+ // It's an error response and requires special treatment.
+ //
+ // In case of a timeout error, the response must be written before the HTTP
+ // server's write timeout occurs. So we need to flush the response. The
+ // Content-Length header also needs to be set to ensure the client knows
+ // when it has the full response.
+ encdata, err := json.Marshal(v)
+ if err != nil {
+ return err
+ }
+ w.Header().Set("content-length", strconv.Itoa(len(encdata)))
+
+ // If this request is wrapped in a handler that might remove Content-Length (such
+ // as the automatic gzip we do in package node), we need to ensure the HTTP server
+ // doesn't perform chunked encoding. In case WriteTimeout is reached, the chunked
+ // encoding might not be finished correctly, and some clients do not like it when
+ // the final chunk is missing.
+ w.Header().Set("transfer-encoding", "identity")
+
+ _, err = w.Write(encdata)
+ if f, ok := w.(http.Flusher); ok {
+ f.Flush()
+ }
+ return err
+ }
+
+ dec := json.NewDecoder(conn)
+ dec.UseNumber()
+
+ return NewFuncCodec(conn, encoder, dec.Decode)
}
// Close does nothing and always returns nil.
@@ -300,3 +364,35 @@ func validateRequest(r *http.Request) (int, error) {
err := fmt.Errorf("invalid content type, only %s is supported", contentType)
return http.StatusUnsupportedMediaType, err
}
+
+// ContextRequestTimeout returns the request timeout derived from the given context.
+func ContextRequestTimeout(ctx context.Context) (time.Duration, bool) {
+ timeout := time.Duration(math.MaxInt64)
+ hasTimeout := false
+ setTimeout := func(d time.Duration) {
+ if d < timeout {
+ timeout = d
+ hasTimeout = true
+ }
+ }
+
+ if deadline, ok := ctx.Deadline(); ok {
+ setTimeout(time.Until(deadline))
+ }
+
+ // If the context is an HTTP request context, use the server's WriteTimeout.
+ httpSrv, ok := ctx.Value(http.ServerContextKey).(*http.Server)
+ if ok && httpSrv.WriteTimeout > 0 {
+ wt := httpSrv.WriteTimeout
+ // When a write timeout is configured, we need to send the response message before
+ // the HTTP server cuts connection. So our internal timeout must be earlier than
+ // the server's true timeout.
+ //
+ // Note: Timeouts are sanitized to be a minimum of 1 second.
+ // Also see issue: https://github.com/golang/go/issues/47229
+ wt -= 100 * time.Millisecond
+ setTimeout(wt)
+ }
+
+ return timeout, hasTimeout
+}
diff --git a/rpc/http_test.go b/rpc/http_test.go
index c84d7705f2..528e1bcfc5 100644
--- a/rpc/http_test.go
+++ b/rpc/http_test.go
@@ -17,6 +17,8 @@
package rpc
import (
+ "context"
+ "fmt"
"net/http"
"net/http/httptest"
"strings"
@@ -198,3 +200,43 @@ func TestHTTPPeerInfo(t *testing.T) {
t.Errorf("wrong HTTP.Origin %q", info.HTTP.UserAgent)
}
}
+
+func TestNewContextWithHeaders(t *testing.T) {
+ expectedHeaders := 0
+ server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
+ for i := 0; i < expectedHeaders; i++ {
+ key, want := fmt.Sprintf("key-%d", i), fmt.Sprintf("val-%d", i)
+ if have := request.Header.Get(key); have != want {
+ t.Errorf("wrong request headers for %s, want: %s, have: %s", key, want, have)
+ }
+ }
+ writer.WriteHeader(http.StatusOK)
+ _, _ = writer.Write([]byte(`{}`))
+ }))
+ defer server.Close()
+
+ client, err := Dial(server.URL)
+ if err != nil {
+ t.Fatalf("failed to dial: %s", err)
+ }
+ defer client.Close()
+
+ newHdr := func(k, v string) http.Header {
+ header := http.Header{}
+ header.Set(k, v)
+ return header
+ }
+ ctx1 := NewContextWithHeaders(context.Background(), newHdr("key-0", "val-0"))
+ ctx2 := NewContextWithHeaders(ctx1, newHdr("key-1", "val-1"))
+ ctx3 := NewContextWithHeaders(ctx2, newHdr("key-2", "val-2"))
+
+ expectedHeaders = 3
+ if err := client.CallContext(ctx3, nil, "test"); err != ErrNoResult {
+ t.Error("call failed", err)
+ }
+
+ expectedHeaders = 2
+ if err := client.CallContext(ctx2, nil, "test"); err != ErrNoResult {
+ t.Error("call failed:", err)
+ }
+}
diff --git a/rpc/ipc.go b/rpc/ipc.go
index 07a211c627..d9e0de62e8 100644
--- a/rpc/ipc.go
+++ b/rpc/ipc.go
@@ -46,11 +46,15 @@ func (s *Server) ServeListener(l net.Listener) error {
// The context is used for the initial connection establishment. It does not
// affect subsequent interactions with the client.
func DialIPC(ctx context.Context, endpoint string) (*Client, error) {
- return newClient(ctx, func(ctx context.Context) (ServerCodec, error) {
+ return newClient(ctx, newClientTransportIPC(endpoint))
+}
+
+func newClientTransportIPC(endpoint string) reconnectFunc {
+ return func(ctx context.Context) (ServerCodec, error) {
conn, err := newIPCConnection(ctx, endpoint)
if err != nil {
return nil, err
}
return NewCodec(conn), err
- })
+ }
}
diff --git a/rpc/ipc_unix.go b/rpc/ipc_unix.go
index 249a9cf044..9876347708 100644
--- a/rpc/ipc_unix.go
+++ b/rpc/ipc_unix.go
@@ -31,8 +31,9 @@ import (
// ipcListen will create a Unix socket on the given endpoint.
func ipcListen(endpoint string) (net.Listener, error) {
- if len(endpoint) > int(max_path_size) {
- log.Warn(fmt.Sprintf("The ipc endpoint is longer than %d characters. ", max_path_size),
+ // account for null-terminator too
+ if len(endpoint)+1 > int(max_path_size) {
+ log.Warn(fmt.Sprintf("The ipc endpoint is longer than %d characters. ", max_path_size-1),
"endpoint", endpoint)
}
diff --git a/rpc/json.go b/rpc/json.go
index 6024f1e7dc..8a3b162cab 100644
--- a/rpc/json.go
+++ b/rpc/json.go
@@ -58,21 +58,25 @@ type jsonrpcMessage struct {
}
func (msg *jsonrpcMessage) isNotification() bool {
- return msg.ID == nil && msg.Method != ""
+ return msg.hasValidVersion() && msg.ID == nil && msg.Method != ""
}
func (msg *jsonrpcMessage) isCall() bool {
- return msg.hasValidID() && msg.Method != ""
+ return msg.hasValidVersion() && msg.hasValidID() && msg.Method != ""
}
func (msg *jsonrpcMessage) isResponse() bool {
- return msg.hasValidID() && msg.Method == "" && msg.Params == nil && (msg.Result != nil || msg.Error != nil)
+ return msg.hasValidVersion() && msg.hasValidID() && msg.Method == "" && msg.Params == nil && (msg.Result != nil || msg.Error != nil)
}
func (msg *jsonrpcMessage) hasValidID() bool {
return len(msg.ID) > 0 && msg.ID[0] != '{' && msg.ID[0] != '['
}
+func (msg *jsonrpcMessage) hasValidVersion() bool {
+ return msg.Version == vsn
+}
+
func (msg *jsonrpcMessage) isSubscribe() bool {
return strings.HasSuffix(msg.Method, subscribeMethodSuffix)
}
@@ -100,15 +104,14 @@ func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage {
func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage {
enc, err := json.Marshal(result)
if err != nil {
- // TODO: wrap with 'internal server error'
- return msg.errorResponse(err)
+ return msg.errorResponse(&internalServerError{errcodeMarshalError, err.Error()})
}
return &jsonrpcMessage{Version: vsn, ID: msg.ID, Result: enc}
}
func errorMessage(err error) *jsonrpcMessage {
msg := &jsonrpcMessage{Version: vsn, ID: null, Error: &jsonError{
- Code: defaultErrorCode,
+ Code: errcodeDefault,
Message: err.Error(),
}}
ec, ok := err.(Error)
@@ -165,18 +168,22 @@ type ConnRemoteAddr interface {
// support for parsing arguments and serializing (result) objects.
type jsonCodec struct {
remote string
- closer sync.Once // close closed channel once
- closeCh chan interface{} // closed on Close
- decode func(v interface{}) error // decoder to allow multiple transports
- encMu sync.Mutex // guards the encoder
- encode func(v interface{}) error // encoder to allow multiple transports
+ closer sync.Once // close closed channel once
+ closeCh chan interface{} // closed on Close
+ decode decodeFunc // decoder to allow multiple transports
+ encMu sync.Mutex // guards the encoder
+ encode encodeFunc // encoder to allow multiple transports
conn deadlineCloser
}
+type encodeFunc = func(v interface{}, isErrorResponse bool) error
+
+type decodeFunc = func(v interface{}) error
+
// NewFuncCodec creates a codec which uses the given functions to read and write. If conn
// implements ConnRemoteAddr, log messages will use it to include the remote address of
// the connection.
-func NewFuncCodec(conn deadlineCloser, encode, decode func(v interface{}) error) ServerCodec {
+func NewFuncCodec(conn deadlineCloser, encode encodeFunc, decode decodeFunc) ServerCodec {
codec := &jsonCodec{
closeCh: make(chan interface{}),
encode: encode,
@@ -195,7 +202,11 @@ func NewCodec(conn Conn) ServerCodec {
enc := json.NewEncoder(conn)
dec := json.NewDecoder(conn)
dec.UseNumber()
- return NewFuncCodec(conn, enc.Encode, dec.Decode)
+
+ encode := func(v interface{}, isErrorResponse bool) error {
+ return enc.Encode(v)
+ }
+ return NewFuncCodec(conn, encode, dec.Decode)
}
func (c *jsonCodec) peerInfo() PeerInfo {
@@ -225,7 +236,7 @@ func (c *jsonCodec) readBatch() (messages []*jsonrpcMessage, batch bool, err err
return messages, batch, nil
}
-func (c *jsonCodec) writeJSON(ctx context.Context, v interface{}) error {
+func (c *jsonCodec) writeJSON(ctx context.Context, v interface{}, isErrorResponse bool) error {
c.encMu.Lock()
defer c.encMu.Unlock()
@@ -234,7 +245,7 @@ func (c *jsonCodec) writeJSON(ctx context.Context, v interface{}) error {
deadline = time.Now().Add(defaultWriteTimeout)
}
c.conn.SetWriteDeadline(deadline)
- return c.encode(v)
+ return c.encode(v, isErrorResponse)
}
func (c *jsonCodec) close() {
diff --git a/rpc/server.go b/rpc/server.go
index bf1f71a28e..9c72c26d7b 100644
--- a/rpc/server.go
+++ b/rpc/server.go
@@ -19,9 +19,9 @@ package rpc
import (
"context"
"io"
+ "sync"
"sync/atomic"
- mapset "github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/log"
)
@@ -45,13 +45,19 @@ const (
type Server struct {
services serviceRegistry
idgen func() ID
- run int32
- codecs mapset.Set
+
+ mutex sync.Mutex
+ codecs map[ServerCodec]struct{}
+ run int32
}
// NewServer creates a new server instance with no registered handlers.
func NewServer() *Server {
- server := &Server{idgen: randomIDGenerator(), codecs: mapset.NewSet(), run: 1}
+ server := &Server{
+ idgen: randomIDGenerator(),
+ codecs: make(map[ServerCodec]struct{}),
+ run: 1,
+ }
// Register the default service providing meta information about the RPC service such
// as the services and methods it offers.
rpcService := &RPCService{server}
@@ -75,20 +81,34 @@ func (s *Server) RegisterName(name string, receiver interface{}) error {
func (s *Server) ServeCodec(codec ServerCodec, options CodecOption) {
defer codec.close()
- // Don't serve if server is stopped.
- if atomic.LoadInt32(&s.run) == 0 {
+ if !s.trackCodec(codec) {
return
}
-
- // Add the codec to the set so it can be closed by Stop.
- s.codecs.Add(codec)
- defer s.codecs.Remove(codec)
+ defer s.untrackCodec(codec)
c := initClient(codec, s.idgen, &s.services)
<-codec.closed()
c.Close()
}
+func (s *Server) trackCodec(codec ServerCodec) bool {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ if atomic.LoadInt32(&s.run) == 0 {
+ return false // Don't serve if server is stopped.
+ }
+ s.codecs[codec] = struct{}{}
+ return true
+}
+
+func (s *Server) untrackCodec(codec ServerCodec) {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ delete(s.codecs, codec)
+}
+
// serveSingleRequest reads and processes a single RPC request from the given codec. This
// is used to serve HTTP connections. Subscriptions and reverse calls are not allowed in
// this mode.
@@ -105,7 +125,8 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) {
reqs, batch, err := codec.readBatch()
if err != nil {
if err != io.EOF {
- codec.writeJSON(ctx, errorMessage(&invalidMessageError{"parse error"}))
+ resp := errorMessage(&invalidMessageError{"parse error"})
+ codec.writeJSON(ctx, resp, true)
}
return
}
@@ -120,12 +141,14 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) {
// requests to finish, then closes all codecs which will cancel pending requests and
// subscriptions.
func (s *Server) Stop() {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
if atomic.CompareAndSwapInt32(&s.run, 1, 0) {
log.Debug("RPC server shutting down")
- s.codecs.Each(func(c interface{}) bool {
- c.(ServerCodec).close()
- return true
- })
+ for codec := range s.codecs {
+ codec.close()
+ }
}
}
diff --git a/rpc/server_test.go b/rpc/server_test.go
index d09d31634b..f1a9b3d5cd 100644
--- a/rpc/server_test.go
+++ b/rpc/server_test.go
@@ -45,7 +45,7 @@ func TestServerRegisterName(t *testing.T) {
t.Fatalf("Expected service calc to be registered")
}
- wantCallbacks := 10
+ wantCallbacks := 13
if len(svc.callbacks) != wantCallbacks {
t.Errorf("Expected %d callbacks for service 'service', got %d", wantCallbacks, len(svc.callbacks))
}
diff --git a/rpc/service.go b/rpc/service.go
index bef891ea11..cfdfba023a 100644
--- a/rpc/service.go
+++ b/rpc/service.go
@@ -18,7 +18,6 @@ package rpc
import (
"context"
- "errors"
"fmt"
"reflect"
"runtime"
@@ -199,7 +198,7 @@ func (c *callback) call(ctx context.Context, method string, args []reflect.Value
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.Error("RPC method " + method + " crashed: " + fmt.Sprintf("%v\n%s", err, buf))
- errRes = errors.New("method handler crashed")
+ errRes = &internalServerError{errcodePanic, "method handler crashed"}
}
}()
// Run the callback.
diff --git a/rpc/stdio.go b/rpc/stdio.go
index be2bab1c98..ae32db26ef 100644
--- a/rpc/stdio.go
+++ b/rpc/stdio.go
@@ -32,12 +32,16 @@ func DialStdIO(ctx context.Context) (*Client, error) {
// DialIO creates a client which uses the given IO channels
func DialIO(ctx context.Context, in io.Reader, out io.Writer) (*Client, error) {
- return newClient(ctx, func(_ context.Context) (ServerCodec, error) {
+ return newClient(ctx, newClientTransportIO(in, out))
+}
+
+func newClientTransportIO(in io.Reader, out io.Writer) reconnectFunc {
+ return func(context.Context) (ServerCodec, error) {
return NewCodec(stdioConn{
in: in,
out: out,
}), nil
- })
+ }
}
type stdioConn struct {
diff --git a/rpc/subscription.go b/rpc/subscription.go
index d7ba784fc5..334ead3ace 100644
--- a/rpc/subscription.go
+++ b/rpc/subscription.go
@@ -175,11 +175,13 @@ func (n *Notifier) activate() error {
func (n *Notifier) send(sub *Subscription, data json.RawMessage) error {
params, _ := json.Marshal(&subscriptionResult{ID: string(sub.ID), Result: data})
ctx := context.Background()
- return n.h.conn.writeJSON(ctx, &jsonrpcMessage{
+
+ msg := &jsonrpcMessage{
Version: vsn,
Method: n.namespace + notificationMethodSuffix,
Params: params,
- })
+ }
+ return n.h.conn.writeJSON(ctx, msg, false)
}
// A Subscription is created by a notifier and tied to that notifier. The client can use
diff --git a/rpc/subscription_test.go b/rpc/subscription_test.go
index a920205c00..b270457829 100644
--- a/rpc/subscription_test.go
+++ b/rpc/subscription_test.go
@@ -79,7 +79,7 @@ func TestSubscriptions(t *testing.T) {
request := map[string]interface{}{
"id": i,
"method": fmt.Sprintf("%s_subscribe", namespace),
- "version": "2.0",
+ "jsonrpc": "2.0",
"params": []interface{}{"someSubscription", notificationCount, i},
}
if err := out.Encode(&request); err != nil {
diff --git a/rpc/testdata/internal-error.js b/rpc/testdata/internal-error.js
new file mode 100644
index 0000000000..2ba387401f
--- /dev/null
+++ b/rpc/testdata/internal-error.js
@@ -0,0 +1,7 @@
+// These tests trigger various 'internal error' conditions.
+
+--> {"jsonrpc":"2.0","id":1,"method":"test_marshalError","params": []}
+<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32603,"message":"json: error calling MarshalText for type *rpc.MarshalErrObj: marshal error"}}
+
+--> {"jsonrpc":"2.0","id":2,"method":"test_panic","params": []}
+<-- {"jsonrpc":"2.0","id":2,"error":{"code":-32603,"message":"method handler crashed"}}
diff --git a/rpc/testdata/invalid-badversion.js b/rpc/testdata/invalid-badversion.js
new file mode 100644
index 0000000000..75b5291dc3
--- /dev/null
+++ b/rpc/testdata/invalid-badversion.js
@@ -0,0 +1,19 @@
+// This test checks processing of messages with invalid Version.
+
+--> {"jsonrpc":"2.0","id":1,"method":"test_echo","params":["x", 3]}
+<-- {"jsonrpc":"2.0","id":1,"result":{"String":"x","Int":3,"Args":null}}
+
+--> {"jsonrpc":"2.1","id":1,"method":"test_echo","params":["x", 3]}
+<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}}
+
+--> {"jsonrpc":"go-ethereum","id":1,"method":"test_echo","params":["x", 3]}
+<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}}
+
+--> {"jsonrpc":1,"id":1,"method":"test_echo","params":["x", 3]}
+<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}}
+
+--> {"jsonrpc":2.0,"id":1,"method":"test_echo","params":["x", 3]}
+<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}}
+
+--> {"id":1,"method":"test_echo","params":["x", 3]}
+<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}}
diff --git a/rpc/testservice_test.go b/rpc/testservice_test.go
index 253e263289..eab67f1dd5 100644
--- a/rpc/testservice_test.go
+++ b/rpc/testservice_test.go
@@ -70,8 +70,18 @@ func (testError) Error() string { return "testError" }
func (testError) ErrorCode() int { return 444 }
func (testError) ErrorData() interface{} { return "testError data" }
+type MarshalErrObj struct{}
+
+func (o *MarshalErrObj) MarshalText() ([]byte, error) {
+ return nil, errors.New("marshal error")
+}
+
func (s *testService) NoArgsRets() {}
+func (s *testService) Null() any {
+ return nil
+}
+
func (s *testService) Echo(str string, i int, args *echoArgs) echoResult {
return echoResult{str, i, args}
}
@@ -114,6 +124,14 @@ func (s *testService) ReturnError() error {
return testError{}
}
+func (s *testService) MarshalError() *MarshalErrObj {
+ return &MarshalErrObj{}
+}
+
+func (s *testService) Panic() string {
+ panic("service panic")
+}
+
func (s *testService) CallMeBack(ctx context.Context, method string, args []interface{}) (interface{}, error) {
c, ok := ClientFromContext(ctx)
if !ok {
diff --git a/rpc/types.go b/rpc/types.go
index e3d1a48968..55d11fbaaf 100644
--- a/rpc/types.go
+++ b/rpc/types.go
@@ -51,7 +51,9 @@ type ServerCodec interface {
// jsonWriter can write JSON messages to its underlying connection.
// Implementations must be safe for concurrent use.
type jsonWriter interface {
- writeJSON(context.Context, interface{}) error
+ // writeJSON writes a message to the connection.
+ writeJSON(ctx context.Context, msg interface{}, isError bool) error
+
// Closed returns a channel which is closed when the connection is closed.
closed() <-chan interface{}
// RemoteAddr returns the peer address of the connection.
@@ -69,7 +71,7 @@ const (
)
// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports:
-// - "latest", "earliest" or "pending" as string arguments
+// - "safe", "finalized", "latest", "earliest" or "pending" as string arguments
// - the block number
// Returned errors:
// - an invalid block number error when the given argument isn't a known strings
@@ -110,7 +112,7 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
}
// MarshalText implements encoding.TextMarshaler. It marshals:
-// - "latest", "earliest" or "pending" as strings
+// - "safe", "finalized", "latest", "earliest" or "pending" as strings
// - other numbers as hex
func (bn BlockNumber) MarshalText() ([]byte, error) {
switch bn {
@@ -241,24 +243,3 @@ func BlockNumberOrHashWithHash(hash common.Hash, canonical bool) BlockNumberOrHa
RequireCanonical: canonical,
}
}
-
-// DecimalOrHex unmarshals a non-negative decimal or hex parameter into a uint64.
-type DecimalOrHex uint64
-
-// UnmarshalJSON implements json.Unmarshaler.
-func (dh *DecimalOrHex) UnmarshalJSON(data []byte) error {
- input := strings.TrimSpace(string(data))
- if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
- input = input[1 : len(input)-1]
- }
-
- value, err := strconv.ParseUint(input, 10, 64)
- if err != nil {
- value, err = hexutil.DecodeUint64(input)
- }
- if err != nil {
- return err
- }
- *dh = DecimalOrHex(value)
- return nil
-}
diff --git a/rpc/websocket.go b/rpc/websocket.go
index 28380d8aa4..0ac2a2792d 100644
--- a/rpc/websocket.go
+++ b/rpc/websocket.go
@@ -27,7 +27,7 @@ import (
"sync"
"time"
- mapset "github.com/deckarep/golang-set"
+ mapset "github.com/deckarep/golang-set/v2"
"github.com/ethereum/go-ethereum/log"
"github.com/gorilla/websocket"
)
@@ -35,7 +35,7 @@ import (
const (
wsReadBuffer = 1024
wsWriteBuffer = 1024
- wsPingInterval = 60 * time.Second
+ wsPingInterval = 30 * time.Second
wsPingWriteTimeout = 5 * time.Second
wsPongTimeout = 30 * time.Second
wsMessageSizeLimit = 15 * 1024 * 1024
@@ -69,7 +69,7 @@ func (s *Server) WebsocketHandler(allowedOrigins []string) http.Handler {
// websocket upgrade process. When a '*' is specified as an allowed origins all
// connections are accepted.
func wsHandshakeValidator(allowedOrigins []string) func(*http.Request) bool {
- origins := mapset.NewSet()
+ origins := mapset.NewSet[string]()
allowAllOrigins := false
for _, origin := range allowedOrigins {
@@ -122,10 +122,10 @@ func (e wsHandshakeError) Error() string {
return s
}
-func originIsAllowed(allowedOrigins mapset.Set, browserOrigin string) bool {
+func originIsAllowed(allowedOrigins mapset.Set[string], browserOrigin string) bool {
it := allowedOrigins.Iterator()
for origin := range it.C {
- if ruleAllowsOrigin(origin.(string), browserOrigin) {
+ if ruleAllowsOrigin(origin, browserOrigin) {
return true
}
}
@@ -181,24 +181,23 @@ func parseOriginURL(origin string) (string, string, string, error) {
return scheme, hostname, port, nil
}
-// DialWebsocketWithDialer creates a new RPC client that communicates with a JSON-RPC server
-// that is listening on the given endpoint using the provided dialer.
+// DialWebsocketWithDialer creates a new RPC client using WebSocket.
+//
+// The context is used for the initial connection establishment. It does not
+// affect subsequent interactions with the client.
+//
+// Deprecated: use DialOptions and the WithWebsocketDialer option.
func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, dialer websocket.Dialer) (*Client, error) {
- endpoint, header, err := wsClientHeaders(endpoint, origin)
+ cfg := new(clientConfig)
+ cfg.wsDialer = &dialer
+ if origin != "" {
+ cfg.setHeader("origin", origin)
+ }
+ connect, err := newClientTransportWS(endpoint, cfg)
if err != nil {
return nil, err
}
- return newClient(ctx, func(ctx context.Context) (ServerCodec, error) {
- conn, resp, err := dialer.DialContext(ctx, endpoint, header)
- if err != nil {
- hErr := wsHandshakeError{err: err}
- if resp != nil {
- hErr.status = resp.Status
- }
- return nil, hErr
- }
- return newWebsocketCodec(conn, endpoint, header), nil
- })
+ return newClient(ctx, connect)
}
// DialWebsocket creates a new RPC client that communicates with a JSON-RPC server
@@ -207,12 +206,53 @@ func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, diale
// The context is used for the initial connection establishment. It does not
// affect subsequent interactions with the client.
func DialWebsocket(ctx context.Context, endpoint, origin string) (*Client, error) {
- dialer := websocket.Dialer{
- ReadBufferSize: wsReadBuffer,
- WriteBufferSize: wsWriteBuffer,
- WriteBufferPool: wsBufferPool,
+ cfg := new(clientConfig)
+ if origin != "" {
+ cfg.setHeader("origin", origin)
}
- return DialWebsocketWithDialer(ctx, endpoint, origin, dialer)
+ connect, err := newClientTransportWS(endpoint, cfg)
+ if err != nil {
+ return nil, err
+ }
+ return newClient(ctx, connect)
+}
+
+func newClientTransportWS(endpoint string, cfg *clientConfig) (reconnectFunc, error) {
+ dialer := cfg.wsDialer
+ if dialer == nil {
+ dialer = &websocket.Dialer{
+ ReadBufferSize: wsReadBuffer,
+ WriteBufferSize: wsWriteBuffer,
+ WriteBufferPool: wsBufferPool,
+ }
+ }
+
+ dialURL, header, err := wsClientHeaders(endpoint, "")
+ if err != nil {
+ return nil, err
+ }
+ for key, values := range cfg.httpHeaders {
+ header[key] = values
+ }
+
+ connect := func(ctx context.Context) (ServerCodec, error) {
+ header := header.Clone()
+ if cfg.httpAuth != nil {
+ if err := cfg.httpAuth(header); err != nil {
+ return nil, err
+ }
+ }
+ conn, resp, err := dialer.DialContext(ctx, dialURL, header)
+ if err != nil {
+ hErr := wsHandshakeError{err: err}
+ if resp != nil {
+ hErr.status = resp.Status
+ }
+ return nil, hErr
+ }
+ return newWebsocketCodec(conn, dialURL, header), nil
+ }
+ return connect, nil
}
func wsClientHeaders(endpoint, origin string) (string, http.Header, error) {
@@ -247,8 +287,12 @@ func newWebsocketCodec(conn *websocket.Conn, host string, req http.Header) Serve
conn.SetReadDeadline(time.Time{})
return nil
})
+
+ encode := func(v interface{}, isErrorResponse bool) error {
+ return conn.WriteJSON(v)
+ }
wc := &websocketCodec{
- jsonCodec: NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON).(*jsonCodec),
+ jsonCodec: NewFuncCodec(conn, encode, conn.ReadJSON).(*jsonCodec),
conn: conn,
pingReset: make(chan struct{}, 1),
info: PeerInfo{
@@ -275,8 +319,8 @@ func (wc *websocketCodec) peerInfo() PeerInfo {
return wc.info
}
-func (wc *websocketCodec) writeJSON(ctx context.Context, v interface{}) error {
- err := wc.jsonCodec.writeJSON(ctx, v)
+func (wc *websocketCodec) writeJSON(ctx context.Context, v interface{}, isError bool) error {
+ err := wc.jsonCodec.writeJSON(ctx, v, isError)
if err == nil {
// Notify pingLoop to delay the next idle ping.
select {
diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go
index f74b7fd08b..fb9357605b 100644
--- a/rpc/websocket_test.go
+++ b/rpc/websocket_test.go
@@ -19,14 +19,10 @@ package rpc
import (
"context"
"errors"
- "io"
"net"
"net/http"
"net/http/httptest"
- "net/http/httputil"
- "net/url"
"strings"
- "sync/atomic"
"testing"
"time"
@@ -227,63 +223,6 @@ func TestClientWebsocketLargeMessage(t *testing.T) {
}
}
-func TestClientWebsocketSevered(t *testing.T) {
- t.Parallel()
-
- var (
- server = wsPingTestServer(t, nil)
- ctx = context.Background()
- )
- defer server.Shutdown(ctx)
-
- u, err := url.Parse("http://" + server.Addr)
- if err != nil {
- t.Fatal(err)
- }
- rproxy := httputil.NewSingleHostReverseProxy(u)
- var severable *severableReadWriteCloser
- rproxy.ModifyResponse = func(response *http.Response) error {
- severable = &severableReadWriteCloser{ReadWriteCloser: response.Body.(io.ReadWriteCloser)}
- response.Body = severable
- return nil
- }
- frontendProxy := httptest.NewServer(rproxy)
- defer frontendProxy.Close()
-
- wsURL := "ws:" + strings.TrimPrefix(frontendProxy.URL, "http:")
- client, err := DialWebsocket(ctx, wsURL, "")
- if err != nil {
- t.Fatalf("client dial error: %v", err)
- }
- defer client.Close()
-
- resultChan := make(chan int)
- sub, err := client.EthSubscribe(ctx, resultChan, "foo")
- if err != nil {
- t.Fatalf("client subscribe error: %v", err)
- }
-
- // sever the connection
- severable.Sever()
-
- // Wait for subscription error.
- timeout := time.NewTimer(3 * wsPingInterval)
- defer timeout.Stop()
- for {
- select {
- case err := <-sub.Err():
- t.Log("client subscription error:", err)
- return
- case result := <-resultChan:
- t.Error("unexpected result:", result)
- return
- case <-timeout.C:
- t.Error("didn't get any error within the test timeout")
- return
- }
- }
-}
-
// wsPingTestServer runs a WebSocket server which accepts a single subscription request.
// When a value arrives on sendPing, the server sends a ping frame, waits for a matching
// pong and finally delivers a single subscription result.
@@ -386,31 +325,3 @@ func wsPingTestHandler(t *testing.T, conn *websocket.Conn, shutdown, sendPing <-
}
}
}
-
-// severableReadWriteCloser wraps an io.ReadWriteCloser and provides a Sever() method to drop writes and read empty.
-type severableReadWriteCloser struct {
- io.ReadWriteCloser
- severed int32 // atomic
-}
-
-func (s *severableReadWriteCloser) Sever() {
- atomic.StoreInt32(&s.severed, 1)
-}
-
-func (s *severableReadWriteCloser) Read(p []byte) (n int, err error) {
- if atomic.LoadInt32(&s.severed) > 0 {
- return 0, nil
- }
- return s.ReadWriteCloser.Read(p)
-}
-
-func (s *severableReadWriteCloser) Write(p []byte) (n int, err error) {
- if atomic.LoadInt32(&s.severed) > 0 {
- return len(p), nil
- }
- return s.ReadWriteCloser.Write(p)
-}
-
-func (s *severableReadWriteCloser) Close() error {
- return s.ReadWriteCloser.Close()
-}
diff --git a/signer/core/api.go b/signer/core/api.go
index f10f03d83a..3c1c94801b 100644
--- a/signer/core/api.go
+++ b/signer/core/api.go
@@ -17,6 +17,7 @@
package core
import (
+ "bytes"
"context"
"encoding/json"
"errors"
@@ -31,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/usbwallet"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
@@ -409,7 +411,7 @@ func (api *SignerAPI) List(ctx context.Context) ([]common.Address, error) {
// New creates a new password protected Account. The private key is protected with
// the given password. Users are responsible to backup the private key that is stored
-// in the keystore location thas was specified when this API was created.
+// in the keystore location that was specified when this API was created.
func (api *SignerAPI) New(ctx context.Context) (common.Address, error) {
if be := api.am.Backends(keystore.KeyStoreType); len(be) == 0 {
return common.Address{}, errors.New("password based accounts not supported")
@@ -627,7 +629,26 @@ func (api *SignerAPI) SignGnosisSafeTx(ctx context.Context, signerAddress common
}
}
typedData := gnosisTx.ToTypedData()
+ // might aswell error early.
+ // we are expected to sign. If our calculated hash does not match what they want,
+ // The gnosis safetx input contains a 'safeTxHash' which is the expected safeTxHash that
+ sighash, _, err := apitypes.TypedDataAndHash(typedData)
+ if err != nil {
+ return nil, err
+ }
+ if !bytes.Equal(sighash, gnosisTx.InputExpHash.Bytes()) {
+ // It might be the case that the json is missing chain id.
+ if gnosisTx.ChainId == nil {
+ gnosisTx.ChainId = (*math.HexOrDecimal256)(api.chainID)
+ typedData = gnosisTx.ToTypedData()
+ sighash, _, _ = apitypes.TypedDataAndHash(typedData)
+ if !bytes.Equal(sighash, gnosisTx.InputExpHash.Bytes()) {
+ return nil, fmt.Errorf("mismatched safeTxHash; have %#x want %#x", sighash, gnosisTx.InputExpHash[:])
+ }
+ }
+ }
signature, preimage, err := api.signTypedData(ctx, signerAddress, typedData, msgs)
+
if err != nil {
return nil, err
}
@@ -635,7 +656,7 @@ func (api *SignerAPI) SignGnosisSafeTx(ctx context.Context, signerAddress common
gnosisTx.Signature = signature
gnosisTx.SafeTxHash = common.BytesToHash(preimage)
- gnosisTx.Sender = *checkSummedSender // Must be checksumed to be accepted by relay
+ gnosisTx.Sender = *checkSummedSender // Must be checksummed to be accepted by relay
return &gnosisTx, nil
}
diff --git a/signer/core/api_test.go b/signer/core/api_test.go
index 6fa2af1836..9bb55bddca 100644
--- a/signer/core/api_test.go
+++ b/signer/core/api_test.go
@@ -39,7 +39,7 @@ import (
"github.com/ethereum/go-ethereum/signer/storage"
)
-//Used for testing
+// Used for testing
type headlessUi struct {
approveCh chan string // to send approve/deny
inputCh chan string // to send password
diff --git a/signer/core/apitypes/signed_data_internal_test.go b/signer/core/apitypes/signed_data_internal_test.go
index 121cc00dec..af7fc93ed8 100644
--- a/signer/core/apitypes/signed_data_internal_test.go
+++ b/signer/core/apitypes/signed_data_internal_test.go
@@ -21,7 +21,9 @@ import (
"math/big"
"testing"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
)
func TestBytesPadding(t *testing.T) {
@@ -84,6 +86,55 @@ func TestBytesPadding(t *testing.T) {
}
}
+func TestParseAddress(t *testing.T) {
+ tests := []struct {
+ Input interface{}
+ Output []byte // nil => error
+ }{
+ {
+ Input: [20]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14},
+ Output: common.FromHex("0x0000000000000000000000000102030405060708090A0B0C0D0E0F1011121314"),
+ },
+ {
+ Input: "0x0102030405060708090A0B0C0D0E0F1011121314",
+ Output: common.FromHex("0x0000000000000000000000000102030405060708090A0B0C0D0E0F1011121314"),
+ },
+ {
+ Input: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14},
+ Output: common.FromHex("0x0000000000000000000000000102030405060708090A0B0C0D0E0F1011121314"),
+ },
+ // Various error-cases:
+ {Input: "0x000102030405060708090A0B0C0D0E0F1011121314"}, // too long string
+ {Input: "0x01"}, // too short string
+ {Input: ""},
+ {Input: [32]byte{}}, // too long fixed-size array
+ {Input: [21]byte{}}, // too long fixed-size array
+ {Input: make([]byte, 19)}, // too short slice
+ {Input: make([]byte, 21)}, // too long slice
+ {Input: nil},
+ }
+
+ d := TypedData{}
+ for i, test := range tests {
+ val, err := d.EncodePrimitiveValue("address", test.Input, 1)
+ if test.Output == nil {
+ if err == nil {
+ t.Errorf("test %d: expected error, got no error (result %x)", i, val)
+ }
+ continue
+ }
+ if err != nil {
+ t.Errorf("test %d: expected no error, got %v", i, err)
+ }
+ if have, want := len(val), 32; have != want {
+ t.Errorf("test %d: have len %d, want %d", i, have, want)
+ }
+ if !bytes.Equal(val, test.Output) {
+ t.Errorf("test %d: want %x, have %x", i, test.Output, val)
+ }
+ }
+}
+
func TestParseBytes(t *testing.T) {
for i, tt := range []struct {
v interface{}
@@ -98,6 +149,9 @@ func TestParseBytes(t *testing.T) {
{"not a hex string", nil},
{15, nil},
{nil, nil},
+ {[2]byte{12, 34}, []byte{12, 34}},
+ {[8]byte{12, 34, 56, 78, 90, 12, 34, 56}, []byte{12, 34, 56, 78, 90, 12, 34, 56}},
+ {[16]byte{12, 34, 56, 78, 90, 12, 34, 56, 12, 34, 56, 78, 90, 12, 34, 56}, []byte{12, 34, 56, 78, 90, 12, 34, 56, 12, 34, 56, 78, 90, 12, 34, 56}},
} {
out, ok := parseBytes(tt.v)
if tt.exp == nil {
@@ -123,6 +177,7 @@ func TestParseInteger(t *testing.T) {
}{
{"uint32", "-123", nil},
{"int32", "-123", big.NewInt(-123)},
+ {"int32", big.NewInt(-124), big.NewInt(-124)},
{"uint32", "0xff", big.NewInt(0xff)},
{"int8", "0xffff", nil},
} {
@@ -143,3 +198,38 @@ func TestParseInteger(t *testing.T) {
}
}
}
+
+func TestConvertStringDataToSlice(t *testing.T) {
+ slice := []string{"a", "b", "c"}
+ var it interface{} = slice
+ _, err := convertDataToSlice(it)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestConvertUint256DataToSlice(t *testing.T) {
+ slice := []*math.HexOrDecimal256{
+ math.NewHexOrDecimal256(1),
+ math.NewHexOrDecimal256(2),
+ math.NewHexOrDecimal256(3),
+ }
+ var it interface{} = slice
+ _, err := convertDataToSlice(it)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestConvertAddressDataToSlice(t *testing.T) {
+ slice := []common.Address{
+ common.HexToAddress("0x0000000000000000000000000000000000000001"),
+ common.HexToAddress("0x0000000000000000000000000000000000000002"),
+ common.HexToAddress("0x0000000000000000000000000000000000000003"),
+ }
+ var it interface{} = slice
+ _, err := convertDataToSlice(it)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go
index 0652108f88..c72cad5939 100644
--- a/signer/core/apitypes/types.go
+++ b/signer/core/apitypes/types.go
@@ -27,8 +27,6 @@ import (
"sort"
"strconv"
"strings"
- "unicode"
- "unicode/utf8"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
@@ -38,7 +36,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
)
-var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`)
+var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Za-z](\w*)(\[\])?$`)
type ValidationInfo struct {
Typ string `json:"type"`
@@ -64,7 +62,7 @@ func (vs *ValidationMessages) Info(msg string) {
vs.Messages = append(vs.Messages, ValidationInfo{INFO, msg})
}
-/// getWarnings returns an error with all messages of type WARN of above, or nil if no warnings were present
+// getWarnings returns an error with all messages of type WARN of above, or nil if no warnings were present
func (v *ValidationMessages) GetWarnings() error {
var messages []string
for _, msg := range v.Messages {
@@ -224,15 +222,6 @@ func (t *Type) typeName() string {
return t.Type
}
-func (t *Type) isReferenceType() bool {
- if len(t.Type) == 0 {
- return false
- }
- // Reference types must have a leading uppercase character
- r, _ := utf8.DecodeRuneInString(t.Type)
- return unicode.IsUpper(r)
-}
-
type Types map[string][]Type
type TypePriority struct {
@@ -367,8 +356,8 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter
encType := field.Type
encValue := data[field.Name]
if encType[len(encType)-1:] == "]" {
- arrayValue, ok := encValue.([]interface{})
- if !ok {
+ arrayValue, err := convertDataToSlice(encValue)
+ if err != nil {
return nil, dataMismatchError(encType, encValue)
}
@@ -418,6 +407,14 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter
// Attempt to parse bytes in different formats: byte array, hex string, hexutil.Bytes.
func parseBytes(encType interface{}) ([]byte, bool) {
+ // Handle array types.
+ val := reflect.ValueOf(encType)
+ if val.Kind() == reflect.Array && val.Type().Elem().Kind() == reflect.Uint8 {
+ v := reflect.MakeSlice(reflect.TypeOf([]byte{}), val.Len(), val.Len())
+ reflect.Copy(v, val)
+ return v.Bytes(), true
+ }
+
switch v := encType.(type) {
case []byte:
return v, true
@@ -458,6 +455,8 @@ func parseInteger(encType string, encValue interface{}) (*big.Int, error) {
switch v := encValue.(type) {
case *math.HexOrDecimal256:
b = (*big.Int)(v)
+ case *big.Int:
+ b = v
case string:
var hexIntValue math.HexOrDecimal256
if err := hexIntValue.UnmarshalText([]byte(v)); err != nil {
@@ -490,13 +489,23 @@ func parseInteger(encType string, encValue interface{}) (*big.Int, error) {
func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interface{}, depth int) ([]byte, error) {
switch encType {
case "address":
- stringValue, ok := encValue.(string)
- if !ok || !common.IsHexAddress(stringValue) {
- return nil, dataMismatchError(encType, encValue)
- }
retval := make([]byte, 32)
- copy(retval[12:], common.HexToAddress(stringValue).Bytes())
- return retval, nil
+ switch val := encValue.(type) {
+ case string:
+ if common.IsHexAddress(val) {
+ copy(retval[12:], common.HexToAddress(val).Bytes())
+ return retval, nil
+ }
+ case []byte:
+ if len(val) == 20 {
+ copy(retval[12:], val)
+ return retval, nil
+ }
+ case [20]byte:
+ copy(retval[12:], val[:])
+ return retval, nil
+ }
+ return nil, dataMismatchError(encType, encValue)
case "bool":
boolValue, ok := encValue.(bool)
if !ok {
@@ -553,6 +562,19 @@ func dataMismatchError(encType string, encValue interface{}) error {
return fmt.Errorf("provided data '%v' doesn't match type '%s'", encValue, encType)
}
+func convertDataToSlice(encValue interface{}) ([]interface{}, error) {
+ var outEncValue []interface{}
+ rv := reflect.ValueOf(encValue)
+ if rv.Kind() == reflect.Slice {
+ for i := 0; i < rv.Len(); i++ {
+ outEncValue = append(outEncValue, rv.Index(i).Interface())
+ }
+ } else {
+ return outEncValue, fmt.Errorf("provided data '%v' is not slice", encValue)
+ }
+ return outEncValue, nil
+}
+
// validate makes sure the types are sound
func (typedData *TypedData) validate() error {
if err := typedData.Types.validate(); err != nil {
@@ -612,7 +634,7 @@ func (typedData *TypedData) formatData(primaryType string, data map[string]inter
Typ: field.Type,
}
if field.isArray() {
- arrayValue, _ := encValue.([]interface{})
+ arrayValue, _ := convertDataToSlice(encValue)
parsedType := field.typeName()
for _, v := range arrayValue {
if typedData.Types[parsedType] != nil {
@@ -698,15 +720,15 @@ func (t Types) validate() error {
if typeKey == typeObj.Type {
return fmt.Errorf("type %q cannot reference itself", typeObj.Type)
}
- if typeObj.isReferenceType() {
- if _, exist := t[typeObj.typeName()]; !exist {
- return fmt.Errorf("reference type %q is undefined", typeObj.Type)
- }
- if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) {
- return fmt.Errorf("unknown reference type %q", typeObj.Type)
- }
- } else if !isPrimitiveTypeValid(typeObj.Type) {
- return fmt.Errorf("unknown type %q", typeObj.Type)
+ if isPrimitiveTypeValid(typeObj.Type) {
+ continue
+ }
+ // Must be reference type
+ if _, exist := t[typeObj.typeName()]; !exist {
+ return fmt.Errorf("reference type %q is undefined", typeObj.Type)
+ }
+ if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) {
+ return fmt.Errorf("unknown reference type %q", typeObj.Type)
}
}
}
diff --git a/signer/core/cliui.go b/signer/core/cliui.go
index 187eb1390a..b1bd3206ed 100644
--- a/signer/core/cliui.go
+++ b/signer/core/cliui.go
@@ -18,6 +18,7 @@ package core
import (
"bufio"
+ "context"
"encoding/json"
"fmt"
"os"
@@ -31,8 +32,9 @@ import (
)
type CommandlineUI struct {
- in *bufio.Reader
- mu sync.Mutex
+ in *bufio.Reader
+ mu sync.Mutex
+ api *UIServerAPI
}
func NewCommandlineUI() *CommandlineUI {
@@ -40,7 +42,7 @@ func NewCommandlineUI() *CommandlineUI {
}
func (ui *CommandlineUI) RegisterUIServer(api *UIServerAPI) {
- // noop
+ ui.api = api
}
// readString reads a single line from stdin, trimming if from spaces, enforcing
@@ -241,9 +243,33 @@ func (ui *CommandlineUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
}
}
+func (ui *CommandlineUI) showAccounts() {
+ accounts, err := ui.api.ListAccounts(context.Background())
+ if err != nil {
+ log.Error("Error listing accounts", "err", err)
+ return
+ }
+ if len(accounts) == 0 {
+ fmt.Print("No accounts found\n")
+ return
+ }
+ var msg string
+ var out = new(strings.Builder)
+ if limit := 20; len(accounts) > limit {
+ msg = fmt.Sprintf("\nFirst %d accounts listed (%d more available).\n", limit, len(accounts)-limit)
+ accounts = accounts[:limit]
+ }
+ fmt.Fprint(out, "\n------- Available accounts -------\n")
+ for i, account := range accounts {
+ fmt.Fprintf(out, "%d. %s at %s\n", i, account.Address, account.URL)
+ }
+ fmt.Print(out.String(), msg)
+}
+
func (ui *CommandlineUI) OnSignerStartup(info StartupInfo) {
- fmt.Printf("------- Signer info -------\n")
+ fmt.Print("\n------- Signer info -------\n")
for k, v := range info.Info {
fmt.Printf("* %v : %v\n", k, v)
}
+ go ui.showAccounts()
}
diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go
index c0da22e626..8ee572f53e 100644
--- a/signer/core/signed_data.go
+++ b/signer/core/signed_data.go
@@ -18,6 +18,7 @@ package core
import (
"context"
+ "encoding/json"
"errors"
"fmt"
"mime"
@@ -135,11 +136,7 @@ func (api *SignerAPI) determineSignatureFormat(ctx context.Context, contentType
req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash}
case apitypes.ApplicationClique.Mime:
// Clique is the Ethereum PoA standard
- stringData, ok := data.(string)
- if !ok {
- return nil, useEthereumV, fmt.Errorf("input for %v must be an hex-encoded string", apitypes.ApplicationClique.Mime)
- }
- cliqueData, err := hexutil.Decode(stringData)
+ cliqueData, err := fromHex(data)
if err != nil {
return nil, useEthereumV, err
}
@@ -167,27 +164,30 @@ func (api *SignerAPI) determineSignatureFormat(ctx context.Context, contentType
// Clique uses V on the form 0 or 1
useEthereumV = false
req = &SignDataRequest{ContentType: mediaType, Rawdata: cliqueRlp, Messages: messages, Hash: sighash}
+ case apitypes.DataTyped.Mime:
+ // EIP-712 conformant typed data
+ var err error
+ req, err = typedDataRequest(data)
+ if err != nil {
+ return nil, useEthereumV, err
+ }
default: // also case TextPlain.Mime:
// Calculates an Ethereum ECDSA signature for:
// hash = keccak256("\x19Ethereum Signed Message:\n${message length}${message}")
- // We expect it to be a string
- if stringData, ok := data.(string); !ok {
- return nil, useEthereumV, fmt.Errorf("input for text/plain must be an hex-encoded string")
- } else {
- if textData, err := hexutil.Decode(stringData); err != nil {
- return nil, useEthereumV, err
- } else {
- sighash, msg := accounts.TextAndHash(textData)
- messages := []*apitypes.NameValueType{
- {
- Name: "message",
- Typ: accounts.MimetypeTextPlain,
- Value: msg,
- },
- }
- req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash}
- }
+ // We expect input to be a hex-encoded string
+ textData, err := fromHex(data)
+ if err != nil {
+ return nil, useEthereumV, err
}
+ sighash, msg := accounts.TextAndHash(textData)
+ messages := []*apitypes.NameValueType{
+ {
+ Name: "message",
+ Typ: accounts.MimetypeTextPlain,
+ Value: msg,
+ },
+ }
+ req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash}
}
req.Address = addr
req.Meta = MetadataFromContext(ctx)
@@ -233,20 +233,12 @@ func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAd
// - the signature preimage (hash)
func (api *SignerAPI) signTypedData(ctx context.Context, addr common.MixedcaseAddress,
typedData apitypes.TypedData, validationMessages *apitypes.ValidationMessages) (hexutil.Bytes, hexutil.Bytes, error) {
- sighash, rawData, err := apitypes.TypedDataAndHash(typedData)
+ req, err := typedDataRequest(typedData)
if err != nil {
return nil, nil, err
}
- messages, err := typedData.Format()
- if err != nil {
- return nil, nil, err
- }
- req := &SignDataRequest{
- ContentType: apitypes.DataTyped.Mime,
- Rawdata: []byte(rawData),
- Messages: messages,
- Hash: sighash,
- Address: addr}
+ req.Address = addr
+ req.Meta = MetadataFromContext(ctx)
if validationMessages != nil {
req.Callinfo = validationMessages.Messages
}
@@ -255,7 +247,46 @@ func (api *SignerAPI) signTypedData(ctx context.Context, addr common.MixedcaseAd
api.UI.ShowError(err.Error())
return nil, nil, err
}
- return signature, sighash, nil
+ return signature, req.Hash, nil
+}
+
+// fromHex tries to interpret the data as type string, and convert from
+// hexadecimal to []byte
+func fromHex(data any) ([]byte, error) {
+ if stringData, ok := data.(string); ok {
+ binary, err := hexutil.Decode(stringData)
+ return binary, err
+ }
+ return nil, fmt.Errorf("wrong type %T", data)
+}
+
+// typeDataRequest tries to convert the data into a SignDataRequest.
+func typedDataRequest(data any) (*SignDataRequest, error) {
+ var typedData apitypes.TypedData
+ if td, ok := data.(apitypes.TypedData); ok {
+ typedData = td
+ } else { // Hex-encoded data
+ jsonData, err := fromHex(data)
+ if err != nil {
+ return nil, err
+ }
+ if err = json.Unmarshal(jsonData, &typedData); err != nil {
+ return nil, err
+ }
+ }
+ messages, err := typedData.Format()
+ if err != nil {
+ return nil, err
+ }
+ sighash, rawData, err := apitypes.TypedDataAndHash(typedData)
+ if err != nil {
+ return nil, err
+ }
+ return &SignDataRequest{
+ ContentType: apitypes.DataTyped.Mime,
+ Rawdata: []byte(rawData),
+ Messages: messages,
+ Hash: sighash}, nil
}
// EcRecover recovers the address associated with the given sig.
@@ -293,30 +324,20 @@ func UnmarshalValidatorData(data interface{}) (apitypes.ValidatorData, error) {
if !ok {
return apitypes.ValidatorData{}, errors.New("validator input is not a map[string]interface{}")
}
- addr, ok := raw["address"].(string)
- if !ok {
- return apitypes.ValidatorData{}, errors.New("validator address is not sent as a string")
- }
- addrBytes, err := hexutil.Decode(addr)
+ addrBytes, err := fromHex(raw["address"])
if err != nil {
- return apitypes.ValidatorData{}, err
+ return apitypes.ValidatorData{}, fmt.Errorf("validator address error: %w", err)
}
- if !ok || len(addrBytes) == 0 {
+ if len(addrBytes) == 0 {
return apitypes.ValidatorData{}, errors.New("validator address is undefined")
}
-
- message, ok := raw["message"].(string)
- if !ok {
- return apitypes.ValidatorData{}, errors.New("message is not sent as a string")
- }
- messageBytes, err := hexutil.Decode(message)
+ messageBytes, err := fromHex(raw["message"])
if err != nil {
- return apitypes.ValidatorData{}, err
+ return apitypes.ValidatorData{}, fmt.Errorf("message error: %w", err)
}
- if !ok || len(messageBytes) == 0 {
+ if len(messageBytes) == 0 {
return apitypes.ValidatorData{}, errors.New("message is undefined")
}
-
return apitypes.ValidatorData{
Address: common.BytesToAddress(addrBytes),
Message: messageBytes,
diff --git a/signer/core/signed_data_test.go b/signer/core/signed_data_test.go
index 7d5661e7e6..3e3837cae2 100644
--- a/signer/core/signed_data_test.go
+++ b/signer/core/signed_data_test.go
@@ -21,6 +21,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "math/big"
"os"
"path"
"strings"
@@ -220,15 +221,29 @@ func TestSignData(t *testing.T) {
if signature == nil || len(signature) != 65 {
t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
}
- // data/typed
+ // data/typed via SignTypeData
control.approveCh <- "Y"
control.inputCh <- "a_long_password"
- signature, err = api.SignTypedData(context.Background(), a, typedData)
- if err != nil {
+ var want []byte
+ if signature, err = api.SignTypedData(context.Background(), a, typedData); err != nil {
t.Fatal(err)
+ } else if signature == nil || len(signature) != 65 {
+ t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
+ } else {
+ want = signature
}
- if signature == nil || len(signature) != 65 {
+
+ // data/typed via SignData / mimetype typed data
+ control.approveCh <- "Y"
+ control.inputCh <- "a_long_password"
+ if typedDataJson, err := json.Marshal(typedData); err != nil {
+ t.Fatal(err)
+ } else if signature, err = api.SignData(context.Background(), apitypes.DataTyped.Mime, a, hexutil.Encode(typedDataJson)); err != nil {
+ t.Fatal(err)
+ } else if signature == nil || len(signature) != 65 {
t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
+ } else if have := signature; !bytes.Equal(have, want) {
+ t.Fatalf("want %x, have %x", want, have)
}
}
@@ -812,3 +827,174 @@ func TestComplexTypedData(t *testing.T) {
t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash)
}
}
+
+func TestGnosisSafe(t *testing.T) {
+ // json missing chain id
+ js := "{\n \"safe\": \"0x899FcB1437DE65DC6315f5a69C017dd3F2837557\",\n \"to\": \"0x899FcB1437DE65DC6315f5a69C017dd3F2837557\",\n \"value\": \"0\",\n \"data\": \"0x0d582f13000000000000000000000000d3ed2b8756b942c98c851722f3bd507a17b4745f0000000000000000000000000000000000000000000000000000000000000005\",\n \"operation\": 0,\n \"gasToken\": \"0x0000000000000000000000000000000000000000\",\n \"safeTxGas\": 0,\n \"baseGas\": 0,\n \"gasPrice\": \"0\",\n \"refundReceiver\": \"0x0000000000000000000000000000000000000000\",\n \"nonce\": 0,\n \"executionDate\": null,\n \"submissionDate\": \"2022-02-23T14:09:00.018475Z\",\n \"modified\": \"2022-12-01T15:52:21.214357Z\",\n \"blockNumber\": null,\n \"transactionHash\": null,\n \"safeTxHash\": \"0x6f0f5cffee69087c9d2471e477a63cab2ae171cf433e754315d558d8836274f4\",\n \"executor\": null,\n \"isExecuted\": false,\n \"isSuccessful\": null,\n \"ethGasPrice\": null,\n \"maxFeePerGas\": null,\n \"maxPriorityFeePerGas\": null,\n \"gasUsed\": null,\n \"fee\": null,\n \"origin\": \"https://gnosis-safe.io\",\n \"dataDecoded\": {\n \"method\": \"addOwnerWithThreshold\",\n \"parameters\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"value\": \"0xD3Ed2b8756b942c98c851722F3bd507a17B4745F\"\n },\n {\n \"name\": \"_threshold\",\n \"type\": \"uint256\",\n \"value\": \"5\"\n }\n ]\n },\n \"confirmationsRequired\": 4,\n \"confirmations\": [\n {\n \"owner\": \"0x30B714E065B879F5c042A75Bb40a220A0BE27966\",\n \"submissionDate\": \"2022-03-01T14:56:22Z\",\n \"transactionHash\": \"0x6d0a9c83ac7578ef3be1f2afce089fb83b619583dfa779b82f4422fd64ff3ee9\",\n \"signature\": \"0x00000000000000000000000030b714e065b879f5c042a75bb40a220a0be27966000000000000000000000000000000000000000000000000000000000000000001\",\n \"signatureType\": \"APPROVED_HASH\"\n },\n {\n \"owner\": \"0x8300dFEa25Da0eb744fC0D98c23283F86AB8c10C\",\n \"submissionDate\": \"2022-12-01T15:52:21.214357Z\",\n \"transactionHash\": null,\n \"signature\": \"0xbce73de4cc6ee208e933a93c794dcb8ba1810f9848d1eec416b7be4dae9854c07dbf1720e60bbd310d2159197a380c941cfdb55b3ce58f9dd69efd395d7bef881b\",\n \"signatureType\": \"EOA\"\n }\n ],\n \"trusted\": true,\n \"signatures\": null\n}\n"
+ var gnosisTx core.GnosisSafeTx
+ if err := json.Unmarshal([]byte(js), &gnosisTx); err != nil {
+ t.Fatal(err)
+ }
+ sighash, _, err := apitypes.TypedDataAndHash(gnosisTx.ToTypedData())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if bytes.Equal(sighash, gnosisTx.InputExpHash.Bytes()) {
+ t.Fatal("expected inequality")
+ }
+ gnosisTx.ChainId = (*math.HexOrDecimal256)(big.NewInt(1))
+ sighash, _, _ = apitypes.TypedDataAndHash(gnosisTx.ToTypedData())
+ if !bytes.Equal(sighash, gnosisTx.InputExpHash.Bytes()) {
+ t.Fatal("expected equality")
+ }
+}
+
+var complexTypedDataLCRefType = `
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ }
+ ],
+ "Action": [
+ {
+ "name": "action",
+ "type": "string"
+ },
+ {
+ "name": "params",
+ "type": "string"
+ }
+ ],
+ "cCell": [
+ {
+ "name": "capacity",
+ "type": "string"
+ },
+ {
+ "name": "lock",
+ "type": "string"
+ },
+ {
+ "name": "type",
+ "type": "string"
+ },
+ {
+ "name": "data",
+ "type": "string"
+ },
+ {
+ "name": "extraData",
+ "type": "string"
+ }
+ ],
+ "Transaction": [
+ {
+ "name": "DAS_MESSAGE",
+ "type": "string"
+ },
+ {
+ "name": "inputsCapacity",
+ "type": "string"
+ },
+ {
+ "name": "outputsCapacity",
+ "type": "string"
+ },
+ {
+ "name": "fee",
+ "type": "string"
+ },
+ {
+ "name": "action",
+ "type": "Action"
+ },
+ {
+ "name": "inputs",
+ "type": "cCell[]"
+ },
+ {
+ "name": "outputs",
+ "type": "cCell[]"
+ },
+ {
+ "name": "digest",
+ "type": "bytes32"
+ }
+ ]
+ },
+ "primaryType": "Transaction",
+ "domain": {
+ "chainId": "56",
+ "name": "da.systems",
+ "verifyingContract": "0x0000000000000000000000000000000020210722",
+ "version": "1"
+ },
+ "message": {
+ "DAS_MESSAGE": "SELL mobcion.bit FOR 100000 CKB",
+ "inputsCapacity": "1216.9999 CKB",
+ "outputsCapacity": "1216.9998 CKB",
+ "fee": "0.0001 CKB",
+ "digest": "0x53a6c0f19ec281604607f5d6817e442082ad1882bef0df64d84d3810dae561eb",
+ "action": {
+ "action": "start_account_sale",
+ "params": "0x00"
+ },
+ "inputs": [
+ {
+ "capacity": "218 CKB",
+ "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...",
+ "type": "account-cell-type,0x01,0x",
+ "data": "{ account: mobcion.bit, expired_at: 1670913958 }",
+ "extraData": "{ status: 0, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }"
+ }
+ ],
+ "outputs": [
+ {
+ "capacity": "218 CKB",
+ "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...",
+ "type": "account-cell-type,0x01,0x",
+ "data": "{ account: mobcion.bit, expired_at: 1670913958 }",
+ "extraData": "{ status: 1, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }"
+ },
+ {
+ "capacity": "201 CKB",
+ "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...",
+ "type": "account-sale-cell-type,0x01,0x",
+ "data": "0x1209460ef3cb5f1c68ed2c43a3e020eec2d9de6e...",
+ "extraData": ""
+ }
+ ]
+ }
+}
+`
+
+func TestComplexTypedDataWithLowercaseReftype(t *testing.T) {
+ var td apitypes.TypedData
+ err := json.Unmarshal([]byte(complexTypedDataLCRefType), &td)
+ if err != nil {
+ t.Fatalf("unmarshalling failed '%v'", err)
+ }
+ _, sighash, err := sign(td)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expSigHash := common.FromHex("0x49191f910874f0148597204d9076af128d4694a7c4b714f1ccff330b87207bff")
+ if !bytes.Equal(expSigHash, sighash) {
+ t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash)
+ }
+}
diff --git a/swarm/README.md b/swarm/README.md
index 2658831813..41b6aa9100 100644
--- a/swarm/README.md
+++ b/swarm/README.md
@@ -1,7 +1,7 @@
# Swarm
-https://swarm.ethereum.org
+https://www.ethswarm.org/
Swarm is a distributed storage platform and content distribution service, a native base layer service of the ethereum web3 stack. The primary objective of Swarm is to provide a decentralized and redundant store for dapp code and data as well as block chain and state data. Swarm is also set out to provide various base layer services for web3, including node-to-node messaging, media streaming, decentralised database services and scalable state-channel infrastructure for decentralised service economies.
-**Note**: The codebase has been moved to [ethersphere/swarm](https://github.com/ethersphere/swarm)
+**Note**: The codebase has been moved to [ethersphere/bee](https://github.com/ethersphere/bee)
diff --git a/tests/block_test.go b/tests/block_test.go
index 74c7ed8197..9e72f7f971 100644
--- a/tests/block_test.go
+++ b/tests/block_test.go
@@ -46,6 +46,7 @@ func TestBlockchain(t *testing.T) {
// test takes a lot for time and goes easily OOM because of sha3 calculation on a huge range,
// using 4.6 TGas
bt.skipLoad(`.*randomStatetest94.json.*`)
+
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
if err := bt.checkFailure(t, test.Run(false)); err != nil {
t.Errorf("test without snapshotter failed: %v", err)
diff --git a/tests/block_test_util.go b/tests/block_test_util.go
index 76f0b880b4..8da95a640a 100644
--- a/tests/block_test_util.go
+++ b/tests/block_test_util.go
@@ -24,11 +24,13 @@ import (
"fmt"
"math/big"
"os"
+ "reflect"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus"
+ "github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -86,6 +88,7 @@ type btHeader struct {
GasUsed uint64
Timestamp uint64
BaseFeePerGas *big.Int
+ WithdrawalsRoot *common.Hash
}
type btHeaderMarshaling struct {
@@ -106,10 +109,8 @@ func (t *BlockTest) Run(snapshotter bool) error {
// import pre accounts & construct test genesis block & state root
db := rawdb.NewMemoryDatabase()
- gblock, err := t.genesis(config).Commit(db)
- if err != nil {
- return err
- }
+ gspec := t.genesis(config)
+ gblock := gspec.MustCommit(db)
if gblock.Hash() != t.json.Genesis.Hash {
return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6])
}
@@ -122,12 +123,15 @@ func (t *BlockTest) Run(snapshotter bool) error {
} else {
engine = ethash.NewShared()
}
+ // Wrap the original engine within the beacon-engine
+ engine = beacon.New(engine)
+
cache := &core.CacheConfig{TrieCleanLimit: 0}
if snapshotter {
cache.SnapshotLimit = 1
cache.SnapshotWait = true
}
- chain, err := core.NewBlockChain(db, cache, config, engine, vm.Config{}, nil, nil)
+ chain, err := core.NewBlockChain(db, cache, gspec, nil, engine, vm.Config{}, nil, nil)
if err != nil {
return err
}
@@ -174,17 +178,18 @@ func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
}
}
-/* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
+/*
+See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
- Whether a block is valid or not is a bit subtle, it's defined by presence of
- blockHeader, transactions and uncleHeaders fields. If they are missing, the block is
- invalid and we must verify that we do not accept it.
+ Whether a block is valid or not is a bit subtle, it's defined by presence of
+ blockHeader, transactions and uncleHeaders fields. If they are missing, the block is
+ invalid and we must verify that we do not accept it.
- Since some tests mix valid and invalid blocks we need to check this for every block.
+ Since some tests mix valid and invalid blocks we need to check this for every block.
- If a block is invalid it does not necessarily fail the test, if it's invalidness is
- expected we are expected to ignore it and continue processing and then validate the
- post state.
+ If a block is invalid it does not necessarily fail the test, if it's invalidness is
+ expected we are expected to ignore it and continue processing and then validate the
+ post state.
*/
func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
validBlocks := make([]btBlock, 0)
@@ -272,6 +277,12 @@ func validateHeader(h *btHeader, h2 *types.Header) error {
if h.Timestamp != h2.Time {
return fmt.Errorf("timestamp: want: %v have: %v", h.Timestamp, h2.Time)
}
+ if !reflect.DeepEqual(h.BaseFeePerGas, h2.BaseFee) {
+ return fmt.Errorf("baseFeePerGas: want: %v have: %v", h.BaseFeePerGas, h2.BaseFee)
+ }
+ if !reflect.DeepEqual(h.WithdrawalsRoot, h2.WithdrawalsHash) {
+ return fmt.Errorf("withdrawalsRoot: want: %v have: %v", h.WithdrawalsRoot, h2.WithdrawalsHash)
+ }
return nil
}
diff --git a/tests/difficulty_test.go b/tests/difficulty_test.go
index 8239850b76..a8273303e3 100644
--- a/tests/difficulty_test.go
+++ b/tests/difficulty_test.go
@@ -36,6 +36,26 @@ var (
EIP158Block: big.NewInt(2675000),
ByzantiumBlock: big.NewInt(4370000),
}
+
+ ropstenChainConfig = params.ChainConfig{
+ ChainID: big.NewInt(3),
+ HomesteadBlock: big.NewInt(0),
+ DAOForkBlock: nil,
+ DAOForkSupport: true,
+ EIP150Block: big.NewInt(0),
+ EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"),
+ EIP155Block: big.NewInt(10),
+ EIP158Block: big.NewInt(10),
+ ByzantiumBlock: big.NewInt(1_700_000),
+ ConstantinopleBlock: big.NewInt(4_230_000),
+ PetersburgBlock: big.NewInt(4_939_394),
+ IstanbulBlock: big.NewInt(6_485_846),
+ MuirGlacierBlock: big.NewInt(7_117_117),
+ BerlinBlock: big.NewInt(9_812_189),
+ LondonBlock: big.NewInt(10_499_401),
+ TerminalTotalDifficulty: new(big.Int).SetUint64(50_000_000_000_000_000),
+ TerminalTotalDifficultyPassed: true,
+ }
)
func TestDifficulty(t *testing.T) {
@@ -52,11 +72,8 @@ func TestDifficulty(t *testing.T) {
// files are 2 years old, contains strange values
dt.skipLoad("difficultyCustomHomestead\\.json")
- dt.skipLoad("difficultyMorden\\.json")
- dt.skipLoad("difficultyOlimpic\\.json")
- dt.config("Ropsten", *params.RopstenChainConfig)
- dt.config("Morden", *params.RopstenChainConfig)
+ dt.config("Ropsten", ropstenChainConfig)
dt.config("Frontier", params.ChainConfig{})
dt.config("Homestead", params.ChainConfig{
@@ -67,7 +84,7 @@ func TestDifficulty(t *testing.T) {
ByzantiumBlock: big.NewInt(0),
})
- dt.config("Frontier", *params.RopstenChainConfig)
+ dt.config("Frontier", ropstenChainConfig)
dt.config("MainNetwork", mainnetChainConfig)
dt.config("CustomMainNetwork", mainnetChainConfig)
dt.config("Constantinople", params.ChainConfig{
diff --git a/tests/evm-benchmarks b/tests/evm-benchmarks
index 849b3e239a..d8b88f4046 160000
--- a/tests/evm-benchmarks
+++ b/tests/evm-benchmarks
@@ -1 +1 @@
-Subproject commit 849b3e239a28f236dc99574b2e10e0c720895105
+Subproject commit d8b88f4046a87d6b902378cef752591f95427b43
diff --git a/tests/fuzzers/bls12381/bls12381_fuzz.go b/tests/fuzzers/bls12381/bls12381_fuzz.go
index a5b4b9e798..ced87dd41a 100644
--- a/tests/fuzzers/bls12381/bls12381_fuzz.go
+++ b/tests/fuzzers/bls12381/bls12381_fuzz.go
@@ -26,6 +26,7 @@ import (
"io"
"math/big"
+ "github.com/consensys/gnark-crypto/ecc"
gnark "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
@@ -198,7 +199,7 @@ func FuzzCrossG1MultiExp(data []byte) int {
}
gethScalars = append(gethScalars, s)
var gnarkScalar = &fr.Element{}
- gnarkScalar = gnarkScalar.SetBigInt(s).FromMont()
+ gnarkScalar = gnarkScalar.SetBigInt(s)
gnarkScalars = append(gnarkScalars, *gnarkScalar)
gethPoints = append(gethPoints, new(bls12381.PointG1).Set(kp1))
@@ -217,7 +218,7 @@ func FuzzCrossG1MultiExp(data []byte) int {
// gnark multi exp
cp := new(gnark.G1Affine)
- cp.MultiExp(gnarkPoints, gnarkScalars)
+ cp.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{})
// compare result
if !(bytes.Equal(cp.Marshal(), g1.ToBytes(&kp))) {
diff --git a/tests/fuzzers/bls12381/precompile_fuzzer.go b/tests/fuzzers/bls12381/precompile_fuzzer.go
index bc3c456526..cab2bcba38 100644
--- a/tests/fuzzers/bls12381/precompile_fuzzer.go
+++ b/tests/fuzzers/bls12381/precompile_fuzzer.go
@@ -70,12 +70,14 @@ func checkInput(id byte, inputLen int) bool {
panic("programmer error")
}
-// The fuzzer functions must return
-// 1 if the fuzzer should increase priority of the
-// given input during subsequent fuzzing (for example, the input is lexically
-// correct and was parsed successfully);
-// -1 if the input must not be added to corpus even if gives new coverage; and
-// 0 otherwise
+// The function must return
+//
+// - 1 if the fuzzer should increase priority of the
+// given input during subsequent fuzzing (for example, the input is lexically
+// correct and was parsed successfully);
+// - -1 if the input must not be added to corpus even if gives new coverage; and
+// - 0 otherwise
+//
// other values are reserved for future use.
func fuzz(id byte, data []byte) int {
// Even on bad input, it should not crash, so we still test the gas calc
diff --git a/tests/fuzzers/difficulty/difficulty-fuzz.go b/tests/fuzzers/difficulty/difficulty-fuzz.go
index 2112abac1a..e8753bb623 100644
--- a/tests/fuzzers/difficulty/difficulty-fuzz.go
+++ b/tests/fuzzers/difficulty/difficulty-fuzz.go
@@ -66,12 +66,14 @@ func (f *fuzzer) readBool() bool {
return f.read(1)[0]&0x1 == 0
}
-// The function must return
-// 1 if the fuzzer should increase priority of the
-// given input during subsequent fuzzing (for example, the input is lexically
-// correct and was parsed successfully);
-// -1 if the input must not be added to corpus even if gives new coverage; and
-// 0 otherwise
+// Fuzz function must return
+//
+// - 1 if the fuzzer should increase priority of the
+// given input during subsequent fuzzing (for example, the input is lexically
+// correct and was parsed successfully);
+// - -1 if the input must not be added to corpus even if gives new coverage; and
+// - 0 otherwise
+//
// other values are reserved for future use.
func Fuzz(data []byte) int {
f := fuzzer{
diff --git a/tests/fuzzers/les/les-fuzzer.go b/tests/fuzzers/les/les-fuzzer.go
index 6777022088..924a749e58 100644
--- a/tests/fuzzers/les/les-fuzzer.go
+++ b/tests/fuzzers/les/les-fuzzer.go
@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
@@ -54,15 +55,13 @@ var (
)
func makechain() (bc *core.BlockChain, addrHashes, txHashes []common.Hash) {
- db := rawdb.NewMemoryDatabase()
- gspec := core.Genesis{
+ gspec := &core.Genesis{
Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}},
GasLimit: 100000000,
}
- genesis := gspec.MustCommit(db)
signer := types.HomesteadSigner{}
- blocks, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, testChainLen,
+ _, blocks, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), testChainLen,
func(i int, gen *core.BlockGen) {
var (
tx *types.Transaction
@@ -80,7 +79,7 @@ func makechain() (bc *core.BlockChain, addrHashes, txHashes []common.Hash) {
addrHashes = append(addrHashes, crypto.Keccak256Hash(addr[:]))
txHashes = append(txHashes, tx.Hash())
})
- bc, _ = core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+ bc, _ = core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
if _, err := bc.InsertChain(blocks); err != nil {
panic(err)
}
@@ -113,7 +112,7 @@ func init() {
type fuzzer struct {
chain *core.BlockChain
- pool *core.TxPool
+ pool *txpool.TxPool
chainLen int
addr, txs []common.Hash
@@ -139,7 +138,7 @@ func newFuzzer(input []byte) *fuzzer {
chtKeys: chtKeys,
bloomKeys: bloomKeys,
nonce: uint64(len(txHashes)),
- pool: core.NewTxPool(core.DefaultTxPoolConfig, params.TestChainConfig, chain),
+ pool: txpool.NewTxPool(txpool.DefaultConfig, params.TestChainConfig, chain),
input: bytes.NewReader(input),
}
}
@@ -231,7 +230,7 @@ func (f *fuzzer) BlockChain() *core.BlockChain {
return f.chain
}
-func (f *fuzzer) TxPool() *core.TxPool {
+func (f *fuzzer) TxPool() *txpool.TxPool {
return f.pool
}
diff --git a/mobile/init.go b/tests/fuzzers/modexp/debug/main.go
similarity index 59%
rename from mobile/init.go
rename to tests/fuzzers/modexp/debug/main.go
index 94f5baf28b..22002bd3f8 100644
--- a/mobile/init.go
+++ b/tests/fuzzers/modexp/debug/main.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// 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
@@ -14,21 +14,27 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// Contains initialization code for the mobile library.
-
-package geth
+package main
import (
+ "fmt"
"os"
- "runtime"
- "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/tests/fuzzers/modexp"
)
-func init() {
- // Initialize the logger
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
-
- // Initialize the goroutine count
- runtime.GOMAXPROCS(runtime.NumCPU())
+func main() {
+ if len(os.Args) != 2 {
+ fmt.Fprintf(os.Stderr, "Usage: debug \n")
+ fmt.Fprintf(os.Stderr, "Example\n")
+ fmt.Fprintf(os.Stderr, " $ debug ../crashers/4bbef6857c733a87ecf6fd8b9e7238f65eb9862a\n")
+ os.Exit(1)
+ }
+ crasher := os.Args[1]
+ data, err := os.ReadFile(crasher)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err)
+ os.Exit(1)
+ }
+ modexp.Fuzz(data)
}
diff --git a/tests/fuzzers/modexp/modexp-fuzzer.go b/tests/fuzzers/modexp/modexp-fuzzer.go
new file mode 100644
index 0000000000..086d9e1153
--- /dev/null
+++ b/tests/fuzzers/modexp/modexp-fuzzer.go
@@ -0,0 +1,90 @@
+// 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 modexp
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm"
+ big2 "github.com/holiman/big"
+)
+
+// Fuzz is the fuzzing entry-point.
+// The function must return
+//
+// - 1 if the fuzzer should increase priority of the
+// given input during subsequent fuzzing (for example, the input is lexically
+// correct and was parsed successfully);
+// - -1 if the input must not be added to corpus even if gives new coverage; and
+// - 0 otherwise
+//
+// other values are reserved for future use.
+func Fuzz(input []byte) int {
+ if len(input) <= 96 {
+ return -1
+ }
+ // Abort on too expensive inputs
+ precomp := vm.PrecompiledContractsBerlin[common.BytesToAddress([]byte{5})]
+ if gas := precomp.RequiredGas(input); gas > 40_000_000 {
+ return 0
+ }
+ var (
+ baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
+ expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
+ modLen = new(big.Int).SetBytes(getData(input, 64, 32)).Uint64()
+ )
+ // Handle a special case when both the base and mod length is zero
+ if baseLen == 0 && modLen == 0 {
+ return -1
+ }
+ input = input[96:]
+ // Retrieve the operands and execute the exponentiation
+ var (
+ base = new(big.Int).SetBytes(getData(input, 0, baseLen))
+ exp = new(big.Int).SetBytes(getData(input, baseLen, expLen))
+ mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen))
+ base2 = new(big2.Int).SetBytes(getData(input, 0, baseLen))
+ exp2 = new(big2.Int).SetBytes(getData(input, baseLen, expLen))
+ mod2 = new(big2.Int).SetBytes(getData(input, baseLen+expLen, modLen))
+ )
+ if mod.BitLen() == 0 {
+ // Modulo 0 is undefined, return zero
+ return -1
+ }
+ var a = new(big2.Int).Exp(base2, exp2, mod2).String()
+ var b = new(big.Int).Exp(base, exp, mod).String()
+ if a != b {
+ panic(fmt.Sprintf("Inequality %#x ^ %#x mod %#x \n have %s\n want %s", base, exp, mod, a, b))
+ }
+ return 1
+}
+
+// getData returns a slice from the data based on the start and size and pads
+// up to size with zero's. This function is overflow safe.
+func getData(data []byte, start uint64, size uint64) []byte {
+ length := uint64(len(data))
+ if start > length {
+ start = length
+ }
+ end := start + size
+ if end > length {
+ end = length
+ }
+ return common.RightPadBytes(data[start:end], int(size))
+}
diff --git a/tests/fuzzers/rangeproof/rangeproof-fuzzer.go b/tests/fuzzers/rangeproof/rangeproof-fuzzer.go
index c2db919d5a..bca93bbe19 100644
--- a/tests/fuzzers/rangeproof/rangeproof-fuzzer.go
+++ b/tests/fuzzers/rangeproof/rangeproof-fuzzer.go
@@ -179,12 +179,16 @@ func (f *fuzzer) fuzz() int {
return ok
}
+// Fuzz is the fuzzing entry-point.
// The function must return
-// 1 if the fuzzer should increase priority of the
-// given input during subsequent fuzzing (for example, the input is lexically
-// correct and was parsed successfully);
-// -1 if the input must not be added to corpus even if gives new coverage; and
-// 0 otherwise; other values are reserved for future use.
+//
+// - 1 if the fuzzer should increase priority of the
+// given input during subsequent fuzzing (for example, the input is lexically
+// correct and was parsed successfully);
+// - -1 if the input must not be added to corpus even if gives new coverage; and
+// - 0 otherwise
+//
+// other values are reserved for future use.
func Fuzz(input []byte) int {
if len(input) < 100 {
return 0
diff --git a/tests/fuzzers/runtime/runtime_fuzz.go b/tests/fuzzers/runtime/runtime_fuzz.go
index 9b96045752..b30e9243d8 100644
--- a/tests/fuzzers/runtime/runtime_fuzz.go
+++ b/tests/fuzzers/runtime/runtime_fuzz.go
@@ -22,7 +22,7 @@ import (
// Fuzz is the basic entry point for the go-fuzz tool
//
-// This returns 1 for valid parsable/runable code, 0
+// This returns 1 for valid parse:able/runnable code, 0
// for invalid opcode.
func Fuzz(input []byte) int {
_, _, err := runtime.Execute(input, input, &runtime.Config{
diff --git a/tests/fuzzers/snap/fuzz_handler.go b/tests/fuzzers/snap/fuzz_handler.go
index 1ae61df29d..2e5dcd6e29 100644
--- a/tests/fuzzers/snap/fuzz_handler.go
+++ b/tests/fuzzers/snap/fuzz_handler.go
@@ -39,7 +39,6 @@ import (
var trieRoot common.Hash
func getChain() *core.BlockChain {
- db := rawdb.NewMemoryDatabase()
ga := make(core.GenesisAlloc, 1000)
var a = make([]byte, 20)
var mkStorage = func(k, v int) (common.Hash, common.Hash) {
@@ -62,13 +61,11 @@ func getChain() *core.BlockChain {
}
ga[common.BytesToAddress(a)] = acc
}
- gspec := core.Genesis{
+ gspec := &core.Genesis{
Config: params.TestChainConfig,
Alloc: ga,
}
- genesis := gspec.MustCommit(db)
- blocks, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 2,
- func(i int, gen *core.BlockGen) {})
+ _, blocks, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 2, func(i int, gen *core.BlockGen) {})
cacheConf := &core.CacheConfig{
TrieCleanLimit: 0,
TrieDirtyLimit: 0,
@@ -79,7 +76,7 @@ func getChain() *core.BlockChain {
SnapshotWait: true,
}
trieRoot = blocks[len(blocks)-1].Root()
- bc, _ := core.NewBlockChain(db, cacheConf, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+ bc, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), cacheConf, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
if _, err := bc.InsertChain(blocks); err != nil {
panic(err)
}
diff --git a/tests/fuzzers/stacktrie/trie_fuzzer.go b/tests/fuzzers/stacktrie/trie_fuzzer.go
index e6165df08c..435aa3a47c 100644
--- a/tests/fuzzers/stacktrie/trie_fuzzer.go
+++ b/tests/fuzzers/stacktrie/trie_fuzzer.go
@@ -25,6 +25,9 @@ import (
"io"
"sort"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
"golang.org/x/crypto/sha3"
@@ -113,12 +116,15 @@ func (k kvs) Swap(i, j int) {
k[j], k[i] = k[i], k[j]
}
+// Fuzz is the fuzzing entry-point.
// The function must return
-// 1 if the fuzzer should increase priority of the
-// given input during subsequent fuzzing (for example, the input is lexically
-// correct and was parsed successfully);
-// -1 if the input must not be added to corpus even if gives new coverage; and
-// 0 otherwise
+//
+// - 1 if the fuzzer should increase priority of the
+// given input during subsequent fuzzing (for example, the input is lexically
+// correct and was parsed successfully);
+// - -1 if the input must not be added to corpus even if gives new coverage; and
+// - 0 otherwise
+//
// other values are reserved for future use.
func Fuzz(data []byte) int {
f := fuzzer{
@@ -140,11 +146,14 @@ func Debug(data []byte) int {
func (f *fuzzer) fuzz() int {
// This spongeDb is used to check the sequence of disk-db-writes
var (
- spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
- dbA = trie.NewDatabase(spongeA)
- trieA = trie.NewEmpty(dbA)
- spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
- trieB = trie.NewStackTrie(spongeB)
+ spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
+ dbA = trie.NewDatabase(rawdb.NewDatabase(spongeA))
+ trieA = trie.NewEmpty(dbA)
+ spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
+ dbB = trie.NewDatabase(rawdb.NewDatabase(spongeB))
+ trieB = trie.NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, blob []byte) {
+ rawdb.WriteTrieNode(spongeB, owner, path, hash, blob, dbB.Scheme())
+ })
vals kvs
useful bool
maxElements = 10000
@@ -173,15 +182,12 @@ func (f *fuzzer) fuzz() int {
return 0
}
// Flush trie -> database
- rootA, nodes, err := trieA.Commit(false)
- if err != nil {
- panic(err)
- }
+ rootA, nodes := trieA.Commit(false)
if nodes != nil {
dbA.Update(trie.NewWithNodeSet(nodes))
}
// Flush memdb -> disk (sponge)
- dbA.Commit(rootA, false, nil)
+ dbA.Commit(rootA, false)
// Stacktrie requires sorted insertion
sort.Sort(vals)
@@ -192,9 +198,7 @@ func (f *fuzzer) fuzz() int {
trieB.Update(kv.k, kv.v)
}
rootB := trieB.Hash()
- if _, err := trieB.Commit(); err != nil {
- panic(err)
- }
+ trieB.Commit()
if rootA != rootB {
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB))
}
@@ -203,5 +207,48 @@ func (f *fuzzer) fuzz() int {
if !bytes.Equal(sumA, sumB) {
panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB))
}
+
+ // Ensure all the nodes are persisted correctly
+ var (
+ nodeset = make(map[string][]byte) // path -> blob
+ trieC = trie.NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, blob []byte) {
+ if crypto.Keccak256Hash(blob) != hash {
+ panic("invalid node blob")
+ }
+ if owner != (common.Hash{}) {
+ panic("invalid node owner")
+ }
+ nodeset[string(path)] = common.CopyBytes(blob)
+ })
+ checked int
+ )
+ for _, kv := range vals {
+ trieC.Update(kv.k, kv.v)
+ }
+ rootC, _ := trieC.Commit()
+ if rootA != rootC {
+ panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC))
+ }
+ trieA, _ = trie.New(trie.TrieID(rootA), dbA)
+ iterA := trieA.NodeIterator(nil)
+ for iterA.Next(true) {
+ if iterA.Hash() == (common.Hash{}) {
+ if _, present := nodeset[string(iterA.Path())]; present {
+ panic("unexpected tiny node")
+ }
+ continue
+ }
+ nodeBlob, present := nodeset[string(iterA.Path())]
+ if !present {
+ panic("missing node")
+ }
+ if !bytes.Equal(nodeBlob, iterA.NodeBlob()) {
+ panic("node blob is not matched")
+ }
+ checked += 1
+ }
+ if checked != len(nodeset) {
+ panic("node number is not matched")
+ }
return 1
}
diff --git a/tests/fuzzers/trie/trie-fuzzer.go b/tests/fuzzers/trie/trie-fuzzer.go
index f36b613d47..40dec76b8f 100644
--- a/tests/fuzzers/trie/trie-fuzzer.go
+++ b/tests/fuzzers/trie/trie-fuzzer.go
@@ -21,8 +21,7 @@ import (
"encoding/binary"
"fmt"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
+ "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/trie"
)
@@ -118,12 +117,15 @@ func Generate(input []byte) randTest {
return steps
}
+// Fuzz is the fuzzing entry-point.
// The function must return
-// 1 if the fuzzer should increase priority of the
-// given input during subsequent fuzzing (for example, the input is lexically
-// correct and was parsed successfully);
-// -1 if the input must not be added to corpus even if gives new coverage; and
-// 0 otherwise
+//
+// - 1 if the fuzzer should increase priority of the
+// given input during subsequent fuzzing (for example, the input is lexically
+// correct and was parsed successfully);
+// - -1 if the input must not be added to corpus even if gives new coverage; and
+// - 0 otherwise
+//
// other values are reserved for future use.
func Fuzz(input []byte) int {
program := Generate(input)
@@ -137,7 +139,7 @@ func Fuzz(input []byte) int {
}
func runRandTest(rt randTest) error {
- triedb := trie.NewDatabase(memorydb.New())
+ triedb := trie.NewDatabase(rawdb.NewMemoryDatabase())
tr := trie.NewEmpty(triedb)
values := make(map[string]string) // tracks content of the trie
@@ -159,16 +161,13 @@ func runRandTest(rt randTest) error {
case opHash:
tr.Hash()
case opCommit:
- hash, nodes, err := tr.Commit(false)
- if err != nil {
- return err
- }
+ hash, nodes := tr.Commit(false)
if nodes != nil {
if err := triedb.Update(trie.NewWithNodeSet(nodes)); err != nil {
return err
}
}
- newtr, err := trie.New(common.Hash{}, hash, triedb)
+ newtr, err := trie.New(trie.TrieID(hash), triedb)
if err != nil {
return err
}
diff --git a/tests/gen_btheader.go b/tests/gen_btheader.go
index 4387f8db41..985ea692d7 100644
--- a/tests/gen_btheader.go
+++ b/tests/gen_btheader.go
@@ -34,6 +34,7 @@ func (b btHeader) MarshalJSON() ([]byte, error) {
GasUsed math.HexOrDecimal64
Timestamp math.HexOrDecimal64
BaseFeePerGas *math.HexOrDecimal256
+ WithdrawalsRoot *common.Hash
}
var enc btHeader
enc.Bloom = b.Bloom
@@ -53,6 +54,7 @@ func (b btHeader) MarshalJSON() ([]byte, error) {
enc.GasUsed = math.HexOrDecimal64(b.GasUsed)
enc.Timestamp = math.HexOrDecimal64(b.Timestamp)
enc.BaseFeePerGas = (*math.HexOrDecimal256)(b.BaseFeePerGas)
+ enc.WithdrawalsRoot = b.WithdrawalsRoot
return json.Marshal(&enc)
}
@@ -76,6 +78,7 @@ func (b *btHeader) UnmarshalJSON(input []byte) error {
GasUsed *math.HexOrDecimal64
Timestamp *math.HexOrDecimal64
BaseFeePerGas *math.HexOrDecimal256
+ WithdrawalsRoot *common.Hash
}
var dec btHeader
if err := json.Unmarshal(input, &dec); err != nil {
@@ -132,5 +135,8 @@ func (b *btHeader) UnmarshalJSON(input []byte) error {
if dec.BaseFeePerGas != nil {
b.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas)
}
+ if dec.WithdrawalsRoot != nil {
+ b.WithdrawalsRoot = dec.WithdrawalsRoot
+ }
return nil
}
diff --git a/tests/init.go b/tests/init.go
index 87ffc65a67..db037e3e1a 100644
--- a/tests/init.go
+++ b/tests/init.go
@@ -24,6 +24,8 @@ import (
"github.com/ethereum/go-ethereum/params"
)
+func u64(val uint64) *uint64 { return &val }
+
// Forks table defines supported forks and their chain config.
var Forks = map[string]*params.ChainConfig{
"Frontier": {
@@ -197,6 +199,24 @@ var Forks = map[string]*params.ChainConfig{
LondonBlock: big.NewInt(0),
ArrowGlacierBlock: big.NewInt(0),
},
+ "ArrowGlacierToMergeAtDiffC0000": {
+ ChainID: big.NewInt(1),
+ HomesteadBlock: big.NewInt(0),
+ 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: big.NewInt(0),
+ TerminalTotalDifficulty: big.NewInt(0xC0000),
+ },
"GrayGlacier": {
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
@@ -213,7 +233,42 @@ var Forks = map[string]*params.ChainConfig{
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
},
- "Merged": {
+ "Merge": {
+ ChainID: big.NewInt(1),
+ HomesteadBlock: big.NewInt(0),
+ 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),
+ MergeNetsplitBlock: big.NewInt(0),
+ TerminalTotalDifficulty: big.NewInt(0),
+ },
+ "Shanghai": {
+ ChainID: big.NewInt(1),
+ HomesteadBlock: big.NewInt(0),
+ 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),
+ MergeNetsplitBlock: big.NewInt(0),
+ TerminalTotalDifficulty: big.NewInt(0),
+ ShanghaiTime: u64(0),
+ },
+ "MergeToShanghaiAtTime15k": {
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
@@ -229,10 +284,11 @@ var Forks = map[string]*params.ChainConfig{
ArrowGlacierBlock: big.NewInt(0),
MergeNetsplitBlock: big.NewInt(0),
TerminalTotalDifficulty: big.NewInt(0),
+ ShanghaiTime: u64(15_000),
},
}
-// Returns the set of defined fork names
+// AvailableForks returns the set of defined fork names
func AvailableForks() []string {
var availableForks []string
for k := range Forks {
diff --git a/tests/init_test.go b/tests/init_test.go
index 4ef5aaf737..9d315f9511 100644
--- a/tests/init_test.go
+++ b/tests/init_test.go
@@ -116,6 +116,7 @@ func (tm *testMatcher) skipLoad(pattern string) {
}
// fails adds an expected failure for tests matching the pattern.
+//
//nolint:unused
func (tm *testMatcher) fails(pattern string, reason string) {
if reason == "" {
diff --git a/tests/state_test.go b/tests/state_test.go
index d33ebc4b00..7dd2f678c6 100644
--- a/tests/state_test.go
+++ b/tests/state_test.go
@@ -26,6 +26,7 @@ import (
"reflect"
"strings"
"testing"
+ "time"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -56,13 +57,11 @@ func TestState(t *testing.T) {
st.skipLoad(`^stStaticCall/static_Call1MB`)
// Broken tests:
+ //
+ // The stEOF tests are generated with EOF as part of Shanghai, which
+ // is erroneous. Therefore, these tests are skipped.
+ st.skipLoad(`^EIPTests/stEOF/`)
// Expected failures:
- //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Byzantium/0`, "bug in test")
- //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Byzantium/3`, "bug in test")
- //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Constantinople/0`, "bug in test")
- //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/Constantinople/3`, "bug in test")
- //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/ConstantinopleFix/0`, "bug in test")
- //st.fails(`^stRevertTest/RevertPrecompiledTouch(_storage)?\.json/ConstantinopleFix/3`, "bug in test")
// For Istanbul, older tests were moved into LegacyTests
for _, dir := range []string{
@@ -78,10 +77,6 @@ func TestState(t *testing.T) {
t.Run(key+"/trie", func(t *testing.T) {
withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
_, _, err := test.Run(subtest, vmconfig, false)
- if err != nil && len(test.json.Post[subtest.Fork][subtest.Index].ExpectException) > 0 {
- // Ignore expected errors (TODO MariusVanDerWijden check error string)
- return nil
- }
return st.checkFailure(t, err)
})
})
@@ -93,10 +88,6 @@ func TestState(t *testing.T) {
return err
}
}
- if err != nil && len(test.json.Post[subtest.Fork][subtest.Index].ExpectException) > 0 {
- // Ignore expected errors (TODO MariusVanDerWijden check error string)
- return nil
- }
return st.checkFailure(t, err)
})
})
@@ -192,12 +183,14 @@ func runBenchmark(b *testing.B, t *StateTest) {
b.Error(err)
return
}
+ var rules = config.Rules(new(big.Int), false, 0)
+
vmconfig.ExtraEips = eips
block := t.genesis(config).ToBlock()
_, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, false)
var baseFee *big.Int
- if config.IsLondon(new(big.Int)) {
+ if rules.IsLondon {
baseFee = t.json.Env.BaseFee
if baseFee == nil {
// Retesteth uses `0x10` for genesis baseFee. Therefore, it defaults to
@@ -238,17 +231,38 @@ func runBenchmark(b *testing.B, t *StateTest) {
sender := vm.NewContract(vm.AccountRef(msg.From()), vm.AccountRef(msg.From()),
nil, 0)
+ var (
+ gasUsed uint64
+ elapsed uint64
+ refund uint64
+ )
b.ResetTimer()
for n := 0; n < b.N; n++ {
- // Execute the message.
snapshot := statedb.Snapshot()
- _, _, err = evm.Call(sender, *msg.To(), msg.Data(), msg.Gas(), msg.Value())
+ statedb.Prepare(rules, msg.From(), context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
+ b.StartTimer()
+ start := time.Now()
+
+ // Execute the message.
+ _, leftOverGas, err := evm.Call(sender, *msg.To(), msg.Data(), msg.Gas(), msg.Value())
if err != nil {
b.Error(err)
return
}
+
+ b.StopTimer()
+ elapsed += uint64(time.Since(start))
+ refund += statedb.GetRefund()
+ gasUsed += msg.Gas() - leftOverGas
+
statedb.RevertToSnapshot(snapshot)
}
+ if elapsed < 1 {
+ elapsed = 1
+ }
+ // Keep it as uint64, multiply 100 to get two digit float later
+ mgasps := (100 * 1000 * (gasUsed - refund)) / elapsed
+ b.ReportMetric(float64(mgasps)/100, "mgas/s")
})
}
}
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 38cdbc4d65..b2e87fb004 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -159,11 +159,39 @@ func (t *StateTest) Subtests() []StateSubtest {
return sub
}
+// checkError checks if the error returned by the state transition matches any expected error.
+// A failing expectation returns a wrapped version of the original error, if any,
+// or a new error detailing the failing expectation.
+// This function does not return or modify the original error, it only evaluates and returns expectations for the error.
+func (t *StateTest) checkError(subtest StateSubtest, err error) error {
+ expectedError := t.json.Post[subtest.Fork][subtest.Index].ExpectException
+ if err == nil && expectedError == "" {
+ return nil
+ }
+ if err == nil && expectedError != "" {
+ return fmt.Errorf("expected error %q, got no error", expectedError)
+ }
+ if err != nil && expectedError == "" {
+ return fmt.Errorf("unexpected error: %w", err)
+ }
+ if err != nil && expectedError != "" {
+ // Ignore expected errors (TODO MariusVanDerWijden check error string)
+ return nil
+ }
+ return nil
+}
+
// Run executes a specific subtest and verifies the post-state and logs
func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bool) (*snapshot.Tree, *state.StateDB, error) {
snaps, statedb, root, err := t.RunNoVerify(subtest, vmconfig, snapshotter)
+ if checkedErr := t.checkError(subtest, err); checkedErr != nil {
+ return snaps, statedb, checkedErr
+ }
+ // The error has been checked; if it was unexpected, it's already returned.
if err != nil {
- return snaps, statedb, err
+ // Here, an error exists but it was expected.
+ // We do not check the post state or logs.
+ return snaps, statedb, nil
}
post := t.json.Post[subtest.Fork][subtest.Index]
// N.B: We need to do this in a two-step process, because the first Commit takes care
@@ -221,6 +249,9 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
context.GetHash = vmTestBlockHash
context.BaseFee = baseFee
context.Random = nil
+ if t.json.Env.Difficulty != nil {
+ context.Difficulty = new(big.Int).Set(t.json.Env.Difficulty)
+ }
if config.IsLondon(new(big.Int)) && t.json.Env.Random != nil {
rnd := common.BigToHash(t.json.Env.Random)
context.Random = &rnd
@@ -231,7 +262,8 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
snapshot := statedb.Snapshot()
gaspool := new(core.GasPool)
gaspool.AddGas(block.GasLimit())
- if _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
+ _, err = core.ApplyMessage(evm, msg, gaspool)
+ if err != nil {
statedb.RevertToSnapshot(snapshot)
}
// Add 0-value mining reward. This only makes a difference in the cases
@@ -244,7 +276,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
statedb.Commit(config.IsEIP158(block.Number()))
// And _now_ get the state root
root := statedb.IntermediateRoot(config.IsEIP158(block.Number()))
- return snaps, statedb, root, nil
+ return snaps, statedb, root, err
}
func (t *StateTest) gasLimit(subtest StateSubtest) uint64 {
@@ -267,7 +299,13 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo
var snaps *snapshot.Tree
if snapshotter {
- snaps, _ = snapshot.New(db, sdb.TrieDB(), 1, root, false, true, false)
+ snapconfig := snapshot.Config{
+ CacheSize: 1,
+ Recovery: false,
+ NoBuild: false,
+ AsyncBuild: false,
+ }
+ snaps, _ = snapshot.New(snapconfig, db, sdb.TrieDB(), root)
}
statedb, _ = state.New(root, sdb, snaps)
return snaps, statedb
diff --git a/tests/testdata b/tests/testdata
index a380655e5f..bac70c50a5 160000
--- a/tests/testdata
+++ b/tests/testdata
@@ -1 +1 @@
-Subproject commit a380655e5ffab1a5ea0f4d860224bdb19013f06a
+Subproject commit bac70c50a579197af68af5fc6d8c7b6163b92c52
diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go
index 82ee01de15..391aa57584 100644
--- a/tests/transaction_test_util.go
+++ b/tests/transaction_test_util.go
@@ -55,7 +55,7 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error {
return nil, nil, err
}
// Intrinsic gas
- requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul)
+ requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul, false)
if err != nil {
return nil, nil, err
}
diff --git a/trie/committer.go b/trie/committer.go
index d9f0ecf3de..c4957f3490 100644
--- a/trie/committer.go
+++ b/trie/committer.go
@@ -33,32 +33,37 @@ type leaf struct {
// insertion order.
type committer struct {
nodes *NodeSet
+ tracer *tracer
collectLeaf bool
}
// newCommitter creates a new committer or picks one from the pool.
-func newCommitter(owner common.Hash, collectLeaf bool) *committer {
+func newCommitter(owner common.Hash, tracer *tracer, collectLeaf bool) *committer {
return &committer{
nodes: NewNodeSet(owner),
+ tracer: tracer,
collectLeaf: collectLeaf,
}
}
-// Commit collapses a node down into a hash node and inserts it into the database
-func (c *committer) Commit(n node) (hashNode, *NodeSet, error) {
- h, err := c.commit(nil, n)
- if err != nil {
- return nil, nil, err
- }
- return h.(hashNode), c.nodes, nil
+// Commit collapses a node down into a hash node and returns it along with
+// the modified nodeset.
+func (c *committer) Commit(n node) (hashNode, *NodeSet) {
+ h := c.commit(nil, n)
+ // Some nodes can be deleted from trie which can't be captured
+ // by committer itself. Iterate all deleted nodes tracked by
+ // tracer and marked them as deleted only if they are present
+ // in database previously.
+ c.tracer.markDeletions(c.nodes)
+ return h.(hashNode), c.nodes
}
-// commit collapses a node down into a hash node and inserts it into the database
-func (c *committer) commit(path []byte, n node) (node, error) {
+// commit collapses a node down into a hash node and returns it.
+func (c *committer) commit(path []byte, n node) node {
// if this path is clean, use available cached data
hash, dirty := n.cache()
if hash != nil && !dirty {
- return hash, nil
+ return hash
}
// Commit children, then parent, and remove the dirty flag.
switch cn := n.(type) {
@@ -69,34 +74,42 @@ func (c *committer) commit(path []byte, n node) (node, error) {
// If the child is fullNode, recursively commit,
// otherwise it can only be hashNode or valueNode.
if _, ok := cn.Val.(*fullNode); ok {
- childV, err := c.commit(append(path, cn.Key...), cn.Val)
- if err != nil {
- return nil, err
- }
+ childV := c.commit(append(path, cn.Key...), cn.Val)
+
collapsed.Val = childV
}
- // The key needs to be copied, since we're delivering it to database
+ // The key needs to be copied, since we're adding it to the
+ // modified nodeset.
collapsed.Key = hexToCompact(cn.Key)
hashedNode := c.store(path, collapsed)
if hn, ok := hashedNode.(hashNode); ok {
- return hn, nil
+ return hn
}
- return collapsed, nil
- case *fullNode:
- hashedKids, err := c.commitChildren(path, cn)
- if err != nil {
- return nil, err
+ // The short node now is embedded in its parent. Mark the node as
+ // deleted if it's present in database previously. It's equivalent
+ // as deletion from database's perspective.
+ if prev := c.tracer.getPrev(path); len(prev) != 0 {
+ c.nodes.markDeleted(path, prev)
}
+ return collapsed
+ case *fullNode:
+ hashedKids := c.commitChildren(path, cn)
collapsed := cn.copy()
collapsed.Children = hashedKids
hashedNode := c.store(path, collapsed)
if hn, ok := hashedNode.(hashNode); ok {
- return hn, nil
+ return hn
+ }
+ // The full node now is embedded in its parent. Mark the node as
+ // deleted if it's present in database previously. It's equivalent
+ // as deletion from database's perspective.
+ if prev := c.tracer.getPrev(path); len(prev) != 0 {
+ c.nodes.markDeleted(path, prev)
}
- return collapsed, nil
+ return collapsed
case hashNode:
- return cn, nil
+ return cn
default:
// nil, valuenode shouldn't be committed
panic(fmt.Sprintf("%T: invalid node: %v", n, n))
@@ -104,7 +117,7 @@ func (c *committer) commit(path []byte, n node) (node, error) {
}
// commitChildren commits the children of the given fullnode
-func (c *committer) commitChildren(path []byte, n *fullNode) ([17]node, error) {
+func (c *committer) commitChildren(path []byte, n *fullNode) [17]node {
var children [17]node
for i := 0; i < 16; i++ {
child := n.Children[i]
@@ -121,30 +134,26 @@ func (c *committer) commitChildren(path []byte, n *fullNode) ([17]node, error) {
// Commit the child recursively and store the "hashed" value.
// Note the returned node can be some embedded nodes, so it's
// possible the type is not hashNode.
- hashed, err := c.commit(append(path, byte(i)), child)
- if err != nil {
- return children, err
- }
+ hashed := c.commit(append(path, byte(i)), child)
children[i] = hashed
}
// For the 17th child, it's possible the type is valuenode.
if n.Children[16] != nil {
children[16] = n.Children[16]
}
- return children, nil
+ return children
}
-// store hashes the node n and if we have a storage layer specified, it writes
-// the key/value pair to it and tracks any node->child references as well as any
-// node->external trie references.
+// store hashes the node n and adds it to the modified nodeset. If leaf collection
+// is enabled, leaf nodes will be tracked in the modified nodeset as well.
func (c *committer) store(path []byte, n node) node {
// Larger nodes are replaced by their hash and stored in the database.
var hash, _ = n.cache()
// This was not generated - must be a small node stored in the parent.
// In theory, we should check if the node is leaf here (embedded node
- // usually is leaf node). But small value(less than 32bytes) is not
- // our target(leaves in account trie only).
+ // usually is leaf node). But small value (less than 32bytes) is not
+ // our target (leaves in account trie only).
if hash == nil {
return n
}
@@ -160,7 +169,7 @@ func (c *committer) store(path []byte, n node) node {
}
)
// Collect the dirty node to nodeset for return.
- c.nodes.add(string(path), mnode)
+ c.nodes.markUpdated(path, mnode, c.tracer.getPrev(path))
// Collect the corresponding leaf node if it's required. We don't check
// full node since it's impossible to store value in fullNode. The key
diff --git a/trie/database.go b/trie/database.go
index b10bbca9bd..895ffdf89d 100644
--- a/trie/database.go
+++ b/trie/database.go
@@ -68,7 +68,7 @@ var (
// behind this split design is to provide read access to RPC handlers and sync
// servers even while the trie is executing expensive garbage collection.
type Database struct {
- diskdb ethdb.KeyValueStore // Persistent storage for matured trie nodes
+ diskdb ethdb.Database // Persistent storage for matured trie nodes
cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs
dirties map[common.Hash]*cachedNode // Data and references relationships of dirty trie nodes
@@ -163,7 +163,7 @@ func (n *cachedNode) rlp() []byte {
// or by regenerating it from the rlp encoded blob.
func (n *cachedNode) obj(hash common.Hash) node {
if node, ok := n.node.(rawNode); ok {
- // The raw-blob format nodes are loaded from either from
+ // The raw-blob format nodes are loaded either from the
// clean cache or the database, they are all in their own
// copy and safe to use unsafe decoder.
return mustDecodeNodeUnsafe(hash[:], node)
@@ -273,14 +273,14 @@ type Config struct {
// NewDatabase creates a new trie database to store ephemeral trie content before
// its written out to disk or garbage collected. No read cache is created, so all
// data retrievals will hit the underlying disk database.
-func NewDatabase(diskdb ethdb.KeyValueStore) *Database {
+func NewDatabase(diskdb ethdb.Database) *Database {
return NewDatabaseWithConfig(diskdb, nil)
}
// NewDatabaseWithConfig creates a new trie database to store ephemeral trie content
// before its written out to disk or garbage collected. It also acts as a read cache
// for nodes loaded from disk.
-func NewDatabaseWithConfig(diskdb ethdb.KeyValueStore, config *Config) *Database {
+func NewDatabaseWithConfig(diskdb ethdb.Database, config *Config) *Database {
var cleans *fastcache.Cache
if config != nil && config.Cache > 0 {
if config.Journal == "" {
@@ -304,11 +304,6 @@ func NewDatabaseWithConfig(diskdb ethdb.KeyValueStore, config *Config) *Database
return db
}
-// DiskDB retrieves the persistent storage backing the trie database.
-func (db *Database) DiskDB() ethdb.KeyValueStore {
- return db.diskdb
-}
-
// insert inserts a simplified trie node into the memory database.
// All nodes inserted by this function will be reference tracked
// and in theory should only used for **trie nodes** insertion.
@@ -410,7 +405,7 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
memcacheDirtyMissMeter.Mark(1)
// Content unavailable in memory, attempt to retrieve from disk
- enc := rawdb.ReadTrieNode(db.diskdb, hash)
+ enc := rawdb.ReadLegacyTrieNode(db.diskdb, hash)
if len(enc) != 0 {
if db.cleans != nil {
db.cleans.Set(hash[:], enc)
@@ -567,14 +562,16 @@ func (db *Database) Cap(limit common.StorageSize) error {
// If the preimage cache got large enough, push to disk. If it's still small
// leave for later to deduplicate writes.
if db.preimages != nil {
- db.preimages.commit(false)
+ if err := db.preimages.commit(false); err != nil {
+ return err
+ }
}
// Keep committing nodes from the flush-list until we're below allowance
oldest := db.oldest
for size > limit && oldest != (common.Hash{}) {
// Fetch the oldest referenced node and push into the batch
node := db.dirties[oldest]
- rawdb.WriteTrieNode(batch, oldest, node.rlp())
+ rawdb.WriteLegacyTrieNode(batch, oldest, node.rlp())
// If we exceeded the ideal batch size, commit and reset
if batch.ValueSize() >= ethdb.IdealBatchSize {
@@ -635,7 +632,7 @@ func (db *Database) Cap(limit common.StorageSize) error {
//
// Note, this method is a non-synchronized mutator. It is unsafe to call this
// concurrently with other mutators.
-func (db *Database) Commit(node common.Hash, report bool, callback func(common.Hash)) error {
+func (db *Database) Commit(node common.Hash, report bool) error {
// Create a database batch to flush persistent data out. It is important that
// outside code doesn't see an inconsistent state (referenced data removed from
// memory cache during commit but not yet in persistent storage). This is ensured
@@ -645,13 +642,15 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H
// Move all of the accumulated preimages into a write batch
if db.preimages != nil {
- db.preimages.commit(true)
+ if err := db.preimages.commit(true); err != nil {
+ return err
+ }
}
// Move the trie itself into the batch, flushing if enough data is accumulated
nodes, storage := len(db.dirties), db.dirtiesSize
uncacher := &cleaner{db}
- if err := db.commit(node, batch, uncacher, callback); err != nil {
+ if err := db.commit(node, batch, uncacher); err != nil {
log.Error("Failed to commit trie from trie database", "err", err)
return err
}
@@ -663,8 +662,9 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H
// Uncache any leftovers in the last batch
db.lock.Lock()
defer db.lock.Unlock()
-
- batch.Replay(uncacher)
+ if err := batch.Replay(uncacher); err != nil {
+ return err
+ }
batch.Reset()
// Reset the storage counters and bumped metrics
@@ -687,7 +687,7 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H
}
// commit is the private locked version of Commit.
-func (db *Database) commit(hash common.Hash, batch ethdb.Batch, uncacher *cleaner, callback func(common.Hash)) error {
+func (db *Database) commit(hash common.Hash, batch ethdb.Batch, uncacher *cleaner) error {
// If the node does not exist, it's a previously committed node
node, ok := db.dirties[hash]
if !ok {
@@ -696,25 +696,25 @@ func (db *Database) commit(hash common.Hash, batch ethdb.Batch, uncacher *cleane
var err error
node.forChilds(func(child common.Hash) {
if err == nil {
- err = db.commit(child, batch, uncacher, callback)
+ err = db.commit(child, batch, uncacher)
}
})
if err != nil {
return err
}
// If we've reached an optimal batch size, commit and start over
- rawdb.WriteTrieNode(batch, hash, node.rlp())
- if callback != nil {
- callback(hash)
- }
+ rawdb.WriteLegacyTrieNode(batch, hash, node.rlp())
if batch.ValueSize() >= ethdb.IdealBatchSize {
if err := batch.Write(); err != nil {
return err
}
db.lock.Lock()
- batch.Replay(uncacher)
+ err := batch.Replay(uncacher)
batch.Reset()
db.lock.Unlock()
+ if err != nil {
+ return err
+ }
}
return nil
}
@@ -792,8 +792,8 @@ func (db *Database) Update(nodes *MergedNodeSet) error {
}
for _, owner := range order {
subset := nodes.sets[owner]
- for _, path := range subset.paths {
- n, ok := subset.nodes[path]
+ for _, path := range subset.updates.order {
+ n, ok := subset.updates.nodes[path]
if !ok {
return fmt.Errorf("missing node %x %v", owner, path)
}
@@ -808,7 +808,7 @@ func (db *Database) Update(nodes *MergedNodeSet) error {
if err := rlp.DecodeBytes(n.blob, &account); err != nil {
return err
}
- if account.Root != emptyRoot {
+ if account.Root != types.EmptyRootHash {
db.reference(account.Root, n.parent)
}
}
@@ -834,6 +834,34 @@ func (db *Database) Size() (common.StorageSize, common.StorageSize) {
return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, preimageSize
}
+// GetReader retrieves a node reader belonging to the given state root.
+func (db *Database) GetReader(root common.Hash) Reader {
+ return newHashReader(db)
+}
+
+// hashReader is reader of hashDatabase which implements the Reader interface.
+type hashReader struct {
+ db *Database
+}
+
+// newHashReader initializes the hash reader.
+func newHashReader(db *Database) *hashReader {
+ return &hashReader{db: db}
+}
+
+// Node retrieves the trie node with the given node hash.
+// No error will be returned if the node is not found.
+func (reader *hashReader) Node(_ common.Hash, _ []byte, hash common.Hash) (node, error) {
+ return reader.db.node(hash), nil
+}
+
+// NodeBlob retrieves the RLP-encoded trie node blob with the given node hash.
+// No error will be returned if the node is not found.
+func (reader *hashReader) NodeBlob(_ common.Hash, _ []byte, hash common.Hash) ([]byte, error) {
+ blob, _ := reader.db.Node(hash)
+ return blob, nil
+}
+
// saveCache saves clean state cache to given directory path
// using specified CPU cores.
func (db *Database) saveCache(dir string, threads int) error {
@@ -886,3 +914,8 @@ func (db *Database) CommitPreimages() error {
}
return db.preimages.commit(true)
}
+
+// Scheme returns the node scheme used in the database.
+func (db *Database) Scheme() string {
+ return rawdb.HashScheme
+}
diff --git a/trie/database_test.go b/trie/database_test.go
index 81c469500f..54d7529476 100644
--- a/trie/database_test.go
+++ b/trie/database_test.go
@@ -20,13 +20,13 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
+ "github.com/ethereum/go-ethereum/core/rawdb"
)
// Tests that the trie database returns a missing trie node error if attempting
// to retrieve the meta root.
func TestDatabaseMetarootFetch(t *testing.T) {
- db := NewDatabase(memorydb.New())
+ db := NewDatabase(rawdb.NewMemoryDatabase())
if _, err := db.Node(common.Hash{}); err == nil {
t.Fatalf("metaroot retrieval succeeded")
}
diff --git a/trie/encoding_test.go b/trie/encoding_test.go
index 16393313f7..d16d25c359 100644
--- a/trie/encoding_test.go
+++ b/trie/encoding_test.go
@@ -18,6 +18,7 @@ package trie
import (
"bytes"
+ crand "crypto/rand"
"encoding/hex"
"math/rand"
"testing"
@@ -78,17 +79,17 @@ func TestHexKeybytes(t *testing.T) {
}
func TestHexToCompactInPlace(t *testing.T) {
- for i, keyS := range []string{
+ for i, key := range []string{
"00",
"060a040c0f000a090b040803010801010900080d090a0a0d0903000b10",
"10",
} {
- hexBytes, _ := hex.DecodeString(keyS)
+ hexBytes, _ := hex.DecodeString(key)
exp := hexToCompact(hexBytes)
sz := hexToCompactInPlace(hexBytes)
got := hexBytes[:sz]
if !bytes.Equal(exp, got) {
- t.Fatalf("test %d: encoding err\ninp %v\ngot %x\nexp %x\n", i, keyS, got, exp)
+ t.Fatalf("test %d: encoding err\ninp %v\ngot %x\nexp %x\n", i, key, got, exp)
}
}
}
@@ -97,7 +98,7 @@ func TestHexToCompactInPlaceRandom(t *testing.T) {
for i := 0; i < 10000; i++ {
l := rand.Intn(128)
key := make([]byte, l)
- rand.Read(key)
+ crand.Read(key)
hexBytes := keybytesToHex(key)
hexOrig := []byte(string(hexBytes))
exp := hexToCompact(hexBytes)
diff --git a/trie/hasher.go b/trie/hasher.go
index 183e96c229..e594d6d6b2 100644
--- a/trie/hasher.go
+++ b/trie/hasher.go
@@ -170,8 +170,8 @@ func (h *hasher) fullnodeToHash(n *fullNode, force bool) node {
//
// All node encoding must be done like this:
//
-// node.encode(h.encbuf)
-// enc := h.encodedBytes()
+// node.encode(h.encbuf)
+// enc := h.encodedBytes()
//
// This convention exists because node.encode can only be inlined/escape-analyzed when
// called on a concrete receiver type.
diff --git a/trie/iterator.go b/trie/iterator.go
index 1e76625c62..f42beec4ab 100644
--- a/trie/iterator.go
+++ b/trie/iterator.go
@@ -22,9 +22,16 @@ import (
"errors"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/core/types"
)
+// NodeResolver is used for looking up trie nodes before reaching into the real
+// persistent layer. This is not mandatory, rather is an optimization for cases
+// where trie nodes can be recovered from some external mechanism without reading
+// from disk. In those cases, this resolver allows short circuiting accesses and
+// returning them from memory.
+type NodeResolver func(owner common.Hash, path []byte, hash common.Hash) []byte
+
// Iterator is a key-value trie iterator that traverses a Trie.
type Iterator struct {
nodeIt NodeIterator
@@ -107,8 +114,8 @@ type NodeIterator interface {
// to the value after calling Next.
LeafProof() [][]byte
- // AddResolver sets an intermediate database to use for looking up trie nodes
- // before reaching into the real persistent layer.
+ // AddResolver sets a node resolver to use for looking up trie nodes before
+ // reaching into the real persistent layer.
//
// This is not required for normal operation, rather is an optimization for
// cases where trie nodes can be recovered from some external mechanism without
@@ -118,7 +125,7 @@ type NodeIterator interface {
// Before adding a similar mechanism to any other place in Geth, consider
// making trie.Database an interface and wrapping at that level. It's a huge
// refactor, but it could be worth it if another occurrence arises.
- AddResolver(ethdb.KeyValueReader)
+ AddResolver(NodeResolver)
}
// nodeIteratorState represents the iteration state at one particular node of the
@@ -137,7 +144,7 @@ type nodeIterator struct {
path []byte // Path to the current node
err error // Failure set in case of an internal error in the iterator
- resolver ethdb.KeyValueReader // Optional intermediate resolver above the disk layer
+ resolver NodeResolver // optional node resolver for avoiding disk hits
}
// errIteratorEnd is stored in nodeIterator.err when iteration is done.
@@ -154,7 +161,7 @@ func (e seekError) Error() string {
}
func newNodeIterator(trie *Trie, start []byte) NodeIterator {
- if trie.Hash() == emptyRoot {
+ if trie.Hash() == types.EmptyRootHash {
return &nodeIterator{
trie: trie,
err: errIteratorEnd,
@@ -165,7 +172,7 @@ func newNodeIterator(trie *Trie, start []byte) NodeIterator {
return it
}
-func (it *nodeIterator) AddResolver(resolver ethdb.KeyValueReader) {
+func (it *nodeIterator) AddResolver(resolver NodeResolver) {
it.resolver = resolver
}
@@ -296,7 +303,7 @@ func (it *nodeIterator) seek(prefix []byte) error {
func (it *nodeIterator) init() (*nodeIteratorState, error) {
root := it.trie.Hash()
state := &nodeIteratorState{node: it.trie.root, index: -1}
- if root != emptyRoot {
+ if root != types.EmptyRootHash {
state.hash = root
}
return state, state.resolve(it, nil)
@@ -369,22 +376,32 @@ func (it *nodeIterator) peekSeek(seekKey []byte) (*nodeIteratorState, *int, []by
func (it *nodeIterator) resolveHash(hash hashNode, path []byte) (node, error) {
if it.resolver != nil {
- if blob, err := it.resolver.Get(hash); err == nil && len(blob) > 0 {
+ if blob := it.resolver(it.trie.owner, path, common.BytesToHash(hash)); len(blob) > 0 {
if resolved, err := decodeNode(hash, blob); err == nil {
return resolved, nil
}
}
}
- return it.trie.resolveHash(hash, path)
+ // Retrieve the specified node from the underlying node reader.
+ // it.trie.resolveAndTrack is not used since in that function the
+ // loaded blob will be tracked, while it's not required here since
+ // all loaded nodes won't be linked to trie at all and track nodes
+ // may lead to out-of-memory issue.
+ return it.trie.reader.node(path, common.BytesToHash(hash))
}
func (it *nodeIterator) resolveBlob(hash hashNode, path []byte) ([]byte, error) {
if it.resolver != nil {
- if blob, err := it.resolver.Get(hash); err == nil && len(blob) > 0 {
+ if blob := it.resolver(it.trie.owner, path, common.BytesToHash(hash)); len(blob) > 0 {
return blob, nil
}
}
- return it.trie.resolveBlob(hash, path)
+ // Retrieve the specified node from the underlying node reader.
+ // it.trie.resolveAndTrack is not used since in that function the
+ // loaded blob will be tracked, while it's not required here since
+ // all loaded nodes won't be linked to trie at all and track nodes
+ // may lead to out-of-memory issue.
+ return it.trie.reader.nodeBlob(path, common.BytesToHash(hash))
}
func (st *nodeIteratorState) resolve(it *nodeIterator, path []byte) error {
@@ -579,7 +596,7 @@ func (it *differenceIterator) NodeBlob() []byte {
return it.b.NodeBlob()
}
-func (it *differenceIterator) AddResolver(resolver ethdb.KeyValueReader) {
+func (it *differenceIterator) AddResolver(resolver NodeResolver) {
panic("not implemented")
}
@@ -694,7 +711,7 @@ func (it *unionIterator) NodeBlob() []byte {
return (*it.items)[0].NodeBlob()
}
-func (it *unionIterator) AddResolver(resolver ethdb.KeyValueReader) {
+func (it *unionIterator) AddResolver(resolver NodeResolver) {
panic("not implemented")
}
diff --git a/trie/iterator_test.go b/trie/iterator_test.go
index e9d822a9a4..ac2a3d2a4c 100644
--- a/trie/iterator_test.go
+++ b/trie/iterator_test.go
@@ -60,13 +60,10 @@ func TestIterator(t *testing.T) {
all[val.k] = val.v
trie.Update([]byte(val.k), []byte(val.v))
}
- root, nodes, err := trie.Commit(false)
- if err != nil {
- t.Fatalf("Failed to commit trie %v", err)
- }
+ root, nodes := trie.Commit(false)
db.Update(NewWithNodeSet(nodes))
- trie, _ = New(common.Hash{}, root, db)
+ trie, _ = New(TrieID(root), db)
found := make(map[string]string)
it := NewIterator(trie.NodeIterator(nil))
for it.Next() {
@@ -225,18 +222,18 @@ func TestDifferenceIterator(t *testing.T) {
for _, val := range testdata1 {
triea.Update([]byte(val.k), []byte(val.v))
}
- rootA, nodesA, _ := triea.Commit(false)
+ rootA, nodesA := triea.Commit(false)
dba.Update(NewWithNodeSet(nodesA))
- triea, _ = New(common.Hash{}, rootA, dba)
+ triea, _ = New(TrieID(rootA), dba)
dbb := NewDatabase(rawdb.NewMemoryDatabase())
trieb := NewEmpty(dbb)
for _, val := range testdata2 {
trieb.Update([]byte(val.k), []byte(val.v))
}
- rootB, nodesB, _ := trieb.Commit(false)
+ rootB, nodesB := trieb.Commit(false)
dbb.Update(NewWithNodeSet(nodesB))
- trieb, _ = New(common.Hash{}, rootB, dbb)
+ trieb, _ = New(TrieID(rootB), dbb)
found := make(map[string]string)
di, _ := NewDifferenceIterator(triea.NodeIterator(nil), trieb.NodeIterator(nil))
@@ -267,18 +264,18 @@ func TestUnionIterator(t *testing.T) {
for _, val := range testdata1 {
triea.Update([]byte(val.k), []byte(val.v))
}
- rootA, nodesA, _ := triea.Commit(false)
+ rootA, nodesA := triea.Commit(false)
dba.Update(NewWithNodeSet(nodesA))
- triea, _ = New(common.Hash{}, rootA, dba)
+ triea, _ = New(TrieID(rootA), dba)
dbb := NewDatabase(rawdb.NewMemoryDatabase())
trieb := NewEmpty(dbb)
for _, val := range testdata2 {
trieb.Update([]byte(val.k), []byte(val.v))
}
- rootB, nodesB, _ := trieb.Commit(false)
+ rootB, nodesB := trieb.Commit(false)
dbb.Update(NewWithNodeSet(nodesB))
- trieb, _ = New(common.Hash{}, rootB, dbb)
+ trieb, _ = New(TrieID(rootB), dbb)
di, _ := NewUnionIterator([]NodeIterator{triea.NodeIterator(nil), trieb.NodeIterator(nil)})
it := NewIterator(di)
@@ -327,17 +324,17 @@ func TestIteratorContinueAfterErrorDisk(t *testing.T) { testIteratorContinueA
func TestIteratorContinueAfterErrorMemonly(t *testing.T) { testIteratorContinueAfterError(t, true) }
func testIteratorContinueAfterError(t *testing.T, memonly bool) {
- diskdb := memorydb.New()
+ diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)
tr := NewEmpty(triedb)
for _, val := range testdata1 {
tr.Update([]byte(val.k), []byte(val.v))
}
- _, nodes, _ := tr.Commit(false)
+ _, nodes := tr.Commit(false)
triedb.Update(NewWithNodeSet(nodes))
if !memonly {
- triedb.Commit(tr.Hash(), true, nil)
+ triedb.Commit(tr.Hash(), false)
}
wantNodeCount := checkIteratorNoDups(t, tr.NodeIterator(nil), nil)
@@ -356,7 +353,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) {
}
for i := 0; i < 20; i++ {
// Create trie that will load all nodes from DB.
- tr, _ := New(common.Hash{}, tr.Hash(), triedb)
+ tr, _ := New(TrieID(tr.Hash()), triedb)
// Remove a random node from the database. It can't be the root node
// because that one is already loaded.
@@ -419,17 +416,17 @@ func TestIteratorContinueAfterSeekErrorMemonly(t *testing.T) {
func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
// Commit test trie to db, then remove the node containing "bars".
- diskdb := memorydb.New()
+ diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)
ctr := NewEmpty(triedb)
for _, val := range testdata1 {
ctr.Update([]byte(val.k), []byte(val.v))
}
- root, nodes, _ := ctr.Commit(false)
+ root, nodes := ctr.Commit(false)
triedb.Update(NewWithNodeSet(nodes))
if !memonly {
- triedb.Commit(root, true, nil)
+ triedb.Commit(root, false)
}
barNodeHash := common.HexToHash("05041990364eb72fcb1127652ce40d8bab765f2bfe53225b1170d276cc101c2e")
var (
@@ -445,7 +442,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
}
// Create a new iterator that seeks to "bars". Seeking can't proceed because
// the node is missing.
- tr, _ := New(common.Hash{}, root, triedb)
+ tr, _ := New(TrieID(root), triedb)
it := tr.NodeIterator([]byte("bars"))
missing, ok := it.Error().(*MissingNodeError)
if !ok {
@@ -532,8 +529,8 @@ func (l *loggingDb) Close() error {
func makeLargeTestTrie() (*Database, *StateTrie, *loggingDb) {
// Create an empty trie
logDb := &loggingDb{0, memorydb.New()}
- triedb := NewDatabase(logDb)
- trie, _ := NewStateTrie(common.Hash{}, common.Hash{}, triedb)
+ triedb := NewDatabase(rawdb.NewDatabase(logDb))
+ trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
// Fill it with some arbitrary data
for i := 0; i < 10000; i++ {
@@ -545,7 +542,7 @@ func makeLargeTestTrie() (*Database, *StateTrie, *loggingDb) {
val = crypto.Keccak256(val)
trie.Update(key, val)
}
- _, nodes, _ := trie.Commit(false)
+ _, nodes := trie.Commit(false)
triedb.Update(NewWithNodeSet(nodes))
// Return the generated trie
return triedb, trie, logDb
@@ -567,7 +564,7 @@ func TestNodeIteratorLargeTrie(t *testing.T) {
func TestIteratorNodeBlob(t *testing.T) {
var (
- db = memorydb.New()
+ db = rawdb.NewMemoryDatabase()
triedb = NewDatabase(db)
trie = NewEmpty(triedb)
)
@@ -585,7 +582,7 @@ func TestIteratorNodeBlob(t *testing.T) {
all[val.k] = val.v
trie.Update([]byte(val.k), []byte(val.v))
}
- _, nodes, _ := trie.Commit(false)
+ _, nodes := trie.Commit(false)
triedb.Update(NewWithNodeSet(nodes))
triedb.Cap(0)
diff --git a/trie/nodeset.go b/trie/nodeset.go
index 08b9b35ebc..9281723501 100644
--- a/trie/nodeset.go
+++ b/trie/nodeset.go
@@ -18,6 +18,8 @@ package trie
import (
"fmt"
+ "reflect"
+ "strings"
"github.com/ethereum/go-ethereum/common"
)
@@ -25,18 +27,77 @@ import (
// memoryNode is all the information we know about a single cached trie node
// in the memory.
type memoryNode struct {
- hash common.Hash // Node hash, computed by hashing rlp value
- size uint16 // Byte size of the useful cached data
- node node // Cached collapsed trie node, or raw rlp data
+ hash common.Hash // Node hash, computed by hashing rlp value, empty for deleted nodes
+ size uint16 // Byte size of the useful cached data, 0 for deleted nodes
+ node node // Cached collapsed trie node, or raw rlp data, nil for deleted nodes
+}
+
+// memoryNodeSize is the raw size of a memoryNode data structure without any
+// node data included. It's an approximate size, but should be a lot better
+// than not counting them.
+// nolint:unused
+var memoryNodeSize = int(reflect.TypeOf(memoryNode{}).Size())
+
+// memorySize returns the total memory size used by this node.
+// nolint:unused
+func (n *memoryNode) memorySize(key int) int {
+ return int(n.size) + memoryNodeSize + key
+}
+
+// rlp returns the raw rlp encoded blob of the cached trie node, either directly
+// from the cache, or by regenerating it from the collapsed node.
+// nolint:unused
+func (n *memoryNode) rlp() []byte {
+ if node, ok := n.node.(rawNode); ok {
+ return node
+ }
+ return nodeToBytes(n.node)
+}
+
+// obj returns the decoded and expanded trie node, either directly from the cache,
+// or by regenerating it from the rlp encoded blob.
+// nolint:unused
+func (n *memoryNode) obj() node {
+ if node, ok := n.node.(rawNode); ok {
+ return mustDecodeNode(n.hash[:], node)
+ }
+ return expandNode(n.hash[:], n.node)
+}
+
+// nodeWithPrev wraps the memoryNode with the previous node value.
+type nodeWithPrev struct {
+ *memoryNode
+ prev []byte // RLP-encoded previous value, nil means it's non-existent
+}
+
+// unwrap returns the internal memoryNode object.
+// nolint:unused
+func (n *nodeWithPrev) unwrap() *memoryNode {
+ return n.memoryNode
+}
+
+// memorySize returns the total memory size used by this node. It overloads
+// the function in memoryNode by counting the size of previous value as well.
+// nolint: unused
+func (n *nodeWithPrev) memorySize(key int) int {
+ return n.memoryNode.memorySize(key) + len(n.prev)
+}
+
+// nodesWithOrder represents a collection of dirty nodes which includes
+// newly-inserted and updated nodes. The modification order of all nodes
+// is represented by order list.
+type nodesWithOrder struct {
+ order []string // the path list of dirty nodes, sort by insertion order
+ nodes map[string]*nodeWithPrev // the map of dirty nodes, keyed by node path
}
// NodeSet contains all dirty nodes collected during the commit operation.
// Each node is keyed by path. It's not thread-safe to use.
type NodeSet struct {
- owner common.Hash // the identifier of the trie
- paths []string // the path of dirty nodes, sort by insertion order
- nodes map[string]*memoryNode // the map of dirty nodes, keyed by node path
- leaves []*leaf // the list of dirty leaves
+ owner common.Hash // the identifier of the trie
+ updates *nodesWithOrder // the set of updated nodes(newly inserted, updated)
+ deletes map[string][]byte // the map of deleted nodes, keyed by node
+ leaves []*leaf // the list of dirty leaves
}
// NewNodeSet initializes an empty node set to be used for tracking dirty nodes
@@ -45,24 +106,80 @@ type NodeSet struct {
func NewNodeSet(owner common.Hash) *NodeSet {
return &NodeSet{
owner: owner,
- nodes: make(map[string]*memoryNode),
+ updates: &nodesWithOrder{
+ nodes: make(map[string]*nodeWithPrev),
+ },
+ deletes: make(map[string][]byte),
+ }
+}
+
+/*
+// NewNodeSetWithDeletion initializes the nodeset with provided deletion set.
+func NewNodeSetWithDeletion(owner common.Hash, paths [][]byte, prev [][]byte) *NodeSet {
+ set := NewNodeSet(owner)
+ for i, path := range paths {
+ set.markDeleted(path, prev[i])
}
+ return set
}
+*/
-// add caches node with provided path and node object.
-func (set *NodeSet) add(path string, node *memoryNode) {
- set.paths = append(set.paths, path)
- set.nodes[path] = node
+// markUpdated marks the node as dirty(newly-inserted or updated) with provided
+// node path, node object along with its previous value.
+func (set *NodeSet) markUpdated(path []byte, node *memoryNode, prev []byte) {
+ set.updates.order = append(set.updates.order, string(path))
+ set.updates.nodes[string(path)] = &nodeWithPrev{
+ memoryNode: node,
+ prev: prev,
+ }
+}
+
+// markDeleted marks the node as deleted with provided path and previous value.
+func (set *NodeSet) markDeleted(path []byte, prev []byte) {
+ set.deletes[string(path)] = prev
}
-// addLeaf caches the provided leaf node.
+// addLeaf collects the provided leaf node into set.
func (set *NodeSet) addLeaf(node *leaf) {
set.leaves = append(set.leaves, node)
}
-// Len returns the number of dirty nodes contained in the set.
-func (set *NodeSet) Len() int {
- return len(set.nodes)
+// Size returns the number of updated and deleted nodes contained in the set.
+func (set *NodeSet) Size() (int, int) {
+ return len(set.updates.order), len(set.deletes)
+}
+
+// Hashes returns the hashes of all updated nodes. TODO(rjl493456442) how can
+// we get rid of it?
+func (set *NodeSet) Hashes() []common.Hash {
+ var ret []common.Hash
+ for _, node := range set.updates.nodes {
+ ret = append(ret, node.hash)
+ }
+ return ret
+}
+
+// Summary returns a string-representation of the NodeSet.
+func (set *NodeSet) Summary() string {
+ var out = new(strings.Builder)
+ fmt.Fprintf(out, "nodeset owner: %v\n", set.owner)
+ if set.updates != nil {
+ for _, key := range set.updates.order {
+ updated := set.updates.nodes[key]
+ if updated.prev != nil {
+ fmt.Fprintf(out, " [*]: %x -> %v prev: %x\n", key, updated.hash, updated.prev)
+ } else {
+ fmt.Fprintf(out, " [+]: %x -> %v\n", key, updated.hash)
+ }
+ }
+ }
+ for k, n := range set.deletes {
+ fmt.Fprintf(out, " [-]: %x -> %x\n", k, n)
+ }
+ for _, n := range set.leaves {
+ fmt.Fprintf(out, "[leaf]: %v\n", n)
+ }
+ return out.String()
}
// MergedNodeSet represents a merged dirty node set for a group of tries.
diff --git a/trie/proof.go b/trie/proof.go
index fa8361eefd..af49ce36b3 100644
--- a/trie/proof.go
+++ b/trie/proof.go
@@ -22,7 +22,6 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
@@ -60,10 +59,15 @@ func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) e
key = key[1:]
nodes = append(nodes, n)
case hashNode:
+ // Retrieve the specified node from the underlying node reader.
+ // trie.resolveAndTrack is not used since in that function the
+ // loaded blob will be tracked, while it's not required here since
+ // all loaded nodes won't be linked to trie at all and track nodes
+ // may lead to out-of-memory issue.
var err error
- tn, err = t.resolveHash(n, prefix)
+ tn, err = t.reader.node(prefix, common.BytesToHash(n))
if err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ log.Error("Unhandled trie error in Trie.Prove", "err", err)
return err
}
default:
@@ -339,9 +343,9 @@ findFork:
// unset removes all internal node references either the left most or right most.
// It can meet these scenarios:
//
-// - The given path is existent in the trie, unset the associated nodes with the
-// specific direction
-// - The given path is non-existent in the trie
+// - The given path is existent in the trie, unset the associated nodes with the
+// specific direction
+// - The given path is non-existent in the trie
// - the fork point is a fullnode, the corresponding child pointed by path
// is nil, return
// - the fork point is a shortnode, the shortnode is included in the range,
@@ -369,7 +373,7 @@ func unset(parent node, child node, key []byte, pos int, removeLeft bool) error
if removeLeft {
if bytes.Compare(cld.Key, key[pos:]) < 0 {
// The key of fork shortnode is less than the path
- // (it belongs to the range), unset the entrie
+ // (it belongs to the range), unset the entire
// branch. The parent must be a fullnode.
fn := parent.(*fullNode)
fn.Children[key[pos-1]] = nil
@@ -458,15 +462,15 @@ func hasRightElement(node node, key []byte) bool {
// Expect the normal case, this function can also be used to verify the following
// range proofs:
//
-// - All elements proof. In this case the proof can be nil, but the range should
-// be all the leaves in the trie.
+// - All elements proof. In this case the proof can be nil, but the range should
+// be all the leaves in the trie.
//
-// - One element proof. In this case no matter the edge proof is a non-existent
-// proof or not, we can always verify the correctness of the proof.
+// - One element proof. In this case no matter the edge proof is a non-existent
+// proof or not, we can always verify the correctness of the proof.
//
-// - Zero element proof. In this case a single non-existent proof is enough to prove.
-// Besides, if there are still some other leaves available on the right side, then
-// an error will be returned.
+// - Zero element proof. In this case a single non-existent proof is enough to prove.
+// Besides, if there are still some other leaves available on the right side, then
+// an error will be returned.
//
// Except returning the error to indicate the proof is valid or not, the function will
// also return a flag to indicate whether there exists more accounts/slots in the trie.
@@ -559,7 +563,7 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, key
}
// Rebuild the trie with the leaf stream, the shape of trie
// should be same with the original one.
- tr := &Trie{root: root, db: NewDatabase(rawdb.NewMemoryDatabase())}
+ tr := &Trie{root: root, reader: newEmptyReader()}
if empty {
tr.root = nil
}
diff --git a/trie/proof_test.go b/trie/proof_test.go
index 61667b20ab..5796a30893 100644
--- a/trie/proof_test.go
+++ b/trie/proof_test.go
@@ -23,7 +23,6 @@ import (
mrand "math/rand"
"sort"
"testing"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -31,10 +30,6 @@ import (
"github.com/ethereum/go-ethereum/ethdb/memorydb"
)
-func init() {
- mrand.Seed(time.Now().Unix())
-}
-
// makeProvers creates Merkle trie provers based on different implementations to
// test all variations.
func makeProvers(trie *Trie) []func(key []byte) *memorydb.Database {
diff --git a/trie/secure_trie.go b/trie/secure_trie.go
index 3d468f56ee..83b92cebd2 100644
--- a/trie/secure_trie.go
+++ b/trie/secure_trie.go
@@ -17,8 +17,6 @@
package trie
import (
- "fmt"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
@@ -31,18 +29,23 @@ type SecureTrie = StateTrie
// NewSecure creates a new StateTrie.
// Deprecated: use NewStateTrie.
-func NewSecure(owner common.Hash, root common.Hash, db *Database) (*SecureTrie, error) {
- return NewStateTrie(owner, root, db)
+func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db *Database) (*SecureTrie, error) {
+ id := &ID{
+ StateRoot: stateRoot,
+ Owner: owner,
+ Root: root,
+ }
+ return NewStateTrie(id, db)
}
-// StateTrie wraps a trie with key hashing. In a secure trie, all
+// StateTrie wraps a trie with key hashing. In a stateTrie trie, all
// access operations hash the key using keccak256. This prevents
// calling code from creating long chains of nodes that
// increase the access time.
//
// Contrary to a regular trie, a StateTrie can only be created with
// New and must have an attached database. The database also stores
-// the preimage of each key.
+// the preimage of each key if preimage recording is enabled.
//
// StateTrie is not safe for concurrent use.
type StateTrie struct {
@@ -53,22 +56,16 @@ type StateTrie struct {
secKeyCacheOwner *StateTrie // Pointer to self, replace the key cache on mismatch
}
-// NewStateTrie creates a trie with an existing root node from a backing database
-// and optional intermediate in-memory node pool.
+// NewStateTrie creates a trie with an existing root node from a backing database.
//
// If root is the zero hash or the sha3 hash of an empty string, the
// trie is initially empty. Otherwise, New will panic if db is nil
// and returns MissingNodeError if the root node cannot be found.
-//
-// Accessing the trie loads nodes from the database or node pool on demand.
-// Loaded nodes are kept around until their 'cache generation' expires.
-// A new cache generation is created by each call to Commit.
-// cachelimit sets the number of past cache generations to keep.
-func NewStateTrie(owner common.Hash, root common.Hash, db *Database) (*StateTrie, error) {
+func NewStateTrie(id *ID, db *Database) (*StateTrie, error) {
if db == nil {
- panic("trie.NewSecure called without a database")
+ panic("trie.NewStateTrie called without a database")
}
- trie, err := New(owner, root, db)
+ trie, err := New(id, db)
if err != nil {
return nil, err
}
@@ -80,70 +77,53 @@ func NewStateTrie(owner common.Hash, root common.Hash, db *Database) (*StateTrie
func (t *StateTrie) Get(key []byte) []byte {
res, err := t.TryGet(key)
if err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ log.Error("Unhandled trie error in StateTrie.Get", "err", err)
}
return res
}
// TryGet returns the value for key stored in the trie.
// The value bytes must not be modified by the caller.
-// If a node was not found in the database, a MissingNodeError is returned.
+// If the specified node is not in the trie, nil will be returned.
+// If a trie node is not found in the database, a MissingNodeError is returned.
func (t *StateTrie) TryGet(key []byte) ([]byte, error) {
return t.trie.TryGet(t.hashKey(key))
}
-func (t *StateTrie) TryGetAccount(key []byte) (*types.StateAccount, error) {
- var ret types.StateAccount
- res, err := t.trie.TryGet(t.hashKey(key))
- if err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
- return &ret, err
- }
- if res == nil {
- return nil, nil
+// TryGetAccount attempts to retrieve an account with provided account address.
+// If the specified account is not in the trie, nil will be returned.
+// If a trie node is not found in the database, a MissingNodeError is returned.
+func (t *StateTrie) TryGetAccount(address common.Address) (*types.StateAccount, error) {
+ res, err := t.trie.TryGet(t.hashKey(address.Bytes()))
+ if res == nil || err != nil {
+ return nil, err
}
- err = rlp.DecodeBytes(res, &ret)
- return &ret, err
+ ret := new(types.StateAccount)
+ err = rlp.DecodeBytes(res, ret)
+ return ret, err
}
-// TryGetAccountWithPreHashedKey does the same thing as TryGetAccount, however
-// it expects a key that is already hashed. This constitutes an abstraction leak,
-// since the client code needs to know the key format.
-func (t *StateTrie) TryGetAccountWithPreHashedKey(key []byte) (*types.StateAccount, error) {
- var ret types.StateAccount
- res, err := t.trie.TryGet(key)
- if err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
- return &ret, err
- }
- if res == nil {
- return nil, nil
+// TryGetAccountByHash does the same thing as TryGetAccount, however
+// it expects an account hash that is the hash of address. This constitutes an
+// abstraction leak, since the client code needs to know the key format.
+func (t *StateTrie) TryGetAccountByHash(addrHash common.Hash) (*types.StateAccount, error) {
+ res, err := t.trie.TryGet(addrHash.Bytes())
+ if res == nil || err != nil {
+ return nil, err
}
- err = rlp.DecodeBytes(res, &ret)
- return &ret, err
+ ret := new(types.StateAccount)
+ err = rlp.DecodeBytes(res, ret)
+ return ret, err
}
// TryGetNode attempts to retrieve a trie node by compact-encoded path. It is not
// possible to use keybyte-encoding as the path might contain odd nibbles.
+// If the specified trie node is not in the trie, nil will be returned.
+// If a trie node is not found in the database, a MissingNodeError is returned.
func (t *StateTrie) TryGetNode(path []byte) ([]byte, int, error) {
return t.trie.TryGetNode(path)
}
-// TryUpdateAccount account will abstract the write of an account to the
-// secure trie.
-func (t *StateTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
- hk := t.hashKey(key)
- data, err := rlp.EncodeToBytes(acc)
- if err != nil {
- return err
- }
- if err := t.trie.TryUpdate(hk, data); err != nil {
- return err
- }
- t.getSecKeyCache()[string(hk)] = common.CopyBytes(key)
- return nil
-}
-
// Update associates key with value in the trie. Subsequent calls to
// Get will return value. If value has length zero, any existing value
// is deleted from the trie and calls to Get will return nil.
@@ -152,7 +132,7 @@ func (t *StateTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error
// stored in the trie.
func (t *StateTrie) Update(key, value []byte) {
if err := t.TryUpdate(key, value); err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ log.Error("Unhandled trie error in StateTrie.Update", "err", err)
}
}
@@ -163,7 +143,7 @@ func (t *StateTrie) Update(key, value []byte) {
// The value bytes must not be modified by the caller while they are
// stored in the trie.
//
-// If a node was not found in the database, a MissingNodeError is returned.
+// If a node is not found in the database, a MissingNodeError is returned.
func (t *StateTrie) TryUpdate(key, value []byte) error {
hk := t.hashKey(key)
err := t.trie.TryUpdate(hk, value)
@@ -174,24 +154,40 @@ func (t *StateTrie) TryUpdate(key, value []byte) error {
return nil
}
+// TryUpdateAccount account will abstract the write of an account to the
+// secure trie.
+func (t *StateTrie) TryUpdateAccount(address common.Address, acc *types.StateAccount) error {
+ hk := t.hashKey(address.Bytes())
+ data, err := rlp.EncodeToBytes(acc)
+ if err != nil {
+ return err
+ }
+ if err := t.trie.TryUpdate(hk, data); err != nil {
+ return err
+ }
+ t.getSecKeyCache()[string(hk)] = address.Bytes()
+ return nil
+}
+
// Delete removes any existing value for key from the trie.
func (t *StateTrie) Delete(key []byte) {
if err := t.TryDelete(key); err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ log.Error("Unhandled trie error in StateTrie.Delete", "err", err)
}
}
// TryDelete removes any existing value for key from the trie.
-// If a node was not found in the database, a MissingNodeError is returned.
+// If the specified trie node is not in the trie, nothing will be changed.
+// If a node is not found in the database, a MissingNodeError is returned.
func (t *StateTrie) TryDelete(key []byte) error {
hk := t.hashKey(key)
delete(t.getSecKeyCache(), string(hk))
return t.trie.TryDelete(hk)
}
-// TryDeleteACcount abstracts an account deletion from the trie.
-func (t *StateTrie) TryDeleteAccount(key []byte) error {
- hk := t.hashKey(key)
+// TryDeleteAccount abstracts an account deletion from the trie.
+func (t *StateTrie) TryDeleteAccount(address common.Address) error {
+ hk := t.hashKey(address.Bytes())
delete(t.getSecKeyCache(), string(hk))
return t.trie.TryDelete(hk)
}
@@ -208,14 +204,14 @@ func (t *StateTrie) GetKey(shaKey []byte) []byte {
return t.preimages.preimage(common.BytesToHash(shaKey))
}
-// Commit collects all dirty nodes in the trie and replace them with the
-// corresponding node hash. All collected nodes(including dirty leaves if
+// Commit collects all dirty nodes in the trie and replaces them with the
+// corresponding node hash. All collected nodes (including dirty leaves if
// collectLeaf is true) will be encapsulated into a nodeset for return.
-// The returned nodeset can be nil if the trie is clean(nothing to commit).
+// The returned nodeset can be nil if the trie is clean (nothing to commit).
// All cached preimages will be also flushed if preimages recording is enabled.
// Once the trie is committed, it's not usable anymore. A new trie must
// be created with new root and updated trie database for following usage
-func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *NodeSet, error) {
+func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *NodeSet) {
// Write all the pre-images to the actual disk database
if len(t.getSecKeyCache()) > 0 {
if t.preimages != nil {
@@ -227,7 +223,7 @@ func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *NodeSet, error) {
}
t.secKeyCache = make(map[string][]byte)
}
- // Commit the trie to its intermediate node database
+ // Commit the trie and return its modified nodeset.
return t.trie.Commit(collectLeaf)
}
diff --git a/trie/secure_trie_test.go b/trie/secure_trie_test.go
index 862c3a3ec4..d3e6c67069 100644
--- a/trie/secure_trie_test.go
+++ b/trie/secure_trie_test.go
@@ -24,20 +24,20 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
)
func newEmptySecure() *StateTrie {
- trie, _ := NewStateTrie(common.Hash{}, common.Hash{}, NewDatabase(memorydb.New()))
+ trie, _ := NewStateTrie(TrieID(common.Hash{}), NewDatabase(rawdb.NewMemoryDatabase()))
return trie
}
// makeTestStateTrie creates a large enough secure trie for testing.
func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) {
// Create an empty trie
- triedb := NewDatabase(memorydb.New())
- trie, _ := NewStateTrie(common.Hash{}, common.Hash{}, triedb)
+ triedb := NewDatabase(rawdb.NewMemoryDatabase())
+ trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
// Fill it with some arbitrary data
content := make(map[string][]byte)
@@ -58,15 +58,12 @@ func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) {
trie.Update(key, val)
}
}
- root, nodes, err := trie.Commit(false)
- if err != nil {
- panic(fmt.Errorf("failed to commit trie %v", err))
- }
+ root, nodes := trie.Commit(false)
if err := triedb.Update(NewWithNodeSet(nodes)); err != nil {
panic(fmt.Errorf("failed to commit db %v", err))
}
// Re-create the trie based on the new state
- trie, _ = NewSecure(common.Hash{}, root, triedb)
+ trie, _ = NewStateTrie(TrieID(root), triedb)
return triedb, trie, content
}
diff --git a/trie/stacktrie.go b/trie/stacktrie.go
index a22fa0dd67..e7b3171af6 100644
--- a/trie/stacktrie.go
+++ b/trie/stacktrie.go
@@ -21,12 +21,11 @@ import (
"bytes"
"encoding/gob"
"errors"
- "fmt"
"io"
"sync"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
@@ -38,10 +37,14 @@ var stPool = sync.Pool{
},
}
-func stackTrieFromPool(db ethdb.KeyValueWriter, owner common.Hash) *StackTrie {
+// NodeWriteFunc is used to provide all information of a dirty node for committing
+// so that callers can flush nodes into database with desired scheme.
+type NodeWriteFunc = func(owner common.Hash, path []byte, hash common.Hash, blob []byte)
+
+func stackTrieFromPool(writeFn NodeWriteFunc, owner common.Hash) *StackTrie {
st := stPool.Get().(*StackTrie)
- st.db = db
st.owner = owner
+ st.writeFn = writeFn
return st
}
@@ -54,41 +57,41 @@ func returnToPool(st *StackTrie) {
// in order. Once it determines that a subtree will no longer be inserted
// into, it will hash it and free up the memory it uses.
type StackTrie struct {
- owner common.Hash // the owner of the trie
- nodeType uint8 // node type (as in branch, ext, leaf)
- val []byte // value contained by this node if it's a leaf
- key []byte // key chunk covered by this (leaf|ext) node
- children [16]*StackTrie // list of children (for branch and exts)
- db ethdb.KeyValueWriter // Pointer to the commit db, can be nil
+ owner common.Hash // the owner of the trie
+ nodeType uint8 // node type (as in branch, ext, leaf)
+ val []byte // value contained by this node if it's a leaf
+ key []byte // key chunk covered by this (leaf|ext) node
+ children [16]*StackTrie // list of children (for branch and exts)
+ writeFn NodeWriteFunc // function for committing nodes, can be nil
}
// NewStackTrie allocates and initializes an empty trie.
-func NewStackTrie(db ethdb.KeyValueWriter) *StackTrie {
+func NewStackTrie(writeFn NodeWriteFunc) *StackTrie {
return &StackTrie{
nodeType: emptyNode,
- db: db,
+ writeFn: writeFn,
}
}
// NewStackTrieWithOwner allocates and initializes an empty trie, but with
// the additional owner field.
-func NewStackTrieWithOwner(db ethdb.KeyValueWriter, owner common.Hash) *StackTrie {
+func NewStackTrieWithOwner(writeFn NodeWriteFunc, owner common.Hash) *StackTrie {
return &StackTrie{
owner: owner,
nodeType: emptyNode,
- db: db,
+ writeFn: writeFn,
}
}
// NewFromBinary initialises a serialized stacktrie with the given db.
-func NewFromBinary(data []byte, db ethdb.KeyValueWriter) (*StackTrie, error) {
+func NewFromBinary(data []byte, writeFn NodeWriteFunc) (*StackTrie, error) {
var st StackTrie
if err := st.UnmarshalBinary(data); err != nil {
return nil, err
}
// If a database is used, we need to recursively add it to every child
- if db != nil {
- st.setDb(db)
+ if writeFn != nil {
+ st.setWriter(writeFn)
}
return &st, nil
}
@@ -161,25 +164,25 @@ func (st *StackTrie) unmarshalBinary(r io.Reader) error {
return nil
}
-func (st *StackTrie) setDb(db ethdb.KeyValueWriter) {
- st.db = db
+func (st *StackTrie) setWriter(writeFn NodeWriteFunc) {
+ st.writeFn = writeFn
for _, child := range st.children {
if child != nil {
- child.setDb(db)
+ child.setWriter(writeFn)
}
}
}
-func newLeaf(owner common.Hash, key, val []byte, db ethdb.KeyValueWriter) *StackTrie {
- st := stackTrieFromPool(db, owner)
+func newLeaf(owner common.Hash, key, val []byte, writeFn NodeWriteFunc) *StackTrie {
+ st := stackTrieFromPool(writeFn, owner)
st.nodeType = leafNode
st.key = append(st.key, key...)
st.val = val
return st
}
-func newExt(owner common.Hash, key []byte, child *StackTrie, db ethdb.KeyValueWriter) *StackTrie {
- st := stackTrieFromPool(db, owner)
+func newExt(owner common.Hash, key []byte, child *StackTrie, writeFn NodeWriteFunc) *StackTrie {
+ st := stackTrieFromPool(writeFn, owner)
st.nodeType = extNode
st.key = append(st.key, key...)
st.children[0] = child
@@ -201,19 +204,19 @@ func (st *StackTrie) TryUpdate(key, value []byte) error {
if len(value) == 0 {
panic("deletion not supported")
}
- st.insert(k[:len(k)-1], value)
+ st.insert(k[:len(k)-1], value, nil)
return nil
}
func (st *StackTrie) Update(key, value []byte) {
if err := st.TryUpdate(key, value); err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ log.Error("Unhandled trie error in StackTrie.Update", "err", err)
}
}
func (st *StackTrie) Reset() {
st.owner = common.Hash{}
- st.db = nil
+ st.writeFn = nil
st.key = st.key[:0]
st.val = nil
for i := range st.children {
@@ -236,7 +239,7 @@ func (st *StackTrie) getDiffIndex(key []byte) int {
// Helper function to that inserts a (key, value) pair into
// the trie.
-func (st *StackTrie) insert(key, value []byte) {
+func (st *StackTrie) insert(key, value []byte, prefix []byte) {
switch st.nodeType {
case branchNode: /* Branch */
idx := int(key[0])
@@ -245,7 +248,7 @@ func (st *StackTrie) insert(key, value []byte) {
for i := idx - 1; i >= 0; i-- {
if st.children[i] != nil {
if st.children[i].nodeType != hashedNode {
- st.children[i].hash()
+ st.children[i].hash(append(prefix, byte(i)))
}
break
}
@@ -253,9 +256,9 @@ func (st *StackTrie) insert(key, value []byte) {
// Add new child
if st.children[idx] == nil {
- st.children[idx] = newLeaf(st.owner, key[1:], value, st.db)
+ st.children[idx] = newLeaf(st.owner, key[1:], value, st.writeFn)
} else {
- st.children[idx].insert(key[1:], value)
+ st.children[idx].insert(key[1:], value, append(prefix, key[0]))
}
case extNode: /* Ext */
@@ -270,7 +273,7 @@ func (st *StackTrie) insert(key, value []byte) {
if diffidx == len(st.key) {
// Ext key and key segment are identical, recurse into
// the child node.
- st.children[0].insert(key[diffidx:], value)
+ st.children[0].insert(key[diffidx:], value, append(prefix, key[:diffidx]...))
return
}
// Save the original part. Depending if the break is
@@ -279,14 +282,19 @@ func (st *StackTrie) insert(key, value []byte) {
// node directly.
var n *StackTrie
if diffidx < len(st.key)-1 {
- n = newExt(st.owner, st.key[diffidx+1:], st.children[0], st.db)
+ // Break on the non-last byte, insert an intermediate
+ // extension. The path prefix of the newly-inserted
+ // extension should also contain the different byte.
+ n = newExt(st.owner, st.key[diffidx+1:], st.children[0], st.writeFn)
+ n.hash(append(prefix, st.key[:diffidx+1]...))
} else {
// Break on the last byte, no need to insert
- // an extension node: reuse the current node
+ // an extension node: reuse the current node.
+ // The path prefix of the original part should
+ // still be same.
n = st.children[0]
+ n.hash(append(prefix, st.key...))
}
- // Convert to hash
- n.hash()
var p *StackTrie
if diffidx == 0 {
// the break is on the first byte, so
@@ -299,12 +307,12 @@ func (st *StackTrie) insert(key, value []byte) {
// the common prefix is at least one byte
// long, insert a new intermediate branch
// node.
- st.children[0] = stackTrieFromPool(st.db, st.owner)
+ st.children[0] = stackTrieFromPool(st.writeFn, st.owner)
st.children[0].nodeType = branchNode
p = st.children[0]
}
// Create a leaf for the inserted part
- o := newLeaf(st.owner, key[diffidx+1:], value, st.db)
+ o := newLeaf(st.owner, key[diffidx+1:], value, st.writeFn)
// Insert both child leaves where they belong:
origIdx := st.key[diffidx]
@@ -340,7 +348,7 @@ func (st *StackTrie) insert(key, value []byte) {
// Convert current node into an ext,
// and insert a child branch node.
st.nodeType = extNode
- st.children[0] = NewStackTrieWithOwner(st.db, st.owner)
+ st.children[0] = NewStackTrieWithOwner(st.writeFn, st.owner)
st.children[0].nodeType = branchNode
p = st.children[0]
}
@@ -349,11 +357,11 @@ func (st *StackTrie) insert(key, value []byte) {
// value and another containing the new value. The child leaf
// is hashed directly in order to free up some memory.
origIdx := st.key[diffidx]
- p.children[origIdx] = newLeaf(st.owner, st.key[diffidx+1:], st.val, st.db)
- p.children[origIdx].hash()
+ p.children[origIdx] = newLeaf(st.owner, st.key[diffidx+1:], st.val, st.writeFn)
+ p.children[origIdx].hash(append(prefix, st.key[:diffidx+1]...))
newIdx := key[diffidx]
- p.children[newIdx] = newLeaf(st.owner, key[diffidx+1:], value, st.db)
+ p.children[newIdx] = newLeaf(st.owner, key[diffidx+1:], value, st.writeFn)
// Finally, cut off the key part that has been passed
// over to the children.
@@ -376,21 +384,22 @@ func (st *StackTrie) insert(key, value []byte) {
// hash converts st into a 'hashedNode', if possible. Possible outcomes:
//
// 1. The rlp-encoded value was >= 32 bytes:
-// - Then the 32-byte `hash` will be accessible in `st.val`.
-// - And the 'st.type' will be 'hashedNode'
+// - Then the 32-byte `hash` will be accessible in `st.val`.
+// - And the 'st.type' will be 'hashedNode'
+//
// 2. The rlp-encoded value was < 32 bytes
-// - Then the <32 byte rlp-encoded value will be accessible in 'st.val'.
-// - And the 'st.type' will be 'hashedNode' AGAIN
+// - Then the <32 byte rlp-encoded value will be accessible in 'st.val'.
+// - And the 'st.type' will be 'hashedNode' AGAIN
//
// This method also sets 'st.type' to hashedNode, and clears 'st.key'.
-func (st *StackTrie) hash() {
+func (st *StackTrie) hash(path []byte) {
h := newHasher(false)
defer returnHasherToPool(h)
- st.hashRec(h)
+ st.hashRec(h, path)
}
-func (st *StackTrie) hashRec(hasher *hasher) {
+func (st *StackTrie) hashRec(hasher *hasher, path []byte) {
// The switch below sets this to the RLP-encoding of this node.
var encodedNode []byte
@@ -399,7 +408,7 @@ func (st *StackTrie) hashRec(hasher *hasher) {
return
case emptyNode:
- st.val = emptyRoot.Bytes()
+ st.val = types.EmptyRootHash.Bytes()
st.key = st.key[:0]
st.nodeType = hashedNode
return
@@ -411,8 +420,7 @@ func (st *StackTrie) hashRec(hasher *hasher) {
nodes[i] = nilValueNode
continue
}
-
- child.hashRec(hasher)
+ child.hashRec(hasher, append(path, byte(i)))
if len(child.val) < 32 {
nodes[i] = rawNode(child.val)
} else {
@@ -428,10 +436,9 @@ func (st *StackTrie) hashRec(hasher *hasher) {
encodedNode = hasher.encodedBytes()
case extNode:
- st.children[0].hashRec(hasher)
+ st.children[0].hashRec(hasher, append(path, st.key...))
- sz := hexToCompactInPlace(st.key)
- n := rawShortNode{Key: st.key[:sz]}
+ n := rawShortNode{Key: hexToCompact(st.key)}
if len(st.children[0].val) < 32 {
n.Val = rawNode(st.children[0].val)
} else {
@@ -447,8 +454,7 @@ func (st *StackTrie) hashRec(hasher *hasher) {
case leafNode:
st.key = append(st.key, byte(16))
- sz := hexToCompactInPlace(st.key)
- n := rawShortNode{Key: st.key[:sz], Val: valueNode(st.val)}
+ n := rawShortNode{Key: hexToCompact(st.key), Val: valueNode(st.val)}
n.encode(hasher.encbuf)
encodedNode = hasher.encodedBytes()
@@ -467,10 +473,8 @@ func (st *StackTrie) hashRec(hasher *hasher) {
// Write the hash to the 'val'. We allocate a new val here to not mutate
// input values
st.val = hasher.hashData(encodedNode)
- if st.db != nil {
- // TODO! Is it safe to Put the slice here?
- // Do all db implementations copy the value provided?
- st.db.Put(st.val, encodedNode)
+ if st.writeFn != nil {
+ st.writeFn(st.owner, path, common.BytesToHash(st.val), encodedNode)
}
}
@@ -479,12 +483,11 @@ func (st *StackTrie) Hash() (h common.Hash) {
hasher := newHasher(false)
defer returnHasherToPool(hasher)
- st.hashRec(hasher)
+ st.hashRec(hasher, nil)
if len(st.val) == 32 {
copy(h[:], st.val)
return h
}
-
// If the node's RLP isn't 32 bytes long, the node will not
// be hashed, and instead contain the rlp-encoding of the
// node. For the top level node, we need to force the hashing.
@@ -494,7 +497,7 @@ func (st *StackTrie) Hash() (h common.Hash) {
return h
}
-// Commit will firstly hash the entrie trie if it's still not hashed
+// Commit will firstly hash the entire trie if it's still not hashed
// and then commit all nodes to the associated database. Actually most
// of the trie nodes MAY have been committed already. The main purpose
// here is to commit the root node.
@@ -502,25 +505,24 @@ func (st *StackTrie) Hash() (h common.Hash) {
// The associated database is expected, otherwise the whole commit
// functionality should be disabled.
func (st *StackTrie) Commit() (h common.Hash, err error) {
- if st.db == nil {
+ if st.writeFn == nil {
return common.Hash{}, ErrCommitDisabled
}
-
hasher := newHasher(false)
defer returnHasherToPool(hasher)
- st.hashRec(hasher)
+ st.hashRec(hasher, nil)
if len(st.val) == 32 {
copy(h[:], st.val)
return h, nil
}
-
// If the node's RLP isn't 32 bytes long, the node will not
- // be hashed (and committed), and instead contain the rlp-encoding of the
+ // be hashed (and committed), and instead contain the rlp-encoding of the
// node. For the top level node, we need to force the hashing+commit.
hasher.sha.Reset()
hasher.sha.Write(st.val)
hasher.sha.Read(h[:])
- st.db.Put(h[:], st.val)
+
+ st.writeFn(st.owner, nil, h, st.val)
return h, nil
}
diff --git a/trie/stacktrie_test.go b/trie/stacktrie_test.go
index 069e4981d7..3e6cc8cd57 100644
--- a/trie/stacktrie_test.go
+++ b/trie/stacktrie_test.go
@@ -22,8 +22,8 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
)
func TestStackTrieInsertAndHash(t *testing.T) {
@@ -188,7 +188,7 @@ func TestStackTrieInsertAndHash(t *testing.T) {
func TestSizeBug(t *testing.T) {
st := NewStackTrie(nil)
- nt := NewEmpty(NewDatabase(memorydb.New()))
+ nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
@@ -203,7 +203,7 @@ func TestSizeBug(t *testing.T) {
func TestEmptyBug(t *testing.T) {
st := NewStackTrie(nil)
- nt := NewEmpty(NewDatabase(memorydb.New()))
+ nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
@@ -229,7 +229,7 @@ func TestEmptyBug(t *testing.T) {
func TestValLength56(t *testing.T) {
st := NewStackTrie(nil)
- nt := NewEmpty(NewDatabase(memorydb.New()))
+ nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
@@ -254,8 +254,7 @@ func TestValLength56(t *testing.T) {
// which causes a lot of node-within-node. This case was found via fuzzing.
func TestUpdateSmallNodes(t *testing.T) {
st := NewStackTrie(nil)
- nt := NewEmpty(NewDatabase(memorydb.New()))
-
+ nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
kvs := []struct {
K string
V string
@@ -283,8 +282,7 @@ func TestUpdateSmallNodes(t *testing.T) {
func TestUpdateVariableKeys(t *testing.T) {
t.SkipNow()
st := NewStackTrie(nil)
- nt := NewEmpty(NewDatabase(memorydb.New()))
-
+ nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
kvs := []struct {
K string
V string
@@ -353,7 +351,7 @@ func TestStacktrieNotModifyValues(t *testing.T) {
func TestStacktrieSerialization(t *testing.T) {
var (
st = NewStackTrie(nil)
- nt = NewEmpty(NewDatabase(memorydb.New()))
+ nt = NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
keyB = big.NewInt(1)
keyDelta = big.NewInt(1)
vals [][]byte
diff --git a/trie/sync.go b/trie/sync.go
index 862ce7e16e..4f55845991 100644
--- a/trie/sync.go
+++ b/trie/sync.go
@@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/prque"
"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"
)
@@ -64,7 +65,7 @@ type SyncPath [][]byte
// version that can be sent over the network.
func NewSyncPath(path []byte) SyncPath {
// If the hash is from the account trie, append a single item, if it
- // is from the a storage trie, append a tuple. Note, the length 64 is
+ // is from a storage trie, append a tuple. Note, the length 64 is
// clashing between account leaf and storage root. It's fine though
// because having a trie node at 64 depth means a hash collision was
// found and we're long dead.
@@ -74,6 +75,22 @@ func NewSyncPath(path []byte) SyncPath {
return SyncPath{hexToKeybytes(path[:64]), hexToCompact(path[64:])}
}
+// LeafCallback is a callback type invoked when a trie operation reaches a leaf
+// node.
+//
+// The keys is a path tuple identifying a particular trie node either in a single
+// trie (account) or a layered trie (account -> storage). Each key in the tuple
+// is in the raw format(32 bytes).
+//
+// The path is a composite hexary path identifying the trie node. All the key
+// bytes are converted to the hexary nibbles and composited with the parent path
+// if the trie node is in a layered trie.
+//
+// It's used by state sync and commit to allow handling external references
+// between account and storage tries. And also it's used in the state healing
+// for extracting the raw states(leaf nodes) with corresponding paths.
+type LeafCallback func(keys [][]byte, path []byte, leaf []byte, parent common.Hash, parentPath []byte) error
+
// nodeRequest represents a scheduled or already in-flight trie node retrieval request.
type nodeRequest struct {
hash common.Hash // Hash of the trie node to retrieve
@@ -111,6 +128,7 @@ type syncMemBatch struct {
nodes map[string][]byte // In-memory membatch of recently completed nodes
hashes map[string]common.Hash // Hashes of recently completed nodes
codes map[common.Hash][]byte // In-memory membatch of recently completed codes
+ size uint64 // Estimated batch-size of in-memory data.
}
// newSyncMemBatch allocates a new memory-buffer for not-yet persisted trie nodes.
@@ -138,22 +156,24 @@ func (batch *syncMemBatch) hasCode(hash common.Hash) bool {
// unknown trie hashes to retrieve, accepts node data associated with said hashes
// and reconstructs the trie step by step until all is done.
type Sync struct {
+ scheme string // Node scheme descriptor used in database.
database ethdb.KeyValueReader // Persistent database to check for existing entries
membatch *syncMemBatch // Memory buffer to avoid frequent database writes
nodeReqs map[string]*nodeRequest // Pending requests pertaining to a trie node path
codeReqs map[common.Hash]*codeRequest // Pending requests pertaining to a code hash
- queue *prque.Prque // Priority queue with the pending requests
+ queue *prque.Prque[int64, any] // Priority queue with the pending requests
fetches map[int]int // Number of active fetches per trie node depth
}
// NewSync creates a new trie data download scheduler.
-func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallback) *Sync {
+func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallback, scheme string) *Sync {
ts := &Sync{
+ scheme: scheme,
database: database,
membatch: newSyncMemBatch(),
nodeReqs: make(map[string]*nodeRequest),
codeReqs: make(map[common.Hash]*codeRequest),
- queue: prque.New(nil),
+ queue: prque.New[int64, any](nil), // Ugh, can contain both string and hash, whyyy
fetches: make(map[int]int),
}
ts.AddSubTrie(root, nil, common.Hash{}, nil, callback)
@@ -165,13 +185,14 @@ func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallb
// hex format and contain all the parent path if it's layered trie node.
func (s *Sync) AddSubTrie(root common.Hash, path []byte, parent common.Hash, parentPath []byte, callback LeafCallback) {
// Short circuit if the trie is empty or already known
- if root == emptyRoot {
+ if root == types.EmptyRootHash {
return
}
if s.membatch.hasNode(path) {
return
}
- if rawdb.HasTrieNode(s.database, root) {
+ owner, inner := ResolvePath(path)
+ if rawdb.HasTrieNode(s.database, owner, inner, root, s.scheme) {
return
}
// Assemble the new sub-trie sync request
@@ -197,14 +218,14 @@ func (s *Sync) AddSubTrie(root common.Hash, path []byte, parent common.Hash, par
// as is.
func (s *Sync) AddCodeEntry(hash common.Hash, path []byte, parent common.Hash, parentPath []byte) {
// Short circuit if the entry is empty or already known
- if hash == emptyState {
+ if hash == types.EmptyCodeHash {
return
}
if s.membatch.hasCode(hash) {
return
}
// If database says duplicate, the blob is present for sure.
- // Note we only check the existence with new code scheme, fast
+ // Note we only check the existence with new code scheme, snap
// sync is expected to run with a fresh new node. Even there
// exists the code with legacy format, fetch and store with
// new scheme anyway.
@@ -328,7 +349,8 @@ func (s *Sync) ProcessNode(result NodeSyncResult) error {
func (s *Sync) Commit(dbw ethdb.Batch) error {
// Dump the membatch into a database dbw
for path, value := range s.membatch.nodes {
- rawdb.WriteTrieNode(dbw, s.membatch.hashes[path], value)
+ owner, inner := ResolvePath([]byte(path))
+ rawdb.WriteTrieNode(dbw, owner, inner, s.membatch.hashes[path], value, s.scheme)
}
for hash, value := range s.membatch.codes {
rawdb.WriteCode(dbw, hash, value)
@@ -338,6 +360,11 @@ func (s *Sync) Commit(dbw ethdb.Batch) error {
return nil
}
+// MemSize returns an estimated size (in bytes) of the data held in the membatch.
+func (s *Sync) MemSize() uint64 {
+ return s.membatch.size
+}
+
// Pending returns the number of state entries currently pending for download.
func (s *Sync) Pending() int {
return len(s.nodeReqs) + len(s.codeReqs)
@@ -444,8 +471,11 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) {
// If database says duplicate, then at least the trie node is present
// and we hold the assumption that it's NOT legacy contract code.
- chash := common.BytesToHash(node)
- if rawdb.HasTrieNode(s.database, chash) {
+ var (
+ chash = common.BytesToHash(node)
+ owner, inner = ResolvePath(child.path)
+ )
+ if rawdb.HasTrieNode(s.database, owner, inner, chash, s.scheme) {
return
}
// Locally unknown node, schedule for retrieval
@@ -479,7 +509,10 @@ func (s *Sync) commitNodeRequest(req *nodeRequest) error {
// Write the node content to the membatch
s.membatch.nodes[string(req.path)] = req.data
s.membatch.hashes[string(req.path)] = req.hash
-
+ // The size tracking refers to the db-batch, not the in-memory data.
+ // Therefore, we ignore the req.path, and account only for the hash+data
+ // which eventually is written to db.
+ s.membatch.size += common.HashLength + uint64(len(req.data))
delete(s.nodeReqs, string(req.path))
s.fetches[len(req.path)]--
@@ -501,6 +534,7 @@ func (s *Sync) commitNodeRequest(req *nodeRequest) error {
func (s *Sync) commitCodeRequest(req *codeRequest) error {
// Write the node content to the membatch
s.membatch.codes[req.hash] = req.data
+ s.membatch.size += common.HashLength + uint64(len(req.data))
delete(s.codeReqs, req.hash)
s.fetches[len(req.path)]--
@@ -515,3 +549,14 @@ func (s *Sync) commitCodeRequest(req *codeRequest) error {
}
return nil
}
+
+// ResolvePath resolves the provided composite node path by separating the
+// path in account trie if it's existent.
+func ResolvePath(path []byte) (common.Hash, []byte) {
+ var owner common.Hash
+ if len(path) >= 2*common.HashLength {
+ owner = common.BytesToHash(hexToKeybytes(path[:2*common.HashLength]))
+ path = path[2*common.HashLength:]
+ }
+ return owner, path
+}
diff --git a/trie/sync_test.go b/trie/sync_test.go
index 9fd1d636c0..fc871a22c8 100644
--- a/trie/sync_test.go
+++ b/trie/sync_test.go
@@ -22,6 +22,8 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
)
@@ -29,8 +31,8 @@ import (
// makeTestTrie create a sample test trie to test node-wise reconstruction.
func makeTestTrie() (*Database, *StateTrie, map[string][]byte) {
// Create an empty trie
- triedb := NewDatabase(memorydb.New())
- trie, _ := NewStateTrie(common.Hash{}, common.Hash{}, triedb)
+ triedb := NewDatabase(rawdb.NewMemoryDatabase())
+ trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
// Fill it with some arbitrary data
content := make(map[string][]byte)
@@ -51,15 +53,12 @@ func makeTestTrie() (*Database, *StateTrie, map[string][]byte) {
trie.Update(key, val)
}
}
- root, nodes, err := trie.Commit(false)
- if err != nil {
- panic(fmt.Errorf("failed to commit trie %v", err))
- }
+ root, nodes := trie.Commit(false)
if err := triedb.Update(NewWithNodeSet(nodes)); err != nil {
panic(fmt.Errorf("failed to commit db %v", err))
}
// Re-create the trie based on the new state
- trie, _ = NewSecure(common.Hash{}, root, triedb)
+ trie, _ = NewStateTrie(TrieID(root), triedb)
return triedb, trie, content
}
@@ -67,7 +66,7 @@ func makeTestTrie() (*Database, *StateTrie, map[string][]byte) {
// content map.
func checkTrieContents(t *testing.T, db *Database, root []byte, content map[string][]byte) {
// Check root availability and trie contents
- trie, err := NewStateTrie(common.Hash{}, common.BytesToHash(root), db)
+ trie, err := NewStateTrie(TrieID(common.BytesToHash(root)), db)
if err != nil {
t.Fatalf("failed to create trie at %x: %v", root, err)
}
@@ -84,7 +83,7 @@ func checkTrieContents(t *testing.T, db *Database, root []byte, content map[stri
// checkTrieConsistency checks that all nodes in a trie are indeed present.
func checkTrieConsistency(db *Database, root common.Hash) error {
// Create and iterate a trie rooted in a subnode
- trie, err := NewStateTrie(common.Hash{}, root, db)
+ trie, err := NewStateTrie(TrieID(root), db)
if err != nil {
return nil // Consider a non existent state consistent
}
@@ -103,13 +102,13 @@ type trieElement struct {
// Tests that an empty trie is not scheduled for syncing.
func TestEmptySync(t *testing.T) {
- dbA := NewDatabase(memorydb.New())
- dbB := NewDatabase(memorydb.New())
- emptyA := NewEmpty(dbA)
- emptyB, _ := New(common.Hash{}, emptyRoot, dbB)
+ dbA := NewDatabase(rawdb.NewMemoryDatabase())
+ dbB := NewDatabase(rawdb.NewMemoryDatabase())
+ emptyA, _ := New(TrieID(common.Hash{}), dbA)
+ emptyB, _ := New(TrieID(types.EmptyRootHash), dbB)
for i, trie := range []*Trie{emptyA, emptyB} {
- sync := NewSync(trie.Hash(), memorydb.New(), nil)
+ sync := NewSync(trie.Hash(), memorydb.New(), nil, []*Database{dbA, dbB}[i].Scheme())
if paths, nodes, codes := sync.Missing(1); len(paths) != 0 || len(nodes) != 0 || len(codes) != 0 {
t.Errorf("test %d: content requested for empty trie: %v, %v, %v", i, paths, nodes, codes)
}
@@ -128,9 +127,9 @@ func testIterativeSync(t *testing.T, count int, bypath bool) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler
- diskdb := memorydb.New()
+ diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
+ sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
// The code requests are ignored here since there is no code
// at the testing trie.
@@ -194,9 +193,9 @@ func TestIterativeDelayedSync(t *testing.T) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler
- diskdb := memorydb.New()
+ diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
+ sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
// The code requests are ignored here since there is no code
// at the testing trie.
@@ -255,9 +254,9 @@ func testIterativeRandomSync(t *testing.T, count int) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler
- diskdb := memorydb.New()
+ diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
+ sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
// The code requests are ignored here since there is no code
// at the testing trie.
@@ -313,9 +312,9 @@ func TestIterativeRandomDelayedSync(t *testing.T) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler
- diskdb := memorydb.New()
+ diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
+ sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
// The code requests are ignored here since there is no code
// at the testing trie.
@@ -376,9 +375,9 @@ func TestDuplicateAvoidanceSync(t *testing.T) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler
- diskdb := memorydb.New()
+ diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
+ sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
// The code requests are ignored here since there is no code
// at the testing trie.
@@ -439,9 +438,9 @@ func TestIncompleteSync(t *testing.T) {
srcDb, srcTrie, _ := makeTestTrie()
// Create a destination trie and sync with the scheduler
- diskdb := memorydb.New()
+ diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
+ sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
// The code requests are ignored here since there is no code
// at the testing trie.
@@ -519,9 +518,9 @@ func TestSyncOrdering(t *testing.T) {
srcDb, srcTrie, srcData := makeTestTrie()
// Create a destination trie and sync with the scheduler, tracking the requests
- diskdb := memorydb.New()
+ diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)
- sched := NewSync(srcTrie.Hash(), diskdb, nil)
+ sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
// The code requests are ignored here since there is no code
// at the testing trie.
diff --git a/trie/trie.go b/trie/trie.go
index 9274d88380..cf9108f107 100644
--- a/trie/trie.go
+++ b/trie/trie.go
@@ -23,34 +23,10 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
-var (
- // emptyRoot is the known root hash of an empty trie.
- emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
-
- // emptyState is the known hash of an empty state trie entry.
- emptyState = crypto.Keccak256Hash(nil)
-)
-
-// LeafCallback is a callback type invoked when a trie operation reaches a leaf
-// node.
-//
-// The keys is a path tuple identifying a particular trie node either in a single
-// trie (account) or a layered trie (account -> storage). Each key in the tuple
-// is in the raw format(32 bytes).
-//
-// The path is a composite hexary path identifying the trie node. All the key
-// bytes are converted to the hexary nibbles and composited with the parent path
-// if the trie node is in a layered trie.
-//
-// It's used by state sync and commit to allow handling external references
-// between account and storage tries. And also it's used in the state healing
-// for extracting the raw states(leaf nodes) with corresponding paths.
-type LeafCallback func(keys [][]byte, path []byte, leaf []byte, parent common.Hash, parentPath []byte) error
-
// Trie is a Merkle Patricia Trie. Use New to create a trie that sits on
// top of a database. Whenever trie performs a commit operation, the generated
// nodes will be gathered and returned in a set. Once the trie is committed,
@@ -67,9 +43,8 @@ type Trie struct {
// actually unhashed nodes.
unhashed int
- // db is the handler trie can retrieve nodes from. It's
- // only for reading purpose and not available for writing.
- db *Database
+ // reader is the handler trie can retrieve nodes from.
+ reader *trieReader
// tracer is the tool to track the trie changes.
// It will be reset after each commit operation.
@@ -87,26 +62,29 @@ func (t *Trie) Copy() *Trie {
root: t.root,
owner: t.owner,
unhashed: t.unhashed,
- db: t.db,
+ reader: t.reader,
tracer: t.tracer.copy(),
}
}
-// New creates a trie with an existing root node from db and an assigned
-// owner for storage proximity.
-//
-// If root is the zero hash or the sha3 hash of an empty string, the
-// trie is initially empty and does not require a database. Otherwise,
-// New will panic if db is nil and returns a MissingNodeError if root does
-// not exist in the database. Accessing the trie loads nodes from db on demand.
-func New(owner common.Hash, root common.Hash, db *Database) (*Trie, error) {
+// New creates the trie instance with provided trie id and the read-only
+// database. The state specified by trie id must be available, otherwise
+// an error will be returned. The trie root specified by trie id can be
+// zero hash or the sha3 hash of an empty string, then trie is initially
+// empty, otherwise, the root node must be present in database or returns
+// a MissingNodeError if not.
+func New(id *ID, db NodeReader) (*Trie, error) {
+ reader, err := newTrieReader(id.StateRoot, id.Owner, db)
+ if err != nil {
+ return nil, err
+ }
trie := &Trie{
- owner: owner,
- db: db,
+ owner: id.Owner,
+ reader: reader,
//tracer: newTracer(),
}
- if root != (common.Hash{}) && root != emptyRoot {
- rootnode, err := trie.resolveHash(root[:], nil)
+ if id.Root != (common.Hash{}) && id.Root != types.EmptyRootHash {
+ rootnode, err := trie.resolveAndTrack(id.Root[:], nil)
if err != nil {
return nil, err
}
@@ -117,7 +95,7 @@ func New(owner common.Hash, root common.Hash, db *Database) (*Trie, error) {
// NewEmpty is a shortcut to create empty tree. It's mostly used in tests.
func NewEmpty(db *Database) *Trie {
- tr, _ := New(common.Hash{}, common.Hash{}, db)
+ tr, _ := New(TrieID(common.Hash{}), db)
return tr
}
@@ -132,7 +110,7 @@ func (t *Trie) NodeIterator(start []byte) NodeIterator {
func (t *Trie) Get(key []byte) []byte {
res, err := t.TryGet(key)
if err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ log.Error("Unhandled trie error in Trie.Get", "err", err)
}
return res
}
@@ -173,7 +151,7 @@ func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode
}
return value, n, didResolve, err
case hashNode:
- child, err := t.resolveHash(n, key[:pos])
+ child, err := t.resolveAndTrack(n, key[:pos])
if err != nil {
return nil, n, true, err
}
@@ -219,7 +197,7 @@ func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item []byte, new
if hash == nil {
return nil, origNode, 0, errors.New("non-consensus node")
}
- blob, err := t.db.Node(common.BytesToHash(hash))
+ blob, err := t.reader.nodeBlob(path, common.BytesToHash(hash))
return blob, origNode, 1, err
}
// Path still needs to be traversed, descend into children
@@ -249,7 +227,7 @@ func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item []byte, new
return item, n, resolved, err
case hashNode:
- child, err := t.resolveHash(n, path[:pos])
+ child, err := t.resolveAndTrack(n, path[:pos])
if err != nil {
return nil, n, 1, err
}
@@ -269,7 +247,7 @@ func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item []byte, new
// stored in the trie.
func (t *Trie) Update(key, value []byte) {
if err := t.TryUpdate(key, value); err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ log.Error("Unhandled trie error in Trie.Update", "err", err)
}
}
@@ -370,7 +348,7 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error
// We've hit a part of the trie that isn't loaded yet. Load
// the node and insert into it. This leaves all child nodes on
// the path to the value in the trie.
- rn, err := t.resolveHash(n, prefix)
+ rn, err := t.resolveAndTrack(n, prefix)
if err != nil {
return false, nil, err
}
@@ -388,7 +366,7 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error
// Delete removes any existing value for key from the trie.
func (t *Trie) Delete(key []byte) {
if err := t.TryDelete(key); err != nil {
- log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ log.Error("Unhandled trie error in Trie.Delete", "err", err)
}
}
@@ -524,7 +502,7 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
// We've hit a part of the trie that isn't loaded yet. Load
// the node and delete from it. This leaves all child nodes on
// the path to the value in the trie.
- rn, err := t.resolveHash(n, prefix)
+ rn, err := t.resolveAndTrack(n, prefix)
if err != nil {
return false, nil, err
}
@@ -548,30 +526,22 @@ func concat(s1 []byte, s2 ...byte) []byte {
func (t *Trie) resolve(n node, prefix []byte) (node, error) {
if n, ok := n.(hashNode); ok {
- return t.resolveHash(n, prefix)
+ return t.resolveAndTrack(n, prefix)
}
return n, nil
}
-// resolveHash loads node from the underlying database with the provided
-// node hash and path prefix.
-func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) {
- hash := common.BytesToHash(n)
- if node := t.db.node(hash); node != nil {
- return node, nil
- }
- return nil, &MissingNodeError{Owner: t.owner, NodeHash: hash, Path: prefix}
-}
-
-// resolveHash loads rlp-encoded node blob from the underlying database
-// with the provided node hash and path prefix.
-func (t *Trie) resolveBlob(n hashNode, prefix []byte) ([]byte, error) {
- hash := common.BytesToHash(n)
- blob, _ := t.db.Node(hash)
- if len(blob) != 0 {
- return blob, nil
+// resolveAndTrack loads node from the underlying store with the given node hash
+// and path prefix and also tracks the loaded node blob in tracer treated as the
+// node's original value. The rlp-encoded blob is preferred to be loaded from
+// database because it's easy to decode node while complex to encode node to blob.
+func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) {
+ blob, err := t.reader.nodeBlob(prefix, common.BytesToHash(n))
+ if err != nil {
+ return nil, err
}
- return nil, &MissingNodeError{Owner: t.owner, NodeHash: hash, Path: prefix}
+ t.tracer.onRead(prefix, blob)
+ return mustDecodeNode(n, blob), nil
}
// Hash returns the root hash of the trie. It does not write to the
@@ -582,17 +552,23 @@ func (t *Trie) Hash() common.Hash {
return common.BytesToHash(hash.(hashNode))
}
-// Commit collects all dirty nodes in the trie and replace them with the
-// corresponding node hash. All collected nodes(including dirty leaves if
+// Commit collects all dirty nodes in the trie and replaces them with the
+// corresponding node hash. All collected nodes (including dirty leaves if
// collectLeaf is true) will be encapsulated into a nodeset for return.
-// The returned nodeset can be nil if the trie is clean(nothing to commit).
+// The returned nodeset can be nil if the trie is clean (nothing to commit).
// Once the trie is committed, it's not usable anymore. A new trie must
// be created with new root and updated trie database for following usage
-func (t *Trie) Commit(collectLeaf bool) (common.Hash, *NodeSet, error) {
+func (t *Trie) Commit(collectLeaf bool) (common.Hash, *NodeSet) {
defer t.tracer.reset()
+ // Trie is empty and can be classified into two types of situations:
+ // - The trie was empty and no update happens
+ // - The trie was non-empty and all nodes are dropped
if t.root == nil {
- return emptyRoot, nil, nil
+ // Wrap tracked deletions as the return
+ set := NewNodeSet(t.owner)
+ t.tracer.markDeletions(set)
+ return types.EmptyRootHash, set
}
// Derive the hash for all dirty nodes first. We hold the assumption
// in the following procedure that all nodes are hashed.
@@ -604,21 +580,18 @@ func (t *Trie) Commit(collectLeaf bool) (common.Hash, *NodeSet, error) {
// Replace the root node with the origin hash in order to
// ensure all resolved nodes are dropped after the commit.
t.root = hashedNode
- return rootHash, nil, nil
- }
- h := newCommitter(t.owner, collectLeaf)
- newRoot, nodes, err := h.Commit(t.root)
- if err != nil {
- return common.Hash{}, nil, err
+ return rootHash, nil
}
+ h := newCommitter(t.owner, t.tracer, collectLeaf)
+ newRoot, nodes := h.Commit(t.root)
t.root = newRoot
- return rootHash, nodes, nil
+ return rootHash, nodes
}
// hashRoot calculates the root hash of the given trie
func (t *Trie) hashRoot() (node, node, error) {
if t.root == nil {
- return hashNode(emptyRoot.Bytes()), nil, nil
+ return hashNode(types.EmptyRootHash.Bytes()), nil, nil
}
// If the number of changes is below 100, we let one thread handle it
h := newHasher(t.unhashed >= 100)
@@ -633,6 +606,5 @@ func (t *Trie) Reset() {
t.root = nil
t.owner = common.Hash{}
t.unhashed = 0
- //t.db = nil
t.tracer.reset()
}
diff --git a/trie/trie_id.go b/trie/trie_id.go
new file mode 100644
index 0000000000..8ab490ca3b
--- /dev/null
+++ b/trie/trie_id.go
@@ -0,0 +1,55 @@
+// 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 trie
+
+import "github.com/ethereum/go-ethereum/common"
+
+// ID is the identifier for uniquely identifying a trie.
+type ID struct {
+ StateRoot common.Hash // The root of the corresponding state(block.root)
+ Owner common.Hash // The contract address hash which the trie belongs to
+ Root common.Hash // The root hash of trie
+}
+
+// StateTrieID constructs an identifier for state trie with the provided state root.
+func StateTrieID(root common.Hash) *ID {
+ return &ID{
+ StateRoot: root,
+ Owner: common.Hash{},
+ Root: root,
+ }
+}
+
+// StorageTrieID constructs an identifier for storage trie which belongs to a certain
+// state and contract specified by the stateRoot and owner.
+func StorageTrieID(stateRoot common.Hash, owner common.Hash, root common.Hash) *ID {
+ return &ID{
+ StateRoot: stateRoot,
+ Owner: owner,
+ Root: root,
+ }
+}
+
+// TrieID constructs an identifier for a standard trie(not a second-layer trie)
+// with provided root. It's mostly used in tests and some other tries like CHT trie.
+func TrieID(root common.Hash) *ID {
+ return &ID{
+ StateRoot: root,
+ Owner: common.Hash{},
+ Root: root,
+ }
+}
diff --git a/trie/trie_reader.go b/trie/trie_reader.go
new file mode 100644
index 0000000000..14186159b7
--- /dev/null
+++ b/trie/trie_reader.go
@@ -0,0 +1,106 @@
+// 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 trie
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// Reader wraps the Node and NodeBlob method of a backing trie store.
+type Reader interface {
+ // Node retrieves the trie node with the provided trie identifier, hexary
+ // node path and the corresponding node hash.
+ // No error will be returned if the node is not found.
+ Node(owner common.Hash, path []byte, hash common.Hash) (node, error)
+
+ // NodeBlob retrieves the RLP-encoded trie node blob with the provided trie
+ // identifier, hexary node path and the corresponding node hash.
+ // No error will be returned if the node is not found.
+ NodeBlob(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
+}
+
+// NodeReader wraps all the necessary functions for accessing trie node.
+type NodeReader interface {
+ // GetReader returns a reader for accessing all trie nodes with provided
+ // state root. Nil is returned in case the state is not available.
+ GetReader(root common.Hash) Reader
+}
+
+// trieReader is a wrapper of the underlying node reader. It's not safe
+// for concurrent usage.
+type trieReader struct {
+ owner common.Hash
+ reader Reader
+ banned map[string]struct{} // Marker to prevent node from being accessed, for tests
+}
+
+// newTrieReader initializes the trie reader with the given node reader.
+func newTrieReader(stateRoot, owner common.Hash, db NodeReader) (*trieReader, error) {
+ reader := db.GetReader(stateRoot)
+ if reader == nil {
+ return nil, fmt.Errorf("state not found #%x", stateRoot)
+ }
+ return &trieReader{owner: owner, reader: reader}, nil
+}
+
+// newEmptyReader initializes the pure in-memory reader. All read operations
+// should be forbidden and returns the MissingNodeError.
+func newEmptyReader() *trieReader {
+ return &trieReader{}
+}
+
+// node retrieves the trie node with the provided trie node information.
+// An MissingNodeError will be returned in case the node is not found or
+// any error is encountered.
+func (r *trieReader) node(path []byte, hash common.Hash) (node, error) {
+ // Perform the logics in tests for preventing trie node access.
+ if r.banned != nil {
+ if _, ok := r.banned[string(path)]; ok {
+ return nil, &MissingNodeError{Owner: r.owner, NodeHash: hash, Path: path}
+ }
+ }
+ if r.reader == nil {
+ return nil, &MissingNodeError{Owner: r.owner, NodeHash: hash, Path: path}
+ }
+ node, err := r.reader.Node(r.owner, path, hash)
+ if err != nil || node == nil {
+ return nil, &MissingNodeError{Owner: r.owner, NodeHash: hash, Path: path, err: err}
+ }
+ return node, nil
+}
+
+// node retrieves the rlp-encoded trie node with the provided trie node
+// information. An MissingNodeError will be returned in case the node is
+// not found or any error is encountered.
+func (r *trieReader) nodeBlob(path []byte, hash common.Hash) ([]byte, error) {
+ // Perform the logics in tests for preventing trie node access.
+ if r.banned != nil {
+ if _, ok := r.banned[string(path)]; ok {
+ return nil, &MissingNodeError{Owner: r.owner, NodeHash: hash, Path: path}
+ }
+ }
+ if r.reader == nil {
+ return nil, &MissingNodeError{Owner: r.owner, NodeHash: hash, Path: path}
+ }
+ blob, err := r.reader.NodeBlob(r.owner, path, hash)
+ if err != nil || len(blob) == 0 {
+ return nil, &MissingNodeError{Owner: r.owner, NodeHash: hash, Path: path, err: err}
+ }
+ return blob, nil
+}
diff --git a/trie/trie_test.go b/trie/trie_test.go
index 3e29600bbd..2f56c89cde 100644
--- a/trie/trie_test.go
+++ b/trie/trie_test.go
@@ -18,6 +18,7 @@ package trie
import (
"bytes"
+ crand "crypto/rand"
"encoding/binary"
"errors"
"fmt"
@@ -34,7 +35,6 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
)
@@ -47,7 +47,7 @@ func init() {
func TestEmptyTrie(t *testing.T) {
trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
res := trie.Hash()
- exp := emptyRoot
+ exp := types.EmptyRootHash
if res != exp {
t.Errorf("expected %x got %x", exp, res)
}
@@ -64,7 +64,8 @@ func TestNull(t *testing.T) {
}
func TestMissingRoot(t *testing.T) {
- trie, err := New(common.Hash{}, common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), NewDatabase(memorydb.New()))
+ root := common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")
+ trie, err := New(TrieID(root), NewDatabase(rawdb.NewMemoryDatabase()))
if trie != nil {
t.Error("New returned non-nil trie for invalid root")
}
@@ -77,39 +78,39 @@ func TestMissingNodeDisk(t *testing.T) { testMissingNode(t, false) }
func TestMissingNodeMemonly(t *testing.T) { testMissingNode(t, true) }
func testMissingNode(t *testing.T, memonly bool) {
- diskdb := memorydb.New()
+ diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)
trie := NewEmpty(triedb)
updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer")
updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
- root, nodes, _ := trie.Commit(false)
+ root, nodes := trie.Commit(false)
triedb.Update(NewWithNodeSet(nodes))
if !memonly {
- triedb.Commit(root, true, nil)
+ triedb.Commit(root, false)
}
- trie, _ = New(common.Hash{}, root, triedb)
+ trie, _ = New(TrieID(root), triedb)
_, err := trie.TryGet([]byte("120000"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
- trie, _ = New(common.Hash{}, root, triedb)
+ trie, _ = New(TrieID(root), triedb)
_, err = trie.TryGet([]byte("120099"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
- trie, _ = New(common.Hash{}, root, triedb)
+ trie, _ = New(TrieID(root), triedb)
_, err = trie.TryGet([]byte("123456"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
- trie, _ = New(common.Hash{}, root, triedb)
+ trie, _ = New(TrieID(root), triedb)
err = trie.TryUpdate([]byte("120099"), []byte("zxcvzxcvzxcvzxcvzxcvzxcvzxcvzxcv"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
- trie, _ = New(common.Hash{}, root, triedb)
+ trie, _ = New(TrieID(root), triedb)
err = trie.TryDelete([]byte("123456"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
@@ -122,27 +123,27 @@ func testMissingNode(t *testing.T, memonly bool) {
diskdb.Delete(hash[:])
}
- trie, _ = New(common.Hash{}, root, triedb)
+ trie, _ = New(TrieID(root), triedb)
_, err = trie.TryGet([]byte("120000"))
if _, ok := err.(*MissingNodeError); !ok {
t.Errorf("Wrong error: %v", err)
}
- trie, _ = New(common.Hash{}, root, triedb)
+ trie, _ = New(TrieID(root), triedb)
_, err = trie.TryGet([]byte("120099"))
if _, ok := err.(*MissingNodeError); !ok {
t.Errorf("Wrong error: %v", err)
}
- trie, _ = New(common.Hash{}, root, triedb)
+ trie, _ = New(TrieID(root), triedb)
_, err = trie.TryGet([]byte("123456"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
- trie, _ = New(common.Hash{}, root, triedb)
+ trie, _ = New(TrieID(root), triedb)
err = trie.TryUpdate([]byte("120099"), []byte("zxcv"))
if _, ok := err.(*MissingNodeError); !ok {
t.Errorf("Wrong error: %v", err)
}
- trie, _ = New(common.Hash{}, root, triedb)
+ trie, _ = New(TrieID(root), triedb)
err = trie.TryDelete([]byte("123456"))
if _, ok := err.(*MissingNodeError); !ok {
t.Errorf("Wrong error: %v", err)
@@ -166,10 +167,7 @@ func TestInsert(t *testing.T) {
updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab")
- root, _, err := trie.Commit(false)
- if err != nil {
- t.Fatalf("commit error: %v", err)
- }
+ root, _ = trie.Commit(false)
if root != exp {
t.Errorf("case 2: exp %x got %x", exp, root)
}
@@ -194,9 +192,9 @@ func TestGet(t *testing.T) {
if i == 1 {
return
}
- root, nodes, _ := trie.Commit(false)
+ root, nodes := trie.Commit(false)
db.Update(NewWithNodeSet(nodes))
- trie, _ = New(common.Hash{}, root, db)
+ trie, _ = New(TrieID(root), db)
}
}
@@ -266,14 +264,11 @@ func TestReplication(t *testing.T) {
for _, val := range vals {
updateString(trie, val.k, val.v)
}
- exp, nodes, err := trie.Commit(false)
- if err != nil {
- t.Fatalf("commit error: %v", err)
- }
+ exp, nodes := trie.Commit(false)
triedb.Update(NewWithNodeSet(nodes))
// create a new trie on top of the database and check that lookups work.
- trie2, err := New(common.Hash{}, exp, triedb)
+ trie2, err := New(TrieID(exp), triedb)
if err != nil {
t.Fatalf("can't recreate trie at %x: %v", exp, err)
}
@@ -282,10 +277,7 @@ func TestReplication(t *testing.T) {
t.Errorf("trie2 doesn't have %q => %q", kv.k, kv.v)
}
}
- hash, nodes, err := trie2.Commit(false)
- if err != nil {
- t.Fatalf("commit error: %v", err)
- }
+ hash, nodes := trie2.Commit(false)
if hash != exp {
t.Errorf("root failure. expected %x got %x", exp, hash)
}
@@ -294,7 +286,7 @@ func TestReplication(t *testing.T) {
if nodes != nil {
triedb.Update(NewWithNodeSet(nodes))
}
- trie2, err = New(common.Hash{}, hash, triedb)
+ trie2, err = New(TrieID(hash), triedb)
if err != nil {
t.Fatalf("can't recreate trie at %x: %v", exp, err)
}
@@ -377,6 +369,7 @@ const (
opCommit
opItercheckhash
opNodeDiff
+ opProve
opMax // boundary value, not an actual op
)
@@ -402,7 +395,7 @@ func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
step.key = genKey()
step.value = make([]byte, 8)
binary.BigEndian.PutUint64(step.value, uint64(i))
- case opGet, opDelete:
+ case opGet, opDelete, opProve:
step.key = genKey()
}
steps = append(steps, step)
@@ -412,7 +405,7 @@ func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
func runRandTest(rt randTest) bool {
var (
- triedb = NewDatabase(memorydb.New())
+ triedb = NewDatabase(rawdb.NewMemoryDatabase())
tr = NewEmpty(triedb)
values = make(map[string]string) // tracks content of the trie
origTrie = NewEmpty(triedb)
@@ -436,24 +429,56 @@ func runRandTest(rt randTest) bool {
if string(v) != want {
rt[i].err = fmt.Errorf("mismatch for key %#x, got %#x want %#x", step.key, v, want)
}
+ case opProve:
+ hash := tr.Hash()
+ if hash == types.EmptyRootHash {
+ continue
+ }
+ proofDb := rawdb.NewMemoryDatabase()
+ err := tr.Prove(step.key, 0, proofDb)
+ if err != nil {
+ rt[i].err = fmt.Errorf("failed for proving key %#x, %v", step.key, err)
+ }
+ _, err = VerifyProof(hash, step.key, proofDb)
+ if err != nil {
+ rt[i].err = fmt.Errorf("failed for verifying key %#x, %v", step.key, err)
+ }
case opHash:
tr.Hash()
case opCommit:
- hash, nodes, err := tr.Commit(false)
- if err != nil {
- rt[i].err = err
- return false
+ root, nodes := tr.Commit(true)
+ // Validity the returned nodeset
+ if nodes != nil {
+ for path, node := range nodes.updates.nodes {
+ blob, _, _ := origTrie.TryGetNode(hexToCompact([]byte(path)))
+ got := node.prev
+ if !bytes.Equal(blob, got) {
+ rt[i].err = fmt.Errorf("prevalue mismatch for 0x%x, got 0x%x want 0x%x", path, got, blob)
+ panic(rt[i].err)
+ }
+ }
+ for path, prev := range nodes.deletes {
+ blob, _, _ := origTrie.TryGetNode(hexToCompact([]byte(path)))
+ if !bytes.Equal(blob, prev) {
+ rt[i].err = fmt.Errorf("prevalue mismatch for 0x%x, got 0x%x want 0x%x", path, prev, blob)
+ return false
+ }
+ }
}
if nodes != nil {
triedb.Update(NewWithNodeSet(nodes))
}
- newtr, err := New(common.Hash{}, hash, triedb)
+ newtr, err := New(TrieID(root), triedb)
if err != nil {
rt[i].err = err
return false
}
tr = newtr
+
+ // Enable node tracing. Resolve the root node again explicitly
+ // since it's not captured at the beginning.
tr.tracer = newTracer()
+ tr.resolveAndTrack(root.Bytes(), nil)
origTrie = tr.Copy()
case opItercheckhash:
@@ -571,7 +596,7 @@ func benchUpdate(b *testing.B, e binary.ByteOrder) *Trie {
}
// Benchmarks the trie hashing. Since the trie caches the result of any operation,
-// we cannot use b.N as the number of hashing rouns, since all rounds apart from
+// we cannot use b.N as the number of hashing rounds, since all rounds apart from
// the first one will be NOOP. As such, we'll use b.N as the number of account to
// insert into the trie before measuring the hashing.
// BenchmarkHash-6 288680 4561 ns/op 682 B/op 9 allocs/op
@@ -606,7 +631,7 @@ func BenchmarkHash(b *testing.B) {
}
// Benchmarks the trie Commit following a Hash. Since the trie caches the result of any operation,
-// we cannot use b.N as the number of hashing rouns, since all rounds apart from
+// we cannot use b.N as the number of hashing rounds, since all rounds apart from
// the first one will be NOOP. As such, we'll use b.N as the number of account to
// insert into the trie before measuring the hashing.
func BenchmarkCommitAfterHash(b *testing.B) {
@@ -673,7 +698,7 @@ func TestCommitAfterHash(t *testing.T) {
if exp != root {
t.Errorf("got %x, exp %x", root, exp)
}
- root, _, _ = trie.Commit(false)
+ root, _ = trie.Commit(false)
if exp != root {
t.Errorf("got %x, exp %x", root, exp)
}
@@ -693,7 +718,7 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) {
for i := 0; i < len(accounts); i++ {
var (
nonce = uint64(random.Int63())
- root = emptyRoot
+ root = types.EmptyRootHash
code = crypto.Keccak256(nil)
)
// The big.Rand function is not deterministic with regards to 64 vs 32 bit systems,
@@ -754,47 +779,35 @@ func (b *spongeBatch) Reset() {}
func (b *spongeBatch) Replay(w ethdb.KeyValueWriter) error { return nil }
// TestCommitSequence tests that the trie.Commit operation writes the elements of the trie
-// in the expected order, and calls the callbacks in the expected order.
+// in the expected order.
// The test data was based on the 'master' code, and is basically random. It can be used
// to check whether changes to the trie modifies the write order or data in any way.
func TestCommitSequence(t *testing.T) {
for i, tc := range []struct {
- count int
- expWriteSeqHash []byte
- expCallbackSeqHash []byte
+ count int
+ expWriteSeqHash []byte
}{
- {20, common.FromHex("873c78df73d60e59d4a2bcf3716e8bfe14554549fea2fc147cb54129382a8066"),
- common.FromHex("ff00f91ac05df53b82d7f178d77ada54fd0dca64526f537034a5dbe41b17df2a")},
- {200, common.FromHex("ba03d891bb15408c940eea5ee3d54d419595102648d02774a0268d892add9c8e"),
- common.FromHex("f3cd509064c8d319bbdd1c68f511850a902ad275e6ed5bea11547e23d492a926")},
- {2000, common.FromHex("f7a184f20df01c94f09537401d11e68d97ad0c00115233107f51b9c287ce60c7"),
- common.FromHex("ff795ea898ba1e4cfed4a33b4cf5535a347a02cf931f88d88719faf810f9a1c9")},
+ {20, common.FromHex("873c78df73d60e59d4a2bcf3716e8bfe14554549fea2fc147cb54129382a8066")},
+ {200, common.FromHex("ba03d891bb15408c940eea5ee3d54d419595102648d02774a0268d892add9c8e")},
+ {2000, common.FromHex("f7a184f20df01c94f09537401d11e68d97ad0c00115233107f51b9c287ce60c7")},
} {
addresses, accounts := makeAccounts(tc.count)
// This spongeDb is used to check the sequence of disk-db-writes
s := &spongeDb{sponge: sha3.NewLegacyKeccak256()}
- db := NewDatabase(s)
+ db := NewDatabase(rawdb.NewDatabase(s))
trie := NewEmpty(db)
- // Another sponge is used to check the callback-sequence
- callbackSponge := sha3.NewLegacyKeccak256()
// Fill the trie with elements
for i := 0; i < tc.count; i++ {
trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i])
}
// Flush trie -> database
- root, nodes, _ := trie.Commit(false)
+ root, nodes := trie.Commit(false)
db.Update(NewWithNodeSet(nodes))
// Flush memdb -> disk (sponge)
- db.Commit(root, false, func(c common.Hash) {
- // And spongify the callback-order
- callbackSponge.Write(c[:])
- })
+ db.Commit(root, false)
if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) {
t.Errorf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp)
}
- if got, exp := callbackSponge.Sum(nil), tc.expCallbackSeqHash; !bytes.Equal(got, exp) {
- t.Errorf("test %d, call back sequence wrong:\ngot: %x exp %x\n", i, got, exp)
- }
}
}
@@ -802,24 +815,18 @@ func TestCommitSequence(t *testing.T) {
// but uses random blobs instead of 'accounts'
func TestCommitSequenceRandomBlobs(t *testing.T) {
for i, tc := range []struct {
- count int
- expWriteSeqHash []byte
- expCallbackSeqHash []byte
+ count int
+ expWriteSeqHash []byte
}{
- {20, common.FromHex("8e4a01548551d139fa9e833ebc4e66fc1ba40a4b9b7259d80db32cff7b64ebbc"),
- common.FromHex("450238d73bc36dc6cc6f926987e5428535e64be403877c4560e238a52749ba24")},
- {200, common.FromHex("6869b4e7b95f3097a19ddb30ff735f922b915314047e041614df06958fc50554"),
- common.FromHex("0ace0b03d6cb8c0b82f6289ef5b1a1838306b455a62dafc63cada8e2924f2550")},
- {2000, common.FromHex("444200e6f4e2df49f77752f629a96ccf7445d4698c164f962bbd85a0526ef424"),
- common.FromHex("117d30dafaa62a1eed498c3dfd70982b377ba2b46dd3e725ed6120c80829e518")},
+ {20, common.FromHex("8e4a01548551d139fa9e833ebc4e66fc1ba40a4b9b7259d80db32cff7b64ebbc")},
+ {200, common.FromHex("6869b4e7b95f3097a19ddb30ff735f922b915314047e041614df06958fc50554")},
+ {2000, common.FromHex("444200e6f4e2df49f77752f629a96ccf7445d4698c164f962bbd85a0526ef424")},
} {
prng := rand.New(rand.NewSource(int64(i)))
// This spongeDb is used to check the sequence of disk-db-writes
s := &spongeDb{sponge: sha3.NewLegacyKeccak256()}
- db := NewDatabase(s)
+ db := NewDatabase(rawdb.NewDatabase(s))
trie := NewEmpty(db)
- // Another sponge is used to check the callback-sequence
- callbackSponge := sha3.NewLegacyKeccak256()
// Fill the trie with elements
for i := 0; i < tc.count; i++ {
key := make([]byte, 32)
@@ -835,19 +842,13 @@ func TestCommitSequenceRandomBlobs(t *testing.T) {
trie.Update(key, val)
}
// Flush trie -> database
- root, nodes, _ := trie.Commit(false)
+ root, nodes := trie.Commit(false)
db.Update(NewWithNodeSet(nodes))
// Flush memdb -> disk (sponge)
- db.Commit(root, false, func(c common.Hash) {
- // And spongify the callback-order
- callbackSponge.Write(c[:])
- })
+ db.Commit(root, false)
if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) {
t.Fatalf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp)
}
- if got, exp := callbackSponge.Sum(nil), tc.expCallbackSeqHash; !bytes.Equal(got, exp) {
- t.Fatalf("test %d, call back sequence wrong:\ngot: %x exp %x\n", i, got, exp)
- }
}
}
@@ -856,11 +857,13 @@ func TestCommitSequenceStackTrie(t *testing.T) {
prng := rand.New(rand.NewSource(int64(count)))
// This spongeDb is used to check the sequence of disk-db-writes
s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"}
- db := NewDatabase(s)
+ db := NewDatabase(rawdb.NewDatabase(s))
trie := NewEmpty(db)
// Another sponge is used for the stacktrie commits
stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
- stTrie := NewStackTrie(stackTrieSponge)
+ stTrie := NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, blob []byte) {
+ rawdb.WriteTrieNode(stackTrieSponge, owner, path, hash, blob, db.Scheme())
+ })
// Fill the trie with elements
for i := 0; i < count; i++ {
// For the stack trie, we need to do inserts in proper order
@@ -878,10 +881,10 @@ func TestCommitSequenceStackTrie(t *testing.T) {
stTrie.TryUpdate(key, val)
}
// Flush trie -> database
- root, nodes, _ := trie.Commit(false)
+ root, nodes := trie.Commit(false)
// Flush memdb -> disk (sponge)
db.Update(NewWithNodeSet(nodes))
- db.Commit(root, false, nil)
+ db.Commit(root, false)
// And flush stacktrie -> disk
stRoot, err := stTrie.Commit()
if err != nil {
@@ -913,21 +916,23 @@ func TestCommitSequenceStackTrie(t *testing.T) {
// not fit into 32 bytes, rlp-encoded. However, it's still the correct thing to do.
func TestCommitSequenceSmallRoot(t *testing.T) {
s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"}
- db := NewDatabase(s)
+ db := NewDatabase(rawdb.NewDatabase(s))
trie := NewEmpty(db)
// Another sponge is used for the stacktrie commits
stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
- stTrie := NewStackTrie(stackTrieSponge)
+ stTrie := NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, blob []byte) {
+ rawdb.WriteTrieNode(stackTrieSponge, owner, path, hash, blob, db.Scheme())
+ })
// Add a single small-element to the trie(s)
key := make([]byte, 5)
key[0] = 1
trie.TryUpdate(key, []byte{0x1})
stTrie.TryUpdate(key, []byte{0x1})
// Flush trie -> database
- root, nodes, _ := trie.Commit(false)
+ root, nodes := trie.Commit(false)
// Flush memdb -> disk (sponge)
db.Update(NewWithNodeSet(nodes))
- db.Commit(root, false, nil)
+ db.Commit(root, false)
// And flush stacktrie -> disk
stRoot, err := stTrie.Commit()
if err != nil {
@@ -1097,7 +1102,7 @@ func benchmarkDerefRootFixedSize(b *testing.B, addresses [][20]byte, accounts []
trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i])
}
h := trie.Hash()
- _, nodes, _ := trie.Commit(false)
+ _, nodes := trie.Commit(false)
triedb.Update(NewWithNodeSet(nodes))
b.StartTimer()
triedb.Dereference(h)
@@ -1123,8 +1128,8 @@ func TestDecodeNode(t *testing.T) {
elems = make([]byte, 20)
)
for i := 0; i < 5000000; i++ {
- rand.Read(hash)
- rand.Read(elems)
+ crand.Read(hash)
+ crand.Read(elems)
decodeNode(hash, elems)
}
}
diff --git a/trie/util_test.go b/trie/util_test.go
index 252dc09e08..8d925a16aa 100644
--- a/trie/util_test.go
+++ b/trie/util_test.go
@@ -17,10 +17,12 @@
package trie
import (
+ "bytes"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
)
// Tests if the trie diffs are tracked correctly.
@@ -68,9 +70,11 @@ func TestTrieTracer(t *testing.T) {
}
// Commit the changes and re-create with new root
- root, nodes, _ := trie.Commit(false)
- db.Update(NewWithNodeSet(nodes))
- trie, _ = New(common.Hash{}, root, db)
+ root, nodes := trie.Commit(false)
+ if err := db.Update(NewWithNodeSet(nodes)); err != nil {
+ t.Fatal(err)
+ }
+ trie, _ = New(TrieID(root), db)
trie.tracer = newTracer()
// Delete all the elements, check deletion set
@@ -122,3 +126,180 @@ func TestTrieTracerNoop(t *testing.T) {
t.Fatalf("Unexpected deleted node tracked %d", len(trie.tracer.deleteList()))
}
}
+
+func TestTrieTracePrevValue(t *testing.T) {
+ db := NewDatabase(rawdb.NewMemoryDatabase())
+ trie := NewEmpty(db)
+ trie.tracer = newTracer()
+
+ paths, blobs := trie.tracer.prevList()
+ if len(paths) != 0 || len(blobs) != 0 {
+ t.Fatalf("Nothing should be tracked")
+ }
+ // Insert a batch of entries, all the nodes should be marked as inserted
+ vals := []struct{ k, v string }{
+ {"do", "verb"},
+ {"ether", "wookiedoo"},
+ {"horse", "stallion"},
+ {"shaman", "horse"},
+ {"doge", "coin"},
+ {"dog", "puppy"},
+ {"somethingveryoddindeedthis is", "myothernodedata"},
+ }
+ for _, val := range vals {
+ trie.Update([]byte(val.k), []byte(val.v))
+ }
+ paths, blobs = trie.tracer.prevList()
+ if len(paths) != 0 || len(blobs) != 0 {
+ t.Fatalf("Nothing should be tracked")
+ }
+
+ // Commit the changes and re-create with new root
+ root, nodes := trie.Commit(false)
+ if err := db.Update(NewWithNodeSet(nodes)); err != nil {
+ t.Fatal(err)
+ }
+ trie, _ = New(TrieID(root), db)
+ trie.tracer = newTracer()
+ trie.resolveAndTrack(root.Bytes(), nil)
+
+ // Load all nodes in trie
+ for _, val := range vals {
+ trie.TryGet([]byte(val.k))
+ }
+
+ // Ensure all nodes are tracked by tracer with correct prev-values
+ iter := trie.NodeIterator(nil)
+ seen := make(map[string][]byte)
+ for iter.Next(true) {
+ // Embedded nodes are ignored since they are not present in
+ // database.
+ if iter.Hash() == (common.Hash{}) {
+ continue
+ }
+ seen[string(iter.Path())] = common.CopyBytes(iter.NodeBlob())
+ }
+
+ paths, blobs = trie.tracer.prevList()
+ if len(paths) != len(seen) || len(blobs) != len(seen) {
+ t.Fatalf("Unexpected tracked values")
+ }
+ for i, path := range paths {
+ blob := blobs[i]
+ prev, ok := seen[string(path)]
+ if !ok {
+ t.Fatalf("Missing node %v", path)
+ }
+ if !bytes.Equal(blob, prev) {
+ t.Fatalf("Unexpected value path: %v, want: %v, got: %v", path, prev, blob)
+ }
+ }
+
+ // Re-open the trie and iterate the trie, ensure nothing will be tracked.
+ // Iterator will not link any loaded nodes to trie.
+ trie, _ = New(TrieID(root), db)
+ trie.tracer = newTracer()
+
+ iter = trie.NodeIterator(nil)
+ for iter.Next(true) {
+ }
+ paths, blobs = trie.tracer.prevList()
+ if len(paths) != 0 || len(blobs) != 0 {
+ t.Fatalf("Nothing should be tracked")
+ }
+
+ // Re-open the trie and generate proof for entries, ensure nothing will
+ // be tracked. Prover will not link any loaded nodes to trie.
+ trie, _ = New(TrieID(root), db)
+ trie.tracer = newTracer()
+ for _, val := range vals {
+ trie.Prove([]byte(val.k), 0, rawdb.NewMemoryDatabase())
+ }
+ paths, blobs = trie.tracer.prevList()
+ if len(paths) != 0 || len(blobs) != 0 {
+ t.Fatalf("Nothing should be tracked")
+ }
+
+ // Delete entries from trie, ensure all previous values are correct.
+ trie, _ = New(TrieID(root), db)
+ trie.tracer = newTracer()
+ trie.resolveAndTrack(root.Bytes(), nil)
+
+ for _, val := range vals {
+ trie.TryDelete([]byte(val.k))
+ }
+ paths, blobs = trie.tracer.prevList()
+ if len(paths) != len(seen) || len(blobs) != len(seen) {
+ t.Fatalf("Unexpected tracked values")
+ }
+ for i, path := range paths {
+ blob := blobs[i]
+ prev, ok := seen[string(path)]
+ if !ok {
+ t.Fatalf("Missing node %v", path)
+ }
+ if !bytes.Equal(blob, prev) {
+ t.Fatalf("Unexpected value path: %v, want: %v, got: %v", path, prev, blob)
+ }
+ }
+}
+
+func TestDeleteAll(t *testing.T) {
+ db := NewDatabase(rawdb.NewMemoryDatabase())
+ trie := NewEmpty(db)
+ trie.tracer = newTracer()
+
+ // Insert a batch of entries, all the nodes should be marked as inserted
+ vals := []struct{ k, v string }{
+ {"do", "verb"},
+ {"ether", "wookiedoo"},
+ {"horse", "stallion"},
+ {"shaman", "horse"},
+ {"doge", "coin"},
+ {"dog", "puppy"},
+ {"somethingveryoddindeedthis is", "myothernodedata"},
+ }
+ for _, val := range vals {
+ trie.Update([]byte(val.k), []byte(val.v))
+ }
+ root, set := trie.Commit(false)
+ if err := db.Update(NewWithNodeSet(set)); err != nil {
+ t.Fatal(err)
+ }
+ // Delete entries from trie, ensure all values are detected
+ trie, _ = New(TrieID(root), db)
+ trie.tracer = newTracer()
+ trie.resolveAndTrack(root.Bytes(), nil)
+
+ // Iterate all existent nodes
+ var (
+ it = trie.NodeIterator(nil)
+ nodes = make(map[string][]byte)
+ )
+ for it.Next(true) {
+ if it.Hash() != (common.Hash{}) {
+ nodes[string(it.Path())] = common.CopyBytes(it.NodeBlob())
+ }
+ }
+
+ // Perform deletion to purge the entire trie
+ for _, val := range vals {
+ trie.Delete([]byte(val.k))
+ }
+ root, set = trie.Commit(false)
+ if root != types.EmptyRootHash {
+ t.Fatalf("Invalid trie root %v", root)
+ }
+ for path, blob := range set.deletes {
+ prev, ok := nodes[path]
+ if !ok {
+ t.Fatalf("Extra node deleted %v", []byte(path))
+ }
+ if !bytes.Equal(prev, blob) {
+ t.Fatalf("Unexpected previous value %v", []byte(path))
+ }
+ }
+ if len(set.deletes) != len(nodes) {
+ t.Fatalf("Unexpected deletion set")
+ }
+}
diff --git a/trie/utils.go b/trie/utils.go
index 7e26915041..5dce65cd29 100644
--- a/trie/utils.go
+++ b/trie/utils.go
@@ -50,45 +50,43 @@ func newTracer() *tracer {
}
}
-/*
// onRead tracks the newly loaded trie node and caches the rlp-encoded blob internally.
// Don't change the value outside of function since it's not deep-copied.
-func (t *tracer) onRead(key []byte, val []byte) {
+func (t *tracer) onRead(path []byte, val []byte) {
// Tracer isn't used right now, remove this check later.
if t == nil {
return
}
- t.origin[string(key)] = val
+ t.origin[string(path)] = val
}
-*/
// onInsert tracks the newly inserted trie node. If it's already in the deletion set
// (resurrected node), then just wipe it from the deletion set as the "untouched".
-func (t *tracer) onInsert(key []byte) {
+func (t *tracer) onInsert(path []byte) {
// Tracer isn't used right now, remove this check later.
if t == nil {
return
}
- if _, present := t.delete[string(key)]; present {
- delete(t.delete, string(key))
+ if _, present := t.delete[string(path)]; present {
+ delete(t.delete, string(path))
return
}
- t.insert[string(key)] = struct{}{}
+ t.insert[string(path)] = struct{}{}
}
// onDelete tracks the newly deleted trie node. If it's already
// in the addition set, then just wipe it from the addition set
// as it's untouched.
-func (t *tracer) onDelete(key []byte) {
+func (t *tracer) onDelete(path []byte) {
// Tracer isn't used right now, remove this check later.
if t == nil {
return
}
- if _, present := t.insert[string(key)]; present {
- delete(t.insert, string(key))
+ if _, present := t.insert[string(path)]; present {
+ delete(t.insert, string(path))
return
}
- t.delete[string(key)] = struct{}{}
+ t.delete[string(path)] = struct{}{}
}
// insertList returns the tracked inserted trie nodes in list format.
@@ -98,8 +96,8 @@ func (t *tracer) insertList() [][]byte {
return nil
}
var ret [][]byte
- for key := range t.insert {
- ret = append(ret, []byte(key))
+ for path := range t.insert {
+ ret = append(ret, []byte(path))
}
return ret
}
@@ -111,22 +109,37 @@ func (t *tracer) deleteList() [][]byte {
return nil
}
var ret [][]byte
- for key := range t.delete {
- ret = append(ret, []byte(key))
+ for path := range t.delete {
+ ret = append(ret, []byte(path))
}
return ret
}
-/*
+// prevList returns the tracked node blobs in list format.
+func (t *tracer) prevList() ([][]byte, [][]byte) {
+ // Tracer isn't used right now, remove this check later.
+ if t == nil {
+ return nil, nil
+ }
+ var (
+ paths [][]byte
+ blobs [][]byte
+ )
+ for path, blob := range t.origin {
+ paths = append(paths, []byte(path))
+ blobs = append(blobs, blob)
+ }
+ return paths, blobs
+}
+
// getPrev returns the cached original value of the specified node.
-func (t *tracer) getPrev(key []byte) []byte {
- // Don't panic on uninitialized tracer, it's possible in testing.
+func (t *tracer) getPrev(path []byte) []byte {
+ // Tracer isn't used right now, remove this check later.
if t == nil {
return nil
}
- return t.origin[string(key)]
+ return t.origin[string(path)]
}
-*/
// reset clears the content tracked by tracer.
func (t *tracer) reset() {
@@ -165,3 +178,22 @@ func (t *tracer) copy() *tracer {
origin: origin,
}
}
+
+// markDeletions puts all tracked deletions into the provided nodeset.
+func (t *tracer) markDeletions(set *NodeSet) {
+ // Tracer isn't used right now, remove this check later.
+ if t == nil {
+ return
+ }
+ for _, path := range t.deleteList() {
+ // There are a few possibilities for this scenario(the node is deleted
+ // but not present in database previously), for example the node was
+ // embedded in the parent and now deleted from the trie. In this case
+ // it's noop from database's perspective.
+ val := t.getPrev(path)
+ if len(val) == 0 {
+ continue
+ }
+ set.markDeleted(path, val)
+ }
+}