Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 40 additions & 12 deletions Chapter 2/Lesson 5.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,40 @@
# The scalable smart contracts. #
What is the big idea of this lesson is that the Ton is designed to scale as a blockchain. But you need to understand what are the tools that it gives you at your disposal to keep your contracts and your application scalable as well. So, in Ton, really the only kind of key feature that allows scaling is the idea that you could shared the blockchain indefinitely down to individual contracts. If you have a system that has billion accounts, or contracts, or the wallets were billion tokens, the blockchain guarantees you that it can scale indefinitely to the whole magnitude of these entities. However, if you're building an application, and it has an account or a single contract that has a large amount of data that stores and processes a lot of transactions, then this becomes a bottleneck and Ton doesn't give you any tools to deal with this. The there's one disclaimer that in the original design, there was an idea to perform, like give the contracts ability to kind of split and merge their own state. But as far as I know, this is not implemented and not supported right now. And there are better ways to make scalable applications and talent just using the single feature that exists, which is sharding of the blockchain itself.
What kind of problems you could encounter if you design your application?
First of all, if you're coming from Aetherium, or if you coming from any traditional kind of client server application development, then you might see a lot of similarities of what token contracts are to what you're familiar with. So in ton contract is a self contained storage and computation device that receives messages from the outside, does some internal processing, updates data, and then spits out some outgoing messages, so it can talk to other contracts this way. And this is pretty kind of familiar to many people and the natural inclination is to take this model and build it like any other application. So let's say you have a decentralized exchange. So the centralized exchange would have an application that gives you like a database of orders, and users and will match the orders in the world perform the traits. And naturally, you could invent something like this and the tone contracts. However, you will quickly find that if you have billions of users, the top blockchain would comfortably store and process the transactions for billions of words. But then, whenever they interact with your application, they will all go to single account, which is your exchange. And this account will be big, and it will potentially grow in like unbounded manner. So the more users you add, the more data it has to store and so it becomes a bottleneck in the network. How exactly will it manifest with this problem? First of all, there will be a bottleneck of transactions that coming in and out of this contract. Because if you like shard indefinitely, the blockchain then it will be just a single shard, just for this contract and will be overloaded still, because there will be billing people trying to access this shard, which is not good. It will slow down everyone's transactions. Second, if it stores the list of orders, and you have millions of users, they probably have millions of orders in this contract. This means that this data of your contract will consume a lot of money to pay for its own rent. You would have to design the system in a way that is protected from running out of money and make sure that whenever people interact with this, they they pay their share of the rent for this contract. And if the put an order and disappear, then disorder should be cleaned up afterwards money expires or something like that. So it kind of adds complexity to your system, you have to kind of invent additional features in your contracts in order for it not to be destroyed, because someone store too much data in it. And another thing is that in turn, you everything's based on the trees of cells and so you don't have just a flat amount of data that you can access, everything's packed into this tree. And the bigger the amount of data you have, the more intermediate steps you have to do to like, unpack the state and this costs, fees in terms of gas. And so every time anyone has to interact with your contract, the bigger the state of this contract is, the more they have to pay in fees to access to state. So if you have just 10 users, there will be like a low cost for them to interact with. There's like 10 items that they store in the contract that if you have 10 million users, then there will be 10 million items they will packed in a giant tree, and there will be significantly more like the cost will probably be like 30 times higher for just to every individual user to interact with this contract. So to summarize, what are what are the issues if you try to put a lot of things in just a single contract. First of all, there's a bottleneck in terms of number of transactions that go in and out and it doesn't short. Second, is that the largest storage requires higher rent, and it becomes complicated to manage the payments for the friends. I mean, if you're paying for this, then you could be attacked by users, like malicious users who would use the state and causes you to pay more fees and lead to denial of service attacks or you have to increase complexity of your contract to kind of like spread this costume on the users. And then you have to do a lot of extra bookkeeping, just to protect yourself against denial of service attacks, and not solving your core application like business logic. And then the third issue is that the larger your amount of data is, the more expensive it becomes to do any changes to this data by any user. So the whole thing becomes kind of heavier and heavier and all the users suffer as a result of this. So how do we solve this problem for as in like a general framework that is applicable to any sort of application. And so there's again, this technology that offers which is sharding of the contracts, it says, Okay, you could have unlimited amount of contracts. This is what Ton says, and you should keep your individual contracts rather small, right. So any, it doesn't specify how small they should be. Because I mean, sometimes you may be like big and fat and expensive, and it will be fine. But we talked talking about scalability, and there should be some clear bound on on the size of the contract.
### List that scales ###
So here's the big idea that I'd like to propose to you as developers. And the idea is kind of crazy. So every time you want to store a list of things, in wide sense of things like its array, or dictionary, or database or whatever, you have no list of items that you want to take care of. In your contract, then the rule number one is you're not allowed to do that within the contract, like the lists are prohibited by default. So the only way to create a list would be to use the only list that scales, which is the blockchain itself. So you're allowed to have the blockchain state as a whole, like the the entirety of all the contracts, in other words, as your mechanism for storing lists of things, that's the only thing you're allowed to do. I mean, technically, in tvm, it supports lists and tuples, and hash maps and you can use all bound you can have unbounded loops that iterate over them, you're totally allowed to do that on a technical level. But this is not something that you should. So here's the idea. So by default, try to figure out how to have the constant size, state and constant cost of execution of every contract and Do whatever you need to store list of things, then figure out how to outsource them to separate contract entities. An example, if you have multi user application, and users have some balances in your system, then the obvious approach would be to tokenize those bounces. So your application will have a central contract that acts as an issuer and some kind of authentication device, it may have it could be quite big, it could have a number of entry points and do a lot of things. But there will be bounded amount of things that will be like 5 or 10, or like 14, but it will not grow with number of users. And some of those things would be issuing and reclaiming the tokens that this contract creates. So every time the user wants to create an account, you issue them tokens using the jetten standard, for instance. And this allows you to infinitely scale up your database of users because now the blockchain itself that takes care of tracking who are the users of your application, because they keep track of their own tokens and only when those users want to interact with the app and the for instance, send the messages through those token contracts. And at this point, the central contract will authenticate those requests again, in constant time, it will just check that the token is correctly issued. And that's the correct user coming in. And then it allows the action for this users, let's say plus voting it allows withdrawing funds or doing conversions, or whatever you do. So idea number one is to like bounce execution and tokenize participation.
### tokenize ###
And then the second idea is that you could tokenize not just something that has monetary value, you could tokenize, all sorts of things. So even if you have a temporary state of account, let's say you have a staking pool, and the user comes in and says okay, I want to deposit money in this pool. And due to the way the pool is designed to not accept the money and exchange it for a share in the pool right away, because the pool is in the process of the validation cycle. And you don't the price yet. So there's some reasons to delay the separation. But you want to help the user to deposit the money not to wait for some good window and time. And you want to remember internally that these are deposited some amount of money and you owe them some share to be issued later, maybe half a day or something like that. So how do you remember this temporary transient state, if you can, like go back to the traditional model, where you have a lot of like a local database, then you would keep a list of users pending requests and that again, would not scale on top because then the million users could come in, could fill in this corrupted like table of the spending requests, and your whole system would go belly up so that would be effectively denial of service attack on your application. So again, if you have this transit state, what do you do, you will sort of tokenize this state, so you accept money from the user, and you give them back something like a receipt. So something like what you would do in real life, you would issue them a paper receipt that they would take with them. And then you don't have to keep track of all the issued receipts like an urgent book, you will just remember that this receipt is this or you will do you will design your system in a way that makes authentication of this receipt secure, just like you do authentication of any token or any NFT or like fungible token or anything like that. And this receipt will encode specific like date, time and end data that is important to remember the user states for instance, in case of the sticking points would remember how much and for which, how many ton coins and for which validation cycle, those coins were deposited. And you would create this token contract that would be assigned to the user and you would accept the money and kind of park them in one giant pile of coins. Not keep track of individual contributions to will be just a single balance. And then later when your validation cycle is ready to begin, and how to do the do the conversion, then the user could send this receipt to your system and say, Hey, like, I have this pending thing, could you replace it with the proper pool shares. And this is when you could authenticate this data, remember the state of the user and do the conversion based on the current price. And this approach allows you to make the system that has constant size state, bounded number of entry points, and bounded execution for each entry point. So you never stored the list of anything, the only list you have is the blockchain itself. And for any kind of per user state, you will issue specific dedicated tokens. So as an example, we have a prototype of a staking pool that has seven total tunnel number seven contracts that encode different states for different users in this pool. And one of them was this central entry point, and others are various tokenized in a state representations.
### issue with gas and rent ###
And one more thing back to the beginning of the talk. There is an issue with gas and rent is that if you haven't bothered execution, then it becomes really hard to analyze how much in fees the users have to pay. So on one hand, you could have a denial of service attacks. On the other hand, the cost could be high for the users. But there's a third way that is, like the kind of more realistic is where you don't even know exactly what the cost is and this means that whatever applications interact with the system, they would start failing and losing users money, because they will try to attach insufficient amount of coins to pay for the costs and the costs grew out of some usual bounds and suddenly, people start to see failures. And sometimes in a poorly designed system, those failures could be somewhere, not on the outside, where just the user tries to initiate the operation fails fairly, it just loses some a little bit of coins on the feet, but could be somewhere in the chain of communication between various contracts. And then some contract was always touching one coin. But but your system suddenly grew larger and requires slightly more than one coin, and this interaction fails in that suddenly you're in a chain of interaction with the contracts starts failing in the middle. And other parts enter some kind of inconsistent state, and this would be horrible for you. So it's your obligation that's so designed to scale with suddenly fail in a very inconsistent manner. Not for scalability reasons, but because everything starts falling apart, because the costs grow out of usual bounds. And this is very bad. And this is one more reason why you want to keep your contracts going about it. And have very explicit fixed costs for storage and for execution. So one more note on that is that sometimes you, you're of course allowed to have loops, and lists and contracts. The tricky part is, of course, to keep it bounded, and like in which cases, it is possible to.
### conclusion ###
So as I said, your priority number one is to avoid the list at all costs, right, so you don't have any any variable length data. The second priority would be to allow storing data in a list that is managed by a single user and owned by that user. One example is the list of plugins in your users wallet. So the user may add a number of plugins into their wallet, it's under their control, they usually have a limited amount of those, they pay the fees to they pay the rent for these plugins, and they keep a reasonable amount of ton coins on the balance would be like peace for the rent. So this is an example where you have a variable that data, but it's fully managed by the user. So there is no possibility for anyone from outside to kind of explore the state. In other kind of like this is going to secondary priorities when this list is fixed in size. So let's imagine you have a multi user multi signature account where you have like you and your cofounders of a company, manage shared pool of money. And you naturally want to encode this as a list of participants. And you can totally do that. Because from the perspective of the system, this list is fixed. So it's not going to grow arbitrarily by slamming away any of the external actors, like the whole thing could be defined from this start written down in the contract. And then from the point of view of the network, it's a fixed size list with fixed costs of interacting with that, although looking at the code, you will definitely see that there is there is an array of things and there is a loop and such, and this is also totally scalable approach. I mean, unless you want to have this fixed size list contain millions of people then of course, it would be much more expensive, but then, again, try to tokenize your contract.
# Scalable contracts

