diff --git a/external/geth b/external/geth index 37ac2e86a..9992ef494 160000 --- a/external/geth +++ b/external/geth @@ -1 +1 @@ -Subproject commit 37ac2e86ae23513633c5b1b24b2c75a1c65cc0a5 +Subproject commit 9992ef494e2dd9b6a4e0f2512939e84628b0c6b1 diff --git a/infrastructure/nomad/playbooks/templates/jobs/points-service.nomad.j2 b/infrastructure/nomad/playbooks/templates/jobs/points-service.nomad.j2 index 634d82a26..edcd9187e 100644 --- a/infrastructure/nomad/playbooks/templates/jobs/points-service.nomad.j2 +++ b/infrastructure/nomad/playbooks/templates/jobs/points-service.nomad.j2 @@ -48,6 +48,12 @@ job "{{ job.name }}" { task "points-service" { driver = "exec" + {% if profile == 'testnet' or profile == 'mainnet' %} + resources { + memory = 4096 + } + {% endif %} + {% if env != 'devenv' %} artifact { source = "https://primev-infrastructure-artifacts.s3.us-west-2.amazonaws.com/points-service_{{ version }}_Linux_{{ target_system_architecture }}.tar.gz" @@ -64,6 +70,7 @@ job "{{ job.name }}" { POINTS_LOG_TAGS="{{ 'service.name:' + job.name + '-{{ env "NOMAD_ALLOC_INDEX" }}' + ',service.version:' + version }}" POINTS_LOG_LEVEL="{{ job.env.get('log-level', 'info') }}" POINTS_L1_RPC_URL="{{ job.env['l1_rpc_url'] }}" + POINTS_API_AUTH_TOKEN="0123456789" EOH destination = "secrets/.env" env = true diff --git a/tools/go.mod b/tools/go.mod index ba11ab765..d6a48ce79 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -26,6 +26,7 @@ replace github.com/primev/mev-commit/contracts-abi => ../contracts-abi require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20240221180331-f05a6f4403ce.1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -67,5 +68,6 @@ require ( golang.org/x/text v0.17.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/tools/go.sum b/tools/go.sum index ec5178959..04b2dde4e 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -1,5 +1,7 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20240221180331-f05a6f4403ce.1 h1:AmmAwHbvaeOIxDKG2+aTn5C36HjmFIMkrdTp49rp80Q= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20240221180331-f05a6f4403ce.1/go.mod h1:tiTMKD8j6Pd/D2WzREoweufjzaJKHZg35f/VGcZ2v3I= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -200,6 +202,9 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/tools/points-service/api.go b/tools/points-service/api.go index 7e89dbcce..10d283fa4 100644 --- a/tools/points-service/api.go +++ b/tools/points-service/api.go @@ -8,6 +8,7 @@ import ( "log/slog" "net/http" "strconv" + "strings" "github.com/gorilla/mux" ) @@ -16,10 +17,11 @@ type PointsAPI struct { logger *slog.Logger db *sql.DB ps *PointsService + token string } -func NewPointsAPI(logger *slog.Logger, db *sql.DB, ps *PointsService) *PointsAPI { - return &PointsAPI{logger: logger, db: db, ps: ps} +func NewPointsAPI(logger *slog.Logger, db *sql.DB, ps *PointsService, token string) *PointsAPI { + return &PointsAPI{logger: logger, db: db, ps: ps, token: token} } func (p *PointsAPI) StartAPIServer(ctx context.Context, addr string) error { @@ -33,6 +35,9 @@ func (p *PointsAPI) StartAPIServer(ctx context.Context, addr string) error { // Personal API r.HandleFunc("/{address}", p.GetAnyPointsForAddress).Methods("GET") + // Authenticated POST endpoint for manually adding points entry + r.HandleFunc("/admin", p.AddPointsEntry).Methods("POST") + srv := &http.Server{ Addr: addr, Handler: r, @@ -52,6 +57,68 @@ func (p *PointsAPI) StartAPIServer(ctx context.Context, addr string) error { return nil } +func (p *PointsAPI) AddPointsEntry(w http.ResponseWriter, r *http.Request) { + authHeader := r.Header.Get("Authorization") + if authHeader == "" { + http.Error(w, "Authorization header missing", http.StatusUnauthorized) + return + } + + // Expected format "Bearer " + headerToken, found := strings.CutPrefix(authHeader, "Bearer ") + if !found { + http.Error(w, "Invalid Authorization header format", http.StatusUnauthorized) + return + } + + if headerToken != p.token { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Parse JSON request body + var req struct { + Pubkey string `json:"pubkey"` + Adder string `json:"adder"` + Points int64 `json:"points"` + InBlock uint64 `json:"in_block"` + OutBlock *uint64 `json:"out_block,omitempty"` + } + + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + // Basic validation + if req.Pubkey == "" || req.Adder == "" || req.Points < 0 || req.InBlock == 0 { + http.Error(w, "Missing or invalid required fields", http.StatusBadRequest) + return + } + + // Insert record + _, err := p.db.Exec(` + INSERT INTO validator_records ( + pubkey, adder, points_accumulated, opted_in_block, opted_out_block + ) VALUES (?, ?, ?, ?, ?) + `, req.Pubkey, req.Adder, req.Points, req.InBlock, req.OutBlock) + + if err != nil { + p.logger.Error("failed to insert points entry", "error", err) + http.Error(w, "Failed to insert points entry", http.StatusInternalServerError) + return + } + + p.logger.Info("manually added points entry", + "pubkey", req.Pubkey, + "adder", req.Adder, + "points", req.Points, + "in_block", req.InBlock) + + resp := map[string]string{"status": "success"} + writeJSON(w, resp, http.StatusOK) +} + func (p *PointsAPI) HealthCheck(w http.ResponseWriter, r *http.Request) { if !p.ps.IsPointsRoutineRunning() { http.Error(w, "Points routine not running", http.StatusServiceUnavailable) diff --git a/tools/points-service/main.go b/tools/points-service/main.go index dbde234c4..a1ce6f2a8 100644 --- a/tools/points-service/main.go +++ b/tools/points-service/main.go @@ -29,6 +29,7 @@ import ( "github.com/primev/mev-commit/x/contracts/events/publisher" "github.com/primev/mev-commit/x/util" "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2/altsrc" _ "github.com/mattn/go-sqlite3" ) @@ -119,6 +120,13 @@ var ( }, } + optionPointsAPIAuthToken = altsrc.NewStringFlag(&cli.StringFlag{ + Name: "points-api-auth-token", + Usage: "Authorization token for the points service", + EnvVars: []string{"POINTS_API_AUTH_TOKEN"}, + Value: "points-service-api-key", + }) + optionLogLevel = &cli.StringFlag{ Name: "log-level", Usage: "log level to use, options are 'debug', 'info', 'warn', 'error'", @@ -511,6 +519,7 @@ func main() { optionLogFmt, optionLogLevel, optionLogTags, + optionPointsAPIAuthToken, }, Action: func(c *cli.Context) error { logger, err := util.NewLogger( @@ -819,10 +828,10 @@ func main() { } }() - // Start the points accrual routine (once every 24 hours) - StartPointsRoutine(ctx, db, logger, 24*time.Hour, ethClient, ps) + // Start the points accrual routine (once every 30 minutes) + StartPointsRoutine(ctx, db, logger, 30*time.Minute, ethClient, ps) - pointsAPI := NewPointsAPI(logger, db, ps) + pointsAPI := NewPointsAPI(logger, db, ps, c.String(optionPointsAPIAuthToken.Name)) go func() { if err := pointsAPI.StartAPIServer(ctx, ":8080"); err != nil { logger.Error("API server error", "error", err)