Skip to content

fix: avoid key err when no app-config provided#3

Merged
mmsqe merged 19 commits intomainfrom
app_config
Oct 9, 2025
Merged

fix: avoid key err when no app-config provided#3
mmsqe merged 19 commits intomainfrom
app_config

Conversation

@mmsqe
Copy link
Copy Markdown

@mmsqe mmsqe commented Jun 12, 2025

and sync upstream

Summary by CodeRabbit

  • New Features

    • Coin-type-aware account handling, per-chain data directories, and multi-chain command support.
    • IBC denom query support, offline gentx flagging, and relayer debug/server options.
  • Chores

    • Updated changelog with unreleased entries.
    • Added project linting configuration (.flake8).
    • Removed automated protobuf conversion script and its shell automation.
  • Bug Fixes

    • More robust supervisord/relayer and config wiring; safely skips missing or nested configs.

mmsqe and others added 10 commits December 5, 2024 12:06
* Problem: wrong cmd runs with chain_binary

* Revert "Problem: wrong cmd runs with chain_binary"

This reverts commit 8f26321.

* Problem: cmd flag don't support multiple chains

* changelog

* Update pystarport/cluster.py

Signed-off-by: yihuang <huang@crypto.com>

---------

Signed-off-by: yihuang <huang@crypto.com>
Co-authored-by: HuangYi <huang@crypto.com>
…m#143)

* Problem: debug-addr rename to debug-listen-addr in relayer

for more info, https://github.com/cosmos/relayer/pull/1504/files

* keep previous
…m#145)

* Problem: event-query-tx-for not compatible with chain-main

* Update CHANGELOG.md

Signed-off-by: yihuang <huang@crypto.com>

* fix lint

---------

Signed-off-by: yihuang <huang@crypto.com>
Bumps [setuptools](https://github.com/pypa/setuptools) from 70.0.0 to 78.1.1.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](pypa/setuptools@v70.0.0...v78.1.1)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-version: 78.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jun 12, 2025

Walkthrough

Adds a .flake8 config, updates CHANGELOG, removes a protobuf conversion script and its Nix shell hook, and extends cluster and CLI code with coin_type propagation, conditional event-query behavior, supervisord/relayer wiring improvements, JSON config and multi-chain init support, and an ibc_denom query.

Changes

Cohort / File(s) Change Summary
Lint config
\.flake8
New flake8 configuration: max line length 88, ignore E203, exclude .git, __pycache__, and integration-test protobuf *_pb2.py files.
Changelog
CHANGELOG.md
Added four unreleased entries: multi-chain command flags, coin-type for account creation, backward compatibility for missing event-query-tx-for, and support for external community-pool queries.
Cluster logic
pystarport/cluster.py
Added data_dir handling; propagated coin_type through node/account creation and gentx wiring; public methods accept **kwargs; JSON config support and multi-chain init; offline gentx account_number/sequence handling; supervisord/app-config and relayer debug wiring; added ibc_denom forwarder.
CLI / chain commands
pystarport/cosmoscli.py
Added tendermint-subcommand probe and has_tendermint_subcommand; many methods accept **kwargs and coin_type mapping (eth_secp256k1 for coin_type 60); conditional event-query invoked only when tx succeeded and feature present; node-id/pubkey subcommand selection; added ibc_denom(path, node).
Protobuf generation script (deleted)
pystarport/convert.sh
Removed Bash script that generated Python gRPC code from Cosmos/Tendermint protobufs.
Nix shell
pystarport/default.nix
Removed shellHook that previously executed ./convert.sh when entering the development shell.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ClusterCLI
    participant CosmosCLI
    Note right of ClusterCLI #DDEBF7: node/account creation flow
    User->>ClusterCLI: create_node(..., coin_type)
    ClusterCLI->>ClusterCLI: determine data_dir per-chain
    ClusterCLI->>ClusterCLI: create_account(..., coin_type / **kwargs)
    ClusterCLI->>CosmosCLI: create_account(..., coin_type / **kwargs)
    CosmosCLI-->>ClusterCLI: account info (address/pubkey)
Loading
sequenceDiagram
    participant User
    participant CosmosCLI
    participant ChainCmd
    Note right of CosmosCLI #F7F3DD: tx submit + optional event-query
    User->>CosmosCLI: submit tx (transfer/delegate/...)
    CosmosCLI->>ChainCmd: run tx command
    ChainCmd-->>CosmosCLI: response {code, txhash}
    alt has_event_query_tx_for && event_query_tx && response.code == 0
        CosmosCLI->>ChainCmd: event_query_tx_for(response.txhash)
        ChainCmd-->>CosmosCLI: event details
    end
    CosmosCLI-->>User: final response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I nibble lines with tidy care,
A flake8 cap upon my hair.
Coins hop into wallets, bold and neat,
Old scripts fold up their tired feet.
I twitch my whiskers — relays greet! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title accurately describes the key‐error fix for missing app‐config, which is one part of the changes, but it does not capture the broader upstream synchronization and additional API and tooling enhancements included in this PR. It is therefore partially related to the overall changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch app_config

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
pystarport/cosmoscli.py (1)

159-173: 🛠️ Refactor suggestion

Thread-shared state is fragile

self.output / self.error are mutable instance attributes; concurrent ledger calls will race. Use local variables or a small dataclass to carry the result back from the thread.

🧹 Nitpick comments (5)
pystarport/cosmoscli.py (2)

298-319: Error-string matching is brittle

Relying on substring "CommunityPool query exposed by the external community pool" ties the code to one exact SDK error. Prefer catching a specific exception type or probing module support via --help first.


416-423: DRY: factor out duplicate success-handler

The 5-line if rsp["code"] == 0 … block is repeated many times. Extract to _post_tx(rsp) helper to cut maintenance overhead and avoid divergent future fixes.

pystarport/cluster.py (2)

189-190: Document new coin_type param

create_node() signature changed but docstring didn’t. Add a short note describing the flag and default (118).


1373-1388: Graceful skip when no app-config

Nice safeguard. Minor: grpc_port = grpc_address.split(":")[1] can raise if address lacks colon. Use rsplit(":",1) with default.

CHANGELOG.md (1)

30-31: Grammar

“binary that don't” → “binary that doesn't
“suppport” → “support”.

🧰 Tools
🪛 LanguageTool

[grammar] ~30-~30: There seems to be a noun/verb agreement error. Did you mean “doesn't” or “didn't”?
Context: ...5) Backward compatible with binary that don't have event-query-tx-for. - [crypto-com#147](https...

