Skip to content

Add support for bitcoin invariants 2#51

Draft
neavra wants to merge 7 commits intomainfrom
add-support-for-bitcoin-invariants-2
Draft

Add support for bitcoin invariants 2#51
neavra wants to merge 7 commits intomainfrom
add-support-for-bitcoin-invariants-2

Conversation

@neavra
Copy link
Contributor

@neavra neavra commented Jul 27, 2025

This PR implements Bitcoin transaction invariant validation to ensure Bitcoin transfers always follow security requirements.

Adds transaction invariant validation for Bitcoin to prevent malicious transactions by enforcing that:

  1. Bitcoin transactions follow expected structure patterns (e.g. 1 input, 2 output for transfers, can be expanded)
  2. Change outputs always go back to sender (derived from vault)

Core Implementation

  • Add ValidateInvariants method to Chain interface
  • Implement Bitcoin validation logic with two checks:
    • checkTransactionStructure: Validates transaction has 1-2 outputs
    • validateChangeOutput: Ensures change goes to vault-derived address
  • Add Ethereum stub no implementation yet

Engine Integration

  • Call ValidateInvariants in Engine.Evaluate after policy rules match
  • Add context parameter to Evaluate method signature
  • Fail transaction if invariants don't pass, even if policy allows it

Test: Create a my-test-vault.vult with testing btc address bc1qxpeg8k8xrygj9ae8q6pkzj29sf7w8e7krm4v5f (refer to engine_test)

# 1 input 1 output, full utxo transfer, should pass
go run cmd/vulticheck/main.go \
  -policy testdata/payroll.json \
  -chain bitcoin \
  -tx 010000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff01404b4c00000000001976a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac00000000 \
  -vault testdata/my-test-vault.vult \
  -password password

# 1 input 2 output, wrong recipient, should fail
go run cmd/vulticheck/main.go \
  -policy testdata/payroll.json \
  -chain bitcoin \
  -tx 010000000100000000000000000000000000000000000000000000000000000000000000000000000000fdffffff0280969800000000001976a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac809698000000000016001431534c2f291e04ad8404f653f35a0bee167a504d00000000 \
  -vault testdata/my-test-vault.vult \
  -password password  
  
# 1 input 2 output, correct recipient, should pass
go run cmd/vulticheck/main.go \
  -policy testdata/payroll.json \
  -chain bitcoin \
  -tx 010000000100000000000000000000000000000000000000000000000000000000000000000000000000fdffffff02404b4c00000000001976a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac404b4c0000000000160014307283d8e6191122f7270683614945827ce3e7d600000000 \
  -vault testdata/my-test-vault.vult \
  -password password  

Summary by CodeRabbit

  • New Features

    • Introduced enhanced transaction validation for Bitcoin, enforcing stricter checks on transaction structure and change output addresses.
    • Added support for loading and decrypting vault files via new command-line options in the transaction evaluation tool.
    • Extended policy evaluation to support context-aware invariant validation for different blockchain types.
  • Bug Fixes

    • Improved test coverage for vault-dependent policy evaluations.
  • Chores

    • Updated interface definitions and method signatures to support invariant validation across blockchain implementations.

@neavra neavra linked an issue Jul 27, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 27, 2025

Walkthrough

Invariant validation logic was introduced for Bitcoin transactions, requiring a vault context and enforcing transaction structure and change output rules. The Chain interface was extended with invariant-related methods, implemented for both Bitcoin and Ethereum (Ethereum as a stub). The engine and its tests were updated to support optional context injection for invariant checks. Command-line tooling was updated to handle vault decryption and context passing.

Changes

Cohort / File(s) Change Summary
Bitcoin Chain Invariant Validation
bitcoin/chain.go
Added ValidateInvariants and RequiresInvariants methods; implemented transaction structure and change output validation using vault context.
Ethereum Chain Stub Methods
ethereum/ethereum.go
Added stub implementations of ValidateInvariants (returns nil) and RequiresInvariants (returns false).
Chain Interface Update
types/chain.go
Extended Chain interface with ValidateInvariants and RequiresInvariants methods.
Engine Evaluation Logic
engine/engine.go
Updated Engine.Evaluate to accept optional context; performs invariant validation if required by the chain.
Engine Tests and Vault Setup
engine/engine_test.go
Added requiresVault field to test vectors; injects vault context for relevant tests; added createTestVault.
Command-line Vault Integration
cmd/vulticheck/main.go
Added vault file/password flags; loads and decrypts vault; passes context to engine.Evaluate.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant VaultUtils
    participant Engine
    participant Chain

    User->>CLI: Provide transaction, vault file, and password
    CLI->>VaultUtils: DecryptVaultFromBackup(vault file, password)
    VaultUtils-->>CLI: Decrypted vault object
    CLI->>Engine: Evaluate(policy, chain, tx, context{vault})
    Engine->>Chain: RequiresInvariants()
    alt Invariants required
        Engine->>Chain: ValidateInvariants(context, tx)
        Chain-->>Engine: Validation result
    end
    Engine-->>CLI: Evaluation result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~18 minutes

Poem

In the warren of code, a new rule appears,
Bitcoin’s transactions must face their fears.
With vaults unlocked and change outputs checked,
Invariants guard what bunnies protect.
Ethereum hops by, with nothing to prove—
While engine and tests in harmony move.
🐇🔒✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a1853dc and a33244c.

📒 Files selected for processing (1)
  • engine/engine.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • engine/engine.go
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-support-for-bitcoin-invariants-2

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@neavra neavra marked this pull request as ready for review July 27, 2025 03:36
Copy link
Contributor

@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

🧹 Nitpick comments (1)
engine/engine_test.go (1)

202-214: Consider simplifying the Engine.Evaluate call pattern.

The conditional calling of engine.Evaluate with different signatures works but could be simplified. Consider always passing the context map (even if empty) to avoid branching logic.

-			var transactionAllowedByPolicy bool
-			var matchingRule *types.Rule
-			if len(context) > 0 {
-				transactionAllowedByPolicy, matchingRule, err = engine.Evaluate(&policy, c, tx, context)
-			} else {
-				transactionAllowedByPolicy, matchingRule, err = engine.Evaluate(&policy, c, tx)
-			}
+			transactionAllowedByPolicy, matchingRule, err := engine.Evaluate(&policy, c, tx, context)

This assumes engine.Evaluate can handle empty context maps gracefully.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f9e11a4 and a712a75.

📒 Files selected for processing (6)
  • bitcoin/chain.go (2 hunks)
  • cmd/vulticheck/main.go (4 hunks)
  • engine/engine.go (2 hunks)
  • engine/engine_test.go (5 hunks)
  • ethereum/ethereum.go (1 hunks)
  • types/chain.go (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
cmd/vulticheck/main.go (1)

Learnt from: webpiratt
PR: #29
File: cmd/gen_abi_bind/main.go:49-58
Timestamp: 2025-06-26T23:30:39.905Z
Learning: In the vultisig/recipes repository, tool dependencies like abigen are documented in the README rather than validated at runtime in the code generation tool.

🧬 Code Graph Analysis (5)
ethereum/ethereum.go (1)
types/transaction.go (1)
  • DecodedTransaction (7-26)
cmd/vulticheck/main.go (1)
common/utils.go (1)
  • DecryptVaultFromBackup (133-162)
types/chain.go (1)
types/transaction.go (1)
  • DecodedTransaction (7-26)
engine/engine.go (1)
types/transaction.go (1)
  • DecodedTransaction (7-26)
bitcoin/chain.go (3)
types/transaction.go (1)
  • DecodedTransaction (7-26)
bitcoin/decode.go (1)
  • ParsedBitcoinTransaction (17-22)
address/address.go (1)
  • GetAddress (12-72)
🔇 Additional comments (22)
types/chain.go (2)

3-5: Import formatting follows Go conventions.

The multi-line import format is consistent with Go style guidelines.


34-36: Well-designed interface extension for invariant validation.

The new methods provide a clean, extensible approach for optional chain-specific invariant validation:

  • ValidateInvariants uses a flexible context map for chain-specific data
  • RequiresInvariants allows chains to opt-in/opt-out of validation
  • Design follows Go patterns and enables future extensibility
ethereum/ethereum.go (1)

237-244: Appropriate stub implementation for Ethereum.

The implementation correctly opts out of invariant validation:

  • ValidateInvariants returns nil (no validation errors)
  • RequiresInvariants returns false (validation not required)
  • Satisfies the extended Chain interface requirements

This is consistent with the design where Bitcoin requires vault-based validation but Ethereum does not.

engine/engine_test.go (4)

12-13: Necessary imports for vault test functionality.

The added imports support the new test vault creation functionality.


26-26: Clean extension to support vault-dependent tests.

The requiresVault field allows test vectors to specify when vault context is needed for invariant validation.


36-48: Bitcoin test vectors correctly marked for vault dependency.

The Bitcoin test vectors appropriately set requiresVault: true to enable invariant validation testing.


230-241: Well-implemented test vault helper.

The createTestVault function provides consistent test data with:

  • Proper vault structure and field values
  • Clear documentation of expected Bitcoin address
  • Centralized test data creation following good test practices
cmd/vulticheck/main.go (4)

11-11: Necessary import for vault decryption functionality.

The utils import provides access to DecryptVaultFromBackup function needed for vault processing.


21-22: Well-designed vault-related command-line flags.

The flags provide necessary vault functionality:

  • vault flag with reasonable default test location
  • password flag for encrypted vault support
  • Clear naming and appropriate defaults

42-55: Robust vault loading implementation.

The vault loading logic includes:

  • Proper error handling for file operations and decryption
  • Conditional loading based on vault path availability
  • Consistent context key usage ("vault")
  • Helpful logging and error messages

100-100: Correct usage of updated Engine.Evaluate signature.

The call properly passes the context parameter to the updated engine.Evaluate method.

bitcoin/chain.go (8)

7-7: Necessary import for logging functionality.

The log import supports logging in the validation methods.


11-11: Required import for vault type definitions.

The v1 vault import provides the vault type used in invariant validation.


14-15: Essential imports for address derivation.

The address and common imports provide utilities for deriving Bitcoin addresses from vault data.


101-128: Well-structured invariant validation implementation.

The method provides comprehensive Bitcoin transaction validation:

  • Proper type assertions for transaction and vault
  • Required context validation with clear error messages
  • Modular approach with separate validation concerns
  • Good error handling and descriptive failure messages

130-143: Appropriate transaction structure validation.

The validation correctly limits Bitcoin transactions to standard patterns:

  • 1 output: entire UTXO spent (no change)
  • 2 outputs: recipient + change back to sender
  • Extensible design for future transaction types

The current limit of 2 outputs aligns with the security requirements for standard transfers.


145-173: Robust change output validation logic.

The validation ensures change outputs return to the vault:

  • Handles single output case (entire UTXO spent)
  • Validates last output address matches vault-derived address
  • Uses proper address derivation utilities
  • Provides clear error messages for debugging

This prevents malicious redirection of change outputs.


175-188: Correct implementation of vault address derivation.

The method properly derives Bitcoin addresses from vault data:

  • Uses established address.GetAddress utility for consistency
  • Passes appropriate parameters for Bitcoin chain
  • Good error handling with contextual error messages

190-192: Correct implementation indicating Bitcoin requires invariants.

The method appropriately returns true, enabling vault-based invariant validation for Bitcoin transactions.

engine/engine.go (3)

5-5: LGTM: Import reordering improves code organization.

The import statements have been reordered, likely by automated formatting tools. This improves code organization without affecting functionality.

Also applies to: 10-10


30-30: Good approach using variadic parameter for backward compatibility.

The method signature change adds context support while maintaining backward compatibility through the variadic parameter. This enables invariant validation without breaking existing callers.


31-43: Excellent implementation of invariant validation logic.

The invariant validation is well-structured with:

  • Proper conditional execution based on chain requirements
  • Comprehensive context validation covering all edge cases
  • Appropriate error handling with logging and error wrapping
  • Clean integration with existing evaluation flow

The placement before policy rule evaluation is correct, as invariants represent fundamental constraints that must pass first.

@RaghavSood RaghavSood marked this pull request as draft September 16, 2025 04:28
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.

Add support for Bitcoin change output invariants

1 participant