Skip to content

feat(derive): add P2SH-P2WPKH address support#89

Merged
oritwoen merged 3 commits into
mainfrom
feat/derive-p2sh-p2wpkh
Mar 22, 2026
Merged

feat(derive): add P2SH-P2WPKH address support#89
oritwoen merged 3 commits into
mainfrom
feat/derive-p2sh-p2wpkh

Conversation

@oritwoen
Copy link
Copy Markdown
Owner

Closes #82

P2SH-P2WPKH (the 3-prefixed wrapped segwit addresses) was the dominant address format from ~2017 through 2021. If a vulnerable key had funds sent to its P2SH-P2WPKH address, the scanner couldn't find them because that address was never computed.

Adds p2sh_p2wpkh field to DerivedKey using Address::p2shwpkh() from the bitcoin crate. Updated everywhere that matters: matcher, provider verification, console output (verbose + hit), storage schema (Parquet + Iceberg), and the single command display.

DerivedKey::addresses() returns 5 elements now instead of 4.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e935a0ae-114e-474c-9d2c-569f7514912e

📥 Commits

Reviewing files that changed from the base of the PR and between 1d7e94a and 14a8197.

📒 Files selected for processing (1)
  • src/storage/iceberg/schema.rs
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Order imports as: external crates → std → blank line → super:: → blank line → crate::
Prefer ? operator over .unwrap() in new code
Use PascalCase for types and structs
Use snake_case for function and method names
Use SCREAMING_SNAKE_CASE for constants
Use snake_case for file and module names

Files:

  • src/storage/iceberg/schema.rs
src/{derive,matcher,network,benchmark,provider,transform,analyze,source,output,gpu,storage}/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Implement custom error enums with Display and Error trait implementations for domain modules

Files:

  • src/storage/iceberg/schema.rs
src/{transform,output,storage}/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Use builder pattern for configuration (e.g., ElectrumTransform::new().with_change(), ParquetBackend::new().with_compression())

Files:

  • src/storage/iceberg/schema.rs
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Use indicatif::ProgressBar for long-running operations
Place inline tests in #[cfg(test)] mod tests at the end of each file, using standard assert! and assert_eq! macros
Use tempfile crate for file I/O tests

Files:

  • src/storage/iceberg/schema.rs
🧠 Learnings (7)
📓 Common learnings
Learnt from: oritwoen
Repo: oritwoen/vuke PR: 86
File: src/provider.rs:442-443
Timestamp: 2026-03-22T18:38:29.431Z
Learning: In oritwoen/vuke src/provider.rs, the `derived.p2tr == addr` branch in `verify_key_boha` has no test coverage intentionally — no boha puzzles currently have P2TR/Taproot addresses. The branch is forward-compatible for when taproot puzzles are added to boha. Do not flag missing P2TR test coverage in this function.
📚 Learning: 2026-03-22T19:15:02.128Z
Learnt from: oritwoen
Repo: oritwoen/vuke PR: 89
File: src/storage/iceberg/schema.rs:136-141
Timestamp: 2026-03-22T19:15:02.128Z
Learning: In oritwoen/vuke src/storage/iceberg/schema.rs, iceberg field-presence tests are intentionally deferred — the Parquet schema tests (src/storage/schema.rs) are considered the canonical coverage for field names and counts. Do not flag missing per-field iceberg schema assertions as issues; only flag broken existing count assertions.

Applied to files:

  • src/storage/iceberg/schema.rs
📚 Learning: 2026-03-02T14:18:34.668Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: src/storage/AGENTS.md:0-0
Timestamp: 2026-03-02T14:18:34.668Z
Learning: Applies to src/storage/storage/schema.rs : Update Arrow schema definition in `schema.rs`, including `result_schema()` function and field constants

Applied to files:

  • src/storage/iceberg/schema.rs
📚 Learning: 2026-03-02T14:18:34.668Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: src/storage/AGENTS.md:0-0
Timestamp: 2026-03-02T14:18:34.668Z
Learning: Applies to src/storage/storage/schema.rs : Update `records_to_batch()` conversion when adding new columns to the schema

