diff --git a/common/model/sponsor.go b/common/model/sponsor.go index 1be53091..d93700e4 100644 --- a/common/model/sponsor.go +++ b/common/model/sponsor.go @@ -3,13 +3,13 @@ package model import "math/big" type DepositSponsorRequest struct { - Source string - Amount *big.Float - TxHash string + Source string `json:"source"` + Amount *big.Float `json:"amount"` + TxHash string `json:"tx_hash"` - TxInfo map[string]string - PayUserId string - IsTestNet bool + TxInfo map[string]string `json:"tx_info"` + PayUserId string `json:"pay_user_id"` + IsTestNet bool `json:"is_test_net"` } type WithdrawSponsorRequest struct { Amount *big.Float diff --git a/common/network/ethereum_adaptable_executor_test.go b/common/network/ethereum_adaptable_executor_test.go index 07e01441..2012d5e3 100644 --- a/common/network/ethereum_adaptable_executor_test.go +++ b/common/network/ethereum_adaptable_executor_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/sirupsen/logrus" "testing" + "time" ) func TestEthereumAdaptableExecutor(t *testing.T) { @@ -365,3 +366,8 @@ func testEthereumExecutorClientConnect(t *testing.T, chain global_const.Network) } t.Logf("network %s chainId: %s", chain, chainId.String()) } +func TestTime(t *testing.T) { + start := time.Now() + t.Logf("start time: %v", start.String()) + t.Logf("start time: %v", start.Format("2006-01-02 15:04:05.MST")) +} diff --git a/common/utils/util.go b/common/utils/util.go index 25105bc6..3333ccbd 100644 --- a/common/utils/util.go +++ b/common/utils/util.go @@ -62,6 +62,7 @@ func GenerateMockUservOperation() *map[string]any { return &MockUserOpData } + func ValidateHex(value string) bool { if HexPattern.MatchString(value) { return true diff --git a/go.mod b/go.mod index cdc2ee6d..5d7e7e81 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module AAStarCommunity/EthPaymaster_BackService -go 1.22.3 +go 1.22.4 require ( github.com/NethermindEth/starknet.go v0.7.0 diff --git a/rpc_server/api/health.go b/rpc_server/api/health.go index 4dc6680f..0487013a 100644 --- a/rpc_server/api/health.go +++ b/rpc_server/api/health.go @@ -4,7 +4,6 @@ import ( "AAStarCommunity/EthPaymaster_BackService/common/model" "AAStarCommunity/EthPaymaster_BackService/envirment" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" "time" ) @@ -17,7 +16,6 @@ import ( // @Success 200 func Healthz(c *gin.Context) { response := model.GetResponse() - logrus.Debug("In the Healthz") response.WithDataSuccess(c, gin.H{ "hello": "Eth Paymaster", "environment": envirment.Environment.Name, diff --git a/rpc_server/api/v1/paymaster.go b/rpc_server/api/v1/paymaster.go index 97c5d8bb..af329106 100644 --- a/rpc_server/api/v1/paymaster.go +++ b/rpc_server/api/v1/paymaster.go @@ -3,7 +3,6 @@ package v1 import ( "AAStarCommunity/EthPaymaster_BackService/common/global_const" "AAStarCommunity/EthPaymaster_BackService/common/model" - "AAStarCommunity/EthPaymaster_BackService/common/utils" "AAStarCommunity/EthPaymaster_BackService/config" "AAStarCommunity/EthPaymaster_BackService/service/operator" "fmt" @@ -38,16 +37,17 @@ func Paymaster(ctx *gin.Context) { jsonRpcRequest := model.JsonRpcRequest{} response := model.GetResponse() - - defer func() { - if r := recover(); r != nil { - errInfo := fmt.Sprintf("[panic]: err : [%v] , stack :[%v]", r, utils.GetCurrentGoroutineStack()) - logrus.Error(errInfo) - response.SetHttpCode(http.StatusInternalServerError).FailCode(ctx, http.StatusInternalServerError, fmt.Sprintf("%v", r)) - } - - }() + // + //defer func() { + // if r := recover(); r != nil { + // errInfo := fmt.Sprintf("[panic]: err : [%v] , stack :[%v]", r, utils.GetCurrentGoroutineStack()) + // logrus.Error(errInfo) + // response.SetHttpCode(http.StatusInternalServerError).FailCode(ctx, http.StatusInternalServerError, fmt.Sprintf("%v", r)) + // } + // + //}() network := ctx.Param("network") + logrus.Debugf("Paymaster network: %s", network) if network == "" { errStr := fmt.Sprintf("Request Error [network is empty]") response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, errStr) @@ -58,13 +58,13 @@ func Paymaster(ctx *gin.Context) { response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, errStr) return } - jsonRpcRequest.Network = global_const.Network(network) if err := ctx.ShouldBindJSON(&jsonRpcRequest); err != nil { - errStr := fmt.Sprintf("Request Error [%v]", err) + errStr := fmt.Sprintf("Parse Request Error [%v]", err) response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, errStr) return } + jsonRpcRequest.Network = global_const.Network(network) method := jsonRpcRequest.Method if method == "" { errStr := fmt.Sprintf("Request Error [method is empty]") diff --git a/rpc_server/api/v1/sponsor.go b/rpc_server/api/v1/sponsor.go index 35c28ebb..e64071e9 100644 --- a/rpc_server/api/v1/sponsor.go +++ b/rpc_server/api/v1/sponsor.go @@ -7,11 +7,16 @@ import ( "errors" "fmt" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" "gorm.io/gorm" "net/http" "strconv" ) +var sourcePublicKeyMap = map[string]string{ + "Dashboard": "0x17EE97b5F4Ab8a4b2CfEcfb42b66718F15557687", +} + // DepositSponsor // @Tags Sponsor // @Description Deposit Sponsor @@ -29,6 +34,13 @@ func DepositSponsor(ctx *gin.Context) { response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, errStr) return } + signerAddress, ok := sourcePublicKeyMap["Dashboard"] + if !ok { + response.SetHttpCode(http.StatusInternalServerError).FailCode(ctx, http.StatusInternalServerError, "Invalid Source") + return + } + logrus.Debugf("Signer Address [%v]", signerAddress) + //TODO Add Signature Verification result, err := sponsor_manager.DepositSponsor(&request) if err != nil { diff --git a/rpc_server/middlewares/logger.go b/rpc_server/middlewares/logger.go index ef5a96b0..2da6b2fa 100644 --- a/rpc_server/middlewares/logger.go +++ b/rpc_server/middlewares/logger.go @@ -6,5 +6,7 @@ import ( // LogHandler log handler func LogHandler() gin.HandlerFunc { - return gin.Logger() + return gin.LoggerWithConfig(gin.LoggerConfig{ + SkipPaths: []string{"/api/healthz"}, + }) } diff --git a/rpc_server/middlewares/pv_mertics.go b/rpc_server/middlewares/pv_mertics.go new file mode 100644 index 00000000..ad60b112 --- /dev/null +++ b/rpc_server/middlewares/pv_mertics.go @@ -0,0 +1,106 @@ +package middlewares + +import ( + "AAStarCommunity/EthPaymaster_BackService/common/global_const" + "AAStarCommunity/EthPaymaster_BackService/common/model" + "AAStarCommunity/EthPaymaster_BackService/service/dashboard_service" + "bytes" + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + "io" + "net/http" + "strings" + "time" +) + +type PayMasterParam struct { + ApiUserId int64 + ApiKey string + Method string + SendTime string + Latency time.Duration + RequestBody string + ResponseBody string + NetWork string + Status int +} + +func PvMetrics() gin.HandlerFunc { + return func(c *gin.Context) { + path := c.Request.URL.Path + if strings.HasPrefix(path, "/api/v1/paymaster/") { + responseWriter := &CustomResponseWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer} + c.Writer = responseWriter + start := time.Now() + // get Request Body + requestBodyBytes, err := io.ReadAll(c.Request.Body) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to read request body"}) + c.Abort() + return + } + //Restore the request body to Request.Body for use by subsequent handlers. + c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBodyBytes)) + + c.Next() + + request := model.JsonRpcRequest{} + _ = json.Unmarshal(requestBodyBytes, &request) + requestJson, _ := json.Marshal(request) + requestBodyStr := string(requestJson) + + responseWriter.body.Bytes() + responseBody := responseWriter.body.String() + endEnd := time.Now() + network := c.Param("network") + metricsParam := PayMasterParam{ + NetWork: network, + Method: request.Method, + SendTime: start.Format("2006-01-02 15:04:05.MST"), + Latency: endEnd.Sub(start), + Status: c.Writer.Status(), + RequestBody: requestBodyStr, + ResponseBody: responseBody, + } + apiKeyContext, exist := c.Get(global_const.ContextKeyApiMoDel) + if exist { + apiKeyModel := apiKeyContext.(*model.ApiKeyModel) + metricsParam.ApiKey = apiKeyModel.ApiKey + metricsParam.ApiUserId = apiKeyModel.UserId + } + metricsPaymaster(c, metricsParam) + } else { + return + } + + } +} + +type CustomResponseWriter struct { + gin.ResponseWriter + body *bytes.Buffer +} + +func (w *CustomResponseWriter) Write(b []byte) (int, error) { + w.body.Write(b) + return w.ResponseWriter.Write(b) +} +func metricsPaymaster(c *gin.Context, metricsParam PayMasterParam) { + + recallModel := dashboard_service.PaymasterRecallLogDbModel{ + ProjectApikey: metricsParam.ApiKey, + ProjectUserId: metricsParam.ApiUserId, + PaymasterMethod: metricsParam.Method, + SendTime: metricsParam.SendTime, + Latency: int64(metricsParam.Latency), + RequestBody: metricsParam.RequestBody, + ResponseBody: metricsParam.ResponseBody, + Status: metricsParam.Status, + NetWork: metricsParam.NetWork, + } + err := dashboard_service.CreatePaymasterCall(&recallModel) + if err != nil { + logrus.Error("CreatePaymasterCall error:", err) + } +} diff --git a/rpc_server/middlewares/recovery.go b/rpc_server/middlewares/recovery.go index b9a9d472..0c20d616 100644 --- a/rpc_server/middlewares/recovery.go +++ b/rpc_server/middlewares/recovery.go @@ -2,10 +2,11 @@ package middlewares import ( "AAStarCommunity/EthPaymaster_BackService/common/model" - "AAStarCommunity/EthPaymaster_BackService/envirment" + "AAStarCommunity/EthPaymaster_BackService/common/utils" "errors" "fmt" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" "net/http" "strings" ) @@ -14,11 +15,11 @@ import ( func GenericRecoveryHandler() gin.HandlerFunc { DefaultErrorWriter := &PanicExceptionRecord{} return gin.RecoveryWithWriter(DefaultErrorWriter, func(c *gin.Context, err interface{}) { - errStr := "" - if envirment.Environment.Debugger { - errStr = fmt.Sprintf("%v", err) - } - model.GetResponse().SetHttpCode(http.StatusInternalServerError).FailCode(c, http.StatusInternalServerError, errStr) + errInfo := fmt.Sprintf("[panic]: err : [%v] , stack :[%v]", err, utils.GetCurrentGoroutineStack()) + logrus.Error(errInfo) + logrus.Errorf("%v", errInfo) + returnError := fmt.Sprintf("%v", err) + model.GetResponse().SetHttpCode(http.StatusInternalServerError).FailCode(c, http.StatusInternalServerError, returnError) }) } diff --git a/rpc_server/routers/boot.go b/rpc_server/routers/boot.go index 0d2fd18e..aa33bd97 100644 --- a/rpc_server/routers/boot.go +++ b/rpc_server/routers/boot.go @@ -45,9 +45,10 @@ func SetRouters() (routers *gin.Engine) { func buildRoute(routers *gin.Engine) { // build http routers and middleware routers.Use(middlewares.GenericRecoveryHandler()) - if envirment.Environment.IsDevelopment() { - routers.Use(middlewares.LogHandler()) - } + + routers.Use(middlewares.LogHandler()) + + routers.Use(middlewares.PvMetrics()) routers.Use(middlewares.CorsHandler()) //build the routers not need api access like auth or Traffic limit buildRouters(routers, PublicRouterMaps) diff --git a/service/dashboard_service/dashboard_service.go b/service/dashboard_service/dashboard_service.go index 615a00d1..2da6b72d 100644 --- a/service/dashboard_service/dashboard_service.go +++ b/service/dashboard_service/dashboard_service.go @@ -17,8 +17,8 @@ import ( ) var ( - configDB *gorm.DB - onlyOnce = sync.Once{} + dashBoardDb *gorm.DB + onlyOnce = sync.Once{} ) func Init() { @@ -29,8 +29,7 @@ func Init() { if err != nil { panic(err) } - configDB = configDBVar - + dashBoardDb = configDBVar }) } @@ -58,7 +57,7 @@ func GetStrategyByCode(strategyCode string, entryPointVersion global_const.Entry entryPointVersion = global_const.EntrypointV06 } strategyDbModel := &StrategyDBModel{} - tx := configDB.Where("strategy_code = ?", strategyCode).First(&strategyDbModel) + tx := dashBoardDb.Where("strategy_code = ?", strategyCode).First(&strategyDbModel) if tx.Error != nil { if errors.Is(tx.Error, gorm.ErrRecordNotFound) { return nil, xerrors.Errorf("strategy not found: %w", tx.Error) @@ -242,7 +241,7 @@ func convertApiKeyDbModelToApiKeyModel(apiKeyDbModel *ApiKeyDbModel) *model.ApiK } func GetAPiInfoByApiKey(apiKey string) (*model.ApiKeyModel, error) { apikeyModel := &ApiKeyDbModel{} - tx := configDB.Where("api_key = ?", apiKey).First(&apikeyModel) + tx := dashBoardDb.Where("api_key = ?", apiKey).First(&apikeyModel) if tx.Error != nil { if errors.Is(tx.Error, gorm.ErrRecordNotFound) { return nil, tx.Error @@ -252,3 +251,24 @@ func GetAPiInfoByApiKey(apiKey string) (*model.ApiKeyModel, error) { apikeyRes := convertApiKeyDbModelToApiKeyModel(apikeyModel) return apikeyRes, nil } + +type PaymasterRecallLogDbModel struct { + model.BaseData + ProjectUserId int64 `gorm:"column:project_user_id;type:integer" json:"project_user_id"` + ProjectApikey string `gorm:"column:project_apikey;type:varchar(255)" json:"project_apikey"` + PaymasterMethod string `gorm:"column:paymaster_method;type:varchar(25)" json:"paymaster_method"` + SendTime string `gorm:"column:send_time;type:varchar(50)" json:"send_time"` + Latency int64 `gorm:"column:latency;type:integer" json:"latency"` + RequestBody string `gorm:"column:request_body;type:varchar(500)" json:"request_body"` + ResponseBody string `gorm:"column:response_body;type:varchar(1000)" json:"response_body"` + NetWork string `gorm:"column:network;type:varchar(25)" json:"network"` + Status int `gorm:"column:status;type:integer" json:"status"` + Extra datatypes.JSON `gorm:"column:extra" json:"extra"` +} + +func (*PaymasterRecallLogDbModel) TableName() string { + return "paymaster_recall_log" +} +func CreatePaymasterCall(recallModel *PaymasterRecallLogDbModel) error { + return dashBoardDb.Create(recallModel).Error +} diff --git a/sponsor_manager/sponsor_balance_repository.go b/sponsor_manager/sponsor_balance_repository.go index 1f87f4e0..dd2adfee 100644 --- a/sponsor_manager/sponsor_balance_repository.go +++ b/sponsor_manager/sponsor_balance_repository.go @@ -25,6 +25,7 @@ func CreateSponsorBalance(balanceModel *UserSponsorBalanceDBModel) error { return relayDB.Create(balanceModel).Error } func FindUserSponsorBalance(userId string, isTestNet bool) (balanceModel *UserSponsorBalanceDBModel, err error) { + balanceModel = &UserSponsorBalanceDBModel{} tx := relayDB.Where("pay_user_id = ?", userId).Where("is_test_net = ?", isTestNet).First(balanceModel) if tx.Error != nil {