From 34f05f0e2360396409057dda9f8459a22cc6a6e5 Mon Sep 17 00:00:00 2001 From: Gusarich Date: Fri, 27 Jan 2023 08:43:48 +0000 Subject: [PATCH 1/4] wip: random-number-generatin article --- .../guidelines/random-number-generation.md | 117 ++++++++++++++++++ sidebars.js | 1 + 2 files changed, 118 insertions(+) create mode 100644 docs/develop/smart-contracts/guidelines/random-number-generation.md diff --git a/docs/develop/smart-contracts/guidelines/random-number-generation.md b/docs/develop/smart-contracts/guidelines/random-number-generation.md new file mode 100644 index 0000000000..ba420236c1 --- /dev/null +++ b/docs/develop/smart-contracts/guidelines/random-number-generation.md @@ -0,0 +1,117 @@ +# Random number generation + +Generating random numbers is a common task which you may need in many different projects. You might have already seen `random()` function in FunC docs, but note that its result can be easily predicted unless you use some additional tricks. + +## How can someone predict a random number? + +Computers are really bad at generating random information, because all they do is following the instructions of users. But since people really need random numbers often, they came up with different ways of generating _pseudo-random_ numbers. + +These algorithms usually require you to provide some `seed` value which will be used to generate a sequence of pseudo-random numbers. So if you run the same program with the same seed several times, you will eventually get the same result every time. In TON, the seed is different for each block. + + * [Generation of block random seed](https://ton.org/docs/participate/own-blockchain-software/random) + +So in order to predict the result of `random()` function in some smart contract, you just need to know the current seed of the block (which is possible). + +## And what about `randomize_lt()`? + +To make the random number generation a bit more unpredictable, you can add the current `Logical time` to the seed, so different transactions will have different seeds and different results. But the thing is that `Logical time` is easily predictable. + +As was seen in TON Hack Challenge, generating random numbers with just `randomize_lt()` and `random()` is not safe. Take a look at the solution of 4th task called **Lottery**: + - https://github.com/ton-blockchain/hack-challenge-1/blob/master/Solutions/4.%20lottery.md + - https://ton.org/docs/develop/smart-contracts/security/ton-hack-challenge-1#4-lottery + +By writing a simple smart contract you can predict the results of `random()` function even if there was a `randomize_lt()` call. + +## So how do I generate random numbers safely? + +There can possibly be different approaches, but one of the simpliest is just skipping at least one block before generating a number. If we skip a block, the seed will change in a less predictable way, thus your RNG will be a lot safer. + +Skipping blocks is not a complex task. You can do that by simply sending a message to Masterchain and back to the workchain of your contract. Let's have a look at the simple example! + +:::caution +Do not use this example contract in real projects, write your own instead. +::: + +### Masterchain echo-contract + +The purpose of this contract is just to forward the message back to sender. This can be done in a few FunC lines of code: + +```func +() recv_internal (cell in_msg_full, slice in_msg_body) impure { + var cs = in_msg_full.begin_parse(); + var flags = cs~load_uint(4); + if (flags & 1) { ;; ignore bounced messages + return (); + } + slice sender = cs~load_msg_addr(); + send_raw_message( + begin_cell() + .store_uint(0x18, 6) + .store_slice(sender) + .store_coins(0) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) + .store_slice(in_msg_body) + .end_cell(), + 64 + 2 ;; send the remaining value of an incoming msg & ignore errors + ); +} +``` + +Just deploy this contract in Masterchain and let's move to the main contract. + +### Main contract in any workchain + +Let's write a simple lottery contract as an example. User will send 1 TON to it and with a chance of 50% will get 2 TON back. + +```func +;; set the echo-contract address +const echo_address = "Ef8Nb7157K5bVxNKAvIWreRcF0RcUlzcCA7lwmewWVNtqM3s"a; + +() recv_internal (int msg_value, cell in_msg_full, slice in_msg_body) impure { + var cs = in_msg_full.begin_parse(); + var flags = cs~load_uint(4); + if (flags & 1) { ;; ignore bounced messages + return (); + } + slice sender = cs~load_msg_addr(); + + int op = in_msg_body~load_uint(32); + if (op == 0) { ;; bet from user + throw_unless(501, msg_value == 1000000000); ;; 1 TON + + send_raw_message( + begin_cell() + .store_uint(0x18, 6) + .store_slice(echo_address) + .store_coins(0) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) + .store_uint(1, 32) ;; let 1 be echo opcode in our contract + .store_slice(sender) ;; forward user address + .end_cell(), + 64 ;; send the remaining value of an incoming msg + ); + } + elseif (op == 1) { ;; echo + throw_unless(502, equal_slice_bits(sender, echo_address)); ;; only accept echoes from our echo-contract + + slice user = in_msg_body~load_msg_addr(); + + ;; at this point, the random is already unpredictable, so let's just generate it + randomize_lt(); + int x = rand(2); ;; generate a random number (either 0 or 1) + if (x == 1) { ;; user won + send_raw_message( + begin_cell() + .store_uint(0x18, 6) + .store_slice(user) + .store_coins(2000000000) ;; 2 TON + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) + .end_cell(), + 3 ;; ignore errors & pay fees separately + ); + } + } +} +``` + +Deploy this contract in any workchain you need (probably Basechain) and you're done! \ No newline at end of file diff --git a/sidebars.js b/sidebars.js index 27a4624c80..4205c9b0fc 100644 --- a/sidebars.js +++ b/sidebars.js @@ -246,6 +246,7 @@ const sidebars = { 'develop/smart-contracts/guidelines/get-methods', 'develop/smart-contracts/guidelines/accept', 'develop/smart-contracts/guidelines/processing', + 'develop/smart-contracts/guidelines/random-number-generation', 'develop/smart-contracts/governance', 'develop/smart-contracts/guidelines/tips', { From 9d7bc6d8e92b58ab76f312c5245349c83b6bee65 Mon Sep 17 00:00:00 2001 From: Gusarich Date: Sun, 29 Jan 2023 18:15:46 +0000 Subject: [PATCH 2/4] wip: changes in article --- .../guidelines/random-number-generation.md | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/develop/smart-contracts/guidelines/random-number-generation.md b/docs/develop/smart-contracts/guidelines/random-number-generation.md index ba420236c1..43f490cbb2 100644 --- a/docs/develop/smart-contracts/guidelines/random-number-generation.md +++ b/docs/develop/smart-contracts/guidelines/random-number-generation.md @@ -17,10 +17,10 @@ So in order to predict the result of `random()` function in some smart contract, To make the random number generation a bit more unpredictable, you can add the current `Logical time` to the seed, so different transactions will have different seeds and different results. But the thing is that `Logical time` is easily predictable. As was seen in TON Hack Challenge, generating random numbers with just `randomize_lt()` and `random()` is not safe. Take a look at the solution of 4th task called **Lottery**: - - https://github.com/ton-blockchain/hack-challenge-1/blob/master/Solutions/4.%20lottery.md - - https://ton.org/docs/develop/smart-contracts/security/ton-hack-challenge-1#4-lottery + - [Original solution of the 4th task](https://github.com/ton-blockchain/hack-challenge-1/blob/master/Solutions/4.%20lottery.md) + - [Drawing conclusions from TON Hack Challenge \(Task 4\)](/develop/smart-contracts/security/ton-hack-challenge-1#4-lottery) -By writing a simple smart contract you can predict the results of `random()` function even if there was a `randomize_lt()` call. +You can predict the results of `random()` by writing a simple smart contract function even if there was a `randomize_lt()` call. ## So how do I generate random numbers safely? @@ -76,9 +76,9 @@ const echo_address = "Ef8Nb7157K5bVxNKAvIWreRcF0RcUlzcCA7lwmewWVNtqM3s"a; slice sender = cs~load_msg_addr(); int op = in_msg_body~load_uint(32); - if (op == 0) { ;; bet from user + if ((op == 0) & equal_slice_bits(in_msg_body, "bet")) { ;; bet from user throw_unless(501, msg_value == 1000000000); ;; 1 TON - + send_raw_message( begin_cell() .store_uint(0x18, 6) @@ -96,7 +96,10 @@ const echo_address = "Ef8Nb7157K5bVxNKAvIWreRcF0RcUlzcCA7lwmewWVNtqM3s"a; slice user = in_msg_body~load_msg_addr(); - ;; at this point, the random is already unpredictable, so let's just generate it + {- + at this point we have skipped 1+ blocks + so let's just generate the random number + -} randomize_lt(); int x = rand(2); ;; generate a random number (either 0 or 1) if (x == 1) { ;; user won @@ -114,4 +117,10 @@ const echo_address = "Ef8Nb7157K5bVxNKAvIWreRcF0RcUlzcCA7lwmewWVNtqM3s"a; } ``` -Deploy this contract in any workchain you need (probably Basechain) and you're done! \ No newline at end of file +Deploy this contract in any workchain you need (probably Basechain) and you're done! + +## Is this method 100% secure? + +It is safe in most cases, but there is still a **chance of manipulating it**. + +An evil validator with some probability [can affect](/participate/own-blockchain-software/random#conclusion) the `seed`, on which the random depends. Even if this probability is extremely small, it is still worth considering. \ No newline at end of file From e5e6122961d417ed94c0e8f978cf6b6a5a0aee4e Mon Sep 17 00:00:00 2001 From: Gusarich Date: Sun, 29 Jan 2023 18:25:47 +0000 Subject: [PATCH 3/4] wip: move article to security rules --- sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sidebars.js b/sidebars.js index 4205c9b0fc..70929a294b 100644 --- a/sidebars.js +++ b/sidebars.js @@ -225,6 +225,7 @@ const sidebars = { items: [ 'develop/smart-contracts/security/README', 'develop/smart-contracts/security/ton-hack-challenge-1', + 'develop/smart-contracts/guidelines/random-number-generation' ], }, { @@ -246,7 +247,6 @@ const sidebars = { 'develop/smart-contracts/guidelines/get-methods', 'develop/smart-contracts/guidelines/accept', 'develop/smart-contracts/guidelines/processing', - 'develop/smart-contracts/guidelines/random-number-generation', 'develop/smart-contracts/governance', 'develop/smart-contracts/guidelines/tips', { From aa9995ba74b9096ad528af05845f07109ebecdec Mon Sep 17 00:00:00 2001 From: Gusarich Date: Sun, 29 Jan 2023 18:25:57 +0000 Subject: [PATCH 4/4] grammar fix --- .../smart-contracts/guidelines/random-number-generation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/develop/smart-contracts/guidelines/random-number-generation.md b/docs/develop/smart-contracts/guidelines/random-number-generation.md index 43f490cbb2..7720870e2b 100644 --- a/docs/develop/smart-contracts/guidelines/random-number-generation.md +++ b/docs/develop/smart-contracts/guidelines/random-number-generation.md @@ -4,7 +4,7 @@ Generating random numbers is a common task which you may need in many different ## How can someone predict a random number? -Computers are really bad at generating random information, because all they do is following the instructions of users. But since people really need random numbers often, they came up with different ways of generating _pseudo-random_ numbers. +Computers are terrible at generating random information, because all they do is following the instructions of users. But since people really need random numbers often, they came up with different ways of generating _pseudo-random_ numbers. These algorithms usually require you to provide some `seed` value which will be used to generate a sequence of pseudo-random numbers. So if you run the same program with the same seed several times, you will eventually get the same result every time. In TON, the seed is different for each block. @@ -22,7 +22,7 @@ As was seen in TON Hack Challenge, generating random numbers with just `randomiz You can predict the results of `random()` by writing a simple smart contract function even if there was a `randomize_lt()` call. -## So how do I generate random numbers safely? +## So, how do I generate random numbers safely? There can possibly be different approaches, but one of the simpliest is just skipping at least one block before generating a number. If we skip a block, the seed will change in a less predictable way, thus your RNG will be a lot safer. @@ -34,7 +34,7 @@ Do not use this example contract in real projects, write your own instead. ### Masterchain echo-contract -The purpose of this contract is just to forward the message back to sender. This can be done in a few FunC lines of code: +The purpose of this contract is just to forward the message back to the sender. This can be done in a few FunC lines of code: ```func () recv_internal (cell in_msg_full, slice in_msg_body) impure {