Applied to files:

  • src/storage/iceberg/schema.rs
📚 Learning: 2026-03-02T14:18:34.668Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: src/storage/AGENTS.md:0-0
Timestamp: 2026-03-02T14:18:34.668Z
Learning: Applies to src/storage/storage/schema.rs : Map variable-length fields to fixed columns in schema for SQL-friendly querying

Applied to files:

  • src/storage/iceberg/schema.rs
📚 Learning: 2026-03-22T18:38:29.431Z
Learnt from: oritwoen
Repo: oritwoen/vuke PR: 86
File: src/provider.rs:442-443
Timestamp: 2026-03-22T18:38:29.431Z
Learning: In oritwoen/vuke src/provider.rs, the `derived.p2tr == addr` branch in `verify_key_boha` has no test coverage intentionally — no boha puzzles currently have P2TR/Taproot addresses. The branch is forward-compatible for when taproot puzzles are added to boha. Do not flag missing P2TR test coverage in this function.

Applied to files:

  • src/storage/iceberg/schema.rs
📚 Learning: 2026-03-13T13:20:58.369Z
Learnt from: oritwoen
Repo: oritwoen/vuke PR: 76
File: src/source/wordlist.rs:134-143
Timestamp: 2026-03-13T13:20:58.369Z
Learning: Do not flag .ok() calls on output.hit() / output.key() inside Rayon par_chunks().for_each() closures in Rust files (e.g., src/source/wordlist.rs) as bugs. Rayon for_each requires FnMut() -> (), which cannot propagate Result with ?. This is a known limitation discussed in issue `#77`. Treat these .ok() usages in this pattern as intentional and not regressions; focus review on actual logic or error handling outside this specific closure pattern.

Applied to files:

  • src/storage/iceberg/schema.rs
🧬 Code graph analysis (1)
src/storage/iceberg/schema.rs (2)
src/storage/parquet_backend.rs (1)
  • schema (267-269)
src/storage/mod.rs (1)
  • schema (161-161)
🔇 Additional comments (2)
src/storage/iceberg/schema.rs (2)

136-141: LGTM!

New field follows the existing pattern exactly — optional String type, sequential ID via next_id(), positioned at end to avoid breaking existing field IDs. Matches the other address fields (P2WPKH, P2TR).


166-166: LGTM!

Count assertion correctly updated to 21 to match the new field.


📝 Walkthrough

Walkthrough

This PR adds P2SH-P2WPKH address derivation to DerivedKey, includes it in DerivedKey::addresses(), extends matching to detect P2SH-P2WPKH, and propagates the new field through console output, storage records, Arrow/Iceberg schemas, and provider verification.

Changes

Cohort / File(s) Summary
Core Derivation & Addresses
src/derive.rs
Added pub p2sh_p2wpkh: String to DerivedKey; addresses() now returns 5 elements; KeyDeriver::derive computes p2sh_p2wpkh via Address::p2shwpkh.
Matching Logic
src/matcher.rs
Added AddressType::P2shP2wpkh and as_str() arm; Matcher::check() tests derived.p2sh_p2wpkh (checked before P2WPKH); new unit test verifies matching and prefix.
Console & Output Rendering
src/main.rs, src/output/console.rs, src/output/multi.rs
Console printing (run_single) and verbose/hit outputs include p2sh_p2wpkh; test fixtures updated to populate the field.
Storage Output
src/output/storage.rs
StorageOutput::write_record now writes an AddressRecord for p2sh_p2wpkh; test fixture updated.
Arrow & Iceberg Schemas
src/storage/schema.rs, src/storage/iceberg/schema.rs
Added ADDRESS_P2SH_P2WPKH constant; Arrow schema and records_to_batch now include new nullable address_p2sh_p2wpkh column (schema size 21). Iceberg schema builder appends optional string field for address_p2sh_p2wpkh.
Provider Verification
src/provider.rs
verify_key_boha gains an equality check for derived.p2sh_p2wpkh and records matches as "p2sh_p2wpkh" when found.

Sequence Diagram(s)

