KLYNTAR Virtual Machines. Part 0: KLY-EVM — shardable, low-level managed, mutable, and WASM-improved EVM.
Intro
Hi, we’re back in touch! With this story, we start a new cycle of articles about KLY. Here we’ll discuss virtual machines available on KLYNTAR. You can find much interesting information about low-level management, the workflow of VMs execution, cross-vm interaction, and so on! Also, we’ll publish a set of articles about different use cases of how to write contracts, and how to use KLY features available for different VMs( EVM & WASM & others) — predeployed contracts, special logs, WORA principle, mutations, and much more!
KLY-EVM
Definitely, the most popular VM in the blockchain industry is EVM. Projects with EVM compatibility propose a well-known set of tools for developers for a quick start with their ecosystem. You even shouldn’t learn new languages or work with docs — just use solidity docs, web3 library, and life is good!
Not all of the projects propose new features for their EVM port. But, it’s not true in the case of KLYNTAR.
Multi-level sharding — the implementation on EVM level🔥🔥🔥
Probably, it’s the hottest part of this article. As you remember from previous articles in our blog, we’ve discussed multi-level sharding implementation on KLY. It’s here
In the article about basics, you can get information about the first level of sharding — the whole ecosystem consists of a set of independent chains named symbiotes. Also, we’re working on the next article in this cycle where you’ll find out how sharding works on a single symbiote level, how the pool-per-chain principle works, and so on!
In this (still not published😅) article you can find the explanation of sharding inside a single symbiote and this illustration can help you.
Since all the subchains here are related to a single symbiote — all of them are part of a single system of accounts. Also, each symbiote will have its own chainID (the number for the EVM registry and 256/512-bit BLAKE3 hash for the KLY ecosystem).
For example, our initial symbiote kNULL(which will be launched by KLYNTAR Team) will have 7331 as a chainID for EVM. You can see it here
So, exactly on the subchains level, we need to implement the sharding for EVM. But, to make it possible to securely bind accounts to the appropriate subchain(shard) we should dive deep into bytecodes, the workflow of EVM execution(via debugging), and so on. Should be interesting!
General idea
Back to our picture with subchains
The simple idea of sharding on KLY is to bind accounts(default and accounts of smart contracts) to a single subchain and deny all attempts of state changes from txs from other subchains. This image can help you to understand:
Here you see that if you bind your account to a subchain A this is a signal for everyone that the state of your contract or default account can be changed only via transactions via the same subchain. Why it’s cool?!
Because now, we can separate the work and explore each symbiote independently. In simple words — you can run the special software from the Bumblebee packet which will be the backend for explorers & wallets and set the subchain to track changes.
Since the block time is short and TTF(time-to-finality) is instant — you can easily get updates of state from each symbiote in a parallel way.
Sounds good and it seems we can implement it. For KLY-VM(WASM based) it’s easy because all the state is changed via KLYNTAR core level and these operations are transparent for us. Even, advanced logic like cross-contract calls can be tracked, because this functionality is passed to the wasm module as an injected dependency from the JS context. For instance, here is an example from KLY-VM tests:
In the same way, a contract changes its own state and the state of other accounts/contracts.
But what about EVM ? Let’s use reverse engineering skills and try to debug.
Low-level control
To reverse the workflow of EVM on the deep level — we’ve built the graph of workflow. It helps us to dive into the bytecode level of executing, to see the stack changes and black-boxed values of EVM inside. This is how this graph looks like:
Our goal is to allow changes of contract X only from transactions on subchain A. For this, we should track all the accounts whose state will be changed and revert & stop the execution in case the transaction tries to change the account state that is bound to subchain B
The dangerous moment here is that we should remember about so advanced stuff like cross-contract calls, delegations, and so on! We know about it😅
When EVM is going to change the state of some account the address of its will be added to the inner property _touched — this is the set of accounts whose state will be changed in a certain tx.
For demonstration, we have a repository with an example of a cross-contract call and the results of debug process. You can find & try it here
Here we try to deploy 2 contracts — Caller and Receiver. Inside contract Caller.sol there is a function for cross-contract execution which calls a function inside Receiver.sol contract. We’ll deploy using our account 0xbe862ad9abfe6f22bcb087716c7d89a26051f74c
After deployment, we can see these logs
And, if we manually override the debug logic inside the EVM source code and call the function crossContractCallFromCallerToReceiver()
we can see this:
As you see — this set contains all the addresses that will be changed during operation.
This is exactly what we need because now we can override the logic of the function which add the address to this set. We need to stop execution once we detect binding to another subchain.
This function is touchAccount() inside the vmState.js module from @ethereumjs/vm/eei package. The modified function looks like this:
In simple words, an exception will be thrown and vm will be reverted. The extra logic also contains work with mempool to allow the JSON-RPC server to accept the transactions, and test them in the sandbox with wished context(useful for load balancers, explorers, etc.) before adding to mempool(some filtration practice).
So, in simple words & using examples we’ve demonstrated to you a proof of concept(PoC) of future KLY-EVM.
Mutations principle
We wouldn’t be ourselves if we didn’t go even further. During research work, we noticed tons of interesting moments of EVM, and going to add them to KLY-EVM.
Since KLYNTAR is a mutable project, you’ll have the ability to add mutations for EVM workflows.
For example, the stack tracing looks like this:
The interpreter.js has the following code:
…and opcodes list looks like this:
What it gives us? Why it’s cool and what we can do with it?
We’ll with this low-level control we can create different custom mutations of EVM — with different gas prices for opcodes, custom opcodes, custom inner logic, change the execution context for different contracts & addresses, use the off-chain functionality, and much more! It’s like having a super-powerful debugger and doing everything that you want.
We promised you that KLY-EVM is a gigachad. Hope, we proved it)
WORA — write functionality for EVM with WASM
It will be the topic of one of the next articles, but since we have a WASM-compatible VM on the board, we should remind you about the ability to use WASM modules for EVM.
Long story short, we assume that it will be worked as a callback mechanism where you stop the EVM execution, call the WASM module, and return to EVM execution with new/extra values.
But wait for the article)
Summary
Damn, this article looks so hot! Detailed, simple, with examples — perfect! We’re working to make KLY the leading project. Not only in the crypto industry. But too.
With KLY-EVM we started another cycle of articles in our blog. Hope, you find it interesting. We’ll try to publish such articles often to get you a detailed explanation of all the components of KLY ecosystem.
Just imagine your first time launching the KLY node🤩