diff --git a/.github/workflows/test-e2e.yaml b/.github/workflows/test-e2e.yaml index babbb3a39..55c82689e 100644 --- a/.github/workflows/test-e2e.yaml +++ b/.github/workflows/test-e2e.yaml @@ -43,8 +43,8 @@ jobs: uses: actions/checkout@v4 with: repository: vechain/thor-e2e-tests - # https://github.com/vechain/thor-e2e-tests/tree/209f6ea9a81a98dc2d5e42bf036d2878c5837036 - ref: 209f6ea9a81a98dc2d5e42bf036d2878c5837036 + # https://github.com/vechain/thor-e2e-tests/tree/8b72bedff11c9e8873d88b6e2dba356d43b56779 + ref: 8b72bedff11c9e8873d88b6e2dba356d43b56779 - name: Download artifact uses: actions/download-artifact@v4 diff --git a/api/accounts/accounts.go b/api/accounts/accounts.go index 54058a160..22698bdbd 100644 --- a/api/accounts/accounts.go +++ b/api/accounts/accounts.go @@ -27,11 +27,12 @@ import ( ) type Accounts struct { - repo *chain.Repository - stater *state.Stater - callGasLimit uint64 - forkConfig thor.ForkConfig - bft bft.Committer + repo *chain.Repository + stater *state.Stater + callGasLimit uint64 + forkConfig thor.ForkConfig + bft bft.Committer + enabledDeprecated bool } func New( @@ -40,6 +41,7 @@ func New( callGasLimit uint64, forkConfig thor.ForkConfig, bft bft.Committer, + enabledDeprecated bool, ) *Accounts { return &Accounts{ repo, @@ -47,6 +49,7 @@ func New( callGasLimit, forkConfig, bft, + enabledDeprecated, } } @@ -168,6 +171,9 @@ func (a *Accounts) handleGetStorage(w http.ResponseWriter, req *http.Request) er } func (a *Accounts) handleCallContract(w http.ResponseWriter, req *http.Request) error { + if !a.enabledDeprecated { + return utils.HTTPError(nil, http.StatusGone) + } callData := &CallData{} if err := utils.ParseJSON(req.Body, &callData); err != nil { return utils.BadRequest(errors.WithMessage(err, "body")) diff --git a/api/accounts/accounts_test.go b/api/accounts/accounts_test.go index 9294723eb..8630bea4b 100644 --- a/api/accounts/accounts_test.go +++ b/api/accounts/accounts_test.go @@ -103,7 +103,7 @@ var ( ) func TestAccount(t *testing.T) { - initAccountServer(t) + initAccountServer(t, true) defer ts.Close() tclient = thorclient.New(ts.URL) @@ -126,6 +126,21 @@ func TestAccount(t *testing.T) { } } +func TestDeprecated(t *testing.T) { + initAccountServer(t, false) + defer ts.Close() + + tclient = thorclient.New(ts.URL) + + body := &accounts.CallData{} + + _, statusCode, _ := tclient.RawHTTPClient().RawHTTPPost("/accounts", body) + assert.Equal(t, http.StatusGone, statusCode, "invalid address") + + _, statusCode, _ = tclient.RawHTTPClient().RawHTTPPost("/accounts/"+contractAddr.String(), body) + assert.Equal(t, http.StatusGone, statusCode, "invalid address") +} + func getAccount(t *testing.T) { _, statusCode, err := tclient.RawHTTPClient().RawHTTPGet("/accounts/" + invalidAddr) require.NoError(t, err) @@ -264,7 +279,7 @@ func getStorageWithNonExistingRevision(t *testing.T) { assert.Equal(t, "revision: leveldb: not found\n", string(res), "revision not found") } -func initAccountServer(t *testing.T) { +func initAccountServer(t *testing.T, enabledDeprecated bool) { thorChain, err := testchain.NewIntegrationTestChain() require.NoError(t, err) @@ -291,7 +306,7 @@ func initAccountServer(t *testing.T) { ) router := mux.NewRouter() - accounts.New(thorChain.Repo(), thorChain.Stater(), uint64(gasLimit), thor.NoFork, thorChain.Engine()). + accounts.New(thorChain.Repo(), thorChain.Stater(), uint64(gasLimit), thor.NoFork, thorChain.Engine(), enabledDeprecated). Mount(router, "/accounts") ts = httptest.NewServer(router) diff --git a/api/api.go b/api/api.go index 38b412a97..0385929ec 100644 --- a/api/api.go +++ b/api/api.go @@ -32,6 +32,21 @@ import ( var logger = log.WithContext("pkg", "api") +type Config struct { + AllowedOrigins string + BacktraceLimit uint32 + CallGasLimit uint64 + PprofOn bool + SkipLogs bool + AllowCustomTracer bool + EnableReqLogger bool + EnableMetrics bool + LogsLimit uint64 + AllowedTracers []string + SoloMode bool + EnableDeprecated bool +} + // New return api router func New( repo *chain.Repository, @@ -41,19 +56,9 @@ func New( bft bft.Committer, nw node.Network, forkConfig thor.ForkConfig, - allowedOrigins string, - backtraceLimit uint32, - callGasLimit uint64, - pprofOn bool, - skipLogs bool, - allowCustomTracer bool, - enableReqLogger bool, - enableMetrics bool, - logsLimit uint64, - allowedTracers []string, - soloMode bool, + config Config, ) (http.HandlerFunc, func()) { - origins := strings.Split(strings.TrimSpace(allowedOrigins), ",") + origins := strings.Split(strings.TrimSpace(config.AllowedOrigins), ",") for i, o := range origins { origins[i] = strings.ToLower(strings.TrimSpace(o)) } @@ -71,27 +76,27 @@ func New( http.Redirect(w, req, "doc/stoplight-ui/", http.StatusTemporaryRedirect) }) - accounts.New(repo, stater, callGasLimit, forkConfig, bft). + accounts.New(repo, stater, config.CallGasLimit, forkConfig, bft, config.EnableDeprecated). Mount(router, "/accounts") - if !skipLogs { - events.New(repo, logDB, logsLimit). + if !config.SkipLogs { + events.New(repo, logDB, config.LogsLimit). Mount(router, "/logs/event") - transfers.New(repo, logDB, logsLimit). + transfers.New(repo, logDB, config.LogsLimit). Mount(router, "/logs/transfer") } blocks.New(repo, bft). Mount(router, "/blocks") transactions.New(repo, txPool). Mount(router, "/transactions") - debug.New(repo, stater, forkConfig, callGasLimit, allowCustomTracer, bft, allowedTracers, soloMode). + debug.New(repo, stater, forkConfig, config.CallGasLimit, config.AllowCustomTracer, bft, config.AllowedTracers, config.SoloMode). Mount(router, "/debug") node.New(nw). Mount(router, "/node") - subs := subscriptions.New(repo, origins, backtraceLimit, txPool) + subs := subscriptions.New(repo, origins, config.BacktraceLimit, txPool, config.EnableDeprecated) subs.Mount(router, "/subscriptions") - if pprofOn { + if config.PprofOn { router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) router.HandleFunc("/debug/pprof/profile", pprof.Profile) router.HandleFunc("/debug/pprof/symbol", pprof.Symbol) @@ -99,7 +104,7 @@ func New( router.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index) } - if enableMetrics { + if config.EnableMetrics { router.Use(metricsMiddleware) } @@ -110,7 +115,7 @@ func New( handlers.ExposedHeaders([]string{"x-genesis-id", "x-thorest-ver"}), )(handler) - if enableReqLogger { + if config.EnableReqLogger { handler = RequestLoggerHandler(handler, logger) } diff --git a/api/metrics_test.go b/api/metrics_test.go index 4e4b6daaf..9b83a08a6 100644 --- a/api/metrics_test.go +++ b/api/metrics_test.go @@ -48,7 +48,7 @@ func TestMetricsMiddleware(t *testing.T) { assert.NotNil(t, err) router := mux.NewRouter() - acc := accounts.New(thorChain.Repo(), thorChain.Stater(), math.MaxUint64, thor.NoFork, thorChain.Engine()) + acc := accounts.New(thorChain.Repo(), thorChain.Stater(), math.MaxUint64, thor.NoFork, thorChain.Engine(), true) acc.Mount(router, "/accounts") router.PathPrefix("/metrics").Handler(metrics.HTTPHandler()) router.Use(metricsMiddleware) @@ -103,7 +103,7 @@ func TestWebsocketMetrics(t *testing.T) { require.NoError(t, err) router := mux.NewRouter() - sub := subscriptions.New(thorChain.Repo(), []string{"*"}, 10, txpool.New(thorChain.Repo(), thorChain.Stater(), txpool.Options{})) + sub := subscriptions.New(thorChain.Repo(), []string{"*"}, 10, txpool.New(thorChain.Repo(), thorChain.Stater(), txpool.Options{}), true) sub.Mount(router, "/subscriptions") router.PathPrefix("/metrics").Handler(metrics.HTTPHandler()) router.Use(metricsMiddleware) diff --git a/api/subscriptions/subscriptions.go b/api/subscriptions/subscriptions.go index 7582da5bb..715a71308 100644 --- a/api/subscriptions/subscriptions.go +++ b/api/subscriptions/subscriptions.go @@ -25,14 +25,15 @@ import ( const txQueueSize = 20 type Subscriptions struct { - backtraceLimit uint32 - repo *chain.Repository - upgrader *websocket.Upgrader - pendingTx *pendingTx - done chan struct{} - wg sync.WaitGroup - beat2Cache *messageCache[Beat2Message] - beatCache *messageCache[BeatMessage] + backtraceLimit uint32 + enabledDeprecated bool + repo *chain.Repository + upgrader *websocket.Upgrader + pendingTx *pendingTx + done chan struct{} + wg sync.WaitGroup + beat2Cache *messageCache[Beat2Message] + beatCache *messageCache[BeatMessage] } type msgReader interface { @@ -50,10 +51,11 @@ const ( pingPeriod = (pongWait * 7) / 10 ) -func New(repo *chain.Repository, allowedOrigins []string, backtraceLimit uint32, txpool *txpool.TxPool) *Subscriptions { +func New(repo *chain.Repository, allowedOrigins []string, backtraceLimit uint32, txpool *txpool.TxPool, enabledDeprecated bool) *Subscriptions { sub := &Subscriptions{ - backtraceLimit: backtraceLimit, - repo: repo, + backtraceLimit: backtraceLimit, + repo: repo, + enabledDeprecated: enabledDeprecated, upgrader: &websocket.Upgrader{ EnableCompression: true, CheckOrigin: func(r *http.Request) bool { @@ -195,6 +197,9 @@ func (s *Subscriptions) handleSubject(w http.ResponseWriter, req *http.Request) return err } case "beat": + if !s.enabledDeprecated { + return utils.HTTPError(nil, http.StatusGone) + } if reader, err = s.handleBeatReader(w, req); err != nil { return err } diff --git a/api/subscriptions/subscriptions_test.go b/api/subscriptions/subscriptions_test.go index 0c0bffe3a..8cfb55f7f 100644 --- a/api/subscriptions/subscriptions_test.go +++ b/api/subscriptions/subscriptions_test.go @@ -36,7 +36,7 @@ var ts *httptest.Server var blocks []*block.Block func TestSubscriptions(t *testing.T) { - initSubscriptionsServer(t) + initSubscriptionsServer(t, true) defer ts.Close() for name, tt := range map[string]func(*testing.T){ @@ -51,6 +51,17 @@ func TestSubscriptions(t *testing.T) { } } +func TestDeprecatedSubscriptions(t *testing.T) { + initSubscriptionsServer(t, false) + defer ts.Close() + + u := url.URL{Scheme: "ws", Host: strings.TrimPrefix(ts.URL, "http://"), Path: "/subscriptions/beat"} + + _, resp, err := websocket.DefaultDialer.Dial(u.String(), nil) + assert.Error(t, err) + assert.Equal(t, http.StatusGone, resp.StatusCode) +} + func testHandleSubjectWithBlock(t *testing.T) { genesisBlock := blocks[0] queryArg := fmt.Sprintf("pos=%s", genesisBlock.Header().ID().String()) @@ -216,7 +227,7 @@ func TestParseAddress(t *testing.T) { assert.Equal(t, expectedAddr, *result) } -func initSubscriptionsServer(t *testing.T) { +func initSubscriptionsServer(t *testing.T, enabledDeprecated bool) { thorChain, err := testchain.NewIntegrationTestChain() require.NoError(t, err) @@ -263,7 +274,7 @@ func initSubscriptionsServer(t *testing.T) { require.NoError(t, err) router := mux.NewRouter() - New(thorChain.Repo(), []string{}, 5, txPool). + New(thorChain.Repo(), []string{}, 5, txPool, enabledDeprecated). Mount(router, "/subscriptions") ts = httptest.NewServer(router) } @@ -319,7 +330,7 @@ func TestSubscriptionsBacktrace(t *testing.T) { require.NoError(t, err) router := mux.NewRouter() - New(thorChain.Repo(), []string{}, 5, txPool).Mount(router, "/subscriptions") + New(thorChain.Repo(), []string{}, 5, txPool, true).Mount(router, "/subscriptions") ts = httptest.NewServer(router) defer ts.Close() diff --git a/cmd/thor/flags.go b/cmd/thor/flags.go index 4b18f22ad..2ce97516e 100644 --- a/cmd/thor/flags.go +++ b/cmd/thor/flags.go @@ -69,6 +69,10 @@ var ( Value: 1000, Usage: "limit the number of logs returned by /logs API", } + apiEnableDeprecatedFlag = cli.BoolFlag{ + Name: "api-enable-deprecated", + Usage: "enable deprecated API endpoints (POST /accounts/{address}, POST /accounts, WS /subscriptions/beat", + } enableAPILogsFlag = cli.BoolFlag{ Name: "enable-api-logs", Usage: "enables API requests logging", diff --git a/cmd/thor/main.go b/cmd/thor/main.go index 4b934bc13..7ddccc08c 100644 --- a/cmd/thor/main.go +++ b/cmd/thor/main.go @@ -11,7 +11,6 @@ import ( "io" "os" "path/filepath" - "strings" "time" "github.com/ethereum/go-ethereum/accounts/keystore" @@ -80,6 +79,7 @@ func main() { apiCallGasLimitFlag, apiBacktraceLimitFlag, apiAllowCustomTracerFlag, + apiEnableDeprecatedFlag, enableAPILogsFlag, apiLogsLimitFlag, verbosityFlag, @@ -115,6 +115,7 @@ func main() { apiCallGasLimitFlag, apiBacktraceLimitFlag, apiAllowCustomTracerFlag, + apiEnableDeprecatedFlag, enableAPILogsFlag, apiLogsLimitFlag, onDemandFlag, @@ -255,17 +256,7 @@ func defaultAction(ctx *cli.Context) error { bftEngine, p2pCommunicator.Communicator(), forkConfig, - ctx.String(apiCorsFlag.Name), - uint32(ctx.Uint64(apiBacktraceLimitFlag.Name)), - ctx.Uint64(apiCallGasLimitFlag.Name), - ctx.Bool(pprofFlag.Name), - skipLogs, - ctx.Bool(apiAllowCustomTracerFlag.Name), - ctx.Bool(enableAPILogsFlag.Name), - ctx.Bool(enableMetricsFlag.Name), - ctx.Uint64(apiLogsLimitFlag.Name), - parseTracerList(strings.TrimSpace(ctx.String(allowedTracersFlag.Name))), - false, + makeAPIConfig(ctx, false), ) defer func() { log.Info("closing API..."); apiCloser() }() @@ -399,6 +390,7 @@ func soloAction(ctx *cli.Context) error { defer func() { log.Info("closing tx pool..."); txPool.Close() }() bftEngine := solo.NewBFTEngine(repo) + apiHandler, apiCloser := api.New( repo, state.NewStater(mainDB), @@ -407,17 +399,7 @@ func soloAction(ctx *cli.Context) error { bftEngine, &solo.Communicator{}, forkConfig, - ctx.String(apiCorsFlag.Name), - uint32(ctx.Uint64(apiBacktraceLimitFlag.Name)), - ctx.Uint64(apiCallGasLimitFlag.Name), - ctx.Bool(pprofFlag.Name), - skipLogs, - ctx.Bool(apiAllowCustomTracerFlag.Name), - ctx.Bool(enableAPILogsFlag.Name), - ctx.Bool(enableMetricsFlag.Name), - ctx.Uint64(apiLogsLimitFlag.Name), - parseTracerList(strings.TrimSpace(ctx.String(allowedTracersFlag.Name))), - true, + makeAPIConfig(ctx, false), ) defer func() { log.Info("closing API..."); apiCloser() }() diff --git a/cmd/thor/utils.go b/cmd/thor/utils.go index 5c6799354..6877a45ee 100644 --- a/cmd/thor/utils.go +++ b/cmd/thor/utils.go @@ -37,6 +37,7 @@ import ( "github.com/mattn/go-isatty" "github.com/mattn/go-tty" "github.com/pkg/errors" + "github.com/vechain/thor/v2/api" "github.com/vechain/thor/v2/api/doc" "github.com/vechain/thor/v2/chain" "github.com/vechain/thor/v2/cmd/thor/node" @@ -274,6 +275,23 @@ func parseGenesisFile(filePath string) (*genesis.Genesis, thor.ForkConfig, error return customGen, forkConfig, nil } +func makeAPIConfig(ctx *cli.Context, soloMode bool) api.Config { + return api.Config{ + AllowedOrigins: ctx.String(apiCorsFlag.Name), + BacktraceLimit: uint32(ctx.Uint64(apiBacktraceLimitFlag.Name)), + CallGasLimit: ctx.Uint64(apiCallGasLimitFlag.Name), + PprofOn: ctx.Bool(pprofFlag.Name), + SkipLogs: ctx.Bool(skipLogsFlag.Name), + AllowCustomTracer: ctx.Bool(apiAllowCustomTracerFlag.Name), + EnableReqLogger: ctx.Bool(enableAPILogsFlag.Name), + EnableMetrics: ctx.Bool(enableMetricsFlag.Name), + LogsLimit: ctx.Uint64(apiLogsLimitFlag.Name), + AllowedTracers: parseTracerList(strings.TrimSpace(ctx.String(allowedTracersFlag.Name))), + EnableDeprecated: ctx.Bool(apiEnableDeprecatedFlag.Name), + SoloMode: soloMode, + } +} + func makeConfigDir(ctx *cli.Context) (string, error) { dir := ctx.String(configDirFlag.Name) if dir == "" { diff --git a/thorclient/api_test.go b/thorclient/api_test.go index e6a0e43be..e8ae49a8a 100644 --- a/thorclient/api_test.go +++ b/thorclient/api_test.go @@ -50,7 +50,7 @@ func initAPIServer(t *testing.T) (*testchain.Chain, *httptest.Server) { router := mux.NewRouter() - accounts.New(thorChain.Repo(), thorChain.Stater(), uint64(gasLimit), thor.NoFork, thorChain.Engine()). + accounts.New(thorChain.Repo(), thorChain.Stater(), uint64(gasLimit), thor.NoFork, thorChain.Engine(), true). Mount(router, "/accounts") blocks.New(thorChain.Repo(), thorChain.Engine()).Mount(router, "/blocks")