sequenceDiagram
    participant KeyDeriver
    participant DerivedKey
    participant Matcher
    participant Provider
    participant Storage
    participant Console

    KeyDeriver->>DerivedKey: derive(pubkey) -> fill addresses (p2pkh, p2wpkh, p2tr, p2sh_p2wpkh)
    DerivedKey->>Matcher: addresses()
    Matcher->>Provider: check(puzzle/address against addresses)
    alt match found
        Provider->>Console: emit hit with address_type (may be p2sh_p2wpkh)
        Provider->>Storage: write record including address_p2sh_p2wpkh
    else no match
        Provider->>Console: no-hit
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • cubic-dev-ai

Poem

A wrapped witness wears a friendly '3',
One more field joins the forensic tree,
Derive, match, print, and store,
Old outputs gain a little more,
Coins once hidden find a key.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly and specifically describes the main change: adding P2SH-P2WPKH address support to the derive functionality.
Description check ✅ Passed Description is directly related to the changeset, explaining the motivation (missing address format), technical approach, and scope of updates across the codebase.
Linked Issues check ✅ Passed PR fully satisfies issue #82 requirements: adds p2sh_p2wpkh field to DerivedKey, computes it via Address::p2shwpkh(), adds P2shP2wpkh variant to AddressType, updates addresses() to return 5 elements, and updates matcher/output/storage across all affected modules.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing P2SH-P2WPKH support; no unrelated modifications introduced.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/derive-p2sh-p2wpkh
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/derive-p2sh-p2wpkh

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

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a 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 9 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Requires human review: Contains storage schema changes (Parquet/Iceberg) and modifications to core address derivation/matching logic, which require human review.

Architecture diagram
sequenceDiagram
    participant CLI as CLI / Main
    participant KD as KeyDeriver
    participant DK as DerivedKey
    participant M as Matcher
    participant P as Provider
    participant Out as Output (Console/Storage)

    Note over CLI,Out: Address Derivation & Matching Flow

    CLI->>KD: derive(private_key)
    KD->>KD: Generate P2PKH (Comp/Uncomp)
    KD->>KD: NEW: Generate P2SH-P2WPKH (Address::p2shwpkh)
    KD->>KD: Generate P2WPKH & P2TR
    KD-->>CLI: Return DerivedKey object

    CLI->>M: check(DerivedKey)
    M->>DK: CHANGED: addresses() returns 5 strings
    loop For each address in DerivedKey
        M->>M: Compare against target list
        alt NEW: Match on P2SH-P2WPKH
            M-->>CLI: Return MatchInfo (AddressType::P2shP2wpkh)
        else Match on other types
            M-->>CLI: Return MatchInfo
        end
    end

    opt Match Found
        CLI->>P: verify_key_boha(key, query)
        P->>DK: Compare address strings
        Note right of P: NEW: Includes P2SH-P2WPKH check
        P-->>CLI: Return VerifyReport

        CLI->>Out: emit(DerivedKey, MatchInfo)
        
        par Console Output
            Out->>Out: CHANGED: Print P2SH-P2WPKH address
        and Storage Output (Parquet/Iceberg)
            Out->>Out: NEW: Map P2SH-P2WPKH to AddressRecord
        end
    end

    Note over CLI: CLI 'single' command displays 5 addresses (CHANGED)
Loading

@oritwoen oritwoen self-assigned this Mar 22, 2026
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

Caution

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

⚠️ Outside diff range comments (3)
src/storage/schema.rs (3)

473-474: ⚠️ Potential issue | 🔴 Critical

Test will fail: num_columns() assertion is stale.

Same issue as records_to_batch_empty — should be 21.

Proposed fix
         assert_eq!(batch.num_rows(), 1);
-        assert_eq!(batch.num_columns(), 20);
+        assert_eq!(batch.num_columns(), 21);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/storage/schema.rs` around lines 473 - 474, The test asserts
batch.num_columns() == 20 but the schema now has 21 columns; update the
assertion in the failing test (the same test that checks batch.num_rows() and
batch.num_columns(), referenced by the test name around these assertions, e.g.,
records_to_batch or records_to_batch_empty) to expect 21 instead of 20 so the
assertion matches the current schema column count.

391-396: ⚠️ Potential issue | 🔴 Critical

Test will fail: num_columns() assertion is stale.

Schema now has 21 columns, not 20.

Proposed fix
     fn records_to_batch_empty() {
         let batch = records_to_batch(&[]).unwrap();
         assert_eq!(batch.num_rows(), 0);
-        assert_eq!(batch.num_columns(), 20);
+        assert_eq!(batch.num_columns(), 21);
         assert_eq!(batch.schema(), Arc::new(result_schema()));
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/storage/schema.rs` around lines 391 - 396, The unit test
records_to_batch_empty in src/storage/schema.rs asserts that batch.num_columns()
== 20 but the schema was changed to 21 columns; update the expected column count
to 21 (and ensure the expected schema from result_schema() still matches) by
changing the assertion in records_to_batch_empty to
assert_eq!(batch.num_columns(), 21) and run the tests to confirm consistency
with records_to_batch and result_schema().

518-530: ⚠️ Potential issue | 🔴 Critical

Test will fail: column indices shifted after new field insertion.

Column 16 is now address_p2sh_p2wpkh, not address_p2wpkh. Column 18 is now address_p2tr, not wif_compressed.

Proposed fix
         let addr_p2wpkh_col = batch
-            .column(16)
+            .column(17)
             .as_any()
             .downcast_ref::<StringArray>()
             .unwrap();
         assert_eq!(addr_p2wpkh_col.value(0), "bc1qtest");

         let wif_compressed_col = batch
-            .column(18)
+            .column(19)
             .as_any()
             .downcast_ref::<StringArray>()
             .unwrap();
         assert_eq!(wif_compressed_col.value(0), "L1234");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/storage/schema.rs` around lines 518 - 530, The test uses hard-coded
column indices that shifted after inserting a new field; update the indices so
addr_p2wpkh_col reads from column 17 (not 16) and wif_compressed_col reads from
column 19 (not 18). Locate the downcast/ref calls for addr_p2wpkh_col and
wif_compressed_col in src/storage/schema.rs and change their .column(...) index
values accordingly, keeping the same downcast to StringArray and the existing
assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/output/console.rs`:
- Line 171: Add a regression assertion that the newly added p2sh_p2wpkh field is
actually rendered in the verbose output: in the test that constructs the fixture
and produces the verbose/printed output (the test that calls the console
renderer/to_string()/render for the fixture), assert that the output contains
the p2sh_p2wpkh value (e.g. "3Wrapped..." or the fixture variable) so a broken
print line will fail; locate the fixture usage and add a
contains/assert!(output.contains(...)) for p2sh_p2wpkh.

In `@src/storage/iceberg/schema.rs`:
- Around line 136-141: Add a unit test that explicitly asserts the presence of
the new field name "address_p2sh_p2wpkh" in the Iceberg schema instead of
relying only on field count/subset checks; locate the test that currently
validates the schema (the one doing a field count and subset name checks) and
add an assertion that the schema's field names include "address_p2sh_p2wpkh"
(the field created by NestedField::optional(next_id(),
fields::ADDRESS_P2SH_P2WPKH, Type::Primitive(PrimitiveType::String))). Ensure
the test fails if that exact name is missing to catch regressions.

---

Outside diff comments:
In `@src/storage/schema.rs`:
- Around line 473-474: The test asserts batch.num_columns() == 20 but the schema
now has 21 columns; update the assertion in the failing test (the same test that
checks batch.num_rows() and batch.num_columns(), referenced by the test name
around these assertions, e.g., records_to_batch or records_to_batch_empty) to
expect 21 instead of 20 so the assertion matches the current schema column
count.
- Around line 391-396: The unit test records_to_batch_empty in
src/storage/schema.rs asserts that batch.num_columns() == 20 but the schema was
changed to 21 columns; update the expected column count to 21 (and ensure the
expected schema from result_schema() still matches) by changing the assertion in
records_to_batch_empty to assert_eq!(batch.num_columns(), 21) and run the tests
to confirm consistency with records_to_batch and result_schema().
- Around line 518-530: The test uses hard-coded column indices that shifted
after inserting a new field; update the indices so addr_p2wpkh_col reads from
column 17 (not 16) and wif_compressed_col reads from column 19 (not 18). Locate
the downcast/ref calls for addr_p2wpkh_col and wif_compressed_col in
src/storage/schema.rs and change their .column(...) index values accordingly,
keeping the same downcast to StringArray and the existing assertions.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f52c1550-35e2-4e74-869b-207bddad25ef

📥 Commits

Reviewing files that changed from the base of the PR and between 39a3fe6 and b7eb5d5.

📒 Files selected for processing (9)
  • src/derive.rs
  • src/main.rs
  • src/matcher.rs
  • src/output/console.rs
  • src/output/multi.rs
  • src/output/storage.rs
  • src/provider.rs
  • src/storage/iceberg/schema.rs
  • src/storage/schema.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (9)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Order imports as: external crates → std → blank line → super:: → blank line → crate::
Prefer ? operator over .unwrap() in new code
Use PascalCase for types and structs
Use snake_case for function and method names
Use SCREAMING_SNAKE_CASE for constants
Use snake_case for file and module names

Files:

  • src/output/multi.rs
  • src/main.rs
  • src/output/console.rs
  • src/provider.rs
  • src/storage/iceberg/schema.rs
  • src/matcher.rs
  • src/derive.rs
  • src/storage/schema.rs
  • src/output/storage.rs
src/{derive,matcher,network,benchmark,provider,transform,analyze,source,output,gpu,storage}/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Implement custom error enums with Display and Error trait implementations for domain modules

Files:

  • src/output/multi.rs
  • src/output/console.rs
  • src/storage/iceberg/schema.rs
  • src/storage/schema.rs
  • src/output/storage.rs
src/{transform,output,storage}/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Use builder pattern for configuration (e.g., ElectrumTransform::new().with_change(), ParquetBackend::new().with_compression())

Files:

  • src/output/multi.rs
  • src/output/console.rs
  • src/storage/iceberg/schema.rs
  • src/storage/schema.rs
  • src/output/storage.rs
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Use indicatif::ProgressBar for long-running operations
Place inline tests in #[cfg(test)] mod tests at the end of each file, using standard assert! and assert_eq! macros
Use tempfile crate for file I/O tests

Files:

  • src/output/multi.rs
  • src/main.rs
  • src/output/console.rs
  • src/provider.rs
  • src/storage/iceberg/schema.rs
  • src/matcher.rs
  • src/derive.rs
  • src/storage/schema.rs
  • src/output/storage.rs
src/output/**/*.rs

📄 CodeRabbit inference engine (src/output/AGENTS.md)

src/output/**/*.rs: Use escape_csv_field() from mod.rs for any field written to CSV in the output module
All outputs must be Send + Sync (use interior mutability where needed) for thread safety
Output trait must implement three methods: key(), hit(), and flush() with specified signatures

Files:

  • src/output/multi.rs
  • src/output/console.rs
  • src/output/storage.rs
