Skip to content

Instantly share code, notes, and snippets.

@OlegJakushkin
Forked from robivirt/NodeDriver.kt
Last active June 14, 2019 09:06
Show Gist options
  • Save OlegJakushkin/d62c66b6e738a87438d48df4268c13b8 to your computer and use it in GitHub Desktop.
Save OlegJakushkin/d62c66b6e738a87438d48df4268c13b8 to your computer and use it in GitHub Desktop.
package com.template
import co.paralleluniverse.fibers.Suspendable
import freemarker.cache.ClassTemplateLoader
import freemarker.template.Configuration
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.contracts.*
import net.corda.core.flows.*
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.randomOrNull
import net.corda.core.messaging.startFlow
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.getOrThrow
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import net.corda.testing.node.User
import spark.ModelAndView
import spark.Service
import spark.template.freemarker.FreeMarkerEngine
import java.util.*
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Table
class BotContract : Contract {
companion object {
const val ID = "com.template.BotContract"
}
class QAOperation : CommandData
override fun verify(tx: LedgerTransaction) {
requireThat {}
}
}
class UserContract : Contract {
companion object {
const val ID = "com.template.UserContract"
}
class QuestionRequest : CommandData
override fun verify(tx: LedgerTransaction) {
requireThat {}
}
}
object NodeDriver {
object UserSchema
object UserSchemaV1 : MappedSchema(
schemaFamily = UserSchema.javaClass,
version = 1,
mappedTypes = listOf(PersistentTest::class.java)
) {
@Entity
@Table(name = "user_states")
class PersistentTest(
@Column(name = "lender")
var lenderName: String,
@Column(name = "borrower")
var borrowerName: String,
@Column(name = "Question")
var Question: String,
@Column(name = "Answer")
var Answer: String
) : PersistentState() {
// Default constructor required by hibernate.
constructor() : this(
"",
"",
"",
""
)
}
}
object QASchema
object QASchemaV1 : MappedSchema(
schemaFamily = QASchema.javaClass,
version = 1,
mappedTypes = listOf(PersistentTest::class.java)
) {
@Entity
@Table(name = "test_states")
class PersistentTest(
@Column(name = "lender")
var lenderName: String,
@Column(name = "borrower")
var borrowerName: String,
@Column(name = "Question")
var Question: String,
@Column(name = "Answer")
var Answer: String,
@Column(name = "User")
var User: String
) : PersistentState() {
// Default constructor required by hibernate.
constructor() : this(
"",
"",
"",
"",
""
)
}
}
class QAState(val lender: Party,
val borrower: Party,
val Question: String,
val Answer: String,
val User: String) : ContractState, QueryableState {
override val participants get() = listOf(lender, borrower)
fun GetU():String {
return User
}
fun GetQ():String {
return Question
}
fun GetA():String {
return Answer
}
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
is QASchemaV1 -> QASchemaV1.PersistentTest(
this.lender.name.toString(),
this.borrower.name.toString(),
this.Question,
this.Answer,
this.User
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(QASchemaV1)
}
class UserState(val lender: Party,
val borrower: Party,
val Question: String,
val Answer: String) : ContractState, QueryableState {
override val participants get() = listOf(lender, borrower)
fun GetQ():String {
return Question
}
fun GetA():String {
return Answer
}
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
is UserSchemaV1 -> UserSchemaV1.PersistentTest(
this.lender.name.toString(),
this.borrower.name.toString(),
this.Question,
this.Answer
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(UserSchemaV1)
}
@InitiatingFlow
@StartableByRPC
@CordaSerializable
class UserOperationFlow(val Q:String, val A:String) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
// We retrieve the notary identity from the network map.
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val x500Name = CordaX500Name("PartyA", "London", "GB")
val otherParty = serviceHub.networkMapCache.getPeerByLegalName(x500Name)
val state = UserState(ourIdentity, otherParty!!, Q,A)
val txCommand = Command(UserContract.QuestionRequest(), state.participants.map { it.owningKey })
val txBuilder = TransactionBuilder(notary)
.addOutputState(state, BotContract.ID)
.addCommand(txCommand)
txBuilder.verify(serviceHub)
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
val otherPartyFlow = initiateFlow(otherParty)
val fullySignedTx = subFlow(CollectSignaturesFlow(partSignedTx, setOf(otherPartyFlow)))
return subFlow(FinalityFlow(fullySignedTx))
}
}
@InitiatedBy(UserOperationFlow::class)
class UserOperationResponder(val otherPartySession: FlowSession) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
val signTransactionFlow = object : SignTransactionFlow(otherPartySession) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be a user transaction." using (output is UserState)
}
}
return subFlow(signTransactionFlow)
}
}
@InitiatingFlow
@StartableByRPC
@CordaSerializable
class QAOperationFlow(val Q:String, val A:String, val U:String) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
// We retrieve the notary identity from the network map.
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val x500Name = CordaX500Name("PartyB", "New York", "US")
val otherParty = serviceHub.networkMapCache.getPeerByLegalName(x500Name)
val state = QAState(ourIdentity, otherParty!!, Q,A,U)
val txCommand = Command(BotContract.QAOperation(), state.participants.map { it.owningKey })
val txBuilder = TransactionBuilder(notary)
.addOutputState(state, BotContract.ID)
.addCommand(txCommand)
txBuilder.verify(serviceHub)
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
val otherPartyFlow = initiateFlow(otherParty)
val fullySignedTx = subFlow(CollectSignaturesFlow(partSignedTx, setOf(otherPartyFlow)))
return subFlow(FinalityFlow(fullySignedTx))
}
}
@InitiatedBy(QAOperationFlow::class)
class QAOperationResponder(val otherPartySession: FlowSession) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
val signTransactionFlow = object : SignTransactionFlow(otherPartySession) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be a user transaction." using (output is QAState)
}
}
return subFlow(signTransactionFlow)
}
}
@JvmStatic
fun main(args: Array<String>) {
// corda and spark setup
val rpcUser = User("user1", "test", permissions = setOf("ALL")) // пользователь
val rpcUsers = listOf(rpcUser) // список пользователей допущенных к операциям на корда узлах
val http = Service.ignite().port(args.getOrNull(0)?.toInt() ?: 1357) // запустить спарк-сервер по адресу localhost:1357
//Запуск системы
driver(DriverParameters(startNodesInProcess = true, waitForAllNodesToFinish = true,
extraCordappPackagesToScan = listOf("com.template.SparkDriver.QAState"),
isDebug = true)) {
//Запуск корда-узлов (3 - минимум для любого консенсуса)
startNode(providedName = CordaX500Name("PartyG", "New York", "US"), rpcUsers = rpcUsers).getOrThrow()
startNode(providedName = CordaX500Name("PartyA", "London", "GB"), rpcUsers = rpcUsers).getOrThrow()
startNode(providedName = CordaX500Name("PartyB", "New York", "US"), rpcUsers = rpcUsers).getOrThrow()
////////////////////////////////////// corda 101
val hostAndPort = "localhost:10005"
val username = "user1"
val password = "test"
val nodeAddress = NetworkHostAndPort.parse(hostAndPort)
val rpcConnection = CordaRPCClient(nodeAddress).start(username, password)
val proxy = rpcConnection.proxy //наша точка доступа к реестру
/*
var states = proxy.vaultQuery(QAState::class.java).states
.map { it.state.data }
.filter { it -> it.TestData.contains("hello") }
.toList()
states.forEach { it -> println(it.TestData) }
*/
//////////////////////////////////////
// Spark 101
val freeMarkerEngine = FreeMarkerEngine()
val freeMarkerConfiguration = Configuration()
freeMarkerConfiguration.setTemplateLoader(ClassTemplateLoader(this::class.java, "/templates/"))
freeMarkerEngine.setConfiguration(freeMarkerConfiguration)
http.staticFileLocation("/spark")
http.get("/admin") { req, _ ->
val states = proxy.vaultQuery(QAState::class.java).states.map { it.state.data }
.toList()
val model = HashMap<String, Any>()
model["posts"] = states.map { it }
freeMarkerEngine.render(ModelAndView(model, "QAAdmin.ftl"))
}
http.get("/") { req, _ ->
val states = proxy.vaultQuery(NodeDriver.UserState::class.java).states.map { it.state.data }
.toList()
val model = HashMap<String, Any>()
model["posts"] = states.map { it }
freeMarkerEngine.render(ModelAndView(model, "UserLand.ftl"))
}
http.post("/QACallServer") { req, res ->
val hasParams = !req.queryParams().isEmpty()
if (hasParams) {
val n = req.queryParamsValues("un").single()
val q = req.queryParamsValues("uq").single()
val a = req.queryParamsValues("ua").single()
proxy.startFlow(NodeDriver::QAOperationFlow, n,q,a).returnValue.getOrThrow()
}
res.redirect("/admin")
}
http.post("/UCallServer") { req, res ->
val hasParams = !req.queryParams().isEmpty()
if (hasParams) {
val q = req.queryParamsValues("uq").single()
val states = proxy.vaultQuery(QAState::class.java).states.map { it.state.data }
.toList()
var a = states.filter { it -> it.Question.contains(q) }.map{it.Answer}
.randomOrNull()
if(a == null) {
a = "Нет ответа"
}
proxy.startFlow(NodeDriver::UserOperationFlow, q, a).returnValue.getOrThrow()
}
res.redirect("/")
}
}
}
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
</head>
<body style="height: 100%;">
<div class="card mx-auto" style="width: 90%; margin:10px">
<form method="post" action="/QACallServer">
<div class="form-group">
<input class="form-control" placeholder="введите Имя!" name="un" required="" type="text">
</div>
<div class="form-group">
<input class="form-control" placeholder="введите Вопрос!" name="uq" required="" type="text">
</div>
<div class="form-group">
<input class="form-control" placeholder="введите Ответ!" name="ua" required="" type="text">
</div>
<div class="form-group">
<button class="btn btn-warning" style="width: 180px; margin:10px" type="submit">Отправить в реестр!</button>
</div>
</form>
</div>
<div class="card mx-auto" style="width: 90%; margin:10px">
<div class="card-body">
<table class="table">
<tbody>
<#list posts as it>
<tr>
<td>${it.GetU() }</td>
<td>${it.GetQ() }</td>
<td>${it.GetA() }</td>
</tr>
</#list>
</tbody>
</table>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
</head>
<body style="height: 100%;">
<div class="card mx-auto" style="width: 90%; margin:10px">
<form method="post" action="/UCallServer">
<div class="form-group">
<input class="form-control" placeholder="введите ВОПРОС!" name="uq" required="" type="text">
</div>
<div class="form-group">
<button class="btn btn-warning" style="width: 180px; margin:10px" type="submit">Отправить в реестр!</button>
</div>
</form>
</div>
<div class="card mx-auto" style="width: 90%; margin:10px">
<div class="card-body">
<table class="table">
<tbody>
<#list posts as it>
<tr>
<td>${it.GetQ() }</td>
<td>${it.GetA() }</td>
</tr>
</#list>
</tbody>
</table>
</div>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment