Skip to content

security-fix: resolve critical network, state, and sandbox vulnerabilities#120

Open
SIDDHANTCOOKIE wants to merge 2 commits into
feat/improved-tokenomicsfrom
fix/defensive-security
Open

security-fix: resolve critical network, state, and sandbox vulnerabilities#120
SIDDHANTCOOKIE wants to merge 2 commits into
feat/improved-tokenomicsfrom
fix/defensive-security

Conversation

@SIDDHANTCOOKIE

@SIDDHANTCOOKIE SIDDHANTCOOKIE commented Jul 8, 2026

Copy link
Copy Markdown
Member

Addressed Issues:

This PR addresses 6 major security vulnerabilities identified during a comprehensive defensive security audit of the MiniChain node architecture. These fixes are essential for ensuring network stability, fund safety, and resistance against targeted DoS attacks.

Key Changes

  • Mempool Validation (Critical): Incoming transactions now undergo full state validation (balance/nonce checks) before mempool insertion. Stale/invalid transactions are explicitly evicted during block mining to prevent perpetual queue clogging.
  • Smart Contract Sandbox (Critical): Hardened the execution environment by reducing the multiprocessing timeout (10s -> 1s) and strictly rejecting unbounded AST operations (*, **, @) to mitigate CPU and memory exhaustion attacks.
  • State Preservation (High): Pre-existing funds transferred to a contract's deterministic address are now safely incremented instead of being completely overwritten during contract initialization.
  • Arithmetic DoS Protection (High): Added strict upper bounds (max 256) when parsing incoming network block difficulty to prevent arbitrary-precision memory overflows during PoW chain evaluation.
  • P2P Fork Synchronization (High): Modified resolve_conflicts to validate partial chain responses natively. Nodes will now fetch chain chunks backward from the fork point instead of requesting the entire history from genesis, avoiding libp2p 1MB max frame limits.
  • RBF Spam Prevention (Medium): Replacement transactions in the mempool now strictly require a 10% fee increase (max_fee_per_gas), eliminating free bandwidth exhaustion attacks.

Additional Notes:

AI Usage Disclosure:

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact. AI slop is strongly discouraged and may lead to banning and blocking. Do not spam our repos with AI slop.

Check one of the checkboxes below:

  • This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have read the AI Usage Policy and this PR complies with this policy. I have tested the code locally and I am responsible for it.

I have used the following AI models and tools: TODO

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I have filled this PR template completely and carefully, and I understand that my PR may be closed without review otherwise.

Summary by CodeRabbit

  • New Features

    • Added gas-based transaction settings for sends, deploys, and calls, with support for gas_limit and max_fee_per_gas.
    • Genesis configuration now supports an initial supply value.
  • Bug Fixes

    • Improved transaction and block validation during syncing, RPC submission, and mempool handling.
    • Updated fee calculations to use gas usage, and tightened chain reorganization and fork recovery behavior.
    • Strengthened contract execution safety checks and shortened timeout handling.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b7fbdd8f-d352-411c-a65d-eb8a71f58a6c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR replaces the flat transaction fee field with gas_limit/max_fee_per_gas across transactions, state transitions, mempool, RPC, CLI, and mining code. It adds replace-by-fee logic, stricter PoW/timestamp/genesis-supply validation, a reworked chain reorg/sync flow, and tighter contract execution limits, with tests updated accordingly.

Changes

Gas fee model and consensus hardening