src/output/*.rs

📄 CodeRabbit inference engine (src/output/AGENTS.md)

Create new output formats by implementing the Output trait in src/output/{name}.rs and registering with mod {name}; and pub use in mod.rs

Files:

  • src/output/multi.rs
  • src/output/console.rs
  • src/output/storage.rs
src/output/multi.rs

📄 CodeRabbit inference engine (src/output/AGENTS.md)

Multi-output behavior is implemented by dispatching to Vec<Box<dyn Output>> in the multi-output dispatcher

Files:

  • src/output/multi.rs
src/main.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/main.rs: Use anyhow::Result<T> and anyhow::bail!() for CLI and top-level error handling
CLI command and subcommand changes go in src/main.rs using clap derive macros

Files:

  • src/main.rs
src/output/console.rs

📄 CodeRabbit inference engine (src/output/AGENTS.md)

src/output/console.rs: Compact CSV format for file output should be: source,transform,privkey_hex,address
Use verbose YAML-like multi-line format for console stdout output

Files:

  • src/output/console.rs
🧠 Learnings (11)
📓 Common learnings
Learnt from: oritwoen
Repo: oritwoen/vuke PR: 86
File: src/provider.rs:442-443
Timestamp: 2026-03-22T18:38:26.890Z
Learning: In oritwoen/vuke src/provider.rs, the `derived.p2tr == addr` branch in `verify_key_boha` has no test coverage intentionally — no boha puzzles currently have P2TR/Taproot addresses. The branch is forward-compatible for when taproot puzzles are added to boha. Do not flag missing P2TR test coverage in this function.
📚 Learning: 2026-03-22T18:38:26.890Z
Learnt from: oritwoen
Repo: oritwoen/vuke PR: 86
File: src/provider.rs:442-443
Timestamp: 2026-03-22T18:38:26.890Z
Learning: In oritwoen/vuke src/provider.rs, the `derived.p2tr == addr` branch in `verify_key_boha` has no test coverage intentionally — no boha puzzles currently have P2TR/Taproot addresses. The branch is forward-compatible for when taproot puzzles are added to boha. Do not flag missing P2TR test coverage in this function.

Applied to files:

  • src/output/multi.rs
  • src/main.rs
  • src/output/console.rs
  • src/provider.rs
  • src/matcher.rs
  • src/derive.rs
  • src/output/storage.rs
📚 Learning: 2026-03-13T13:20:58.369Z
Learnt from: oritwoen
Repo: oritwoen/vuke PR: 76
File: src/source/wordlist.rs:134-143
Timestamp: 2026-03-13T13:20:58.369Z
Learning: Do not flag .ok() calls on output.hit() / output.key() inside Rayon par_chunks().for_each() closures in Rust files (e.g., src/source/wordlist.rs) as bugs. Rayon for_each requires FnMut() -> (), which cannot propagate Result with ?. This is a known limitation discussed in issue `#77`. Treat these .ok() usages in this pattern as intentional and not regressions; focus review on actual logic or error handling outside this specific closure pattern.

Applied to files:

  • src/output/multi.rs
  • src/main.rs
  • src/output/console.rs
  • src/provider.rs
  • src/storage/iceberg/schema.rs
  • src/matcher.rs
  • src/derive.rs
  • src/storage/schema.rs
  • src/output/storage.rs
📚 Learning: 2026-03-12T18:25:01.029Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: src/output/AGENTS.md:0-0
Timestamp: 2026-03-12T18:25:01.029Z
Learning: Applies to src/output/console.rs : Compact CSV format for file output should be: `source,transform,privkey_hex,address`

Applied to files:

  • src/main.rs
  • src/output/console.rs
📚 Learning: 2026-03-12T18:24:52.371Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T18:24:52.371Z
Learning: Applies to src/main.rs : CLI command and subcommand changes go in `src/main.rs` using clap derive macros

Applied to files:

  • src/main.rs
📚 Learning: 2026-03-12T18:25:01.029Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: src/output/AGENTS.md:0-0
Timestamp: 2026-03-12T18:25:01.029Z
Learning: Applies to src/output/console.rs : Use verbose YAML-like multi-line format for console stdout output

Applied to files:

  • src/output/console.rs
📚 Learning: 2026-03-02T14:18:34.668Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: src/storage/AGENTS.md:0-0
Timestamp: 2026-03-02T14:18:34.668Z
Learning: Applies to src/storage/storage/schema.rs : Update Arrow schema definition in `schema.rs`, including `result_schema()` function and field constants

Applied to files:

  • src/storage/iceberg/schema.rs
  • src/storage/schema.rs
  • src/output/storage.rs
📚 Learning: 2026-03-02T14:18:34.668Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: src/storage/AGENTS.md:0-0
Timestamp: 2026-03-02T14:18:34.668Z
Learning: Applies to src/storage/storage/schema.rs : Map variable-length fields to fixed columns in schema for SQL-friendly querying

Applied to files:

  • src/storage/iceberg/schema.rs
  • src/storage/schema.rs
📚 Learning: 2026-03-02T14:18:34.668Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: src/storage/AGENTS.md:0-0
Timestamp: 2026-03-02T14:18:34.668Z
Learning: Applies to src/storage/storage/schema.rs : Update `records_to_batch()` conversion when adding new columns to the schema

Applied to files:

  • src/storage/iceberg/schema.rs
  • src/storage/schema.rs
  • src/output/storage.rs
📚 Learning: 2026-03-02T14:18:34.668Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: src/storage/AGENTS.md:0-0
Timestamp: 2026-03-02T14:18:34.668Z
Learning: Applies to src/storage/storage/mod.rs : Define record structs in `mod.rs` when modifying the record structure

Applied to files:

  • src/storage/schema.rs
  • src/output/storage.rs
📚 Learning: 2026-03-02T14:18:34.668Z
Learnt from: CR
Repo: oritwoen/vuke PR: 0
File: src/storage/AGENTS.md:0-0
Timestamp: 2026-03-02T14:18:34.668Z
Learning: Applies to src/storage/storage/mod.rs : Use `&'a str` and `&'a [T]` references in record structs for zero-copy efficiency

Applied to files:

  • src/output/storage.rs
🔇 Additional comments (11)
src/main.rs (1)

769-769: single now exposes wrapped-segwit address output.

Line 769 correctly prints derived.p2sh_p2wpkh, so single stays aligned with the expanded derivation set.

src/output/console.rs (1)

74-75: Wrapped-segwit field is consistently printed in both verbose paths.

Lines 74 and 119 wire p2sh_p2wpkh into key() and hit(), so operators see the same address set in both flows.

As per coding guidelines, src/output/console.rs should keep verbose YAML-like multi-line output, and this change follows that format.

Also applies to: 119-120

src/output/multi.rs (1)

94-94: Test fixture update is correct for the expanded DerivedKey.

Line 94 keeps make_test_derived() structurally in sync with the new p2sh_p2wpkh field.

src/provider.rs (1)

440-441: Provider verification now covers wrapped-segwit matches.

Lines 440-441 add the missing derived.p2sh_p2wpkh == addr path and report the correct address type string.

src/output/storage.rs (1)

184-187: Parquet record mapping includes the new address type end-to-end.

Lines 184-187 persist p2sh_p2wpkh into addresses, and Line 286 keeps test data aligned with that schema expansion.

Also applies to: 286-286

src/matcher.rs (1)

25-25: Matcher support for wrapped-segwit is complete and covered.

Variant, as_str() mapping, runtime check, and test were all added consistently, so matching/reporting for p2sh_p2wpkh is now end-to-end in this module.

Also applies to: 35-35, 93-99, 176-190

src/derive.rs (1)

39-40: Derivation and public address list are correctly extended to P2SH-P2WPKH.

The new field is generated from the compressed pubkey, exposed in DerivedKey::addresses(), and validated by tests, so this module’s address set expansion is coherent.

Also applies to: 49-54, 121-125, 174-174, 212-213, 223-228

src/storage/schema.rs (4)

42-44: LGTM!

Constant follows naming convention and is placed in correct position between the address fields.


110-114: LGTM!

Field insertion position is correct — between ADDRESS_P2PKH_UNCOMPRESSED and ADDRESS_P2WPKH. Doc table matches code.


235-240: LGTM!

Array construction and vec ordering matches schema. The "p2sh_p2wpkh" lookup key needs to match whatever AddressRecord.address_type is set to in the derive module — worth a quick sanity check that they align.

Also applies to: 287-288


304-306: LGTM!

Test assertions properly updated for the new column count and field positions.

Also applies to: 333-333, 362-362, 380-380

Comment thread src/output/console.rs
Comment thread src/storage/iceberg/schema.rs
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Requires human review: This PR modifies storage schemas (Arrow and Iceberg) and the core address derivation logic, which is high-impact and similar to a database migration.

@oritwoen oritwoen merged commit e467e13 into main Mar 22, 2026
2 checks passed
@oritwoen oritwoen deleted the feat/derive-p2sh-p2wpkh branch March 22, 2026 19:21
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.

Derive and match P2SH-P2WPKH addresses

1 participant