###### tags: `Chapter 2`

*Let's talk about scalability.*

> :gem: **In TON, you really have a very specific guarantee around scalability. TON guarantees you that it can scale down to granularity of individual contracts.**

<u>Therefore, operations on one contract will not create a bottleneck :baby_bottle: for operations on another contract. And then the TON network obviously will take care of all the message routing :bullettrain_front: between them.</u>

> :exclamation: **It's your job to take advantage of the entire scalability of blockchain and not create bottlenecks in your own contracts!**

## Problems with monolithic contracts.

*One of the obvious problems for people who are coming from traditional systems, the large scale web databases or from Ethereum, is that they design their contracts as a **monolithic applications** with their own variable length storage.*

> :globe_with_meridians: *For instance, in Ethereum, the token is implemented as a banking ledger, where you have a single smart contract that contains the list of accounts. And then if you have a million users, this list will be a million rows long. And there will be an entry for each user saying what is the address of the user. And what is their balance. And then designing such contracts is pretty easy and straightforward. You have atomic transactions, you deduct from one balance and add to another. And it's very easy to design and see how the system would work.
**However, this model will not scale in TON because you would put this long list in a single contract and you will immediately run into issues.***

1. <u> :one: First, you will have progressively growing cost of rent for all of this data that you store in your contract. </u> :money_with_wings:
2. <u> :two: Second, every time the user comes along and wants to make an operation with this contract, they will have to pay the larger and larger transaction fees because they will be operating on the larger amount of data in the contract. </u> :sunrise_over_mountains:

## Rules.

*To help you, give you a guideline on how to properly design multi-user and large scale applications and TON, there are some rules. :newspaper:*

> 1. :exclamation: *Avoid having variable length data.*
> 2. :exclamation: *If you have to have a list, then at least make it short and bounded, like statically defined.*
> 3. :exclamation: *If you need to have a list and it should be dynamically growing, then at least make sure that this contract is owned by a single user and they have the exclusive right to control its storage.*
> <u> A simple example are the records in the TON DNS items. </u>

## Blockchcain as an array.

*The scalable way to make large lists or dictionaries of data in TON is to use blockchain itself as an array. Here's a simple example.*

> <u>*You could imagine a system where you have multiple contracts that are connected :pushpin: with each other, and every user is taking care of their own individual contract. And you don't have to store the entire list inside your central part of the application.*</u>

> :gem: **The most popular example of implementing this idea is the design of tokens in TON ecosystem.**
Balances of individual users are spread in so-called Jetton wallets.
> :fire: **What's cool about that is that the operations between two Jeton wallets in one place do not interfere with operations of the other two in some other place, and they could be spread among the separate shardchains and not create any bottleneck.**