Skip to content

Commit c8cccda

Browse files
drsk0drsktd202
authored
COR-1651: Add touch kernel method (#1441)
* implementation of `touch` Adds definitions and implementation of the `touchTokenAccount` operation in the token kernel and the block state operations. The interface in the kernel is ``` touch :: PLTAccount m -> m Bool ``` The return value indicates whether the account already had a token balance associated (False) or if a zero account balance was newly created (True). * `touch` account before `setAccountState` * extend trace tests * added changelog entry * address review Added a new function `updateTokenAccountState` to Persistent/Account.hs that deals with updating the token account state table. The function is then used in Persistent/BlockState.hs abstractions for the persistent account versions. * format * Update concordium-consensus/src/Concordium/GlobalState/Persistent/Account.hs Co-authored-by: Thomas Dinsdale-Young <td202@users.noreply.github.com> * move updateTokenAccountState to Account.ProtocolLevelTokens --------- Co-authored-by: drsk <rsk@concordium.com> Co-authored-by: Thomas Dinsdale-Young <td202@users.noreply.github.com>
1 parent 2d840ac commit c8cccda

File tree

9 files changed

+130
-35
lines changed

9 files changed

+130
-35
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22

33
## Unreleased changes
44

5-
- Change energy charging to occur as early as possible in the token module.
65
- Add P8 -> P9 update.
76
- Update GHC version to 9.10.2 (lts-24.0).
87

8+
- Protocol-level tokens:
9+
- Change energy charging to occur as early as possible in the token module.
10+
- Added `touch` kernel method. The `touch` method initializes the token state
11+
of an account by setting its balance to zero. This method only affects
12+
accounts that have no existing state for the token.
13+
914
## 9.0.6 (DevNet)
1015

1116
- Protocol-level tokens:

concordium-consensus/src/Concordium/GlobalState/BlockState.hs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,21 @@ class (BlockStateQuery m, PLTQuery (UpdatableBlockState m) (MutableTokenState m)
16891689
GSAccount.TokenAmountDelta ->
16901690
m (Maybe (UpdatableBlockState m))
16911691

1692+
-- Touch the token account. This initializes a token account state with a
1693+
-- balance of zero. This only affects an account if its state for the token
1694+
-- is empty.
1695+
--
1696+
-- Returns nothing, if the account already contained a token account state,
1697+
-- otherwise the updated block state.
1698+
bsoTouchTokenAccount ::
1699+
(PVSupportsPLT (MPV m)) =>
1700+
UpdatableBlockState m ->
1701+
-- | The token index to update
1702+
TokenIndex ->
1703+
-- | The account to update
1704+
AccountIndex ->
1705+
m (Maybe (UpdatableBlockState m))
1706+
16921707
-- | A snapshot of the block state that can be used to roll back to a previous state.
16931708
type StateSnapshot m
16941709

@@ -2060,6 +2075,7 @@ instance (Monad (t m), MonadTrans t, BlockStateOperations m) => BlockStateOperat
20602075
bsoSetTokenCirculatingSupply s tokIx = lift . bsoSetTokenCirculatingSupply s tokIx
20612076
bsoCreateToken s = lift . bsoCreateToken s
20622077
bsoUpdateTokenAccountBalance s tokIx accIx = lift . bsoUpdateTokenAccountBalance s tokIx accIx
2078+
bsoTouchTokenAccount s tokIx = lift . bsoTouchTokenAccount s tokIx
20632079
type StateSnapshot (MGSTrans t m) = StateSnapshot m
20642080
bsoSnapshotState = lift . bsoSnapshotState
20652081
bsoRollback s = lift . bsoRollback s
@@ -2121,6 +2137,7 @@ instance (Monad (t m), MonadTrans t, BlockStateOperations m) => BlockStateOperat
21212137
{-# INLINE bsoSetTokenCirculatingSupply #-}
21222138
{-# INLINE bsoCreateToken #-}
21232139
{-# INLINE bsoUpdateTokenAccountBalance #-}
2140+
{-# INLINE bsoTouchTokenAccount #-}
21242141
{-# INLINE bsoSetTokenState #-}
21252142
{-# INLINE bsoSuspendValidators #-}
21262143
{-# INLINE bsoSnapshotState #-}

concordium-consensus/src/Concordium/GlobalState/Persistent/Account.hs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,27 @@ updateAccount upd (PAV3 acc) = PAV3 <$> V1.updateAccount upd acc
427427
updateAccount upd (PAV4 acc) = PAV4 <$> V1.updateAccount upd acc
428428
updateAccount upd (PAV5 acc) = PAV5 <$> V1.updateAccount upd acc
429429

430+
-- | Apply an update to a token account state.
431+
updateTokenAccountState ::
432+
(AVSupportsPLT av, MonadBlobStore m) =>
433+
-- | The token index
434+
BlockState.TokenIndex ->
435+
-- | How to update the token account state if present (Just) and if not present (Nothing) in the token account state table.
436+
(Maybe BlockState.TokenAccountState -> m BlockState.TokenAccountState) ->
437+
-- | The account to update
438+
PersistentAccount av ->
439+
m (PersistentAccount av)
440+
updateTokenAccountState tokenIx upd (PAV5 acc) =
441+
PAV5 <$> case V1.accountTokenStateTable acc of
442+
CTrue (Some ref) -> doUpdate ref
443+
CTrue Null -> do
444+
ref <- refMake BlockState.emptyTokenAccountStateTable
445+
doUpdate ref
446+
where
447+
doUpdate ref = do
448+
ref' <- BlockState.updateTokenAccountStateTable ref tokenIx (upd Nothing) (upd . Just)
449+
return acc{V1.accountTokenStateTable = CTrue $ Some ref'}
450+
430451
-- | Add or remove credentials on an account.
431452
-- The caller must ensure the following, which are not checked:
432453
--

concordium-consensus/src/Concordium/GlobalState/Persistent/Account/ProtocolLevelTokens.hs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,35 @@ emptyTokenAccountState =
6666
{ tasBalance = TokenRawAmount 0
6767
}
6868

69+
-- | Helper function to update a reference to a token account state table.
70+
updateTokenAccountStateTable ::
71+
(MonadBlobStore m, Reference m ref TokenAccountStateTable) =>
72+
-- | The token account state table to update
73+
ref TokenAccountStateTable ->
74+
-- | The index of the token in question
75+
TokenIndex ->
76+
-- | How to create a new token account state if the token doesn't have a token account state associated yet
77+
m TokenAccountState ->
78+
-- | How to update an existing token account state
79+
(TokenAccountState -> m TokenAccountState) ->
80+
m (ref TokenAccountStateTable)
81+
updateTokenAccountStateTable ref tokIx createNewState updateExisting = do
82+
TokenAccountStateTable tst <- refLoad ref
83+
tst' <-
84+
Map.alterF
85+
( \case
86+
Nothing -> do
87+
newState <- createNewState
88+
Just <$> refMake newState
89+
Just sRef -> do
90+
s <- refLoad sRef
91+
s' <- updateExisting s
92+
Just <$> refMake s'
93+
)
94+
tokIx
95+
tst
96+
refMake $ TokenAccountStateTable{tokenAccountStateTable = tst'}
97+
6998
-- | Token state at the account level
7099
newtype TokenAccountState = TokenAccountState
71100
{ -- | The available balance for the account.

concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ import Concordium.GlobalState.Persistent.Account
5858
import Concordium.GlobalState.Persistent.Account.CooldownQueue (NextCooldownChange (..))
5959
import qualified Concordium.GlobalState.Persistent.Account.MigrationState as MigrationState
6060
import Concordium.GlobalState.Persistent.Account.ProtocolLevelTokens
61-
import qualified Concordium.GlobalState.Persistent.Account.StructureV1 as StructureV1
6261
import Concordium.GlobalState.Persistent.Accounts (SupportsPersistentAccount)
6362
import qualified Concordium.GlobalState.Persistent.Accounts as Accounts
6463
import qualified Concordium.GlobalState.Persistent.Accounts as LMDBAccountMap
@@ -4367,30 +4366,6 @@ doSetTokenState pbs tokenIndex mutableState = do
43674366
newPLTs <- PLT.setTokenState tokenIndex mutableState (bspProtocolLevelTokens bsp)
43684367
storePBS pbs bsp{bspProtocolLevelTokens = newPLTs}
43694368

4370-
-- | Helper function to update a reference to a token account state table.
4371-
updateTokenAccountStateTable ::
4372-
(Monad m, MonadBlobStore m, Reference m ref TokenAccountStateTable) =>
4373-
ref TokenAccountStateTable ->
4374-
PLT.TokenIndex ->
4375-
(TokenAccountState -> m TokenAccountState) ->
4376-
m (ref TokenAccountStateTable)
4377-
updateTokenAccountStateTable ref tokIx update = do
4378-
TokenAccountStateTable tst <- refLoad ref
4379-
tst' <-
4380-
Map.alterF
4381-
( \case
4382-
Nothing -> do
4383-
newState <- update emptyTokenAccountState
4384-
Just <$> refMake newState
4385-
Just sRef -> do
4386-
s <- refLoad sRef
4387-
s' <- update s
4388-
Just <$> refMake s'
4389-
)
4390-
tokIx
4391-
tst
4392-
refMake $ TokenAccountStateTable{tokenAccountStateTable = tst'}
4393-
43944369
-- | Update the token balance.
43954370
doUpdateTokenAccountBalance ::
43964371
forall pv m.
@@ -4405,15 +4380,13 @@ doUpdateTokenAccountBalance pbs tokIx accIx (TokenAmountDelta delta) = runMaybeT
44054380
newAccounts <- Accounts.updateAccountsAtIndex' upd accIx (bspAccounts bsp)
44064381
storePBS pbs bsp{bspAccounts = newAccounts}
44074382
where
4408-
upd (PAV5 acc) = case StructureV1.accountTokenStateTable acc of
4409-
CTrue (Some ref) -> doUpdate ref
4410-
CTrue Null -> do
4411-
ref <- refMake emptyTokenAccountStateTable
4412-
doUpdate ref
4413-
where
4414-
doUpdate ref = do
4415-
ref' <- updateTokenAccountStateTable ref tokIx updateBalance
4416-
return (PAV5 $ acc{StructureV1.accountTokenStateTable = CTrue $ Some ref'})
4383+
upd =
4384+
updateTokenAccountState
4385+
tokIx
4386+
( \case
4387+
Nothing -> updateBalance emptyTokenAccountState
4388+
Just tas -> updateBalance tas
4389+
)
44174390

44184391
updateBalance :: TokenAccountState -> MaybeT m TokenAccountState
44194392
updateBalance tas
@@ -4423,6 +4396,28 @@ doUpdateTokenAccountBalance pbs tokIx accIx (TokenAmountDelta delta) = runMaybeT
44234396
where
44244397
newBalanceInteger = fromIntegral (tasBalance tas) + delta
44254398

4399+
-- | Touch a token account, i.e. set the balance of the given token to zero if
4400+
-- the account didn't have a balance before.
4401+
doTouchTokenAccount ::
4402+
forall pv m.
4403+
(SupportsPersistentState pv m, PVSupportsPLT pv) =>
4404+
PersistentBlockState pv ->
4405+
PLT.TokenIndex ->
4406+
AccountIndex ->
4407+
m (Maybe (PersistentBlockState pv))
4408+
doTouchTokenAccount pbs tokIx accIx = runMaybeT $ do
4409+
bsp <- lift $ loadPBS pbs
4410+
newAccounts <- Accounts.updateAccountsAtIndex' upd accIx (bspAccounts bsp)
4411+
storePBS pbs bsp{bspAccounts = newAccounts}
4412+
where
4413+
upd =
4414+
updateTokenAccountState
4415+
tokIx
4416+
( \case
4417+
Nothing -> return emptyTokenAccountState
4418+
Just _tas -> hoistMaybe Nothing
4419+
)
4420+
44264421
-- | Context that supports the persistent block state.
44274422
data PersistentBlockStateContext pv = PersistentBlockStateContext
44284423
{ -- | The 'BlobStore' used for storing the persistent state.
@@ -4738,6 +4733,7 @@ instance (IsProtocolVersion pv, PersistentState av pv r m) => BlockStateOperatio
47384733
bsoCreateToken = doCreateToken
47394734
bsoSetTokenState = doSetTokenState
47404735
bsoUpdateTokenAccountBalance = doUpdateTokenAccountBalance
4736+
bsoTouchTokenAccount = doTouchTokenAccount
47414737
type StateSnapshot (PersistentBlockStateMonad pv r m) = BlockStatePointers pv
47424738
bsoSnapshotState = loadPBS
47434739
bsoRollback = storePBS

concordium-consensus/src/Concordium/Scheduler/EnvironmentImplementation.hs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,17 @@ instance (BS.BlockStateOperations m, PVSupportsPLT (MPV m)) => PLTKernelUpdate (
652652
tokenId <- asks (_pltTokenId . _pltecConfiguration)
653653
plteEvents %= (TokenModuleEvent tokenId eventType eventDetails :)
654654

655+
touch (accIx, _) = do
656+
context <- ask
657+
let tokenIx = _pltecTokenIndex context
658+
bs0 <- use plteBlockState
659+
mbBs1 <- lift $ BS.bsoTouchTokenAccount bs0 tokenIx accIx
660+
case mbBs1 of
661+
Nothing -> return False
662+
Just bs1 -> do
663+
plteBlockState .= bs1
664+
return True
665+
655666
instance (BS.BlockStateOperations m, PVSupportsPLT (MPV m)) => PLTKernelPrivilegedUpdate (KernelT fail ret m) where
656667
mint (accIx, accAddr) amount = do
657668
context <- ask

concordium-consensus/src/Concordium/Scheduler/ProtocolLevelTokens/Kernel.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ class (PLTKernelQuery m) => PLTKernelUpdate m where
5151
-- | Log a token module event with the specified type and details.
5252
logTokenEvent :: TokenEventType -> TokenEventDetails -> m ()
5353

54+
-- | Update the balance of the given account to zero if it didn't have a
55+
-- balance before.
56+
touch ::
57+
-- | The account to update
58+
PLTAccount m ->
59+
-- | Returns 'True' if the balance wasn't present on the given account
60+
-- and 'False' otherwise.
61+
m Bool
62+
5463
class (PLTKernelUpdate m) => PLTKernelPrivilegedUpdate m where
5564
-- | Mint a specified amount and deposit it in the specified account.
5665
-- The return value indicates if this was successful.

concordium-consensus/src/Concordium/Scheduler/ProtocolLevelTokens/Module.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,10 @@ accountStateKey accountIndex subkey = runPut $ do
455455
-- | Set the value in the account state.
456456
setAccountState :: (Monad m, PLTKernelUpdate m) => PLTAccount m -> TokenStateKey -> Maybe TokenStateValue -> m ()
457457
setAccountState account key maybeValue = do
458+
-- make sure that the account state contains a balance. Otherwise the
459+
-- updated account state might not be displayed in future queries.
460+
void $ touch account
461+
458462
accountIndex <- getAccountIndex account
459463
let accountKey = accountStateKey accountIndex key
460464
void $ Kernel.setTokenState accountKey maybeValue

concordium-consensus/tests/scheduler/SchedulerTests/TokenModule.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ data PLTKernelUpdateCall acct ret where
9292
SetTokenState :: TokenStateKey -> Maybe TokenStateValue -> PLTKernelUpdateCall acct (Maybe Bool)
9393
Transfer :: acct -> acct -> TokenRawAmount -> Maybe Memo -> PLTKernelUpdateCall acct Bool
9494
LogTokenEvent :: TokenEventType -> TokenEventDetails -> PLTKernelUpdateCall acct ()
95+
Touch :: acct -> PLTKernelUpdateCall acct Bool
9596

9697
deriving instance (Show acct) => Show (PLTKernelUpdateCall acct ret)
9798
deriving instance (Eq acct) => Eq (PLTKernelUpdateCall acct ret)
@@ -282,6 +283,7 @@ instance
282283
setTokenState key mValue = handleEvent $ PLTU $ SetTokenState key mValue
283284
transfer sender receiver amount mMemo = handleEvent $ PLTU $ Transfer sender receiver amount mMemo
284285
logTokenEvent eventType details = handleEvent $ PLTU $ LogTokenEvent eventType details
286+
touch acc = handleEvent $ PLTU $ Touch acc
285287

286288
instance
287289
(Eq e, Eq acct, Show e, Show acct, Show ret, Typeable e, Typeable acct, Typeable ret) =>
@@ -1143,6 +1145,7 @@ testLists = do
11431145
:>>: (PLTQ (GetAccountIndex 0) :-> AccountIndex 0)
11441146
:>>: (PLTQ (getModuleStateCall (ltcFeature listConf)) :-> Just "")
11451147
:>>: (PLTQ (GetAccount (dummyAccountAddress 1)) :-> Just 4)
1148+
:>>: (PLTU (Touch 4) :-> False)
11461149
:>>: (PLTQ (GetAccountIndex 4) :-> 4)
11471150
:>>: (PLTU (setAccountStateCall 4 (ltcFeature listConf) (ltcNewValue listConf)) :-> Just False)
11481151
:>>: ( PLTU

0 commit comments

Comments
 (0)