github.com/KiraCore/sekai@v0.3.43/x/layer2/keeper/dapp_session.go (about)

     1  package keeper
     2  
     3  import (
     4  	"github.com/KiraCore/sekai/x/layer2/types"
     5  	sdk "github.com/cosmos/cosmos-sdk/types"
     6  )
     7  
     8  func (k Keeper) SetDappSession(ctx sdk.Context, session types.ExecutionRegistrar) {
     9  	bz := k.cdc.MustMarshal(&session)
    10  	store := ctx.KVStore(k.storeKey)
    11  	store.Set(types.ExecutionRegistrarKey(session.DappName), bz)
    12  }
    13  
    14  func (k Keeper) DeleteDappSession(ctx sdk.Context, name string) {
    15  	store := ctx.KVStore(k.storeKey)
    16  	store.Delete(types.ExecutionRegistrarKey(name))
    17  }
    18  
    19  func (k Keeper) GetDappSession(ctx sdk.Context, name string) types.ExecutionRegistrar {
    20  	store := ctx.KVStore(k.storeKey)
    21  	bz := store.Get(types.ExecutionRegistrarKey(name))
    22  	if bz == nil {
    23  		return types.ExecutionRegistrar{}
    24  	}
    25  
    26  	sessionInfo := types.ExecutionRegistrar{}
    27  	k.cdc.MustUnmarshal(bz, &sessionInfo)
    28  	return sessionInfo
    29  }
    30  
    31  func (k Keeper) GetAllDappSessions(ctx sdk.Context) []types.ExecutionRegistrar {
    32  	store := ctx.KVStore(k.storeKey)
    33  
    34  	sessions := []types.ExecutionRegistrar{}
    35  	it := sdk.KVStorePrefixIterator(store, []byte(types.PrefixDappSessionKey))
    36  	defer it.Close()
    37  
    38  	for ; it.Valid(); it.Next() {
    39  		session := types.ExecutionRegistrar{}
    40  		k.cdc.MustUnmarshal(it.Value(), &session)
    41  		sessions = append(sessions, session)
    42  	}
    43  	return sessions
    44  }
    45  
    46  // Halt the dapp if verifiers and executors number does not meet on ExitDapp and after Jail operation
    47  func (k Keeper) HaltDappIfNoEnoughActiveOperators(ctx sdk.Context, dappName string) bool {
    48  	dapp := k.GetDapp(ctx, dappName)
    49  	executors := k.GetDappExecutors(ctx, dappName)
    50  	verifiers := k.GetDappVerifiers(ctx, dappName)
    51  	// active executors
    52  	activeExecutors := int(0)
    53  	for _, executor := range executors {
    54  		if executor.Status == types.OperatorActive {
    55  			activeExecutors++
    56  		}
    57  	}
    58  	activeVerifiers := int(0)
    59  	for _, verifier := range verifiers {
    60  		if verifier.Status == types.OperatorActive {
    61  			activeVerifiers++
    62  		}
    63  	}
    64  
    65  	if activeExecutors < int(dapp.ExecutorsMin) || activeVerifiers < int(dapp.VerifiersMin) {
    66  		dapp.Status = types.Halted
    67  		k.SetDapp(ctx, dapp)
    68  		return true
    69  	}
    70  	return false
    71  }
    72  
    73  func (k Keeper) ResetNewSession(ctx sdk.Context, name string, prevLeader string) {
    74  	operators := k.GetDappOperators(ctx, name)
    75  	for _, operator := range operators {
    76  		if operator.Status == types.OperatorExiting {
    77  			// If the operator leaving the dApp was a verifier then
    78  			// as the result of the exit tx his LP tokens bond should be returned once the record is deleted.
    79  			// The bond can only be claimed if and only if the status didn’t change to jailed in the meantime.
    80  			if operator.BondedLpAmount.IsPositive() {
    81  				dapp := k.GetDapp(ctx, name)
    82  				dappLpToken := dapp.LpToken()
    83  				lpCoins := sdk.NewCoins(sdk.NewCoin(dappLpToken, operator.BondedLpAmount))
    84  				addr := sdk.MustAccAddressFromBech32(operator.Operator)
    85  				err := k.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addr, lpCoins)
    86  				if err != nil {
    87  					panic(err)
    88  				}
    89  			}
    90  			// remove operators exiting when session ends
    91  			k.DeleteDappOperator(ctx, name, operator.Operator)
    92  		}
    93  	}
    94  
    95  	// halt the dapp if operators condition does not meet on session reset
    96  	halted := k.HaltDappIfNoEnoughActiveOperators(ctx, name)
    97  	if halted {
    98  		return
    99  	}
   100  
   101  	leader := ""
   102  	executors := k.GetDappExecutors(ctx, name)
   103  	if len(executors) > 0 {
   104  		leader = executors[0].Operator
   105  		for index, executor := range executors {
   106  			if executor.Operator == prevLeader {
   107  				leader = executors[(index+1)%len(executors)].Interx
   108  			}
   109  		}
   110  	}
   111  	if leader == "" {
   112  		operator := k.GetDappOperator(ctx, name, prevLeader)
   113  		if operator.Status == types.OperatorActive {
   114  			leader = prevLeader
   115  		}
   116  	}
   117  
   118  	session := k.GetDappSession(ctx, name)
   119  	session.NextSession = &types.DappSession{
   120  		Leader:     leader,
   121  		Start:      uint64(ctx.BlockTime().Unix()),
   122  		StatusHash: "",
   123  		Status:     types.SessionScheduled,
   124  	}
   125  	k.SetDappSession(ctx, session)
   126  
   127  	// halt the dapp if next session leader is not available
   128  	if session.NextSession.Leader == "" {
   129  		dapp := k.GetDapp(ctx, name)
   130  		dapp.Status = types.Halted
   131  		k.SetDapp(ctx, dapp)
   132  	}
   133  }
   134  
   135  func (k Keeper) CreateNewSession(ctx sdk.Context, name string, prevLeader string) {
   136  	session := k.GetDappSession(ctx, name)
   137  	session.PrevSession = session.CurrSession
   138  	session.CurrSession = session.NextSession
   139  	k.SetDappSession(ctx, session)
   140  
   141  	// handle bridge and mint messages
   142  	msgServer := NewMsgServerImpl(k)
   143  	for _, msg := range session.PrevSession.OnchainMessages {
   144  		cacheCtx, write := ctx.CacheContext()
   145  		var err error
   146  		switch msg := msg.GetCachedValue().(type) {
   147  		case *types.MsgTransferDappTx:
   148  			_, err = msgServer.TransferDappTx(sdk.WrapSDKContext(cacheCtx), msg)
   149  		case *types.MsgAckTransferDappTx:
   150  			_, err = msgServer.AckTransferDappTx(sdk.WrapSDKContext(ctx), msg)
   151  		case *types.MsgMintCreateFtTx:
   152  			_, err = msgServer.MintCreateFtTx(sdk.WrapSDKContext(ctx), msg)
   153  		case *types.MsgMintCreateNftTx:
   154  			_, err = msgServer.MintCreateNftTx(sdk.WrapSDKContext(ctx), msg)
   155  		case *types.MsgMintIssueTx:
   156  			_, err = msgServer.MintIssueTx(sdk.WrapSDKContext(ctx), msg)
   157  		case *types.MsgMintBurnTx:
   158  			_, err = msgServer.MintBurnTx(sdk.WrapSDKContext(ctx), msg)
   159  		}
   160  		if err == nil {
   161  			write()
   162  		}
   163  	}
   164  	k.ResetNewSession(ctx, name, prevLeader)
   165  }
   166  
   167  // **Next Session**
   168  
   169  // A summary of possible “next session” states:
   170  // - `unscheduled` - The session is NOT ready yet to be picked up by another dApp Leader (e.g. previous dApp Session is ongoing)
   171  // - `scheduled`- The session is ready to be picked up by the next dApp Leader
   172  // - `ongoing` - Session was claimed by the dApp Leader and is currently being executed
   173  
   174  // **Current Session**
   175  
   176  // Allow every executor to send a `denounce-leader-tx` (true/false) which at any point in time via majority vote can invalidate
   177  // further changes to the current session
   178  // and will cause the next session to change its status to `scheduled`.
   179  
   180  // It must be possible for the automatic denouncement of the leader to happen if the current session data is not updated within
   181  // `update_time_max` since `sessions.next.data.time` while the executor
   182  // did NOT change his status to `paused`
   183  // If automatic denouement happens then the same as in the case of manual votes of denouement the current session status
   184  // should change to `denounced` while the next session status should be set to `scheduled`.
   185  
   186  // The most important property of the current session is `data`.
   187  // The objective of the leader is to periodically update the `sessions.current.data` property with hashes of the `old` state,
   188  // user `input`, the `new` proposed state, expected changes to account balances,
   189  // and optional `proof` of the correctness of the execution
   190  
   191  // The `transition-dapp-tx` can be submitted ONLY by the leader and can be sent at ANY point in time allowing for finality
   192  // the moment a sufficient number of verifiers or executors accept the changes**.
   193  
   194  // Whenever leader submits the new dApp state transition the `version` MUST be included and match the application version**.
   195  // The transition tx should fail if the dApp `version` is NOT correct, the leader status is NOT active or
   196  // if the `old` state hash does NOT match the `sessions.previous.data.new` hash.
   197  
   198  // There are no limitations to how many times the current session leader can submit `transition-dapp-tx` to replace the content
   199  // of the current session data, however, every time a new dApp state transition is submitted,
   200  // the list of approvals must be wiped, meaning any verifications will be lost.
   201  
   202  // Regardless if the approvals are lost or not the performance counters should maintain their count allowing for a fair
   203  // reward distribution to operators later on.
   204  // Additionally, the dApp leader must send the `transition-dapp-tx` before `update_time_max` elapses,
   205  // otherwise his session will expire, and sending of any further `transition-dapp-tx` should fail, and the
   206  // [sessions.currend.data.final](http://sessions.currend.data.final)` flag should be set to true.
   207  // Finally, the session leader must be able to include a boolean flag in the `transition-dapp-tx`
   208  // indicating if the transition tx is final on his own.
   209  // If the final session flag is set then the leader should NOT be allowed to submit another
   210  // `transition-dapp-tx` to the current session.
   211  
   212  // If the non-final session is approved before the finality flag is set and the session changes state to `approved`
   213  // then the default next session leader should become the current session leader.
   214  // This way the operator can continue execution with all data already available to him and provide fully uninterrupted service
   215  // to the users.
   216  // In order to give verifiers sufficient time to approve non-final sessions we should prohibit the submission of new
   217  // `transition-dapp-tx` by the leader unless no less than `update_time_max/2` seconds elapsed since the last submission.
   218  
   219  // A summary of possible “current session” states:
   220  
   221  // - `accepted` - Session was accepted by verifiers and the new state is irreversible (changes will be applied to the blockchain unless status changes to halted or failed)
   222  // - `ongoing` - Session was claimed by the dApp Leader and is currently being executed
   223  // - `denounced` - The session was rejected by either a manual vote of executors to change the leader or due to the leader becoming jailed.
   224  // - `halted` - The session was halted because it was questioned by one of the verifiers submitting evidence against it. Validators will have to assist unhalting and decide slashing penalty to either validator or fisherman
   225  // - `failed` - The session failed to transition from “current” into “previous session” because internal data submitted by the leader was invalid and could NOT result in the modification of the blockchain state.
   226  
   227  // In the case where the current dApp Session becomes `failed` or `halted` but the new Leader already started execution based on an unverified state then both sessions (current and next) should be rejected by the network and a consecutive dApp Leader MUST start the execution from the dApp State that was persisted as a result of previous Session. If by any chance resources from the previous dApp state are no longer accessible (e.g. IPFS/URL link does not work and no validator has it saved on their execution node) then users can begin recovery of funds in accordance to the latest known settlement of balances within the Execution Registrar.
   228  
   229  // **Previous Session**
   230  
   231  // In order for the already approved “current session” to become a “previous session” the changes proposed in the `data.internal`
   232  // section of the current session properties must be applied to the blockchain state.
   233  // We will differentiate two types of internal data:
   234  
   235  // - Application Data (`appdata`) - A simple key-value pair dictionary, each application should be able to store on-chain
   236  // up to `256` keys with up to `8192` characters each.
   237  // The purpose of `appdata` is to enable applications communicate informations to the users that can be easily accessed
   238  // and can be trusted, this might include location of IPFS gateways preserving dApp state files,
   239  // deposit addresses of bridges, communicates to other applications and more.
   240  //     - JSON Structure:
   241  
   242  //         ```json
   243  //         { "<key-1>": "<value-1>", "<key-2>": "<value-2>", ... }
   244  //         ```
   245  
   246  //     - Key names must be unique and adhere to the same rules as IR keys
   247  //     - Values must be a string or set to integer value `0` indicating that the key should be deleted
   248  //     - Compression Algorithm: `zip (max compression, level 9)`
   249  //     - Format: `base64`
   250  // - Cross-Application Message Data (`xamdata`) - A simple json structure containing changes that must be communicated
   251  // to the blockchain and in particular to the Application Bridge Registrar (ABR).
   252  // The ABR is responsible for maintaining balances of users who deposit tokens to various dApps,
   253  // minting new tokens and sending communicates between dApps & internal modules.
   254  //     - JSON Structure:
   255  
   256  //         ```json
   257  //         { // key-array list defninig changes that must be applied to the Application Bridge Registrar
   258  //         	"<key-1>": [ "<value-1>", "<value-2>", "<value-3>", ...  ], ... }
   259  //         ```
   260  
   261  //     - Compression Algorithm: `zip (max compression, level 9)`
   262  //     - Format: `base64`
   263  
   264  // It is NOT guaranteed that even if dApp state was accepted the changes to the blockchain can actually be executed.
   265  // For example application might be requesting to transfer to the user more assets then it actually holds.
   266  // In such cases the State of the “current session” state must change to `failed` and the next session should automatically be wiped
   267  // and set to `scheaduled`.
   268  // The operators must also be explicitly informed what is the exact reason for the failure though `errors`
   269  // property in the current session.
   270  // Any failures should automatically result in the current session leader being denounced,
   271  //  meaning that the next session leader should be changed even if session finalization flag was not set in `transition-dapp-tx`.
   272  
   273  // **Reporting Failed Execution**
   274  
   275  // Fisherman (verifiers and executors who are not a leader) must be able to report any potential misbehavior of a leader
   276  // and/or otherwise issues with the application to ensure that funds locked within can remain safe.
   277  // In certain cases the finality gadget might not even exists or be possible to be created while fisherman might want
   278  // to create custom systems monitoring behavior of the application far beyond the code logic itself.
   279  // ANY fisherman must be able to send a `reject-dapp-transition-tx` with a message body (up to `8192` characters)
   280  // containing a dApp name, session id and a reason for halting the application.
   281  // As the result of `reject-dapp-transition-tx` the status of the current session should immediately change to
   282  // `halted` and a `dapp-slash` proposal raised, similar to the way that the validator slashing proposals are created.
   283  // In this case however the validators will need to make following judgements:
   284  
   285  // - should the dApp session be unhalted and state transition accepted (boolean) - true / false
   286  //     - if `true` then status of session should change to `ongoing` unless sufficient number of approvals is sent then `accepted`
   287  //     - if `false` then status should be set to `failed` and `errors` specify that governance vote decided to reject the session
   288  // - what percentage of the fisherman bond should be slashed (percentage) - from 0 to 1 (100%)
   289  //     - slashed dApp LP tokens should be simply deminted (increasing value of other LP token holders positions)
   290  //     - if fisherman is slashed the validator can NOT be slashed
   291  // - what percentage of the validator stake should be slashed (percentage) - from 0 to 0.1 (10%)
   292  //     - From the amount slashed 1% should be sent to fisherman address as reward and 99% to community pool
   293  //     - if validator is slashed the fisherman can NOT be slashed
   294  
   295  // Voting on `dapp-slash` proposal should only be possible by validators **with exception for the dApp leader**.
   296  // If quorum is not reached or result is inconclusive then the default behavior should be no slashing and
   297  // rejection of the dApp session state. If as the result of the proposal the bond of the fisherman was slashed,
   298  // then he should be removed from the fisherman position until sufficient amount is locked by him again.