diff --git a/cmd/server/main.go b/cmd/server/main.go index 4c29167f..28a5ffbc 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -4,6 +4,7 @@ import ( "AAStarCommunity/EthPaymaster_BackService/config" "AAStarCommunity/EthPaymaster_BackService/envirment" "AAStarCommunity/EthPaymaster_BackService/rpc_server/routers" + "AAStarCommunity/EthPaymaster_BackService/service/dashboard_service" "flag" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" @@ -67,6 +68,7 @@ func initEngine(strategyPath string, basicConfigPath string, secretPath string) } else { logrus.SetLevel(logrus.InfoLevel) } + dashboard_service.Init() logrus.Infof("Environment: %s", envirment.Environment.Name) logrus.Infof("Debugger: %v", envirment.Environment.Debugger) Engine = routers.SetRouters() diff --git a/common/global_const/common_const.go b/common/global_const/common_const.go index 97d2c421..fb5b7675 100644 --- a/common/global_const/common_const.go +++ b/common/global_const/common_const.go @@ -9,6 +9,8 @@ import ( "math/big" ) +type StrategyStatus string + const ( DummyPrivateKeyText = "0a82406dc7fcf16090e05215ff394c7465608dd1a698632471b1eb37b8ece2f7" DummySignature = "0x3054659b5e29460a8f3ac9afc3d5fcbe4b76f92aed454b944e9b29e55d80fde807716530b739540e95cfa4880d69f710a9d45910f2951a227675dc1fb0fdf2c71c" @@ -17,6 +19,10 @@ const ( DummyVerificationGas = 50000 DummyPaymasterPostOpGasLimit = 2000000 DummyPaymasterVerificationGasLimit = 5000000 + + ContextKeyApiMoDel = "api_model" + StrategyStatusDisable StrategyStatus = "disable" + StrategyStatusAchieve StrategyStatus = "enable" ) var ( diff --git a/common/global_const/token.go b/common/global_const/token.go index 69ab4fd6..36f5fb81 100644 --- a/common/global_const/token.go +++ b/common/global_const/token.go @@ -22,8 +22,9 @@ func IsStableToken(token TokenType) bool { } const ( - TokenTypeUSDT TokenType = "USDT" - TokenTypeUSDC TokenType = "USDC" - TokenTypeETH TokenType = "ETH" - TokenTypeOP TokenType = "OP" + TokenTypeUSDT TokenType = "USDT" + TokenTypeUSDC TokenType = "USDC" + TokenTypeETH TokenType = "ETH" + TokenTypeOP TokenType = "OP" + TokenTypeAAStar TokenType = "AAStar" ) diff --git a/common/model/api_key_info.go b/common/model/api_key_info.go new file mode 100644 index 00000000..42b287d8 --- /dev/null +++ b/common/model/api_key_info.go @@ -0,0 +1,9 @@ +package model + +import "golang.org/x/time/rate" + +type ApiKeyModel struct { + Disable bool `gorm:"column:disable;type:bool" json:"disable"` + ApiKey string `gorm:"column:api_key;type:varchar(255)" json:"api_key"` + RateLimit rate.Limit `gorm:"column:rate_limit;type:int" json:"rate_limit"` +} diff --git a/common/model/api_request.go b/common/model/api_request.go index b068f33b..62e16c79 100644 --- a/common/model/api_request.go +++ b/common/model/api_request.go @@ -5,20 +5,20 @@ import ( ) type UserOpRequest struct { - ForceStrategyId string `json:"force_strategy_id"` - ForceNetwork global_const.Network `json:"force_network"` - Erc20Token global_const.TokenType `json:"force_token"` - ForceEntryPointAddress string `json:"force_entrypoint_address"` - UserOp map[string]any `json:"user_operation"` - Extra interface{} `json:"extra"` - EstimateOpGas bool `json:"estimate_op_gas"` - EntryPointVersion global_const.EntrypointVersion `json:"entrypoint_version"` + StrategyCode string `json:"strategy_code"` + Network global_const.Network `json:"network"` + UserPayErc20Token global_const.TokenType `json:"user_pay_erc20_token"` + UserOp map[string]any `json:"user_operation"` + Extra interface{} `json:"extra"` + EstimateOpGas bool `json:"estimate_op_gas"` + EntryPointVersion global_const.EntrypointVersion `json:"entrypoint_version"` } type JsonRpcRequest struct { - JsonRpc string `json:"jsonrpc"` - Method string `json:"method"` - Params []interface{} `json:"params"` - Id int `json:"id"` + JsonRpc string `json:"jsonrpc"` + Method string `json:"method"` + Params []interface{} `json:"params"` + Id int `json:"id"` + Network global_const.Network `json:"-"` } type ClientCredential struct { ApiKey string `json:"apiKey"` diff --git a/common/model/secret_config.go b/common/model/secret_config.go index cf7c7542..5f8a4f81 100644 --- a/common/model/secret_config.go +++ b/common/model/secret_config.go @@ -1,9 +1,24 @@ package model +type DBConfig struct { + Host string `json:"host"` + Port int `json:"port"` + User string `json:"user"` + Password string `json:"password"` + DBName string `json:"db_name"` + TimeZone string `json:"tz"` + SslMode string `json:"ssl_mode"` +} + type SecretConfig struct { PriceOracleApiKey string `json:"price_oracle_api_key"` NetWorkSecretConfigMap map[string]NetWorkSecretConfig `json:"network_secret_configs"` + + ConfigDBConfig DBConfig `json:"config_db_config"` + RelayDBConfig DBConfig `json:"relay_db_config"` + ApiKeyTableName string `json:"api_key_table_name"` + StrategyConfigTableName string `json:"strategy_config_table_name"` } type NetWorkSecretConfig struct { diff --git a/common/model/strategy.go b/common/model/strategy.go index 80f761f3..92aaa487 100644 --- a/common/model/strategy.go +++ b/common/model/strategy.go @@ -8,22 +8,23 @@ import ( ) type Strategy struct { - Id string `json:"id"` - StrategyCode string `json:"strategy_code"` - PaymasterInfo *PaymasterInfo `json:"paymaster_info"` - NetWorkInfo *NetWorkInfo `json:"network_info"` - EntryPointInfo *EntryPointInfo `json:"entrypoint_info"` - Description string `json:"description"` - ExecuteRestriction StrategyExecuteRestriction `json:"execute_restriction"` + Id string `json:"id"` + StrategyCode string `json:"strategy_code"` + PaymasterInfo *PaymasterInfo `json:"paymaster_info"` + NetWorkInfo *NetWorkInfo `json:"network_info"` + EntryPointInfo *EntryPointInfo `json:"entrypoint_info"` + Description string `json:"description"` + ExecuteRestriction *StrategyExecuteRestriction `json:"execute_restriction"` Erc20TokenType global_const.TokenType } type PaymasterInfo struct { - PayMasterAddress *common.Address `json:"paymaster_address"` - PayType global_const.PayType `json:"pay_type"` + PayMasterAddress *common.Address `json:"paymaster_address"` + PayType global_const.PayType `json:"pay_type"` + IsProjectErc20PayEnable bool `json:"is_project_erc20_pay_enable"` } type NetWorkInfo struct { - NetWork global_const.Network `json:"network"` - Token global_const.TokenType `json:"tokens"` + NetWork global_const.Network `json:"network"` + GasToken global_const.TokenType `json:"tokens"` } type EntryPointInfo struct { EntryPointAddress *common.Address `json:"entrypoint_address"` @@ -41,7 +42,7 @@ func (strategy *Strategy) GetNewWork() global_const.Network { } func (strategy *Strategy) GetUseToken() global_const.TokenType { - return strategy.NetWorkInfo.Token + return strategy.NetWorkInfo.GasToken } func (strategy *Strategy) GetPayType() global_const.PayType { return strategy.PaymasterInfo.PayType @@ -49,21 +50,21 @@ func (strategy *Strategy) GetPayType() global_const.PayType { func (strategy *Strategy) GetStrategyEntrypointVersion() global_const.EntrypointVersion { return strategy.EntryPointInfo.EntryPointVersion } -func (strategy Strategy) IsCurrencyPayEnable() bool { +func (strategy *Strategy) IsCurrencyPayEnable() bool { return false } type StrategyExecuteRestriction struct { - BanSenderAddress string `json:"ban_sender_address"` - EffectiveStartTime *big.Int `json:"effective_start_time"` - EffectiveEndTime *big.Int `json:"effective_end_time"` - GlobalMaxUSD int64 `json:"global_max_usd"` - GlobalMaxOpCount int64 `json:"global_max_op_count"` - DayMaxUSD int64 `json:"day_max_usd"` - StartTime int64 `json:"start_time"` - EndTime int64 `json:"end_time"` + BanSenderAddress mapset.Set[string] `json:"ban_sender_address"` + EffectiveStartTime *big.Int `json:"start_time"` + EffectiveEndTime *big.Int `json:"end_time"` + GlobalMaxUSD *big.Float `json:"global_max_usd"` + GlobalMaxOpCount *big.Int `json:"global_max_op_count"` + DayMaxUSD *big.Float `json:"day_max_usd"` AccessProject mapset.Set[string] `json:"access_project"` AccessErc20 mapset.Set[string] `json:"access_erc20"` + ChainIdWhiteList mapset.Set[string] `json:"chain_id_whitelist"` + Status global_const.StrategyStatus } type StrategyValidateConfig struct { diff --git a/config/basic_config.go b/config/basic_config.go index ca6e57dc..c4c5d520 100644 --- a/config/basic_config.go +++ b/config/basic_config.go @@ -133,20 +133,20 @@ func GetChainId(networkParam global_const.Network) string { return networkConfig.ChainId } -func GetPaymasterAddress(network global_const.Network, version global_const.EntrypointVersion) common.Address { +func GetPaymasterAddress(network global_const.Network, version global_const.EntrypointVersion) *common.Address { networkConfig := basicConfig.NetworkConfigMap[network] if version == global_const.EntrypointV07 { - return networkConfig.V07PaymasterAddress + return &networkConfig.V07PaymasterAddress } - return networkConfig.V06PaymasterAddress + return &networkConfig.V06PaymasterAddress } -func GetEntrypointAddress(network global_const.Network, version global_const.EntrypointVersion) common.Address { +func GetEntrypointAddress(network global_const.Network, version global_const.EntrypointVersion) *common.Address { networkConfig := basicConfig.NetworkConfigMap[network] if version == global_const.EntrypointV07 { - return networkConfig.V07EntryPointAddress + return &networkConfig.V07EntryPointAddress } - return networkConfig.V06EntryPointAddress + return &networkConfig.V06EntryPointAddress } @@ -170,8 +170,12 @@ var ( global_const.ScrollMainnet: common.HexToAddress("0x5300000000000000000000000000000000000002"), } Disable1559Chain = mapset.NewSet(global_const.ScrollSepolia, global_const.ScrollMainnet) + PErc20TokenSet = mapset.NewSet(global_const.TokenTypeAAStar) ) +func IsPErc20Token(token global_const.TokenType) bool { + return PErc20TokenSet.Contains(token) +} func IsDisable1559Chain(network global_const.Network) bool { return Disable1559Chain.Contains(network) } diff --git a/config/basic_strategy_config.go b/config/basic_strategy_config.go index 2462970d..8b5a2dbf 100644 --- a/config/basic_strategy_config.go +++ b/config/basic_strategy_config.go @@ -19,9 +19,9 @@ var suitableStrategyMap = make(map[global_const.Network]map[global_const.Entrypo func GetBasicStrategyConfig(strategyCode global_const.BasicStrategyCode) *model.Strategy { strategy := basicStrategyConfig[string(strategyCode)] paymasterAddress := GetPaymasterAddress(strategy.GetNewWork(), strategy.GetStrategyEntrypointVersion()) - strategy.PaymasterInfo.PayMasterAddress = &paymasterAddress + strategy.PaymasterInfo.PayMasterAddress = paymasterAddress entryPointAddress := GetEntrypointAddress(strategy.GetNewWork(), strategy.GetStrategyEntrypointVersion()) - strategy.EntryPointInfo.EntryPointAddress = &entryPointAddress + strategy.EntryPointInfo.EntryPointAddress = entryPointAddress return strategy } @@ -79,7 +79,7 @@ func convertMapToStrategyConfig(data map[string]map[string]any) (map[string]*mod EntryPointVersion: global_const.EntrypointVersion(value["entrypoint_version"].(string)), }, - ExecuteRestriction: model.StrategyExecuteRestriction{ + ExecuteRestriction: &model.StrategyExecuteRestriction{ EffectiveStartTime: effectiveStartTime, EffectiveEndTime: effectiveEndTime, AccessProject: utils.ConvertStringToSet(accessProjectStr, ","), @@ -90,7 +90,7 @@ func convertMapToStrategyConfig(data map[string]map[string]any) (map[string]*mod } if strategy.GetPayType() == global_const.PayTypeERC20 { erc20TokenStr := value["access_erc20"].(string) - strategy.NetWorkInfo.Token = global_const.TokenType(erc20TokenStr) + strategy.NetWorkInfo.GasToken = global_const.TokenType(erc20TokenStr) strategy.ExecuteRestriction.AccessErc20 = utils.ConvertStringToSet(erc20TokenStr, ",") } diff --git a/config/secret_config.go b/config/secret_config.go index 9148b405..98017d11 100644 --- a/config/secret_config.go +++ b/config/secret_config.go @@ -8,6 +8,8 @@ import ( "os" ) +var dsnTemplate = "host=%s port=%v user=%s password=%s dbname=%s TimeZone=%s sslmode=%s" + var secretConfig *model.SecretConfig var signerConfig = make(SignerConfigMap) @@ -42,6 +44,10 @@ func GetNetworkSecretConfig(network global_const.Network) model.NetWorkSecretCon return secretConfig.NetWorkSecretConfigMap[string(network)] } +func CheckNetworkSupport(network global_const.Network) bool { + _, ok := secretConfig.NetWorkSecretConfigMap[string(network)] + return ok +} func GetPriceOracleApiKey() string { return secretConfig.PriceOracleApiKey } @@ -54,3 +60,29 @@ func GetSignerKey(network global_const.Network) string { func GetSigner(network global_const.Network) *global_const.EOA { return signerConfig[network] } +func GetAPIKeyTableName() string { + return secretConfig.ApiKeyTableName +} +func GetStrategyConfigTableName() string { + return secretConfig.StrategyConfigTableName +} +func GetConfigDBDSN() string { + return fmt.Sprintf(dsnTemplate, + secretConfig.ConfigDBConfig.Host, + secretConfig.ConfigDBConfig.Port, + secretConfig.ConfigDBConfig.User, + secretConfig.ConfigDBConfig.Password, + secretConfig.ConfigDBConfig.DBName, + secretConfig.ConfigDBConfig.TimeZone, + secretConfig.ConfigDBConfig.SslMode) +} +func GetRelayDBDSN() string { + return fmt.Sprintf(dsnTemplate, + secretConfig.RelayDBConfig.Host, + secretConfig.RelayDBConfig.Port, + secretConfig.RelayDBConfig.User, + secretConfig.RelayDBConfig.Password, + secretConfig.RelayDBConfig.DBName, + secretConfig.RelayDBConfig.TimeZone, + secretConfig.RelayDBConfig.SslMode) +} diff --git a/docs/docs.go b/docs/docs.go index ab889b9e..c6ae8568 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -61,7 +61,7 @@ const docTemplate = `{ } } }, - "/api/v1/paymaster": { + "/api/v1/paymaster/{network}": { "post": { "security": [ { @@ -76,6 +76,13 @@ const docTemplate = `{ "Paymaster" ], "parameters": [ + { + "type": "string", + "description": "Network", + "name": "network", + "in": "path", + "required": true + }, { "description": "JsonRpcRequest Model", "name": "rpcRequest", diff --git a/docs/swagger.json b/docs/swagger.json index f813c38c..9eede293 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -50,7 +50,7 @@ } } }, - "/api/v1/paymaster": { + "/api/v1/paymaster/{network}": { "post": { "security": [ { @@ -65,6 +65,13 @@ "Paymaster" ], "parameters": [ + { + "type": "string", + "description": "Network", + "name": "network", + "in": "path", + "required": true + }, { "description": "JsonRpcRequest Model", "name": "rpcRequest", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 282e5be6..052a2180 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -48,12 +48,17 @@ paths: description: OK tags: - Healthz - /api/v1/paymaster: + /api/v1/paymaster/{network}: post: consumes: - application/json description: Paymaster JSON-RPC API parameters: + - description: Network + in: path + name: network + required: true + type: string - description: JsonRpcRequest Model in: body name: rpcRequest diff --git a/gas_executor/gas_computor_test.go b/gas_executor/gas_computor_test.go index 26107316..1fd6f008 100644 --- a/gas_executor/gas_computor_test.go +++ b/gas_executor/gas_computor_test.go @@ -7,7 +7,6 @@ import ( "AAStarCommunity/EthPaymaster_BackService/common/user_op" "AAStarCommunity/EthPaymaster_BackService/common/utils" "AAStarCommunity/EthPaymaster_BackService/config" - "AAStarCommunity/EthPaymaster_BackService/service/dashboard_service" "encoding/json" "github.com/sirupsen/logrus" "math/big" @@ -73,7 +72,7 @@ func TestComputeGas(t *testing.T) { { "testEstimateVerificationGasLimit", func(*testing.T) { - strategy := dashboard_service.GetStrategyByCode("Ethereum_Sepolia_v06_verifyPaymaster") + strategy := config.GetBasicStrategyConfig("Ethereum_Sepolia_v06_verifyPaymaster") if strategy == nil { t.Fatal("strategy is nil") } @@ -84,7 +83,7 @@ func TestComputeGas(t *testing.T) { "testScrollGetUserOpEstimateGas", func(*testing.T) { - strategy := dashboard_service.GetStrategyByCode(string(global_const.StrategyCodeScrollSepoliaV06Verify)) + strategy := config.GetBasicStrategyConfig(global_const.StrategyCodeScrollSepoliaV06Verify) testGetUserOpEstimateGas(t, opFor1559NotSupport, strategy) }, diff --git a/go.mod b/go.mod index c31bbc71..cdc2ee6d 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,8 @@ require ( github.com/deckarep/golang-set/v2 v2.6.0 github.com/ethereum/go-ethereum v1.14.3 github.com/gin-contrib/cors v1.7.1 - github.com/gin-gonic/gin v1.9.1 - github.com/go-playground/validator/v10 v10.19.0 + github.com/gin-gonic/gin v1.10.0 + github.com/go-playground/validator/v10 v10.20.0 github.com/mitchellh/mapstructure v1.5.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 @@ -18,21 +18,35 @@ require ( github.com/swaggo/swag v1.16.3 golang.org/x/time v0.5.0 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 + gorm.io/datatypes v1.2.0 + gorm.io/driver/postgres v1.5.7 + gorm.io/gorm v1.25.10 k8s.io/apimachinery v0.29.3 ) require ( github.com/NethermindEth/juno v0.3.1 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/holiman/uint256 v1.2.4 // indirect github.com/huin/goupnp v1.3.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.5 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/compress v1.17.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/test-go/testify v1.1.4 // indirect github.com/x448/float16 v0.8.4 // indirect + gorm.io/driver/mysql v1.4.7 // indirect ) require ( @@ -40,9 +54,7 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/bytedance/sonic v1.11.3 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect - github.com/chenzhuoyu/iasm v0.9.1 // indirect + github.com/bytedance/sonic v1.11.6 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect; indirect(force degrade) @@ -72,7 +84,7 @@ require ( github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/supranational/blst v0.3.11 // indirect @@ -81,16 +93,16 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - golang.org/x/arch v0.7.0 // indirect - golang.org/x/crypto v0.22.0 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.24.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.20.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/go.sum b/go.sum index b9871cf8..bf71db54 100644 --- a/go.sum +++ b/go.sum @@ -22,21 +22,18 @@ github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= -github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= -github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= -github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= @@ -93,8 +90,8 @@ github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= -github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -112,8 +109,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= -github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= @@ -122,6 +121,10 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -159,8 +162,21 @@ github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXei github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -191,8 +207,12 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= +github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= @@ -219,8 +239,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= -github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -298,13 +318,13 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= -golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -318,8 +338,8 @@ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -344,8 +364,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -354,8 +374,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -374,8 +394,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -391,6 +411,19 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco= +gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04= +gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y= +gorm.io/driver/mysql v1.4.7/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc= +gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= +gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= +gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= +gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= +gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= +gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig= +gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/rpc_server/api/v1/paymaster.go b/rpc_server/api/v1/paymaster.go index 18ad6cc7..55ec9418 100644 --- a/rpc_server/api/v1/paymaster.go +++ b/rpc_server/api/v1/paymaster.go @@ -29,8 +29,9 @@ func init() { // @Description Paymaster JSON-RPC API // @Accept json // @Product json +// @param network path string true "Network" // @Param rpcRequest body model.JsonRpcRequest true "JsonRpcRequest Model" -// @Router /api/v1/paymaster [post] +// @Router /api/v1/paymaster/{network} [post] // @Success 200 // @Security JWT func Paymaster(ctx *gin.Context) { @@ -46,6 +47,18 @@ func Paymaster(ctx *gin.Context) { } }() + network := ctx.Param("network") + if network == "" { + errStr := fmt.Sprintf("Request Error [network is empty]") + response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, errStr) + return + } + if !config.CheckNetworkSupport(global_const.Network(network)) { + errStr := fmt.Sprintf("Request Error [network not support]") + 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) @@ -79,14 +92,8 @@ func Paymaster(ctx *gin.Context) { func GetSupportPaymaster() MethodFunctionFunc { return func(ctx *gin.Context, jsonRpcRequest model.JsonRpcRequest) (result interface{}, err error) { - if jsonRpcRequest.Params[0] == nil { - return nil, xerrors.Errorf("Request Error [network is empty]") - } - networkStr, ok := jsonRpcRequest.Params[0].(string) - if !ok { - return nil, xerrors.Errorf("Request Error [network is not string]") - } - paymasterSet, err := config.GetSupportPaymaster(global_const.Network(networkStr)) + + paymasterSet, err := config.GetSupportPaymaster(jsonRpcRequest.Network) if err != nil { return nil, err } @@ -96,14 +103,7 @@ func GetSupportPaymaster() MethodFunctionFunc { func GetSupportEntryPointFunc() MethodFunctionFunc { return func(ctx *gin.Context, jsonRpcRequest model.JsonRpcRequest) (result interface{}, err error) { - if jsonRpcRequest.Params[0] == nil { - return nil, xerrors.Errorf("Request Error [network is empty]") - } - networkStr, ok := jsonRpcRequest.Params[0].(string) - if !ok { - return nil, xerrors.Errorf("Request Error [network is not string]") - } - entryPoints, err := config.GetSupportEntryPoints(global_const.Network(networkStr)) + entryPoints, err := config.GetSupportEntryPoints(jsonRpcRequest.Network) if err != nil { return nil, err } @@ -113,6 +113,7 @@ func GetSupportEntryPointFunc() MethodFunctionFunc { func EstimateUserOpGasFunc() MethodFunctionFunc { return func(ctx *gin.Context, jsonRpcRequest model.JsonRpcRequest) (result interface{}, err error) { request, err := parseTryPayUserOperationParams(jsonRpcRequest.Params) + request.Network = jsonRpcRequest.Network if err != nil { return nil, xerrors.Errorf("parseTryPayUserOperationParams ERROR [%v]", err) } @@ -130,6 +131,7 @@ func EstimateUserOpGasFunc() MethodFunctionFunc { func TryPayUserOperationMethod() MethodFunctionFunc { return func(ctx *gin.Context, jsonRpcRequest model.JsonRpcRequest) (result interface{}, err error) { request, err := parseTryPayUserOperationParams(jsonRpcRequest.Params) + request.Network = jsonRpcRequest.Network logrus.Debug("parseTryPayUserOperationParams result: ", request) if err != nil { @@ -165,13 +167,11 @@ func parseTryPayUserOperationParams(params []interface{}) (*model.UserOpRequest, } extra := extraParam.(map[string]any) if extra["strategy_code"] != nil { - result.ForceStrategyId = extra["strategy_code"].(string) - } - if extra["network"] != nil { - result.ForceNetwork = extra["network"].(global_const.Network) + result.StrategyCode = extra["strategy_code"].(string) } + if extra["token"] != nil { - result.Erc20Token = extra["token"].(global_const.TokenType) + result.UserPayErc20Token = extra["token"].(global_const.TokenType) } if extra["version"] != nil { result.EntryPointVersion = extra["version"].(global_const.EntrypointVersion) @@ -180,10 +180,10 @@ func parseTryPayUserOperationParams(params []interface{}) (*model.UserOpRequest, } func validateUserOpRequest(request *model.UserOpRequest) error { - if request.ForceStrategyId != "" { + if request.StrategyCode != "" { return nil } - if request.ForceNetwork == "" { + if request.Network == "" { return xerrors.Errorf("ForceNetwork is empty") } diff --git a/rpc_server/middlewares/auth.go b/rpc_server/middlewares/auth.go index 472bca3c..40ccf278 100644 --- a/rpc_server/middlewares/auth.go +++ b/rpc_server/middlewares/auth.go @@ -1,9 +1,14 @@ package middlewares import ( + "AAStarCommunity/EthPaymaster_BackService/common/global_const" + "AAStarCommunity/EthPaymaster_BackService/common/model" "AAStarCommunity/EthPaymaster_BackService/envirment" + "AAStarCommunity/EthPaymaster_BackService/service/dashboard_service" jwt "github.com/appleboy/gin-jwt/v2" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + "golang.org/x/xerrors" "time" ) @@ -37,18 +42,50 @@ func AuthHandler() gin.HandlerFunc { if err := c.ShouldBind(&apiKey); err != nil { return "", jwt.ErrMissingLoginValues } - - // TODO: verify if the key is correct + apiModel, err := dashboard_service.GetAPiInfoByApiKey(apiKey.Key) + if err != nil { + return "", err + } + err = CheckAPIKeyAvailable(apiModel) + if err != nil { + return "", err + } return apiKey.Key, nil - - // if incorrect - //return nil, jwt.ErrFailedAuthentication }, + //every Request will be checked Authorizator: func(data interface{}, c *gin.Context) bool { // always return true unless the permission feature started + if data == nil { + logrus.Errorf("Authorizator id is nil") + return false + } + apiKey := data.(string) + if apiKey == "" { + logrus.Errorf("Authorizator id is nil") + return false + } + + apiModel, err := dashboard_service.GetAPiInfoByApiKey(apiKey) + if err != nil { + c.Set("ERROR_REASON", err.Error()) + return false + } + if apiModel == nil { + c.Set("ERROR_REASON", "API Key is not found") + return false + } + err = CheckAPIKeyAvailable(apiModel) + if err != nil { + c.Set("ERROR_REASON", err.Error()) + return false + } + c.Set(global_const.ContextKeyApiMoDel, apiModel) return true }, Unauthorized: func(c *gin.Context, code int, message string) { + if c.GetString("ERROR_REASON") != "" { + message = c.GetString("ERROR_REASON") + } c.JSON(code, gin.H{ "code": code, "message": message, @@ -66,3 +103,9 @@ func AuthHandler() gin.HandlerFunc { return m.MiddlewareFunc() } +func CheckAPIKeyAvailable(apiModel *model.ApiKeyModel) error { + if apiModel.Disable { + return xerrors.Errorf("API Key is disabled") + } + return nil +} diff --git a/rpc_server/middlewares/rate_limit.go b/rpc_server/middlewares/rate_limit.go index 5c1ea090..b8f1c4ad 100644 --- a/rpc_server/middlewares/rate_limit.go +++ b/rpc_server/middlewares/rate_limit.go @@ -1,6 +1,8 @@ package middlewares import ( + "AAStarCommunity/EthPaymaster_BackService/common/global_const" + "AAStarCommunity/EthPaymaster_BackService/common/model" "AAStarCommunity/EthPaymaster_BackService/rpc_server/api/utils" "errors" "github.com/gin-gonic/gin" @@ -19,8 +21,12 @@ var limiter map[string]*rate.Limiter func RateLimiterByApiKeyHandler() gin.HandlerFunc { return func(ctx *gin.Context) { if exists, current := utils.CurrentUser(ctx); exists { - - if limiting(¤t) { + apiKeyModel, _ := ctx.Get(global_const.ContextKeyApiMoDel) + defaultLimit := DefaultLimit + if apiKeyModel != nil { + defaultLimit = apiKeyModel.(*model.ApiKeyModel).RateLimit + } + if limiting(¤t, defaultLimit) { ctx.Next() } else { _ = ctx.AbortWithError(http.StatusTooManyRequests, errors.New("too many requests")) @@ -34,14 +40,13 @@ func clearLimiter(apiKey *string) { delete(limiter, *apiKey) } -func limiting(apiKey *string) bool { +func limiting(apiKey *string, defaultLimit rate.Limit) bool { var l *rate.Limiter if limit, ok := limiter[*apiKey]; ok { l = limit } else { - // TODO: different rate config for each current(apiKey) should get from dashboard service - l = rate.NewLimiter(DefaultLimit, DefaultBurst) + l = rate.NewLimiter(defaultLimit, DefaultBurst) limiter[*apiKey] = l } diff --git a/rpc_server/middlewares/rate_limit_test.go b/rpc_server/middlewares/rate_limit_test.go index e7d86dbc..f46d1063 100644 --- a/rpc_server/middlewares/rate_limit_test.go +++ b/rpc_server/middlewares/rate_limit_test.go @@ -33,10 +33,9 @@ func TestLimit(t *testing.T) { func testRateLimitShouldPreventRequestWhenOverDefaultLimit(t *testing.T) { mockApiKey := "TestingAipKey" - // assuming this for loop taking less than 1 second to finish for i := 0; i < int(DefaultLimit)+5; i++ { - b := limiting(&mockApiKey) + b := limiting(&mockApiKey, DefaultLimit) if i < int(DefaultLimit) { assert.Equal(t, true, b) } else { @@ -55,7 +54,7 @@ func testRateLimiterShouldAllowDefaultLimitPerSecond(t *testing.T) { for x := 1; x <= 2; x++ { for i := 0; i < int(DefaultLimit)+5; i++ { - b := limiting(&mockApiKey) + b := limiting(&mockApiKey, DefaultLimit) if i < int(DefaultLimit) { assert.Equal(t, true, b) } else { diff --git a/rpc_server/routers/routers_map.go b/rpc_server/routers/routers_map.go index 341b397d..de13ba27 100644 --- a/rpc_server/routers/routers_map.go +++ b/rpc_server/routers/routers_map.go @@ -20,5 +20,5 @@ type Path string const ( Auth Path = "api/auth" Healthz Path = "api/healthz" - Paymaster Path = "api/v1/paymaster" + Paymaster Path = "api/v1/paymaster/:network" ) diff --git a/service/dashboard_service/dashboard_service.go b/service/dashboard_service/dashboard_service.go index a49ea4f3..2b5d69f0 100644 --- a/service/dashboard_service/dashboard_service.go +++ b/service/dashboard_service/dashboard_service.go @@ -4,34 +4,205 @@ import ( "AAStarCommunity/EthPaymaster_BackService/common/global_const" "AAStarCommunity/EthPaymaster_BackService/common/model" "AAStarCommunity/EthPaymaster_BackService/config" + "encoding/json" "errors" + mapset "github.com/deckarep/golang-set/v2" + "golang.org/x/time/rate" + "golang.org/x/xerrors" + "gorm.io/datatypes" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "math/big" + "sync" ) -func GetStrategyByCode(strategyCode string) *model.Strategy { - strategy := config.GetBasicStrategyConfig(global_const.BasicStrategyCode(strategyCode)) - paymasterAddress := config.GetPaymasterAddress(strategy.GetNewWork(), strategy.GetStrategyEntrypointVersion()) - strategy.PaymasterInfo.PayMasterAddress = &paymasterAddress - entryPointAddress := config.GetEntrypointAddress(strategy.GetNewWork(), strategy.GetStrategyEntrypointVersion()) - strategy.EntryPointInfo.EntryPointAddress = &entryPointAddress - return strategy +var ( + configDB *gorm.DB + relayDB *gorm.DB + onlyOnce = sync.Once{} +) + +func Init() { + onlyOnce.Do(func() { + configDBDsn := config.GetConfigDBDSN() + relayDBDsn := config.GetRelayDBDSN() + + configDBVar, err := gorm.Open(postgres.Open(configDBDsn), &gorm.Config{}) + if err != nil { + panic(err) + } + configDB = configDBVar + + relayDBVar, err := gorm.Open(postgres.Open(relayDBDsn), &gorm.Config{}) + if err != nil { + panic(err) + } + relayDB = relayDBVar + }) + +} + +type StrategyDBModel struct { + DeletedAt gorm.DeletedAt `gorm:"softDelete:flag" json:"deleted_at"` + Description string `gorm:"type:varchar(500)" json:"description"` + StrategyCode string `gorm:"type:varchar(255)" json:"strategy_code"` + ProjectCode string `gorm:"type:varchar(255)" json:"project_code"` + StrategyName string `gorm:"type:varchar(255)" json:"strategy_name"` + UserId string `gorm:"type:varchar(255)" json:"user_id"` + Status global_const.StrategyStatus `gorm:"type:varchar(20)" json:"status"` + ExecuteRestriction datatypes.JSON `gorm:"type:json" json:"execute_restriction"` + Extra datatypes.JSON `gorm:"type:json" json:"extra"` +} + +func (StrategyDBModel) TableName() string { + return config.GetStrategyConfigTableName() } -func GetSuitableStrategy(entryPointVersion global_const.EntrypointVersion, chain global_const.Network, payType global_const.PayType) (*model.Strategy, error) { +// GetStrategyByCode is Sponsor Type , need GasTank +func GetStrategyByCode(strategyCode string, entryPointVersion global_const.EntrypointVersion, network global_const.Network) (*model.Strategy, error) { if entryPointVersion == "" { entryPointVersion = global_const.EntrypointV06 } - strategy, err := config.GetSuitableStrategy(entryPointVersion, chain, payType) + strategyDbModel := &StrategyDBModel{} + tx := configDB.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) + } else { + return nil, xerrors.Errorf("error when finding strategy: %w", tx.Error) + } + } + strategy, err := convertStrategyDBModelToStrategy(strategyDbModel, entryPointVersion, network) if err != nil { return nil, err } + return strategy, nil +} + +func convertStrategyDBModelToStrategy(strategyDBModel *StrategyDBModel, entryPointVersion global_const.EntrypointVersion, network global_const.Network) (*model.Strategy, error) { + entryPointAddress := config.GetEntrypointAddress(network, entryPointVersion) + + if entryPointAddress == nil { + return nil, errors.New("entryPointAddress not found") + } + paymasterAddress := config.GetPaymasterAddress(network, entryPointVersion) + if paymasterAddress == nil { + return nil, errors.New("paymasterAddress not found") + } + + if strategyDBModel.Status == "" { + strategyDBModel.Status = global_const.StrategyStatusDisable + } + strategyExecuteRestrictionJson := StrategyExecuteRestrictionJson{} + if strategyDBModel.ExecuteRestriction != nil { + eJson, _ := strategyDBModel.ExecuteRestriction.MarshalJSON() + err := json.Unmarshal(eJson, &strategyExecuteRestrictionJson) + if err != nil { + return nil, xerrors.Errorf("error when unmarshal strategyExecuteRestriction: %w", err) + } + + if err != nil { + return nil, xerrors.Errorf("error when unmarshal strategyExecuteRestriction: %w", err) + } + } + strategyExecuteRestriction := &model.StrategyExecuteRestriction{ + EffectiveStartTime: big.NewInt(strategyExecuteRestrictionJson.EffectiveStartTime), + EffectiveEndTime: big.NewInt(strategyExecuteRestrictionJson.EffectiveEndTime), + GlobalMaxUSD: big.NewFloat(strategyExecuteRestrictionJson.GlobalMaxUSD), + GlobalMaxOpCount: big.NewInt(strategyExecuteRestrictionJson.GlobalMaxOpCount), + DayMaxUSD: big.NewFloat(strategyExecuteRestrictionJson.DayMaxUSD), + Status: strategyDBModel.Status, + } + if strategyExecuteRestrictionJson.BanSenderAddress != nil { + strategyExecuteRestriction.BanSenderAddress = mapset.NewSetWithSize[string](len(strategyExecuteRestrictionJson.BanSenderAddress)) + for _, v := range strategyExecuteRestrictionJson.BanSenderAddress { + strategyExecuteRestriction.BanSenderAddress.Add(v) + } + } + if strategyExecuteRestrictionJson.AccessProject != nil { + strategyExecuteRestriction.AccessProject = mapset.NewSetWithSize[string](len(strategyExecuteRestrictionJson.AccessProject)) + for _, v := range strategyExecuteRestrictionJson.AccessProject { + strategyExecuteRestriction.AccessProject.Add(v) + } + } + if strategyExecuteRestrictionJson.ChainIdWhiteList != nil { + strategyExecuteRestriction.ChainIdWhiteList = mapset.NewSetWithSize[string](len(strategyExecuteRestrictionJson.ChainIdWhiteList)) + for _, v := range strategyExecuteRestrictionJson.ChainIdWhiteList { + strategyExecuteRestriction.ChainIdWhiteList.Add(v) + } + } + + return &model.Strategy{ + StrategyCode: strategyDBModel.StrategyCode, + Description: strategyDBModel.Description, + NetWorkInfo: &model.NetWorkInfo{ + NetWork: network, + GasToken: config.GetGasToken(network), + }, + EntryPointInfo: &model.EntryPointInfo{ + EntryPointVersion: entryPointVersion, + EntryPointAddress: config.GetEntrypointAddress(network, entryPointVersion), + }, + PaymasterInfo: &model.PaymasterInfo{ + PayMasterAddress: config.GetPaymasterAddress(network, entryPointVersion), + PayType: global_const.PayTypeVerifying, + IsProjectErc20PayEnable: false, + }, + ExecuteRestriction: strategyExecuteRestriction, + }, nil +} + +type StrategyExecuteRestrictionJson struct { + BanSenderAddress []string `json:"ban_sender_address"` + EffectiveStartTime int64 `json:"start_time"` + EffectiveEndTime int64 `json:"end_time"` + GlobalMaxUSD float64 `json:"global_max_usd"` + GlobalMaxOpCount int64 `json:"global_max_op_count"` + DayMaxUSD float64 `json:"day_max_usd"` + AccessProject []string `json:"access_project"` + AccessErc20 []string `json:"access_erc20"` + ChainIdWhiteList []string `json:"chain_id_whitelist"` +} + +// GetSuitableStrategy get suitable strategy by entryPointVersion, chain, +// +// For Offical StrategyConfig, +func GetSuitableStrategy(entryPointVersion global_const.EntrypointVersion, chain global_const.Network, gasUseToken global_const.TokenType) (*model.Strategy, error) { + if entryPointVersion == "" { + entryPointVersion = global_const.EntrypointV06 + } + gasToken := config.GetGasToken(chain) + entryPointAddress := config.GetEntrypointAddress(chain, entryPointVersion) + paymasterAddress := config.GetPaymasterAddress(chain, entryPointVersion) + payType := global_const.PayTypeVerifying + isPerc20Enable := false + if gasUseToken != "" { + payType = global_const.PayTypeERC20 + if config.IsPErc20Token(gasUseToken) { + isPerc20Enable = true + } + } + + strategy := &model.Strategy{ + NetWorkInfo: &model.NetWorkInfo{ + NetWork: chain, + GasToken: gasToken, + }, + EntryPointInfo: &model.EntryPointInfo{ + EntryPointVersion: entryPointVersion, + EntryPointAddress: entryPointAddress, + }, + PaymasterInfo: &model.PaymasterInfo{ + PayMasterAddress: paymasterAddress, + PayType: payType, + IsProjectErc20PayEnable: isPerc20Enable, + }, + Erc20TokenType: gasUseToken, + } if strategy == nil { return nil, errors.New("strategy not found") } - paymasterAddress := config.GetPaymasterAddress(strategy.GetNewWork(), strategy.GetStrategyEntrypointVersion()) - strategy.PaymasterInfo.PayMasterAddress = &paymasterAddress - entryPointAddress := config.GetEntrypointAddress(strategy.GetNewWork(), strategy.GetStrategyEntrypointVersion()) - strategy.EntryPointInfo.EntryPointAddress = &entryPointAddress return strategy, nil } @@ -50,3 +221,38 @@ func IsPayMasterSupport(address string, chain global_const.Network) bool { return supportPayMasterSet.Contains(address) } + +type ApiKeyDbModel struct { + Disable bool `gorm:"column:disable;type:bool" json:"disable"` + ApiKey string `gorm:"column:api_key;type:varchar(255)" json:"api_key"` + KeyName string `gorm:"column:key_name;type:varchar(255)" json:"key_name"` + DeletedAt gorm.DeletedAt `gorm:"softDelete:flag" json:"deleted_at"` + Extra datatypes.JSON `gorm:"column:extra" json:"extra"` +} + +func (*ApiKeyDbModel) TableName() string { + return config.GetAPIKeyTableName() +} + +func (m *ApiKeyDbModel) GetRateLimit() rate.Limit { + return 10 +} +func convertApiKeyDbModelToApiKeyModel(apiKeyDbModel *ApiKeyDbModel) *model.ApiKeyModel { + return &model.ApiKeyModel{ + Disable: apiKeyDbModel.Disable, + ApiKey: apiKeyDbModel.ApiKey, + RateLimit: 10, + } +} +func GetAPiInfoByApiKey(apiKey string) (*model.ApiKeyModel, error) { + apikeyModel := &ApiKeyDbModel{} + tx := configDB.Where("api_key = ?", apiKey).First(&apikeyModel) + if tx.Error != nil { + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { + return nil, tx.Error + } + return nil, xerrors.Errorf("error when finding apikey: %w", tx.Error) + } + apikeyRes := convertApiKeyDbModelToApiKeyModel(apikeyModel) + return apikeyRes, nil +} diff --git a/service/dashboard_service/dashboard_service_test.go b/service/dashboard_service/dashboard_service_test.go index 8bdb5ee5..0e47bfd6 100644 --- a/service/dashboard_service/dashboard_service_test.go +++ b/service/dashboard_service/dashboard_service_test.go @@ -2,11 +2,53 @@ package dashboard_service import ( "AAStarCommunity/EthPaymaster_BackService/common/global_const" - "fmt" + "AAStarCommunity/EthPaymaster_BackService/config" + "encoding/json" "testing" ) -func TestGetSuitableStrategy(t *testing.T) { - x := global_const.Network("Ethereum") - fmt.Println(x) +func TestDashBoardService(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + //t.Run("TestGetSuitableStrategy", TestGetSuitableStrategy) + config.InitConfig("../../config/basic_strategy_config.json", "../../config/basic_config.json", "../../config/secret_config.json") + Init() + tests := []struct { + name string + test func(t *testing.T) + }{ + { + "TestGetSuitableStrategy", + func(t *testing.T) { + testGetStrategyByCode(t) + }, + }, + { + "TestGetSuitableStrategy", + func(t *testing.T) { + testGetSuitableStrategy(t) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, tt.test) + } +} +func testGetSuitableStrategy(t *testing.T) { + strategy, err := GetSuitableStrategy("", global_const.EthereumSepolia, global_const.TokenTypeOP) + if err != nil { + t.Error(err) + } + jsonByte, _ := json.Marshal(strategy) + t.Logf("Strategy: %s", string(jsonByte)) +} +func testGetStrategyByCode(t *testing.T) { + strategy, err := GetStrategyByCode("basic_arb_strategy__vHUZk", "", global_const.ArbitrumSpeolia) + if err != nil { + t.Error(err) + } + jsonByte, _ := json.Marshal(strategy) + + t.Logf("Strategy: %s", string(jsonByte)) } diff --git a/service/operator/operator_test.go b/service/operator/operator_test.go index 195c45a8..a7dbdadc 100644 --- a/service/operator/operator_test.go +++ b/service/operator/operator_test.go @@ -49,7 +49,7 @@ func TestOperator(t *testing.T) { { "Test_ScrollSepoliaV06Verify_TryPayUserOpExecute", func(t *testing.T) { - mockRequestNotSupport1559.ForceStrategyId = string(global_const.StrategyCodeScrollSepoliaV06Verify) + mockRequestNotSupport1559.StrategyCode = string(global_const.StrategyCodeScrollSepoliaV06Verify) mockRequest := getMockTryPayUserOpRequest() testTryPayUserOpExecute(t, mockRequest) @@ -60,7 +60,7 @@ func TestOperator(t *testing.T) { func(t *testing.T) { mockRequest := getMockTryPayUserOpRequest() - mockRequest.ForceStrategyId = string(global_const.StrategyCodeEthereumSepoliaV06Verify) + mockRequest.StrategyCode = string(global_const.StrategyCodeEthereumSepoliaV06Verify) testTryPayUserOpExecute(t, mockRequest) }, @@ -70,7 +70,7 @@ func TestOperator(t *testing.T) { func(t *testing.T) { mockRequest := getMockTryPayUserOpRequest() - mockRequest.ForceStrategyId = string(global_const.StrategyCodeOptimismSepoliaV06Verify) + mockRequest.StrategyCode = string(global_const.StrategyCodeOptimismSepoliaV06Verify) testTryPayUserOpExecute(t, mockRequest) }, @@ -80,7 +80,7 @@ func TestOperator(t *testing.T) { func(t *testing.T) { mockRequest := getMockTryPayUserOpRequest() - mockRequest.ForceStrategyId = string(global_const.StrategyCodeArbitrumSepoliaV06Verify) + mockRequest.StrategyCode = string(global_const.StrategyCodeArbitrumSepoliaV06Verify) testTryPayUserOpExecute(t, mockRequest) }, @@ -89,7 +89,7 @@ func TestOperator(t *testing.T) { "Test_BaseSepoliaV06Verify_TryPayUserOpExecute", func(t *testing.T) { mockRequest := getMockTryPayUserOpRequest() - mockRequest.ForceStrategyId = string(global_const.StrategyCodeArbitrumSepoliaV06Verify) + mockRequest.StrategyCode = string(global_const.StrategyCodeArbitrumSepoliaV06Verify) testTryPayUserOpExecute(t, mockRequest) }, @@ -99,8 +99,8 @@ func TestOperator(t *testing.T) { func(t *testing.T) { mockRequest := getMockTryPayUserOpRequest() - mockRequest.Erc20Token = global_const.TokenTypeUSDT - mockRequest.ForceStrategyId = string(global_const.StrategyCodeEthereumSepoliaV06Erc20) + mockRequest.UserPayErc20Token = global_const.TokenTypeUSDT + mockRequest.StrategyCode = string(global_const.StrategyCodeEthereumSepoliaV06Erc20) testTryPayUserOpExecute(t, mockRequest) }, }, @@ -109,8 +109,8 @@ func TestOperator(t *testing.T) { func(t *testing.T) { mockRequest := getMockTryPayUserOpRequest() - mockRequest.Erc20Token = global_const.TokenTypeUSDT - mockRequest.ForceStrategyId = string(global_const.StrategyCodeOptimismSepoliaV06Erc20) + mockRequest.UserPayErc20Token = global_const.TokenTypeUSDT + mockRequest.StrategyCode = string(global_const.StrategyCodeOptimismSepoliaV06Erc20) testTryPayUserOpExecute(t, mockRequest) }, }, @@ -118,8 +118,8 @@ func TestOperator(t *testing.T) { "Test_ArbSepoliaV06Erc20_TryPayUserOpExecute", func(t *testing.T) { mockRequest := getMockTryPayUserOpRequest() - mockRequest.Erc20Token = global_const.TokenTypeUSDT - mockRequest.ForceStrategyId = string(global_const.StrategyCodeArbitrumSepoliaV06Erc20) + mockRequest.UserPayErc20Token = global_const.TokenTypeUSDT + mockRequest.StrategyCode = string(global_const.StrategyCodeArbitrumSepoliaV06Erc20) testTryPayUserOpExecute(t, mockRequest) }, }, @@ -209,7 +209,7 @@ func testTryPayUserOpExecute(t *testing.T, request *model.UserOpRequest) { func getMockTryPayUserOpRequest() *model.UserOpRequest { return &model.UserOpRequest{ - ForceStrategyId: "Ethereum_Sepolia_v06_verifyPaymaster", - UserOp: *utils.GenerateMockUservOperation(), + StrategyCode: "Ethereum_Sepolia_v06_verifyPaymaster", + UserOp: *utils.GenerateMockUservOperation(), } } diff --git a/service/operator/try_pay_user_op_execute.go b/service/operator/try_pay_user_op_execute.go index 0981134d..e8fbc539 100644 --- a/service/operator/try_pay_user_op_execute.go +++ b/service/operator/try_pay_user_op_execute.go @@ -49,13 +49,15 @@ func prepareExecute(request *model.UserOpRequest) (*user_op.UserOpInput, *model. return nil, nil, nil, generateErr } - userOp, err := user_op.NewUserOp(&request.UserOp) - if err != nil { + if err := validator_service.ValidateStrategy(strategy, request); err != nil { return nil, nil, nil, err } - if err := validator_service.ValidateStrategy(strategy); err != nil { + + userOp, err := user_op.NewUserOp(&request.UserOp) + if err != nil { return nil, nil, nil, err } + if err := validator_service.ValidateUserOp(userOp, strategy); err != nil { return nil, nil, nil, err } @@ -140,15 +142,19 @@ func postExecute(userOp *user_op.UserOpInput, strategy *model.Strategy, gasRespo func StrategyGenerate(request *model.UserOpRequest) (*model.Strategy, error) { var strategyResult *model.Strategy - if forceStrategyId := request.ForceStrategyId; forceStrategyId != "" { + if forceStrategyId := request.StrategyCode; forceStrategyId != "" { //force strategy - if strategy := dashboard_service.GetStrategyByCode(forceStrategyId); strategy == nil { - return nil, xerrors.Errorf("Not Support Strategy ID: [%w]", forceStrategyId) - } else { - strategyResult = strategy + strategy, err := dashboard_service.GetStrategyByCode(forceStrategyId, request.EntryPointVersion, request.Network) + if err != nil { + return nil, err + } + if strategy == nil { + return nil, xerrors.Errorf("Empty Strategies") } + strategyResult = strategy + } else { - suitableStrategy, err := dashboard_service.GetSuitableStrategy(request.EntryPointVersion, request.ForceNetwork, global_const.PayTypeSuperVerifying) //TODO + suitableStrategy, err := dashboard_service.GetSuitableStrategy(request.EntryPointVersion, request.Network, request.UserPayErc20Token) if err != nil { return nil, err } @@ -158,12 +164,5 @@ func StrategyGenerate(request *model.UserOpRequest) (*model.Strategy, error) { strategyResult = suitableStrategy } - if strategyResult.GetPayType() == global_const.PayTypeERC20 { - if request.Erc20Token == "" { - return nil, xerrors.Errorf("Empty Erc20Token") - } - strategyResult.Erc20TokenType = request.Erc20Token - - } return strategyResult, nil } diff --git a/service/validator_service/basic_validator.go b/service/validator_service/basic_validator.go index 3240355d..578b7457 100644 --- a/service/validator_service/basic_validator.go +++ b/service/validator_service/basic_validator.go @@ -7,15 +7,18 @@ import ( "AAStarCommunity/EthPaymaster_BackService/service/chain_service" "github.com/ethereum/go-ethereum/common" "golang.org/x/xerrors" + "math/big" + "time" ) -func ValidateStrategy(strategy *model.Strategy) error { +func ValidateStrategy(strategy *model.Strategy, request *model.UserOpRequest) error { if strategy == nil { return xerrors.Errorf("empty strategy") } if strategy.GetNewWork() == "" { return xerrors.Errorf("empty strategy network") } + // check Paymaster _, err := chain_service.CheckContractAddressAccess(strategy.GetPaymasterAddress(), strategy.GetNewWork()) if err != nil { @@ -26,7 +29,60 @@ func ValidateStrategy(strategy *model.Strategy) error { if err != nil { return err } + + if strategy.ExecuteRestriction == nil { + return nil + } + if strategy.ExecuteRestriction.Status != global_const.StrategyStatusAchieve { + return xerrors.Errorf("strategy status is not active") + } + curTime := time.Now().Unix() + //check Time + if strategy.ExecuteRestriction.EffectiveStartTime != nil { + if curTime < strategy.ExecuteRestriction.EffectiveStartTime.Int64() { + return xerrors.Errorf("curTime [%s] is OutOff EffectiveStartTime [%s]", curTime, strategy.ExecuteRestriction.EffectiveStartTime.Int64()) + } + } + if strategy.ExecuteRestriction.EffectiveEndTime != nil { + if curTime > strategy.ExecuteRestriction.EffectiveEndTime.Int64() { + return xerrors.Errorf("curTime [%s] is OutOff EffectiveEndTime [%s]", curTime, strategy.ExecuteRestriction.EffectiveEndTime.Int64()) + } + } + if strategy.ExecuteRestriction.AccessErc20 != nil && request.UserPayErc20Token != "" { + if !strategy.ExecuteRestriction.AccessErc20.Contains(string(request.UserPayErc20Token)) { + return xerrors.Errorf("strategy not support erc20 token") + } + } + if strategy.ExecuteRestriction.GlobalMaxUSD != nil || strategy.ExecuteRestriction.GlobalMaxUSD.Sign() != 0 { + curGlobalUse, err := GetStrategyGlobalUse(strategy) + if err != nil { + return err + } + if strategy.ExecuteRestriction.GlobalMaxUSD.Cmp(curGlobalUse) < 0 { + return xerrors.Errorf("strategy global max usd use out of limit") + } + } + if strategy.ExecuteRestriction.DayMaxUSD != nil || strategy.ExecuteRestriction.DayMaxUSD.Sign() != 0 { + curDayUse, err := GetStrategyDayUse(strategy) + if err != nil { + return err + } + if strategy.ExecuteRestriction.DayMaxUSD.Cmp(curDayUse) < 0 { + return xerrors.Errorf("strategy day max usd use out of limit") + } + + } return nil + +} + +func GetStrategyDayUse(strategy *model.Strategy) (*big.Float, error) { + //TODO + return big.NewFloat(0), nil +} +func GetStrategyGlobalUse(strategy *model.Strategy) (*big.Float, error) { + //TODO + return big.NewFloat(0), nil } func ValidateUserOp(userOpParam *user_op.UserOpInput, strategy *model.Strategy) error { @@ -37,6 +93,12 @@ func ValidateUserOp(userOpParam *user_op.UserOpInput, strategy *model.Strategy) if !userOpValue.Nonce.IsInt64() { return xerrors.Errorf("nonce is not in uint64 range") } + if strategy.ExecuteRestriction.BanSenderAddress != nil { + if strategy.ExecuteRestriction.BanSenderAddress.Contains(userOpValue.Sender.String()) { + return xerrors.Errorf("sender is banned") + + } + } //If initCode is not empty, parse its first 20 bytes as a factory address. Record whether the factory is staked, in case the later simulation indicates that it needs to be. If the factory accesses global state, it must be staked - see reputation, throttling and banning section for details. //The verificationGasLimit is sufficiently low (<= MAX_VERIFICATION_GAS) and the preVerificationGas is sufficiently high (enough to pay for the calldata gas cost of serializing the UserOperationV06 plus PRE_VERIFICATION_OVERHEAD_GAS) return nil diff --git a/service/validator_service/validator_test.go b/service/validator_service/validator_test.go new file mode 100644 index 00000000..b619c8d2 --- /dev/null +++ b/service/validator_service/validator_test.go @@ -0,0 +1,52 @@ +package validator_service + +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/dashboard_service" + "testing" +) + +func TestValidatorService(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + config.InitConfig("../../config/basic_strategy_config.json", "../../config/basic_config.json", "../../config/secret_config.json") + dashboard_service.Init() + strategyCode := "basic_arb_strategy__vHUZk" + strategy, err := dashboard_service.GetStrategyByCode(strategyCode, "", global_const.ArbitrumSpeolia) + request := getMockTryPayUserOpRequest(strategyCode, global_const.ArbitrumSpeolia) + if err != nil { + t.Fatalf("GetStrategyByCode error: %v", err) + } + tests := []struct { + name string + test func(t *testing.T) + }{ + { + "TestValidateStrategy", + func(t *testing.T) { + testValidateStrategy(t, strategy, request) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, tt.test) + } +} + +func testValidateStrategy(t *testing.T, strategy *model.Strategy, request *model.UserOpRequest) { + if err := ValidateStrategy(strategy, request); err != nil { + t.Fatalf("ValidateStrategy error: %v", err) + } +} + +func getMockTryPayUserOpRequest(strategyCode string, network global_const.Network) *model.UserOpRequest { + return &model.UserOpRequest{ + StrategyCode: strategyCode, + Network: network, + UserOp: *utils.GenerateMockUservOperation(), + } +}