diff --git a/cmd/soroban-rpc/internal/methods/get_events.go b/cmd/soroban-rpc/internal/methods/get_events.go index dda0fa06..8b4a9439 100644 --- a/cmd/soroban-rpc/internal/methods/get_events.go +++ b/cmd/soroban-rpc/internal/methods/get_events.go @@ -8,7 +8,6 @@ import ( "time" "github.com/creachadair/jrpc2" - "github.com/creachadair/jrpc2/handler" "github.com/stellar/go/strkey" "github.com/stellar/go/support/errors" @@ -429,7 +428,7 @@ func NewGetEventsHandler(eventsStore *events.MemoryStore, maxLimit, defaultLimit maxLimit: maxLimit, defaultLimit: defaultLimit, } - return handler.New(func(ctx context.Context, request GetEventsRequest) (GetEventsResponse, error) { + return NewHandler(func(ctx context.Context, request GetEventsRequest) (GetEventsResponse, error) { return eventsHandler.getEvents(request) }) } diff --git a/cmd/soroban-rpc/internal/methods/get_latest_ledger.go b/cmd/soroban-rpc/internal/methods/get_latest_ledger.go index 2f933304..3617e70d 100644 --- a/cmd/soroban-rpc/internal/methods/get_latest_ledger.go +++ b/cmd/soroban-rpc/internal/methods/get_latest_ledger.go @@ -4,7 +4,6 @@ import ( "context" "github.com/creachadair/jrpc2" - "github.com/creachadair/jrpc2/handler" "github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/db" ) @@ -20,7 +19,7 @@ type GetLatestLedgerResponse struct { // NewGetLatestLedgerHandler returns a JSON RPC handler to retrieve the latest ledger entry from Stellar core. func NewGetLatestLedgerHandler(ledgerEntryReader db.LedgerEntryReader, ledgerReader db.LedgerReader) jrpc2.Handler { - return handler.New(func(ctx context.Context) (GetLatestLedgerResponse, error) { + return NewHandler(func(ctx context.Context) (GetLatestLedgerResponse, error) { tx, err := ledgerEntryReader.NewTx(ctx) if err != nil { return GetLatestLedgerResponse{}, &jrpc2.Error{ diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go index e9e58868..c3f2b617 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/creachadair/jrpc2" - "github.com/creachadair/jrpc2/handler" "github.com/stellar/go/support/log" "github.com/stellar/go/xdr" @@ -40,7 +39,7 @@ const getLedgerEntriesMaxKeys = 200 // NewGetLedgerEntriesHandler returns a JSON RPC handler to retrieve the specified ledger entries from Stellar Core. func NewGetLedgerEntriesHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntryReader) jrpc2.Handler { - return handler.New(func(ctx context.Context, request GetLedgerEntriesRequest) (GetLedgerEntriesResponse, error) { + return NewHandler(func(ctx context.Context, request GetLedgerEntriesRequest) (GetLedgerEntriesResponse, error) { if len(request.Keys) > getLedgerEntriesMaxKeys { return GetLedgerEntriesResponse{}, &jrpc2.Error{ Code: jrpc2.InvalidParams, diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go index e457a3b1..910d2155 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go @@ -5,8 +5,6 @@ import ( "fmt" "github.com/creachadair/jrpc2" - "github.com/creachadair/jrpc2/handler" - "github.com/stellar/go/support/log" "github.com/stellar/go/xdr" @@ -33,7 +31,7 @@ type GetLedgerEntryResponse struct { // Deprecated. use NewGetLedgerEntriesHandler instead. // TODO(https://github.com/stellar/soroban-tools/issues/374) remove after getLedgerEntries is deployed. func NewGetLedgerEntryHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntryReader) jrpc2.Handler { - return handler.New(func(ctx context.Context, request GetLedgerEntryRequest) (GetLedgerEntryResponse, error) { + return NewHandler(func(ctx context.Context, request GetLedgerEntryRequest) (GetLedgerEntryResponse, error) { var key xdr.LedgerKey if err := xdr.SafeUnmarshalBase64(request.Key, &key); err != nil { logger.WithError(err).WithField("request", request). diff --git a/cmd/soroban-rpc/internal/methods/get_network.go b/cmd/soroban-rpc/internal/methods/get_network.go index 5c990c41..f706e7c6 100644 --- a/cmd/soroban-rpc/internal/methods/get_network.go +++ b/cmd/soroban-rpc/internal/methods/get_network.go @@ -4,7 +4,6 @@ import ( "context" "github.com/creachadair/jrpc2" - "github.com/creachadair/jrpc2/handler" "github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/daemon/interfaces" ) @@ -20,7 +19,7 @@ type GetNetworkResponse struct { // NewGetNetworkHandler returns a json rpc handler to for the getNetwork method func NewGetNetworkHandler(daemon interfaces.Daemon, networkPassphrase, friendbotURL string) jrpc2.Handler { coreClient := daemon.CoreClient() - return handler.New(func(ctx context.Context, request GetNetworkRequest) (GetNetworkResponse, error) { + return NewHandler(func(ctx context.Context, request GetNetworkRequest) (GetNetworkResponse, error) { info, err := coreClient.Info(ctx) if err != nil { return GetNetworkResponse{}, (&jrpc2.Error{ diff --git a/cmd/soroban-rpc/internal/methods/get_transaction.go b/cmd/soroban-rpc/internal/methods/get_transaction.go index 8b1846f8..12bc2d10 100644 --- a/cmd/soroban-rpc/internal/methods/get_transaction.go +++ b/cmd/soroban-rpc/internal/methods/get_transaction.go @@ -7,7 +7,6 @@ import ( "fmt" "github.com/creachadair/jrpc2" - "github.com/creachadair/jrpc2/handler" "github.com/stellar/go/xdr" "github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/transactions" @@ -120,7 +119,7 @@ func GetTransaction(getter transactionGetter, request GetTransactionRequest) (Ge // NewGetTransactionHandler returns a get transaction json rpc handler func NewGetTransactionHandler(getter transactionGetter) jrpc2.Handler { - return handler.New(func(ctx context.Context, request GetTransactionRequest) (GetTransactionResponse, error) { + return NewHandler(func(ctx context.Context, request GetTransactionRequest) (GetTransactionResponse, error) { return GetTransaction(getter, request) }) } diff --git a/cmd/soroban-rpc/internal/methods/handler.go b/cmd/soroban-rpc/internal/methods/handler.go new file mode 100644 index 00000000..2dc4d99b --- /dev/null +++ b/cmd/soroban-rpc/internal/methods/handler.go @@ -0,0 +1,17 @@ +package methods + +import ( + "github.com/creachadair/jrpc2" + "github.com/creachadair/jrpc2/handler" +) + +func NewHandler(fn any) jrpc2.Handler { + fi, err := handler.Check(fn) + if err != nil { + panic(err) + } + // explicitly disable array arguments since otherwise we cannot add + // new method arguments without breaking backwards compatibility with clients + fi.AllowArray(false) + return fi.Wrap() +} diff --git a/cmd/soroban-rpc/internal/methods/handler_test.go b/cmd/soroban-rpc/internal/methods/handler_test.go new file mode 100644 index 00000000..564f7f51 --- /dev/null +++ b/cmd/soroban-rpc/internal/methods/handler_test.go @@ -0,0 +1,60 @@ +package methods + +import ( + "context" + "testing" + + "github.com/creachadair/jrpc2" + "github.com/creachadair/jrpc2/handler" + "github.com/stretchr/testify/assert" +) + +type Request struct { + Parameter string `json:"parameter"` +} + +func TestNewHandlerNoArrayParameters(t *testing.T) { + callCount := 0 + f := func(ctx context.Context, request Request) error { + callCount++ + assert.Equal(t, "bar", request.Parameter) + return nil + } + objectRequest := `{ +"jsonrpc": "2.0", +"id": 1, +"method": "foo", +"params": { "parameter": "bar" } +}` + requests, err := jrpc2.ParseRequests([]byte(objectRequest)) + assert.NoError(t, err) + assert.Len(t, requests, 1) + finalObjectRequest := requests[0].ToRequest() + + // object parameters should work with our handlers + customHandler := NewHandler(f) + _, err = customHandler(context.Background(), finalObjectRequest) + assert.NoError(t, err) + assert.Equal(t, 1, callCount) + + arrayRequest := `{ +"jsonrpc": "2.0", +"id": 1, +"method": "foo", +"params": ["bar"] +}` + requests, err = jrpc2.ParseRequests([]byte(arrayRequest)) + assert.NoError(t, err) + assert.Len(t, requests, 1) + finalArrayRequest := requests[0].ToRequest() + + // Array requests should work with the normal handler, but not with our handlers + stdHandler := handler.New(f) + _, err = stdHandler(context.Background(), finalArrayRequest) + assert.NoError(t, err) + assert.Equal(t, 2, callCount) + + _, err = customHandler(context.Background(), finalArrayRequest) + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid parameters") +} diff --git a/cmd/soroban-rpc/internal/methods/health.go b/cmd/soroban-rpc/internal/methods/health.go index ab46d62a..32732fb6 100644 --- a/cmd/soroban-rpc/internal/methods/health.go +++ b/cmd/soroban-rpc/internal/methods/health.go @@ -6,7 +6,6 @@ import ( "time" "github.com/creachadair/jrpc2" - "github.com/creachadair/jrpc2/handler" "github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/transactions" ) @@ -17,7 +16,7 @@ type HealthCheckResult struct { // NewHealthCheck returns a health check json rpc handler func NewHealthCheck(txStore *transactions.MemoryStore, maxHealthyLedgerLatency time.Duration) jrpc2.Handler { - return handler.New(func(ctx context.Context) (HealthCheckResult, error) { + return NewHandler(func(ctx context.Context) (HealthCheckResult, error) { ledgerInfo := txStore.GetLatestLedger() if ledgerInfo.Sequence < 1 { return HealthCheckResult{}, jrpc2.Error{ diff --git a/cmd/soroban-rpc/internal/methods/send_transaction.go b/cmd/soroban-rpc/internal/methods/send_transaction.go index c8404c69..ea033d95 100644 --- a/cmd/soroban-rpc/internal/methods/send_transaction.go +++ b/cmd/soroban-rpc/internal/methods/send_transaction.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "github.com/creachadair/jrpc2" - "github.com/creachadair/jrpc2/handler" "github.com/stellar/go/network" proto "github.com/stellar/go/protocols/stellarcore" "github.com/stellar/go/support/log" @@ -54,7 +53,7 @@ type LatestLedgerStore interface { // NewSendTransactionHandler returns a submit transaction json rpc handler func NewSendTransactionHandler(daemon interfaces.Daemon, logger *log.Entry, store LatestLedgerStore, passphrase string) jrpc2.Handler { submitter := daemon.CoreClient() - return handler.New(func(ctx context.Context, request SendTransactionRequest) (SendTransactionResponse, error) { + return NewHandler(func(ctx context.Context, request SendTransactionRequest) (SendTransactionResponse, error) { var envelope xdr.TransactionEnvelope err := xdr.SafeUnmarshalBase64(request.Transaction, &envelope) if err != nil { diff --git a/cmd/soroban-rpc/internal/methods/simulate_transaction.go b/cmd/soroban-rpc/internal/methods/simulate_transaction.go index 580a5d2f..c0393603 100644 --- a/cmd/soroban-rpc/internal/methods/simulate_transaction.go +++ b/cmd/soroban-rpc/internal/methods/simulate_transaction.go @@ -6,7 +6,6 @@ import ( "fmt" "github.com/creachadair/jrpc2" - "github.com/creachadair/jrpc2/handler" "github.com/stellar/go/support/log" "github.com/stellar/go/xdr" @@ -53,7 +52,7 @@ type PreflightGetter interface { // NewSimulateTransactionHandler returns a json rpc handler to run preflight simulations func NewSimulateTransactionHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntryReader, ledgerReader db.LedgerReader, getter PreflightGetter) jrpc2.Handler { - return handler.New(func(ctx context.Context, request SimulateTransactionRequest) SimulateTransactionResponse { + return NewHandler(func(ctx context.Context, request SimulateTransactionRequest) SimulateTransactionResponse { var txEnvelope xdr.TransactionEnvelope if err := xdr.SafeUnmarshalBase64(request.Transaction, &txEnvelope); err != nil { logger.WithError(err).WithField("request", request). diff --git a/go.mod b/go.mod index b81f67ed..793453c6 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.22.1 require ( github.com/Masterminds/squirrel v1.5.4 github.com/cenkalti/backoff/v4 v4.2.1 - github.com/creachadair/jrpc2 v1.1.2 + github.com/creachadair/jrpc2 v1.2.0 github.com/go-chi/chi v4.1.2+incompatible github.com/go-git/go-git/v5 v5.9.0 github.com/mattn/go-sqlite3 v1.14.17 @@ -74,7 +74,7 @@ require ( github.com/aws/aws-sdk-go v1.45.27 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/creachadair/mds v0.3.0 // indirect + github.com/creachadair/mds v0.13.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect @@ -109,7 +109,7 @@ require ( golang.org/x/crypto v0.16.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/net v0.19.0 // indirect - golang.org/x/sync v0.5.0 // indirect + golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/protobuf v1.32.0 // indirect diff --git a/go.sum b/go.sum index 4cd67af2..a5083ee2 100644 --- a/go.sum +++ b/go.sum @@ -92,10 +92,10 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creachadair/jrpc2 v1.1.2 h1:UOYMipEFYlwd5qmcvs9GZBurn3oXt1UDIX5JLjWWFzo= -github.com/creachadair/jrpc2 v1.1.2/go.mod h1:JcCe2Eny3lIvVwZLm92WXyU+tNUgTBWFCLMsfNkjEGk= -github.com/creachadair/mds v0.3.0 h1:uKbCKVtd3iOKVv3uviOm13fFNfe9qoCXJh1Vo7y3Kr0= -github.com/creachadair/mds v0.3.0/go.mod h1:4vrFYUzTXMJpMBU+OA292I6IUxKWCCfZkgXg+/kBZMo= +github.com/creachadair/jrpc2 v1.2.0 h1:SXr0OgnwM0X18P+HccJP0uT3KGSDk/BCSRlJBvE2bMY= +github.com/creachadair/jrpc2 v1.2.0/go.mod h1:66uKSdr6tR5ZeNvkIjDSbbVUtOv0UhjS/vcd8ECP7Iw= +github.com/creachadair/mds v0.13.4 h1:RgU0MhiVqkzp6/xtNWhK6Pw7tDeaVuGFtA0UA2RBYvY= +github.com/creachadair/mds v0.13.4/go.mod h1:4vrFYUzTXMJpMBU+OA292I6IUxKWCCfZkgXg+/kBZMo= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -543,8 +543,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=