mirror of
https://github.com/fluencelabs/tendermint-demo
synced 2025-03-16 00:00:49 +00:00
entry class refactored, comments added
This commit is contained in:
parent
08dc8574f5
commit
2a1d836953
3
.gitignore
vendored
3
.gitignore
vendored
@ -99,3 +99,6 @@ ENV/
|
|||||||
|
|
||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
|
|
||||||
|
# IntelliJ Idea
|
||||||
|
.idea
|
@ -3,18 +3,23 @@ package kvstore
|
|||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
import com.github.jtendermint.jabci.api._
|
import com.github.jtendermint.jabci.api._
|
||||||
import com.github.jtendermint.jabci.socket.TSocket
|
|
||||||
import com.github.jtendermint.jabci.types.{ResponseCheckTx, _}
|
import com.github.jtendermint.jabci.types.{ResponseCheckTx, _}
|
||||||
import com.google.protobuf.ByteString
|
import com.google.protobuf.ByteString
|
||||||
|
|
||||||
import scala.collection.mutable.ArrayBuffer
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
object KVStoreServerRunner extends IDeliverTx with ICheckTx with ICommit with IQuery {
|
/**
|
||||||
|
* Tendermint establishes 3 socket connections with the app:
|
||||||
def main(args: Array[String]): Unit = {
|
* - Mempool for CheckTx
|
||||||
KVStoreServerRunner.start()
|
* - Consensus for DeliverTx and Commit
|
||||||
}
|
* - Info for Query
|
||||||
|
*
|
||||||
|
* According to specification the app maintains separate in-memory states for every connections:
|
||||||
|
* – [[ABCIHandler.consensusRoot]]: the latest state modified with every successful transaction on DeliverTx
|
||||||
|
* – [[ABCIHandler.storage]]: array of committed snapshots for Info connection
|
||||||
|
* – [[ABCIHandler.mempoolRoot]]: the latest committed snapshot for Mempool connection (not used currently)
|
||||||
|
*/
|
||||||
|
object ABCIHandler extends IDeliverTx with ICheckTx with ICommit with IQuery {
|
||||||
private val storage: ArrayBuffer[Node] = new ArrayBuffer[Node]()
|
private val storage: ArrayBuffer[Node] = new ArrayBuffer[Node]()
|
||||||
|
|
||||||
private var consensusRoot: Node = Node.emptyNode
|
private var consensusRoot: Node = Node.emptyNode
|
||||||
@ -22,20 +27,6 @@ object KVStoreServerRunner extends IDeliverTx with ICheckTx with ICommit with IQ
|
|||||||
@volatile
|
@volatile
|
||||||
private var mempoolRoot: Node = Node.emptyNode
|
private var mempoolRoot: Node = Node.emptyNode
|
||||||
|
|
||||||
def start(): Unit = {
|
|
||||||
System.out.println("starting KVStore")
|
|
||||||
val socket = new TSocket
|
|
||||||
|
|
||||||
socket.registerListener(this)
|
|
||||||
|
|
||||||
val t = new Thread(() => socket.start(46658))
|
|
||||||
t.setName("KVStore server Main Thread")
|
|
||||||
t.start()
|
|
||||||
while (true) {
|
|
||||||
Thread.sleep(1000L)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override def receivedDeliverTx(req: RequestDeliverTx): ResponseDeliverTx = {
|
override def receivedDeliverTx(req: RequestDeliverTx): ResponseDeliverTx = {
|
||||||
val tx = req.getTx.toStringUtf8
|
val tx = req.getTx.toStringUtf8
|
||||||
val txPayload = tx.split("###")(0)
|
val txPayload = tx.split("###")(0)
|
||||||
@ -72,7 +63,8 @@ object KVStoreServerRunner extends IDeliverTx with ICheckTx with ICommit with IQ
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def requestCheckTx(req: RequestCheckTx): ResponseCheckTx = {
|
override def requestCheckTx(req: RequestCheckTx): ResponseCheckTx = {
|
||||||
// check mempoolRoot
|
// no transaction processing logic currently
|
||||||
|
// mempoolRoot is intended to be used here as the latest committed state
|
||||||
|
|
||||||
val tx = req.getTx.toStringUtf8
|
val tx = req.getTx.toStringUtf8
|
||||||
if (tx == "BAD_CHECK") {
|
if (tx == "BAD_CHECK") {
|
||||||
@ -104,28 +96,18 @@ object KVStoreServerRunner extends IDeliverTx with ICheckTx with ICommit with IQ
|
|||||||
val lsPattern = "ls:(.*)".r
|
val lsPattern = "ls:(.*)".r
|
||||||
|
|
||||||
val query = req.getData.toStringUtf8
|
val query = req.getData.toStringUtf8
|
||||||
query match {
|
val (key, result) = query match {
|
||||||
case getPattern(key) =>
|
case getPattern(key) => (key, root.getValue(key))
|
||||||
val result = root.getValue(key)
|
case lsPattern(key) => (key, root.listChildren(key).map(x => x.mkString(" ")))
|
||||||
val proof = if (result.isDefined && req.getProve) twoLevelMerkleListToString(root.getProof(key)) else ""
|
|
||||||
|
|
||||||
ResponseQuery.newBuilder.setCode(CodeType.OK)
|
|
||||||
.setValue(ByteString.copyFromUtf8(result.getOrElse("")))
|
|
||||||
.setProof(ByteString.copyFromUtf8(proof))
|
|
||||||
.build
|
|
||||||
case lsPattern(key) =>
|
|
||||||
val result = root.listChildren(key)
|
|
||||||
val proof = if (result.isDefined && req.getProve) twoLevelMerkleListToString(root.getProof(key)) else ""
|
|
||||||
|
|
||||||
ResponseQuery.newBuilder.setCode(CodeType.OK)
|
|
||||||
.setValue(ByteString.copyFromUtf8(result.map(x => x.mkString(" ")).getOrElse("")))
|
|
||||||
.setProof(ByteString.copyFromUtf8(proof))
|
|
||||||
.build
|
|
||||||
case _ =>
|
case _ =>
|
||||||
ResponseQuery.newBuilder.setCode(CodeType.BAD).setLog("Invalid query path. Got " + query).build
|
return ResponseQuery.newBuilder.setCode(CodeType.BAD).setLog("Invalid query path. Got " + query).build
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private def twoLevelMerkleListToString(list: List[List[MerkleHash]]): String =
|
val proof = if (result.isDefined && req.getProve) MerkleUtil.twoLevelMerkleListToString(root.getProof(key)) else ""
|
||||||
list.map(level => level.map(MerkleUtil.merkleHashToHex).mkString(" ")).mkString(", ")
|
|
||||||
|
ResponseQuery.newBuilder.setCode(CodeType.OK)
|
||||||
|
.setValue(ByteString.copyFromUtf8(result.getOrElse("")))
|
||||||
|
.setProof(ByteString.copyFromUtf8(proof))
|
||||||
|
.build
|
||||||
|
}
|
||||||
}
|
}
|
@ -19,4 +19,7 @@ object MerkleUtil {
|
|||||||
|
|
||||||
def merkleHashToHex(merkleHash: MerkleHash): String =
|
def merkleHashToHex(merkleHash: MerkleHash): String =
|
||||||
merkleHash.map("%02x".format(_)).mkString
|
merkleHash.map("%02x".format(_)).mkString
|
||||||
|
|
||||||
|
def twoLevelMerkleListToString(list: List[List[MerkleHash]]): String =
|
||||||
|
list.map(level => level.map(MerkleUtil.merkleHashToHex).mkString(" ")).mkString(", ")
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,15 @@ import kvstore.MerkleUtil._
|
|||||||
import scala.collection.immutable.HashMap
|
import scala.collection.immutable.HashMap
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node
|
||||||
|
*
|
||||||
|
* merkleHash set to None when branch changed. Later None merkle hashes recalculated when [[Node.merkelize]] invoked
|
||||||
|
*
|
||||||
|
* @param children child nodes
|
||||||
|
* @param value assigned value, if exists
|
||||||
|
* @param merkleHash Merkle hash of a node's subtree, if calculated
|
||||||
|
*/
|
||||||
case class Node(children: NodeStorage, value: Option[String], merkleHash: Option[MerkleHash]) {
|
case class Node(children: NodeStorage, value: Option[String], merkleHash: Option[MerkleHash]) {
|
||||||
def merkelize(): Node =
|
def merkelize(): Node =
|
||||||
if (merkleHash.isDefined)
|
if (merkleHash.isDefined)
|
||||||
@ -34,6 +43,9 @@ case class Node(children: NodeStorage, value: Option[String], merkleHash: Option
|
|||||||
|
|
||||||
key match {
|
key match {
|
||||||
case rangeKeyValuePattern(rangeStartStr, rangeEndStr, keyPattern) =>
|
case rangeKeyValuePattern(rangeStartStr, rangeEndStr, keyPattern) =>
|
||||||
|
// range pattern allows to set multiple keys in single transaction
|
||||||
|
// range defined by starting and ending index, key pattern may contains hexadecimal digits of current index
|
||||||
|
|
||||||
val rangeStart = rangeStartStr.toInt
|
val rangeStart = rangeStartStr.toInt
|
||||||
val rangeEnd = rangeEndStr.toInt
|
val rangeEnd = rangeEndStr.toInt
|
||||||
System.out.println(s"setting range from=$rangeStart to=$rangeEnd keyPattern=$keyPattern valuePattern=$value")
|
System.out.println(s"setting range from=$rangeStart to=$rangeEnd keyPattern=$keyPattern valuePattern=$value")
|
||||||
|
25
tmdemoapp/src/main/scala/kvstore/ServerRunner.scala
Normal file
25
tmdemoapp/src/main/scala/kvstore/ServerRunner.scala
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package kvstore
|
||||||
|
|
||||||
|
import com.github.jtendermint.jabci.socket.TSocket
|
||||||
|
|
||||||
|
object ServerRunner {
|
||||||
|
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
val port = if (args.length > 0) args(0).toInt else 46658
|
||||||
|
ServerRunner.start(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
def start(port: Int): Unit = {
|
||||||
|
System.out.println("starting KVStore")
|
||||||
|
val socket = new TSocket
|
||||||
|
|
||||||
|
socket.registerListener(ABCIHandler)
|
||||||
|
|
||||||
|
val t = new Thread(() => socket.start(port))
|
||||||
|
t.setName("KVStore server Main Thread")
|
||||||
|
t.start()
|
||||||
|
while (true) {
|
||||||
|
Thread.sleep(1000L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user