Skip to content

Commit

Permalink
Add eth_accounts and eth_sign RPC methods (#149)
Browse files Browse the repository at this point in the history
* added eth_accounts and eth_sign methods to rpc provider

* added eth_accounts and eth_sign methods to rpc provider

* removed unnecessary blank line

* bug fix in BaseEthereumRPC.accounts

* bug fix in BaseEthereumRPC.sign
  • Loading branch information
krisbitney authored Aug 15, 2023
1 parent 8dd0457 commit b9c1893
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 6 deletions.
24 changes: 19 additions & 5 deletions rpc/src/main/kotlin/org/kethereum/rpc/BaseEthereumRPC.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ import com.squareup.moshi.Moshi
import org.kethereum.extensions.BigIntegerAdapter
import org.kethereum.extensions.hexToBigInteger
import org.kethereum.extensions.toHexString
import org.kethereum.model.Address
import org.kethereum.model.ByteCode
import org.kethereum.model.ChainId
import org.kethereum.model.Transaction
import org.kethereum.model.*
import org.kethereum.rpc.model.*
import org.kethereum.rpc.model.BaseResponse
import org.kethereum.rpc.model.BlockInformationResponse
import org.kethereum.rpc.model.FeeHistoryResponse
import org.kethereum.rpc.model.StringResultResponse
import org.kethereum.rpc.model.TransactionResponse
import org.komputing.khex.extensions.hexToByteArray
import org.komputing.khex.extensions.toHexString
import org.komputing.khex.model.HexString
import java.io.IOException
import java.math.BigInteger
Expand All @@ -34,6 +32,8 @@ open class BaseEthereumRPC(private val transport: RPCTransport) : EthereumRPC {

private val feeHistoryAdapter: JsonAdapter<FeeHistoryResponse> = moshi.adapter(FeeHistoryResponse::class.java)

private val stringArrayAdapter: JsonAdapter<StringArrayResponse> = moshi.adapter(StringArrayResponse::class.java)

override fun stringCall(function: String, params: String): StringResultResponse? {
return transport.call(function, params)?.let { string -> stringResultAdapter.fromJsonNoThrow(string) }
}
Expand Down Expand Up @@ -61,6 +61,21 @@ open class BaseEthereumRPC(private val transport: RPCTransport) : EthereumRPC {
@Throws(EthereumRPCException::class)
override fun chainId() = stringCall("eth_chainId")?.getBigIntegerFromStringResult()?.let { ChainId(it) }

@Throws(EthereumRPCException::class)
override fun accounts(): List<Address>? {
val call = transport.call("eth_accounts", "")?.let { string ->
stringArrayAdapter.fromJson(string)
}
return call?.result?.map { Address(it) }
}

@Throws(EthereumRPCException::class)
override fun sign(address: Address, message: ByteArray): SignatureData? =
stringCall("eth_sign", "\"${address.hex}\",\"${message.toHexString()}\"")
?.throwOrString()
?.let { SignatureData.fromHex(it) }
?.let { SignatureData(r = it.r, s = it.s, v = it.v.plus(27.toBigInteger())) }

@Throws(EthereumRPCException::class)
override fun getStorageAt(address: Address, position: String, block: String) =
stringCall("eth_getStorageAt", "\"${address.hex}\",\"$position\",\"$block\"")?.throwOrString()?.let { HexString(it) }
Expand Down Expand Up @@ -98,6 +113,5 @@ private fun StringResultResponse.throwOrString() = throwOnError().result!!
@Throws(EthereumRPCException::class)
private fun <T : BaseResponse> T.throwOnError() = error?.let { throw EthereumRPCException(it.message, it.code) } ?: this


@Throws(EthereumRPCException::class)
private fun StringResultResponse.getBigIntegerFromStringResult() = HexString(throwOrString()).hexToBigInteger()
3 changes: 2 additions & 1 deletion rpc/src/main/kotlin/org/kethereum/rpc/EthereumRPC.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ interface EthereumRPC {

fun getBlockByNumber(number: BigInteger): BlockInformation?
fun getTransactionByHash(hash: String): SignedTransaction?

fun stringCall(function: String, params: String = ""): StringResultResponse?
fun sendRawTransaction(data: String): String?
fun blockNumber(): BigInteger?
fun call(transaction: Transaction, block: String = "latest"): HexString?
fun gasPrice(): BigInteger?
fun clientVersion(): String?
fun chainId(): ChainId?
fun accounts(): List<Address>?
fun sign(address: Address, message: ByteArray): SignatureData?
fun getStorageAt(address: Address, position: String, block: String = "latest"): HexString?
fun getTransactionCount(address: Address, block: String = "latest"): BigInteger?
fun getCode(address: Address, block: String = "latest"): ByteCode?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.kethereum.rpc.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
internal data class StringArrayResponse(val result: List<String>) : BaseResponse()
25 changes: 25 additions & 0 deletions rpc/src/test/kotlin/org/kethereum/rpc/TheEthereumRPC.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.junit.Before
import org.junit.jupiter.api.Test
import org.kethereum.extensions.hexToBigInteger
import org.kethereum.model.Address
import org.kethereum.model.SignatureData
import org.kethereum.model.SignedTransaction
import org.kethereum.rpc.model.BlockInformation
import org.komputing.khex.extensions.hexToByteArray
Expand All @@ -16,6 +17,7 @@ import java.math.BigInteger
import java.math.BigInteger.TEN
import java.math.BigInteger.ZERO
import kotlin.test.assertFails
import kotlin.test.assertNotEquals

class TheEthereumRPC {

Expand Down Expand Up @@ -252,5 +254,28 @@ class TheEthereumRPC {
.isEqualTo(HexString("0x76d4be3d62b9e6e6bb8c494c3228f4df31b5c20d8f892fe1d9d35f07afab3d73").hexToBigInteger())
}
@Test
fun accountsWorks() {
//language=JSON
val response = "{\"jsonrpc\":\"2.0\",\"id\":83,\"result\":[\"0x694b63b2e053a0c93074795a1025869576389b70\"]}\n"
server.enqueue(MockResponse().setBody(response))
val expected = listOf(Address("0x694b63b2e053a0c93074795a1025869576389b70"))
assertThat(tested.accounts()).isEqualTo(expected)
}
@Test
fun signWorks() {
//language=JSON
val response = "{\"jsonrpc\":\"2.0\",\"id\":83,\"result\":\"0xa4708243bf782c6769ed04d83e7192dbcf4fc131aa54fde9d889d8633ae39dab03d7babd2392982dff6bc20177f7d887e27e50848c851320ee89c6c63d18ca761c\"}\n"
server.enqueue(MockResponse().setBody(response))
val result: SignatureData? = tested.sign(
address = Address("0x694b63b2e053a0c93074795a1025869576389b70"),
message = "Hello World".encodeToByteArray()
)
assertNotEquals(ZERO, result?.r)
assertNotEquals(ZERO, result?.s)
assertNotEquals(ZERO, result?.v)
}
}

0 comments on commit b9c1893

Please sign in to comment.