-
Notifications
You must be signed in to change notification settings - Fork 0
feat: improve txmetadata features #22
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
base: main
Are you sure you want to change the base?
Conversation
b31d98e
to
29d7618
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good
merge with |
…form into feat/improve-txmetadata
…form into feat/improve-txmetadata
…n-platform into feat/improve-txmetadata
…n-platform into feat/improve-txmetadata
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good
…form into feat/improve-txmetadata
WalkthroughVersion updates across Gradle and README. Significant SWIG/FFI remapping of clone/ignore surfaces. New wallet-utils protobuf schema and migration of TxMetadata to support versioned CBOR/Protobuf batching, encryption, publishing, and decryption. Platform app IDs updated and new wallet-utils IDs added. Tests and examples expanded. Rust SDK adds error handling and adjusts export. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor App
participant BI as BlockchainIdentity
participant TM as TxMetadata
participant Doc as Documents API
participant DC as Data Contract (wallet-utils)
App->>BI: publishTxMetaData(items, keyParam?, encKeyIndex, version, progress?)
BI->>BI: createTxMetadata(items, keyParam?, encKeyIndex, version)
note right of BI: Build versioned payloads (CBOR/Protobuf)<br/>Select encryption key by index<br/>Encrypt per document
BI->>Doc: publish(Document for DC)
loop for each document
Doc-->>BI: ack with stored Document
BI-->>App: progress?(i)
end
BI-->>App: List<Document> (published)
sequenceDiagram
autonumber
participant App
participant BI as BlockchainIdentity
participant TMD as TxMetadataDocument
participant Dec as Decrypt
App->>BI: decryptTxMetadata(doc, keyParam?)
BI->>TMD: decrypt(keyParam?)
TMD->>Dec: decrypt payload
alt first byte == VERSION_CBOR
Note right of TMD: Set txMetadataVersion = CBOR
TMD-->>BI: items = Cbor.decodeList(...)
else first byte == VERSION_PROTOBUF
Note right of TMD: Set txMetadataVersion = Protobuf
TMD-->>BI: items = TxMetadataBatch.parseFrom(...)
else
TMD-->>BI: error (unknown version)
end
BI-->>App: List<TxMetadataItem>
sequenceDiagram
autonumber
participant Caller
participant FD as fetch_document.rs
Caller->>FD: build all_docs_query
alt DocumentQuery::new Ok
FD-->>Caller: proceed with with_where(...)
else Err(e)
Note right of FD: Return error instead of panic
FD-->>Caller: Err(String)
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
README.md (1)
55-60
: Fix README version to match published artifact.Line 55 advertises
dppVersion = "2.0.1"
, but the build currently ships as2.0.1-TX-SNAPSHOT
(see build.gradle Line 2). Anyone following the README will pull a non-existent artifact and break their build. Please update the snippet (or release a 2.0.1 artifact) so the instructions resolve correctly.
🧹 Nitpick comments (3)
dash-sdk-java/src/test/java/org/dashj/platform/sdk/ValueTest.java (1)
42-47
: Remove the commented dead code.The commented
//v2.delete()
on line 43 should be removed entirely, as thev2
variable no longer exists in this test.Apply this diff to clean up the commented code:
v1.delete(); - //v2.delete(); v3.delete();
dpp/src/main/proto/wallet-utils.proto (1)
9-9
: Consider fixed-point representation for financial fields.The use of
double
forexchangeRate
(line 9) andoriginalPrice
(line 17) can lead to precision issues with financial calculations due to floating-point representation limitations.Consider using one of these alternatives:
- Option 1 (Recommended): Use string representation with a documented format (e.g., "123.45" for currency amounts), allowing the application layer to parse with appropriate decimal precision.
- Option 2: Use fixed-point representation with separate integer fields for the whole and fractional parts, or use an integer representing the smallest currency unit (e.g., cents).
Example for Option 1:
- optional double exchangeRate = 4; + optional string exchangeRate = 4; // decimal string, e.g., "1.2345"- optional double originalPrice = 12; + optional string originalPrice = 12; // decimal string, e.g., "99.99"Also applies to: 17-17
dpp/src/test/kotlin/org/dashj/platform/contracts/wallet/TxMetaDataTests.kt (1)
226-229
: Remove the empty test stub.
publishToPlatform()
contains no assertions or behavior. Keeping an empty test just adds noise and trips detekt’s empty-block rule. Drop the method or flesh it out with real coverage.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
dash-sdk-android/src/main/rust/Cargo.lock
is excluded by!**/*.lock
dash-sdk-bindings/Cargo.lock
is excluded by!**/*.lock
platform-mobile/Cargo.lock
is excluded by!**/*.lock
📒 Files selected for processing (24)
README.md
(1 hunks)build.gradle
(1 hunks)dash-sdk-java/build.gradle
(1 hunks)dash-sdk-java/src/main/cpp/clone.h
(2 hunks)dash-sdk-java/src/main/swig/generics/result.i
(0 hunks)dash-sdk-java/src/main/swig/ignore.i
(5 hunks)dash-sdk-java/src/test/java/org/dashj/platform/sdk/DataContractTest.java
(3 hunks)dash-sdk-java/src/test/java/org/dashj/platform/sdk/ValueTest.java
(1 hunks)dpp/src/main/java/org/dashj/platform/dapiclient/SystemIds.kt
(1 hunks)dpp/src/main/java/org/dashj/platform/dashpay/BlockchainIdentity.kt
(6 hunks)dpp/src/main/java/org/dashj/platform/sdk/platform/Platform.kt
(1 hunks)dpp/src/main/java/org/dashj/platform/wallet/IdentityVerify.kt
(1 hunks)dpp/src/main/java/org/dashj/platform/wallet/TxMetadata.kt
(3 hunks)dpp/src/main/java/org/dashj/platform/wallet/TxMetadataDocument.kt
(3 hunks)dpp/src/main/java/org/dashj/platform/wallet/TxMetadataItem.kt
(4 hunks)dpp/src/main/proto/wallet-utils.proto
(1 hunks)dpp/src/test/kotlin/org/dashj/platform/contracts/wallet/TxMetaDataTests.kt
(5 hunks)dpp/src/test/kotlin/org/dashj/platform/contracts/wallet/TxMetadataWalletTest.kt
(1 hunks)examples/src/main/kotlin/dashj/org/platform/CreateTxMetadata.kt
(2 hunks)examples/src/main/kotlin/dashj/org/platform/CreateTxMetadataTest.kt
(1 hunks)examples/src/main/kotlin/dashj/org/platform/DisplayTxMetadata.kt
(2 hunks)examples/src/main/kotlin/dashj/org/platform/RegisteredNamesWithBalance.kt
(1 hunks)platform-mobile/src/fetch_document.rs
(1 hunks)platform-mobile/src/sdk.rs
(2 hunks)
💤 Files with no reviewable changes (1)
- dash-sdk-java/src/main/swig/generics/result.i
🧰 Additional context used
🧬 Code graph analysis (1)
dpp/src/main/java/org/dashj/platform/wallet/TxMetadata.kt (2)
dpp/src/main/java/org/dashj/platform/dashpay/Profiles.kt (2)
get
(166-168)get
(170-185)dpp/src/main/java/org/dashj/platform/sdk/platform/Documents.kt (2)
get
(151-167)get
(169-187)
🪛 Buf (1.57.2)
dpp/src/main/proto/wallet-utils.proto
3-3: Files with package "org.dashj.platform.wallet" must be within a directory "org/dashj/platform/wallet" relative to root but were in directory "dpp/src/main/proto".
(PACKAGE_DIRECTORY_MATCH)
🪛 detekt (1.23.8)
examples/src/main/kotlin/dashj/org/platform/RegisteredNamesWithBalance.kt
[warning] 74-74: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
dpp/src/test/kotlin/org/dashj/platform/contracts/wallet/TxMetaDataTests.kt
[warning] 227-229: This empty block of code can be removed.
(detekt.empty-blocks.EmptyFunctionBlock)
🔇 Additional comments (20)
platform-mobile/src/fetch_document.rs (1)
106-109
: LGTM! Improved error handling.The change from
.unwrap()
to explicit error matching prevents potential panics and provides better error propagation, which is consistent with the function's error handling pattern.dash-sdk-java/src/test/java/org/dashj/platform/sdk/ValueTest.java (1)
23-48
: LGTM! Redundant test code appropriately removed.The removal of the v2 test code for I16 is appropriate since I16 is already comprehensively tested in
createPlatformValuePrimitiveTest()
at line 148. This cleanup reduces code duplication without affecting test coverage.dash-sdk-java/src/test/java/org/dashj/platform/sdk/DataContractTest.java (2)
20-22
: LGTM: Improved null-safety.The defensive pattern of checking
isPresent()
before callingget()
prevents potentialNoSuchElementException
if theOptional
is empty.
34-36
: LGTM: Improved null-safety.Consistent with the pattern above—checking
isPresent()
before callingget()
prevents potential runtime exceptions.platform-mobile/src/sdk.rs (2)
225-225
: LGTM! Good observability improvement.Adding the SDK version log is consistent with the existing pattern in
create_dash_sdk_with_context
(line 173) and improves observability.
62-92
: No external FFI bindings referenceupdate_sdk_with_address_list
; removing its export attribute is safe.dpp/src/main/java/org/dashj/platform/dapiclient/SystemIds.kt (1)
10-19
: LGTM! Wallet-utils identifiers added consistently.The addition of wallet-utils identifiers and the refactoring of existing IDs to use
Sha256Hash.ZERO_HASH
follow a consistent pattern and align with the broader wallet-utils integration across the codebase.dpp/src/main/java/org/dashj/platform/sdk/platform/Platform.kt (1)
80-91
: LGTM! App definitions updated to support wallet-utils and identity-verify.The addition of the wallet-utils app and the environment-specific identity-verify app definitions are consistent with the system-wide migration to wallet-utils identifiers introduced in SystemIds.kt.
examples/src/main/kotlin/dashj/org/platform/DisplayTxMetadata.kt (2)
29-38
: LGTM! Improved argument handling with default identity support.The refactored argument parsing now supports both interactive phrase input and default identity seeds, which improves the utility's flexibility.
55-55
: LGTM! Consistent with updated metadata ordering.The change from
createdAt
toupdatedAt
aligns with the broader shift in TxMetadata.kt where ordering and filtering now use$updatedAt
instead of$createdAt
.dpp/src/main/java/org/dashj/platform/wallet/IdentityVerify.kt (1)
30-30
: LGTM! Document constant updated to reflect identity-verify app.The DOCUMENT constant change from "dashwallet.identityVerify" to "identity-verify.identityVerify" is consistent with the Platform.kt updates where the identity-verify app definition replaces dashwallet in environment mappings.
examples/src/main/kotlin/dashj/org/platform/CreateTxMetadata.kt (2)
10-10
: LGTM! Import updated to reflect TxMetadataItem package relocation.The import change from
org.dashj.platform.contracts.wallet.TxMetadataItem
toorg.dashj.platform.wallet.TxMetadataItem
is consistent with the broader reorganization of wallet-related types into the wallet package.
47-47
: LGTM! Updated to use versioned metadata publishing.The addition of the keyIndex parameter (1) and version parameter (TxMetadataDocument.VERSION_PROTOBUF) aligns with the new versioned metadata handling introduced in TxMetadataDocument.kt and the updated publishTxMetaData signature.
dpp/src/main/java/org/dashj/platform/wallet/TxMetadata.kt (4)
69-91
: LGTM! New publish method follows established patterns.The new
publish()
method correctly retrieves the HIGH security level key, calls the platform SDK with appropriate parameters, and returns a Document. The implementation is consistent with the existingcreate()
method pattern.
113-121
: LGTM! Version-aware buffer serialization implemented correctly.The
getBuffer()
method properly handles both CBOR and Protobuf serialization formats based on the version parameter, with appropriate error handling for invalid versions.
37-37
: LGTM! Document constant updated for wallet-utils migration.The DOCUMENT constant change from "dashwallet.tx_metadata" to "wallet-utils.txMetadata" is consistent with the broader migration to wallet-utils identifiers and data contracts.
130-133
: ```bash
#!/bin/bashShow get() signature in TxMetadata.kt
rg -n 'fun get' -g 'dpp/src/main/java/org/dashj/platform/wallet/TxMetadata.kt' -C3
Find direct calls to TxMetadata(...).get(
rg -nP 'TxMetadata([^)]).get(' -g '.kt'
Find instantiations of TxMetadata
rg -nP 'val\s+\w+\s*=\sTxMetadata(' -g '.kt'
</blockquote></details> <details> <summary>dpp/src/main/java/org/dashj/platform/wallet/TxMetadataDocument.kt (1)</summary><blockquote> `27-39`: **LGTM! Version tracking added with good encapsulation.** The addition of version constants and the `txMetadataVersion` field with a private setter provides clean version tracking for metadata documents. The field is appropriately initialized to `VERSION_UNKNOWN` and updated during decryption. </blockquote></details> <details> <summary>dpp/src/main/java/org/dashj/platform/wallet/TxMetadataItem.kt (2)</summary><blockquote> `8-8`: **LGTM: Package reorganization and new imports.** The package move to `org.dashj.platform.wallet` and the addition of protobuf/formatting imports appropriately support the new proto conversion features. Also applies to: 11-12, 15-15 --- `281-281`: **Note: toString() now uses locale-dependent formatting.** The toString() method now delegates to `toJson()`, which means numeric fields (exchangeRate, originalPrice) will be displayed with locale-specific currency formatting. This is acceptable for debugging/logging but be aware that the string representation will vary across locales. </blockquote></details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
assertEquals(3, dataContract.getDoc_types().size()); | ||
assertTrue("didn't find txMetadata", dataContract.getDoc_types().stream().allMatch(type -> type.equals("txMetadata"))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the assertion logic.
Line 39 uses allMatch(type -> type.equals("txMetadata"))
, which checks that every doc_type is named "txMetadata"
. With 3 doc_types, this assertion would only pass if all three have identical names, which violates the principle that data contract doc types should have distinct names.
Additionally, the error message "didn't find txMetadata"
is misleading—it suggests checking for existence, but allMatch
verifies that all types are "txMetadata"
.
Did you intend to:
- Verify that
"txMetadata"
exists among the doc_types? UseanyMatch
:-assertTrue("didn't find txMetadata", dataContract.getDoc_types().stream().allMatch(type -> type.equals("txMetadata"))); +assertTrue("didn't find txMetadata", dataContract.getDoc_types().stream().anyMatch(type -> type.equals("txMetadata")));
- Or verify specific doc_type names (e.g., three distinct types)? Assert the exact set:
Set<String> expectedTypes = Set.of("txMetadata", "typeA", "typeB"); assertEquals(expectedTypes, new HashSet<>(dataContract.getDoc_types()));
Please clarify the intended assertion and apply the appropriate fix.
🤖 Prompt for AI Agents
In dash-sdk-java/src/test/java/org/dashj/platform/sdk/DataContractTest.java
around lines 38-39, the test uses allMatch which wrongly asserts every doc_type
equals "txMetadata". Replace that with anyMatch to assert existence (and
keep/update the failure message to "didn't find txMetadata"), OR if the intent
is to verify all three specific distinct types, assert the exact set of names by
comparing a Set of expected names to new HashSet<>(dataContract.getDoc_types())
so the test validates the exact distinct types.
val version = decryptedData.copyOfRange(0, 1)[0].toInt() and 0xFF | ||
return when (version) { | ||
VERSION_CBOR -> { | ||
val list = Cbor.decodeList(decryptedData) | ||
this.txMetadataVersion = VERSION_CBOR | ||
// use .map to convert to List<TxMetadataItem> | ||
list.map { TxMetadataItem(it as Map<String, Any?>) } | ||
} | ||
VERSION_PROTOBUF -> { | ||
val batch = TxMetadataBatch.parser().parseFrom(decryptedData, 1, decryptedData.size - 1) | ||
txMetadataVersion = VERSION_PROTOBUF | ||
batch.itemsList.map { TxMetadataItem(it) } | ||
} | ||
else -> error("") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Improve error message for invalid version.
The version-aware decryption logic correctly handles both CBOR and Protobuf formats, but the error message on line 77 is empty, making debugging difficult if an invalid version is encountered.
Apply this diff to provide a more informative error message:
- else -> error("")
+ else -> error("Unknown metadata version: $version. Expected $VERSION_CBOR (CBOR) or $VERSION_PROTOBUF (Protobuf)")
🤖 Prompt for AI Agents
In dpp/src/main/java/org/dashj/platform/wallet/TxMetadataDocument.kt around
lines 64 to 78, the else branch currently calls error("") which provides no
context; change it to throw an informative exception that includes the invalid
version value (e.g., "Unsupported tx metadata version: X") and optionally lists
supported versions (VERSION_CBOR, VERSION_PROTOBUF), so the runtime log shows
the actual version byte encountered for easier debugging.
🧩 Analysis chain
Verify version byte format consistency.
The decrypt logic assumes the first byte of decrypted data is a version byte (line 64), and the Protobuf parsing starts at offset 1 (line 73). Ensure this format is consistent with how TxMetadata.getBuffer()
serializes the data.
Run the following script to verify that getBuffer()
prepends a version byte to the serialized data:
🏁 Script executed:
#!/bin/bash
# Description: Verify that TxMetadata.getBuffer() prepends version byte to match decrypt expectations
# Search for getBuffer implementation and any version byte prepending logic
ast-grep --pattern 'fun getBuffer($$$) {
$$$
}'
# Also search for any references to version byte handling in serialization
rg -nP --type=kt -B 5 -A 10 'VERSION_CBOR|VERSION_PROTOBUF' dpp/src/main/java/org/dashj/platform/wallet/
Length of output: 26
🏁 Script executed:
#!/bin/bash
# Locate the Kotlin file defining TxMetadata
file=$(find . -type f -name 'TxMetadata.kt' | head -n1)
if [ -z "$file" ]; then
echo "ERROR: TxMetadata.kt not found"
exit 1
fi
echo "Inspecting: $file"
# Show getBuffer implementation
echo -e "\n---- getBuffer() definition ----"
rg -n 'fun getBuffer' "$file" -C5
# Show where version bytes are prepended
echo -e "\n---- VERSION_CBOR / VERSION_PROTOBUF usage ----"
rg -n 'VERSION_CBOR|VERSION_PROTOBUF' "$file" -C5
Length of output: 1290
Prefix version byte in getBuffer()
In TxMetadata.kt
(line 113), getBuffer()
returns only the raw payload, yet TxMetadataDocument.kt
(lines 64–78) reads the first byte of decrypted data as the version. Update getBuffer()
to prepend the version byte, e.g.:
val payload = when (version) {
VERSION_CBOR -> Cbor.encode(items.map { it.toObject() })
VERSION_PROTOBUF -> TxMetadataBatch.newBuilder()…
}
return byteArrayOf(version.toByte()) + payload
🤖 Prompt for AI Agents
In dpp/src/main/java/org/dashj/platform/wallet/TxMetadataDocument.kt around
lines 64 to 78, the code expects the first byte of the decrypted buffer to be a
version byte but getBuffer() currently returns only the raw payload; update the
getBuffer() implementation (in TxMetadata.kt) so it prepends the version byte to
the serialized payload: serialize payload based on version (CBOR or PROTOBUF)
then return a new byte array composed of the single version byte followed by the
payload bytes, ensuring callers can read the version from index 0 and the
payload from index 1; keep existing serialization logic unchanged and just
concatenate the version byte in front.
constructor(protoTxMetadata: WalletUtils.TxMetadataItem) : this( | ||
protoTxMetadata.txId.toByteArray(), | ||
if (protoTxMetadata.timestamp != 0L) protoTxMetadata.timestamp else null, | ||
if (protoTxMetadata.memo != "") protoTxMetadata.memo else null, | ||
if (protoTxMetadata.exchangeRate != 0.0) protoTxMetadata.exchangeRate else null, | ||
if (protoTxMetadata.currencyCode != "") protoTxMetadata.currencyCode else null, | ||
if (protoTxMetadata.taxCategory != "") protoTxMetadata.taxCategory else null, | ||
if (protoTxMetadata.service != "") protoTxMetadata.service else null, | ||
if (protoTxMetadata.customIconUrl != "") protoTxMetadata.customIconUrl else null, | ||
if (protoTxMetadata.giftCardNumber != "") protoTxMetadata.giftCardNumber else null, | ||
if (protoTxMetadata.giftCardPin != "") protoTxMetadata.giftCardPin else null, | ||
if (protoTxMetadata.merchantName != "") protoTxMetadata.merchantName else null, | ||
if (protoTxMetadata.originalPrice != 0.00) protoTxMetadata.originalPrice else null, | ||
if (protoTxMetadata.barcodeValue != "") protoTxMetadata.barcodeValue else null, | ||
if (protoTxMetadata.barcodeFormat != "") protoTxMetadata.barcodeFormat else null, | ||
if (protoTxMetadata.merchantUrl != "") protoTxMetadata.merchantUrl else null | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider more robust default handling for protobuf fields.
The constructor relies on empty-string and zero-value checks to map protobuf defaults to null. This approach is fragile:
- Line 90: Floating-point equality check (
!= 0.00
) is imprecise - If protobuf defaults change or if legitimate zero/empty values need representation, this logic breaks
Consider using protobuf's hasField()
methods or explicit presence indicators to distinguish between "not set" and "set to zero/empty".
🤖 Prompt for AI Agents
In dpp/src/main/java/org/dashj/platform/wallet/TxMetadataItem.kt around lines 78
to 94, the constructor treats protobuf default values (empty strings and zeros)
as "unset" by comparing to "" and 0.00 which is fragile and incorrect for
floating-point and when legitimate zero/empty values are intended; update the
mapping to use protobuf presence checks (e.g. hasTimestamp(), hasMemo(),
hasExchangeRate(), or the generated oneof/optional presence methods) or switch
the proto schema to wrapper/optional types and call those presence methods so
you only map to null when the field is actually unset, avoiding direct equality
checks for doubles and empty-string heuristics.
timestamp?.let { | ||
map["timestamp"] to it | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Assignment operator missing.
Line 101 uses the to
infix operator, which creates a Pair<String, Long>
but does not assign it to the map. The timestamp will never be added to the returned map.
Apply this diff to fix the assignment:
timestamp?.let {
- map["timestamp"] to it
+ map["timestamp"] = it
}
🤖 Prompt for AI Agents
In dpp/src/main/java/org/dashj/platform/wallet/TxMetadataItem.kt around lines
100 to 102, the timestamp?.let block uses the `to` infix operator which creates
a Pair but does not insert it into the map; change the block to assign the value
into the map (e.g., use map["timestamp"] = it or map.put("timestamp", it)) so
the timestamp is actually added to the returned map.
timestamp?.let { | ||
map["timestamp"] to it | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Assignment operator missing.
Line 164 has the same bug as in toObject()
- using to
instead of =
for map assignment.
Apply this diff:
timestamp?.let {
- map["timestamp"] to it
+ map["timestamp"] = it
}
📝 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.
timestamp?.let { | |
map["timestamp"] to it | |
} | |
timestamp?.let { | |
map["timestamp"] = it | |
} |
🤖 Prompt for AI Agents
In dpp/src/main/java/org/dashj/platform/wallet/TxMetadataItem.kt around lines
163 to 165, the map assignment uses the Kotlin pair operator "to" instead of the
assignment operator; replace the expression map["timestamp"] to it with an
actual assignment map["timestamp"] = it (same fix as in toObject()), so the
timestamp is stored into the map rather than creating a Pair.
@@ -0,0 +1,26 @@ | |||
syntax = "proto3"; | |||
|
|||
package org.dashj.platform.wallet; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address package directory mismatch.
The static analysis tool flags that files with package org.dashj.platform.wallet
should be in directory org/dashj/platform/wallet
relative to root, but this file is in dpp/src/main/proto
. This mismatch may cause issues with protobuf tooling or build systems that expect strict package-to-directory mapping.
Consider either:
- Moving this file to
dpp/src/main/proto/org/dashj/platform/wallet/wallet-utils.proto
, or - Adjusting the package name to match the directory structure, or
- Documenting why the mismatch is acceptable in this codebase (if there's a build system that handles this correctly).
🧰 Tools
🪛 Buf (1.57.2)
3-3: Files with package "org.dashj.platform.wallet" must be within a directory "org/dashj/platform/wallet" relative to root but were in directory "dpp/src/main/proto".
(PACKAGE_DIRECTORY_MATCH)
🤖 Prompt for AI Agents
In dpp/src/main/proto/wallet-utils.proto around line 3, the proto package is
declared as org.dashj.platform.wallet but the file lives in dpp/src/main/proto
which violates the expected package-to-directory mapping; fix by either moving
the file to dpp/src/main/proto/org/dashj/platform/wallet/wallet-utils.proto so
the path matches the package, or change the package declaration to reflect its
current directory, or add a short repository-level comment/doc explaining and
referencing the build config that intentionally handles this mismatch if you
choose to leave it as-is.
// println("before: $balanceBefore") | ||
// println("after: $balanceAfter") | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix off-by-one in loop.
IntRange(0, count)
is inclusive, so you enqueue count + 1
metadata items. Publishing extra documents unnecessarily spends credits and skews tests. Use repeat(count)
or for (i in 0 until count)
instead.
Apply:
- for (i in IntRange(0, count)) {
+ repeat(count) {
txMetadataList.add(txMetadata)
}
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
dpp/src/test/kotlin/org/dashj/platform/contracts/wallet/TxMetadataWalletTest.kt
around lines 142 to 144, the loop uses IntRange(0, count) which is inclusive and
thus enqueues count + 1 metadata items; change the loop to produce exactly count
items by replacing IntRange(0, count) with either repeat(count) or for (i in 0
until count) so you publish the intended number of documents (and remove any
now-unnecessary commented debug prints if desired).
for (i in IntRange(0, count)) { | ||
txMetadataList.add(txMetadata) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid off-by-one when duplicating items.
IntRange(0, count)
produces count + 1
iterations because the end value is inclusive. That means you publish one more metadata entry than requested, which can skew balance checks and waste credits. Switch to repeat(count)
or for (i in 0 until count)
to honor the intended count.
Suggested fix:
- for (i in IntRange(0, count)) {
+ repeat(count) {
txMetadataList.add(txMetadata)
}
📝 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.
for (i in IntRange(0, count)) { | |
txMetadataList.add(txMetadata) | |
} | |
repeat(count) { | |
txMetadataList.add(txMetadata) | |
} |
🤖 Prompt for AI Agents
In examples/src/main/kotlin/dashj/org/platform/CreateTxMetadataTest.kt around
lines 142-144, the loop uses IntRange(0, count) which is inclusive and produces
count+1 iterations (off-by-one); replace it with either repeat(count) {
txMetadataList.add(txMetadata) } or for (i in 0 until count) {
txMetadataList.add(txMetadata) } so exactly count copies are added.
lastItem = documents.last().id | ||
if (documents.size == 100) { | ||
queryOpts = DocumentQuery.Builder() | ||
.startAt(lastItem) | ||
.orderBy("normalizedLabel") | ||
.limit(100) | ||
.build() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard empty pages before dereferencing last()
If the first page (or any subsequent page) returns zero documents, documents.last()
throws NoSuchElementException
, crashing the example. Break out of the loop when the page is empty before dereferencing lastItem
.
- documents = platform.documents.get("dpns.domain", queryOpts)
- allDocuments.addAll(documents)
- requests += 1
-
- lastItem = documents.last().id
- if (documents.size == 100) {
+ documents = platform.documents.get("dpns.domain", queryOpts)
+ allDocuments.addAll(documents)
+ requests += 1
+
+ if (documents.isEmpty()) {
+ break
+ }
+
+ lastItem = documents.last().id
+ if (documents.size == limit) {
queryOpts = DocumentQuery.Builder()
.startAt(lastItem)
.orderBy("normalizedLabel")
- .limit(100)
+ .limit(limit)
.build()
}
```<!-- review_comment_end -->
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **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.
```suggestion
documents = platform.documents.get("dpns.domain", queryOpts)
allDocuments.addAll(documents)
requests += 1
if (documents.isEmpty()) {
break
}
lastItem = documents.last().id
if (documents.size == limit) {
queryOpts = DocumentQuery.Builder()
.startAt(lastItem)
.orderBy("normalizedLabel")
.limit(limit)
.build()
}
🤖 Prompt for AI Agents
In examples/src/main/kotlin/dashj/org/platform/RegisteredNamesWithBalance.kt
around lines 56 to 63, guard against empty pages before calling
documents.last(): check if documents.isEmpty() and break out of the paging loop
(or return) before dereferencing last(); only compute lastItem and build the
next DocumentQuery when documents is not empty and documents.size == 100,
ensuring you don't call last() on an empty list which causes
NoSuchElementException.
allDocuments.forEachIndexed { i, document -> | ||
val nameDocument = DomainDocument(document) | ||
val balance = try { platform.client.getIdentityBalance(nameDocument.dashUniqueIdentityId!!) } catch (e: Exception) { - 2 } | ||
nameBalanceMap[nameDocument.normalizedLabel] = balance | ||
} | ||
println("username count: ${allDocuments.size}") | ||
allDocuments.forEachIndexed { i, document -> | ||
val nameDocument = DomainDocument(document) | ||
val balance = nameBalanceMap[nameDocument.normalizedLabel] | ||
println("$i. ${nameDocument.label}: ${Coin.valueOf((balance ?: -1) / 1000).toFriendlyString()}") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t swallow balance-fetch failures
Catching every Exception
and inserting -2
causes failed lookups to render as “0 DASH”, hiding real errors. Log the failure and skip (or mark as unavailable) so the output stays truthful.
- val balance = try { platform.client.getIdentityBalance(nameDocument.dashUniqueIdentityId!!) } catch (e: Exception) { - 2 }
- nameBalanceMap[nameDocument.normalizedLabel] = balance
+ val balance = try {
+ platform.client.getIdentityBalance(nameDocument.dashUniqueIdentityId!!)
+ } catch (e: Exception) {
+ println("Failed to fetch balance for ${nameDocument.label}: ${e.message}")
+ null
+ } ?: return@forEachIndexed
+ nameBalanceMap[nameDocument.normalizedLabel] = balance
@@
- val balance = nameBalanceMap[nameDocument.normalizedLabel]
- println("$i. ${nameDocument.label}: ${Coin.valueOf((balance ?: -1) / 1000).toFriendlyString()}")
+ val balance = nameBalanceMap[nameDocument.normalizedLabel]
+ if (balance == null) {
+ println("$i. ${nameDocument.label}: <balance unavailable>")
+ } else {
+ println("$i. ${nameDocument.label}: ${Coin.valueOf(balance / 1000).toFriendlyString()}")
+ }
```<!-- review_comment_end -->
<!-- file_end -->
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **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.
```suggestion
allDocuments.forEachIndexed { i, document ->
val nameDocument = DomainDocument(document)
val balance = try {
platform.client.getIdentityBalance(nameDocument.dashUniqueIdentityId!!)
} catch (e: Exception) {
println("Failed to fetch balance for ${nameDocument.label}: ${e.message}")
null
} ?: return@forEachIndexed
nameBalanceMap[nameDocument.normalizedLabel] = balance
}
println("username count: ${allDocuments.size}")
allDocuments.forEachIndexed { i, document ->
val nameDocument = DomainDocument(document)
val balance = nameBalanceMap[nameDocument.normalizedLabel]
if (balance == null) {
println("$i. ${nameDocument.label}: <balance unavailable>")
} else {
println("$i. ${nameDocument.label}: ${Coin.valueOf(balance / 1000).toFriendlyString()}")
}
}
🧰 Tools
🪛 detekt (1.23.8)
[warning] 74-74: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
🤖 Prompt for AI Agents
In examples/src/main/kotlin/dashj/org/platform/RegisteredNamesWithBalance.kt
around lines 72–82 the code swallows all Exceptions when fetching balances and
writes -2 into the map (which later prints as 0 DASH), hiding real errors;
change the balance handling to catch the exception, log the error with context
(e.g., identity id and exception message/stack) and store a nullable or explicit
"unavailable" marker in nameBalanceMap (or skip insertion) instead of -2, then
update the printing loop to detect null/unavailable and print "unavailable" (or
skip) rather than converting a sentinel into currency; ensure you use the
project logger (or println if none) and avoid dividing/formatting when balance
is unavailable.
Updates to support tx metadata changes
Summary by CodeRabbit
New Features
Improvements
Documentation
Chores