Turning FunC into FunCtional with Haskell: How Serokell Won Telegram Blockchain Competition

You have probably heard that Telegram is going to launch the Ton blockchain platform . But you could have missed the news that not so long ago Telegram announced a competition for the implementation of one or more smart contracts for this platform.







The Serokell team with rich experience in developing large blockchain projects could not stay away. We delegated five employees to the contest, and two weeks later they took first place in it under the (un) modest random nickname Sexy Chameleon. In this article I will talk about how they succeeded. We hope that in the next ten minutes you will at least read an interesting story, and at the maximum you will find something useful in it that you can apply in your work.







But let's start with a little dive into the context.







Competition and its conditions



So, the main tasks of the participants were the implementation of one or more of the proposed smart contracts, as well as making suggestions for improving the TON ecosystem. The competition was held from September 24 to October 15, and the results were announced only on November 15. Quite a long time, given that during this time Telegram managed to conduct and announce the results of contests on design and application development in C ++ for testing and evaluating the quality of VoIP calls in Telegram.







We selected two smart contracts from the list proposed by the organizers. For one of them, we used tools distributed with TON, and the second we implemented in a new language developed by our engineers specifically for TON and built into Haskell.







The choice of a functional programming language is not accidental. In our corporate blog, we often talk about why we consider the complexity of functional languages ​​a great exaggeration and why we generally prefer them to be object-oriented. By the way, it also contains the original of this article .







Why did we decide to participate



In short, because our specialization is non-standard and complex projects that require special skills and are often of scientific value to the IT community. We warmly support open-source development and are engaged in its popularization, as well as cooperate with leading universities in Russia in the field of computer science and mathematics.







The interesting tasks of the competition and involvement in the Telegram project, which we so dearly loved, were in themselves an excellent motivation, but the prize fund became an additional incentive. :)







TON Blockchain Research



We closely monitor new developments in the blockchain, artificial intelligence and machine learning and try not to miss a single significant release in each of the areas in which we work. Therefore, by the time the competition started, our team was already familiar with the ideas from TON white paper . However, before starting work with TON, we did not analyze the technical documentation and the actual source code of the platform, so the first step was quite obvious - a thorough study of the official documentation on the website and in the project repository .







By the beginning of the competition, the code had already been published, so in order to save time, we decided to look for a guide or squeeze written by users . Unfortunately, this did not give a result - apart from the instructions for building the platform on Ubuntu, we did not find other materials.







The documentation itself was thoroughly developed, but it was difficult to read it at some points. Quite often, we had to return to certain points and switch from high-level descriptions of abstract ideas to low-level implementation details.







It would be easier if the specification did not have a detailed description of the implementation at all. Information about how the virtual machine presents its stack is more distracting to developers creating smart contracts for the TON platform than helping them.







Nix: building a project



At Serokell, we are big fans of Nix . We collect our projects for them and deploy them using NixOps , and NixOS is installed on all our servers. Thanks to this, all our builds are reproducible and work under any operating system on which Nix can be installed.







So we started by creating Nix overlay with an expression to build TON . Using it to compile TON is as simple as possible:







$ cd ~/.config/nixpkgs/overlays && git clone https://github.com/serokell/ton.nix $ cd /path/to/ton/repo && nix-shell [nix-shell]$ cmakeConfigurePhase && make
      
      





Note that you do not need to install any dependencies. Nix will magically do everything for you, whether you use NixOS, Ubuntu, or macOS.







Programming for TON



The TON Network smart contract code is executed on the TON Virtual Machine (TVM). TVM is more complicated than most other virtual machines, and has a very interesting functionality, for example, it can work with continuations and links to data .







Moreover, the TON guys created three new programming languages:







Fift is a universal stack programming language reminiscent of Forth . His super ability is the ability to interact with TVM.







FunC is a smart contract programming language that is similar to C and compiled into another language - Fift Assembler.







Fift Assembler - Fift library for generating binary executable code for TVM. Fift Assembler lacks a compiler. It is an embedded domain-specific language (eDSL) .







Our competitive works



Finally, it's time to look at the results of our efforts.







Asynchronous Payment Channel



Payment channel - a smart contract that allows two users to send payments outside the blockchain. As a result, not only money is saved (there is no commission), but also time (you do not have to wait until the next block is processed). Payments can be arbitrarily small and occur as often as required. At the same time, the parties do not have to trust each other, since the fairness of the final settlement is guaranteed by a smart contract.







We found a fairly simple solution to the problem. Two parties can exchange signed messages, each of which contains two numbers - the total amount paid by each of the participants. These two numbers work like vector clocks in traditional distributed systems and set the “happened before” order on transactions. Using this data, the contract will be able to resolve any possible conflict.







In fact, to implement this idea, a single number is enough, but we left both, since we were able to make a more convenient user interface. In addition, we decided to include a payment amount in each message. Without it, if the message is lost for some reason, then although all the amounts and the final calculation will be correct, the user may not notice the loss.







To test our idea, we looked for examples of using such a simple and concise payment channel protocol. Surprisingly, we found only two:







  1. Description of a similar approach, only for the case of a unidirectional channel.
  2. A tutorial that describes the same idea as ours, but without explaining many important details, such as general correctness and the procedure for resolving conflicts.


