Skip to content

solana attestation to provenance#48

Open
chenchan09 wants to merge 4 commits intoprovenancefrom
chen/solana-attestation
Open

solana attestation to provenance#48
chenchan09 wants to merge 4 commits intoprovenancefrom
chen/solana-attestation

Conversation

@chenchan09
Copy link
Contributor

adding attestation program and imagehash abs.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 provenance module with public Provenance APIs 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 on TransactionService.
  • 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()
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.

2 participants