Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V1 contract state #243

Merged
merged 48 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
2cd970c
Introduce support for V1 contract state.
abizjak Jan 29, 2022
51e6b28
Fix.
abizjak Feb 3, 2022
488b254
Minor refinements based on implementation of the state on the other s…
abizjak Feb 6, 2022
59195ef
Remove the use of nullFunPtr since they are underspecified.
abizjak Feb 6, 2022
d7641af
Fix compilation of most of the tests after changes in globalstate.
abizjak Feb 6, 2022
57a743d
Transition the counter test to the new state implementation.
abizjak Feb 6, 2022
000c452
Charge for new state size.
abizjak Feb 7, 2022
840c443
Bump dependency to add contract state serialization.
abizjak Feb 7, 2022
13ae880
Merge branch 'main' into v1-contract-state
abizjak Feb 12, 2022
f2509e3
Merge remote-tracking branch 'origin/main' into v1-contract-state
abizjak Feb 19, 2022
c16b42f
Update dependencies.
abizjak Feb 19, 2022
8d22851
Fix remaining scheduler tests for V1 contracts.
abizjak Feb 20, 2022
acffcc3
Add a more complex test exercising new state and re-entrancy.
abizjak Feb 20, 2022
9184f47
Simplify contract state operations a bit, reinstate paired state.
abizjak Feb 21, 2022
a3ca9f8
Revise globalstate Instances test to use V0 and V1 instances.
abizjak Feb 21, 2022
763f625
Keep track of state changes in contracts more precisely.
abizjak Feb 22, 2022
5dda1eb
Introduce fallback entrypoints for V1 contracts.
abizjak Feb 24, 2022
265e2ed
Documentation and additional type annotations.
abizjak Feb 27, 2022
00bc95d
Introduced iterator smart contract tests.
Mar 1, 2022
db20c80
Merge pull request #250 from Concordium/v1-contract-state-iterator-tests
MilkywayPirate Mar 1, 2022
773724e
Bump base to add construction of fallback names.
abizjak Mar 2, 2022
7b4d623
Merge branch 'v1-contract-state' of github.com:Concordium/concordium-…
abizjak Mar 2, 2022
de2aa85
Fix iterator tests.
abizjak Mar 6, 2022
2d98618
Charge for storing contract state at the end of execution.
abizjak Mar 7, 2022
38a0abc
Fix V1 contract storage related bugs.
abizjak Mar 9, 2022
be873af
Fix computation of new state storage for V1 contracts.
abizjak Mar 10, 2022
d3d5626
Better charging for V1 storage.
abizjak Mar 10, 2022
f28323c
Documentation and cleanup.
abizjak Mar 12, 2022
ee23b24
Tests/checkpointing (#252)
MilkywayPirate Mar 14, 2022
bac53fa
Bump submodule.
abizjak Mar 14, 2022
09e6774
Fixes the broken link to `smart-contracts` repository
rimbi Mar 15, 2022
effae36
Merge pull request #255 from Concordium/fix-broken-link
rimbi Mar 15, 2022
f640131
Fix tracking of modifications.
abizjak Mar 18, 2022
3b09a06
Tests cross checkpointing (#256)
MilkywayPirate Mar 21, 2022
9e5d8eb
Bump base after merge.
abizjak Mar 21, 2022
7dd5e48
Merge remote-tracking branch 'origin/main' into v1-contract-state
abizjak Mar 21, 2022
0ff1722
Bump smart contracts.
abizjak Mar 23, 2022
6a724e4
Make the cost reported by invokeContract in line with transaction cost.
abizjak Mar 27, 2022
aec0948
Reduce max entry size to 1GB to allow the use of 32 bit integers for …
abizjak Mar 30, 2022
a79657f
Fix bug in `freeze` where changes were not correctly propagated to th…
abizjak Mar 31, 2022
05324da
Add more new contract state documentation.
abizjak Apr 7, 2022
4b198de
Clarify entry invalidation.
abizjak Apr 7, 2022
b7857f2
Merge remote-tracking branch 'origin/main' into v1-contract-state
abizjak Apr 21, 2022
25c96f4
Merge remote-tracking branch 'origin/main' into v1-contract-state
abizjak Apr 25, 2022
842729e
Fix tests after merging.
abizjak Apr 26, 2022
1312083
Improve documentation and naming. Remove redundant code.
abizjak Apr 26, 2022
ac8d863
Bump smart contract submodule after merge.
abizjak Apr 26, 2022
6664b13
Add contract changes to the changelog.
abizjak Apr 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion concordium-consensus/smart-contracts
2 changes: 1 addition & 1 deletion concordium-consensus/src/Concordium/Afgjort/Finalize.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1059,7 +1059,7 @@ nextFinalizationRecord parentBlock = do
-- |'ActiveFinalizationM' provides an implementation of 'FinalizationMonad' that
-- actively participates in finalization.
newtype ActiveFinalizationM (pv :: ProtocolVersion) r s m a = ActiveFinalizationM {runActiveFinalizationM :: m a}
deriving (Functor, Applicative, Monad, MonadState s, MonadReader r, TimerMonad, BlockStateTypes, BlockStateQuery, AccountOperations, BlockStateOperations, BlockStateStorage, BlockPointerMonad, PerAccountDBOperations, TreeStateMonad pv, SkovMonad pv, TimeMonad, MonadLogger, MonadIO, FinalizationOutputMonad, SkovQueryMonad pv)
deriving (Functor, Applicative, Monad, MonadState s, MonadReader r, TimerMonad, BlockStateTypes, BlockStateQuery, AccountOperations, ContractStateOperations, BlockStateOperations, BlockStateStorage, BlockPointerMonad, PerAccountDBOperations, TreeStateMonad pv, SkovMonad pv, TimeMonad, MonadLogger, MonadIO, FinalizationOutputMonad, SkovQueryMonad pv)

deriving instance (BlockPointerData (BlockPointerType m)) => GlobalStateTypes (ActiveFinalizationM pv r s m)
deriving instance (CanExtend (ATIStorage m), CanRecordFootprint (Footprint (ATIStorage m))) => ATITypes (ActiveFinalizationM pv r s m)
Expand Down
21 changes: 20 additions & 1 deletion concordium-consensus/src/Concordium/GlobalState.hs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ deriving via PureBlockStateMonad pv m
instance (Monad m, IsProtocolVersion pv)
=> AccountOperations (MemoryBlockStateM pv r g s m)

deriving via PureBlockStateMonad pv m
instance Monad m
=> ContractStateOperations (MemoryBlockStateM pv r g s m)

deriving via PureBlockStateMonad pv m
instance (Monad m,
IsProtocolVersion pv,
Expand Down Expand Up @@ -161,6 +165,16 @@ deriving via PersistentBlockStateMonad pv
(FocusGlobalStateM PersistentBlockStateContext g m)))
=> AccountOperations (PersistentBlockStateM pv r g s m)

deriving via PersistentBlockStateMonad pv
PersistentBlockStateContext
(FocusGlobalStateM PersistentBlockStateContext g m)
instance (MonadIO m,
ContractStateOperations (PersistentBlockStateMonad pv
PersistentBlockStateContext
(FocusGlobalStateM PersistentBlockStateContext g m)))
=> ContractStateOperations (PersistentBlockStateM pv r g s m)


deriving via PersistentBlockStateMonad pv
PersistentBlockStateContext
(FocusGlobalStateM PersistentBlockStateContext g m)
Expand Down Expand Up @@ -191,7 +205,7 @@ deriving via PersistentBlockStateMonad pv
-- * If @s@ is 'SkovData pv bs', then the in-memory, Haskell tree state is used.
-- * If @s@ is 'SkovPersistentData pv ati bs', then the persistent Haskell tree state is used.
newtype TreeStateM s m a = TreeStateM {runTreeStateM :: m a}
deriving (Functor, Applicative, Monad, MonadState s, MonadIO, BlockStateTypes, BlockStateQuery, AccountOperations, BlockStateOperations, BlockStateStorage)
deriving (Functor, Applicative, Monad, MonadState s, MonadIO, BlockStateTypes, BlockStateQuery, AccountOperations, BlockStateOperations, BlockStateStorage, ContractStateOperations)

-- * Specializations
type MemoryTreeStateM pv bs m = TreeStateM (SkovData pv bs) m
Expand Down Expand Up @@ -269,6 +283,11 @@ deriving via BlockStateM pv c r g s m
AccountOperations (BlockStateM pv c r g s m))
=> AccountOperations (GlobalStateM pv db c r g s m)