(SINGULAR_NOUN_AGREEMENT_WHO_DONT)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2e6e59 and 595d7d2.

⛔ Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • .flake8 (1 hunks)
  • CHANGELOG.md (1 hunks)
  • pystarport/cluster.py (11 hunks)
  • pystarport/convert.sh (0 hunks)
  • pystarport/cosmoscli.py (31 hunks)
  • pystarport/default.nix (0 hunks)
💤 Files with no reviewable changes (2)
  • pystarport/default.nix
  • pystarport/convert.sh
🧰 Additional context used
🧬 Code Graph Analysis (2)
pystarport/cluster.py (2)
pystarport/cosmoscli.py (6)
  • create_account (133-157)
  • create_account_ledger (159-187)
  • distribution_community (298-319)
  • make_multisig (595-605)
  • ibc_denom (1487-1498)
  • account (346-351)
pystarport/ports.py (1)
  • oracle_port (37-38)
pystarport/cosmoscli.py (2)
pystarport/cluster.py (7)
  • create_account (315-317)
  • create_account (906-937)
  • create_account_ledger (319-321)
  • distribution_community (372-373)
  • event_query_tx_for (778-779)
  • make_multisig (514-515)
  • ibc_denom (838-839)
pystarport/utils.py (1)
  • parse_amount (71-80)
🪛 LanguageTool
CHANGELOG.md

[grammar] ~30-~30: There seems to be a noun/verb agreement error. Did you mean “doesn't” or “didn't”?
Context: ...5) Backward compatible with binary that don't have event-query-tx-for. - [crypto-com#147](https...

(SINGULAR_NOUN_AGREEMENT_WHO_DONT)

🪛 Ruff (0.11.9)
pystarport/cluster.py

1256-1259: Use ternary operator cmds = cmd.split(",") if cmd is not None else [None] * len(chains) instead of if-else-block

Replace if-else-block with cmds = cmd.split(",") if cmd is not None else [None] * len(chains)

(SIM108)

🪛 Pylint (3.3.7)
pystarport/cluster.py

[refactor] 1261-1261: Redefining argument with the local name 'cmd'

(R1704)

🔇 Additional comments (10)
.flake8 (1)

2-4: Config looks sensible

Black-friendly E203 ignore and 88-char line length are standard; exclude list is explicit. No issues.

pystarport/cosmoscli.py (3)

133-156: Pass-through kwargs needs validation

Blindly forwarding **kwargs to keys add lets callers inject arbitrary CLI flags (e.g. --home, --output) and break invariants. Recommend whitelisting accepted options (currently only coin_type) or documenting that unrestricted flags are intentional.


294-297: Good compatibility tweak

Gracefully handles nested commission response – nice!


1487-1498: LGTM – neat helper

Hashing path once and re-using existing REST endpoint keeps things simple.

pystarport/cluster.py (6)

261-262: Propagates coin_type correctly

Nice to see the new option wired through.


315-322: Same kwargs-validation concern

For consistency, consider the same whitelisting / validation here as in CosmosCLI.create_account.


372-374: Expose extra query flags – good addition


514-516: LGTM


838-840: LGTM – thin delegate


1303-1305: Typo / duplication