It became clear that it makes sense to describe our protocol in detail, paying particular attention to its correctness. After several iterations, the specification was ready, and now you too can look at it .







We implemented a contract for FunC, and we wrote the command-line utility for interacting with our contract in Fift, as the organizers recommended. We could choose any other language for our CLI, but it was interesting for us to try Fift in order to see how it shows itself in business.







Honestly, having worked with Fift, we saw no good reason to prefer this language to popular and actively used languages ​​with developed tools and libraries. Programming in the stack language is rather unpleasant, because you have to constantly keep in mind what is on the stack, and the compiler does not help.







Therefore, the only, in our opinion, justification for the existence of Fift is its role as the host language for the Fift Assembler. But wouldn’t it be better to embed the TVM assembler in some existing language, and not come up with a new one for this, essentially the only purpose?







TVM Haskell eDSL



Now it's time to talk about our second smart contract. We decided to develop a multi-signature wallet, but writing another smart contract on FunC would be too boring. We wanted to add some zest, and it became our own assembly language for TVM.







Like Fift Assembler, our new language is embeddable, but instead of Fift we chose Haskell as the host, which allowed us to fully use its advanced type system. When working with smart contracts, where the price of even a small error can be very high, static typing, in our opinion, is a big advantage.







To demonstrate what the TVM assembler built into Haskell looks like, we implemented a standard wallet on it. Here are a few things to watch out for:









Here's what the multisig wallet implementation looks like on our eDSL:







 main :: IO () main = putText $ pretty $ declProgram procedures methods where procedures = [ ("recv_external", decl recvExternal) , ("recv_internal", decl recvInternal) ] methods = [ ("seqno", declMethod getSeqno) ] data Storage = Storage { sCnt :: Word32 , sPubKey :: PublicKey } instance DecodeSlice Storage where type DecodeSliceFields Storage = [PublicKey, Word32] decodeFromSliceImpl = do decodeFromSliceImpl @Word32 decodeFromSliceImpl @PublicKey instance EncodeBuilder Storage where encodeToBuilder = do encodeToBuilder @Word32 encodeToBuilder @PublicKey data WalletError = SeqNoMismatch | SignatureMismatch deriving (Eq, Ord, Show, Generic) instance Exception WalletError instance Enum WalletError where toEnum 33 = SeqNoMismatch toEnum 34 = SignatureMismatch toEnum _ = error "Uknown MultiSigError id" fromEnum SeqNoMismatch = 33 fromEnum SignatureMismatch = 34 recvInternal :: '[Slice] :-> '[] recvInternal = drop recvExternal :: '[Slice] :-> '[] recvExternal = do decodeFromSlice @Signature dup preloadFromSlice @Word32 stacktype @[Word32, Slice, Signature] -- cnt cs sign pushRoot decodeFromCell @Storage stacktype @[PublicKey, Word32, Word32, Slice, Signature] -- pk cnt' cnt cs sign xcpu @1 @2 stacktype @[Word32, Word32, PublicKey, Word32, Slice, Signature] -- cnt cnt' pk cnt cs sign equalInt >> throwIfNot SeqNoMismatch push @2 sliceHash stacktype @[Hash Slice, PublicKey, Word32, Slice, Signature] -- hash pk cnt cs sign xc2pu @0 @4 @4 stacktype @[PublicKey, Signature, Hash Slice, Word32, Slice, PublicKey] -- pubk sign hash cnt cs pubk chkSignU stacktype @[Bool, Word32, Slice, PublicKey] -- ? cnt cs pubk throwIfNot SignatureMismatch accept swap decodeFromSlice @Word32 nip dup srefs @Word8 pushInt 0 if IsEq then ignore else do decodeFromSlice @Word8 decodeFromSlice @(Cell MessageObject) stacktype @[Slice, Cell MessageObject, Word8, Word32, PublicKey] xchg @2 sendRawMsg stacktype @[Slice, Word32, PublicKey] endS inc encodeToCell @Storage popRoot getSeqno :: '[] :-> '[Word32] getSeqno = do pushRoot cToS preloadFromSlice @Word32
      
      





The full source code of our eDSL and the multi-signature wallet contract can be found in this repository. And in more detail our colleague George Agapov spoke about the built-in languages.







Conclusions about the competition and TON



In total, our work took 380 hours (along with acquaintance with the documentation, meetings and the development itself). Five developers took part in the competition: STO, team leader, blockchain platform specialists, and Haskell software developers.







We found the resources to participate in the contest without difficulty, since the spirit of the hackathon, close teamwork, the need for quick immersion in aspects of new technologies are always exciting. A few sleepless nights in order to achieve maximum results in conditions of limited resources are compensated by invaluable experience and excellent memories. In addition, work on such tasks is always a good test of the company's processes, since it is extremely difficult to achieve truly decent results without excellently tuned internal interaction.







Besides the lyrics: we were impressed by the amount of work done by the TON team. They managed to build a complex, beautiful, and most importantly, working system. TON proved to be a platform with great potential. However, in order for this ecosystem to develop, much more needs to be done, both in terms of its use in blockchain projects and in terms of improving development tools. We are proud to be part of this process now.







If after reading this article you still have any questions or ideas about how to apply TON to solve your problems, write to us - we will gladly share our experience.








All Articles