diff --git a/cmd/stellar-rpc/internal/.DS_Store b/cmd/stellar-rpc/internal/.DS_Store new file mode 100644 index 00000000..3a7dc716 Binary files /dev/null and b/cmd/stellar-rpc/internal/.DS_Store differ diff --git a/cmd/stellar-rpc/internal/config/.DS_Store b/cmd/stellar-rpc/internal/config/.DS_Store new file mode 100644 index 00000000..6cd01d73 Binary files /dev/null and b/cmd/stellar-rpc/internal/config/.DS_Store differ diff --git a/cmd/stellar-rpc/internal/daemon/interfaces/interfaces.go b/cmd/stellar-rpc/internal/daemon/interfaces/interfaces.go index 7d23d9fb..d2cc4f19 100644 --- a/cmd/stellar-rpc/internal/daemon/interfaces/interfaces.go +++ b/cmd/stellar-rpc/internal/daemon/interfaces/interfaces.go @@ -23,6 +23,7 @@ type Daemon interface { type CoreClient interface { Info(ctx context.Context) (*proto.InfoResponse, error) + SorobanInfo(ctx context.Context) (*proto.SorobanInfoResponse, error) SubmitTransaction(ctx context.Context, txBase64 string) (*proto.TXResponse, error) } diff --git a/cmd/stellar-rpc/internal/daemon/interfaces/noOpDaemon.go b/cmd/stellar-rpc/internal/daemon/interfaces/noOpDaemon.go index 9826b120..8e8d215c 100644 --- a/cmd/stellar-rpc/internal/daemon/interfaces/noOpDaemon.go +++ b/cmd/stellar-rpc/internal/daemon/interfaces/noOpDaemon.go @@ -56,6 +56,10 @@ func (s noOpCoreClient) Info(context.Context) (*proto.InfoResponse, error) { return &proto.InfoResponse{}, nil } +func (s noOpCoreClient) SorobanInfo(context.Context) (*proto.SorobanInfoResponse, error) { + return &proto.SorobanInfoResponse{}, nil +} + func (s noOpCoreClient) SubmitTransaction(context.Context, string) (*proto.TXResponse, error) { return &proto.TXResponse{Status: proto.PreflightStatusOk}, nil } diff --git a/cmd/stellar-rpc/internal/jsonrpc.go b/cmd/stellar-rpc/internal/jsonrpc.go index f7acbf8a..9319e409 100644 --- a/cmd/stellar-rpc/internal/jsonrpc.go +++ b/cmd/stellar-rpc/internal/jsonrpc.go @@ -190,7 +190,8 @@ func NewJSONRPCHandler(cfg *config.Config, params HandlerParams) Handler { underlyingHandler: methods.NewGetNetworkHandler( cfg.NetworkPassphrase, cfg.FriendbotURL, - params.LedgerReader, + params.Daemon.CoreClient(), + cfg.StellarCoreBinaryPath, ), longName: toSnakeCase(protocol.GetNetworkMethodName), queueLimit: cfg.RequestBacklogGetNetworkQueueLimit, diff --git a/cmd/stellar-rpc/internal/methods/get_network.go b/cmd/stellar-rpc/internal/methods/get_network.go index e61bba60..157e8eaa 100644 --- a/cmd/stellar-rpc/internal/methods/get_network.go +++ b/cmd/stellar-rpc/internal/methods/get_network.go @@ -1,22 +1,28 @@ package methods import ( + "bytes" "context" + "os/exec" + "regexp" + "slices" + "strconv" "github.com/creachadair/jrpc2" - "github.com/stellar/stellar-rpc/cmd/stellar-rpc/internal/db" - "github.com/stellar/stellar-rpc/protocol" + protocol "github.com/stellar/go-stellar-sdk/protocols/rpc" + "github.com/stellar/stellar-rpc/cmd/stellar-rpc/internal/daemon/interfaces" ) // NewGetNetworkHandler returns a json rpc handler to for the getNetwork method func NewGetNetworkHandler( networkPassphrase string, friendbotURL string, - ledgerReader db.LedgerReader, + coreClient interfaces.CoreClient, + coreBinaryPath string, ) jrpc2.Handler { return NewHandler(func(ctx context.Context, _ protocol.GetNetworkRequest) (protocol.GetNetworkResponse, error) { - protocolVersion, err := getProtocolVersion(ctx, ledgerReader) + info, err := coreClient.Info(context.Background()) if err != nil { return protocol.GetNetworkResponse{}, &jrpc2.Error{ Code: jrpc2.InternalError, @@ -24,10 +30,64 @@ func NewGetNetworkHandler( } } + versionInfo, err := getSupportedProtocolVersions(ctx, coreBinaryPath) + if err != nil { + return protocol.GetNetworkResponse{}, &jrpc2.Error{ + Code: jrpc2.InternalError, + Message: "failed to get supported protocol versions: " + err.Error(), + } + } + + sorobanInfo, err := coreClient.SorobanInfo(context.Background()) + if err != nil { + return protocol.GetNetworkResponse{}, &jrpc2.Error{ + Code: jrpc2.InternalError, + Message: "failed to get soroban info: " + err.Error(), + } + } + return protocol.GetNetworkResponse{ - FriendbotURL: friendbotURL, - Passphrase: networkPassphrase, - ProtocolVersion: int(protocolVersion), + FriendbotURL: friendbotURL, + Passphrase: networkPassphrase, + Build: info.Info.Build, + ProtocolVersions: versionInfo, + Limits: *sorobanInfo, }, nil }) } + +func getSupportedProtocolVersions(ctx context.Context, coreBinaryPath string) (protocol.GetProtocolVersions, error) { + // Exec `stellar-core version` to get supported protocol versions + cmd := exec.CommandContext(ctx, coreBinaryPath, "version") + var out, stderr bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + return protocol.GetProtocolVersions{}, &jrpc2.Error{ + Code: jrpc2.InternalError, + Message: "failed to exec `stellar-core version`: " + err.Error() + " stderr: " + stderr.String(), + } + } + + // Find all matches for protocol versions across hosts in stdout + outStr := out.String() + re := regexp.MustCompile(`ledger protocol version:\s*(\d+)`) + matches := re.FindAllStringSubmatch(outStr, -1) + + versions := make([]int, len(matches)) + for i, match := range matches { + version, err := strconv.Atoi(match[1]) + if err != nil { + return protocol.GetProtocolVersions{}, &jrpc2.Error{ + Code: jrpc2.InternalError, + Message: "failed to parse protocol version from stellar-core version output: " + err.Error(), + } + } + versions[i] = version + } + return protocol.GetProtocolVersions{ + MinSupportedProtocolVersion: slices.Min(versions), // min supported ledger protocol version + MaxSupportedProtocolVersion: slices.Max(versions), // max supported ledger protocol version + CoreSupportedProtocolVersion: versions[0], // core's protocol version. Should == MaxSupportedProtocolVersion + }, nil +} diff --git a/protocol/get_network.go b/protocol/get_network.go index aef1e2e9..cc04435f 100644 --- a/protocol/get_network.go +++ b/protocol/get_network.go @@ -8,4 +8,5 @@ type GetNetworkResponse struct { FriendbotURL string `json:"friendbotUrl,omitempty"` Passphrase string `json:"passphrase"` ProtocolVersion int `json:"protocolVersion"` + Info string `json:"info,omitempty"` }