Нихао!
Введение
Я долго ничего не писал, потому что ЕГЭ само себя не сдаст, но к Балтийскому конкурсу я не мог не написать чего-нибудь классное. Хороших идей из ниоткуда я выдавить не мог, поэтому решил окунуться в абсолютно незнакомую мне на тот момент(пол месяца назад) тему, в мир блокчейна, криптовалют, смарт контрактов и других умных английских слов. На уроках утыкался в телефон, читая множество текстов про блокчейн, peer2peer сети и все такое, постепенно разбираясь. Все пошло легче, когда я начал писать простые прототипы на Javascript'e: в очередной раз убеждаюсь, что в коде все понятнее, нежели на тексте. В итоге, когда я вроде разобрался, я определился с темой работы, которую видно в заголовке статьи.
Чего это вообще такое и зачем нужно
Vibrant — это библиотека, написанная на Kotlin, для быстрого прототипирования распределенных приложений. Идея в том, что можно сосредотачиваться на определенных аспектах будущей системы, заменяя нереализованный код готовыми решениями. Например, Вы задумали написать распределенный чат.
Сразу возникает несколько пунктов, которые требуют реализации: как обеспечить соединение между пирами, какой протокол общения юзать, а нужно ли мне UDP в локалке, если я просто пробую сделать чат, а может сделать по TCP, а может просто HTTP… В общем, много первостепенных задач, без решения которых ничего не будет работать. И чтобы написать простой чат на распределенной платформе, придется реализовывать некоторое количество функционала. Именно эту задачу решает Vibrant, состоящий из 2-х пакетов, org.vibrant.core
— абстракция архитектуры и org.vibrant.base
— различные реализации абстракций из core пакета, например, HTTPPeer
, HTTPJsonRPCPeer
— классы пиров, общение которых происходит по http, первый — более абстрактный, второй использует протокол JSON RPC 2.0 для общения между узлами.
В общем, вместо написания пира с нуля, берем готовый JSON RPC пир и используем его. Если возникает необходимость сменить протокол или острое желание написать что-то свое — абстракция позволяет, флаг в руки.
И как этим пользоваться?
class Peer(port: Int, rpc: BaseJSONRPCProtocol): HTTPJsonRPCPeer(port, rpc){
val miners = arrayListOf<RemoteNode>()
fun broadcastMiners(jsonrpcRequest: JSONRPCRequest): List<JSONRPCResponse<*>> {
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)
}
}
}
Вот простая реализация HTTPJsonPeer
. По ней невозможно сказать, что она умеет, только если не заметить аргумента конструктора rpc: BaseJSONRPCProtocol
. Если инициализировать этот класс и запустить, то на выбранном порте будет запущен HTTP сервер, который принимает POST
JSON RPC запросы на /rpc
endpoint, трансформирует их в Kotlinовские объекты и вызывает соотвествующий метод в переданном BaseJSONRPCProtocol
. Так сказать, plug and play.
Вот пример методов, которые могут быть запущены через JSON RPC запрос:
@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
)
}
Вот так вот быстренько можно сделать пир.
Но где же блокчейн?! Вот он.
abstract class InMemoryBlockChain<B: BlockModel, out T: BlockChainModel>: BlockChain<B, T>() {
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()
})
}
}
Это класс из пакета org.vibrant.base
, наследовав его можно спокойно юзать блокчейн, существующий только в памяти. Такой блокчейн хорошо подойдет, например, для тестирования приложения.
Стоит отметить, что наличие InMemoryBlockChain
не ограничивает разработчика: можно написать свой InMemoryBlockChain
, где вместо arraylist'a используется h2 database, но это уже относится к пункту про "не хочу париться над boilerplate, дайте мне готовый boilerplate и возможность писать мой код".
Предзаключение
Я активно пыхчу над этим проектом, тут есть еще куча вещей, которые я хотел бы добавить, например, реализовать Tangle по образу и подобию Iota, написать качественный UDPPeer, используя, например, Netty channels. Ах да, сейчас я работаю над смарт контрактами, которые тоже можно будет plug and play
. Думаю, получится забавно
Заключение
Буду премного рад энтузиазму читателей в виде pull request'ов.
Ссылки на github:
Core пакет с абстракцией
Base пакет с реализациями
Рабочее приложение-чат