mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-26 02:45:29 +00:00
Merge pull request #496 from status-im/stateless_client_experiment
[WIP] Stateless client experiment: The Block Witness
This commit is contained in:
commit
7a0215608e
27
stateless/fixtures/1_keys_0_storage_extended.json
Normal file
27
stateless/fixtures/1_keys_0_storage_extended.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
19
stateless/fixtures/1_keys_0_storage_simple.json
Normal file
19
stateless/fixtures/1_keys_0_storage_simple.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
30
stateless/fixtures/1_keys_0_storage_untouched.json
Normal file
30
stateless/fixtures/1_keys_0_storage_untouched.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
26
stateless/fixtures/1_keys_1_storage.json
Normal file
26
stateless/fixtures/1_keys_1_storage.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
368
stateless/fixtures/1_keys_30_storage.json
Normal file
368
stateless/fixtures/1_keys_30_storage.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
630
stateless/fixtures/30_keys_0_storage.json
Normal file
630
stateless/fixtures/30_keys_0_storage.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
2680
stateless/fixtures/30_keys_30_storage.json
Normal file
2680
stateless/fixtures/30_keys_30_storage.json
Normal file
File diff suppressed because it is too large
Load Diff
316
stateless/json_from_tree.nim
Normal file
316
stateless/json_from_tree.nim
Normal file
@ -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()
|
117
stateless/json_witness_gen.nim
Normal file
117
stateless/json_witness_gen.nim
Normal file
@ -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()
|
153
stateless/multi_keys.nim
Normal file
153
stateless/multi_keys.nim
Normal file
@ -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]
|
59
stateless/randutils.nim
Normal file
59
stateless/randutils.nim
Normal file
@ -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)
|
196
stateless/readme.md
Normal file
196
stateless/readme.md
Normal file
@ -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.
|
92
stateless/stack_based_witness_builder.nim
Normal file
92
stateless/stack_based_witness_builder.nim
Normal file
@ -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")
|
134
stateless/test_block_witness.nim
Normal file
134
stateless/test_block_witness.nim
Normal file
@ -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)
|
117
stateless/test_witness_json.nim
Normal file
117
stateless/test_witness_json.nim
Normal file
@ -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()
|
227
stateless/test_witness_keys.nim
Normal file
227
stateless/test_witness_keys.nim
Normal file
@ -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()
|
434
stateless/tree_from_witness.nim
Normal file
434
stateless/tree_from_witness.nim
Normal file
@ -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))
|
318
stateless/witness_from_tree.nim
Normal file
318
stateless/witness_from_tree.nim
Normal file
@ -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])
|
53
stateless/witness_types.nim
Normal file
53
stateless/witness_types.nim
Normal file
@ -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
|
||||
|
||||
|
3039
witnessBuilderBC.md
Normal file
3039
witnessBuilderBC.md
Normal file
File diff suppressed because it is too large
Load Diff
2648
witnessBuilderGST.md
Normal file
2648
witnessBuilderGST.md
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user