Implement two way communication with Dero node.

- Implemented basic two way communication with the Dero node. The Bridge is listening for Dero node updates and requests the Block Height when receiving a Height update from the node.
- Renamed Bridge to StargateBridge and added basic documentation.
- Created model for DeroNode instances. We use only one node for now, but will add more down the line.
This commit is contained in:
Severiano Jaramillo 2024-03-28 19:55:52 -07:00
parent 8b9727082a
commit 8189b590d3
4 changed files with 75 additions and 37 deletions

View file

@ -5,19 +5,19 @@ import cafe.adriel.voyager.core.model.screenModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.launch
import net.agorise.shared.stargate.Bridge
import net.agorise.shared.stargate.StargateBridge
class ImportAccountScreenModel : ScreenModel {
private val bridge = Bridge()
private val stargateBridge = StargateBridge()
init {
screenModelScope.launch(Dispatchers.IO) {
bridge.start()
stargateBridge.start()
}
}
override fun onDispose() {
super.onDispose()
bridge.stop()
stargateBridge.stop()
}
}

View file

@ -1,33 +0,0 @@
package net.agorise.shared.stargate
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
import io.ktor.client.plugins.websocket.WebSockets
import io.ktor.client.plugins.websocket.webSocket
import io.ktor.http.HttpMethod
import io.ktor.websocket.Frame
import io.ktor.websocket.readText
import kotlinx.coroutines.flow.receiveAsFlow
class Bridge {
private val client: HttpClient = HttpClient(CIO) { install(WebSockets) }
suspend fun start() {
client.webSocket(method = HttpMethod.Get, host = "node.derofoundation.org", port = 11012, path = "/ws") {
outputMessages()
}
}
fun stop() {
client.close()
}
private suspend fun DefaultClientWebSocketSession.outputMessages() {
incoming.receiveAsFlow().collect { message ->
(message as? Frame.Text)?.let { textFrame ->
println(textFrame.readText())
}
}
}
}

View file

@ -0,0 +1,14 @@
package net.agorise.shared.stargate
/**
* Represents a Dero node
*
* @param host The host or base url of the node
* @param port The port of the node
* @param path The path of the node
*/
data class DeroNode(
val host: String,
val port: Int,
val path: String,
)

View file

@ -0,0 +1,57 @@
package net.agorise.shared.stargate
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
import io.ktor.client.plugins.websocket.WebSockets
import io.ktor.client.plugins.websocket.webSocket
import io.ktor.websocket.Frame
import io.ktor.websocket.readText
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
/**
* Bridge to communicate with the DERO Stargate RPC API: https://docs.dero.io/developer/RPC.html
*/
class StargateBridge {
private val foundationNode = DeroNode(host = "node.derofoundation.org", port = 11012, path = "/ws")
private val client: HttpClient = HttpClient(CIO) { install(WebSockets) }
private val rendezvousChannel = Channel<Boolean>()
private val blockCountRequest = "{ \"jsonrpc\": \"2.0\", \"id\": \"1\", \"method\": \"DERO.GetBlockCount\" }"
suspend fun start() {
val node = foundationNode
client.webSocket(host = node.host, port = node.port, path = node.path) {
writeMessages()
readMessages()
}
}
fun stop() {
client.close()
}
private suspend fun DefaultClientWebSocketSession.readMessages() {
incoming.receiveAsFlow().collect { message ->
(message as? Frame.Text)?.let { textFrame ->
val text = textFrame.readText()
println(text)
if (text.contains("\"method\":\"Block\"")) {
rendezvousChannel.send(true)
}
}
}
}
private suspend fun DefaultClientWebSocketSession.writeMessages() {
rendezvousChannel.receiveAsFlow().onEach {
println("Prepared to send...")
outgoing.send(Frame.Text(blockCountRequest))
}.launchIn(this)
}
}