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

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

Go to download

Library providing common functionality for interacting with the Pylons ecosystem

The newest version!
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 levels : 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!!, levels!!, 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
    ) : 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!!),
            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
    ) : 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 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
    ) : 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!!),
            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