Skip to content

Commit

Permalink
Add the ACTION_SIGN_MESSAGE action to Seed Vault
Browse files Browse the repository at this point in the history
  • Loading branch information
sdlaver committed Sep 8, 2022
1 parent d4b7e62 commit 1f6ad7f
Show file tree
Hide file tree
Showing 22 changed files with 320 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ import com.solanamobile.fakewallet.ui.setaccountname.SetAccountNameDialogFragmen

class AccountListAdapter(
private val onSignTransaction: (Account) -> Unit,
private val onSignMessage: (Account) -> Unit,
private val onAccountNameUpdated: (Account, String) -> Unit
) : ListAdapter<Account, AccountListAdapter.AccountViewHolder>(AccountDiffCallback) {
class AccountViewHolder(
inner class AccountViewHolder(
private val binding: ItemAccountBinding,
private val onSignTransaction: (Account) -> Unit,
private val onAccountNameUpdated: (Account, String) -> Unit
) : ViewHolder(binding.root) {
private var account: Account? = null

Expand All @@ -30,6 +29,11 @@ class AccountListAdapter(
onSignTransaction(a)
}
}
binding.buttonSignMessage.setOnClickListener {
account?.let { a ->
onSignMessage(a)
}
}
val activity = binding.root.context as FragmentActivity
binding.buttonEditName.setOnClickListener {
SetAccountNameDialogFragment(
Expand Down Expand Up @@ -58,7 +62,7 @@ class AccountListAdapter(

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccountViewHolder {
val binding = ItemAccountBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return AccountViewHolder(binding, onSignTransaction, onAccountNameUpdated)
return AccountViewHolder(binding)
}

override fun onBindViewHolder(holder: AccountViewHolder, position: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ import com.solanamobile.fakewallet.databinding.ItemHasUnauthorizedSeedsBinding
class HasUnauthorizedSeedsAdapter(
private val onAuthorizeNewSeed: () -> Unit
) : ListAdapter<Boolean, HasUnauthorizedSeedsAdapter.HasUnauthorizedSeedsViewHolder>(HasUnauthorizedSeedsDiffCallback) {
class HasUnauthorizedSeedsViewHolder(
inner class HasUnauthorizedSeedsViewHolder(
private val binding: ItemHasUnauthorizedSeedsBinding,
private val onAuthorizeNewSeed: () -> Unit
) : RecyclerView.ViewHolder(binding.root) {
init {
binding.buttonAuthorizeSeed.setOnClickListener {
Expand All @@ -39,7 +38,7 @@ class HasUnauthorizedSeedsAdapter(

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HasUnauthorizedSeedsViewHolder {
val binding = ItemHasUnauthorizedSeedsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return HasUnauthorizedSeedsViewHolder(binding, onAuthorizeNewSeed)
return HasUnauthorizedSeedsViewHolder(binding)
}

override fun onBindViewHolder(holder: HasUnauthorizedSeedsViewHolder, position: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class MainActivity : AppCompatActivity() {
onSignTransaction = { seed, account ->
viewModel.signFakeTransaction(seed.authToken, account)
},
onSignMessage = { seed, account ->
viewModel.signFakeMessage(seed.authToken, account)
},
onAccountNameUpdated = { seed, account, name ->
viewModel.updateAccountName(
seed.authToken,
Expand All @@ -62,6 +65,9 @@ class MainActivity : AppCompatActivity() {
},
onSignMaxTransactionsWithMaxSignatures = { seed ->
viewModel.signMaxTransactionsWithMaxSignatures(seed.authToken)
},
onSignMaxMessagesWithMaxSignatures = { seed ->
viewModel.signMaxMessagesWithMaxSignatures(seed.authToken)
}
)
val remainingSeedsAdapter = HasUnauthorizedSeedsAdapter(
Expand Down Expand Up @@ -152,6 +158,12 @@ class MainActivity : AppCompatActivity() {
@Suppress("deprecation")
startActivityForResult(i, REQUEST_SIGN_TRANSACTIONS)
}
is ViewModelEvent.SignMessages -> {
val i = Wallet.signMessages(
event.authToken, event.messages)
@Suppress("deprecation")
startActivityForResult(i, REQUEST_SIGN_MESSAGES)
}
is ViewModelEvent.RequestPublicKeys -> {
val i = Wallet.requestPublicKeys(
event.authToken, event.derivationPaths)
Expand Down Expand Up @@ -189,6 +201,16 @@ class MainActivity : AppCompatActivity() {
viewModel.onSignTransactionsFailure(resultCode)
}
}
REQUEST_SIGN_MESSAGES -> {
try {
val result = Wallet.onSignMessagesResult(resultCode, data)
Log.d(TAG, "Message signed: signatures=$result")
viewModel.onSignMessagesSuccess(result)
} catch (e: Wallet.ActionFailedException) {
Log.e(TAG, "Message signing failed", e)
viewModel.onSignMessagesFailure(resultCode)
}
}
REQUEST_GET_PUBLIC_KEYS -> {
try {
val result = Wallet.onRequestPublicKeysResult(resultCode, data)
Expand All @@ -206,6 +228,7 @@ class MainActivity : AppCompatActivity() {
private val TAG = MainActivity::class.simpleName
private const val REQUEST_AUTHORIZE_SEED_ACCESS = 0
private const val REQUEST_SIGN_TRANSACTIONS = 1
private const val REQUEST_GET_PUBLIC_KEYS = 2
private const val REQUEST_SIGN_MESSAGES = 2
private const val REQUEST_GET_PUBLIC_KEYS = 3
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,48 @@ class MainViewModel(
showErrorMessage(resultCode)
}

fun signFakeMessage(@WalletContractV1.AuthToken authToken: Long, account: Account) {
val fakeMessage = byteArrayOf(1.toByte())
viewModelScope.launch {
val message = SigningRequest(fakeMessage, listOf(account.derivationPath))
_viewModelEvents.emit(
ViewModelEvent.SignMessages(authToken, arrayListOf(message))
)
}
}

fun signMaxMessagesWithMaxSignatures(@WalletContractV1.AuthToken authToken: Long) {
signMMessagesWithNSignatures(authToken, maxSigningRequests, maxRequestedSignatures)
}

private fun signMMessagesWithNSignatures(
@WalletContractV1.AuthToken authToken: Long,
m: Int,
n: Int
) {
val signingRequests = (0 until m).map { i ->
val derivationPaths = (0 until n).map { j ->
Bip44DerivationPath.newBuilder()
.setAccount(BipLevel(i * maxRequestedSignatures + j, true)).build().toUri()
}
SigningRequest(byteArrayOf(i.toByte()), derivationPaths)
}

viewModelScope.launch {
_viewModelEvents.emit(
ViewModelEvent.SignMessages(authToken, ArrayList(signingRequests))
)
}
}

fun onSignMessagesSuccess(signatures: List<SigningResponse>) {
showMessage("Messages signed successfully")
}

fun onSignMessagesFailure(resultCode: Int) {
showErrorMessage(resultCode)
}

fun requestPublicKeys(@WalletContractV1.AuthToken authToken: Long) {
requestMPublicKeys(authToken, maxRequestedPublicKeys)
}
Expand Down Expand Up @@ -397,6 +439,11 @@ sealed interface ViewModelEvent {
val transactions: ArrayList<SigningRequest>,
) : ViewModelEvent

data class SignMessages(
@WalletContractV1.AuthToken val authToken: Long,
val messages: ArrayList<SigningRequest>,
) : ViewModelEvent

data class RequestPublicKeys(
@WalletContractV1.AuthToken val authToken: Long,
val derivationPaths: ArrayList<Uri>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ class SeedListAdapter(
private val lifecycleScope: CoroutineScope,
private val implementationLimits: Flow<ImplementationLimits>,
private val onSignTransaction: (Seed, Account) -> Unit,
private val onSignMessage: (Seed, Account) -> Unit,
private val onAccountNameUpdated: (Seed, Account, String) -> Unit,
private val onDeauthorizeSeed: (Seed) -> Unit,
private val onRequestPublicKeys: (Seed) -> Unit,
private val onSignMaxTransactionsWithMaxSignatures: (Seed) -> Unit
private val onSignMaxTransactionsWithMaxSignatures: (Seed) -> Unit,
private val onSignMaxMessagesWithMaxSignatures: (Seed) -> Unit
) : ListAdapter<Seed, SeedListAdapter.SeedViewHolder>(SeedDiffCallback) {
data class ImplementationLimits(
val maxSigningRequests: Int,
Expand All @@ -42,6 +44,11 @@ class SeedListAdapter(
onSignTransaction(s, account)
}
},
onSignMessage = { account ->
seed?.let { s ->
onSignMessage(s, account)
}
},
onAccountNameUpdated = { account, name ->
seed?.let { s ->
onAccountNameUpdated(s, account, name)
Expand All @@ -58,6 +65,12 @@ class SeedListAdapter(
it.maxSigningRequests,
it.maxRequestedSignatures
)
binding.buttonSignMaxMessagesWithMaxSignatures.text =
binding.root.context.getString(
R.string.action_sign_max_messages_with_max_signatures,
it.maxSigningRequests,
it.maxRequestedSignatures
)
binding.buttonRequestPublicKeys.text = binding.root.context.getString(
R.string.action_request_public_keys,
it.firstRequestedPublicKey,
Expand All @@ -81,6 +94,11 @@ class SeedListAdapter(
onSignMaxTransactionsWithMaxSignatures(s)
}
}
binding.buttonSignMaxMessagesWithMaxSignatures.setOnClickListener {
seed?.let { s ->
onSignMaxMessagesWithMaxSignatures(s)
}
}
binding.recyclerviewAccounts.adapter = adapter
}

Expand Down
26 changes: 25 additions & 1 deletion fakewallet/src/main/res/layout/item_account.xml
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,38 @@
app:layout_constraintEnd_toEndOf="parent"
android:textSize="10pt" />

<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/label_sign"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/label_sign"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/button_sign_transaction"
app:layout_constraintBottom_toBottomOf="@id/button_sign_transaction"
android:textSize="10pt" />

<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_sign_transaction"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintTop_toBottomOf="@id/textview_derivation_path"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/label_sign"
android:text="@string/action_sign_transaction"
android:textSize="8pt" />

<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_sign_message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintTop_toTopOf="@id/button_sign_transaction"
app:layout_constraintBottom_toBottomOf="@id/button_sign_transaction"
app:layout_constraintStart_toEndOf="@id/button_sign_transaction"
app:layout_constraintEnd_toEndOf="parent"
android:text="@string/action_sign_message"
android:textSize="8pt" />

</androidx.constraintlayout.widget.ConstraintLayout>
10 changes: 9 additions & 1 deletion fakewallet/src/main/res/layout/item_seed.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,21 @@
app:layout_constraintTop_toBottomOf="@id/button_request_public_keys"
android:textSize="8pt" />

<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_sign_max_messages_with_max_signatures"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintTop_toBottomOf="@id/button_sign_max_transactions_with_max_signatures"
android:textSize="8pt" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview_accounts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:layout_marginStart="16dp"
app:layout_constraintTop_toBottomOf="@id/button_sign_max_transactions_with_max_signatures"
app:layout_constraintTop_toBottomOf="@id/button_sign_max_messages_with_max_signatures"
app:layout_constraintBottom_toBottomOf="parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:orientation="vertical" />
Expand Down
5 changes: 4 additions & 1 deletion fakewallet/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
<string name="label_account_name">Account:</string>
<string name="label_public_key">Public Key:</string>
<string name="label_derivation_path">Path:</string>
<string name="action_sign_transaction">Sign a fake transaction</string>
<string name="label_sign">Sign a:</string>
<string name="action_sign_transaction">transaction</string>
<string name="action_sign_message">message</string>
<string name="action_request_public_keys">Get public keys for %1$s - %2$s</string>
<string name="action_sign_max_transactions_with_max_signatures">Sign %1$d transactions x %2$d keys</string>
<string name="action_sign_max_messages_with_max_signatures">Sign %1$d messages x %2$d keys</string>
<plurals name="label_has_unauthorized_seeds">
<item quantity="other">(No seeds remaining to be authorized for PURPOSE_SIGN_SOLANA_TRANSACTION)</item>
<item quantity="one">Authorize another seed for PURPOSE_SIGN_SOLANA_TRANSACTION</item>
Expand Down
4 changes: 4 additions & 0 deletions impl/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@
<action android:name="com.solanamobile.seedvault.wallet.v1.ACTION_SIGN_TRANSACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="com.solanamobile.seedvault.wallet.v1.ACTION_SIGN_MESSAGE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="com.solanamobile.seedvault.wallet.v1.ACTION_GET_PUBLIC_KEY" />
<category android:name="android.intent.category.DEFAULT" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ class AuthorizeViewModel : ViewModel() {
_requests.emit(request)
}
}
WalletContractV1.ACTION_SIGN_TRANSACTION -> {
WalletContractV1.ACTION_SIGN_TRANSACTION,
WalletContractV1.ACTION_SIGN_MESSAGE -> {
val authToken = getAuthTokenFromIntent(callerIntent)
if (authToken == -1L) {
Log.e(TAG, "No or invalid auth token provided; aborting...")
Expand All @@ -66,11 +67,15 @@ class AuthorizeViewModel : ViewModel() {
val signingRequests = callerIntent.getParcelableArrayListExtra<SigningRequest>(WalletContractV1.EXTRA_SIGNING_REQUEST)
if (signingRequests == null || signingRequests.isEmpty()) {
Log.e(TAG, "No or empty signing requests provided; aborting...")
completeAuthorizationWithError(WalletContractV1.RESULT_INVALID_TRANSACTION)
completeAuthorizationWithError(WalletContractV1.RESULT_INVALID_PAYLOAD)
return
}
val type = if (callerIntent.action == WalletContractV1.ACTION_SIGN_TRANSACTION)
AuthorizeRequestType.Signature.Type.Transaction
else
AuthorizeRequestType.Signature.Type.Message
startAuthorization()
val request = AuthorizeRequest(AuthorizeRequestType.Transaction(authToken, signingRequests), callerActivity, callerUid)
val request = AuthorizeRequest(AuthorizeRequestType.Signature(type, authToken, signingRequests), callerActivity, callerUid)
cachedRequest = request
viewModelScope.launch {
_requests.emit(request)
Expand Down Expand Up @@ -173,10 +178,13 @@ sealed interface AuthorizeRequestType {
val seedId: Long? = null
) : AuthorizeRequestType

data class Transaction(
data class Signature(
val type: Type,
@WalletContractV1.AuthToken val authToken: Long,
val transactions: List<SigningRequest>,
) : AuthorizeRequestType
) : AuthorizeRequestType {
enum class Type { Transaction, Message }
}

data class PublicKey(
@WalletContractV1.AuthToken val authToken: Long,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class AuthorizeFragment : Fragment() {
binding.labelAuthorizationType.setText(when (uiState.authorizationType) {
AuthorizeUiState.AuthorizationType.SEED -> R.string.label_authorize_seed
AuthorizeUiState.AuthorizationType.TRANSACTION -> R.string.label_authorize_transaction
AuthorizeUiState.AuthorizationType.MESSAGE -> R.string.label_authorize_message
AuthorizeUiState.AuthorizationType.PUBLIC_KEY -> R.string.label_authorize_public_key
null -> android.R.string.unknownName
})
Expand Down
Loading

0 comments on commit 1f6ad7f

Please sign in to comment.