diff --git a/cmd/cartesi-rollups-cli/root/inspect/inspect.go b/cmd/cartesi-rollups-cli/root/inspect/inspect.go index 90e09fb6d..8805e6e09 100644 --- a/cmd/cartesi-rollups-cli/root/inspect/inspect.go +++ b/cmd/cartesi-rollups-cli/root/inspect/inspect.go @@ -39,18 +39,15 @@ cartesi-rollups-cli inspect echo-dapp 0x6869 --hex # Reads payload from stdin: echo -n "hi" | cartesi-rollups-cli inspect echo-dapp` -var ( - isHex bool - inspectEndpoint string -) +var isHex bool func init() { Cmd.Flags().BoolVarP(&isHex, "hex", "x", false, "Force interpretation of payload as hex.") - Cmd.Flags().StringVar(&inspectEndpoint, "inspect-endpoint", "http://localhost:10012/", - "address used to connect to the inspect api") - cobra.CheckErr(viper.BindPFlag(config.INSPECT_ADDRESS, Cmd.Flags().Lookup("inspect-endpoint"))) + Cmd.Flags().String("inspect-url", "http://localhost:10012/", + "address used to connect to the inspect API") + cobra.CheckErr(viper.BindPFlag(config.INSPECT_URL, Cmd.Flags().Lookup("inspect-url"))) origHelpFunc := Cmd.HelpFunc() Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { @@ -96,10 +93,10 @@ func run(cmd *cobra.Command, args []string) { nameOrAddress, err := config.ToApplicationNameOrAddressFromString(args[0]) cobra.CheckErr(err) - endpoint, err := config.GetInspectAddress() + url, err := config.GetInspectUrl() cobra.CheckErr(err) - client, err := inspectclient.NewClient(endpoint) + client, err := inspectclient.NewClient(url) cobra.CheckErr(err) payload, err := resolvePayload(args) diff --git a/cmd/cartesi-rollups-cli/root/read/commitments/commitments.go b/cmd/cartesi-rollups-cli/root/read/commitments/commitments.go index feda859b1..80f3bd0f5 100644 --- a/cmd/cartesi-rollups-cli/root/read/commitments/commitments.go +++ b/cmd/cartesi-rollups-cli/root/read/commitments/commitments.go @@ -5,67 +5,69 @@ package commitments import ( "encoding/json" - "errors" "fmt" + "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/service" "github.com/cartesi/rollups-node/internal/config" "github.com/cartesi/rollups-node/internal/jsonrpc" - "github.com/cartesi/rollups-node/internal/model" - "github.com/cartesi/rollups-node/internal/repository" - "github.com/cartesi/rollups-node/internal/repository/factory" "github.com/spf13/cobra" ) var Cmd = &cobra.Command{ - Use: "commitments [application-name-or-address] [epoch-index] [tournament-address] [commitment-hash]", + Use: "commitments [epoch index] [tournament address] [commitment]", Short: "Reads commitments", Example: examples, - Args: cobra.RangeArgs(1, 4), // nolint: mnd + Args: cobra.RangeArgs(1, 4), //nolint: mnd Run: run, Long: ` +Arguments: + application name or address + [epoch index] decimal or hex encoded + [tournament address] hex encoded + [commitment] hex encoded + Supported Environment Variables: CARTESI_DATABASE_CONNECTION Database connection string`, } -const examples = `# Read all commitments: -cartesi-rollups-cli read commitments echo-dapp +const examples = `# Read specific commitment: +cartesi-rollups-cli read commitments echo-dapp 10 0x0073a8637d98649717bdc02ecb439c80aa8a10d0 0xdb99c9cdb2e2070a4e4e633c2e6874648dfe3971d14da843465b3d950df3dd19 -# Read specific commitment by epoch, tournament and commitment hash: -cartesi-rollups-cli read commitments echo-dapp 1 0xa2835312696afa86c969e40831857dbb1412627f 0x1b7087e9580fb7946f37a40ced7ffeded336da790e00cc88e5f4e8e25301546a +# Read all commitments: +cartesi-rollups-cli read commitments echo-dapp -# Read commitments filtered by epoch: -cartesi-rollups-cli read commitments echo-dapp --epoch-index 1 +# Read all commitments with filter: +cartesi-rollups-cli read commitments echo-dapp --epoch-index 10 --tournament-address 0x0073a8637d98649717bdc02ecb439c80aa8a10d0 -# Read commitments with pagination: -cartesi-rollups-cli read commitments echo-dapp --limit 10 --offset 20` - -func init() { - origHelpFunc := Cmd.HelpFunc() - Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { - command.Flags().Lookup("verbose").Hidden = false - command.Flags().Lookup("database-connection").Hidden = false - origHelpFunc(command, strings) - }) -} +# Read all commitments with pagination: +cartesi-rollups-cli read commitments echo-dapp --limit 10 --offset 10 +` var ( - epochIndex uint64 + epochIndex string tournamentAddress string limit uint64 offset uint64 ) func init() { - Cmd.Flags().Uint64Var(&epochIndex, "epoch-index", 0, + Cmd.Flags().StringVar(&epochIndex, "epoch-index", "", "Filter commitments by epoch index (decimal or hex encoded)") Cmd.Flags().StringVar(&tournamentAddress, "tournament-address", "", "Filter commitments by tournament address (hex encoded)") - Cmd.Flags().Uint64Var(&limit, "limit", 50, // nolint: mnd + Cmd.Flags().Uint64Var(&limit, "limit", 50, //nolint: mnd "Maximum number of commitments to return") Cmd.Flags().Uint64Var(&offset, "offset", 0, "Starting point for the list of commitments") + origHelpFunc := Cmd.HelpFunc() + Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { + command.Flags().Lookup("verbose").Hidden = false + command.Flags().Lookup("database-connection").Hidden = false + origHelpFunc(command, strings) + }) + Cmd.PreRunE = func(cmd *cobra.Command, args []string) error { if limit > jsonrpc.LIST_ITEM_LIMIT { return fmt.Errorf("limit cannot exceed %d", jsonrpc.LIST_ITEM_LIMIT) @@ -79,106 +81,42 @@ func init() { func run(cmd *cobra.Command, args []string) { ctx := cmd.Context() - nameOrAddress, err := config.ToApplicationNameOrAddressFromString(args[0]) + readServ, err := service.CreateReadService(ctx, cmd) cobra.CheckErr(err) + defer readServ.Close() - dsn, err := config.GetDatabaseConnection() - cobra.CheckErr(err) - - repo, err := factory.NewRepositoryFromConnectionString(ctx, dsn.String()) - cobra.CheckErr(err) - defer repo.Close() - - var result []byte - if len(args) == 4 { // nolint: mnd - // Get a specific commitment by epoch, tournament and commitment - - epochIndex, err := config.ToUint64FromDecimalOrHexString(args[1]) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid value for epoch-index: %w", err)) - } - - // TODO: [maia] check out if the following check is necessary, because - // such conversion is already done in the 'repo.GetCommitment' function, - // but it doesn't seem to check for invalid addresses. - tournamentAddressHex := args[2] - _, err = config.ToAddressFromString(tournamentAddressHex) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid tournament address: %w", err)) - } - commitmentHashHex := args[3] - _, err = config.ToHashFromString(commitmentHashHex) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid commitment hash: %w", err)) - } + var result json.RawMessage + if len(args) >= 4 { + var params jsonrpc.GetCommitmentParams + params.Application = args[0] + params.EpochIndex = config.AsHexString(args[1]) + params.TournamentAddress = args[2] + params.Commitment = args[3] - commitment, err := repo.GetCommitment(ctx, nameOrAddress, epochIndex, tournamentAddressHex, commitmentHashHex) - cobra.CheckErr(err) - if commitment == nil { - cobra.CheckErr(errors.New("not found")) - } - - // Format response to match JSON-RPC API - response := struct { - Data *model.Commitment `json:"data"` - }{ - Data: commitment, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.GetCommitment(ctx, params) } else { - // Create filter based on flags - filter := repository.CommitmentFilter{} + var params jsonrpc.ListCommitmentsParams + params.Application = args[0] // Add epoch index filter if provided if cmd.Flags().Changed("epoch-index") { - filter.EpochIndex = &epochIndex + epochIndexHex := config.AsHexString(epochIndex) + params.EpochIndex = &epochIndexHex } - // Add parent commitment address filter if provided + // Add tournament address filter if provided if cmd.Flags().Changed("tournament-address") { - // TODO: [maia] check out if the following check is necessary. - _, err := config.ToAddressFromString(tournamentAddress) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid tournament address: %w", err)) - } - filter.TournamentAddress = &tournamentAddress + params.TournamentAddress = &tournamentAddress } + params.Limit = limit + params.Offset = offset - // Limit is validated in PreRunE - - // List commitments with filters - commitments, total, err := repo.ListCommitments(ctx, nameOrAddress, filter, repository.Pagination{ - Limit: limit, - Offset: offset, - }, false) - cobra.CheckErr(err) - - // Format response to match JSON-RPC API - response := struct { - Data []*model.Commitment `json:"data"` - Pagination struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - } `json:"pagination"` - }{ - Data: commitments, - Pagination: struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - }{ - TotalCount: total, - Limit: limit, - Offset: offset, - }, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.ListCommitments(ctx, params) } + cobra.CheckErr(err) + + formatted, err := json.MarshalIndent(result, "", " ") + cobra.CheckErr(err) - fmt.Println(string(result)) + fmt.Println(string(formatted)) } diff --git a/cmd/cartesi-rollups-cli/root/read/epochs/epochs.go b/cmd/cartesi-rollups-cli/root/read/epochs/epochs.go index a8c0c2a04..f982a26c6 100644 --- a/cmd/cartesi-rollups-cli/root/read/epochs/epochs.go +++ b/cmd/cartesi-rollups-cli/root/read/epochs/epochs.go @@ -5,64 +5,64 @@ package epochs import ( "encoding/json" - "errors" "fmt" + "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/service" "github.com/cartesi/rollups-node/internal/config" "github.com/cartesi/rollups-node/internal/jsonrpc" - "github.com/cartesi/rollups-node/internal/model" - "github.com/cartesi/rollups-node/internal/repository" - "github.com/cartesi/rollups-node/internal/repository/factory" "github.com/spf13/cobra" ) var Cmd = &cobra.Command{ - Use: "epochs [application-name-or-address] [epoch-index]", + Use: "epochs [epoch index]", Short: "Reads epochs", Example: examples, - Args: cobra.RangeArgs(1, 2), // nolint: mnd + Args: cobra.RangeArgs(1, 2), //nolint: mnd Run: run, Long: ` +Arguments: + application name or address + [epoch index] decimal or hex encoded + Supported Environment Variables: CARTESI_DATABASE_CONNECTION Database connection string`, } -const examples = `# Read all epochs: -cartesi-rollups-cli read epochs echo-dapp +const examples = `# Read specific epoch: +cartesi-rollups-cli read epochs echo-dapp 10 -# Read specific epoch by index: -cartesi-rollups-cli read epochs echo-dapp 2 +# Read all epochs: +cartesi-rollups-cli read epochs echo-dapp -# Read epochs filtered by status: +# Read all epochs with filter: cartesi-rollups-cli read epochs echo-dapp --status OPEN -# Read epochs with pagination: -cartesi-rollups-cli read epochs echo-dapp --limit 10 --offset 20` - -func init() { - origHelpFunc := Cmd.HelpFunc() - Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { - command.Flags().Lookup("verbose").Hidden = false - command.Flags().Lookup("database-connection").Hidden = false - origHelpFunc(command, strings) - }) -} +# Read all epochs with pagination: +cartesi-rollups-cli read epochs echo-dapp --limit 10 --offset 10 +` var ( - statusFilter string - limit uint64 - offset uint64 + status string + limit uint64 + offset uint64 ) func init() { - Cmd.Flags().StringVar(&statusFilter, "status", "", + Cmd.Flags().StringVar(&status, "status", "", "Filter epochs by status (OPEN, CLOSED, INPUTS_PROCESSED, CLAIM_COMPUTED, CLAIM_SUBMITTED, CLAIM_ACCEPTED, CLAIM_REJECTED)") - Cmd.Flags().Uint64Var(&limit, "limit", 50, // nolint: mnd + Cmd.Flags().Uint64Var(&limit, "limit", 50, //nolint: mnd "Maximum number of epochs to return") Cmd.Flags().Uint64Var(&offset, "offset", 0, "Starting point for the list of epochs") + origHelpFunc := Cmd.HelpFunc() + Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { + command.Flags().Lookup("verbose").Hidden = false + command.Flags().Lookup("database-connection").Hidden = false + origHelpFunc(command, strings) + }) + Cmd.PreRunE = func(cmd *cobra.Command, args []string) error { if limit > jsonrpc.LIST_ITEM_LIMIT { return fmt.Errorf("limit cannot exceed %d", jsonrpc.LIST_ITEM_LIMIT) @@ -76,80 +76,34 @@ func init() { func run(cmd *cobra.Command, args []string) { ctx := cmd.Context() - nameOrAddress, err := config.ToApplicationNameOrAddressFromString(args[0]) + readServ, err := service.CreateReadService(ctx, cmd) cobra.CheckErr(err) + defer readServ.Close() - dsn, err := config.GetDatabaseConnection() - cobra.CheckErr(err) - - repo, err := factory.NewRepositoryFromConnectionString(ctx, dsn.String()) - cobra.CheckErr(err) - defer repo.Close() - - var result []byte - if len(args) == 1 { - // Create filter based on flags - filter := repository.EpochFilter{} - if statusFilter != "" { - var status model.EpochStatus - if err := status.Scan(statusFilter); err != nil { - cobra.CheckErr(fmt.Errorf("invalid status filter: %w", err)) - } - filter.Status = []model.EpochStatus{status} - } + var result json.RawMessage + if len(args) >= 2 { + var params jsonrpc.GetEpochParams + params.Application = args[0] + params.EpochIndex = config.AsHexString(args[1]) - // Limit is validated in PreRunE - - epochs, total, err := repo.ListEpochs(ctx, nameOrAddress, filter, repository.Pagination{ - Limit: limit, - Offset: offset, - }, false) - cobra.CheckErr(err) - - // Format response to match JSON-RPC API - response := struct { - Data []*model.Epoch `json:"data"` - Pagination struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - } `json:"pagination"` - }{ - Data: epochs, - Pagination: struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - }{ - TotalCount: total, - Limit: limit, - Offset: offset, - }, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.GetEpoch(ctx, params) } else { - epochIndex, err := config.ToUint64FromDecimalOrHexString(args[1]) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid value for epoch-index: %w", err)) - } - epoch, err := repo.GetEpoch(ctx, nameOrAddress, epochIndex) - cobra.CheckErr(err) - if epoch == nil { - cobra.CheckErr(errors.New("not found")) - } + var params jsonrpc.ListEpochsParams + params.Application = args[0] - // Format response to match JSON-RPC API - response := struct { - Data *model.Epoch `json:"data"` - }{ - Data: epoch, + // Add status filter if provided + if cmd.Flags().Changed("status") { + params.Status = &status } + params.Limit = limit + params.Offset = offset - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.ListEpochs(ctx, params) } + cobra.CheckErr(err) + + formatted, err := json.MarshalIndent(result, "", " ") + cobra.CheckErr(err) - fmt.Println(string(result)) + fmt.Println(string(formatted)) } diff --git a/cmd/cartesi-rollups-cli/root/read/inputs/inputs.go b/cmd/cartesi-rollups-cli/root/read/inputs/inputs.go index 563357e88..d97e9eb81 100644 --- a/cmd/cartesi-rollups-cli/root/read/inputs/inputs.go +++ b/cmd/cartesi-rollups-cli/root/read/inputs/inputs.go @@ -5,57 +5,56 @@ package inputs import ( "encoding/json" - "errors" "fmt" + "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/service" "github.com/cartesi/rollups-node/internal/config" "github.com/cartesi/rollups-node/internal/jsonrpc" - "github.com/cartesi/rollups-node/internal/repository" - "github.com/cartesi/rollups-node/internal/repository/factory" - "github.com/cartesi/rollups-node/pkg/contracts/inputs" "github.com/spf13/cobra" ) var Cmd = &cobra.Command{ - Use: "inputs [application-name-or-address] [input-index]", - Short: "Reads inputs ordered by index", + Use: "inputs [input index]", + Short: "Reads inputs", Example: examples, - Args: cobra.RangeArgs(1, 2), // nolint: mnd + Args: cobra.RangeArgs(1, 2), //nolint: mnd Run: run, Long: ` +Arguments: + application name or address + [input index] decimal or hex encoded + Supported Environment Variables: CARTESI_DATABASE_CONNECTION Database connection string`, } -const examples = `# Read all inputs: -cartesi-rollups-cli read inputs echo-dapp - -# Read a specific input by index: -cartesi-rollups-cli read inputs echo-dapp 42 +const examples = `# Read specific input: +cartesi-rollups-cli read inputs echo-dapp 10 -# Read inputs filtered byt epoch index: -cartesi-rollups-cli read inputs echo-dapp --epoch-index 0x3 +# Read all inputs: +cartesi-rollups-cli read inputs echo-dapp -# Read inputs filtered by sender address: -cartesi-rollups-cli read inputs echo-dapp --sender 0x0123456789abcdef0123456789abcdef0123456789abcdef +# Read all inputs with filter: +cartesi-rollups-cli read inputs echo-dapp --epoch-index 10 --sender 0x95eac57f9d67c5e0f255d5a19eb5d3fd00cafa73 -# Read inputs with pagination: -cartesi-rollups-cli read inputs echo-dapp --epoch-index 0x3 --limit 20 --offset 0` +# Read all inputs with pagination: +cartesi-rollups-cli read inputs echo-dapp --limit 10 --offset 10 +` var ( - epochIndex uint64 + epochIndex string sender string limit uint64 offset uint64 ) func init() { - Cmd.Flags().Uint64Var(&epochIndex, "epoch-index", 0, + Cmd.Flags().StringVar(&epochIndex, "epoch-index", "", "Filter inputs by epoch index (decimal or hex encoded)") Cmd.Flags().StringVar(&sender, "sender", "", "Filter inputs by sender address (hex encoded)") - Cmd.Flags().Uint64Var(&limit, "limit", 50, // nolint: mnd + Cmd.Flags().Uint64Var(&limit, "limit", 50, //nolint: mnd "Maximum number of inputs to return") Cmd.Flags().Uint64Var(&offset, "offset", 0, "Starting point for the list of inputs") @@ -80,104 +79,40 @@ func init() { func run(cmd *cobra.Command, args []string) { ctx := cmd.Context() - nameOrAddress, err := config.ToApplicationNameOrAddressFromString(args[0]) - cobra.CheckErr(err) - - dsn, err := config.GetDatabaseConnection() - cobra.CheckErr(err) - - repo, err := factory.NewRepositoryFromConnectionString(ctx, dsn.String()) + readServ, err := service.CreateReadService(ctx, cmd) cobra.CheckErr(err) - defer repo.Close() + defer readServ.Close() - parsedAbi, err := inputs.InputsMetaData.GetAbi() - cobra.CheckErr(err) + var result json.RawMessage + if len(args) >= 2 { + var params jsonrpc.GetInputParams + params.Application = args[0] + params.InputIndex = config.AsHexString(args[1]) - var result []byte - if len(args) == 1 { - // Create filter based on flags - filter := repository.InputFilter{} + result, err = readServ.GetInput(ctx, params) + } else { + var params jsonrpc.ListInputsParams + params.Application = args[0] // Add epoch index filter if provided if cmd.Flags().Changed("epoch-index") { - filter.EpochIndex = &epochIndex + epochIndexHex := config.AsHexString(epochIndex) + params.EpochIndex = &epochIndexHex } // Add sender filter if provided - if sender != "" { - senderAddr, err := config.ToAddressFromString(sender) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid sender address: %w", err)) - } - filter.Sender = &senderAddr - } - - // Limit is validated in PreRunE - - // List all inputs with filters - inputList, total, err := repo.ListInputs(ctx, nameOrAddress, filter, repository.Pagination{ - Limit: limit, - Offset: offset, - }, false) - cobra.CheckErr(err) - - // Create decoded inputs - var decodedInputs []*jsonrpc.DecodedInput - for _, input := range inputList { - decoded, _ := jsonrpc.DecodeInput(input, parsedAbi) - decodedInputs = append(decodedInputs, decoded) + if cmd.Flags().Changed("sender") { + params.Sender = &sender } + params.Limit = limit + params.Offset = offset - // Format response to match JSON-RPC API - response := struct { - Data []*jsonrpc.DecodedInput `json:"data"` - Pagination struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - } `json:"pagination"` - }{ - Data: decodedInputs, - Pagination: struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - }{ - TotalCount: total, - Limit: limit, - Offset: offset, - }, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) - } else { - // Get specific input by index - inputIndex, err := config.ToUint64FromDecimalOrHexString(args[1]) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid value for input-index: %w", err)) - } - - input, err := repo.GetInput(ctx, nameOrAddress, inputIndex) - cobra.CheckErr(err) - - if input == nil { - cobra.CheckErr(errors.New("not found")) - } - - // Create decoded input - decoded, _ := jsonrpc.DecodeInput(input, parsedAbi) - - // Format response to match JSON-RPC API - response := struct { - Data *jsonrpc.DecodedInput `json:"data"` - }{ - Data: decoded, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.ListInputs(ctx, params) } + cobra.CheckErr(err) + + formatted, err := json.MarshalIndent(result, "", " ") + cobra.CheckErr(err) - fmt.Println(string(result)) + fmt.Println(string(formatted)) } diff --git a/cmd/cartesi-rollups-cli/root/read/matchadvances/matchadvances.go b/cmd/cartesi-rollups-cli/root/read/matchadvances/matchadvances.go index bac7cee20..4c7a8fe98 100644 --- a/cmd/cartesi-rollups-cli/root/read/matchadvances/matchadvances.go +++ b/cmd/cartesi-rollups-cli/root/read/matchadvances/matchadvances.go @@ -5,46 +5,42 @@ package matchadvances import ( "encoding/json" - "errors" "fmt" + "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/service" "github.com/cartesi/rollups-node/internal/config" "github.com/cartesi/rollups-node/internal/jsonrpc" - "github.com/cartesi/rollups-node/internal/model" - "github.com/cartesi/rollups-node/internal/repository" - "github.com/cartesi/rollups-node/internal/repository/factory" "github.com/spf13/cobra" ) var Cmd = &cobra.Command{ - Use: "matches_advances [parent]", - Short: "Reads matches advances", + Use: "match_advances [parent]", + Short: "Reads match advances", Example: examples, - Args: cobra.RangeArgs(4, 5), // nolint: mnd + Args: cobra.RangeArgs(4, 5), //nolint: mnd Run: run, Long: ` +Arguments: + application name or address + decimal or hex encoded + hex encoded + hex encoded + [parent] hex encoded + Supported Environment Variables: CARTESI_DATABASE_CONNECTION Database connection string`, } -const examples = `# Read all matches advances: -cartesi-rollups-cli read matches_advances echo-dapp - -# Read specific match advances by address: -cartesi-rollups-cli read matches_advances echo-dapp 1 0x0123456789abcdef0123456789abcdef0123456789abcdef +const examples = `# Read specific match advanced: +cartesi-rollups-cli read match_advances echo-dapp 10 0x0073a8637d98649717bdc02ecb439c80aa8a10d0 0xdb99c9cdb2e2070a4e4e633c2e6874648dfe3971d14da843465b3d950df3dd19 0xdb99c9cdb2e2070a4e4e633c2e6874648dfe3971d14da843465b3d950df3dd19 -# Read matches advances with pagination: -cartesi-rollups-cli read matches_advances echo-dapp --limit 10 --offset 20` +# Read all match advances: +cartesi-rollups-cli read match_advances echo-dapp 10 0x0073a8637d98649717bdc02ecb439c80aa8a10d0 0xdb99c9cdb2e2070a4e4e633c2e6874648dfe3971d14da843465b3d950df3dd19 -func init() { - origHelpFunc := Cmd.HelpFunc() - Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { - command.Flags().Lookup("verbose").Hidden = false - command.Flags().Lookup("database-connection").Hidden = false - origHelpFunc(command, strings) - }) -} +# Read all match advances with pagination: +cartesi-rollups-cli read match_advances echo-dapp 10 0x0073a8637d98649717bdc02ecb439c80aa8a10d0 0xdb99c9cdb2e2070a4e4e633c2e6874648dfe3971d14da843465b3d950df3dd19 --limit 10 --offset 10 +` var ( limit uint64 @@ -52,10 +48,17 @@ var ( ) func init() { - Cmd.Flags().Uint64Var(&limit, "limit", 50, // nolint: mnd - "Maximum number of matches advances to return") + Cmd.Flags().Uint64Var(&limit, "limit", 50, //nolint: mnd + "Maximum number of match advances to return") Cmd.Flags().Uint64Var(&offset, "offset", 0, - "Starting point for the list of matches advances") + "Starting point for the list of match advances") + + origHelpFunc := Cmd.HelpFunc() + Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { + command.Flags().Lookup("verbose").Hidden = false + command.Flags().Lookup("database-connection").Hidden = false + origHelpFunc(command, strings) + }) Cmd.PreRunE = func(cmd *cobra.Command, args []string) error { if limit > jsonrpc.LIST_ITEM_LIMIT { @@ -70,90 +73,35 @@ func init() { func run(cmd *cobra.Command, args []string) { ctx := cmd.Context() - nameOrAddress, err := config.ToApplicationNameOrAddressFromString(args[0]) + readServ, err := service.CreateReadService(ctx, cmd) cobra.CheckErr(err) - - dsn, err := config.GetDatabaseConnection() + defer readServ.Close() + + var result json.RawMessage + if len(args) >= 5 { + var params jsonrpc.GetMatchAdvancedParams + params.Application = args[0] + params.EpochIndex = config.AsHexString(args[1]) + params.TournamentAddress = args[2] + params.IDHash = args[3] + params.Parent = args[4] + + result, err = readServ.GetMatchAdvanced(ctx, params) + } else { + var params jsonrpc.ListMatchAdvancesParams + params.Application = args[0] + params.EpochIndex = config.AsHexString(args[1]) + params.TournamentAddress = args[2] + params.IDHash = args[3] + params.Limit = limit + params.Offset = offset + + result, err = readServ.ListMatchAdvances(ctx, params) + } cobra.CheckErr(err) - repo, err := factory.NewRepositoryFromConnectionString(ctx, dsn.String()) + formatted, err := json.MarshalIndent(result, "", " ") cobra.CheckErr(err) - defer repo.Close() - - epochIndex, err := config.ToUint64FromDecimalOrHexString(args[1]) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid value for epoch-index: %w", err)) - } - - // TODO: [maia] check out if the following check is necessary, because - // such conversion is already done in the 'repo.GetMatchAdvanced' function, - // but it doesn't seem to check for invalid addresses. - tournamentAddressHex := args[2] - _, err = config.ToAddressFromString(tournamentAddressHex) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid tournament address: %w", err)) - } - idHashHex := args[3] - _, err = config.ToHashFromString(idHashHex) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid ID hash: %w", err)) - } - - var result []byte - if len(args) == 5 { // nolint: mnd - // Get a specific match advance by address - - parentHex := args[4] - - matchAdvanced, err := repo.GetMatchAdvanced(ctx, nameOrAddress, epochIndex, tournamentAddressHex, idHashHex, parentHex) - cobra.CheckErr(err) - if matchAdvanced == nil { - cobra.CheckErr(errors.New("not found")) - } - - // Format response to match JSON-RPC API - response := struct { - Data *model.MatchAdvanced `json:"data"` - }{ - Data: matchAdvanced, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) - } else { - // Limit is validated in PreRunE - - // List matches advances with filters - matchAdvances, total, err := repo.ListMatchAdvances(ctx, nameOrAddress, epochIndex, tournamentAddressHex, idHashHex, repository.Pagination{ - Limit: limit, - Offset: offset, - }, false) - cobra.CheckErr(err) - - // Format response to match JSON-RPC API - response := struct { - Data []*model.MatchAdvanced `json:"data"` - Pagination struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - } `json:"pagination"` - }{ - Data: matchAdvances, - Pagination: struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - }{ - TotalCount: total, - Limit: limit, - Offset: offset, - }, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) - } - fmt.Println(string(result)) + fmt.Println(string(formatted)) } diff --git a/cmd/cartesi-rollups-cli/root/read/matches/matches.go b/cmd/cartesi-rollups-cli/root/read/matches/matches.go index 7d3cc9d0f..9178f5665 100644 --- a/cmd/cartesi-rollups-cli/root/read/matches/matches.go +++ b/cmd/cartesi-rollups-cli/root/read/matches/matches.go @@ -5,67 +5,69 @@ package matches import ( "encoding/json" - "errors" "fmt" + "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/service" "github.com/cartesi/rollups-node/internal/config" "github.com/cartesi/rollups-node/internal/jsonrpc" - "github.com/cartesi/rollups-node/internal/model" - "github.com/cartesi/rollups-node/internal/repository" - "github.com/cartesi/rollups-node/internal/repository/factory" "github.com/spf13/cobra" ) var Cmd = &cobra.Command{ - Use: "matches [application-name-or-address] [epoch-index] [tournament-address] [id-hash]", + Use: "matches [epoch index] [tournament address] [ID hash]", Short: "Reads matches", Example: examples, - Args: cobra.RangeArgs(1, 4), // nolint: mnd + Args: cobra.RangeArgs(1, 4), //nolint: mnd Run: run, Long: ` +Arguments: + application name or address + [epoch index] decimal or hex encoded + [tournament address] hex encoded + [ID hash] hex encoded + Supported Environment Variables: CARTESI_DATABASE_CONNECTION Database connection string`, } -const examples = `# Read all matches: -cartesi-rollups-cli read matches echo-dapp +const examples = `# Read specific match: +cartesi-rollups-cli read matches echo-dapp 10 0x0073a8637d98649717bdc02ecb439c80aa8a10d0 0xdb99c9cdb2e2070a4e4e633c2e6874648dfe3971d14da843465b3d950df3dd19 -# Read specific match by epoch, tournament address, and hash: -cartesi-rollups-cli read matches echo-dapp 1 0x0123456789abcdef0123456789abcdef0123456789abcdef 0x1b7087e9580fb7946f37a40ced7ffeded336da790e00cc88e5f4e8e25301546a +# Read all matches: +cartesi-rollups-cli read matches echo-dapp -# Read matches filtered by tournament address: -cartesi-rollups-cli read matches echo-dapp --tournament-address 0x0123456789abcdef0123456789abcdef0123456789abcdef +# Read all matches with filter: +cartesi-rollups-cli read matches echo-dapp --epoch-index 10 --tournament-address 0x0073a8637d98649717bdc02ecb439c80aa8a10d0 -# Read matches with pagination: -cartesi-rollups-cli read matches echo-dapp --limit 10 --offset 20` - -func init() { - origHelpFunc := Cmd.HelpFunc() - Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { - command.Flags().Lookup("verbose").Hidden = false - command.Flags().Lookup("database-connection").Hidden = false - origHelpFunc(command, strings) - }) -} +# Read all matches with pagination: +cartesi-rollups-cli read matches echo-dapp --limit 10 --offset 10 +` var ( - epochIndex uint64 + epochIndex string tournamentAddress string limit uint64 offset uint64 ) func init() { - Cmd.Flags().Uint64Var(&epochIndex, "epoch-index", 0, + Cmd.Flags().StringVar(&epochIndex, "epoch-index", "", "Filter matches by epoch index (decimal or hex encoded)") Cmd.Flags().StringVar(&tournamentAddress, "tournament-address", "", "Filter matches by tournament address (hex encoded)") - Cmd.Flags().Uint64Var(&limit, "limit", 50, // nolint: mnd + Cmd.Flags().Uint64Var(&limit, "limit", 50, //nolint: mnd "Maximum number of matches to return") Cmd.Flags().Uint64Var(&offset, "offset", 0, "Starting point for the list of matches") + origHelpFunc := Cmd.HelpFunc() + Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { + command.Flags().Lookup("verbose").Hidden = false + command.Flags().Lookup("database-connection").Hidden = false + origHelpFunc(command, strings) + }) + Cmd.PreRunE = func(cmd *cobra.Command, args []string) error { if limit > jsonrpc.LIST_ITEM_LIMIT { return fmt.Errorf("limit cannot exceed %d", jsonrpc.LIST_ITEM_LIMIT) @@ -79,106 +81,42 @@ func init() { func run(cmd *cobra.Command, args []string) { ctx := cmd.Context() - nameOrAddress, err := config.ToApplicationNameOrAddressFromString(args[0]) + readServ, err := service.CreateReadService(ctx, cmd) cobra.CheckErr(err) + defer readServ.Close() - dsn, err := config.GetDatabaseConnection() - cobra.CheckErr(err) - - repo, err := factory.NewRepositoryFromConnectionString(ctx, dsn.String()) - cobra.CheckErr(err) - defer repo.Close() - - var result []byte - if len(args) == 4 { // nolint: mnd - // Get a specific match by address - - epochIndex, err := config.ToUint64FromDecimalOrHexString(args[1]) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid value for epoch-index: %w", err)) - } - - // TODO: [maia] check out if the following check is necessary, because - // such conversion is already done in the 'repo.GetMatch' function, - // but it doesn't seem to check for invalid addresses. - tournamentAddressHex := args[2] - _, err = config.ToAddressFromString(tournamentAddressHex) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid tournament address: %w", err)) - } - idHashHex := args[3] - _, err = config.ToHashFromString(idHashHex) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid ID hash: %w", err)) - } + var result json.RawMessage + if len(args) >= 4 { + var params jsonrpc.GetMatchParams + params.Application = args[0] + params.EpochIndex = config.AsHexString(args[1]) + params.TournamentAddress = args[2] + params.IDHash = args[3] - match, err := repo.GetMatch(ctx, nameOrAddress, epochIndex, tournamentAddressHex, idHashHex) - cobra.CheckErr(err) - if match == nil { - cobra.CheckErr(errors.New("not found")) - } - - // Format response to match JSON-RPC API - response := struct { - Data *model.Match `json:"data"` - }{ - Data: match, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.GetMatch(ctx, params) } else { - // Create filter based on flags - filter := repository.MatchFilter{} + var params jsonrpc.ListMatchesParams + params.Application = args[0] // Add epoch index filter if provided if cmd.Flags().Changed("epoch-index") { - filter.EpochIndex = &epochIndex + epochIndexHex := config.AsHexString(epochIndex) + params.EpochIndex = &epochIndexHex } - // Add parent commitment address filter if provided + // Add tournament address filter if provided if cmd.Flags().Changed("tournament-address") { - // TODO: [maia] check out if the following check is necessary. - _, err := config.ToAddressFromString(tournamentAddress) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid tournament address: %w", err)) - } - filter.TournamentAddress = &tournamentAddress + params.TournamentAddress = &tournamentAddress } + params.Limit = limit + params.Offset = offset - // Limit is validated in PreRunE - - // List matches with filters - matches, total, err := repo.ListMatches(ctx, nameOrAddress, filter, repository.Pagination{ - Limit: limit, - Offset: offset, - }, false) - cobra.CheckErr(err) - - // Format response to match JSON-RPC API - response := struct { - Data []*model.Match `json:"data"` - Pagination struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - } `json:"pagination"` - }{ - Data: matches, - Pagination: struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - }{ - TotalCount: total, - Limit: limit, - Offset: offset, - }, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.ListMatches(ctx, params) } + cobra.CheckErr(err) + + formatted, err := json.MarshalIndent(result, "", " ") + cobra.CheckErr(err) - fmt.Println(string(result)) + fmt.Println(string(formatted)) } diff --git a/cmd/cartesi-rollups-cli/root/read/outputs/outputs.go b/cmd/cartesi-rollups-cli/root/read/outputs/outputs.go index e2ea78f5d..36a955644 100644 --- a/cmd/cartesi-rollups-cli/root/read/outputs/outputs.go +++ b/cmd/cartesi-rollups-cli/root/read/outputs/outputs.go @@ -5,47 +5,46 @@ package outputs import ( "encoding/json" - "errors" "fmt" - "github.com/spf13/cobra" - + "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/service" "github.com/cartesi/rollups-node/internal/config" "github.com/cartesi/rollups-node/internal/jsonrpc" - "github.com/cartesi/rollups-node/internal/repository" - "github.com/cartesi/rollups-node/internal/repository/factory" - "github.com/cartesi/rollups-node/pkg/contracts/outputs" + + "github.com/spf13/cobra" ) var Cmd = &cobra.Command{ - Use: "outputs [application-name-or-address] [output-index]", + Use: "outputs [output index]", Short: "Reads outputs", Example: examples, - Args: cobra.RangeArgs(1, 2), // nolint: mnd + Args: cobra.RangeArgs(1, 2), //nolint: mnd Run: run, Long: ` +Arguments: + application name or address + [output index] decimal or hex encoded + Supported Environment Variables: CARTESI_DATABASE_CONNECTION Database connection string`, } -const examples = `# Read all outputs: -cartesi-rollups-cli read outputs echo-dapp - -# Read specific output by index: -cartesi-rollups-cli read outputs echo-dapp 42 +const examples = `# Read specific output: +cartesi-rollups-cli read outputs echo-dapp 10 -# Read outputs filtered by epoch index and input index: -cartesi-rollups-cli read outputs echo-dapp --epoch-index 0x3 +# Read all outputs: +cartesi-rollups-cli read outputs echo-dapp -# Read outputs filtered by output type and voucher address: -cartesi-rollups-cli read outputs echo-dapp --output-type 0x237a816f --voucher-address 0x0123456789abcdef0123456789abcdef0123456789abcdef +# Read all outputs with filter: +cartesi-rollups-cli read outputs echo-dapp --epoch-index 10 --input-index 10 --output-type 0x237a816f --voucher-address 0x95eac57f9d67c5e0f255d5a19eb5d3fd00cafa73 -# Read outputs with pagination: -cartesi-rollups-cli read outputs echo-dapp --limit 20 --offset 0` +# Read all outputs with pagination: +cartesi-rollups-cli read outputs echo-dapp --limit 10 --offset 10 +` var ( - epochIndex uint64 - inputIndex uint64 + epochIndex string + inputIndex string outputType string voucherAddress string limit uint64 @@ -53,15 +52,15 @@ var ( ) func init() { - Cmd.Flags().Uint64Var(&epochIndex, "epoch-index", 0, + Cmd.Flags().StringVar(&epochIndex, "epoch-index", "", "Filter outputs by epoch index (decimal or hex encoded)") - Cmd.Flags().Uint64Var(&inputIndex, "input-index", 0, + Cmd.Flags().StringVar(&inputIndex, "input-index", "", "Filter outputs by input index (decimal or hex encoded)") Cmd.Flags().StringVar(&outputType, "output-type", "", "Filter outputs by output type (first 4 bytes of raw data hex encoded)") Cmd.Flags().StringVar(&voucherAddress, "voucher-address", "", "Filter outputs by voucher address (hex encoded)") - Cmd.Flags().Uint64Var(&limit, "limit", 50, // nolint: mnd + Cmd.Flags().Uint64Var(&limit, "limit", 50, //nolint: mnd "Maximum number of outputs to return") Cmd.Flags().Uint64Var(&offset, "offset", 0, "Starting point for the list of outputs") @@ -86,117 +85,51 @@ func init() { func run(cmd *cobra.Command, args []string) { ctx := cmd.Context() - nameOrAddress, err := config.ToApplicationNameOrAddressFromString(args[0]) + readServ, err := service.CreateReadService(ctx, cmd) cobra.CheckErr(err) + defer readServ.Close() - dsn, err := config.GetDatabaseConnection() - cobra.CheckErr(err) - - repo, err := factory.NewRepositoryFromConnectionString(ctx, dsn.String()) - cobra.CheckErr(err) - defer repo.Close() - - parsedAbi, err := outputs.OutputsMetaData.GetAbi() - cobra.CheckErr(err) - - var result []byte - if len(args) == 2 { // nolint: mnd - // Get a specific output by index - outputIndex, err := config.ToUint64FromDecimalOrHexString(args[1]) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid output index value: %w", err)) - } + var result json.RawMessage + if len(args) >= 2 { + var params jsonrpc.GetOutputParams + params.Application = args[0] + params.OutputIndex = config.AsHexString(args[1]) - output, err := repo.GetOutput(ctx, nameOrAddress, outputIndex) - cobra.CheckErr(err) - if output == nil { - cobra.CheckErr(errors.New("not found")) - } - - // Create decoded output - decoded, _ := jsonrpc.DecodeOutput(output, parsedAbi) - - // Format response to match JSON-RPC API - response := struct { - Data *jsonrpc.DecodedOutput `json:"data"` - }{ - Data: decoded, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.GetOutput(ctx, params) } else { - // Create filter based on flags - filter := repository.OutputFilter{} + var params jsonrpc.ListOutputsParams + params.Application = args[0] // Add epoch index filter if provided if cmd.Flags().Changed("epoch-index") { - filter.EpochIndex = &epochIndex + epochIndexHex := config.AsHexString(epochIndex) + params.EpochIndex = &epochIndexHex } // Add input index filter if provided if cmd.Flags().Changed("input-index") { - filter.InputIndex = &inputIndex + inputIndexHex := config.AsHexString(inputIndex) + params.InputIndex = &inputIndexHex } // Add output type filter if provided if cmd.Flags().Changed("output-type") { - outputTypeBytes, err := jsonrpc.ParseOutputType(outputType) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid output type: %w", err)) - } - filter.OutputType = &outputTypeBytes + params.OutputType = &outputType } // Add voucher address filter if provided if cmd.Flags().Changed("voucher-address") { - voucherAddr, err := config.ToAddressFromString(voucherAddress) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid voucher address: %w", err)) - } - filter.VoucherAddress = &voucherAddr - } - - // Limit is validated in PreRunE - - // List outputs with filters - outputList, total, err := repo.ListOutputs(ctx, nameOrAddress, filter, repository.Pagination{ - Limit: limit, - Offset: offset, - }, false) - cobra.CheckErr(err) - - // Create decoded outputs - var decodedOutputs []*jsonrpc.DecodedOutput - for _, output := range outputList { - decoded, _ := jsonrpc.DecodeOutput(output, parsedAbi) - decodedOutputs = append(decodedOutputs, decoded) - } - - // Format response to match JSON-RPC API - response := struct { - Data []*jsonrpc.DecodedOutput `json:"data"` - Pagination struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - } `json:"pagination"` - }{ - Data: decodedOutputs, - Pagination: struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - }{ - TotalCount: total, - Limit: limit, - Offset: offset, - }, + params.VoucherAddress = &voucherAddress } + params.Limit = limit + params.Offset = offset - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.ListOutputs(ctx, params) } + cobra.CheckErr(err) + + formatted, err := json.MarshalIndent(result, "", " ") + cobra.CheckErr(err) - fmt.Println(string(result)) + fmt.Println(string(formatted)) } diff --git a/cmd/cartesi-rollups-cli/root/read/read.go b/cmd/cartesi-rollups-cli/root/read/read.go index c78eb69c4..36dc2bb73 100644 --- a/cmd/cartesi-rollups-cli/root/read/read.go +++ b/cmd/cartesi-rollups-cli/root/read/read.go @@ -12,8 +12,10 @@ import ( "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/outputs" "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/reports" "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/tournaments" + "github.com/cartesi/rollups-node/internal/config" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var Cmd = &cobra.Command{ @@ -22,6 +24,10 @@ var Cmd = &cobra.Command{ } func init() { + Cmd.PersistentFlags().String("jsonrpc-api-address", "", + "JSON-RPC endpoint string in the URL format\n(eg.: 'https://localhost:10011/rpc')") + cobra.CheckErr(viper.BindPFlag(config.JSONRPC_API_URL, Cmd.PersistentFlags().Lookup("jsonrpc-api-address"))) + Cmd.AddCommand(epochs.Cmd) Cmd.AddCommand(inputs.Cmd) Cmd.AddCommand(outputs.Cmd) diff --git a/cmd/cartesi-rollups-cli/root/read/reports/reports.go b/cmd/cartesi-rollups-cli/root/read/reports/reports.go index e836c16b2..44f4e91c4 100644 --- a/cmd/cartesi-rollups-cli/root/read/reports/reports.go +++ b/cmd/cartesi-rollups-cli/root/read/reports/reports.go @@ -5,54 +5,56 @@ package reports import ( "encoding/json" - "errors" "fmt" - "github.com/spf13/cobra" - + "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/service" "github.com/cartesi/rollups-node/internal/config" "github.com/cartesi/rollups-node/internal/jsonrpc" - "github.com/cartesi/rollups-node/internal/model" - "github.com/cartesi/rollups-node/internal/repository" - "github.com/cartesi/rollups-node/internal/repository/factory" + + "github.com/spf13/cobra" ) var Cmd = &cobra.Command{ - Use: "reports [application-name-or-address] [report-index]", + Use: "reports [report index]", Short: "Reads reports", Example: examples, - Args: cobra.RangeArgs(1, 2), // nolint: mnd + Args: cobra.RangeArgs(1, 2), //nolint: mnd Run: run, Long: ` +Arguments: + application name or address + [report index] decimal or hex encoded + Supported Environment Variables: CARTESI_DATABASE_CONNECTION Database connection string`, } -const examples = `# Read all reports: -cartesi-rollups-cli read reports echo-dapp +const examples = `# Read specific report: +cartesi-rollups-cli read reports echo-dapp 10 -# Read specific report by index: -cartesi-rollups-cli read reports echo-dapp 42 +# Read all reports: +cartesi-rollups-cli read reports echo-dapp -# Read reports filtered by epoch index: -cartesi-rollups-cli read reports echo-dapp --epoch-index 0x3 +# Read all reports with filter: +cartesi-rollups-cli read reports echo-dapp --epoch-index 10 --input-index 10 -# Read reports with pagination: -cartesi-rollups-cli read reports echo-dapp --limit 10 --offset 5` +# Read all reports with pagination: +cartesi-rollups-cli read reports echo-dapp --limit 10 --offset 10 +` var ( - epochIndex uint64 - inputIndex uint64 + epochIndex string + inputIndex string limit uint64 offset uint64 ) func init() { - Cmd.Flags().Uint64Var(&epochIndex, "epoch-index", 0, - "Filter reports by epoch index (hex encoded)") - Cmd.Flags().Uint64Var(&inputIndex, "input-index", 0, - "Filter reports by input index (hex encoded)") - Cmd.Flags().Uint64Var(&limit, "limit", 50, // nolint: mnd + Cmd.Flags().StringVar(&epochIndex, "epoch-index", "", + "Filter reports by epoch index (decimal or hex encoded)") + Cmd.Flags().StringVar(&inputIndex, "input-index", "", + "Filter reports by input index (decimal or hex encoded)") + Cmd.Flags().Uint64Var(&limit, "limit", 50, //nolint: mnd "Maximum number of reports to return") Cmd.Flags().Uint64Var(&offset, "offset", 0, "Starting point for the list of reports") @@ -77,86 +79,41 @@ func init() { func run(cmd *cobra.Command, args []string) { ctx := cmd.Context() - nameOrAddress, err := config.ToApplicationNameOrAddressFromString(args[0]) - cobra.CheckErr(err) - - dsn, err := config.GetDatabaseConnection() - cobra.CheckErr(err) - - repo, err := factory.NewRepositoryFromConnectionString(ctx, dsn.String()) + readServ, err := service.CreateReadService(ctx, cmd) cobra.CheckErr(err) - defer repo.Close() - - var result []byte - if len(args) == 2 { // nolint: mnd - // Get a specific report by index - reportIndex, err := config.ToUint64FromDecimalOrHexString(args[1]) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid report index value: %w", err)) - } + defer readServ.Close() - report, err := repo.GetReport(ctx, nameOrAddress, reportIndex) - cobra.CheckErr(err) - if report == nil { - cobra.CheckErr(errors.New("not found")) - } - - // Format response to match JSON-RPC API - response := struct { - Data *model.Report `json:"data"` - }{ - Data: report, - } + var result json.RawMessage + if len(args) >= 2 { + var params jsonrpc.GetReportParams + params.Application = args[0] + params.ReportIndex = config.AsHexString(args[1]) - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.GetReport(ctx, params) } else { - // Create filter based on flags - filter := repository.ReportFilter{} + var params jsonrpc.ListReportsParams + params.Application = args[0] // Add epoch index filter if provided if cmd.Flags().Changed("epoch-index") { - filter.EpochIndex = &epochIndex + epochIndexHex := config.AsHexString(epochIndex) + params.EpochIndex = &epochIndexHex } // Add input index filter if provided if cmd.Flags().Changed("input-index") { - filter.InputIndex = &inputIndex + inputIndexHex := config.AsHexString(inputIndex) + params.InputIndex = &inputIndexHex } + params.Limit = limit + params.Offset = offset - // Limit is validated in PreRunE - - // List reports with filters - reports, total, err := repo.ListReports(ctx, nameOrAddress, filter, repository.Pagination{ - Limit: limit, - Offset: offset, - }, false) - cobra.CheckErr(err) - - // Format response to match JSON-RPC API - response := struct { - Data []*model.Report `json:"data"` - Pagination struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - } `json:"pagination"` - }{ - Data: reports, - Pagination: struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - }{ - TotalCount: total, - Limit: limit, - Offset: offset, - }, - } - - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.ListReports(ctx, params) } + cobra.CheckErr(err) + + formatted, err := json.MarshalIndent(result, "", " ") + cobra.CheckErr(err) - fmt.Println(string(result)) + fmt.Println(string(formatted)) } diff --git a/cmd/cartesi-rollups-cli/root/read/service/jsonrpc.go b/cmd/cartesi-rollups-cli/root/read/service/jsonrpc.go new file mode 100644 index 000000000..b1974fe1a --- /dev/null +++ b/cmd/cartesi-rollups-cli/root/read/service/jsonrpc.go @@ -0,0 +1,429 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +package service + +import ( + "context" + "encoding/json" + "errors" + "net/url" + + "github.com/cartesi/rollups-node/internal/config" + "github.com/cartesi/rollups-node/internal/jsonrpc" + "github.com/cartesi/rollups-node/internal/model" + "github.com/cartesi/rollups-node/pkg/jsonrpc/client" +) + +type JsonrpcReadService struct { + Client *client.Client +} + +func (self *JsonrpcReadService) GetApplication(ctx context.Context, params jsonrpc.GetApplicationParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_getApplication", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) GetEpoch(ctx context.Context, params jsonrpc.GetEpochParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + _, err = config.ToIndexFromString(params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_getEpoch", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) ListEpochs(ctx context.Context, params jsonrpc.ListEpochsParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + // Add status filter if provided + if params.Status != nil { + var statusVal model.EpochStatus + err := statusVal.Scan(*params.Status) + if err != nil { + return nil, errors.New("invalid status: expected OPEN, CLOSED, INPUTS_PROCESSED, CLAIM_COMPUTED, CLAIM_SUBMITTED, CLAIM_ACCEPTED, CLAIM_REJECTED") + } + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_listEpochs", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) GetInput(ctx context.Context, params jsonrpc.GetInputParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + _, err = config.ToIndexFromString(params.InputIndex) + if err != nil { + return nil, errors.New("invalid input index: expected hex encoded") + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_getInput", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) ListInputs(ctx context.Context, params jsonrpc.ListInputsParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + // Add epoch index filter if provided + if params.EpochIndex != nil { + _, err = config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + } + // Add sender filter if provided + if params.Sender != nil { + _, err = config.ToAddressFromString(*params.Sender) + if err != nil { + return nil, errors.New("invalid sender: expected hex encoded") + } + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_listInputs", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) GetOutput(ctx context.Context, params jsonrpc.GetOutputParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + _, err = config.ToIndexFromString(params.OutputIndex) + if err != nil { + return nil, errors.New("invalid output index: expected hex encoded") + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_getOutput", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) ListOutputs(ctx context.Context, params jsonrpc.ListOutputsParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + // Add epoch index filter if provided + if params.EpochIndex != nil { + _, err = config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + } + // Add input index filter if provided + if params.InputIndex != nil { + _, err = config.ToIndexFromString(*params.InputIndex) + if err != nil { + return nil, errors.New("invalid input index: expected hex encoded") + } + } + // Add output type filter if provided + if params.OutputType != nil { + _, err = jsonrpc.ParseOutputType(*params.OutputType) + if err != nil { + return nil, errors.New("invalid output type: expected first 4 bytes of raw data hex encoded") + } + } + // Add voucher address filter if provided + if params.VoucherAddress != nil { + _, err = config.ToAddressFromString(*params.VoucherAddress) + if err != nil { + return nil, errors.New("invalid voucher address: expected hex encoded") + } + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_listOutputs", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) GetReport(ctx context.Context, params jsonrpc.GetReportParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + _, err = config.ToIndexFromString(params.ReportIndex) + if err != nil { + return nil, errors.New("invalid report index: expected hex encoded") + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_getReport", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) ListReports(ctx context.Context, params jsonrpc.ListReportsParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + // Add epoch index filter if provided + if params.EpochIndex != nil { + _, err = config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + } + // Add input index filter if provided + if params.InputIndex != nil { + _, err = config.ToIndexFromString(*params.InputIndex) + if err != nil { + return nil, errors.New("invalid input index: expected hex encoded") + } + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_listReports", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) GetTournament(ctx context.Context, params jsonrpc.GetTournamentParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + _, err = config.ToAddressFromString(params.Address) + if err != nil { + return nil, errors.New("invalid address: expected hex encoded") + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_getTournament", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) ListTournaments(ctx context.Context, params jsonrpc.ListTournamentsParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + // Add epoch index filter if provided + if params.EpochIndex != nil { + _, err = config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + } + // Add level filter if provided + if params.Level != nil { + _, err = config.ToIndexFromString(*params.Level) + if err != nil { + return nil, errors.New("invalid level: expected hex encoded") + } + } + // Add parent tournament address filter if provided + if params.ParentTournamentAddress != nil { + _, err = config.ToAddressFromString(*params.ParentTournamentAddress) + if err != nil { + return nil, errors.New("invalid parent tournament address: expected hex encoded") + } + } + // Add parent match ID hash filter if provided + if params.ParentMatchIDHash != nil { + _, err = config.ToHashFromString(*params.ParentMatchIDHash) + if err != nil { + return nil, errors.New("invalid parent match ID hash: expected hex encoded") + } + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_listTournaments", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) GetCommitment(ctx context.Context, params jsonrpc.GetCommitmentParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + _, err = config.ToIndexFromString(params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + _, err = config.ToAddressFromString(params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + _, err = config.ToHashFromString(params.Commitment) + if err != nil { + return nil, errors.New("invalid commitment: expected hex encoded") + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_getCommitment", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) ListCommitments(ctx context.Context, params jsonrpc.ListCommitmentsParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + // Add epoch index filter if provided + if params.EpochIndex != nil { + _, err = config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + } + // Add tournament address filter if provided + if params.TournamentAddress != nil { + _, err = config.ToAddressFromString(*params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_listCommitments", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) GetMatch(ctx context.Context, params jsonrpc.GetMatchParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + _, err = config.ToIndexFromString(params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + _, err = config.ToAddressFromString(params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + _, err = config.ToHashFromString(params.IDHash) + if err != nil { + return nil, errors.New("invalid ID hash: expected hex encoded") + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_getMatch", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) ListMatches(ctx context.Context, params jsonrpc.ListMatchesParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + // Add epoch index filter if provided + if params.EpochIndex != nil { + _, err = config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + } + // Add tournament address filter if provided + if params.TournamentAddress != nil { + _, err = config.ToAddressFromString(*params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_listMatches", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) GetMatchAdvanced(ctx context.Context, params jsonrpc.GetMatchAdvancedParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + _, err = config.ToIndexFromString(params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + _, err = config.ToAddressFromString(params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + _, err = config.ToHashFromString(params.IDHash) + if err != nil { + return nil, errors.New("invalid ID hash: expected hex encoded") + } + _, err = config.ToHashFromString(params.Parent) + if err != nil { + return nil, errors.New("invalid parent: expected hex encoded") + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_getMatchAdvanced", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) ListMatchAdvances(ctx context.Context, params jsonrpc.ListMatchAdvancesParams) (json.RawMessage, error) { + var err error + _, err = config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + _, err = config.ToIndexFromString(params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + _, err = config.ToAddressFromString(params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + _, err = config.ToHashFromString(params.IDHash) + if err != nil { + return nil, errors.New("invalid ID hash: expected hex encoded") + } + + var resp json.RawMessage + err = self.Client.Call(ctx, "cartesi_listMatchAdvances", params, &resp) + return resp, err +} + +func (self *JsonrpcReadService) Close() { +} + +func NewJsonrpcReadService(_ context.Context, serviceURL string) (ReadService, error) { + if _, err := url.ParseRequestURI(serviceURL); err != nil { + return nil, err + } + service := &JsonrpcReadService{ + Client: client.NewClient(serviceURL), + } + return service, nil +} diff --git a/cmd/cartesi-rollups-cli/root/read/service/repository.go b/cmd/cartesi-rollups-cli/root/read/service/repository.go new file mode 100644 index 000000000..49a0fe1f3 --- /dev/null +++ b/cmd/cartesi-rollups-cli/root/read/service/repository.go @@ -0,0 +1,820 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +package service + +import ( + "context" + "encoding/json" + "errors" + + "github.com/cartesi/rollups-node/internal/config" + "github.com/cartesi/rollups-node/internal/jsonrpc" + "github.com/cartesi/rollups-node/internal/model" + "github.com/cartesi/rollups-node/internal/repository" + "github.com/cartesi/rollups-node/internal/repository/factory" + "github.com/cartesi/rollups-node/pkg/contracts/inputs" + "github.com/cartesi/rollups-node/pkg/contracts/outputs" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +type RepositoryReadService struct { + Repository repository.Repository + InputAbi *abi.ABI + OutputAbi *abi.ABI +} + +func (self *RepositoryReadService) GetApplication(ctx context.Context, params jsonrpc.GetApplicationParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + + data, err := repo.GetApplication(ctx, application) + if err != nil { + return nil, err + } + if data == nil { + return nil, errors.New("not found") + } + + response := map[string]any{ + "data": data, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) GetEpoch(ctx context.Context, params jsonrpc.GetEpochParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + epochIndex, err := config.ToIndexFromString(params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + + data, err := repo.GetEpoch(ctx, application, epochIndex) + if err != nil { + return nil, err + } + if data == nil { + return nil, errors.New("not found") + } + + response := map[string]any{ + "data": data, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) ListEpochs(ctx context.Context, params jsonrpc.ListEpochsParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + filter := repository.EpochFilter{} + pagination := repository.Pagination{} + // Add status filter if provided + if params.Status != nil { + var statusVal model.EpochStatus + err := statusVal.Scan(*params.Status) + if err != nil { + return nil, errors.New("invalid status: expected OPEN, CLOSED, INPUTS_PROCESSED, CLAIM_COMPUTED, CLAIM_SUBMITTED, CLAIM_ACCEPTED, CLAIM_REJECTED") + } + filter.Status = []model.EpochStatus{statusVal} + } + pagination.Limit = params.Limit + pagination.Offset = params.Offset + + data, total, err := repo.ListEpochs(ctx, application, filter, pagination, params.Descending) + if err != nil { + return nil, err + } + if len(data) == 0 { + app, err := repo.GetApplication(ctx, application) + if err != nil { + return nil, err + } + if app == nil { + return nil, errors.New("application not found") + } + data = make([]*model.Epoch, 0) + } + + response := map[string]any{ + "data": data, + "pagination": map[string]uint64{ + "total_count": total, + "limit": pagination.Limit, + "offset": pagination.Offset, + }, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) GetInput(ctx context.Context, params jsonrpc.GetInputParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + inputIndex, err := config.ToIndexFromString(params.InputIndex) + if err != nil { + return nil, errors.New("invalid input index: expected hex encoded") + } + + data, err := repo.GetInput(ctx, application, inputIndex) + if err != nil { + return nil, err + } + if data == nil { + return nil, errors.New("not found") + } + dataVal, err := jsonrpc.DecodeInput(data, self.InputAbi) + if err != nil { + dataVal = &jsonrpc.DecodedInput{Input: data} + } + + response := map[string]any{ + "data": dataVal, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) ListInputs(ctx context.Context, params jsonrpc.ListInputsParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + filter := repository.InputFilter{} + pagination := repository.Pagination{} + // Add epoch index filter if provided + if params.EpochIndex != nil { + epochIndexVal, err := config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + filter.EpochIndex = &epochIndexVal + } + // Add sender filter if provided + if params.Sender != nil { + senderVal, err := config.ToAddressFromString(*params.Sender) + if err != nil { + return nil, errors.New("invalid sender: expected hex encoded") + } + filter.Sender = &senderVal + } + pagination.Limit = params.Limit + pagination.Offset = params.Offset + + data, total, err := repo.ListInputs(ctx, application, filter, pagination, params.Descending) + if err != nil { + return nil, err + } + if len(data) == 0 { + app, err := repo.GetApplication(ctx, application) + if err != nil { + return nil, err + } + if app == nil { + return nil, errors.New("application not found") + } + data = make([]*model.Input, 0) + } + var dataVal []*jsonrpc.DecodedInput + for _, item := range data { + decoded, err := jsonrpc.DecodeInput(item, self.InputAbi) + if err != nil { + decoded = &jsonrpc.DecodedInput{Input: item} + } + dataVal = append(dataVal, decoded) + } + + response := map[string]any{ + "data": dataVal, + "pagination": map[string]uint64{ + "total_count": total, + "limit": pagination.Limit, + "offset": pagination.Offset, + }, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) GetOutput(ctx context.Context, params jsonrpc.GetOutputParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + outputIndex, err := config.ToIndexFromString(params.OutputIndex) + if err != nil { + return nil, errors.New("invalid output index: expected hex encoded") + } + + data, err := repo.GetOutput(ctx, application, outputIndex) + if err != nil { + return nil, err + } + if data == nil { + return nil, errors.New("not found") + } + dataVal, err := jsonrpc.DecodeOutput(data, self.OutputAbi) + if err != nil { + dataVal = &jsonrpc.DecodedOutput{Output: data, DecodedData: err.Error()} + } + + response := map[string]any{ + "data": dataVal, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) ListOutputs(ctx context.Context, params jsonrpc.ListOutputsParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + filter := repository.OutputFilter{} + pagination := repository.Pagination{} + // Add epoch index filter if provided + if params.EpochIndex != nil { + epochIndexVal, err := config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + filter.EpochIndex = &epochIndexVal + } + // Add input index filter if provided + if params.InputIndex != nil { + inputIndexVal, err := config.ToIndexFromString(*params.InputIndex) + if err != nil { + return nil, errors.New("invalid input index: expected hex encoded") + } + filter.InputIndex = &inputIndexVal + } + // Add output type filter if provided + if params.OutputType != nil { + outputTypeVal, err := jsonrpc.ParseOutputType(*params.OutputType) + if err != nil { + return nil, errors.New("invalid output type: expected first 4 bytes of raw data hex encoded") + } + filter.OutputType = &outputTypeVal + } + // Add voucher address filter if provided + if params.VoucherAddress != nil { + voucherAddressVal, err := config.ToAddressFromString(*params.VoucherAddress) + if err != nil { + return nil, errors.New("invalid voucher address: expected hex encoded") + } + filter.VoucherAddress = &voucherAddressVal + } + pagination.Limit = params.Limit + pagination.Offset = params.Offset + + data, total, err := repo.ListOutputs(ctx, application, filter, pagination, params.Descending) + if err != nil { + return nil, err + } + if len(data) == 0 { + app, err := repo.GetApplication(ctx, application) + if err != nil { + return nil, err + } + if app == nil { + return nil, errors.New("application not found") + } + data = make([]*model.Output, 0) + } + var dataVal []*jsonrpc.DecodedOutput + for _, item := range data { + decoded, err := jsonrpc.DecodeOutput(item, self.OutputAbi) + if err != nil { + decoded = &jsonrpc.DecodedOutput{Output: item, DecodedData: err.Error()} + } + dataVal = append(dataVal, decoded) + } + + response := map[string]any{ + "data": dataVal, + "pagination": map[string]uint64{ + "total_count": total, + "limit": pagination.Limit, + "offset": pagination.Offset, + }, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) GetReport(ctx context.Context, params jsonrpc.GetReportParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + reportIndex, err := config.ToIndexFromString(params.ReportIndex) + if err != nil { + return nil, errors.New("invalid report index: expected hex encoded") + } + + data, err := repo.GetReport(ctx, application, reportIndex) + if err != nil { + return nil, err + } + if data == nil { + return nil, errors.New("not found") + } + + response := map[string]any{ + "data": data, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) ListReports(ctx context.Context, params jsonrpc.ListReportsParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + filter := repository.ReportFilter{} + pagination := repository.Pagination{} + // Add epoch index filter if provided + if params.EpochIndex != nil { + epochIndexVal, err := config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + filter.EpochIndex = &epochIndexVal + } + // Add input index filter if provided + if params.InputIndex != nil { + inputIndexVal, err := config.ToIndexFromString(*params.InputIndex) + if err != nil { + return nil, errors.New("invalid input index: expected hex encoded") + } + filter.InputIndex = &inputIndexVal + } + pagination.Limit = params.Limit + pagination.Offset = params.Offset + + data, total, err := repo.ListReports(ctx, application, filter, pagination, params.Descending) + if err != nil { + return nil, err + } + if len(data) == 0 { + app, err := repo.GetApplication(ctx, application) + if err != nil { + return nil, err + } + if app == nil { + return nil, errors.New("application not found") + } + data = make([]*model.Report, 0) + } + + response := map[string]any{ + "data": data, + "pagination": map[string]uint64{ + "total_count": total, + "limit": pagination.Limit, + "offset": pagination.Offset, + }, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) GetTournament(ctx context.Context, params jsonrpc.GetTournamentParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + _, err = config.ToAddressFromString(params.Address) + if err != nil { + return nil, errors.New("invalid address: expected hex encoded") + } + + data, err := repo.GetTournament(ctx, application, params.Address) + if err != nil { + return nil, err + } + if data == nil { + return nil, errors.New("not found") + } + + response := map[string]any{ + "data": data, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) ListTournaments(ctx context.Context, params jsonrpc.ListTournamentsParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + filter := repository.TournamentFilter{} + pagination := repository.Pagination{} + // Add epoch index filter if provided + if params.EpochIndex != nil { + epochIndexVal, err := config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + filter.EpochIndex = &epochIndexVal + } + // Add level filter if provided + if params.Level != nil { + levelVal, err := config.ToIndexFromString(*params.Level) + if err != nil { + return nil, errors.New("invalid level: expected hex encoded") + } + filter.Level = &levelVal + } + // Add parent tournament address filter if provided + if params.ParentTournamentAddress != nil { + parentTournamentAddressVal, err := config.ToAddressFromString(*params.ParentTournamentAddress) + if err != nil { + return nil, errors.New("invalid parent tournament address: expected hex encoded") + } + filter.ParentTournamentAddress = &parentTournamentAddressVal + } + // Add parent match ID hash filter if provided + if params.ParentMatchIDHash != nil { + parentMatchIDHashVal, err := config.ToHashFromString(*params.ParentMatchIDHash) + if err != nil { + return nil, errors.New("invalid parent match ID hash: expected hex encoded") + } + filter.ParentMatchIDHash = &parentMatchIDHashVal + } + pagination.Limit = params.Limit + pagination.Offset = params.Offset + + data, total, err := repo.ListTournaments(ctx, application, filter, pagination, params.Descending) + if err != nil { + return nil, err + } + if len(data) == 0 { + app, err := repo.GetApplication(ctx, application) + if err != nil { + return nil, err + } + if app == nil { + return nil, errors.New("application not found") + } + data = make([]*model.Tournament, 0) + } + + response := map[string]any{ + "data": data, + "pagination": map[string]uint64{ + "total_count": total, + "limit": pagination.Limit, + "offset": pagination.Offset, + }, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) GetCommitment(ctx context.Context, params jsonrpc.GetCommitmentParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + epochIndex, err := config.ToIndexFromString(params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + _, err = config.ToAddressFromString(params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + _, err = config.ToHashFromString(params.Commitment) + if err != nil { + return nil, errors.New("invalid commitment: expected hex encoded") + } + + data, err := repo.GetCommitment(ctx, application, epochIndex, params.TournamentAddress, params.Commitment) + if err != nil { + return nil, err + } + if data == nil { + return nil, errors.New("not found") + } + + response := map[string]any{ + "data": data, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) ListCommitments(ctx context.Context, params jsonrpc.ListCommitmentsParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + filter := repository.CommitmentFilter{} + pagination := repository.Pagination{} + // Add epoch index filter if provided + if params.EpochIndex != nil { + epochIndexVal, err := config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + filter.EpochIndex = &epochIndexVal + } + // Add tournament address filter if provided + if params.TournamentAddress != nil { + _, err = config.ToAddressFromString(*params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + filter.TournamentAddress = params.TournamentAddress + } + pagination.Limit = params.Limit + pagination.Offset = params.Offset + + data, total, err := repo.ListCommitments(ctx, application, filter, pagination, params.Descending) + if err != nil { + return nil, err + } + if len(data) == 0 { + app, err := repo.GetApplication(ctx, application) + if err != nil { + return nil, err + } + if app == nil { + return nil, errors.New("application not found") + } + data = make([]*model.Commitment, 0) + } + + response := map[string]any{ + "data": data, + "pagination": map[string]uint64{ + "total_count": total, + "limit": pagination.Limit, + "offset": pagination.Offset, + }, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) GetMatch(ctx context.Context, params jsonrpc.GetMatchParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + epochIndex, err := config.ToIndexFromString(params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + _, err = config.ToAddressFromString(params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + _, err = config.ToHashFromString(params.IDHash) + if err != nil { + return nil, errors.New("invalid ID hash: expected hex encoded") + } + + data, err := repo.GetMatch(ctx, application, epochIndex, params.TournamentAddress, params.IDHash) + if err != nil { + return nil, err + } + if data == nil { + return nil, errors.New("not found") + } + + response := map[string]any{ + "data": data, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) ListMatches(ctx context.Context, params jsonrpc.ListMatchesParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + filter := repository.MatchFilter{} + pagination := repository.Pagination{} + // Add epoch index filter if provided + if params.EpochIndex != nil { + epochIndexVal, err := config.ToIndexFromString(*params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + filter.EpochIndex = &epochIndexVal + } + // Add tournament address filter if provided + if params.TournamentAddress != nil { + _, err = config.ToAddressFromString(*params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + filter.TournamentAddress = params.TournamentAddress + } + pagination.Limit = params.Limit + pagination.Offset = params.Offset + + data, total, err := repo.ListMatches(ctx, application, filter, pagination, params.Descending) + if err != nil { + return nil, err + } + if len(data) == 0 { + app, err := repo.GetApplication(ctx, application) + if err != nil { + return nil, err + } + if app == nil { + return nil, errors.New("application not found") + } + data = make([]*model.Match, 0) + } + + response := map[string]any{ + "data": data, + "pagination": map[string]uint64{ + "total_count": total, + "limit": pagination.Limit, + "offset": pagination.Offset, + }, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) GetMatchAdvanced(ctx context.Context, params jsonrpc.GetMatchAdvancedParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + epochIndex, err := config.ToIndexFromString(params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + _, err = config.ToAddressFromString(params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + _, err = config.ToHashFromString(params.IDHash) + if err != nil { + return nil, errors.New("invalid ID hash: expected hex encoded") + } + _, err = config.ToHashFromString(params.Parent) + if err != nil { + return nil, errors.New("invalid parent: expected hex encoded") + } + + data, err := repo.GetMatchAdvanced(ctx, application, epochIndex, params.TournamentAddress, params.IDHash, params.Parent) + if err != nil { + return nil, err + } + if data == nil { + return nil, errors.New("not found") + } + + response := map[string]any{ + "data": data, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) ListMatchAdvances(ctx context.Context, params jsonrpc.ListMatchAdvancesParams) (json.RawMessage, error) { + repo := self.Repository + application, err := config.ToApplicationNameOrAddressFromString(params.Application) + if err != nil { + return nil, errors.New("invalid application: expected application name or address") + } + epochIndex, err := config.ToIndexFromString(params.EpochIndex) + if err != nil { + return nil, errors.New("invalid epoch index: expected hex encoded") + } + _, err = config.ToAddressFromString(params.TournamentAddress) + if err != nil { + return nil, errors.New("invalid tournament address: expected hex encoded") + } + _, err = config.ToHashFromString(params.IDHash) + if err != nil { + return nil, errors.New("invalid ID hash: expected hex encoded") + } + pagination := repository.Pagination{} + pagination.Limit = params.Limit + pagination.Offset = params.Offset + + data, total, err := repo.ListMatchAdvances(ctx, application, epochIndex, params.TournamentAddress, params.IDHash, pagination, params.Descending) + if err != nil { + return nil, err + } + if len(data) == 0 { + app, err := repo.GetApplication(ctx, application) + if err != nil { + return nil, err + } + if app == nil { + return nil, errors.New("application not found") + } + data = make([]*model.MatchAdvanced, 0) + } + + response := map[string]any{ + "data": data, + "pagination": map[string]uint64{ + "total_count": total, + "limit": pagination.Limit, + "offset": pagination.Offset, + }, + } + + result, err := json.Marshal(response) + + return json.RawMessage(result), err +} + +func (self *RepositoryReadService) Close() { + self.Repository.Close() +} + +func NewRepositoryReadService(ctx context.Context, url string) (ReadService, error) { + inputAbi, err := inputs.InputsMetaData.GetAbi() + if err != nil { + return nil, err + } + outputAbi, err := outputs.OutputsMetaData.GetAbi() + if err != nil { + return nil, err + } + repo, err := factory.NewRepositoryFromConnectionString(ctx, url) + if err != nil { + return nil, err + } + + service := &RepositoryReadService{ + Repository: repo, + InputAbi: inputAbi, + OutputAbi: outputAbi, + } + return service, nil +} diff --git a/cmd/cartesi-rollups-cli/root/read/service/types.go b/cmd/cartesi-rollups-cli/root/read/service/types.go new file mode 100644 index 000000000..5604e4528 --- /dev/null +++ b/cmd/cartesi-rollups-cli/root/read/service/types.go @@ -0,0 +1,47 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +package service + +import ( + "context" + "encoding/json" + + "github.com/cartesi/rollups-node/internal/config" + "github.com/cartesi/rollups-node/internal/jsonrpc" + + "github.com/spf13/cobra" +) + +type ReadService interface { + GetApplication(ctx context.Context, params jsonrpc.GetApplicationParams) (json.RawMessage, error) + GetEpoch(ctx context.Context, params jsonrpc.GetEpochParams) (json.RawMessage, error) + ListEpochs(ctx context.Context, params jsonrpc.ListEpochsParams) (json.RawMessage, error) + GetInput(ctx context.Context, params jsonrpc.GetInputParams) (json.RawMessage, error) + ListInputs(ctx context.Context, params jsonrpc.ListInputsParams) (json.RawMessage, error) + GetOutput(ctx context.Context, params jsonrpc.GetOutputParams) (json.RawMessage, error) + ListOutputs(ctx context.Context, params jsonrpc.ListOutputsParams) (json.RawMessage, error) + GetReport(ctx context.Context, params jsonrpc.GetReportParams) (json.RawMessage, error) + ListReports(ctx context.Context, params jsonrpc.ListReportsParams) (json.RawMessage, error) + GetTournament(ctx context.Context, params jsonrpc.GetTournamentParams) (json.RawMessage, error) + ListTournaments(ctx context.Context, params jsonrpc.ListTournamentsParams) (json.RawMessage, error) + GetCommitment(ctx context.Context, params jsonrpc.GetCommitmentParams) (json.RawMessage, error) + ListCommitments(ctx context.Context, params jsonrpc.ListCommitmentsParams) (json.RawMessage, error) + GetMatch(ctx context.Context, params jsonrpc.GetMatchParams) (json.RawMessage, error) + ListMatches(ctx context.Context, params jsonrpc.ListMatchesParams) (json.RawMessage, error) + GetMatchAdvanced(ctx context.Context, params jsonrpc.GetMatchAdvancedParams) (json.RawMessage, error) + ListMatchAdvances(ctx context.Context, params jsonrpc.ListMatchAdvancesParams) (json.RawMessage, error) + Close() +} + +func CreateReadService(ctx context.Context, cmd *cobra.Command) (ReadService, error) { + url, err := config.GetJsonrpcApiUrl() + if url == "" && err.Error() == "CARTESI_JSONRPC_API_URL: variable not defined" { + dsn, err := config.GetDatabaseConnection() + if err != nil { + return nil, err + } + return NewRepositoryReadService(ctx, dsn.String()) + } + return NewJsonrpcReadService(ctx, url) +} diff --git a/cmd/cartesi-rollups-cli/root/read/tournaments/tournaments.go b/cmd/cartesi-rollups-cli/root/read/tournaments/tournaments.go index 48eb43b5c..7be53a4fa 100644 --- a/cmd/cartesi-rollups-cli/root/read/tournaments/tournaments.go +++ b/cmd/cartesi-rollups-cli/root/read/tournaments/tournaments.go @@ -5,73 +5,73 @@ package tournaments import ( "encoding/json" - "errors" "fmt" + "github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/root/read/service" "github.com/cartesi/rollups-node/internal/config" "github.com/cartesi/rollups-node/internal/jsonrpc" - "github.com/cartesi/rollups-node/internal/model" - "github.com/cartesi/rollups-node/internal/repository" - "github.com/cartesi/rollups-node/internal/repository/factory" "github.com/spf13/cobra" ) var Cmd = &cobra.Command{ - Use: "tournaments [application-name-or-address] [tournament-address]", + Use: "tournaments [address]", Short: "Reads tournaments", Example: examples, - Args: cobra.RangeArgs(1, 2), // nolint: mnd + Args: cobra.RangeArgs(1, 2), //nolint: mnd Run: run, Long: ` +Arguments: + application name or address + [address] hex encoded + Supported Environment Variables: CARTESI_DATABASE_CONNECTION Database connection string`, } -const examples = `# Read all tournaments: -cartesi-rollups-cli read tournaments echo-dapp - -# Read specific tournament by address: -cartesi-rollups-cli read tournaments echo-dapp 0x0123456789abcdef0123456789abcdef0123456789abcdef +const examples = `# Read specific tournament: +cartesi-rollups-cli read tournaments echo-dapp 0x0073a8637d98649717bdc02ecb439c80aa8a10d0 -# Read tournaments filtered by level: -cartesi-rollups-cli read tournaments echo-dapp --level 1 +# Read all tournaments: +cartesi-rollups-cli read tournaments echo-dapp -# Read tournaments with pagination: -cartesi-rollups-cli read tournaments echo-dapp --limit 10 --offset 20` +# Read all tournaments with filter: +cartesi-rollups-cli read tournaments echo-dapp --epoch-index 10 --level 10 --parent-tournament-address 0x95eac57f9d67c5e0f255d5a19eb5d3fd00cafa73 --parent-match-id-hash 0xdb99c9cdb2e2070a4e4e633c2e6874648dfe3971d14da843465b3d950df3dd19 -func init() { - origHelpFunc := Cmd.HelpFunc() - Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { - command.Flags().Lookup("verbose").Hidden = false - command.Flags().Lookup("database-connection").Hidden = false - origHelpFunc(command, strings) - }) -} +# Read all tournaments with pagination: +cartesi-rollups-cli read tournaments echo-dapp --limit 10 --offset 10 +` var ( - epochIndex uint64 - level uint64 + epochIndex string + level string parentTournamentAddress string - parentMatchIdHash string + parentMatchIDHash string limit uint64 offset uint64 ) func init() { - Cmd.Flags().Uint64Var(&epochIndex, "epoch-index", 0, + Cmd.Flags().StringVar(&epochIndex, "epoch-index", "", "Filter tournaments by epoch index (decimal or hex encoded)") - Cmd.Flags().Uint64Var(&level, "level", 0, + Cmd.Flags().StringVar(&level, "level", "", "Filter tournaments by level (decimal or hex encoded)") Cmd.Flags().StringVar(&parentTournamentAddress, "parent-tournament-address", "", - "Filter tournaments by its parent address (hex encoded)") - Cmd.Flags().StringVar(&parentMatchIdHash, "parent-match-id-hash", "", - "Filter tournaments by its parent match ID hash (hex encoded)") - Cmd.Flags().Uint64Var(&limit, "limit", 50, // nolint: mnd + "Filter tournaments by parent tournament address (hex encoded)") + Cmd.Flags().StringVar(&parentMatchIDHash, "parent-match-id-hash", "", + "Filter tournaments by parent match ID hash (hex encoded)") + Cmd.Flags().Uint64Var(&limit, "limit", 50, //nolint: mnd "Maximum number of tournaments to return") Cmd.Flags().Uint64Var(&offset, "offset", 0, "Starting point for the list of tournaments") + origHelpFunc := Cmd.HelpFunc() + Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) { + command.Flags().Lookup("verbose").Hidden = false + command.Flags().Lookup("database-connection").Hidden = false + origHelpFunc(command, strings) + }) + Cmd.PreRunE = func(cmd *cobra.Command, args []string) error { if limit > jsonrpc.LIST_ITEM_LIMIT { return fmt.Errorf("limit cannot exceed %d", jsonrpc.LIST_ITEM_LIMIT) @@ -85,109 +85,51 @@ func init() { func run(cmd *cobra.Command, args []string) { ctx := cmd.Context() - nameOrAddress, err := config.ToApplicationNameOrAddressFromString(args[0]) - cobra.CheckErr(err) - - dsn, err := config.GetDatabaseConnection() - cobra.CheckErr(err) - - repo, err := factory.NewRepositoryFromConnectionString(ctx, dsn.String()) + readServ, err := service.CreateReadService(ctx, cmd) cobra.CheckErr(err) - defer repo.Close() - - var result []byte - if len(args) == 2 { // nolint: mnd - // Get a specific tournament by address - - // TODO: [maia] check out if the following check is necessary, because - // such conversion is already done in the 'repo.GetTournament' function, - // but it doesn't seem to check for invalid addresses. - tournamentAddressHex := args[1] - _, err := config.ToAddressFromString(tournamentAddressHex) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid tournament address: %w", err)) - } + defer readServ.Close() - tournament, err := repo.GetTournament(ctx, nameOrAddress, tournamentAddressHex) - cobra.CheckErr(err) - if tournament == nil { - cobra.CheckErr(errors.New("not found")) - } - - // Format response to match JSON-RPC API - response := struct { - Data *model.Tournament `json:"data"` - }{ - Data: tournament, - } + var result json.RawMessage + if len(args) >= 2 { + var params jsonrpc.GetTournamentParams + params.Application = args[0] + params.Address = args[1] - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.GetTournament(ctx, params) } else { - // Create filter based on flags - filter := repository.TournamentFilter{} + var params jsonrpc.ListTournamentsParams + params.Application = args[0] // Add epoch index filter if provided if cmd.Flags().Changed("epoch-index") { - filter.EpochIndex = &epochIndex + epochIndexHex := config.AsHexString(epochIndex) + params.EpochIndex = &epochIndexHex } // Add level filter if provided if cmd.Flags().Changed("level") { - filter.Level = &level + levelHex := config.AsHexString(level) + params.Level = &levelHex } // Add parent tournament address filter if provided if cmd.Flags().Changed("parent-tournament-address") { - parentAddr, err := config.ToAddressFromString(parentTournamentAddress) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid tournament address: %w", err)) - } - filter.ParentTournamentAddress = &parentAddr + params.ParentTournamentAddress = &parentTournamentAddress } // Add parent match ID hash filter if provided if cmd.Flags().Changed("parent-match-id-hash") { - matchHash, err := config.ToHashFromString(parentMatchIdHash) - if err != nil { - cobra.CheckErr(fmt.Errorf("invalid parent match ID hash: %w", err)) - } - filter.ParentMatchIDHash = &matchHash - } - - // Limit is validated in PreRunE - - // List tournaments with filters - tournaments, total, err := repo.ListTournaments(ctx, nameOrAddress, filter, repository.Pagination{ - Limit: limit, - Offset: offset, - }, false) - cobra.CheckErr(err) - - // Format response to match JSON-RPC API - response := struct { - Data []*model.Tournament `json:"data"` - Pagination struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - } `json:"pagination"` - }{ - Data: tournaments, - Pagination: struct { - TotalCount uint64 `json:"total_count"` - Limit uint64 `json:"limit"` - Offset uint64 `json:"offset"` - }{ - TotalCount: total, - Limit: limit, - Offset: offset, - }, + params.ParentMatchIDHash = &parentMatchIDHash } + params.Limit = limit + params.Offset = offset - result, err = json.MarshalIndent(response, "", " ") - cobra.CheckErr(err) + result, err = readServ.ListTournaments(ctx, params) } + cobra.CheckErr(err) + + formatted, err := json.MarshalIndent(result, "", " ") + cobra.CheckErr(err) - fmt.Println(string(result)) + fmt.Println(string(formatted)) } diff --git a/internal/config/config.go b/internal/config/config.go index 5a9446e64..e4669a502 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -7,6 +7,7 @@ package config import ( "encoding/hex" + "errors" "fmt" "log/slog" "net/url" @@ -85,6 +86,26 @@ func ToUint64FromDecimalOrHexString(s string) (uint64, error) { return ToUint64FromString(s) } +func AsHexString(s string) string { + val, err := ToUint64FromDecimalOrHexString(s) + if err != nil { + return s + } + return fmt.Sprintf("0x%x", val) +} + +func ToIndexFromString(indexString string) (uint64, error) { + if len(indexString) < 3 || (!strings.HasPrefix(indexString, "0x") && !strings.HasPrefix(indexString, "0X")) { + return 0, errors.New("expected hex encoded value") + } + str := indexString[2:] + index, err := strconv.ParseUint(str, 16, 64) + if err != nil { + return 0, err + } + return index, nil +} + func ToStringFromString(s string) (string, error) { return s, nil } diff --git a/internal/config/generate/Config.toml b/internal/config/generate/Config.toml index c19a3386e..3b11a7294 100644 --- a/internal/config/generate/Config.toml +++ b/internal/config/generate/Config.toml @@ -322,15 +322,28 @@ used-by = ["advancer", "claimer", "evmreader", "validator", "jsonrpc", "node", " default = ":10011" go-type = "string" description = """ -HTTP address for the jsonrpc api.""" -used-by = ["jsonrpc", "node", "cli"] +HTTP address for the JSON-RPC API.""" +used-by = ["jsonrpc", "node"] [http.CARTESI_INSPECT_ADDRESS] default = ":10012" go-type = "string" description = """ HTTP address for inspect.""" -used-by = ["advancer", "node", "cli"] +used-by = ["advancer", "node"] + +[http.CARTESI_JSONRPC_API_URL] +go-type = "string" +description = """ +URL for the JSON-RPC API.""" +used-by = ["cli"] + +[http.CARTESI_INSPECT_URL] +default = "http://localhost:10012" +go-type = "string" +description = """ +URL of the inspect service.""" +used-by = ["cli"] # # Remote Cartesi Machine diff --git a/internal/config/generated.go b/internal/config/generated.go index 443217d51..1ba5db9a4 100644 --- a/internal/config/generated.go +++ b/internal/config/generated.go @@ -46,7 +46,9 @@ const ( FEATURE_JSONRPC_API_ENABLED = "CARTESI_FEATURE_JSONRPC_API_ENABLED" FEATURE_MACHINE_HASH_CHECK_ENABLED = "CARTESI_FEATURE_MACHINE_HASH_CHECK_ENABLED" INSPECT_ADDRESS = "CARTESI_INSPECT_ADDRESS" + INSPECT_URL = "CARTESI_INSPECT_URL" JSONRPC_API_ADDRESS = "CARTESI_JSONRPC_API_ADDRESS" + JSONRPC_API_URL = "CARTESI_JSONRPC_API_URL" TELEMETRY_ADDRESS = "CARTESI_TELEMETRY_ADDRESS" LOG_COLOR = "CARTESI_LOG_COLOR" LOG_LEVEL = "CARTESI_LOG_LEVEL" @@ -127,8 +129,12 @@ func SetDefaults() { viper.SetDefault(INSPECT_ADDRESS, ":10012") + viper.SetDefault(INSPECT_URL, "http://localhost:10012") + viper.SetDefault(JSONRPC_API_ADDRESS, ":10011") + // no default for CARTESI_JSONRPC_API_URL + // no default for CARTESI_TELEMETRY_ADDRESS viper.SetDefault(LOG_COLOR, "true") @@ -667,7 +673,7 @@ type JsonrpcConfig struct { // for more information. DatabaseConnection URL `mapstructure:"CARTESI_DATABASE_CONNECTION"` - // HTTP address for the jsonrpc api. + // HTTP address for the JSON-RPC API. JsonrpcApiAddress string `mapstructure:"CARTESI_JSONRPC_API_ADDRESS"` // HTTP address for telemetry service. @@ -793,7 +799,7 @@ type NodeConfig struct { // HTTP address for inspect. InspectAddress string `mapstructure:"CARTESI_INSPECT_ADDRESS"` - // HTTP address for the jsonrpc api. + // HTTP address for the JSON-RPC API. JsonrpcApiAddress string `mapstructure:"CARTESI_JSONRPC_API_ADDRESS"` // HTTP address for telemetry service. @@ -1794,6 +1800,19 @@ func GetInspectAddress() (string, error) { return notDefinedstring(), fmt.Errorf("%s: %w", INSPECT_ADDRESS, ErrNotDefined) } +// GetInspectUrl returns the value for the environment variable CARTESI_INSPECT_URL. +func GetInspectUrl() (string, error) { + s := viper.GetString(INSPECT_URL) + if s != "" { + v, err := toString(s) + if err != nil { + return v, fmt.Errorf("failed to parse %s: %w", INSPECT_URL, err) + } + return v, nil + } + return notDefinedstring(), fmt.Errorf("%s: %w", INSPECT_URL, ErrNotDefined) +} + // GetJsonrpcApiAddress returns the value for the environment variable CARTESI_JSONRPC_API_ADDRESS. func GetJsonrpcApiAddress() (string, error) { s := viper.GetString(JSONRPC_API_ADDRESS) @@ -1807,6 +1826,19 @@ func GetJsonrpcApiAddress() (string, error) { return notDefinedstring(), fmt.Errorf("%s: %w", JSONRPC_API_ADDRESS, ErrNotDefined) } +// GetJsonrpcApiUrl returns the value for the environment variable CARTESI_JSONRPC_API_URL. +func GetJsonrpcApiUrl() (string, error) { + s := viper.GetString(JSONRPC_API_URL) + if s != "" { + v, err := toString(s) + if err != nil { + return v, fmt.Errorf("failed to parse %s: %w", JSONRPC_API_URL, err) + } + return v, nil + } + return notDefinedstring(), fmt.Errorf("%s: %w", JSONRPC_API_URL, ErrNotDefined) +} + // GetTelemetryAddress returns the value for the environment variable CARTESI_TELEMETRY_ADDRESS. func GetTelemetryAddress() (string, error) { s := viper.GetString(TELEMETRY_ADDRESS) diff --git a/internal/jsonrpc/jsonrpc.go b/internal/jsonrpc/jsonrpc.go index e2ed526b3..26752d53e 100644 --- a/internal/jsonrpc/jsonrpc.go +++ b/internal/jsonrpc/jsonrpc.go @@ -11,7 +11,6 @@ import ( "fmt" "io" "net/http" - "strconv" "strings" "github.com/cartesi/rollups-node/internal/config" @@ -281,18 +280,6 @@ func handleListEpochs(s *Service, w http.ResponseWriter, r *http.Request, req RP writeRPCResult(w, req.ID, result) } -func parseIndex(indexString string, field string) (uint64, error) { - if len(indexString) < 3 || (!strings.HasPrefix(indexString, "0x") && !strings.HasPrefix(indexString, "0X")) { - return 0, fmt.Errorf("invalid %s: expected hex encoded value", field) - } - str := indexString[2:] - index, err := strconv.ParseUint(str, 16, 64) - if err != nil { - return 0, fmt.Errorf("invalid %s: %v", field, "error parsing") - } - return index, nil -} - func handleGetEpoch(s *Service, w http.ResponseWriter, r *http.Request, req RPCRequest) { var params GetEpochParams if err := UnmarshalParams(req.Params, ¶ms); err != nil { @@ -307,9 +294,9 @@ func handleGetEpoch(s *Service, w http.ResponseWriter, r *http.Request, req RPCR return } - index, err := parseIndex(params.EpochIndex, "epoch_index") + index, err := config.ToIndexFromString(params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } @@ -395,9 +382,9 @@ func handleListInputs(s *Service, w http.ResponseWriter, r *http.Request, req RP // Create input filter based on params inputFilter := repository.InputFilter{} if params.EpochIndex != nil { - epochIndex, err := parseIndex(*params.EpochIndex, "epoch_index") + epochIndex, err := config.ToIndexFromString(*params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } inputFilter.EpochIndex = &epochIndex @@ -473,9 +460,9 @@ func handleGetInput(s *Service, w http.ResponseWriter, r *http.Request, req RPCR return } - index, err := parseIndex(params.InputIndex, "input_index") + index, err := config.ToIndexFromString(params.InputIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid input index: %v", err), nil) return } @@ -519,7 +506,7 @@ func handleGetProcessedInputCount(s *Service, w http.ResponseWriter, r *http.Req return } - processedInputs, err := s.repository.GetProcessedInputs(r.Context(), params.Application) + processedInputs, err := s.repository.GetProcessedInputCount(r.Context(), params.Application) if errors.Is(err, repository.ErrNotFound) { writeRPCError(w, req.ID, JSONRPC_RESOURCE_NOT_FOUND, "Application not found", nil) return @@ -581,18 +568,18 @@ func handleListOutputs(s *Service, w http.ResponseWriter, r *http.Request, req R // Create output filter based on params outputFilter := repository.OutputFilter{} if params.EpochIndex != nil { - epochIndex, err := parseIndex(*params.EpochIndex, "epoch_index") + epochIndex, err := config.ToIndexFromString(*params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } outputFilter.EpochIndex = &epochIndex } if params.InputIndex != nil { - inputIndex, err := parseIndex(*params.InputIndex, "input_index") + inputIndex, err := config.ToIndexFromString(*params.InputIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid input index: %v", err), nil) return } outputFilter.InputIndex = &inputIndex @@ -679,9 +666,9 @@ func handleGetOutput(s *Service, w http.ResponseWriter, r *http.Request, req RPC return } - index, err := parseIndex(params.OutputIndex, "output_index") + index, err := config.ToIndexFromString(params.OutputIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid output index: %v", err), nil) return } @@ -737,18 +724,18 @@ func handleListReports(s *Service, w http.ResponseWriter, r *http.Request, req R // Create report filter based on params reportFilter := repository.ReportFilter{} if params.EpochIndex != nil { - epochIndex, err := parseIndex(*params.EpochIndex, "epoch_index") + epochIndex, err := config.ToIndexFromString(*params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } reportFilter.EpochIndex = &epochIndex } if params.InputIndex != nil { - inputIndex, err := parseIndex(*params.InputIndex, "input_index") + inputIndex, err := config.ToIndexFromString(*params.InputIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid input index: %v", err), nil) return } reportFilter.InputIndex = &inputIndex @@ -809,9 +796,9 @@ func handleGetReport(s *Service, w http.ResponseWriter, r *http.Request, req RPC return } - index, err := parseIndex(params.ReportIndex, "report_index") + index, err := config.ToIndexFromString(params.ReportIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid report index: %v", err), nil) return } @@ -862,18 +849,18 @@ func handleListTournaments(s *Service, w http.ResponseWriter, r *http.Request, r // Create tournament filter based on params tournamentFilter := repository.TournamentFilter{} if params.EpochIndex != nil { - epochIndex, err := parseIndex(*params.EpochIndex, "epoch_index") + epochIndex, err := config.ToIndexFromString(*params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } tournamentFilter.EpochIndex = &epochIndex } if params.Level != nil { - level, err := parseIndex(*params.Level, "level") + level, err := config.ToIndexFromString(*params.Level) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid level: %v", err), nil) return } tournamentFilter.Level = &level @@ -1002,9 +989,9 @@ func handleListCommitments(s *Service, w http.ResponseWriter, r *http.Request, r // Create commitment filter based on params commitmentFilter := repository.CommitmentFilter{} if params.EpochIndex != nil { - epochIndex, err := parseIndex(*params.EpochIndex, "epoch_index") + epochIndex, err := config.ToIndexFromString(*params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } commitmentFilter.EpochIndex = &epochIndex @@ -1072,9 +1059,9 @@ func handleGetCommitment(s *Service, w http.ResponseWriter, r *http.Request, req return } - epochIndex, err := parseIndex(params.EpochIndex, "epoch_index") + epochIndex, err := config.ToIndexFromString(params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } @@ -1087,7 +1074,7 @@ func handleGetCommitment(s *Service, w http.ResponseWriter, r *http.Request, req writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, "Invalid commitment hex: Empty string", nil) return } - if _, err := hex.DecodeString(params.Commitment); err != nil { + if _, err := config.ToHashFromString(params.Commitment); err != nil { writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid commitment hex: %v", err), nil) return } @@ -1139,9 +1126,9 @@ func handleListMatches(s *Service, w http.ResponseWriter, r *http.Request, req R // Create match filter based on params matchFilter := repository.MatchFilter{} if params.EpochIndex != nil { - epochIndex, err := parseIndex(*params.EpochIndex, "epoch_index") + epochIndex, err := config.ToIndexFromString(*params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } matchFilter.EpochIndex = &epochIndex @@ -1209,9 +1196,9 @@ func handleGetMatch(s *Service, w http.ResponseWriter, r *http.Request, req RPCR return } - epochIndex, err := parseIndex(params.EpochIndex, "epoch_index") + epochIndex, err := config.ToIndexFromString(params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } @@ -1270,9 +1257,9 @@ func handleListMatchAdvances(s *Service, w http.ResponseWriter, r *http.Request, } // Create match advance filter based on params - epochIndex, err := parseIndex(params.EpochIndex, "epoch_index") + epochIndex, err := config.ToIndexFromString(params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } @@ -1342,9 +1329,9 @@ func handleGetMatchAdvanced(s *Service, w http.ResponseWriter, r *http.Request, return } - epochIndex, err := parseIndex(params.EpochIndex, "epoch_index") + epochIndex, err := config.ToIndexFromString(params.EpochIndex) if err != nil { - writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, err.Error(), nil) + writeRPCError(w, req.ID, JSONRPC_INVALID_PARAMS, fmt.Sprintf("Invalid epoch index: %v", err), nil) return } diff --git a/internal/jsonrpc/jsonrpc_test.go b/internal/jsonrpc/jsonrpc_test.go index ca155a3ef..7c775fc16 100644 --- a/internal/jsonrpc/jsonrpc_test.go +++ b/internal/jsonrpc/jsonrpc_test.go @@ -236,7 +236,7 @@ func TestMethod(t *testing.T) { resp := testRPCResponse[any]{} assert.Nil(t, json.Unmarshal(body, &resp)) assert.Equal(t, JSONRPC_INVALID_PARAMS, resp.Error.Code) - assert.Equal(t, "invalid epoch_index: expected hex encoded value", resp.Error.Message) + assert.Equal(t, "Invalid epoch index: expected hex encoded value", resp.Error.Message) }) // failure: epoch not in the database -> resource not found @@ -336,7 +336,7 @@ func TestMethod(t *testing.T) { resp := testRPCResponse[any]{} assert.Nil(t, json.Unmarshal(body, &resp)) assert.Equal(t, JSONRPC_INVALID_PARAMS, resp.Error.Code) - assert.Equal(t, "invalid input_index: expected hex encoded value", resp.Error.Message) + assert.Equal(t, "Invalid input index: expected hex encoded value", resp.Error.Message) }) // failure: input_index not in database -> absent input @@ -527,7 +527,7 @@ func TestMethod(t *testing.T) { resp := testRPCResponse[any]{} assert.Nil(t, json.Unmarshal(body, &resp)) assert.Equal(t, JSONRPC_INVALID_PARAMS, resp.Error.Code) - assert.Equal(t, "invalid output_index: expected hex encoded value", resp.Error.Message) + assert.Equal(t, "Invalid output index: expected hex encoded value", resp.Error.Message) }) // failure: input_index not in database -> absent input @@ -706,7 +706,7 @@ func TestMethod(t *testing.T) { resp := testRPCResponse[any]{} assert.Nil(t, json.Unmarshal(body, &resp)) assert.Equal(t, JSONRPC_INVALID_PARAMS, resp.Error.Code) - assert.Equal(t, "invalid report_index: expected hex encoded value", resp.Error.Message) + assert.Equal(t, "Invalid report index: expected hex encoded value", resp.Error.Message) }) // success: output_index of Voucher in the database -> retrieve output @@ -1857,7 +1857,7 @@ func TestMethod(t *testing.T) { resp := testRPCResponse[any]{} assert.Nil(t, json.Unmarshal(body, &resp)) assert.Equal(t, JSONRPC_INVALID_PARAMS, resp.Error.Code) - assert.Equal(t, "invalid epoch_index: expected hex encoded value", resp.Error.Message) + assert.Equal(t, "Invalid epoch index: expected hex encoded value", resp.Error.Message) }) // failure: commitment not in the database -> resource not found @@ -1886,7 +1886,7 @@ func TestMethod(t *testing.T) { "application": "%v", "epoch_index": "%v", "tournament_address": "0x%020x", - "commitment": "%020x" + "commitment": "0x%020x" }, "id": 0 }`, numberToName(app), hexutil.EncodeUint64(nr+1), 0, 0)) @@ -1940,7 +1940,7 @@ func TestMethod(t *testing.T) { "application": "%v", "epoch_index": "%v", "tournament_address": "%v", - "commitment": "%020x" + "commitment": "0x%020x" }, "id": 0 }`, numberToName(app), hexutil.EncodeUint64(nr), address, commitment)) @@ -1978,7 +1978,7 @@ func TestMethod(t *testing.T) { resp := testRPCResponse[any]{} assert.Nil(t, json.Unmarshal(body, &resp)) assert.Equal(t, JSONRPC_INVALID_PARAMS, resp.Error.Code) - assert.Equal(t, "invalid epoch_index: expected hex encoded value", resp.Error.Message) + assert.Equal(t, "Invalid epoch index: expected hex encoded value", resp.Error.Message) }) // failure: epoch not in the database -> resource not found @@ -2109,7 +2109,7 @@ func TestMethod(t *testing.T) { resp := testRPCResponse[any]{} assert.Nil(t, json.Unmarshal(body, &resp)) assert.Equal(t, JSONRPC_INVALID_PARAMS, resp.Error.Code) - assert.Equal(t, "invalid epoch_index: expected hex encoded value", resp.Error.Message) + assert.Equal(t, "Invalid epoch index: expected hex encoded value", resp.Error.Message) }) // failure: epoch not in the database -> resource not found diff --git a/internal/repository/postgres/application.go b/internal/repository/postgres/application.go index a30ce142c..920766491 100644 --- a/internal/repository/postgres/application.go +++ b/internal/repository/postgres/application.go @@ -241,8 +241,8 @@ func (r *PostgresRepository) GetApplication( return &app, nil } -// GetProcessedInputs retrieves the ProcessedInputs field from a application by Name or address. -func (r *PostgresRepository) GetProcessedInputs( +// GetProcessedInputCount retrieves the ProcessedInputs field from an application by Name or address. +func (r *PostgresRepository) GetProcessedInputCount( ctx context.Context, nameOrAddress string, ) (uint64, error) { diff --git a/internal/repository/repository.go b/internal/repository/repository.go index a7314d17b..99308b9bb 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -82,7 +82,7 @@ type MatchFilter struct { type ApplicationRepository interface { CreateApplication(ctx context.Context, app *Application, withExecutionParameters bool) (int64, error) GetApplication(ctx context.Context, nameOrAddress string) (*Application, error) - GetProcessedInputs(ctx context.Context, nameOrAddress string) (uint64, error) + GetProcessedInputCount(ctx context.Context, nameOrAddress string) (uint64, error) UpdateApplication(ctx context.Context, app *Application) error UpdateApplicationState(ctx context.Context, appID int64, state ApplicationState, reason *string) error DeleteApplication(ctx context.Context, id int64) error diff --git a/internal/validator/validator_test.go b/internal/validator/validator_test.go index 55cb9a950..fb5cc866e 100644 --- a/internal/validator/validator_test.go +++ b/internal/validator/validator_test.go @@ -376,7 +376,7 @@ func (s *ValidatorSuite) TestValidateApplicationFailure() { Name: "dummy-application-name", } xerror := fmt.Errorf("Error") - + s.Run("getProcessedEpochsFailure", func() { repo.On("ListEpochs", mock.Anything, app.IApplicationAddress.String(), mock.Anything, mock.Anything, false, @@ -386,7 +386,7 @@ func (s *ValidatorSuite) TestValidateApplicationFailure() { s.NotNil(err) repo.AssertExpectations(s.T()) }) - + s.Run("computeMerkleTreeAndProofsFailure", func() { repo.On("ListEpochs", mock.Anything, app.IApplicationAddress.String(), mock.Anything, mock.Anything, false, @@ -400,7 +400,7 @@ func (s *ValidatorSuite) TestValidateApplicationFailure() { s.NotNil(err) repo.AssertExpectations(s.T()) }) - + s.Run("GetLastInputFailure", func() { input := Input{ EpochApplicationID: app.ID,