diff --git a/concordium-consensus/tests/consensus/ConcordiumTests/EndToEnd/CredentialDeploymentTests.hs b/concordium-consensus/tests/consensus/ConcordiumTests/EndToEnd/CredentialDeploymentTests.hs index 39d0951b68..8a17a3dcd6 100644 --- a/concordium-consensus/tests/consensus/ConcordiumTests/EndToEnd/CredentialDeploymentTests.hs +++ b/concordium-consensus/tests/consensus/ConcordiumTests/EndToEnd/CredentialDeploymentTests.hs @@ -106,7 +106,7 @@ testBB1 = SP8 -> read "9edf091441b19468d82a637c270472b0474592f3089e56415e4982351b095d35" SP9 -> read "ad588c91476f5865dabf2ffe9b6954c924479aa0a2ef4057a5b79dbc110b1219" SP10 -> read "5272b398aa5ade5ef14c6be41d586537f5212d96ea609b414bf2051e0c3780f7" - SP11 -> read "985caa840e7db8cf1bc92c426c0c18efd8d84c2a3079963cc8a36675925a34ca" + SP11 -> read "5a286c00ef3f83a6dcb7c2e461fee2ab806217a8a5bf176d2edffc5937433139" } } where @@ -140,7 +140,7 @@ testBB2 = SP8 -> read "7ead7edff60ac23771f15052278304e3e2c9186826439ec7d04e28e55676f41b" SP9 -> read "29d974384b047eaa8cb5b809dd37e9fe617e046ad22b7f9dbe605cdac9cf8e40" SP10 -> read "29d974384b047eaa8cb5b809dd37e9fe617e046ad22b7f9dbe605cdac9cf8e40" - SP11 -> read "6cfbb4bbdde0a59c62c7235fd143bb3d4035be6944a3647230c6e6467b002958" + SP11 -> read "dca21bbc3d8ff517a94cc3f4767cd5af3c0ddc18a82f83dfd1107f3d4f30fde7" } } where @@ -174,7 +174,7 @@ testBB3 = SP8 -> read "c8b3fc868c79703945638c709c9e2d03b67c3f70b023aac8ae5b980b41181726" SP9 -> read "0282c255df3cb95180050ae3ee8838c0ab303fa7fe4e5e754ecf5d4c8db5152a" SP10 -> read "0282c255df3cb95180050ae3ee8838c0ab303fa7fe4e5e754ecf5d4c8db5152a" - SP11 -> read "f0aa67c74436cfc94c980bde7ab17477877bedb51e65af2ee7b4d1d73d2267fc" + SP11 -> read "bf2216807a09431a102ad4e8f1eb58bdbebc7b0630b1d8f2460dcc9147f48688" } } where @@ -232,7 +232,7 @@ testBB2' = SP8 -> read "3abd796108d6fdcdf8c4361973d7152973cad3695b58b0c92a4e5021c0f80e33" SP9 -> read "e10fe99a06aec8675f442ef0232ff0af9106ad6f5845335513603bcb4f3ff707" SP10 -> read "0182d9155fb80b22fa43b8f7e1d9da389209bfa27340ec5a823a3f587e4455f9" - SP11 -> read "058787128015e24fe915eff52ca85954744e1f370345f1b975fc86198cfe7812" + SP11 -> read "134415ff5ee3871a00805f9d99ab0d262c15c60b7dd0fc748c39a2041810ce07" } } where @@ -266,7 +266,7 @@ testBB3' = SP8 -> read "9e46988a9afd8470e25c33f2133d2c10cbb38050979957f200b4aca072e3c932" SP9 -> read "075e1732e475a84b89f1ba89c06d58df7d7e42936dc16e0cf3d8b02dacf27c52" SP10 -> read "2a9b336c419c9e64dc7db6735a297119649c4b579ac4d32ac58e1ab09302d17c" - SP11 -> read "ccc46f1b465b4eda4d97ca9b8569fcedfc188c52faa6bcbf75459a8eab4a56d4" + SP11 -> read "72cb461a2be08aec4ba5ccba84e09300b09ee1af6912018b25ec54eab2da0e23" } } where @@ -298,7 +298,7 @@ testBB4 = SP8 -> read "5bdf447992d82321a921bca9eeb6211bf3a290029164976885cf6b2fd14d923c" SP9 -> read "ff90510a80285645170b5ff4614af366de2d8898e2bee111cae61e05ad640ada" SP10 -> read "ff90510a80285645170b5ff4614af366de2d8898e2bee111cae61e05ad640ada" - SP11 -> read "aa25851283710d72272f03a285fdfd9ebcae508ef7d16bd6f5951042c57305af" + SP11 -> read "9f43139a2d197c7c9c839e1cef473fa5139ade2649ae800fa14c47c1e91c356e" } } where @@ -330,7 +330,7 @@ testBB5 = SP8 -> read "e704583a45aec569aca9039e977d9a3a7c2db8bdfd650532182600b6a19cbb70" SP9 -> read "6ad235a2db0349340044197b9c23da565f6bf4dd1ad40b89eab5bae7bcab0997" SP10 -> read "6ad235a2db0349340044197b9c23da565f6bf4dd1ad40b89eab5bae7bcab0997" - SP11 -> read "3eb83f38796c308f13c39c0520e3b691acfbb04d3a2ad95286a39da09f0dcad7" + SP11 -> read "c7582c9cdf70fbb4463dce18277c1e495d2b74e3635ecfc5707e30ae5c3cf213" } } where diff --git a/concordium-consensus/tests/consensus/ConcordiumTests/EndToEnd/TransactionTableIntegrationTest.hs b/concordium-consensus/tests/consensus/ConcordiumTests/EndToEnd/TransactionTableIntegrationTest.hs index 3341151078..ee290b41cf 100644 --- a/concordium-consensus/tests/consensus/ConcordiumTests/EndToEnd/TransactionTableIntegrationTest.hs +++ b/concordium-consensus/tests/consensus/ConcordiumTests/EndToEnd/TransactionTableIntegrationTest.hs @@ -75,7 +75,7 @@ testBB1 = SP8 -> read "6f02a3e339abab1af9d23bc74369b6e142ad3b09f4d7042d16e076e225e3753f" SP9 -> read "27ad830abd9d1f456f2a1666365ffff1fb216911d8dab2fbae5d3ad701816733" SP10 -> read "5634a54bfdfb954205b78129c93236fba97b3190ad3fa412b4e2ffbaad6e7324" - SP11 -> read "477b6cbbd9b72bf9589c8c351321ab4a6fac4e2d0f4151270ca722df7e8bc136" + SP11 -> read "54d0c1eac303ede359f6f5fae1d92ecd7f6d8413fec5c78c78b6c4dddba02d5c" } } where @@ -109,7 +109,7 @@ testBB2 = SP8 -> read "b0c8b7a3872b7bb35a9df1f620e47f2b7dad09889b29772d7bc03d713cff862d" SP9 -> read "3017ca78e30e5bfc25a15849c07b1266c46185340b909e26aee439ddc694af7b" SP10 -> read "3017ca78e30e5bfc25a15849c07b1266c46185340b909e26aee439ddc694af7b" - SP11 -> read "d1ef497abc35fe056e61c60332e3925fab4ce4bb51dc916424f2a5bbd485b99b" + SP11 -> read "ce6422ea14d286970f346aa59025debb89ceb1be99324b7a9f087de4f65e91cb" } } where @@ -143,7 +143,7 @@ testBB3 = SP8 -> read "8adf29ed11f4784b4b32dbf84887ff9f5dd38ef2f78dbefe579822b48acd9e51" SP9 -> read "8935d7fb4c2906ecf1e10b225e319fb39933ca7b0970b93084e4b7b6b01a8dca" SP10 -> read "8935d7fb4c2906ecf1e10b225e319fb39933ca7b0970b93084e4b7b6b01a8dca" - SP11 -> read "977afde3caecc14b671f315564f324fb950e7017ee5183983fb50395730ad4f4" + SP11 -> read "15279dd38b91f69114e25658e537f12fe3c4839bf56bf0bbb4249f8ba2ebab20" } } where @@ -176,7 +176,7 @@ testBB4 = SP8 -> read "487c4bdf8af054727f0688bd8a7176f6c2a9c85dffc5d8acc1de23e0c3b6ef49" SP9 -> read "fe5c4a3c4943440c34a72884ccf178af8f7b2043f0179b6bbf554574bb4ba581" SP10 -> read "7ccdea365c8283b97af6b5d00fedc17853e84021dcbf1417c34fefbfe7736091" - SP11 -> read "ad4a9a8ad34d0877a53928530b115e340f7c28a7b4b5a32a6e1f6071e7968f56" + SP11 -> read "7871e2eace30dda1d602db9438b9a5b7ef90827e10711e0a282017ad421815f1" } } where diff --git a/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/CatchUp.hs b/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/CatchUp.hs index 540e2df807..459cb074ef 100644 --- a/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/CatchUp.hs +++ b/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/CatchUp.hs @@ -426,7 +426,7 @@ hashesRound4Block sProtocolVersion = case sBlockHashVersionFor sProtocolVersion spv | TestBlocks.blockResultHashAsP9 spv -> read "93837fbd7183afca6f32723f44f13f68a980b30e832ff93b5ed1c7a5e19ecdaa" - SP11 -> read "1c90ffc50d99067cca86d9b971692fa721ba17e5bcaf3921ee832b34737491cd" + SP11 -> read "8fef1fa579c3fa44a592592f5b5c683e05188927cd147b680b8f1c70e39129b6" spv -> TestBlocks.dummyBRH spv 0xc04 } diff --git a/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/Consensus/Blocks.hs b/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/Consensus/Blocks.hs index 025cb878f2..b1be1b2cf7 100644 --- a/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/Consensus/Blocks.hs +++ b/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/Consensus/Blocks.hs @@ -352,7 +352,7 @@ testBB1 = SP7 -> read "0970b0f7459e5150a56ac283eee6f587fc49cb1c3408146b46ee05457235bec7" SP8 -> read "69f807bdafa7ef495089545cf89837cbcd5d1424fe53770efae4608a8bbf7560" spv | blockResultHashAsP9 spv -> read "747769e675019732bd4de37f2486ed696bea329b9c31b618a1e3a583ed5e4aaf" - SP11 -> read "7f864445d8a9ca71c0bfaa7663e8fe84f389a7fd5edd78b04d897a9f0ab98538" + SP11 -> read "254a46adc7c472f81f6fdb9227f26cadce54616fa55d9a29b299acbf7d45b286" spv -> dummyBRH spv 1 } } @@ -385,7 +385,7 @@ testBB2 = SP7 -> read "2e6636b8275663e44452650e4b7968ecb26a32d57998fbbccc0292fdecb1522d" SP8 -> read "8315e1c9ac06bf9b8d0902616dda6781648baef8765be529d583267ea6376a17" spv | blockResultHashAsP9 spv -> read "6434d129fa41e0b469a056369f63e1f6eaafbfed3539c84c2f1542ae8e6cbcb2" - SP11 -> read "59ff8f0037088f7a8e03db3ff633ae6698803e5525e4f7a0c2119b5b0b845641" + SP11 -> read "86bba28814121db31389fe10beadfa8b17d49d2315f11fb0683f117bb1ed0230" spv -> dummyBRH spv 2 } } @@ -418,7 +418,7 @@ testBB3 = SP7 -> read "5777ce2df452ce52ee6beb43c555051588cdad67ad742e7be438cf9d22e31950" SP8 -> read "aa38f2426aecf0103671767ececf2a5a5cbc6ebf20cb929d90f6cfd086bba258" spv | blockResultHashAsP9 spv -> read "df8dd05c64cab1f1a6ba4c971aa751d1dfb653dfed3605a1334f601f1f808acf" - SP11 -> read "ca236e4f2acef9a68d9544ed2cb505de2b9b0e302090812b39ed0e6c62aa29ef" + SP11 -> read "57e6bdcb2a68735c4cbbd904b73347253ed156225d40a0b841c758bb95fd7944" spv -> dummyBRH spv 3 } } @@ -444,7 +444,7 @@ testBB2' = SP7 -> read "04185ac844f6aea6e32b667debd1e9a337d67a80350d12f1cb813bf212a4bc23" SP8 -> read "0b7a0106ac9293606424bf03e9f0314c3ac8e41a4097f30435eb9b2ee9463aa4" spv | blockResultHashAsP9 spv -> read "bf7634ab27d1c7509d6d230f78e7df94eea7f53c04f3f92619c901b042a1b663" - SP11 -> read "b08694914933758ed023bcd10aae983a13facb5bb92eef228e78565fc3c4e389" + SP11 -> read "a19e7e286a146c98ecca8ff6248281eaa071faf8c2eca86e8316e62c341de947" spv -> dummyBRH spv 0x102 } } @@ -471,7 +471,7 @@ testBB3' = SP7 -> read "11e59c1a721359a64b86a0c6bcee8f6cac7c5bc3a6b98517b0b4f8c5a726f9c5" SP8 -> read "0a1d22fd9404974bb43616ad3d0152acb9de40f856b6fb66150b2c496730add5" spv | blockResultHashAsP9 spv -> read "0d6386d76cff950e60543a03485cbbee5e11667e2ad09dc77a48cce5168dab58" - SP11 -> read "8e740206a731236210ec07601b7f8cacdaef88e8feb4839cf8493fa514d19362" + SP11 -> read "9211d233f24c44770e4553c42dcb0d56e222c86a20c7101e422c8559d69f1392" spv -> dummyBRH spv 0x103 } } @@ -505,7 +505,7 @@ testBB4' = SP7 -> read "ad8a288f88806037899d782b7dd3a37ade59e0d5f3e7a90b1db2b722ae9cbe3d" SP8 -> read "1afb0b0bd86301671549f1b1e407d71818d1d9d29c19beed584d17e2ace638d4" spv | blockResultHashAsP9 spv -> read "874ad0c151a7e2ec3252af524de4348203f5ac607580dd99e1c70bce94787b93" - SP11 -> read "79d8e2cf055e1de119f23a0e63115206f662edb83391d04d851cf5a00a9b0f10" + SP11 -> read "9aa754b61722d5f8129c4e3690df6dad3659a570a3f2fdfc4ef2d222a7afc3bd" spv -> dummyBRH spv 0x104 } } @@ -564,7 +564,7 @@ testBB1E = SP7 -> read "1811353ec3811af6241f3e5dc2e19740acf518f02dccc51f427310b8cfe9ca6c" SP8 -> read "a002fe8adf77ca8cb992fd25dd3c5430db7ee52e383212eeb0827170af323d2e" spv | blockResultHashAsP9 spv -> read "f59f26a1b1da5858ccc51589c7eb9227493434aa0788f3fb8ffb3104ecebe4fb" - SP11 -> read "b7b257e29be23c459f0edf98ef9366d7ba47e24a902eba4e6a6b1940dd265ff0" + SP11 -> read "db39ad8956645cbcbcc053dff0d96cc6b04c639aa21e280f6eb7a6b56a297f20" spv -> dummyBRH spv 0xe01 } } @@ -597,7 +597,7 @@ testBB2E = SP7 -> read "99fc52af1ee2336f0d353a84c4d6c15345882271f648f7b84e69d9c40d5571c2" SP8 -> read "af4910f96957a8fc36844cfd1ca9dfadd47776b2a5b4e5f23011b4f580a01902" spv | blockResultHashAsP9 spv -> read "8e068776dc01e0079a00d9b531897e2a1e732c91ce9db92d64fd2cd183cea83e" - SP11 -> read "c592709ee287860eede9c8b4a3b33b6e26773b3466ff723a690ab8b049013557" + SP11 -> read "0b932aee7d4ad4c6567759ab723d655e0e1423d1f425c1664aafa9d69279f9e2" spv -> dummyBRH spv 0xe02 } } @@ -632,7 +632,7 @@ testBB3EX = SP7 -> read "0236b72bafe1575fdde0b01b35d24ad16613569600b83ed46b7e17c3c3dbaf28" SP8 -> read "854f08741a720a65b136eceae61cdb5fa59dd5f1af8bdcb58e41e850fec5034d" spv | blockResultHashAsP9 spv -> read "abe0485900b7b651c670f2fa506534ec2bc095f6d586a92f5b5b1160ca73c58c" - SP11 -> read "bf87f2ea9936340b8ca321ce54d259ebf8ddba9ad430950a12b696d1e92fafce" + SP11 -> read "42a2780099bfb291f58847348cc64bb0f92f6ef96cfbbbb4cfa92b9bbdcefdf8" spv -> dummyBRH spv 0x1e03 } } @@ -683,7 +683,7 @@ testBB3E = SP7 -> read "2a12a1b02d8cfb835d6f572ffd3a0156145ec2142b47ef7b9e9495b61ff241b7" SP8 -> read "d3cf0aa2e2c6fc2fd7d0be7f4192cc8d04e3afce1041d194a2c9ae437bc4aad6" spv | blockResultHashAsP9 spv -> read "87df979129f4462d51ca7214068557add1e4ecbb7ef097f2dc76bcc9ea7c90bb" - SP11 -> read "382b299734b376e30cfac2021617538d4759ea9e585370068329e1d21896b7b1" + SP11 -> read "d1f819ecf0f67b3c59da232d8802d67d1ed1a131da1195534d0b515e7a93e183" spv -> dummyBRH spv 0xe03 } } @@ -727,7 +727,7 @@ testBB4E = SP7 -> read "66cf8d97a956e2306e60337848775d606f575bd48f4d1e4420d4cf579d5bfb0e" SP8 -> read "d11da8da6f4a14fe5221847690e754450e79d65ad7e7ab1606f4f14d333f3a53" spv | blockResultHashAsP9 spv -> read "5f84d37c5c7d22893a285f3229d3b19dc9933e8220dbce84dc3f5bb7d1e325f1" - SP11 -> read "0aa6c3322b7b8e504b02abb4028707b3bf962cad3789a71ff7ca0fb83cffcc23" + SP11 -> read "8bdbe771749fd21e8b9b5981eee43ea21254193240f565384e0756790b27ab8b" spv -> dummyBRH spv 0xe04 } } @@ -755,7 +755,7 @@ testBB4E' = SP7 -> read "e0d460456228a923c4d0116b6add192b491279b24ce160067652e1afa11bac56" SP8 -> read "51894272a8818fc7ad4290f921bb2ff0488a098ebd0697b479136ec6ae5f5ed7" spv | blockResultHashAsP9 spv -> read "e44e1e7f0eac84030f80fe191c8ca9216da54a8533b9602c249d9b0a89b08ed9" - SP11 -> read "6a84f7a894bd9a517c57d99d9ffa7f0b3b476688d51a0ce1c54898f5247a59b9" + SP11 -> read "54f835e79543a15b08aad0bd899a95b908582f62fe0b77701da33ec468ecf4b3" spv -> dummyBRH spv 0x1e04 } } @@ -790,7 +790,7 @@ testBB5E' = SP7 -> read "2c8ff8fbc07b5e1486ebad3e241fa0aefdb7637651c6120b0a50a27057f7431a" SP8 -> read "970f5145277abea69844b53189c803ad7d5a8fd19105208ae4c580957224099a" spv | blockResultHashAsP9 spv -> read "d15789473fdb0b472bd1e1c2590f2b069ffe4c58a7079fec0e4f737a46d2c3f5" - SP11 -> read "87ebfa2764f6b7f167b5b1b93676d20fd9077d2895bed019164e72152b5310ab" + SP11 -> read "f907023b395cc115cad152fabe69c43b6674232a8e161bdf73ef3a96794a8d72" spv -> dummyBRH spv 0x1e05 } } @@ -838,7 +838,7 @@ testBB2Ex = SP7 -> read "a562963223c07cbcee46e78bba06968d578120b71eaf59d8ce12f3f384b21f47" SP8 -> read "85974099bbc92a64a27df7d057d130dd4eabcc0a8e605f701b449db3e68b3c3b" spv | blockResultHashAsP9 spv -> read "781624811b67e3c24f2e0d4b3596e4b973fe52306ba43448a47748e6f25269a0" - SP11 -> read "6e328b135dd3471ac91eabbb452b1669d896a12f672b8a0f2002134e3f09631f" + SP11 -> read "7e7777955e70566567f3e8cf8cdee8332d85590eb22822b4c893628a8679bbde" spv -> dummyBRH spv 0x0f02 } } @@ -888,7 +888,7 @@ testBB3Ex = SP7 -> read "2ea4f556dc29b5a1774635cb670f7b9aa3182eb2cc685b0e4f59daf9541cb539" SP8 -> read "fcb11273e588af86426c36f3d825d6eaf3d6db29339a071253ce764b4be3d45e" spv | blockResultHashAsP9 spv -> read "0cf1a98c6f1c0798cc2441633cabada717c2c9c2344f9f8593934ae5abf1d56f" - SP11 -> read "bf87f2ea9936340b8ca321ce54d259ebf8ddba9ad430950a12b696d1e92fafce" + SP11 -> read "91dfb964896dd5996cf5d8a48829ca88841a203dba9209cb2c05e030270fac3b" spv -> dummyBRH spv 0x1e03 } } @@ -926,7 +926,7 @@ testBB3EA = SP7 -> read "99fc52af1ee2336f0d353a84c4d6c15345882271f648f7b84e69d9c40d5571c2" SP8 -> read "af4910f96957a8fc36844cfd1ca9dfadd47776b2a5b4e5f23011b4f580a01902" spv | blockResultHashAsP9 spv -> read "8e068776dc01e0079a00d9b531897e2a1e732c91ce9db92d64fd2cd183cea83e" - SP11 -> read "c592709ee287860eede9c8b4a3b33b6e26773b3466ff723a690ab8b049013557" + SP11 -> read "0b932aee7d4ad4c6567759ab723d655e0e1423d1f425c1664aafa9d69279f9e2" spv -> dummyBRH spv 0x2f03 } } @@ -964,7 +964,7 @@ testBB4EA = SP7 -> read "e0d460456228a923c4d0116b6add192b491279b24ce160067652e1afa11bac56" SP8 -> read "e5c82e4ff33f6f068704a4da8a829e3e27659df26512a5b6449d4e27c50b0c5d" spv | blockResultHashAsP9 spv -> read "71df1f4743ed4401ff0ec3d3b8dc3edc66b639f3aa2c564f583a7c622fa46f76" - SP11 -> read "6b920342c97f7e26362169707b434762fb5260fdae8e6a5e38948dc6bcf21261" + SP11 -> read "cecab17d3b19b09785116c754e205a0a53410bdf5f4064ce7585b584b51654b9" spv -> dummyBRH spv 0x2f04 } } @@ -1017,7 +1017,7 @@ testBB1T = SP8 -> read "928a6ab0ca2a38086812bdbf39d46737345c358479d35c435f97c609ee0e215d" SP9 -> read "ba0d5ccac35703d012901d95669aea8088530b89d0e887f76c0b0ade4dd9c829" SP10 -> read "4a3d62fc566532a0151727c50cd80190e07f44b474f7074623b1e89646be9044" - SP11 -> read "c0b9d9194c5f99066263d7d26ed422f74ea33a216aa8b755590ec51b897c6f9a" + SP11 -> read "347a54c18d2d9395aadca56be5fa2601dbd2bdc60ecbd350a0d78472eef57965" spv -> dummyBRH spv 0x7001 } } @@ -1051,7 +1051,7 @@ testBB2T = SP8 -> read "60905a3baea93cf9564621ff1d8bbbc2469b50f26d101d125a092d104ca4fe16" SP9 -> read "691a94879d0b135e6b55bb8afdc6740f4a29d0a5ab94670bec0fb02b36291bd0" SP10 -> read "3a1fe79e0c5df5a73a55a02c9f410da73e10cfb02094fd053ef180ca3d6d8bac" - SP11 -> read "ced90933f07fe5475eb39c8d1f396a54030f22b65d181463be17ecaa4b8f525f" + SP11 -> read "1d1e9bd0d690cfbdbaadb656c0968729730e2ad81a809f5e38e3c537fef10ce9" spv -> dummyBRH spv 0x7002 } } @@ -1091,7 +1091,7 @@ testBB3T = SP8 -> read "8f1d79480a04412221a6a90c02ff98b8c59f08510067691c005f2e36b3c32563" SP9 -> read "51fbaf0502c838c7455786ac3c0397c51f3e6116a71cdc4a759b3a4bd243907f" SP10 -> read "eafb304cf3a771810688878fbc3b19d265d3728c455d9ae4941deacdbd7abbc6" - SP11 -> read "cda8fee450932bb94c2bdd617c63d1d05619397e0f36299065b943154904e78b" + SP11 -> read "74ceb7b50405b7ba469faa456cf03b7c6f2b252fcca5b2eee59e3d352bf6a2aa" spv -> dummyBRH spv 0x7003 } } @@ -1126,7 +1126,7 @@ testBB4T = SP8 -> read "efbd04f230226d140dcb1bd9a4eb06c0583a2b6edfa16dac2dde895383559998" SP9 -> read "0f73ad403626533632c43f2ca5938f9c3abb658df73a45328a5ca8c6630a973b" SP10 -> read "0f73ad403626533632c43f2ca5938f9c3abb658df73a45328a5ca8c6630a973b" - SP11 -> read "e0c0f297e67ae589f88201fb5563fcbb8d3e1cfacfe406851e1de893cf0f6f14" + SP11 -> read "22f37d67c6eb781310e244aae54daca34362fa1f92507ed34f25379df97b6e2c" spv -> dummyBRH spv 0x7004 } } @@ -1161,7 +1161,7 @@ testBB5T = SP8 -> read "242e8f6bf7e2a6fc9263b95b6e233b43010308d061e1f511b920ff7deea3ac9f" SP9 -> read "f375cd289ab3c51ff09cb910dcc89d942ea26a3c22904b0dd37a933c21805634" SP10 -> read "f375cd289ab3c51ff09cb910dcc89d942ea26a3c22904b0dd37a933c21805634" - SP11 -> read "fa69690ecb980c901775d08f7cedec32b43dfc2427e4c34e84a6a07d095b32b2" + SP11 -> read "13df774a7af01705c6f76d9589cb11088c3c23ec931428952e2d440a146ddae2" spv -> dummyBRH spv 0x7005 } } diff --git a/plt/plt-block-state/src/block_state.rs b/plt/plt-block-state/src/block_state.rs index 9ef3b653c1..991ee943ed 100644 --- a/plt/plt-block-state/src/block_state.rs +++ b/plt/plt-block-state/src/block_state.rs @@ -271,11 +271,11 @@ impl BlockStateOperations for ExecutionTimeBlockStateP9 Option { + fn delete_lock(&mut self, _: &LockId) -> bool { panic!("no locks on P9") } @@ -478,6 +478,8 @@ impl BlockStateQuery for ExecutionTimeBlockStateP11 { fn lock_configuration(&self, lock: &LockP11) -> LockConfiguration { lock.lock_configuration(&self.context) + .expect("lock must contain the configuration") + .to_owned() } fn lock_balances(&self, lock: &LockP11) -> impl Iterator { @@ -550,13 +552,13 @@ impl BlockStateOperations for ExecutionTimeBlockStateP11< self.block_state.update_token(&self.context, token).unwrap(); } - fn create_lock(&mut self, lock_id: LockId, configuration: LockConfiguration) { + fn create_lock(&mut self, configuration: LockConfiguration) { self.block_state - .create_lock(&self.context, lock_id, configuration) + .create_lock(&self.context, configuration) .unwrap(); } - fn delete_lock(&mut self, lock_id: &LockId) -> Option { + fn delete_lock(&mut self, lock_id: &LockId) -> bool { self.block_state .delete_lock(&self.context, lock_id) .unwrap() diff --git a/plt/plt-block-state/src/block_state_interface.rs b/plt/plt-block-state/src/block_state_interface.rs index fcacb78208..47afc6ef7e 100644 --- a/plt/plt-block-state/src/block_state_interface.rs +++ b/plt/plt-block-state/src/block_state_interface.rs @@ -161,6 +161,19 @@ pub trait BlockStateQuery { /// /// If the protocol version does not support protocol-level locks, this will return the empty /// list. + /// + /// # Ordering + /// + /// The order of the returned lock IDs is **not guaranteed**. Callers must not rely on any + /// particular ordering. + /// + /// # Warning — consensus safety + /// + /// Do **not** use this iterator directly to drive block-state mutations in the scheduler. + /// All nodes must execute transactions in identical order to reach the same state hash; + /// iterating in an unspecified order and acting on each element would produce diverging + /// state across nodes. Sort the result (or otherwise canonicalise it) before using it + /// to determine the sequence of any state-changing operations. fn lock_list(&self) -> impl ExactSizeIterator; /// Get the lock associated with a [`LockId`] (if it exists). If the protocol @@ -298,9 +311,9 @@ pub trait BlockStateOperations: BlockStateQuery { /// - The `lock` of the given configuration MUST NOT already be in use by a protocol-level /// lock, i.e. `assert_eq!(s.lock_by_id(lock_id).ok(), None)`. /// - The protocol version of the block state MUST support PLT locks. - fn create_lock(&mut self, lock_id: LockId, configuration: LockConfiguration); + fn create_lock(&mut self, configuration: LockConfiguration); - /// Delete a PLT lock with the given Lock ID. Returns the lock if it existed, or `None` + /// Delete a PLT lock with the given Lock ID. Returns `true` if it existed, or `false` /// if it did not exist. /// /// # Arguments @@ -310,7 +323,7 @@ pub trait BlockStateOperations: BlockStateQuery { /// # Preconditions /// /// This function may panic if the protocol version does not support locks. - fn delete_lock(&mut self, lock_id: &LockId) -> Option; + fn delete_lock(&mut self, lock_id: &LockId) -> bool; /// Track that a lock holds a balance for the given account and token. /// diff --git a/plt/plt-block-state/src/entity/block_state/p11.rs b/plt/plt-block-state/src/entity/block_state/p11.rs index 94baf9bcb4..b1da4e2385 100644 --- a/plt/plt-block-state/src/entity/block_state/p11.rs +++ b/plt/plt-block-state/src/entity/block_state/p11.rs @@ -137,19 +137,18 @@ impl BlockStateP11 { pub fn create_lock( &mut self, context: &EntityContext, - lock_id: LockId, configuration: LockConfiguration, ) -> BlockStateResult<()> { let mut new_locks = self.persistent.locks.value(&context.loader)?.into_owned(); - protocol_level_locks::p11::create_lock(context, &mut new_locks, lock_id, configuration)?; + protocol_level_locks::p11::create_lock(context, &mut new_locks, configuration)?; self.persistent.locks = HashedCacheableRef::new(new_locks); Ok(()) } - /// Delete the lock with the given [`LockId`] if it exists. Returns the - /// deleted lock if it existed, or `None` if it did not exist. + /// Delete the lock with the given [`LockId`] if it exists. Returns `true` if it existed, or + /// `false` if it did not exist. /// /// # Arguments /// - `lock_id` The ID of the PLT lock to delete. @@ -157,15 +156,14 @@ impl BlockStateP11 { &mut self, context: &EntityContext, lock_id: &LockId, - ) -> BlockStateResult> { + ) -> BlockStateResult { let mut new_locks = self.persistent.locks.value(&context.loader)?.into_owned(); let existing = protocol_level_locks::p11::delete_lock(context, &mut new_locks, lock_id)?; - if existing.is_some() { + if existing { // We only need to update the locks if a lock was actually deleted, // otherwise we would be unnecessarily updating the block state. self.persistent.locks = HashedCacheableRef::new(new_locks); } - Ok(existing) } @@ -179,6 +177,7 @@ impl BlockStateP11 { context, &*self.persistent.locks.value(&context.loader)?, ) + .cloned() .collect()) } diff --git a/plt/plt-block-state/src/entity/protocol_level_locks/p11.rs b/plt/plt-block-state/src/entity/protocol_level_locks/p11.rs index ac555a50f5..8945e91c6d 100644 --- a/plt/plt-block-state/src/entity/protocol_level_locks/p11.rs +++ b/plt/plt-block-state/src/entity/protocol_level_locks/p11.rs @@ -1,30 +1,40 @@ use crate::entity::{EntityContext, EntityContextTypes}; use crate::failure::{BlockStateFailure, BlockStateResult}; +use crate::persistent::blob_reference::hashed_cacheable_reference::HashedCacheableRef; +use crate::persistent::blob_store::StoreSerialized; use crate::persistent::protocol_level_locks::p11::{ - LockConfiguration, PersistentLockP11, PersistentLocksP11, + LockConfiguration, LockIndex, PersistentLockP11, PersistentLocksP11, }; use crate::persistent::protocol_level_tokens::p9::TokenIndex; +use crate::utils; use concordium_base::base::AccountIndex; use concordium_base::protocol_level_locks::LockId; -pub(crate) fn lock_list( +/// List all non-deleted lock ids in *no particular order*. +pub(crate) fn lock_list<'a, C: EntityContextTypes>( _context: &EntityContext, - persistent_locks: &PersistentLocksP11, -) -> impl ExactSizeIterator { - persistent_locks.locks.0.keys().cloned() + persistent_locks: &'a PersistentLocksP11, +) -> impl Iterator { + persistent_locks.lock_id_map.keys() } pub(crate) fn create_lock( - _context: &EntityContext, + context: &EntityContext, persistent_locks: &mut PersistentLocksP11, - lock_id: LockId, configuration: LockConfiguration, ) -> BlockStateResult<()> { + let lock_id = configuration.lock_id().clone(); let persistent = PersistentLockP11 { locked_balances: Default::default(), - configuration, + configuration: HashedCacheableRef::new(StoreSerialized(configuration)), }; - let existing = persistent_locks.locks.0.insert(lock_id.clone(), persistent); + let (lock_index, updated_locks) = persistent_locks + .locks + .insert_value(&context.loader, Some(persistent))?; + persistent_locks.locks = updated_locks; + let existing = persistent_locks + .lock_id_map + .insert(lock_id.clone(), lock_index); if existing.is_some() { return Err(BlockStateFailure::Invariant(format!( "lock with id {:?} already exists", @@ -36,65 +46,88 @@ pub(crate) fn create_lock( } pub(crate) fn delete_lock( - _context: &EntityContext, + context: &EntityContext, persistent_locks: &mut PersistentLocksP11, lock_id: &LockId, -) -> BlockStateResult> { - let existing = persistent_locks.locks.0.remove(lock_id); - Ok(existing.map(|persistent| LockP11 { - lock_id: lock_id.clone(), - persistent, - })) +) -> BlockStateResult { + let Some(lock_index) = persistent_locks.lock_id_map.remove(lock_id) else { + return Ok(false); + }; + persistent_locks.locks = persistent_locks + .locks + .update_value(&context.loader, lock_index, |_| Ok(None))? + .ok_or_else(|| { + BlockStateFailure::Invariant(format!("Lock not found by index: {:?}", lock_id)) + })?; + Ok(true) } pub(crate) fn update_lock( - _context: &EntityContext, + context: &EntityContext, persistent_locks: &mut PersistentLocksP11, lock: LockP11, ) -> BlockStateResult<()> { - persistent_locks + persistent_locks.locks = persistent_locks .locks - .0 - .insert(lock.lock_id, lock.persistent); + .update_value(&context.loader, lock.lock_index, |_| { + Ok(Some(lock.persistent)) + })? + .ok_or_else(|| { + BlockStateFailure::Invariant(format!("Lock not found by index: {:?}", lock.lock_index)) + })?; Ok(()) } pub(crate) fn lock_by_id( - _context: &EntityContext, + context: &EntityContext, persistent_locks: &PersistentLocksP11, lock_id: LockId, ) -> BlockStateResult> { - let Some(persistent) = persistent_locks.locks.0.get(&lock_id) else { + let Some(&lock_index) = persistent_locks.lock_id_map.get(&lock_id) else { + return Ok(None); + }; + let Some(persistent) = persistent_locks + .locks + .lookup_value(&context.loader, lock_index)? + else { + return Err(BlockStateFailure::Invariant(format!( + "No lock entry found for lock index {} ({lock_id})", + lock_index.0 + ))); + }; + let Some(persistent) = persistent.to_owned() else { + // Lock is deleted. return Ok(None); }; - Ok(Some(LockP11 { - lock_id, - persistent: persistent.clone(), + lock_index, + persistent, })) } /// Representation of protocol-level lock on P11 and later protocols with compatible model. #[derive(Debug)] pub struct LockP11 { - /// Lock ID - pub(crate) lock_id: LockId, + pub(crate) lock_index: LockIndex, /// Persistent model of the protocol-level lock. pub(crate) persistent: PersistentLockP11, } impl LockP11 { - /// Get the Lock ID of the lock. - pub fn lock_id(&self) -> &LockId { - &self.lock_id + /// Get the internal block state index of the lock. + pub fn lock_index(&self) -> LockIndex { + self.lock_index } /// Get the configuration of the protocol-level lock. pub fn lock_configuration( &self, - _context: &EntityContext, - ) -> LockConfiguration { - self.persistent.configuration.clone() + context: &EntityContext, + ) -> BlockStateResult> { + self.persistent + .configuration + .value(&context.loader) + .map(|cow| cow.cow_project()) } /// Get the set of account/token balances currently tracked under the lock. @@ -103,7 +136,7 @@ impl LockP11 { /// hold a non-zero locked balance. The corresponding amount is tracked in the /// token module state. pub fn lock_balance_refs(&self) -> Vec<(AccountIndex, TokenIndex)> { - self.persistent.locked_balances.iter().cloned().collect() + self.persistent.locked_balances.0.iter().cloned().collect() } /// Track that the lock holds a balance for the given account and token. @@ -118,6 +151,7 @@ impl LockP11 { pub fn add_lock_balance_ref(&mut self, account_index: AccountIndex, token_index: TokenIndex) { self.persistent .locked_balances + .0 .insert((account_index, token_index)); } @@ -140,6 +174,7 @@ impl LockP11 { ) -> bool { self.persistent .locked_balances + .0 .remove(&(account_index, token_index)) } } diff --git a/plt/plt-block-state/src/persistent/blob_store.rs b/plt/plt-block-state/src/persistent/blob_store.rs index fe8f74d7ff..9595d3461c 100644 --- a/plt/plt-block-state/src/persistent/blob_store.rs +++ b/plt/plt-block-state/src/persistent/blob_store.rs @@ -50,6 +50,22 @@ pub trait Loadable: Sized { ) -> Result; } +impl Loadable for Option { + fn load_from_buffer( + mut buffer: impl Read, + loader: &impl BlobStoreLoad, + ) -> Result { + let tag: u8 = buffer.get().map_parse_err_to_block_state_err()?; + match tag { + 0 => Ok(None), + 1 => Ok(Some(Loadable::load_from_buffer(buffer, loader)?)), + _ => Err(BlockStateFailure::BlobStoreDecode(format!( + "Invalid option tag: {tag}" + ))), + } + } +} + /// A trait implemented by types that can be stored to a [blob store](BlobStoreStore). pub trait Storable { /// Store the value in the given `buffer` that will be written to the blob store. @@ -74,6 +90,17 @@ impl Storable for &mut T { } } +impl Storable for Option { + fn store_to_buffer(&self, mut buffer: impl Buffer, storer: &mut impl BlobStoreStore) { + match self { + None => buffer.put(0u8), + Some(inner) => { + buffer.put(1u8); + inner.store_to_buffer(buffer, storer) + } + } + } +} /// Adapter for types implementing [`Serialize`] that /// allows them to be used as block state components. #[derive(Debug, Clone, Copy, Default, Eq, PartialEq)] diff --git a/plt/plt-block-state/src/persistent/block_state/p11.rs b/plt/plt-block-state/src/persistent/block_state/p11.rs index d8f3de65cf..6c782a0cc4 100644 --- a/plt/plt-block-state/src/persistent/block_state/p11.rs +++ b/plt/plt-block-state/src/persistent/block_state/p11.rs @@ -81,7 +81,13 @@ mod test { let mut block_state = BlockStateP11::default(); // Create locks + let lock_id1 = LockId { + account_index: 1, + sequence_number: 1, + creation_order: 0, + }; let configuration1 = LockConfiguration::new( + lock_id1.clone(), vec![AccountIndex::from(1), AccountIndex::from(2)], TransactionTime::from(100u64), LockControllerConfig::SimpleV0(LockControllerSimpleV0 { @@ -97,13 +103,9 @@ mod test { memo: Some(CborMemo::Raw(Memo::try_from(vec![0, 1]).unwrap())), }), ); - let lock_id1 = LockId { - account_index: 1, - sequence_number: 1, - creation_order: 0, - }; + block_state - .create_lock(&context, lock_id1.clone(), configuration1.clone()) + .create_lock(&context, configuration1.clone()) .unwrap(); let mut lock1 = block_state .lock_by_id(&context, &lock_id1) @@ -118,6 +120,7 @@ mod test { creation_order: 0, }; let configuration2 = LockConfiguration::new( + lock_id2.clone(), vec![], TransactionTime::from(0u64), LockControllerConfig::SimpleV0(LockControllerSimpleV0 { @@ -128,15 +131,42 @@ mod test { }), ); block_state - .create_lock(&context, lock_id2.clone(), configuration2.clone()) + .create_lock(&context, configuration2.clone()) .unwrap(); + // Create a third lock and then delete it + let lock_id3 = LockId { + account_index: 3, + sequence_number: 1, + creation_order: 0, + }; + let configuration3 = LockConfiguration::new( + lock_id3.clone(), + vec![], + TransactionTime::from(0u64), + LockControllerConfig::SimpleV0(LockControllerSimpleV0 { + grants: Vec::new(), + tokens: Vec::new(), + keep_alive: false, + memo: None, + }), + ); + block_state.create_lock(&context, configuration3).unwrap(); + let was_deleted = block_state.delete_lock(&context, &lock_id3).unwrap(); + assert!(was_deleted, "lock3 should be deleted"); + // Store and load block state let blob_ref = blob_store::store_to_store(&mut context.loader, block_state.persistent); let block_state = entity_test_stub::load_block_state_p11(&context, blob_ref); // Assert loaded state assert_eq!(block_state.lock_list(&context).unwrap().len(), 2); + + // Assert the deleted lock is absent + block_state + .lock_by_id(&context, &lock_id3) + .unwrap() + .expect_err("lock3 should not exist after deletion and reload"); let lock1 = block_state .lock_by_id(&context, &lock_id1) .unwrap() @@ -148,13 +178,19 @@ mod test { (AccountIndex::from(1), TokenIndex(1)) ] ); - assert_eq!(lock1.lock_configuration(&context), configuration1); + assert_eq!( + lock1.lock_configuration(&context).unwrap().into_owned(), + configuration1 + ); let lock2 = block_state .lock_by_id(&context, &lock_id2) .unwrap() .unwrap(); assert_eq!(lock2.lock_balance_refs(), vec![]); - assert_eq!(lock2.lock_configuration(&context), configuration2); + assert_eq!( + lock2.lock_configuration(&context).unwrap().into_owned(), + configuration2 + ); } /// Assert that hash and stored bytes of an empty block state matches snapshot. @@ -168,7 +204,7 @@ mod test { let hash = persistent_block_state.hash(&context.loader).expect("hash"); assert_eq!( format!("{}", hash), - "984855a88cabec51de8e854465656e6e5151843bffe8ae219b04e987b663e601" + "db35d91962f8f0315adb99d687d65c796ac67f2956b02e80fb667589f64efcb5" ); // Assert storage @@ -219,7 +255,13 @@ mod test { let _token2 = block_state.create_token(&context, configuration2.clone()); // Create locks + let lock_id1 = LockId { + account_index: 1, + sequence_number: 1, + creation_order: 0, + }; let configuration1 = LockConfiguration::new( + lock_id1.clone(), vec![AccountIndex::from(1), AccountIndex::from(2)], TransactionTime::from(100u64), LockControllerConfig::SimpleV0(LockControllerSimpleV0 { @@ -235,14 +277,7 @@ mod test { memo: Some(CborMemo::Raw(Memo::try_from(vec![0, 1]).unwrap())), }), ); - let lock_id1 = LockId { - account_index: 1, - sequence_number: 1, - creation_order: 0, - }; - block_state - .create_lock(&context, lock_id1.clone(), configuration1) - .unwrap(); + block_state.create_lock(&context, configuration1).unwrap(); let mut lock1 = block_state .lock_by_id(&context, &lock_id1) .unwrap() @@ -256,6 +291,7 @@ mod test { creation_order: 0, }; let configuration2 = LockConfiguration::new( + lock_id2.clone(), vec![], TransactionTime::from(0u64), LockControllerConfig::SimpleV0(LockControllerSimpleV0 { @@ -265,22 +301,20 @@ mod test { memo: None, }), ); - block_state - .create_lock(&context, lock_id2.clone(), configuration2) - .unwrap(); + block_state.create_lock(&context, configuration2).unwrap(); // Assert hash let hash = block_state.persistent.hash(&context.loader).expect("hash"); assert_eq!( format!("{}", hash), - "520022f22e5c63de1f3dd4e8aac8fcd72a35d1dcba3e0b5dec132650ccb0be3b" + "d4c2f4dae46bab4c1eb62d4dfa57a9fd998778adf4c74e1fdf94957eda559922" ); // Assert storage blob_store::store_to_store(&mut context.loader, &block_state.persistent); assert_eq!( hex::encode(context.loader.0), - "000000000000002806746f6b656e310505050505050505050505050505050505050505050505050505050505050505020000000000000025edbda48b85971b3a874334ca94f07e55e6a6e63eabca968d1257a3223e1b84e14002010100000000000000002503b0eab929105fd6df1ec793cbaf1b554a7a385520a9f7c902adf0219ace6dab4002000000000000000000003648b07111a93452374c7bcf66ee01959af6b4a52cb7cd299341e9ea77b378b0230300000201000000000000005d020000000000000030000000000000000901000000000000008a0000000000000011000000000000000000000000000000c86400000000000000090000000000000000d9000000000000002806746f6b656e3205050505050505050505050505050505050505050505050505050505050505050400000000000000010000000000000000110000000000000103000000000000013300000000000000000900000000000000013c0000000000000021000000000000000201000000000000000000000000000000f2000000000000015500000000000000bc00000000000000020000000000000001000000000000000100000000000000000000000000000002000000000000000000000000000000000000000000000001000000000000000100020000000000000001000000000000000200000000000000640000010000000000000001020300000208746f6b656e69643108746f6b656e696432010100000200010000000000000002000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000166000000000000018f" + "000000000000002806746f6b656e310505050505050505050505050505050505050505050505050505050505050505020000000000000025edbda48b85971b3a874334ca94f07e55e6a6e63eabca968d1257a3223e1b84e14002010100000000000000002503b0eab929105fd6df1ec793cbaf1b554a7a385520a9f7c902adf0219ace6dab4002000000000000000000003648b07111a93452374c7bcf66ee01959af6b4a52cb7cd299341e9ea77b378b0230300000201000000000000005d020000000000000030000000000000000901000000000000008a0000000000000011000000000000000000000000000000c86400000000000000090000000000000000d9000000000000002806746f6b656e3205050505050505050505050505050505050505050505050505050505050505050400000000000000010000000000000000110000000000000103000000000000013300000000000000000900000000000000013c0000000000000021000000000000000201000000000000000000000000000000f20000000000000155000000000000005b00000000000000010000000000000001000000000000000000020000000000000001000000000000000200000000000000640000010000000000000001020300000208746f6b656e69643108746f6b656e6964320101000002000100000000000000310100000000000000020000000000000000000000000000000000000000000000010000000000000001000000000000018f00000000000000090000000000000001f2000000000000002900000000000000020000000000000007000000000000000000000000000000000000000000000000000000000000000011010000000000000000000000000000023c000000000000000900000000000000026d00000000000000210000000000000002010000000000000000000000000000022b0000000000000286000000000000001000000000000001660000000000000297" ); } } diff --git a/plt/plt-block-state/src/persistent/cacheable.rs b/plt/plt-block-state/src/persistent/cacheable.rs index 2a33b3da42..1c238eba5d 100644 --- a/plt/plt-block-state/src/persistent/cacheable.rs +++ b/plt/plt-block-state/src/persistent/cacheable.rs @@ -22,3 +22,12 @@ impl Cacheable for StoreSerialized { Ok(()) } } + +impl Cacheable for Option { + fn cache_reference_values(&self, loader: &impl BlobStoreLoad) -> BlockStateResult<()> { + match self { + Some(inner) => inner.cache_reference_values(loader), + None => Ok(()), + } + } +} diff --git a/plt/plt-block-state/src/persistent/hash.rs b/plt/plt-block-state/src/persistent/hash.rs index 564a99b2e8..23ae9e83bc 100644 --- a/plt/plt-block-state/src/persistent/hash.rs +++ b/plt/plt-block-state/src/persistent/hash.rs @@ -25,6 +25,15 @@ impl Hashable for StoreSerialized { } } +impl Hashable for Option { + fn hash(&self, loader: &impl BlobStoreLoad) -> BlockStateResult { + match self { + None => Ok(hash_of_serialization(0u8)), + Some(inner) => Ok(hash_of_serialization((1u8, inner.hash(loader)?))), + } + } +} + /// Calculate hash by digesting the bytes of two hashes. pub fn hash_of_hashes(hash1: Hash, hash2: Hash) -> Hash { let mut hasher = sha2::Sha256::new(); diff --git a/plt/plt-block-state/src/persistent/protocol_level_locks/p11.rs b/plt/plt-block-state/src/persistent/protocol_level_locks/p11.rs index 6e24030350..5a0da45743 100644 --- a/plt/plt-block-state/src/persistent/protocol_level_locks/p11.rs +++ b/plt/plt-block-state/src/persistent/protocol_level_locks/p11.rs @@ -1,9 +1,11 @@ use crate::failure::{BlockStateFailure, BlockStateResult}; +use crate::persistent::blob_reference::hashed_cacheable_reference::HashedCacheableRef; use crate::persistent::blob_store::{ BlobStoreLoad, BlobStoreStore, Loadable, Storable, StoreSerialized, }; use crate::persistent::cacheable::Cacheable; -use crate::persistent::hash::Hashable; +use crate::persistent::hash::{self, Hashable}; +use crate::persistent::lfmb_tree::{LfmbTree, LfmbTreeKey}; use crate::persistent::protocol_level_tokens::p9::TokenIndex; use concordium_base::base::AccountIndex; use concordium_base::common::types::TransactionTime; @@ -11,23 +13,61 @@ use concordium_base::common::{Buffer, Serialize}; use concordium_base::hashes::Hash; use concordium_base::protocol_level_locks::{LockControllerSimpleV0Capability, LockId}; use concordium_base::protocol_level_tokens::{CborMemo, TokenId}; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeSet; use std::io::Read; +/// Index of the protocol-level lock in the block state map of locks. +/// +/// This type is the internal identifier of the lock in the block state and should never be exposed +/// in the API, events or used in state hashing. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct LockIndex(pub u64); + +impl LfmbTreeKey for LockIndex { + fn to_u64(self) -> u64 { + self.0 + } + + fn from_u64(key: u64) -> Self { + Self(key) + } +} + /// Block state for protocol level locks on P11 and later protocols that uses the same representation. #[derive(Debug, Clone, Default)] pub struct PersistentLocksP11 { - pub(crate) locks: StoreSerialized>, + /// Persistent map of lock index to locks. + /// + /// Here `None` represents a lock which has been deleted and acts as tombstone, preventing new + /// locks from using the same index. + pub(crate) locks: LfmbTree>, + /// Index for mapping Lock ID to the internal lock index used above. + /// + /// Deleted locks are absent from this index. + pub(crate) lock_id_map: im::HashMap, } impl Loadable for PersistentLocksP11 { fn load_from_buffer( - buffer: impl Read, + mut buffer: impl Read, loader: &impl BlobStoreLoad, ) -> Result { - let locks = Loadable::load_from_buffer(buffer, loader)?; - - Ok(Self { locks }) + let locks: LfmbTree> = + Loadable::load_from_buffer(&mut buffer, loader)?; + // To construct the full lock id to lock index map, we need to read the LFMBTree from + // the blob store. This is not ideal. If the state is to be cached after loading, we would + // rather wait until it is cached in memory before constructing the map. + let mut lock_id_map = im::HashMap::new(); + for item in locks.values(loader) { + let (lock_index, lock) = item?; + // Skip the deleted locks. + let Some(lock) = lock.as_ref() else { + continue; + }; + let conf = lock.configuration.value(loader)?; + lock_id_map.insert(conf.0.lock_id.clone(), lock_index); + } + Ok(Self { locks, lock_id_map }) } } @@ -50,19 +90,56 @@ impl Hashable for PersistentLocksP11 { } /// The block state for a single protocol-level lock. -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone)] pub struct PersistentLockP11 { - /// Contains references to the tokens with balances locked within this lock - pub locked_balances: BTreeSet<(AccountIndex, TokenIndex)>, + /// Contains references to the tokens with balances locked within this lock. + /// + /// Note the entire collection will be written to disk every time this struct is written to disk. + pub locked_balances: StoreSerialized>, /// The configuration parameters for the lock. - pub configuration: LockConfiguration, + pub configuration: HashedCacheableRef>, +} + +impl Loadable for PersistentLockP11 { + fn load_from_buffer( + mut buffer: impl Read, + loader: &impl BlobStoreLoad, + ) -> Result { + let locked_balances = Loadable::load_from_buffer(&mut buffer, loader)?; + let configuration = Loadable::load_from_buffer(&mut buffer, loader)?; + Ok(Self { + locked_balances, + configuration, + }) + } +} + +impl Storable for PersistentLockP11 { + fn store_to_buffer(&self, mut buffer: impl Buffer, storer: &mut impl BlobStoreStore) { + self.locked_balances.store_to_buffer(&mut buffer, storer); + self.configuration.store_to_buffer(&mut buffer, storer); + } +} + +impl Cacheable for PersistentLockP11 { + fn cache_reference_values(&self, loader: &impl BlobStoreLoad) -> BlockStateResult<()> { + self.configuration.cache_reference_values(loader) + } +} + +impl Hashable for PersistentLockP11 { + fn hash(&self, loader: &impl BlobStoreLoad) -> BlockStateResult { + let locked_balances = self.locked_balances.hash(loader)?; + let configuration = self.configuration.hash(loader)?; + Ok(hash::hash_of_hashes(locked_balances, configuration)) + } } /// Lock configuration at the block state level. -/// -/// TODO: COR-2295 - proper state implementation #[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub struct LockConfiguration { + /// Identifier of the lock. + lock_id: LockId, /// Accounts that can receive funds from this lock. /// /// The recipients are stored as a sorted vector of account indices, and @@ -79,6 +156,7 @@ pub struct LockConfiguration { impl LockConfiguration { pub fn new( + lock_id: LockId, mut recipients: Vec, expiry: TransactionTime, controller: LockControllerConfig, @@ -86,12 +164,17 @@ impl LockConfiguration { assert!(recipients.len() <= u16::MAX as usize, "Too many recipients"); recipients.sort(); Self { + lock_id, recipients, expiry, controller, } } + pub fn lock_id(&self) -> &LockId { + &self.lock_id + } + /// Get an iterator over the recipient accounts. pub fn recipients_iter(&self) -> impl Iterator { self.recipients.iter() @@ -178,6 +261,11 @@ mod test { use concordium_base::protocol_level_locks::LockControllerSimpleV0Capability; let lock_config = LockConfiguration { + lock_id: LockId { + account_index: 50, + sequence_number: 2, + creation_order: 0, + }, recipients: vec![AccountIndex::from(1u64), AccountIndex::from(2u64)], expiry: TransactionTime::from(1000u64), controller: LockControllerConfig::SimpleV0(LockControllerSimpleV0 { @@ -194,7 +282,7 @@ mod test { let bytes = common::to_bytes(&lock_config); assert_eq!( hex::encode(&bytes), - "00020000000000000001000000000000000200000000000003e800000100000000000000010100000106746f6b656e310100" + "00000000000000320000000000000002000000000000000000020000000000000001000000000000000200000000000003e800000100000000000000010100000106746f6b656e310100" ); let deserialized: LockConfiguration = @@ -207,6 +295,11 @@ mod test { use concordium_base::common::types::TransactionTime; let lock_config = LockConfiguration { + lock_id: LockId { + account_index: 50, + sequence_number: 2, + creation_order: 0, + }, recipients: vec![], expiry: TransactionTime::from(500u64), controller: LockControllerConfig::SimpleV0(LockControllerSimpleV0 { @@ -218,7 +311,10 @@ mod test { }; let bytes = common::to_bytes(&lock_config); - assert_eq!(hex::encode(&bytes), "000000000000000001f400000000000000"); + assert_eq!( + hex::encode(&bytes), + "000000000000003200000000000000020000000000000000000000000000000001f400000000000000" + ); let deserialized: LockConfiguration = common::from_bytes_complete(bytes.as_slice()).unwrap(); diff --git a/plt/plt-block-state/tests/block_state_p11.rs b/plt/plt-block-state/tests/block_state_p11.rs index 3f6b61b0ab..09ffd14db1 100644 --- a/plt/plt-block-state/tests/block_state_p11.rs +++ b/plt/plt-block-state/tests/block_state_p11.rs @@ -245,7 +245,13 @@ fn test_create_lock() { let mut block_state = BlockStateP11::default(); // Create lock + let lock_id = LockId { + account_index: 1, + sequence_number: 1, + creation_order: 0, + }; let configuration = LockConfiguration::new( + lock_id.clone(), vec![AccountIndex::from(1), AccountIndex::from(2)], TransactionTime::from(100u64), LockControllerConfig::SimpleV0(LockControllerSimpleV0 { @@ -262,14 +268,8 @@ fn test_create_lock() { }), ); - let lock_id = LockId { - account_index: 1, - sequence_number: 1, - creation_order: 0, - }; - block_state - .create_lock(&context, lock_id.clone(), configuration.clone()) + .create_lock(&context, configuration.clone()) .unwrap(); // Read configuration @@ -277,7 +277,9 @@ fn test_create_lock() { .lock_by_id(&context, &lock_id) .unwrap() .unwrap() - .lock_configuration(&context); + .lock_configuration(&context) + .unwrap() + .into_owned(); assert_eq!(read_configuration, configuration); } @@ -288,7 +290,13 @@ fn test_lock_by_id() { let mut block_state = BlockStateP11::default(); // Create lock + let lock_id = LockId { + account_index: 1, + sequence_number: 1, + creation_order: 0, + }; let configuration = LockConfiguration::new( + lock_id.clone(), vec![], TransactionTime::from(0u64), LockControllerConfig::SimpleV0(LockControllerSimpleV0 { @@ -299,22 +307,17 @@ fn test_lock_by_id() { }), ); - let lock_id = LockId { - account_index: 1, - sequence_number: 1, - creation_order: 0, - }; - - block_state - .create_lock(&context, lock_id.clone(), configuration.clone()) - .unwrap(); + block_state.create_lock(&context, configuration).unwrap(); // Get lock by id let lock = block_state .lock_by_id(&context, &lock_id) .unwrap() .expect("lock should exist"); - assert_eq!(lock.lock_id(), &lock_id); + assert_eq!( + lock.lock_configuration(&context).unwrap().lock_id(), + &lock_id + ); // Get non-existing lock by id let non_existing_lock_id = LockId { @@ -336,7 +339,13 @@ fn test_lock_balance_refs() { let mut block_state = BlockStateP11::default(); // Create lock + let lock_id = LockId { + account_index: 1, + sequence_number: 1, + creation_order: 0, + }; let configuration = LockConfiguration::new( + lock_id.clone(), vec![], TransactionTime::from(0u64), LockControllerConfig::SimpleV0(LockControllerSimpleV0 { @@ -347,15 +356,7 @@ fn test_lock_balance_refs() { }), ); - let lock_id = LockId { - account_index: 1, - sequence_number: 1, - creation_order: 0, - }; - - block_state - .create_lock(&context, lock_id.clone(), configuration.clone()) - .unwrap(); + block_state.create_lock(&context, configuration).unwrap(); let mut lock = block_state .lock_by_id(&context, &lock_id) .unwrap() @@ -385,6 +386,59 @@ fn test_lock_balance_refs() { ); } +/// Test creating a lock then deleting it. +#[test] +fn test_create_and_delete_lock() { + let context = entity_test_stub::new_no_external_context(); + let mut block_state = BlockStateP11::default(); + + // Create lock + let lock_id = LockId { + account_index: 1, + sequence_number: 1, + creation_order: 0, + }; + let configuration = LockConfiguration::new( + lock_id.clone(), + vec![], + TransactionTime::from(0u64), + LockControllerConfig::SimpleV0(LockControllerSimpleV0 { + grants: Vec::new(), + tokens: Vec::new(), + keep_alive: false, + memo: None, + }), + ); + + block_state.create_lock(&context, configuration).unwrap(); + + // Verify lock exists + block_state + .lock_by_id(&context, &lock_id) + .unwrap() + .expect("lock should exist after creation"); + + // Delete lock + let was_deleted = block_state.delete_lock(&context, &lock_id).unwrap(); + assert!( + was_deleted, + "delete_lock should return true for an existing lock" + ); + + // Verify lock no longer exists + block_state + .lock_by_id(&context, &lock_id) + .unwrap() + .expect_err("lock should not exist after deletion"); + + // Deleting again should return false + let was_deleted_again = block_state.delete_lock(&context, &lock_id).unwrap(); + assert!( + !was_deleted_again, + "delete_lock should return false for a non-existing lock" + ); +} + /// Test getting list of locks. Mirrors `test_plt_list` for the lock side of the block state. #[test] fn test_lock_list() { @@ -396,7 +450,13 @@ fn test_lock_list() { assert_eq!(locks, vec![]); // Create locks - let configuration = LockConfiguration::new( + let lock_id_a = LockId { + account_index: 1, + sequence_number: 1, + creation_order: 0, + }; + let configuration_a = LockConfiguration::new( + lock_id_a.clone(), vec![], TransactionTime::from(0u64), LockControllerConfig::SimpleV0(LockControllerSimpleV0 { @@ -407,24 +467,27 @@ fn test_lock_list() { }), ); - let lock_id_a = LockId { - account_index: 1, - sequence_number: 1, - creation_order: 0, - }; let lock_id_b = LockId { account_index: 2, sequence_number: 7, creation_order: 0, }; - block_state - .create_lock(&context, lock_id_a.clone(), configuration.clone()) - .unwrap(); - block_state - .create_lock(&context, lock_id_b.clone(), configuration) - .unwrap(); + let configuration_b = LockConfiguration::new( + lock_id_b.clone(), + vec![], + TransactionTime::from(0u64), + LockControllerConfig::SimpleV0(LockControllerSimpleV0 { + grants: Vec::new(), + tokens: Vec::new(), + keep_alive: false, + memo: None, + }), + ); + block_state.create_lock(&context, configuration_a).unwrap(); + block_state.create_lock(&context, configuration_b).unwrap(); - // Read lock list - let locks = block_state.lock_list(&context).unwrap(); + // Read lock list and sort for a stable comparison (lock_list order is not guaranteed). + let mut locks = block_state.lock_list(&context).unwrap(); + locks.sort(); assert_eq!(locks, vec![lock_id_a, lock_id_b]); } diff --git a/plt/plt-scheduler/src/locks/lock_configuration.rs b/plt/plt-scheduler/src/locks/lock_configuration.rs index f96d814ece..c8fab6c6df 100644 --- a/plt/plt-scheduler/src/locks/lock_configuration.rs +++ b/plt/plt-scheduler/src/locks/lock_configuration.rs @@ -50,17 +50,17 @@ pub fn get_lock_config( pub fn get_lock_info( bsq: &BSQ, lock: &LockP11, - configuration: &LockConfiguration, + lock_configuration: &LockConfiguration, ) -> Result { // Resolve recipients (block-state `AccountIndex`es) into `CborHolderAccount` values // by looking up each account's canonical address. - let recipients = get_recipients(bsq, configuration)?; + let recipients = get_recipients(bsq, lock_configuration)?; // Convert the lock controller configuration into the CBOR `LockController` shape used // by the `lock-info` payload. Variant-specific resolution (e.g. expanding grant // `AccountIndex`es to `CborHolderAccount`) lives on the per-variant // `crate::locks::lock_controller::LockController` impl. - let controller = configuration.controller().to_cbor_controller(bsq)?; + let controller = lock_configuration.controller().to_cbor_controller(bsq)?; // Group the tracked `(account, token)` balances by account so we emit a single // `LockAccountFunds` entry per account. @@ -74,7 +74,7 @@ pub fn get_lock_info( bsq.context(), &bsq.token_p11(&token), account_index, - lock.lock_id(), + lock_configuration.lock_id(), ) .map_err(|err| QueryLockError::StateInvariantViolation(err.to_string()))?; let amount = TokenAmount::from_raw(raw_balance.0, token_configuration.decimals); @@ -105,9 +105,9 @@ pub fn get_lock_info( .collect::>()?; Ok(LockInfo { - lock: lock.lock_id().clone(), + lock: lock_configuration.lock_id().clone(), recipients, - expiry: configuration.expiry(), + expiry: lock_configuration.expiry(), controller, funds, }) diff --git a/plt/plt-scheduler/src/scheduler/plt_scheduler.rs b/plt/plt-scheduler/src/scheduler/plt_scheduler.rs index a9141c60eb..f76a9e1081 100644 --- a/plt/plt-scheduler/src/scheduler/plt_scheduler.rs +++ b/plt/plt-scheduler/src/scheduler/plt_scheduler.rs @@ -104,12 +104,14 @@ where .lock_by_id(context, &details.lock)? .map_err(|err| TransactionRejectReason::NonExistentLockId(err.0))?; - let lock_configuration = lock.lock_configuration(context); + let lock_configuration = lock.lock_configuration(context)?; if lock_configuration .expiry() .is_expired(transaction_execution.timestamp()) { - return Err(TransactionRejectReason::LockExpired(lock.lock_id().clone()).into()); + return Err( + TransactionRejectReason::LockExpired(lock_configuration.lock_id().clone()).into(), + ); } lock_configuration.controller().validate_operation( @@ -132,7 +134,7 @@ where &mut token, transaction_execution.sender_account(), transaction_execution.sender_account_address(), - lock.lock_id(), + lock_configuration.lock_id(), raw_amount, memo, )? { @@ -180,12 +182,14 @@ where .lock_by_id(context, &details.lock)? .map_err(|err| TransactionRejectReason::NonExistentLockId(err.0))?; - let lock_configuration = lock.lock_configuration(context); + let lock_configuration = lock.lock_configuration(context)?; if lock_configuration .expiry() .is_expired(transaction_execution.timestamp()) { - return Err(TransactionRejectReason::LockExpired(lock.lock_id().clone()).into()); + return Err( + TransactionRejectReason::LockExpired(lock_configuration.lock_id().clone()).into(), + ); } let source_address = details.source.address; @@ -221,7 +225,7 @@ where if !lock_configuration.is_recipient(&recipient.account_index()) { return Err(TransactionRejectReason::LockRecipientNotPermitted( - lock.lock_id().clone(), + lock_configuration.lock_id().clone(), recipient_address, ) .into()); @@ -245,7 +249,7 @@ where source_address, &recipient, recipient_address, - lock.lock_id(), + lock_configuration.lock_id(), raw_amount, memo, )? @@ -261,8 +265,8 @@ where context, block_state, events, + lock_configuration_keeps_alive(&lock_configuration), lock, - &lock_configuration, source.account_index(), token_index, details.lock, @@ -292,12 +296,14 @@ where .lock_by_id(context, &details.lock)? .map_err(|err| TransactionRejectReason::NonExistentLockId(err.0))?; - let lock_configuration = lock.lock_configuration(context); + let lock_configuration = lock.lock_configuration(context)?; if lock_configuration .expiry() .is_expired(transaction_execution.timestamp()) { - return Err(TransactionRejectReason::LockExpired(lock.lock_id().clone()).into()); + return Err( + TransactionRejectReason::LockExpired(lock_configuration.lock_id().clone()).into(), + ); } let source_address = details.source.address; @@ -325,7 +331,7 @@ where &mut token, source.account_index(), source_address, - lock.lock_id(), + lock_configuration.lock_id(), raw_amount, memo, )? @@ -341,8 +347,8 @@ where context, block_state, events, + lock_configuration_keeps_alive(&lock_configuration), lock, - &lock_configuration, source.account_index(), token_index, details.lock, @@ -386,7 +392,8 @@ where }, ) .collect::, TransactionRejectReason>>()?; - let configuration = LockConfiguration::new(recipients, config.expiry, controller); + let configuration = + LockConfiguration::new(lock_id.clone(), recipients, config.expiry, controller); let config = get_lock_config(&bsq, &configuration).map_err(|err| { BlockStateFailure::Invariant(format!("Failed to get lock config for created lock: {err}")) @@ -397,7 +404,7 @@ where }; events.push(BlockItemEvent::LockCreated(event)); - block_state.create_lock(context, lock_id.clone(), configuration)?; + block_state.create_lock(context, configuration)?; Ok(()) } @@ -421,7 +428,7 @@ where .lock_by_id(context, &details.lock)? .map_err(|err| TransactionRejectReason::NonExistentLockId(err.0))?; - let lock_configuration = lock.lock_configuration(context); + let lock_configuration = lock.lock_configuration(context)?; let memo: Option = details.memo.clone().map(transactions::Memo::from); if !lock_configuration @@ -442,14 +449,14 @@ where events, &mut token, account_index, - lock.lock_id(), + lock_configuration.lock_id(), &memo, )?; block_state.update_token(context, token)?; } - block_state.delete_lock(context, lock.lock_id())?; + block_state.delete_lock(context, lock_configuration.lock_id())?; let event = events::LockDestroyEvent { - lock_id: lock.lock_id().clone(), + lock_id: lock_configuration.lock_id().clone(), }; events.push(BlockItemEvent::LockDestroyed(event)); Ok(()) @@ -460,8 +467,8 @@ fn remove_lock_balance_ref( context: &EntityContext, block_state: &mut BlockStateP11, events: &mut Vec, + lock_keeps_alive: bool, mut lock: plt_block_state::entity::protocol_level_locks::p11::LockP11, - lock_configuration: &LockConfiguration, account_index: AccountIndex, token_index: plt_block_state::persistent::protocol_level_tokens::p9::TokenIndex, lock_id: concordium_base::protocol_level_locks::LockId, @@ -471,7 +478,7 @@ fn remove_lock_balance_ref( // controlled by the lock, or there was no balance reference to remove. return Ok(()); } - if lock.lock_balance_refs().is_empty() && !lock_configuration_keeps_alive(lock_configuration) { + if lock.lock_balance_refs().is_empty() && !lock_keeps_alive { block_state.delete_lock(context, &lock_id)?; events.push(BlockItemEvent::LockDestroyed(events::LockDestroyEvent { lock_id,