api-listen-addr and newly added debug-listen-addr both set to :5183; confirm rly accepts duplicates and this port isn’t already bound.

Comment on lines +71 to +79
def prob_event_query_tx_for(self):
'test if the command has "event-query-tx-for" subcommand'
try:
output = self("q", "event-query-tx-for")
except AssertionError:
# non-zero return code
return False

return "Available Commands" in output.decode()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Detection may give false negatives

chain-maind q event-query-tx-for usually needs extra args; running it without may return usage (exit-code 0) on some versions, yielding a misleading “sub-command supported” result. Consider adding --help or checking exit-code == 0 and help text to reduce false positives.

🤖 Prompt for AI Agents
In pystarport/cosmoscli.py around lines 71 to 79, the prob_event_query_tx_for
method runs the subcommand without arguments, which may return usage text with
exit code 0 and cause false positives. Modify the method to run the subcommand
with a --help argument or similar, then check that the exit code is 0 and the
output contains expected help text to confirm the subcommand is supported
reliably.

Comment on lines +1255 to +1260
# for multiple chains, there can be multiple cmds splited by `,`
if cmd is not None:
cmds = cmd.split(",")
else:
cmds = [None] * len(chains)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Edge-case: command count mismatch

zip(chains, cmds) silently drops extra chains if fewer cmds are supplied. Warn or pad cmds to len(chains) to avoid half-initialised clusters.

-    if cmd is not None:
-        cmds = cmd.split(",")
-    else:
-        cmds = [None] * len(chains)
+    cmds = (cmd.split(",") if cmd is not None else [])
+    if len(cmds) < len(chains):
+        cmds.extend([None] * (len(chains) - len(cmds)))
📝 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.

Suggested change
# for multiple chains, there can be multiple cmds splited by `,`
if cmd is not None:
cmds = cmd.split(",")
else:
cmds = [None] * len(chains)
# for multiple chains, there can be multiple cmds splited by `,`
cmds = cmd.split(",") if cmd is not None else []
if len(cmds) < len(chains):
cmds.extend([None] * (len(chains) - len(cmds)))
🧰 Tools
🪛 Ruff (0.11.9)

1256-1259: Use ternary operator cmds = cmd.split(",") if cmd is not None else [None] * len(chains) instead of if-else-block

Replace if-else-block with cmds = cmd.split(",") if cmd is not None else [None] * len(chains)

(SIM108)

🤖 Prompt for AI Agents
In pystarport/cluster.py around lines 1255 to 1260, the code splits cmds by
commas but does not handle cases where the number of cmds is less than the
number of chains, causing zip to drop extra chains silently. Modify the code to
check if cmds length is less than chains length and pad cmds with None or empty
strings to match the length of chains, ensuring all chains are initialized
properly without being dropped.

def node_id(self):
"get tendermint node id"
output = self.raw("tendermint", "show-node-id", home=self.data_dir)
subcmd = "tendermint" if self.has_tendermint_subcommand else "comet"
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for more info

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (4)
pystarport/cosmoscli.py (4)

145-158: Don’t pass coin_type through to CLI; map to key_type and drop it (handle str/int uniformly).

Older CLIs may not recognize --coin-type; after mapping coin_type==60 to key_type, drop coin_type to avoid “unknown flag” errors. Also accept both int 60 and string "60".

-        if kwargs.get("coin_type") == 60:
-            kwargs["key_type"] = "eth_secp256k1"
+        # Map coin_type to key_type (eth) and avoid passing coin_type to the CLI
+        ct = kwargs.pop("coin_type", None)
+        if ct is not None and str(ct) == "60":
+            kwargs.setdefault("key_type", "eth_secp256k1")

Also applies to: 169-170


173-189: Mirror the coin_type-to-key_type mapping behaviour for ledger keys.

Same rationale as create_account: remove coin_type after mapping to prevent unknown-flag issues and accept both 60 and "60".

-        if kwargs.get("coin_type") == 60:
-            kwargs["key_type"] = "eth_secp256k1"
+        ct = kwargs.pop("coin_type", None)
+        if ct is not None and str(ct) == "60":
+            kwargs.setdefault("key_type", "eth_secp256k1")

1474-1490: Add event_query_tx toggle to pay_packet_fee for consistency

The pay_packet_fee call still always queries events when has_event_query_tx_for is true—even when consumers may want to disable it. Align its signature and gating with other tx methods.

Location:

  • pystarport/cosmoscli.py, around lines 1474–1490

Suggested diff:

-    def pay_packet_fee(self, port_id, channel_id, packet_seq, **kwargs):
+    def pay_packet_fee(self, port_id, channel_id, packet_seq, event_query_tx=True, **kwargs):
@@
-        if rsp["code"] == 0 and self.has_event_query_tx_for:
+        if rsp["code"] == 0 and event_query_tx and self.has_event_query_tx_for:
             rsp = self.event_query_tx_for(rsp["txhash"])

