-
-
Save OlegJakushkin/d62c66b6e738a87438d48df4268c13b8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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("/") | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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