Merge pull request #496 from status-im/stateless_client_experiment
[WIP] Stateless client experiment: The Block Witness
This commit is contained in:
commit
7a0215608e
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"version": "0x01",
|
||||
"metadata": "0x00",
|
||||
"rootHash": "0xb629598af09d10fde456989823c057fd43f3e1bdb89edf2fb03db28e3db2e7e6",
|
||||
"error": false,
|
||||
"tree": [
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x01",
|
||||
"nibblesLen": "0x40",
|
||||
"nibbles": "0xa07af6d1916daa43b026a3ed4dd13ede4bb6564c533079f656a1914977f53b20",
|
||||
"address": "0x4105c32aa5078e4c5a5cdc3963191cd6742f89a6",
|
||||
"balance": "0x76c2193f6e05e17d5e57ea46ac608248bf517ee093cdfc4b23a92bbf4a1198de",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000fd3a49b23e5c4d37",
|
||||
"codeLen": "0x00000065",
|
||||
"code": "0x70a92f12189c6d8a3d42aa6d43fed21c096611ddfec2712d1ccad9bb3025f52c1d3a2c698c54aa620c535c23cfff68adab866d3db0c4eeec6a3bc3cddbb2f0d2526a89b0e216c031bd7e79d3d92aa48aaa7d918130d6170c0097eab79a8a0a865f35ad7996",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x00",
|
||||
"debugHash": "0xb629598af09d10fde456989823c057fd43f3e1bdb89edf2fb03db28e3db2e7e6"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": "0x01",
|
||||
"metadata": "0x00",
|
||||
"rootHash": "0x2cb26cf92076431e8549bdad4c81cfb37134cfcea04c3618bfb053e0278f84d4",
|
||||
"error": false,
|
||||
"tree": [
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x00",
|
||||
"nibblesLen": "0x40",
|
||||
"nibbles": "0x5a8c577c20f79ce03b2ee5755d3c933cb066d1fd7a34da5da38ae442307d00ce",
|
||||
"address": "0xa18c851c300dc383bc1b4617e329e06c0de4a0dd",
|
||||
"balance": "0x918bfb1039de884aa3360be3e7aaf2cd8532e58325079a976182f696ce2a747f",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000c4568a0c22badf95",
|
||||
"debugDepth": "0x00",
|
||||
"debugHash": "0x2cb26cf92076431e8549bdad4c81cfb37134cfcea04c3618bfb053e0278f84d4"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"version": "0x01",
|
||||
"metadata": "0x00",
|
||||
"rootHash": "0x7606196682dd424ec6eefb69e61e876066caacc128c5512781f8a99ccd31466e",
|
||||
"error": false,
|
||||
"tree": [
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x40",
|
||||
"nibbles": "0x04189264d135c92ef0a3f28012ac29fd1606b207ad1dc20a6e7cb94f020aa851",
|
||||
"address": "0x44ae06bf1d2c4ec0d1b32f5c231817acffafff3a",
|
||||
"balance": "0x88e7daf3c42e645edc66102f736e1f411a59ceeaef060384800471cdb7924b64",
|
||||
"nonce": "0x00000000000000000000000000000000000000000000000071f4ab8ce322bf60",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0x5472554f2f4e2e3939fed9720dcaff5f8e4359e5ee8a6376e42bb94218449710"
|
||||
},
|
||||
"codeLen": "0x00000031",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x00",
|
||||
"debugHash": "0x7606196682dd424ec6eefb69e61e876066caacc128c5512781f8a99ccd31466e"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"version": "0x01",
|
||||
"metadata": "0x00",
|
||||
"rootHash": "0x6b7a89f83333a21042df2fbeed69f85800278b3c9a02af68b9c31e19808bf505",
|
||||
"error": false,
|
||||
"tree": [
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x01",
|
||||
"nibblesLen": "0x40",
|
||||
"nibbles": "0x6acb512a5750d737c3ab7d040617934b15e0b5874e2e5fe976ed87498e53c430",
|
||||
"address": "0x02c391005fb75ad7c7bfdd17514257918c9408ef",
|
||||
"balance": "0x1da1cbddf6d6f1881a3b4425729c28f2376140aa0cb5c6f053c19f4423873191",
|
||||
"nonce": "0x0000000000000000000000000000000000000000000000008efea63d92fe33cf",
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0xba3718b0e569473fe0738a3b9002d9f98a0bc006e32c3b9169c41d8aefa98389"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x00",
|
||||
"debugHash": "0x6b7a89f83333a21042df2fbeed69f85800278b3c9a02af68b9c31e19808bf505"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,368 @@
|
|||
{
|
||||
"version": "0x01",
|
||||
"metadata": "0x00",
|
||||
"rootHash": "0x1339b5d780ff5c4713b6a6d38faf4448cf5afd4b616ffe3c88d15f38d2c575ac",
|
||||
"error": false,
|
||||
"tree": [
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x40",
|
||||
"nibbles": "0xa57728d9ff180513d0f4a43a19487dc761e3a1a855a8ebf69a2539a0701feae6",
|
||||
"address": "0x8a2dd1126fcff91b18f4a4c39d44d5dbc67c39fc",
|
||||
"balance": "0x3112cc29335cf1b5f3c06a4931b324b36d4968e52aecbbe757dd2d972a876f00",
|
||||
"nonce": "0x0000000000000000000000000000000000000000000000000e428f438a0c77af",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0xF775",
|
||||
"debugDepth": "0x00",
|
||||
"debugHash": "0xd6b117fadf28c9270701aed45935300890d9ad9e49f9e357f7f3b507ea6a9720"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x0060",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x38aa0f0b22bc6f5a56493334f8e45ccc8a9b1af635db4aae2eb1e74e259c6d53"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x670ec6cb145c744027c61392367a6295d103b422dd59736cdfce88b02a10fc",
|
||||
"key": "0xd47303578066f85f494628ecbbdbfce93934b0c88b57000ef475d5a932e0e614",
|
||||
"value": "0x1d10ff79b7b0669088fa516d4231b5768cde9e5331852fe19175473e647afd02",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x4d236fe1cf06227aaa78d93391635312f08c0b175a946902fc280aeffc12a72d"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x36ee270a10f395a3ba43efc7a6fe7beca941b22423a36fd13aa435161eb9df",
|
||||
"key": "0x51a2287382f8ac3a56ae9836ba516a8960b1684b7445e6e0f88f0382855dff46",
|
||||
"value": "0xfe3bd104e8ba40ffce7fe51f8bd0055b8eb4e223a9e160526c43165647dc84ac",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x2f6c0056e0133db054d02978bdf777484f2d4c095e0e1470e8751f6ddc2700fd"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x88A4",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0xb0010cee8b764bf1de83ff22402f50467e915982eb23a70f11dff5d34c9fc6d3"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xb1123325eff75f1b1068bff50b94305d0b41a46b362ce68e667539ed2836bf",
|
||||
"key": "0x805e8ae7acddfc2e458dd7635fa4b56c86889163334b37032607315d0ca7a6e9",
|
||||
"value": "0x7471c619144edeeca438052ce4d5f9229dd71f02e7135898fda4035421d935e7",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xaf87d5710f7a6e22a42de8d5acb887c81ff3ae048caf4a5a4e7ce221fa1fde55"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x15af5858ac6d05f3d8be1ea71b2f6fa30efc8c16b19fc7d286d589bd66af80",
|
||||
"key": "0x05d8aa012a701b4efd4efb4c120519f163952b90e21340c6ef1173fe956ddef4",
|
||||
"value": "0xc1720d16624c82fd76a363fc18bc4c87d7e565031ced6ec1820b6578a0f4c81c",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x8e14a0921e13c2a523b6df04c37021a871b44c467d4830dda68f3ae07ee25d21"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x66aaf0c3b22227acc68b729c59cdbf683c277bb7af3a707f8f585b40874ff3",
|
||||
"key": "0x9a019ba831a4c0210320bf1889028db5ae90b7bd11ab10dc4a3ddde8ee84d14c",
|
||||
"value": "0xc683ade70bffc7ef21fd8551307e5b20fce6bfd9711ec075b3f1c96d41033152",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x487bf79cb03594d96cf2e8a04d9e6fa8b851c32a803103b378b5c709e419b39f"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x23e0151c7fcb23426f2da041d4f6019c6f6eb13c1499ecc50194710891a7e6",
|
||||
"key": "0xe5f78889ca7f3dbfe969d8a7a010aa6295633d8a48b230656dfd8d66920fc1ea",
|
||||
"value": "0x95a04bb13a597ff974c4a29cdb75247e23cc8edbd22675e552dbf5af98e40202",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x888b26ca068a203d42cc03a5f1abdc05318ce1b9c60a10f9e26beb8c2ef1d556"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x92133c50c4750f77d1919381bbac96d3d6b547a04fbfc491f2a09acf5341f7",
|
||||
"key": "0x0362fc5636f51d7d599e38b03c04335ee4f25ff3a57836770ec15aef3c5b1dc6",
|
||||
"value": "0xc1a218145629baa10e8e240bf441b885b07334b2524385832af163453ef2a579",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xaee90a9ec4acbd77c9994392ab04bcdb5c3948e9fb7db0dc4f28a68ca1dd41c4"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0x492c255d77b7c939c4ac0d4e7e454a6097a7069e4dbe8b64a09ede2ff2946090",
|
||||
"key": "0xb399be595c10390baec326c817989157fba6a9a79b2f3353932145282e4fca2b",
|
||||
"value": "0xd8f3398e619934b608690ca8a2c28f1ac6640c8b8ad62ad4cc8f3414ceb33d53",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x9b08aa2850104aef77f08ad2b762f8f4ae6a9befaadf533fde71bbeb73810397"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x4100",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x6bbb61d3afb91adc479f566515a528f957a363c98988ebc4553811486a11a5e8"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xa859d85f4d223a3358edd3556de6d301310926ac690c4d13dceaaa15b05b17",
|
||||
"key": "0xfedf29d1f7ff6065f56a548c7da12a6b4ee2cd6b088940b32a82bbb1b6ff395d",
|
||||
"value": "0xb47d2ae9ab9971ab268cc111f0fac015b66def89fa91b44d077af6cc18b1b948",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x0d1348b49bd2189cff90ac7482ffb5f448ff67a4b5ee4ea52fa6cd243183d6fc"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x0092",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x63d2a41df24fd6273e1af812dd9b63714916ee77c9992c1ca530892099361af6"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x4080",
|
||||
"debugDepth": "0x03",
|
||||
"debugHash": "0x57916fd66b5b9034e6cbdb1e372581d9a4424f354001bdce453609f74f8e8154"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3C",
|
||||
"nibbles": "0xe51ecd3ca9d839e76cecb1899605d9823042d0c7e952677889d60c85a470",
|
||||
"key": "0x173418876815874e12e0cf64ee8e715fa50206e3701b609d7492794c4ccd5ccf",
|
||||
"value": "0x278583f4b39f083e342a4fb9e73f5e960574cc3d169f8dba489323b096846c46",
|
||||
"debugDepth": "0x04",
|
||||
"debugHash": "0xf2da2f0f6a12c2608cf9e0f74aeb1c810fbb78333dfd693998e283bbf0b2d9e7"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3C",
|
||||
"nibbles": "0x058b46b73db3148a9a602ca3ca61c84ae6910ff52dee0a6a8496847b06d5",
|
||||
"key": "0x0f26d7f25a28e42c0dccb25ffe80b8b5809c959b3569c3846cff92fcb0c9fe78",
|
||||
"value": "0x4565d0128407097f6501663f4d4dcbc5dda8de6d0f94e011fc5047fadd0a0300",
|
||||
"debugDepth": "0x04",
|
||||
"debugHash": "0xe2522da28a84aaeb27559b46ae11a0178aab3d037fe0afeb8c841cf4c67e3f18"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3D",
|
||||
"nibbles": "0x0b5dbc85adfa492866bba900002898da8bc62c304741a9c6f5a86197a48dd0",
|
||||
"key": "0x6bf8a2cfb8a1145e1f15bfaba4bed473c587e83c5ea9b4c301ad377ca34eba5b",
|
||||
"value": "0xca4a36a0492a335c3e8c514fe22636ddafeeda2dbf165d1e5eceb5bfb3829c64",
|
||||
"debugDepth": "0x03",
|
||||
"debugHash": "0x98ea9a4289c03dd2e159425974fbcd33ff7d0dc5a8a7fce3cd7560f866d7f2bd"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3D",
|
||||
"nibbles": "0x1d8579629c13701a44923e49f90bfa26f68e945794cbba0a786440996a4f90",
|
||||
"key": "0x339db4aef12c6c97fcf9c0b6b15b311fab3e3f3e20638bee68b240a69e2f8adb",
|
||||
"value": "0x0281eba07ad0a496b31ee78cdfb31e288e62955a863c571a1427a0c1c2a95fd9",
|
||||
"debugDepth": "0x03",
|
||||
"debugHash": "0x69d5b64fe7d673dbbcdf19dbaaae6cfb82c2757be0493647645f28ad7ffaf37a"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0x38569ce15d242a83b80dc88dcc23d9f992546f3937aaa1d5647b4d764ba40550",
|
||||
"key": "0xd1c1388e9002ace271300b3be4d770e019f1111e893f1b9584687e63cc2c5a17",
|
||||
"value": "0xef019bf1216665c77c65b2340d8c15ffaed389986a93cf9b93ab27c73ef3321e",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x2ee61de4a374d6e41db18773574381184e82c23aced8c2f0300de22ffb556cd4"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x6080",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x8edad43d497090bad60736a22ee2392157e329006155e2310b78d0a54dcba5ce"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x3000",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x307fc7ba651c35a1fa257d978e0841bf5399226fb81dc1e06483cc9be829e4e1"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3D",
|
||||
"nibbles": "0xcadb4169ff5ce8ae9b4ca06179e2502aaf828313cb0013f6e914c38d3d9bd0",
|
||||
"key": "0x8ffe8b9343b6bc02f514f476b13628f38534e92ac97ded5276facc4f4cfa8250",
|
||||
"value": "0x0a9d9838420e37b10ad52beb5be1559c1bcb2f093f234397a5e654c69bb9f190",
|
||||
"debugDepth": "0x03",
|
||||
"debugHash": "0x0341fc421bc0a7589bdfc068a4ade59bc6f186b3c438e24c646426aab179f854"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3D",
|
||||
"nibbles": "0x97605b517295134624827074186b88ad216329a24a6021faa56f590e2e4620",
|
||||
"key": "0xec2dc7abea0ed2bf9fb0658b3bab68271e4497a88641a96ce105ae55ac4ea49a",
|
||||
"value": "0x8aa06378b75cef01014f24059c69352d306acd08dacf06665ee72ee718dc2d6c",
|
||||
"debugDepth": "0x03",
|
||||
"debugHash": "0x92789930301fb213ab4f6042f4f2e7a3ee3b5afba225abce7696e99af2c63ff7"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xb88bca75c3618c9c09965bdbd9b51e1d9b5ba88681302b8b2bbff74e6ff4a4",
|
||||
"key": "0xd8aeab934633fd7e3e450f14bd872460083d1de3c77646b90be32b185ff281f7",
|
||||
"value": "0xb4be3be2bfb96e126a6f93aa3ea0a52d51e60438fa0e7d9659070ecacdf7af8e",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xd181c045e343efc64cac28c95177c12008d28ececbbd89efc8b11a86768017f8"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xfdecc844a495ac72a2deb78001aab6396cc1336ec686fe4e1d43af94f49766",
|
||||
"key": "0x295746206953cbbab7ea0b19a4b44833cc0355715a2f70ec23106cb2683c250d",
|
||||
"value": "0xdcaf2a863256a392a5d3033b3df492f6ef0d13b4ce87e443cc04b9f27af8f9e9",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x51f8ced73dfae44084e80177da9e2c64a2851393f67ddafbff3f5135ac1685f2"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x1800",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x9e79a6c3e231c1f73238c630882f0598fe3a2494b1be9fb01a4c70f3d99534d9"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x717b9e72c9f6e1fe46a73fb9ece0486c5c315e4312d89daabaa8f56679a505",
|
||||
"key": "0x24b3cfaa33689e2c5d6ba7f8c745079a6e970b853626ffd3ff6e9becaaf8c4ed",
|
||||
"value": "0xffbe98e3216fad147196eef602efd204edd66b29905a6c3c0715d4b0ac870953",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xbe2e89feda819f370ee9c38800b7a9f204f22493eaa3b94165134bfd47630ee4"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x7a47a87a25fe4036884f4617fc08b10ce31de4519cbb697ecabc6b20a9a6cd",
|
||||
"key": "0x965383b1aac06662174f12a830c8450d1720c990abc9e84f03e0c8cb7d302ace",
|
||||
"value": "0x39c6d0dc737d09851cc33457173ad226bc2e2a6c48652640dc58c8688499517c",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xb4691deec34163d079adee1f7b7352081b394835a4b64a9d071407c7c2280dd6"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0xB010",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x87a9e46802cc57d7e0fb9a446c206f878184edfcb4fd65f1098cec0ee4e3cbb0"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xf1c64c60a7378e7ff606ec7b0bc9478fab2aa8a6997716a0a2ae70b71fc1a7",
|
||||
"key": "0x679e6657894cd6490e4b1e324e432b2e0503926425e9c2366008d96af1ae8107",
|
||||
"value": "0x00b8a87b3ecd85957e4d838db426cc071d3eade358facecd1007d8b50f4e5ec4",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xdab2d5f2c4f5655ed4c5999577fd91e1abaa12fa8f16ee2775d3ccbed7b1050a"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x6ee282483aa94e5bfad3a454bbce74252ace5047ab53510d048a6fbeabe246",
|
||||
"key": "0xe9a7a5de344d9613bbd89869bec0ef5ecf27da5d25fb4798e87ac536093f0219",
|
||||
"value": "0xa00f88b6abd0b009bd7588276c20ff7c022f5f262fa94b101af23e4568c71d77",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xc3d0a14d3586743c0e46934eceeeb27fa318278d8fd3c73353bdf03228d4ffd0"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xe083b63a7e214b9332a0425184a1fff969a41b8b3d7ad722788560ec3ddb6e",
|
||||
"key": "0x4ae89c3f9baf604b2a7577e2fb777234eceaef38a2e1d9c43f31a285860dd7ae",
|
||||
"value": "0x2e941e13d3e10058b9a51c55a5dede81503bf251fe88c998e4773a7cd2444210",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x9618fa5b387166dac0c5730072df1c6c049a3377409d3f73f12545af5de679cb"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x48c88b69e4b28061ab6ef96e3deb32ee2baeef3d95d60182af3282c011a73c",
|
||||
"key": "0xca91b36bc6f1dbc05de1c192d295a1ec0b63e5aa242e938500fbd5e22a1d79d7",
|
||||
"value": "0x71c624a3514d58477cf2d7de820e86aaab76801bfb5efa33c0f0a8dc5c93540d",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x3fa774bb0791206032f45e46bb3ddbe0469af43c3bcb34f50e967f691b631024"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x0204",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0xdd5c358287de72f92177c363b4168138c8c1065876b9ec86edfaaf7fab52a29e"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x6cafca256c143fc834bdaaa2bef8ba11524017ef21a5f6f14627fe358c0199",
|
||||
"key": "0x2d8f35e61403a3cdb3a500ecc8ba7f38ed131bf9332e489acfbea7a526ed2998",
|
||||
"value": "0x55dfa122dcb9ec9a355dccd5ad980eef647997f0c8d8416b3ad3a5c1f73aeee8",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xd92bc164e7a3cf5706b86f69234939490c3380a2fba47fd40c806c49bf196c2a"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x2800",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xea45093605710f0f3caecfbccf866604b6f9f4d4b6aff15401a81b3e8028d81e"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3D",
|
||||
"nibbles": "0x62fcb48dad8fde60aa65c56fcde7f42d29ab0f26ffc65d6ebafce1a599d230",
|
||||
"key": "0x39d9e036e274a15b9b3995e5abeaacff6a16ecc6a7cdac267bdd68ff30f4fe92",
|
||||
"value": "0x29281a7408424d86a1d03d34944c41c7119568957bb83368e4943b19da821639",
|
||||
"debugDepth": "0x03",
|
||||
"debugHash": "0x3a372e5dcca1838848d69daae87e0e600b0ee93d91e3353aaac5cca660b4ea8f"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3D",
|
||||
"nibbles": "0x2c77fe45326219bc001b2289094ff91cf31b54cd094e088724ae86ec422710",
|
||||
"key": "0x86c484bd02cd510673437bad7e1311476357cf79c22aa39940aa346bbe422c82",
|
||||
"value": "0x9cff6938b49d23ad4aafcc1287a1dc4b92a4ae6d8b2a19e2eb2dbf69cb832c6f",
|
||||
"debugDepth": "0x03",
|
||||
"debugHash": "0xc8453e3fedf4a978147c46c3cf79e948578c65d5cfc8c383bfd961c6c406537b"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0x85a7bf6131d98e5ff192f2de020362355d25eca26ad7cd0a102a2d0b75e539f0",
|
||||
"key": "0xeb1447a26ff7a6774c0829023d035a00c5692a5a3f2674b5fdcb8cf7dff1f076",
|
||||
"value": "0x7f66777ee81441a2e40f8f63bbdce7212efea7e71c0a1822029cf242dd76a83c",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x5506f096d6aad311f63c3859ca99914add436af18b5d8548fdf71d5f10634eb5"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0xc03a5a73bf46960cf0faf50744a498bb3f859030474126168938a6a34accb690",
|
||||
"key": "0x5d3d053e01934e396430f5acbbc02a830386535c0a9bd703c8af6433b31c66ba",
|
||||
"value": "0x3f7cd3b23789334d059c04e73dba087e308df6290b027d6362975e70383da845",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x4bf8c9b2c727abc65fa1c7da36af8f729ef3724996a4c047fdbbe989fdc84cae"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0xa8b7f66172e2fb3a7280af6fd79a568396ba20ceb096c7ccd6cde239f00ffa50",
|
||||
"key": "0x4eeae1d7c640f79f8eb64db23114e1b02295937597216e317016a16066f0a7ae",
|
||||
"value": "0x20dff778776c5a927feb12c2fdb789fc1e2e61059cf84cdec2d39c83858892fe",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0xd25c39394a76913e380c010e4816b6fa1442e27d7323de9b5388bbc538b7b203"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x00",
|
||||
"debugHash": "0x1339b5d780ff5c4713b6a6d38faf4448cf5afd4b616ffe3c88d15f38d2c575ac"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,630 @@
|
|||
{
|
||||
"version": "0x01",
|
||||
"metadata": "0x00",
|
||||
"rootHash": "0x33ab08b27c4e481586bdedb4a9ce49dd58c2cafae01d876f7916e4dac7580228",
|
||||
"error": false,
|
||||
"tree": [
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0xEFFA",
|
||||
"debugDepth": "0x00",
|
||||
"debugHash": "0x33ab08b27c4e481586bdedb4a9ce49dd58c2cafae01d876f7916e4dac7580228"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0x50917b30ccc2fe4514748ff36f56cc7e9fd6ee58b30c123c7490145c0b558450",
|
||||
"address": "0xbd18f5b00d7356ec4668d8023a42ebe7c1b5c45d",
|
||||
"balance": "0xaf438892e5541b99b47d934f71356b59f507c5c1ac7f188fb2a2d4021d1dc1d3",
|
||||
"nonce": "0x0000000000000000000000000000000000000000000000005ff06a504991ad39",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xb70b054f6e7e87fe30ec6b0a082984b448157e04f5e1b6855ecb0290975ea1dc"
|
||||
},
|
||||
"codeLen": "0x00000024",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x1a408f6d5b218a89bc4f34effe5922e84ff034b87cab3cf3850adb72583d1fc2"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x0044",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x859ee667e5324accddc05e207a752e6434416f3071b76b2c56c9031ec4846f43"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xa6f9fd2a4bf2e3524e9b1ba2aa6465999b8dd980e92bccf04abdeac3dedbad",
|
||||
"address": "0xcb8137818b2815b061db70d16336676828049463",
|
||||
"balance": "0x465c956e5ed65a2b437d1f3a16ffd878945bb92d0c7e08e7f31a4ca06af644ae",
|
||||
"nonce": "0x00000000000000000000000000000000000000000000000032c893507f54a253",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xa3d6ad96683394495619e010f0fcc9fb51e765666209a7bc8e8267067672708c"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xf2c51c0dcff6af94072475f192ad0ffc6afb8a1dc5e5f7213687f2317ca3c7",
|
||||
"address": "0x21969343e6db5a4de7e64819d40f132bbf9daa41",
|
||||
"balance": "0x3fb0bacc5d560fe6638315a1ca612b529f7c03dbfcbe62d0307595fadc24eab5",
|
||||
"nonce": "0x0000000000000000000000000000000000000000000000001a364673a996e4be",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x0ffe84bfbe0d6d1d3cf54ff82260e4295166cf9ea6fff807e002d99e8e36796e"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0xf366db1cd3c6295c23c1479c5d42f3f9aecaaa9ed5ea3a5957327690d62e62b0",
|
||||
"address": "0xac68dcfdd88b953d9cbb1e868fd06899a0acb7c4",
|
||||
"balance": "0x32e58ab2d20a6fe5176d2c8af8e039eec6fac3c6292d358f1d7659587a99d798",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000f3f69e1544e903d5",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x038f42ab3523495b5f5d007c79e2d787f1c73260663270fe9af5e835f44423dc"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0x5c2b78e50db91990b3815ddedc6b5f41e8694ba638d045a960008e7c2cf732a0",
|
||||
"address": "0x4cf50aa9656ed022a487076b65a2cdbe7708c691",
|
||||
"balance": "0x34fb938aab1b8ecf716a70831b08b75d27cd63db26f01944ed69af164be41106",
|
||||
"nonce": "0x0000000000000000000000000000000000000000000000009fcded31b0abe602",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0xb25dc179bc8acecc614f3b06e3e04874cebe0741568229a196ada12396c91f05"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0x474d595e53aeff09a17aed2a78ce269a4367c6beca46fd6603d2dcedad88d930",
|
||||
"address": "0x44ec24c3284ea17787d3bf48d792653b59f1f1e5",
|
||||
"balance": "0x24b37a8a65c46c819951bb71cffa933f78f1933c52c12a52feb03e39c822d405",
|
||||
"nonce": "0x00000000000000000000000000000000000000000000000015f6d7d02500bd62",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x03522e7be57615ecbf4b99be35204d2da9282717fc0f75172ad2b9cd82b56566"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x00",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0x4cf3b6c855a4df3556b22bc6e32734f30581ec912904b8a85f2f68144b7dd2c0",
|
||||
"address": "0xdace27b32bfd1448c1d0babfa857ee03b612205a",
|
||||
"balance": "0x2eb87328b7cad0f6880275c03f3be52cac66e4a29c60d9aa38063fbf701ebcf3",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000498c2ecb4e61acb8",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0xda18555896116c7e0349358cc54b6fe3d9629b5b472ce4dcfdcf2dbbddab9419"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3F",
|
||||
"nibbles": "0x51c116bd7ce99bb83a965ce473b1d4a49883f2c10ae503eeadf1776866d22580",
|
||||
"address": "0x6f57c4e9cc210a0d58665d13de26584e1c575521",
|
||||
"balance": "0x4ba06cc5944b1f3be8bbdd9bd33728d572d2203c88edb071796c59e7fe4140db",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000f0a8e1741eafc2b3",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xbea52d3e1eec728ace8471598d267be569f975c9774f0089d5d0bb5599ea48a7"
|
||||
},
|
||||
"codeLen": "0x00000086",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x5d8df2a76c49dc0c13a5c2eef574ed578dbc2d04edc22a58d264459b91125293"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x8444",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0xe40d0be6259bc8c56e1af344d3b3003d7547a3046e95fd4b06e86a2803c20cb1"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x30fa6db60fb6747667f256165435321c583473b858ac6c72bed9d77cc1c653",
|
||||
"address": "0x5b9dfbf41e8a6282e87072d14503f72951a1cd68",
|
||||
"balance": "0x1919498c9878362f95f33f943af6a4592c7b25118e4a5315262b4a320cbbec3f",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000e7ae7ffab34d65a8",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0x8ebdbfa384e5d516ff0ce37c0d00a8c9dec4c12fdb79caca43dba923a1649082"
|
||||
},
|
||||
"codeLen": "0x00000015",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x8888110b7a04a0ab191b80bb60b42f4b86facf6401cd315d84c37300ba9deff3"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x01",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x1e4cd52a51ccb5faff13808bcc653192c25ffda77de8f1e8e37842a99a6802",
|
||||
"address": "0x504d8b02705b8f00e1de1f7b5c43d8e384bace30",
|
||||
"balance": "0xb0bde0adc91726d59d4007543d8ec27571ae4fd0ad413beb8e62c63c451d6805",
|
||||
"nonce": "0x00000000000000000000000000000000000000000000000009f8a98eb93d6c49",
|
||||
"codeLen": "0x00000071",
|
||||
"code": "0x6082dee0c4e43955f41b5d54fbef20edae2e441fd57cf47195ad350ade2f32c8050124cc8e32e09dc309e7017e7730db3a2d84ff092fad7491a942d8809262da884b82f2a310228d42ef20089a70d55b99df7b7b80fa8fedce7e6beec4c3fb236f912be4c404790abcc2bec78eb99bb111",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x469505c77970145ec4befdd2c8e2584d9d8eb5447d14bfd7d1dddf49ccdad507"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x00",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xb16c0811e068ced610e7547ad9c10aee66db3534a5eb90e0c04f9a789fd6aa",
|
||||
"address": "0xb64a7ed17ae2a52663279ef66455c7fa484c9b2f",
|
||||
"balance": "0x3e290c37379c3ad5e6d6487d098d5f2b95ffce965b5ac40ef90e9b3f790b4a99",
|
||||
"nonce": "0x0000000000000000000000000000000000000000000000001adfbef693fa606e",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xe85b5df972dfc36ddb431d12cade6cb232f35360508a781ac3533846e42720be"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x00",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xefa3370ffa90841c69bca4842768abafb56fa42968142a36e7b0ee4e6c7d62",
|
||||
"address": "0x0c3b48739d78c7d7b88764aec8e37c697b4f7bbc",
|
||||
"balance": "0x73efcd30d74a91b8608260ec4169ae5b0c725373a1a96887ab88858c3204cfc3",
|
||||
"nonce": "0x0000000000000000000000000000000000000000000000005d70b93581ad8e76",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xae0dba2ec2f669f0b745bc89e13907f0c006f5fb17dd0a647bc2a004895d56a8"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x0252",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0xe8ecd9180f6bc504049b9d1dbf18026da807e942b03153efe11e5e4310961dd4"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xed4e13ede6837db69acb01ee1b6c5e564dfb04bab66081cc91390bba6a8b7f",
|
||||
"address": "0x833524a6144afd2bfdf3227d61ce43c028847c5a",
|
||||
"balance": "0x54d61a5c663350d3b2acd6a569f5b791744099a5e956b978c99a9190108e80b0",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000c008cd97ba288ba4",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xbdd35facffb1b35e4036b93bbc1c210f0f4a6cb1ae6e4bed8389e717bcb7ed6e"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x00",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x576f079f2f1b58a815acce9a4c9948914b2ec587c554c78b650a210052f3bd",
|
||||
"address": "0xe4ae35d1dbdb1a046453683ef242f7486e4a3c86",
|
||||
"balance": "0x80130042ad828ed842ef8426ad8d16a4f2e7df72f2266b1d9fe39c1b47c921dc",
|
||||
"nonce": "0x00000000000000000000000000000000000000000000000010b4b50967dc1006",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xce357e297b023c8340177f7d1a30cfd0a5950ce3f96c300f8987510fd70e3dfe"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x00",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x163cc6675ee6c35321cda2e34671e2ce2535191d69723e28c0a04637c8275b",
|
||||
"address": "0x1ca28c312ff091aaf67e2763d7378baf95b91432",
|
||||
"balance": "0x0344a8a83ec8a260d55c447ed8a6c630c37cb73648431216a58201e5c2dc90da",
|
||||
"nonce": "0x0000000000000000000000000000000000000000000000000725b78844c9b0ef",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x0e7845242c0fc9c84079817a9a18121d3dd2caf4d3fbf09194f3e44566ad2d4d"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x287dd358063ce07afed1847b28d6bd660bb7b985352b8e217b5f3be72ad767",
|
||||
"address": "0xe3a57baa2f36eaa4b63d9f8023bc40e283b0f9a6",
|
||||
"balance": "0x05bbd29c7499d0dbbab3c571df50ccb0d4e7c01450e78940a1b56333d2106695",
|
||||
"nonce": "0x0000000000000000000000000000000000000000000000004ba238278fa829fd",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0x2f5a79bbaae04e2ca602ada697490f04d753c3372eabf21ec52d874a8e90c190"
|
||||
},
|
||||
"codeLen": "0x00000039",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x732a6af656b27855faec883c0b2c26eb15295a0371f95b4dba6e0cdbd52422d8"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x04A1",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0x9f0ee6ab549f33423e05805e7ce0c34334a99253cfe826834bd5e25b10f50c09"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x0eeef0aac3c0f6da0451f500e95af310a8e948305954edae3f50e2d49ba1bf",
|
||||
"address": "0x5b222f9ca37d9b2ac2f9ad8727706d0bff1adfa5",
|
||||
"balance": "0x59664f23453dec779670d852bada8679a2190ae51a02332cbc6ea5e5d59d73be",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000c97285bbf4450f7d",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xbe3b09dd41d1e72268537a47d78e5fdd285cbbf614b608da562517959c79f724"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x401d60ae5538f2a7bb660b145ef9d64364dd2fba94af20e295001888c9b18a",
|
||||
"address": "0x0bd512e0da9a6fdf693c76c71dbc7e1cf70b2edd",
|
||||
"balance": "0x191071075bbbbfb4ccc2612ee72f68b2ec50f05c4efefd0ed1ef3c79fc83e185",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000f0e076184037a367",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0x39e86c2ca43f0a9a037d537209ca28627e39bc3404811f52bf214736d1cdc689"
|
||||
},
|
||||
"codeLen": "0x00000087",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x87b65484de1f04df0326397e98772c98d66f4108f45d12ea18e5a6d376b45db0"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x01",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xd3dc28ddcafbede1eeb720334d83f3fd886633026d442dda98cb97aceded9e",
|
||||
"address": "0xb353de7447723a82f666e272b876fd4924794d2c",
|
||||
"balance": "0xec4c9a9ceb972b6e9befa307b16eab56739efe41b765ab94e3fb28a9be633af2",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000c13cdf484abe0556",
|
||||
"codeLen": "0x0000006d",
|
||||
"code": "0xd91e970ee979e6bd5569dc5cceaca8ab99a65e60c8b4e01712d72c19c6e799cde8b66305e1700557f1f54a1d2388e57c2427e663d67e194c6bc5691a862e89973300bffc2f4cbe1c92c67ab0a1309a1a7d47ba1573279a742131ab47677098f75c77ea75b658e5c7f90f6a94d6",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xdb21accb96e62f7f32cc8365382737e5082660c42007c3b33fe4fa0837f681ff"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xc99ba08f7c5820c725c6ced1623bb243e657717ecf998e2afc47434fcfd8c3",
|
||||
"address": "0x91907ad931a7db204091e7efe5ce42fff81793d5",
|
||||
"balance": "0x981ac79256fa899a8d0dc3d39bcaccbac61d9ddf6ae312d429186efa47d5c45f",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000443fab4f03bb80a2",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x66941324a32ce7dbb54c7add8e9caa75f7892dd4d743927b28e3150f4b3c564c"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x2061",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0xe0767ca3d93636ed584da75ce1b5a423100b4a53e7d5f67173927dc7bad29fe2"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x01",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xd8eef084aae1f4f72d66049aaf45fbe1463e0b2c1314ddce9bbd26d10e6831",
|
||||
"address": "0x6dfdc54b603cfff7bef12eaf0d1553b926093647",
|
||||
"balance": "0x9f16e6d923d43161b0af8fb5fb294e8e6331e19b71d04828187edd79926b522a",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000fc610a8f798bc046",
|
||||
"codeLen": "0x0000005c",
|
||||
"code": "0x094393c3f02d5db6965c943b7cd586d6121dc4340f10a274bfec1206e7e6c8fba55fc7ac312d7650a90688c244ca05ba86a49a5cacf33aa551ae0af77ddba6928a379f4516737eb6bcea828bbf3108c3f5d89e249231095b87dd2181",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x8b0831bb9c30253d4f87095aeedddd61940c973592a04d6f1f1897bb89aabcca"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x1eaff2a2b35d24c80a19319a8f0f647f780c582cfb73040b43135162ab122f",
|
||||
"address": "0x010e516429b597cbc1dd1335d1bb71344188b175",
|
||||
"balance": "0x4195dae0a829799f66d6087e81f69fcca2d8ac8b1e91441651ce1a76c6d36b70",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000e5b9a297e4cc0359",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x2cf40c59acc31c47f2ebba72b06b92fd741eb1ce0202ef3112ed3699ea342405"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x00",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x1d08df6afc15e003fd5fecf197544315ee81ea6d94012cd5ed11c830814ce4",
|
||||
"address": "0x05a945d3d7d42ea7edb13452a779f5967a5124b6",
|
||||
"balance": "0x20c44e58aa64ffa2c3b3ec10a3f555ff5bd09f01d821eadfe2bf3cc680008b1f",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000004e227da5c251f4",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xaa7249baa8a3e60f3e88594457a341ce997e1947d78418d17ee2ac65b43ae59b"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x31041ef3dfe2108bb113bc0e5aa893ca493f56b44e3a66f62883431af7d7af",
|
||||
"address": "0xf5ebce43a192f56757d007acd6279a551345cdb7",
|
||||
"balance": "0xec6d67c4aaa4b3ab924df0f05869f2d9ed9d8a87d49fad5970f93621313da108",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000797d04d1c536e32e",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xaf51e50607c4bae5e45fb90697ebebe4d702fbdf3d0d12a54267a770ffb1a144"
|
||||
},
|
||||
"codeLen": "0x00000095",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xdc7f6099982dc0f68bb51394785ccf7c79d71bd16f43573d1c7462a4a35a28de"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x0003",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0xb9d4fdfc712b511fb3a6e644e3f3591a64ee2b5623a048d9feb4929fe3dcd43a"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x0220",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x27812ea0e95193d31ef04a5804d66a249b7f985b5b471d48344445c398065efa"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3D",
|
||||
"nibbles": "0xfae6cb5549de19c35dd9b844747119a539c3b8174d7669b43a6ae3aa24aa60",
|
||||
"address": "0x67c101f45f7815b2ea52a3cb3875dc745782a679",
|
||||
"balance": "0x7c646d13fed03073b8e26fb2f5534025bc1a0d5fd7350c5be54718c0aaca2d3a",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000ca8ff2c76fcebd72",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x03",
|
||||
"debugHash": "0xeefd411e941ca3342801bf17f470187823c80d01248c640a079dd9ad3ae0dea9"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3D",
|
||||
"nibbles": "0x4a0f67aebeaccb5d3b3e79b364f4e88319e6abaec53cc4c37d820551c779b0",
|
||||
"address": "0x0a4c146a22fe765724139522319ee322fe543a46",
|
||||
"balance": "0x7519a83f8d075c41039a54b85dfa08ba8be1921d4763aaf64e26651800274865",
|
||||
"nonce": "0x000000000000000000000000000000000000000000000000b3d5bfa8c0551d1e",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0x2c33a267918bd92623a8043525055e167725af4d6de88935ae739560b11ef8d6"
|
||||
},
|
||||
"codeLen": "0x0000003b",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x03",
|
||||
"debugHash": "0x95ca5ca7f809a92822ad5b78f04e9ba6878f8239ad57a699e4060f78aba9a881"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x00",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x93af897b20d6aecc8bc998ed9919efe1f27d5cdcdd559cee2eb3f4bdea3b20",
|
||||
"address": "0x2e322b1d15211d6228937b3d7ad4e8889ff2c1e4",
|
||||
"balance": "0x750d978e0d7cf5fa983e624a514846233c21393cb3655d78dae7cf2fed724f49",
|
||||
"nonce": "0x00000000000000000000000000000000000000000000000058a2d96d970a8b6b",
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x88c1db3c937e62781726b71dedb669f51ee29f00a44686bfb3ccdca6c78e1e16"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x00",
|
||||
"mask": "0x0805",
|
||||
"debugDepth": "0x01",
|
||||
"debugHash": "0xa58b9af46406de5e5f249308f0f1e5e961431e42b21e7ed9940e2c3cf0b7fb00"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x01",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xc4a591be6849791dcf157167e92a0827e6ccbe0f77544207c9bf42fb04e112",
|
||||
"address": "0x231936cb38eb94a6ed026e569c0f5196cb02d01f",
|
||||
"balance": "0x88a02f6772fef345f8fccb37905635832a07e9d75639976772802023f7db3a88",
|
||||
"nonce": "0x0000000000000000000000000000000000000000000000004518bbd97bc330b6",
|
||||
"codeLen": "0x0000000f",
|
||||
"code": "0x802e383c4083366f4b08cd2c87ba30",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xb64956bc3c115e4b66fcec3e44952142eec38c89123cb2bfd8ad00840c2a58cc"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x01",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0x53343e7c6935e37df0e84447c24ac4da8ea6868b1d50d8699ba4fc8666f1e1",
|
||||
"address": "0x681591b66ea7f2921708a5d0906ee54cfe4f032d",
|
||||
"balance": "0xa9c1f939bd16ba2d4d87c5f3f9cb08964d16f1f437700cc8f750b5d7b4065323",
|
||||
"nonce": "0x00000000000000000000000000000000000000000000000064008f081b402198",
|
||||
"codeLen": "0x0000003d",
|
||||
"code": "0x70203eee4f3c22c6844c47941c50164777f0063ff3bbc17ee9f0f299ab2de7ac1347aa87204d0290dce7fbb8cc274eb2e21af63b3b2363f6aa1d00e072",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0x115b609d07d16b3bd1c71b6127d9edbef86c4281151388d13f44e2b1fa3395d7"
|
||||
},
|
||||
{
|
||||
"nodeType": "0x02",
|
||||
"accountType": "0x02",
|
||||
"nibblesLen": "0x3E",
|
||||
"nibbles": "0xf154b79558fd6b8e56a1a13b3d2c7d66400cdcfa7e8ac9e9abd196dcb27557",
|
||||
"address": "0xf54e3ecea25c6e6a554e86ae6b83676b08547181",
|
||||
"balance": "0x056957fabab12c6e5654cd5869a774f7c269f7bc208fd242173a409df15d469f",
|
||||
"nonce": "0x00000000000000000000000000000000000000000000000030baa534518a3ab2",
|
||||
"codeHash": {
|
||||
"nodeType": "0x03",
|
||||
"data": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
},
|
||||
"codeLen": "0x00000000",
|
||||
"storage": [
|
||||
{
|
||||
"nodeType": "0x03",
|
||||
"data": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
],
|
||||
"debugDepth": "0x02",
|
||||
"debugHash": "0xe56e7b18e824dc8b2851ebb7149452f09e2f09fbe7298362dc014f72a5fe02ca"
|
||||
}
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,316 @@
|
|||
import
|
||||
stew/[byteutils, endians2], json, strutils,
|
||||
nimcrypto/[keccak, hash], eth/[common, rlp],
|
||||
eth/trie/[trie_defs, nibbles, db],
|
||||
./witness_types, ../nimbus/constants,
|
||||
../nimbus/db/storage_types, ./multi_keys
|
||||
|
||||
type
|
||||
DB = TrieDatabaseRef
|
||||
|
||||
WitnessBuilder* = object
|
||||
db*: DB
|
||||
root: KeccakHash
|
||||
flags: WitnessFlags
|
||||
node: JsonNode
|
||||
jStack: seq[JsonNode]
|
||||
|
||||
StackElem = object
|
||||
node: seq[byte]
|
||||
parentGroup: Group
|
||||
keys: MultikeysRef
|
||||
depth: int
|
||||
storageMode: bool
|
||||
|
||||
proc initWitnessBuilder*(db: DB, rootHash: KeccakHash, flags: WitnessFlags = {}): WitnessBuilder =
|
||||
result.db = db
|
||||
result.root = rootHash
|
||||
result.flags = flags
|
||||
result.node = newJObject()
|
||||
result.jStack = @[]
|
||||
|
||||
template extensionNodeKey(r: Rlp): auto =
|
||||
hexPrefixDecode r.listElem(0).toBytes
|
||||
|
||||
proc expectHash(r: Rlp): seq[byte] =
|
||||
result = r.toBytes
|
||||
if result.len != 32:
|
||||
raise newException(RlpTypeMismatch,
|
||||
"RLP expected to be a Keccak hash value, but has an incorrect length")
|
||||
|
||||
template getNode(elem: untyped): untyped =
|
||||
if elem.isList: @(elem.rawData)
|
||||
else: @(get(wb.db, elem.expectHash))
|
||||
|
||||
proc rlpListToBitmask(r: var Rlp): uint =
|
||||
var i = 0
|
||||
for branch in r:
|
||||
if not branch.isEmpty:
|
||||
result.setBranchMaskBit(i)
|
||||
inc i
|
||||
r.position = 0
|
||||
|
||||
proc writeByte(wb: var WitnessBuilder, x: byte, name: string) =
|
||||
wb.node[name] = newJString("0x" & toHex(x.int, 2))
|
||||
|
||||
proc write(wb: var WitnessBuilder, x: openArray[byte], name: string) =
|
||||
wb.node[name] = newJString("0x" & toHex(x))
|
||||
|
||||
proc write(wb: var WitnessBuilder, a, b: byte, name: string) =
|
||||
wb.node[name] = newJString("0x" & toHex(a.int, 2) & toHex(b.int, 2))
|
||||
|
||||
proc write(wb: var WitnessBuilder, x: bool, name: string) =
|
||||
wb.node[name] = newJBool(x)
|
||||
|
||||
proc pushArray(wb: var WitnessBuilder, name: string) =
|
||||
var node = newJArray()
|
||||
wb.node[name] = node
|
||||
wb.jStack.add wb.node
|
||||
wb.node = node
|
||||
|
||||
proc pushObject(wb: var WitnessBuilder, name: string) =
|
||||
var node = newJObject()
|
||||
wb.node[name] = node
|
||||
wb.jStack.add wb.node
|
||||
wb.node = node
|
||||
|
||||
proc addObject(wb: var WitnessBuilder) =
|
||||
var node = newJObject()
|
||||
wb.node.add node
|
||||
wb.jStack.add wb.node
|
||||
wb.node = node
|
||||
|
||||
proc pop(wb: var WitnessBuilder) =
|
||||
wb.node = wb.jStack.pop()
|
||||
|
||||
proc writeU32Impl(wb: var WitnessBuilder, x: uint32, name: string) =
|
||||
let y = toBytesBE(x)
|
||||
wb.node[name] = newJString("0x" & toHex(y))
|
||||
|
||||
template writeU32(wb: var WitnessBuilder, x: untyped, name: string) =
|
||||
wb.writeU32Impl(uint32(x), name)
|
||||
|
||||
template writeByte(wb: var WitnessBuilder, x: untyped, name: string) =
|
||||
writeByte(wb, byte(x), name)
|
||||
|
||||
proc writeNibbles(wb: var WitnessBuilder; n: NibblesSeq) =
|
||||
# convert the NibblesSeq into left aligned byte seq
|
||||
# perhaps we can optimize it if the NibblesSeq already left aligned
|
||||
let nibblesLen = n.len
|
||||
let numBytes = nibblesLen div 2 + nibblesLen mod 2
|
||||
var bytes: array[32, byte]
|
||||
doAssert(nibblesLen >= 1)
|
||||
doAssert(numBytes >= 0 and numBytes <= 64)
|
||||
for pos in 0..<n.len:
|
||||
if (pos and 1) != 0:
|
||||
bytes[pos div 2] = bytes[pos div 2] or n[pos]
|
||||
else:
|
||||
bytes[pos div 2] = bytes[pos div 2] or (n[pos] shl 4)
|
||||
|
||||
wb.writeByte(nibblesLen, "nibblesLen")
|
||||
wb.write(bytes.toOpenArray(0, numBytes-1), "nibbles")
|
||||
|
||||
proc writeExtensionNode(wb: var WitnessBuilder, n: NibblesSeq, depth: int, node: openArray[byte]) =
|
||||
wb.addObject()
|
||||
wb.writeByte(ExtensionNodeType, "nodeType")
|
||||
wb.writeNibbles(n)
|
||||
wb.writeByte(depth, "debugDepth")
|
||||
wb.write(keccak(node).data, "debugHash")
|
||||
wb.pop()
|
||||
|
||||
proc writeBranchNode(wb: var WitnessBuilder, mask: uint, depth: int, node: openArray[byte]) =
|
||||
doAssert mask.branchMaskBitIsSet(16) == false
|
||||
|
||||
wb.addObject()
|
||||
wb.writeByte(BranchNodeType, "nodeType")
|
||||
wb.write(byte((mask shr 8) and 0xFF), byte(mask and 0xFF), "mask")
|
||||
wb.writeByte(depth, "debugDepth")
|
||||
wb.write(keccak(node).data, "debugHash")
|
||||
wb.pop()
|
||||
|
||||
proc writeHashNode(wb: var WitnessBuilder, node: openArray[byte]) =
|
||||
wb.addObject()
|
||||
wb.writeByte(HashNodeType, "nodeType")
|
||||
wb.write(node, "data")
|
||||
wb.pop()
|
||||
|
||||
proc writeHashNode(wb: var WitnessBuilder, node: openArray[byte], name: string) =
|
||||
wb.pushObject(name)
|
||||
wb.writeByte(HashNodeType, "nodeType")
|
||||
wb.write(node, "data")
|
||||
wb.pop()
|
||||
|
||||
proc getBranchRecurse(wb: var WitnessBuilder, z: var StackElem)
|
||||
|
||||
proc writeAccountNode(wb: var WitnessBuilder, kd: KeyData, acc: Account, nibbles: NibblesSeq, node: openArray[byte], depth: int) =
|
||||
|
||||
wb.addObject()
|
||||
wb.writeByte(AccountNodeType, "nodeType")
|
||||
|
||||
doAssert(nibbles.len == 64 - depth)
|
||||
var accountType = if acc.codeHash == blankStringHash and acc.storageRoot == emptyRlpHash: SimpleAccountType
|
||||
else: ExtendedAccountType
|
||||
|
||||
if not kd.codeTouched:
|
||||
accountType = CodeUntouched
|
||||
|
||||
wb.writeByte(accountType, "accountType")
|
||||
wb.writeNibbles(nibbles)
|
||||
wb.write(kd.address, "address")
|
||||
wb.write(acc.balance.toBytesBE, "balance")
|
||||
wb.write(acc.nonce.u256.toBytesBE, "nonce")
|
||||
|
||||
if accountType != SimpleAccountType:
|
||||
if not kd.codeTouched:
|
||||
wb.writeHashNode(acc.codeHash.data, "codeHash")
|
||||
let code = get(wb.db, contractHashKey(acc.codeHash).toOpenArray)
|
||||
if wfEIP170 in wb.flags and code.len > EIP170_CODE_SIZE_LIMIT:
|
||||
raise newException(ContractCodeError, "code len exceed EIP170 code size limit")
|
||||
wb.writeU32(code.len, "codeLen")
|
||||
elif acc.codeHash != blankStringHash:
|
||||
let code = get(wb.db, contractHashKey(acc.codeHash).toOpenArray)
|
||||
if wfEIP170 in wb.flags and code.len > EIP170_CODE_SIZE_LIMIT:
|
||||
raise newException(ContractCodeError, "code len exceed EIP170 code size limit")
|
||||
wb.writeU32(code.len, "codeLen")
|
||||
wb.write(code, "code")
|
||||
else:
|
||||
wb.writeU32(0'u32, "codeLen")
|
||||
|
||||
wb.pushArray("storage")
|
||||
if kd.storageKeys.isNil:
|
||||
wb.writeHashNode(acc.storageRoot.data)
|
||||
elif acc.storageRoot != emptyRlpHash:
|
||||
var zz = StackElem(
|
||||
node: wb.db.get(acc.storageRoot.data),
|
||||
parentGroup: kd.storageKeys.initGroup(),
|
||||
keys: kd.storageKeys,
|
||||
depth: 0, # reset depth
|
||||
storageMode: true # switch to storage mode
|
||||
)
|
||||
getBranchRecurse(wb, zz)
|
||||
else:
|
||||
wb.writeHashNode(emptyRlpHash.data)
|
||||
wb.pop()
|
||||
|
||||
wb.writeByte(depth, "debugDepth")
|
||||
wb.write(keccak(node).data, "debugHash")
|
||||
wb.pop()
|
||||
|
||||
proc writeAccountStorageLeafNode(wb: var WitnessBuilder, key: openArray[byte], val: UInt256, nibbles: NibblesSeq, node: openArray[byte], depth: int) =
|
||||
doAssert(nibbles.len == 64 - depth)
|
||||
|
||||
wb.addObject()
|
||||
wb.writeByte(StorageLeafNodeType, "nodeType")
|
||||
wb.writeNibbles(nibbles)
|
||||
wb.write(key, "key")
|
||||
wb.write(val.toBytesBE, "value")
|
||||
wb.writeByte(depth, "debugDepth")
|
||||
wb.write(keccak(node).data, "debugHash")
|
||||
wb.pop()
|
||||
|
||||
proc getBranchRecurse(wb: var WitnessBuilder, z: var StackElem) =
|
||||
if z.node.len == 0: return
|
||||
var nodeRlp = rlpFromBytes z.node
|
||||
|
||||
case nodeRlp.listLen
|
||||
of 2:
|
||||
let (isLeaf, k) = nodeRlp.extensionNodeKey
|
||||
let mg = groups(z.keys, z.depth, k, z.parentGroup)
|
||||
|
||||
if not mg.match:
|
||||
# return immediately if there is no match
|
||||
writeHashNode(wb, keccak(z.node).data)
|
||||
return
|
||||
|
||||
let value = nodeRlp.listElem(1)
|
||||
if not isLeaf:
|
||||
# recursion will go deeper depend on the common-prefix length nibbles
|
||||
writeExtensionNode(wb, k, z.depth, z.node)
|
||||
var zz = StackElem(
|
||||
node: value.getNode,
|
||||
parentGroup: mg.group,
|
||||
keys: z.keys,
|
||||
depth: z.depth + k.len, # increase the depth by k.len
|
||||
storageMode: z.storageMode
|
||||
)
|
||||
getBranchRecurse(wb, zz)
|
||||
return
|
||||
|
||||
# there should be only one match
|
||||
let kd = z.keys.visitMatch(mg, z.depth, k)
|
||||
if z.storageMode:
|
||||
doAssert(kd.storageMode)
|
||||
writeAccountStorageLeafNode(wb, kd.storageSlot, value.toBytes.decode(UInt256), k, z.node, z.depth)
|
||||
else:
|
||||
doAssert(not kd.storageMode)
|
||||
writeAccountNode(wb, kd, value.toBytes.decode(Account), k, z.node, z.depth)
|
||||
|
||||
of 17:
|
||||
let branchMask = rlpListToBitmask(nodeRlp)
|
||||
writeBranchNode(wb, branchMask, z.depth, z.node)
|
||||
|
||||
# if there is a match in any branch elem
|
||||
# 1st to 16th, the recursion will go deeper
|
||||
# by one nibble
|
||||
doAssert(z.depth != 64) # notLeaf or path.len == 0
|
||||
|
||||
let path = groups(z.keys, z.parentGroup, z.depth)
|
||||
for i in nonEmpty(branchMask):
|
||||
let branch = nodeRlp.listElem(i)
|
||||
if branchMaskBitIsSet(path.mask, i):
|
||||
# it is a match between multikeys and Branch Node elem
|
||||
var zz = StackElem(
|
||||
node: branch.getNode,
|
||||
parentGroup: path.groups[i],
|
||||
keys: z.keys,
|
||||
depth: z.depth + 1, # increase the depth by one
|
||||
storageMode: z.storageMode
|
||||
)
|
||||
getBranchRecurse(wb, zz)
|
||||
continue
|
||||
|
||||
if branch.isList:
|
||||
# short node appear in yellow paper
|
||||
# but never in the actual ethereum state trie
|
||||
# an rlp encoded ethereum account will have length > 32 bytes
|
||||
# block witness spec silent about this
|
||||
doAssert(false, "Short node should not exist in block witness")
|
||||
else:
|
||||
# if branch elem not empty and not a match, emit hash
|
||||
writeHashNode(wb, branch.expectHash)
|
||||
|
||||
# 17th elem should always empty
|
||||
# 17th elem appear in yellow paper but never in
|
||||
# the actual ethereum state trie
|
||||
# the 17th elem also not included in block witness spec
|
||||
doAssert branchMask.branchMaskBitIsSet(16) == false
|
||||
else:
|
||||
raise newException(CorruptedTrieDatabase,
|
||||
"HexaryTrie node with an unexpected number of children")
|
||||
|
||||
proc buildWitness*(wb: var WitnessBuilder, keys: MultikeysRef): string =
|
||||
|
||||
# witness version
|
||||
wb.writeByte(BlockWitnessVersion, "version")
|
||||
|
||||
# one or more trees
|
||||
|
||||
# we only output one tree
|
||||
wb.writeByte(MetadataNothing, "metadata")
|
||||
|
||||
wb.write(wb.root.data, "rootHash")
|
||||
wb.write(false, "error")
|
||||
|
||||
wb.pushArray("tree")
|
||||
|
||||
var z = StackElem(
|
||||
node: @(wb.db.get(wb.root.data)),
|
||||
parentGroup: keys.initGroup(),
|
||||
keys: keys,
|
||||
depth: 0,
|
||||
storageMode: false
|
||||
)
|
||||
getBranchRecurse(wb, z)
|
||||
|
||||
wb.pop()
|
||||
result = wb.node.pretty()
|
|
@ -0,0 +1,117 @@
|
|||
import
|
||||
randutils, random, parseopt, strutils, os,
|
||||
eth/[common, rlp], eth/trie/[hexary, db, trie_defs],
|
||||
nimcrypto/sysrand, ../stateless/[json_from_tree],
|
||||
../nimbus/db/storage_types, ./witness_types, ./multi_keys
|
||||
|
||||
type
|
||||
DB = TrieDatabaseRef
|
||||
|
||||
StorageKeys = tuple[storageRoot: Hash256, keys: MultikeysRef]
|
||||
|
||||
AccountDef = object
|
||||
storageKeys: MultiKeysRef
|
||||
account: Account
|
||||
codeTouched: bool
|
||||
|
||||
proc randU256(): UInt256 =
|
||||
var bytes: array[32, byte]
|
||||
discard randomBytes(bytes[0].addr, sizeof(result))
|
||||
result = UInt256.fromBytesBE(bytes)
|
||||
|
||||
proc randStorageSlot(): StorageSlot =
|
||||
discard randomBytes(result[0].addr, sizeof(result))
|
||||
|
||||
proc randNonce(): AccountNonce =
|
||||
discard randomBytes(result.addr, sizeof(result))
|
||||
|
||||
proc randCode(db: DB): Hash256 =
|
||||
if rand(0..1) == 0:
|
||||
result = blankStringHash
|
||||
else:
|
||||
let codeLen = rand(1..150)
|
||||
let code = randList(byte, rng(0, 255), codeLen, unique = false)
|
||||
result = hexary.keccak(code)
|
||||
db.put(contractHashKey(result).toOpenArray, code)
|
||||
|
||||
proc randStorage(db: DB, numSlots: int): StorageKeys =
|
||||
if rand(0..1) == 0 or numSlots == 0:
|
||||
result = (emptyRlpHash, MultikeysRef(nil))
|
||||
else:
|
||||
var trie = initSecureHexaryTrie(db)
|
||||
var keys = newSeq[StorageSlot](numSlots)
|
||||
|
||||
for i in 0..<numSlots:
|
||||
keys[i] = randStorageSlot()
|
||||
trie.put(keys[i], rlp.encode(randU256()))
|
||||
|
||||
if rand(0..1) == 0:
|
||||
result = (trie.rootHash, MultikeysRef(nil))
|
||||
else:
|
||||
var m = newMultikeys(keys)
|
||||
result = (trie.rootHash, m)
|
||||
|
||||
proc randAccount(db: DB, numSlots: int): AccountDef =
|
||||
result.account.nonce = randNonce()
|
||||
result.account.balance = randU256()
|
||||
let z = randStorage(db, numSlots)
|
||||
result.account.codeHash = randCode(db)
|
||||
result.account.storageRoot = z.storageRoot
|
||||
result.storageKeys = z.keys
|
||||
result.codeTouched = rand(0..1) == 0
|
||||
|
||||
proc randAddress(): EthAddress =
|
||||
discard randomBytes(result.addr, sizeof(result))
|
||||
|
||||
proc runGenerator(numPairs, numSlots: int): string =
|
||||
var memDB = newMemoryDB()
|
||||
var trie = initSecureHexaryTrie(memDB)
|
||||
var addrs = newSeq[AccountKey](numPairs)
|
||||
var accs = newSeq[Account](numPairs)
|
||||
|
||||
for i in 0..<numPairs:
|
||||
let acc = randAccount(memDB, numSlots)
|
||||
addrs[i] = (randAddress(), acc.codeTouched, acc.storageKeys)
|
||||
accs[i] = acc.account
|
||||
trie.put(addrs[i].address, rlp.encode(accs[i]))
|
||||
|
||||
var mkeys = newMultiKeys(addrs)
|
||||
let rootHash = trie.rootHash
|
||||
|
||||
var wb = initWitnessBuilder(memDB, rootHash, {wfEIP170})
|
||||
result = wb.buildWitness(mkeys)
|
||||
|
||||
proc writeHelp() =
|
||||
echo "json_witness_gen output --pairs:val --slots:val -s:val -p:val"
|
||||
|
||||
proc main() =
|
||||
var filename: string
|
||||
var outputDir: string
|
||||
var numPairs = 1
|
||||
var numSlots = 1
|
||||
for kind, key, val in getopt():
|
||||
case kind
|
||||
of cmdArgument:
|
||||
filename = key
|
||||
of cmdLongOption, cmdShortOption:
|
||||
case key
|
||||
of "pairs", "p":
|
||||
numPairs = parseInt(val)
|
||||
if numPairs <= 0: numPairs = 1
|
||||
of "slots", "s":
|
||||
numSlots = parseInt(val)
|
||||
if numSlots < 0: numSlots = 0
|
||||
of "output", "o":
|
||||
outputDir = val
|
||||
of cmdEnd: assert(false) # cannot happen
|
||||
|
||||
if filename == "":
|
||||
writeHelp()
|
||||
quit(0)
|
||||
|
||||
randomize()
|
||||
let witness = runGenerator(numPairs, numSlots)
|
||||
let filePath = if outputDir.len > 0: outputDir / filename: else: filename
|
||||
writeFile(filePath, witness)
|
||||
|
||||
main()
|
|
@ -0,0 +1,153 @@
|
|||
import
|
||||
eth/common, eth/trie/[db, nibbles], algorithm,
|
||||
./witness_types
|
||||
|
||||
type
|
||||
KeyHash* = array[32, byte]
|
||||
|
||||
KeyData* = object
|
||||
visited*: bool
|
||||
hash*: KeyHash
|
||||
case storageMode*: bool
|
||||
of true:
|
||||
storageSlot*: StorageSlot
|
||||
of false:
|
||||
storageKeys*: MultikeysRef
|
||||
address*: EthAddress
|
||||
codeTouched*: bool
|
||||
|
||||
Multikeys* = object
|
||||
keys*: seq[KeyData]
|
||||
|
||||
MultikeysRef* = ref Multikeys
|
||||
|
||||
Group* = object
|
||||
first*, last*: int16
|
||||
|
||||
BranchGroup* = object
|
||||
mask*: uint
|
||||
groups*: array[16, Group]
|
||||
|
||||
AccountKey* = tuple[address: EthAddress, codeTouched: bool, storageKeys: MultikeysRef]
|
||||
MatchGroup* = tuple[match: bool, group: Group]
|
||||
|
||||
func cmpHash(a, b: KeyHash): int =
|
||||
var i = 0
|
||||
var m = min(a.len, b.len)
|
||||
while i < m:
|
||||
result = a[i].int - b[i].int
|
||||
if result != 0: return
|
||||
inc(i)
|
||||
result = a.len - b.len
|
||||
|
||||
func cmpHash(a, b: KeyData): int =
|
||||
cmpHash(a.hash, b.hash)
|
||||
|
||||
func getNibble(x: openArray[byte], i: int): byte =
|
||||
if(i and 0x01) == 0x01:
|
||||
result = x[i shr 1] and 0x0F
|
||||
else:
|
||||
result = x[i shr 1] shr 4
|
||||
|
||||
func compareNibbles(x: openArray[byte], start: int, n: NibblesSeq): bool =
|
||||
var i = 0
|
||||
while i < n.len:
|
||||
if getNibble(x, start + i) != n[i]:
|
||||
return false
|
||||
inc i
|
||||
result = true
|
||||
|
||||
proc newMultiKeys*(keys: openArray[AccountKey]): MultikeysRef =
|
||||
result = new Multikeys
|
||||
result.keys = newSeq[KeyData](keys.len)
|
||||
for i, a in keys:
|
||||
result.keys[i] = KeyData(
|
||||
storageMode: false,
|
||||
hash: keccak(a.address).data,
|
||||
address: a.address,
|
||||
codeTouched: a.codeTouched,
|
||||
storageKeys: a.storageKeys)
|
||||
result.keys.sort(cmpHash)
|
||||
|
||||
proc newMultiKeys*(keys: openArray[StorageSlot]): MultikeysRef =
|
||||
result = new Multikeys
|
||||
result.keys = newSeq[KeyData](keys.len)
|
||||
for i, a in keys:
|
||||
result.keys[i] = KeyData(storageMode: true, hash: keccak(a).data, storageSlot: a)
|
||||
result.keys.sort(cmpHash)
|
||||
|
||||
func initGroup*(m: MultikeysRef): Group =
|
||||
type T = type result.last
|
||||
result = Group(first: 0.T, last: (m.keys.len - 1).T)
|
||||
|
||||
func groups*(m: MultikeysRef, parentGroup: Group, depth: int): BranchGroup =
|
||||
# similar to a branch node, the product of this func
|
||||
# is a 16 bits bitmask and an array of max 16 groups
|
||||
# if the bit is set, the n-th elem of array have a group
|
||||
# each group consist of at least one key
|
||||
var g = Group(first: parentGroup.first)
|
||||
var nibble = getNibble(m.keys[g.first].hash, depth)
|
||||
for i in parentGroup.first..parentGroup.last:
|
||||
let currNibble = getNibble(m.keys[i].hash, depth)
|
||||
if currNibble != nibble:
|
||||
# close current group and start a new group
|
||||
g.last = i - 1
|
||||
setBranchMaskBit(result.mask, nibble.int)
|
||||
result.groups[nibble.int] = g
|
||||
nibble = currNibble
|
||||
g.first = i
|
||||
|
||||
# always close the last group
|
||||
g.last = parentGroup.last
|
||||
setBranchMaskBit(result.mask, nibble.int)
|
||||
result.groups[nibble.int] = g
|
||||
|
||||
func groups*(m: MultikeysRef, depth: int, n: NibblesSeq, parentGroup: Group): MatchGroup =
|
||||
# using common-prefix comparison, this iterator
|
||||
# will produce one match group or no match at all
|
||||
var g = Group(first: parentGroup.first)
|
||||
|
||||
if compareNibbles(m.keys[g.first].hash, depth, n):
|
||||
var i = g.first + 1
|
||||
while i <= parentGroup.last:
|
||||
if not compareNibbles(m.keys[i].hash, depth, n):
|
||||
g.last = i - 1
|
||||
# case 1: match and no match
|
||||
return (true, g)
|
||||
inc i
|
||||
|
||||
# case 2: all is a match group
|
||||
g.last = parentGroup.last
|
||||
return (true, g)
|
||||
|
||||
# no match came first, skip no match
|
||||
# we only interested in a match group
|
||||
var i = g.first + 1
|
||||
while i <= parentGroup.last:
|
||||
if compareNibbles(m.keys[i].hash, depth, n):
|
||||
g.first = i
|
||||
break
|
||||
inc i
|
||||
|
||||
if i <= parentGroup.last:
|
||||
while i <= parentGroup.last:
|
||||
if not compareNibbles(m.keys[i].hash, depth, n):
|
||||
# case 3: no match, match, and no match
|
||||
g.last = i - 1
|
||||
return (true, g)
|
||||
inc i
|
||||
|
||||
# case 4: no match and match
|
||||
g.last = parentGroup.last
|
||||
return (true, g)
|
||||
|
||||
# case 5: no match at all
|
||||
result = (false, g)
|
||||
|
||||
func isValidMatch(mg: MatchGroup): bool {.inline.} =
|
||||
result = mg.match and mg.group.first == mg.group.last
|
||||
|
||||
proc visitMatch*(m: var MultikeysRef, mg: MatchGroup, depth: int, k: NibblesSeq): KeyData =
|
||||
doAssert(mg.isValidMatch)
|
||||
m.keys[mg.group.first].visited = true
|
||||
result = m.keys[mg.group.first]
|
|
@ -0,0 +1,59 @@
|
|||
import random, sets, nimcrypto/sysrand
|
||||
|
||||
type
|
||||
RandGen*[T] = object
|
||||
minVal, maxVal: T
|
||||
|
||||
Bytes* = seq[byte]
|
||||
|
||||
proc rng*[T](minVal, maxVal: T): RandGen[T] =
|
||||
doAssert(minVal <= maxVal)
|
||||
result.minVal = minVal
|
||||
result.maxVal = maxVal
|
||||
|
||||
proc rng*[T](minMax: T): RandGen[T] =
|
||||
rng(minMax, minMax)
|
||||
|
||||
proc getVal*[T](x: RandGen[T]): T =
|
||||
if x.minVal == x.maxVal: return x.minVal
|
||||
rand(x.minVal..x.maxVal)
|
||||
|
||||
proc randString*(len: int): string =
|
||||
result = newString(len)
|
||||
discard randomBytes(result[0].addr, len)
|
||||
|
||||
proc randBytes*(len: int): Bytes =
|
||||
result = newSeq[byte](len)
|
||||
discard randomBytes(result[0].addr, len)
|
||||
|
||||
proc randPrimitives*[T](val: int): T =
|
||||
type
|
||||
ByteLike = uint8 | byte | char
|
||||
|
||||
when T is string:
|
||||
randString(val)
|
||||
elif T is int:
|
||||
result = val
|
||||
elif T is ByteLike:
|
||||
result = T(val)
|
||||
elif T is Bytes:
|
||||
result = randBytes(val)
|
||||
|
||||
proc randList*(T: typedesc, fillGen: RandGen, listLen: int, unique: static[bool] = true): seq[T] =
|
||||
result = newSeqOfCap[T](listLen)
|
||||
when unique:
|
||||
var set = initHashSet[T]()
|
||||
for len in 0..<listLen:
|
||||
while true:
|
||||
let x = randPrimitives[T](fillGen.getVal())
|
||||
if x notin set:
|
||||
result.add x
|
||||
set.incl x
|
||||
break
|
||||
else:
|
||||
for len in 0..<listLen:
|
||||
let x = randPrimitives[T](fillGen.getVal())
|
||||
result.add x
|
||||
|
||||
proc randList*(T: typedesc, fillGen, listGen: RandGen, unique: static[bool] = true): seq[T] =
|
||||
randList(T, fillGen, listGen.getVal(), unique)
|
|
@ -0,0 +1,196 @@
|
|||
# How to build multiproof block witness from state trie
|
||||
|
||||
The [block witness spec](https://github.com/ethereum/stateless-ethereum-specs/blob/master/witness.md) define the
|
||||
binary format in BNF form notation. It will help the trie builder implementer quickly implement a working block
|
||||
witness parser using simple LL(1) parser.
|
||||
|
||||
If you have a working `Hexary Trie` implementation, you'll also probably can quickly implement a working witness
|
||||
builder for a single proof. You don't need to alter the algorithm, you only need to alter the output.
|
||||
The output will not an `Account` anymore, but binary block witness containing one proof for single `Account`.
|
||||
|
||||
However, the block witness spec does not provide specific implementation algorithms. You might already know
|
||||
how to generate a single proof block witness, but how to generate a block witness contains multiple proofs?
|
||||
|
||||
You can try to read [turbo geth's multiproof algorithm](https://github.com/ledgerwatch/turbo-geth/blob/master/docs/programmers_guide/guide.md).
|
||||
And I will try to provide an alternative implementation, a simpler to understand algorithm that require only minimum changes
|
||||
in the single proof generation algorithm and delegate the details into `multi-keys` algorithm.
|
||||
|
||||
## Basic single proof
|
||||
|
||||
I assume you have basic knowledge of how `Merkle Patricia Trie` works. As you probably already know, `Hexary Trie` have 4 types of node:
|
||||
|
||||
* __Leaf Node__
|
||||
A leaf node is a two elements node: [nibbles, value].
|
||||
* __Extension Node__
|
||||
An extension node also a two elements node: [nibbles, hash to next node].
|
||||
* __Branch Node__
|
||||
A branch node is a 17 elements node: [0, 1, ..., 16, value]. All of 0th to 16th elements are a hash to next node.
|
||||
|
||||
Every time you request a node using a hash key, you'll get one of the 3 types of node above.
|
||||
|
||||
### Deviation from yellow paper
|
||||
|
||||
* In the Yellow Paper, the `hash to next node` may be replaced by the next node directly if the RLP encoded node bytes count
|
||||
less than 32. But in a real Ethereum State trie, this never happened. An empty RLP encoded `Account` will have length of 70.
|
||||
Combined with the Hex Prefix encoding of nibbles, it will be more than 70 bytes.
|
||||
* In Yellow Paper, the 17th elem of the `Branch Node` can contains a value. But it always empty in a real Ethereum State trie.
|
||||
The block witness spec also ignore this 17th elem when encoding or decoding `Branch Node`.
|
||||
This can happen because in Ethereum `Secure Hexary Trie`, every keys have uniform length of 32 bytes or 64 nibbles.
|
||||
With the absence of 17th element, a `Branch Node` will never contains leaf value.
|
||||
* When processing a `Branch Node` you need to emit the `hash to next elem` if the elem is not match for the current path nibble.
|
||||
* When processing a `Leaf Node` or an `Extension Node` and you meet no match condition, you'll also emit a hash.
|
||||
|
||||
|
||||
If you try to build witness on something else that is not an `Ethereum Account` and using keys with different length,
|
||||
you will probably need to implement full spec from the Yellow Paper.
|
||||
|
||||
## Multi keys
|
||||
|
||||
Before we produce multiproof block witness, let us create a multi keys data structure that will help us doing nibbles comparison.
|
||||
|
||||
### Sort the keys lexicographically
|
||||
|
||||
For example, I have 16 keys. Before sort:
|
||||
|
||||
```text
|
||||
e26f87f8d83b61dbd890cda95c46c74f8d22067c323a89b58e6e8f561f2fb8ea
|
||||
5e00236babd8b0737512348d0a6bae0ed3e69e76391a8f16085c1c7a4864a098
|
||||
28d0cacafa7c17f7a9b759289c11908f3ca0783fc1940399b8e8c216dcccab2d
|
||||
a1ba56edb2cfcd4914d5bfc35965be5b7df3fc289f8c8c4f3987aaf58196119a
|
||||
5021c9457544d81b9870ab986ba52a1fccedd35df09c66de268ecdf289e1127d
|
||||
bac9405b4813ac28cc27bc09fb6b27aefa3e341d3ab7f91c63f2482446abb28b
|
||||
d676c8ea429a4b2e075538475c4cc89cf0251335d167cac2bb516a6cd046fbfd
|
||||
df3585baa4162db6431f36ea2d70380b855cdb53203c707463b5df2c4ed573dc
|
||||
903b206fc2b1aed80eecc439e7ce5049e955b1d5e7b784aadf1c424c99bd270a
|
||||
26eb8904b00d91adf989f5919b71e8bdf96ded347ee25f8cceeb32fb68fb396f
|
||||
6a52cf44e5d529973c5f8c10e4a88301076065529370776136b08ddf28617634
|
||||
6c4cb76d2205904095b8ac41e9deb533ced6d3f5cc5c4f5a55d6abd50b21d022
|
||||
850169badff8c49045afcb92bddaa59bf0aa3bd996d5a9a2f19984659e0df156
|
||||
1d86f4ba779b3e61f65cd0f1b4eea004ddb1cd42b6294979447579e57bb32e02
|
||||
b63e59b25dc10e89b04f622ca45cd3da097e1ba41ff2fe202ca0587c53fdbe98
|
||||
5b0f8a5612111ffbc215a7fb82ee382c1a36f0035653c1f3fa3f520c83bee256
|
||||
```
|
||||
|
||||
After sort:
|
||||
```
|
||||
1d86f4ba779b3e61f65cd0f1b4eea004ddb1cd42b6294979447579e57bb32e02
|
||||
26eb8904b00d91adf989f5919b71e8bdf96ded347ee25f8cceeb32fb68fb396f
|
||||
28d0cacafa7c17f7a9b759289c11908f3ca0783fc1940399b8e8c216dcccab2d
|
||||
5021c9457544d81b9870ab986ba52a1fccedd35df09c66de268ecdf289e1127d
|
||||
5b0f8a5612111ffbc215a7fb82ee382c1a36f0035653c1f3fa3f520c83bee256
|
||||
5e00236babd8b0737512348d0a6bae0ed3e69e76391a8f16085c1c7a4864a098
|
||||
6a52cf44e5d529973c5f8c10e4a88301076065529370776136b08ddf28617634
|
||||
6c4cb76d2205904095b8ac41e9deb533ced6d3f5cc5c4f5a55d6abd50b21d022
|
||||
850169badff8c49045afcb92bddaa59bf0aa3bd996d5a9a2f19984659e0df156
|
||||
903b206fc2b1aed80eecc439e7ce5049e955b1d5e7b784aadf1c424c99bd270a
|
||||
a1ba56edb2cfcd4914d5bfc35965be5b7df3fc289f8c8c4f3987aaf58196119a
|
||||
b63e59b25dc10e89b04f622ca45cd3da097e1ba41ff2fe202ca0587c53fdbe98
|
||||
bac9405b4813ac28cc27bc09fb6b27aefa3e341d3ab7f91c63f2482446abb28b
|
||||
d676c8ea429a4b2e075538475c4cc89cf0251335d167cac2bb516a6cd046fbfd
|
||||
df3585baa4162db6431f36ea2d70380b855cdb53203c707463b5df2c4ed573dc
|
||||
e26f87f8d83b61dbd890cda95c46c74f8d22067c323a89b58e6e8f561f2fb8ea
|
||||
```
|
||||
|
||||
### A group
|
||||
|
||||
After you have nicely sorted keys, now is the time to make a parent group.
|
||||
A `group` is a tuple of [first, last] act as index of keys.
|
||||
A top level parent group will always have `first: 0` and `last: numkeys-1`
|
||||
Besides sorting, we are not going to produce groups before the actual block witness take place.
|
||||
We produce the top level group right before entering the block witness generation algorithm.
|
||||
Top level group always start with `depth: 0`.
|
||||
|
||||
### Multi keys and Branch Node
|
||||
|
||||
During block witness construction, and you encounter a `Branch Node` you'll grouping the keys together
|
||||
based on their prefix nibble. We only use a single nibble in this case. Therefore you'll probably end up with
|
||||
16 groups of keys. __Each of the group consist of the same single nibble prefix__
|
||||
|
||||
Assume we are at `depth: 0`, the parent group is: `[0, 15]`, this is the result we have:
|
||||
|
||||
```
|
||||
1d86f4ba779b3e61f65cd0f1b4eea004ddb1cd42b6294979447579e57bb32e02 # group 1: [0, 0]
|
||||
|
||||
26eb8904b00d91adf989f5919b71e8bdf96ded347ee25f8cceeb32fb68fb396f # group 2: [1, 2]
|
||||
28d0cacafa7c17f7a9b759289c11908f3ca0783fc1940399b8e8c216dcccab2d
|
||||
|
||||
5021c9457544d81b9870ab986ba52a1fccedd35df09c66de268ecdf289e1127d # group 3: [3, 5]
|
||||
5021b0f8a5612111ffbc215a7fb82ee382c1a36f0035653c1f3fa3f520c83bee
|
||||
5e00236babd8b0737512348d0a6bae0ed3e69e76391a8f16085c1c7a4864a098
|
||||
|
||||
6a52cf44e5d529973c5f8c10e4a88301076065529370776136b08ddf28617634 # group 4: [6, 7]
|
||||
6c4cb76d2205904095b8ac41e9deb533ced6d3f5cc5c4f5a55d6abd50b21d022
|
||||
|
||||
850169badff8c49045afcb92bddaa59bf0aa3bd996d5a9a2f19984659e0df156 # group 5: [8, 8]
|
||||
|
||||
903b206fc2b1aed80eecc439e7ce5049e955b1d5e7b784aadf1c424c99bd270a # group 6: [9, 9]
|
||||
|
||||
a1ba56edb2cfcd4914d5bfc35965be5b7df3fc289f8c8c4f3987aaf58196119a # group 7: [10, 10]
|
||||
|
||||
b63e59b25dc10e89b04f622ca45cd3da097e1ba41ff2fe202ca0587c53fdbe98 # group 8: [11, 12]
|
||||
bac9405b4813ac28cc27bc09fb6b27aefa3e341d3ab7f91c63f2482446abb28b
|
||||
|
||||
d676c8ea429a4b2e075538475c4cc89cf0251335d167cac2bb516a6cd046fbfd # group 9: [13, 14]
|
||||
df3585baa4162db6431f36ea2d70380b855cdb53203c707463b5df2c4ed573dc
|
||||
|
||||
e26f87f8d83b61dbd890cda95c46c74f8d22067c323a89b58e6e8f561f2fb8ea # group 10: [15, 15]
|
||||
```
|
||||
|
||||
In a `Hexary Trie` you'll only match the current head(nibble) of the path with one elem from `Branch Node`.
|
||||
In multiproof algorithm, you need to match every elem with as much groups as possible.
|
||||
If there is no __invalid address__ or the invalid address hiding in one of the group, you will have
|
||||
branches as much as non empty elements in a `Branch Node` and they will have the same nibble/prefix.
|
||||
|
||||
Because the match only involve one nibble, we advance the depth only one.
|
||||
|
||||
### Multi keys and Leaf Node and Extension Node
|
||||
|
||||
If you encounter a `Leaf Node` or `Extension Node`, they will have the same algorithm to generate groups.
|
||||
For example, we are at `depth: 1`, and we are processing `group 3: [3, 5]`.
|
||||
Using the prefix nibbles from `Leaf Node` or `Extension Node`, we produce two groups if our prefix nibbles is `021`:
|
||||
|
||||
```
|
||||
5 021c9457544d81b9870ab986ba52a1fccedd35df09c66de268ecdf289e1127d # group 1: [3, 4]
|
||||
5 021b0f8a5612111ffbc215a7fb82ee382c1a36f0035653c1f3fa3f520c83bee
|
||||
|
||||
5 e00236babd8b0737512348d0a6bae0ed3e69e76391a8f16085c1c7a4864a098 # group 2: [5, 5]
|
||||
```
|
||||
|
||||
At max we will have 3 groups, and every possible combinations will be:
|
||||
|
||||
* match(1 group): all keys are matching the prefix nibbles.
|
||||
* no match(1 group): there is no match.
|
||||
* not match, match( 2 groups): a non matching group preceding matching group.
|
||||
* match, not match(2 groups): a matching group before non matching group.
|
||||
* not match, match, not match(3 groups): a matching group is between two non matching groups.
|
||||
|
||||
As you can see, we will only have a single match group or no match at all during constructing these groups.
|
||||
And we only interested in this match group if it exist and ignore all other not matching groups.
|
||||
|
||||
#### A matching group for Extension Node
|
||||
|
||||
If we have a matching group for `Extension Node`, we will use this group as parent group
|
||||
when we move deeper into the trie. We will advance our depth with the length of the prefix nibbles.
|
||||
|
||||
Let's say we have a match using nibbles `021`, the matching group is `group 1: [3, 4]`,
|
||||
we can move deeper after `Extension Node` by adding 3 to our depth.
|
||||
|
||||
#### A matching group for Leaf Node
|
||||
|
||||
If we move deeper, finally we will encounter a `Leaf Node`.
|
||||
If you have multiple keys inside your match group, then it is a bug in your multi keys algorithm.
|
||||
If there is an __invalid address__ hiding in a matching group, you also have bug in your multi keys algorithm.
|
||||
If you meet with a leaf group and a match group, emit an `Account` or a `Account Storage Leaf`.
|
||||
|
||||
```
|
||||
5 021 c9457544d81b9870ab986ba52a1fccedd35df09c66de268ecdf289e1127d # group 1: [3, 3]
|
||||
|
||||
5 021 b0f8a5612111ffbc215a7fb82ee382c1a36f0035653c1f3fa3f520c83bee # group 2: [3, 4]
|
||||
```
|
||||
|
||||
One of this group is a match for a `Leaf Node`, or no match at all.
|
||||
|
||||
### Emitting an `Account`
|
||||
|
||||
During emitting a `Leaf Node` or an `Account`, and the account have storage trie along with keys and values needs
|
||||
to be included in the block witness too, we again repeat the algorithm in account storage mode and set the new depth to 0.
|
|
@ -0,0 +1,92 @@
|
|||
proc getBranchStack*(wb: WitnessBuilder; key: openArray[byte]): string =
|
||||
var
|
||||
node = wb.db.get(wb.root.data)
|
||||
stack = @[(node, initNibbleRange(key))]
|
||||
|
||||
result.add node.toHex
|
||||
while stack.len > 0:
|
||||
let (node, path) = stack.pop()
|
||||
if node.len == 0: continue
|
||||
var nodeRlp = rlpFromBytes node
|
||||
|
||||
case nodeRlp.listLen
|
||||
of 2:
|
||||
let (isLeaf, k) = nodeRlp.extensionNodeKey
|
||||
let sharedNibbles = sharedPrefixLen(path, k)
|
||||
if sharedNibbles == k.len:
|
||||
let value = nodeRlp.listElem(1)
|
||||
if not isLeaf:
|
||||
let nextLookup = value.getNode
|
||||
stack.add((nextLookup, path.slice(sharedNibbles)))
|
||||
result.add nextLookup.toHex
|
||||
of 17:
|
||||
if path.len != 0:
|
||||
var branch = nodeRlp.listElem(path[0].int)
|
||||
if not branch.isEmpty:
|
||||
let nextLookup = branch.getNode
|
||||
stack.add((nextLookup, path.slice(1)))
|
||||
result.add nextLookup.toHex
|
||||
else:
|
||||
raise newException(CorruptedTrieDatabase,
|
||||
"HexaryTrie node with an unexpected number of children")
|
||||
|
||||
proc getBranch*(wb: WitnessBuilder; key: openArray[byte]): seq[seq[byte]] =
|
||||
var
|
||||
node = wb.db.get(wb.root.data)
|
||||
stack = @[(node, initNibbleRange(key))]
|
||||
|
||||
result.add node
|
||||
while stack.len > 0:
|
||||
let (node, path) = stack.pop()
|
||||
if node.len == 0: continue
|
||||
var nodeRlp = rlpFromBytes node
|
||||
|
||||
case nodeRlp.listLen
|
||||
of 2:
|
||||
let (isLeaf, k) = nodeRlp.extensionNodeKey
|
||||
let sharedNibbles = sharedPrefixLen(path, k)
|
||||
if sharedNibbles == k.len:
|
||||
let value = nodeRlp.listElem(1)
|
||||
if not isLeaf:
|
||||
let nextLookup = value.getNode
|
||||
stack.add((nextLookup, path.slice(sharedNibbles)))
|
||||
result.add nextLookup
|
||||
of 17:
|
||||
if path.len != 0:
|
||||
var branch = nodeRlp.listElem(path[0].int)
|
||||
if not branch.isEmpty:
|
||||
let nextLookup = branch.getNode
|
||||
stack.add((nextLookup, path.slice(1)))
|
||||
result.add nextLookup
|
||||
else:
|
||||
raise newException(CorruptedTrieDatabase,
|
||||
"HexaryTrie node with an unexpected number of children")
|
||||
|
||||
proc buildWitness*(wb: var WitnessBuilder; key: openArray[byte]) =
|
||||
var
|
||||
node = wb.db.get(wb.root.data)
|
||||
stack = @[(node, initNibbleRange(key))]
|
||||
|
||||
while stack.len > 0:
|
||||
let (node, path) = stack.pop()
|
||||
if node.len == 0: continue
|
||||
var nodeRlp = rlpFromBytes node
|
||||
|
||||
case nodeRlp.listLen
|
||||
of 2:
|
||||
let (isLeaf, k) = nodeRlp.extensionNodeKey
|
||||
let sharedNibbles = sharedPrefixLen(path, k)
|
||||
if sharedNibbles == k.len:
|
||||
let value = nodeRlp.listElem(1)
|
||||
if not isLeaf:
|
||||
let nextLookup = value.getNode
|
||||
stack.add((nextLookup, path.slice(sharedNibbles)))
|
||||
of 17:
|
||||
if path.len != 0:
|
||||
var branch = nodeRlp.listElem(path[0].int)
|
||||
if not branch.isEmpty:
|
||||
let nextLookup = branch.getNode
|
||||
stack.add((nextLookup, path.slice(1)))
|
||||
else:
|
||||
raise newException(CorruptedTrieDatabase,
|
||||
"HexaryTrie node with an unexpected number of children")
|
|
@ -0,0 +1,134 @@
|
|||
import
|
||||
unittest2, os, json, strutils,
|
||||
eth/[common, rlp], eth/trie/[hexary, db, trie_defs],
|
||||
stew/byteutils, faststreams/input_stream,
|
||||
../tests/[test_helpers, test_config],
|
||||
../nimbus/db/accounts_cache, ./witness_types,
|
||||
../stateless/[witness_from_tree, tree_from_witness],
|
||||
./multi_keys
|
||||
|
||||
type
|
||||
Tester = object
|
||||
keys: MultikeysRef
|
||||
memDB: TrieDatabaseRef
|
||||
|
||||
proc testGetBranch(tester: Tester, rootHash: KeccakHash, testStatusIMPL: var TestStatus) =
|
||||
var trie = initSecureHexaryTrie(tester.memdb, rootHash)
|
||||
let flags = {wfEIP170}
|
||||
|
||||
try:
|
||||
var wb = initWitnessBuilder(tester.memdb, rootHash, flags)
|
||||
var witness = wb.buildWitness(tester.keys)
|
||||
|
||||
var db = newMemoryDB()
|
||||
when defined(useInputStream):
|
||||
var input = memoryInput(witness)
|
||||
var tb = initTreeBuilder(input, db, flags)
|
||||
else:
|
||||
var tb = initTreeBuilder(witness, db, flags)
|
||||
|
||||
var root = tb.buildTree()
|
||||
check root.data == rootHash.data
|
||||
|
||||
let newTrie = initSecureHexaryTrie(tb.getDB(), root)
|
||||
for kd in tester.keys.keys:
|
||||
let account = rlp.decode(trie.get(kd.address), Account)
|
||||
let recordFound = newTrie.get(kd.address)
|
||||
if recordFound.len > 0:
|
||||
let acc = rlp.decode(recordFound, Account)
|
||||
doAssert acc == account
|
||||
else:
|
||||
doAssert(false, "BUG IN TREE BUILDER")
|
||||
|
||||
except ContractCodeError as e:
|
||||
debugEcho "CONTRACT CODE ERROR: ", e.msg
|
||||
|
||||
func parseHash256(n: JsonNode, name: string): Hash256 =
|
||||
hexToByteArray(n[name].getStr(), result.data)
|
||||
|
||||
proc setupStateDB(tester: var Tester, wantedState: JsonNode, stateDB: var AccountsCache): Hash256 =
|
||||
var keys = newSeqOfCap[AccountKey](wantedState.len)
|
||||
|
||||
for ac, accountData in wantedState:
|
||||
let account = ethAddressFromHex(ac)
|
||||
let slotVals = accountData{"storage"}
|
||||
var storageKeys = newSeqOfCap[StorageSlot](slotVals.len)
|
||||
|
||||
for slotStr, value in slotVals:
|
||||
let slot = fromHex(UInt256, slotStr)
|
||||
storageKeys.add(slot.toBytesBE)
|
||||
stateDB.setStorage(account, slot, fromHex(UInt256, value.getStr))
|
||||
|
||||
let nonce = accountData{"nonce"}.getHexadecimalInt.AccountNonce
|
||||
let code = accountData{"code"}.getStr.safeHexToSeqByte
|
||||
let balance = UInt256.fromHex accountData{"balance"}.getStr
|
||||
|
||||
stateDB.setNonce(account, nonce)
|
||||
stateDB.setCode(account, code)
|
||||
stateDB.setBalance(account, balance)
|
||||
|
||||
let sKeys = if storageKeys.len != 0: newMultiKeys(storageKeys) else: MultikeysRef(nil)
|
||||
let codeTouched = code.len > 0
|
||||
keys.add((account, codeTouched, sKeys))
|
||||
|
||||
tester.keys = newMultiKeys(keys)
|
||||
stateDB.persist()
|
||||
result = stateDB.rootHash
|
||||
|
||||
proc testBlockWitness(node: JsonNode, rootHash: Hash256, testStatusIMPL: var TestStatus) =
|
||||
var
|
||||
tester = Tester(memDB: newMemoryDB())
|
||||
ac = AccountsCache.init(tester.memDB, emptyRlpHash, true)
|
||||
|
||||
let root = tester.setupStateDB(node, ac)
|
||||
if rootHash != emptyRlpHash:
|
||||
check root == rootHash
|
||||
|
||||
tester.testGetBranch(root, testStatusIMPL)
|
||||
|
||||
proc testFixtureBC(node: JsonNode, testStatusIMPL: var TestStatus) =
|
||||
for fixtureName, fixture in node:
|
||||
let rootHash = parseHash256(fixture["genesisBlockHeader"], "stateRoot")
|
||||
fixture["pre"].testBlockWitness(rootHash, testStatusIMPL)
|
||||
|
||||
proc testFixtureGST(node: JsonNode, testStatusIMPL: var TestStatus) =
|
||||
var fixture: JsonNode
|
||||
|
||||
for fixtureName, child in node:
|
||||
fixture = child
|
||||
break
|
||||
|
||||
fixture["pre"].testBlockWitness(emptyRlpHash, testStatusIMPL)
|
||||
|
||||
proc blockWitnessMain*(debugMode = false) =
|
||||
if paramCount() == 0 or not debugMode:
|
||||
# run all test fixtures
|
||||
suite "Block Witness":
|
||||
jsonTest("newBlockChainTests", "witnessBuilderBC", testFixtureBC)
|
||||
suite "Block Witness":
|
||||
jsonTest("GeneralStateTests", "witnessBuilderGST", testFixtureGST)
|
||||
else:
|
||||
# execute single test in debug mode
|
||||
let config = getConfiguration()
|
||||
if config.testSubject.len == 0:
|
||||
echo "missing test subject"
|
||||
quit(QuitFailure)
|
||||
|
||||
let folder = if config.legacy: "GeneralStateTests" else: "newGeneralStateTests"
|
||||
let path = "tests" / "fixtures" / folder
|
||||
let n = json.parseFile(path / config.testSubject)
|
||||
var testStatusIMPL: TestStatus
|
||||
testFixtureGST(n, testStatusIMPL)
|
||||
|
||||
when isMainModule:
|
||||
var message: string
|
||||
|
||||
## Processing command line arguments
|
||||
if processArguments(message) != Success:
|
||||
echo message
|
||||
quit(QuitFailure)
|
||||
else:
|
||||
if len(message) > 0:
|
||||
echo message
|
||||
quit(QuitSuccess)
|
||||
blockWitnessMain(true)
|
|
@ -0,0 +1,117 @@
|
|||
import
|
||||
eth/common, eth/trie/db, json, os, unittest,
|
||||
../stateless/[tree_from_witness],
|
||||
./witness_types, stew/byteutils
|
||||
|
||||
type
|
||||
Tester = object
|
||||
rootHash: KeccakHash
|
||||
error: bool
|
||||
output: seq[byte]
|
||||
|
||||
proc write(t: var Tester, x: openArray[byte]) =
|
||||
t.output.add x
|
||||
|
||||
proc write(t: var Tester, x: string) =
|
||||
let len = (x.len - 2) div 2
|
||||
var buf: array[4096, byte]
|
||||
hexToByteArray(x, buf, 0, len - 1)
|
||||
t.write(buf.toOpenArray(0, len - 1))
|
||||
|
||||
proc write(t: var Tester, x: JsonNode) =
|
||||
t.write(x.getStr())
|
||||
|
||||
proc processBranchNode(t: var Tester, x: JsonNode) =
|
||||
t.write(x["mask"])
|
||||
|
||||
proc processExtensionNode(t: var Tester, x: JsonNode) =
|
||||
t.write(x["nibblesLen"])
|
||||
t.write(x["nibbles"])
|
||||
|
||||
proc processHashNode(t: var Tester, x: JsonNode) =
|
||||
t.write(x["data"])
|
||||
|
||||
proc processNode(t: var Tester, x: JsonNode, storageMode: bool = false)
|
||||
|
||||
proc processStorage(t: var Tester, tree: JsonNode) =
|
||||
for x in tree:
|
||||
t.processNode(x, true)
|
||||
|
||||
proc processAccountNode(t: var Tester, x: JsonNode) =
|
||||
let accountType = x["accountType"].getStr()
|
||||
t.write(accountType)
|
||||
t.write(x["nibbles"])
|
||||
|
||||
t.write(x["address"])
|
||||
t.write(x["balance"])
|
||||
t.write(x["nonce"])
|
||||
|
||||
case accountType:
|
||||
of "0x00":
|
||||
discard
|
||||
of "0x01":
|
||||
let codeLen = x["codeLen"].getStr()
|
||||
t.write(codeLen)
|
||||
if codeLen != "0x00000000":
|
||||
t.write(x["code"])
|
||||
t.processStorage(x["storage"])
|
||||
of "0x02":
|
||||
t.write("0x03")
|
||||
t.processHashNode(x["codeHash"])
|
||||
t.write(x["codeLen"])
|
||||
t.processStorage(x["storage"])
|
||||
else:
|
||||
doAssert(false, "wrong account type")
|
||||
|
||||
proc processStorageLeafNode(t: var Tester, x: JsonNode) =
|
||||
t.write(x["nibbles"])
|
||||
t.write(x["key"])
|
||||
t.write(x["value"])
|
||||
|
||||
proc processNode(t: var Tester, x: JsonNode, storageMode: bool = false) =
|
||||
let nodeType = x["nodeType"].getStr()
|
||||
t.write(nodeType)
|
||||
case nodeType
|
||||
of "0x00": t.processBranchNode(x)
|
||||
of "0x01": t.processExtensionNode(x)
|
||||
of "0x02":
|
||||
if storageMode:
|
||||
t.processStorageLeafNode(x)
|
||||
else:
|
||||
t.processAccountNode(x)
|
||||
of "0x03": t.processHashNode(x)
|
||||
else:
|
||||
doAssert(false, "wrong node type")
|
||||
|
||||
proc parseRootHash(x: string): KeccakHash =
|
||||
result.data = hexToByteArray[32](x)
|
||||
|
||||
proc parseTester(t: var Tester, n: JsonNode) =
|
||||
t.error = n["error"].getBool()
|
||||
t.rootHash = parseRootHash(n["rootHash"].getStr())
|
||||
t.write(n["version"])
|
||||
t.write(n["metadata"])
|
||||
|
||||
let tree = n["tree"]
|
||||
for x in tree:
|
||||
t.processNode(x)
|
||||
|
||||
proc parseTester(filename: string): Tester =
|
||||
let n = parseFile(filename)
|
||||
parseTester(result, n)
|
||||
|
||||
proc runTest(filePath, fileName: string) =
|
||||
test fileName:
|
||||
let t = parseTester(filePath)
|
||||
var db = newMemoryDB()
|
||||
var tb = initTreeBuilder(t.output, db, {wfEIP170})
|
||||
let root = tb.buildTree()
|
||||
check root == t.rootHash
|
||||
|
||||
proc witnessJsonMain*() =
|
||||
for x in walkDirRec("stateless" / "fixtures"):
|
||||
let y = splitPath(x)
|
||||
runTest(x, y.tail)
|
||||
|
||||
when isMainModule:
|
||||
witnessJsonMain()
|
|
@ -0,0 +1,227 @@
|
|||
import
|
||||
randutils, random, unittest, stew/byteutils,
|
||||
eth/[common, rlp], eth/trie/[hexary, db, trie_defs, nibbles],
|
||||
faststreams/input_stream, nimcrypto/sysrand,
|
||||
../stateless/[witness_from_tree, tree_from_witness],
|
||||
../nimbus/db/storage_types, ./witness_types, ./multi_keys
|
||||
|
||||
type
|
||||
DB = TrieDatabaseRef
|
||||
|
||||
StorageKeys = tuple[storageRoot: Hash256, keys: MultikeysRef]
|
||||
|
||||
AccountDef = object
|
||||
storageKeys: MultiKeysRef
|
||||
account: Account
|
||||
codeTouched: bool
|
||||
|
||||
proc randU256(): UInt256 =
|
||||
var bytes: array[32, byte]
|
||||
discard randomBytes(bytes[0].addr, sizeof(result))
|
||||
result = UInt256.fromBytesBE(bytes)
|
||||
|
||||
proc randStorageSlot(): StorageSlot =
|
||||
discard randomBytes(result[0].addr, sizeof(result))
|
||||
|
||||
proc randNonce(): AccountNonce =
|
||||
discard randomBytes(result.addr, sizeof(result))
|
||||
|
||||
proc randCode(db: DB): Hash256 =
|
||||
if rand(0..1) == 0:
|
||||
result = blankStringHash
|
||||
else:
|
||||
let codeLen = rand(1..150)
|
||||
let code = randList(byte, rng(0, 255), codeLen, unique = false)
|
||||
result = hexary.keccak(code)
|
||||
db.put(contractHashKey(result).toOpenArray, code)
|
||||
|
||||
proc randStorage(db: DB): StorageKeys =
|
||||
if rand(0..1) == 0:
|
||||
result = (emptyRlpHash, MultikeysRef(nil))
|
||||
else:
|
||||
var trie = initSecureHexaryTrie(db)
|
||||
let numPairs = rand(1..10)
|
||||
var keys = newSeq[StorageSlot](numPairs)
|
||||
|
||||
for i in 0..<numPairs:
|
||||
keys[i] = randStorageSlot()
|
||||
trie.put(keys[i], rlp.encode(randU256()))
|
||||
|
||||
if rand(0..1) == 0:
|
||||
result = (trie.rootHash, MultikeysRef(nil))
|
||||
else:
|
||||
var m = newMultikeys(keys)
|
||||
result = (trie.rootHash, m)
|
||||
|
||||
proc randAccount(db: DB): AccountDef =
|
||||
result.account.nonce = randNonce()
|
||||
result.account.balance = randU256()
|
||||
let z = randStorage(db)
|
||||
result.account.codeHash = randCode(db)
|
||||
result.account.storageRoot = z.storageRoot
|
||||
result.storageKeys = z.keys
|
||||
result.codeTouched = rand(0..1) == 0
|
||||
|
||||
proc randAddress(): EthAddress =
|
||||
discard randomBytes(result.addr, sizeof(result))
|
||||
|
||||
proc runTest(numPairs: int, testStatusIMPL: var TestStatus, addInvalidKeys: static[bool] = false) =
|
||||
var memDB = newMemoryDB()
|
||||
var trie = initSecureHexaryTrie(memDB)
|
||||
var addrs = newSeq[AccountKey](numPairs)
|
||||
var accs = newSeq[Account](numPairs)
|
||||
|
||||
for i in 0..<numPairs:
|
||||
let acc = randAccount(memDB)
|
||||
addrs[i] = (randAddress(), acc.codeTouched, acc.storageKeys)
|
||||
accs[i] = acc.account
|
||||
trie.put(addrs[i].address, rlp.encode(accs[i]))
|
||||
|
||||
when addInvalidKeys:
|
||||
# invalidAddress should not end up in block witness
|
||||
let invalidAddress = randAddress()
|
||||
addrs.add((invalidAddress, false, MultikeysRef(nil)))
|
||||
|
||||
var mkeys = newMultiKeys(addrs)
|
||||
let rootHash = trie.rootHash
|
||||
|
||||
var wb = initWitnessBuilder(memDB, rootHash, {wfEIP170})
|
||||
var witness = wb.buildWitness(mkeys)
|
||||
var db = newMemoryDB()
|
||||
when defined(useInputStream):
|
||||
var input = memoryInput(witness)
|
||||
var tb = initTreeBuilder(input, db, {wfEIP170})
|
||||
else:
|
||||
var tb = initTreeBuilder(witness, db, {wfEIP170})
|
||||
let root = tb.buildTree()
|
||||
check root.data == rootHash.data
|
||||
|
||||
let newTrie = initSecureHexaryTrie(tb.getDB(), root)
|
||||
for i in 0..<numPairs:
|
||||
let recordFound = newTrie.get(addrs[i].address)
|
||||
if recordFound.len > 0:
|
||||
let acc = rlp.decode(recordFound, Account)
|
||||
check acc == accs[i]
|
||||
else:
|
||||
debugEcho "BUG IN TREE BUILDER ", i
|
||||
check false
|
||||
|
||||
when addInvalidKeys:
|
||||
for kd in mkeys.keys:
|
||||
if kd.address == invalidAddress:
|
||||
check kd.visited == false
|
||||
else:
|
||||
check kd.visited == true
|
||||
else:
|
||||
for kd in mkeys.keys:
|
||||
check kd.visited == true
|
||||
|
||||
proc initMultiKeys(keys: openArray[string]): MultikeysRef =
|
||||
result.new
|
||||
for x in keys:
|
||||
result.keys.add KeyData(
|
||||
storageMode: false,
|
||||
hash: hexToByteArray[32](x)
|
||||
)
|
||||
|
||||
proc witnessKeysMain*() =
|
||||
suite "random keys block witness roundtrip test":
|
||||
randomize()
|
||||
|
||||
test "random multiple keys":
|
||||
for i in 0..<100:
|
||||
runTest(rand(1..30), testStatusIMPL)
|
||||
|
||||
test "there is no short node":
|
||||
let acc = newAccount()
|
||||
let rlpBytes = rlp.encode(acc)
|
||||
check rlpBytes.len > 32
|
||||
|
||||
test "invalid address ignored":
|
||||
runTest(rand(1..30), testStatusIMPL, addInvalidKeys = true)
|
||||
|
||||
test "case 1: all keys is a match":
|
||||
let keys = [
|
||||
"0abc7124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
||||
"0abccc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606",
|
||||
"0abca163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6bb"
|
||||
]
|
||||
|
||||
let m = initMultiKeys(keys)
|
||||
let pg = m.initGroup()
|
||||
let n = initNibbleRange(hexToByteArray[2]("0abc"))
|
||||
let mg = m.groups(0, n, pg)
|
||||
check:
|
||||
mg.match == true
|
||||
mg.group.first == 0
|
||||
mg.group.last == 2
|
||||
|
||||
test "case 2: all keys is not a match":
|
||||
let keys = [
|
||||
"01237124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
||||
"0890cc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606",
|
||||
"0456a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6bb"
|
||||
]
|
||||
|
||||
let m = initMultiKeys(keys)
|
||||
let pg = m.initGroup()
|
||||
let n = initNibbleRange(hexToByteArray[2]("0abc"))
|
||||
let mg = m.groups(0, n, pg)
|
||||
check:
|
||||
mg.match == false
|
||||
|
||||
test "case 3: not match and match":
|
||||
let keys = [
|
||||
"01237124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
||||
"0890cc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606",
|
||||
"0abc6a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
||||
"0abc7a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b"
|
||||
]
|
||||
|
||||
let m = initMultiKeys(keys)
|
||||
let pg = m.initGroup()
|
||||
let n = initNibbleRange(hexToByteArray[2]("0abc"))
|
||||
let mg = m.groups(0, n, pg)
|
||||
check:
|
||||
mg.match == true
|
||||
mg.group.first == 2
|
||||
mg.group.last == 3
|
||||
|
||||
test "case 4: match and not match":
|
||||
let keys = [
|
||||
"0abc6a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
||||
"0abc7a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
||||
"01237124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
||||
"0890cc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606"
|
||||
]
|
||||
|
||||
let m = initMultiKeys(keys)
|
||||
let pg = m.initGroup()
|
||||
let n = initNibbleRange(hexToByteArray[2]("0abc"))
|
||||
let mg = m.groups(0, n, pg)
|
||||
check:
|
||||
mg.match == true
|
||||
mg.group.first == 0
|
||||
mg.group.last == 1
|
||||
|
||||
test "case 5: not match, match and not match":
|
||||
let keys = [
|
||||
"01237124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
||||
"0890cc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606",
|
||||
"0abc6a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
||||
"0abc7a163140158288775c8912aed274fb9d6a3a260e9e95e03e70ba8df30f6b",
|
||||
"01237124bce7762869be690036144c12c256bdb06ee9073ad5ecca18a47c3254",
|
||||
"0890cc5b491732f964182ce4bde5e2468318692ed446e008f621b26f8ff56606"
|
||||
]
|
||||
|
||||
let m = initMultiKeys(keys)
|
||||
let pg = m.initGroup()
|
||||
let n = initNibbleRange(hexToByteArray[2]("0abc"))
|
||||
let mg = m.groups(0, n, pg)
|
||||
check:
|
||||
mg.match == true
|
||||
mg.group.first == 2
|
||||
mg.group.last == 3
|
||||
|
||||
when isMainModule:
|
||||
witnessKeysMain()
|
|
@ -0,0 +1,434 @@
|
|||
import
|
||||
typetraits,
|
||||
faststreams/input_stream, eth/[common, rlp], stint, stew/endians2,
|
||||
eth/trie/[db, trie_defs], nimcrypto/[keccak, hash],
|
||||
./witness_types, stew/byteutils, ../nimbus/constants
|
||||
|
||||
type
|
||||
DB = TrieDatabaseRef
|
||||
|
||||
NodeKey = object
|
||||
usedBytes: int
|
||||
data: array[32, byte]
|
||||
|
||||
AccountAndSlots* = object
|
||||
address*: EthAddress
|
||||
codeLen*: int
|
||||
slots*: seq[StorageSlot]
|
||||
|
||||
TreeBuilder = object
|
||||
when defined(useInputStream):
|
||||
input: InputStream
|
||||
else:
|
||||
input: seq[byte]
|
||||
pos: int
|
||||
db: DB
|
||||
root: KeccakHash
|
||||
flags: WitnessFlags
|
||||
keys: seq[AccountAndSlots]
|
||||
|
||||
# this TreeBuilder support short node parsing
|
||||
# but a block witness should not contains short node
|
||||
|
||||
# the InputStream still unstable
|
||||
# when using large dataset for testing
|
||||
# or run longer
|
||||
|
||||
when defined(useInputStream):
|
||||
proc initTreeBuilder*(input: InputStream, db: DB, flags: WitnessFlags): TreeBuilder =
|
||||
result.input = input
|
||||
result.db = db
|
||||
result.root = emptyRlpHash
|
||||
result.flags = flags
|
||||
|
||||
proc initTreeBuilder*(input: openArray[byte], db: DB, flags: WitnessFlags): TreeBuilder =
|
||||
result.input = memoryInput(input)
|
||||
result.db = db
|
||||
result.root = emptyRlpHash
|
||||
result.flags = flags
|
||||
else:
|
||||
proc initTreeBuilder*(input: openArray[byte], db: DB, flags: WitnessFlags): TreeBuilder =
|
||||
result.input = @input
|
||||
result.db = db
|
||||
result.root = emptyRlpHash
|
||||
result.flags = flags
|
||||
|
||||
func rootHash*(t: TreeBuilder): KeccakHash {.inline.} =
|
||||
t.root
|
||||
|
||||
func getDB*(t: TreeBuilder): DB {.inline.} =
|
||||
t.db
|
||||
|
||||
when defined(useInputStream):
|
||||
template readByte(t: var TreeBuilder): byte =
|
||||
t.input.read
|
||||
|
||||
template len(t: TreeBuilder): int =
|
||||
t.input.len
|
||||
|
||||
template read(t: var TreeBuilder, len: int): auto =
|
||||
t.input.read(len)
|
||||
|
||||
template readable(t: var TreeBuilder): bool =
|
||||
t.input.readable
|
||||
|
||||
template readable(t: var TreeBuilder, len: int): bool =
|
||||
t.input.readable(len)
|
||||
|
||||
else:
|
||||
template readByte(t: var TreeBuilder): byte =
|
||||
let pos = t.pos
|
||||
inc t.pos
|
||||
t.input[pos]
|
||||
|
||||
template len(t: TreeBuilder): int =
|
||||
t.input.len
|
||||
|
||||
template readable(t: var TreeBuilder): bool =
|
||||
t.pos < t.input.len
|
||||
|
||||
template readable(t: var TreeBuilder, length: int): bool =
|
||||
t.pos + length <= t.input.len
|
||||
|
||||
template read(t: var TreeBuilder, len: int): auto =
|
||||
let pos = t.pos
|
||||
inc(t.pos, len)
|
||||
toOpenArray(t.input, pos, pos+len-1)
|
||||
|
||||
proc safeReadByte(t: var TreeBuilder): byte =
|
||||
if t.readable:
|
||||
result = t.readByte()
|
||||
else:
|
||||
raise newException(IOError, "Cannot read byte from input stream")
|
||||
|
||||
proc safeReadU32(t: var TreeBuilder): uint32 =
|
||||
if t.readable(4):
|
||||
result = fromBytesBE(uint32, t.read(4))
|
||||
else:
|
||||
raise newException(IOError, "Cannot read U32 from input stream")
|
||||
|
||||
template safeReadEnum(t: var TreeBuilder, T: type): untyped =
|
||||
let typ = t.safeReadByte.int
|
||||
if typ < low(T).int or typ > high(T).int:
|
||||
raise newException(ParsingError, "Wrong " & T.name & " value " & $typ)
|
||||
T(typ)
|
||||
|
||||
template safeReadBytes(t: var TreeBuilder, length: int, body: untyped) =
|
||||
if t.readable(length):
|
||||
body
|
||||
else:
|
||||
raise newException(ParsingError, "Failed when try to read " & $length & " bytes")
|
||||
|
||||
proc toKeccak(r: var NodeKey, x: openArray[byte]) {.inline.} =
|
||||
r.data[0..31] = x[0..31]
|
||||
r.usedBytes = 32
|
||||
|
||||
proc append(r: var RlpWriter, n: NodeKey) =
|
||||
if n.usedBytes < 32:
|
||||
r.append rlpFromBytes(n.data.toOpenArray(0, n.usedBytes-1))
|
||||
else:
|
||||
r.append n.data.toOpenArray(0, n.usedBytes-1)
|
||||
|
||||
proc toNodeKey(t: var TreeBuilder, z: openArray[byte]): NodeKey =
|
||||
if z.len < 32:
|
||||
result.usedBytes = z.len
|
||||
result.data[0..z.len-1] = z[0..z.len-1]
|
||||
else:
|
||||
result.data = keccak(z).data
|
||||
result.usedBytes = 32
|
||||
t.db.put(result.data, z)
|
||||
|
||||
proc forceSmallNodeKeyToHash(t: var TreeBuilder, r: NodeKey): NodeKey =
|
||||
let hash = keccak(r.data.toOpenArray(0, r.usedBytes-1))
|
||||
t.db.put(hash.data, r.data.toOpenArray(0, r.usedBytes-1))
|
||||
result.data = hash.data
|
||||
result.usedBytes = 32
|
||||
|
||||
proc writeCode(t: var TreeBuilder, code: openArray[byte]): Hash256 =
|
||||
result = keccak(code)
|
||||
put(t.db, result.data, code)
|
||||
|
||||
proc branchNode(t: var TreeBuilder, depth: int, storageMode: bool): NodeKey
|
||||
proc extensionNode(t: var TreeBuilder, depth: int, storageMode: bool): NodeKey
|
||||
proc accountNode(t: var TreeBuilder, depth: int): NodeKey
|
||||
proc accountStorageLeafNode(t: var TreeBuilder, depth: int): NodeKey
|
||||
proc hashNode(t: var TreeBuilder): NodeKey
|
||||
proc treeNode(t: var TreeBuilder, depth: int = 0, storageMode = false): NodeKey
|
||||
|
||||
proc buildTree*(t: var TreeBuilder): KeccakHash
|
||||
{.raises: [ContractCodeError, Defect, IOError, ParsingError, Exception].} =
|
||||
let version = t.safeReadByte().int
|
||||
if version != BlockWitnessVersion.int:
|
||||
raise newException(ParsingError, "Wrong block witness version")
|
||||
|
||||
# one or more trees
|
||||
|
||||
# we only parse one tree here
|
||||
let metadataType = t.safeReadByte().int
|
||||
if metadataType != MetadataNothing.int:
|
||||
raise newException(ParsingError, "This tree builder support no metadata")
|
||||
|
||||
var res = treeNode(t)
|
||||
if res.usedBytes != 32:
|
||||
raise newException(ParsingError, "Buildtree should produce hash")
|
||||
|
||||
result.data = res.data
|
||||
|
||||
# after the block witness spec mention how to split the big tree into
|
||||
# chunks, modify this buildForest into chunked witness tree builder
|
||||
proc buildForest*(t: var TreeBuilder): seq[KeccakHash]
|
||||
{.raises: [ContractCodeError, Defect, IOError, ParsingError, Exception].} =
|
||||
let version = t.safeReadByte().int
|
||||
if version != BlockWitnessVersion.int:
|
||||
raise newException(ParsingError, "Wrong block witness version")
|
||||
|
||||
while t.readable:
|
||||
let metadataType = t.safeReadByte().int
|
||||
if metadataType != MetadataNothing.int:
|
||||
raise newException(ParsingError, "This tree builder support no metadata")
|
||||
|
||||
var res = treeNode(t)
|
||||
if res.usedBytes != 32:
|
||||
raise newException(ParsingError, "Buildtree should produce hash")
|
||||
|
||||
result.add KeccakHash(data: res.data)
|
||||
|
||||
proc treeNode(t: var TreeBuilder, depth: int, storageMode = false): NodeKey =
|
||||
assert(depth < 64)
|
||||
let nodeType = safeReadEnum(t, TrieNodeType)
|
||||
case nodeType
|
||||
of BranchNodeType: result = t.branchNode(depth, storageMode)
|
||||
of ExtensionNodeType: result = t.extensionNode(depth, storageMode)
|
||||
of AccountNodeType:
|
||||
if storageMode:
|
||||
# parse account storage leaf node
|
||||
result = t.accountStorageLeafNode(depth)
|
||||
else:
|
||||
result = t.accountNode(depth)
|
||||
of HashNodeType: result = t.hashNode()
|
||||
|
||||
if depth == 0 and result.usedBytes < 32:
|
||||
result = t.forceSmallNodeKeyToHash(result)
|
||||
|
||||
proc branchNode(t: var TreeBuilder, depth: int, storageMode: bool): NodeKey =
|
||||
assert(depth < 64)
|
||||
let mask = constructBranchMask(t.safeReadByte, t.safeReadByte)
|
||||
|
||||
when defined(debugDepth):
|
||||
let readDepth = t.safeReadByte().int
|
||||
doAssert(readDepth == depth, "branchNode " & $readDepth & " vs. " & $depth)
|
||||
|
||||
when defined(debugHash):
|
||||
var hash: NodeKey
|
||||
toKeccak(hash, t.read(32))
|
||||
|
||||
var r = initRlpList(17)
|
||||
|
||||
for i in 0 ..< 16:
|
||||
if mask.branchMaskBitIsSet(i):
|
||||
r.append t.treeNode(depth+1, storageMode)
|
||||
else:
|
||||
r.append ""
|
||||
|
||||
if branchMaskBitIsSet(mask, 16):
|
||||
raise newException(ParsingError, "The 17th elem of branch node should empty")
|
||||
|
||||
# 17th elem should always empty
|
||||
r.append ""
|
||||
|
||||
result = t.toNodeKey(r.finish)
|
||||
|
||||
when defined(debugHash):
|
||||
if result != hash:
|
||||
debugEcho "DEPTH: ", depth
|
||||
debugEcho "result: ", result.data.toHex, " vs. ", hash.data.toHex
|
||||
|
||||
func hexPrefix(r: var RlpWriter, x: openArray[byte], nibblesLen: int, isLeaf: static[bool] = false) =
|
||||
doAssert(nibblesLen >= 1 and nibblesLen <= 64)
|
||||
var bytes: array[33, byte]
|
||||
if (nibblesLen mod 2) == 0: # even
|
||||
when isLeaf:
|
||||
bytes[0] = 0b0010_0000.byte
|
||||
else:
|
||||
bytes[0] = 0.byte
|
||||
var i = 1
|
||||
for y in x:
|
||||
bytes[i] = y
|
||||
inc i
|
||||
else: # odd
|
||||
when isLeaf:
|
||||
bytes[0] = 0b0011_0000.byte or (x[0] shr 4)
|
||||
else:
|
||||
bytes[0] = 0b0001_0000.byte or (x[0] shr 4)
|
||||
var last = nibblesLen div 2
|
||||
for i in 1..last:
|
||||
bytes[i] = (x[i-1] shl 4) or (x[i] shr 4)
|
||||
|
||||
r.append toOpenArray(bytes, 0, nibblesLen div 2)
|
||||
|
||||
proc extensionNode(t: var TreeBuilder, depth: int, storageMode: bool): NodeKey =
|
||||
assert(depth < 63)
|
||||
let nibblesLen = t.safeReadByte().int
|
||||
assert(nibblesLen < 65)
|
||||
var r = initRlpList(2)
|
||||
let pathLen = nibblesLen div 2 + nibblesLen mod 2
|
||||
safeReadBytes(t, pathLen):
|
||||
r.hexPrefix(t.read(pathLen), nibblesLen)
|
||||
|
||||
when defined(debugDepth):
|
||||
let readDepth = t.safeReadByte().int
|
||||
doAssert(readDepth == depth, "extensionNode " & $readDepth & " vs. " & $depth)
|
||||
|
||||
when defined(debugHash):
|
||||
var hash: NodeKey
|
||||
toKeccak(hash, t.read(32))
|
||||
|
||||
assert(depth + nibblesLen < 65)
|
||||
let nodeType = safeReadEnum(t, TrieNodeType)
|
||||
case nodeType
|
||||
of BranchNodeType: r.append t.branchNode(depth + nibblesLen, storageMode)
|
||||
of HashNodeType: r.append t.hashNode()
|
||||
else: raise newException(ParsingError, "wrong type during parsing child of extension node")
|
||||
|
||||
result = t.toNodeKey(r.finish)
|
||||
|
||||
when defined(debugHash):
|
||||
if result != hash:
|
||||
debugEcho "DEPTH: ", depth
|
||||
doAssert(result == hash, "EXT HASH DIFF " & result.data.toHex & " vs. " & hash.data.toHex)
|
||||
|
||||
func toAddress(x: openArray[byte]): EthAddress =
|
||||
result[0..19] = result[0..19]
|
||||
|
||||
proc readAddress(t: var TreeBuilder) =
|
||||
safeReadBytes(t, 20):
|
||||
t.keys.add AccountAndSlots(address: toAddress(t.read(20)))
|
||||
|
||||
proc readCodeLen(t: var TreeBuilder): int =
|
||||
let codeLen = t.safeReadU32()
|
||||
if wfEIP170 in t.flags and codeLen > EIP170_CODE_SIZE_LIMIT:
|
||||
raise newException(ContractCodeError, "code len exceed EIP170 code size limit: " & $codeLen)
|
||||
t.keys[^1].codeLen = codeLen.int
|
||||
result = codeLen.int
|
||||
|
||||
proc readHashNode(t: var TreeBuilder): NodeKey =
|
||||
let nodeType = safeReadEnum(t, TrieNodeType)
|
||||
if nodeType != HashNodeType:
|
||||
raise newException(ParsingError, "hash node expected but got " & $nodeType)
|
||||
result = t.hashNode()
|
||||
|
||||
proc accountNode(t: var TreeBuilder, depth: int): NodeKey =
|
||||
assert(depth < 65)
|
||||
|
||||
when defined(debugHash):
|
||||
let len = t.safeReadU32().int
|
||||
let node = @(t.read(len))
|
||||
let nodeKey = t.toNodeKey(node)
|
||||
|
||||
when defined(debugDepth):
|
||||
let readDepth = t.safeReadByte().int
|
||||
doAssert(readDepth == depth, "accountNode " & $readDepth & " vs. " & $depth)
|
||||
|
||||
let accountType = safeReadEnum(t, AccountType)
|
||||
let nibblesLen = 64 - depth
|
||||
var r = initRlpList(2)
|
||||
|
||||
let pathLen = nibblesLen div 2 + nibblesLen mod 2
|
||||
safeReadBytes(t, pathLen):
|
||||
r.hexPrefix(t.read(pathLen), nibblesLen, true)
|
||||
|
||||
t.readAddress()
|
||||
|
||||
safeReadBytes(t, 64):
|
||||
var acc = Account(
|
||||
balance: UInt256.fromBytesBE(t.read(32), false),
|
||||
# TODO: why nonce must be 32 bytes, isn't 64 bit uint enough?
|
||||
nonce: UInt256.fromBytesBE(t.read(32), false).truncate(AccountNonce)
|
||||
)
|
||||
|
||||
case accountType
|
||||
of SimpleAccountType:
|
||||
acc.codeHash = blankStringHash
|
||||
acc.storageRoot = emptyRlpHash
|
||||
of ExtendedAccountType:
|
||||
let codeLen = t.readCodeLen()
|
||||
safeReadBytes(t, codeLen):
|
||||
acc.codeHash = t.writeCode(t.read(codeLen))
|
||||
|
||||
# switch to account storage parsing mode
|
||||
# and reset the depth
|
||||
let storageRoot = t.treeNode(0, storageMode = true)
|
||||
doAssert(storageRoot.usedBytes == 32)
|
||||
acc.storageRoot.data = storageRoot.data
|
||||
of CodeUntouched:
|
||||
let codeHash = t.readHashNode()
|
||||
doAssert(codeHash.usedBytes == 32)
|
||||
acc.codeHash.data = codeHash.data
|
||||
|
||||
# readCodeLen already save the codeLen
|
||||
# along with recovered address
|
||||
# we could discard it here
|
||||
discard t.readCodeLen()
|
||||
|
||||
let storageRoot = t.treeNode(0, storageMode = true)
|
||||
doAssert(storageRoot.usedBytes == 32)
|
||||
acc.storageRoot.data = storageRoot.data
|
||||
|
||||
r.append rlp.encode(acc)
|
||||
|
||||
let nodeRes = r.finish
|
||||
result = t.toNodeKey(nodeRes)
|
||||
|
||||
when defined(debugHash):
|
||||
if result != nodeKey:
|
||||
debugEcho "result.usedBytes: ", result.usedBytes
|
||||
debugEcho "nodeKey.usedBytes: ", nodeKey.usedBytes
|
||||
var rlpa = rlpFromBytes(node)
|
||||
var rlpb = rlpFromBytes(nodeRes)
|
||||
debugEcho "Expected: ", inspect(rlpa)
|
||||
debugEcho "Actual: ", inspect(rlpb)
|
||||
var a = rlpa.listElem(1).toBytes.decode(Account)
|
||||
var b = rlpb.listElem(1).toBytes.decode(Account)
|
||||
debugEcho "Expected: ", a
|
||||
debugEcho "Actual: ", b
|
||||
|
||||
doAssert(result == nodeKey, "account node parsing error")
|
||||
|
||||
func toStorageSlot(x: openArray[byte]): StorageSlot =
|
||||
result[0..31] = result[0..31]
|
||||
|
||||
proc readStorageSlot(t: var TreeBuilder) =
|
||||
safeReadBytes(t, 32):
|
||||
t.keys[^1].slots.add toStorageSlot(t.read(32))
|
||||
|
||||
proc accountStorageLeafNode(t: var TreeBuilder, depth: int): NodeKey =
|
||||
assert(depth < 65)
|
||||
|
||||
when defined(debugHash):
|
||||
let len = t.safeReadU32().int
|
||||
let node = @(t.read(len))
|
||||
let nodeKey = t.toNodeKey(node)
|
||||
|
||||
when defined(debugDepth):
|
||||
let readDepth = t.safeReadByte().int
|
||||
doAssert(readDepth == depth, "accountNode " & $readDepth & " vs. " & $depth)
|
||||
|
||||
let nibblesLen = 64 - depth
|
||||
var r = initRlpList(2)
|
||||
let pathLen = nibblesLen div 2 + nibblesLen mod 2
|
||||
safeReadBytes(t, pathLen):
|
||||
r.hexPrefix(t.read(pathLen), nibblesLen, true)
|
||||
|
||||
t.readStorageSlot()
|
||||
|
||||
safeReadBytes(t, 32):
|
||||
let val = UInt256.fromBytesBE(t.read(32))
|
||||
r.append rlp.encode(val)
|
||||
result = t.toNodeKey(r.finish)
|
||||
|
||||
when defined(debugHash):
|
||||
doAssert(result == nodeKey, "account storage leaf node parsing error")
|
||||
|
||||
proc hashNode(t: var TreeBuilder): NodeKey =
|
||||
safeReadBytes(t, 32):
|
||||
result.toKeccak(t.read(32))
|
|
@ -0,0 +1,318 @@
|
|||
import
|
||||
stew/[byteutils, endians2],
|
||||
nimcrypto/[keccak, hash], eth/[common, rlp],
|
||||
eth/trie/[trie_defs, nibbles, db],
|
||||
faststreams/output_stream,
|
||||
./witness_types, ../nimbus/constants,
|
||||
../nimbus/db/storage_types, ./multi_keys
|
||||
|
||||
type
|
||||
DB = TrieDatabaseRef
|
||||
|
||||
WitnessBuilder* = object
|
||||
db*: DB
|
||||
root: KeccakHash
|
||||
output: OutputStream
|
||||
flags: WitnessFlags
|
||||
|
||||
StackElem = object
|
||||
node: seq[byte]
|
||||
parentGroup: Group
|
||||
keys: MultikeysRef
|
||||
depth: int
|
||||
storageMode: bool
|
||||
|
||||
proc initWitnessBuilder*(db: DB, rootHash: KeccakHash, flags: WitnessFlags = {}): WitnessBuilder =
|
||||
result.db = db
|
||||
result.root = rootHash
|
||||
result.output = memoryOutput().s
|
||||
result.flags = flags
|
||||
|
||||
template extensionNodeKey(r: Rlp): auto =
|
||||
hexPrefixDecode r.listElem(0).toBytes
|
||||
|
||||
proc expectHash(r: Rlp): seq[byte] =
|
||||
result = r.toBytes
|
||||
if result.len != 32:
|
||||
raise newException(RlpTypeMismatch,
|
||||
"RLP expected to be a Keccak hash value, but has an incorrect length")
|
||||
|
||||
template getNode(elem: untyped): untyped =
|
||||
if elem.isList: @(elem.rawData)
|
||||
else: @(get(wb.db, elem.expectHash))
|
||||
|
||||
proc rlpListToBitmask(r: var Rlp): uint =
|
||||
# only bit 1st to 16th are valid
|
||||
# the 1st bit is the rightmost bit
|
||||
var i = 0
|
||||
for branch in r:
|
||||
if not branch.isEmpty:
|
||||
result.setBranchMaskBit(i)
|
||||
inc i
|
||||
r.position = 0
|
||||
|
||||
template write(wb: var WitnessBuilder, x: untyped) =
|
||||
wb.output.append(x)
|
||||
|
||||
proc writeU32Impl(wb: var WitnessBuilder, x: uint32) =
|
||||
wb.write(toBytesBE(x))
|
||||
|
||||
template writeU32(wb: var WitnessBuilder, x: untyped) =
|
||||
wb.writeU32Impl(uint32(x))
|
||||
|
||||
template writeByte(wb: var WitnessBuilder, x: untyped) =
|
||||
wb.write(byte(x))
|
||||
|
||||
proc writeNibbles(wb: var WitnessBuilder; n: NibblesSeq, withLen: bool = true) =
|
||||
# convert the NibblesSeq into left aligned byte seq
|
||||
# perhaps we can optimize it if the NibblesSeq already left aligned
|
||||
let nibblesLen = n.len
|
||||
let numBytes = nibblesLen div 2 + nibblesLen mod 2
|
||||
var bytes: array[32, byte]
|
||||
doAssert(nibblesLen >= 1 and nibblesLen <= 64)
|
||||
for pos in 0..<n.len:
|
||||
if (pos and 1) != 0:
|
||||
bytes[pos div 2] = bytes[pos div 2] or n[pos]
|
||||
else:
|
||||
bytes[pos div 2] = bytes[pos div 2] or (n[pos] shl 4)
|
||||
|
||||
if withLen:
|
||||
# write nibblesLen
|
||||
wb.writeByte(nibblesLen)
|
||||
# write nibbles
|
||||
wb.write(bytes.toOpenArray(0, numBytes-1))
|
||||
|
||||
proc writeExtensionNode(wb: var WitnessBuilder, n: NibblesSeq, depth: int, node: openArray[byte]) =
|
||||
# write type
|
||||
wb.writeByte(ExtensionNodeType)
|
||||
# write nibbles
|
||||
wb.writeNibbles(n)
|
||||
|
||||
when defined(debugDepth):
|
||||
wb.writeByte(depth)
|
||||
|
||||
when defined(debugHash):
|
||||
wb.write(keccak(node).data)
|
||||
|
||||
proc writeBranchNode(wb: var WitnessBuilder, mask: uint, depth: int, node: openArray[byte]) =
|
||||
# write type
|
||||
# branch node 17th elem should always empty
|
||||
doAssert mask.branchMaskBitIsSet(16) == false
|
||||
wb.writeByte(BranchNodeType)
|
||||
# write branch mask
|
||||
# countOnes(branch mask) >= 2 and <= 16
|
||||
wb.writeByte((mask shr 8) and 0xFF)
|
||||
wb.writeByte(mask and 0xFF)
|
||||
|
||||
when defined(debugDepth):
|
||||
wb.writeByte(depth)
|
||||
|
||||
when defined(debugHash):
|
||||
wb.write(keccak(node).data)
|
||||
|
||||
proc writeHashNode(wb: var WitnessBuilder, node: openArray[byte]) =
|
||||
# usually a hash node means the recursion will not go deeper
|
||||
# and the information can be represented by the hash
|
||||
# for chunked witness, a hash node can be a root to another
|
||||
# sub-trie in one of the chunks
|
||||
wb.writeByte(HashNodeType)
|
||||
wb.write(node)
|
||||
|
||||
proc getBranchRecurse(wb: var WitnessBuilder, z: var StackElem) {.raises: [ContractCodeError, IOError, Defect, CatchableError, Exception].}
|
||||
|
||||
proc writeAccountNode(wb: var WitnessBuilder, kd: KeyData, acc: Account, nibbles: NibblesSeq,
|
||||
node: openArray[byte], depth: int) {.raises: [ContractCodeError, IOError, Defect, CatchableError, Exception].} =
|
||||
|
||||
# write type
|
||||
wb.writeByte(AccountNodeType)
|
||||
|
||||
when defined(debugHash):
|
||||
wb.writeU32(node.len)
|
||||
wb.write(node)
|
||||
|
||||
when defined(debugDepth):
|
||||
wb.writeByte(depth)
|
||||
|
||||
doAssert(nibbles.len == 64 - depth)
|
||||
var accountType = if acc.codeHash == blankStringHash and acc.storageRoot == emptyRlpHash: SimpleAccountType
|
||||
else: ExtendedAccountType
|
||||
|
||||
if not kd.codeTouched:
|
||||
accountType = CodeUntouched
|
||||
|
||||
wb.writeByte(accountType)
|
||||
wb.writeNibbles(nibbles, false)
|
||||
wb.write(kd.address)
|
||||
wb.write(acc.balance.toBytesBE)
|
||||
wb.write(acc.nonce.u256.toBytesBE)
|
||||
|
||||
if accountType != SimpleAccountType:
|
||||
if not kd.codeTouched:
|
||||
# the account have code but not touched by the EVM
|
||||
# in current block execution
|
||||
wb.writeHashNode(acc.codeHash.data)
|
||||
let code = get(wb.db, contractHashKey(acc.codeHash).toOpenArray)
|
||||
if wfEIP170 in wb.flags and code.len > EIP170_CODE_SIZE_LIMIT:
|
||||
raise newException(ContractCodeError, "code len exceed EIP170 code size limit")
|
||||
wb.writeU32(code.len)
|
||||
# no code here
|
||||
elif acc.codeHash != blankStringHash:
|
||||
# the account have code and the EVM use it
|
||||
let code = get(wb.db, contractHashKey(acc.codeHash).toOpenArray)
|
||||
if wfEIP170 in wb.flags and code.len > EIP170_CODE_SIZE_LIMIT:
|
||||
raise newException(ContractCodeError, "code len exceed EIP170 code size limit")
|
||||
wb.writeU32(code.len)
|
||||
wb.write(code)
|
||||
else:
|
||||
# no code
|
||||
wb.writeU32(0'u32)
|
||||
|
||||
if kd.storageKeys.isNil:
|
||||
# the account have storage but not touched by EVM
|
||||
wb.writeHashNode(acc.storageRoot.data)
|
||||
elif acc.storageRoot != emptyRlpHash:
|
||||
# the account have storage and the EVM use it
|
||||
var zz = StackElem(
|
||||
node: wb.db.get(acc.storageRoot.data),
|
||||
parentGroup: kd.storageKeys.initGroup(),
|
||||
keys: kd.storageKeys,
|
||||
depth: 0, # set depth to zero
|
||||
storageMode: true # switch to storage mode
|
||||
)
|
||||
getBranchRecurse(wb, zz)
|
||||
else:
|
||||
# no storage at all
|
||||
wb.writeHashNode(emptyRlpHash.data)
|
||||
|
||||
# rule 0x01 and 0x02 can be optimized again to save some bytes
|
||||
# nibbles can be removed to save space, it can be constructed by the parser
|
||||
# using depth dan hash of address with `nibblesLen = 64-depth` (right side bytes)
|
||||
|
||||
#0x00 pathnibbles:<Nibbles(64-d)> address:<Address> balance:<Bytes32> nonce:<Bytes32>
|
||||
#0x01 pathnibbles:<Nibbles(64-d)> address:<Address> balance:<Bytes32> nonce:<Bytes32> bytecode:<Bytecode> storage:<Tree_Node(0,1)>
|
||||
#0x02 pathnibbles:<Nibbles(64-d)> address:<Address> balance:<Bytes32> nonce:<Bytes32> codehash:<Bytes32> codesize:<U32> storage:<Account_Storage_Tree_Node(0)>
|
||||
|
||||
proc writeAccountStorageLeafNode(wb: var WitnessBuilder, key: openArray[byte], val: UInt256, nibbles: NibblesSeq, node: openArray[byte], depth: int) =
|
||||
wb.writeByte(StorageLeafNodeType)
|
||||
|
||||
when defined(debugHash):
|
||||
wb.writeU32(node.len)
|
||||
wb.write(node)
|
||||
|
||||
when defined(debugDepth):
|
||||
wb.writeByte(depth)
|
||||
|
||||
doAssert(nibbles.len == 64 - depth)
|
||||
# nibbles can be removed to save space, it can be constructed by the parser
|
||||
# using depth dan hash of key with `nibblesLen = 64-depth` (right side bytes)
|
||||
wb.writeNibbles(nibbles, false)
|
||||
|
||||
wb.write(key)
|
||||
wb.write(val.toBytesBE)
|
||||
|
||||
#<Storage_Leaf_Node(d<65)> := pathnibbles:<Nibbles(64-d))> key:<Bytes32> val:<Bytes32>
|
||||
|
||||
proc getBranchRecurse(wb: var WitnessBuilder, z: var StackElem) =
|
||||
if z.node.len == 0: return
|
||||
var nodeRlp = rlpFromBytes z.node
|
||||
|
||||
case nodeRlp.listLen
|
||||
of 2:
|
||||
let (isLeaf, k) = nodeRlp.extensionNodeKey
|
||||
let mg = groups(z.keys, z.depth, k, z.parentGroup)
|
||||
|
||||
if not mg.match:
|
||||
# return immediately if there is no match
|
||||
writeHashNode(wb, keccak(z.node).data)
|
||||
return
|
||||
|
||||
let value = nodeRlp.listElem(1)
|
||||
if not isLeaf:
|
||||
# recursion will go deeper depend on the common-prefix length nibbles
|
||||
writeExtensionNode(wb, k, z.depth, z.node)
|
||||
var zz = StackElem(
|
||||
node: value.getNode,
|
||||
parentGroup: mg.group,
|
||||
keys: z.keys,
|
||||
depth: z.depth + k.len, # increase the depth by k.len
|
||||
storageMode: z.storageMode
|
||||
)
|
||||
getBranchRecurse(wb, zz)
|
||||
return
|
||||
|
||||
# there should be only one match
|
||||
let kd = z.keys.visitMatch(mg, z.depth, k)
|
||||
if z.storageMode:
|
||||
doAssert(kd.storageMode)
|
||||
writeAccountStorageLeafNode(wb, kd.storageSlot, value.toBytes.decode(UInt256), k, z.node, z.depth)
|
||||
else:
|
||||
doAssert(not kd.storageMode)
|
||||
writeAccountNode(wb, kd, value.toBytes.decode(Account), k, z.node, z.depth)
|
||||
|
||||
of 17:
|
||||
let branchMask = rlpListToBitmask(nodeRlp)
|
||||
writeBranchNode(wb, branchMask, z.depth, z.node)
|
||||
|
||||
# if there is a match in any branch elem
|
||||
# 1st to 16th, the recursion will go deeper
|
||||
# by one nibble
|
||||
doAssert(z.depth != 64) # notLeaf or path.len == 0
|
||||
|
||||
let path = groups(z.keys, z.parentGroup, z.depth)
|
||||
for i in nonEmpty(branchMask):
|
||||
let branch = nodeRlp.listElem(i)
|
||||
if branchMaskBitIsSet(path.mask, i):
|
||||
# it is a match between multikeys and Branch Node elem
|
||||
var zz = StackElem(
|
||||
node: branch.getNode,
|
||||
parentGroup: path.groups[i],
|
||||
keys: z.keys,
|
||||
depth: z.depth + 1, # increase the depth by one
|
||||
storageMode: z.storageMode
|
||||
)
|
||||
getBranchRecurse(wb, zz)
|
||||
continue
|
||||
|
||||
if branch.isList:
|
||||
# short node appear in yellow paper
|
||||
# but never in the actual ethereum state trie
|
||||
# an rlp encoded ethereum account will have length > 32 bytes
|
||||
# block witness spec silent about this
|
||||
doAssert(false, "Short node should not exist in block witness")
|
||||
else:
|
||||
# if branch elem not empty and not a match, emit hash
|
||||
writeHashNode(wb, branch.expectHash)
|
||||
|
||||
# 17th elem should always empty
|
||||
# 17th elem appear in yellow paper but never in
|
||||
# the actual ethereum state trie
|
||||
# the 17th elem also not included in block witness spec
|
||||
doAssert branchMask.branchMaskBitIsSet(16) == false
|
||||
else:
|
||||
raise newException(CorruptedTrieDatabase,
|
||||
"HexaryTrie node with an unexpected number of children")
|
||||
|
||||
proc buildWitness*(wb: var WitnessBuilder, keys: MultikeysRef): seq[byte]
|
||||
{.raises: [ContractCodeError, IOError, Defect, CatchableError, Exception].} =
|
||||
|
||||
# witness version
|
||||
wb.writeByte(BlockWitnessVersion)
|
||||
|
||||
# one or more trees
|
||||
|
||||
# we only output one big tree here
|
||||
# the condition to split the big tree into chunks of sub-tries
|
||||
# is not clear in the spec
|
||||
wb.writeByte(MetadataNothing)
|
||||
|
||||
var z = StackElem(
|
||||
node: @(wb.db.get(wb.root.data)),
|
||||
parentGroup: keys.initGroup(),
|
||||
keys: keys,
|
||||
depth: 0, # always start with a zero depth
|
||||
storageMode: false # build account witness first
|
||||
)
|
||||
getBranchRecurse(wb, z)
|
||||
|
||||
# result
|
||||
result = wb.output.getOutput(seq[byte])
|
|
@ -0,0 +1,53 @@
|
|||
import nimcrypto/hash, stew/bitops2
|
||||
|
||||
type
|
||||
TrieNodeType* = enum
|
||||
BranchNodeType
|
||||
ExtensionNodeType
|
||||
AccountNodeType
|
||||
HashNodeType
|
||||
|
||||
AccountType* = enum
|
||||
SimpleAccountType
|
||||
ExtendedAccountType
|
||||
CodeUntouched
|
||||
|
||||
WitnessFlag* = enum
|
||||
wfNoFlag
|
||||
wfEIP170 # fork >= Spurious Dragon
|
||||
|
||||
MetadataType* = enum
|
||||
MetadataNothing
|
||||
MetadataSomething
|
||||
|
||||
WitnessFlags* = set[WitnessFlag]
|
||||
|
||||
ContractCodeError* = object of ValueError
|
||||
ParsingError* = object of ValueError
|
||||
|
||||
StorageSlot* = array[32, byte]
|
||||
|
||||
const
|
||||
StorageLeafNodeType* = AccountNodeType
|
||||
BlockWitnessVersion* = 0x01
|
||||
|
||||
proc setBranchMaskBit*(x: var uint, i: int) {.inline.} =
|
||||
assert(i >= 0 and i < 17)
|
||||
x = x or (1 shl i).uint
|
||||
|
||||
func branchMaskBitIsSet*(x: uint, i: int): bool {.inline.} =
|
||||
assert(i >= 0 and i < 17)
|
||||
result = ((x shr i.uint) and 1'u) == 1'u
|
||||
|
||||
func constructBranchMask*(b1, b2: byte): uint {.inline.} =
|
||||
result = uint(b1) shl 8 or uint(b2)
|
||||
if countOnes(result) < 2 or ((result and (not 0x1FFFF'u)) != 0):
|
||||
raise newException(ParsingError, "Invalid branch mask pattern " & $result)
|
||||
|
||||
iterator nonEmpty*(branchMask: uint): int =
|
||||
for i in 0..<16:
|
||||
if not branchMask.branchMaskBitIsSet(i):
|
||||
# we skip an empty elem
|
||||
continue
|
||||
yield i
|
||||
|
|
@ -99,5 +99,8 @@ cliBuilder:
|
|||
./test_state_db,
|
||||
./test_difficulty,
|
||||
./test_transaction_json,
|
||||
./test_blockchain_json
|
||||
./test_blockchain_json,
|
||||
../stateless/test_witness_keys,
|
||||
../stateless/test_block_witness,
|
||||
../stateless/test_witness_json
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue