Skip to content

adding solana attestation#44

Open
chenchan09 wants to merge 26 commits intomainfrom
chen/test-002
Open

adding solana attestation#44
chenchan09 wants to merge 26 commits intomainfrom
chen/test-002

Conversation

@chenchan09
Copy link
Contributor

add solana attestation service

@chenchan09 chenchan09 changed the title Chen/test 002 solana attestation Mar 18, 2026
@chenchan09 chenchan09 changed the title solana attestation adding solana attestation Mar 18, 2026
@mocolicious mocolicious requested a review from Copilot March 18, 2026 19:20
@mocolicious mocolicious self-assigned this Mar 18, 2026
@mocolicious mocolicious self-requested a review March 18, 2026 19:20
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

This PR introduces Solana Attestation Service (SAS) support end-to-end (instruction builders, API requests, and public SDK entrypoints), while also refactoring Vault initialization/auth flows and updating Android/Gradle build configuration plus the sample app UX.

Changes:

  • Add SAS program instruction builders + Retrofit endpoints + SDK methods for createSchema/attest/revoke.
  • Improve Vault/VaultStorage resilience (stale keyset purge) and change init flow to unlock immediately and cache public key.
  • Update build tooling (AGP/Gradle/compileSdk) and refactor the sample app to view-based examples with new screens.

Reviewed changes

Copilot reviewed 54 out of 58 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
vault/src/main/java/com/altude/vault/storage/VaultStorage.kt Adds stale keyset detection/purging and master key rebuild logic; changes keystore init/clear semantics.
vault/src/main/java/com/altude/vault/model/VaultStorageCorruptedException.kt New exception for stale/mismatched encrypted-file keyset scenarios.
vault/src/main/java/com/altude/vault/model/VaultSigner.kt Allows injecting an initial cached public key to avoid first-derivation overhead.
vault/src/main/java/com/altude/vault/model/VaultException.kt Adds new error code VAULT-0305 for stale keyset.
vault/src/main/java/com/altude/vault/crypto/BiometricHandler.kt Updates biometric availability strategy and attempts to handle API 28 credential limitations.
vault/build.gradle.kts Bumps compileSdk to 36 for the vault module.
smart-account/build.gradle.kts Bumps compileSdk to 36.
nft/build.gradle.kts Bumps compileSdk to 36.
gasstation/build.gradle.kts Bumps compileSdk to 36.
core/build.gradle.kts Bumps compileSdk to 36.
settings.gradle.kts Removes foojay toolchain resolver convention plugin block.
gradle/wrapper/gradle-wrapper.properties Changes Gradle wrapper distribution (and adds sha256 sum).
gradle/libs.versions.toml Bumps AGP version to 8.10.0.
gradle/gradle-daemon-jvm.properties Removes daemon JVM toolchain properties file.
gradle.properties Adds multiple Android/AGP-related feature flags and defaults.
build.gradle.kts Removes unused buildToolsVersion extra and whitespace changes.
gasstation/src/main/java/com/altude/gasstation/data/SwapOption.kt Makes account optional with a default empty string.
gasstation/src/main/java/com/altude/gasstation/data/GetHistoryOption.kt Adds defaults for account/limit/offset/walletAddress.
gasstation/src/main/java/com/altude/gasstation/data/AttestationResponse.kt New response model for attestation operations.
gasstation/src/main/java/com/altude/gasstation/data/AttestationOption.kt New option models for attesting, revoking, and schema creation.
core/src/main/java/com/altude/core/data/AttestationRequest.kt New request bodies for SAS API endpoints.
core/src/main/java/com/altude/core/api/TransactionService.kt Adds Retrofit endpoints for SAS createSchema/attest/revoke.
core/src/main/java/com/altude/core/Programs/AttestationProgram.kt New SAS instruction builders + PDA derivations + binary encoding.
gasstation/src/main/java/com/altude/gasstation/AltudeGasStation.kt Initializes storage earlier; unlocks vault during init to prompt once and capture public key.
gasstation/src/main/java/com/altude/gasstation/Altude.kt Adds FragmentActivity-based init overload + per-call signer overrides + SAS methods.
core/src/main/java/com/altude/core/service/StorageService.kt Refactors keystore handling and recovery flow; changes error behavior to return null on decrypt failure.
app/src/main/res/values/themes.xml Switches theme parent to MaterialComponents and keeps color item mapping.
app/src/main/res/layout/activity_vault_example.xml Adds wallet address display + copy/reveal buttons.
app/src/main/res/layout/activity_signer_examples.xml New layout for signer comparison example screen.
app/src/main/res/layout/activity_main.xml New main menu layout for selecting sample screens.
app/src/main/res/layout/activity_error_handling_example.xml Refactors error handling demo layout and adds clear button/progress bar.
app/src/main/java/com/altude/android/VaultExampleActivity.kt Reworks example to use Altude.setApiKey(activity, ...), add reveal/copy wallet flows, and unified error handling.
app/src/main/java/com/altude/android/SignerExamplesActivity.kt New activity demonstrating default vault vs custom signer usage.
app/src/main/java/com/altude/android/MainActivity.kt Replaces Compose launcher with view-based menu launcher.
app/src/main/java/com/altude/android/ErrorHandlingExampleActivity.kt Reworks to trigger “real” SDK error paths and adds vault clearing flow.
app/src/main/AndroidManifest.xml Changes launcher to MainActivity; registers new sample activities.
app/build.gradle.kts Removes Compose, uses Material components, adjusts packaging flags.
app/src/main/java/com/altude/android/ui/theme/Type.kt Deletes Compose typography file (Compose removed).
app/src/main/java/com/altude/android/ui/theme/Theme.kt Deletes Compose theme file (Compose removed).
app/src/main/java/com/altude/android/ui/theme/Color.kt Deletes Compose color palette file (Compose removed).
VERIFICATION_COMPLETE.md Deletes verification doc.
READY_TO_BUILD.md Deletes build readiness doc.
QUICK_FIX_REFERENCE.md Deletes quick fix reference doc.
QUICK_FIX_CHECKLIST.md Deletes quick fix checklist doc.
PHASE_6_COMPLETION_SUMMARY.md Deletes phase completion summary doc.
INDEX.md Deletes documentation index doc.
IMPLEMENTATION_GUIDE.md Deletes implementation guide doc.
GRADLE_FIX_FINAL.md Deletes gradle fix doc.
GET_STARTED.md Deletes getting started doc.
FIX_SUMMARY.md Deletes fix summary doc.
FIXES_APPLIED_SUMMARY.md Deletes fixes applied summary doc.
DETAILED_CHANGES.md Deletes detailed changes doc.
16KB_PAGE_SIZE_FIX_SUMMARY.md Deletes 16KB fix summary doc.
16KB_ALIGNMENT_FIX_FINAL.md Deletes 16KB alignment final doc.
.idea/gradle.xml Sets IDE Gradle JVM to jbr-21 and enables parallel model fetch.
.idea/deploymentTargetSelector.xml Adds IDE deployment target selection (includes physical device identifier).
Files not reviewed (2)
  • .idea/deploymentTargetSelector.xml: Language not supported
  • .idea/gradle.xml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +5