1453-1472: Ensure event_query_tx toggle is added to both fee‐related tx methods

Both register_counterparty_payee and pay_packet_fee currently always post‐query when has_event_query_tx_for is true, but lack the event_query_tx parameter found on other tx methods. Add the toggle for caller control:

Affected locations:

  • pystarport/cosmoscli.py: register_counterparty_payee (around line 1453)
  • pystarport/cosmoscli.py: pay_packet_fee (around line 1474)

Suggested diff:

--- a/pystarport/cosmoscli.py
+++ b/pystarport/cosmoscli.py
@@ def register_counterparty_payee(
-    def register_counterparty_payee(
-        self, port_id, channel_id, relayer, counterparty_payee, **kwargs
+    def register_counterparty_payee(
+        self, port_id, channel_id, relayer, counterparty_payee,
+        event_query_tx=True, **kwargs
     ):
@@
-        if rsp["code"] == 0 and self.has_event_query_tx_for:
+        if rsp["code"] == 0 and event_query_tx and self.has_event_query_tx_for:
             rsp = self.event_query_tx_for(rsp["txhash"])
         return rsp

@@ def pay_packet_fee(self, port_id, channel_id, packet_seq, **kwargs):
-    def pay_packet_fee(self, port_id, channel_id, packet_seq, **kwargs):
+    def pay_packet_fee(
+        self, port_id, channel_id, packet_seq,
+        event_query_tx=True, **kwargs
+    ):
@@
-        if rsp["code"] == 0 and self.has_event_query_tx_for:
+        if rsp["code"] == 0 and event_query_tx and self.has_event_query_tx_for:
             rsp = self.event_query_tx_for(rsp["txhash"])
         return rsp
♻️ Duplicate comments (1)
pystarport/cosmoscli.py (1)

81-89: Make event-query-tx-for capability probe reliable (use --help; avoid “Available Commands” check).

Running the subcommand without args can return “usage” with exit code 0 on some versions, causing false positives/negatives. Also, leaf commands won’t print “Available Commands”. Probe with --help and look for expected help text.

Apply this diff:

     def prob_event_query_tx_for(self):
         'test if the command has "event-query-tx-for" subcommand'
         try:
-            output = self("q", "event-query-tx-for")
+            # Use --help to reduce false positives from bare invocation
+            output = self("q", "event-query-tx-for", "--help")
         except AssertionError:
             # non-zero return code
             return False
 
-        return "Available Commands" in output.decode()
+        txt = output.decode()
+        # Leaf command: expect its name and standard help headers
+        return "event-query-tx-for" in txt and ("Usage" in txt or "Flags" in txt)
🧹 Nitpick comments (1)
pystarport/cosmoscli.py (1)

126-130: Nit: Update the docstring to reflect comet fallback.

You now select tendermint or comet dynamically; make the docstring generic.

-    def node_id(self):
-        "get tendermint node id"
+    def node_id(self):
+        "get node id (tendermint/comet)"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between a8b7c6c and b143725.

📒 Files selected for processing (1)
  • pystarport/cosmoscli.py (33 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
pystarport/cosmoscli.py (2)
pystarport/cluster.py (10)
  • node_id (307-309)
  • home (286-288)
  • create_account (315-317)
  • create_account (906-937)
  • create_account_ledger (319-321)
  • distribution_community (372-373)
  • node_rpc (293-295)
  • event_query_tx_for (778-779)
  • make_multisig (514-515)
  • ibc_denom (838-839)
pystarport/utils.py (1)
  • parse_amount (71-80)
🔇 Additional comments (2)
pystarport/cosmoscli.py (2)

432-438: Event query gating looks good.

Conditional invocation of event-query-tx-for based on rsp["code"] == 0, generate_only, and capability probe avoids unnecessary calls and errors on unsupported CLIs.


1505-1516: LGTM: IBC denom query helper is correct and useful.

Hashing the path and querying ibc-transfer denom aligns with ICS20; returning the denom string is the expected output.

Comment on lines +311 to 313
res = res["commission"]
return parse_amount(res[0])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard against empty commission arrays.

When no commission has accrued, res can be an empty list, leading to IndexError on res[0].

         if isinstance(res, dict):
             res = res["commission"]
-        return parse_amount(res[0])
+        if not res:
+            return 0
+        return parse_amount(res[0])
📝 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.

Suggested change
res = res["commission"]
return parse_amount(res[0])
if isinstance(res, dict):
res = res["commission"]
if not res:
return 0
return parse_amount(res[0])
🤖 Prompt for AI Agents
In pystarport/cosmoscli.py around lines 311 to 313, the code assumes
res["commission"] is a non-empty list and does return parse_amount(res[0]),
which raises IndexError when commission is empty; guard by checking if res is a
list and empty (len(res) == 0) and in that case return a zero amount (e.g.,
return parse_amount("0") or the function's canonical zero value); otherwise
proceed to parse res[0] as before.

Comment on lines +314 to 336
def distribution_community(self, **kwargs):
for module in ["distribution", "protocolpool"]:
try:
res = json.loads(
self.raw(
"query",
module,
"community-pool",
output="json",
node=self.node_rpc,
**kwargs,
)
)
return parse_amount(res["pool"][0])
except Exception as e:
if (
module == "distribution"
and "CommunityPool query exposed by the external community pool"
in str(e)
):
continue
raise

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle empty community pool safely; avoid index errors and continue fallback cleanly.

If pool is empty, res["pool"][0] raises IndexError. Return 0 instead. Keep the existing “distribution” -> “protocolpool” fallback.

-                return parse_amount(res["pool"][0])
+                pool = res.get("pool") or []
+                if not pool:
+                    return 0
+                return parse_amount(pool[0])
📝 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.

Suggested change
def distribution_community(self, **kwargs):
for module in ["distribution", "protocolpool"]:
try:
res = json.loads(
self.raw(
"query",
module,
"community-pool",
output="json",
node=self.node_rpc,
**kwargs,
)
)
return parse_amount(res["pool"][0])
except Exception as e:
if (
module == "distribution"
and "CommunityPool query exposed by the external community pool"
in str(e)
):
continue
raise
def distribution_community(self, **kwargs):
for module in ["distribution", "protocolpool"]:
try:
res = json.loads(
self.raw(
"query",
module,
"community-pool",
output="json",
node=self.node_rpc,
**kwargs,
)
)
pool = res.get("pool") or []
if not pool:
return 0
return parse_amount(pool[0])
except Exception as e:
if (
module == "distribution"
and "CommunityPool query exposed by the external community pool"
in str(e)
):
continue
raise
🤖 Prompt for AI Agents
pystarport/cosmoscli.py around lines 314-336: the code assumes res["pool"][0]
exists and will raise IndexError when the community pool is empty; after parsing
the JSON, check res.get("pool", []) and if it's empty return 0, otherwise call
parse_amount on the first element; keep the existing try/except and the
distribution→protocolpool fallback behavior unchanged so only empty-pool cases
return 0 instead of raising.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
pystarport/cluster.py (1)

1267-1274: Edge-case: command count mismatch silently drops chains (zip truncation)

When fewer cmds than chains are supplied, zip() truncates, leaving some chains uninitialized. Pad cmds to match chains length. Also satisfies Ruff’s SIM108 (ternary).

Apply this diff:

-    # for multiple chains, there can be multiple cmds splited by `,`
-    if cmd is not None:
-        cmds = cmd.split(",")
-    else:
-        cmds = [None] * len(chains)
+    # for multiple chains, there can be multiple cmds split by `,`
+    cmds = cmd.split(",") if cmd is not None else []
+    if len(cmds) < len(chains):
+        cmds.extend([None] * (len(chains) - len(cmds)))
 
     for chain, cmd in zip(chains, cmds):
🧹 Nitpick comments (2)
pystarport/cluster.py (2)

189-190: Add coin_type to create_node: good propagation hook

Adding coin_type here is appropriate and aligns with downstream handling in cosmoscli. Consider also persisting this value into the validators entry you append (only when provided) so it’s discoverable later (parity with config-driven init).

Example (outside this hunk):

# inside the validators.append(...) block:
validator_cfg = {
    "base_port": base_port,
    "hostname": hostname,
    "moniker": moniker,
}
if coin_type is not None:
    validator_cfg["coin-type"] = coin_type
self.config["validators"].append(validator_cfg)

907-918: Normalize coin_type to int to ensure key_type switch triggers

If coin_type is provided as a string from YAML (e.g., "60"), CosmosCLI’s comparison (== 60) won’t match. Normalize early to avoid surprises.

Apply this diff:

-        coin_type = account.get("coin-type")
+        coin_type = account.get("coin-type")
+        if coin_type is not None:
+            try:
+                coin_type = int(coin_type)
+            except (TypeError, ValueError):
+                raise ValueError(f"invalid coin-type: {coin_type!r}")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between b143725 and dba7804.

📒 Files selected for processing (1)
  • pystarport/cluster.py (11 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
pystarport/cluster.py (3)
pystarport/cosmoscli.py (7)
  • create_account (145-171)
  • create_account_ledger (173-203)
  • distribution_community (314-335)
  • make_multisig (611-621)
  • ibc_denom (1505-1516)
  • account (362-367)
  • gentx (233-245)
pystarport/cli.py (1)
  • cli (174-181)
pystarport/ports.py (1)
  • oracle_port (37-38)
🪛 Ruff (0.12.2)
pystarport/cluster.py

1268-1271: Use ternary operator cmds = cmd.split(",") if cmd is not None else [None] * len(chains) instead of if-else-block

Replace if-else-block with cmds = cmd.split(",") if cmd is not None else [None] * len(chains)

(SIM108)

🔇 Additional comments (8)
pystarport/cluster.py (8)

261-261: LGTM: pass coin_type during validator account creation

The new coin_type is correctly forwarded into account creation.


315-318: LGTM: create_account forwards kwargs

Forwarding kwargs keeps ClusterCLI in sync with CosmosCLI (e.g., coin_type). Signature and call are consistent.


319-322: LGTM: create_account_ledger forwards kwargs

Ledger variant mirrors the standard account creation path and supports coin_type. Good.


372-374: LGTM: distribution_community passthrough

Propagating kwargs allows selective module behaviors/upstream flags. Matches CosmosCLI changes.


514-516: LGTM: make_multisig passthrough

Extending to kwargs is consistent with CosmosCLI.


838-840: LGTM: ibc_denom forwarding method

This cleanly exposes the CosmosCLI ibc_denom helper through ClusterCLI.


1023-1026: LGTM: propagate per-node coin-type into validator account creation

This aligns validator keys with the chain’s configured coin-type.


1315-1317: LGTM: relayer debug knobs (rly)

Adding debug-listen-addr and enable-debug-server improves operability. Defaults are reasonable.

Comment on lines +1050 to +1057
gentx_extra_args = [config.get("cmd-flags")]
# build extra positional args; inject --offline if sequence or account_number set
has_acct = "account_number" in extra_kwargs
has_seq = "sequence" in extra_kwargs
if has_acct ^ has_seq: # xor: only one provided
raise ValueError("Both 'account_number' and 'sequence' must be provided together for offline gentx")
if has_acct and has_seq:
gentx_extra_args.append("--offline")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Avoid passing None or opaque “cmd-flags” into gentx args

gentx_extra_args currently includes config.get("cmd-flags") even when None, which risks propagating None into the underlying CLI builder. Also, if cmd-flags is a list/tuple, flattening is safer.

Apply this diff:

-            gentx_extra_args = [config.get("cmd-flags")]
+            gentx_extra_args = []
+            flags = config.get("cmd-flags")
+            if flags:
+                if isinstance(flags, (list, tuple)):
+                    gentx_extra_args.extend(map(str, flags))
+                else:
+                    gentx_extra_args.append(str(flags))
             # build extra positional args; inject --offline if sequence or account_number set
             has_acct = "account_number" in extra_kwargs
             has_seq = "sequence" in extra_kwargs
             if has_acct ^ has_seq:  # xor: only one provided
                 raise ValueError("Both 'account_number' and 'sequence' must be provided together for offline gentx")
             if has_acct and has_seq:
                 gentx_extra_args.append("--offline")
📝 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.

Suggested change
gentx_extra_args = [config.get("cmd-flags")]
# build extra positional args; inject --offline if sequence or account_number set
has_acct = "account_number" in extra_kwargs
has_seq = "sequence" in extra_kwargs
if has_acct ^ has_seq: # xor: only one provided
raise ValueError("Both 'account_number' and 'sequence' must be provided together for offline gentx")
if has_acct and has_seq:
gentx_extra_args.append("--offline")
gentx_extra_args = []
flags = config.get("cmd-flags")
if flags:
if isinstance(flags, (list, tuple)):
gentx_extra_args.extend(map(str, flags))
else:
gentx_extra_args.append(str(flags))
# build extra positional args; inject --offline if sequence or account_number set
has_acct = "account_number" in extra_kwargs
has_seq = "sequence" in extra_kwargs
if has_acct ^ has_seq: # xor: only one provided
raise ValueError("Both 'account_number' and 'sequence' must be provided together for offline gentx")
if has_acct and has_seq:
gentx_extra_args.append("--offline")
🤖 Prompt for AI Agents
In pystarport/cluster.py around lines 1050 to 1057, gentx_extra_args currently
unconditionally includes config.get("cmd-flags") which may be None or a non-flat
sequence; change the logic to skip None/empty values and to normalize/flatten
cmd-flags: if cmd-flags is a string, split it into tokens; if it's a list/tuple,
extend gentx_extra_args with its items; if it's a single other value, append it;
ensure you only modify gentx_extra_args when the normalized flags are non-empty
so no None or nested sequences are passed to the CLI builder.

Comment on lines +1385 to 1393
app_cfg = node.get("app-config", {})
if not app_cfg:
continue
oracle_config = app_cfg.get("oracle", {})
if oracle_config.get("enabled"):
oracle_port = ports.oracle_port(node["base_port"])
grpc_address = node["app-config"].get("grpc", {}).get("address", "")
grpc_address = app_cfg.get("grpc", {}).get("address", "")
grpc_port = grpc_address.split(":")[1] if ":" in grpc_address else ports.grpc_port(node["base_port"])
oracle_section = f"program:{chain_id}-node{i}-oracle"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: grpc_port extraction breaks for addresses with scheme (e.g., tcp://127.0.0.1:9090)

Using split(":")[1] returns the second segment, which is incorrect when a scheme is present. Use rsplit(":", 1) to reliably capture the port; also preserve the existing fallback.

Apply this diff:

-        grpc_address = app_cfg.get("grpc", {}).get("address", "")
-        grpc_port = grpc_address.split(":")[1] if ":" in grpc_address else ports.grpc_port(node["base_port"])
+        grpc_address = app_cfg.get("grpc", {}).get("address", "")
+        grpc_port = ports.grpc_port(node["base_port"])
+        if ":" in grpc_address:
+            grpc_port = grpc_address.rsplit(":", 1)[-1]

Optionally, guard for IPv6 literals with brackets in the future.

📝 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.

Suggested change
app_cfg = node.get("app-config", {})
if not app_cfg:
continue
oracle_config = app_cfg.get("oracle", {})
if oracle_config.get("enabled"):
oracle_port = ports.oracle_port(node["base_port"])
grpc_address = node["app-config"].get("grpc", {}).get("address", "")
grpc_address = app_cfg.get("grpc", {}).get("address", "")
grpc_port = grpc_address.split(":")[1] if ":" in grpc_address else ports.grpc_port(node["base_port"])
oracle_section = f"program:{chain_id}-node{i}-oracle"
app_cfg = node.get("app-config", {})
if not app_cfg:
continue
oracle_config = app_cfg.get("oracle", {})
if oracle_config.get("enabled"):
oracle_port = ports.oracle_port(node["base_port"])
grpc_address = app_cfg.get("grpc", {}).get("address", "")
grpc_port = ports.grpc_port(node["base_port"])
if ":" in grpc_address:
grpc_port = grpc_address.rsplit(":", 1)[-1]
oracle_section = f"program:{chain_id}-node{i}-oracle"

Solution:
- raw json config can be used if user want to expand jsonnet by itself
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
pystarport/cluster.py (3)

1271-1278: Handle cmd count vs. chain count; avoid silent truncation by zip

zip drops extra chains if fewer cmds are provided. Pad cmds to match chains and keep behavior explicit. Also satisfies Ruff SIM108 if you keep the ternary.

Apply this diff:

-    # for multiple chains, there can be multiple cmds splited by `,`
-    if cmd is not None:
-        cmds = cmd.split(",")
-    else:
-        cmds = [None] * len(chains)
-
-    for chain, cmd in zip(chains, cmds):
+    # for multiple chains, there can be multiple cmds split by `,`
+    cmds = cmd.split(",") if cmd is not None else []
+    if len(cmds) < len(chains):
+        cmds.extend([None] * (len(chains) - len(cmds)))
+    # optionally warn if extra cmds > chains (ignored)
+    for chain, cmd in zip(chains, cmds):

1050-1057: Avoid passing None and normalize cmd-flags; keep offline gating

Currently gentx_extra_args = [config.get("cmd-flags")] may inject None and/or an unparsed string. Normalize and skip empties.

Apply this diff:

-            gentx_extra_args = [config.get("cmd-flags")]
-            # build extra positional args; inject --offline if sequence or account_number set
+            gentx_extra_args = []
+            # normalize optional CLI flags (string/list/tuple); skip None/empty
+            flags = config.get("cmd-flags")
+            if flags:
+                if isinstance(flags, str):
+                    gentx_extra_args.extend(shlex.split(flags))
+                elif isinstance(flags, (list, tuple)):
+                    gentx_extra_args.extend(map(str, flags))
+                else:
+                    gentx_extra_args.append(str(flags))
+            # build extra positional args; inject --offline if sequence or account_number set
             has_acct = "account_number" in extra_kwargs
             has_seq = "sequence" in extra_kwargs
             if has_acct ^ has_seq:  # xor: only one provided
                 raise ValueError("Both 'account_number' and 'sequence' must be provided together for offline gentx")
             if has_acct and has_seq:
                 gentx_extra_args.append("--offline")

Add this import at the top of the file:

import shlex

1395-1396: grpc_port parsing breaks with scheme; use rsplit with safe fallback

split(":")[1] is wrong for values like tcp://127.0.0.1:9090. Use rsplit(":", 1).

Apply this diff:

-            grpc_address = app_cfg.get("grpc", {}).get("address", "")
-            grpc_port = grpc_address.split(":")[1] if ":" in grpc_address else ports.grpc_port(node["base_port"])
+            grpc_address = app_cfg.get("grpc", {}).get("address", "")
+            grpc_port = ports.grpc_port(node["base_port"])
+            if ":" in grpc_address:
+                grpc_port = grpc_address.rsplit(":", 1)[-1]
🧹 Nitpick comments (1)
pystarport/cluster.py (1)

1319-1321: Potential port conflict: api-listen-addr and debug-listen-addr both :5183

RLY may try to bind two servers to the same port. Consider a separate default (e.g., :5184) or make it configurable.

Apply this diff:

-                            "api-listen-addr": ":5183",
-                            "debug-listen-addr": ":5183",
+                            "api-listen-addr": ":5183",
+                            "debug-listen-addr": ":5184",
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between dba7804 and 0605611.

📒 Files selected for processing (1)
  • pystarport/cluster.py (11 hunks)
🧰 Additional context used
🪛 Ruff (0.12.2)
pystarport/cluster.py

1272-1275: Use ternary operator cmds = cmd.split(",") if cmd is not None else [None] * len(chains) instead of if-else-block

Replace if-else-block with cmds = cmd.split(",") if cmd is not None else [None] * len(chains)

(SIM108)

🔇 Additional comments (12)
pystarport/cluster.py (12)

189-190: Good addition: coin_type surfaced on node creation

Passing coin_type at node creation is useful for downstream key derivation.


261-262: Coin type forwarded to validator account

Correctly propagates coin_type into account creation.


315-318: create_account now forwards kwargs

Allows future extensibility (e.g., coin_type). Looks good.


319-322: create_account_ledger forwards kwargs

Ledger flow can now accept coin_type. All good.


372-374: distribution_community forwards kwargs

Keeps CLI surface consistent. OK.


514-516: make_multisig forwards kwargs

Consistent forwarding; fine.


838-840: New ibc_denom passthrough

Useful convenience method; matches ibc_denom_trace counterpart.


907-918: coin_type respected during devnet account creation

Both ledger and mnemonic flows handle coin_type correctly.


1023-1026: Validator account coin_type honored from node config

Correct propagation during init_devnet.


1034-1046: Expanded gentx optional fields

Additional gentx kwargs (account_number, sequence, gas, fees) are reasonable and align with CLI expectations.


1256-1263: JSON config support and explicit error on unsupported formats

Nice enhancement; error message is clear.


1389-1392: Fix aligns with PR goal: guard when no app-config provided

Skipping oracle process when app-config is absent prevents KeyError. Good.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
pystarport/cosmoscli.py (2)

303-324: Handle empty community pool safely.

The past review comment about empty pool arrays remains unaddressed. If the pool is empty, res["pool"][0] raises IndexError. The module fallback logic is good, but empty pool handling is still needed.

Apply this diff to address the issue:

                     **kwargs,
                 )
             )
-            return parse_amount(res["pool"][0])
+            pool = res.get("pool") or []
+            if not pool:
+                return 0
+            return parse_amount(pool[0])
         except Exception as e:

299-301: Guard against empty commission arrays.

The past review comment about empty arrays remains unaddressed. When no commission has accrued, res can be an empty list after the dict extraction, causing IndexError on res[0].

Apply this diff to address the issue:

 if isinstance(res, dict):
     res = res["commission"]
+if not res:
+    return 0
 return parse_amount(res[0])
🧹 Nitpick comments (1)
pystarport/cosmoscli.py (1)

71-79: Potential detection reliability issue.

Similar to the existing concern with prob_event_query_tx_for, running tendermint without arguments may return usage text with exit code 0, potentially causing false positives. Consider adding --help or verifying both exit code and help text content.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5537ab7 and 0876f18.

📒 Files selected for processing (1)
  • pystarport/cosmoscli.py (14 hunks)
🔇 Additional comments (10)
pystarport/cosmoscli.py (10)

113-113: LGTM!

The has_tendermint_subcommand attribute is properly initialized and follows the established pattern for capability detection.


117-118: LGTM!

The conditional subcommand selection properly handles compatibility between versions that use "tendermint" versus "comet" subcommands.


134-160: LGTM!

The coin_type to key_type mapping for Ethereum (coin_type==60) is correct, and kwargs forwarding enhances extensibility.


162-192: LGTM!

The changes are consistent with create_account and properly handle Ethereum key types for Ledger devices.


421-426: LGTM!

The event_query_tx gating correctly preserves generate-only behavior and allows callers to conditionally disable event querying, improving flexibility.


461-466: LGTM!

The event_query_tx gating is consistent with the transfer method and correctly handles the conditional behavior.


598-607: LGTM!

The kwargs forwarding enhances extensibility and is consistent with other method updates in this PR.


758-761: LGTM!

The conditional subcommand selection maintains compatibility across different chain versions and is consistent with the node_id method.


805-808: LGTM!

The conditional subcommand selection is consistent with create_validator and properly handles version compatibility.


1491-1503: LGTM!

The new ibc_denom method is correctly implemented, follows the same pattern as ibc_denom_trace, and uses standard SHA256 hashing for the denom calculation.

@mmsqe mmsqe merged commit 3bee057 into main Oct 9, 2025
1 check passed
@mmsqe mmsqe deleted the app_config branch November 9, 2025 02:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants