Compare commits
1090 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4316c3ce95 | |||
| 77dfd0c2bf | |||
| b1225959e4 | |||
| 0c6892fe65 | |||
| 6491c667a3 | |||
| d6e6a93150 | |||
| 3e9d098e4b | |||
| 48f2396904 | |||
| fc08e7d99c | |||
| 8eb33401eb | |||
| e7e66a5d19 | |||
| 18e812a99f | |||
| 7d78b885d6 | |||
| c46400c527 | |||
| 3ea8cf1b5c | |||
| 9c066a76af | |||
| 7815146f8a | |||
| 42df97f5d5 | |||
| e3b31cda96 | |||
| 326c169e10 | |||
| 8c8ad50862 | |||
| 17c4ce3a5c | |||
| 81ec6137c8 | |||
| 4c9a2fe2a1 | |||
| af1dcf23cf | |||
| b52517bef3 | |||
| af808b72e6 | |||
| 1cdb4c5bdc | |||
| 08b1eb5252 | |||
| 60902e95fe | |||
| 5ab118f982 | |||
| 61b0efe536 | |||
| 1d95b12268 | |||
| b6007e022d | |||
| be154da603 | |||
| 38f8c59642 | |||
| 6e0672ece4 | |||
| d4ffa14da8 | |||
| c8d36c2ff7 | |||
| 315874ae2f | |||
| ed5aeca689 | |||
| e94cb14af8 | |||
| 9921db1a5c | |||
| 74696f2c0c | |||
| d55b043b8d | |||
| b2db52d14b | |||
| 82093b0203 | |||
| 58d6eb0327 | |||
| 152198f779 | |||
| e6020fbec2 | |||
| 1e042d15df | |||
| b72027cf22 | |||
| 0f15f5f94a | |||
| 135a9ee265 | |||
| 696f30f8ed | |||
| 5cac6638ed | |||
| 156a055bcc | |||
| e7b4866553 | |||
| ecc8fb9b04 | |||
| 794b4b4a04 | |||
| a17d8f4293 | |||
| 22605db687 | |||
| a5036cab0d | |||
| c2c41c2c5c | |||
| 1e70108ab4 | |||
| 5e48d00e22 | |||
| cce332d1c9 | |||
| 6023f71672 | |||
| a211e57c8b | |||
| 07fad93eb2 | |||
| 061f010258 | |||
| 68df37da0c | |||
| b25ee3c306 | |||
| 75fb80b6f8 | |||
| 0a01800886 | |||
| e43d6339e7 | |||
| 39c2242d17 | |||
| d0d5d02f0d | |||
| 1041600558 | |||
| 3ec37d2eee | |||
| d56b97e527 | |||
| 41601acac4 | |||
| 6f16347f6e | |||
| c6b839025e | |||
| dfe1c55ed6 | |||
| 3346d1e04f | |||
| 0a1ec82bcd | |||
| f81921ad3b | |||
| d0ef1cf667 | |||
| 9adc66513e | |||
| dfc8d629f3 | |||
| c8487beb17 | |||
| 8997ae2f89 | |||
| 8502c2ea66 | |||
| dbf901a55f | |||
| 76bef699db | |||
| b4b677866e | |||
| ec624b5dcc | |||
| 7f9002f10f | |||
| 9a9ba76f5b | |||
| cf75235531 | |||
| 6b78bac09c | |||
| ae158d5a77 | |||
| 8f2b48e2a8 | |||
| 832db1158d | |||
| fd9c2a64b9 | |||
| 2573a5e7f5 | |||
| 63157c187b | |||
| 1880d22796 | |||
| 6475b8fe70 | |||
| e643eb6331 | |||
| df5ad0cf0c | |||
| 536546e12a | |||
| 2a9c20cc16 | |||
| e0feb2466f | |||
| 24cd36d37c | |||
| a39e7b59a8 | |||
| 84fd3ada3c | |||
| adde995462 | |||
| 02e7433a37 | |||
| 84c48b78e9 | |||
| 44ed18203e | |||
| df5dd69af2 | |||
| b020a077ec | |||
| f573906fe2 | |||
| ff671fcd35 | |||
| cad96d77e3 | |||
| e12d2befb8 | |||
| 7aafa96d3d | |||
| 7d4a9e802e | |||
| 52606ac5fe | |||
| debcd87f70 | |||
| cf833519a5 | |||
| 5bc9c1ab72 | |||
| f8118ee918 | |||
| d6a0062ca1 | |||
| 6e3ee81336 | |||
| 3be20fa28b | |||
| d5deb4eef1 | |||
| 2d5a313f00 | |||
| 23baf6dfe9 | |||
| 48b40afbca | |||
| eac39208ff | |||
| 7e4b451c9b | |||
| 5522e274af | |||
| f9becdbf21 | |||
| ed647f8628 | |||
| cb160e00c2 | |||
| da92673aff | |||
| a9aea207be | |||
| 7f258bcd43 | |||
| ab0752c7c9 | |||
| 0c180d04c5 | |||
| e0c3b9daaf | |||
| 94d8f4470b | |||
| 46b0a2a55c | |||
| 8bf37aac8a | |||
| a24b05cd4e | |||
| 93f52ea34f | |||
| 7ee35bb31d | |||
| 9063dd87e1 | |||
| df957cd13d | |||
| 094448ac06 | |||
| 6ba406ff2d | |||
| 4394506093 | |||
| 70a6c94b3a | |||
| c2c784fb25 | |||
| 2099dc1d36 | |||
| 5ec692a2be | |||
| cdc3ffe4da | |||
| 7baee720cd | |||
| d680c62f79 | |||
| 2cac6cd13f | |||
| 500814876e | |||
| 1c612e8189 | |||
| 93fc50a277 | |||
| 041d897df0 | |||
| 045db81995 | |||
| 0dd648a10e | |||
| 2b67c0f654 | |||
| c6717c4054 | |||
| 70fe996081 | |||
| f250a2d545 | |||
| 72451aad53 | |||
| bafd7ddd69 | |||
| 173fff951f | |||
| 2b469c4748 | |||
| c1c32ee562 | |||
| 35df1bc591 | |||
| 9784e89666 | |||
| c0ed157959 | |||
| ba8ba7f0e9 | |||
| 3e2fd4d55b | |||
| 8eb0d86f71 | |||
| 16e5a1c5ff | |||
| 832fa96e09 | |||
| 3a35e0e799 | |||
| 045b439cfa | |||
| 778ff304e6 | |||
| b1972b154e | |||
| abf26654a0 | |||
| 3bcf55d1f7 | |||
| cb153b637b | |||
| 320cc044aa | |||
| 8128ea9502 | |||
| 3cd1b3609c | |||
| 178f3988db | |||
| 784fc5e176 | |||
| 2d31597efc | |||
| eed59be134 | |||
| 7d36182587 | |||
| a8c8f4d278 | |||
| 19cd5ee282 | |||
| 7988545513 | |||
| 264ff183cb | |||
| efc31df65d | |||
| 740e60de64 | |||
| 7894aabf1e | |||
| 6073cce8d6 | |||
| 9bd0629176 | |||
| 8d7845f89c | |||
| 23670d00ca | |||
| 7b529301a3 | |||
| bbf4a2e110 | |||
| fe8e3dfca3 | |||
| dec7c1103a | |||
| ed797fbc16 | |||
| f04718b930 | |||
| 8470e01f24 | |||
| 71a155b9cf | |||
| 0ec3c58be4 | |||
| 7702f1a6d3 | |||
| 5d3988570c | |||
| cd42afafcb | |||
| b3a1eafdaf | |||
| 602e9eec89 | |||
| c2ef8b2f59 | |||
| 5ace1197c6 | |||
| 2c5bb903e7 | |||
| dc5c15426f | |||
| 62b9337cb8 | |||
| 428de82034 | |||
| 7cf95895eb | |||
| 3598fda51b | |||
| 37f374f2d5 | |||
| a8877f19cd | |||
| bd8d23a2e8 | |||
| 9d1ea27ed7 | |||
| 09434dabfb | |||
| 30eb5da5c6 | |||
| d0a21f7828 | |||
| a0ddca815b | |||
| dfd1444e1c | |||
| 3677252f0a | |||
| 283daf3d2e | |||
| 792f5184a1 | |||
| ac5286401c | |||
| e2c8a2ad81 | |||
| c0c0659822 | |||
| 70e23921de | |||
| b4fb60f1c9 | |||
| 809d72e029 | |||
| cfa3c11bd5 | |||
| f171585ae8 | |||
| 2e896e3c5a | |||
| 19bd633d07 | |||
| a08aeceaef | |||
| 0da31eaa35 | |||
| 3e1f6cabe3 | |||
| 4c27d35bd0 | |||
| 759291d018 | |||
| 83de948f5d | |||
| 0a5ea49da8 | |||
| 5037c40a61 | |||
| 6dc85d861a | |||
| 71548a6425 | |||
| 72c5d299fe | |||
| 3ed53c2b3e | |||
| 258a06ab82 | |||
| 5cf31c9929 | |||
| a0ff189eab | |||
| 6811be23de | |||
| e9976d2ce0 | |||
| c913c9e098 | |||
| bd14a62314 | |||
| d2aeeb7e3e | |||
| 5b441a3d68 | |||
| c4c448ea6c | |||
| 44af6d8532 | |||
| fe49865132 | |||
| 11c42ce651 | |||
| d010733bcf | |||
| 5b0108f7a2 | |||
| 8f83e49f90 | |||
| 111e4bd3e4 | |||
| 83307587a2 | |||
| 0046163ad7 | |||
| f13bf0ab1b | |||
| 941b525093 | |||
| a2905e7472 | |||
| cf07a4e629 | |||
| 61173fc332 | |||
| c5e2bcc983 | |||
| 401ece1a58 | |||
| 4c93d2fafb | |||
| 447fc3c722 | |||
| 60299d5cb5 | |||
| a631088f4d | |||
| cfeaa64b41 | |||
| ef17c150ea | |||
| 6444b2796d | |||
| 837b33ef33 | |||
| e6bfc9e214 | |||
| 9e5aaf8de9 | |||
| e4c20c835c | |||
| e8797dfd4f | |||
| 4d4e3606b5 | |||
| 130fbbf804 | |||
| 7d9fd3b771 | |||
| 1e36c5123f | |||
| 629aa81aeb | |||
| 4ad4135fc2 | |||
| a215b2cd12 | |||
| 84fa8238b5 | |||
| 5613660355 | |||
| 6e7d98ea2c | |||
| 505b5d8b4a | |||
| d2a91c9398 | |||
| e377339f04 | |||
| c7a2f6130e | |||
| 4be2e1cd13 | |||
| 407e0ba5fe | |||
| 27d1430af5 | |||
| d74575396b | |||
| 558f69064b | |||
| cfc122d890 | |||
| c1112fcd48 | |||
| 480eb31260 | |||
| 993bcbae73 | |||
| 3f007ba932 | |||
| 05d9c4bfe5 | |||
| d5491960f0 | |||
| 42f3250826 | |||
| 5e59a1709a | |||
| 19acc7d847 | |||
| 4107e46348 | |||
| e161d29bcf | |||
| 397293bd80 | |||
| 44f8c70d45 | |||
| 9cf5ffe5c0 | |||
| ef96d952b9 | |||
| 19dd2520de | |||
| 863360411b | |||
| 9178e5ef3b | |||
| 48a4d871db | |||
| 5819266db8 | |||
| 4a85352462 | |||
| 85eecb0961 | |||
| 9de88d468b | |||
| ce4f840bfa | |||
| 5530a26449 | |||
| b71a10c21a | |||
| f14f2b36e6 | |||
| 3c0e506959 | |||
| 2ae98cb29c | |||
| 4a3b81cf9a | |||
| 5aecde251b | |||
| cd0d56ab7f | |||
| d0c8046edd | |||
| 6080511bbe | |||
| b14fc5a4b4 | |||
| 6bc3df865d | |||
| 837a5761f1 | |||
| a7df2e7d59 | |||
| 576ba161f2 | |||
| c90430b316 | |||
| d144ac5999 | |||
| 06cc38e9f7 | |||
| 08bd7b5941 | |||
| de7274be4e | |||
| d24cbb1509 | |||
| afd52cb105 | |||
| d42b860039 | |||
| b152475c8f | |||
| 5bdf3fb20a | |||
| ddd64861b0 | |||
| 3ec5a36a75 | |||
| b62ea2b878 | |||
| 9c7b0b721e | |||
| bad5a9bc91 | |||
| ec5db248b7 | |||
| e7a1572118 | |||
| 48022c93b6 | |||
| 962f0a975d | |||
| bc7d7e2c84 | |||
| 8d524d965f | |||
| 96fa497a43 | |||
| fcbdb0ae98 | |||
| b1b7ae76ef | |||
| 95852be12e | |||
| c13777a99c | |||
| 5531d0e978 | |||
| 248ed21b54 | |||
| e6b2a4ec7e | |||
| 4e9a7ec8ec | |||
| a28e873134 | |||
| b84b293e08 | |||
| 7c009bb934 | |||
| 7075447265 | |||
| 0fbdbfa4e0 | |||
| 2eae5267eb | |||
| 64aee92db8 | |||
| c5b6b0dcc2 | |||
| ca1a8d3ad7 | |||
| 0d30973b85 | |||
| 76816a83aa | |||
| a5adbda94f | |||
| eddd8373b5 | |||
| 078197250d | |||
| 21630d3a5c | |||
| cdc00a2385 | |||
| 62126c6482 | |||
| 2940d5aa92 | |||
| 715cd765df | |||
| a88bf8d9fd | |||
| dc66574b2d | |||
| 3b2c22d240 | |||
| f3b5c74cc8 | |||
| 21d988d279 | |||
| c5010d9df3 | |||
| a6872c8e84 | |||
| be0054bb3c | |||
| 1ce9036357 | |||
| 021597ff7b | |||
| 6dfb66edaa | |||
| 655c1c58da | |||
| 63de610175 | |||
| 5d0abaf7a2 | |||
| c6df34b1e1 | |||
| 4e04ff329c | |||
| cac4714b9c | |||
| 733c6057bb | |||
| e49e1a8909 | |||
| 3617b438b6 | |||
| 93f7a50d1f | |||
| 9892f9abf7 | |||
| 4957dd5b63 | |||
| ecc5c4ec11 | |||
| e95ae9c239 | |||
| 06a8032662 | |||
| 4f9baf57c6 | |||
| 91f689483f | |||
| 34cda2a910 | |||
| caa01ca443 | |||
| 9755a45304 | |||
| 6e9e2edd59 | |||
| 9255393bac | |||
| 2c0b46f70f | |||
| e226e464c3 | |||
| ba66693ace | |||
| 3cc87ef0af | |||
| 75caa12f25 | |||
| 84cd370504 | |||
| aff761c7b4 | |||
| 3b4c66ba50 | |||
| a13f48ba3c | |||
| 181fe40036 | |||
| 1dea8a9e3d | |||
| a6c1e92aea | |||
| 90cdd6741f | |||
| 3e585d0f8a | |||
| cd40443f94 | |||
| 967ab869e0 | |||
| d16743f120 | |||
| f65b2ec92b | |||
| 201793adb8 | |||
| b657fc6eb7 | |||
| e3c6016bd4 | |||
| e88ccece3f | |||
| 481811969e | |||
| dcd8bc6df1 | |||
| d2e38e2ef5 | |||
| ad6a0969df | |||
| 43f6dca8f5 | |||
| 150c18c34f | |||
| 826cf9905b | |||
| 1dc4ff8cd8 | |||
| a3e0756474 | |||
| 36f89c71e1 | |||
| 88322f7d6b | |||
| 23b5f27c3c | |||
| d6947eff58 | |||
| d181cacf46 | |||
| 2952699ebc | |||
| 8a0adc4b18 | |||
| 02ddc0375e | |||
| 5716438cea | |||
| e9b6c9ee21 | |||
| 82ae03985d | |||
| cbcad80c61 | |||
| ab7c555879 | |||
| b4f18d5d20 | |||
| f6d51f1989 | |||
| 505bae8ef3 | |||
| db73142095 | |||
| dcbb111741 | |||
| 4b7e8274c7 | |||
| b1ce2718b7 | |||
| a5997fe0ee | |||
| b46629b9d0 | |||
| 33c09d385e | |||
| 4dd5651a11 | |||
| 97a8d1299f | |||
| fd0c702d18 | |||
| 4f90bf6ff0 | |||
| f6f93c0a0a | |||
| 3f191a9adc | |||
| 5980ed3431 | |||
| 60bf50dc64 | |||
| c15ed53794 | |||
| 1935103562 | |||
| 2fd6ad6057 | |||
| 3fe0fdf357 | |||
| df2060fcca | |||
| f1affa6b8e | |||
| 97ed6b6118 | |||
| fd4cdbd52c | |||
| 8d13c2aae1 | |||
| 90e773a370 | |||
| 0b1b2a26eb | |||
| ba6b9a95db | |||
| 08f614c121 | |||
| a2ee13ad39 | |||
| f759571449 | |||
| b160db9044 | |||
| b8cb13ffdd | |||
| 8d300377d5 | |||
| 1cf32d615d | |||
| 074471b413 | |||
| 94424bf5c3 | |||
| 9d737860c0 | |||
| 7e22fed30d | |||
| ee77041529 | |||
| af23774f13 | |||
| 34612f2dab | |||
| 5a94445f3f | |||
| d12726c58f | |||
| a642ad3071 | |||
| 251c2a31d9 | |||
| 6e5cb4a67c | |||
| 8f75c30e2e | |||
| 86791fe7cc | |||
| 0d497be7c5 | |||
| 051546bfc2 | |||
| fa61e5c475 | |||
| 16c2a92432 | |||
| bd664892b3 | |||
| 06b6d651b3 | |||
| 707e72f8db | |||
| a79358fd61 | |||
| 4c6de84598 | |||
| 24d9998f55 | |||
| 2282b7bac1 | |||
| 8301c6bd7b | |||
| f0b44752c4 | |||
| 726a0333e7 | |||
| 897e950c4b | |||
| 270ea091f9 | |||
| e27ebfa1b8 | |||
| a2aecb3e23 | |||
| 4d31b149ff | |||
| 2dd3a18235 | |||
| 37f0b285b4 | |||
| 52cfbd8346 | |||
| 210b884ffe | |||
| 5a67397e1c | |||
| 724ba6cdae | |||
| 96ae1ec49a | |||
| a7a401edb2 | |||
| 71378b9b1a | |||
| 679b2053a0 | |||
| 94d4b4be24 | |||
| 8f84257950 | |||
| 12ec8ece1d | |||
| 2f9903033a | |||
| 9283b46856 | |||
| 3364a714a1 | |||
| fe98d62640 | |||
| 46502e5814 | |||
| 1b77051987 | |||
| d00908e0f4 | |||
| 2aad1dcaef | |||
| d8485297bb | |||
| 06c418e813 | |||
| 986dc8f388 | |||
| 077f4fbf93 | |||
| 8bb930c87b | |||
| 165e72ff95 | |||
| 84e8facc78 | |||
| dd59a99722 | |||
| 0dee765de8 | |||
| 8adaef4cf0 | |||
| 8a176e9ec5 | |||
| aaaa31184b | |||
| f79b1a21c7 | |||
| 576143901a | |||
| 6f9ad52fbb | |||
| e7a7f211a5 | |||
| 3c78db6777 | |||
| d35b2f9515 | |||
| 8a1fecd9a7 | |||
| f25de1b5a8 | |||
| 239fa1fdc8 | |||
| c9df8e9f1b | |||
| ac8002b2d1 | |||
| 016183a467 | |||
| 66fc7ddeec | |||
| 4384455481 | |||
| 77ba8eacfe | |||
| dd4fb9143f | |||
| 73105095a5 | |||
| d0be3b93ef | |||
| 3ee279a10d | |||
| 8fa412616e | |||
| ec5681d775 | |||
| 9837593164 | |||
| b1654c9763 | |||
| 5ee9f0f3d0 | |||
| c42d216143 | |||
| 1ca46a897c | |||
| e0f14275f5 | |||
| 2fda9e983e | |||
| 86ba65b852 | |||
| 9a1fd644ad | |||
| 4fc996d0a0 | |||
| 56863d0f69 | |||
| 12868230fb | |||
| 273a4a2fdf | |||
| d14493b75a | |||
| 24e376cf87 | |||
| b2c8fb8f72 | |||
| 1ad0888997 | |||
| 0dc7b3ae58 | |||
| 82bcada40a | |||
| 422878fb11 | |||
| 3edbfdda74 | |||
| 5eff003966 | |||
| 91720b8f74 | |||
| 305f41b7c6 | |||
| d47c760921 | |||
| 30e99e66c2 | |||
| 5169a113bd | |||
| 0d977585ce | |||
| 3c6bb070d0 | |||
| 938d4abc53 | |||
| f6f56e5c5d | |||
| 299c9e9ee0 | |||
| 310e6b8de7 | |||
| 424a0a7fe2 | |||
| 68fe107e1a | |||
| e9ef2026be | |||
| 7d250f6866 | |||
| 3022c7c9f7 | |||
| c91f3c6c45 | |||
| def487d61b | |||
| 5082223a01 | |||
| 9a8d79212d | |||
| df12ef4835 | |||
| 9aaac36e12 | |||
| eba31b1187 | |||
| d8d9fa3195 | |||
| 9cbca20f81 | |||
| e302b74fd9 | |||
| 1ffa26a3df | |||
| 0bfc796926 | |||
| e2e591fb54 | |||
| 3d2ad4de22 | |||
| a357765c73 | |||
| 280d799958 | |||
| f694588f9f | |||
| a4129fc6d9 | |||
| 8d95662be5 | |||
| 40f04073d3 | |||
| 34baed929b | |||
| 2eb0b43a65 | |||
| f44705d37d | |||
| a10d6bc3be | |||
| b1af6cf101 | |||
| cd3aaf24c1 | |||
| ed2e91d68d | |||
| eb0d3dc090 | |||
| 368fdd4ba9 | |||
| 62903c04ab | |||
| b7fdd1c884 | |||
| 625662a2d2 | |||
| d34e1cf1d0 | |||
| 26c03a57d3 | |||
| 54ff3feba9 | |||
| 38f3988ac5 | |||
| 2aa860d871 | |||
| b245ba43c2 | |||
| 7b010976a4 | |||
| eb2d2d5d6e | |||
| c4629c7c3a | |||
| e702efa4d4 | |||
| 7eb38c4cd5 | |||
| ffc8716284 | |||
| 08d545a566 | |||
| 511ec439a2 | |||
| 94cb1d6353 | |||
| 89963f6fdc | |||
| 34a45f8d92 | |||
| a8022cdd1b | |||
| 3ac58ebd50 | |||
| 754b2d81f9 | |||
| 2def372081 | |||
| 2281382f15 | |||
| f93a6084ea | |||
| 217152d7fe | |||
| 3e89847085 | |||
| 26025d7382 | |||
| d13d06886c | |||
| 889de5b282 | |||
| 6c5bc1e721 | |||
| 517ef20396 | |||
| 8d17b5cf40 | |||
| 6f16383453 | |||
| debba62dd8 | |||
| 7e1ef84c9b | |||
| 404e5a9372 | |||
| 717685c037 | |||
| 4365542bc4 | |||
| 1b18206a79 | |||
| b35c3d6fba | |||
| 88cbbb5b71 | |||
| 2914633fc3 | |||
| febc1a1629 | |||
| 2b6775334d | |||
| d865960c07 | |||
| 165a5a575e | |||
| f52beda0a2 | |||
| caa8f41586 | |||
| cfe83467aa | |||
| 01bb0d406b | |||
| 04815fa968 | |||
| 7f0b67f6b8 | |||
| a6565005ff | |||
| 24b533397a | |||
| 8e7b6ec66b | |||
| a4d6ecc40b | |||
| 585dca0157 | |||
| 61969f6701 | |||
| 5c5a37395e | |||
| c137951259 | |||
| ed420c2198 | |||
| 0e5cd164ed | |||
| 96930a1616 | |||
| e17e772e0c | |||
| 337b81122c | |||
| dc542bd122 | |||
| 6b708e375c | |||
| ac1c5db6cc | |||
| 33656e3ee6 | |||
| f149ce6af0 | |||
| 34b8e11f2e | |||
| de7c7b0e48 | |||
| 2540fc16c8 | |||
| 5add73c439 | |||
| 23089d6545 | |||
| 5f1770b58e | |||
| 0271a56e9a | |||
| 6fda64452c | |||
| 93173ce827 | |||
| e54032ab72 | |||
| 2a904da020 | |||
| 8f32316705 | |||
| b950953ae6 | |||
| 9cd3fc4271 | |||
| f0196780b1 | |||
| 3ed8ac7eee | |||
| 2ee7407eb5 | |||
| 7143ea4269 | |||
| b815f304aa | |||
| 8e95e7c2c9 | |||
| 031f5959bb | |||
| acb1bdb79e | |||
| 2aad4aecd8 | |||
| fe5e1f77c7 | |||
| 62c426081f | |||
| 21bdaed5f2 | |||
| 9aa3ffc00a | |||
| 922d4486fa | |||
| 112702a870 | |||
| 9af6e9023d | |||
| b69a03f3dc | |||
| 8f62c272fb | |||
| 7cb3a08ef3 | |||
| ed72be42f7 | |||
| 1381a0f19e | |||
| 4ca1528956 | |||
| ba0f16834c | |||
| 6c95e51b5a | |||
| e1320e7931 | |||
| 0db18fc295 | |||
| da7dff2c39 | |||
| 031f5fe969 | |||
| f72b6fd047 | |||
| a70daf9c87 | |||
| 0743686658 | |||
| 1334957ae5 | |||
| be375bf42e | |||
| 9d2235b672 | |||
| fb12fe627f | |||
| bda0d82103 | |||
| 18d654f3bc | |||
| 3775f5050a | |||
| baac068f92 | |||
| f164461d6c | |||
| 02163a4519 | |||
| 08746f0e55 | |||
| 92870e10d4 | |||
| 2651eaf7d1 | |||
| d86782461b | |||
| 777d9d30bf | |||
| 03750c994a | |||
| e290f59aef | |||
| 48174929b2 | |||
| 3fe6780a91 | |||
| 292a0a3826 | |||
| 912cc20f24 | |||
| c26432e9be | |||
| 26f0b5a111 | |||
| b0e950a660 | |||
| 24ce5d97d8 | |||
| f84884ccfa | |||
| 060be863a3 | |||
| 136262250a | |||
| 6da9928831 | |||
| eb8337adb1 | |||
| a3fcefbd3f | |||
| 2970357110 | |||
| e75b2dda6a | |||
| 9e32a10396 | |||
| 00a552f1b0 | |||
| 58fed8c512 | |||
| 654aadedda | |||
| 95ebafbcf1 | |||
| 596487257d | |||
| 5260e4c8a5 | |||
| 0148c8d162 | |||
| 92f751d0d4 | |||
| 9380de4812 | |||
| 22b60a3ae3 | |||
| 15061f94fb | |||
| 186a0b038e | |||
| fba333be4d | |||
| 6caa809804 | |||
| e990619600 | |||
| b646c92db3 | |||
| bb1943de18 | |||
| 7a5d57b434 | |||
| 7ab1586970 | |||
| a8ea37a27c | |||
| 6dd154b4e0 | |||
| 5cc242518e | |||
| 20951f1ce0 | |||
| 3fd598ba65 | |||
| bd1bd7d573 | |||
| bbfe01eb34 | |||
| dc1575ba6b | |||
| 099bbe45fa | |||
| 5e5a2c60c4 | |||
| a9cea07eca | |||
| 7b2efb8ee7 | |||
| 65163a90d4 | |||
| eacc66da47 | |||
| 1c667f77be | |||
| 9329fa3a62 | |||
| 2e9de779f9 | |||
| 068ac71968 | |||
| 1b1a191816 | |||
| 99dd39dd8f | |||
| 4a53c71ed0 | |||
| 1d3bc44203 | |||
| ec3c5ed1dc | |||
| a33f61099a | |||
| e10013e9b9 | |||
| 290fc7a04b | |||
| 7beadb0a2c | |||
| 15300dc01b | |||
| 0351d913b0 | |||
| 842fa04bce | |||
| afbb418cb5 | |||
| d9fd83b82b | |||
| fb4b5d5fab | |||
| b90e937734 | |||
| e370adf3d7 | |||
| 372005e8c1 | |||
| 726b56ec71 | |||
| 8a43b014df | |||
| 18f4ce2c75 | |||
| e084cce8f8 | |||
| 827ffe89f3 | |||
| 64f4c4b57e | |||
| 272d5f3afc | |||
| 77f8a4a6d2 | |||
| 4de10f5a45 | |||
| 97b28b66b4 | |||
| 17ada31c33 | |||
| 54af028748 | |||
| 339d1c8624 | |||
| 914c0cb96c | |||
| 789d077cde | |||
| 7adb00bc92 | |||
| e0aa788ce2 | |||
| 2b6f6bb74c | |||
| bf1126db27 | |||
| acfdfc53a6 | |||
| 39342990c3 | |||
| ed08c5e8ab | |||
| 2fb428f796 | |||
| f7c341b74a | |||
| 2fb3e06c60 | |||
| a1f8820031 | |||
| 525055fb9d | |||
| 95d99fb643 | |||
| bd017586b6 | |||
| e095f2b154 | |||
| 79e387ea76 | |||
| b7bf037e4c | |||
| 864e31e798 | |||
| 75994a38c1 | |||
| 525a454414 | |||
| 0d5f7ddad8 | |||
| d444a1a616 | |||
| bf92e33c5b | |||
| 30e77a1db1 | |||
| 53caa49d80 | |||
| 059330e9ac | |||
| f5ac3f77bc | |||
| 110303d87d | |||
| 39464f00ad | |||
| 367123bdea | |||
| 598a2c8086 | |||
| 817f9f1ccf | |||
| 19ff2d0f89 | |||
| 4091091b2e | |||
| 08e7e1c103 | |||
| 4e5a2cc197 | |||
| 54b1c4f8af | |||
| 45c6376270 | |||
| 2e4495e651 | |||
| 792a0f8559 | |||
| 52b7903d92 | |||
| cc6879c467 | |||
| bea0ec0195 | |||
| cdb1342fc8 | |||
| 7703c6ef08 | |||
| d1d717d8b0 | |||
| 0cd997ff6e | |||
| d23ae4cde9 | |||
| 7dc7133cd0 | |||
| 6936b4144f | |||
| 8655e4dca1 | |||
| 23aea602b1 | |||
| 44d9bb75c0 | |||
| cf4097217f | |||
| 2bda949c35 | |||
| b745639b0a | |||
| b805a8802c | |||
| de6fe8c595 | |||
| d4fa489e80 | |||
| 2954299bd8 | |||
| 6ad0eb7668 | |||
| c15e275e38 | |||
| 6eabf64f74 | |||
| 7861f772f5 | |||
| 0c2e0e61e7 | |||
| ecea4c38ff | |||
| 811817f60c | |||
| ffffe77a96 | |||
| 118a2a0b1b | |||
| dc76de1576 | |||
| c175d6be97 | |||
| 33b94ba2a4 | |||
| d291e17022 | |||
| 0d5980d2da | |||
| 8bfa573dfc | |||
| f1d78e2be2 | |||
| c1bbfe226d | |||
| d32cc2066f | |||
| 119dd4dce8 | |||
| bd4a525f2f | |||
| b6b6bcf767 | |||
| ee1795570d | |||
| ed975c6425 | |||
| 452b719504 | |||
| 8e2da33d08 | |||
| 577b088713 | |||
| 84b11790c4 | |||
| 1db0e6c7c3 | |||
| 2ca2eb9d44 | |||
| 92fa92a1b2 | |||
| 7ba252b71c | |||
| 22b0118aae | |||
| c7f962836a | |||
| 40a0507b3b | |||
| ae8eee9824 | |||
| be41cdbdf4 | |||
| 56a11f1874 | |||
| d4b31f341c | |||
| d35b65c863 | |||
| 3fe37f3b92 | |||
| f7a486f88f | |||
| f09a1becd0 | |||
| bda146d96e | |||
| 841011b13e | |||
| f7c008ad72 | |||
| 90a1cbd5e8 | |||
| 20f7ccf399 | |||
| f852de1a13 | |||
| e3e688ad51 | |||
| 4dd551f00f | |||
| b7bc0fece0 | |||
| 8fd065c0cf | |||
| 9f59b5ea4c | |||
| 2afac0031c | |||
| 138fb0e096 | |||
| 9986399867 | |||
| 0780d77b60 | |||
| 722508e86e | |||
| 0ef5a84596 | |||
| 7417565481 | |||
| 5ae2aef2ed | |||
| 4b17541372 | |||
| 6be258e1aa | |||
| c0edf6c3ef | |||
| 96a377cdcf | |||
| 5f857313ac | |||
| 91e96d01b8 | |||
| d5e9dcdc86 | |||
| 4a52633f5d | |||
| 1a7212c931 | |||
| 1557592bd5 | |||
| a370d138a0 | |||
| 4cb2657ac8 | |||
| 58c9879d69 | |||
| 6eabcab35e | |||
| 8034303068 | |||
| 760ddde988 | |||
| 8e6242da0f | |||
| 3c699a43ec | |||
| a084e2e001 | |||
| 3b4fa6f59c | |||
| 811ac8aab8 | |||
| 50859e7b81 | |||
| bab3b16795 | |||
| 590dfce426 | |||
| 70bc518bb8 | |||
| 3aefcbbe8c | |||
| fbefdd1490 | |||
| cf28213223 | |||
| f2aae03434 | |||
| 122a7ae43b | |||
| 81495ef1fd | |||
| 1735247897 | |||
| 21e342fc80 | |||
| b6e93df693 | |||
| 5ec19e70dc | |||
| bb86490614 | |||
| cc81f9e137 | |||
| cf1ca0678b | |||
| 3c5561dd70 | |||
| 0881ece722 | |||
| 30f69ec758 | |||
| c673e984b0 | |||
| 26b8e59066 | |||
| b8b0e75513 | |||
| 472ccc3929 | |||
| b16ed1de3b | |||
| 167f9216be | |||
| 1a9de428cb | |||
| eaeaf4ab3d | |||
| cf24fe9cbf | |||
| d351840a70 | |||
| 3b71f2a74a | |||
| 78c6c77c00 | |||
| 46582601ea | |||
| cdb4156702 | |||
| 642653a37f |
File diff suppressed because one or more lines are too long
@@ -28,7 +28,7 @@ try {
|
||||
let subprocesses = [];
|
||||
tests.forEach(t => {
|
||||
subprocesses.push(new Promise(resolve => {
|
||||
child_process.spawn("docker", ["run", "--name", t, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t}$'`], {stdio:"inherit"}).on('close', code => resolve(code));
|
||||
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t}$'`], {stdio:"inherit"}).on('close', code => resolve(code));
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -120,7 +120,9 @@ jobs:
|
||||
matrix:
|
||||
platform: [ubuntu20, ubuntu22]
|
||||
runs-on: ["self-hosted", "enf-x86-hightier"]
|
||||
container: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
container:
|
||||
image: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download builddir
|
||||
@@ -157,7 +159,7 @@ jobs:
|
||||
uses: ./.github/actions/parallel-ctest-containers
|
||||
with:
|
||||
container: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
error-log-paths: '["build/etc", "build/var", "build/leap-ignition-wd"]'
|
||||
error-log-paths: '["build/etc", "build/var", "build/leap-ignition-wd", "build/TestLogs"]'
|
||||
log-tarball-prefix: ${{matrix.platform}}
|
||||
tests-label: nonparallelizable_tests
|
||||
- name: Upload logs from failed tests
|
||||
@@ -186,7 +188,7 @@ jobs:
|
||||
uses: ./.github/actions/parallel-ctest-containers
|
||||
with:
|
||||
container: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
error-log-paths: '["build/etc", "build/var", "build/leap-ignition-wd"]'
|
||||
error-log-paths: '["build/etc", "build/var", "build/leap-ignition-wd", "build/TestLogs"]'
|
||||
log-tarball-prefix: ${{matrix.platform}}
|
||||
tests-label: long_running_tests
|
||||
- name: Upload logs from failed tests
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ deps/*
|
||||
dependencies/*
|
||||
|
||||
etc/eosio/node_*
|
||||
var/lib/node_*
|
||||
TestLogs*
|
||||
.vscode
|
||||
.idea/
|
||||
*.iws
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
[submodule "libraries/libfc/secp256k1/secp256k1"]
|
||||
path = libraries/libfc/secp256k1/secp256k1
|
||||
url = https://github.com/bitcoin-core/secp256k1
|
||||
[submodule "libraries/prometheus/prometheus-cpp"]
|
||||
path = libraries/prometheus/prometheus-cpp
|
||||
url = https://github.com/jupp0r/prometheus-cpp.git
|
||||
[submodule "libraries/libfc/libraries/bn256"]
|
||||
path = libraries/libfc/libraries/bn256
|
||||
url = https://github.com/AntelopeIO/bn256
|
||||
|
||||
+70
-5
@@ -77,9 +77,29 @@ if(ENABLE_OC AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT WIN32)
|
||||
list(APPEND EOSIO_WASM_RUNTIMES eos-vm-oc)
|
||||
# EOS VM OC requires LLVM, but move the check up here to a central location so that the EosioTester.cmakes
|
||||
# can be created with the exact version found
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
# find_package VERSION range is only available in 3.19 and newer.
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.19")
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
else()
|
||||
# Preferring the latest LLVM version, search through major 11..7 and minor 9..0 in reverse order.
|
||||
# Minor versions may break API. This manifests in the LLVM config files such that a range of `11...<12`
|
||||
# will NOT find LLVM `11.1`. We loop over minor as well as major to resolve this.
|
||||
set(max_minor 9)
|
||||
set(major 11)
|
||||
set(minor ${max_minor})
|
||||
while(NOT LLVM_FOUND AND major GREATER_EQUAL 7)
|
||||
math(EXPR end "${major} + 1")
|
||||
find_package(LLVM ${major}.${minor}...<${end} CONFIG QUIET)
|
||||
if(minor GREATER 0)
|
||||
math(EXPR minor "${minor} - 1")
|
||||
else()
|
||||
math(EXPR major "${major} - 1")
|
||||
set(minor ${max_minor})
|
||||
endif()
|
||||
endwhile()
|
||||
endif()
|
||||
if(LLVM_VERSION_MAJOR VERSION_LESS 7 OR LLVM_VERSION_MAJOR VERSION_GREATER_EQUAL 12)
|
||||
message(FATAL_ERROR "Leap requires an LLVM version 7 through 11")
|
||||
message(FATAL_ERROR "Leap requires an LLVM version 7 through 11")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -200,9 +220,6 @@ add_subdirectory( benchmark )
|
||||
|
||||
option(DISABLE_WASM_SPEC_TESTS "disable building of wasm spec unit tests" OFF)
|
||||
|
||||
install(FILES testnet.template DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/eosio/launcher COMPONENT dev EXCLUDE_FROM_ALL)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testnet.template ${CMAKE_CURRENT_BINARY_DIR}/etc/eosio/launcher/testnet.template COPYONLY)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/eosio.version.in ${CMAKE_CURRENT_BINARY_DIR}/eosio.version.hpp)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/eosio.version.hpp DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR} COMPONENT dev EXCLUDE_FROM_ALL)
|
||||
|
||||
@@ -235,10 +252,58 @@ configure_file(libraries/wasm-jit/LICENSE licen
|
||||
configure_file(libraries/libfc/secp256k1/secp256k1/COPYING licenses/leap/LICENSE.secp256k1 COPYONLY)
|
||||
configure_file(libraries/libfc/include/fc/crypto/webauthn_json/license.txt licenses/leap/LICENSE.rapidjson COPYONLY)
|
||||
configure_file(libraries/eos-vm/LICENSE licenses/leap/LICENSE.eos-vm COPYONLY)
|
||||
configure_file(libraries/prometheus/prometheus-cpp/LICENSE licenses/leap/LICENSE.prom COPYONLY)
|
||||
configure_file(programs/cleos/LICENSE.CLI11 licenses/leap/LICENSE.CLI11 COPYONLY)
|
||||
|
||||
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/licenses/leap" DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/licenses/" COMPONENT base)
|
||||
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libraries/testing/contracts DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/leap_testing/libraries/testing COMPONENT dev EXCLUDE_FROM_ALL
|
||||
PATTERN "CMakeFiles" EXCLUDE
|
||||
PATTERN "*.cmake" EXCLUDE
|
||||
PATTERN "Makefile" EXCLUDE)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/unittests/contracts DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/leap_testing/unittests COMPONENT dev EXCLUDE_FROM_ALL
|
||||
PATTERN "CMakeFiles" EXCLUDE
|
||||
PATTERN "*.cmake" EXCLUDE
|
||||
PATTERN "Makefile" EXCLUDE)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests/TestHarness DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/leap_testing/tests COMPONENT dev EXCLUDE_FROM_ALL
|
||||
FILES_MATCHING
|
||||
PATTERN "*.py"
|
||||
PATTERN "*.json"
|
||||
PATTERN "__pycache__" EXCLUDE
|
||||
PATTERN "CMakeFiles" EXCLUDE)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/tests/launcher.py DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/leap_testing/tests COMPONENT dev EXCLUDE_FROM_ALL)
|
||||
|
||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21)
|
||||
# Cmake versions < 3.21 did not support installing symbolic links to a directory via install(FILES ...)
|
||||
# Cmake 3.21 fixed this (https://gitlab.kitware.com/cmake/cmake/-/commit/d71a7cc19d6e03f89581efdaa8d63db216184d40)
|
||||
# If/when the lowest cmake version supported becomes >= 3.21, the else block as well as the postinit and prerm scripts
|
||||
# would be a good option to remove in favor of using the facilities in cmake / cpack directly.
|
||||
|
||||
add_custom_target(link_target ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory lib/python3/dist-packages
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ../../../share/leap_testing/tests/TestHarness lib/python3/dist-packages/TestHarness
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory share/leap_testing
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ../../bin share/leap_testing/bin)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib/python3/dist-packages/TestHarness DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/python3/dist-packages COMPONENT dev EXCLUDE_FROM_ALL)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/share/leap_testing/bin DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/leap_testing COMPONENT dev EXCLUDE_FROM_ALL)
|
||||
else()
|
||||
# The following install(CODE ...) steps are necessary for `make dev-install` to work on cmake versions < 3.21.
|
||||
# However, it can flag as an error if using `make package` instead of `make dev-install` for installation.
|
||||
# This is due to those directories and symbolic links being created in the postinit script during the package install instead.
|
||||
|
||||
# Note If/when doing `make package`, it is not a true error if you see:
|
||||
# Error creating directory "/<leap_install_dir>/lib/python3/dist-packages".
|
||||
# CMake Error: failed to create symbolic link '/<leap_install_dir>/lib/python3/dist-packages/TestHarness': no such file or directory
|
||||
# CMake Error: failed to create symbolic link '/<leap_install_dir>/share/leap_testing/bin': no such file or directory
|
||||
|
||||
install(CODE "execute_process( COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_INSTALL_FULL_LIBDIR}/python3/dist-packages)" COMPONENT dev EXCLUDE_FROM_ALL)
|
||||
install(CODE "execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink ../../../share/leap_testing/tests/TestHarness ${CMAKE_INSTALL_FULL_LIBDIR}/python3/dist-packages/TestHarness)" COMPONENT dev EXCLUDE_FROM_ALL)
|
||||
install(CODE "execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink ../../bin ${CMAKE_INSTALL_FULL_DATAROOTDIR}/leap_testing/bin)" COMPONENT dev EXCLUDE_FROM_ALL)
|
||||
|
||||
# The `make package` installation of symlinks happens via the `postinst` script installed in cmake.package via the line below
|
||||
endif()
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/libraries/cli11/bash-completion/completions/leap-util
|
||||
${CMAKE_BINARY_DIR}/programs/leap-util/bash-completion/completions/leap-util COPYONLY)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/libraries/cli11/bash-completion/completions/cleos
|
||||
|
||||
@@ -55,6 +55,7 @@ Requirements to build:
|
||||
- git
|
||||
- GMP
|
||||
- Python 3
|
||||
- python3-numpy
|
||||
- zlib
|
||||
|
||||
### Step 1 - Clone
|
||||
@@ -145,9 +146,11 @@ To build, make sure you are in the root of the `leap` repo, then run the followi
|
||||
```bash
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/lib/llvm-11 ..
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
make -j "$(nproc)" package
|
||||
```
|
||||
|
||||
If CMake fails to find a valid llvm configure file, it may be necessary to add `-DCMAKE_PREFIX_PATH=/usr/lib/llvm-11` to the cmake command.
|
||||
</details>
|
||||
|
||||
<details> <summary>Ubuntu 18.04 Bionic</summary>
|
||||
|
||||
@@ -143,7 +143,7 @@ We now have an account that is available to have a contract assigned to it, enab
|
||||
In the fourth terminal window, start a second `nodeos` instance. Notice that this command line is substantially longer than the one we used above to create the first producer. This is necessary to avoid collisions with the first `nodeos` instance. Fortunately, you can just cut and paste this command line and adjust the keys:
|
||||
|
||||
```sh
|
||||
nodeos --producer-name inita --plugin eosio::chain_api_plugin --plugin eosio::net_api_plugin --http-server-address 127.0.0.1:8889 --p2p-listen-endpoint 127.0.0.1:9877 --p2p-peer-address 127.0.0.1:9876 --config-dir node2 --data-dir node2 --private-key [\"EOS6hMjoWRF2L8x9YpeqtUEcsDKAyxSuM1APicxgRU1E3oyV5sDEg\",\"5JgbL2ZnoEAhTudReWH1RnMuQS6DBeLZt4ucV6t8aymVEuYg7sr\"]
|
||||
nodeos --producer-name inita --plugin eosio::chain_api_plugin --plugin eosio::net_api_plugin --http-server-address 127.0.0.1:8889 --p2p-listen-endpoint 127.0.0.1:9877 --p2p-peer-address 127.0.0.1:9876 --config-dir node2 --data-dir node2 --signature-provider EOS6hMjoWRF2L8x9YpeqtUEcsDKAyxSuM1APicxgRU1E3oyV5sDEg=KEY:5JgbL2ZnoEAhTudReWH1RnMuQS6DBeLZt4ucV6t8aymVEuYg7sr
|
||||
```
|
||||
|
||||
The output from this new node will show a little activity but will stop reporting until the last step in this tutorial, when the `inita` account is registered as a producer account and activated. Here is some example output from a newly started node. Your output might look a little different, depending on how much time you took entering each of these commands. Furthermore, this example is only the last few lines of output:
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
## Description
|
||||
|
||||
The `http_client_plugin` is an internal utility plugin, providing the `producer_plugin` the ability to use securely an external `keosd` instance as its block signer. It can only be used when the `producer_plugin` is configured to produce blocks.
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
# config.ini
|
||||
plugin = eosio::http_client_plugin
|
||||
https-client-root-cert = "path/to/my/certificate.pem"
|
||||
https-client-validate-peers = 1
|
||||
```
|
||||
```sh
|
||||
# command-line
|
||||
nodeos ... --plugin eosio::http_client_plugin \
|
||||
--https-client-root-cert "path/to/my/certificate.pem" \
|
||||
--https-client-validate-peers 1
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
These can be specified from both the `nodeos` command-line or the `config.ini` file:
|
||||
|
||||
```console
|
||||
Config Options for eosio::http_client_plugin:
|
||||
--https-client-root-cert arg PEM encoded trusted root certificate
|
||||
(or path to file containing one) used
|
||||
to validate any TLS connections made.
|
||||
(may specify multiple times)
|
||||
|
||||
--https-client-validate-peers arg (=1)
|
||||
true: validate that the peer
|
||||
certificates are valid and trusted,
|
||||
false: ignore cert errors
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
* [`producer_plugin`](../producer_plugin/index.md)
|
||||
@@ -29,16 +29,6 @@ Config Options for eosio::http_plugin:
|
||||
The local IP and port to listen for
|
||||
incoming http connections; set blank to
|
||||
disable.
|
||||
--https-server-address arg The local IP and port to listen for
|
||||
incoming https connections; leave blank
|
||||
to disable.
|
||||
--https-certificate-chain-file arg Filename with the certificate chain to
|
||||
present on https connections. PEM
|
||||
format. Required for https.
|
||||
--https-private-key-file arg Filename with https private key in PEM
|
||||
format. Required for https
|
||||
--https-ecdh-curve arg (=secp384r1) Configure https ECDH curve to use:
|
||||
secp384r1 or prime256v1
|
||||
--access-control-allow-origin arg Specify the Access-Control-Allow-Origin
|
||||
to be returned on each request
|
||||
--access-control-allow-headers arg Specify the Access-Control-Allow-Header
|
||||
|
||||
@@ -11,7 +11,6 @@ For information on specific plugins, just select from the list below:
|
||||
* [`chain_api_plugin`](chain_api_plugin/index.md)
|
||||
* [`chain_plugin`](chain_plugin/index.md)
|
||||
* [`db_size_api_plugin`](db_size_api_plugin/index.md)
|
||||
* [`http_client_plugin`](http_client_plugin/index.md)
|
||||
* [`http_plugin`](http_plugin/index.md)
|
||||
* [`net_api_plugin`](net_api_plugin/index.md)
|
||||
* [`net_plugin`](net_plugin/index.md)
|
||||
|
||||
@@ -39,10 +39,6 @@ Config Options for eosio::producer_plugin:
|
||||
-p [ --producer-name ] arg ID of producer controlled by this node
|
||||
(e.g. inita; may specify multiple
|
||||
times)
|
||||
--private-key arg (DEPRECATED - Use signature-provider
|
||||
instead) Tuple of [public key, WIF
|
||||
private key] (may specify multiple
|
||||
times)
|
||||
--signature-provider arg (=EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV=KEY:5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3)
|
||||
Key=Value pairs in the form
|
||||
<public-key>=<provider-spec>
|
||||
|
||||
@@ -81,7 +81,7 @@ Sample `logging.json`:
|
||||
- Answers the question: is this useful information for a user that is monitoring the log output. Care should be taken not to create excessive log output; similar to `info` level logging.
|
||||
- Enabling `debug` level logging should provide greater insight into behavior without overwhelming the output with log entries.
|
||||
- `debug` level should not be used for *trace* level logging; to that end, use `all` (see below).
|
||||
- Like `info`, no `debug` level logging should be produced for every transaction. There are specific transaction level loggers dedicated to transaction level logging: `transaction`, `transaction_trace_failure`, `transaction_trace_success`, `transaction_failure_tracing`, `transaction_success_tracing`.
|
||||
- Like `info`, no `debug` level logging should be produced for every transaction. There are specific transaction level loggers dedicated to transaction level logging: `transaction`, `transaction_trace_failure`, `transaction_trace_success`, `transaction_failure_tracing`, `transaction_success_tracing`, `transient_trx_success_tracing`, `transient_trx_failure_tracing`.
|
||||
* `all` (trace) - For logging that would be overwhelming if `debug` level logging were used.
|
||||
- Can be used for trace level logging. Only used in a few places and not completely supported.
|
||||
- *Note*: In the future a different logging library may provide better trace level logging support. The current logging framework is not performant enough to enable excess trace level output.
|
||||
|
||||
@@ -44,16 +44,6 @@ Config Options for eosio::http_plugin:
|
||||
--http-server-address arg The local IP and port to listen for
|
||||
incoming http connections; leave blank
|
||||
to disable.
|
||||
--https-server-address arg The local IP and port to listen for
|
||||
incoming https connections; leave blank
|
||||
to disable.
|
||||
--https-certificate-chain-file arg Filename with the certificate chain to
|
||||
present on https connections. PEM
|
||||
format. Required for https.
|
||||
--https-private-key-file arg Filename with https private key in PEM
|
||||
format. Required for https
|
||||
--https-ecdh-curve arg (=secp384r1) Configure https ECDH curve to use:
|
||||
secp384r1 or prime256v1
|
||||
--access-control-allow-origin arg Specify the Access-Control-Allow-Origin
|
||||
to be returned on each request.
|
||||
--access-control-allow-headers arg Specify the Access-Control-Allow-Header
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
content_title: eosio-blocklog
|
||||
link_text: eosio-blocklog
|
||||
---
|
||||
|
||||
`eosio-blocklog` is a command-line interface (CLI) utility that allows node operators to perform low-level tasks on the block logs created by a `nodeos` instance. `eosio-blocklog` can perform one of the following operations:
|
||||
|
||||
* Convert a range of blocks to JSON format, as single objects or array.
|
||||
* Generate `blocks.index` from `blocks.log` in blocks directory.
|
||||
* Trim `blocks.log` and `blocks.index` between a range of blocks.
|
||||
* Perform consistency test between `blocks.log` and `blocks.index`.
|
||||
* Output the results of the operation to a file or `stdout` (default).
|
||||
|
||||
## Usage
|
||||
```sh
|
||||
eosio-blocklog <options> ...
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
Option (=default) | Description
|
||||
-|-
|
||||
`--blocks-dir arg (="blocks")` | The location of the blocks directory (absolute path or relative to the current directory)
|
||||
`-o [ --output-file ] arg` | The file to write the generated output to (absolute or relative path). If not specified then output is to `stdout`
|
||||
`-f [ --first ] arg (=0)` | The first block number to log or the first block to keep if `trim-blocklog` specified
|
||||
`-l [ --last ] arg (=4294967295)` | the last block number to log or the last block to keep if `trim-blocklog` specified
|
||||
`--no-pretty-print` | Do not pretty print the output. Useful if piping to `jq` to improve performance
|
||||
`--as-json-array` | Print out JSON blocks wrapped in JSON array (otherwise the output is free-standing JSON objects)
|
||||
`--make-index` | Create `blocks.index` from `blocks.log`. Must give `blocks-dir` location. Give `output-file` relative to current directory or absolute path (default is `<blocks-dir>/blocks.index`)
|
||||
`--trim-blocklog` | Trim `blocks.log` and `blocks.index`. Must give `blocks-dir` and `first` and/or `last` options.
|
||||
`--smoke-test` | Quick test that `blocks.log` and `blocks.index` are well formed and agree with each other
|
||||
`-h [ --help ]` | Print this help message and exit
|
||||
|
||||
## Remarks
|
||||
|
||||
When `eosio-blocklog` is launched, the utility attempts to perform the specified operation, then yields the following possible outcomes:
|
||||
* If successful, the selected operation is performed and the utility terminates with a zero error code (no error).
|
||||
* If unsuccessful, the utility outputs an error to `stderr` and terminates with a non-zero error code (indicating an error).
|
||||
@@ -5,5 +5,4 @@ link_text: Antelope Utilities
|
||||
|
||||
This section contains documentation for additional utilities that complement or extend `nodeos` and potentially other Antelope software:
|
||||
|
||||
* [eosio-blocklog](eosio-blocklog.md) - Low-level utility for node operators to interact with block log files.
|
||||
* [trace_api_util](trace_api_util.md) - Low-level utility for performing tasks associated with the [Trace API](../01_nodeos/03_plugins/trace_api_plugin/index.md).
|
||||
|
||||
@@ -12,6 +12,7 @@ add_subdirectory( wasm-jit )
|
||||
add_subdirectory( chainbase )
|
||||
set(APPBASE_ENABLE_AUTO_VERSION OFF CACHE BOOL "enable automatic discovery of version via 'git describe'")
|
||||
add_subdirectory( appbase )
|
||||
add_subdirectory( custom_appbase )
|
||||
add_subdirectory( chain )
|
||||
add_subdirectory( testing )
|
||||
add_subdirectory( version )
|
||||
@@ -27,3 +28,4 @@ set(ENABLE_PROFILE OFF CACHE BOOL "Enable for profile builds")
|
||||
if(eos-vm IN_LIST EOSIO_WASM_RUNTIMES OR eos-vm-jit IN_LIST EOSIO_WASM_RUNTIMES)
|
||||
add_subdirectory( eos-vm )
|
||||
endif()
|
||||
add_subdirectory( prometheus )
|
||||
|
||||
+1
-1
Submodule libraries/appbase updated: 78062207ea...fe1e8ae173
@@ -70,9 +70,9 @@ namespace eosio { namespace chain {
|
||||
);
|
||||
}
|
||||
|
||||
abi_serializer::abi_serializer( const abi_def& abi, const yield_function_t& yield ) {
|
||||
abi_serializer::abi_serializer( abi_def abi, const yield_function_t& yield ) {
|
||||
configure_built_in_types();
|
||||
set_abi(abi, yield);
|
||||
set_abi(std::move(abi), yield);
|
||||
}
|
||||
|
||||
abi_serializer::abi_serializer( const abi_def& abi, const fc::microseconds& max_serialization_time) {
|
||||
@@ -128,11 +128,19 @@ namespace eosio { namespace chain {
|
||||
built_in_types.emplace("extended_asset", pack_unpack<extended_asset>());
|
||||
}
|
||||
|
||||
void abi_serializer::set_abi(const abi_def& abi, const yield_function_t& yield) {
|
||||
void abi_serializer::set_abi(abi_def abi, const yield_function_t& yield) {
|
||||
impl::abi_traverse_context ctx(yield);
|
||||
|
||||
EOS_ASSERT(starts_with(abi.version, "eosio::abi/1."), unsupported_abi_version_exception, "ABI has an unsupported version");
|
||||
|
||||
size_t types_size = abi.types.size();
|
||||
size_t structs_size = abi.structs.size();
|
||||
size_t actions_size = abi.actions.size();
|
||||
size_t tables_size = abi.tables.size();
|
||||
size_t error_messages_size = abi.error_messages.size();
|
||||
size_t variants_size = abi.variants.value.size();
|
||||
size_t action_results_size = abi.action_results.value.size();
|
||||
|
||||
typedefs.clear();
|
||||
structs.clear();
|
||||
actions.clear();
|
||||
@@ -141,41 +149,41 @@ namespace eosio { namespace chain {
|
||||
variants.clear();
|
||||
action_results.clear();
|
||||
|
||||
for( const auto& st : abi.structs )
|
||||
structs[st.name] = st;
|
||||
for( auto& st : abi.structs )
|
||||
structs[st.name] = std::move(st);
|
||||
|
||||
for( const auto& td : abi.types ) {
|
||||
for( auto& td : abi.types ) {
|
||||
EOS_ASSERT(!_is_type(td.new_type_name, ctx), duplicate_abi_type_def_exception,
|
||||
"type already exists", ("new_type_name",impl::limit_size(td.new_type_name)));
|
||||
typedefs[td.new_type_name] = td.type;
|
||||
typedefs[std::move(td.new_type_name)] = std::move(td.type);
|
||||
}
|
||||
|
||||
for( const auto& a : abi.actions )
|
||||
actions[a.name] = a.type;
|
||||
for( auto& a : abi.actions )
|
||||
actions[std::move(a.name)] = std::move(a.type);
|
||||
|
||||
for( const auto& t : abi.tables )
|
||||
tables[t.name] = t.type;
|
||||
for( auto& t : abi.tables )
|
||||
tables[std::move(t.name)] = std::move(t.type);
|
||||
|
||||
for( const auto& e : abi.error_messages )
|
||||
error_messages[e.error_code] = e.error_msg;
|
||||
for( auto& e : abi.error_messages )
|
||||
error_messages[std::move(e.error_code)] = std::move(e.error_msg);
|
||||
|
||||
for( const auto& v : abi.variants.value )
|
||||
variants[v.name] = v;
|
||||
for( auto& v : abi.variants.value )
|
||||
variants[v.name] = std::move(v);
|
||||
|
||||
for( const auto& r : abi.action_results.value )
|
||||
action_results[r.name] = r.result_type;
|
||||
for( auto& r : abi.action_results.value )
|
||||
action_results[std::move(r.name)] = std::move(r.result_type);
|
||||
|
||||
/**
|
||||
* The ABI vector may contain duplicates which would make it
|
||||
* an invalid ABI
|
||||
*/
|
||||
EOS_ASSERT( typedefs.size() == abi.types.size(), duplicate_abi_type_def_exception, "duplicate type definition detected" );
|
||||
EOS_ASSERT( structs.size() == abi.structs.size(), duplicate_abi_struct_def_exception, "duplicate struct definition detected" );
|
||||
EOS_ASSERT( actions.size() == abi.actions.size(), duplicate_abi_action_def_exception, "duplicate action definition detected" );
|
||||
EOS_ASSERT( tables.size() == abi.tables.size(), duplicate_abi_table_def_exception, "duplicate table definition detected" );
|
||||
EOS_ASSERT( error_messages.size() == abi.error_messages.size(), duplicate_abi_err_msg_def_exception, "duplicate error message definition detected" );
|
||||
EOS_ASSERT( variants.size() == abi.variants.value.size(), duplicate_abi_variant_def_exception, "duplicate variant definition detected" );
|
||||
EOS_ASSERT( action_results.size() == abi.action_results.value.size(), duplicate_abi_action_results_def_exception, "duplicate action results definition detected" );
|
||||
EOS_ASSERT( typedefs.size() == types_size, duplicate_abi_type_def_exception, "duplicate type definition detected" );
|
||||
EOS_ASSERT( structs.size() == structs_size, duplicate_abi_struct_def_exception, "duplicate struct definition detected" );
|
||||
EOS_ASSERT( actions.size() == actions_size, duplicate_abi_action_def_exception, "duplicate action definition detected" );
|
||||
EOS_ASSERT( tables.size() == tables_size, duplicate_abi_table_def_exception, "duplicate table definition detected" );
|
||||
EOS_ASSERT( error_messages.size() == error_messages_size, duplicate_abi_err_msg_def_exception, "duplicate error message definition detected" );
|
||||
EOS_ASSERT( variants.size() == variants_size, duplicate_abi_variant_def_exception, "duplicate variant definition detected" );
|
||||
EOS_ASSERT( action_results.size() == action_results_size, duplicate_abi_action_results_def_exception, "duplicate action results definition detected" );
|
||||
|
||||
validate(ctx);
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ void apply_context::exec_one()
|
||||
print_debug(receiver, trace);
|
||||
}
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger())
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient()))
|
||||
{
|
||||
dm_logger->on_end_action();
|
||||
}
|
||||
@@ -289,7 +289,7 @@ void apply_context::require_recipient( account_name recipient ) {
|
||||
schedule_action( action_ordinal, recipient, false )
|
||||
);
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_require_recipient();
|
||||
}
|
||||
}
|
||||
@@ -354,7 +354,7 @@ void apply_context::execute_inline( action&& a ) {
|
||||
"inline action too big for nonprivileged account ${account}", ("account", a.account));
|
||||
}
|
||||
// No need to check authorization if replaying irreversible blocks or contract is privileged
|
||||
if( !control.skip_auth_check() && !privileged ) {
|
||||
if( !control.skip_auth_check() && !privileged && !trx_context.is_read_only() ) {
|
||||
try {
|
||||
control.get_authorization_manager()
|
||||
.check_authorization( {a},
|
||||
@@ -363,7 +363,7 @@ void apply_context::execute_inline( action&& a ) {
|
||||
control.pending_block_time() - trx_context.published,
|
||||
std::bind(&transaction_context::checktime, &this->trx_context),
|
||||
false,
|
||||
trx_context.is_dry_run(),
|
||||
trx_context.is_dry_run(), // check_but_dont_fail
|
||||
inherited_authorizations
|
||||
);
|
||||
|
||||
@@ -394,7 +394,7 @@ void apply_context::execute_inline( action&& a ) {
|
||||
schedule_action( std::move(a), inline_receiver, false )
|
||||
);
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_send_inline();
|
||||
}
|
||||
}
|
||||
@@ -419,13 +419,14 @@ void apply_context::execute_context_free_inline( action&& a ) {
|
||||
schedule_action( std::move(a), inline_receiver, true )
|
||||
);
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_send_context_free_inline();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction&& trx, bool replace_existing ) {
|
||||
EOS_ASSERT( !trx_context.is_read_only(), transaction_exception, "cannot schedule a deferred transaction from within a readonly transaction" );
|
||||
EOS_ASSERT( trx.context_free_actions.size() == 0, cfa_inside_generated_tx, "context free actions are not currently allowed in generated transactions" );
|
||||
|
||||
bool enforce_actor_whitelist_blacklist = trx_context.enforce_whiteblacklist && control.is_speculative_block()
|
||||
@@ -557,7 +558,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
|
||||
subjective_block_production_exception,
|
||||
"Replacing a deferred transaction is temporarily disabled." );
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", ptr->id)), "deferred_trx", "cancel", "deferred_trx_cancel");
|
||||
}
|
||||
|
||||
@@ -575,7 +576,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
|
||||
trx_id_for_new_obj = ptr->trx_id;
|
||||
}
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_cancel_deferred(deep_mind_handler::operation_qualifier::modify, *ptr);
|
||||
}
|
||||
|
||||
@@ -592,7 +593,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
|
||||
|
||||
trx_size = gtx.set( trx );
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_send_deferred(deep_mind_handler::operation_qualifier::modify, gtx);
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", gtx.id)), "deferred_trx", "update", "deferred_trx_add");
|
||||
}
|
||||
@@ -609,7 +610,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
|
||||
|
||||
trx_size = gtx.set( trx );
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_send_deferred(deep_mind_handler::operation_qualifier::none, gtx);
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", gtx.id)), "deferred_trx", "add", "deferred_trx_add");
|
||||
}
|
||||
@@ -626,10 +627,11 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
|
||||
}
|
||||
|
||||
bool apply_context::cancel_deferred_transaction( const uint128_t& sender_id, account_name sender ) {
|
||||
EOS_ASSERT( !trx_context.is_read_only(), transaction_exception, "cannot cancel a deferred transaction from within a readonly transaction" );
|
||||
auto& generated_transaction_idx = db.get_mutable_index<generated_transaction_multi_index>();
|
||||
const auto* gto = db.find<generated_transaction_object,by_sender_id>(boost::make_tuple(sender, sender_id));
|
||||
if ( gto ) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_cancel_deferred(deep_mind_handler::operation_qualifier::none, *gto);
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", gto->id)), "deferred_trx", "cancel", "deferred_trx_cancel");
|
||||
}
|
||||
@@ -670,7 +672,7 @@ const table_id_object& apply_context::find_or_create_table( name code, name scop
|
||||
return *existing_tid;
|
||||
}
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
std::string event_id = RAM_EVENT_ID("${code}:${scope}:${table}",
|
||||
("code", code)
|
||||
("scope", scope)
|
||||
@@ -687,14 +689,14 @@ const table_id_object& apply_context::find_or_create_table( name code, name scop
|
||||
t_id.table = table;
|
||||
t_id.payer = payer;
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_create_table(t_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void apply_context::remove_table( const table_id_object& tid ) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
std::string event_id = RAM_EVENT_ID("${code}:${scope}:${table}",
|
||||
("code", tid.code)
|
||||
("scope", tid.scope)
|
||||
@@ -705,7 +707,7 @@ void apply_context::remove_table( const table_id_object& tid ) {
|
||||
|
||||
update_db_usage(tid.payer, - config::billable_size_v<table_id_object>);
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_remove_table(tid);
|
||||
}
|
||||
|
||||
@@ -778,11 +780,13 @@ int apply_context::get_context_free_data( uint32_t index, char* buffer, size_t b
|
||||
}
|
||||
|
||||
int apply_context::db_store_i64( name scope, name table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size ) {
|
||||
EOS_ASSERT( !trx_context.is_read_only(), table_operation_not_permitted, "cannot store a db record when executing a readonly transaction" );
|
||||
return db_store_i64( receiver, scope, table, payer, id, buffer, buffer_size);
|
||||
}
|
||||
|
||||
int apply_context::db_store_i64( name code, name scope, name table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size ) {
|
||||
// require_write_lock( scope );
|
||||
EOS_ASSERT( !trx_context.is_read_only(), table_operation_not_permitted, "cannot store a db record when executing a readonly transaction" );
|
||||
const auto& tab = find_or_create_table( code, scope, table, payer );
|
||||
auto tableid = tab.id;
|
||||
|
||||
@@ -801,7 +805,7 @@ int apply_context::db_store_i64( name code, name scope, name table, const accoun
|
||||
|
||||
int64_t billable_size = (int64_t)(buffer_size + config::billable_size_v<key_value_object>);
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
std::string event_id = RAM_EVENT_ID("${table_code}:${scope}:${table_name}:${primkey}",
|
||||
("table_code", tab.code)
|
||||
("scope", tab.scope)
|
||||
@@ -813,7 +817,7 @@ int apply_context::db_store_i64( name code, name scope, name table, const accoun
|
||||
|
||||
update_db_usage( payer, billable_size);
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_db_store_i64(tab, obj);
|
||||
}
|
||||
|
||||
@@ -822,6 +826,7 @@ int apply_context::db_store_i64( name code, name scope, name table, const accoun
|
||||
}
|
||||
|
||||
void apply_context::db_update_i64( int iterator, account_name payer, const char* buffer, size_t buffer_size ) {
|
||||
EOS_ASSERT( !trx_context.is_read_only(), table_operation_not_permitted, "cannot update a db record when executing a readonly transaction" );
|
||||
const key_value_object& obj = keyval_cache.get( iterator );
|
||||
|
||||
const auto& table_obj = keyval_cache.get_table( obj.t_id );
|
||||
@@ -836,7 +841,7 @@ void apply_context::db_update_i64( int iterator, account_name payer, const char*
|
||||
if( payer == account_name() ) payer = obj.payer;
|
||||
|
||||
std::string event_id;
|
||||
if (control.get_deep_mind_logger() != nullptr) {
|
||||
if (control.get_deep_mind_logger(trx_context.is_transient()) != nullptr) {
|
||||
event_id = RAM_EVENT_ID("${table_code}:${scope}:${table_name}:${primkey}",
|
||||
("table_code", table_obj.code)
|
||||
("scope", table_obj.scope)
|
||||
@@ -847,27 +852,27 @@ void apply_context::db_update_i64( int iterator, account_name payer, const char*
|
||||
|
||||
if( account_name(obj.payer) != payer ) {
|
||||
// refund the existing payer
|
||||
if (auto dm_logger = control.get_deep_mind_logger())
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient()))
|
||||
{
|
||||
dm_logger->on_ram_trace(std::string(event_id), "table_row", "remove", "primary_index_update_remove_old_payer");
|
||||
}
|
||||
update_db_usage( obj.payer, -(old_size) );
|
||||
// charge the new payer
|
||||
if (auto dm_logger = control.get_deep_mind_logger())
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient()))
|
||||
{
|
||||
dm_logger->on_ram_trace(std::move(event_id), "table_row", "add", "primary_index_update_add_new_payer");
|
||||
}
|
||||
update_db_usage( payer, (new_size));
|
||||
} else if(old_size != new_size) {
|
||||
// charge/refund the existing payer the difference
|
||||
if (auto dm_logger = control.get_deep_mind_logger())
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient()))
|
||||
{
|
||||
dm_logger->on_ram_trace(std::move(event_id) , "table_row", "update", "primary_index_update");
|
||||
}
|
||||
update_db_usage( obj.payer, new_size - old_size);
|
||||
}
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_db_update_i64(table_obj, obj, payer, buffer, buffer_size);
|
||||
}
|
||||
|
||||
@@ -878,6 +883,7 @@ void apply_context::db_update_i64( int iterator, account_name payer, const char*
|
||||
}
|
||||
|
||||
void apply_context::db_remove_i64( int iterator ) {
|
||||
EOS_ASSERT( !trx_context.is_read_only(), table_operation_not_permitted, "cannot remove a db record when executing a readonly transaction" );
|
||||
const key_value_object& obj = keyval_cache.get( iterator );
|
||||
|
||||
const auto& table_obj = keyval_cache.get_table( obj.t_id );
|
||||
@@ -885,7 +891,7 @@ void apply_context::db_remove_i64( int iterator ) {
|
||||
|
||||
// require_write_lock( table_obj.scope );
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
std::string event_id = RAM_EVENT_ID("${table_code}:${scope}:${table_name}:${primkey}",
|
||||
("table_code", table_obj.code)
|
||||
("scope", table_obj.scope)
|
||||
@@ -897,7 +903,7 @@ void apply_context::db_remove_i64( int iterator ) {
|
||||
|
||||
update_db_usage( obj.payer, -(obj.value.size() + config::billable_size_v<key_value_object>) );
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_db_remove_i64(table_obj, obj);
|
||||
}
|
||||
|
||||
@@ -1029,17 +1035,27 @@ int apply_context::db_end_i64( name code, name scope, name table ) {
|
||||
|
||||
uint64_t apply_context::next_global_sequence() {
|
||||
const auto& p = control.get_dynamic_global_properties();
|
||||
db.modify( p, [&]( auto& dgp ) {
|
||||
++dgp.global_action_sequence;
|
||||
});
|
||||
return p.global_action_sequence;
|
||||
if ( trx_context.is_read_only() ) {
|
||||
// To avoid confusion of duplicated global sequence number, hard code to be 0.
|
||||
return 0;
|
||||
} else {
|
||||
db.modify( p, [&]( auto& dgp ) {
|
||||
++dgp.global_action_sequence;
|
||||
});
|
||||
return p.global_action_sequence;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t apply_context::next_recv_sequence( const account_metadata_object& receiver_account ) {
|
||||
db.modify( receiver_account, [&]( auto& ra ) {
|
||||
++ra.recv_sequence;
|
||||
});
|
||||
return receiver_account.recv_sequence;
|
||||
if ( trx_context.is_read_only() ) {
|
||||
// To avoid confusion of duplicated receive sequence number, hard code to be 0.
|
||||
return 0;
|
||||
} else {
|
||||
db.modify( receiver_account, [&]( auto& ra ) {
|
||||
++ra.recv_sequence;
|
||||
});
|
||||
return receiver_account.recv_sequence;
|
||||
}
|
||||
}
|
||||
uint64_t apply_context::next_auth_sequence( account_name actor ) {
|
||||
const auto& amo = db.get<account_metadata_object,by_name>( actor );
|
||||
|
||||
@@ -134,6 +134,7 @@ namespace eosio { namespace chain {
|
||||
permission_name name,
|
||||
permission_id_type parent,
|
||||
const authority& auth,
|
||||
bool is_trx_transient,
|
||||
time_point initial_creation_time
|
||||
)
|
||||
{
|
||||
@@ -158,7 +159,7 @@ namespace eosio { namespace chain {
|
||||
p.last_updated = creation_time;
|
||||
p.auth = auth;
|
||||
|
||||
if (auto dm_logger = _control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = _control.get_deep_mind_logger(is_trx_transient)) {
|
||||
dm_logger->on_create_permission(p);
|
||||
}
|
||||
});
|
||||
@@ -169,6 +170,7 @@ namespace eosio { namespace chain {
|
||||
permission_name name,
|
||||
permission_id_type parent,
|
||||
authority&& auth,
|
||||
bool is_trx_transient,
|
||||
time_point initial_creation_time
|
||||
)
|
||||
{
|
||||
@@ -193,20 +195,20 @@ namespace eosio { namespace chain {
|
||||
p.last_updated = creation_time;
|
||||
p.auth = std::move(auth);
|
||||
|
||||
if (auto dm_logger = _control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = _control.get_deep_mind_logger(is_trx_transient)) {
|
||||
dm_logger->on_create_permission(p);
|
||||
}
|
||||
});
|
||||
return perm;
|
||||
}
|
||||
|
||||
void authorization_manager::modify_permission( const permission_object& permission, const authority& auth ) {
|
||||
void authorization_manager::modify_permission( const permission_object& permission, const authority& auth, bool is_trx_transient ) {
|
||||
for(const key_weight& k: auth.keys)
|
||||
EOS_ASSERT(k.key.which() < _db.get<protocol_state_object>().num_supported_key_types, unactivated_key_type,
|
||||
"Unactivated key type used when modifying permission");
|
||||
|
||||
_db.modify( permission, [&](permission_object& po) {
|
||||
auto dm_logger = _control.get_deep_mind_logger();
|
||||
auto dm_logger = _control.get_deep_mind_logger(is_trx_transient);
|
||||
|
||||
std::optional<permission_object> old_permission;
|
||||
if (dm_logger) {
|
||||
@@ -216,13 +218,13 @@ namespace eosio { namespace chain {
|
||||
po.auth = auth;
|
||||
po.last_updated = _control.pending_block_time();
|
||||
|
||||
if (auto dm_logger = _control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = _control.get_deep_mind_logger(is_trx_transient)) {
|
||||
dm_logger->on_modify_permission(*old_permission, po);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void authorization_manager::remove_permission( const permission_object& permission ) {
|
||||
void authorization_manager::remove_permission( const permission_object& permission, bool is_trx_transient ) {
|
||||
const auto& index = _db.template get_index<permission_index, by_parent>();
|
||||
auto range = index.equal_range(permission.id);
|
||||
EOS_ASSERT( range.first == range.second, action_validate_exception,
|
||||
@@ -230,7 +232,7 @@ namespace eosio { namespace chain {
|
||||
|
||||
_db.get_mutable_index<permission_usage_index>().remove_object( permission.usage_id._id );
|
||||
|
||||
if (auto dm_logger = _control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = _control.get_deep_mind_logger(is_trx_transient)) {
|
||||
dm_logger->on_remove_permission(permission);
|
||||
}
|
||||
|
||||
|
||||
+1512
-1356
File diff suppressed because it is too large
Load Diff
+202
-124
@@ -34,6 +34,7 @@
|
||||
#include <fc/variant_object.hpp>
|
||||
|
||||
#include <new>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace eosio { namespace chain {
|
||||
|
||||
@@ -224,7 +225,6 @@ struct controller_impl {
|
||||
std::optional<pending_state> pending;
|
||||
block_state_ptr head;
|
||||
fork_database fork_db;
|
||||
wasm_interface wasmif;
|
||||
resource_limits_manager resource_limits;
|
||||
authorization_manager authorization;
|
||||
protocol_feature_manager protocol_features;
|
||||
@@ -236,18 +236,27 @@ struct controller_impl {
|
||||
std::optional<fc::microseconds> subjective_cpu_leeway;
|
||||
bool trusted_producer_light_validation = false;
|
||||
uint32_t snapshot_head_block = 0;
|
||||
named_thread_pool<eosio::chain::make_name_v("chain")> thread_pool;
|
||||
platform_timer timer;
|
||||
struct chain; // chain is a namespace so use an embedded type for the named_thread_pool tag
|
||||
named_thread_pool<chain> thread_pool;
|
||||
deep_mind_handler* deep_mind_logger = nullptr;
|
||||
bool okay_to_print_integrity_hash_on_stop = false;
|
||||
|
||||
std::thread::id main_thread_id;
|
||||
thread_local static platform_timer timer; // a copy for main thread and each read-only thread
|
||||
#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED)
|
||||
vm::wasm_allocator wasm_alloc;
|
||||
thread_local static vm::wasm_allocator wasm_alloc; // a copy for main thread and each read-only thread
|
||||
#endif
|
||||
// Ideally wasmif should be thread_local which must be a static.
|
||||
// Unittests can create multiple controller objects (testers) at the same time,
|
||||
// which overwrites the same static wasmif, is used for eosvmoc too.
|
||||
wasm_interface wasmif; // used by main thread and all threads for EOSVMOC
|
||||
thread_local static std::unique_ptr<wasm_interface> wasmif_thread_local; // a copy for each read-only thread, used by eos-vm and eos-vm-jit
|
||||
|
||||
typedef pair<scope_name,action_name> handler_key;
|
||||
map< account_name, map<handler_key, apply_handler> > apply_handlers;
|
||||
unordered_map< builtin_protocol_feature_t, std::function<void(controller_impl&)>, enum_hash<builtin_protocol_feature_t> > protocol_feature_activation_handlers;
|
||||
|
||||
|
||||
void pop_block() {
|
||||
auto prev = fork_db.get_block( head->header.previous );
|
||||
|
||||
@@ -292,16 +301,17 @@ struct controller_impl {
|
||||
db( cfg.state_dir,
|
||||
cfg.read_only ? database::read_only : database::read_write,
|
||||
cfg.state_size, false, cfg.db_map_mode ),
|
||||
blog( cfg.blocks_dir, cfg.prune_config ),
|
||||
blog( cfg.blocks_dir, cfg.blog ),
|
||||
fork_db( cfg.blocks_dir / config::reversible_blocks_dir_name ),
|
||||
wasmif( cfg.wasm_runtime, cfg.eosvmoc_tierup, db, cfg.state_dir, cfg.eosvmoc_config, !cfg.profile_accounts.empty() ),
|
||||
resource_limits( db, [&s]() { return s.get_deep_mind_logger(); }),
|
||||
resource_limits( db, [&s](bool is_trx_transient) { return s.get_deep_mind_logger(is_trx_transient); }),
|
||||
authorization( s, db ),
|
||||
protocol_features( std::move(pfs), [&s]() { return s.get_deep_mind_logger(); } ),
|
||||
protocol_features( std::move(pfs), [&s](bool is_trx_transient) { return s.get_deep_mind_logger(is_trx_transient); } ),
|
||||
conf( cfg ),
|
||||
chain_id( chain_id ),
|
||||
read_mode( cfg.read_mode ),
|
||||
thread_pool()
|
||||
thread_pool(),
|
||||
main_thread_id( std::this_thread::get_id() ),
|
||||
wasmif( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty() )
|
||||
{
|
||||
fork_db.open( [this]( block_timestamp_type timestamp,
|
||||
const flat_set<digest_type>& cur_features,
|
||||
@@ -327,7 +337,7 @@ struct controller_impl {
|
||||
set_activation_handler<builtin_protocol_feature_t::crypto_primitives>();
|
||||
|
||||
self.irreversible_block.connect([this](const block_state_ptr& bsp) {
|
||||
wasmif.current_lib(bsp->block_num);
|
||||
get_wasm_interface().current_lib(bsp->block_num);
|
||||
});
|
||||
|
||||
|
||||
@@ -383,7 +393,10 @@ struct controller_impl {
|
||||
}
|
||||
|
||||
void dmlog_applied_transaction(const transaction_trace_ptr& t) {
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// dmlog_applied_transaction is called by push_scheduled_transaction
|
||||
// where transient transactions are not possible, and by push_transaction
|
||||
// only when the transaction is not transient
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_applied_transaction(self.head_block_num() + 1, t);
|
||||
}
|
||||
}
|
||||
@@ -391,20 +404,21 @@ struct controller_impl {
|
||||
void log_irreversible() {
|
||||
EOS_ASSERT( fork_db.root(), fork_database_exception, "fork database not properly initialized" );
|
||||
|
||||
const auto& log_head = blog.head();
|
||||
const block_id_type log_head_id = blog.head_id();
|
||||
const bool valid_log_head = !log_head_id.empty();
|
||||
|
||||
auto lib_num = log_head ? log_head->block_num() : (blog.first_block_num() - 1);
|
||||
const auto lib_num = valid_log_head ? block_header::num_from_id(log_head_id) : (blog.first_block_num() - 1);
|
||||
|
||||
auto root_id = fork_db.root()->id;
|
||||
|
||||
if( log_head ) {
|
||||
EOS_ASSERT( root_id == blog.head_id(), fork_database_exception, "fork database root does not match block log head" );
|
||||
if( valid_log_head ) {
|
||||
EOS_ASSERT( root_id == log_head_id, fork_database_exception, "fork database root does not match block log head" );
|
||||
} else {
|
||||
EOS_ASSERT( fork_db.root()->block_num == lib_num, fork_database_exception,
|
||||
"empty block log expects the first appended block to build off a block that is not the fork database root. root block number: ${block_num}, lib: ${lib_num}", ("block_num", fork_db.root()->block_num) ("lib_num", lib_num) );
|
||||
}
|
||||
|
||||
auto fork_head = (read_mode == db_read_mode::IRREVERSIBLE) ? fork_db.pending_head() : fork_db.head();
|
||||
const auto fork_head = fork_db_head();
|
||||
|
||||
if( fork_head->dpos_irreversible_blocknum <= lib_num )
|
||||
return;
|
||||
@@ -482,12 +496,12 @@ struct controller_impl {
|
||||
}
|
||||
|
||||
void replay(std::function<bool()> check_shutdown) {
|
||||
if( !blog.head() && !fork_db.root() ) {
|
||||
auto blog_head = blog.head();
|
||||
if( !blog_head && !fork_db.root() ) {
|
||||
fork_db.reset( *head );
|
||||
return;
|
||||
}
|
||||
|
||||
auto blog_head = blog.head();
|
||||
replaying = true;
|
||||
auto start_block_num = head->block_num + 1;
|
||||
auto start = fc::time_point::now();
|
||||
@@ -569,8 +583,8 @@ struct controller_impl {
|
||||
ilog( "Starting initialization from snapshot, this may take a significant amount of time" );
|
||||
try {
|
||||
snapshot->validate();
|
||||
if( blog.head() ) {
|
||||
read_from_snapshot( snapshot, blog.first_block_num(), blog.head()->block_num() );
|
||||
if( auto blog_head = blog.head() ) {
|
||||
read_from_snapshot( snapshot, blog.first_block_num(), blog_head->block_num() );
|
||||
} else {
|
||||
read_from_snapshot( snapshot, 0, std::numeric_limits<uint32_t>::max() );
|
||||
const uint32_t lib_num = head->block_num;
|
||||
@@ -586,10 +600,6 @@ struct controller_impl {
|
||||
elog( "Failed initialization from snapshot - db storage not configured to have enough storage for the provided snapshot, please increase and retry snapshot" );
|
||||
shutdown();
|
||||
}
|
||||
|
||||
if (conf.prune_config && conf.prune_config->prune_blocks == 0) {
|
||||
blog.remove();
|
||||
}
|
||||
}
|
||||
|
||||
void startup(std::function<void()> shutdown, std::function<bool()> check_shutdown, const genesis_state& genesis) {
|
||||
@@ -623,10 +633,6 @@ struct controller_impl {
|
||||
blog.reset( genesis, head->block );
|
||||
}
|
||||
init(check_shutdown);
|
||||
|
||||
if (conf.prune_config && conf.prune_config->prune_blocks == 0) {
|
||||
blog.remove();
|
||||
}
|
||||
}
|
||||
|
||||
void startup(std::function<void()> shutdown, std::function<bool()> check_shutdown) {
|
||||
@@ -636,15 +642,15 @@ struct controller_impl {
|
||||
this->shutdown = shutdown;
|
||||
uint32_t lib_num = fork_db.root()->block_num;
|
||||
auto first_block_num = blog.first_block_num();
|
||||
if( blog.head() ) {
|
||||
EOS_ASSERT( first_block_num <= lib_num && lib_num <= blog.head()->block_num(),
|
||||
if( auto blog_head = blog.head() ) {
|
||||
EOS_ASSERT( first_block_num <= lib_num && lib_num <= blog_head->block_num(),
|
||||
block_log_exception,
|
||||
"block log (ranging from ${block_log_first_num} to ${block_log_last_num}) does not contain the last irreversible block (${fork_db_lib})",
|
||||
("block_log_first_num", first_block_num)
|
||||
("block_log_last_num", blog.head()->block_num())
|
||||
("block_log_last_num", blog_head->block_num())
|
||||
("fork_db_lib", lib_num)
|
||||
);
|
||||
lib_num = blog.head()->block_num();
|
||||
lib_num = blog_head->block_num();
|
||||
} else {
|
||||
if( first_block_num != (lib_num + 1) ) {
|
||||
blog.reset( chain_id, lib_num + 1 );
|
||||
@@ -657,10 +663,6 @@ struct controller_impl {
|
||||
head = fork_db.head();
|
||||
|
||||
init(check_shutdown);
|
||||
|
||||
if (conf.prune_config && conf.prune_config->prune_blocks == 0) {
|
||||
blog.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -711,7 +713,8 @@ struct controller_impl {
|
||||
|
||||
protocol_features.init( db );
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// At startup, no transaction specific logging is possible
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_startup(db, head->block_num);
|
||||
}
|
||||
|
||||
@@ -1027,22 +1030,23 @@ struct controller_impl {
|
||||
});
|
||||
|
||||
const auto& owner_permission = authorization.create_permission(name, config::owner_name, 0,
|
||||
owner, initial_timestamp );
|
||||
owner, false, initial_timestamp );
|
||||
const auto& active_permission = authorization.create_permission(name, config::active_name, owner_permission.id,
|
||||
active, initial_timestamp );
|
||||
active, false, initial_timestamp );
|
||||
|
||||
resource_limits.initialize_account(name);
|
||||
resource_limits.initialize_account(name, false);
|
||||
|
||||
int64_t ram_delta = config::overhead_per_account_ram_bytes;
|
||||
ram_delta += 2*config::billable_size_v<permission_object>;
|
||||
ram_delta += owner_permission.auth.get_billable_size();
|
||||
ram_delta += active_permission.auth.get_billable_size();
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// This is only called at startup, no transaction specific logging is possible
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${name}", ("name", name)), "account", "add", "newaccount");
|
||||
}
|
||||
|
||||
resource_limits.add_pending_ram_usage(name, ram_delta);
|
||||
resource_limits.add_pending_ram_usage(name, ram_delta, false); // false for doing dm logging
|
||||
resource_limits.verify_account_ram_usage(name);
|
||||
}
|
||||
|
||||
@@ -1095,17 +1099,24 @@ struct controller_impl {
|
||||
config::majority_producers_permission_name,
|
||||
active_permission.id,
|
||||
active_producers_authority,
|
||||
false,
|
||||
genesis.initial_timestamp );
|
||||
authorization.create_permission( config::producers_account_name,
|
||||
config::minority_producers_permission_name,
|
||||
majority_permission.id,
|
||||
active_producers_authority,
|
||||
false,
|
||||
genesis.initial_timestamp );
|
||||
|
||||
}
|
||||
|
||||
// The returned scoped_exit should not exceed the lifetime of the pending which existed when make_block_restore_point was called.
|
||||
fc::scoped_exit<std::function<void()>> make_block_restore_point() {
|
||||
fc::scoped_exit<std::function<void()>> make_block_restore_point( bool is_read_only = false ) {
|
||||
if ( is_read_only ) {
|
||||
std::function<void()> callback = []() { };
|
||||
return fc::make_scoped_exit( std::move(callback) );
|
||||
}
|
||||
|
||||
auto& bb = std::get<building_block>(pending->_block_stage);
|
||||
auto orig_trx_receipts_size = bb._pending_trx_receipts.size();
|
||||
auto orig_trx_metas_size = bb._pending_trx_metas.size();
|
||||
@@ -1152,14 +1163,14 @@ struct controller_impl {
|
||||
etrx.set_reference_block( self.head_block_id() );
|
||||
}
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
dm_logger->on_onerror(etrx);
|
||||
}
|
||||
|
||||
transaction_checktime_timer trx_timer(timer);
|
||||
const packed_transaction trx( std::move( etrx ) );
|
||||
transaction_context trx_context( self, trx, std::move(trx_timer), start );
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_onerror(etrx);
|
||||
}
|
||||
|
||||
trx_context.block_deadline = block_deadline;
|
||||
trx_context.max_transaction_time_subjective = max_transaction_time;
|
||||
trx_context.explicit_billed_cpu_time = explicit_billed_cpu_time;
|
||||
@@ -1208,12 +1219,13 @@ struct controller_impl {
|
||||
}
|
||||
|
||||
int64_t remove_scheduled_transaction( const generated_transaction_object& gto ) {
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// deferred transactions cannot be transient.
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", gto.id)), "deferred_trx", "remove", "deferred_trx_removed");
|
||||
}
|
||||
|
||||
int64_t ram_delta = -(config::billable_size_v<generated_transaction_object> + gto.packed_trx.size());
|
||||
resource_limits.add_pending_ram_usage( gto.payer, ram_delta );
|
||||
resource_limits.add_pending_ram_usage( gto.payer, ram_delta, false ); // false for doing dm logging
|
||||
// No need to verify_account_ram_usage since we are only reducing memory
|
||||
|
||||
db.remove( gto );
|
||||
@@ -1318,7 +1330,7 @@ struct controller_impl {
|
||||
|
||||
uint32_t cpu_time_to_bill_us = billed_cpu_time_us;
|
||||
|
||||
transaction_checktime_timer trx_timer(timer);
|
||||
transaction_checktime_timer trx_timer( timer );
|
||||
transaction_context trx_context( self, *trx->packed_trx(), std::move(trx_timer) );
|
||||
trx_context.leeway = fc::microseconds(0); // avoid stealing cpu resource
|
||||
trx_context.block_deadline = block_deadline;
|
||||
@@ -1336,7 +1348,8 @@ struct controller_impl {
|
||||
trace->except_ptr = std::current_exception();
|
||||
trace->elapsed = fc::time_point::now() - start;
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// deferred transactions cannot be transient
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_fail_deferred();
|
||||
}
|
||||
};
|
||||
@@ -1517,7 +1530,7 @@ struct controller_impl {
|
||||
transaction_trace_ptr trace;
|
||||
try {
|
||||
auto start = fc::time_point::now();
|
||||
const bool check_auth = !self.skip_auth_check() && !trx->implicit();
|
||||
const bool check_auth = !self.skip_auth_check() && !trx->implicit() && !trx->is_read_only();
|
||||
const fc::microseconds sig_cpu_usage = trx->signature_cpu_usage();
|
||||
|
||||
if( !explicit_billed_cpu_time ) {
|
||||
@@ -1576,14 +1589,14 @@ struct controller_impl {
|
||||
trx_context.exec();
|
||||
trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
|
||||
|
||||
auto restore = make_block_restore_point();
|
||||
auto restore = make_block_restore_point( trx->is_read_only() );
|
||||
|
||||
if (!trx->implicit()) {
|
||||
trx->billed_cpu_time_us = trx_context.billed_cpu_time_us;
|
||||
if (!trx->implicit() && !trx->is_read_only()) {
|
||||
transaction_receipt::status_enum s = (trx_context.delay == fc::seconds(0))
|
||||
? transaction_receipt::executed
|
||||
: transaction_receipt::delayed;
|
||||
trace->receipt = push_receipt(*trx->packed_trx(), s, trx_context.billed_cpu_time_us, trace->net_usage);
|
||||
trx->billed_cpu_time_us = trx_context.billed_cpu_time_us;
|
||||
std::get<building_block>(pending->_block_stage)._pending_trx_metas.emplace_back(trx);
|
||||
} else {
|
||||
transaction_receipt_header r;
|
||||
@@ -1593,22 +1606,22 @@ struct controller_impl {
|
||||
trace->receipt = r;
|
||||
}
|
||||
|
||||
fc::move_append( std::get<building_block>(pending->_block_stage)._action_receipt_digests,
|
||||
std::move(trx_context.executed_action_receipt_digests) );
|
||||
if ( !trx->is_read_only() ) {
|
||||
fc::move_append( std::get<building_block>(pending->_block_stage)._action_receipt_digests,
|
||||
std::move(trx_context.executed_action_receipt_digests) );
|
||||
if ( !trx->is_dry_run() ) {
|
||||
// call the accept signal but only once for this transaction
|
||||
if (!trx->accepted) {
|
||||
trx->accepted = true;
|
||||
emit(self.accepted_transaction, trx);
|
||||
}
|
||||
|
||||
// call the accept signal but only once for this transaction
|
||||
if (!trx->is_dry_run()) {
|
||||
if (!trx->accepted) {
|
||||
trx->accepted = true;
|
||||
emit(self.accepted_transaction, trx);
|
||||
dmlog_applied_transaction(trace);
|
||||
emit(self.applied_transaction, std::tie(trace, trx->packed_trx()));
|
||||
}
|
||||
|
||||
dmlog_applied_transaction(trace);
|
||||
emit(self.applied_transaction, std::tie(trace, trx->packed_trx()));
|
||||
}
|
||||
|
||||
|
||||
if ( trx->is_dry_run() ) {
|
||||
if ( trx->is_transient() ) {
|
||||
// remove trx from pending block by not canceling 'restore'
|
||||
trx_context.undo(); // this will happen automatically in destructor, but make it more explicit
|
||||
} else if ( pending->_block_status == controller::block_status::ephemeral ) {
|
||||
@@ -1623,7 +1636,7 @@ struct controller_impl {
|
||||
trx_context.squash();
|
||||
}
|
||||
|
||||
if( !trx->is_dry_run() ) {
|
||||
if( !trx->is_transient() ) {
|
||||
pending->_block_report.total_net_usage += trace->net_usage;
|
||||
pending->_block_report.total_cpu_usage_us += trace->receipt->cpu_usage_us;
|
||||
pending->_block_report.total_elapsed_time += trace->elapsed;
|
||||
@@ -1646,7 +1659,7 @@ struct controller_impl {
|
||||
handle_exception(wrapper);
|
||||
}
|
||||
|
||||
if (!trx->is_dry_run()) {
|
||||
if (!trx->is_transient()) {
|
||||
emit(self.accepted_transaction, trx);
|
||||
dmlog_applied_transaction(trace);
|
||||
emit(self.applied_transaction, std::tie(trace, trx->packed_trx()));
|
||||
@@ -1672,7 +1685,8 @@ struct controller_impl {
|
||||
|
||||
emit( self.block_start, head->block_num + 1 );
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// at block level, no transaction specific logging is possible
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
// The head block represents the block just before this one that is about to start, so add 1 to get this block num
|
||||
dm_logger->on_start_block(head->block_num + 1);
|
||||
}
|
||||
@@ -1924,7 +1938,8 @@ struct controller_impl {
|
||||
EOS_ASSERT( bsp == head, fork_database_exception, "committed block did not become the new head in fork database");
|
||||
}
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// at block level, no transaction specific logging is possible
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_accepted_block(bsp);
|
||||
}
|
||||
|
||||
@@ -2218,7 +2233,7 @@ struct controller_impl {
|
||||
EOS_ASSERT( bsp, block_validate_exception, "null block" );
|
||||
const auto& b = bsp->block;
|
||||
|
||||
if( conf.terminate_at_block > 0 && conf.terminate_at_block < self.head_block_num()) {
|
||||
if( conf.terminate_at_block > 0 && conf.terminate_at_block <= self.head_block_num()) {
|
||||
ilog("Reached configured maximum block ${num}; terminating", ("num", conf.terminate_at_block) );
|
||||
shutdown();
|
||||
return;
|
||||
@@ -2253,7 +2268,7 @@ struct controller_impl {
|
||||
EOS_ASSERT( (s == controller::block_status::irreversible || s == controller::block_status::validated),
|
||||
block_validate_exception, "invalid block status for replay" );
|
||||
|
||||
if( conf.terminate_at_block > 0 && conf.terminate_at_block < self.head_block_num() ) {
|
||||
if( conf.terminate_at_block > 0 && conf.terminate_at_block <= self.head_block_num() ) {
|
||||
ilog("Reached configured maximum block ${num}; terminating", ("num", conf.terminate_at_block) );
|
||||
shutdown();
|
||||
return;
|
||||
@@ -2319,7 +2334,8 @@ struct controller_impl {
|
||||
ilog("switching forks from ${current_head_id} (block number ${current_head_num}) to ${new_head_id} (block number ${new_head_num})",
|
||||
("current_head_id", head->id)("current_head_num", head->block_num)("new_head_id", new_head->id)("new_head_num", new_head->block_num) );
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// not possible to log transaction specific infor when switching forks
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_switch_forks(head->id, new_head->id);
|
||||
}
|
||||
|
||||
@@ -2638,23 +2654,65 @@ struct controller_impl {
|
||||
trx.set_reference_block( self.head_block_id() );
|
||||
}
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// onblock transaction cannot be transient
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_onblock(trx);
|
||||
}
|
||||
|
||||
return trx;
|
||||
}
|
||||
|
||||
inline deep_mind_handler* get_deep_mind_logger() const {
|
||||
return deep_mind_logger;
|
||||
inline deep_mind_handler* get_deep_mind_logger(bool is_trx_transient) const {
|
||||
// do not perform deep mind logging for read-only and dry-run transactions
|
||||
return is_trx_transient ? nullptr : deep_mind_logger;
|
||||
}
|
||||
|
||||
uint32_t earliest_available_block_num() const {
|
||||
return (blog.first_block_num() != 0) ? blog.first_block_num() : fork_db.root()->block_num;
|
||||
}
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
bool is_eos_vm_oc_enabled() const {
|
||||
return ( conf.eosvmoc_tierup || conf.wasm_runtime == wasm_interface::vm_type::eos_vm_oc );
|
||||
}
|
||||
#endif
|
||||
|
||||
// only called from non-main threads (read-only trx execution threads)
|
||||
// when producer_plugin starts them
|
||||
void init_thread_local_data() {
|
||||
EOS_ASSERT( !is_main_thread(), misc_exception, "init_thread_local_data called on the main thread");
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
if ( is_eos_vm_oc_enabled() )
|
||||
// EOSVMOC needs further initialization of its thread local data
|
||||
wasmif.init_thread_local_data();
|
||||
else
|
||||
#endif
|
||||
// Non-EOSVMOC needs a wasmif per thread
|
||||
wasmif_thread_local = std::make_unique<wasm_interface>( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty());
|
||||
}
|
||||
|
||||
bool is_main_thread() { return main_thread_id == std::this_thread::get_id(); };
|
||||
|
||||
wasm_interface& get_wasm_interface() {
|
||||
if ( is_main_thread()
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
|| is_eos_vm_oc_enabled()
|
||||
#endif
|
||||
)
|
||||
return wasmif;
|
||||
else
|
||||
return *wasmif_thread_local;
|
||||
}
|
||||
|
||||
block_state_ptr fork_db_head() const;
|
||||
}; /// controller_impl
|
||||
|
||||
thread_local platform_timer controller_impl::timer;
|
||||
#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED)
|
||||
thread_local eosio::vm::wasm_allocator controller_impl::wasm_alloc;
|
||||
#endif
|
||||
thread_local std::unique_ptr<wasm_interface> controller_impl::wasmif_thread_local;
|
||||
|
||||
const resource_limits_manager& controller::get_resource_limits_manager()const
|
||||
{
|
||||
return my->resource_limits;
|
||||
@@ -2725,7 +2783,7 @@ chainbase::database& controller::mutable_db()const { return my->db; }
|
||||
|
||||
const fork_database& controller::fork_db()const { return my->fork_db; }
|
||||
|
||||
void controller::preactivate_feature( const digest_type& feature_digest ) {
|
||||
void controller::preactivate_feature( const digest_type& feature_digest, bool is_trx_transient ) {
|
||||
const auto& pfs = my->protocol_features.get_protocol_feature_set();
|
||||
auto cur_time = pending_block_time();
|
||||
|
||||
@@ -2822,7 +2880,7 @@ void controller::preactivate_feature( const digest_type& feature_digest ) {
|
||||
("digest", feature_digest)
|
||||
);
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
if (auto dm_logger = get_deep_mind_logger(is_trx_transient)) {
|
||||
const auto feature = pfs.get_protocol_feature(feature_digest);
|
||||
|
||||
dm_logger->on_preactivate_feature(feature);
|
||||
@@ -3007,36 +3065,23 @@ block_state_ptr controller::head_block_state()const {
|
||||
return my->head;
|
||||
}
|
||||
|
||||
block_state_ptr controller_impl::fork_db_head() const {
|
||||
if( read_mode == db_read_mode::IRREVERSIBLE ) {
|
||||
// When in IRREVERSIBLE mode fork_db blocks are marked valid when they become irreversible so that
|
||||
// fork_db.head() returns irreversible block
|
||||
// Use pending_head since this method should return the chain head and not last irreversible.
|
||||
return fork_db.pending_head();
|
||||
} else {
|
||||
return fork_db.head();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t controller::fork_db_head_block_num()const {
|
||||
return my->fork_db.head()->block_num;
|
||||
return my->fork_db_head()->block_num;
|
||||
}
|
||||
|
||||
block_id_type controller::fork_db_head_block_id()const {
|
||||
return my->fork_db.head()->id;
|
||||
}
|
||||
|
||||
time_point controller::fork_db_head_block_time()const {
|
||||
return my->fork_db.head()->header.timestamp;
|
||||
}
|
||||
|
||||
account_name controller::fork_db_head_block_producer()const {
|
||||
return my->fork_db.head()->header.producer;
|
||||
}
|
||||
|
||||
uint32_t controller::fork_db_pending_head_block_num()const {
|
||||
return my->fork_db.pending_head()->block_num;
|
||||
}
|
||||
|
||||
block_id_type controller::fork_db_pending_head_block_id()const {
|
||||
return my->fork_db.pending_head()->id;
|
||||
}
|
||||
|
||||
time_point controller::fork_db_pending_head_block_time()const {
|
||||
return my->fork_db.pending_head()->header.timestamp;
|
||||
}
|
||||
|
||||
account_name controller::fork_db_pending_head_block_producer()const {
|
||||
return my->fork_db.pending_head()->header.producer;
|
||||
return my->fork_db_head()->id;
|
||||
}
|
||||
|
||||
time_point controller::pending_block_time()const {
|
||||
@@ -3100,7 +3145,7 @@ const global_property_object& controller::get_global_properties()const {
|
||||
return my->db.get<global_property_object>();
|
||||
}
|
||||
|
||||
signed_block_ptr controller::fetch_block_by_id( block_id_type id )const {
|
||||
signed_block_ptr controller::fetch_block_by_id( const block_id_type& id )const {
|
||||
auto state = my->fork_db.get_block(id);
|
||||
if( state && state->block ) return state->block;
|
||||
auto bptr = my->blog.read_block_by_num( block_header::num_from_id(id) );
|
||||
@@ -3108,6 +3153,14 @@ signed_block_ptr controller::fetch_block_by_id( block_id_type id )const {
|
||||
return signed_block_ptr();
|
||||
}
|
||||
|
||||
std::optional<signed_block_header> controller::fetch_block_header_by_id( const block_id_type& id )const {
|
||||
auto state = my->fork_db.get_block(id);
|
||||
if( state && state->block ) return state->header;
|
||||
auto result = my->blog.read_block_header_by_num( block_header::num_from_id(id) );
|
||||
if( result && result->calculate_id() == id ) return result;
|
||||
return {};
|
||||
}
|
||||
|
||||
signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const { try {
|
||||
auto blk_state = fetch_block_state_by_number( block_num );
|
||||
if( blk_state ) {
|
||||
@@ -3117,25 +3170,25 @@ signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const {
|
||||
return my->blog.read_block_by_num(block_num);
|
||||
} FC_CAPTURE_AND_RETHROW( (block_num) ) }
|
||||
|
||||
std::optional<signed_block_header> controller::fetch_block_header_by_number( uint32_t block_num )const { try {
|
||||
auto blk_state = fetch_block_state_by_number( block_num );
|
||||
if( blk_state ) {
|
||||
return blk_state->header;
|
||||
}
|
||||
|
||||
return my->blog.read_block_header_by_num(block_num);
|
||||
} FC_CAPTURE_AND_RETHROW( (block_num) ) }
|
||||
|
||||
block_state_ptr controller::fetch_block_state_by_id( block_id_type id )const {
|
||||
auto state = my->fork_db.get_block(id);
|
||||
return state;
|
||||
}
|
||||
|
||||
block_state_ptr controller::fetch_block_state_by_number( uint32_t block_num )const { try {
|
||||
if( my->read_mode == db_read_mode::IRREVERSIBLE ) {
|
||||
return my->fork_db.search_on_branch( my->fork_db.pending_head()->id, block_num );
|
||||
} else {
|
||||
return my->fork_db.search_on_branch( my->fork_db.head()->id, block_num );
|
||||
}
|
||||
return my->fork_db.search_on_branch( fork_db_head_block_id(), block_num );
|
||||
} FC_CAPTURE_AND_RETHROW( (block_num) ) }
|
||||
|
||||
block_id_type controller::get_block_id_for_num( uint32_t block_num )const { try {
|
||||
const auto& tapos_block_summary = db().get<block_summary_object>((uint16_t)block_num);
|
||||
|
||||
if( block_header::num_from_id(tapos_block_summary.block_id) == block_num )
|
||||
return tapos_block_summary.block_id;
|
||||
|
||||
const auto& blog_head = my->blog.head();
|
||||
|
||||
bool find_in_blog = (blog_head && block_num <= blog_head->block_num());
|
||||
@@ -3334,7 +3387,7 @@ const apply_handler* controller::find_apply_handler( account_name receiver, acco
|
||||
return nullptr;
|
||||
}
|
||||
wasm_interface& controller::get_wasm_interface() {
|
||||
return my->wasmif;
|
||||
return my->get_wasm_interface();
|
||||
}
|
||||
|
||||
const account_object& controller::get_account( account_name name )const
|
||||
@@ -3485,7 +3538,9 @@ void controller::add_to_ram_correction( account_name account, uint64_t ram_bytes
|
||||
} );
|
||||
}
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// on_add_ram_correction is only called for deferred transaction
|
||||
// (in apply_context::schedule_deferred_transaction)
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_add_ram_correction(*ptr, ram_bytes);
|
||||
}
|
||||
}
|
||||
@@ -3494,8 +3549,8 @@ bool controller::all_subjective_mitigations_disabled()const {
|
||||
return my->conf.disable_all_subjective_mitigations;
|
||||
}
|
||||
|
||||
deep_mind_handler* controller::get_deep_mind_logger()const {
|
||||
return my->get_deep_mind_logger();
|
||||
deep_mind_handler* controller::get_deep_mind_logger(bool is_trx_transient)const {
|
||||
return my->get_deep_mind_logger(is_trx_transient);
|
||||
}
|
||||
|
||||
void controller::enable_deep_mind(deep_mind_handler* logger) {
|
||||
@@ -3512,6 +3567,12 @@ vm::wasm_allocator& controller::get_wasm_allocator() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
bool controller::is_eos_vm_oc_enabled() const {
|
||||
return my->is_eos_vm_oc_enabled();
|
||||
}
|
||||
#endif
|
||||
|
||||
std::optional<uint64_t> controller::convert_exception_to_error_code( const fc::exception& e ) {
|
||||
const chain_exception* e_ptr = dynamic_cast<const chain_exception*>( &e );
|
||||
|
||||
@@ -3571,7 +3632,11 @@ std::optional<chain_id_type> controller::extract_chain_id_from_db( const path& s
|
||||
if (gpo==nullptr) return {};
|
||||
|
||||
return gpo->chain_id;
|
||||
} catch (std::system_error &) {} // do not propagate db_error_code::not_found" for absent db, so it will be created
|
||||
} catch( const std::system_error& e ) {
|
||||
// do not propagate db_error_code::not_found for absent db, so it will be created
|
||||
if( e.code().value() != chainbase::db_error_code::not_found )
|
||||
throw;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -3605,10 +3670,22 @@ void controller::replace_account_keys( name account, name permission, const publ
|
||||
p.auth = authority(key);
|
||||
});
|
||||
int64_t new_size = (int64_t)(chain::config::billable_size_v<permission_object> + perm->auth.get_billable_size());
|
||||
rlm.add_pending_ram_usage(account, new_size - old_size);
|
||||
rlm.add_pending_ram_usage(account, new_size - old_size, false); // false for doing dm logging
|
||||
rlm.verify_account_ram_usage(account);
|
||||
}
|
||||
|
||||
void controller::set_db_read_only_mode() {
|
||||
mutable_db().set_read_only_mode();
|
||||
}
|
||||
|
||||
void controller::unset_db_read_only_mode() {
|
||||
mutable_db().unset_read_only_mode();
|
||||
}
|
||||
|
||||
void controller::init_thread_local_data() {
|
||||
my->init_thread_local_data();
|
||||
}
|
||||
|
||||
/// Protocol feature activation handlers:
|
||||
|
||||
template<>
|
||||
@@ -3638,11 +3715,12 @@ void controller_impl::on_activation<builtin_protocol_feature_t::replace_deferred
|
||||
("name", itr->name)("adjust", itr->ram_correction)("current", current_ram_usage) );
|
||||
}
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
// This method is only called for deferred transaction
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", itr->id._id)), "deferred_trx", "correction", "deferred_trx_ram_correction");
|
||||
}
|
||||
|
||||
resource_limits.add_pending_ram_usage( itr->name, ram_delta );
|
||||
resource_limits.add_pending_ram_usage( itr->name, ram_delta, false ); // false for doing dm logging
|
||||
db.remove( *itr );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ void validate_authority_precondition( const apply_context& context, const author
|
||||
* This method is called assuming precondition_system_newaccount succeeds a
|
||||
*/
|
||||
void apply_eosio_newaccount(apply_context& context) {
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "newaccount not allowed in read-only transaction" );
|
||||
auto create = context.get_action().data_as<newaccount>();
|
||||
try {
|
||||
context.require_authorization(create.creator);
|
||||
@@ -107,18 +108,18 @@ void apply_eosio_newaccount(apply_context& context) {
|
||||
}
|
||||
|
||||
const auto& owner_permission = authorization.create_permission( create.name, config::owner_name, 0,
|
||||
std::move(create.owner) );
|
||||
std::move(create.owner), context.trx_context.is_transient() );
|
||||
const auto& active_permission = authorization.create_permission( create.name, config::active_name, owner_permission.id,
|
||||
std::move(create.active) );
|
||||
std::move(create.active), context.trx_context.is_transient() );
|
||||
|
||||
context.control.get_mutable_resource_limits_manager().initialize_account(create.name);
|
||||
context.control.get_mutable_resource_limits_manager().initialize_account(create.name, context.trx_context.is_transient());
|
||||
|
||||
int64_t ram_delta = config::overhead_per_account_ram_bytes;
|
||||
ram_delta += 2*config::billable_size_v<permission_object>;
|
||||
ram_delta += owner_permission.auth.get_billable_size();
|
||||
ram_delta += active_permission.auth.get_billable_size();
|
||||
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${name}", ("name", create.name)), "account", "add", "newaccount");
|
||||
}
|
||||
|
||||
@@ -127,6 +128,7 @@ void apply_eosio_newaccount(apply_context& context) {
|
||||
} FC_CAPTURE_AND_RETHROW( (create) ) }
|
||||
|
||||
void apply_eosio_setcode(apply_context& context) {
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "setcode not allowed in read-only transaction" );
|
||||
auto& db = context.db;
|
||||
auto act = context.get_action().data_as<setcode>();
|
||||
context.require_authorization(act.account);
|
||||
@@ -194,7 +196,7 @@ void apply_eosio_setcode(apply_context& context) {
|
||||
});
|
||||
|
||||
if (new_size != old_size) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
|
||||
const char* operation = "update";
|
||||
if (old_size <= 0) {
|
||||
operation = "add";
|
||||
@@ -210,6 +212,7 @@ void apply_eosio_setcode(apply_context& context) {
|
||||
}
|
||||
|
||||
void apply_eosio_setabi(apply_context& context) {
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "setabi ot allowed in read-only transaction" );
|
||||
auto& db = context.db;
|
||||
auto act = context.get_action().data_as<setabi>();
|
||||
|
||||
@@ -232,7 +235,7 @@ void apply_eosio_setabi(apply_context& context) {
|
||||
});
|
||||
|
||||
if (new_size != old_size) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
|
||||
const char* operation = "update";
|
||||
if (old_size <= 0) {
|
||||
operation = "add";
|
||||
@@ -248,6 +251,7 @@ void apply_eosio_setabi(apply_context& context) {
|
||||
}
|
||||
|
||||
void apply_eosio_updateauth(apply_context& context) {
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "updateauth not allowed in read-only transaction" );
|
||||
|
||||
auto update = context.get_action().data_as<updateauth>();
|
||||
context.require_authorization(update.account); // only here to mark the single authority on this action as used
|
||||
@@ -297,21 +301,21 @@ void apply_eosio_updateauth(apply_context& context) {
|
||||
|
||||
int64_t old_size = (int64_t)(config::billable_size_v<permission_object> + permission->auth.get_billable_size());
|
||||
|
||||
authorization.modify_permission( *permission, update.auth );
|
||||
authorization.modify_permission( *permission, update.auth, context.trx_context.is_transient() );
|
||||
|
||||
int64_t new_size = (int64_t)(config::billable_size_v<permission_object> + permission->auth.get_billable_size());
|
||||
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", permission->id)), "auth", "update", "updateauth_update");
|
||||
}
|
||||
|
||||
context.add_ram_usage( permission->owner, new_size - old_size );
|
||||
} else {
|
||||
const auto& p = authorization.create_permission( update.account, update.permission, parent_id, update.auth );
|
||||
const auto& p = authorization.create_permission( update.account, update.permission, parent_id, update.auth, context.trx_context.is_transient() );
|
||||
|
||||
int64_t new_size = (int64_t)(config::billable_size_v<permission_object> + p.auth.get_billable_size());
|
||||
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", p.id)), "auth", "add", "updateauth_create");
|
||||
}
|
||||
|
||||
@@ -322,6 +326,8 @@ void apply_eosio_updateauth(apply_context& context) {
|
||||
void apply_eosio_deleteauth(apply_context& context) {
|
||||
// context.require_write_lock( config::eosio_auth_scope );
|
||||
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "deleteauth not allowed in read-only transaction" );
|
||||
|
||||
auto remove = context.get_action().data_as<deleteauth>();
|
||||
context.require_authorization(remove.account); // only here to mark the single authority on this action as used
|
||||
|
||||
@@ -344,11 +350,11 @@ void apply_eosio_deleteauth(apply_context& context) {
|
||||
const auto& permission = authorization.get_permission({remove.account, remove.permission});
|
||||
int64_t old_size = config::billable_size_v<permission_object> + permission.auth.get_billable_size();
|
||||
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", permission.id)), "auth", "remove", "deleteauth");
|
||||
}
|
||||
|
||||
authorization.remove_permission( permission );
|
||||
authorization.remove_permission( permission, context.trx_context.is_transient() );
|
||||
|
||||
context.add_ram_usage( remove.account, -old_size );
|
||||
|
||||
@@ -357,6 +363,8 @@ void apply_eosio_deleteauth(apply_context& context) {
|
||||
void apply_eosio_linkauth(apply_context& context) {
|
||||
// context.require_write_lock( config::eosio_auth_scope );
|
||||
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "linkauth not allowed in read-only transaction" );
|
||||
|
||||
auto requirement = context.get_action().data_as<linkauth>();
|
||||
try {
|
||||
EOS_ASSERT(!requirement.requirement.empty(), action_validate_exception, "Required permission cannot be empty");
|
||||
@@ -401,7 +409,7 @@ void apply_eosio_linkauth(apply_context& context) {
|
||||
link.required_permission = requirement.requirement;
|
||||
});
|
||||
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", l.id)), "auth_link", "add", "linkauth");
|
||||
}
|
||||
|
||||
@@ -417,6 +425,8 @@ void apply_eosio_linkauth(apply_context& context) {
|
||||
void apply_eosio_unlinkauth(apply_context& context) {
|
||||
// context.require_write_lock( config::eosio_auth_scope );
|
||||
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "unlinkauth not allowed in read-only transaction" );
|
||||
|
||||
auto& db = context.db;
|
||||
auto unlink = context.get_action().data_as<unlinkauth>();
|
||||
|
||||
@@ -426,7 +436,7 @@ void apply_eosio_unlinkauth(apply_context& context) {
|
||||
auto link = db.find<permission_link_object, by_action_name>(link_key);
|
||||
EOS_ASSERT(link != nullptr, action_validate_exception, "Attempting to unlink authority, but no link found");
|
||||
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
|
||||
dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", link->id)), "auth_link", "remove", "unlinkauth");
|
||||
}
|
||||
|
||||
@@ -439,6 +449,7 @@ void apply_eosio_unlinkauth(apply_context& context) {
|
||||
}
|
||||
|
||||
void apply_eosio_canceldelay(apply_context& context) {
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "canceldelay not allowed in read-only transaction" );
|
||||
auto cancel = context.get_action().data_as<canceldelay>();
|
||||
context.require_authorization(cancel.canceling_auth.actor); // only here to mark the single authority on this action as used
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@ struct abi_serializer {
|
||||
using yield_function_t = fc::optional_delegate<void(size_t)>;
|
||||
|
||||
abi_serializer(){ configure_built_in_types(); }
|
||||
abi_serializer( const abi_def& abi, const yield_function_t& yield );
|
||||
abi_serializer( abi_def abi, const yield_function_t& yield );
|
||||
[[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]]
|
||||
abi_serializer( const abi_def& abi, const fc::microseconds& max_serialization_time );
|
||||
void set_abi( const abi_def& abi, const yield_function_t& yield );
|
||||
void set_abi( abi_def abi, const yield_function_t& yield );
|
||||
[[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]]
|
||||
void set_abi(const abi_def& abi, const fc::microseconds& max_serialization_time);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <eosio/chain/controller.hpp>
|
||||
#include <eosio/chain/transaction.hpp>
|
||||
#include <eosio/chain/transaction_context.hpp>
|
||||
#include <eosio/chain/contract_table_objects.hpp>
|
||||
#include <eosio/chain/deep_mind.hpp>
|
||||
#include <fc/utility.hpp>
|
||||
@@ -13,7 +14,6 @@ namespace chainbase { class database; }
|
||||
namespace eosio { namespace chain {
|
||||
|
||||
class controller;
|
||||
class transaction_context;
|
||||
|
||||
class apply_context {
|
||||
private:
|
||||
@@ -177,6 +177,7 @@ class apply_context {
|
||||
int store( uint64_t scope, uint64_t table, const account_name& payer,
|
||||
uint64_t id, secondary_key_proxy_const_type value )
|
||||
{
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), table_operation_not_permitted, "cannot store a db record when executing a readonly transaction" );
|
||||
EOS_ASSERT( payer != account_name(), invalid_table_payer, "must specify a valid account to pay for new record" );
|
||||
|
||||
// context.require_write_lock( scope );
|
||||
@@ -193,7 +194,7 @@ class apply_context {
|
||||
context.db.modify( tab, [&]( auto& t ) {
|
||||
++t.count;
|
||||
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
|
||||
std::string event_id = RAM_EVENT_ID("${code}:${scope}:${table}:${index_name}",
|
||||
("code", t.code)
|
||||
("scope", t.scope)
|
||||
@@ -211,12 +212,13 @@ class apply_context {
|
||||
}
|
||||
|
||||
void remove( int iterator ) {
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), table_operation_not_permitted, "cannot remove a db record when executing a readonly transaction" );
|
||||
const auto& obj = itr_cache.get( iterator );
|
||||
|
||||
const auto& table_obj = itr_cache.get_table( obj.t_id );
|
||||
EOS_ASSERT( table_obj.code == context.receiver, table_access_violation, "db access violation" );
|
||||
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
|
||||
std::string event_id = RAM_EVENT_ID("${code}:${scope}:${table}:${index_name}",
|
||||
("code", table_obj.code)
|
||||
("scope", table_obj.scope)
|
||||
@@ -243,6 +245,7 @@ class apply_context {
|
||||
}
|
||||
|
||||
void update( int iterator, account_name payer, secondary_key_proxy_const_type secondary ) {
|
||||
EOS_ASSERT( !context.trx_context.is_read_only(), table_operation_not_permitted, "cannot update a db record when executing a readonly transaction" );
|
||||
const auto& obj = itr_cache.get( iterator );
|
||||
|
||||
const auto& table_obj = itr_cache.get_table( obj.t_id );
|
||||
@@ -255,7 +258,7 @@ class apply_context {
|
||||
int64_t billing_size = config::billable_size_v<ObjectType>;
|
||||
|
||||
std::string event_id;
|
||||
if (context.control.get_deep_mind_logger() != nullptr) {
|
||||
if (context.control.get_deep_mind_logger(context.trx_context.is_transient()) != nullptr) {
|
||||
event_id = RAM_EVENT_ID("${code}:${scope}:${table}:${index_name}",
|
||||
("code", table_obj.code)
|
||||
("scope", table_obj.scope)
|
||||
@@ -265,12 +268,12 @@ class apply_context {
|
||||
}
|
||||
|
||||
if( obj.payer != payer ) {
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger())
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient()))
|
||||
{
|
||||
dm_logger->on_ram_trace(std::string(event_id), "secondary_index", "remove", "secondary_index_remove");
|
||||
}
|
||||
context.update_db_usage( obj.payer, -(billing_size) );
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger())
|
||||
if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient()))
|
||||
{
|
||||
dm_logger->on_ram_trace(std::move(event_id), "secondary_index", "add", "secondary_index_update_add_new_payer");
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace eosio { namespace chain {
|
||||
permission_name name,
|
||||
permission_id_type parent,
|
||||
const authority& auth,
|
||||
bool is_trx_transient,
|
||||
time_point initial_creation_time = time_point()
|
||||
);
|
||||
|
||||
@@ -38,12 +39,13 @@ namespace eosio { namespace chain {
|
||||
permission_name name,
|
||||
permission_id_type parent,
|
||||
authority&& auth,
|
||||
bool is_trx_transient,
|
||||
time_point initial_creation_time = time_point()
|
||||
);
|
||||
|
||||
void modify_permission( const permission_object& permission, const authority& auth );
|
||||
void modify_permission( const permission_object& permission, const authority& auth, bool is_trx_transient );
|
||||
|
||||
void remove_permission( const permission_object& permission );
|
||||
void remove_permission( const permission_object& permission, bool is_trx_transient );
|
||||
|
||||
void update_permission_usage( const permission_object& permission );
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
#include <fc/filesystem.hpp>
|
||||
#include <eosio/chain/block.hpp>
|
||||
#include <eosio/chain/genesis_state.hpp>
|
||||
#include <eosio/chain/block_log_config.hpp>
|
||||
|
||||
namespace eosio { namespace chain {
|
||||
|
||||
namespace detail { class block_log_impl; }
|
||||
namespace detail { struct block_log_impl; }
|
||||
|
||||
/* The block log is an external append only log of the blocks with a header. Blocks should only
|
||||
* be written to the log after they irreverisble as the log is append only. The log is a doubly
|
||||
@@ -34,18 +35,15 @@ namespace eosio { namespace chain {
|
||||
* An optional "pruned" mode can be activated which stores a 4 byte trailer on the log file indicating
|
||||
* how many blocks at the end of the log are valid. Any earlier blocks in the log are assumed destroyed
|
||||
* and unreadable due to reclamation for purposes of saving space.
|
||||
*
|
||||
* Object thread-safe. Not safe to have multiple block_log objects to same data_dir.
|
||||
*/
|
||||
|
||||
struct block_log_prune_config {
|
||||
uint32_t prune_blocks; //number of blocks to prune to when doing a prune
|
||||
size_t prune_threshold = 4*1024*1024; //(approximately) how many bytes need to be added before a prune is performed
|
||||
std::optional<size_t> vacuum_on_close; //when set, a vacuum is performed on dtor if log contains less than this many live bytes
|
||||
};
|
||||
|
||||
class block_log {
|
||||
public:
|
||||
block_log(const fc::path& data_dir, std::optional<block_log_prune_config> prune_config);
|
||||
block_log(block_log&& other);
|
||||
explicit block_log(const fc::path& data_dir, const block_log_config& config = block_log_config{});
|
||||
block_log(block_log&& other) noexcept;
|
||||
~block_log();
|
||||
|
||||
void append(const signed_block_ptr& b, const block_id_type& id);
|
||||
@@ -54,12 +52,11 @@ namespace eosio { namespace chain {
|
||||
void flush();
|
||||
void reset( const genesis_state& gs, const signed_block_ptr& genesis_block );
|
||||
void reset( const chain_id_type& chain_id, uint32_t first_block_num );
|
||||
void remove(); // remove blocks.log and blocks.index
|
||||
|
||||
signed_block_ptr read_block(uint64_t file_pos)const;
|
||||
void read_block_header(block_header& bh, uint64_t file_pos)const;
|
||||
signed_block_ptr read_block_by_num(uint32_t block_num)const;
|
||||
std::optional<signed_block_header> read_block_header_by_num(uint32_t block_num)const;
|
||||
block_id_type read_block_id_by_num(uint32_t block_num)const;
|
||||
|
||||
signed_block_ptr read_block_by_id(const block_id_type& id)const {
|
||||
return read_block_by_num(block_header::num_from_id(id));
|
||||
}
|
||||
@@ -67,10 +64,11 @@ namespace eosio { namespace chain {
|
||||
/**
|
||||
* Return offset of block in file, or block_log::npos if it does not exist.
|
||||
*/
|
||||
uint64_t get_block_pos(uint32_t block_num) const;
|
||||
signed_block_ptr read_head()const;
|
||||
const signed_block_ptr& head()const;
|
||||
const block_id_type& head_id()const;
|
||||
|
||||
signed_block_ptr read_head()const; //use blocklog
|
||||
signed_block_ptr head()const;
|
||||
block_id_type head_id()const;
|
||||
|
||||
uint32_t first_block_num() const;
|
||||
|
||||
static const uint64_t npos = std::numeric_limits<uint64_t>::max();
|
||||
@@ -78,6 +76,10 @@ namespace eosio { namespace chain {
|
||||
static const uint32_t min_supported_version;
|
||||
static const uint32_t max_supported_version;
|
||||
|
||||
/**
|
||||
* All static methods expected to be called on quiescent block log
|
||||
*/
|
||||
|
||||
static fc::path repair_log( const fc::path& data_dir, uint32_t truncate_at_block = 0, const char* reversible_block_dir_name="" );
|
||||
|
||||
static std::optional<genesis_state> extract_genesis_state( const fc::path& data_dir );
|
||||
@@ -94,37 +96,24 @@ namespace eosio { namespace chain {
|
||||
|
||||
static bool is_pruned_log(const fc::path& data_dir);
|
||||
|
||||
static bool extract_block_range(const fc::path& block_dir, const fc::path&output_dir, block_num_type& start, block_num_type& end, bool rename_input=false);
|
||||
static void extract_block_range(const fc::path& block_dir, const fc::path&output_dir, block_num_type start, block_num_type end);
|
||||
|
||||
static bool trim_blocklog_front(const fc::path& block_dir, const fc::path& temp_dir, uint32_t truncate_at_block);
|
||||
static int trim_blocklog_end(const fc::path& block_dir, uint32_t n);
|
||||
|
||||
// used for unit test to generate older version blocklog
|
||||
static void set_initial_version(uint32_t);
|
||||
uint32_t version() const;
|
||||
uint64_t get_block_pos(uint32_t block_num) const;
|
||||
|
||||
/**
|
||||
* @param n Only test 1 block out of every n blocks. If n is 0, the interval is adjusted so that at most 8 blocks are tested.
|
||||
*/
|
||||
static void smoke_test(const fc::path& block_dir, uint32_t n);
|
||||
|
||||
static void split_blocklog(const fc::path& block_dir, const fc::path& dest_dir, uint32_t stride);
|
||||
static void merge_blocklogs(const fc::path& block_dir, const fc::path& dest_dir);
|
||||
private:
|
||||
void open(const fc::path& data_dir);
|
||||
void construct_index();
|
||||
|
||||
std::unique_ptr<detail::block_log_impl> my;
|
||||
};
|
||||
|
||||
//to derive blknum_offset==14 see block_header.hpp and note on disk struct is packed
|
||||
// block_timestamp_type timestamp; //bytes 0:3
|
||||
// account_name producer; //bytes 4:11
|
||||
// uint16_t confirmed; //bytes 12:13
|
||||
// block_id_type previous; //bytes 14:45, low 4 bytes is big endian block number of previous block
|
||||
|
||||
struct trim_data { //used by trim_blocklog_front(), trim_blocklog_end(), and smoke_test()
|
||||
trim_data(fc::path block_dir);
|
||||
~trim_data();
|
||||
uint64_t block_index(uint32_t n) const;
|
||||
uint64_t block_pos(uint32_t n);
|
||||
fc::path block_file_name, index_file_name; //full pathname for blocks.log and blocks.index
|
||||
uint32_t version = 0; //blocklog version
|
||||
uint32_t first_block = 0; //first block in blocks.log
|
||||
uint32_t last_block = 0; //last block in blocks.log
|
||||
FILE* blk_in = nullptr; //C style files for reading blocks.log and blocks.index
|
||||
FILE* ind_in = nullptr; //C style files for reading blocks.log and blocks.index
|
||||
//we use low level file IO because it is distinctly faster than C++ filebuf or iostream
|
||||
uint64_t first_block_pos = 0; //file position in blocks.log for the first block in the log
|
||||
genesis_state gs;
|
||||
chain_id_type chain_id;
|
||||
|
||||
static constexpr int blknum_offset{14}; //offset from start of block to 4 byte block number, valid for the only allowed versions
|
||||
};
|
||||
} }
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <variant>
|
||||
|
||||
namespace eosio { namespace chain {
|
||||
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
struct basic_blocklog_config {};
|
||||
|
||||
struct empty_blocklog_config {};
|
||||
|
||||
struct partitioned_blocklog_config {
|
||||
bfs::path retained_dir;
|
||||
bfs::path archive_dir;
|
||||
uint32_t stride = UINT32_MAX;
|
||||
uint32_t max_retained_files = UINT32_MAX;
|
||||
};
|
||||
|
||||
struct prune_blocklog_config {
|
||||
uint32_t prune_blocks; // number of blocks to prune to when doing a prune
|
||||
size_t prune_threshold =
|
||||
4 * 1024 * 1024; //(approximately) how many bytes need to be added before a prune is performed
|
||||
std::optional<size_t>
|
||||
vacuum_on_close; // when set, a vacuum is performed on dtor if log contains less than this many live bytes
|
||||
};
|
||||
|
||||
using block_log_config =
|
||||
std::variant<basic_blocklog_config, empty_blocklog_config, partitioned_blocklog_config, prune_blocklog_config>;
|
||||
|
||||
}} // namespace eosio::chain
|
||||
@@ -37,6 +37,10 @@ namespace chain {
|
||||
|
||||
void reflector_init()const;
|
||||
|
||||
static chain_id_type empty_chain_id() {
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
chain_id_type() = default;
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace eosio { namespace chain {
|
||||
flat_set< pair<account_name, action_name> > action_blacklist;
|
||||
flat_set<public_key_type> key_blacklist;
|
||||
path blocks_dir = chain::config::default_blocks_dir_name;
|
||||
std::optional<block_log_prune_config> prune_config;
|
||||
block_log_config blog;
|
||||
path state_dir = chain::config::default_state_dir_name;
|
||||
uint64_t state_size = chain::config::default_state_size;
|
||||
uint64_t state_guard_size = chain::config::default_state_guard_size;
|
||||
@@ -82,7 +82,7 @@ namespace eosio { namespace chain {
|
||||
bool allow_ram_billing_in_notify = false;
|
||||
uint32_t maximum_variable_signature_length = chain::config::default_max_variable_signature_length;
|
||||
bool disable_all_subjective_mitigations = false; //< for developer & testing purposes, can be configured using `disable-all-subjective-mitigations` when `EOSIO_DEVELOPER` build option is provided
|
||||
uint32_t terminate_at_block = 0; //< primarily for testing purposes
|
||||
uint32_t terminate_at_block = 0;
|
||||
bool integrity_hash_on_start= false;
|
||||
bool integrity_hash_on_stop = false;
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace eosio { namespace chain {
|
||||
void startup( std::function<void()> shutdown, std::function<bool()> check_shutdown, const genesis_state& genesis);
|
||||
void startup( std::function<void()> shutdown, std::function<bool()> check_shutdown);
|
||||
|
||||
void preactivate_feature( const digest_type& feature_digest );
|
||||
void preactivate_feature( const digest_type& feature_digest, bool is_trx_transient );
|
||||
|
||||
vector<digest_type> get_preactivated_protocol_features()const;
|
||||
|
||||
@@ -221,13 +221,6 @@ namespace eosio { namespace chain {
|
||||
|
||||
uint32_t fork_db_head_block_num()const;
|
||||
block_id_type fork_db_head_block_id()const;
|
||||
time_point fork_db_head_block_time()const;
|
||||
account_name fork_db_head_block_producer()const;
|
||||
|
||||
uint32_t fork_db_pending_head_block_num()const;
|
||||
block_id_type fork_db_pending_head_block_id()const;
|
||||
time_point fork_db_pending_head_block_time()const;
|
||||
account_name fork_db_pending_head_block_producer()const;
|
||||
|
||||
time_point pending_block_time()const;
|
||||
account_name pending_block_producer()const;
|
||||
@@ -243,13 +236,19 @@ namespace eosio { namespace chain {
|
||||
block_id_type last_irreversible_block_id() const;
|
||||
time_point last_irreversible_block_time() const;
|
||||
|
||||
// thread-safe
|
||||
signed_block_ptr fetch_block_by_number( uint32_t block_num )const;
|
||||
signed_block_ptr fetch_block_by_id( block_id_type id )const;
|
||||
|
||||
// thread-safe
|
||||
signed_block_ptr fetch_block_by_id( const block_id_type& id )const;
|
||||
// thread-safe
|
||||
std::optional<signed_block_header> fetch_block_header_by_number( uint32_t block_num )const;
|
||||
// thread-safe
|
||||
std::optional<signed_block_header> fetch_block_header_by_id( const block_id_type& id )const;
|
||||
// return block_state from forkdb, thread-safe
|
||||
block_state_ptr fetch_block_state_by_number( uint32_t block_num )const;
|
||||
// return block_state from forkdb, thread-safe
|
||||
block_state_ptr fetch_block_state_by_id( block_id_type id )const;
|
||||
|
||||
// thread-safe
|
||||
block_id_type get_block_id_for_num( uint32_t block_num )const;
|
||||
|
||||
sha256 calculate_integrity_hash();
|
||||
@@ -311,12 +310,13 @@ namespace eosio { namespace chain {
|
||||
void add_to_ram_correction( account_name account, uint64_t ram_bytes );
|
||||
bool all_subjective_mitigations_disabled()const;
|
||||
|
||||
deep_mind_handler* get_deep_mind_logger() const;
|
||||
deep_mind_handler* get_deep_mind_logger(bool is_trx_transient) const;
|
||||
void enable_deep_mind( deep_mind_handler* logger );
|
||||
uint32_t earliest_available_block_num() const;
|
||||
|
||||
#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED)
|
||||
vm::wasm_allocator& get_wasm_allocator();
|
||||
bool is_eos_vm_oc_enabled() const;
|
||||
#endif
|
||||
|
||||
static std::optional<uint64_t> convert_exception_to_error_code( const fc::exception& e );
|
||||
@@ -348,9 +348,8 @@ namespace eosio { namespace chain {
|
||||
if( n.good() ) {
|
||||
try {
|
||||
const auto& a = get_account( n );
|
||||
abi_def abi;
|
||||
if( abi_serializer::to_abi( a.abi, abi ))
|
||||
return abi_serializer( abi, yield );
|
||||
if( abi_def abi; abi_serializer::to_abi( a.abi, abi ))
|
||||
return abi_serializer( std::move(abi), yield );
|
||||
} FC_CAPTURE_AND_LOG((n))
|
||||
}
|
||||
return std::optional<abi_serializer>();
|
||||
@@ -371,6 +370,10 @@ namespace eosio { namespace chain {
|
||||
void replace_producer_keys( const public_key_type& key );
|
||||
void replace_account_keys( name account, name permission, const public_key_type& key );
|
||||
|
||||
void set_db_read_only_mode();
|
||||
void unset_db_read_only_mode();
|
||||
void init_thread_local_data();
|
||||
|
||||
private:
|
||||
friend class apply_context;
|
||||
friend class transaction_context;
|
||||
|
||||
@@ -573,6 +573,12 @@ namespace eosio { namespace chain {
|
||||
3170011, "The signer returned no valid block signatures" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( unsupported_multiple_block_signatures, producer_exception,
|
||||
3170012, "The signer returned multiple signatures but that is not supported" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( duplicate_snapshot_request, producer_exception,
|
||||
3170013, "Snapshot has been already scheduled with specified parameters" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( snapshot_request_not_found, producer_exception,
|
||||
3170014, "Snapshot request not found" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( invalid_snapshot_request, producer_exception,
|
||||
3170015, "Invalid snapshot request" )
|
||||
|
||||
FC_DECLARE_DERIVED_EXCEPTION( reversible_blocks_exception, chain_exception,
|
||||
3180000, "Reversible Blocks exception" )
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
#pragma once
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/iostreams/device/mapped_file.hpp>
|
||||
#include <fc/io/cfile.hpp>
|
||||
#include <fc/io/datastream.hpp>
|
||||
#include <regex>
|
||||
|
||||
namespace eosio {
|
||||
namespace chain {
|
||||
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
template <typename Lambda>
|
||||
void for_each_file_in_dir_matches(const bfs::path& dir, std::string pattern, Lambda&& lambda) {
|
||||
const std::regex my_filter(pattern);
|
||||
std::smatch what;
|
||||
bfs::directory_iterator end_itr; // Default ctor yields past-the-end
|
||||
for (bfs::directory_iterator p(dir); p != end_itr; ++p) {
|
||||
// Skip if not a file
|
||||
if (!bfs::is_regular_file(p->status()))
|
||||
continue;
|
||||
// skip if it does not match the pattern
|
||||
if (!std::regex_match(p->path().filename().string(), what, my_filter))
|
||||
continue;
|
||||
lambda(p->path());
|
||||
}
|
||||
}
|
||||
|
||||
struct null_verifier {
|
||||
template <typename LogData>
|
||||
void verify(const LogData&, const bfs::path&) {}
|
||||
};
|
||||
|
||||
template <typename LogData, typename LogIndex, typename LogVerifier = null_verifier>
|
||||
struct log_catalog {
|
||||
using block_num_t = uint32_t;
|
||||
|
||||
struct mapped_type {
|
||||
block_num_t last_block_num;
|
||||
bfs::path filename_base;
|
||||
};
|
||||
using collection_t = boost::container::flat_map<block_num_t, mapped_type>;
|
||||
using size_type = typename collection_t::size_type;
|
||||
static constexpr size_type npos = std::numeric_limits<size_type>::max();
|
||||
|
||||
bfs::path retained_dir;
|
||||
bfs::path archive_dir;
|
||||
size_type max_retained_files = std::numeric_limits<size_type>::max();
|
||||
collection_t collection;
|
||||
size_type active_index = npos;
|
||||
LogData log_data;
|
||||
LogIndex log_index;
|
||||
LogVerifier verifier;
|
||||
|
||||
bool empty() const { return collection.empty(); }
|
||||
|
||||
block_num_t first_block_num() const {
|
||||
if (empty())
|
||||
return std::numeric_limits<block_num_t>::max();
|
||||
return collection.begin()->first;
|
||||
}
|
||||
|
||||
block_num_t last_block_num() const {
|
||||
if (empty())
|
||||
return std::numeric_limits<block_num_t>::min();
|
||||
return collection.rbegin()->second.last_block_num;
|
||||
}
|
||||
|
||||
static bfs::path make_absolute_dir(const bfs::path& base_dir, bfs::path new_dir) {
|
||||
if (new_dir.is_relative())
|
||||
new_dir = base_dir / new_dir;
|
||||
|
||||
if (!bfs::is_directory(new_dir))
|
||||
bfs::create_directories(new_dir);
|
||||
|
||||
return new_dir;
|
||||
}
|
||||
|
||||
void open(const bfs::path& log_dir, const bfs::path& retained_path, const bfs::path& archive_path, const char* name,
|
||||
const char* suffix_pattern = R"(-\d+-\d+\.log)") {
|
||||
|
||||
retained_dir = make_absolute_dir(log_dir, retained_path.empty() ? log_dir : retained_path);
|
||||
if (!archive_path.empty()) {
|
||||
archive_dir = make_absolute_dir(log_dir, archive_path);
|
||||
}
|
||||
|
||||
for_each_file_in_dir_matches(retained_dir, std::string(name) + suffix_pattern, [this](bfs::path path) {
|
||||
auto log_path = path;
|
||||
auto index_path = path.replace_extension("index");
|
||||
auto path_without_extension = log_path.parent_path() / log_path.stem().string();
|
||||
|
||||
LogData log(log_path);
|
||||
|
||||
verifier.verify(log, log_path);
|
||||
|
||||
// check if index file matches the log file
|
||||
if (!index_matches_data(index_path, log))
|
||||
log.construct_index(index_path);
|
||||
|
||||
auto existing_itr = collection.find(log.first_block_num());
|
||||
if (existing_itr != collection.end()) {
|
||||
if (log.last_block_num() <= existing_itr->second.last_block_num) {
|
||||
wlog("${log_path} contains the overlapping range with ${existing_path}.log, dropping ${log_path} "
|
||||
"from catalog",
|
||||
("log_path", log_path.string())("existing_path", existing_itr->second.filename_base.string()));
|
||||
return;
|
||||
} else {
|
||||
wlog(
|
||||
"${log_path} contains the overlapping range with ${existing_path}.log, droping ${existing_path}.log "
|
||||
"from catelog",
|
||||
("log_path", log_path.string())("existing_path", existing_itr->second.filename_base.string()));
|
||||
}
|
||||
}
|
||||
|
||||
collection.insert_or_assign(log.first_block_num(), mapped_type{log.last_block_num(), path_without_extension});
|
||||
});
|
||||
}
|
||||
|
||||
bool index_matches_data(const bfs::path& index_path, LogData& log) const {
|
||||
if (!bfs::exists(index_path))
|
||||
return false;
|
||||
|
||||
auto num_blocks_in_index = bfs::file_size(index_path) / sizeof(uint64_t);
|
||||
if (num_blocks_in_index != log.num_blocks())
|
||||
return false;
|
||||
|
||||
// make sure the last 8 bytes of index and log matches
|
||||
fc::cfile index_file;
|
||||
index_file.set_file_path(index_path);
|
||||
index_file.open("r");
|
||||
index_file.seek_end(-sizeof(uint64_t));
|
||||
uint64_t pos;
|
||||
index_file.read(reinterpret_cast<char*>(&pos), sizeof(pos));
|
||||
return pos == log.last_block_position();
|
||||
}
|
||||
|
||||
std::optional<uint64_t> get_block_position(uint32_t block_num) {
|
||||
try {
|
||||
if (active_index != npos) {
|
||||
auto active_item = collection.nth(active_index);
|
||||
if (active_item->first <= block_num && block_num <= active_item->second.last_block_num) {
|
||||
return log_index.nth_block_position(block_num - log_data.first_block_num());
|
||||
}
|
||||
}
|
||||
if (block_num < first_block_num())
|
||||
return {};
|
||||
|
||||
auto it = --collection.upper_bound(block_num);
|
||||
|
||||
if (block_num <= it->second.last_block_num) {
|
||||
auto name = it->second.filename_base;
|
||||
log_data.open(name.replace_extension("log"));
|
||||
log_index.open(name.replace_extension("index"));
|
||||
active_index = collection.index_of(it);
|
||||
return log_index.nth_block_position(block_num - log_data.first_block_num());
|
||||
}
|
||||
return {};
|
||||
} catch (...) {
|
||||
active_index = npos;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
fc::datastream<fc::cfile>* ro_stream_for_block(uint32_t block_num) {
|
||||
auto pos = get_block_position(block_num);
|
||||
if (pos) {
|
||||
return &log_data.ro_stream_at(*pos);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename ...Rest>
|
||||
auto ro_stream_for_block(uint32_t block_num, Rest&& ...rest) -> std::optional<decltype( std::declval<LogData>().ro_stream_at(0, std::forward<Rest&&>(rest)...))> {
|
||||
auto pos = get_block_position(block_num);
|
||||
if (pos) {
|
||||
return log_data.ro_stream_at(*pos, std::forward<Rest&&>(rest)...);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<block_id_type> id_for_block(uint32_t block_num) {
|
||||
auto pos = get_block_position(block_num);
|
||||
if (pos) {
|
||||
return log_data.block_id_at(*pos);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static void rename_if_not_exists(bfs::path old_name, bfs::path new_name) {
|
||||
if (!bfs::exists(new_name)) {
|
||||
bfs::rename(old_name, new_name);
|
||||
} else {
|
||||
bfs::remove(old_name);
|
||||
wlog("${new_name} already exists, just removing ${old_name}",
|
||||
("old_name", old_name.string())("new_name", new_name.string()));
|
||||
}
|
||||
}
|
||||
|
||||
static void rename_bundle(bfs::path orig_path, bfs::path new_path) {
|
||||
rename_if_not_exists(orig_path.replace_extension(".log"), new_path.replace_extension(".log"));
|
||||
rename_if_not_exists(orig_path.replace_extension(".index"), new_path.replace_extension(".index"));
|
||||
}
|
||||
|
||||
/// Add a new entry into the catalog.
|
||||
///
|
||||
/// Notice that \c start_block_num must be monotonically increasing between the invocations of this function
|
||||
/// so that the new entry would be inserted at the end of the flat_map; otherwise, \c active_index would be
|
||||
/// invalidated and the mapping between the log data their block range would be wrong. This function is only used
|
||||
/// during the splitting of block log. Using this function for other purpose should make sure if the monotonically
|
||||
/// increasing block num guarantee can be met.
|
||||
void add(uint32_t start_block_num, uint32_t end_block_num, const bfs::path& dir, const char* name) {
|
||||
|
||||
const int bufsize = 64;
|
||||
char buf[bufsize];
|
||||
snprintf(buf, bufsize, "%s-%u-%u", name, start_block_num, end_block_num);
|
||||
bfs::path new_path = retained_dir / buf;
|
||||
rename_bundle(dir / name, new_path);
|
||||
size_type items_to_erase = 0;
|
||||
collection.emplace(start_block_num, mapped_type{end_block_num, new_path});
|
||||
if (collection.size() >= max_retained_files) {
|
||||
items_to_erase =
|
||||
max_retained_files > 0 ? collection.size() - max_retained_files : collection.size();
|
||||
|
||||
for (auto it = collection.begin(); it < collection.begin() + items_to_erase; ++it) {
|
||||
auto orig_name = it->second.filename_base;
|
||||
if (archive_dir.empty()) {
|
||||
// delete the old files when no backup dir is specified
|
||||
bfs::remove(orig_name.replace_extension("log"));
|
||||
bfs::remove(orig_name.replace_extension("index"));
|
||||
} else {
|
||||
// move the the archive dir
|
||||
rename_bundle(orig_name, archive_dir / orig_name.filename());
|
||||
}
|
||||
}
|
||||
collection.erase(collection.begin(), collection.begin() + items_to_erase);
|
||||
active_index = active_index == npos || active_index < items_to_erase
|
||||
? npos
|
||||
: active_index - items_to_erase;
|
||||
}
|
||||
}
|
||||
|
||||
/// Truncate the catalog so that the log/index bundle containing the block with \c block_num
|
||||
/// would be rename to \c new_name; the log/index bundles with blocks strictly higher
|
||||
/// than \c block_num would be deleted, and all the renamed/removed entries would be erased
|
||||
/// from the catalog.
|
||||
///
|
||||
/// \return if nonzero, it's the starting block number for the log/index bundle being renamed.
|
||||
uint32_t truncate(uint32_t block_num, bfs::path new_name) {
|
||||
if (collection.empty())
|
||||
return 0;
|
||||
|
||||
auto remove_files = [](typename collection_t::const_reference v) {
|
||||
auto name = v.second.filename_base;
|
||||
bfs::remove(name.replace_extension("log"));
|
||||
bfs::remove(name.replace_extension("index"));
|
||||
};
|
||||
|
||||
active_index = npos;
|
||||
auto it = collection.upper_bound(block_num);
|
||||
|
||||
if (it == collection.begin() || block_num > (it - 1)->second.last_block_num) {
|
||||
std::for_each(it, collection.end(), remove_files);
|
||||
collection.erase(it, collection.end());
|
||||
return 0;
|
||||
} else {
|
||||
auto truncate_it = --it;
|
||||
auto name = truncate_it->second.filename_base;
|
||||
bfs::rename(name.replace_extension("log"), new_name.replace_extension("log"));
|
||||
bfs::rename(name.replace_extension("index"), new_name.replace_extension("index"));
|
||||
std::for_each(truncate_it + 1, collection.end(), remove_files);
|
||||
auto result = truncate_it->first;
|
||||
collection.erase(truncate_it, collection.end());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace chain
|
||||
} // namespace eosio
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <fc/io/cfile.hpp>
|
||||
|
||||
namespace eosio {
|
||||
namespace chain {
|
||||
|
||||
template <typename T>
|
||||
T read_data_at(fc::datastream<fc::cfile>& file, std::size_t offset) {
|
||||
file.seek(offset);
|
||||
T value;
|
||||
fc::raw::unpack(file, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
class log_data_base {
|
||||
protected:
|
||||
fc::datastream<fc::cfile> file;
|
||||
|
||||
Derived* self() { return static_cast<Derived*>(this); }
|
||||
const Derived* self() const { return static_cast<const Derived*>(this); }
|
||||
|
||||
public:
|
||||
|
||||
log_data_base() = default;
|
||||
|
||||
void close() { file.close(); }
|
||||
bool is_open() const { return file.is_open(); }
|
||||
|
||||
uint64_t size() const { return self()->size(); }
|
||||
|
||||
uint32_t last_block_num() { return self()->block_num_at(last_block_position()); }
|
||||
uint64_t last_block_position() {
|
||||
uint32_t offset = sizeof(uint64_t);
|
||||
if (self()->is_currently_pruned())
|
||||
offset += sizeof(uint32_t);
|
||||
return read_data_at<uint64_t>(file, size() - offset);
|
||||
}
|
||||
|
||||
uint32_t num_blocks() {
|
||||
if (self()->first_block_position() == size())
|
||||
return 0;
|
||||
else if (self()->is_currently_pruned())
|
||||
return read_data_at<uint32_t>(file, size() - sizeof(uint32_t));
|
||||
return last_block_num() - self()->first_block_num() + 1;
|
||||
}
|
||||
};
|
||||
} // namespace chain
|
||||
} // namespace eosio
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <fc/io/cfile.hpp>
|
||||
|
||||
namespace eosio {
|
||||
namespace chain {
|
||||
/// copy up to n bytes from the current position of src to dest
|
||||
void copy_file_content(fc::cfile& src, fc::cfile& dest, uint64_t n = UINT64_MAX);
|
||||
|
||||
template <typename Exception>
|
||||
class log_index {
|
||||
fc::cfile file_;
|
||||
std::size_t num_blocks_ = 0;
|
||||
public:
|
||||
log_index() = default;
|
||||
log_index(const boost::filesystem::path& path) {
|
||||
open(path);
|
||||
}
|
||||
|
||||
void open(const boost::filesystem::path& path) {
|
||||
if (file_.is_open())
|
||||
file_.close();
|
||||
file_.set_file_path(path);
|
||||
file_.open("rb");
|
||||
file_.seek_end(0);
|
||||
num_blocks_ = file_.tellp()/ sizeof(uint64_t);
|
||||
EOS_ASSERT(file_.tellp() % sizeof(uint64_t) == 0, Exception,
|
||||
"The size of ${file} is not a multiple of sizeof(uint64_t)", ("file", path.generic_string()));
|
||||
}
|
||||
|
||||
bool is_open() const { return file_.is_open(); }
|
||||
|
||||
uint64_t back() { return nth_block_position(num_blocks()-1); }
|
||||
int num_blocks() const { return num_blocks_; }
|
||||
uint64_t nth_block_position(uint32_t n) {
|
||||
file_.seek(n*sizeof(uint64_t));
|
||||
uint64_t r;
|
||||
file_.read((char*)&r, sizeof(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
void copy_to(fc::cfile& dest, uint64_t nbytes) {
|
||||
file_.seek(0);
|
||||
copy_file_content(file_, dest, nbytes);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace chain
|
||||
} // namespace eosio
|
||||
@@ -335,4 +335,3 @@ FC_REFLECT_DERIVED( eosio::chain::producer_schedule_change_extension, (eosio::ch
|
||||
FC_REFLECT( eosio::chain::shared_block_signing_authority_v0, (threshold)(keys))
|
||||
FC_REFLECT( eosio::chain::shared_producer_authority, (producer_name)(authority) )
|
||||
FC_REFLECT( eosio::chain::shared_producer_authority_schedule, (version)(producers) )
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ protected:
|
||||
class protocol_feature_manager {
|
||||
public:
|
||||
|
||||
protocol_feature_manager( protocol_feature_set&& pfs, std::function<deep_mind_handler*()> get_deep_mind_logger );
|
||||
protocol_feature_manager( protocol_feature_set&& pfs, std::function<deep_mind_handler*(bool is_trx_transient)> get_deep_mind_logger );
|
||||
|
||||
class const_iterator {
|
||||
public:
|
||||
@@ -393,7 +393,7 @@ protected:
|
||||
bool _initialized = false;
|
||||
|
||||
private:
|
||||
std::function<deep_mind_handler*()> _get_deep_mind_logger;
|
||||
std::function<deep_mind_handler*(bool is_trx_transient)> _get_deep_mind_logger;
|
||||
};
|
||||
|
||||
std::optional<builtin_protocol_feature> read_builtin_protocol_feature( const fc::path& p );
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace eosio { namespace chain {
|
||||
class resource_limits_manager {
|
||||
public:
|
||||
|
||||
explicit resource_limits_manager(chainbase::database& db, std::function<deep_mind_handler*()> get_deep_mind_logger)
|
||||
explicit resource_limits_manager(chainbase::database& db, std::function<deep_mind_handler*(bool is_trx_transient)> get_deep_mind_logger)
|
||||
:_db(db),_get_deep_mind_logger(get_deep_mind_logger)
|
||||
{
|
||||
}
|
||||
@@ -74,17 +74,17 @@ namespace eosio { namespace chain {
|
||||
void add_to_snapshot( const snapshot_writer_ptr& snapshot ) const;
|
||||
void read_from_snapshot( const snapshot_reader_ptr& snapshot );
|
||||
|
||||
void initialize_account( const account_name& account );
|
||||
void initialize_account( const account_name& account, bool is_trx_transient );
|
||||
void set_block_parameters( const elastic_limit_parameters& cpu_limit_parameters, const elastic_limit_parameters& net_limit_parameters );
|
||||
|
||||
void update_account_usage( const flat_set<account_name>& accounts, uint32_t ordinal );
|
||||
void add_transaction_usage( const flat_set<account_name>& accounts, uint64_t cpu_usage, uint64_t net_usage, uint32_t ordinal );
|
||||
void add_transaction_usage( const flat_set<account_name>& accounts, uint64_t cpu_usage, uint64_t net_usage, uint32_t ordinal, bool is_trx_transient = false );
|
||||
|
||||
void add_pending_ram_usage( const account_name account, int64_t ram_delta );
|
||||
void add_pending_ram_usage( const account_name account, int64_t ram_delta, bool is_trx_transient = false );
|
||||
void verify_account_ram_usage( const account_name accunt )const;
|
||||
|
||||
/// set_account_limits returns true if new ram_bytes limit is more restrictive than the previously set one
|
||||
bool set_account_limits( const account_name& account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight);
|
||||
bool set_account_limits( const account_name& account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight, bool is_trx_transient);
|
||||
void get_account_limits( const account_name& account, int64_t& ram_bytes, int64_t& net_weight, int64_t& cpu_weight) const;
|
||||
|
||||
bool is_unlimited_cpu( const account_name& account ) const;
|
||||
@@ -114,7 +114,7 @@ namespace eosio { namespace chain {
|
||||
|
||||
private:
|
||||
chainbase::database& _db;
|
||||
std::function<deep_mind_handler*()> _get_deep_mind_logger;
|
||||
std::function<deep_mind_handler*(bool is_trx_transient)> _get_deep_mind_logger;
|
||||
};
|
||||
} } } /// eosio::chain
|
||||
|
||||
|
||||
@@ -12,27 +12,22 @@
|
||||
|
||||
namespace eosio { namespace chain {
|
||||
|
||||
inline constexpr uint64_t make_name_v( std::string_view str ) {
|
||||
return string_to_uint64_t(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for thread pool of boost asio io_context run.
|
||||
* Also names threads so that tools like htop can see thread name.
|
||||
* Example: named_thread_pool<eosio::chain::make_name_v("chain")> thread_pool;
|
||||
* @param NamePrefix is eosio::name appended with -## of thread.
|
||||
* A short name_prefix (6 chars or under) is recommended as console_appender uses 9 chars
|
||||
* for the thread name.
|
||||
* Example: named_thread_pool<struct net> thread_pool;
|
||||
* or: struct net{}; named_thread_pool<net> thread_pool;
|
||||
* @param NamePrefixTag is a type name appended with -## of thread.
|
||||
* A short NamePrefixTag type name (6 chars or under) is recommended as console_appender uses
|
||||
* 9 chars for the thread name.
|
||||
*/
|
||||
template<uint64_t NamePrefix>
|
||||
template<typename NamePrefixTag>
|
||||
class named_thread_pool {
|
||||
public:
|
||||
using on_except_t = std::function<void(const fc::exception& e)>;
|
||||
using init_t = std::function<void()>;
|
||||
|
||||
explicit named_thread_pool()
|
||||
: _name_prefix(NamePrefix)
|
||||
, _ioc()
|
||||
{}
|
||||
named_thread_pool() = default;
|
||||
|
||||
~named_thread_pool(){
|
||||
stop();
|
||||
@@ -45,14 +40,15 @@ namespace eosio { namespace chain {
|
||||
/// @param num_threads is number of threads spawned
|
||||
/// @param on_except is the function to call if io_context throws an exception, is called from thread pool thread.
|
||||
/// if an empty function then logs and rethrows exception on thread which will terminate.
|
||||
/// @param init is an optional function to call at startup to initialize any data.
|
||||
/// @throw assert_exception if already started and not stopped.
|
||||
void start( size_t num_threads, on_except_t on_except ) {
|
||||
void start( size_t num_threads, on_except_t on_except, init_t init = {} ) {
|
||||
FC_ASSERT( !_ioc_work, "Thread pool already started" );
|
||||
_ioc_work.emplace( boost::asio::make_work_guard( _ioc ) );
|
||||
_ioc.restart();
|
||||
_thread_pool.reserve( num_threads );
|
||||
for( size_t i = 0; i < num_threads; ++i ) {
|
||||
_thread_pool.emplace_back( std::thread( &named_thread_pool::run_thread, this, i, on_except ) );
|
||||
_thread_pool.emplace_back( std::thread( &named_thread_pool::run_thread, this, i, on_except, init ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,11 +63,16 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
private:
|
||||
void run_thread( size_t i, const on_except_t& on_except ) {
|
||||
// copying the name_prefix and i into lambda makes it available to be seen on the stack in the debugger
|
||||
std::string tn = _name_prefix.to_string() + "-" + std::to_string( i );
|
||||
void run_thread( size_t i, const on_except_t& on_except, const init_t& init ) {
|
||||
std::string tn = boost::core::demangle(typeid(this).name());
|
||||
auto offset = tn.rfind("::");
|
||||
if (offset != std::string::npos)
|
||||
tn.erase(0, offset+2);
|
||||
tn = tn.substr(0, tn.find('>')) + "-" + std::to_string( i );
|
||||
try {
|
||||
fc::set_os_thread_name( tn );
|
||||
if ( init )
|
||||
init();
|
||||
_ioc.run();
|
||||
} catch( const fc::exception& e ) {
|
||||
if( on_except ) {
|
||||
@@ -103,7 +104,6 @@ namespace eosio { namespace chain {
|
||||
private:
|
||||
using ioc_work_t = boost::asio::executor_work_guard<boost::asio::io_context::executor_type>;
|
||||
|
||||
eosio::chain::name _name_prefix;
|
||||
boost::asio::io_context _ioc;
|
||||
std::vector<std::thread> _thread_pool;
|
||||
std::optional<ioc_work_t> _ioc_work;
|
||||
|
||||
@@ -84,6 +84,8 @@ namespace eosio { namespace chain {
|
||||
void validate_referenced_accounts( const transaction& trx, bool enforce_actor_whitelist_blacklist )const;
|
||||
|
||||
bool is_dry_run()const { return trx_type == transaction_metadata::trx_type::dry_run; };
|
||||
bool is_read_only()const { return trx_type == transaction_metadata::trx_type::read_only; };
|
||||
bool is_transient()const { return trx_type == transaction_metadata::trx_type::read_only || trx_type == transaction_metadata::trx_type::dry_run; };
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ class transaction_metadata {
|
||||
input,
|
||||
implicit,
|
||||
scheduled,
|
||||
dry_run
|
||||
dry_run,
|
||||
read_only
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -73,6 +74,8 @@ class transaction_metadata {
|
||||
bool implicit() const { return _trx_type == trx_type::implicit; };
|
||||
bool scheduled() const { return _trx_type == trx_type::scheduled; };
|
||||
bool is_dry_run() const { return _trx_type == trx_type::dry_run; };
|
||||
bool is_read_only() const { return _trx_type == trx_type::read_only; };
|
||||
bool is_transient() const { return _trx_type == trx_type::read_only || _trx_type == trx_type::dry_run; };
|
||||
|
||||
/// Thread safe.
|
||||
/// @returns transaction_metadata_ptr or exception via future
|
||||
|
||||
@@ -45,6 +45,9 @@ namespace eosio { namespace chain {
|
||||
wasm_interface(vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const boost::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile);
|
||||
~wasm_interface();
|
||||
|
||||
// initialize exec per thread
|
||||
void init_thread_local_data();
|
||||
|
||||
//call before dtor to skip what can be minutes of dtor overhead with some runtimes; can cause leaks
|
||||
void indicate_shutting_down();
|
||||
|
||||
@@ -72,6 +75,7 @@ namespace eosio { namespace chain {
|
||||
const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, apply_context& context)> substitute_apply;
|
||||
private:
|
||||
unique_ptr<struct wasm_interface_impl> my;
|
||||
vm_type vm;
|
||||
};
|
||||
|
||||
} } // eosio::chain
|
||||
|
||||
@@ -50,11 +50,21 @@ namespace eosio { namespace chain {
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
struct eosvmoc_tier {
|
||||
eosvmoc_tier(const boost::filesystem::path& d, const eosvmoc::config& c, const chainbase::database& db)
|
||||
: cc(d, c, db), exec(cc),
|
||||
mem(wasm_constraints::maximum_linear_memory/wasm_constraints::wasm_page_size) {}
|
||||
: cc(d, c, db) {
|
||||
// construct exec for the main thread
|
||||
init_thread_local_data();
|
||||
}
|
||||
|
||||
// Support multi-threaded execution.
|
||||
void init_thread_local_data() {
|
||||
exec = std::make_unique<eosvmoc::executor>(cc);
|
||||
}
|
||||
|
||||
eosvmoc::code_cache_async cc;
|
||||
eosvmoc::executor exec;
|
||||
eosvmoc::memory mem;
|
||||
|
||||
// Each thread requires its own exec and mem. Defined in wasm_interface.cpp
|
||||
thread_local static std::unique_ptr<eosvmoc::executor> exec;
|
||||
thread_local static eosvmoc::memory mem;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -34,11 +34,16 @@ class eosvmoc_runtime : public eosio::chain::wasm_runtime_interface {
|
||||
const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) override;
|
||||
|
||||
void immediately_exit_currently_running_module() override;
|
||||
void init_thread_local_data() override;
|
||||
|
||||
friend eosvmoc_instantiated_module;
|
||||
eosvmoc::code_cache_sync cc;
|
||||
eosvmoc::executor exec;
|
||||
eosvmoc::memory mem;
|
||||
|
||||
// Defined in eos-vm-oc.cpp. Used for non-main thread in multi-threaded execution
|
||||
thread_local static std::unique_ptr<eosvmoc::executor> exec_thread_local;
|
||||
thread_local static eosvmoc::memory mem_thread_local;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
|
||||
#include <thread>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace std {
|
||||
template<> struct hash<eosio::chain::eosvmoc::code_tuple> {
|
||||
@@ -84,6 +85,9 @@ class code_cache_base {
|
||||
|
||||
template <typename T>
|
||||
void serialize_cache_index(fc::datastream<T>& ds);
|
||||
|
||||
std::thread::id _main_thread_id;
|
||||
bool is_main_thread() const;
|
||||
};
|
||||
|
||||
class code_cache_async : public code_cache_base {
|
||||
@@ -114,4 +118,4 @@ class code_cache_sync : public code_cache_base {
|
||||
const code_descriptor* const get_descriptor_for_code_sync(const digest_type& code_id, const uint8_t& vm_version);
|
||||
};
|
||||
|
||||
}}}
|
||||
}}}
|
||||
|
||||
@@ -27,6 +27,9 @@ class wasm_runtime_interface {
|
||||
virtual void immediately_exit_currently_running_module() = 0;
|
||||
|
||||
virtual ~wasm_runtime_interface();
|
||||
|
||||
// eosvmoc_runtime needs this
|
||||
virtual void init_thread_local_data() {};
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -569,7 +569,7 @@ Enables new `get_block_num` intrinsic which returns the current block number.
|
||||
|
||||
protocol_feature_manager::protocol_feature_manager(
|
||||
protocol_feature_set&& pfs,
|
||||
std::function<deep_mind_handler*()> get_deep_mind_logger
|
||||
std::function<deep_mind_handler*(bool is_trx_transient)> get_deep_mind_logger
|
||||
):_protocol_feature_set( std::move(pfs) ), _get_deep_mind_logger(get_deep_mind_logger)
|
||||
{
|
||||
_builtin_protocol_features.resize( _protocol_feature_set._recognized_builtin_protocol_features.size() );
|
||||
@@ -750,7 +750,8 @@ Enables new `get_block_num` intrinsic which returns the current block number.
|
||||
("digest", feature_digest)
|
||||
);
|
||||
|
||||
if (auto dm_logger = _get_deep_mind_logger()) {
|
||||
// activate_feature is called by init. no transaction specific logging is possible
|
||||
if (auto dm_logger = _get_deep_mind_logger(false)) {
|
||||
dm_logger->on_activate_feature(*itr);
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,8 @@ void resource_limits_manager::initialize_database() {
|
||||
state.virtual_net_limit = config.net_limit_parameters.max;
|
||||
});
|
||||
|
||||
if (auto dm_logger = _get_deep_mind_logger()) {
|
||||
// At startup, no transaction specific logging is possible
|
||||
if (auto dm_logger = _get_deep_mind_logger(false)) {
|
||||
dm_logger->on_init_resource_limits(config, state);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +94,7 @@ void resource_limits_manager::read_from_snapshot( const snapshot_reader_ptr& sna
|
||||
});
|
||||
}
|
||||
|
||||
void resource_limits_manager::initialize_account(const account_name& account) {
|
||||
void resource_limits_manager::initialize_account(const account_name& account, bool is_trx_transient) {
|
||||
const auto& limits = _db.create<resource_limits_object>([&]( resource_limits_object& bl ) {
|
||||
bl.owner = account;
|
||||
});
|
||||
@@ -101,7 +102,7 @@ void resource_limits_manager::initialize_account(const account_name& account) {
|
||||
const auto& usage = _db.create<resource_usage_object>([&]( resource_usage_object& bu ) {
|
||||
bu.owner = account;
|
||||
});
|
||||
if (auto dm_logger = _get_deep_mind_logger()) {
|
||||
if (auto dm_logger = _get_deep_mind_logger(is_trx_transient)) {
|
||||
dm_logger->on_newaccount_resource_limits(limits, usage);
|
||||
}
|
||||
}
|
||||
@@ -116,7 +117,9 @@ void resource_limits_manager::set_block_parameters(const elastic_limit_parameter
|
||||
c.cpu_limit_parameters = cpu_limit_parameters;
|
||||
c.net_limit_parameters = net_limit_parameters;
|
||||
|
||||
if (auto dm_logger = _get_deep_mind_logger()) {
|
||||
// set_block_parameters is called by controller::finalize_block,
|
||||
// where transaction specific logging is not possible
|
||||
if (auto dm_logger = _get_deep_mind_logger(false)) {
|
||||
dm_logger->on_update_resource_limits_config(c);
|
||||
}
|
||||
});
|
||||
@@ -133,7 +136,7 @@ void resource_limits_manager::update_account_usage(const flat_set<account_name>&
|
||||
}
|
||||
}
|
||||
|
||||
void resource_limits_manager::add_transaction_usage(const flat_set<account_name>& accounts, uint64_t cpu_usage, uint64_t net_usage, uint32_t time_slot ) {
|
||||
void resource_limits_manager::add_transaction_usage(const flat_set<account_name>& accounts, uint64_t cpu_usage, uint64_t net_usage, uint32_t time_slot, bool is_trx_transient ) {
|
||||
const auto& state = _db.get<resource_limits_state_object>();
|
||||
const auto& config = _db.get<resource_limits_config_object>();
|
||||
|
||||
@@ -149,7 +152,7 @@ void resource_limits_manager::add_transaction_usage(const flat_set<account_name>
|
||||
bu.net_usage.add( net_usage, time_slot, config.account_net_usage_average_window );
|
||||
bu.cpu_usage.add( cpu_usage, time_slot, config.account_cpu_usage_average_window );
|
||||
|
||||
if (auto dm_logger = _get_deep_mind_logger()) {
|
||||
if (auto dm_logger = _get_deep_mind_logger(is_trx_transient)) {
|
||||
dm_logger->on_update_account_usage(bu);
|
||||
}
|
||||
});
|
||||
@@ -205,7 +208,7 @@ void resource_limits_manager::add_transaction_usage(const flat_set<account_name>
|
||||
EOS_ASSERT( state.pending_net_usage <= config.net_limit_parameters.max, block_resource_exhausted, "Block has insufficient net resources" );
|
||||
}
|
||||
|
||||
void resource_limits_manager::add_pending_ram_usage( const account_name account, int64_t ram_delta ) {
|
||||
void resource_limits_manager::add_pending_ram_usage( const account_name account, int64_t ram_delta, bool is_trx_transient ) {
|
||||
if (ram_delta == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -220,7 +223,7 @@ void resource_limits_manager::add_pending_ram_usage( const account_name account,
|
||||
_db.modify( usage, [&]( auto& u ) {
|
||||
u.ram_usage += ram_delta;
|
||||
|
||||
if (auto dm_logger = _get_deep_mind_logger()) {
|
||||
if (auto dm_logger = _get_deep_mind_logger(is_trx_transient)) {
|
||||
dm_logger->on_ram_event(account, u.ram_usage, ram_delta);
|
||||
}
|
||||
});
|
||||
@@ -243,7 +246,7 @@ int64_t resource_limits_manager::get_account_ram_usage( const account_name& name
|
||||
}
|
||||
|
||||
|
||||
bool resource_limits_manager::set_account_limits( const account_name& account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight) {
|
||||
bool resource_limits_manager::set_account_limits( const account_name& account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight, bool is_trx_transient) {
|
||||
//const auto& usage = _db.get<resource_usage_object,by_owner>( account );
|
||||
/*
|
||||
* Since we need to delay these until the next resource limiting boundary, these are created in a "pending"
|
||||
@@ -289,7 +292,7 @@ bool resource_limits_manager::set_account_limits( const account_name& account, i
|
||||
pending_limits.net_weight = net_weight;
|
||||
pending_limits.cpu_weight = cpu_weight;
|
||||
|
||||
if (auto dm_logger = _get_deep_mind_logger()) {
|
||||
if (auto dm_logger = _get_deep_mind_logger(is_trx_transient)) {
|
||||
dm_logger->on_set_account_limits(pending_limits);
|
||||
}
|
||||
});
|
||||
@@ -356,7 +359,9 @@ void resource_limits_manager::process_account_limit_updates() {
|
||||
multi_index.remove(*itr);
|
||||
}
|
||||
|
||||
if (auto dm_logger = _get_deep_mind_logger()) {
|
||||
// process_account_limit_updates is called by controller::finalize_block,
|
||||
// where transaction specific logging is not possible
|
||||
if (auto dm_logger = _get_deep_mind_logger(false)) {
|
||||
dm_logger->on_update_resource_limits_state(state);
|
||||
}
|
||||
});
|
||||
@@ -376,7 +381,9 @@ void resource_limits_manager::process_block_usage(uint32_t block_num) {
|
||||
state.update_virtual_net_limit(config);
|
||||
state.pending_net_usage = 0;
|
||||
|
||||
if (auto dm_logger = _get_deep_mind_logger()) {
|
||||
// process_block_usage is called by controller::finalize_block,
|
||||
// where transaction specific logging is not possible
|
||||
if (auto dm_logger = _get_deep_mind_logger(false)) {
|
||||
dm_logger->on_update_resource_limits_state(state);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -62,16 +62,19 @@ fc::microseconds transaction::get_signature_keys( const vector<signature_type>&
|
||||
{ try {
|
||||
auto start = fc::time_point::now();
|
||||
recovered_pub_keys.clear();
|
||||
const digest_type digest = sig_digest(chain_id, cfd);
|
||||
|
||||
for(const signature_type& sig : signatures) {
|
||||
auto now = fc::time_point::now();
|
||||
EOS_ASSERT( now < deadline, tx_cpu_usage_exceeded, "transaction signature verification executed for too long ${time}us",
|
||||
("time", now - start)("now", now)("deadline", deadline)("start", start) );
|
||||
auto[ itr, successful_insertion ] = recovered_pub_keys.emplace( sig, digest );
|
||||
EOS_ASSERT( allow_duplicate_keys || successful_insertion, tx_duplicate_sig,
|
||||
"transaction includes more than one signature signed using the same key associated with public key: ${key}",
|
||||
("key", *itr ) );
|
||||
if ( !signatures.empty() ) {
|
||||
const digest_type digest = sig_digest(chain_id, cfd);
|
||||
|
||||
for(const signature_type& sig : signatures) {
|
||||
auto now = fc::time_point::now();
|
||||
EOS_ASSERT( now < deadline, tx_cpu_usage_exceeded, "transaction signature verification executed for too long ${time}us",
|
||||
("time", now - start)("now", now)("deadline", deadline)("start", start) );
|
||||
auto[ itr, successful_insertion ] = recovered_pub_keys.emplace( sig, digest );
|
||||
EOS_ASSERT( allow_duplicate_keys || successful_insertion, tx_duplicate_sig,
|
||||
"transaction includes more than one signature signed using the same key associated with public key: ${key}",
|
||||
("key", *itr ) );
|
||||
}
|
||||
}
|
||||
|
||||
return fc::time_point::now() - start;
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace eosio { namespace chain {
|
||||
,net_usage(trace->net_usage)
|
||||
,pseudo_start(s)
|
||||
{
|
||||
if (!c.skip_db_sessions()) {
|
||||
if (!c.skip_db_sessions() && !is_read_only()) {
|
||||
undo_session.emplace(c.mutable_db().start_undo_session(true));
|
||||
}
|
||||
trace->id = packed_trx.id();
|
||||
@@ -67,7 +67,7 @@ namespace eosio { namespace chain {
|
||||
trace->block_time = c.pending_block_time();
|
||||
trace->producer_block_id = c.pending_producer_block_id();
|
||||
|
||||
if(auto dm_logger = control.get_deep_mind_logger())
|
||||
if(auto dm_logger = c.get_deep_mind_logger(is_transient()))
|
||||
{
|
||||
dm_logger->on_start_transaction();
|
||||
}
|
||||
@@ -75,7 +75,7 @@ namespace eosio { namespace chain {
|
||||
|
||||
transaction_context::~transaction_context()
|
||||
{
|
||||
if(auto dm_logger = control.get_deep_mind_logger())
|
||||
if(auto dm_logger = control.get_deep_mind_logger(is_transient()))
|
||||
{
|
||||
dm_logger->on_end_transaction();
|
||||
}
|
||||
@@ -105,13 +105,13 @@ namespace eosio { namespace chain {
|
||||
_deadline = start + objective_duration_limit;
|
||||
|
||||
// Possibly lower net_limit to the maximum net usage a transaction is allowed to be billed
|
||||
if( cfg.max_transaction_net_usage <= net_limit ) {
|
||||
if( cfg.max_transaction_net_usage <= net_limit && !is_read_only() ) {
|
||||
net_limit = cfg.max_transaction_net_usage;
|
||||
net_limit_due_to_block = false;
|
||||
}
|
||||
|
||||
// Possibly lower objective_duration_limit to the maximum cpu usage a transaction is allowed to be billed
|
||||
if( cfg.max_transaction_cpu_usage <= objective_duration_limit.count() ) {
|
||||
if( cfg.max_transaction_cpu_usage <= objective_duration_limit.count() && !is_read_only() ) {
|
||||
objective_duration_limit = fc::microseconds(cfg.max_transaction_cpu_usage);
|
||||
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
|
||||
tx_cpu_usage_reason = tx_cpu_usage_exceeded_reason::on_chain_consensus_max_transaction_cpu_usage;
|
||||
@@ -138,53 +138,57 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
initial_objective_duration_limit = objective_duration_limit;
|
||||
|
||||
if( explicit_billed_cpu_time )
|
||||
validate_cpu_usage_to_bill( billed_cpu_time_us, std::numeric_limits<int64_t>::max(), false, subjective_cpu_bill_us); // Fail early if the amount to be billed is too high
|
||||
|
||||
// Record accounts to be billed for network and CPU usage
|
||||
if( control.is_builtin_activated(builtin_protocol_feature_t::only_bill_first_authorizer) ) {
|
||||
bill_to_accounts.insert( trx.first_authorizer() );
|
||||
} else {
|
||||
for( const auto& act : trx.actions ) {
|
||||
for( const auto& auth : act.authorization ) {
|
||||
bill_to_accounts.insert( auth.actor );
|
||||
}
|
||||
}
|
||||
}
|
||||
validate_ram_usage.reserve( bill_to_accounts.size() );
|
||||
|
||||
// Update usage values of accounts to reflect new time
|
||||
rl.update_account_usage( bill_to_accounts, block_timestamp_type(control.pending_block_time()).slot );
|
||||
|
||||
// Calculate the highest network usage and CPU time that all of the billed accounts can afford to be billed
|
||||
int64_t account_net_limit = 0;
|
||||
int64_t account_cpu_limit = 0;
|
||||
bool greylisted_net = false, greylisted_cpu = false;
|
||||
std::tie( account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu) = max_bandwidth_billed_accounts_can_pay();
|
||||
net_limit_due_to_greylist |= greylisted_net;
|
||||
cpu_limit_due_to_greylist |= greylisted_cpu;
|
||||
|
||||
if ( !is_read_only() ) {
|
||||
if( explicit_billed_cpu_time )
|
||||
validate_cpu_usage_to_bill( billed_cpu_time_us, std::numeric_limits<int64_t>::max(), false, subjective_cpu_bill_us); // Fail early if the amount to be billed is too high
|
||||
|
||||
// Record accounts to be billed for network and CPU usage
|
||||
if( control.is_builtin_activated(builtin_protocol_feature_t::only_bill_first_authorizer) ) {
|
||||
bill_to_accounts.insert( trx.first_authorizer() );
|
||||
} else {
|
||||
for( const auto& act : trx.actions ) {
|
||||
for( const auto& auth : act.authorization ) {
|
||||
bill_to_accounts.insert( auth.actor );
|
||||
}
|
||||
}
|
||||
}
|
||||
validate_ram_usage.reserve( bill_to_accounts.size() );
|
||||
|
||||
// Update usage values of accounts to reflect new time
|
||||
rl.update_account_usage( bill_to_accounts, block_timestamp_type(control.pending_block_time()).slot );
|
||||
|
||||
// Calculate the highest network usage and CPU time that all of the billed accounts can afford to be billed
|
||||
bool greylisted_net = false, greylisted_cpu = false;
|
||||
std::tie( account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu) = max_bandwidth_billed_accounts_can_pay();
|
||||
net_limit_due_to_greylist |= greylisted_net;
|
||||
cpu_limit_due_to_greylist |= greylisted_cpu;
|
||||
}
|
||||
|
||||
eager_net_limit = net_limit;
|
||||
|
||||
// Possibly lower eager_net_limit to what the billed accounts can pay plus some (objective) leeway
|
||||
auto new_eager_net_limit = std::min( eager_net_limit, static_cast<uint64_t>(account_net_limit + cfg.net_usage_leeway) );
|
||||
if( new_eager_net_limit < eager_net_limit ) {
|
||||
eager_net_limit = new_eager_net_limit;
|
||||
net_limit_due_to_block = false;
|
||||
}
|
||||
if ( !is_read_only() ) {
|
||||
// Possibly lower eager_net_limit to what the billed accounts can pay plus some (objective) leeway
|
||||
auto new_eager_net_limit = std::min( eager_net_limit, static_cast<uint64_t>(account_net_limit + cfg.net_usage_leeway) );
|
||||
if( new_eager_net_limit < eager_net_limit ) {
|
||||
eager_net_limit = new_eager_net_limit;
|
||||
net_limit_due_to_block = false;
|
||||
}
|
||||
|
||||
// Possibly limit deadline if the duration accounts can be billed for (+ a subjective leeway) does not exceed current delta
|
||||
if( (fc::microseconds(account_cpu_limit) + leeway) <= (_deadline - start) ) {
|
||||
_deadline = start + fc::microseconds(account_cpu_limit) + leeway;
|
||||
billing_timer_exception_code = leeway_deadline_exception::code_value;
|
||||
// Possibly limit deadline if the duration accounts can be billed for (+ a subjective leeway) does not exceed current delta
|
||||
if( (fc::microseconds(account_cpu_limit) + leeway) <= (_deadline - start) ) {
|
||||
_deadline = start + fc::microseconds(account_cpu_limit) + leeway;
|
||||
billing_timer_exception_code = leeway_deadline_exception::code_value;
|
||||
}
|
||||
}
|
||||
|
||||
// Possibly limit deadline to subjective max_transaction_time
|
||||
if( max_transaction_time_subjective != fc::microseconds::maximum() && (start + max_transaction_time_subjective) <= _deadline ) {
|
||||
_deadline = start + max_transaction_time_subjective;
|
||||
tx_cpu_usage_reason = billed_cpu_time_us > 0 ? tx_cpu_usage_exceeded_reason::speculative_executed_adjusted_max_transaction_time :
|
||||
tx_cpu_usage_exceeded_reason::node_configured_max_transaction_time;
|
||||
tx_cpu_usage_reason = billed_cpu_time_us > 0 ?
|
||||
tx_cpu_usage_exceeded_reason::speculative_executed_adjusted_max_transaction_time : tx_cpu_usage_exceeded_reason::node_configured_max_transaction_time;
|
||||
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
|
||||
}
|
||||
|
||||
@@ -194,20 +198,22 @@ namespace eosio { namespace chain {
|
||||
billing_timer_exception_code = deadline_exception::code_value;
|
||||
}
|
||||
|
||||
if( !explicit_billed_cpu_time ) {
|
||||
int64_t validate_account_cpu_limit = account_cpu_limit - subjective_cpu_bill_us + leeway.count(); // Add leeway to allow powerup
|
||||
// Possibly limit deadline to account subjective cpu left
|
||||
if( subjective_cpu_bill_us > 0 && (start + fc::microseconds(validate_account_cpu_limit) < _deadline) ) {
|
||||
_deadline = start + fc::microseconds(validate_account_cpu_limit);
|
||||
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
|
||||
tx_cpu_usage_reason = tx_cpu_usage_exceeded_reason::account_cpu_limit;
|
||||
}
|
||||
if ( !is_read_only() ) {
|
||||
if( !explicit_billed_cpu_time ) {
|
||||
int64_t validate_account_cpu_limit = account_cpu_limit - subjective_cpu_bill_us + leeway.count(); // Add leeway to allow powerup
|
||||
// Possibly limit deadline to account subjective cpu left
|
||||
if( subjective_cpu_bill_us > 0 && (start + fc::microseconds(validate_account_cpu_limit) < _deadline) ) {
|
||||
_deadline = start + fc::microseconds(validate_account_cpu_limit);
|
||||
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
|
||||
tx_cpu_usage_reason = tx_cpu_usage_exceeded_reason::account_cpu_limit;
|
||||
}
|
||||
|
||||
// Fail early if amount of the previous speculative execution is within 10% of remaining account cpu available
|
||||
if( validate_account_cpu_limit > 0 )
|
||||
validate_account_cpu_limit -= EOS_PERCENT( validate_account_cpu_limit, 10 * config::percent_1 );
|
||||
if( validate_account_cpu_limit < 0 ) validate_account_cpu_limit = 0;
|
||||
validate_account_cpu_usage_estimate( billed_cpu_time_us, validate_account_cpu_limit, subjective_cpu_bill_us );
|
||||
// Fail early if amount of the previous speculative execution is within 10% of remaining account cpu available
|
||||
if( validate_account_cpu_limit > 0 )
|
||||
validate_account_cpu_limit -= EOS_PERCENT( validate_account_cpu_limit, 10 * config::percent_1 );
|
||||
if( validate_account_cpu_limit < 0 ) validate_account_cpu_limit = 0;
|
||||
validate_account_cpu_usage_estimate( billed_cpu_time_us, validate_account_cpu_limit, subjective_cpu_bill_us );
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit billed_cpu_time_us should be used, block_deadline will be maximum unless in test code
|
||||
@@ -248,6 +254,9 @@ namespace eosio { namespace chain {
|
||||
uint64_t packed_trx_prunable_size )
|
||||
{
|
||||
const transaction& trx = packed_trx.get_transaction();
|
||||
if ( is_transient() ) {
|
||||
EOS_ASSERT( trx.delay_sec.value == 0, transaction_exception, "dry-run or read-only transaction cannot be delayed" );
|
||||
}
|
||||
if( trx.transaction_extensions.size() > 0 ) {
|
||||
disallow_transaction_extensions( "no transaction extensions supported yet for input transactions" );
|
||||
}
|
||||
@@ -277,12 +286,17 @@ namespace eosio { namespace chain {
|
||||
published = control.pending_block_time();
|
||||
is_input = true;
|
||||
if (!control.skip_trx_checks()) {
|
||||
control.validate_expiration(trx);
|
||||
control.validate_tapos(trx);
|
||||
if ( !is_read_only() ) {
|
||||
control.validate_expiration(trx);
|
||||
control.validate_tapos(trx);
|
||||
}
|
||||
validate_referenced_accounts( trx, enforce_whiteblacklist && control.is_speculative_block() );
|
||||
}
|
||||
init( initial_net_usage);
|
||||
record_transaction( packed_trx.id(), trx.expiration ); /// checks for dupes
|
||||
|
||||
init( initial_net_usage );
|
||||
if ( !is_read_only() ) {
|
||||
record_transaction( packed_trx.id(), trx.expiration );
|
||||
}
|
||||
}
|
||||
|
||||
void transaction_context::init_for_deferred_trx( fc::time_point p )
|
||||
@@ -332,6 +346,13 @@ namespace eosio { namespace chain {
|
||||
void transaction_context::finalize() {
|
||||
EOS_ASSERT( is_initialized, transaction_exception, "must first initialize" );
|
||||
|
||||
// read-only transactions only need net_usage and elapsed in the trace
|
||||
if ( is_read_only() ) {
|
||||
net_usage = ((net_usage + 7)/8)*8; // Round up to nearest multiple of word size (8 bytes)
|
||||
trace->elapsed = fc::time_point::now() - start;
|
||||
return;
|
||||
}
|
||||
|
||||
if( is_input ) {
|
||||
const transaction& trx = packed_trx.get_transaction();
|
||||
auto& am = control.get_mutable_authorization_manager();
|
||||
@@ -383,7 +404,7 @@ namespace eosio { namespace chain {
|
||||
validate_cpu_usage_to_bill( billed_cpu_time_us, account_cpu_limit, true, subjective_cpu_bill_us );
|
||||
|
||||
rl.add_transaction_usage( bill_to_accounts, static_cast<uint64_t>(billed_cpu_time_us), net_usage,
|
||||
block_timestamp_type(control.pending_block_time()).slot ); // Should never fail
|
||||
block_timestamp_type(control.pending_block_time()).slot, is_transient() ); // Should never fail
|
||||
}
|
||||
|
||||
void transaction_context::squash() {
|
||||
@@ -588,7 +609,7 @@ namespace eosio { namespace chain {
|
||||
|
||||
void transaction_context::add_ram_usage( account_name account, int64_t ram_delta ) {
|
||||
auto& rl = control.get_mutable_resource_limits_manager();
|
||||
rl.add_pending_ram_usage( account, ram_delta );
|
||||
rl.add_pending_ram_usage( account, ram_delta, is_transient() );
|
||||
if( ram_delta > 0 ) {
|
||||
validate_ram_usage.insert( account );
|
||||
}
|
||||
@@ -709,7 +730,7 @@ namespace eosio { namespace chain {
|
||||
apply_context acontext( control, *this, action_ordinal, recurse_depth );
|
||||
|
||||
if (recurse_depth == 0) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(is_transient())) {
|
||||
dm_logger->on_input_action();
|
||||
}
|
||||
}
|
||||
@@ -741,7 +762,7 @@ namespace eosio { namespace chain {
|
||||
gto.expiration = gto.delay_until + fc::seconds(control.get_global_properties().configuration.deferred_trx_expiration_window);
|
||||
trx_size = gto.set( trx );
|
||||
|
||||
if (auto dm_logger = control.get_deep_mind_logger()) {
|
||||
if (auto dm_logger = control.get_deep_mind_logger(is_transient())) {
|
||||
std::string event_id = RAM_EVENT_ID("${id}", ("id", gto.id));
|
||||
|
||||
dm_logger->on_create_deferred(deep_mind_handler::operation_qualifier::push, gto, packed_trx);
|
||||
@@ -789,6 +810,10 @@ namespace eosio { namespace chain {
|
||||
auto* code = db.find<account_object, by_name>(a.account);
|
||||
EOS_ASSERT( code != nullptr, transaction_exception,
|
||||
"action's code account '${account}' does not exist", ("account", a.account) );
|
||||
if ( is_read_only() ) {
|
||||
EOS_ASSERT( a.authorization.size() == 0, transaction_exception,
|
||||
"read-only action '${name}' cannot have authorizations", ("name", a.name) );
|
||||
}
|
||||
for( const auto& auth : a.authorization ) {
|
||||
one_auth = true;
|
||||
auto* actor = db.find<account_object, by_name>(auth.actor);
|
||||
@@ -801,7 +826,7 @@ namespace eosio { namespace chain {
|
||||
actors.insert( auth.actor );
|
||||
}
|
||||
}
|
||||
EOS_ASSERT( one_auth || is_dry_run(), tx_no_auths, "transaction must have at least one authorization" );
|
||||
EOS_ASSERT( one_auth || is_transient(), tx_no_auths, "transaction must have at least one authorization" );
|
||||
|
||||
if( enforce_actor_whitelist_blacklist ) {
|
||||
control.check_actor_list( actors );
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <softfloat.hpp>
|
||||
#include <compiler_builtins.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <fstream>
|
||||
#include <string.h>
|
||||
|
||||
@@ -34,10 +33,19 @@
|
||||
namespace eosio { namespace chain {
|
||||
|
||||
wasm_interface::wasm_interface(vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const boost::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile)
|
||||
: my( new wasm_interface_impl(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile) ) {}
|
||||
: my( new wasm_interface_impl(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile) ), vm( vm ) {}
|
||||
|
||||
wasm_interface::~wasm_interface() {}
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
void wasm_interface::init_thread_local_data() {
|
||||
if (my->eosvmoc)
|
||||
my->eosvmoc->init_thread_local_data();
|
||||
else if (vm == wasm_interface::vm_type::eos_vm_oc && my->runtime_interface)
|
||||
my->runtime_interface->init_thread_local_data();
|
||||
}
|
||||
#endif
|
||||
|
||||
void wasm_interface::validate(const controller& control, const bytes& code) {
|
||||
const auto& pso = control.db().get<protocol_state_object>();
|
||||
|
||||
@@ -96,7 +104,7 @@ namespace eosio { namespace chain {
|
||||
once_is_enough = true;
|
||||
}
|
||||
if(cd) {
|
||||
my->eosvmoc->exec.execute(*cd, my->eosvmoc->mem, context);
|
||||
my->eosvmoc->exec->execute(*cd, my->eosvmoc->mem, context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -115,6 +123,11 @@ namespace eosio { namespace chain {
|
||||
wasm_instantiated_module_interface::~wasm_instantiated_module_interface() {}
|
||||
wasm_runtime_interface::~wasm_runtime_interface() {}
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
thread_local std::unique_ptr<eosvmoc::executor> wasm_interface_impl::eosvmoc_tier::exec {};
|
||||
thread_local eosvmoc::memory wasm_interface_impl::eosvmoc_tier::mem{ wasm_constraints::maximum_linear_memory/wasm_constraints::wasm_page_size };
|
||||
#endif
|
||||
|
||||
std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime) {
|
||||
std::string s;
|
||||
in >> s;
|
||||
|
||||
@@ -24,9 +24,10 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
void interface::set_action_return_value( span<const char> packed_blob ) {
|
||||
auto max_action_return_value_size =
|
||||
context.control.get_global_properties().configuration.max_action_return_value_size;
|
||||
EOS_ASSERT(packed_blob.size() <= max_action_return_value_size,
|
||||
action_return_value_exception,
|
||||
"action return value size must be less or equal to ${s} bytes", ("s", max_action_return_value_size));
|
||||
if( !context.trx_context.is_read_only() )
|
||||
EOS_ASSERT(packed_blob.size() <= max_action_return_value_size,
|
||||
action_return_value_exception,
|
||||
"action return value size must be less or equal to ${s} bytes", ("s", max_action_return_value_size));
|
||||
context.action_return_value.assign( packed_blob.data(), packed_blob.data() + packed_blob.size() );
|
||||
}
|
||||
}}} // ns eosio::chain::webassembly
|
||||
|
||||
@@ -17,14 +17,16 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
}
|
||||
|
||||
void interface::preactivate_feature( legacy_ptr<const digest_type> feature_digest ) {
|
||||
context.control.preactivate_feature( *feature_digest );
|
||||
EOS_ASSERT(!context.trx_context.is_read_only(), wasm_execution_error, "preactivate_feature not allowed in a readonly transaction");
|
||||
context.control.preactivate_feature( *feature_digest, context.trx_context.is_transient() );
|
||||
}
|
||||
|
||||
void interface::set_resource_limits( account_name account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight ) {
|
||||
EOS_ASSERT(!context.trx_context.is_read_only(), wasm_execution_error, "set_resource_limits not allowed in a readonly transaction");
|
||||
EOS_ASSERT(ram_bytes >= -1, wasm_execution_error, "invalid value for ram resource limit expected [-1,INT64_MAX]");
|
||||
EOS_ASSERT(net_weight >= -1, wasm_execution_error, "invalid value for net resource weight expected [-1,INT64_MAX]");
|
||||
EOS_ASSERT(cpu_weight >= -1, wasm_execution_error, "invalid value for cpu resource weight expected [-1,INT64_MAX]");
|
||||
if( context.control.get_mutable_resource_limits_manager().set_account_limits(account, ram_bytes, net_weight, cpu_weight) ) {
|
||||
if( context.control.get_mutable_resource_limits_manager().set_account_limits(account, ram_bytes, net_weight, cpu_weight, context.trx_context.is_transient()) ) {
|
||||
context.trx_context.validate_ram_usage.insert( account );
|
||||
}
|
||||
}
|
||||
@@ -99,6 +101,7 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
return s;
|
||||
}
|
||||
void interface::set_wasm_parameters_packed( span<const char> packed_parameters ) {
|
||||
EOS_ASSERT(!context.trx_context.is_read_only(), wasm_execution_error, "set_wasm_parameters_packed not allowed in a readonly transaction");
|
||||
datastream<const char*> ds( packed_parameters.data(), packed_parameters.size() );
|
||||
uint32_t version;
|
||||
chain::wasm_config cfg;
|
||||
@@ -113,6 +116,7 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
);
|
||||
}
|
||||
int64_t interface::set_proposed_producers( legacy_span<const char> packed_producer_schedule) {
|
||||
EOS_ASSERT(!context.trx_context.is_read_only(), wasm_execution_error, "set_proposed_producers not allowed in a readonly transaction");
|
||||
datastream<const char*> ds( packed_producer_schedule.data(), packed_producer_schedule.size() );
|
||||
std::vector<producer_authority> producers;
|
||||
std::vector<legacy::producer_key> old_version;
|
||||
@@ -129,6 +133,7 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
}
|
||||
|
||||
int64_t interface::set_proposed_producers_ex( uint64_t packed_producer_format, legacy_span<const char> packed_producer_schedule) {
|
||||
EOS_ASSERT(!context.trx_context.is_read_only(), wasm_execution_error, "set_proposed_producers_ex not allowed in a readonly transaction");
|
||||
if (packed_producer_format == 0) {
|
||||
return set_proposed_producers(std::move(packed_producer_schedule));
|
||||
} else if (packed_producer_format == 1) {
|
||||
@@ -157,6 +162,7 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
}
|
||||
|
||||
void interface::set_blockchain_parameters_packed( legacy_span<const char> packed_blockchain_parameters ) {
|
||||
EOS_ASSERT(!context.trx_context.is_read_only(), wasm_execution_error, "set_blockchain_parameters_packed not allowed in a readonly transaction");
|
||||
datastream<const char*> ds( packed_blockchain_parameters.data(), packed_blockchain_parameters.size() );
|
||||
chain::chain_config_v0 cfg;
|
||||
fc::raw::unpack(ds, cfg);
|
||||
@@ -188,6 +194,7 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
}
|
||||
|
||||
void interface::set_parameters_packed( span<const char> packed_parameters ){
|
||||
EOS_ASSERT(!context.trx_context.is_read_only(), wasm_execution_error, "set_parameters_packed not allowed in a readonly transaction");
|
||||
datastream<const char*> ds( packed_parameters.data(), packed_parameters.size() );
|
||||
|
||||
chain::chain_config cfg = context.control.get_global_properties().configuration;
|
||||
@@ -207,6 +214,7 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
}
|
||||
|
||||
void interface::set_privileged( account_name n, bool is_priv ) {
|
||||
EOS_ASSERT(!context.trx_context.is_read_only(), wasm_execution_error, "set_privileged not allowed in a readonly transaction");
|
||||
const auto& a = context.db.get<account_metadata_object, by_name>( n );
|
||||
context.db.modify( a, [&]( auto& ma ){
|
||||
ma.set_privileged( is_priv );
|
||||
|
||||
@@ -15,7 +15,8 @@ class eosvmoc_instantiated_module : public wasm_instantiated_module_interface {
|
||||
eosvmoc_instantiated_module(const digest_type& code_hash, const uint8_t& vm_version, eosvmoc_runtime& wr) :
|
||||
_code_hash(code_hash),
|
||||
_vm_version(vm_version),
|
||||
_eosvmoc_runtime(wr)
|
||||
_eosvmoc_runtime(wr),
|
||||
_main_thread_id(std::this_thread::get_id())
|
||||
{
|
||||
|
||||
}
|
||||
@@ -24,16 +25,22 @@ class eosvmoc_instantiated_module : public wasm_instantiated_module_interface {
|
||||
_eosvmoc_runtime.cc.free_code(_code_hash, _vm_version);
|
||||
}
|
||||
|
||||
bool is_main_thread() { return _main_thread_id == std::this_thread::get_id(); };
|
||||
|
||||
void apply(apply_context& context) override {
|
||||
const code_descriptor* const cd = _eosvmoc_runtime.cc.get_descriptor_for_code_sync(_code_hash, _vm_version);
|
||||
EOS_ASSERT(cd, wasm_execution_error, "EOS VM OC instantiation failed");
|
||||
|
||||
_eosvmoc_runtime.exec.execute(*cd, _eosvmoc_runtime.mem, context);
|
||||
if ( is_main_thread() )
|
||||
_eosvmoc_runtime.exec.execute(*cd, _eosvmoc_runtime.mem, context);
|
||||
else
|
||||
_eosvmoc_runtime.exec_thread_local->execute(*cd, _eosvmoc_runtime.mem_thread_local, context);
|
||||
}
|
||||
|
||||
const digest_type _code_hash;
|
||||
const uint8_t _vm_version;
|
||||
eosvmoc_runtime& _eosvmoc_runtime;
|
||||
std::thread::id _main_thread_id;
|
||||
};
|
||||
|
||||
eosvmoc_runtime::eosvmoc_runtime(const boost::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db)
|
||||
@@ -45,11 +52,17 @@ eosvmoc_runtime::~eosvmoc_runtime() {
|
||||
|
||||
std::unique_ptr<wasm_instantiated_module_interface> eosvmoc_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector<uint8_t> initial_memory,
|
||||
const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) {
|
||||
|
||||
return std::make_unique<eosvmoc_instantiated_module>(code_hash, vm_type, *this);
|
||||
}
|
||||
|
||||
//never called. EOS VM OC overrides eosio_exit to its own implementation
|
||||
void eosvmoc_runtime::immediately_exit_currently_running_module() {}
|
||||
|
||||
void eosvmoc_runtime::init_thread_local_data() {
|
||||
exec_thread_local = std::make_unique<eosvmoc::executor>(cc);
|
||||
}
|
||||
|
||||
thread_local std::unique_ptr<eosvmoc::executor> eosvmoc_runtime::exec_thread_local {};
|
||||
thread_local eosvmoc::memory eosvmoc_runtime::mem_thread_local{ wasm_constraints::maximum_linear_memory/wasm_constraints::wasm_page_size };
|
||||
|
||||
}}}}
|
||||
|
||||
@@ -106,9 +106,11 @@ std::tuple<size_t, size_t> code_cache_async::consume_compile_thread_queue() {
|
||||
return {gotsome, bytes_remaining};
|
||||
}
|
||||
|
||||
|
||||
const code_descriptor* const code_cache_async::get_descriptor_for_code(const digest_type& code_id, const uint8_t& vm_version) {
|
||||
//if there are any outstanding compiles, process the result queue now
|
||||
if(_outstanding_compiles_and_poison.size()) {
|
||||
//do this only on main thread (which is in single threaded write window)
|
||||
if(is_main_thread() && _outstanding_compiles_and_poison.size()) {
|
||||
auto [count_processed, bytes_remaining] = consume_compile_thread_queue();
|
||||
|
||||
if(count_processed)
|
||||
@@ -134,9 +136,12 @@ const code_descriptor* const code_cache_async::get_descriptor_for_code(const dig
|
||||
//check for entry in cache
|
||||
code_cache_index::index<by_hash>::type::iterator it = _cache_index.get<by_hash>().find(boost::make_tuple(code_id, vm_version));
|
||||
if(it != _cache_index.get<by_hash>().end()) {
|
||||
_cache_index.relocate(_cache_index.begin(), _cache_index.project<0>(it));
|
||||
if (is_main_thread())
|
||||
_cache_index.relocate(_cache_index.begin(), _cache_index.project<0>(it));
|
||||
return &*it;
|
||||
}
|
||||
if(!is_main_thread()) // on read-only thread
|
||||
return nullptr;
|
||||
|
||||
const code_tuple ct = code_tuple{code_id, vm_version};
|
||||
|
||||
@@ -178,9 +183,12 @@ const code_descriptor* const code_cache_sync::get_descriptor_for_code_sync(const
|
||||
//check for entry in cache
|
||||
code_cache_index::index<by_hash>::type::iterator it = _cache_index.get<by_hash>().find(boost::make_tuple(code_id, vm_version));
|
||||
if(it != _cache_index.get<by_hash>().end()) {
|
||||
_cache_index.relocate(_cache_index.begin(), _cache_index.project<0>(it));
|
||||
if (is_main_thread())
|
||||
_cache_index.relocate(_cache_index.begin(), _cache_index.project<0>(it));
|
||||
return &*it;
|
||||
}
|
||||
if(!is_main_thread())
|
||||
return nullptr;
|
||||
|
||||
const code_object* const codeobject = _db.find<code_object,by_code_hash>(boost::make_tuple(code_id, 0, vm_version));
|
||||
if(!codeobject) //should be impossible right?
|
||||
@@ -204,7 +212,8 @@ const code_descriptor* const code_cache_sync::get_descriptor_for_code_sync(const
|
||||
|
||||
code_cache_base::code_cache_base(const boost::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db) :
|
||||
_db(db),
|
||||
_cache_file_path(data_dir/"code_cache.bin")
|
||||
_cache_file_path(data_dir/"code_cache.bin"),
|
||||
_main_thread_id(std::this_thread::get_id())
|
||||
{
|
||||
static_assert(sizeof(allocator_t) <= header_offset, "header offset intersects with allocator");
|
||||
|
||||
@@ -382,4 +391,7 @@ void code_cache_base::check_eviction_threshold(size_t free_bytes) {
|
||||
run_eviction_round();
|
||||
}
|
||||
|
||||
bool code_cache_base::is_main_thread() const {
|
||||
return _main_thread_id == std::this_thread::get_id();
|
||||
}
|
||||
}}}
|
||||
|
||||
+1
-1
Submodule libraries/chainbase updated: 1f058fff94...50540fa7bc
@@ -0,0 +1,5 @@
|
||||
add_library(custom_appbase INTERFACE)
|
||||
target_include_directories(custom_appbase INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(custom_appbase INTERFACE appbase)
|
||||
|
||||
add_subdirectory(tests)
|
||||
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include <appbase/application_base.hpp>
|
||||
#include <appbase/execution_priority_queue.hpp>
|
||||
|
||||
/*
|
||||
* Custmomize appbase to support two-queue exector.
|
||||
*/
|
||||
namespace appbase {
|
||||
|
||||
enum class exec_window {
|
||||
read, // the window during which operations from read_only_trx_safe queue
|
||||
// can be executed in app thread in parallel with read-only transactions
|
||||
// in read-only transaction excuting threads.
|
||||
write, // the window during which operations from both general and
|
||||
// read_only_trx_safe queues can be executed in app thread,
|
||||
// while read-only transactions are not executed in read-only
|
||||
// transaction excuting threads.
|
||||
};
|
||||
|
||||
enum class exec_queue {
|
||||
read_only_trx_safe, // the queue storing operations which are safe to execute
|
||||
// on app thread in parallel with read-only transactions
|
||||
// in read-only transaction excuting threads.
|
||||
general // the queue storing operations which can be only executed
|
||||
// on the app thread while read-only transactions are
|
||||
// not being executed in read-only threads
|
||||
};
|
||||
|
||||
class two_queue_executor {
|
||||
public:
|
||||
|
||||
template <typename Func>
|
||||
auto post( int priority, exec_queue q, Func&& func ) {
|
||||
if ( q == exec_queue::general )
|
||||
return boost::asio::post(io_serv_, general_queue_.wrap(priority, --order_, std::forward<Func>(func)));
|
||||
else
|
||||
return boost::asio::post(io_serv_, read_only_trx_safe_queue_.wrap(priority, --order_, std::forward<Func>(func)));
|
||||
}
|
||||
|
||||
// Legacy and deprecated. To be removed after cleaning up its uses in base appbase
|
||||
template <typename Func>
|
||||
auto post( int priority, Func&& func ) {
|
||||
// safer to use general queue for unknown type of operation since operations
|
||||
// from general queue are not executed in parallel with read-only transactions
|
||||
return boost::asio::post(io_serv_, general_queue_.wrap(priority, --order_, std::forward<Func>(func)));
|
||||
}
|
||||
|
||||
boost::asio::io_service& get_io_service() { return io_serv_; }
|
||||
|
||||
bool execute_highest() {
|
||||
if ( exec_window_ == exec_window::write ) {
|
||||
if( !general_queue_.empty() && ( read_only_trx_safe_queue_.empty() || *read_only_trx_safe_queue_.top() < *general_queue_.top()) ) {
|
||||
// general_queue_'s top function's priority greater than read_only_trx_safe_queue_'s top function's, or general_queue_ empty
|
||||
general_queue_.execute_highest();
|
||||
} else if( !read_only_trx_safe_queue_.empty() ) {
|
||||
read_only_trx_safe_queue_.execute_highest();
|
||||
}
|
||||
return !read_only_trx_safe_queue_.empty() || !general_queue_.empty();
|
||||
} else {
|
||||
return read_only_trx_safe_queue_.execute_highest();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
boost::asio::executor_binder<Function, appbase::execution_priority_queue::executor>
|
||||
wrap(int priority, exec_queue q, Function&& func ) {
|
||||
if ( q == exec_queue::general )
|
||||
return general_queue_.wrap(priority, --order_, std::forward<Function>(func));
|
||||
else
|
||||
return read_only_trx_safe_queue_.wrap(priority, --order_, std::forward<Function>(func));
|
||||
}
|
||||
|
||||
void clear() {
|
||||
read_only_trx_safe_queue_.clear();
|
||||
general_queue_.clear();
|
||||
}
|
||||
|
||||
void set_to_read_window() {
|
||||
exec_window_ = exec_window::read;
|
||||
}
|
||||
|
||||
void set_to_write_window() {
|
||||
exec_window_ = exec_window::write;
|
||||
}
|
||||
|
||||
bool is_read_window() const {
|
||||
return exec_window_ == exec_window::read;
|
||||
}
|
||||
|
||||
bool is_write_window() const {
|
||||
return exec_window_ == exec_window::write;
|
||||
}
|
||||
|
||||
auto& read_only_trx_safe_queue() { return read_only_trx_safe_queue_; }
|
||||
|
||||
auto& general_queue() { return general_queue_; }
|
||||
|
||||
// members are ordered taking into account that the last one is destructed first
|
||||
private:
|
||||
boost::asio::io_service io_serv_;
|
||||
appbase::execution_priority_queue read_only_trx_safe_queue_;
|
||||
appbase::execution_priority_queue general_queue_;
|
||||
std::atomic<std::size_t> order_ { std::numeric_limits<size_t>::max() }; // to maintain FIFO ordering in both queues within priority
|
||||
exec_window exec_window_ { exec_window::write };
|
||||
};
|
||||
|
||||
using application = application_t<two_queue_executor>;
|
||||
}
|
||||
|
||||
#include <appbase/application_instance.hpp>
|
||||
@@ -0,0 +1,13 @@
|
||||
if(CMAKE_CXX_STANDARD EQUAL 98 OR CMAKE_CXX_STANDARD LESS 17)
|
||||
message(FATAL_ERROR "appbase requires c++17 or newer")
|
||||
elseif(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
file(GLOB UNIT_TESTS "*.cpp")
|
||||
add_executable( custom_appbase_test ${UNIT_TESTS} )
|
||||
target_link_libraries( custom_appbase_test appbase ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
|
||||
target_include_directories( custom_appbase_test PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include" "${CMAKE_CURRENT_SOURCE_DIR}/../../appbase/include" )
|
||||
|
||||
add_test( custom_appbase_test custom_appbase_test )
|
||||
@@ -0,0 +1,215 @@
|
||||
#define BOOST_TEST_MODULE custom_appbase_tests
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
#include <eosio/chain/application.hpp>
|
||||
|
||||
using namespace appbase;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(custom_appbase_tests)
|
||||
|
||||
std::thread start_app_thread(appbase::scoped_app& app) {
|
||||
const char* argv[] = { boost::unit_test::framework::current_test_case().p_name->c_str() };
|
||||
BOOST_CHECK(app->initialize(sizeof(argv) / sizeof(char*), const_cast<char**>(argv)));
|
||||
app->startup();
|
||||
std::thread app_thread( [&]() {
|
||||
app->exec();
|
||||
} );
|
||||
return app_thread;
|
||||
}
|
||||
|
||||
// verify functions from both queues are executed when execution window is not explictly set
|
||||
BOOST_AUTO_TEST_CASE( default_exec_window ) {
|
||||
appbase::scoped_app app;
|
||||
auto app_thread = start_app_thread(app);
|
||||
|
||||
// post functions
|
||||
std::map<int, int> rslts {};
|
||||
int seq_num = 0;
|
||||
app->executor().post( priority::medium, exec_queue::read_only_trx_safe, [&]() { rslts[0]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::medium, exec_queue::general, [&]() { rslts[1]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::general, [&]() { rslts[2]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::read_only_trx_safe, [&]() { rslts[3]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::low, exec_queue::read_only_trx_safe, [&]() { rslts[4]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::low, exec_queue::general, [&]() { rslts[5]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::highest,exec_queue::read_only_trx_safe, [&]() { rslts[6]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::general, [&]() { rslts[7]=seq_num; ++seq_num; } );
|
||||
|
||||
// Stop app. Use the lowest priority to make sure this function to execute the last
|
||||
app->executor().post( priority::lowest, exec_queue::read_only_trx_safe, [&]() {
|
||||
// read_only_trx_safe_queue should only contain the current lambda function,
|
||||
// and general_queue should have execute all its functions
|
||||
BOOST_REQUIRE_EQUAL( app->executor().read_only_trx_safe_queue().size(), 1);
|
||||
BOOST_REQUIRE_EQUAL( app->executor().general_queue().size(), 0 );
|
||||
app->quit();
|
||||
} );
|
||||
app_thread.join();
|
||||
|
||||
// both queues are cleared after execution
|
||||
BOOST_REQUIRE_EQUAL( app->executor().read_only_trx_safe_queue().empty(), true);
|
||||
BOOST_REQUIRE_EQUAL( app->executor().general_queue().empty(), true);
|
||||
|
||||
// exactly number of both queues' functions processed
|
||||
BOOST_REQUIRE_EQUAL( rslts.size(), 8 );
|
||||
|
||||
// same priority of functions executed by the post order
|
||||
BOOST_CHECK_LT( rslts[0], rslts[1] ); // medium
|
||||
BOOST_CHECK_LT( rslts[2], rslts[3] ); // high
|
||||
BOOST_CHECK_LT( rslts[3], rslts[7] ); // high
|
||||
BOOST_CHECK_LT( rslts[4], rslts[5] ); // low
|
||||
|
||||
// higher priority posted earlier executed earlier
|
||||
BOOST_CHECK_LT( rslts[3], rslts[4] );
|
||||
BOOST_CHECK_LT( rslts[6], rslts[7] );
|
||||
}
|
||||
|
||||
// verify functions only from read_only_trx_safe queue are processed during read window
|
||||
BOOST_AUTO_TEST_CASE( execute_from_read_queue ) {
|
||||
appbase::scoped_app app;
|
||||
auto app_thread = start_app_thread(app);
|
||||
|
||||
// set to run functions from read_only_trx_safe queue only
|
||||
app->executor().set_to_read_window();
|
||||
|
||||
// post functions
|
||||
std::map<int, int> rslts {};
|
||||
int seq_num = 0;
|
||||
app->executor().post( priority::medium, exec_queue::general, [&]() { rslts[0]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::read_only_trx_safe, [&]() { rslts[1]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::general, [&]() { rslts[2]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::read_only_trx_safe, [&]() { rslts[3]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::low, exec_queue::read_only_trx_safe, [&]() { rslts[4]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::low, exec_queue::general, [&]() { rslts[5]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::highest,exec_queue::read_only_trx_safe, [&]() { rslts[6]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::highest,exec_queue::read_only_trx_safe, [&]() { rslts[7]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::read_only_trx_safe, [&]() { rslts[8]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::general, [&]() { rslts[9]=seq_num; ++seq_num; } );
|
||||
|
||||
// stop application. Use lowest at the end to make sure this executes the last
|
||||
app->executor().post( priority::lowest, exec_queue::read_only_trx_safe, [&]() {
|
||||
// read_queue should have current function and write_queue should have all its functions
|
||||
BOOST_REQUIRE_EQUAL( app->executor().read_only_trx_safe_queue().size(), 1);
|
||||
BOOST_REQUIRE_EQUAL( app->executor().general_queue().size(), 4 );
|
||||
app->quit();
|
||||
} );
|
||||
app_thread.join();
|
||||
|
||||
// both queues are cleared after execution
|
||||
BOOST_REQUIRE_EQUAL( app->executor().read_only_trx_safe_queue().empty(), true);
|
||||
BOOST_REQUIRE_EQUAL( app->executor().general_queue().empty(), true);
|
||||
|
||||
// exactly number of posts processed
|
||||
BOOST_REQUIRE_EQUAL( rslts.size(), 6 );
|
||||
|
||||
// same priority (high) of functions in read_queue executed by the post order
|
||||
BOOST_CHECK_LT( rslts[1], rslts[3] );
|
||||
|
||||
// higher priority posted earlier in read_queue executed earlier
|
||||
BOOST_CHECK_LT( rslts[3], rslts[4] );
|
||||
}
|
||||
|
||||
// verify no functions are executed during read window if read_only_trx_safe queue is empty
|
||||
BOOST_AUTO_TEST_CASE( execute_from_empty_read_queue ) {
|
||||
appbase::scoped_app app;
|
||||
auto app_thread = start_app_thread(app);
|
||||
|
||||
// set to run functions from read_only_trx_safe queue only
|
||||
app->executor().set_to_read_window();
|
||||
|
||||
// post functions
|
||||
std::map<int, int> rslts {};
|
||||
int seq_num = 0;
|
||||
app->executor().post( priority::medium, exec_queue::general, [&]() { rslts[0]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::general, [&]() { rslts[1]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::general, [&]() { rslts[2]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::general, [&]() { rslts[3]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::low, exec_queue::general, [&]() { rslts[4]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::low, exec_queue::general, [&]() { rslts[5]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::highest,exec_queue::general, [&]() { rslts[6]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::highest,exec_queue::general, [&]() { rslts[7]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::general, [&]() { rslts[8]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::general, [&]() { rslts[9]=seq_num; ++seq_num; } );
|
||||
|
||||
// Stop application. Use lowest at the end to make sure this executes the last
|
||||
app->executor().post( priority::lowest, exec_queue::read_only_trx_safe, [&]() {
|
||||
// read_queue should have current function and write_queue should have all its functions
|
||||
BOOST_REQUIRE_EQUAL( app->executor().read_only_trx_safe_queue().size(), 1);
|
||||
BOOST_REQUIRE_EQUAL( app->executor().general_queue().size(), 10 );
|
||||
app->quit();
|
||||
} );
|
||||
app_thread.join();
|
||||
|
||||
// both queues are cleared after execution
|
||||
BOOST_REQUIRE_EQUAL( app->executor().read_only_trx_safe_queue().empty(), true);
|
||||
BOOST_REQUIRE_EQUAL( app->executor().general_queue().empty(), true);
|
||||
|
||||
// no results
|
||||
BOOST_REQUIRE_EQUAL( rslts.size(), 0 );
|
||||
}
|
||||
|
||||
// verify functions from both queues are processed in write window
|
||||
BOOST_AUTO_TEST_CASE( execute_from_both_queues ) {
|
||||
appbase::scoped_app app;
|
||||
auto app_thread = start_app_thread(app);
|
||||
|
||||
// set to run functions from both queues
|
||||
app->executor().is_write_window();
|
||||
|
||||
// post functions
|
||||
std::map<int, int> rslts {};
|
||||
int seq_num = 0;
|
||||
app->executor().post( priority::medium, exec_queue::read_only_trx_safe, [&]() { rslts[0]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::medium, exec_queue::general, [&]() { rslts[1]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::high, exec_queue::general, [&]() { rslts[2]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::lowest, exec_queue::read_only_trx_safe, [&]() { rslts[3]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::low, exec_queue::read_only_trx_safe, [&]() { rslts[4]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::low, exec_queue::general, [&]() { rslts[5]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::highest,exec_queue::read_only_trx_safe, [&]() { rslts[6]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::low, exec_queue::general, [&]() { rslts[7]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::lowest, exec_queue::read_only_trx_safe, [&]() { rslts[8]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::lowest, exec_queue::read_only_trx_safe, [&]() { rslts[9]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::low, exec_queue::general, [&]() { rslts[10]=seq_num; ++seq_num; } );
|
||||
app->executor().post( priority::medium, exec_queue::general, [&]() { rslts[11]=seq_num; ++seq_num; } );
|
||||
|
||||
// stop application. Use lowest at the end to make sure this executes the last
|
||||
app->executor().post( priority::lowest, exec_queue::read_only_trx_safe, [&]() {
|
||||
// read_queue should have current function and write_queue's functions are all executed
|
||||
BOOST_REQUIRE_EQUAL( app->executor().read_only_trx_safe_queue().size(), 1);
|
||||
BOOST_REQUIRE_EQUAL( app->executor().general_queue().size(), 0 );
|
||||
app->quit();
|
||||
} );
|
||||
|
||||
app_thread.join();
|
||||
|
||||
// queues are emptied after quit
|
||||
BOOST_REQUIRE_EQUAL( app->executor().read_only_trx_safe_queue().empty(), true);
|
||||
BOOST_REQUIRE_EQUAL( app->executor().general_queue().empty(), true);
|
||||
|
||||
// exactly number of posts processed
|
||||
BOOST_REQUIRE_EQUAL( rslts.size(), 12 );
|
||||
|
||||
// all low must be processed the in order of posting
|
||||
BOOST_CHECK_LT( rslts[4], rslts[5] );
|
||||
BOOST_CHECK_LT( rslts[5], rslts[7] );
|
||||
BOOST_CHECK_LT( rslts[7], rslts[10] );
|
||||
|
||||
// all medium must be processed the in order of posting
|
||||
BOOST_CHECK_LT( rslts[0], rslts[1] );
|
||||
BOOST_CHECK_LT( rslts[1], rslts[11] );
|
||||
|
||||
// all functions posted after high before highest must be processed after high
|
||||
BOOST_CHECK_LT( rslts[2], rslts[3] );
|
||||
BOOST_CHECK_LT( rslts[2], rslts[4] );
|
||||
BOOST_CHECK_LT( rslts[2], rslts[5] );
|
||||
|
||||
// all functions posted after highest must be processed after it
|
||||
BOOST_CHECK_LT( rslts[6], rslts[7] );
|
||||
BOOST_CHECK_LT( rslts[6], rslts[8] );
|
||||
BOOST_CHECK_LT( rslts[6], rslts[9] );
|
||||
BOOST_CHECK_LT( rslts[6], rslts[10] );
|
||||
BOOST_CHECK_LT( rslts[6], rslts[11] );
|
||||
BOOST_CHECK_LT( rslts[6], rslts[11] );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -140,7 +140,7 @@ if(APPLE)
|
||||
find_library(corefoundation_framework CoreFoundation)
|
||||
endif()
|
||||
target_link_libraries( fc PUBLIC Boost::date_time Boost::filesystem Boost::chrono Boost::iostreams Threads::Threads
|
||||
OpenSSL::Crypto OpenSSL::SSL ZLIB::ZLIB ${PLATFORM_SPECIFIC_LIBS} ${CMAKE_DL_LIBS} secp256k1 ${security_framework} ${corefoundation_framework})
|
||||
OpenSSL::Crypto ZLIB::ZLIB ${PLATFORM_SPECIFIC_LIBS} ${CMAKE_DL_LIBS} secp256k1 ${security_framework} ${corefoundation_framework})
|
||||
|
||||
# Critically, this ensures that OpenSSL 1.1 & 3.0 both have a variant of BN_zero() with void return value. But it also allows access
|
||||
# to some obsoleted AES functions in 3.0 too, since 3.0's API_COMPAT is effectively 3.0 by default
|
||||
|
||||
@@ -22,6 +22,10 @@ class sha256
|
||||
char* data();
|
||||
size_t data_size() const { return 256 / 8; }
|
||||
|
||||
bool empty()const {
|
||||
return (_hash[0] | _hash[1] | _hash[2] | _hash[3]) == 0;
|
||||
}
|
||||
|
||||
static sha256 hash( const char* d, uint32_t dlen );
|
||||
static sha256 hash( const string& );
|
||||
static sha256 hash( const sha256& );
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace fc
|
||||
code_value = unspecified_exception_code
|
||||
};
|
||||
|
||||
exception( int64_t code = unspecified_exception_code,
|
||||
explicit exception( int64_t code = unspecified_exception_code,
|
||||
const std::string& name_value = "exception",
|
||||
const std::string& what_value = "unspecified");
|
||||
exception( log_message&&, int64_t code = unspecified_exception_code,
|
||||
|
||||
@@ -190,8 +190,8 @@ public:
|
||||
|
||||
bool eof() const { return feof(_file.get()) != 0; }
|
||||
|
||||
int getc() {
|
||||
int ret = fgetc(_file.get());
|
||||
int getc() {
|
||||
int ret = fgetc(_file.get());
|
||||
if (ret == EOF) {
|
||||
throw std::ios_base::failure( "cfile: " + _file_path.generic_string() +
|
||||
" unable to read 1 byte");
|
||||
@@ -210,6 +210,15 @@ public:
|
||||
|
||||
cfile_datastream create_datastream();
|
||||
|
||||
|
||||
friend void swap(cfile& lhs, cfile& rhs) {
|
||||
using std::swap;
|
||||
swap(lhs._open, rhs._open);
|
||||
swap(lhs._file_path, rhs._file_path);
|
||||
swap(lhs._file_blk_size, rhs._file_blk_size);
|
||||
swap(lhs._file, rhs._file);
|
||||
}
|
||||
|
||||
private:
|
||||
bool _open = false;
|
||||
fc::path _file_path;
|
||||
@@ -264,6 +273,11 @@ class datastream<fc::cfile, void> : public fc::cfile {
|
||||
|
||||
fc::cfile& storage() { return *this; }
|
||||
const fc::cfile& storage() const { return *this; }
|
||||
|
||||
friend void swap(datastream<fc::cfile, void>& lhs, datastream<fc::cfile, void>& rhs) {
|
||||
using std::swap;
|
||||
swap(static_cast<fc::cfile&>(lhs), static_cast<fc::cfile&>(rhs));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -97,10 +97,12 @@ class datastream<size_t, void> {
|
||||
};
|
||||
|
||||
template <typename Streambuf>
|
||||
class datastream<Streambuf, typename std::enable_if_t<std::is_base_of_v<std::streambuf, Streambuf>>> {
|
||||
class datastream<Streambuf, typename std::enable_if_t<std::is_base_of_v<std::streambuf, std::remove_reference_t<Streambuf>>>> {
|
||||
private:
|
||||
Streambuf buf;
|
||||
|
||||
using reference_type = std::add_lvalue_reference_t<Streambuf>;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
datastream(Args&&... args)
|
||||
@@ -120,8 +122,8 @@ class datastream<Streambuf, typename std::enable_if_t<std::is_base_of_v<std::str
|
||||
}
|
||||
bool remaining() { return buf.in_avail(); }
|
||||
|
||||
Streambuf& storage() { return buf; }
|
||||
const Streambuf& storage() const { return buf; }
|
||||
reference_type storage() { return buf; }
|
||||
const reference_type storage() const { return buf; }
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
|
||||
@@ -35,7 +35,6 @@ namespace fc
|
||||
static constexpr uint64_t max_length_limit = std::numeric_limits<uint64_t>::max();
|
||||
static constexpr size_t escape_string_yield_check_count = 128;
|
||||
static variant from_string( const string& utf8_str, const parse_type ptype = parse_type::legacy_parser, uint32_t max_depth = DEFAULT_MAX_RECURSION_DEPTH );
|
||||
static variants variants_from_string( const string& utf8_str, const parse_type ptype = parse_type::legacy_parser, uint32_t max_depth = DEFAULT_MAX_RECURSION_DEPTH );
|
||||
static string to_string( const variant& v, const yield_function_t& yield, const output_formatting format = output_formatting::stringify_large_ints_and_doubles);
|
||||
static string to_pretty_string( const variant& v, const yield_function_t& yield, const output_formatting format = output_formatting::stringify_large_ints_and_doubles );
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace fc {
|
||||
public:
|
||||
typedef std::shared_ptr<appender> ptr;
|
||||
|
||||
virtual void initialize( boost::asio::io_service& io_service ) = 0;
|
||||
virtual void initialize() = 0;
|
||||
virtual void log( const log_message& m ) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace fc
|
||||
namespace fc
|
||||
{
|
||||
class console_appender final : public appender
|
||||
class console_appender final : public appender
|
||||
{
|
||||
public:
|
||||
struct color
|
||||
struct color
|
||||
{
|
||||
enum type {
|
||||
red,
|
||||
@@ -24,9 +24,9 @@ namespace fc
|
||||
|
||||
struct stream { enum type { std_out, std_error }; };
|
||||
|
||||
struct level_color
|
||||
struct level_color
|
||||
{
|
||||
level_color( log_level l=log_level::all,
|
||||
level_color( log_level l=log_level::all,
|
||||
color::type c=color::console_default )
|
||||
:level(l),color(c){}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace fc
|
||||
console_appender::color::type color;
|
||||
};
|
||||
|
||||
struct config
|
||||
struct config
|
||||
{
|
||||
config()
|
||||
:format( "${timestamp} ${thread_name} ${context} ${file}:${line} ${method} ${level}] ${message}" ),
|
||||
@@ -52,10 +52,10 @@ namespace fc
|
||||
console_appender();
|
||||
|
||||
~console_appender();
|
||||
void initialize( boost::asio::io_service& io_service ) {}
|
||||
virtual void log( const log_message& m );
|
||||
|
||||
void print( const std::string& text_to_print,
|
||||
void initialize() override {}
|
||||
virtual void log( const log_message& m ) override;
|
||||
|
||||
void print( const std::string& text_to_print,
|
||||
color::type text_color = color::console_default );
|
||||
|
||||
void configure( const config& cfg );
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace fc {
|
||||
explicit dmlog_appender( const std::optional<config>& args) ;
|
||||
|
||||
virtual ~dmlog_appender();
|
||||
virtual void initialize( boost::asio::io_service& io_service ) override;
|
||||
virtual void initialize() override;
|
||||
|
||||
virtual void log( const log_message& m ) override;
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace fc
|
||||
namespace fc
|
||||
{
|
||||
// Log appender that sends log messages in JSON format over UDP
|
||||
// https://www.graylog2.org/resources/gelf/specification
|
||||
class gelf_appender final : public appender
|
||||
class gelf_appender final : public appender
|
||||
{
|
||||
public:
|
||||
struct config
|
||||
struct config
|
||||
{
|
||||
static const std::vector<std::string> reserved_field_names;
|
||||
static const std::regex user_field_name_pattern;
|
||||
@@ -31,11 +31,11 @@ namespace fc
|
||||
* In a single-threaded world with a boost::io_service that's not owned
|
||||
* by this library, ugly things are required. Tough.
|
||||
*/
|
||||
void initialize(boost::asio::io_service& io_service) override;
|
||||
void initialize() override;
|
||||
virtual void log(const log_message& m) override;
|
||||
|
||||
private:
|
||||
class impl;
|
||||
private:
|
||||
std::shared_ptr<impl> my;
|
||||
};
|
||||
} // namespace fc
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include <fc/string.hpp>
|
||||
#include <fc/time.hpp>
|
||||
#include <fc/log/log_message.hpp>
|
||||
#include <string>
|
||||
|
||||
#ifndef DEFAULT_LOGGER
|
||||
#define DEFAULT_LOGGER "default"
|
||||
@@ -25,11 +25,11 @@ namespace fc
|
||||
class logger
|
||||
{
|
||||
public:
|
||||
static logger get( const fc::string& name = DEFAULT_LOGGER );
|
||||
static void update( const fc::string& name, logger& log );
|
||||
static logger get( const std::string& name = DEFAULT_LOGGER );
|
||||
static void update( const std::string& name, logger& log );
|
||||
|
||||
logger();
|
||||
logger( const string& name, const logger& parent = nullptr );
|
||||
logger( const std::string& name, const logger& parent = nullptr );
|
||||
logger( std::nullptr_t );
|
||||
logger( const logger& c );
|
||||
logger( logger&& c );
|
||||
@@ -44,8 +44,8 @@ namespace fc
|
||||
logger& set_parent( const logger& l );
|
||||
logger get_parent()const;
|
||||
|
||||
void set_name( const fc::string& n );
|
||||
const fc::string& name()const;
|
||||
void set_name( const std::string& n );
|
||||
std::string get_name()const;
|
||||
|
||||
bool is_enabled( log_level e )const;
|
||||
void log( log_message m );
|
||||
@@ -74,6 +74,12 @@ namespace fc
|
||||
# define FC_MULTILINE_MACRO_END } while (0)
|
||||
#endif
|
||||
|
||||
#define fc_tlog( LOGGER, FORMAT, ... ) \
|
||||
FC_MULTILINE_MACRO_BEGIN \
|
||||
if( (LOGGER).is_enabled( fc::log_level::all ) ) \
|
||||
(LOGGER).log( FC_LOG_MESSAGE( all, FORMAT, __VA_ARGS__ ) ); \
|
||||
FC_MULTILINE_MACRO_END
|
||||
|
||||
#define fc_dlog( LOGGER, FORMAT, ... ) \
|
||||
FC_MULTILINE_MACRO_BEGIN \
|
||||
if( (LOGGER).is_enabled( fc::log_level::debug ) ) \
|
||||
@@ -98,6 +104,12 @@ namespace fc
|
||||
(LOGGER).log( FC_LOG_MESSAGE( error, FORMAT, __VA_ARGS__ ) ); \
|
||||
FC_MULTILINE_MACRO_END
|
||||
|
||||
#define tlog( FORMAT, ... ) \
|
||||
FC_MULTILINE_MACRO_BEGIN \
|
||||
if( (fc::logger::get(DEFAULT_LOGGER)).is_enabled( fc::log_level::all ) ) \
|
||||
(fc::logger::get(DEFAULT_LOGGER)).log( FC_LOG_MESSAGE( all, FORMAT, __VA_ARGS__ ) ); \
|
||||
FC_MULTILINE_MACRO_END
|
||||
|
||||
#define dlog( FORMAT, ... ) \
|
||||
FC_MULTILINE_MACRO_BEGIN \
|
||||
if( (fc::logger::get(DEFAULT_LOGGER)).is_enabled( fc::log_level::debug ) ) \
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace fc {
|
||||
static logger get_logger( const fc::string& name );
|
||||
static void update_logger( const fc::string& name, logger& log );
|
||||
|
||||
static void initialize_appenders( boost::asio::io_service& ios );
|
||||
static void initialize_appenders();
|
||||
|
||||
static bool configure_logging( const logging_config& l );
|
||||
|
||||
|
||||
Submodule libraries/libfc/libraries/bn256 updated: 9ed6fd84fd...4fe51c043d
@@ -9,6 +9,8 @@
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/iostreams/device/array.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
|
||||
namespace fc
|
||||
{
|
||||
@@ -88,7 +90,7 @@ namespace fc
|
||||
template<typename T>
|
||||
std::string stringFromStream( T& in )
|
||||
{
|
||||
std::stringstream token;
|
||||
std::string token;
|
||||
try
|
||||
{
|
||||
char c = in.peek();
|
||||
@@ -103,28 +105,29 @@ namespace fc
|
||||
switch( c = in.peek() )
|
||||
{
|
||||
case '\\':
|
||||
token << parseEscape( in );
|
||||
token += parseEscape( in );
|
||||
break;
|
||||
case 0x04:
|
||||
FC_THROW_EXCEPTION( parse_error_exception, "EOF before closing '\"' in string '${token}'",
|
||||
("token", token.str() ) );
|
||||
("token", token ) );
|
||||
case '"':
|
||||
in.get();
|
||||
return token.str();
|
||||
return token;
|
||||
default:
|
||||
token << c;
|
||||
token += c;
|
||||
in.get();
|
||||
}
|
||||
}
|
||||
FC_THROW_EXCEPTION( parse_error_exception, "EOF before closing '\"' in string '${token}'",
|
||||
("token", token.str() ) );
|
||||
("token", token ) );
|
||||
} FC_RETHROW_EXCEPTIONS( warn, "while parsing token '${token}'",
|
||||
("token", token.str() ) );
|
||||
("token", token ) );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string stringFromToken( T& in )
|
||||
{
|
||||
std::stringstream token;
|
||||
std::string token;
|
||||
try
|
||||
{
|
||||
char c = in.peek();
|
||||
@@ -134,37 +137,36 @@ namespace fc
|
||||
switch( c = in.peek() )
|
||||
{
|
||||
case '\\':
|
||||
token << parseEscape( in );
|
||||
token += parseEscape( in );
|
||||
break;
|
||||
case '\t':
|
||||
case ' ':
|
||||
case '\n':
|
||||
in.get();
|
||||
return token.str();
|
||||
return token;
|
||||
case '\0':
|
||||
FC_THROW_EXCEPTION( eof_exception, "unexpected end of file" );
|
||||
default:
|
||||
if( isalnum( c ) || c == '_' || c == '-' || c == '.' || c == ':' || c == '/' )
|
||||
{
|
||||
token << c;
|
||||
token += c;
|
||||
in.get();
|
||||
}
|
||||
else return token.str();
|
||||
else return token;
|
||||
}
|
||||
}
|
||||
return token.str();
|
||||
return token;
|
||||
}
|
||||
catch( const fc::eof_exception& eof )
|
||||
{
|
||||
return token.str();
|
||||
return token;
|
||||
}
|
||||
catch (const std::ios_base::failure&)
|
||||
{
|
||||
return token.str();
|
||||
return token;
|
||||
}
|
||||
|
||||
FC_RETHROW_EXCEPTIONS( warn, "while parsing token '${token}'",
|
||||
("token", token.str() ) );
|
||||
FC_RETHROW_EXCEPTIONS( warn, "while parsing token '${token}'", ("token", token) );
|
||||
}
|
||||
|
||||
template<typename T, json::parse_type parser_type>
|
||||
@@ -252,14 +254,14 @@ namespace fc
|
||||
template<typename T, json::parse_type parser_type>
|
||||
variant number_from_stream( T& in )
|
||||
{
|
||||
std::stringstream ss;
|
||||
std::string s;
|
||||
|
||||
bool dot = false;
|
||||
bool neg = false;
|
||||
if( in.peek() == '-')
|
||||
{
|
||||
neg = true;
|
||||
ss.put( in.get() );
|
||||
s += in.get();
|
||||
}
|
||||
bool done = false;
|
||||
|
||||
@@ -284,14 +286,15 @@ namespace fc
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
ss.put( in.get() );
|
||||
s += in.get();
|
||||
break;
|
||||
case '\0':
|
||||
FC_THROW_EXCEPTION( eof_exception, "unexpected end of file" );
|
||||
default:
|
||||
if( isalnum( c ) )
|
||||
{
|
||||
return ss.str() + stringFromToken( in );
|
||||
s += stringFromToken( in );
|
||||
return s;
|
||||
}
|
||||
done = true;
|
||||
break;
|
||||
@@ -304,7 +307,7 @@ namespace fc
|
||||
catch (const std::ios_base::failure&)
|
||||
{
|
||||
}
|
||||
std::string str = ss.str();
|
||||
const std::string& str = s;
|
||||
if (str == "-." || str == "." || str == "-") // check the obviously wrong things we could have encountered
|
||||
FC_THROW_EXCEPTION(parse_error_exception, "Can't parse token \"${token}\" as a JSON numeric constant", ("token", str));
|
||||
if( dot )
|
||||
@@ -313,11 +316,11 @@ namespace fc
|
||||
return to_int64(str);
|
||||
return to_uint64(str);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
variant token_from_stream( T& in )
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.exceptions( std::ifstream::badbit );
|
||||
std::string s;
|
||||
bool received_eof = false;
|
||||
bool done = false;
|
||||
|
||||
@@ -337,7 +340,7 @@ namespace fc
|
||||
case 'f':
|
||||
case 'a':
|
||||
case 's':
|
||||
ss.put( in.get() );
|
||||
s += in.get();
|
||||
break;
|
||||
default:
|
||||
done = true;
|
||||
@@ -356,7 +359,7 @@ namespace fc
|
||||
|
||||
// we can get here either by processing a delimiter as in "null,"
|
||||
// an EOF like "null<EOF>", or an invalid token like "nullZ"
|
||||
std::string str = ss.str();
|
||||
const std::string& str = s;
|
||||
if( str == "null" )
|
||||
return variant();
|
||||
if( str == "true" )
|
||||
@@ -441,50 +444,23 @@ namespace fc
|
||||
|
||||
variant json::from_string( const std::string& utf8_str, const json::parse_type ptype, const uint32_t max_depth )
|
||||
{ try {
|
||||
std::stringstream in( utf8_str );
|
||||
//in.exceptions( std::ifstream::eofbit );
|
||||
using stream_t = boost::iostreams::stream<boost::iostreams::array_source>;
|
||||
stream_t in(utf8_str.c_str(), utf8_str.size());
|
||||
switch( ptype )
|
||||
{
|
||||
case parse_type::legacy_parser:
|
||||
return variant_from_stream<std::stringstream, json::parse_type::legacy_parser>( in, max_depth );
|
||||
return variant_from_stream<stream_t, json::parse_type::legacy_parser>( in, max_depth );
|
||||
case parse_type::legacy_parser_with_string_doubles:
|
||||
return variant_from_stream<std::stringstream, json::parse_type::legacy_parser_with_string_doubles>( in, max_depth );
|
||||
return variant_from_stream<stream_t, json::parse_type::legacy_parser_with_string_doubles>( in, max_depth );
|
||||
case parse_type::strict_parser:
|
||||
return json_relaxed::variant_from_stream<std::stringstream, true>( in, max_depth );
|
||||
return json_relaxed::variant_from_stream<stream_t, true>( in, max_depth );
|
||||
case parse_type::relaxed_parser:
|
||||
return json_relaxed::variant_from_stream<std::stringstream, false>( in, max_depth );
|
||||
return json_relaxed::variant_from_stream<stream_t, false>( in, max_depth );
|
||||
default:
|
||||
FC_ASSERT( false, "Unknown JSON parser type {ptype}", ("ptype", static_cast<int>(ptype)) );
|
||||
}
|
||||
} FC_RETHROW_EXCEPTIONS( warn, "", ("str",utf8_str) ) }
|
||||
|
||||
variants json::variants_from_string( const std::string& utf8_str, const json::parse_type ptype, const uint32_t max_depth )
|
||||
{ try {
|
||||
variants result;
|
||||
std::stringstream in( utf8_str );
|
||||
//in.exceptions( std::ifstream::eofbit );
|
||||
try {
|
||||
while( true )
|
||||
{
|
||||
// result.push_back( variant_from_stream( in ));
|
||||
result.push_back(json_relaxed::variant_from_stream<std::stringstream, false>( in, max_depth ));
|
||||
}
|
||||
} catch ( const fc::eof_exception& ){}
|
||||
return result;
|
||||
} FC_RETHROW_EXCEPTIONS( warn, "", ("str",utf8_str) ) }
|
||||
/*
|
||||
void toUTF8( const char str, std::ostream& os )
|
||||
{
|
||||
// validate str == valid utf8
|
||||
utf8::replace_invalid( &str, &str + 1, std::ostream_iterator<char>(os) );
|
||||
}
|
||||
|
||||
void toUTF8( const wchar_t c, std::ostream& os )
|
||||
{
|
||||
utf8::utf16to8( &c, (&c)+1, std::ostream_iterator<char>(os) );
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert '\t', '\r', '\n', '\\' and '"' to "\t\r\n\\\"" if escape_control_chars == true
|
||||
* Convert all other < 32 & 127 ascii to escaped unicode "\u00xx"
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace fc {
|
||||
}
|
||||
}
|
||||
|
||||
void dmlog_appender::initialize( boost::asio::io_service& io_service ) {}
|
||||
void dmlog_appender::initialize() {}
|
||||
|
||||
void dmlog_appender::log( const log_message& m ) {
|
||||
FILE* out = my->out;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <fc/io/json.hpp>
|
||||
#include <fc/crypto/city.hpp>
|
||||
#include <fc/compress/zlib.hpp>
|
||||
#include <fc/log/logger_config.hpp>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <iomanip>
|
||||
@@ -42,10 +43,15 @@ namespace fc
|
||||
class gelf_appender::impl
|
||||
{
|
||||
public:
|
||||
using work_guard_t = boost::asio::executor_work_guard<boost::asio::io_context::executor_type>;
|
||||
config cfg;
|
||||
std::optional<boost::asio::ip::udp::endpoint> gelf_endpoint;
|
||||
std::thread thread;
|
||||
boost::asio::io_context io_context;
|
||||
work_guard_t work_guard = boost::asio::make_work_guard(io_context);
|
||||
udp_socket gelf_socket;
|
||||
|
||||
|
||||
impl(const variant& c)
|
||||
{
|
||||
mutable_variant_object mvo;
|
||||
@@ -73,6 +79,10 @@ namespace fc
|
||||
|
||||
~impl()
|
||||
{
|
||||
if (thread.joinable()) {
|
||||
work_guard.reset();
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -81,7 +91,7 @@ namespace fc
|
||||
{
|
||||
}
|
||||
|
||||
void gelf_appender::initialize(boost::asio::io_service &io_service)
|
||||
void gelf_appender::initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -100,14 +110,19 @@ namespace fc
|
||||
string::size_type colon_pos = my->cfg.endpoint.find(':');
|
||||
try
|
||||
{
|
||||
uint16_t port = boost::lexical_cast<uint16_t>(my->cfg.endpoint.substr(colon_pos + 1, my->cfg.endpoint.size()));
|
||||
FC_ASSERT(colon_pos != std::string::npos, "The logging destination port is not specified");
|
||||
string port = my->cfg.endpoint.substr(colon_pos + 1);
|
||||
|
||||
string hostname = my->cfg.endpoint.substr( 0, colon_pos );
|
||||
auto endpoints = resolve(io_service, hostname, port);
|
||||
|
||||
boost::asio::ip::udp::resolver resolver{ my->io_context };
|
||||
auto endpoints = resolver.resolve(hostname, port);
|
||||
|
||||
if (endpoints.empty())
|
||||
FC_THROW_EXCEPTION(unknown_host_exception, "The logging destination host name can not be resolved: ${hostname}",
|
||||
("hostname", hostname));
|
||||
my->gelf_endpoint = endpoints.back();
|
||||
|
||||
my->gelf_endpoint = *endpoints.begin();
|
||||
}
|
||||
catch (const boost::bad_lexical_cast&)
|
||||
{
|
||||
@@ -117,9 +132,20 @@ namespace fc
|
||||
|
||||
if (my->gelf_endpoint)
|
||||
{
|
||||
my->gelf_socket.initialize(io_service);
|
||||
my->gelf_socket.initialize(my->io_context);
|
||||
my->gelf_socket.open();
|
||||
std::cerr << "opened GELF socket to endpoint " << my->cfg.endpoint << "\n";
|
||||
|
||||
my->thread = std::thread([this] {
|
||||
try {
|
||||
fc::set_os_thread_name("gelf");
|
||||
my->io_context.run();
|
||||
} catch (std::exception& ex) {
|
||||
fprintf(stderr, "GELF logger caught exception at %s:%d : %s\n", __FILE__, __LINE__, ex.what());
|
||||
} catch (...) {
|
||||
fprintf(stderr, "GELF logger caught exception unknown exception %s:%d\n", __FILE__, __LINE__);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -131,11 +157,8 @@ namespace fc
|
||||
gelf_appender::~gelf_appender()
|
||||
{}
|
||||
|
||||
void gelf_appender::log(const log_message& message)
|
||||
void do_log(gelf_appender::impl* my, uint64_t time_ns, const log_message& message)
|
||||
{
|
||||
if (!my->gelf_endpoint)
|
||||
return;
|
||||
|
||||
log_context context = message.get_context();
|
||||
|
||||
mutable_variant_object gelf_message;
|
||||
@@ -143,8 +166,6 @@ namespace fc
|
||||
gelf_message["host"] = my->cfg.host;
|
||||
gelf_message["short_message"] = format_string(message.get_format(), message.get_data(), true);
|
||||
|
||||
// use now() instead of context.get_timestamp() because log_message construction can include user provided long running calls
|
||||
const auto time_ns = time_point::now().time_since_epoch().count();
|
||||
gelf_message["timestamp"] = time_ns / 1000000.;
|
||||
gelf_message["_timestamp_ns"] = time_ns;
|
||||
|
||||
@@ -248,4 +269,20 @@ namespace fc
|
||||
FC_ASSERT(number_of_packets_sent == total_number_of_packets);
|
||||
}
|
||||
}
|
||||
|
||||
void gelf_appender::log(const log_message& message) {
|
||||
if (!my->gelf_endpoint)
|
||||
return;
|
||||
|
||||
// use now() instead of context.get_timestamp() because log_message construction can include user provided long running calls
|
||||
boost::asio::post(my->io_context, [impl = my.get(), time_ns = time_point::now().time_since_epoch().count(), message] () {
|
||||
try {
|
||||
do_log(impl, time_ns, message);
|
||||
} catch (std::exception& ex) {
|
||||
fprintf(stderr, "GELF logger caught exception at %s:%d : %s\n", __FILE__, __LINE__, ex.what());
|
||||
} catch (...) {
|
||||
fprintf(stderr, "GELF logger caught exception unknown exception %s:%d\n", __FILE__, __LINE__);
|
||||
}
|
||||
});
|
||||
}
|
||||
} // fc
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
#include <fc/log/appender.hpp>
|
||||
#include <fc/exception/exception.hpp>
|
||||
#include <fc/filesystem.hpp>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <fc/log/logger_config.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace fc {
|
||||
|
||||
@@ -13,7 +12,7 @@ namespace fc {
|
||||
public:
|
||||
impl()
|
||||
:_parent(nullptr),_enabled(true),_additivity(false),_level(log_level::warn){}
|
||||
fc::string _name;
|
||||
std::string _name;
|
||||
logger _parent;
|
||||
bool _enabled;
|
||||
bool _additivity;
|
||||
@@ -28,7 +27,7 @@ namespace fc {
|
||||
|
||||
logger::logger(nullptr_t){}
|
||||
|
||||
logger::logger( const string& name, const logger& parent )
|
||||
logger::logger( const std::string& name, const logger& parent )
|
||||
:my( new impl() )
|
||||
{
|
||||
my->_name = name;
|
||||
@@ -40,7 +39,7 @@ namespace fc {
|
||||
:my(l.my){}
|
||||
|
||||
logger::logger( logger&& l )
|
||||
:my(fc::move(l.my)){}
|
||||
:my(std::move(l.my)){}
|
||||
|
||||
logger::~logger(){}
|
||||
|
||||
@@ -82,14 +81,14 @@ namespace fc {
|
||||
}
|
||||
}
|
||||
|
||||
void logger::set_name( const fc::string& n ) { my->_name = n; }
|
||||
const fc::string& logger::name()const { return my->_name; }
|
||||
void logger::set_name( const std::string& n ) { my->_name = n; }
|
||||
std::string logger::get_name()const { return my->_name; }
|
||||
|
||||
logger logger::get( const fc::string& s ) {
|
||||
logger logger::get( const std::string& s ) {
|
||||
return log_config::get_logger( s );
|
||||
}
|
||||
|
||||
void logger::update( const fc::string& name, logger& log ) {
|
||||
void logger::update( const std::string& name, logger& log ) {
|
||||
log_config::update_logger( name, log );
|
||||
}
|
||||
|
||||
|
||||
@@ -43,10 +43,10 @@ namespace fc {
|
||||
}
|
||||
}
|
||||
|
||||
void log_config::initialize_appenders( boost::asio::io_service& ios ) {
|
||||
void log_config::initialize_appenders() {
|
||||
std::lock_guard g( log_config::get().log_mutex );
|
||||
for( auto& iter : log_config::get().appender_map )
|
||||
iter.second->initialize( ios );
|
||||
iter.second->initialize();
|
||||
}
|
||||
|
||||
void configure_logging( const fc::path& lc ) {
|
||||
|
||||
@@ -8,15 +8,12 @@
|
||||
#include <boost/beast/version.hpp>
|
||||
#include <boost/asio/connect.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ssl/error.hpp>
|
||||
#include <boost/asio/ssl/stream.hpp>
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
#include <boost/asio/local/stream_protocol.hpp>
|
||||
#include <boost/asio/ssl/rfc2818_verification.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
|
||||
namespace http = boost::beast::http; // from <boost/beast/http.hpp>
|
||||
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
|
||||
namespace local = boost::asio::local;
|
||||
|
||||
namespace fc {
|
||||
@@ -25,17 +22,15 @@ namespace fc {
|
||||
* mapping of protocols to their standard ports
|
||||
*/
|
||||
static const std::map<string,uint16_t> default_proto_ports = {
|
||||
{"http", 80},
|
||||
{"https", 443}
|
||||
{"http", 80}
|
||||
};
|
||||
|
||||
class http_client_impl {
|
||||
public:
|
||||
using host_key = std::tuple<std::string, std::string, uint16_t>;
|
||||
using raw_socket_ptr = std::unique_ptr<tcp::socket>;
|
||||
using ssl_socket_ptr = std::unique_ptr<ssl::stream<tcp::socket>>;
|
||||
using unix_socket_ptr = std::unique_ptr<local::stream_protocol::socket>;
|
||||
using connection = std::variant<raw_socket_ptr, ssl_socket_ptr, unix_socket_ptr>;
|
||||
using connection = std::variant<raw_socket_ptr, unix_socket_ptr>;
|
||||
using connection_map = std::map<host_key, connection>;
|
||||
using unix_url_split_map = std::map<string, fc::url>;
|
||||
using error_code = boost::system::error_code;
|
||||
@@ -43,24 +38,13 @@ public:
|
||||
|
||||
http_client_impl()
|
||||
:_ioc()
|
||||
,_sslc(ssl::context::sslv23_client)
|
||||
{
|
||||
set_verify_peers(true);
|
||||
}
|
||||
|
||||
void add_cert(const std::string& cert_pem_string) {
|
||||
error_code ec;
|
||||
_sslc.add_certificate_authority(boost::asio::buffer(cert_pem_string.data(), cert_pem_string.size()), ec);
|
||||
FC_ASSERT(!ec, "Failed to add cert: ${msg}", ("msg", ec.message()));
|
||||
}
|
||||
void add_cert(const std::string& cert_pem_string) {}
|
||||
|
||||
void set_verify_peers(bool enabled) {
|
||||
if (enabled) {
|
||||
_sslc.set_verify_mode(ssl::verify_peer);
|
||||
} else {
|
||||
_sslc.set_verify_mode(ssl::verify_none);
|
||||
}
|
||||
}
|
||||
void set_verify_peers(bool enabled) {}
|
||||
|
||||
template<typename SyncReadStream, typename Fn, typename CancelFn>
|
||||
error_code sync_do_with_deadline( SyncReadStream& s, deadline_type deadline, Fn f, CancelFn cf ) {
|
||||
@@ -189,41 +173,9 @@ public:
|
||||
return res.first;
|
||||
}
|
||||
|
||||
connection_map::iterator create_ssl_connection( const url& dest, const deadline_type& deadline ) {
|
||||
auto key = url_to_host_key(dest);
|
||||
auto ssl_socket = std::make_unique<ssl::stream<tcp::socket>>(_ioc, _sslc);
|
||||
|
||||
// Set SNI Hostname (many hosts need this to handshake successfully)
|
||||
if(!SSL_set_tlsext_host_name(ssl_socket->native_handle(), dest.host()->c_str()))
|
||||
{
|
||||
error_code ec{static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
|
||||
FC_THROW("Unable to set SNI Host Name: ${msg}", ("msg", ec.message()));
|
||||
}
|
||||
|
||||
ssl_socket->set_verify_callback(boost::asio::ssl::rfc2818_verification(*dest.host()));
|
||||
|
||||
error_code ec = sync_connect_with_timeout(ssl_socket->next_layer(), *dest.host(), dest.port() ? std::to_string(*dest.port()) : "443", deadline);
|
||||
if (!ec) {
|
||||
ec = sync_do_with_deadline(ssl_socket->next_layer(), deadline, [&ssl_socket](std::optional<error_code>& final_ec) {
|
||||
ssl_socket->async_handshake(ssl::stream_base::client, [&final_ec](const error_code& ec) {
|
||||
final_ec.emplace(ec);
|
||||
});
|
||||
});
|
||||
}
|
||||
FC_ASSERT(!ec, "Failed to connect: ${message}", ("message",ec.message()));
|
||||
|
||||
auto res = _connections.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(key),
|
||||
std::forward_as_tuple(std::move(ssl_socket)));
|
||||
|
||||
return res.first;
|
||||
}
|
||||
|
||||
connection_map::iterator create_connection( const url& dest, const deadline_type& deadline ) {
|
||||
if (dest.proto() == "http") {
|
||||
return create_raw_connection(dest, deadline);
|
||||
} else if (dest.proto() == "https") {
|
||||
return create_ssl_connection(dest, deadline);
|
||||
} else if (dest.proto() == "unix") {
|
||||
return create_unix_connection(dest, deadline);
|
||||
} else {
|
||||
@@ -236,10 +188,6 @@ public:
|
||||
return !ptr->is_open();
|
||||
}
|
||||
|
||||
bool operator() ( const ssl_socket_ptr& ptr ) const {
|
||||
return !ptr->lowest_layer().is_open();
|
||||
}
|
||||
|
||||
bool operator() ( const unix_socket_ptr& ptr) const {
|
||||
return !ptr->is_open();
|
||||
}
|
||||
@@ -420,7 +368,6 @@ public:
|
||||
}
|
||||
|
||||
boost::asio::io_context _ioc;
|
||||
ssl::context _sslc;
|
||||
connection_map _connections;
|
||||
unix_url_split_map _unix_url_paths;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
add_library(prometheus-core
|
||||
prometheus-cpp/core/src/check_names.cc
|
||||
prometheus-cpp/core/src/counter.cc
|
||||
prometheus-cpp/core/src/detail/builder.cc
|
||||
prometheus-cpp/core/src/detail/ckms_quantiles.cc
|
||||
prometheus-cpp/core/src/detail/time_window_quantiles.cc
|
||||
prometheus-cpp/core/src/detail/utils.cc
|
||||
prometheus-cpp/core/src/family.cc
|
||||
prometheus-cpp/core/src/gauge.cc
|
||||
prometheus-cpp/core/src/histogram.cc
|
||||
prometheus-cpp/core/src/info.cc
|
||||
prometheus-cpp/core/src/registry.cc
|
||||
prometheus-cpp/core/src/serializer.cc
|
||||
prometheus-cpp/core/src/summary.cc
|
||||
prometheus-cpp/core/src/text_serializer.cc
|
||||
)
|
||||
|
||||
target_include_directories( prometheus-core PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/prometheus-cpp/core/include)
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
// For prometheus-cpp library, this normally a generated file. we aren't following their process, so
|
||||
// we have it here. The macros are used in prometheus-cpp in relation to shared libraries, which isn't
|
||||
// relevant for us.
|
||||
|
||||
#define PROMETHEUS_CPP_CORE_EXPORT
|
||||
#define PROMETHEUS_CPP_CORE_NO_EXPORT
|
||||
|
||||
|
||||
Submodule
+1
Submodule libraries/prometheus/prometheus-cpp added at 871e782d57
@@ -18,15 +18,17 @@ bytes zlib_compress_bytes(const bytes& in) {
|
||||
return out;
|
||||
}
|
||||
|
||||
bytes zlib_decompress(const bytes& in) {
|
||||
bytes zlib_decompress(std::string_view data) {
|
||||
bytes out;
|
||||
bio::filtering_ostream decomp;
|
||||
decomp.push(bio::zlib_decompressor());
|
||||
decomp.push(bio::back_inserter(out));
|
||||
bio::write(decomp, in.data(), in.size());
|
||||
bio::write(decomp, data.data(), data.size());
|
||||
bio::close(decomp);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace state_history
|
||||
} // namespace eosio
|
||||
|
||||
@@ -57,8 +57,10 @@ bool include_delta(const chain::protocol_state_object& old, const chain::protoco
|
||||
return old.activated_protocol_features != curr.activated_protocol_features;
|
||||
}
|
||||
|
||||
std::vector<table_delta> create_deltas(const chainbase::database& db, bool full_snapshot) {
|
||||
std::vector<table_delta> deltas;
|
||||
void pack_deltas(boost::iostreams::filtering_ostreambuf& obuf, const chainbase::database& db, bool full_snapshot) {
|
||||
|
||||
fc::datastream<boost::iostreams::filtering_ostreambuf&> ds{obuf};
|
||||
|
||||
const auto& table_id_index = db.get_index<chain::table_id_multi_index>();
|
||||
std::map<uint64_t, const chain::table_id_object*> removed_table_id;
|
||||
for (auto& rem : table_id_index.last_undo_session().removed_values)
|
||||
@@ -73,72 +75,118 @@ std::vector<table_delta> create_deltas(const chainbase::database& db, bool full_
|
||||
return *it->second;
|
||||
};
|
||||
|
||||
auto pack_row = [&](auto& row) { return fc::raw::pack(make_history_serial_wrapper(db, row)); };
|
||||
auto pack_contract_row = [&](auto& row) {
|
||||
return fc::raw::pack(make_history_context_wrapper(db, get_table_id(row.t_id._id), row));
|
||||
auto pack_row = [&](auto& ds, auto& row) { fc::raw::pack(ds, make_history_serial_wrapper(db, row)); };
|
||||
auto pack_contract_row = [&](auto& ds, auto& row) {
|
||||
fc::raw::pack(ds, make_history_context_wrapper(db, get_table_id(row.t_id._id), row));
|
||||
};
|
||||
|
||||
auto process_table = [&](auto* name, auto& index, auto& pack_row) {
|
||||
auto process_table = [&](auto& ds, auto* name, auto& index, auto& pack_row) {
|
||||
|
||||
auto pack_row_v0 = [&](auto& ds, bool present, auto& row) {
|
||||
fc::raw::pack(ds, present);
|
||||
fc::datastream<size_t> ps;
|
||||
pack_row(ps, row);
|
||||
fc::raw::pack(ds, fc::unsigned_int(ps.tellp()));
|
||||
pack_row(ds, row);
|
||||
};
|
||||
|
||||
if (full_snapshot) {
|
||||
if (index.indices().empty())
|
||||
return;
|
||||
deltas.push_back({});
|
||||
auto& delta = deltas.back();
|
||||
delta.name = name;
|
||||
for (auto& row : index.indices())
|
||||
delta.rows.obj.emplace_back(true, pack_row(row));
|
||||
|
||||
fc::raw::pack(ds, fc::unsigned_int(0)); // table_delta = std::variant<table_delta_v0> and fc::unsigned_int struct_version
|
||||
fc::raw::pack(ds, name);
|
||||
fc::raw::pack(ds, fc::unsigned_int(index.indices().size()));
|
||||
for (auto& row : index.indices()) {
|
||||
pack_row_v0(ds, true, row);
|
||||
}
|
||||
} else {
|
||||
auto undo = index.last_undo_session();
|
||||
if (undo.old_values.empty() && undo.new_values.empty() && undo.removed_values.empty())
|
||||
return;
|
||||
deltas.push_back({});
|
||||
auto& delta = deltas.back();
|
||||
delta.name = name;
|
||||
for (auto& old : undo.old_values) {
|
||||
auto& row = index.get(old.id);
|
||||
if (include_delta(old, row))
|
||||
delta.rows.obj.emplace_back(true, pack_row(row));
|
||||
}
|
||||
for (auto& old : undo.removed_values)
|
||||
delta.rows.obj.emplace_back(false, pack_row(old));
|
||||
for (auto& row : undo.new_values) {
|
||||
delta.rows.obj.emplace_back(true, pack_row(row));
|
||||
}
|
||||
|
||||
if(delta.rows.obj.empty()) {
|
||||
deltas.pop_back();
|
||||
size_t num_entries =
|
||||
std::count_if(undo.old_values.begin(), undo.old_values.end(),
|
||||
[&index](const auto& old) { return include_delta(old, index.get(old.id)); }) +
|
||||
std::distance(undo.removed_values.begin(), undo.removed_values.end()) +
|
||||
std::distance(undo.new_values.begin(), undo.new_values.end());
|
||||
|
||||
if (num_entries) {
|
||||
fc::raw::pack(ds, fc::unsigned_int(0)); // table_delta = std::variant<table_delta_v0> and fc::unsigned_int struct_version
|
||||
fc::raw::pack(ds, name);
|
||||
fc::raw::pack(ds, fc::unsigned_int((uint32_t)num_entries));
|
||||
|
||||
for (auto& old : undo.old_values) {
|
||||
auto& row = index.get(old.id);
|
||||
if (include_delta(old, row)) {
|
||||
pack_row_v0(ds, true, row);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& old : undo.removed_values) {
|
||||
pack_row_v0(ds, false, old);
|
||||
}
|
||||
|
||||
for (auto& row : undo.new_values) {
|
||||
pack_row_v0(ds, true, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
process_table("account", db.get_index<chain::account_index>(), pack_row);
|
||||
process_table("account_metadata", db.get_index<chain::account_metadata_index>(), pack_row);
|
||||
process_table("code", db.get_index<chain::code_index>(), pack_row);
|
||||
auto has_table = [&](auto x) -> int {
|
||||
auto& index = db.get_index<std::remove_pointer_t<decltype(x)>>();
|
||||
if (full_snapshot) {
|
||||
return !index.indices().empty();
|
||||
} else {
|
||||
auto undo = index.last_undo_session();
|
||||
return std::find_if(undo.old_values.begin(), undo.old_values.end(),
|
||||
[&index](const auto& old) { return include_delta(old, index.get(old.id)); }) != undo.old_values.end() ||
|
||||
!undo.removed_values.empty() || !undo.new_values.empty();
|
||||
}
|
||||
};
|
||||
|
||||
process_table("contract_table", db.get_index<chain::table_id_multi_index>(), pack_row);
|
||||
process_table("contract_row", db.get_index<chain::key_value_index>(), pack_contract_row);
|
||||
process_table("contract_index64", db.get_index<chain::index64_index>(), pack_contract_row);
|
||||
process_table("contract_index128", db.get_index<chain::index128_index>(), pack_contract_row);
|
||||
process_table("contract_index256", db.get_index<chain::index256_index>(), pack_contract_row);
|
||||
process_table("contract_index_double", db.get_index<chain::index_double_index>(), pack_contract_row);
|
||||
process_table("contract_index_long_double", db.get_index<chain::index_long_double_index>(), pack_contract_row);
|
||||
int num_tables = std::apply(
|
||||
[&has_table](auto... args) { return (has_table(args) + ... ); },
|
||||
std::tuple<chain::account_index*, chain::account_metadata_index*, chain::code_index*,
|
||||
chain::table_id_multi_index*, chain::key_value_index*, chain::index64_index*, chain::index128_index*,
|
||||
chain::index256_index*, chain::index_double_index*, chain::index_long_double_index*,
|
||||
chain::global_property_multi_index*, chain::generated_transaction_multi_index*,
|
||||
chain::protocol_state_multi_index*, chain::permission_index*, chain::permission_link_index*,
|
||||
chain::resource_limits::resource_limits_index*, chain::resource_limits::resource_usage_index*,
|
||||
chain::resource_limits::resource_limits_state_index*,
|
||||
chain::resource_limits::resource_limits_config_index*>());
|
||||
|
||||
process_table("global_property", db.get_index<chain::global_property_multi_index>(), pack_row);
|
||||
process_table("generated_transaction", db.get_index<chain::generated_transaction_multi_index>(), pack_row);
|
||||
process_table("protocol_state", db.get_index<chain::protocol_state_multi_index>(), pack_row);
|
||||
fc::raw::pack(ds, fc::unsigned_int(num_tables));
|
||||
|
||||
process_table("permission", db.get_index<chain::permission_index>(), pack_row);
|
||||
process_table("permission_link", db.get_index<chain::permission_link_index>(), pack_row);
|
||||
process_table(ds, "account", db.get_index<chain::account_index>(), pack_row);
|
||||
process_table(ds, "account_metadata", db.get_index<chain::account_metadata_index>(), pack_row);
|
||||
process_table(ds, "code", db.get_index<chain::code_index>(), pack_row);
|
||||
|
||||
process_table("resource_limits", db.get_index<chain::resource_limits::resource_limits_index>(), pack_row);
|
||||
process_table("resource_usage", db.get_index<chain::resource_limits::resource_usage_index>(), pack_row);
|
||||
process_table("resource_limits_state", db.get_index<chain::resource_limits::resource_limits_state_index>(),
|
||||
process_table(ds, "contract_table", db.get_index<chain::table_id_multi_index>(), pack_row);
|
||||
process_table(ds, "contract_row", db.get_index<chain::key_value_index>(), pack_contract_row);
|
||||
process_table(ds, "contract_index64", db.get_index<chain::index64_index>(), pack_contract_row);
|
||||
process_table(ds, "contract_index128", db.get_index<chain::index128_index>(), pack_contract_row);
|
||||
process_table(ds, "contract_index256", db.get_index<chain::index256_index>(), pack_contract_row);
|
||||
process_table(ds, "contract_index_double", db.get_index<chain::index_double_index>(), pack_contract_row);
|
||||
process_table(ds, "contract_index_long_double", db.get_index<chain::index_long_double_index>(), pack_contract_row);
|
||||
|
||||
process_table(ds, "global_property", db.get_index<chain::global_property_multi_index>(), pack_row);
|
||||
process_table(ds, "generated_transaction", db.get_index<chain::generated_transaction_multi_index>(), pack_row);
|
||||
process_table(ds, "protocol_state", db.get_index<chain::protocol_state_multi_index>(), pack_row);
|
||||
|
||||
process_table(ds, "permission", db.get_index<chain::permission_index>(), pack_row);
|
||||
process_table(ds, "permission_link", db.get_index<chain::permission_link_index>(), pack_row);
|
||||
|
||||
process_table(ds, "resource_limits", db.get_index<chain::resource_limits::resource_limits_index>(), pack_row);
|
||||
process_table(ds, "resource_usage", db.get_index<chain::resource_limits::resource_usage_index>(), pack_row);
|
||||
process_table(ds, "resource_limits_state", db.get_index<chain::resource_limits::resource_limits_state_index>(),
|
||||
pack_row);
|
||||
process_table("resource_limits_config", db.get_index<chain::resource_limits::resource_limits_config_index>(),
|
||||
process_table(ds, "resource_limits_config", db.get_index<chain::resource_limits::resource_limits_config_index>(),
|
||||
pack_row);
|
||||
|
||||
return deltas;
|
||||
obuf.pubsync();
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace state_history
|
||||
} // namespace eosio
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace state_history {
|
||||
using chain::bytes;
|
||||
|
||||
bytes zlib_compress_bytes(const bytes& in);
|
||||
bytes zlib_decompress(const bytes& in);
|
||||
bytes zlib_decompress(std::string_view);
|
||||
|
||||
} // namespace state_history
|
||||
} // namespace eosio
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <eosio/state_history/types.hpp>
|
||||
#include <boost/iostreams/filtering_streambuf.hpp>
|
||||
|
||||
namespace eosio {
|
||||
namespace state_history {
|
||||
|
||||
std::vector<table_delta> create_deltas(const chainbase::database& db, bool full_snapshot);
|
||||
void pack_deltas(boost::iostreams::filtering_ostreambuf& ds, const chainbase::database& db, bool full_snapshot);
|
||||
|
||||
|
||||
} // namespace state_history
|
||||
} // namespace eosio
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <fstream>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include <eosio/state_history/compression.hpp>
|
||||
#include <eosio/chain/block_header.hpp>
|
||||
#include <eosio/chain/exceptions.hpp>
|
||||
#include <eosio/chain/types.hpp>
|
||||
#include <eosio/chain/log_catalog.hpp>
|
||||
#include <eosio/chain/log_data_base.hpp>
|
||||
#include <eosio/chain/log_index.hpp>
|
||||
|
||||
#include <fc/io/cfile.hpp>
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/log/logger_config.hpp> //set_thread_name
|
||||
#include <fc/bitutil.hpp>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/iostreams/device/file.hpp>
|
||||
#include <boost/iostreams/device/file_descriptor.hpp>
|
||||
#include <boost/iostreams/filter/zlib.hpp>
|
||||
#include <boost/iostreams/filtering_streambuf.hpp>
|
||||
#include <boost/iostreams/restrict.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
struct state_history_test_fixture;
|
||||
|
||||
namespace eosio {
|
||||
namespace bio = boost::iostreams;
|
||||
|
||||
/*
|
||||
* *.log:
|
||||
@@ -62,18 +77,216 @@ struct state_history_log_header {
|
||||
static const int state_history_log_header_serial_size = sizeof(state_history_log_header::magic) +
|
||||
sizeof(state_history_log_header::block_id) +
|
||||
sizeof(state_history_log_header::payload_size);
|
||||
struct state_history_log_prune_config {
|
||||
uint32_t prune_blocks; //number of blocks to prune to when doing a prune
|
||||
size_t prune_threshold = 4*1024*1024; //(approximately) how many bytes need to be added before a prune is performed
|
||||
std::optional<size_t> vacuum_on_close; //when set, a vacuum is performed on dtor if log contains less than this many bytes
|
||||
|
||||
namespace state_history {
|
||||
struct prune_config {
|
||||
uint32_t prune_blocks; //number of blocks to prune to when doing a prune
|
||||
size_t prune_threshold = 4*1024*1024; //(approximately) how many bytes need to be added before a prune is performed
|
||||
std::optional<size_t> vacuum_on_close; //when set, a vacuum is performed on dtor if log contains less than this many bytes
|
||||
};
|
||||
|
||||
struct partition_config {
|
||||
fc::path retained_dir = "retained";
|
||||
fc::path archive_dir = "archive";
|
||||
uint32_t stride = 1000000;
|
||||
uint32_t max_retained_files = 10;
|
||||
};
|
||||
} // namespace state_history
|
||||
|
||||
using state_history_log_config = std::variant<std::monostate, state_history::prune_config, state_history::partition_config>;
|
||||
|
||||
struct locked_decompress_stream {
|
||||
std::unique_lock<std::mutex> lock; // state_history_log mutex
|
||||
std::variant<std::vector<char>, std::unique_ptr<bio::filtering_istreambuf>> buf;
|
||||
|
||||
locked_decompress_stream() = delete;
|
||||
locked_decompress_stream(locked_decompress_stream&&) = default;
|
||||
|
||||
explicit locked_decompress_stream(std::unique_lock<std::mutex> l)
|
||||
: lock(std::move(l)) {};
|
||||
|
||||
template <typename StateHistoryLog>
|
||||
void init(StateHistoryLog&& log, fc::cfile& stream, uint64_t compressed_size) {
|
||||
auto istream = std::make_unique<bio::filtering_istreambuf>();
|
||||
istream->push(bio::zlib_decompressor());
|
||||
istream->push(bio::restrict(bio::file_source(stream.get_file_path().string()), stream.tellp(), compressed_size));
|
||||
buf = std::move(istream);
|
||||
}
|
||||
|
||||
template <typename LogData>
|
||||
void init(LogData&& log, fc::datastream<const char*>& stream, uint64_t compressed_size) {
|
||||
auto istream = std::make_unique<bio::filtering_istreambuf>();
|
||||
istream->push(bio::zlib_decompressor());
|
||||
istream->push(bio::restrict(bio::file_source(log.filename), stream.pos() - log.data(), compressed_size));
|
||||
buf = std::move(istream);
|
||||
}
|
||||
|
||||
size_t init(std::vector<char> cbuf) {
|
||||
buf.emplace<std::vector<char>>( std::move(cbuf) );
|
||||
return std::get<std::vector<char>>(buf).size();
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
std::vector<char> zlib_decompress(fc::cfile& file, uint64_t compressed_size) {
|
||||
if (compressed_size) {
|
||||
std::vector<char> compressed(compressed_size);
|
||||
file.read(compressed.data(), compressed_size);
|
||||
return state_history::zlib_decompress({compressed.data(), compressed_size});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<char> zlib_decompress(fc::datastream<const char*>& strm, uint64_t compressed_size) {
|
||||
if (compressed_size) {
|
||||
return state_history::zlib_decompress({strm.pos(), compressed_size});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename Log, typename Stream>
|
||||
uint64_t read_unpacked_entry(Log&& log, Stream& stream, uint64_t payload_size, locked_decompress_stream& result) {
|
||||
// result has state_history_log mutex locked
|
||||
|
||||
uint32_t s;
|
||||
stream.read((char*)&s, sizeof(s));
|
||||
if (s == 1 && payload_size > (s + sizeof(uint32_t))) {
|
||||
uint64_t compressed_size = payload_size - sizeof(uint32_t) - sizeof(uint64_t);
|
||||
uint64_t decompressed_size;
|
||||
stream.read((char*)&decompressed_size, sizeof(decompressed_size));
|
||||
result.init(log, stream, compressed_size);
|
||||
return decompressed_size;
|
||||
} else {
|
||||
// Compressed deltas now exceeds 4GB on one of the public chains. This length prefix
|
||||
// was intended to support adding additional fields in the future after the
|
||||
// packed deltas or packed traces. For now we're going to ignore on read.
|
||||
|
||||
uint64_t compressed_size = payload_size - sizeof(uint32_t);
|
||||
return result.init( zlib_decompress(stream, compressed_size) );
|
||||
}
|
||||
}
|
||||
|
||||
class state_history_log_data : public chain::log_data_base<state_history_log_data> {
|
||||
uint32_t version_;
|
||||
bool is_currently_pruned_;
|
||||
uint64_t size_;
|
||||
|
||||
public:
|
||||
state_history_log_data() = default;
|
||||
explicit state_history_log_data(const fc::path& path) { open(path); }
|
||||
|
||||
void open(const fc::path& path) {
|
||||
if (file.is_open())
|
||||
file.close();
|
||||
file.set_file_path(path);
|
||||
file.open("rb");
|
||||
uint64_t v = chain::read_data_at<uint64_t>(file, 0);
|
||||
version_ = get_ship_version(v);
|
||||
is_currently_pruned_ = is_ship_log_pruned(v);
|
||||
file.seek_end(0);
|
||||
size_ = file.tellp();
|
||||
}
|
||||
|
||||
uint64_t size() const { return size_; }
|
||||
uint32_t version() const { return version_; }
|
||||
uint32_t first_block_num() { return block_num_at(0); }
|
||||
uint32_t first_block_position() const { return 0; }
|
||||
|
||||
bool is_currently_pruned() const { return is_currently_pruned_; }
|
||||
|
||||
uint64_t ro_stream_at(uint64_t pos, locked_decompress_stream& result) {
|
||||
uint64_t payload_size = payload_size_at(pos);
|
||||
file.seek(pos + sizeof(state_history_log_header));
|
||||
// fc::datastream<const char*> stream(file.const_data() + pos + sizeof(state_history_log_header), payload_size);
|
||||
return read_unpacked_entry(*this, file, payload_size, result);
|
||||
}
|
||||
|
||||
uint32_t block_num_at(uint64_t position) {
|
||||
return fc::endian_reverse_u32(
|
||||
chain::read_data_at<uint32_t>(file, position + offsetof(state_history_log_header, block_id)));
|
||||
}
|
||||
|
||||
chain::block_id_type block_id_at(uint64_t position) {
|
||||
return chain::read_data_at<chain::block_id_type>(file, position +
|
||||
offsetof(state_history_log_header, block_id));
|
||||
}
|
||||
|
||||
uint64_t payload_size_at(uint64_t pos) {
|
||||
std::string filename = file.get_file_path().generic_string();
|
||||
EOS_ASSERT(size() >= pos + sizeof(state_history_log_header), chain::plugin_exception,
|
||||
"corrupt ${name}: invalid entry size at at position ${pos}", ("name", filename)("pos", pos));
|
||||
|
||||
state_history_log_header header = chain::read_data_at<state_history_log_header>(file, pos);
|
||||
|
||||
EOS_ASSERT(is_ship(header.magic) && is_ship_supported_version(header.magic), chain::plugin_exception,
|
||||
"corrupt ${name}: invalid header for entry at position ${pos}", ("name", filename)("pos", pos));
|
||||
|
||||
EOS_ASSERT(size() >= pos + sizeof(state_history_log_header) + header.payload_size, chain::plugin_exception,
|
||||
"corrupt ${name}: invalid payload size for entry at position ${pos}", ("name", filename)("pos", pos));
|
||||
return header.payload_size;
|
||||
}
|
||||
|
||||
void construct_index(const fc::path& index_file_name) {
|
||||
fc::cfile index_file;
|
||||
index_file.set_file_path(index_file_name);
|
||||
index_file.open("w+b");
|
||||
|
||||
uint64_t pos = 0;
|
||||
while (pos < size()) {
|
||||
uint64_t payload_size = payload_size_at(pos);
|
||||
index_file.write(reinterpret_cast<const char*>(&pos), sizeof(pos));
|
||||
pos += (sizeof(state_history_log_header) + payload_size + sizeof(uint64_t));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// directly adapt from boost/iostreams/filter/counter.hpp and change the type of chars_ to uint64_t.
|
||||
class counter {
|
||||
public:
|
||||
typedef char char_type;
|
||||
struct category
|
||||
: bio::dual_use,
|
||||
bio::filter_tag,
|
||||
bio::multichar_tag,
|
||||
bio::optimally_buffered_tag
|
||||
{ };
|
||||
explicit counter(uint64_t first_char = 0)
|
||||
: chars_(first_char)
|
||||
{ }
|
||||
uint64_t characters() const { return chars_; }
|
||||
std::streamsize optimal_buffer_size() const { return 0; }
|
||||
|
||||
template<typename Source>
|
||||
std::streamsize read(Source& src, char_type* s, std::streamsize n)
|
||||
{
|
||||
std::streamsize result = bio::read(src, s, n);
|
||||
if (result == -1)
|
||||
return -1;
|
||||
chars_ += result;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename Sink>
|
||||
std::streamsize write(Sink& snk, const char_type* s, std::streamsize n)
|
||||
{
|
||||
std::streamsize result = bio::write(snk, s, n);
|
||||
chars_ += result;
|
||||
return result;
|
||||
}
|
||||
private:
|
||||
uint64_t chars_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class state_history_log {
|
||||
private:
|
||||
const char* const name = "";
|
||||
std::string log_filename;
|
||||
std::string index_filename;
|
||||
std::optional<state_history_log_prune_config> prune_config; //is set, log is in pruned mode
|
||||
state_history_log_config config;
|
||||
|
||||
// provide exclusive access to all data of this object since accessed from the main thread and the ship thread
|
||||
mutable std::mutex _mx;
|
||||
fc::cfile log;
|
||||
fc::cfile index;
|
||||
uint32_t _begin_block = 0; //always tracks the first block available even after pruning
|
||||
@@ -81,31 +294,40 @@ class state_history_log {
|
||||
uint32_t _end_block = 0;
|
||||
chain::block_id_type last_block_id;
|
||||
|
||||
std::thread thr;
|
||||
std::atomic<bool> write_thread_has_exception = false;
|
||||
std::exception_ptr eptr;
|
||||
boost::asio::io_context ctx;
|
||||
boost::asio::io_context::strand work_strand{ctx};
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work_guard =
|
||||
boost::asio::make_work_guard(ctx);
|
||||
std::recursive_mutex mx;
|
||||
using catalog_t = chain::log_catalog<detail::state_history_log_data, chain::log_index<chain::plugin_exception>>;
|
||||
catalog_t catalog;
|
||||
|
||||
public:
|
||||
state_history_log(const char* const name, std::string log_filename, std::string index_filename,
|
||||
std::optional<state_history_log_prune_config> prune_conf = std::optional<state_history_log_prune_config>())
|
||||
friend struct ::state_history_test_fixture;
|
||||
|
||||
state_history_log( const state_history_log&) = delete;
|
||||
|
||||
state_history_log(const char* name, const fc::path& log_dir,
|
||||
state_history_log_config conf = {})
|
||||
: name(name)
|
||||
, log_filename(std::move(log_filename))
|
||||
, index_filename(std::move(index_filename))
|
||||
, prune_config(prune_conf) {
|
||||
, config(std::move(conf)) {
|
||||
|
||||
log.set_file_path(log_dir/(std::string(name) + ".log"));
|
||||
index.set_file_path(log_dir/(std::string(name) + ".index"));
|
||||
|
||||
open_log();
|
||||
open_index();
|
||||
|
||||
if(prune_config) {
|
||||
EOS_ASSERT(prune_config->prune_blocks, chain::plugin_exception, "state history log prune configuration requires at least one block");
|
||||
EOS_ASSERT(__builtin_popcount(prune_config->prune_threshold) == 1, chain::plugin_exception, "state history prune threshold must be power of 2");
|
||||
//switch this over to the mask that will be used
|
||||
prune_config->prune_threshold = ~(prune_config->prune_threshold-1);
|
||||
}
|
||||
|
||||
std::visit(eosio::chain::overloaded{
|
||||
[](std::monostate&) {},
|
||||
[](state_history::prune_config& conf) {
|
||||
EOS_ASSERT(conf.prune_blocks, chain::plugin_exception, "state history log prune configuration requires at least one block");
|
||||
EOS_ASSERT(__builtin_popcount(conf.prune_threshold) == 1, chain::plugin_exception, "state history prune threshold must be power of 2");
|
||||
//switch this over to the mask that will be used
|
||||
conf.prune_threshold = ~(conf.prune_threshold-1);
|
||||
}, [name, log_dir, this](state_history::partition_config& conf) {
|
||||
catalog.open(log_dir, conf.retained_dir, conf.archive_dir, name);
|
||||
catalog.max_retained_files = conf.max_retained_files;
|
||||
if (_end_block == 0) {
|
||||
_begin_block = _end_block = catalog.last_block_num() +1;
|
||||
}
|
||||
}
|
||||
}, config);
|
||||
|
||||
//check for conversions to/from pruned log, as long as log contains something
|
||||
if(_begin_block != _end_block) {
|
||||
@@ -113,6 +335,8 @@ class state_history_log {
|
||||
log.seek(0);
|
||||
read_header(first_header);
|
||||
|
||||
auto prune_config = std::get_if<state_history::prune_config>(&config);
|
||||
|
||||
if((is_ship_log_pruned(first_header.magic) == false) && prune_config) {
|
||||
//need to convert non-pruned to pruned; first prune any ranges we can (might be none)
|
||||
prune(fc::log_level::info);
|
||||
@@ -131,36 +355,14 @@ class state_history_log {
|
||||
vacuum();
|
||||
}
|
||||
}
|
||||
|
||||
thr = std::thread([this] {
|
||||
try {
|
||||
fc::set_os_thread_name(this->name);
|
||||
this->ctx.run();
|
||||
} catch (...) {
|
||||
elog("catched exception from ${name} write thread", ("name", this->name));
|
||||
eptr = std::current_exception();
|
||||
write_thread_has_exception = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (thr.joinable()) {
|
||||
work_guard.reset();
|
||||
thr.join();
|
||||
}
|
||||
}
|
||||
|
||||
~state_history_log() {
|
||||
// complete execution before possible vacuuming
|
||||
if (thr.joinable()) {
|
||||
work_guard.reset();
|
||||
thr.join();
|
||||
}
|
||||
|
||||
//nothing to do if log is empty or we aren't pruning
|
||||
if(_begin_block == _end_block)
|
||||
return;
|
||||
|
||||
auto prune_config = std::get_if<state_history::prune_config>(&config);
|
||||
if(!prune_config || !prune_config->vacuum_on_close)
|
||||
return;
|
||||
|
||||
@@ -170,8 +372,96 @@ class state_history_log {
|
||||
vacuum();
|
||||
}
|
||||
|
||||
uint32_t begin_block() const { return _begin_block; }
|
||||
uint32_t end_block() const { return _end_block; }
|
||||
// begin end
|
||||
std::pair<uint32_t, uint32_t> block_range() const {
|
||||
std::lock_guard g(_mx);
|
||||
return { std::min(catalog.first_block_num(), _begin_block), _end_block };
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
auto r = block_range();
|
||||
return r.first == r.second;
|
||||
}
|
||||
|
||||
locked_decompress_stream create_locked_decompress_stream() {
|
||||
return locked_decompress_stream{ std::unique_lock<std::mutex>( _mx ) };
|
||||
}
|
||||
|
||||
/// @return the decompressed entry size
|
||||
uint64_t get_unpacked_entry(uint32_t block_num, locked_decompress_stream& result) {
|
||||
|
||||
// result has mx locked
|
||||
|
||||
auto opt_decompressed_size = catalog.ro_stream_for_block(block_num, result);
|
||||
if (opt_decompressed_size)
|
||||
return *opt_decompressed_size;
|
||||
|
||||
if (block_num < _begin_block || block_num >= _end_block)
|
||||
return 0;
|
||||
|
||||
state_history_log_header header;
|
||||
log.seek(get_pos(block_num));
|
||||
read_header(header);
|
||||
|
||||
return detail::read_unpacked_entry(*this, log, header.payload_size, result);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void pack_and_write_entry(state_history_log_header header, const chain::block_id_type& prev_id, F&& pack_to) {
|
||||
std::lock_guard g(_mx);
|
||||
write_entry(header, prev_id, [&, pack_to = std::forward<F>(pack_to)](auto& stream) {
|
||||
size_t payload_pos = stream.tellp();
|
||||
|
||||
// In order to conserve memory usage for reading the chain state later, we need to
|
||||
// encode the uncompressed data size to the disk so that the reader can send the
|
||||
// decompressed data size before decompressing data. Here we use the number
|
||||
// 1 indicates the format contains a 64 bits unsigned integer for decompressed data
|
||||
// size and then the actually compressed data. The compressed data size can be
|
||||
// computed from the payload size in the header minus sizeof(uint32_t) + sizeof(uint64_t).
|
||||
|
||||
uint32_t s = 1;
|
||||
stream.write((char*)&s, sizeof(s));
|
||||
uint64_t uncompressioned_size = 0;
|
||||
stream.skip(sizeof(uncompressioned_size));
|
||||
|
||||
namespace bio = boost::iostreams;
|
||||
|
||||
detail::counter cnt;
|
||||
{
|
||||
bio::filtering_ostreambuf buf;
|
||||
buf.push(boost::ref(cnt));
|
||||
buf.push(bio::zlib_compressor());
|
||||
buf.push(bio::file_descriptor_sink(stream.fileno(), bio::never_close_handle));
|
||||
pack_to(buf);
|
||||
}
|
||||
|
||||
// calculate the payload size and rewind back to header to write the payload size
|
||||
stream.seek_end(0);
|
||||
size_t end_payload_pos = stream.tellp();
|
||||
uint64_t payload_size = end_payload_pos - payload_pos;
|
||||
stream.seek(payload_pos - sizeof(uint64_t));
|
||||
stream.write((char*)&payload_size, sizeof(payload_size));
|
||||
|
||||
// write the uncompressed data size
|
||||
stream.skip(sizeof(s));
|
||||
uncompressioned_size = cnt.characters();
|
||||
stream.write((char*)&uncompressioned_size, sizeof(uncompressioned_size));
|
||||
|
||||
// make sure we reset the file position to end_payload_pos to preserve API behavior
|
||||
stream.seek(end_payload_pos);
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<chain::block_id_type> get_block_id(uint32_t block_num) {
|
||||
std::lock_guard g(_mx);
|
||||
return get_block_id_i(block_num);
|
||||
}
|
||||
|
||||
#ifdef BOOST_TEST_MODULE
|
||||
fc::cfile& get_log_file() { return log;}
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
void read_header(state_history_log_header& header, bool assert_version = true) {
|
||||
char bytes[state_history_log_header_serial_size];
|
||||
@@ -194,12 +484,6 @@ class state_history_log {
|
||||
|
||||
template <typename F>
|
||||
void write_entry(state_history_log_header header, const chain::block_id_type& prev_id, F write_payload) {
|
||||
if (write_thread_has_exception) {
|
||||
std::rethrow_exception(eptr);
|
||||
}
|
||||
|
||||
std::unique_lock<std::recursive_mutex> lock(mx);
|
||||
|
||||
auto block_num = chain::block_header::num_from_id(header.block_id);
|
||||
EOS_ASSERT(_begin_block == _end_block || block_num <= _end_block, chain::plugin_exception,
|
||||
"missed a block in ${name}.log", ("name", name));
|
||||
@@ -216,24 +500,40 @@ class state_history_log {
|
||||
}
|
||||
}
|
||||
|
||||
if (block_num < _end_block)
|
||||
truncate(block_num); //truncate is expected to always leave file pointer at the end
|
||||
else if (!prune_config)
|
||||
auto prune_config = std::get_if<state_history::prune_config>(&config);
|
||||
if (block_num < _end_block) {
|
||||
// This is typically because of a fork, and we need to truncate the log back to the beginning of the fork.
|
||||
static uint32_t start_block_num = block_num;
|
||||
// Guard agaisnt accidently starting a fresh chain with an existing ship log, require manual removal of ship logs.
|
||||
EOS_ASSERT( block_num > 2, chain::plugin_exception, "Existing ship log with ${eb} blocks when starting from genesis block ${b}",
|
||||
("eb", _end_block)("b", block_num) );
|
||||
// block_num < _begin_block = pruned log, need to call truncate() to reset
|
||||
// get_block_id_i check is an optimization to avoid writing a block that is already in the log (snapshot or replay)
|
||||
if ( block_num < _begin_block || get_block_id_i(block_num) != header.block_id ) {
|
||||
truncate(block_num); //truncate is expected to always leave file pointer at the end
|
||||
} else {
|
||||
if (start_block_num == block_num || block_num % 1000 == 0 )
|
||||
ilog("log ${name}.log already contains block ${b}, end block ${eb}", ("name", name)("b", block_num)("eb", _end_block));
|
||||
return;
|
||||
}
|
||||
} else if (!prune_config) {
|
||||
log.seek_end(0);
|
||||
else if (prune_config && _begin_block != _end_block)
|
||||
} else if (prune_config && _begin_block != _end_block) {
|
||||
log.seek_end(-sizeof(uint32_t)); //overwrite the trailing block count marker on this write
|
||||
}
|
||||
|
||||
//if we're operating on a pruned block log and this is the first entry in the log, make note of the feature in the header
|
||||
if(prune_config && _begin_block == _end_block)
|
||||
header.magic = ship_magic(get_ship_version(header.magic), ship_feature_pruned_log);
|
||||
|
||||
uint64_t pos = log.tellp();
|
||||
|
||||
|
||||
write_header(header);
|
||||
write_payload(log);
|
||||
|
||||
EOS_ASSERT(log.tellp() == pos + state_history_log_header_serial_size + header.payload_size, chain::plugin_exception,
|
||||
"wrote payload with incorrect size to ${name}.log", ("name", name));
|
||||
if (header.payload_size != 0)
|
||||
EOS_ASSERT(log.tellp() == pos + state_history_log_header_serial_size + header.payload_size, chain::plugin_exception,
|
||||
"wrote payload with incorrect size to ${name}.log", ("name", name));
|
||||
fc::raw::pack(log, pos);
|
||||
|
||||
fc::raw::pack(index, pos);
|
||||
@@ -249,28 +549,36 @@ class state_history_log {
|
||||
const uint32_t num_blocks_in_log = _end_block - _begin_block;
|
||||
fc::raw::pack(log, num_blocks_in_log);
|
||||
}
|
||||
|
||||
auto partition_config = std::get_if<state_history::partition_config>(&config);
|
||||
if (partition_config && block_num % partition_config->stride == 0) {
|
||||
split_log();
|
||||
}
|
||||
}
|
||||
|
||||
// returns cfile positioned at payload
|
||||
fc::cfile& get_entry(uint32_t block_num, state_history_log_header& header) {
|
||||
EOS_ASSERT(block_num >= _begin_block && block_num < _end_block, chain::plugin_exception,
|
||||
"read non-existing block in ${name}.log", ("name", name));
|
||||
std::lock_guard lock(mx);
|
||||
log.seek(get_pos(block_num));
|
||||
read_header(header);
|
||||
return log;
|
||||
}
|
||||
|
||||
chain::block_id_type get_block_id(uint32_t block_num) {
|
||||
std::lock_guard lock(mx);
|
||||
state_history_log_header header;
|
||||
get_entry(block_num, header);
|
||||
return header.block_id;
|
||||
std::optional<chain::block_id_type> get_block_id_i(uint32_t block_num) {
|
||||
auto result = catalog.id_for_block(block_num);
|
||||
if (!result) {
|
||||
if (block_num >= _begin_block && block_num < _end_block) {
|
||||
state_history_log_header header;
|
||||
get_entry(block_num, header);
|
||||
return header.block_id;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
//file position must be at start of last block's suffix (back pointer)
|
||||
//called from open_log / ctor
|
||||
//called from open_log / ctor
|
||||
bool get_last_block() {
|
||||
state_history_log_header header;
|
||||
uint64_t suffix;
|
||||
@@ -298,6 +606,8 @@ class state_history_log {
|
||||
}
|
||||
|
||||
void prune(const fc::log_level& loglevel) {
|
||||
auto prune_config = std::get_if<state_history::prune_config>(&config);
|
||||
|
||||
if(!prune_config)
|
||||
return;
|
||||
if(_end_block - _begin_block <= prune_config->prune_blocks)
|
||||
@@ -347,7 +657,7 @@ class state_history_log {
|
||||
}
|
||||
}
|
||||
log.flush();
|
||||
boost::filesystem::resize_file(log_filename, pos);
|
||||
boost::filesystem::resize_file(log.get_file_path().string(), pos);
|
||||
log.flush();
|
||||
|
||||
log.seek_end(-sizeof(pos));
|
||||
@@ -356,13 +666,12 @@ class state_history_log {
|
||||
|
||||
// only called from constructor
|
||||
void open_log() {
|
||||
log.set_file_path(log_filename);
|
||||
log.open("a+b");
|
||||
log.open(fc::cfile::create_or_update_rw_mode);
|
||||
log.seek_end(0);
|
||||
uint64_t size = log.tellp();
|
||||
log.close();
|
||||
|
||||
log.open("r+b");
|
||||
log.open(fc::cfile::update_rw_mode);
|
||||
if (size >= state_history_log_header_serial_size) {
|
||||
state_history_log_header header;
|
||||
log.seek(0);
|
||||
@@ -403,8 +712,7 @@ class state_history_log {
|
||||
|
||||
// only called from constructor
|
||||
void open_index() {
|
||||
index.set_file_path(index_filename);
|
||||
index.open("a+b");
|
||||
index.open(fc::cfile::create_or_update_rw_mode);
|
||||
index.seek_end(0);
|
||||
if (index.tellp() == (static_cast<int>(_end_block) - _index_begin_block) * sizeof(uint64_t))
|
||||
return;
|
||||
@@ -444,7 +752,7 @@ class state_history_log {
|
||||
}
|
||||
|
||||
index.close();
|
||||
index.open("a+b");
|
||||
index.open(fc::cfile::create_or_update_rw_mode);
|
||||
}
|
||||
|
||||
uint64_t get_pos(uint32_t block_num) {
|
||||
@@ -455,27 +763,48 @@ class state_history_log {
|
||||
}
|
||||
|
||||
void truncate(uint32_t block_num) {
|
||||
log.flush();
|
||||
index.flush();
|
||||
uint64_t num_removed = 0;
|
||||
log.close();
|
||||
index.close();
|
||||
|
||||
auto first_block_num = catalog.empty() ? _begin_block : catalog.first_block_num();
|
||||
auto new_begin_block_num = catalog.truncate(block_num, log.get_file_path());
|
||||
|
||||
// notice that catalog.truncate() can replace existing log and index files, so we have to
|
||||
// close the files and reopen them again; otherwise we might operate on the obsolete files instead.
|
||||
|
||||
if (new_begin_block_num > 0) {
|
||||
_begin_block = new_begin_block_num;
|
||||
_index_begin_block = new_begin_block_num;
|
||||
}
|
||||
|
||||
uint32_t num_removed;
|
||||
|
||||
if (block_num <= _begin_block) {
|
||||
num_removed = _end_block - _begin_block;
|
||||
log.seek(0);
|
||||
boost::filesystem::resize_file(log_filename, 0);
|
||||
boost::filesystem::resize_file(index_filename, 0);
|
||||
_begin_block = _end_block = 0;
|
||||
num_removed = _end_block - first_block_num;
|
||||
boost::filesystem::resize_file(log.get_file_path().string(), 0);
|
||||
boost::filesystem::resize_file(index.get_file_path().string(), 0);
|
||||
_begin_block = _end_block = block_num;
|
||||
} else {
|
||||
num_removed = _end_block - block_num;
|
||||
|
||||
index.open("rb");
|
||||
uint64_t pos = get_pos(block_num);
|
||||
log.seek(0);
|
||||
boost::filesystem::resize_file(log_filename, pos);
|
||||
boost::filesystem::resize_file(index_filename, (block_num - _index_begin_block) * sizeof(uint64_t));
|
||||
index.close();
|
||||
|
||||
auto path = log.get_file_path().string();
|
||||
|
||||
boost::filesystem::resize_file(log.get_file_path().string(), pos);
|
||||
boost::filesystem::resize_file(index.get_file_path().string(), (block_num - _index_begin_block) * sizeof(uint64_t));
|
||||
_end_block = block_num;
|
||||
//this will leave the end of the log with the last block's suffix no matter if the log is operating in pruned
|
||||
// mode or not. The assumption is truncate() is always immediately followed up with an append to the log thus
|
||||
// restoring the prune trailer if required
|
||||
}
|
||||
|
||||
log.open(fc::cfile::update_rw_mode);
|
||||
log.seek_end(0);
|
||||
index.open(fc::cfile::create_or_update_rw_mode);
|
||||
|
||||
ilog("fork or replay: removed ${n} blocks from ${name}.log", ("n", num_removed)("name", name));
|
||||
}
|
||||
|
||||
@@ -557,6 +886,50 @@ class state_history_log {
|
||||
_index_begin_block = _begin_block;
|
||||
ilog("Vacuum of pruned log ${n} complete",("n", name));
|
||||
}
|
||||
|
||||
void split_log() {
|
||||
|
||||
fc::path log_file_path = log.get_file_path();
|
||||
fc::path index_file_path = index.get_file_path();
|
||||
|
||||
fc::datastream<fc::cfile> new_log_file;
|
||||
fc::datastream<fc::cfile> new_index_file;
|
||||
|
||||
fc::path tmp_log_file_path = log_file_path;
|
||||
tmp_log_file_path.replace_extension("log.tmp");
|
||||
fc::path tmp_index_file_path = index_file_path;
|
||||
tmp_index_file_path.replace_extension("index.tmp");
|
||||
|
||||
new_log_file.set_file_path(tmp_log_file_path);
|
||||
new_index_file.set_file_path(tmp_index_file_path);
|
||||
|
||||
try {
|
||||
new_log_file.open(fc::cfile::truncate_rw_mode);
|
||||
new_index_file.open(fc::cfile::truncate_rw_mode);
|
||||
|
||||
} catch (...) {
|
||||
wlog("Unable to open new state history log or index file for writing during log spliting, "
|
||||
"continue writing to existing block log file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
index.close();
|
||||
log.close();
|
||||
|
||||
catalog.add(_begin_block, _end_block - 1, log.get_file_path().parent_path(), name);
|
||||
|
||||
_begin_block = _end_block;
|
||||
|
||||
using std::swap;
|
||||
swap(new_log_file, log);
|
||||
swap(new_index_file, index);
|
||||
|
||||
fc::rename(tmp_log_file_path, log_file_path);
|
||||
fc::rename(tmp_index_file_path, index_file_path);
|
||||
|
||||
log.set_file_path(log_file_path);
|
||||
index.set_file_path(index_file_path);
|
||||
}
|
||||
}; // state_history_log
|
||||
|
||||
} // namespace eosio
|
||||
|
||||
@@ -715,4 +715,15 @@ datastream<ST>& operator<<(datastream<ST>& ds, const eosio::state_history::get_b
|
||||
return ds;
|
||||
}
|
||||
|
||||
template <typename ST>
|
||||
datastream<ST>& operator<<(datastream<ST>& ds, const eosio::state_history::get_blocks_result_base& obj) {
|
||||
fc::raw::pack(ds, obj.head);
|
||||
fc::raw::pack(ds, obj.last_irreversible);
|
||||
fc::raw::pack(ds, obj.this_block);
|
||||
fc::raw::pack(ds, obj.prev_block);
|
||||
history_pack_big_bytes(ds, obj.block);
|
||||
return ds;
|
||||
}
|
||||
|
||||
|
||||
} // namespace fc
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <eosio/chain/block_state.hpp>
|
||||
#include <eosio/state_history/types.hpp>
|
||||
#include <boost/iostreams/filtering_streambuf.hpp>
|
||||
|
||||
namespace eosio {
|
||||
namespace state_history {
|
||||
@@ -13,8 +14,8 @@ struct trace_converter {
|
||||
std::map<transaction_id_type, augmented_transaction_trace> cached_traces;
|
||||
std::optional<augmented_transaction_trace> onblock_trace;
|
||||
|
||||
void add_transaction(const transaction_trace_ptr& trace, const chain::packed_transaction_ptr& transaction);
|
||||
bytes pack(const chainbase::database& db, bool trace_debug_mode, const block_state_ptr& block_state);
|
||||
void add_transaction(const transaction_trace_ptr& trace, const chain::packed_transaction_ptr& transaction);
|
||||
void pack(boost::iostreams::filtering_ostreambuf& ds, const chainbase::database& db, bool trace_debug_mode, const block_state_ptr& block_state);
|
||||
};
|
||||
|
||||
} // namespace state_history
|
||||
|
||||
@@ -101,12 +101,15 @@ struct get_blocks_ack_request_v0 {
|
||||
uint32_t num_messages = 0;
|
||||
};
|
||||
|
||||
struct get_blocks_result_v0 {
|
||||
struct get_blocks_result_base {
|
||||
block_position head;
|
||||
block_position last_irreversible;
|
||||
std::optional<block_position> this_block;
|
||||
std::optional<block_position> prev_block;
|
||||
std::optional<bytes> block;
|
||||
};
|
||||
|
||||
struct get_blocks_result_v0 : get_blocks_result_base {
|
||||
std::optional<bytes> traces;
|
||||
std::optional<bytes> deltas;
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ void trace_converter::add_transaction(const transaction_trace_ptr& trace, const
|
||||
}
|
||||
}
|
||||
|
||||
bytes trace_converter::pack(const chainbase::database& db, bool trace_debug_mode, const block_state_ptr& block_state) {
|
||||
void trace_converter::pack(boost::iostreams::filtering_ostreambuf& obuf, const chainbase::database& db, bool trace_debug_mode, const block_state_ptr& block_state) {
|
||||
std::vector<augmented_transaction_trace> traces;
|
||||
if (onblock_trace)
|
||||
traces.push_back(*onblock_trace);
|
||||
@@ -33,7 +33,8 @@ bytes trace_converter::pack(const chainbase::database& db, bool trace_debug_mode
|
||||
cached_traces.clear();
|
||||
onblock_trace.reset();
|
||||
|
||||
return fc::raw::pack(make_history_context_wrapper(db, trace_debug_mode, traces));
|
||||
fc::datastream<boost::iostreams::filtering_ostreambuf&> ds{obuf};
|
||||
return fc::raw::pack(ds, make_history_context_wrapper(db, trace_debug_mode, traces));
|
||||
}
|
||||
|
||||
} // namespace state_history
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#define MAKE_EMBEDDED_WASM_ABI(CN,C, D) \
|
||||
INCBIN(eosio_testing_contract_ ## CN ## _wasm, "${CMAKE_BINARY_DIR}/libraries/testing/" #D "/" #C "/" #C ".wasm"); \
|
||||
INCBIN(eosio_testing_contract_ ## CN ## _abi, "${CMAKE_BINARY_DIR}/libraries/testing/" #D "/" #C "/" #C ".abi"); \
|
||||
INCTXT(eosio_testing_contract_ ## CN ## _abi, "${CMAKE_BINARY_DIR}/libraries/testing/" #D "/" #C "/" #C ".abi"); \
|
||||
\
|
||||
namespace eosio::testing { \
|
||||
std::vector<std::uint8_t> contracts:: CN ## _wasm() { \
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace eosio { namespace testing {
|
||||
unapplied_transaction_queue& get_unapplied_transaction_queue() { return unapplied_transactions; }
|
||||
|
||||
transaction_trace_ptr push_transaction( packed_transaction& trx, fc::time_point deadline = fc::time_point::maximum(), uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US );
|
||||
transaction_trace_ptr push_transaction( signed_transaction& trx, fc::time_point deadline = fc::time_point::maximum(), uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US, bool no_throw = false );
|
||||
transaction_trace_ptr push_transaction( signed_transaction& trx, fc::time_point deadline = fc::time_point::maximum(), uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US, bool no_throw = false, transaction_metadata::trx_type trx_type = transaction_metadata::trx_type::input );
|
||||
|
||||
[[nodiscard]]
|
||||
action_result push_action(action&& cert_act, uint64_t authorizer); // TODO/QUESTION: Is this needed?
|
||||
@@ -341,9 +341,8 @@ namespace eosio { namespace testing {
|
||||
return [this]( const account_name& name ) -> std::optional<abi_serializer> {
|
||||
try {
|
||||
const auto& accnt = control->db().get<account_object, by_name>( name );
|
||||
abi_def abi;
|
||||
if( abi_serializer::to_abi( accnt.abi, abi )) {
|
||||
return abi_serializer( abi, abi_serializer::create_yield_function( abi_serializer_max_time ) );
|
||||
if( abi_def abi; abi_serializer::to_abi( accnt.abi, abi )) {
|
||||
return abi_serializer( std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time ) );
|
||||
}
|
||||
return std::optional<abi_serializer>();
|
||||
} FC_RETHROW_EXCEPTIONS( error, "Failed to find or parse ABI for ${name}", ("name", name))
|
||||
@@ -490,6 +489,9 @@ namespace eosio { namespace testing {
|
||||
}
|
||||
}
|
||||
|
||||
tester(const std::function<void(controller&)>& control_setup, setup_policy policy = setup_policy::full,
|
||||
db_read_mode read_mode = db_read_mode::HEAD);
|
||||
|
||||
using base_tester::produce_block;
|
||||
|
||||
signed_block_ptr produce_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override {
|
||||
|
||||
@@ -582,7 +582,8 @@ namespace eosio { namespace testing {
|
||||
transaction_trace_ptr base_tester::push_transaction( signed_transaction& trx,
|
||||
fc::time_point deadline,
|
||||
uint32_t billed_cpu_time_us,
|
||||
bool no_throw
|
||||
bool no_throw,
|
||||
transaction_metadata::trx_type trx_type
|
||||
)
|
||||
{ try {
|
||||
if( !control->is_building_block() )
|
||||
@@ -597,7 +598,7 @@ namespace eosio { namespace testing {
|
||||
fc::microseconds::maximum() :
|
||||
fc::microseconds( deadline - fc::time_point::now() );
|
||||
auto ptrx = std::make_shared<packed_transaction>( trx, c );
|
||||
auto fut = transaction_metadata::start_recover_keys( ptrx, control->get_thread_pool(), control->get_chain_id(), time_limit, transaction_metadata::trx_type::input );
|
||||
auto fut = transaction_metadata::start_recover_keys( ptrx, control->get_thread_pool(), control->get_chain_id(), time_limit, trx_type );
|
||||
auto r = control->push_transaction( fut.get(), deadline, fc::microseconds::maximum(), billed_cpu_time_us, billed_cpu_time_us > 0, 0 );
|
||||
if (no_throw) return r;
|
||||
if( r->except_ptr ) std::rethrow_exception( r->except_ptr );
|
||||
@@ -682,7 +683,7 @@ namespace eosio { namespace testing {
|
||||
const variant_object& data )const { try {
|
||||
const auto& acnt = control->get_account(code);
|
||||
auto abi = acnt.get_abi();
|
||||
chain::abi_serializer abis(abi, abi_serializer::create_yield_function( abi_serializer_max_time ));
|
||||
chain::abi_serializer abis(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time ));
|
||||
|
||||
string action_type_name = abis.get_action_type(acttype);
|
||||
FC_ASSERT( action_type_name != string(), "unknown action type ${a}", ("a",acttype) );
|
||||
@@ -1206,6 +1207,20 @@ namespace eosio { namespace testing {
|
||||
preactivate_protocol_features( preactivations );
|
||||
}
|
||||
|
||||
tester::tester(const std::function<void(controller&)>& control_setup, setup_policy policy, db_read_mode read_mode) {
|
||||
auto def_conf = default_config(tempdir);
|
||||
def_conf.first.read_mode = read_mode;
|
||||
cfg = def_conf.first;
|
||||
|
||||
base_tester::open(make_protocol_feature_set(), def_conf.second.compute_chain_id(),
|
||||
[&genesis = def_conf.second, &control = this->control, &control_setup]() {
|
||||
control_setup(*control);
|
||||
control->startup([]() {}, []() { return false; }, genesis);
|
||||
});
|
||||
|
||||
execute_setup_policy(policy);
|
||||
}
|
||||
|
||||
bool fc_exception_message_is::operator()( const fc::exception& ex ) {
|
||||
auto message = ex.get_log().at( 0 ).get_message();
|
||||
bool match = (message == expected);
|
||||
|
||||
+2
-1
@@ -61,7 +61,7 @@ set(CPACK_DEBIAN_BASE_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb")
|
||||
string(REGEX REPLACE "^(${CMAKE_PROJECT_NAME})" "\\1-dev" CPACK_DEBIAN_DEV_FILE_NAME "${CPACK_DEBIAN_BASE_FILE_NAME}")
|
||||
|
||||
#deb package tooling will be unable to detect deps for the dev package. llvm is tricky since we don't know what package could have been used; try to figure it out
|
||||
set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libboost-all-dev, libssl-dev, libgmp-dev")
|
||||
set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libboost-all-dev, libssl-dev, libgmp-dev, python3-numpy")
|
||||
find_program(DPKG_QUERY "dpkg-query")
|
||||
if(DPKG_QUERY AND OS_RELEASE MATCHES "\n?ID=\"?ubuntu" AND LLVM_CMAKE_DIR)
|
||||
execute_process(COMMAND "${DPKG_QUERY}" -S "${LLVM_CMAKE_DIR}" COMMAND cut -d: -f1 RESULT_VARIABLE LLVM_PKG_FIND_RESULT OUTPUT_VARIABLE LLVM_PKG_FIND_OUTPUT)
|
||||
@@ -70,6 +70,7 @@ if(DPKG_QUERY AND OS_RELEASE MATCHES "\n?ID=\"?ubuntu" AND LLVM_CMAKE_DIR)
|
||||
string(APPEND CPACK_DEBIAN_DEV_PACKAGE_DEPENDS ", ${LLVM_PKG_FIND_OUTPUT}")
|
||||
endif()
|
||||
endif()
|
||||
set(CPACK_DEBIAN_DEV_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/scripts/postinst;${CMAKE_BINARY_DIR}/scripts/prerm")
|
||||
|
||||
#since rpm packages aren't component based, make sure description picks up more detailed description instead of just summary
|
||||
set(CPACK_RPM_PACKAGE_DESCRIPTION "${CPACK_COMPONENT_BASE_DESCRIPTION}")
|
||||
|
||||
@@ -15,3 +15,4 @@ add_subdirectory(wallet_api_plugin)
|
||||
add_subdirectory(db_size_api_plugin)
|
||||
add_subdirectory(test_control_plugin)
|
||||
add_subdirectory(test_control_api_plugin)
|
||||
add_subdirectory(prometheus_plugin)
|
||||
@@ -548,68 +548,6 @@ paths:
|
||||
type: array
|
||||
items: {}
|
||||
|
||||
/abi_json_to_bin:
|
||||
post:
|
||||
description: Returns an object containing the serialized action data.
|
||||
operationId: abi_json_to_bin
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
title: AbiJsonToBinRequest
|
||||
- code
|
||||
- action
|
||||
- args
|
||||
properties:
|
||||
code:
|
||||
$ref: "https://docs.eosnetwork.com/openapi/v2.0/Name.yaml"
|
||||
action:
|
||||
$ref: "https://docs.eosnetwork.com/openapi/v2.0/Name.yaml"
|
||||
args:
|
||||
type: object
|
||||
description: json object of the action parameters that will be serialized.
|
||||
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
binargs:
|
||||
$ref: "https://docs.eosnetwork.com/openapi/v2.0/Hex.yaml"
|
||||
|
||||
/abi_bin_to_json:
|
||||
post:
|
||||
description: Returns an object containing the deserialized action data.
|
||||
operationId: abi_bin_to_json
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
title: AbiBinToJsonRequest
|
||||
required:
|
||||
- code
|
||||
- action
|
||||
- binargs
|
||||
properties:
|
||||
code:
|
||||
$ref: "https://docs.eosnetwork.com/openapi/v2.0/Name.yaml"
|
||||
action:
|
||||
$ref: "https://docs.eosnetwork.com/openapi/v2.0/Name.yaml"
|
||||
binargs:
|
||||
$ref: "https://docs.eosnetwork.com/openapi/v2.0/Hex.yaml"
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/get_code:
|
||||
post:
|
||||
description: Returns an object containing the smart contract WASM code.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <eosio/chain_api_plugin/chain_api_plugin.hpp>
|
||||
#include <eosio/chain/exceptions.hpp>
|
||||
|
||||
#include <eosio/http_plugin/macros.hpp>
|
||||
#include <fc/io/json.hpp>
|
||||
|
||||
namespace eosio {
|
||||
|
||||
static appbase::abstract_plugin& _chain_api_plugin = app().register_plugin<chain_api_plugin>();
|
||||
static auto _chain_api_plugin = application::register_plugin<chain_api_plugin>();
|
||||
|
||||
using namespace eosio;
|
||||
|
||||
@@ -24,13 +24,6 @@ chain_api_plugin::~chain_api_plugin(){}
|
||||
void chain_api_plugin::set_program_options(options_description&, options_description&) {}
|
||||
void chain_api_plugin::plugin_initialize(const variables_map&) {}
|
||||
|
||||
struct async_result_visitor : public fc::visitor<fc::variant> {
|
||||
template<typename T>
|
||||
fc::variant operator()(const T& v) const {
|
||||
return fc::variant(v);
|
||||
}
|
||||
};
|
||||
|
||||
// Only want a simple 'Invalid transaction id' if unable to parse the body
|
||||
template<>
|
||||
chain_apis::read_only::get_transaction_status_params
|
||||
@@ -50,7 +43,7 @@ parse_params<chain_apis::read_only::get_transaction_status_params, http_params_t
|
||||
|
||||
#define CALL_WITH_400(api_name, api_handle, api_namespace, call_name, http_response_code, params_type) \
|
||||
{std::string("/v1/" #api_name "/" #call_name), \
|
||||
[api_handle](string, string body, url_response_callback cb) mutable { \
|
||||
[api_handle](string&&, string&& body, url_response_callback&& cb) mutable { \
|
||||
auto deadline = api_handle.start(); \
|
||||
try { \
|
||||
auto params = parse_params<api_namespace::call_name ## _params, params_type>(body);\
|
||||
@@ -62,31 +55,6 @@ parse_params<chain_apis::read_only::get_transaction_status_params, http_params_t
|
||||
} \
|
||||
}}
|
||||
|
||||
#define CALL_ASYNC_WITH_400(api_name, api_handle, api_namespace, call_name, call_result, http_response_code, params_type) \
|
||||
{std::string("/v1/" #api_name "/" #call_name), \
|
||||
[api_handle](string, string body, url_response_callback cb) mutable { \
|
||||
auto deadline = api_handle.start(); \
|
||||
try { \
|
||||
auto params = parse_params<api_namespace::call_name ## _params, params_type>(body);\
|
||||
FC_CHECK_DEADLINE(deadline);\
|
||||
api_handle.call_name( std::move(params), \
|
||||
[cb, body](const std::variant<fc::exception_ptr, call_result>& result){\
|
||||
if (std::holds_alternative<fc::exception_ptr>(result)) {\
|
||||
try {\
|
||||
std::get<fc::exception_ptr>(result)->dynamic_rethrow_exception();\
|
||||
} catch (...) {\
|
||||
http_plugin::handle_exception(#api_name, #call_name, body, cb);\
|
||||
}\
|
||||
} else {\
|
||||
cb(http_response_code, fc::time_point::maximum(), std::visit(async_result_visitor(), result));\
|
||||
}\
|
||||
});\
|
||||
} catch (...) { \
|
||||
http_plugin::handle_exception(#api_name, #call_name, body, cb); \
|
||||
} \
|
||||
}\
|
||||
}
|
||||
|
||||
#define CHAIN_RO_CALL(call_name, http_response_code, params_type) CALL_WITH_400(chain, ro_api, chain_apis::read_only, call_name, http_response_code, params_type)
|
||||
#define CHAIN_RW_CALL(call_name, http_response_code, params_type) CALL_WITH_400(chain, rw_api, chain_apis::read_write, call_name, http_response_code, params_type)
|
||||
#define CHAIN_RO_CALL_ASYNC(call_name, call_result, http_response_code, params_type) CALL_ASYNC_WITH_400(chain, ro_api, chain_apis::read_only, call_name, call_result, http_response_code, params_type)
|
||||
@@ -98,25 +66,24 @@ void chain_api_plugin::plugin_startup() {
|
||||
ilog( "starting chain_api_plugin" );
|
||||
my.reset(new chain_api_plugin_impl(app().get_plugin<chain_plugin>().chain()));
|
||||
auto& chain = app().get_plugin<chain_plugin>();
|
||||
auto& http = app().get_plugin<http_plugin>();
|
||||
fc::microseconds max_response_time = http.get_max_response_time();
|
||||
auto& _http_plugin = app().get_plugin<http_plugin>();
|
||||
fc::microseconds max_response_time = _http_plugin.get_max_response_time();
|
||||
|
||||
auto ro_api = chain.get_read_only_api(max_response_time);
|
||||
auto rw_api = chain.get_read_write_api(max_response_time);
|
||||
|
||||
auto& _http_plugin = app().get_plugin<http_plugin>();
|
||||
ro_api.set_shorten_abi_errors( !http_plugin::verbose_errors() );
|
||||
|
||||
_http_plugin.add_api( {
|
||||
CHAIN_RO_CALL(get_info, 200, http_params_types::no_params)}, appbase::priority::medium_high);
|
||||
CHAIN_RO_CALL(get_info, 200, http_params_types::no_params)}, appbase::exec_queue::read_only_trx_safe, appbase::priority::medium_high);
|
||||
_http_plugin.add_api({
|
||||
CHAIN_RO_CALL(get_activated_protocol_features, 200, http_params_types::possible_no_params),
|
||||
CHAIN_RO_CALL(get_block, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_block_info, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_block_header_state, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_account, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_code, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_code_hash, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_consensus_parameters, 200, http_params_types::no_params),
|
||||
CHAIN_RO_CALL(get_abi, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_raw_code_and_abi, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_raw_abi, 200, http_params_types::params_required),
|
||||
@@ -127,18 +94,21 @@ void chain_api_plugin::plugin_startup() {
|
||||
CHAIN_RO_CALL(get_producers, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_producer_schedule, 200, http_params_types::no_params),
|
||||
CHAIN_RO_CALL(get_scheduled_transactions, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(abi_json_to_bin, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(abi_bin_to_json, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_required_keys, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_transaction_id, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL_ASYNC(send_read_only_transaction, chain_apis::read_only::send_read_only_transaction_results, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL_ASYNC(compute_transaction, chain_apis::read_only::compute_transaction_results, 200, http_params_types::params_required),
|
||||
CHAIN_RW_CALL_ASYNC(push_block, chain_apis::read_write::push_block_results, 202, http_params_types::params_required),
|
||||
// transaction related APIs will be posted to general queue after keys are recovered
|
||||
CHAIN_RW_CALL_ASYNC(push_transaction, chain_apis::read_write::push_transaction_results, 202, http_params_types::params_required),
|
||||
CHAIN_RW_CALL_ASYNC(push_transactions, chain_apis::read_write::push_transactions_results, 202, http_params_types::params_required),
|
||||
CHAIN_RW_CALL_ASYNC(send_transaction, chain_apis::read_write::send_transaction_results, 202, http_params_types::params_required),
|
||||
CHAIN_RW_CALL_ASYNC(send_transaction2, chain_apis::read_write::send_transaction_results, 202, http_params_types::params_required),
|
||||
CHAIN_RO_CALL(get_consensus_parameters, 200, http_params_types::no_params)
|
||||
});
|
||||
CHAIN_RW_CALL_ASYNC(send_transaction2, chain_apis::read_write::send_transaction_results, 202, http_params_types::params_required)
|
||||
}, appbase::exec_queue::read_only_trx_safe);
|
||||
|
||||
// Not safe to run in parallel with read-only transactions
|
||||
_http_plugin.add_api({
|
||||
CHAIN_RW_CALL_ASYNC(push_block, chain_apis::read_write::push_block_results, 202, http_params_types::params_required)
|
||||
}, appbase::exec_queue::general, appbase::priority::medium_low );
|
||||
|
||||
if (chain.account_queries_enabled()) {
|
||||
_http_plugin.add_async_api({
|
||||
@@ -146,11 +116,51 @@ void chain_api_plugin::plugin_startup() {
|
||||
});
|
||||
}
|
||||
|
||||
_http_plugin.add_async_api({
|
||||
CHAIN_RO_CALL_WITH_400(get_raw_block, 200, http_params_types::params_required),
|
||||
CHAIN_RO_CALL_WITH_400(get_block_header, 200, http_params_types::params_required)
|
||||
});
|
||||
|
||||
if (chain.transaction_finality_status_enabled()) {
|
||||
_http_plugin.add_api({
|
||||
CHAIN_RO_CALL_WITH_400(get_transaction_status, 200, http_params_types::params_required),
|
||||
});
|
||||
}, appbase::exec_queue::read_only_trx_safe);
|
||||
}
|
||||
|
||||
_http_plugin.add_api({
|
||||
{ std::string("/v1/chain/get_block"),
|
||||
[ro_api, &_http_plugin, max_time=std::min(chain.get_abi_serializer_max_time(),max_response_time)]
|
||||
( string&&, string&& body, url_response_callback&& cb ) mutable {
|
||||
auto deadline = ro_api.start();
|
||||
try {
|
||||
auto start = fc::time_point::now();
|
||||
auto params = parse_params<chain_apis::read_only::get_raw_block_params, http_params_types::params_required>(body);
|
||||
FC_CHECK_DEADLINE( deadline );
|
||||
chain::signed_block_ptr block = ro_api.get_raw_block( params, deadline );
|
||||
|
||||
auto abi_cache = ro_api.get_block_serializers( block, max_time );
|
||||
FC_CHECK_DEADLINE( deadline );
|
||||
|
||||
auto post_time = fc::time_point::now();
|
||||
auto remaining_time = max_time - (post_time - start);
|
||||
_http_plugin.post_http_thread_pool(
|
||||
[ro_api, cb, deadline, post_time, remaining_time, abi_cache{std::move(abi_cache)}, block{std::move( block )}]() mutable {
|
||||
try {
|
||||
auto new_deadline = deadline + (fc::time_point::now() - post_time);
|
||||
|
||||
fc::variant result = ro_api.convert_block( block, std::move(abi_cache), remaining_time );
|
||||
|
||||
cb( 200, new_deadline, std::move( result ) );
|
||||
} catch( ... ) {
|
||||
http_plugin::handle_exception( "chain", "get_block", "", cb );
|
||||
}
|
||||
} );
|
||||
} catch( ... ) {
|
||||
http_plugin::handle_exception("chain", "get_block", body, cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, appbase::exec_queue::read_only_trx_safe);
|
||||
}
|
||||
|
||||
void chain_api_plugin::plugin_shutdown() {}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <eosio/chain_plugin/chain_plugin.hpp>
|
||||
#include <eosio/http_plugin/http_plugin.hpp>
|
||||
|
||||
#include <appbase/application.hpp>
|
||||
#include <eosio/chain/application.hpp>
|
||||
#include <eosio/chain/controller.hpp>
|
||||
|
||||
namespace eosio {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <fc/time.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace eosio::chain::plugin_interface {
|
||||
|
||||
//
|
||||
// prometheus metrics
|
||||
//
|
||||
|
||||
enum class metric_type {
|
||||
gauge = 1,
|
||||
counter = 2
|
||||
};
|
||||
|
||||
struct runtime_metric {
|
||||
metric_type type = metric_type::gauge;
|
||||
std::string family;
|
||||
std::string label;
|
||||
int64_t value = 0;
|
||||
};
|
||||
|
||||
using metrics_listener = std::function<void(std::vector<runtime_metric>)>;
|
||||
|
||||
struct plugin_metrics {
|
||||
|
||||
virtual ~plugin_metrics() = default;
|
||||
virtual std::vector<runtime_metric> metrics()=0;
|
||||
|
||||
bool should_post() {
|
||||
return (_listener && (fc::time_point::now() > (_last_post + _min_post_interval_us)));
|
||||
}
|
||||
|
||||
bool post_metrics() {
|
||||
if (should_post()){
|
||||
_listener(metrics());
|
||||
_last_post = fc::time_point::now();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void register_listener(metrics_listener listener) {
|
||||
_listener = std::move(listener);
|
||||
}
|
||||
|
||||
explicit plugin_metrics(fc::microseconds min_post_interval_us = fc::milliseconds(250))
|
||||
: _min_post_interval_us(min_post_interval_us)
|
||||
, _listener(nullptr) {}
|
||||
|
||||
private:
|
||||
fc::microseconds _min_post_interval_us;
|
||||
metrics_listener _listener;
|
||||
fc::time_point _last_post;
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user