diff --git a/common/api.go b/common/api.go index 016281a..4f657dc 100644 --- a/common/api.go +++ b/common/api.go @@ -19,6 +19,7 @@ import ( tmjson "github.com/cometbft/cometbft/libs/json" tmTypes "github.com/cometbft/cometbft/rpc/core/types" tmJsonRPCTypes "github.com/cometbft/cometbft/rpc/jsonrpc/types" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" ) @@ -125,6 +126,62 @@ func GetAccountBalances(gwCosmosmux *runtime.ServeMux, r *http.Request, bech32ad return result.Balances } +type DappSession struct { + Leader string `protobuf:"bytes,1,opt,name=leader,proto3" json:"leader,omitempty"` + Start string `protobuf:"varint,2,opt,name=start,proto3" json:"start,omitempty"` + StatusHash string `protobuf:"bytes,3,opt,name=status_hash,json=statusHash,proto3" json:"statusHash,omitempty"` + Status string `protobuf:"varint,4,opt,name=status,proto3,enum=kira.layer2.SessionStatus" json:"status,omitempty"` + Gateway string `protobuf:"bytes,5,opt,name=gateway,proto3" json:"gateway,omitempty"` + OnchainMessages []*codectypes.Any `protobuf:"bytes,6,rep,name=onchain_messages,json=onchainMessages,proto3" json:"onchainMessages,omitempty"` +} + +type ExecutionRegistrar struct { + DappName string `protobuf:"bytes,1,opt,name=dapp_name,json=dappName,proto3" json:"dappName,omitempty"` + PrevSession *DappSession `protobuf:"bytes,2,opt,name=prev_session,json=prevSession,proto3" json:"prevSession,omitempty"` + CurrSession *DappSession `protobuf:"bytes,3,opt,name=curr_session,json=currSession,proto3" json:"currSession,omitempty"` + NextSession *DappSession `protobuf:"bytes,4,opt,name=next_session,json=nextSession,proto3" json:"nextSession,omitempty"` +} + +type DappOperator struct { + DappName string `protobuf:"bytes,1,opt,name=dapp_name,json=dappName,proto3" json:"dappName,omitempty"` + Operator string `protobuf:"bytes,2,opt,name=operator,proto3" json:"operator,omitempty"` + Executor bool `protobuf:"varint,3,opt,name=executor,proto3" json:"executor,omitempty"` + Verifier bool `protobuf:"varint,4,opt,name=verifier,proto3" json:"verifier,omitempty"` + Interx string `protobuf:"bytes,5,opt,name=interx,proto3" json:"interx,omitempty"` + Status string `protobuf:"varint,6,opt,name=status,proto3,enum=kira.layer2.OperatorStatus" json:"status,omitempty"` + Rank string `protobuf:"varint,7,opt,name=rank,proto3" json:"rank,omitempty"` + Streak string `protobuf:"varint,8,opt,name=streak,proto3" json:"streak,omitempty"` + Mischance string `protobuf:"varint,9,opt,name=mischance,proto3" json:"mischance,omitempty"` + VerifiedSessions string `protobuf:"varint,10,opt,name=verified_sessions,json=verifiedSessions,proto3" json:"verifiedSessions,omitempty"` + MissedSessions string `protobuf:"varint,11,opt,name=missed_sessions,json=missedSessions,proto3" json:"missedSessions,omitempty"` + BondedLpAmount string `protobuf:"bytes,12,opt,name=bonded_lp_amount,json=bondedLpAmount,proto3" json:"bondedLpAmount"` +} + +type QueryExecutionRegistrarResponse struct { + Dapp interface{} `protobuf:"bytes,1,opt,name=dapp,proto3" json:"dapp,omitempty"` + ExecutionRegistrar *ExecutionRegistrar `json:"executionRegistrar,omitempty"` + Operators []DappOperator `protobuf:"bytes,3,rep,name=operators,proto3" json:"operators"` +} + +func GetExecutionRegistrar(gwCosmosmux *runtime.ServeMux, r *http.Request, appName string) QueryExecutionRegistrarResponse { + r.URL.Path = fmt.Sprintf("/kira/layer2/execution_registrar/%s", appName) + r.URL.RawQuery = "" + r.Method = "GET" + + recorder := httptest.NewRecorder() + gwCosmosmux.ServeHTTP(recorder, r) + resp := recorder.Result() + + result := QueryExecutionRegistrarResponse{} + + err := json.NewDecoder(resp.Body).Decode(&result) + if err != nil { + GetLogger().Error("[grpc-call] Unable to decode response: ", err) + } + + return result +} + // GetAccountNumberSequence is a function to get AccountNumber and Sequence func GetAccountNumberSequence(gwCosmosmux *runtime.ServeMux, r *http.Request, bech32addr string) (uint64, uint64) { _, err := sdk.AccAddressFromBech32(bech32addr) @@ -163,6 +220,44 @@ func GetAccountNumberSequence(gwCosmosmux *runtime.ServeMux, r *http.Request, be return uint64(accountNumber), uint64(sequence) } +func BroadcastTransactionSync(rpcAddr string, txBytes []byte) (string, error) { + endpoint := fmt.Sprintf("%s/broadcast_tx_sync?tx=0x%X", rpcAddr, txBytes) + GetLogger().Info("[rpc-call] Entering rpc call: ", endpoint) + + resp, err := http.Get(endpoint) + if err != nil { + GetLogger().Error("[rpc-call] Unable to connect to ", endpoint) + return "", err + } + defer resp.Body.Close() + + type RPCTempResponse struct { + Jsonrpc string `json:"jsonrpc"` + ID int `json:"id"` + Result struct { + Height string `json:"height"` + Hash string `json:"hash"` + } `json:"result,omitempty"` + Error struct { + Message string `json:"message"` + } `json:"error,omitempty"` + } + + result := new(RPCTempResponse) + err = json.NewDecoder(resp.Body).Decode(result) + if err != nil { + GetLogger().Error("[rpc-call] Unable to decode response: ", err) + return "", err + } + + if resp.StatusCode != http.StatusOK { + GetLogger().Error("[rpc-call] Unable to broadcast transaction: ", result.Error.Message) + return "", errors.New(result.Error.Message) + } + + return result.Result.Hash, nil +} + // BroadcastTransaction is a function to post transaction, returns txHash func BroadcastTransaction(rpcAddr string, txBytes []byte) (string, error) { endpoint := fmt.Sprintf("%s/broadcast_tx_async?tx=0x%X", rpcAddr, txBytes) diff --git a/common/main.go b/common/main.go index 492e238..23c75c6 100644 --- a/common/main.go +++ b/common/main.go @@ -78,3 +78,5 @@ func IsCacheExpired(result types.InterxResponse) bool { return isBlockExpire || isTimestampExpire } + +var Layer2Status map[string]string diff --git a/config/constants.go b/config/constants.go index 208905d..b1f00f3 100755 --- a/config/constants.go +++ b/config/constants.go @@ -2,7 +2,7 @@ package config const ( InterxVersion = "v0.4.48" - SekaiVersion = "v0.3.42" + SekaiVersion = "v0.4.0" CosmosVersion = "v0.47.6" QueryDashboard = "/api/dashboard" @@ -76,6 +76,8 @@ const ( QueryAddrBook = "/api/addrbook" QueryNetInfo = "/api/net_info" + QueryLayer2Status = "/api/layer2/{appName}/status" + Download = "/download" AppDownload = "/app/download" DataReferenceRegistry = "DRR" @@ -183,3 +185,4 @@ var MsgTypes = map[string]string{ } var SupportedEVMChains = [1]string{"goerli"} var SupportedBitcoinChains = [1]string{"testnet"} +var SupportedLayer2Apps = [1]string{"l2chess"} diff --git a/config/init.go b/config/init.go index 3f947f2..bfbc12d 100644 --- a/config/init.go +++ b/config/init.go @@ -202,6 +202,15 @@ func defaultConfig() InterxConfigFromFile { configFromFile.CachingBin = false + configFromFile.Layer2 = make(map[string]Layer2Config) + for _, item := range SupportedLayer2Apps { + layer2Config := Layer2Config{ + RPC: "http://127.0.0.1:9000", + Fee: "300ukex", + } + configFromFile.Layer2[item] = layer2Config + } + return configFromFile } diff --git a/config/load.go b/config/load.go index ea82931..c6e8e5f 100644 --- a/config/load.go +++ b/config/load.go @@ -145,9 +145,9 @@ func LoadAddressAndDenom(configFilePath string, gwCosmosmux *runtime.ServeMux, r panic("Invalid Interx Mnemonic") } - privKey := ConvMnemonic2PrivKey(Config.Mnemonic) + privKey1 := ConvMnemonic2PrivKey(Config.Mnemonic) - Config.PrivKey = &privKey + Config.PrivKey = &privKey1 Config.PubKey = Config.PrivKey.PubKey() Config.Address = sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), Config.PubKey.Address()) @@ -193,9 +193,9 @@ func LoadAddressAndDenom(configFilePath string, gwCosmosmux *runtime.ServeMux, r panic("Invalid Faucet Mnemonic") } - privKey = ConvMnemonic2PrivKey(Config.Faucet.Mnemonic) + privKey2 := ConvMnemonic2PrivKey(Config.Faucet.Mnemonic) - Config.Faucet.PrivKey = &privKey + Config.Faucet.PrivKey = &privKey2 Config.Faucet.PubKey = Config.Faucet.PrivKey.PubKey() Config.Faucet.Address = sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), Config.Faucet.PubKey.Address()) @@ -333,6 +333,7 @@ func LoadConfig(configFilePath string) { Config.Evm = configFromFile.Evm Config.Bitcoin = configFromFile.Bitcoin + Config.Layer2 = configFromFile.Layer2 Config.SnapshotInterval = configFromFile.SnapshotInterval Config.CachingBin = configFromFile.CachingBin diff --git a/config/type.go b/config/type.go index 785c270..63250fa 100644 --- a/config/type.go +++ b/config/type.go @@ -91,6 +91,11 @@ type BitcoinConfig struct { BTC_FAUCET string `json:"btc_faucet"` } +type Layer2Config struct { + RPC string `json:"rpc"` + Fee string `json:"fee"` +} + type AppSettingConfig struct { AppMode int `json:"app_mode"` AppMock bool `json:"app_mock"` @@ -121,6 +126,7 @@ type InterxConfig struct { RPCMethods RPCConfig `json:"rpc_methods"` Evm map[string]EVMConfig `json:"evm"` Bitcoin map[string]BitcoinConfig `json:"bitcoin"` + Layer2 map[string]Layer2Config `json:"layer2"` SnapshotInterval uint64 `json:"snapshot_interval"` AppSetting AppSettingConfig `json:"app_setting"` CachingBin bool `json:"caching_bin"` @@ -154,6 +160,7 @@ type InterxConfigFromFile struct { } `json:"faucet"` Evm map[string]EVMConfig `json:"evm"` Bitcoin map[string]BitcoinConfig `json:"bitcoin"` + Layer2 map[string]Layer2Config `json:"layer2"` SnapshotInterval uint64 `json:"snapshot_interval"` AppSetting AppSettingConfig `json:"app_setting"` CachingBin bool `json:"caching_bin"` diff --git a/database/layer2.go b/database/layer2.go new file mode 100644 index 0000000..8345642 --- /dev/null +++ b/database/layer2.go @@ -0,0 +1,81 @@ +package database + +import ( + "github.com/KiraCore/interx/config" + "github.com/sonyarouje/simdb/db" +) + +// Layer2Data is a struct for layer2 details. +type Layer2Data struct { + Id string `json:"id"` + Data string `json:"data"` +} + +// ID is a field for facuet claim struct. +func (c Layer2Data) ID() (jsonField string, value interface{}) { + value = c.Id + jsonField = "id" + return +} + +func LoadLayer2DbDriver() { + DisableStdout() + driver, _ := db.New(config.GetDbCacheDir() + "/layer2") + EnableStdout() + + layer2Db = driver +} + +// GetLayer2State is a function to get layer2 app state stored +func GetLayer2State(id string) (string, error) { + if layer2Db == nil { + panic("cache dir not set") + } + + data := Layer2Data{} + + DisableStdout() + err := layer2Db.Open(Layer2Data{}).Where("id", "=", id).First().AsEntity(&data) + EnableStdout() + + if err != nil { + return "", err + } + + return data.Data, nil +} + +// GetAllLayer2s is a function to get all layer2Times +func GetAllLayer2s() []interface{} { + if layer2Db == nil { + panic("cache dir not set") + } + + DisableStdout() + rawData := layer2Db.Open(Layer2Data{}).RawArray() + EnableStdout() + + return rawData +} + +// SetLayer2State is a function to set layer2 app status +func SetLayer2State(id string, data string) { + if layer2Db == nil { + panic("cache dir not set") + } + + DisableStdout() + err := layer2Db.Open(Layer2Data{}).Insert(Layer2Data{ + Id: id, + Data: data, + }) + EnableStdout() + + if err != nil { + panic(err) + } +} + +var ( + layer2Db *db.Driver +) diff --git a/functions/interx.go b/functions/interx.go index 52cfa15..e82d68c 100644 --- a/functions/interx.go +++ b/functions/interx.go @@ -1028,7 +1028,7 @@ func RegisterInterxFunctions() { "type": "string", "description": "This is an option to query only nodes by app_name or app_id.", "optional": true - }, + } }, "response": { "last_update": { diff --git a/gateway/application_check.go b/gateway/application_check.go index ce4edac..4a5cb80 100644 --- a/gateway/application_check.go +++ b/gateway/application_check.go @@ -2,13 +2,8 @@ package gateway import ( "encoding/json" - "errors" - "net/http" "github.com/KiraCore/interx/common" - "github.com/KiraCore/interx/config" - layer2types "github.com/KiraCore/sekai/x/layer2/types" - "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" ) func ToString(data interface{}) string { @@ -48,32 +43,3 @@ func NodeSyncState(rpcAddr string) bool { return !result.SyncInfo.CatchingUp } - -func CheckApplicationState(gwCosmosmux *runtime.ServeMux, gatewayAddr string) error { - if config.Config.AppSetting.AppMock { - return nil - } - - result := layer2types.QueryExecutionRegistrarResponse{} - appstateQueryRequest, _ := http.NewRequest("GET", "http://"+gatewayAddr+"/kira/layer2/execution_registrar/"+config.Config.AppSetting.AppName, nil) - - appstateQueryResponse, failure, _ := common.ServeGRPC(appstateQueryRequest, gwCosmosmux) - - if appstateQueryResponse == nil { - return errors.New(ToString(failure)) - } - - byteData, err := json.Marshal(appstateQueryResponse) - if err != nil { - return err - } - - err = json.Unmarshal(byteData, &result) - if err != nil { - return err - } - - // TODO : check if config.appmode is not same with appmode in result - // result.Dapp.verifier - return nil -} diff --git a/gateway/layer2/main.go b/gateway/layer2/main.go new file mode 100644 index 0000000..1019575 --- /dev/null +++ b/gateway/layer2/main.go @@ -0,0 +1,11 @@ +package layer2 + +import ( + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" +) + +// RegisterRequest is a function to register requests. +func RegisterRequest(router *mux.Router, gwCosmosmux *runtime.ServeMux, rpcAddr string) { + RegisterStatusRoutes(router, gwCosmosmux, rpcAddr) +} diff --git a/gateway/layer2/status.go b/gateway/layer2/status.go new file mode 100644 index 0000000..08014d5 --- /dev/null +++ b/gateway/layer2/status.go @@ -0,0 +1,54 @@ +package layer2 + +import ( + "net/http" + + "github.com/KiraCore/interx/common" + "github.com/KiraCore/interx/config" + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" +) + +// RegisterBlockRoutes registers layer2/status query routers. +func RegisterStatusRoutes(r *mux.Router, gwCosmosmux *runtime.ServeMux, rpcAddr string) { + r.HandleFunc(config.QueryLayer2Status, QueryLayer2StatusRequest(gwCosmosmux, rpcAddr)).Methods("GET") + + common.AddRPCMethod("GET", config.QueryLayer2Status, "This is an API to query layer2 status.", true) +} + +func queryLayer2StatusHandle(rpcAddr string, r *http.Request) (interface{}, interface{}, int) { + queries := mux.Vars(r) + appName := queries["appName"] + _ = r.ParseForm() + return common.Layer2Status[appName], nil, http.StatusOK +} + +// QueryLayer2StatusRequest is a function to query layer2 status. +func QueryLayer2StatusRequest(gwCosmosmux *runtime.ServeMux, rpcAddr string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var statusCode int + request := common.GetInterxRequest(r) + response := common.GetResponseFormat(request, rpcAddr) + + common.GetLogger().Info("[query-layer2-status] Entering Layer2 status query") + + if !common.RPCMethods["GET"][config.QueryLayer2Status].Enabled { + response.Response, response.Error, statusCode = common.ServeError(0, "", "API disabled", http.StatusForbidden) + } else { + if common.RPCMethods["GET"][config.QueryLayer2Status].CachingEnabled { + found, cacheResponse, cacheError, cacheStatus := common.SearchCache(request, response) + if found { + response.Response, response.Error, statusCode = cacheResponse, cacheError, cacheStatus + common.WrapResponse(w, request, *response, statusCode, false) + + common.GetLogger().Info("[query-layer2-status] Returning from the cache") + return + } + } + + response.Response, response.Error, statusCode = queryLayer2StatusHandle(rpcAddr, r) + } + + common.WrapResponse(w, request, *response, statusCode, common.RPCMethods["GET"][config.QueryLayer2Status].CachingEnabled) + } +} diff --git a/gateway/main.go b/gateway/main.go index 1fc5c1e..bb662dc 100644 --- a/gateway/main.go +++ b/gateway/main.go @@ -15,6 +15,7 @@ import ( cosmosAuth "github.com/KiraCore/interx/proto-gen/cosmos/auth/v1beta1" cosmosBank "github.com/KiraCore/interx/proto-gen/cosmos/bank/v1beta1" kiraGov "github.com/KiraCore/interx/proto-gen/kira/gov" + kiraLayer2 "github.com/KiraCore/interx/proto-gen/kira/layer2" kiraMultiStaking "github.com/KiraCore/interx/proto-gen/kira/multistaking" kiraSlashing "github.com/KiraCore/interx/proto-gen/kira/slashing/v1beta1" kiraSpending "github.com/KiraCore/interx/proto-gen/kira/spending" @@ -123,6 +124,10 @@ func GetGrpcServeMux(grpcAddr string) (*runtime.ServeMux, error) { return nil, fmt.Errorf("failed to register gateway: %w", err) } + err = kiraLayer2.RegisterQueryHandler(context.Background(), gwCosmosmux, conn) + if err != nil { + return nil, fmt.Errorf("failed to register gateway: %w", err) + } return gwCosmosmux, nil } @@ -136,6 +141,7 @@ func Run(configFilePath string, log grpclog.LoggerV2) error { database.LoadBlockNanoDbDriver() database.LoadFaucetDbDriver() database.LoadReferenceDbDriver() + database.LoadLayer2DbDriver() serveHTTPS := config.Config.ServeHTTPS grpcAddr := config.Config.GRPC @@ -181,11 +187,6 @@ func Run(configFilePath string, log grpclog.LoggerV2) error { time.Sleep(time.Second) } - err = CheckApplicationState(gwCosmosmux, gatewayAddr) - if err != nil { - return err - } - tasks.RunTasks(gwCosmosmux, rpcAddr, gatewayAddr) if serveHTTPS { diff --git a/gateway/register.go b/gateway/register.go index 1c57155..88159fa 100644 --- a/gateway/register.go +++ b/gateway/register.go @@ -6,6 +6,7 @@ import ( "github.com/KiraCore/interx/gateway/evm" "github.com/KiraCore/interx/gateway/interx" "github.com/KiraCore/interx/gateway/kira" + "github.com/KiraCore/interx/gateway/layer2" "github.com/KiraCore/interx/gateway/rosetta" "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" @@ -19,4 +20,5 @@ func RegisterRequest(router *mux.Router, gwCosmosmux *runtime.ServeMux, rpcAddr rosetta.RegisterRequest(router, gwCosmosmux, rpcAddr) bitcoin.RegisterRequest(router, rpcAddr) evm.RegisterRequest(router, rpcAddr) + layer2.RegisterRequest(router, gwCosmosmux, rpcAddr) } diff --git a/tasks/layer2_status.go b/tasks/layer2_status.go new file mode 100644 index 0000000..3c2305f --- /dev/null +++ b/tasks/layer2_status.go @@ -0,0 +1,219 @@ +package tasks + +import ( + "fmt" + "io" + "net/http" + "strconv" + "strings" + "time" + + "github.com/KiraCore/interx/common" + "github.com/KiraCore/interx/config" + "github.com/KiraCore/interx/database" + layer2types "github.com/KiraCore/sekai/x/layer2/types" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + sdk "github.com/cosmos/cosmos-sdk/types" + legacytx "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + + // banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + types "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" +) + +func storeL2Status(gwCosmosmux *runtime.ServeMux, isLog bool) { + for l2AppName, conf := range config.Config.Layer2 { + rpcAddr := conf.RPC + url := fmt.Sprintf("%s/api/v1/status", rpcAddr) + resp, err := http.Get(url) + if err != nil { + common.GetLogger().Error("[layer2-status] Unable to connect to ", url) + return + } + defer resp.Body.Close() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + common.GetLogger().Error("[layer2-status] Unable to read status export result", err) + return + } + + database.SetLayer2State("chess", string(bodyBytes)) + + if isLog { + common.GetLogger().Info("[layer2-status] (new state): ", time.Now().UTC()) + } + + common.Layer2Status[l2AppName] = string(bodyBytes) + + r, err := http.NewRequest("GET", getInterxRPC(), strings.NewReader("")) + if err != nil { + common.GetLogger().Error("[layer2]: ", err) + return + } + + // If leader of session + registrar := common.GetExecutionRegistrar(gwCosmosmux, r, l2AppName) + + // Workflow + // if current session is empty and next session leader is myself, send MsgExecuteDappTx (this is needed at start phase) + // if current session leader is myself and status is scheduled, send MsgExecuteDappTx (this is needed when session's accepted and next session moved to current session) + // if current session leader is myself and status is on-going, send MsgTransitionDappTx + // if current session looks okay, send MsgApproveDappTransitionTx if not, send MsgRejectDappTransitionTx (check registrar.ExecutionRegistrar.CurrSession.Start) + // check all the state transition flow's working fine + // how often should it execute approve tx? Since it will let sessions to be moved + + if registrar.ExecutionRegistrar != nil && registrar.ExecutionRegistrar.NextSession != nil { + currSession := registrar.ExecutionRegistrar.CurrSession + nextSession := registrar.ExecutionRegistrar.NextSession + for _, operator := range registrar.Operators { + if operator.Interx != config.Config.Address { + continue + } + if currSession == nil && operator.Operator == nextSession.Leader { + msg := &layer2types.MsgExecuteDappTx{ + Sender: config.Config.Address, + DappName: l2AppName, + Gateway: "interx.com", + } + + sendTx(msg, conf.Fee, gwCosmosmux, r) + time.Sleep(time.Second * 5) + } + } + } + + if registrar.ExecutionRegistrar != nil && registrar.ExecutionRegistrar.CurrSession != nil { + currSession := registrar.ExecutionRegistrar.CurrSession + + for _, operator := range registrar.Operators { + if operator.Interx != config.Config.Address { + continue + } + + if currSession.Leader == operator.Operator { + // sekai RPC submit + // msg := &banktypes.MsgSend{ + // FromAddress: config.Config.Address, + // ToAddress: config.Config.Faucet.Address, + // Amount: sdk.Coins{sdk.NewInt64Coin("ukex", 100)}, + // } + msg := &layer2types.MsgTransitionDappTx{ + Sender: config.Config.Address, + DappName: l2AppName, + StatusHash: common.GetSha256SumFromBytes([]byte(common.Layer2Status[l2AppName] + fmt.Sprint(time.Now().Unix()))), + OnchainMessages: []*types.Any{}, + Version: "0cc0", // TODO: to be queried from dapp docker - to be equal to dapp.Bin[0].Hash + } + sendTx(msg, conf.Fee, gwCosmosmux, r) + time.Sleep(time.Second * 5) + } + + now := time.Now().Unix() + start, err := strconv.Atoi(currSession.Start) + if err != nil || start+60 < int(now) { // execute transition once per min for now + msg := &layer2types.MsgApproveDappTransitionTx{ + Sender: config.Config.Address, + DappName: l2AppName, + Version: "0cc0", // TODO: to be queried from dapp docker - to be equal to dapp.Bin[0].Hash + } + // msg := layer2types.MsgRejectDappTransitionTx{ + // Sender: config.Config.Address, + // DappName: l2AppName, + // Version: "0cc0", // TODO: to be queried from dapp docker - to be equal to dapp.Bin[0].Hash + // } + sendTx(msg, conf.Fee, gwCosmosmux, r) + time.Sleep(time.Second * 5) + } + } + } + } +} + +func getInterxRPC() string { + return "http://127.0.0.1:" + config.Config.PORT +} + +// StoreL2Status is a function storing layer2 app status +func StoreL2Status(gwCosmosmux *runtime.ServeMux, isLog bool) { + for { + storeL2Status(gwCosmosmux, isLog) + + if isLog { + common.GetLogger().Info("[node-status] Syncing node status") + common.GetLogger().Info("[node-status] Chain_id = ", common.NodeStatus.Chainid) + common.GetLogger().Info("[node-status] Block = ", common.NodeStatus.Block) + common.GetLogger().Info("[node-status] Blocktime = ", common.NodeStatus.Blocktime) + } + + time.Sleep(time.Duration(config.Config.Block.StatusSync) * time.Second) + } +} + +func sendTx(msg cosmostypes.Msg, feeStr string, gwCosmosmux *runtime.ServeMux, r *http.Request) error { + accountNumber, sequence := common.GetAccountNumberSequence(gwCosmosmux, r.Clone(r.Context()), config.Config.Address) + + feeAmount, err := sdk.ParseCoinNormalized(feeStr) + if err != nil { + return err + } + + msgs := []sdk.Msg{msg} + fee := legacytx.NewStdFee(200000, sdk.NewCoins(feeAmount)) //Fee handling + memo := "layer2-transition" + + // TODO: get it from somewhere + common.NodeStatus.Chainid = "testing" + sigs := make([]legacytx.StdSignature, 1) + signBytes := legacytx.StdSignBytes(common.NodeStatus.Chainid, accountNumber, sequence, 0, fee, msgs, memo, nil) + sig, err := config.Config.PrivKey.Sign(signBytes) + if err != nil { + common.GetLogger().Error("[layer2] Failed to sign transaction: ", err) + panic(err) + } + + sigs[0] = legacytx.StdSignature{PubKey: config.Config.PubKey, Signature: sig} + + stdTx := legacytx.NewStdTx(msgs, fee, sigs, memo) + + txBuilder := config.EncodingCg.TxConfig.NewTxBuilder() + err = txBuilder.SetMsgs(stdTx.GetMsgs()...) + if err != nil { + common.GetLogger().Error("[layer2] Failed to set tx msgs: ", err) + return err + } + + sigV2, err := stdTx.GetSignaturesV2() + if err != nil { + common.GetLogger().Error("[layer2] Failed to get SignatureV2: ", err) + return err + } + + sigV2[0].Sequence = sequence + + err = txBuilder.SetSignatures(sigV2...) + if err != nil { + common.GetLogger().Error("[layer2] Failed to set SignatureV2: ", err) + return err + } + + txBuilder.SetMemo(stdTx.GetMemo()) + txBuilder.SetFeeAmount(stdTx.GetFee()) + txBuilder.SetGasLimit(stdTx.GetGas()) + + txBytes, err := config.EncodingCg.TxConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + common.GetLogger().Error("[layer2] Failed to get tx bytes: ", err) + return err + } + + txHash, err := common.BroadcastTransactionSync(config.Config.RPC, txBytes) + if err != nil { + common.GetLogger().Error("[layer2] Failed to broadcast transaction: ", err) + return err + } + + common.GetLogger().Info("txHash: ", txHash) + return nil +} diff --git a/tasks/main.go b/tasks/main.go index 80b5782..0bda7ad 100644 --- a/tasks/main.go +++ b/tasks/main.go @@ -16,4 +16,5 @@ func RunTasks(gwCosmosmux *runtime.ServeMux, rpcAddr string, gatewayAddr string) go SyncProposals(gwCosmosmux, gatewayAddr, rpcAddr, false) go CalcSnapshotChecksum(false) go SyncBitcoinWallets() + go StoreL2Status(gwCosmosmux, false) }