Skip to content

Commit 0552681

Browse files
committed
feat(jsonrpc): add operation to get Node info like its chain ID, version, and default block
1 parent 76dd5e4 commit 0552681

4 files changed

Lines changed: 136 additions & 0 deletions

File tree

internal/jsonrpc/api/response.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@ type ListResponse[T any] struct {
2020
type SingleResponse[T any] struct {
2121
Data T `json:"data"`
2222
}
23+
24+
type NodeInfo struct {
25+
ChainID string `json:"chain_id"`
26+
Version string `json:"version"`
27+
DefaultBlock string `json:"default_block"` // FINALIZED | SAFE | LATEST | PENDING
28+
}

internal/jsonrpc/jsonrpc-discover.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,10 +1365,31 @@
13651365
}
13661366
]
13671367
},
1368+
{
1369+
"name": "cartesi_getNodeInfo",
1370+
"summary": "Get node information",
1371+
"description": "Fetches the chain ID, semantic node version, and default blockchain block tag used by the node.",
1372+
"params": [],
1373+
"result": {
1374+
"name": "result",
1375+
"schema": {
1376+
"$ref": "#/components/schemas/NodeInfoResult"
1377+
}
1378+
},
1379+
"errors": [
1380+
{
1381+
"$ref": "#/components/errors/NodeConfigNotFound"
1382+
},
1383+
{
1384+
"$ref": "#/components/errors/InternalError"
1385+
}
1386+
]
1387+
},
13681388
{
13691389
"name": "cartesi_getChainId",
13701390
"summary": "Get node's chain ID",
13711391
"description": "Fetches the chain ID that node is operating on.",
1392+
"deprecated": true,
13721393
"params": [],
13731394
"result": {
13741395
"name": "result",
@@ -1389,6 +1410,7 @@
13891410
"name": "cartesi_getNodeVersion",
13901411
"summary": "Get node version",
13911412
"description": "Fetches the semantic version of the Cartesi rollups node.",
1413+
"deprecated": true,
13921414
"params": [],
13931415
"result": {
13941416
"name": "result",
@@ -2156,6 +2178,36 @@
21562178
}
21572179
}
21582180
},
2181+
"NodeInfo": {
2182+
"type": "object",
2183+
"properties": {
2184+
"chain_id": {
2185+
"$ref": "#/components/schemas/UnsignedInteger"
2186+
},
2187+
"version": {
2188+
"type": "string",
2189+
"format": "semver",
2190+
"pattern": "^[a-zA-Z0-9_-\\.]+$"
2191+
},
2192+
"default_block": {
2193+
"type": "string",
2194+
"enum": [
2195+
"FINALIZED",
2196+
"SAFE",
2197+
"LATEST",
2198+
"PENDING"
2199+
]
2200+
}
2201+
}
2202+
},
2203+
"NodeInfoResult": {
2204+
"type": "object",
2205+
"properties": {
2206+
"data": {
2207+
"$ref": "#/components/schemas/NodeInfo"
2208+
}
2209+
}
2210+
},
21592211
"NodeVersionResult": {
21602212
"type": "object",
21612213
"properties": {

internal/jsonrpc/jsonrpc.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ var jsonrpcHandlers = dispatchTable{
8080
"cartesi_getMatch": handleGetMatch,
8181
"cartesi_listMatchAdvances": handleListMatchAdvances,
8282
"cartesi_getMatchAdvanced": handleGetMatchAdvanced,
83+
"cartesi_getNodeInfo": handleGetNodeInfo,
8384
"cartesi_getChainId": handleGetChainID,
8485
"cartesi_getNodeVersion": handleGetNodeVersion,
8586
}
@@ -1359,6 +1360,23 @@ func handleGetMatchAdvanced(s *Service, r *http.Request, req RPCRequest) (any, e
13591360
return api.SingleResponse[*model.MatchAdvanced]{Data: matchAdvanced}, nil
13601361
}
13611362

1363+
func handleGetNodeInfo(s *Service, r *http.Request, _ RPCRequest) (any, error) {
1364+
cfg, err := repository.LoadNodeConfig[evmreader.PersistentConfig](r.Context(), s.repository, evmreader.EvmReaderConfigKey)
1365+
if errors.Is(err, repository.ErrNotFound) {
1366+
return nil, newRPCError(JSONRPC_RESOURCE_NOT_FOUND, "EVM Reader config not found")
1367+
}
1368+
if err != nil {
1369+
s.Logger.Error("Unable to retrieve evmreader config from repository", "err", err)
1370+
return nil, newRPCError(JSONRPC_INTERNAL_ERROR, "Internal server error")
1371+
}
1372+
1373+
return api.SingleResponse[api.NodeInfo]{Data: api.NodeInfo{
1374+
ChainID: fmt.Sprintf("0x%x", cfg.Value.ChainID),
1375+
Version: version.BuildVersion,
1376+
DefaultBlock: string(cfg.Value.DefaultBlock), // FINALIZED | SAFE | LATEST | PENDING
1377+
}}, nil
1378+
}
1379+
13621380
func handleGetChainID(s *Service, r *http.Request, _ RPCRequest) (any, error) {
13631381
config, err := repository.LoadNodeConfig[evmreader.PersistentConfig](r.Context(), s.repository, evmreader.EvmReaderConfigKey)
13641382
if errors.Is(err, repository.ErrNotFound) {

internal/jsonrpc/jsonrpc_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,66 @@ func TestMethod(t *testing.T) {
159159
})
160160
})
161161

162+
////////////////////////////////////////////////////////////////////////
163+
// getNodeInfo
164+
////////////////////////////////////////////////////////////////////////
165+
t.Run("cartesi_getNodeInfo", func(t *testing.T) {
166+
method := getName(t.Name())
167+
168+
// failure: evm reader not configured -> resource not found
169+
t.Run("absent", func(t *testing.T) {
170+
testHistogram.inc(method)
171+
s := newTestService(t, t.Name())
172+
173+
body := s.doRequest(t, 0, []byte(`{
174+
"jsonrpc": "2.0",
175+
"method": "cartesi_getNodeInfo",
176+
"params": {},
177+
"id": 0
178+
}`))
179+
180+
resp := testRPCResponse[any]{}
181+
require.NoError(t, json.Unmarshal(body, &resp))
182+
require.NotNil(t, resp.Error)
183+
assert.Equal(t, JSONRPC_RESOURCE_NOT_FOUND, resp.Error.Code)
184+
assert.Equal(t, "EVM Reader config not found", resp.Error.Message)
185+
})
186+
187+
// success: combine persisted node configuration with the build version
188+
t.Run("present", func(t *testing.T) {
189+
testHistogram.inc(method)
190+
ctx := context.Background()
191+
s := newTestService(t, t.Name())
192+
193+
chainID := uint64(0xdeadbeef)
194+
defaultBlock := model.DefaultBlock_Safe
195+
err := repository.SaveNodeConfig(ctx, s.repository,
196+
&model.NodeConfig[evmreader.PersistentConfig]{
197+
Key: evmreader.EvmReaderConfigKey,
198+
Value: evmreader.PersistentConfig{
199+
ChainID: chainID,
200+
DefaultBlock: defaultBlock,
201+
},
202+
},
203+
)
204+
require.NoError(t, err)
205+
206+
body := s.doRequest(t, 0, []byte(`{
207+
"jsonrpc": "2.0",
208+
"method": "cartesi_getNodeInfo",
209+
"params": {},
210+
"id": 0
211+
}`))
212+
213+
resp := testRPCResponse[api.NodeInfo]{}
214+
require.NoError(t, json.Unmarshal(body, &resp))
215+
require.Nil(t, resp.Error)
216+
assert.Equal(t, hexutil.EncodeUint64(chainID), resp.Result.Data.ChainID)
217+
assert.Equal(t, version.BuildVersion, resp.Result.Data.Version)
218+
assert.Equal(t, string(defaultBlock), resp.Result.Data.DefaultBlock)
219+
})
220+
})
221+
162222
////////////////////////////////////////////////////////////////////////
163223
// getChainId
164224
////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)