-
Notifications
You must be signed in to change notification settings - Fork 0
WIP-feat(searchUtxosByAsset): changes to add support for searchUtxosByAsset #510
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: Jenita <[email protected]>
📝 WalkthroughWalkthroughA new HTTP GET endpoint Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–25 minutes Areas requiring extra attention:
Pre-merge checks and finishing touches✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (1)
internal/api/localstatequery.go (1)
23-24: Route + import look fine; please ensure Swagger/route registration stays consistent across docs and clients.Also applies to: 34-34
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
internal/api/localstatequery.go(3 hunks)
🧰 Additional context used
🪛 GitHub Actions: golangci-lint
internal/api/localstatequery.go
[error] 499-499: G115: integer overflow conversion int -> uint32 (gosec)
🪛 GitHub Check: lint
internal/api/localstatequery.go
[failure] 499-499:
G115: integer overflow conversion int -> uint32 (gosec)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Analyze (go)
| type responseLocalStateQuerySearchUTxOsByAsset struct { | ||
| UTxOs []utxoItem `json:"utxos"` | ||
| Count int `json:"count"` | ||
| } | ||
|
|
||
| type utxoItem struct { | ||
| TxHash string `json:"tx_hash"` | ||
| Index uint32 `json:"index"` | ||
| Address string `json:"address"` | ||
| Amount uint64 `json:"amount"` | ||
| Assets interface{} `json:"assets,omitempty"` | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the file mentioned in the review
cat -n internal/api/localstatequery.go | head -100Repository: blinklabs-io/cardano-node-api
Length of output: 3653
🏁 Script executed:
# Find the context around lines 380-392 to understand the structure better
sed -n '370,400p' internal/api/localstatequery.goRepository: blinklabs-io/cardano-node-api
Length of output: 891
🏁 Script executed:
# Search for where utxoItem is created/populated to see what's assigned to Assets
rg "utxoItem\{|utxoItem(" internal/api/ -A 5 -B 2Repository: blinklabs-io/cardano-node-api
Length of output: 163
🏁 Script executed:
# Search for json encoding/marshaling of utxoItem
rg "json\.Marshal|json\.Unmarshal" internal/api/localstatequery.go -B 3 -A 3Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Find the handler implementation
sed -n '392,500p' internal/api/localstatequery.goRepository: blinklabs-io/cardano-node-api
Length of output: 2941
🏁 Script executed:
# Search for where utxoItem is created
rg "utxoItem" internal/api/ -A 3 -B 1Repository: blinklabs-io/cardano-node-api
Length of output: 1270
🏁 Script executed:
# Get more of the handler to see how Assets is populated
sed -n '470,530p' internal/api/localstatequery.goRepository: blinklabs-io/cardano-node-api
Length of output: 1137
🏁 Script executed:
# Search for test files that might show how this is used
find . -name "*localstatequery*test*" -o -name "*test*localstatequery*" | head -20Repository: blinklabs-io/cardano-node-api
Length of output: 115
🏁 Script executed:
# Check the test file to see what the actual JSON looks like
cat -n ./openapi/test/api_localstatequery_test.goRepository: blinklabs-io/cardano-node-api
Length of output: 3628
🏁 Script executed:
# Search for the Assets type definition - likely in ledger package
rg "type.*Assets" --type go -B 2 -A 5Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Check if there's a custom JSON marshaler for the assets type
rg "MarshalJSON|UnmarshalJSON" internal/api/localstatequery.goRepository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Check what types are imported and understand Assets better
head -30 internal/api/localstatequery.goRepository: blinklabs-io/cardano-node-api
Length of output: 1145
🏁 Script executed:
# Search for how assets are used elsewhere in codebase
rg "Assets\(\)|output\.Assets" internal/ -B 2 -A 3Repository: blinklabs-io/cardano-node-api
Length of output: 1028
🏁 Script executed:
# Look for any related issue discussions or comments about Assets
rg -i "asset.*interface|interface.*asset|unstable|json.*asset" internal/api/ -B 2 -A 2Repository: blinklabs-io/cardano-node-api
Length of output: 926
🏁 Script executed:
# Check if there's any example or documentation about JSON format
rg -i "asset.*json|json.*format" . --type md --type txtRepository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Check go.mod to understand the gouroboros dependency
cat go.mod | grep -A 2 -B 2 "gouroboros"Repository: blinklabs-io/cardano-node-api
Length of output: 273
🏁 Script executed:
# Search for any integration tests or examples that might show actual JSON output
find . -type f -name "*.go" -exec grep -l "search-by-asset\|SearchUTxOsByAsset" {} \;Repository: blinklabs-io/cardano-node-api
Length of output: 106
🏁 Script executed:
# Look for any documentation about the Assets response format
find . -type f \( -name "*.md" -o -name "*.yaml" -o -name "*.yml" \) -exec grep -l "assets\|Asset" {} \;Repository: blinklabs-io/cardano-node-api
Length of output: 104
🏁 Script executed:
# Check if there are any examples or docs in openapi spec
find . -name "*openapi*" -o -name "*swagger*" | head -10Repository: blinklabs-io/cardano-node-api
Length of output: 248
🏁 Script executed:
# Check the OpenAPI spec for the Assets field definition
rg "responseLocalStateQuerySearchUTxOsByAsset|utxoItem|Assets" ./docs/swagger.yaml -B 3 -A 10Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Also check the JSON version
rg "responseLocalStateQuerySearchUTxOsByAsset|utxoItem|Assets" ./docs/swagger.json -B 3 -A 10Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Check the openapi-config to understand how the spec is generated
cat -n openapi-config.ymlRepository: blinklabs-io/cardano-node-api
Length of output: 186
🏁 Script executed:
# Read the swagger.yaml file - check size first
wc -l ./docs/swagger.yamlRepository: blinklabs-io/cardano-node-api
Length of output: 96
🏁 Script executed:
# Search more specifically in swagger for utxo or asset
grep -i "utxo\|asset" ./docs/swagger.yaml | head -30Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Check if Assets field exists in swagger
cat ./docs/swagger.yaml | grep -A 10 -B 10 "assets"Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Read the full swagger file
cat ./docs/swagger.yamlRepository: blinklabs-io/cardano-node-api
Length of output: 7742
🏁 Script executed:
# Let's also check what the gouroboros library provides - search for any local copies or vendored code
find . -path ./vendor -prune -o -type d -name "gouroboros*" -printRepository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Check if there are any other API response structures we can learn from
rg "type response.*struct" internal/api/ -A 5 | head -50Repository: blinklabs-io/cardano-node-api
Length of output: 2643
🏁 Script executed:
# Let's check what type the Assets() method returns - search for ledger package
rg "func.*Assets\(\)" . --type goRepository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Check if there are integration tests or real examples showing the JSON output
find . -type f -name "*.go" -exec grep -l "Assets.*json\|json.*Assets" {} \;Repository: blinklabs-io/cardano-node-api
Length of output: 106
🏁 Script executed:
# Let's check if there's documentation or examples of how the API is actually used
find . -type f \( -name "*.md" -o -name "*.txt" -o -name "README*" \) -exec grep -l "search-by-asset\|Assets" {} \;Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Check git history to see if this endpoint was recently added
git log --oneline internal/api/localstatequery.go | head -20Repository: blinklabs-io/cardano-node-api
Length of output: 126
🏁 Script executed:
# Check the recent commit to understand context
git show f8069f1 --statRepository: blinklabs-io/cardano-node-api
Length of output: 5613
🏁 Script executed:
# Let's also look at the full diff of that commit for localstatequery.go
git show f8069f1 -- internal/api/localstatequery.go | head -200Repository: blinklabs-io/cardano-node-api
Length of output: 5592
🏁 Script executed:
# Check if there are any TODOs or FIXMEs related to Assets
rg "TODO|FIXME|XXX" internal/api/localstatequery.go -B 2 -A 2Repository: blinklabs-io/cardano-node-api
Length of output: 529
🏁 Script executed:
# Search for the Output type and Assets method definition in available code or comments
rg "Output.*interface|GetAssets|Assets\(\)" . --type go -B 2 -A 5 | head -50Repository: blinklabs-io/cardano-node-api
Length of output: 1266
🏁 Script executed:
# Check the gouroboros package information from imports and usage
rg "localstatequery\.UTxOsResult\|ledger\." internal/api/localstatequery.go | head -20Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Let's understand what the Assets field should contain by looking at Cardano specifications
# Search for any mapping or conversion of assets in the codebase
rg "asset.*map\|AssetName\|PolicyID" internal/ -B 2 -A 2 | head -50Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Get the full context of assets usage in the handler to understand the type better
sed -n '490,520p' internal/api/localstatequery.goRepository: blinklabs-io/cardano-node-api
Length of output: 597
🏁 Script executed:
# Look for where assets variable is used to understand its type
rg "assets\." internal/api/localstatequery.go -B 1 -A 1Repository: blinklabs-io/cardano-node-api
Length of output: 180
🏁 Script executed:
# Check if there's documentation or type hints about what Assets() returns
rg "// Assets|assets interface|Assets interface" . --type goRepository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Final check - let's see what gets assigned to the Assets field in the response
rg "Assets:" internal/api/localstatequery.go -B 3 -A 1Repository: blinklabs-io/cardano-node-api
Length of output: 201
Convert Assets interface{} to an explicit, concrete type with a defined JSON structure. Since Assets comes from a third-party library type without a guaranteed JSON format, assign it to a concrete struct (e.g., map[string]map[string]uint64 for {policy_id: {asset_name: quantity}}) or define a custom struct that explicitly represents the asset structure. Update the OpenAPI definition to document the response schema. This ensures API stability and allows clients to depend on a predictable contract.
| func handleLocalStateQuerySearchUTxOsByAsset(c *gin.Context) { | ||
| // Get query parameters | ||
| policyIdHex := c.Query("policy_id") | ||
| assetNameHex := c.Query("asset_name") | ||
| addressStr := c.Query("address") | ||
|
|
||
| // Validate required parameters | ||
| if policyIdHex == "" { | ||
| c.JSON(400, apiError("policy_id parameter is required")) | ||
| return | ||
| } | ||
| if assetNameHex == "" { | ||
| c.JSON(400, apiError("asset_name parameter is required")) | ||
| return | ||
| } | ||
|
|
||
| // Parse policy ID (28 bytes) | ||
| policyIdBytes, err := hex.DecodeString(policyIdHex) | ||
| if err != nil { | ||
| c.JSON(400, apiError("invalid policy_id hex: "+err.Error())) | ||
| return | ||
| } | ||
| if len(policyIdBytes) != 28 { | ||
| c.JSON(400, apiError("policy_id must be 28 bytes")) | ||
| return | ||
| } | ||
| var policyId ledger.Blake2b224 | ||
| copy(policyId[:], policyIdBytes) | ||
|
|
||
| // Parse asset name | ||
| assetName, err := hex.DecodeString(assetNameHex) | ||
| if err != nil { | ||
| c.JSON(400, apiError("invalid asset_name hex: "+err.Error())) | ||
| return | ||
| } | ||
|
|
||
| // Parse optional address | ||
| var addrs []ledger.Address | ||
| if addressStr != "" { | ||
| addr, err := ledger.NewAddress(addressStr) | ||
| if err != nil { | ||
| c.JSON(400, apiError("invalid address: "+err.Error())) | ||
| return | ||
| } | ||
| addrs = append(addrs, addr) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate asset_name length (and consider upper-bounding query sizes).
Cardano asset names are bounded (commonly max 32 bytes). Without a length check, a large hex string can force large allocations during decode and later filtering.
Proposed patch:
// Parse asset name
assetName, err := hex.DecodeString(assetNameHex)
if err != nil {
c.JSON(400, apiError("invalid asset_name hex: "+err.Error()))
return
}
+ if len(assetName) > 32 {
+ c.JSON(400, apiError("asset_name must be <= 32 bytes"))
+ return
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func handleLocalStateQuerySearchUTxOsByAsset(c *gin.Context) { | |
| // Get query parameters | |
| policyIdHex := c.Query("policy_id") | |
| assetNameHex := c.Query("asset_name") | |
| addressStr := c.Query("address") | |
| // Validate required parameters | |
| if policyIdHex == "" { | |
| c.JSON(400, apiError("policy_id parameter is required")) | |
| return | |
| } | |
| if assetNameHex == "" { | |
| c.JSON(400, apiError("asset_name parameter is required")) | |
| return | |
| } | |
| // Parse policy ID (28 bytes) | |
| policyIdBytes, err := hex.DecodeString(policyIdHex) | |
| if err != nil { | |
| c.JSON(400, apiError("invalid policy_id hex: "+err.Error())) | |
| return | |
| } | |
| if len(policyIdBytes) != 28 { | |
| c.JSON(400, apiError("policy_id must be 28 bytes")) | |
| return | |
| } | |
| var policyId ledger.Blake2b224 | |
| copy(policyId[:], policyIdBytes) | |
| // Parse asset name | |
| assetName, err := hex.DecodeString(assetNameHex) | |
| if err != nil { | |
| c.JSON(400, apiError("invalid asset_name hex: "+err.Error())) | |
| return | |
| } | |
| // Parse optional address | |
| var addrs []ledger.Address | |
| if addressStr != "" { | |
| addr, err := ledger.NewAddress(addressStr) | |
| if err != nil { | |
| c.JSON(400, apiError("invalid address: "+err.Error())) | |
| return | |
| } | |
| addrs = append(addrs, addr) | |
| } | |
| func handleLocalStateQuerySearchUTxOsByAsset(c *gin.Context) { | |
| // Get query parameters | |
| policyIdHex := c.Query("policy_id") | |
| assetNameHex := c.Query("asset_name") | |
| addressStr := c.Query("address") | |
| // Validate required parameters | |
| if policyIdHex == "" { | |
| c.JSON(400, apiError("policy_id parameter is required")) | |
| return | |
| } | |
| if assetNameHex == "" { | |
| c.JSON(400, apiError("asset_name parameter is required")) | |
| return | |
| } | |
| // Parse policy ID (28 bytes) | |
| policyIdBytes, err := hex.DecodeString(policyIdHex) | |
| if err != nil { | |
| c.JSON(400, apiError("invalid policy_id hex: "+err.Error())) | |
| return | |
| } | |
| if len(policyIdBytes) != 28 { | |
| c.JSON(400, apiError("policy_id must be 28 bytes")) | |
| return | |
| } | |
| var policyId ledger.Blake2b224 | |
| copy(policyId[:], policyIdBytes) | |
| // Parse asset name | |
| assetName, err := hex.DecodeString(assetNameHex) | |
| if err != nil { | |
| c.JSON(400, apiError("invalid asset_name hex: "+err.Error())) | |
| return | |
| } | |
| if len(assetName) > 32 { | |
| c.JSON(400, apiError("asset_name must be <= 32 bytes")) | |
| return | |
| } | |
| // Parse optional address | |
| var addrs []ledger.Address | |
| if addressStr != "" { | |
| addr, err := ledger.NewAddress(addressStr) | |
| if err != nil { | |
| c.JSON(400, apiError("invalid address: "+err.Error())) | |
| return | |
| } | |
| addrs = append(addrs, addr) | |
| } |
🤖 Prompt for AI Agents
In internal/api/localstatequery.go around lines 405 to 450, the handler
currently decodes asset_name hex without checking length which can lead to large
allocations and unexpected inputs; before decoding, validate the hex string
length and resulting byte length (e.g. ensure the hex string is even-length and
the decoded asset name is <= 32 bytes, or a configured MAX_ASSET_NAME_BYTES) and
return a 400 error if it exceeds the limit; additionally, enforce a reasonable
upper bound on query parameter sizes (reject overly long
policy_id/asset_name/address strings) to prevent abuse and resource exhaustion.
| // Connect to node | ||
| oConn, err := node.GetConnection(nil) | ||
| if err != nil { | ||
| c.JSON(500, apiError(err.Error())) | ||
| return | ||
| } | ||
| // Async error handler | ||
| go func() { | ||
| err, ok := <-oConn.ErrorChan() | ||
| if !ok { | ||
| return | ||
| } | ||
| c.JSON(500, apiError(err.Error())) | ||
| }() | ||
| defer func() { | ||
| // Close Ouroboros connection | ||
| oConn.Close() | ||
| }() | ||
| // Start client | ||
| oConn.LocalStateQuery().Client.Start() | ||
|
|
||
| // Get UTxOs (either by address or whole set) | ||
| var utxos *localstatequery.UTxOsResult | ||
| if len(addrs) > 0 { | ||
| utxos, err = oConn.LocalStateQuery().Client.GetUTxOByAddress(addrs) | ||
| } else { | ||
| utxos, err = oConn.LocalStateQuery().Client.GetUTxOWhole() | ||
| } | ||
| if err != nil { | ||
| c.JSON(500, apiError(err.Error())) | ||
| return | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Get the full function context
cd internal/api && head -n 500 localstatequery.go | tail -n +420 | cat -nRepository: blinklabs-io/cardano-node-api
Length of output: 2552
🏁 Script executed:
# Search for other instances of ErrorChan() patterns in the codebase
rg "ErrorChan()" -A 5 -B 2Repository: blinklabs-io/cardano-node-api
Length of output: 4894
🏁 Script executed:
# Look for the function signature to understand the full context
rg -A 50 "func.*addrs.*\(" internal/api/localstatequery.go | head -100Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Look for gin.Context definition or usage patterns in the codebase
rg "gin\." internal/api/*.go | head -20Repository: blinklabs-io/cardano-node-api
Length of output: 1596
🏁 Script executed:
# Search for any response status/write guards in the codebase
rg "Written\|HeadersWritten\|Status" internal/api/*.go | head -20Repository: blinklabs-io/cardano-node-api
Length of output: 55
🏁 Script executed:
# Check if gin.ResponseWriter has synchronization
rg "sync\|mutex\|lock" internal/api/*.go | grep -i "response\|writer" | head -10Repository: blinklabs-io/cardano-node-api
Length of output: 55
🌐 Web query:
gin-gonic gin.Context thread safety concurrent writes JSON
💡 Result:
Short answer: gin.Context is not safe for concurrent writes. Don’t call c.JSON / c.Writer methods from multiple goroutines simultaneously — use c.Copy() when starting goroutines and avoid writing to the ResponseWriter after the handler returns. Instead marshal/prepare the response (or synchronize with a mutex) and write once from the handler, or extract needed data and pass plain values/context.Context to background goroutines.
Key references:
- Gin docs: “When starting new Goroutines ... you SHOULD NOT use the original context inside it, you have to use a read-only copy” (use c.Copy()). [1]
- pkg.go.dev: Context.Copy() documentation: “Copy returns a copy of the current context that can be safely used outside the request's scope.” [2]
- Guidance & examples + community issues showing races/overwrites and cancellation pitfalls (don’t rely on *gin.Context for long-running background work; clone request context if you need no cancellation). [3]
🏁 Script executed:
# Verify the specific instance in localstatequery.go around lines 452-483
sed -n '445,490p' internal/api/localstatequery.go | cat -nRepository: blinklabs-io/cardano-node-api
Length of output: 1410
Don't write to gin.Context from the async ErrorChan() goroutine—this causes race conditions and duplicate response writes.
When the handler returns (e.g., after calling c.JSON() at line 481) and the goroutine later attempts c.JSON(), you get "headers already written" errors or data races on the response. This pattern is repeated across multiple handlers (chainsync.go, localtxmonitor.go, etc.).
Use c.Copy() if passing context to goroutines, or better yet, avoid writing responses from background goroutines. Instead, log the error and close the connection, or signal the error through a channel to let the main handler decide the response.
| // Get UTxOs (either by address or whole set) | ||
| var utxos *localstatequery.UTxOsResult | ||
| if len(addrs) > 0 { | ||
| utxos, err = oConn.LocalStateQuery().Client.GetUTxOByAddress(addrs) | ||
| } else { | ||
| utxos, err = oConn.LocalStateQuery().Client.GetUTxOWhole() | ||
| } | ||
| if err != nil { | ||
| c.JSON(500, apiError(err.Error())) | ||
| return | ||
| } | ||
|
|
||
| // Filter UTxOs by asset | ||
| results := make([]utxoItem, 0) | ||
| for utxoId, output := range utxos.Results { | ||
| // Check if output has assets | ||
| assets := output.Assets() | ||
| if assets == nil { | ||
| continue | ||
| } | ||
|
|
||
| // Check if the asset exists in this UTxO | ||
| amount := assets.Asset(policyId, assetName) | ||
| if amount > 0 { | ||
| item := utxoItem{ | ||
| TxHash: hex.EncodeToString(utxoId.Hash[:]), | ||
| Index: uint32(utxoId.Idx), | ||
| Address: output.Address().String(), | ||
| Amount: output.Amount(), | ||
| Assets: assets, | ||
| } | ||
| results = append(results, item) | ||
| } | ||
| } | ||
|
|
||
| // Create response | ||
| resp := responseLocalStateQuerySearchUTxOsByAsset{ | ||
| UTxOs: results, | ||
| Count: len(results), | ||
| } | ||
| c.JSON(200, resp) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blocker: fix gosec G115 overflow on uint32(utxoId.Idx) (CI failing).
Add a bounds check before converting:
if amount > 0 {
+ if utxoId.Idx < 0 || utxoId.Idx > math.MaxUint32 {
+ c.JSON(500, apiError("utxo index out of range"))
+ return
+ }
item := utxoItem{
TxHash: hex.EncodeToString(utxoId.Hash[:]),
- Index: uint32(utxoId.Idx),
+ Index: uint32(utxoId.Idx),
Address: output.Address().String(),
Amount: output.Amount(),
Assets: assets,
}Also: fetching GetUTxOWhole() when address is omitted is a likely DoS footgun (latency/memory). At minimum, consider adding server-side guardrails (require address, or add pagination/limit + timeouts).
🧰 Tools
🪛 GitHub Actions: golangci-lint
[error] 499-499: G115: integer overflow conversion int -> uint32 (gosec)
🪛 GitHub Check: lint
[failure] 499-499:
G115: integer overflow conversion int -> uint32 (gosec)
🤖 Prompt for AI Agents
In internal/api/localstatequery.go around lines 473-514, add a bounds check
before converting utxoId.Idx to uint32 to fix the gosec G115 overflow: if
utxoId.Idx > math.MaxUint32 either skip that UTxO or return a 400/500 error as
appropriate instead of blindly casting. Additionally, remove or guard the
expensive GetUTxOWhole() path when len(addrs)==0: require an address parameter
(return 400 if missing) or implement server-side guardrails such as a max
limit/pagination (enforce a capped limit on results and support offset/page
token) and wrap the LocalStateQuery client calls with a context timeout to avoid
DoS/latency issues. Ensure any new imports (math, context) are added and
behavior is well-documented in the handler comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No issues found across 1 file
This pull request adds a new API endpoint to the local state query service, enabling users to search for UTxOs containing a specific asset, with optional filtering by address. The implementation includes input validation, integrates with the local state query protocol, and returns the matching UTxOs in a structured response.
New API endpoint for searching UTxOs by asset:
GET /localstatequery/utxos/search-by-assetto allow searching for UTxOs bypolicy_idandasset_name, with optional address filtering.handleLocalStateQuerySearchUTxOsByAsset, which:policy_id,asset_name, and optionaladdress)Supporting changes:
localstatequeryprotocol package to support querying UTxOs.Summary by cubic
Adds a new API endpoint to search UTxOs by asset, with optional address filtering. This helps quickly find where a specific asset is held and returns matching UTxOs with a count.
Written for commit f8069f1. Summary will update automatically on new commits.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.