Vibrant.kt - rapid prototyping and development of distributed applications (DApps) on the JVM

    Nihao!


    Introduction


    I didn’t write anything for a long time, because the USE will not surrender itself, but I couldn’t help writing the class for the Baltic Competition . I could not squeeze out good ideas from nowhere, so I decided to plunge into a topic completely unfamiliar to me at that time (half a month ago), into the world of blockchain, cryptocurrencies, smart contracts and other clever English words. In the lessons I ran into the phone, reading a lot of texts about blockchain, peer2peer network and all that, gradually sorting it out. Everything went easier when I started writing simple prototypes in Javascript'e: once again I am convinced that everything is more understandable in the code than in the text. As a result, when I sort of figured it out, I decided on the topic of the work, which can be seen in the title of the article.


    What is it all about and why is it necessary


    Vibrant is a library written in Kotlin for rapid prototyping of distributed applications. The idea is that you can focus on certain aspects of the future system, replacing unrealized code with ready-made solutions. For example, you are planning to write a distributed chat.


    Immediately there are several points that require implementation: how to provide a connection between peers, which communication protocol to use, and do I need UDP in LAN if I just try to chat, or can do it over TCP, or maybe just HTTP ... In general, a lot primary tasks, without which nothing will work. And to write a simple chat on a distributed platform, you will have to implement a certain amount of functionality. Vibrant, which consists of 2 packages, solves this very problem org.vibrant.core- architecture abstraction and org.vibrant.base- various implementations of abstractions from the core package, for example HTTPPeer, HTTPJsonRPCPeer- peer classes that communicate via http, the first is more abstract, the second uses the JSON RPC 2.0 protocol for communication between nodes.
    In general, instead of writing a feast from scratch, we take a ready-made JSON RPC feast and use it. If there is a need to change the protocol or an acute desire to write something of their own - an abstraction allows the flag in hand.


    And how to use it?


    class Peer(port: Int, rpc: BaseJSONRPCProtocol): HTTPJsonRPCPeer(port, rpc){
        val miners = arrayListOf()
        fun broadcastMiners(jsonrpcRequest: JSONRPCRequest): List> {
            return this.broadcast(jsonrpcRequest, this.miners)
        }
        fun addUniqueRemoteNode(remoteNode: RemoteNode, isMiner: Boolean = false) {
            super.addUniqueRemoteNode(remoteNode)
            if (isMiner && this.miners.find { it.address == remoteNode.address && it.port == remoteNode.port } == null) {
                this.miners.add(remoteNode)
            }
        }
    }

    Here is a simple implementation HTTPJsonPeer. It is impossible to say on it that it can only if you do not notice the argument of the constructor rpc: BaseJSONRPCProtocol. If you initialize this class and run, then the HTTP server will be launched on the selected port, which will accept POSTJSON RPC requests for /rpcendpoint, transform them into Kotlin objects and call the appropriate method in the passed one BaseJSONRPCProtocol. So to say, plug and play.
    Here is an example of methods that can be run through a JSON RPC request:


    @JSONRPCMethod
        fun getLastBlock(request: JSONRPCRequest, remoteNode: RemoteNode): JSONRPCResponse<*>{
            return JSONRPCResponse(
                    result = node.chain.latestBlock().serialize(),
                    error = null,
                    id = request.id
            )
        }
        @JSONRPCMethod
        fun newBlock(request: JSONRPCRequest, remoteNode: RemoteNode): JSONRPCResponse<*>{
            val blockModel = BaseJSONSerializer.deserialize(request.params[0].toString().toByteArray()) as BaseBlockModel
            node.handleLastBlock(blockModel, remoteNode)
            return JSONRPCResponse(
                    result = node.chain.latestBlock().serialize(),
                    error = null,
                    id = request.id
            )
        }

    So here you can quickly make a feast.
    But where is the blockchain ?! There he is.


    abstract class InMemoryBlockChain: BlockChain() {
        protected val blocks = arrayListOf(this.createGenesisBlock())
        override fun latestBlock(): B = this.blocks.last()
        override fun addBlock(block: B): B {
            synchronized(this.blocks, {
                this.blocks.add(block)
                this.notifyNewBlock()
                return this.latestBlock()
            })
        }
    }

    This is a class from a package org.vibrant.base, inheriting it you can safely use a blockchain that exists only in memory. Such a blockchain is well suited, for example, for testing an application.


    It is worth noting that the availability InMemoryBlockChaindoes not limit the developer: you can write your own InMemoryBlockChain, where h2 database is used instead of arraylist, but this already applies to the item “I do not want to steam over the boilerplate, give me a ready boilerplate and the ability to write my code”.


    Preclusion


    I am actively puffing about this project, there are still a lot of things that I would like to add, for example, implement Tangle in the image of Iota , write high-quality UDPPeer using, for example, Netty channels. Oh yes, now I'm working on smart contracts, which will also be possible plug and play. I think it’s going to be funny


    Conclusion


    I will be very glad for the enthusiasm of readers in the form of pull requests.
    Links to github:
    Core package with abstraction
    Base package with implementations
    Working chat application


    Also popular now: