From e333d12d4faac5357309100beb8618b33d114043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Mon, 25 Mar 2024 22:58:09 +0100 Subject: [PATCH 01/13] Integrate new execution engine into the node. --- concordium-base | 2 +- .../GlobalState/Persistent/BlockState.hs | 4 ++ .../Persistent/BlockState/Modules.hs | 50 +++++++++++++++---- .../GlobalState/Persistent/Instances.hs | 39 +++++++++------ .../src/Concordium/GlobalState/Wasm.hs | 19 +++++++ concordium-node/Cargo.lock | 12 ++--- 6 files changed, 93 insertions(+), 33 deletions(-) diff --git a/concordium-base b/concordium-base index 825fab04fb..195bbb5479 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 825fab04fb78445c9f1cdcaf4cb9dae33f3b831a +Subproject commit 195bbb5479af25229efd2c0a344f4110f3c74003 diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs index 1c0ba20332..f592c0831d 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs @@ -3687,6 +3687,10 @@ migratePersistentBlockState migration oldState = do migrateBlockPointers :: forall oldpv pv t m. ( SupportMigration m t, + MonadProtocolVersion m, + MPV m ~ oldpv, + MonadProtocolVersion (t m), + MPV (t m) ~ pv, SupportsPersistentAccount oldpv m, SupportsPersistentAccount pv (t m), Modules.SupportsPersistentModule m, diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs index 047b73164f..217af09b29 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs @@ -38,6 +38,8 @@ import Concordium.GlobalState.Persistent.CachedRef import Concordium.GlobalState.Persistent.LFMBTree (LFMBTree') import qualified Concordium.GlobalState.Persistent.LFMBTree as LFMB import qualified Concordium.GlobalState.Wasm as GSWasm +import qualified Concordium.Scheduler.WasmIntegration as WasmV0 +import qualified Concordium.Scheduler.WasmIntegration.V1 as WasmV1 import Concordium.Types import Concordium.Types.HashableTo import Concordium.Utils @@ -150,7 +152,7 @@ instance (MonadBlobStore m) => Cacheable m Module -- | This instance is based on and should be compatible with the 'Serialize' instance -- for 'BasicModuleInterface'. -instance (MonadBlobStore m) => DirectBlobStorable m Module where +instance (MonadBlobStore m, MonadProtocolVersion m) => DirectBlobStorable m Module where loadDirect br = do bs <- loadRaw br let getModule = do @@ -195,7 +197,27 @@ instance (MonadBlobStore m) => DirectBlobStorable m Module where return $! ModuleV1 (ModuleV{..}) case runGet getModule bs of Left e -> error (e ++ " :: " ++ show bs) - Right !mv -> return mv + Right mv@(ModuleV0 (ModuleV{moduleVInterface = GSWasm.ModuleInterface{miModule = PIMVPtr artPtr}, ..})) -> do + artBS <- loadBlobPtr artPtr + if GSWasm.isLegacyArtifact artBS + then do + source <- loadRef moduleVSource + case WasmV0.processModule source of + Nothing -> error "Stored module that is not valid." + Just iface -> do + return $! ModuleV0 (ModuleV{moduleVInterface = makePersistentInstrumentedModuleV <$> iface, ..}) + else return mv + Right mv@(ModuleV1 (ModuleV{moduleVInterface = GSWasm.ModuleInterface{miModule = PIMVPtr artPtr}, ..})) -> do + artBS <- loadBlobPtr artPtr + if GSWasm.isLegacyArtifact artBS + then do + source <- loadRef moduleVSource + case WasmV1.processModule (protocolVersion @(MPV m)) source of + Nothing -> error "Stored module that is not valid." + Just iface -> do + return $! ModuleV1 (ModuleV{moduleVInterface = makePersistentInstrumentedModuleV <$> iface, ..}) + else return mv + Right mv -> return mv storeUpdateDirect mdl = do case mdl of @@ -279,13 +301,13 @@ data Modules = Modules makeLenses ''Modules -- | The hash of the collection of modules is the hash of the tree. -instance (SupportsPersistentModule m, IsBlockHashVersion (BlockHashVersionFor pv)) => MHashableTo m (ModulesHash pv) Modules where +instance (MonadProtocolVersion m, SupportsPersistentModule m, IsBlockHashVersion (BlockHashVersionFor pv)) => MHashableTo m (ModulesHash pv) Modules where getHashM = fmap (ModulesHash . LFMB.theLFMBTreeHash @(BlockHashVersionFor pv)) . getHashM . _modulesTable -instance (SupportsPersistentModule m) => BlobStorable m Modules where +instance (MonadProtocolVersion m, SupportsPersistentModule m) => BlobStorable m Modules where load = do table <- load return $ do @@ -302,7 +324,7 @@ instance (SupportsPersistentModule m) => BlobStorable m Modules where (pModulesTable, _modulesTable') <- storeUpdate _modulesTable return (pModulesTable, m{_modulesTable = _modulesTable'}) -instance (SupportsPersistentModule m) => Cacheable m Modules where +instance (MonadProtocolVersion m, SupportsPersistentModule m) => Cacheable m Modules where cache Modules{..} = do modulesTable' <- cache _modulesTable return Modules{_modulesTable = modulesTable', ..} @@ -316,7 +338,7 @@ emptyModules = Modules LFMB.empty Map.empty -- | Try to add interfaces to the module table. If a module with the given -- reference exists returns @Nothing@. putInterface :: - (IsWasmVersion v, SupportsPersistentModule m) => + (MonadProtocolVersion m, IsWasmVersion v, SupportsPersistentModule m) => (GSWasm.ModuleInterfaceV v, WasmModuleV v) -> Modules -> m (Maybe Modules) @@ -334,7 +356,7 @@ putInterface (modul, src) m = where mref = GSWasm.moduleReference modul -getModule :: (SupportsPersistentModule m) => ModuleRef -> Modules -> m (Maybe Module) +getModule :: (MonadProtocolVersion m, SupportsPersistentModule m) => ModuleRef -> Modules -> m (Maybe Module) getModule ref mods = let modIdx = Map.lookup ref (mods ^. modulesMap) in case modIdx of @@ -344,7 +366,7 @@ getModule ref mods = -- | Gets the 'HashedCachedRef' to a module as stored in the module table -- to be given to instances when associating them with the interface. -- The reason we return the reference here is to allow for sharing of the reference. -getModuleReference :: (SupportsPersistentModule m) => ModuleRef -> Modules -> m (Maybe CachedModule) +getModuleReference :: (MonadProtocolVersion m, SupportsPersistentModule m) => ModuleRef -> Modules -> m (Maybe CachedModule) getModuleReference ref mods = let modIdx = Map.lookup ref (mods ^. modulesMap) in case modIdx of @@ -353,14 +375,14 @@ getModuleReference ref mods = -- | Get an interface by module reference. getInterface :: - (SupportsPersistentModule m) => + (MonadProtocolVersion m, SupportsPersistentModule m) => ModuleRef -> Modules -> m (Maybe (GSWasm.ModuleInterface PersistentInstrumentedModuleV)) getInterface ref mods = fmap getModuleInterface <$> getModule ref mods -- | Get the source of a module by module reference. -getSource :: (SupportsPersistentModule m) => ModuleRef -> Modules -> m (Maybe WasmModule) +getSource :: (MonadProtocolVersion m, SupportsPersistentModule m) => ModuleRef -> Modules -> m (Maybe WasmModule) getSource ref mods = do m <- getModule ref mods case m of @@ -378,7 +400,12 @@ moduleRefList mods = Map.keys (mods ^. modulesMap) -- | Migrate smart contract modules from context @m@ to the context @t m@. migrateModules :: forall t m. - (SupportsPersistentModule m, SupportsPersistentModule (t m), SupportMigration m t) => + ( MonadProtocolVersion m, + MonadProtocolVersion (t m), + SupportsPersistentModule m, + SupportsPersistentModule (t m), + SupportMigration m t + ) => Modules -> t m Modules migrateModules mods = do @@ -398,6 +425,7 @@ migrateModules mods = do migrateModuleV :: forall v. (IsWasmVersion v) => ModuleV v -> t m CachedModule migrateModuleV ModuleV{..} = do + -- TODO: Recompile stuff that needs to be updated newModuleVSource <- do -- Load the module source from the old context. s <- lift (loadRef moduleVSource) diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/Instances.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/Instances.hs index ce45fc341a..4a19c8c22e 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/Instances.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/Instances.hs @@ -128,6 +128,7 @@ data PersistentInstanceV (v :: Wasm.WasmVersion) = PersistentInstanceV migratePersistentInstanceV :: forall v t m. ( Wasm.IsWasmVersion v, + MonadProtocolVersion (t m), SupportsPersistentModule m, SupportsPersistentModule (t m), MonadTrans t @@ -179,7 +180,7 @@ data PersistentInstance (pv :: ProtocolVersion) where -- | Migrate persistent instances from the old to the new protocol version. migratePersistentInstance :: forall oldpv pv t m. - (SupportsPersistentModule m, SupportsPersistentModule (t m), SupportMigration m t) => + (MonadProtocolVersion (t m), SupportsPersistentModule m, SupportsPersistentModule (t m), SupportMigration m t) => -- | The __already migrated__ modules. The modules were already migrated by the -- module migration, so we want to insert references to the existing modules -- in the instances so that we don't end up with duplicates both in-memory @@ -202,7 +203,7 @@ cacheInstanceParameters :: (MonadBlobStore m) => PersistentInstance pv -> m (Per cacheInstanceParameters (PersistentInstanceV0 PersistentInstanceV{..}) = cacheBufferedRef pinstanceParameters cacheInstanceParameters (PersistentInstanceV1 PersistentInstanceV{..}) = cacheBufferedRef pinstanceParameters -loadInstanceModule :: (SupportsPersistentModule m) => PersistentInstance pv -> m Module +loadInstanceModule :: (MonadProtocolVersion m, SupportsPersistentModule m) => PersistentInstance pv -> m Module loadInstanceModule (PersistentInstanceV0 PersistentInstanceV{..}) = refLoad pinstanceModuleInterface loadInstanceModule (PersistentInstanceV1 PersistentInstanceV{..}) = refLoad pinstanceModuleInterface @@ -215,7 +216,7 @@ instance HashableTo H.Hash (PersistentInstance pv) where -- decide whether we are loading instance V0 or instance V1, we essentially have -- two implementations of BlobStorable. One for protocol versions <= P3, and -- another one for later protocol versions. The latter ones add versioning information. -instance (IsProtocolVersion pv, SupportsPersistentModule m) => BlobStorable m (PersistentInstance pv) where +instance (IsProtocolVersion pv, MonadProtocolVersion m, SupportsPersistentModule m, MPV m ~ pv) => BlobStorable m (PersistentInstance pv) where storeUpdate inst = do if demoteProtocolVersion (protocolVersion @pv) <= P3 then case inst of @@ -307,7 +308,7 @@ instance (MonadBlobStore m) => Cacheable m (InstanceStateV GSWasm.V1) where -- This cacheable instance is a bit unusual. Caching instances requires us to have access -- to the modules so that we can share the module interfaces from different instances. -instance (SupportsPersistentModule m) => Cacheable (ReaderT Modules m) (PersistentInstance pv) where +instance (MonadProtocolVersion m, SupportsPersistentModule m) => Cacheable (ReaderT Modules m) (PersistentInstance pv) where cache (PersistentInstanceV0 p@PersistentInstanceV{..}) = do modules <- ask lift $! do @@ -342,11 +343,11 @@ instance (SupportsPersistentModule m) => Cacheable (ReaderT Modules m) (Persiste -- | Construct instance information from a persistent instance, loading as much -- data as necessary from persistent storage. -mkInstanceInfo :: (SupportsPersistentModule m) => PersistentInstance pv -> m (InstanceInfoType PersistentInstrumentedModuleV InstanceStateV) +mkInstanceInfo :: (MonadProtocolVersion m, SupportsPersistentModule m) => PersistentInstance pv -> m (InstanceInfoType PersistentInstrumentedModuleV InstanceStateV) mkInstanceInfo (PersistentInstanceV0 inst) = InstanceInfoV0 <$> mkInstanceInfoV inst mkInstanceInfo (PersistentInstanceV1 inst) = InstanceInfoV1 <$> mkInstanceInfoV inst -mkInstanceInfoV :: (SupportsPersistentModule m, Wasm.IsWasmVersion v) => PersistentInstanceV v -> m (InstanceInfoTypeV PersistentInstrumentedModuleV InstanceStateV v) +mkInstanceInfoV :: (MonadProtocolVersion m, SupportsPersistentModule m, Wasm.IsWasmVersion v) => PersistentInstanceV v -> m (InstanceInfoTypeV PersistentInstrumentedModuleV InstanceStateV v) mkInstanceInfoV PersistentInstanceV{..} = do PersistentInstanceParameters{..} <- loadBufferedRef pinstanceParameters instanceModuleInterface <- moduleVInterface . unsafeToModuleV <$> refLoad pinstanceModuleInterface @@ -463,7 +464,7 @@ conditionalSetBit :: (Bits a) => Int -> Bool -> a -> a conditionalSetBit _ False x = x conditionalSetBit b True x = setBit x b -instance (IsProtocolVersion pv, BlobStorable m r, Cache.MonadCache ModuleCache m) => BlobStorable m (IT pv r) where +instance (MonadProtocolVersion m, IsProtocolVersion pv, MPV m ~ pv, BlobStorable m r, Cache.MonadCache ModuleCache m) => BlobStorable m (IT pv r) where storeUpdate (Branch{..}) = do (pl, l') <- storeUpdate branchLeft (pr, r') <- storeUpdate branchRight @@ -558,6 +559,10 @@ newContractInstanceIT mk t0 = (\(res, v) -> (res,) <$> membed v) =<< nci 0 t0 =< migrateIT :: forall oldpv pv t m. ( SupportMigration m t, + MonadProtocolVersion m, + MPV m ~ oldpv, + MonadProtocolVersion (t m), + MPV (t m) ~ pv, SupportsPersistentModule m, SupportsPersistentModule (t m), IsProtocolVersion oldpv, @@ -589,6 +594,10 @@ data Instances pv migrateInstances :: ( SupportMigration m t, + MonadProtocolVersion m, + MPV m ~ oldpv, + MonadProtocolVersion (t m), + MPV (t m) ~ pv, SupportsPersistentModule m, SupportsPersistentModule (t m), IsProtocolVersion oldpv, @@ -618,13 +627,13 @@ makeInstancesHash size inner = case sBlockHashVersionFor (protocolVersion @pv) o put inner instance - (IsProtocolVersion pv, SupportsPersistentModule m) => + (IsProtocolVersion pv, MonadProtocolVersion m, MPV m ~ pv, SupportsPersistentModule m) => MHashableTo m (InstancesHash pv) (Instances pv) where getHashM InstancesEmpty = return $ makeInstancesHash 0 $ H.hash "EmptyInstances" getHashM (InstancesTree size t) = makeInstancesHash size . getHash <$> mproject t -instance (IsProtocolVersion pv, SupportsPersistentModule m) => BlobStorable m (Instances pv) where +instance (IsProtocolVersion pv, MonadProtocolVersion m, MPV m ~ pv, SupportsPersistentModule m) => BlobStorable m (Instances pv) where storeUpdate i@InstancesEmpty = return (putWord8 0, i) storeUpdate (InstancesTree s t) = do (pt, t') <- storeUpdate t @@ -645,14 +654,14 @@ instance (MonadBlobStore m, Cacheable m r, Cacheable m (PersistentInstance pv)) cache (Leaf l) = Leaf <$> cache l cache vacant = return vacant -instance (IsProtocolVersion pv, SupportsPersistentModule m) => Cacheable (ReaderT Modules m) (Instances pv) where +instance (IsProtocolVersion pv, MonadProtocolVersion m, MPV m ~ pv, SupportsPersistentModule m) => Cacheable (ReaderT Modules m) (Instances pv) where cache i@InstancesEmpty = return i cache (InstancesTree s r) = InstancesTree s <$> cache r emptyInstances :: Instances pv emptyInstances = InstancesEmpty -newContractInstance :: forall m pv a. (IsProtocolVersion pv, SupportsPersistentModule m) => (ContractAddress -> m (a, PersistentInstance pv)) -> Instances pv -> m (a, Instances pv) +newContractInstance :: forall m pv a. (IsProtocolVersion pv, MonadProtocolVersion m, MPV m ~ pv, SupportsPersistentModule m) => (ContractAddress -> m (a, PersistentInstance pv)) -> Instances pv -> m (a, Instances pv) newContractInstance createInstanceFn InstancesEmpty = do let ca = ContractAddress 0 0 (res, newInst) <- createInstanceFn ca @@ -669,7 +678,7 @@ newContractInstance createInstanceFn (InstancesTree size tree) = do -- Otherwise, a vacancy is filled, and the size does not grow. return ((contractSubindex newContractAddress == 0, result), createdInstance) -deleteContractInstance :: forall m pv. (IsProtocolVersion pv, SupportsPersistentModule m) => ContractAddress -> Instances pv -> m (Instances pv) +deleteContractInstance :: forall m pv. (IsProtocolVersion pv, MonadProtocolVersion m, MPV m ~ pv, SupportsPersistentModule m) => ContractAddress -> Instances pv -> m (Instances pv) deleteContractInstance _ InstancesEmpty = return InstancesEmpty deleteContractInstance addr t0@(InstancesTree s it0) = dci (fmap (InstancesTree s) . membed) (contractIndex addr) =<< mproject it0 where @@ -696,7 +705,7 @@ deleteContractInstance addr t0@(InstancesTree s it0) = dci (fmap (InstancesTree in dci newCont (i - 2 ^ h) =<< mproject r | otherwise = return t0 -lookupContractInstance :: forall m pv. (IsProtocolVersion pv, SupportsPersistentModule m) => ContractAddress -> Instances pv -> m (Maybe (PersistentInstance pv)) +lookupContractInstance :: forall m pv. (IsProtocolVersion pv, MonadProtocolVersion m, MPV m ~ pv, SupportsPersistentModule m) => ContractAddress -> Instances pv -> m (Maybe (PersistentInstance pv)) lookupContractInstance _ InstancesEmpty = return Nothing lookupContractInstance addr (InstancesTree _ it0) = lu (contractIndex addr) =<< mproject it0 where @@ -711,7 +720,7 @@ lookupContractInstance addr (InstancesTree _ it0) = lu (contractIndex addr) =<< | i < 2 ^ (h + 1) = lu (i - 2 ^ h) =<< mproject r | otherwise = return Nothing -updateContractInstance :: forall m pv a. (IsProtocolVersion pv, SupportsPersistentModule m) => (PersistentInstance pv -> m (a, PersistentInstance pv)) -> ContractAddress -> Instances pv -> m (Maybe (a, Instances pv)) +updateContractInstance :: forall m pv a. (IsProtocolVersion pv, MonadProtocolVersion m, MPV m ~ pv, SupportsPersistentModule m) => (PersistentInstance pv -> m (a, PersistentInstance pv)) -> ContractAddress -> Instances pv -> m (Maybe (a, Instances pv)) updateContractInstance _ _ InstancesEmpty = return Nothing updateContractInstance fupd addr (InstancesTree s it0) = upd baseSuccess (contractIndex addr) =<< mproject it0 where @@ -744,7 +753,7 @@ updateContractInstance fupd addr (InstancesTree s it0) = upd baseSuccess (contra | otherwise = return Nothing -- | Retrieve the list of all instance addresses. The addresses are returned in increasing order. -allInstances :: forall m pv. (IsProtocolVersion pv, SupportsPersistentModule m) => Instances pv -> m [ContractAddress] +allInstances :: forall m pv. (IsProtocolVersion pv, MonadProtocolVersion m, MPV m ~ pv, SupportsPersistentModule m) => Instances pv -> m [ContractAddress] allInstances InstancesEmpty = return [] allInstances (InstancesTree _ it) = mapReduceIT mfun it where diff --git a/concordium-consensus/src/Concordium/GlobalState/Wasm.hs b/concordium-consensus/src/Concordium/GlobalState/Wasm.hs index 7af02f9025..c20f6387f7 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Wasm.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Wasm.hs @@ -27,6 +27,7 @@ module Concordium.GlobalState.Wasm ( BasicModuleInterface, HasModuleRef (..), HasEntrypoints (..), + isLegacyArtifact, ) where @@ -34,11 +35,15 @@ import Concordium.Types import Concordium.Utils.Serialization import Concordium.Wasm import qualified Data.ByteString as BS +import qualified Data.ByteString.Unsafe as BSU import Data.Kind import qualified Data.Map.Strict as Map import Data.Serialize import qualified Data.Set as Set import Data.Word +import Foreign (Ptr, castPtr) +import Foreign.C.Types (CSize (..)) +import System.IO.Unsafe -- | A processed module artifact as a 'BS.ByteString', as returned by the @validate_and_process_v*@ -- Rust functions, and used by the @call_receive_v*@ and @call_init_v*@ functions. @@ -212,3 +217,17 @@ instance Serialize BasicModuleInterface where putSafeMapOf put (putSafeSetOf put) miExposedReceive put miModule putWord64be miModuleSize + +foreign import ccall "is_legacy_artifact" + is_legacy_artifact :: + -- | Pointer to the Wasm artifact. + Ptr Word8 -> + -- | Length of the artifact. + CSize -> + -- | 1 for true, 0 for false + IO Word8 + +isLegacyArtifact :: BS.ByteString -> Bool +isLegacyArtifact artifactBS = unsafePerformIO $ + BSU.unsafeUseAsCStringLen artifactBS $ \(wasmArtifactPtr, wasmArtifactLen) -> + (== 1) <$> is_legacy_artifact (castPtr wasmArtifactPtr) (fromIntegral wasmArtifactLen) diff --git a/concordium-node/Cargo.lock b/concordium-node/Cargo.lock index 230308bc55..9505b18943 100644 --- a/concordium-node/Cargo.lock +++ b/concordium-node/Cargo.lock @@ -599,9 +599,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "chrono" -version = "0.4.33" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", @@ -667,7 +667,7 @@ dependencies = [ [[package]] name = "concordium-contracts-common" -version = "9.0.0" +version = "9.1.0" dependencies = [ "base64", "bs58", @@ -687,7 +687,7 @@ dependencies = [ [[package]] name = "concordium-contracts-common-derive" -version = "4.0.1" +version = "4.1.0" dependencies = [ "proc-macro2", "quote", @@ -696,7 +696,7 @@ dependencies = [ [[package]] name = "concordium-smart-contract-engine" -version = "4.0.0" +version = "5.0.0" dependencies = [ "anyhow", "byteorder", @@ -729,7 +729,7 @@ dependencies = [ [[package]] name = "concordium_base" -version = "4.0.0" +version = "5.0.0" dependencies = [ "aes", "anyhow", From fae4eac95abe7de4ef96f229a63c3439e0cc7405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Tue, 26 Mar 2024 09:16:35 +0100 Subject: [PATCH 02/13] Bump LMDB dependency to remove warnings. --- concordium-consensus/haskell-lmdb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concordium-consensus/haskell-lmdb b/concordium-consensus/haskell-lmdb index 774fee7794..6b835b955b 160000 --- a/concordium-consensus/haskell-lmdb +++ b/concordium-consensus/haskell-lmdb @@ -1 +1 @@ -Subproject commit 774fee77940591108a7142f8f1d3d41a1afd6e06 +Subproject commit 6b835b955b93b2f18ab6cf52aac9886b2ab94ebc From c69dab13450f622632945d4b248fe0804896617b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Tue, 26 Mar 2024 09:59:07 +0100 Subject: [PATCH 03/13] Fix test compilation after additional constraints. --- concordium-base | 2 +- .../globalstate/GlobalStateTests/Instances.hs | 28 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/concordium-base b/concordium-base index 195bbb5479..91d8242df5 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 195bbb5479af25229efd2c0a344f4110f3c74003 +Subproject commit 91d8242df56f71c85f2a745b6ef85099f1008161 diff --git a/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs b/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs index 689ec2c904..ad55ea68c4 100644 --- a/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs +++ b/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs @@ -7,6 +7,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} module GlobalStateTests.Instances where @@ -86,7 +87,7 @@ invariantIT :: (IsProtocolVersion pv) => ContractIndex -> Instances.IT pv (BufferedFix (Instances.IT pv)) -> - TestMonad (Word8, Bool, Bool, ContractIndex, H.Hash) + TestMonad pv (Word8, Bool, Bool, ContractIndex, H.Hash) invariantIT offset (Instances.Leaf inst) = do params <- Instances.loadInstanceParameters inst checkBinary (==) (contractIndex $ Instances.pinstanceAddress params) offset "==" "account index" "expected value" @@ -114,7 +115,7 @@ invariantIT offset (Instances.Branch h f v hsh l r) = do -- * The branch is marked full if and only if the right subtree is full. -- * The branch has vacancies exactly when at least one subtree has vacancies. -- * The root records the correct size of the table. -invariantInstances :: (IsProtocolVersion pv) => Instances.Instances pv -> TestMonad () +invariantInstances :: (IsProtocolVersion pv) => Instances.Instances pv -> TestMonad pv () invariantInstances Instances.InstancesEmpty = return () invariantInstances (Instances.InstancesTree size bf) = do (_, _, _, ContractIndex recSize, _) <- invariantIT 0 =<< mproject bf @@ -162,7 +163,7 @@ mInstanceAddr :: ModelInstance -> ContractAddress mInstanceAddr = Instances.pinstanceAddress . mInstanceParameters -- | Construct a 'Instances.PersistentInstance' from a 'ModelInstance'. -toPersistentInstance :: ModelInstance -> TestMonad (Instances.PersistentInstance pv) +toPersistentInstance :: (IsProtocolVersion pv) => ModelInstance -> TestMonad pv (Instances.PersistentInstance pv) toPersistentInstance (ModelInstance @v params modSrc iface pinstanceModel _ pinstanceAmount) = do pinstanceParameters <- refMake @_ @BufferedRef params moduleVSource <- storeRef (Wasm.WasmModuleV modSrc) @@ -189,7 +190,7 @@ toPersistentInstance (ModelInstance @v params modSrc iface pinstanceModel _ pins return $ Instances.PersistentInstanceV1 Instances.PersistentInstanceV{..} -- | Assert that a 'ModelInstance' matches a 'Instances.PersistentInstance'. -modelsPersistentInstance :: ModelInstance -> Instances.PersistentInstance pv -> TestMonad Property +modelsPersistentInstance :: forall pv. (IsProtocolVersion pv) => ModelInstance -> Instances.PersistentInstance pv -> TestMonad pv Property modelsPersistentInstance modInst perInst = do case (modInst, perInst) of (ModelInstance modParams modSrc modIFace modModel@Instances.InstanceStateV0{} _ modAmt, Instances.PersistentInstanceV0 Instances.PersistentInstanceV{..}) -> do @@ -218,7 +219,7 @@ modelsPersistentInstance modInst perInst = do .&&. counterexample "amount" (pinstanceAmount === modAmt) _ -> return $ counterexample "instance version mismatch" False where - compareStates :: Instances.InstanceStateV v -> Instances.InstanceStateV v -> TestMonad Property + compareStates :: Instances.InstanceStateV v -> Instances.InstanceStateV v -> TestMonad pv Property compareStates (Instances.InstanceStateV0 cs0) (Instances.InstanceStateV0 cs1) = return (cs0 === cs1) compareStates (Instances.InstanceStateV1 ps0) (Instances.InstanceStateV1 ps1) = do @@ -320,7 +321,7 @@ genModelInstance = oneof [genV0, genV1] } -- | Convert an instance table to a list of the hashes of the leaves. -instancesToHashList :: (IsProtocolVersion pv) => Instances.Instances pv -> TestMonad [H.Hash] +instancesToHashList :: (IsProtocolVersion pv) => Instances.Instances pv -> TestMonad pv [H.Hash] instancesToHashList Instances.InstancesEmpty = return [] instancesToHashList (Instances.InstancesTree _ instTab) = go [] =<< mproject instTab where @@ -451,7 +452,7 @@ arbitraryMapElement m = do -- | A test monad that can be used for performing operations on an instance table. -- This uses the in-memory blob store. -newtype TestMonad a = TestMonad {runTestMonad :: ModuleCache -> MemBlobStore -> IO a} +newtype TestMonad (pv :: ProtocolVersion) a = TestMonad {runTestMonad :: ModuleCache -> MemBlobStore -> IO a} deriving (Functor, Applicative, Monad, MonadIO, MonadFail) via (ReaderT ModuleCache (ReaderT MemBlobStore IO)) @@ -459,18 +460,21 @@ newtype TestMonad a = TestMonad {runTestMonad :: ModuleCache -> MemBlobStore -> (MonadBlobStore) via (ReaderT ModuleCache (MemBlobStoreT IO)) -instance MonadCache ModuleCache TestMonad where +instance MonadCache ModuleCache (TestMonad pv) where getCache = TestMonad $ \c _ -> return c +instance (IsProtocolVersion pv) => MonadProtocolVersion (TestMonad pv) where + type MPV (TestMonad pv) = pv + -- | Run a 'TestMonad' with a fresh in-memory blob store and an empty 0-sized module cache. -runTestMonadFresh :: TestMonad a -> IO a +runTestMonadFresh :: TestMonad pv a -> IO a runTestMonadFresh a = bracket newMemBlobStore destroyMemBlobStore $ \mbs -> do c <- newModuleCache 0 runTestMonad a c mbs -- | Generate a 'TestMonad' action for generating an instance table (by repeated creation and -- deletion of instances), and a corresponding model. -generateFromUpdates :: (IsProtocolVersion pv) => Int -> Gen (TestMonad (Instances.Instances pv), Model) +generateFromUpdates :: (IsProtocolVersion pv) => Int -> Gen (TestMonad pv (Instances.Instances pv), Model) generateFromUpdates n0 = gen n0 (return Instances.emptyInstances) emptyModel where gen 0 insts model = return (insts, model) @@ -510,7 +514,7 @@ checkInvariants :: (IsProtocolVersion pv) => Model -> Instances.Instances pv -> - TestMonad () + TestMonad pv () checkInvariants model insts = do invariantInstances insts hlActual <- instancesToHashList insts @@ -682,7 +686,7 @@ testUpdates _ n0 = do -- | Given a 'TestMonad' that generates an instance table and a corresponding model, test that -- getting arbitrary contract addresses returns the same result in the instance table and model. -testGetInstance :: (IsProtocolVersion pv) => TestMonad (Instances.Instances pv) -> Model -> Gen Property +testGetInstance :: (IsProtocolVersion pv) => TestMonad pv (Instances.Instances pv) -> Model -> Gen Property testGetInstance insts model = oneof $ [present | not (null $ modelInstances model)] From de91bd46219eeafbf548738770ec158f5389bdaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Tue, 26 Mar 2024 10:50:48 +0100 Subject: [PATCH 04/13] Bump rust to 1.73. --- .github/workflows/build-test.yaml | 2 +- .../SchedulerTests/BakerTransactions.hs | 3 +- concordium-node/src/consensus_ffi/ffi.rs | 33 ++----------------- concordium-node/src/consensus_ffi/mod.rs | 3 ++ concordium-node/src/utils.rs | 4 --- 5 files changed, 7 insertions(+), 38 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 99d5a1790e..31dc0593d7 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -125,7 +125,7 @@ jobs: strategy: matrix: plan: - - rust: 1.68 + - rust: 1.73 ghc: 9.6.4 steps: diff --git a/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs b/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs index c2e1beb13f..b1e68a1d09 100644 --- a/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs +++ b/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs @@ -32,7 +32,6 @@ import qualified Concordium.Crypto.SignatureScheme as SigScheme import qualified Concordium.Crypto.VRF as VRF import Concordium.GlobalState.DummyData import Concordium.Scheduler.DummyData -import System.IO.Unsafe import qualified SchedulerTests.Helpers as Helpers import SchedulerTests.TestUtils @@ -266,7 +265,7 @@ transactionsInput = tests :: Spec tests = do - let (outcomes, endState) = unsafePerformIO $ do + (outcomes, endState) <- runIO $ do txs <- processUngroupedTransactions transactionsInput Helpers.runSchedulerTestWithIntermediateStates @PV1 diff --git a/concordium-node/src/consensus_ffi/ffi.rs b/concordium-node/src/consensus_ffi/ffi.rs index 0d443e33ca..416f7dd882 100644 --- a/concordium-node/src/consensus_ffi/ffi.rs +++ b/concordium-node/src/consensus_ffi/ffi.rs @@ -1643,7 +1643,7 @@ pub fn get_consensus_ptr( regenesis_callback, start_config.maximum_log_level as u8, on_log_emited, - appdata_buf.as_ptr() as *const u8, + appdata_buf.as_ptr(), appdata_buf.len() as i64, runner_ptr_ptr, ) @@ -1677,7 +1677,7 @@ pub fn get_consensus_ptr( regenesis_callback, start_config.maximum_log_level as u8, on_log_emited, - appdata_buf.as_ptr() as *const u8, + appdata_buf.as_ptr(), appdata_buf.len() as i64, runner_ptr_ptr, ) @@ -3308,35 +3308,6 @@ impl TryFrom for CallbackType { } } -pub extern "C" fn on_finalization_message_catchup_out( - peer_id: PeerIdFFI, - data: *const u8, - len: i64, -) { - unsafe { - let msg_variant = PacketType::FinalizationMessage; - let payload = slice::from_raw_parts(data as *const u8, len as usize); - let mut full_payload = Vec::with_capacity(1 + payload.len()); - (msg_variant as u8).serial(&mut full_payload); - - full_payload.write_all(payload).unwrap(); // infallible - let full_payload = Arc::from(full_payload); - - let msg = ConsensusMessage::new( - MessageType::Outbound(Some((peer_id as usize).into())), - PacketType::FinalizationMessage, - full_payload, - vec![], - None, - ); - - match CALLBACK_QUEUE.send_out_blocking_msg(msg) { - Ok(_) => trace!("Queueing a {} of {} bytes", msg_variant, len), - Err(e) => error!("Couldn't queue a {} properly: {}", msg_variant, e), - }; - } -} - macro_rules! sending_callback { ( $target:expr, diff --git a/concordium-node/src/consensus_ffi/mod.rs b/concordium-node/src/consensus_ffi/mod.rs index 89c05fb4af..cbac173aaf 100644 --- a/concordium-node/src/consensus_ffi/mod.rs +++ b/concordium-node/src/consensus_ffi/mod.rs @@ -5,6 +5,7 @@ macro_rules! wrap_send_data_to_c { let consensus = $self.consensus.load(Ordering::SeqCst); let len = $data.len(); + #[allow(clippy::redundant_closure_call)] // allowed let result = unsafe { $c_call(consensus, $genesis_index, $data.as_ptr(), len as i64) }; ConsensusFfiResponse::try_from(result) @@ -15,6 +16,7 @@ macro_rules! wrap_send_data_to_c { macro_rules! wrap_c_call { ($self:ident, $c_call:expr) => {{ let consensus = $self.consensus.load(Ordering::SeqCst); + #[allow(clippy::redundant_closure_call)] // allowed let result = unsafe { $c_call(consensus) }; ConsensusFfiResponse::try_from(result) @@ -25,6 +27,7 @@ macro_rules! wrap_c_call { macro_rules! wrap_c_bool_call { ($self:ident, $c_call:expr) => {{ let consensus = $self.consensus.load(Ordering::SeqCst); + #[allow(clippy::redundant_closure_call)] // allowed match unsafe { $c_call(consensus) } { 0u8 => false, 1u8 => true, diff --git a/concordium-node/src/utils.rs b/concordium-node/src/utils.rs index 5a1a4ff213..cc50779a92 100644 --- a/concordium-node/src/utils.rs +++ b/concordium-node/src/utils.rs @@ -9,10 +9,6 @@ use std::{ path::Path, }; -pub fn to_hex_string(bytes: &[u8]) -> String { - bytes.iter().map(|b| format!("{:02x}", b)).collect() -} - /// Setup a log4rs logger based on the given configuration file. pub fn setup_logger_config(config_file: &Path) { log4rs::init_file(config_file, Default::default()).unwrap(); From 720b8c654836e122e68210f7f57e6ec980f1a5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Tue, 26 Mar 2024 13:03:59 +0100 Subject: [PATCH 05/13] Formatting. --- concordium-base | 2 +- .../scheduler/SchedulerTests/BakerTransactions.hs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/concordium-base b/concordium-base index 91d8242df5..1ca2a006a1 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 91d8242df56f71c85f2a745b6ef85099f1008161 +Subproject commit 1ca2a006a1a50839b7ee418b75e4d27e551111c9 diff --git a/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs b/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs index b1e68a1d09..503544f88d 100644 --- a/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs +++ b/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs @@ -266,13 +266,13 @@ transactionsInput = tests :: Spec tests = do (outcomes, endState) <- runIO $ do - txs <- processUngroupedTransactions transactionsInput - Helpers.runSchedulerTestWithIntermediateStates - @PV1 - Helpers.defaultTestConfig - initialBlockState - (const BS.bsoGetActiveBakers) - txs + txs <- processUngroupedTransactions transactionsInput + Helpers.runSchedulerTestWithIntermediateStates + @PV1 + Helpers.defaultTestConfig + initialBlockState + (const BS.bsoGetActiveBakers) + txs let results = first (Helpers.getResults . Sch.ftAdded . Helpers.srTransactions) <$> outcomes describe "P1: Baker transactions." $ do From 85f7ac15591b59935557117e544e72eacd7c1198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Fri, 29 Mar 2024 20:35:56 +0100 Subject: [PATCH 06/13] Bump base to simplify jumps. --- concordium-base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concordium-base b/concordium-base index 1ca2a006a1..ecb499abe3 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 1ca2a006a1a50839b7ee418b75e4d27e551111c9 +Subproject commit ecb499abe3f7815d3cf542762779fdc9ef683a49 From 14dfc0ba41eb7c66b87f45046550d25e5d5ee7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Sat, 30 Mar 2024 15:36:42 +0100 Subject: [PATCH 07/13] Documentation and preparation for lowering costs. --- concordium-base | 2 +- .../GlobalState/Persistent/BlockState.hs | 2 +- .../Persistent/BlockState/Modules.hs | 61 +++++++++++++++---- .../src/Concordium/GlobalState/Wasm.hs | 12 +++- .../src/Concordium/Scheduler.hs | 2 +- .../Concordium/Scheduler/WasmIntegration.hs | 5 +- .../globalstate/GlobalStateTests/Instances.hs | 2 +- .../SchedulerTests/BakerTransactions.hs | 2 +- .../SchedulerTests/InitPoliciesTest.hs | 3 +- .../SmartContracts/V1/InvokeHelpers.hs | 2 +- .../SmartContracts/V1/ValidInvalidModules.hs | 2 +- 11 files changed, 69 insertions(+), 26 deletions(-) diff --git a/concordium-base b/concordium-base index ecb499abe3..3a07938452 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit ecb499abe3f7815d3cf542762779fdc9ef683a49 +Subproject commit 3a0793845273c7359b91b61a56e438e2cebe340a diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs index f592c0831d..fdaa55c143 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs @@ -3715,7 +3715,7 @@ migrateBlockPointers migration BlockStatePointers{..} = do StateMigrationParametersP6ToP7{} -> RSMNewToNew newReleaseSchedule <- migrateReleaseSchedule rsMigration bspReleaseSchedule newAccounts <- Accounts.migrateAccounts migration bspAccounts - newModules <- migrateHashedBufferedRef Modules.migrateModules bspModules + newModules <- migrateHashedBufferedRef (Modules.migrateModules migration) bspModules modules <- refLoad newModules newInstances <- Instances.migrateInstances modules bspInstances let newBank = bspBank diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs index 217af09b29..2a383d616c 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs @@ -31,6 +31,7 @@ module Concordium.GlobalState.Persistent.BlockState.Modules ( ) where import Concordium.Crypto.SHA256 +import Concordium.Genesis.Data (StateMigrationParameters (..)) import Concordium.GlobalState.BlockState (ModulesHash (..)) import Concordium.GlobalState.Persistent.BlobStore import Concordium.GlobalState.Persistent.Cache @@ -197,19 +198,19 @@ instance (MonadBlobStore m, MonadProtocolVersion m) => DirectBlobStorable m Modu return $! ModuleV1 (ModuleV{..}) case runGet getModule bs of Left e -> error (e ++ " :: " ++ show bs) - Right mv@(ModuleV0 (ModuleV{moduleVInterface = GSWasm.ModuleInterface{miModule = PIMVPtr artPtr}, ..})) -> do + Right mv@(ModuleV0 (ModuleV{moduleVInterface = GSWasm.ModuleInterface{miModule = PIMVPtr artPtr}, ..})) | potentialLegacyArtifacts -> do artBS <- loadBlobPtr artPtr - if GSWasm.isLegacyArtifact artBS + if GSWasm.isV0LegacyArtifact artBS then do source <- loadRef moduleVSource - case WasmV0.processModule source of + case WasmV0.processModule (protocolVersion @(MPV m)) source of Nothing -> error "Stored module that is not valid." Just iface -> do return $! ModuleV0 (ModuleV{moduleVInterface = makePersistentInstrumentedModuleV <$> iface, ..}) else return mv - Right mv@(ModuleV1 (ModuleV{moduleVInterface = GSWasm.ModuleInterface{miModule = PIMVPtr artPtr}, ..})) -> do + Right mv@(ModuleV1 (ModuleV{moduleVInterface = GSWasm.ModuleInterface{miModule = PIMVPtr artPtr}, ..})) | potentialLegacyArtifacts -> do artBS <- loadBlobPtr artPtr - if GSWasm.isLegacyArtifact artBS + if GSWasm.isV0LegacyArtifact artBS then do source <- loadRef moduleVSource case WasmV1.processModule (protocolVersion @(MPV m)) source of @@ -218,6 +219,10 @@ instance (MonadBlobStore m, MonadProtocolVersion m) => DirectBlobStorable m Modu return $! ModuleV1 (ModuleV{moduleVInterface = makePersistentInstrumentedModuleV <$> iface, ..}) else return mv Right mv -> return mv + where + -- When a node is running protocol 6 or lower it might have been started prior to the new notion of Wasm + -- artifacts, which needs to be recompiled on load. + potentialLegacyArtifacts = demoteProtocolVersion (protocolVersion @(MPV m)) <= P6 storeUpdateDirect mdl = do case mdl of @@ -406,9 +411,10 @@ migrateModules :: SupportsPersistentModule (t m), SupportMigration m t ) => + StateMigrationParameters (MPV m) (MPV (t m)) -> Modules -> t m Modules -migrateModules mods = do +migrateModules migration mods = do newModulesTable <- LFMB.migrateLFMBTree migrateCachedModule (_modulesTable mods) return Modules @@ -425,19 +431,32 @@ migrateModules mods = do migrateModuleV :: forall v. (IsWasmVersion v) => ModuleV v -> t m CachedModule migrateModuleV ModuleV{..} = do - -- TODO: Recompile stuff that needs to be updated - newModuleVSource <- do + (newModuleVSource, wasmMod) <- do -- Load the module source from the old context. s <- lift (loadRef moduleVSource) -- and store it in the new context, returning a reference to it. - storeRef s + (,s) <$> storeRef s -- load the module artifact into memory from the old state. This is -- cheap since the artifact, which is the big part, is neither copied, -- nor deserialized. - newArtifact <- lift (loadInstrumentedModuleV (GSWasm.miModule moduleVInterface)) - -- construct the new module interface by loading the artifact. The - -- remaining fields have no blob references, so are just copied over. - let newModuleVInterface = moduleVInterface{GSWasm.miModule = PIMVMem newArtifact} + artifact <- lift (loadInstrumentedModuleV (GSWasm.miModule moduleVInterface)) + newModuleVInterface <- + -- If it is a legacy artifact then we want to migrate it over to the new + -- version by recompiling since execution no longer supports the old format. + if GSWasm.isV0LegacyArtifact (GSWasm.imWasmArtifactBytes artifact) + then recompile wasmMod + else -- If it is not a legacy module then we don't have to recompile + -- unless we're migrating from P6 to P7 where the new reduced + -- execution costs were introduced. + case migration of + StateMigrationParametersTrivial -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} + StateMigrationParametersP1P2 -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} + StateMigrationParametersP2P3 -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} + StateMigrationParametersP3ToP4{} -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} + StateMigrationParametersP4ToP5{} -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} + StateMigrationParametersP5ToP6{} -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} + StateMigrationParametersP6ToP7{} -> recompile wasmMod -- always recompile to lower transaction costs. + -- store the module into the new state, and remove it from memory makeFlushedHashedCachedRef $! mkModule (getWasmVersion @v) $! @@ -449,3 +468,19 @@ migrateModules mods = do mkModule :: SWasmVersion v -> ModuleV v -> Module mkModule SV0 = ModuleV0 mkModule SV1 = ModuleV1 + + -- Recompile a wasm module from the given source for the **target** protocol + -- version (i.e., the protocol version of @t m@). + recompile :: forall v. (IsWasmVersion v) => WasmModuleV v -> t m (GSWasm.ModuleInterfaceA (PersistentInstrumentedModuleV v)) + recompile wasmMod = do + case getWasmVersion @v of + SV0 -> + case WasmV0.processModule (protocolVersion @(MPV (t m))) wasmMod of + Nothing -> error "Stored V0 module that is not valid." + Just iface -> do + return $! makePersistentInstrumentedModuleV <$> iface + SV1 -> + case WasmV1.processModule (protocolVersion @(MPV (t m))) wasmMod of + Nothing -> error "Stored V1 module that is not valid." + Just iface -> do + return $! makePersistentInstrumentedModuleV <$> iface diff --git a/concordium-consensus/src/Concordium/GlobalState/Wasm.hs b/concordium-consensus/src/Concordium/GlobalState/Wasm.hs index c20f6387f7..0f6e599a50 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Wasm.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Wasm.hs @@ -27,7 +27,7 @@ module Concordium.GlobalState.Wasm ( BasicModuleInterface, HasModuleRef (..), HasEntrypoints (..), - isLegacyArtifact, + isV0LegacyArtifact, ) where @@ -227,7 +227,13 @@ foreign import ccall "is_legacy_artifact" -- | 1 for true, 0 for false IO Word8 -isLegacyArtifact :: BS.ByteString -> Bool -isLegacyArtifact artifactBS = unsafePerformIO $ +-- | Return whether the bytestring is a serialization of a legacy "V0" artifact. +-- These were artifact that only exist for P1-P6 for Wasm modules deployed +-- before node version 7. +-- +-- This assumes that the bytestring is a valid serialization of a V0 or V1 +-- artifact and will not validate this. +isV0LegacyArtifact :: BS.ByteString -> Bool +isV0LegacyArtifact artifactBS = unsafePerformIO $ BSU.unsafeUseAsCStringLen artifactBS $ \(wasmArtifactPtr, wasmArtifactLen) -> (== 1) <$> is_legacy_artifact (castPtr wasmArtifactPtr) (fromIntegral wasmArtifactLen) diff --git a/concordium-consensus/src/Concordium/Scheduler.hs b/concordium-consensus/src/Concordium/Scheduler.hs index 83549979d7..1f873b96aa 100644 --- a/concordium-consensus/src/Concordium/Scheduler.hs +++ b/concordium-consensus/src/Concordium/Scheduler.hs @@ -700,7 +700,7 @@ handleDeployModule wtc mod = case mod of Wasm.WasmModuleV0 moduleV0 -> do tickEnergy (Cost.deployModuleCost (Wasm.moduleSourceLength (Wasm.wmvSource moduleV0))) - case WasmV0.processModule moduleV0 of + case WasmV0.processModule (protocolVersion @(MPV m)) moduleV0 of Nothing -> rejectTransaction ModuleNotWF Just iface -> do let mhash = GSWasm.moduleReference iface diff --git a/concordium-consensus/src/Concordium/Scheduler/WasmIntegration.hs b/concordium-consensus/src/Concordium/Scheduler/WasmIntegration.hs index 9d54351cea..8c7194c033 100644 --- a/concordium-consensus/src/Concordium/Scheduler/WasmIntegration.hs +++ b/concordium-consensus/src/Concordium/Scheduler/WasmIntegration.hs @@ -257,8 +257,9 @@ applyReceiveFun miface cm receiveCtx rName param maxParamLen limitLogsAndRvs amn -- compilation or instrumentation) that is needed to apply the exported -- functions from it in an efficient way. {-# NOINLINE processModule #-} -processModule :: WasmModuleV V0 -> Maybe (ModuleInterfaceV V0) -processModule modl = do +processModule :: SProtocolVersion spv -> WasmModuleV V0 -> Maybe (ModuleInterfaceV V0) +-- TODO: The unused spv argument will be used when new cost semantics are introduced. +processModule _spv modl = do (bs, miModule) <- ffiResult case getExports bs of Left _ -> Nothing diff --git a/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs b/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs index ad55ea68c4..7b6f8e6933 100644 --- a/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs +++ b/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs @@ -55,7 +55,7 @@ validContractArtifactsV0 = mapMaybe packModule contractSourcesV0 where packModule (_, sourceBytes) = let source = Wasm.ModuleSource sourceBytes - in (source,) <$> WasmV0.processModule (Wasm.WasmModuleV source) + in (source,) <$> WasmV0.processModule SP1 (Wasm.WasmModuleV source) contractSourcesV1 :: [(FilePath, BS.ByteString)] contractSourcesV1 = $(makeRelativeToProject "../concordium-base/smart-contracts/testdata/contracts/v1" >>= embedDir) diff --git a/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs b/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs index 675b7bbb24..4bc7086eb5 100644 --- a/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs +++ b/concordium-consensus/tests/scheduler/SchedulerTests/BakerTransactions.hs @@ -275,7 +275,7 @@ tests = do (const BS.bsoGetActiveBakers) txs let feeTotal = sum $ Helpers.srExecutionCosts . fst <$> outcomes - _ <- liftIO =<< Helpers.assertBlockStateInvariants endState feeTotal + liftIO =<< Helpers.assertBlockStateInvariants endState feeTotal return outcomes let results = first (Helpers.getResults . Sch.ftAdded . Helpers.srTransactions) <$> outcomes diff --git a/concordium-consensus/tests/scheduler/SchedulerTests/InitPoliciesTest.hs b/concordium-consensus/tests/scheduler/SchedulerTests/InitPoliciesTest.hs index d71f304aec..924f893613 100644 --- a/concordium-consensus/tests/scheduler/SchedulerTests/InitPoliciesTest.hs +++ b/concordium-consensus/tests/scheduler/SchedulerTests/InitPoliciesTest.hs @@ -16,6 +16,7 @@ import Data.Maybe import Concordium.GlobalState.Wasm import Concordium.ID.Types import Concordium.Scheduler.WasmIntegration +import Concordium.Types (SProtocolVersion (..)) import Concordium.Wasm import Concordium.Scheduler.DummyData @@ -26,7 +27,7 @@ setup :: String -> IO (ModuleInterfaceV V0) setup errString = do source <- BS.readFile "../concordium-base/smart-contracts/testdata/contracts/context_test.wasm" let wasmMod = WasmModuleV (ModuleSource source) - let miface = processModule wasmMod + let miface = processModule SP1 wasmMod assertBool ("Module not valid " ++ errString) (isJust miface) return (fromJust miface) diff --git a/concordium-consensus/tests/scheduler/SchedulerTests/SmartContracts/V1/InvokeHelpers.hs b/concordium-consensus/tests/scheduler/SchedulerTests/SmartContracts/V1/InvokeHelpers.hs index 5003538e7c..7935fa7d4c 100644 --- a/concordium-consensus/tests/scheduler/SchedulerTests/SmartContracts/V1/InvokeHelpers.hs +++ b/concordium-consensus/tests/scheduler/SchedulerTests/SmartContracts/V1/InvokeHelpers.hs @@ -61,7 +61,7 @@ deployModuleV0 :: deployModuleV0 sourceFile bs = do ws <- liftIO $ BS.readFile sourceFile let wm = WasmModuleV (ModuleSource ws) - case WasmV0.processModule wm of + case WasmV0.processModule Types.SP4 wm of Nothing -> liftIO $ assertFailure "Invalid module." Just miv -> do (_, modState) <- BS.bsoPutNewModule bs (miv, wm) diff --git a/concordium-consensus/tests/scheduler/SchedulerTests/SmartContracts/V1/ValidInvalidModules.hs b/concordium-consensus/tests/scheduler/SchedulerTests/SmartContracts/V1/ValidInvalidModules.hs index 9582e5670e..2b07f1dc6d 100644 --- a/concordium-consensus/tests/scheduler/SchedulerTests/SmartContracts/V1/ValidInvalidModules.hs +++ b/concordium-consensus/tests/scheduler/SchedulerTests/SmartContracts/V1/ValidInvalidModules.hs @@ -29,7 +29,7 @@ testModule1 = do let expectedReceive = Map.singleton (InitName "init_contract") (Set.singleton (ReceiveName "contract.call")) assertEqual "Only valid receive functions should be exposed" expectedReceive miExposedReceive let wm0 = WasmModuleV (ModuleSource ws) - case WasmV0.processModule wm0 of + case WasmV0.processModule Types.SP1 wm0 of Nothing -> return () Just _ -> assertFailure "Extra exports are not allowed for V0 modules." From 0673c10e6580be3496e21c56ef8d50c60d282250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Wed, 3 Apr 2024 14:11:26 +0200 Subject: [PATCH 08/13] Fix migration. --- concordium-base | 2 +- .../Persistent/BlockState/Modules.hs | 56 ++++++---- .../GlobalState/Persistent/Instances.hs | 3 +- .../Concordium/Scheduler/WasmIntegration.hs | 51 ++++----- .../Scheduler/WasmIntegration/V1.hs | 101 +++++++++++++----- .../globalstate/GlobalStateTests/Instances.hs | 5 + 6 files changed, 147 insertions(+), 71 deletions(-) diff --git a/concordium-base b/concordium-base index 3a07938452..eccc54d76a 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 3a0793845273c7359b91b61a56e438e2cebe340a +Subproject commit eccc54d76aad722d4a49925988a5ef8d73613e59 diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs index 2a383d616c..1356115195 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs @@ -39,6 +39,7 @@ import Concordium.GlobalState.Persistent.CachedRef import Concordium.GlobalState.Persistent.LFMBTree (LFMBTree') import qualified Concordium.GlobalState.Persistent.LFMBTree as LFMB import qualified Concordium.GlobalState.Wasm as GSWasm +import Concordium.Logger (LogLevel (..), LogSource (..), MonadLogger (..)) import qualified Concordium.Scheduler.WasmIntegration as WasmV0 import qualified Concordium.Scheduler.WasmIntegration.V1 as WasmV1 import Concordium.Types @@ -153,7 +154,7 @@ instance (MonadBlobStore m) => Cacheable m Module -- | This instance is based on and should be compatible with the 'Serialize' instance -- for 'BasicModuleInterface'. -instance (MonadBlobStore m, MonadProtocolVersion m) => DirectBlobStorable m Module where +instance (MonadLogger m, MonadBlobStore m, MonadProtocolVersion m) => DirectBlobStorable m Module where loadDirect br = do bs <- loadRaw br let getModule = do @@ -198,25 +199,27 @@ instance (MonadBlobStore m, MonadProtocolVersion m) => DirectBlobStorable m Modu return $! ModuleV1 (ModuleV{..}) case runGet getModule bs of Left e -> error (e ++ " :: " ++ show bs) - Right mv@(ModuleV0 (ModuleV{moduleVInterface = GSWasm.ModuleInterface{miModule = PIMVPtr artPtr}, ..})) | potentialLegacyArtifacts -> do + Right mv@(ModuleV0 mv0@(ModuleV{moduleVInterface = GSWasm.ModuleInterface{miModule = PIMVPtr artPtr, ..}, ..})) | potentialLegacyArtifacts -> do artBS <- loadBlobPtr artPtr if GSWasm.isV0LegacyArtifact artBS then do + logEvent GlobalState LLTrace $ "Recompiling V0 module " ++ show miModuleRef source <- loadRef moduleVSource - case WasmV0.processModule (protocolVersion @(MPV m)) source of + case WasmV0.compileModule CSV0 source of Nothing -> error "Stored module that is not valid." - Just iface -> do - return $! ModuleV0 (ModuleV{moduleVInterface = makePersistentInstrumentedModuleV <$> iface, ..}) + Just (_, compiled) -> do + return $! ModuleV0 mv0{moduleVInterface = (moduleVInterface mv0){GSWasm.miModule = PIMVMem compiled}} else return mv - Right mv@(ModuleV1 (ModuleV{moduleVInterface = GSWasm.ModuleInterface{miModule = PIMVPtr artPtr}, ..})) | potentialLegacyArtifacts -> do + Right mv@(ModuleV1 mv1@(ModuleV{moduleVInterface = GSWasm.ModuleInterface{miModule = PIMVPtr artPtr, ..}, ..})) | potentialLegacyArtifacts -> do artBS <- loadBlobPtr artPtr if GSWasm.isV0LegacyArtifact artBS then do + logEvent GlobalState LLTrace $ "Recompiling V1 module " ++ show miModuleRef source <- loadRef moduleVSource - case WasmV1.processModule (protocolVersion @(MPV m)) source of + case WasmV1.compileModule (WasmV1.validationConfigAllowP1P6 CSV0) source of Nothing -> error "Stored module that is not valid." - Just iface -> do - return $! ModuleV1 (ModuleV{moduleVInterface = makePersistentInstrumentedModuleV <$> iface, ..}) + Just (_, compiled) -> do + return $! ModuleV1 mv1{moduleVInterface = (moduleVInterface mv1){GSWasm.miModule = PIMVMem compiled}} else return mv Right mv -> return mv where @@ -288,7 +291,7 @@ newModuleCache = newCache -- | Make sure that a monad supports the `MonadBlobStore` and `MonadCache` -- for the modules cache. -type SupportsPersistentModule m = (MonadBlobStore m, MonadCache ModuleCache m) +type SupportsPersistentModule m = (MonadLogger m, MonadBlobStore m, MonadCache ModuleCache m) -- | The collection of modules stored in a block state. data Modules = Modules @@ -439,12 +442,13 @@ migrateModules migration mods = do -- load the module artifact into memory from the old state. This is -- cheap since the artifact, which is the big part, is neither copied, -- nor deserialized. + artifact <- lift (loadInstrumentedModuleV (GSWasm.miModule moduleVInterface)) newModuleVInterface <- -- If it is a legacy artifact then we want to migrate it over to the new -- version by recompiling since execution no longer supports the old format. if GSWasm.isV0LegacyArtifact (GSWasm.imWasmArtifactBytes artifact) - then recompile wasmMod + then recompileArtifact @v wasmMod moduleVInterface else -- If it is not a legacy module then we don't have to recompile -- unless we're migrating from P6 to P7 where the new reduced -- execution costs were introduced. @@ -455,7 +459,7 @@ migrateModules migration mods = do StateMigrationParametersP3ToP4{} -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} StateMigrationParametersP4ToP5{} -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} StateMigrationParametersP5ToP6{} -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} - StateMigrationParametersP6ToP7{} -> recompile wasmMod -- always recompile to lower transaction costs. + StateMigrationParametersP6ToP7{} -> migrateToP7 @v wasmMod -- always recompile to lower transaction costs. -- store the module into the new state, and remove it from memory makeFlushedHashedCachedRef $! @@ -469,10 +473,26 @@ migrateModules migration mods = do mkModule SV0 = ModuleV0 mkModule SV1 = ModuleV1 - -- Recompile a wasm module from the given source for the **target** protocol - -- version (i.e., the protocol version of @t m@). - recompile :: forall v. (IsWasmVersion v) => WasmModuleV v -> t m (GSWasm.ModuleInterfaceA (PersistentInstrumentedModuleV v)) - recompile wasmMod = do + -- Recompile a wasm module from the given source for protocols 1-6. + -- This does not change the semantics, but does convert the artifact into the new format. + recompileArtifact :: forall v iface. (IsWasmVersion v) => WasmModuleV v -> GSWasm.ModuleInterfaceA iface -> t m (GSWasm.ModuleInterfaceA (PersistentInstrumentedModuleV v)) + recompileArtifact wasmMod oldIface = do + case getWasmVersion @v of + SV0 -> + case WasmV0.compileModule CSV0 wasmMod of + Nothing -> error "Stored V0 module that is not valid." + Just (_, compiled) -> do + return $! oldIface{GSWasm.miModule = PIMVMem compiled} + SV1 -> + case WasmV1.compileModule (WasmV1.validationConfigAllowP1P6 CSV0) wasmMod of + Nothing -> error "Stored V1 module that is not valid." + Just (_, compiled) -> do + return $! oldIface{GSWasm.miModule = PIMVMem compiled} + + -- Recompile a wasm module from the given source for protocol 7 + -- cost semantics (i.e., the protocol version of @t m@). + migrateToP7 :: forall v. (MPV (t m) ~ P7, IsWasmVersion v) => WasmModuleV v -> t m (GSWasm.ModuleInterfaceA (PersistentInstrumentedModuleV v)) + migrateToP7 wasmMod = do case getWasmVersion @v of SV0 -> case WasmV0.processModule (protocolVersion @(MPV (t m))) wasmMod of @@ -480,7 +500,7 @@ migrateModules migration mods = do Just iface -> do return $! makePersistentInstrumentedModuleV <$> iface SV1 -> - case WasmV1.processModule (protocolVersion @(MPV (t m))) wasmMod of - Nothing -> error "Stored V1 module that is not valid." + case WasmV1.processModuleConfig WasmV1.processingConfigRecompileForP7 wasmMod of + Nothing -> error "Stored V0 module that is not valid." Just iface -> do return $! makePersistentInstrumentedModuleV <$> iface diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/Instances.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/Instances.hs index 4a19c8c22e..737f078880 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/Instances.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/Instances.hs @@ -40,6 +40,7 @@ import qualified Concordium.GlobalState.Persistent.Cache as Cache import Concordium.GlobalState.Persistent.CachedRef import Concordium.GlobalState.Persistent.MonadicRecursive import qualified Concordium.GlobalState.Wasm as GSWasm +import Concordium.Logger (MonadLogger) ---------------------------------------------------------------------------------------------------- @@ -464,7 +465,7 @@ conditionalSetBit :: (Bits a) => Int -> Bool -> a -> a conditionalSetBit _ False x = x conditionalSetBit b True x = setBit x b -instance (MonadProtocolVersion m, IsProtocolVersion pv, MPV m ~ pv, BlobStorable m r, Cache.MonadCache ModuleCache m) => BlobStorable m (IT pv r) where +instance (MonadLogger m, MonadProtocolVersion m, IsProtocolVersion pv, MPV m ~ pv, BlobStorable m r, Cache.MonadCache ModuleCache m) => BlobStorable m (IT pv r) where storeUpdate (Branch{..}) = do (pl, l') <- storeUpdate branchLeft (pr, r') <- storeUpdate branchRight diff --git a/concordium-consensus/src/Concordium/Scheduler/WasmIntegration.hs b/concordium-consensus/src/Concordium/Scheduler/WasmIntegration.hs index 8c7194c033..b6de557cc1 100644 --- a/concordium-consensus/src/Concordium/Scheduler/WasmIntegration.hs +++ b/concordium-consensus/src/Concordium/Scheduler/WasmIntegration.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} -module Concordium.Scheduler.WasmIntegration (applyInitFun, applyReceiveFun, processModule) where +module Concordium.Scheduler.WasmIntegration (applyInitFun, applyReceiveFun, processModule, compileModule) where import Control.Monad import qualified Data.ByteString as BS @@ -258,35 +258,14 @@ applyReceiveFun miface cm receiveCtx rName param maxParamLen limitLogsAndRvs amn -- functions from it in an efficient way. {-# NOINLINE processModule #-} processModule :: SProtocolVersion spv -> WasmModuleV V0 -> Maybe (ModuleInterfaceV V0) --- TODO: The unused spv argument will be used when new cost semantics are introduced. -processModule _spv modl = do - (bs, miModule) <- ffiResult +processModule spv modl = do + (bs, miModule) <- compileModule (pvCostSemanticsVersion spv) modl case getExports bs of Left _ -> Nothing Right (miExposedInit, miExposedReceive) -> let miModuleRef = getModuleRef modl in Just ModuleInterface{miModuleSize = moduleSourceLength (wmvSource modl), ..} where - ffiResult = unsafePerformIO $ do - unsafeUseModuleSourceAsCStringLen (wmvSource modl) $ \(wasmBytesPtr, wasmBytesLen) -> - alloca $ \outputLenPtr -> - alloca $ \artifactLenPtr -> - alloca $ \outputModuleArtifactPtr -> do - outPtr <- validate_and_process (castPtr wasmBytesPtr) (fromIntegral wasmBytesLen) outputLenPtr artifactLenPtr outputModuleArtifactPtr - if outPtr == nullPtr - then return Nothing - else do - len <- peek outputLenPtr - bs <- BSU.unsafePackCStringFinalizer outPtr (fromIntegral len) (rs_free_array_len outPtr (fromIntegral len)) - artifactLen <- peek artifactLenPtr - artifactPtr <- peek outputModuleArtifactPtr - moduleArtifact <- - BSU.unsafePackCStringFinalizer - artifactPtr - (fromIntegral artifactLen) - (rs_free_array_len artifactPtr (fromIntegral artifactLen)) - return (Just (bs, instrumentedModuleFromBytes SV0 moduleArtifact)) - getExports bs = flip runGet bs $ do len <- fromIntegral <$> getWord16be @@ -309,3 +288,27 @@ processModule _spv modl = do Nothing -> fail "Incorrect response from FFI call." Just x@(exposedInits, exposedReceives) -> if Map.keysSet exposedReceives `Set.isSubsetOf` exposedInits then return x else fail "Receive functions that do not correspond to any contract." + +-- | Validate and compile a module. If successful return the artifact and serialization of module exports. +{-# NOINLINE compileModule #-} +compileModule :: CostSemanticsVersion -> WasmModuleV V0 -> Maybe (BS.ByteString, InstrumentedModuleV V0) +-- TODO: The unused spv argument will be used when new cost semantics are introduced. +compileModule _spv modl = unsafePerformIO $ do + unsafeUseModuleSourceAsCStringLen (wmvSource modl) $ \(wasmBytesPtr, wasmBytesLen) -> + alloca $ \outputLenPtr -> + alloca $ \artifactLenPtr -> + alloca $ \outputModuleArtifactPtr -> do + outPtr <- validate_and_process (castPtr wasmBytesPtr) (fromIntegral wasmBytesLen) outputLenPtr artifactLenPtr outputModuleArtifactPtr + if outPtr == nullPtr + then return Nothing + else do + len <- peek outputLenPtr + bs <- BSU.unsafePackCStringFinalizer outPtr (fromIntegral len) (rs_free_array_len outPtr (fromIntegral len)) + artifactLen <- peek artifactLenPtr + artifactPtr <- peek outputModuleArtifactPtr + moduleArtifact <- + BSU.unsafePackCStringFinalizer + artifactPtr + (fromIntegral artifactLen) + (rs_free_array_len artifactPtr (fromIntegral artifactLen)) + return (Just (bs, instrumentedModuleFromBytes SV0 moduleArtifact)) diff --git a/concordium-consensus/src/Concordium/Scheduler/WasmIntegration/V1.hs b/concordium-consensus/src/Concordium/Scheduler/WasmIntegration/V1.hs index 8783f496e8..052c098244 100644 --- a/concordium-consensus/src/Concordium/Scheduler/WasmIntegration/V1.hs +++ b/concordium-consensus/src/Concordium/Scheduler/WasmIntegration/V1.hs @@ -18,6 +18,8 @@ module Concordium.Scheduler.WasmIntegration.V1 ( applyReceiveFun, resumeReceiveFun, processModule, + processModuleConfig, + compileModule, ReturnValue, ReceiveInterruptedState, InvokeResponseCode (..), @@ -28,6 +30,8 @@ module Concordium.Scheduler.WasmIntegration.V1 ( returnValueToByteString, byteStringToReturnValue, RuntimeConfig (..), + validationConfigAllowP1P6, + processingConfigRecompileForP7, ) where import Control.Monad @@ -818,7 +822,9 @@ data ValidationConfig = ValidationConfig -- | Allow globals in data and element segments. vcAllowGlobals :: Bool, -- | Allow sign extension instructions. - vcAllowSignExtensionInstr :: Bool + vcAllowSignExtensionInstr :: Bool, + -- | Version of the cost semantics to use. + vcCostSemantics :: CostSemanticsVersion } -- | Construct a 'ValidationConfig' valid for the given protocol version. @@ -827,48 +833,65 @@ validationConfig spv = ValidationConfig { vcSupportUpgrade = supportsUpgradableContracts spv, vcAllowGlobals = supportsGlobalsInInitSections spv, - vcAllowSignExtensionInstr = supportsSignExtensionInstructions spv + vcAllowSignExtensionInstr = supportsSignExtensionInstructions spv, + vcCostSemantics = pvCostSemanticsVersion spv + } + +-- | Validation configuration that will accept all modules that are valid in +-- any of the protocol version 1-6, and use the given cost assignment. +validationConfigAllowP1P6 :: CostSemanticsVersion -> ValidationConfig +validationConfigAllowP1P6 vcCostSemantics = + ValidationConfig + { vcSupportUpgrade = True, + vcAllowGlobals = True, + vcAllowSignExtensionInstr = True, + .. + } + +-- | Configuration for module processing dependent on which features are allowed +-- in a specific protocol version. +data ProcessingConfig = ProcessingConfig + { pcValidationConfig :: ValidationConfig, + -- | Whether to omit custom section size from module size when cost accounting. + pcOmitCustomSectionSize :: Bool + } + +processingConfig :: SProtocolVersion pv -> ProcessingConfig +processingConfig spv = + ProcessingConfig + { pcValidationConfig = validationConfig spv, + pcOmitCustomSectionSize = omitCustomSectionFromSize spv + } + +-- | Processing configuration that will accept all modules that are valid in +-- any of the protocol version 1-6, and compile with cost semantics defined by P7. +processingConfigRecompileForP7 :: ProcessingConfig +processingConfigRecompileForP7 = + ProcessingConfig + { pcValidationConfig = validationConfigAllowP1P6 CSV1, + pcOmitCustomSectionSize = True } -- | Process a module as received and make a module interface. -- This -- - checks the module is well-formed, and has the right imports and exports for a V1 module. -- - makes a module artifact and allocates it on the Rust side, returning a pointer and a finalizer. -{-# NOINLINE processModule #-} processModule :: SProtocolVersion spv -> WasmModuleV V1 -> Maybe (ModuleInterfaceV V1) -processModule spv modl = do - (bs, miModule) <- ffiResult +processModule spv = processModuleConfig (processingConfig spv) + +processModuleConfig :: ProcessingConfig -> WasmModuleV V1 -> Maybe (ModuleInterfaceV V1) +processModuleConfig config modl = do + (bs, miModule) <- compileModule (pcValidationConfig config) modl case getExports bs of Left _ -> Nothing Right ((miExposedInit, miExposedReceive), customSectionsSize) -> let miModuleRef = getModuleRef modl miModuleSize = - if omitCustomSectionFromSize spv + if pcOmitCustomSectionSize config then moduleSourceLength (wmvSource modl) - customSectionsSize else moduleSourceLength (wmvSource modl) in Just ModuleInterface{..} where - ValidationConfig{..} = validationConfig spv - ffiResult = unsafePerformIO $ do - unsafeUseModuleSourceAsCStringLen (wmvSource modl) $ \(wasmBytesPtr, wasmBytesLen) -> - alloca $ \outputLenPtr -> - alloca $ \artifactLenPtr -> - alloca $ \outputModuleArtifactPtr -> do - outPtr <- validate_and_process (if vcSupportUpgrade then 1 else 0) (if vcAllowGlobals then 1 else 0) (if vcAllowSignExtensionInstr then 1 else 0) (castPtr wasmBytesPtr) (fromIntegral wasmBytesLen) outputLenPtr artifactLenPtr outputModuleArtifactPtr - if outPtr == nullPtr - then return Nothing - else do - len <- peek outputLenPtr - bs <- BSU.unsafePackCStringFinalizer outPtr (fromIntegral len) (rs_free_array_len outPtr (fromIntegral len)) - artifactLen <- peek artifactLenPtr - artifactPtr <- peek outputModuleArtifactPtr - moduleArtifact <- - BSU.unsafePackCStringFinalizer - artifactPtr - (fromIntegral artifactLen) - (rs_free_array_len artifactPtr (fromIntegral artifactLen)) - return (Just (bs, instrumentedModuleFromBytes SV1 moduleArtifact)) - getExports bs = flip runGet bs $ do len <- fromIntegral <$> getWord16be @@ -894,3 +917,27 @@ processModule spv modl = do Nothing -> fail "Incorrect response from FFI call." Just x@(exposedInits, exposedReceives) -> if Map.keysSet exposedReceives `Set.isSubsetOf` exposedInits then return (x, customSectionsSize) else fail "Receive functions that do not correspond to any contract." + +-- | Compile a module into an artifact using the provided configuration for validation. +{-# NOINLINE compileModule #-} +compileModule :: ValidationConfig -> WasmModuleV V1 -> Maybe (BS.ByteString, InstrumentedModuleV V1) +compileModule ValidationConfig{..} modl = + unsafePerformIO $ do + unsafeUseModuleSourceAsCStringLen (wmvSource modl) $ \(wasmBytesPtr, wasmBytesLen) -> + alloca $ \outputLenPtr -> + alloca $ \artifactLenPtr -> + alloca $ \outputModuleArtifactPtr -> do + outPtr <- validate_and_process (if vcSupportUpgrade then 1 else 0) (if vcAllowGlobals then 1 else 0) (if vcAllowSignExtensionInstr then 1 else 0) (castPtr wasmBytesPtr) (fromIntegral wasmBytesLen) outputLenPtr artifactLenPtr outputModuleArtifactPtr + if outPtr == nullPtr + then return Nothing + else do + len <- peek outputLenPtr + bs <- BSU.unsafePackCStringFinalizer outPtr (fromIntegral len) (rs_free_array_len outPtr (fromIntegral len)) + artifactLen <- peek artifactLenPtr + artifactPtr <- peek outputModuleArtifactPtr + moduleArtifact <- + BSU.unsafePackCStringFinalizer + artifactPtr + (fromIntegral artifactLen) + (rs_free_array_len artifactPtr (fromIntegral artifactLen)) + return (Just (bs, instrumentedModuleFromBytes SV1 moduleArtifact)) diff --git a/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs b/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs index 7b6f8e6933..02c4128051 100644 --- a/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs +++ b/concordium-consensus/tests/globalstate/GlobalStateTests/Instances.hs @@ -40,6 +40,7 @@ import Concordium.GlobalState.Persistent.Cache import qualified Concordium.GlobalState.Persistent.Instances as Instances import Concordium.GlobalState.Persistent.MonadicRecursive import Concordium.ID.Types (accountAddressSize) +import Concordium.Logger import Control.Exception import Control.Monad.Reader import Test.Hspec @@ -466,6 +467,10 @@ instance MonadCache ModuleCache (TestMonad pv) where instance (IsProtocolVersion pv) => MonadProtocolVersion (TestMonad pv) where type MPV (TestMonad pv) = pv +-- Do not log anything. +instance MonadLogger (TestMonad pv) where + logEvent _ _ _ = return () + -- | Run a 'TestMonad' with a fresh in-memory blob store and an empty 0-sized module cache. runTestMonadFresh :: TestMonad pv a -> IO a runTestMonadFresh a = bracket newMemBlobStore destroyMemBlobStore $ \mbs -> do From 3cef8bf60e5db90f5ca460af9e166d00ca645560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Sun, 7 Apr 2024 21:17:07 +0200 Subject: [PATCH 09/13] Bump base. --- concordium-base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concordium-base b/concordium-base index eccc54d76a..e658a3634a 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit eccc54d76aad722d4a49925988a5ef8d73613e59 +Subproject commit e658a3634a753dedca6c542c9d0572da7618509a From f78ac9888f4674789b4f7f85c4c5572d3230827e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Thu, 11 Apr 2024 08:28:59 +0200 Subject: [PATCH 10/13] Changelog. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89f1fcdd4c..95760e393f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - query the contract module reference for a given contract address - query the contract name for a given contract address - Update Rust version to 1.73. +- Integrate new Wasm execution engine. Migration of old execution artifacts in + the node's database to the new format is done on-demand, which means node + startup can be a bit slower when a lot of modules exist. All Wasm modules will + be migrated to the new format when the protocol is updated to P7. ## 6.3.0 From 2e40a2e1a49e45031759fd7f12f703686e124aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Thu, 11 Apr 2024 08:37:26 +0200 Subject: [PATCH 11/13] Cleanup. --- concordium-node/src/consensus_ffi/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/concordium-node/src/consensus_ffi/mod.rs b/concordium-node/src/consensus_ffi/mod.rs index b7fbad8841..655512481c 100644 --- a/concordium-node/src/consensus_ffi/mod.rs +++ b/concordium-node/src/consensus_ffi/mod.rs @@ -16,7 +16,6 @@ macro_rules! wrap_send_data_to_c { macro_rules! wrap_c_bool_call { ($self:ident, $c_call:expr) => {{ let consensus = $self.consensus.load(Ordering::SeqCst); - #[allow(clippy::redundant_closure_call)] // allowed match unsafe { $c_call(consensus) } { 0u8 => false, 1u8 => true, From 07fb8384db7490572dd969dad9e431a4001d721d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Sun, 21 Apr 2024 21:38:22 +0200 Subject: [PATCH 12/13] Fix typo in log item. --- concordium-base | 2 +- .../src/Concordium/GlobalState/Persistent/BlockState/Modules.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/concordium-base b/concordium-base index 414e2e4294..0764d4ff1e 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 414e2e42949fbd276557fd18483bb4eeb7304ccc +Subproject commit 0764d4ff1ecb2cdd54fa1de25b24c9c16a87877c diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs index 1356115195..584f6a47ef 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs @@ -501,6 +501,6 @@ migrateModules migration mods = do return $! makePersistentInstrumentedModuleV <$> iface SV1 -> case WasmV1.processModuleConfig WasmV1.processingConfigRecompileForP7 wasmMod of - Nothing -> error "Stored V0 module that is not valid." + Nothing -> error "Stored V1 module that is not valid." Just iface -> do return $! makePersistentInstrumentedModuleV <$> iface From 0a95871a04696dd72fa14071fde1ca431c3698aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Bizjak?= Date: Sun, 21 Apr 2024 23:47:22 +0200 Subject: [PATCH 13/13] Bump base. --- concordium-base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concordium-base b/concordium-base index 0764d4ff1e..4b559dc704 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 0764d4ff1ecb2cdd54fa1de25b24c9c16a87877c +Subproject commit 4b559dc7046eb03c872ee156ca529a109ccc8950