Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new :provenance Android library module to the SDK that builds C2PA-style image manifests, creates/uses a Solana Attestation Service (SAS) schema per wallet, and submits signed schema/attestation transactions to a backend endpoint.
Changes:
- Introduces the
provenancemodule with publicProvenanceAPIs for single and batch image-hash attestations plus manifest storage options (sidecar/embed/both/none). - Adds SAS instruction builders in
core(AttestationProgram) and exposes new attestation-related backend endpoints onTransactionService. - Exposes
StorageService.getContext()to support provenance SharedPreferences storage, and adds multiple IDE (.idea) metadata changes.
Reviewed changes
Copilot reviewed 25 out of 30 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| settings.gradle.kts | Includes the new :provenance module in the Gradle build. |
| provenance/src/main/java/com/altude/provenance/ProvenanceManager.kt | Builds/partially-signs SAS schema + attestation transactions and serializes them for backend submission. |
| provenance/src/main/java/com/altude/provenance/Provenance.kt | Public API for single/batch attest flows; applies post-attestation manifest saving/embedding behavior. |
| provenance/src/main/java/com/altude/provenance/interfaces/ProvenanceService.kt | Retrofit service for provenance backend endpoint. |
| provenance/src/main/java/com/altude/provenance/interfaces/ITransactionResponse.kt | Common response contract for backend transaction responses. |
| provenance/src/main/java/com/altude/provenance/data/TransactionResponse.kt | Serializable implementation of ITransactionResponse. |
| provenance/src/main/java/com/altude/provenance/data/ProvenanceRequests.kt | Placeholder package file (currently empty). |
| provenance/src/main/java/com/altude/provenance/data/ProvenancePrefs.kt | SharedPreferences-based per-wallet “schema created” persistence. |
| provenance/src/main/java/com/altude/provenance/data/ManifestOption.kt | Sealed options for sidecar/embed/both/none manifest storage. |
| provenance/src/main/java/com/altude/provenance/data/ImageHashPayload.kt | Payload + wire DTOs (request/response/result) for image-hash attestations. |
| provenance/src/main/java/com/altude/provenance/data/C2paManifest.kt | Builds and serializes a lightweight C2PA-style manifest; supports sidecar saving and JPEG/PNG embedding. |
| provenance/src/main/java/com/altude/provenance/data/AttestationResponse.kt | Provenance attestation response DTO. |
| provenance/src/main/java/com/altude/provenance/data/AttestationOption.kt | Commitment enum for provenance operations. |
| provenance/proguard-rules.pro | Proguard rules for provenance module. |
| provenance/consumer-rules.pro | Consumer proguard rules for provenance module. |
| provenance/build.gradle.kts | New module build configuration and dependencies (Retrofit, serialization, Solana, ExifInterface). |
| provenance/.gitignore | Ignores provenance module build outputs. |
| gasstation/src/main/java/com/altude/gasstation/GaslessManager.kt | Whitespace-only change. |
| gasstation/src/main/java/com/altude/gasstation/data/AttestationResponse.kt | Adds gasstation attestation response DTO. |
| gasstation/src/main/java/com/altude/gasstation/data/AttestationOption.kt | Adds gasstation attestation options DTOs (attest/revoke/create schema). |
| gasstation/src/main/java/com/altude/gasstation/Altude.kt | Whitespace-only change. |
| core/src/main/java/com/altude/core/service/StorageService.kt | Adds getContext() accessor for cross-module usage. |
| core/src/main/java/com/altude/core/Programs/AttestationProgram.kt | Adds SAS program instruction + PDA derivation utilities. |
| core/src/main/java/com/altude/core/data/AttestationRequest.kt | Adds request DTOs for SAS backend endpoints. |
| core/src/main/java/com/altude/core/api/TransactionService.kt | Adds Retrofit endpoints for SAS createSchema/attest/revoke. |
| .idea/misc.xml | Changes IDE JDK/project settings (developer-specific). |
| .idea/markdown.xml | Adds IDE markdown plugin settings (developer-specific). |
| .idea/gradle.xml | Adds provenance module to IDE Gradle project set (developer-specific). |
| .idea/deploymentTargetSelector.xml | Modifies IDE run/deployment selector state (developer-specific). |
| .idea/copilot.data.migration.ask2agent.xml | Adds IDE/Copilot migration state file (developer-specific). |
Files not reviewed (5)
- .idea/copilot.data.migration.ask2agent.xml: Language not supported
- .idea/deploymentTargetSelector.xml: Language not supported
- .idea/gradle.xml: Language not supported
- .idea/markdown.xml: Language not supported
- .idea/misc.xml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * Exposes the application [Context] to other SDK modules (e.g. provenance). | ||
| * Safe to call after [init] has been invoked. | ||
| */ | ||
| fun getContext(): Context = appContext |
Comment on lines
+50
to
+51
| else throw Error("No seed found in storage") | ||
| } else throw Error("Please set seed first") |
Comment on lines
+48
to
+50
| return if (seedData.privateKey != null) | ||
| SolanaEddsa.createKeypairFromSecretKey(seedData.privateKey!!.copyOfRange(0, 32)) | ||
| else throw Error("No seed found in storage") |
Comment on lines
+33
to
+69
| * Session-level PDA cache: walletBase58 → Schema PDA. | ||
| * Avoids re-deriving the PDA on every call within the same app session. | ||
| * SharedPreferences ([ProvenancePrefs]) handles persistence across restarts. | ||
| */ | ||
| private val schemaPdaCache = mutableMapOf<String, PublicKey>() | ||
|
|
||
| private val rpc get() = AltudeRpc(SdkConfig.apiConfig.RpcUrl) | ||
| private val feePayerPubKey get() = PublicKey(SdkConfig.apiConfig.FeePayer) | ||
|
|
||
| // ── Keypair resolution ──────────────────────────────────────────────────── | ||
|
|
||
| suspend fun getKeyPair(account: String = ""): Keypair { | ||
| val seedData = StorageService.getDecryptedSeed(account) | ||
| if (seedData != null) { | ||
| if (seedData.type == "mnemonic") return Mnemonic(seedData.mnemonic).getKeyPair() | ||
| return if (seedData.privateKey != null) | ||
| SolanaEddsa.createKeypairFromSecretKey(seedData.privateKey!!.copyOfRange(0, 32)) | ||
| else throw Error("No seed found in storage") | ||
| } else throw Error("Please set seed first") | ||
| } | ||
|
|
||
| // ── PDA helpers ─────────────────────────────────────────────────────────── | ||
|
|
||
| /** | ||
| * Derives the Schema PDA for the fixed [SCHEMA_NAME] and the given [account]. | ||
| * Caches the result in [schemaPdaCache] — computed once per session per wallet. | ||
| * Keeps [AttestationProgram] fully hidden from callers outside this class. | ||
| */ | ||
| internal suspend fun deriveSchemaAddress(account: String = ""): PublicKey { | ||
| val keypair = getKeyPair(account) | ||
| val walletKey = keypair.publicKey.toBase58() | ||
| return schemaPdaCache.getOrPut(walletKey) { | ||
| AttestationProgram.deriveSchemaAddress( | ||
| authority = keypair.publicKey, | ||
| name = SCHEMA_NAME | ||
| ) | ||
| } |
Comment on lines
+158
to
+166
| val payloadJson = """ | ||
| { | ||
| "type": "${payload.type}", | ||
| "hash": "${payload.hash}", | ||
| "mime": "${payload.mime}", | ||
| "name": "${payload.name}", | ||
| "timestamp": ${payload.timestamp} | ||
| } | ||
| """.trimIndent() |
Comment on lines
+1
to
+6
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project version="4"> | ||
| <component name="Ask2AgentMigrationStateService"> | ||
| <option name="migrationStatus" value="COMPLETED" /> | ||
| </component> | ||
| </project> No newline at end of file |
Comment on lines
14
to
19
| <option value="$PROJECT_DIR$/core" /> | ||
| <option value="$PROJECT_DIR$/gasstation" /> | ||
| <option value="$PROJECT_DIR$/nft" /> | ||
| <option value="$PROJECT_DIR$/provenance" /> | ||
| <option value="$PROJECT_DIR$/smart-account" /> | ||
| </set> |
Comment on lines
5
to
10
| <SelectionState runConfigName="app"> | ||
| <option name="selectionMode" value="DROPDOWN" /> | ||
| </SelectionState> | ||
| <SelectionState runConfigName="MainActivity"> | ||
| <option name="selectionMode" value="DROPDOWN" /> | ||
| </SelectionState> | ||
| <SelectionState runConfigName="testTransferToken()"> | ||
| <option name="selectionMode" value="DROPDOWN" /> | ||
| </SelectionState> | ||
| <SelectionState runConfigName="testLatencySample()"> | ||
| <option name="selectionMode" value="DROPDOWN" /> | ||
| </SelectionState> | ||
| <SelectionState runConfigName="testCreateAccount()"> | ||
| <option name="selectionMode" value="DROPDOWN" /> | ||
| </SelectionState> | ||
| <SelectionState runConfigName="testGetBalance()"> | ||
| <option name="selectionMode" value="DROPDOWN" /> | ||
| </SelectionState> | ||
| <SelectionState runConfigName="testSwap()"> | ||
| <option name="selectionMode" value="DROPDOWN" /> | ||
| </SelectionState> | ||
| </selectionStates> | ||
| </component> | ||
| </project> No newline at end of file |
Comment on lines
+63
to
+67
| private val canonicalJson = Json { | ||
| encodeDefaults = true | ||
| explicitNulls = false | ||
| } | ||
|
|
Comment on lines
+184
to
+221
| // ── 1. Ensure schema ONCE for the whole batch ───────────────────────── | ||
| val schemaResult = ProvenanceManager.ensureSchema( | ||
| account = first.account, | ||
| commitment = first.commitment.name | ||
| ) | ||
| if (schemaResult.isFailure) { | ||
| payloads.forEachIndexed { i, p -> | ||
| emit(AttestationResult(i, p.name, p.hash, | ||
| Result.failure(schemaResult.exceptionOrNull()!!))) | ||
| } | ||
| return@flow | ||
| } | ||
|
|
||
| // ── 2. Derive Schema PDA ONCE ───────────────────────────────────────── | ||
| val schemaPda = ProvenanceManager.deriveSchemaAddress(first.account) | ||
| val walletKey = ProvenanceManager.getKeyPair(first.account).publicKey.toBase58() | ||
|
|
||
| var schemaMarked = ProvenancePrefs.isSchemaCreated(walletKey) | ||
|
|
||
| // ── 3. Sequential loop — sign offline → submit → emit ───────────────── | ||
| payloads.forEachIndexed { index, payload -> | ||
| val itemResult = runCatching { | ||
| val attestedTx = ProvenanceManager.attest(payload, schemaPda).getOrThrow() | ||
|
|
||
| val request = ImageHashRequest( | ||
| type = payload.type, | ||
| hash = payload.hash, | ||
| mime = payload.mime, | ||
| name = payload.name, | ||
| timestamp = payload.timestamp, | ||
| account = payload.account, | ||
| recipient = payload.recipient, | ||
| expireAt = payload.expireAt, | ||
| manifest = payload.manifest, | ||
| signedSchemaTx = if (index == 0) schemaResult.getOrNull() else null, | ||
| signedAttestationTx = attestedTx | ||
| ) | ||
| val res = service().attestImageHash(request).await() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
adding attestation program and imagehash abs.