deriving via BlockStateM pv c r g s m
instance (Monad m,
ContractStateOperations (BlockStateM pv c r g s m))
=> ContractStateOperations (GlobalStateM pv db c r g s m)

deriving via BlockStateM pv c r g s m
instance (BlockStateQuery (GlobalStateM pv db c r g s m),
BlockStateOperations (BlockStateM pv c r g s m))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
Expand Down Expand Up @@ -38,13 +40,16 @@ import qualified Concordium.GlobalState.Types as GT
import Concordium.GlobalState.BakerInfo
import Concordium.GlobalState.Parameters
import Concordium.GlobalState.AccountTransactionIndex
import Concordium.GlobalState.ContractStateFFIHelpers
import Concordium.GlobalState.Basic.BlockState.Bakers
import qualified Concordium.GlobalState.BlockState as BS
import Concordium.GlobalState.Basic.BlockState.Account
import qualified Concordium.Wasm as Wasm
import qualified Concordium.GlobalState.Wasm as GSWasm
import qualified Concordium.GlobalState.Basic.BlockState.Accounts as Accounts
import qualified Concordium.GlobalState.Basic.BlockState.Modules as Modules
import qualified Concordium.GlobalState.Basic.BlockState.Instances as Instances
import qualified Concordium.GlobalState.Instance as Instance
import qualified Concordium.GlobalState.AccountMap as AccountMap
import qualified Concordium.GlobalState.Rewards as Rewards
import qualified Concordium.Types.IdentityProviders as IPS
Expand All @@ -57,6 +62,8 @@ import Concordium.ID.Types (credId, ArIdentity, IdentityProviderIdentity)
import qualified Concordium.Crypto.SHA256 as H
import Concordium.Types.HashableTo
import Concordium.Utils.Serialization
import Concordium.GlobalState.BlockState (InstanceInfoTypeV(iiParameters), UpdatableContractState)
import qualified Concordium.GlobalState.ContractStateV1 as StateV1