#Fri Mar 13 09:10:32 PST 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
distributionSha256Sum=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Comment on lines +51 to +60
} catch (e: BiometricNotAvailableException) {
// Re-throw only if device genuinely has no screen lock at all.
// BIOMETRIC_ERROR_UNSUPPORTED on older Android = API limitation, not
// a real absence of screen lock — fall through to let the prompt show.
val msg = e.message ?: ""
if (!msg.contains("unsupported", ignoreCase = true) &&
!msg.contains("code: 12", ignoreCase = true) // BIOMETRIC_ERROR_UNSUPPORTED = 12
) {
throw e
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@chenchan09 please separate changes to key storage from this PR. This needs to be for just the provenance module.

Comment on lines 117 to 119
} catch (e: Throwable) {
Result.failure(Exception(e.message ?: e.javaClass.simpleName, e))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Comment on lines +397 to +408
val attester = SdkConfig.currentSigner?.publicKey
val schemaPda = foundation.metaplex.solanapublickeys.PublicKey(option.schemaId)
val recipientKey = if (option.recipient.isBlank()) attester
else foundation.metaplex.solanapublickeys.PublicKey(option.recipient)
val attestationId = if (attester != null && recipientKey != null) {
AttestationProgram.deriveAttestationAddress(
schema = schemaPda,
attester = attester,
recipient = recipientKey,
nonce = option.nonce
).toBase58()
} else ""
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Comment on lines +20 to +41
object AttestationProgram {

/** On-chain program address for the Solana Attestation Service. */
val PROGRAM_ID = PublicKey("22zoJMtdu5rJOmEMCqaYzDVLEqJa4FcFBGmrnEcUhbCr")

// ── Anchor discriminators (sha256("global:<name>")[0..8] little-endian) ───
// Pre-computed from the SAS IDL.
private val DISC_CREATE_SCHEMA = byteArrayOf(102.toByte(), 82.toByte(), 205.toByte(), 109.toByte(), 56.toByte(), 206.toByte(), 253.toByte(), 189.toByte())
private val DISC_CREATE_ATTESTATION = byteArrayOf( 71.toByte(), 149.toByte(), 77.toByte(), 206.toByte(), 166.toByte(), 86.toByte(), 252.toByte(), 185.toByte())
private val DISC_REVOKE_ATTESTATION = byteArrayOf(243.toByte(), 175.toByte(), 179.toByte(), 26.toByte(), 67.toByte(), 213.toByte(), 154.toByte(), 97.toByte())

// ── PDA seeds ─────────────────────────────────────────────────────────────

/** Derives the Schema PDA: seeds = ["schema", authority, name] */
suspend fun deriveSchemaAddress(authority: PublicKey, name: String): PublicKey {
val seeds = listOf(
"schema".toByteArray(StandardCharsets.UTF_8),
authority.toByteArray(),
name.toByteArray(StandardCharsets.UTF_8)
)
return PublicKey.findProgramAddress(seeds, PROGRAM_ID).address
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@chenchan09 this doesn't need to be part of core, its only necessary for provenance.

Comment on lines +82 to +89
@Suppress("UNUSED_PARAMETER")
fun initializeKeystore(context: Context, appId: String, requireBiometric: Boolean = true) {
try {
val masterKey = MasterKey.Builder(context, MASTER_KEY_ALIAS)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
buildMasterKey(context)
} catch (e: Exception) {
if (isKeyPermanentlyInvalidated(e)) throw BiometricInvalidatedException(cause = e)
throw VaultInitFailedException("Failed to initialize keystore: ${e.message}", cause = e)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@chenchan09 separate key storage

Comment on lines 143 to +145
fun clearVault(context: Context, appId: String): Boolean {
val vaultFile = File(context.filesDir, "$VAULT_FILE_PREFIX${appId}$VAULT_FILE_SUFFIX")
return vaultFile.delete()
purgeVault(context, appId)
return true
Copy link
Contributor

Choose a reason for hiding this comment

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

@chenchan09 separate key storage

Comment on lines +339 to +342
val attester = SdkConfig.currentSigner?.publicKey
val schemaId = attester?.let {
AttestationProgram.deriveSchemaAddress(it, option.name).toBase58()
} ?: ""
Copy link
Contributor

Choose a reason for hiding this comment

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

@chenchan09 we shouldn't be creating schema

Copy link
Contributor

Copilot AI commented Mar 18, 2026

@mocolicious I've opened a new pull request, #45, to work on those changes. Once the pull request is ready, I'll request review from you.

mocolicious and others added 2 commits March 18, 2026 14:29
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI commented Mar 18, 2026

@mocolicious I've opened a new pull request, #46, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Contributor

Copilot AI commented Mar 18, 2026

@mocolicious I've opened a new pull request, #47, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 5 commits March 18, 2026 19:33
Co-authored-by: mocolicious <6373607+mocolicious@users.noreply.github.com>
…erve coroutine cancellation

Co-authored-by: mocolicious <6373607+mocolicious@users.noreply.github.com>
Update PR #44 description to reflect full scope of changes
Fix: catch Exception instead of Throwable in suspend APIs to preserve coroutine cancellation
Fix PDA derivation ignoring per-call signer override in attest/createSchema
Copy link
Contributor

@mocolicious mocolicious left a comment

Choose a reason for hiding this comment

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

@chenchan09 there are several issues here.

  1. We shouldnt be creating schema
  2. The provenance changes belong in the provenance module. This includes the references to the program.
  3. Key storage changes need to be merged separately so we need to remove from this PR so we can continue to work on those separately.
  4. Address issues highlighted by copilot

I'd say start with the API and come back to this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants