blob: 5d146a10db3f3df605127823f64e3ded5c94e4f8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tuweni.jsonrpc
import com.fasterxml.jackson.databind.ObjectMapper
import io.vertx.core.Vertx
import io.vertx.core.buffer.Buffer
import io.vertx.ext.web.client.WebClient
import kotlinx.coroutines.CompletableDeferred
import org.apache.tuweni.eth.Address
import org.apache.tuweni.eth.Transaction
import org.apache.tuweni.units.bigints.UInt256
import java.io.Closeable
val mapper = ObjectMapper()
/**
* JSON-RPC client to send requests to an Ethereum client.
*/
class JSONRPCClient(
vertx: Vertx,
val serverPort: Int,
val serverHost: String
) : Closeable {
val client = WebClient.create(vertx)
/**
* Sends a signed transaction to the Ethereum network.
* @param tx the transaction object to send
* @return the hash of the transaction, or an empty string if the hash is not available yet.
* @throws ClientRequestException if the request is rejected
* @throws ConnectException if it cannot dial the remote client
*/
suspend fun sendRawTransaction(tx: Transaction): String {
val body = mapOf(
Pair("jsonrpc", "2.0"),
Pair("method", "eth_sendRawTransaction"),
Pair("id", 1),
Pair("params", listOf(tx.toBytes().toHexString()))
)
val deferred = CompletableDeferred<String>()
client.post(serverPort, serverHost, "/")
.putHeader("Content-Type", "application/json")
.sendBuffer(Buffer.buffer(mapper.writeValueAsBytes(body))) { response ->
if (response.failed()) {
deferred.completeExceptionally(response.cause())
} else {
val jsonResponse = response.result().bodyAsJsonObject()
if (jsonResponse.containsKey("error")) {
val err = jsonResponse.getJsonObject("error")
val errorMessage = "Code ${err.getInteger("code")}: ${err.getString("message")}"
deferred.completeExceptionally(ClientRequestException(errorMessage))
} else {
deferred.complete(jsonResponse.getString("result"))
}
}
}
return deferred.await()
}
/**
* Gets the account balance.
* @param tx the transaction object to send
* @return the hash of the transaction, or an empty string if the hash is not available yet.
* @throws ClientRequestException if the request is rejected
* @throws ConnectException if it cannot dial the remote client
*/
suspend fun getBalance_latest(address: Address): UInt256 {
val body = mapOf(
Pair("jsonrpc", "2.0"),
Pair("method", "eth_getBalance"),
Pair("id", 1),
Pair("params", listOf(address.toHexString(), "latest"))
)
val deferred = CompletableDeferred<UInt256>()
client.post(serverPort, serverHost, "/")
.putHeader("Content-Type", "application/json")
.sendBuffer(Buffer.buffer(mapper.writeValueAsBytes(body))) { response ->
if (response.failed()) {
deferred.completeExceptionally(response.cause())
} else {
val jsonResponse = response.result().bodyAsJsonObject()
deferred.complete(UInt256.fromHexString(jsonResponse.getString("result")))
}
}
return deferred.await()
}
/**
* Gets the number of transactions sent from an address.
* @param tx the transaction object to send
* @return the hash of the transaction, or an empty string if the hash is not available yet.
* @throws ClientRequestException if the request is rejected
* @throws ConnectException if it cannot dial the remote client
*/
suspend fun getTransactionCount_latest(address: Address): UInt256 {
val body = mapOf(
Pair("jsonrpc", "2.0"),
Pair("method", "eth_getTransactionCount"),
Pair("id", 1),
Pair("params", listOf(address.toHexString(), "latest"))
)
val deferred = CompletableDeferred<UInt256>()
client.post(serverPort, serverHost, "/")
.putHeader("Content-Type", "application/json")
.sendBuffer(Buffer.buffer(mapper.writeValueAsBytes(body))) { response ->
if (response.failed()) {
deferred.completeExceptionally(response.cause())
} else {
val jsonResponse = response.result().bodyAsJsonObject()
deferred.complete(UInt256.fromHexString(jsonResponse.getString("result")))
}
}
return deferred.await()
}
override fun close() {
client.close()
}
}