Layer / File(s) Summary
Transaction gas fields contract
minichain/transaction.py, genesis.json, tests/test_contract.py, tests/test_contract_transfers.py, tests/test_core.py
Transaction now carries gas_limit/max_fee_per_gas instead of fee in fields, constructor, and (de)serialization; genesis.json adds initial_supply; tests build transactions with the new gas parameters.
State cost/refund computation
minichain/state.py
Transaction validation, cost deduction, receipt gas_used, refund crediting, and contract balance preservation now use gas_limit/max_fee_per_gas.
Mempool replace-by-fee and ordering
minichain/mempool.py
Replacement transactions require ≥10% higher max_fee_per_gas; insertion ordering compares max_fee_per_gas instead of fee.
RPC/P2P/CLI validation and mining fee wiring
minichain/rpc.py, main.py
RPC and P2P tx handling validate via verify_transaction_logic; CLI send/deploy/call and parse_tx_params/submit_and_broadcast use gas parameters; mining fee totals sum gas_used * max_fee_per_gas.
PoW/timestamp/genesis supply validation
minichain/block.py, minichain/chain.py, tests/test_persistence.py
Block difficulty is bounded and validated; block timestamps must strictly increase within a future bound; genesis initial_supply is checked against alloc sums; persistence tests use deterministic timestamps.
Chain reorg and P2P sync rework
minichain/chain.py, main.py, tests/test_reorg.py
resolve_conflicts rebuilds state from a validated proposed_chain and computes orphaned transactions; P2P sync requests bounded backward chunks and reworks linear-catchup vs. reorg selection; reorg tests use gas fields.
Contract execution hardening
minichain/contract.py
Subprocess timeout reduced to 1s; AST validation rejects multiplication/power/matmul operators.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI(main.py)
  participant Chain as Chain.state
  participant Mempool
  participant Network

  CLI->>Chain: verify_transaction_logic(tx)
  Chain-->>CLI: ValidationStatus
  alt VALID
    CLI->>Mempool: add_transaction(tx)
    CLI->>Network: broadcast(tx)
  else INVALID
    CLI-->>CLI: reject transaction
  end
Loading

Possibly related PRs

  • StabilityNexus/MiniChain#91: Both PRs change the transaction fee model and how total fees are computed/validated and credited to the miner during block processing.
  • StabilityNexus/MiniChain#102: Both PRs heavily modify minichain/chain.py's resolve_conflicts reorg logic and the P2P syncing flow that invokes it.
  • StabilityNexus/MiniChain#106: Both PRs overlap on ValidationStatus-based State.verify_transaction_logic usage across block/tx handling and RPC/CLI paths.

Suggested labels: Python Lang

Suggested reviewers: Zahnentferner

Poem

A rabbit hopped through gas and fee,
Swapped old coins for max_fee_per_gas, wheee!
Forks got checked, and clocks kept true,
Mempool now bids the highest few.
🐇⛏️ Onward the chain hops, safe and new!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the PR’s main security-focused changes across network, state, and sandbox areas.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/defensive-security

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.

@SIDDHANTCOOKIE SIDDHANTCOOKIE changed the base branch from main to feat/improved-tokenomics July 8, 2026 15:30

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
main.py (1)

140-145: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not evict future-nonce transactions as stale.

Line 145 removes any tx that fails current application; a valid nonce-gap transaction (tx.nonce > expected_nonce) is not stale and should remain queued until earlier nonces arrive.

Proposed fix
         expected_nonce = temp_state.get_account(tx.sender).get("nonce", 0)
         if tx.nonce < expected_nonce:
             stale_txs.append(tx)
             continue
