package models.smartcontracts
import StandardTypesConversions._
//todo: make ClauseParams case of it
abstract class State {
val name: String
override def equals(obj: scala.Any): Boolean = obj match{
case s : State => s.name == name
case _ => false
}
override def toString: String = name
}
abstract class InitState extends State
abstract class FinishState extends State
sealed trait Value {
val value: Any
}
case class StringValue(override val value: String) extends Value
case class LongValue(override val value: Long) extends Value
case class DoubleValue(override val value: Double) extends Value
object StandardTypesConversions{
implicit def string2StringValue(value : String) = StringValue(value)
implicit def stringValue2String(value : StringValue) = value.value
}
//todo: scheduling? (in application.properties?)
sealed abstract class Query[K, V <: Value] (val key:K) {
def query(): V
}
abstract class DNSQuery[V <: Value](val domain:StringValue) extends Query[StringValue, V](domain)
case class RegistrantQuery (override val domain:StringValue) extends DNSQuery[StringValue](domain) {
override def query(): StringValue = " "
}
sealed trait Predicate[V <: Value] {
val pattern: V
def matchPattern(value: V): Boolean
}
trait StringPredicate extends Predicate[StringValue]
case class StringIncludesPredicate(override val pattern: StringValue) extends StringPredicate {
override def matchPattern(value: StringValue) = value.value contains pattern.value
}
trait LongPredicate extends Predicate[LongValue]
trait DoublePredicate extends Predicate[DoubleValue]
sealed trait Action{
def process()
}
class ReleaseBitcoins extends Action{
def process() = ???
}
case class SendNxt(sender: Long, receiver: Long, amountNqt: Long) extends Action {
def process() = println(s"Sending nxts from $sender to $receiver")
}
class SendBitcoins extends Action {
def process() = ???
}
case class QueryAndCheck[K <: Value, V <: Value](query: Query[K, V], predicate: Predicate[V]){
def process() = predicate.matchPattern(query.query())
}
/**
* Smart Contract is like Finite State Machine structurally switching from a state to another state if predicate
* returns true. From functional point of view SmartContract is mapping between [ss:State,p:Predicate]
* and [a:Action,sf:State] sets. The structure is immutable, the only way to get modified object is to call doStep()
* returning modified contract
*/
abstract class SmartEscrow {
type Clause = (State, QueryAndCheck[_, _])
type Out = (Action, State)
val initState: InitState
val finishStates: Seq[FinishState]
val currentState: State
val transitions: Map[Clause, Out]
def doStep(): Option[State] = if (finished) None else {
val pt = transitions.filterKeys(_._1 == currentState)
pt.find { case ((_, qc), _) => qc.process()} match {
case Some(((state, qc), (action, stateToGo))) =>
action.process()
onTransition(qc.predicate, action, stateToGo)
Some(stateToGo)
case None => None
}
}
val finished = currentState match{
case _:FinishState => true
case _ => false
}
protected def onTransition(p: Predicate[_], a: Action, s: State) = println(s"State changed to $s")
}
case class DomainTransfer(domain:String, newOwner: String, sender: Long,
receiver: Long, amount: Long) extends SmartEscrow {
override val initState = new InitState {
override val name: String = "Waiting"
}
override val currentState = initState
val done = new FinishState {
override val name: String = "Done"
}
override val finishStates = Seq(done)
override val transitions:Map[Clause, Out] = Map(
(initState, QueryAndCheck(RegistrantQuery(domain), StringIncludesPredicate(newOwner)))
->(SendNxt(sender, receiver, amount), done)
)
}