Skip to content

Generate report IDs #701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: 04-04-add_payer_report_protos
Choose a base branch
from

Conversation

neekolas
Copy link
Contributor

@neekolas neekolas commented Apr 4, 2025

TL;DR

Added functionality to generate unique IDs for payer reports based on their contents.

What changed?

  • Added a new ID() method to the PayerReport struct that generates a unique identifier by hashing the report's fields
  • Created a new payerreport/id.go file with ABI definitions for hashing payer report data
  • Added NodesMerkleRoot and NodesLeafCount fields to the PayerReport struct
  • Implemented a ToProto() method to convert a PayerReport to its protobuf representation
  • Added utility functions for hashing payer report data and converting byte slices to fixed-size arrays
  • Added tests to verify the payer report ID generation

How to test?

Run the new test in pkg/payerreport/report_test.go to verify that the ID generation works correctly:

go test -v ./pkg/payerreport -run TestPayerReportID

Why make this change?

This change enables unique identification of payer reports based on their content, which is necessary for tracking, referencing, and validating reports throughout the system. The ID generation follows Ethereum's ABI encoding standards to ensure compatibility with smart contract verification.

Summary by CodeRabbit

  • New Features

    • Enhanced payer reports by including node merkle root and leaf count details.
    • Added conversion of report data into a protocol-friendly format.
    • Improved unique report ID generation using secure hashing methods.
    • Introduced utilities for byte slice conversion and hashing.
  • Tests

    • Added tests to validate the correctness of report ID generation.

Copy link
Contributor

coderabbitai bot commented Apr 4, 2025

Walkthrough

This update introduces new functionality in the payerreport package to support Ethereum smart contract interactions for payer reports. A new file defines argument structures for encoding report data. The PayerReport struct is enhanced with additional fields for node data and two methods: one to convert the struct to its protobuf representation and another to compute a unique identifier using field packing and a Keccak256 hash. Corresponding tests verify the ID generation. Additionally, utility functions for hashing and for converting a byte slice to a fixed 32-byte array have been added.

Changes

Files Change Summary
pkg/payerreport/id.go, pkg/payerreport/interface.go, pkg/payerreport/report_test.go - Added payerReportMessageHash in id.go defining Ethereum ABI argument mappings.
- Extended PayerReport struct in interface.go with new fields (NodesMerkleRoot, NodesLeafCount) and methods (ToProto(), ID()).
- Introduced test TestPayerReportID in report_test.go for the new ID method.
pkg/utils/hash.go, pkg/utils/slice.go - Added HashPayerReportInput in hash.go to compute the Keccak256 hash of input bytes.
- Added SliceToArray32 in slice.go to convert a byte slice into a fixed 32-byte array.

Sequence Diagram(s)

sequenceDiagram
    participant T as Test
    participant PR as PayerReport
    participant H as HashPayerReportInput (utils)

    T->>PR: Call ID()
    PR->>PR: Pack report fields (including node data)
    PR->>H: Compute Keccak256 hash on packed bytes
    H-->>PR: Return 32-byte hash
    PR-->>T: Return unique identifier (ID)
Loading

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (1.64.8)

Error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2
Failed executing command with error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.

📜 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 1599a16 and b5f8759.

📒 Files selected for processing (5)
  • pkg/payerreport/id.go (1 hunks)
  • pkg/payerreport/interface.go (2 hunks)
  • pkg/payerreport/report_test.go (1 hunks)
  • pkg/utils/hash.go (1 hunks)
  • pkg/utils/slice.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/utils/hash.go
  • pkg/utils/slice.go
  • pkg/payerreport/report_test.go
  • pkg/payerreport/id.go
  • pkg/payerreport/interface.go
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Test (Node)
  • GitHub Check: Push Docker Images to GitHub Packages (xmtpd-cli)
  • GitHub Check: Upgrade Tests
  • GitHub Check: Test (Node)
  • GitHub Check: Upgrade Tests
  • GitHub Check: Push Docker Images to GitHub Packages (xmtpd-cli)
  • GitHub Check: Push Docker Images to GitHub Packages (xmtpd)
  • GitHub Check: Push Docker Images to GitHub Packages (xmtpd)

🪧 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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

Copy link
Contributor Author

neekolas commented Apr 4, 2025

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

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 (11)
pkg/utils/hash.go (1)

35-37: Consider adding a domain separation label for consistency

The new HashPayerReportInput function takes a different approach compared to other hash functions in this file - it doesn't use any domain separation label. While this may be intentional based on your requirements, adding a domain separation label would be consistent with the pattern established by other hash functions (HashPayerSignatureInput, HashJWTSignatureInput, etc.).

func HashPayerReportInput(packedBytes []byte) []byte {
-	return ethcrypto.Keccak256(packedBytes)
+	return ethcrypto.Keccak256(
+		[]byte(constants.PAYER_REPORT_DOMAIN_SEPARATION_LABEL),
+		packedBytes,
+	)
}

This would require defining a new constant in the constants package.

pkg/utils/slice.go (1)

3-7: Add validation for input slice length

The current implementation silently truncates input slices longer than 32 bytes, which could lead to unexpected behavior. Consider adding validation or documentation to make this behavior explicit.

func SliceToArray32(slice []byte) [32]byte {
	var array [32]byte
	copy(array[:], slice)
+	// Note: If slice is longer than 32 bytes, only the first 32 bytes are used
	return array
}

Alternatively, if truncation is undesirable:

func SliceToArray32(slice []byte) ([32]byte, error) {
	var array [32]byte
+	if len(slice) > 32 {
+		return array, fmt.Errorf("input slice length %d exceeds maximum of 32 bytes", len(slice))
+	}
	copy(array[:], slice)
	return array, nil
}
pkg/payerreport/report_test.go (2)

9-24: Enhance test coverage with multiple test cases

The current test validates basic functionality but only tests one set of input values. Consider adding multiple test cases with different inputs to ensure robustness, including edge cases.

func TestPayerReportID(t *testing.T) {
-	report := PayerReport{
-		OriginatorNodeID: 1,
-		StartSequenceID:  1,
-		EndSequenceID:    10,
-		PayersMerkleRoot: []byte{1, 2, 3},
-		PayersLeafCount:  1,
-		NodesMerkleRoot:  []byte{4, 5, 6},
-		NodesLeafCount:   1,
-	}
-
-	id, err := report.ID()
-	require.NoError(t, err)
-	require.NotNil(t, id)
-	require.Len(t, id, 32)
+	testCases := []struct {
+		name     string
+		report   PayerReport
+		wantErr  bool
+	}{
+		{
+			name: "valid report",
+			report: PayerReport{
+				OriginatorNodeID: 1,
+				StartSequenceID:  1,
+				EndSequenceID:    10,
+				PayersMerkleRoot: []byte{1, 2, 3},
+				PayersLeafCount:  1,
+				NodesMerkleRoot:  []byte{4, 5, 6},
+				NodesLeafCount:   1,
+			},
+			wantErr: false,
+		},
+		{
+			name: "zeros",
+			report: PayerReport{
+				OriginatorNodeID: 0,
+				StartSequenceID:  0,
+				EndSequenceID:    0,
+				PayersMerkleRoot: make([]byte, 0),
+				PayersLeafCount:  0,
+				NodesMerkleRoot:  make([]byte, 0),
+				NodesLeafCount:   0,
+			},
+			wantErr: false,
+		},
+		// Add more test cases as needed
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			id, err := tc.report.ID()
+			if tc.wantErr {
+				require.Error(t, err)
+				return
+			}
+			require.NoError(t, err)
+			require.NotNil(t, id)
+			require.Len(t, id, 32)
+		})
+	}
}

14-17: Consider validating the actual ID value

The test only checks that the ID is not nil and has the expected length, but it doesn't validate that the actual ID value is correct. Consider adding an expected value check to ensure the ID generation logic is working as intended.

func TestPayerReportID(t *testing.T) {
	report := PayerReport{
		OriginatorNodeID: 1,
		StartSequenceID:  1,
		EndSequenceID:    10,
		PayersMerkleRoot: []byte{1, 2, 3},
		PayersLeafCount:  1,
		NodesMerkleRoot:  []byte{4, 5, 6},
		NodesLeafCount:   1,
	}

	id, err := report.ID()
	require.NoError(t, err)
	require.NotNil(t, id)
	require.Len(t, id, 32)
+	// Expected ID value (precomputed)
+	expectedID := [32]byte{
+		// Fill in the expected bytes here
+	}
+	require.Equal(t, expectedID, id, "ID does not match expected value")
}
pkg/payerreport/id.go (2)

5-34: Missing documentation for the payerReportMessageHash variable

Consider adding documentation to explain the purpose of this variable and how it's used in the context of the application. This would help other developers understand its role in the report ID generation process.

+// payerReportMessageHash defines the Ethereum ABI encoding structure used for packing
+// PayerReport data before hashing for ID generation or smart contract interactions.
+// Each field maps to a corresponding field in the PayerReport struct with appropriate
+// types for Ethereum compatibility.
var payerReportMessageHash = abi.Arguments{
	{
		Name: "originatorNodeID",
		Type: abi.Type{T: abi.UintTy, Size: 32},
	},
	// ... rest of the definition
}

5-34: Consider making variable unexported if it's not used outside the package

If payerReportMessageHash is only used internally within the payerreport package, consider making it unexported (lowercase first letter) to follow Go's visibility conventions.

-var payerReportMessageHash = abi.Arguments{
+var payerReportMessageHash = abi.Arguments{

If it's intended to be used by other packages, then it should have proper documentation as per the previous comment.

pkg/payerreport/interface.go (5)

31-41: Consider adding function documentation.

The ToProto() method correctly maps the struct fields to their protobuf representation, but lacks documentation explaining its purpose and return value.

Add a comment before the function:

+// ToProto converts a PayerReport to its Protocol Buffers representation
 func (p *PayerReport) ToProto() *proto.PayerReport {

43-58: Consider adding nil checks for merkle root fields.

The method doesn't verify if p.PayersMerkleRoot or p.NodesMerkleRoot are nil before passing them to utils.SliceToArray32. If these could be nil, consider adding validation.

 func (p *PayerReport) ID() ([]byte, error) {
+	if p.PayersMerkleRoot == nil || p.NodesMerkleRoot == nil {
+		return nil, fmt.Errorf("merkle root fields cannot be nil")
+	}
 	packedBytes, err := payerReportMessageHash.Pack(

43-58: Add documentation for the ID method.

The ID() method should include documentation explaining its purpose, return value, and possible errors.

+// ID computes a unique identifier for the PayerReport by packing its fields and returning the keccak256 hash
+// Returns the hash as a byte slice and any error that occurred during packing
 func (p *PayerReport) ID() ([]byte, error) {

44-52: The payerReportMessageHash variable is not defined in this file.

The code refers to payerReportMessageHash which appears to be defined elsewhere. Consider adding a comment referencing where this variable is defined to improve code readability.

 func (p *PayerReport) ID() ([]byte, error) {
+	// payerReportMessageHash is defined in pkg/payerreport/id.go
 	packedBytes, err := payerReportMessageHash.Pack(

53-55: Consider adding more context to the error.

When returning an error from Pack(), add more context to help with debugging.

 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("failed to pack payer report: %w", err)
 	}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0dd174f and 1599a16.

📒 Files selected for processing (5)
  • pkg/payerreport/id.go (1 hunks)
  • pkg/payerreport/interface.go (2 hunks)
  • pkg/payerreport/report_test.go (1 hunks)
  • pkg/utils/hash.go (1 hunks)
  • pkg/utils/slice.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
pkg/payerreport/report_test.go (1)
pkg/payerreport/interface.go (1)
  • PayerReport (12-29)
pkg/payerreport/interface.go (2)
pkg/utils/slice.go (1)
  • SliceToArray32 (3-7)
pkg/utils/hash.go (1)
  • HashPayerReportInput (35-37)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Upgrade Tests
🔇 Additional comments (4)
pkg/payerreport/id.go (1)

5-34:

❓ Verification inconclusive

Verify sizes match Solidity contract expectations

Ensure that the sizes defined in these ABI arguments match the expectations of the Solidity smart contract that will be interacting with this data. For example, the definition uses UintTy, Size: 32 for the originatorNodeID, which would be a uint256 in Solidity.

Please verify that these type definitions match the corresponding smart contract structure:

  • originatorNodeID: uint256 (UintTy, Size: 32)
  • startSequenceID: uint64 (UintTy, Size: 64)
  • endSequenceID: uint64 (UintTy, Size: 64)
  • payersMerkleRoot: bytes32 (FixedBytesTy, Size: 32)
  • payersLeafCount: uint32 (UintTy, Size: 32)
  • nodesMerkleRoot: bytes32 (FixedBytesTy, Size: 32)
  • nodesLeafCount: uint32 (UintTy, Size: 32)

🏁 Script executed:

#!/bin/bash
# Find smart contract files and check for PayerReport structure definitions
rg -t solidity -A 10 "struct PayerReport" || rg -t solidity -A 10 "function.*PayerReport"

Length of output: 156


Verify ABI Type Mappings Against Solidity Contract

The ABI definitions in pkg/payerreport/id.go appear to follow the intended Solidity types:

  • originatorNodeID: UintTy, Size: 32 should correspond to Solidity’s uint256
  • startSequenceID & endSequenceID: UintTy, Size: 64 should map to Solidity’s uint64
  • payersMerkleRoot & nodesMerkleRoot: FixedBytesTy, Size: 32 match Solidity’s bytes32
  • payersLeafCount & nodesLeafCount: UintTy, Size: 32 should correspond to Solidity’s uint32

I attempted to locate the Solidity definitions (e.g. a struct PayerReport) using a file-type filter, but the query using -t solidity failed due to an unsupported file type. To ensure confidence, please verify manually (or update the search criteria) that the Solidity smart contract indeed declares the expected types for the PayerReport structure.

pkg/payerreport/interface.go (3)

8-9: Good additions to the imports.

The added imports support the new functionality - proto package for the protobuf conversion and utils package for hashing and byte array operations.


25-28: Good addition of node-related fields with clear documentation.

The new fields NodesMerkleRoot and NodesLeafCount are well-documented and follow the same pattern as the existing payer-related fields.


31-41:

❓ Verification inconclusive

Verify all necessary fields are mapped in the protobuf conversion.

The Payers field from the struct is not included in the protobuf mapping. Is this intentional or an oversight?


🏁 Script executed:

#!/bin/bash
# Check the proto definition to see if it has a field for Payers
grep -rn "message PayerReport" --include="*.proto" .

Length of output: 54


Action Required: Verify Proto Field Mapping for PayerReport

Our initial automated search for a proto definition (using the pattern "message PayerReport") did not yield any output, so we couldn’t confirm whether the corresponding protobuf message is defined with a Payers field. As a result, it's unclear if the omission of the Payers field in the ToProto conversion is intentional.

  • Verify the Proto Definition Manually: Please check the repository’s proto files to determine if the PayerReport message includes a field for Payers.
  • Mapping Confirmation: If the proto message is meant to include the Payers field, update the ToProto function in pkg/payerreport/interface.go (lines 31-41) to include it. Otherwise, add a clarifying comment indicating that the omission is intentional.

@neekolas neekolas force-pushed the 04-04-generate_report_ids branch from 1599a16 to b5f8759 Compare April 18, 2025 21:47
@neekolas neekolas force-pushed the 04-04-add_payer_report_protos branch from 0dd174f to 139fa98 Compare April 18, 2025 21:47
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.

1 participant