data BasicBirkParameters = BasicBirkParameters {
-- |The currently-registered bakers.
Expand Down Expand Up @@ -169,6 +176,13 @@ getHashedEpochBlocksV0 = do
blocks <- replicateM numBlocks get
return $! foldr' consEpochBlock emptyHashedEpochBlocks blocks

-- |Freeze the contract state and compute its hash.
freeze :: forall v . Wasm.IsWasmVersion v => UpdatableContractState v -> (H.Hash, Instance.InstanceStateV v)
freeze cs = case Wasm.getWasmVersion @v of
Wasm.SV0 -> (getHash cs, Instance.InstanceStateV0 cs)
Wasm.SV1 -> let (hsh, persistent) = StateV1.freezeInMemoryPersistent cs
in (hsh, Instance.InstanceStateV1 persistent)

data BlockState (pv :: ProtocolVersion) = BlockState {
_blockAccounts :: !(Accounts.Accounts pv),
_blockInstances :: !Instances.Instances,
Expand Down Expand Up @@ -342,13 +356,25 @@ instance GT.BlockStateTypes (PureBlockStateMonad pv m) where
type BlockState (PureBlockStateMonad pv m) = HashedBlockState pv
type UpdatableBlockState (PureBlockStateMonad pv m) = BlockState pv
type Account (PureBlockStateMonad pv m) = Account pv
type ContractState (PureBlockStateMonad pv m) = Instance.InstanceStateV

instance ATITypes (PureBlockStateMonad pv m) where
type ATIStorage (PureBlockStateMonad pv m) = ()

instance Monad m => PerAccountDBOperations (PureBlockStateMonad pv m)
-- default implementation

-- |Retrieve instance information from a basic instance.
mkInstanceInfo :: Instance.Instance -> BS.InstanceInfoType Instance.InstanceStateV
mkInstanceInfo = \case (Instance.InstanceV0 inst) -> BS.InstanceInfoV0 (mkInstanceInfoV inst)
(Instance.InstanceV1 inst) -> BS.InstanceInfoV1 (mkInstanceInfoV inst)
where mkInstanceInfoV :: Instance.InstanceV v -> BS.InstanceInfoTypeV Instance.InstanceStateV v
mkInstanceInfoV Instance.InstanceV{..} = BS.InstanceInfoV{
iiParameters = _instanceVParameters,
iiState = _instanceVModel,
iiBalance = _instanceVAmount
}

doGetIndexedAccount :: (Monad m, HasBlockState s pv, IsProtocolVersion pv) => s -> AccountAddress -> m (Maybe (AccountIndex, Account pv))
doGetIndexedAccount bs aaddr = return $! Accounts.getAccountWithIndex aaddr (bs ^. blockAccounts)

Expand Down Expand Up @@ -382,7 +408,8 @@ instance (IsProtocolVersion pv, Monad m) => BS.BlockStateQuery (PureBlockStateMo
return $ bs ^. blockModules . to (Modules.getInterface mref)

{-# INLINE getContractInstance #-}
getContractInstance bs caddr = return (Instances.getInstance caddr (bs ^. blockInstances))
getContractInstance bs caddr =
return $ mkInstanceInfo <$> Instances.getInstance caddr (bs ^. blockInstances)

{-# INLINE getAccount #-}
getAccount = doGetIndexedAccount
Expand All @@ -405,7 +432,7 @@ instance (IsProtocolVersion pv, Monad m) => BS.BlockStateQuery (PureBlockStateMo
getModuleList bs = return $ bs ^. blockModules . to Modules.moduleRefList

{-# INLINE getContractInstanceList #-}
getContractInstanceList bs = return (bs ^.. blockInstances . Instances.foldInstances)
getContractInstanceList bs = return (map Instance.instanceAddress (bs ^.. blockInstances . Instances.foldInstances))

{-# INLINE getAccountList #-}
getAccountList bs =
Expand Down Expand Up @@ -525,13 +552,25 @@ instance (Monad m, IsProtocolVersion pv) => BS.AccountOperations (PureBlockState

getAccountBaker acc = return $ acc ^. accountBaker

instance Monad m => BS.ContractStateOperations (PureBlockStateMonad pv m) where
thawContractState (Instance.InstanceStateV0 st) = return st
thawContractState (Instance.InstanceStateV1 st) = return (StateV1.thawInMemoryPersistent st)
stateSizeV0 (Instance.InstanceStateV0 cs) = return (Wasm.contractStateSize cs)
getV1StateContext = return errorLoadCallBack
contractStateToByteString (Instance.InstanceStateV0 st) = return (Wasm.contractState st)
contractStateToByteString (Instance.InstanceStateV1 st) = return (encode st)
{-# INLINE thawContractState #-}
{-# INLINE stateSizeV0 #-}
{-# INLINE getV1StateContext #-}
{-# INLINE contractStateToByteString #-}

instance (IsProtocolVersion pv, Monad m) => BS.BlockStateOperations (PureBlockStateMonad pv m) where

{-# INLINE bsoGetModule #-}
bsoGetModule bs mref = return $ bs ^. blockModules . to (Modules.getInterface mref)

{-# INLINE bsoGetInstance #-}
bsoGetInstance bs caddr = return (Instances.getInstance caddr (bs ^. blockInstances))
bsoGetInstance bs caddr = return (mkInstanceInfo <$> Instances.getInstance caddr (bs ^. blockInstances))

{-# INLINE bsoGetAccount #-}
bsoGetAccount = doGetIndexedAccount
Expand All @@ -557,8 +596,39 @@ instance (IsProtocolVersion pv, Monad m) => BS.BlockStateOperations (PureBlockSt
accounts = bs ^. blockAccounts
newAccounts = Accounts.putAccountWithRegIds acct accounts

bsoPutNewInstance bs mkInstance = return (Instances.instanceAddress inst, bs')
bsoPutNewInstance :: forall v . Wasm.IsWasmVersion v
=> BlockState pv
-> BS.NewInstanceData v
-> PureBlockStateMonad pv m (ContractAddress, BlockState pv)
bsoPutNewInstance bs BS.NewInstanceData{..} = return (Instances.instanceAddress inst, bs')
where
mkParams addr = Instance.InstanceParameters {
_instanceAddress = addr,
instanceOwner = nidOwner,
instanceInitName = nidInitName,
instanceReceiveFuns = nidEntrypoints,
instanceModuleInterface = nidInterface,
instanceParameterHash = Instance.makeInstanceParameterHash addr nidOwner (GSWasm.miModuleRef nidInterface) nidInitName
}
mkInstance addr = case Wasm.getWasmVersion @v of
Wasm.SV0 ->
let params = mkParams addr
(_, state) = freeze nidInitialState
in Instance.InstanceV0 Instance.InstanceV{
_instanceVParameters = params,
_instanceVModel = state,
_instanceVAmount = nidInitialAmount,
_instanceVHash = Instance.makeInstanceHashV0 params state nidInitialAmount
}
Wasm.SV1 ->
let params = mkParams addr
(_, state) = freeze nidInitialState
in Instance.InstanceV1 Instance.InstanceV{
_instanceVParameters = params,
_instanceVModel = state,
_instanceVAmount = nidInitialAmount,
_instanceVHash = Instance.makeInstanceHashV1 params state nidInitialAmount
}
(inst, instances') = Instances.createInstance mkInstance (bs ^. blockInstances)
bs' = bs
-- Add the instance
Expand All @@ -570,7 +640,7 @@ instance (IsProtocolVersion pv, Monad m) => BS.BlockStateOperations (PureBlockSt
Just mods' -> (True, bs & blockModules .~ mods')

bsoModifyInstance bs caddr delta model = return $!
bs & blockInstances %~ Instances.updateInstanceAt caddr delta model
bs & blockInstances %~ Instances.updateInstanceAt caddr delta (snd . freeze <$> model)

bsoModifyAccount bs accountUpdates = return $!
-- Update the account
Expand Down Expand Up @@ -886,6 +956,9 @@ instance (IsProtocolVersion pv, MonadIO m) => BS.BlockStateStorage (PureBlockSta
{-# INLINE writeBlockState #-}
writeBlockState h = PureBlockStateMonad . liftIO . hPutBuilder h . snd . runPutMBuilder . putBlockState . _unhashedBlockState

{-# INLINE blockStateLoadCallback #-}
blockStateLoadCallback = return errorLoadCallBack -- basic block state is not written, so it never has to be loaded.

-- |Initial block state.
initialState :: (IsProtocolVersion pv)
=> SeedState
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE GADTs #-}
module Concordium.GlobalState.Basic.BlockState.Instances(
InstanceParameters(..),
Instance(..),
Expand All @@ -7,7 +10,6 @@ module Concordium.GlobalState.Basic.BlockState.Instances(
Instances,
emptyInstances,
getInstance,
updateInstance,
updateInstanceAt,
updateInstanceAt',
createInstance,
Expand Down Expand Up @@ -39,17 +41,38 @@ getInstance :: ContractAddress -> Instances -> Maybe Instance
getInstance addr (Instances iss) = iss ^? ix addr

-- |Update the instance at the specified address with an amount delta and
-- potentially a new state. If new state is not provided the state of the
-- instance is not changed. If there is no instance with the given address, this
-- does nothing.
updateInstanceAt :: ContractAddress -> AmountDelta -> Maybe Wasm.ContractState -> Instances -> Instances
updateInstanceAt ca amt val (Instances iss) = Instances (iss & ix ca %~ updateInstance amt val)
-- potentially new state. If new state is not provided the state of the instance
-- is not changed. If there is no instance with the given address, this does
-- nothing. If the instance at the given address has a different version than
-- given this function raises an exception.
updateInstanceAt :: forall v .Wasm.IsWasmVersion v => ContractAddress -> AmountDelta -> Maybe (InstanceStateV v) -> Instances -> Instances
updateInstanceAt ca amt val (Instances iss) = Instances (iss & ix ca %~ updateOnlyV)
where
-- only update if the instance matches the state version. Otherwise raise an exception.
updateOnlyV = case Wasm.getWasmVersion @v of
Wasm.SV0 -> \case
InstanceV0 i -> InstanceV0 $ updateInstanceV amt val i
InstanceV1 _ -> error "Expected a V0 instance, but got V1."
Wasm.SV1 -> \case
InstanceV0 _ -> error "Expected a V1 instance, but got V0"
InstanceV1 i -> InstanceV1 $ updateInstanceV amt val i

-- |Update the instance at the specified address with a __new amount__ and
-- potentially a new state. If new state is not provided the state of the instance is not changed. If
-- there is no instance with the given address, this does nothing.
updateInstanceAt' :: ContractAddress -> Amount -> Maybe Wasm.ContractState -> Instances -> Instances
updateInstanceAt' ca amt val (Instances iss) = Instances (iss & ix ca %~ updateInstance' amt val)
-- potentially new state. If new state is not provided the state of the instance
-- is not changed. If there is no instance with the given address, this does
-- nothing. If the instance at the given address has a different version than
-- given this function raises an exception.
updateInstanceAt' :: forall v . Wasm.IsWasmVersion v => ContractAddress -> Amount -> Maybe (InstanceStateV v) -> Instances -> Instances
updateInstanceAt' ca amt val (Instances iss) = Instances (iss & ix ca %~ updateOnlyV)
where
-- only update if the instance matches the state version. Otherwise raise an exception.
updateOnlyV = case Wasm.getWasmVersion @v of
Wasm.SV0 -> \case
InstanceV0 i -> InstanceV0 $ updateInstanceV' amt val i
InstanceV1 _ -> error "Expected a V0 instance, but got V1."
Wasm.SV1 -> \case
InstanceV0 _ -> error "Expected a V1 instance, but got V0"
InstanceV1 i -> InstanceV1 $ updateInstanceV' amt val i

-- |Create a new smart contract instance.
createInstance :: (ContractAddress -> Instance) -> Instances -> (Instance, Instances)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ initialSkovData rp gd genState = do
-- type used in the implementation.
newtype PureTreeStateMonad (pv :: ProtocolVersion) bs m a = PureTreeStateMonad { runPureTreeStateMonad :: m a }
deriving (Functor, Applicative, Monad, MonadIO, BlockStateTypes,
BS.BlockStateQuery, BS.AccountOperations, BS.BlockStateOperations, BS.BlockStateStorage, TimeMonad)
BS.BlockStateQuery, BS.AccountOperations, BS.BlockStateOperations, BS.BlockStateStorage, BS.ContractStateOperations, TimeMonad)

deriving instance (Monad m, MonadState (SkovData pv bs) m) => MonadState (SkovData pv bs) (PureTreeStateMonad pv bs m)

Expand Down
Loading