All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.pylons.ipc.Message.kt Maven / Gradle / Ivy

package tech.pylons.ipc

import com.beust.klaxon.JsonArray
import com.beust.klaxon.JsonObject
import com.beust.klaxon.Parser
import tech.pylons.lib.PubKeyUtil
import tech.pylons.lib.core.ICore
import tech.pylons.lib.core.IMulticore
import org.apache.tuweni.bytes.Bytes32
import java.lang.StringBuilder
import java.util.*
import kotlin.reflect.full.*
import tech.pylons.lib.klaxon
import tech.pylons.lib.types.*
import tech.pylons.lib.types.credentials.CosmosCredentials
import tech.pylons.lib.types.tx.Coin
import java.io.StringReader

sealed class Message {

    class CancelTrade (
            var tradeId : String? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            txs = listOf(core!!.cancelTrade(tradeId!!)), tradesIn = listOf(tradeId!!))
    }

    class CheckExecution(
            var id : String? = null,
            var payForCompletion : Boolean? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            txs = listOf(core!!.checkExecution(id!!, payForCompletion!!)))
            // we need structured exec data but we can't do it atm
    }

    class CreateCookbooks(
            var ids : List? = null,
            var names : List? = null,
            var developers : List? = null,
            var descriptions : List? = null,
            var versions : List? = null,
            var supportEmails : List? = null,
            var costsPerBlock : List? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            txs = core!!.batchCreateCookbook(ids!!, names!!, developers!!,
            descriptions!!, versions!!, supportEmails!!, costsPerBlock!!))
    }

    class CreateRecipes(
            var names : List? = null,
            var cookbooks : List? = null,
            var descriptions : List? = null,
            var blockIntervals : List? = null,
            var coinInputs : List? = null,
            var itemInputs: List? = null,
            var outputTables : List? = null,
            var outputs : List? = null,
            var extraInfos : List? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true, txs = core!!.batchCreateRecipe(names!!, cookbooks!!,
            descriptions!!, blockIntervals!!, coinInputs!!, itemInputs!!, outputTables!!, outputs!!, extraInfos!!),
            cookbooksIn = cookbooks!!)
    }

    class CreateTrade (
            var coinInputs : List? = null,
            var itemInputs: List? = null,
            var coinOutputs: List? = null,
            var itemOutputs : List? = null,
            var extraInfo : String? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            txs = listOf(core!!.createTrade(coinInputs!!, itemInputs!!, coinOutputs!!, itemOutputs!!, extraInfo!!)))
        // this feels like it should have some structured input data but i'm not sure atm, need to look at how trades
        // work on the node some more
    }

    class DisableRecipes (
            var recipes : List? = null
    ) : Message() {
        companion object {
            fun deserialize(json: String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            txs = core!!.batchDisableRecipe(recipes!!), recipesIn = recipes!!)
    }

    class EnableRecipes (
            var recipes : List? = null
    ) : Message() {
        companion object {
            fun deserialize(json: String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            txs = core!!.batchEnableRecipe(recipes!!), recipesIn = recipes!!)
    }

    class ExecuteRecipe(
            var recipe : String? = null,
            var cookbook : String? = null,
            var itemInputs : List? = null
    ) : Message() {
        companion object {
            fun deserialize(json: String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            txs = listOf(core!!.applyRecipe(recipe!!, cookbook!!, itemInputs!!)),
            recipesIn = listOf(recipe!!), cookbooksIn = listOf(cookbook!!),
            itemsIn = itemInputs!!)
    }

    class FulfillTrade(
            var tradeId : String? = null,
            var itemIds : List? = null,
            var paymentId: String? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            txs = listOf(core!!.fulfillTrade(tradeId!!, itemIds!!)),
            tradesIn = listOf(tradeId!!), itemsIn = itemIds!!)
    }

    class GetCookbooks : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
        cookbooksOut = core!!.getCookbooks())
    }

    class GetPendingExecutions : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
        executionsOut = core!!.getPendingExecutions())
    }

    class GetTrades : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
        tradesOut = core!!.listTrades())
    }

    class GetProfile(
            var address : String? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve(): Response {
            val p = core!!.getProfile(address)
            val ls = mutableListOf()

            if (p != null) ls.add(Profile(p.address, p.strings, p.coins, p.items)) // hack because klaxon will die if we aren't specifically an instance of the base class
            println("GetProfile Response")
            return Response.emit(this, true, profilesOut = ls)
        }
    }

    class GetPylons(
            var count : Long? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            txs = listOf(core!!.getPylons(count!!)))
    }

    class GetRecipes : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            recipesOut = core!!.getRecipes())
    }

    class GetRecipesBySender(): Message() {
        companion object {
            fun deserialize(json: String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            recipesOut = core!!.getRecipesBySender())
    }

    class GoogleIapGetPylons(
            var productId : String? = null,
            var purchaseToken : String? = null,
            var receiptData : String? = null,
            var signature : String? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true,
            txs = listOf(core!!.googleIapGetPylons(productId!!, purchaseToken!!, receiptData!!, signature!!)))
    }

    class GetTransaction(
            var txHash : String? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true, txs = listOf(core!!.getTransaction(txHash!!)))
    }

    class RegisterProfile(
            var name : String? = null,
            var makeKeys : Boolean? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve(): Response {
            if (IMulticore.enabled && makeKeys == true) {
                IMulticore.instance!!.addCore(null)
                makeKeys = false // we made the keys!
            }
            val tx = when (makeKeys!!) {
                // HACK: We shouldn't accept empty name field once names actually exist on the backend
                true -> core!!.newProfile(name.orEmpty())
                false -> core!!.newProfile(name.orEmpty(), core!!.engine.cryptoHandler.keyPair)
            }
            return Response.emit(this, true, txs = listOf(tx))
        }
    }

    class SendCoins(
            var coins : String?,
            var receiver : String?
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true, txs = listOf(core!!.sendCoins(coins!!, receiver!!)),
        coinsIn = Coin.listFromJson(klaxon.parseJsonArray(StringReader(coins)) as JsonArray))
    }

    class SetItemString(
            var itemId : String? = null,
            var field : String? = null,
            var value : String? = null
    ) : Message() {
        companion object {
            fun deserialize(json: String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true, txs = listOf(core!!.setItemString(itemId!!, field!!, value!!)),
        itemsIn = listOf(itemId!!))
    }

    class UpdateCookbooks(
            var ids : List? = null,
            var names : List? = null,
            var developers : List? = null,
            var descriptions : List? = null,
            var versions : List? = null,
            var supportEmails : List? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true, txs =
            core!!.batchUpdateCookbook(names!!, developers!!, descriptions!!, versions!!, supportEmails!!, ids!!),
            cookbooksIn = ids!!)

    }

    class UpdateRecipes(
            var ids : List? = null,
            var names : List? = null,
            var cookbooks : List? = null,
            var descriptions : List? = null,
            var blockIntervals : List? = null,
            var coinInputs : List? = null,
            var itemInputs: List? = null,
            var outputTables : List? = null,
            var outputs : List? = null,
            var extraInfos: List? = null
    ) : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve() : Response =
            Response.emit(this, true, txs = core!!.batchUpdateRecipe(ids!!, names!!, cookbooks!!,
                descriptions!!, blockIntervals!!, coinInputs!!, itemInputs!!, outputTables!!,
                outputs!!, extraInfos!!),
            recipesIn = ids!!, cookbooksIn = cookbooks!!)
    }

    class WalletServiceTest(
            val input : String? = null
    ) : Message() {
        companion object {
            fun deserialize(json: String) = klaxon.parse(json)
        }

        override fun resolve() = Response.emit(this, true, unstructured = listOf(core!!.walletServiceTest(input!!)))
    }

    class WalletUiTest : Message() {
        companion object {
            fun deserialize(json: String) = klaxon.parse(json)
        }

        override fun ui(): UiHook {
            // WalletUiTest never returns until this hook is released
            return UILayer.addUiHook(UiHook(this))
        }

        override fun resolve() = Response.emit(this, true, unstructured = listOf(core!!.walletUiTest()))
    }

    class ExportKeys : Message() {
        companion object {
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve(): Response {
            val keys = core!!.dumpKeys()
            return Response.emit(this, true, unstructured = listOf(core!!.userProfile!!.address, core!!.userProfile!!.strings["name"].orEmpty(),
                keys[0], keys[1]))
        }
    }

    class AddKeypair(
            val privkey : String? = null
    ) : Message() {
        companion object{
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve(): Response {
            if (!IMulticore.enabled) throw Exception("Multicore is not enabled")
            val kp = PylonsSECP256K1.KeyPair.fromSecretKey(
                    PylonsSECP256K1.SecretKey.fromBytes(Bytes32.fromHexString(privkey!!)))
            val credentials = CosmosCredentials(PubKeyUtil.getAddressString(PubKeyUtil.getAddressFromKeyPair(kp).toArray()))
            IMulticore.instance!!.addCore(kp)
            return Response.emit(this, true, profilesOut = listOf(core!!.getProfile(credentials.address)!!))
        }
    }

    class SwitchKeys : Message() {
        val address : String? = null
        companion object{
            fun deserialize(json : String) = klaxon.parse(json)
        }

        override fun resolve(): Response {
            if (!IMulticore.enabled) throw Exception("Multicore is not enabled")
            val core = IMulticore.instance!!.switchCore(address!!)
            return Response.emit(this, true, profilesOut = listOf(core.getProfile(address)!!))
        }
    }

    /**
     * BuyPylons
     * do wallet UI action for buy pylons
     * wallet ui set txHash of the Purchase Transaction
     * return Purchase Transaction to counterpart
     */
    class BuyPylons: Message() {

        //
        var txHash:String? = null

        companion object{
            fun deserialize(json: String) = klaxon.parse(json)
        }

        override fun resolve(): Response {
            //transactions can fail
            if (txHash.isNullOrEmpty()) {
                return Response.emit(this, true)
            }

            return Response.emit(this, true,
                txs=listOf(core!!.getTransaction(txHash!!))
            )
        }
    }

    companion object {
        protected var core : ICore? = null
        fun useCore(core : ICore) {
            Companion.core = core
        }

        fun match(json: String) : Message? {
            println("Trying to match message \n$json")
            val jsonObject = Parser.default().parse(StringBuilder(json)) as JsonObject
            IPCLayer.implementation!!.messageId = jsonObject.int("messageId")!!
            println("Set messageId to ${IPCLayer.implementation!!.messageId}")
            val cid = jsonObject.int("clientId")
            val mid = jsonObject.int("walletId")
            if (cid != IPCLayer.implementation!!.clientId || mid != IPCLayer.implementation!!.walletId)
                throw Exception("Client/wallet ID mismatch - got ${jsonObject.int("clientId").toString()} ${jsonObject.int("walletId").toString()}, expected ${IPCLayer.implementation!!.clientId} ${IPCLayer.implementation!!.walletId}")
            val type = jsonObject.string("type")!!
            val msg =
                    Base64.getDecoder().decode(
                            jsonObject.string("msg")!!).toString(Charsets.US_ASCII)
            var ret : Message? = null
            Message::class.sealedSubclasses.forEach { kClass ->
                if (kClass.simpleName == type) {
                    println("attempting to match ${kClass.simpleName}")
                    val func = kClass.companionObject?.functions?.find { it.name == "deserialize" }
                    ret = func?.call(kClass.companionObjectInstance, msg) as Message?
                }
            }
            return ret
        }
    }

    open class UiHook(val msg : Message) {
        var response : Response? = null
        var live : Boolean = true
            private set
        var confirmed : Boolean = false
            private set

        var rejected: Boolean = false
            private set

        fun confirm() : UiHook {
            confirmed = true
            return this
        }

        fun reject() : UiHook {
            rejected = true
            return this
        }

        fun release() : UiHook {
            live = false
            return this
        }
    }

    open fun ui () : UiHook {
        // Default ui() implementation just forces the ui hook through its entire lifecycle immediately.
        // This simplifies the way we need to deal w/ UI interactions - every message creates
        // a ui hook; it's just that some of them don't actually need to do any work before they're
        // done with it.
        //return UILayer.releaseUiHook(UILayer.confirmUiHook(UILayer.addUiHook(UiHook(this))))

        //break main ui loop
        return UILayer.addUiHook(UiHook(this))
    }

    abstract fun resolve() : Response
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy