add all stubs for handling transaction signing

This commit is contained in:
Michele Balistreri 2019-11-08 15:42:10 +03:00
parent 3409d8a041
commit a8a6b6c054
No known key found for this signature in database
GPG Key ID: E9567DA33A4F791A
3 changed files with 83 additions and 59 deletions

View File

@ -30,6 +30,16 @@ android {
kotlinOptions {
jvmTarget = "1.8"
}
packagingOptions {
exclude 'META-INF/main.kotlin_module'
}
packagingOptions {
pickFirst 'META-INF/kotlinx-io.kotlin_module'
pickFirst 'META-INF/atomicfu.kotlin_module'
pickFirst 'META-INF/kotlinx-coroutines-io.kotlin_module'
}
}
dependencies {
@ -55,6 +65,9 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.2.1'
implementation 'com.squareup.moshi:moshi-kotlin:1.9.1'
implementation "com.github.komputing.kethereum:extensions:$kethereum_version"
implementation "com.github.komputing.kethereum:model:$kethereum_version"
implementation "com.github.komputing.kethereum:functions:$kethereum_version"
implementation "com.github.komputing.kethereum:rpc:$kethereum_version"
implementation "com.github.komputing.kethereum:rlp:$kethereum_version"
implementation 'com.github.komputing:khex:0.6'
testImplementation 'junit:junit:4.12'

View File

@ -7,7 +7,7 @@ import im.status.keycard.android.NFCCardManager
import im.status.keycard.connect.card.*
import im.status.keycard.connect.data.PINCache
import im.status.keycard.connect.data.PairingManager
import im.status.keycard.connect.walletconnect.WalletConnect
import im.status.keycard.connect.net.WalletConnect
@SuppressLint("StaticFieldLeak")
object Registry {

View File

@ -1,4 +1,4 @@
package im.status.keycard.connect.walletconnect
package im.status.keycard.connect.net
import android.app.Activity
import android.content.Intent
@ -17,6 +17,8 @@ import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import org.bouncycastle.jcajce.provider.digest.Keccak
import org.kethereum.model.Transaction
import org.kethereum.model.createEmptyTransaction
import org.walletconnect.Session
import org.walletconnect.Session.Config.Companion.fromWCUri
import org.walletconnect.impls.*
@ -54,57 +56,61 @@ class WalletConnect : ExportKeyCommand.Listener, SignCommand.Listener {
when (call) {
is Session.MethodCall.SessionRequest -> Registry.scriptExecutor.runScript(scriptWithAuthentication().plus(ExportKeyCommand(Registry.walletConnect, bip39Path)))
is Session.MethodCall.SignMessage -> signText(call.id, call.message)
is Session.MethodCall.SendTransaction -> session?.rejectRequest(call.id, 1L, "Not implemented yet")
is Session.MethodCall.SendTransaction -> signTransaction(call.id, toTransaction(call), false)
is Session.MethodCall.Custom -> onCustomCall(call)
}
}
}
// would be more elegant with a single inline generic function with reified type, but apparently in Kotlin 1.3.50 code generation fails if I make this function inline.
// TODO: check newer version of Kotlin
private fun runOnValidParam(call: Session.MethodCall.Custom, body: (String) -> Unit) {
val param = call.params?.firstOrNull()
if (param is String) {
body(param)
} else {
session?.rejectRequest(call.id, 1L, "Invalid params")
}
}
private fun runOnValidParam(call: Session.MethodCall.Custom, index: Int, body: (Map<*, *>) -> Unit) {
val param = call.params?.getOrNull(index)
if (param is Map<*, *>) {
body(param)
} else {
session?.rejectRequest(call.id, 1L, "Invalid params")
}
}
private fun onCustomCall(call: Session.MethodCall.Custom) {
when(call.method) {
"personal_sign" -> {
val message = call.params?.first()
if (message is String) {
signText(call.id, message)
} else {
session?.rejectRequest(call.id, 1L, "Invalid params")
}
}
"eth_signTypedData" -> {
val message = call.params?.get(1)
if (message is Map<*, *>) {
@Suppress("UNCHECKED_CAST")
signTypedData(call.id, message as Map<String, String>)
} else {
session?.rejectRequest(call.id, 1L, "Invalid params")
}
}
"eth_signTransaction" -> {
session?.rejectRequest(call.id, 1L, "Not implemented yet")
}
"eth_sendRawTransaction" -> {
val signedTx = call.params?.first()
if (signedTx is String) {
relayTX(call.id, signedTx)
} else {
session?.rejectRequest(call.id, 1L, "Invalid params")
}
}
"personal_sign" -> runOnValidParam(call) { signText(call.id, it) }
"eth_signTypedData" -> { runOnValidParam(call, 1) { @Suppress("UNCHECKED_CAST") signTypedData(call.id, it as Map<String, String>) } }
"eth_signTransaction" -> { runOnValidParam(call, 0) { signTransaction(call.id, toTransaction(toSendTransaction(call.id, it)), false)} }
"eth_sendRawTransaction" -> { runOnValidParam(call) { relayTX(call.id, it) } }
else -> session?.rejectRequest(call.id, 1L, "Not implemented")
}
}
private fun relayTX(id: Long, signedTx: Any) {
// Ask confirmation and forward tx as-is through Infura
println(signedTx)
private fun toSendTransaction(id: Long, data: Map<*, *>): Session.MethodCall.SendTransaction {
val from = data["from"] as? String ?: throw IllegalArgumentException("from key missing")
val to = data["to"] as? String ?: throw IllegalArgumentException("to key missing")
val nonce = data["nonce"] as? String ?: (data["nonce"] as? Double)?.toLong()?.toString()
val gasPrice = data["gasPrice"] as? String
val gasLimit = data["gasLimit"] as? String
val value = data["value"] as? String ?: throw IllegalArgumentException("value key missing")
val txData = data["data"] as? String ?: throw IllegalArgumentException("data key missing")
return Session.MethodCall.SendTransaction(id, from, to, nonce, gasPrice, gasLimit, value, txData)
}
private fun toTransaction(tx: Session.MethodCall.SendTransaction): Transaction {
return createEmptyTransaction()
}
}
private fun relayTX(id: Long, signedTx: String) {
requestId = id
session?.rejectRequest(id, 1L, "Not implemented yet")
}
@ -127,15 +133,20 @@ class WalletConnect : ExportKeyCommand.Listener, SignCommand.Listener {
}
private fun signTypedData(id: Long, message: Map<String, String>) {
requestId = id
session?.rejectRequest(id, 1L, "Not implemented yet")
}
private fun signTransaction(id: Long, tx: Transaction, send: Boolean) {
requestId = id
session?.rejectRequest(id, 1L, "Not implemented yet")
}
private fun nop(@Suppress("UNUSED_PARAMETER") data: Intent?) { }
fun onUserInteractionReturned(resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
action.invoke(data)
action(data)
} else {
session?.rejectRequest(requestId, -1, "Rejected by user")
}