+        if tx.nonce > expected_nonce:
+            continue
             
         receipt = temp_state.validate_and_apply(tx)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@main.py` around lines 140 - 145, The transaction handling in the apply/queue
flow is marking every non-applied tx as stale, but future-nonce transactions
from validate_and_apply should stay queued instead of being evicted. Update the
logic around receipt handling in the tx processing block to distinguish
nonce-gap cases (tx.nonce greater than the expected nonce) from truly stale
transactions, and only append to stale_txs when the transaction is actually
invalid or outdated; keep valid future-nonce txs pending for later processing.
minichain/state.py (1)

57-96: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Run amount/gas well-formed validation inside verify_transaction_logic().

Line 57 is used by RPC/CLI/P2P before mempool insertion, but only validate_and_apply_with_status() calls _amounts_well_formed(). A signed tx with negative amount, gas_limit, or max_fee_per_gas can pass pre-mempool validation, and non-integer values can raise while handling peer input.

Proposed fix
 def verify_transaction_logic(self, tx):
     from .validators import ValidationStatus
+    if not self._amounts_well_formed(tx):
+        logger.warning("Malformed tx %s: amount/gas fields must be non-negative integers", getattr(tx, "tx_id", "<unknown>"))
+        return ValidationStatus.MALFORMED
+
     if not tx.verify():
         logger.error("Error: Invalid signature for tx from %s...", tx.sender[:8])
         return ValidationStatus.INVALID
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@minichain/state.py` around lines 57 - 96, `verify_transaction_logic()`
currently skips the amount/gas semantic guard, so invalid transactions can pass
pre-mempool checks or raise on bad peer input. Add the
`_amounts_well_formed(tx)` validation at the start of
`verify_transaction_logic()` in `State`, and fail fast with
`ValidationStatus.FAILED` before any balance or nonce checks if `amount`,
`gas_limit`, or `max_fee_per_gas` are not valid non-negative integers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@main.py`:
- Around line 10-12: Update the top-level help text in HELP_TEXT so it matches
the command handlers’ current signatures. The entries for send, deploy, and call
should include the optional [gas_limit] [max_fee_per_gas] parameters, and you
should adjust the help strings in the help text definition rather than the
handlers themselves.
- Around line 228-229: The transaction validation check in the
`verify_transaction_logic` path is collapsing every non-VALID result into
`ValidationStatus.INVALID`, which loses the distinction between `FAILED` and
`INVALID`. Update the logic around `chain.state.verify_transaction_logic(tx)` to
return the exact status produced by state validation instead of remapping it, so
callers can handle `FAILED` without triggering peer penalties. Use the
`verify_transaction_logic` and `ValidationStatus` symbols to locate and adjust
the return flow.

In `@minichain/block.py`:
- Around line 135-140: The difficulty parsing in block.py only enforces an upper
bound, so negative values can still flow into PoW validation and make
validate_block_link_and_hash in chain.py accept invalid hashes. Update the
raw_diff/parsed_diff handling to reject negative difficulty values as well as
values above 256, ideally by validating parsed_diff immediately after conversion
and raising a ValueError for any value below zero.

In `@minichain/contract.py`:
- Around line 226-228: The operator-ban check in the contract validator only
covers ast.BinOp, so augmented assignments like **=, *=, and @= can still bypass
it. Update the existing guard in the contract-checking logic to also reject
ast.AugAssign when its operator is Mult, Pow, or MatMult, using the same
rejection path and warning message pattern already used in the current operator
check.
- Line 172: Remove the wall-clock timeout from execute() so contract completion
is determined only by gas/VM limits and not by p.join(timeout=1), which can
cause nondeterministic success/failure across nodes. Also tighten
_validate_code_ast() so it rejects augmented operator forms as well as
ast.BinOp, especially ast.AugAssign cases like *=, **=, and @=, to close the
operator bypass.

---

Outside diff comments:
In `@main.py`:
- Around line 140-145: The transaction handling in the apply/queue flow is
marking every non-applied tx as stale, but future-nonce transactions from
validate_and_apply should stay queued instead of being evicted. Update the logic
around receipt handling in the tx processing block to distinguish nonce-gap
cases (tx.nonce greater than the expected nonce) from truly stale transactions,
and only append to stale_txs when the transaction is actually invalid or
outdated; keep valid future-nonce txs pending for later processing.

In `@minichain/state.py`:
- Around line 57-96: `verify_transaction_logic()` currently skips the amount/gas
semantic guard, so invalid transactions can pass pre-mempool checks or raise on
bad peer input. Add the `_amounts_well_formed(tx)` validation at the start of
`verify_transaction_logic()` in `State`, and fail fast with
`ValidationStatus.FAILED` before any balance or nonce checks if `amount`,
`gas_limit`, or `max_fee_per_gas` are not valid non-negative integers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4fa74ca5-ebcc-4dba-9f4d-aeb0f4f43872

📥 Commits

Reviewing files that changed from the base of the PR and between 9419fdb and a521a23.

📒 Files selected for processing (14)
  • genesis.json
  • main.py
  • minichain/block.py
  • minichain/chain.py
  • minichain/contract.py
  • minichain/mempool.py
  • minichain/rpc.py
  • minichain/state.py
  • minichain/transaction.py
  • tests/test_contract.py
  • tests/test_contract_transfers.py
  • tests/test_core.py
  • tests/test_persistence.py
  • tests/test_reorg.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
main.py (1)

140-145: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not evict future-nonce transactions as stale.

Line 145 removes any tx that fails current application; a valid nonce-gap transaction (tx.nonce > expected_nonce) is not stale and should remain queued until earlier nonces arrive.

Proposed fix
         expected_nonce = temp_state.get_account(tx.sender).get("nonce", 0)
         if tx.nonce < expected_nonce:
             stale_txs.append(tx)
             continue
+        if tx.nonce > expected_nonce:
+            continue
             
         receipt = temp_state.validate_and_apply(tx)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@main.py` around lines 140 - 145, The transaction handling in the apply/queue
flow is marking every non-applied tx as stale, but future-nonce transactions
from validate_and_apply should stay queued instead of being evicted. Update the
logic around receipt handling in the tx processing block to distinguish
nonce-gap cases (tx.nonce greater than the expected nonce) from truly stale
transactions, and only append to stale_txs when the transaction is actually
invalid or outdated; keep valid future-nonce txs pending for later processing.
minichain/state.py (1)

57-96: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Run amount/gas well-formed validation inside verify_transaction_logic().

Line 57 is used by RPC/CLI/P2P before mempool insertion, but only validate_and_apply_with_status() calls _amounts_well_formed(). A signed tx with negative amount, gas_limit, or max_fee_per_gas can pass pre-mempool validation, and non-integer values can raise while handling peer input.

Proposed fix
 def verify_transaction_logic(self, tx):
     from .validators import ValidationStatus
+    if not self._amounts_well_formed(tx):
+        logger.warning("Malformed tx %s: amount/gas fields must be non-negative integers", getattr(tx, "tx_id", "<unknown>"))
+        return ValidationStatus.MALFORMED
+
     if not tx.verify():
         logger.error("Error: Invalid signature for tx from %s...", tx.sender[:8])
         return ValidationStatus.INVALID
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@minichain/state.py` around lines 57 - 96, `verify_transaction_logic()`
currently skips the amount/gas semantic guard, so invalid transactions can pass
pre-mempool checks or raise on bad peer input. Add the
`_amounts_well_formed(tx)` validation at the start of
`verify_transaction_logic()` in `State`, and fail fast with
`ValidationStatus.FAILED` before any balance or nonce checks if `amount`,
`gas_limit`, or `max_fee_per_gas` are not valid non-negative integers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@main.py`:
- Around line 10-12: Update the top-level help text in HELP_TEXT so it matches
the command handlers’ current signatures. The entries for send, deploy, and call
should include the optional [gas_limit] [max_fee_per_gas] parameters, and you
should adjust the help strings in the help text definition rather than the
handlers themselves.
- Around line 228-229: The transaction validation check in the
`verify_transaction_logic` path is collapsing every non-VALID result into
`ValidationStatus.INVALID`, which loses the distinction between `FAILED` and
`INVALID`. Update the logic around `chain.state.verify_transaction_logic(tx)` to
return the exact status produced by state validation instead of remapping it, so
callers can handle `FAILED` without triggering peer penalties. Use the
`verify_transaction_logic` and `ValidationStatus` symbols to locate and adjust
the return flow.

In `@minichain/block.py`:
- Around line 135-140: The difficulty parsing in block.py only enforces an upper
bound, so negative values can still flow into PoW validation and make
validate_block_link_and_hash in chain.py accept invalid hashes. Update the
raw_diff/parsed_diff handling to reject negative difficulty values as well as
values above 256, ideally by validating parsed_diff immediately after conversion
and raising a ValueError for any value below zero.

In `@minichain/contract.py`:
- Around line 226-228: The operator-ban check in the contract validator only
covers ast.BinOp, so augmented assignments like **=, *=, and @= can still bypass
it. Update the existing guard in the contract-checking logic to also reject
ast.AugAssign when its operator is Mult, Pow, or MatMult, using the same
rejection path and warning message pattern already used in the current operator
check.
- Line 172: Remove the wall-clock timeout from execute() so contract completion
is determined only by gas/VM limits and not by p.join(timeout=1), which can
cause nondeterministic success/failure across nodes. Also tighten
_validate_code_ast() so it rejects augmented operator forms as well as
ast.BinOp, especially ast.AugAssign cases like *=, **=, and @=, to close the
operator bypass.

---

Outside diff comments:
In `@main.py`:
- Around line 140-145: The transaction handling in the apply/queue flow is
marking every non-applied tx as stale, but future-nonce transactions from
validate_and_apply should stay queued instead of being evicted. Update the logic
around receipt handling in the tx processing block to distinguish nonce-gap
cases (tx.nonce greater than the expected nonce) from truly stale transactions,
and only append to stale_txs when the transaction is actually invalid or
outdated; keep valid future-nonce txs pending for later processing.

In `@minichain/state.py`:
- Around line 57-96: `verify_transaction_logic()` currently skips the amount/gas
semantic guard, so invalid transactions can pass pre-mempool checks or raise on
bad peer input. Add the `_amounts_well_formed(tx)` validation at the start of
`verify_transaction_logic()` in `State`, and fail fast with
`ValidationStatus.FAILED` before any balance or nonce checks if `amount`,
`gas_limit`, or `max_fee_per_gas` are not valid non-negative integers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4fa74ca5-ebcc-4dba-9f4d-aeb0f4f43872

📥 Commits

Reviewing files that changed from the base of the PR and between 9419fdb and a521a23.

📒 Files selected for processing (14)
  • genesis.json
  • main.py
  • minichain/block.py
  • minichain/chain.py
  • minichain/contract.py
  • minichain/mempool.py
  • minichain/rpc.py
  • minichain/state.py
  • minichain/transaction.py
  • tests/test_contract.py
  • tests/test_contract_transfers.py
  • tests/test_core.py
  • tests/test_persistence.py
  • tests/test_reorg.py
🛑 Comments failed to post (5)
main.py (2)

10-12: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Update top-level help text to show gas parameters.

The command handlers accept [gas_limit] [max_fee_per_gas], but HELP_TEXT still shows only the legacy short forms.

Proposed fix
-    send <to> <amount>      - send coins                        
-    deploy <file>           - deploy a contract                 
-    call <addr> <data>      - call a contract                   
+    send <to> <amount> [gas_limit] [max_fee_per_gas]       - send coins
+    deploy <file> [amount] [gas_limit] [max_fee_per_gas]   - deploy a contract
+    call <addr> <data> [amount] [gas_limit] [max_fee_per_gas] - call a contract
📝 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.

    send <to> <amount> [gas_limit] [max_fee_per_gas]       - send coins
    deploy <file> [amount] [gas_limit] [max_fee_per_gas]   - deploy a contract
    call <addr> <data> [amount] [gas_limit] [max_fee_per_gas] - call a contract
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@main.py` around lines 10 - 12, Update the top-level help text in HELP_TEXT so
it matches the command handlers’ current signatures. The entries for send,
deploy, and call should include the optional [gas_limit] [max_fee_per_gas]
parameters, and you should adjust the help strings in the help text definition
rather than the handlers themselves.

228-229: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Preserve FAILED vs INVALID from state validation.

The P2P layer bans/disconnects peers on INVALID; mapping every non-VALID state result to INVALID can punish peers for race-prone conditions like insufficient balance or nonce mismatch. Return the actual status.

Proposed fix
-            if chain.state.verify_transaction_logic(tx) != ValidationStatus.VALID:
-                return ValidationStatus.INVALID
+            status = chain.state.verify_transaction_logic(tx)
+            if status != ValidationStatus.VALID:
+                return status
📝 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.

            status = chain.state.verify_transaction_logic(tx)
            if status != ValidationStatus.VALID:
                return status
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@main.py` around lines 228 - 229, The transaction validation check in the
`verify_transaction_logic` path is collapsing every non-VALID result into
`ValidationStatus.INVALID`, which loses the distinction between `FAILED` and
`INVALID`. Update the logic around `chain.state.verify_transaction_logic(tx)` to
return the exact status produced by state validation instead of remapping it, so
callers can handle `FAILED` without triggering peer penalties. Use the
`verify_transaction_logic` and `ValidationStatus` symbols to locate and adjust
the return flow.
minichain/block.py (1)

135-140: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Add lower-bound validation for negative difficulty values.

The upper-bound check (> 256) prevents memory overflow, but there is no lower-bound check. A negative difficulty bypasses PoW in validate_block_link_and_hash (chain.py): target = "0" * (block.difficulty or 1) — since -1 is truthy, "0" * -1 evaluates to "", and block.hash.startswith("") is always True.

This is mitigated by the difficulty-match check in _apply_block (block.difficulty != difficulty), which would reject a peer-sent block whose difficulty doesn't match the expected value. However, the PoW validation function itself is flawed and should not accept negative difficulties.

🔒️ Proposed fix
         if raw_diff is not None:
             parsed_diff = int(raw_diff)
-            if parsed_diff > 256:
-                raise ValueError(f"Difficulty too large: {parsed_diff}")
+            if parsed_diff < 0 or parsed_diff > 256:
+                raise ValueError(f"Difficulty out of range [0, 256]: {parsed_diff}")
         else:
             parsed_diff = None
📝 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.

        if raw_diff is not None:
            parsed_diff = int(raw_diff)
            if parsed_diff < 0 or parsed_diff > 256:
                raise ValueError(f"Difficulty out of range [0, 256]: {parsed_diff}")
        else:
            parsed_diff = None
🧰 Tools
🪛 Ruff (0.15.20)

[warning] 138-138: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@minichain/block.py` around lines 135 - 140, The difficulty parsing in
block.py only enforces an upper bound, so negative values can still flow into
PoW validation and make validate_block_link_and_hash in chain.py accept invalid
hashes. Update the raw_diff/parsed_diff handling to reject negative difficulty
values as well as values above 256, ideally by validating parsed_diff
immediately after conversion and raising a ValueError for any value below zero.
minichain/contract.py (2)

172-172: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how execute() outcomes flow into state/receipts and block validation determinism.
fd -t f 'state.py' minichain -x sed -n '1,220p'
rg -nP 'timed out|Out of gas|execute\(' minichain -C2

Repository: StabilityNexus/MiniChain

Length of output: 15783


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== contract.py around execution and validation =="
sed -n '90,220p' minichain/contract.py
echo
sed -n '220,320p' minichain/contract.py

echo
echo "== AST probe: which operators are rejected by _validate_code_ast? =="
python3 - <<'PY'
import ast
from pathlib import Path

src = Path("minichain/contract.py").read_text()
mod = ast.parse(src)

for node in mod.body:
    if isinstance(node, ast.ClassDef) and node.name == "ContractMachine":
        for item in node.body:
            if isinstance(item, ast.FunctionDef) and item.name == "_validate_code_ast":
                print(ast.get_source_segment(src, item))
                break
        break

samples = {
    "binop_pow": "x = 2 ** 3",
    "augassign_pow": "x = 2\nx **= 3",
    "augassign_mult": "x = 2\nx *= 3",
    "augassign_matmul": "x = 2\nx @= 3",
}

class RejectingVisitor(ast.NodeVisitor):
    def visit_BinOp(self, node):
        if isinstance(node.op, (ast.Mult, ast.Pow, ast.MatMult)):
            raise ValueError(f"rejected BinOp {type(node.op).__name__}")
        self.generic_visit(node)

for name, code in samples.items():
    tree = ast.parse(code)
    try:
        RejectingVisitor().visit(tree)
        print(f"{name}: accepted")
    except Exception as e:
        print(f"{name}: {e}")
PY

Repository: StabilityNexus/MiniChain

Length of output: 8237


Keep contract execution deterministic and close the operator bypass

  • A 1s wall-clock cutoff in execute() can make the same transaction succeed on one node and fail on another, which can split receipts/state roots. Keep the validity bound tied to gas, not elapsed time.
  • _validate_code_ast() only rejects ast.BinOp, so AugAssign forms like *=, **=, and @= still bypass the unbounded-operator guard.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@minichain/contract.py` at line 172, Remove the wall-clock timeout from
execute() so contract completion is determined only by gas/VM limits and not by
p.join(timeout=1), which can cause nondeterministic success/failure across
nodes. Also tighten _validate_code_ast() so it rejects augmented operator forms
as well as ast.BinOp, especially ast.AugAssign cases like *=, **=, and @=, to
close the operator bypass.

226-228: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm no existing AugAssign handling elsewhere in the validator.
rg -nP 'AugAssign|ast\.(Mult|Pow|MatMult)' minichain/contract.py -C1

Repository: StabilityNexus/MiniChain

Length of output: 422


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the validator around the reported lines and the AST traversal context.
ast-grep outline minichain/contract.py --view expanded
printf '\n--- lines 180-260 ---\n'
sed -n '180,260p' minichain/contract.py
printf '\n--- search for generic AST walking / validation logic ---\n'
rg -n "ast\.walk|NodeVisitor|visit_|for node in|ast\.BinOp|ast\.AugAssign" minichain/contract.py -C 2

Repository: StabilityNexus/MiniChain

Length of output: 4292


Cover ast.AugAssign in the operator ban. ast.AugAssign isn’t checked here, so **=, *=, and @= still slip through and can trigger the same resource-exhaustion path. Include ast.AugAssign in the guard.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@minichain/contract.py` around lines 226 - 228, The operator-ban check in the
contract validator only covers ast.BinOp, so augmented assignments like **=, *=,
and @= can still bypass it. Update the existing guard in the contract-checking
logic to also reject ast.AugAssign when its operator is Mult, Pow, or MatMult,
using the same rejection path and warning message pattern already used in the
current operator check.

@SIDDHANTCOOKIE

Copy link
Copy Markdown
Member Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant