1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

172
doc/FullNode-HOWTO Normal file
View file

@ -0,0 +1,172 @@
The aim of this document is to provide step-by-step instructions for setting up a full node for the TON Blockchain. We assume some familiarity with the TON Blockchain Lite Client, at least to the extent explained in the Lite Client HOWTO.
Notice that you need a machine with a public IP address and a high-bandwidth network connection to run a TON Blockchain Full Node. Typically you'll need a sufficiently powerful server in a datacenter. It is a bad idea to run a Full Node on your home computer; instead, you could run a Full Node on a remote server, and use the TON Blockchain Lite Client to connect to it from home.
0. Downloading and compiling
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The complete TON Blockchain Library and Validator software is downloaded and compiled similarly to the Lite Client. This process is outlined in the corresponding README file. The most important difference is that you have to download the complete source archive available at https://test.ton.org/ton-blockchain-full.tar.xz instead of the smaller Lite Client source archive. You should also build all goals defined in CMakeLists.txt (e.g. by running `cmake <path-to-source-directory>` and `make` in your build directory), not only those specifically related to the Lite Client (which is also included in the larger distribution).
1. Full Node binaries
~~~~~~~~~~~~~~~~~~~~~
After the sources have been compiled successfully, you should obtain executable files `validator-engine/validator-engine` and `validator-engine-console/validator-engine-console` in your build directory. These are the most important files you need to run a TON Blockchain Full Node (or even a Validator), and to control it. You might wish to install them into your /usr/bin or similar directory. You are also likely to need the `generate-random-id` utility during setup.
2. Working directory of the Full Node
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Full Node (also known as "validator-engine") stores its data in subdirectories of its working directory, e.g., `/var/ton-work/db`. It requires write access to this directory. If you want to use another directory as the working directory of the Full Node, you can use the command line option --db <path-to-work-dir>:
$ validator-engine --db ${DB_ROOT}
where ${DB_ROOT} is /var/ton-work/db or any other directory where validator-engine has write permissions.
3. Working directory layout
~~~~~~~~~~~~~~~~~~~~~~~~~~~
An approximate layout of the working directory of the TON Blockchain Full Node software is as follows:
* ${DB_ROOT}/config.json -- Automatically generated configuration file. It is automatically regenerated by validator-engine on some occasions. When validator-engine is not running, you can edit this file in a text editor because it is a JSON file.
* ${DB_ROOT}/static -- A directory with files that cannot be downloaded from the network, such as the "zerostate" (corresponding to the Genesis block of other blockchain architectures) of the masterchain and active workchains. Normally you don't have to initialize this directory unless you want to run your own instance of the TON Blockchain (for example, for testing or development purposes). Full nodes of existing instances of the TON Blockchain (such as the "testnet" and the "mainnet") will be able to download all required files from already running full nodes.
* ${DB_ROOT}/keyring -- Stores public and private keys known to validator-engine. For example, if your full node runs as a validator for some of the TON Blockchain shardchains, the validator block signing key is kept here. You may wish to set more restrictive permissions for this directory, such as 0700 (in *nix systems), so that only the user under which validator-engine is running would have access to this directory.
* ${DB_ROOT}/error -- A directory where validator-engine copies files related to severe error conditions (e.g., invalid block candidates) for further study. It is normally empty, and validator-engine never deletes files from this directory.
* ${DB_ROOT}/archive -- A directory where old and rarely used blocks are kept until their storage period expires. You can mount a larger but slower disk partition at this directory, or make this directory a symlink to a directory in such a partition. We recommend locating the remainder of ${DB_ROOT} in a fast storage device such as an SSD.
* ${DB_ROOT}/etc -- (Non-automatic) configuration files may be kept here, or in any other directory read-accessible to validator-engine.
* Other subdirectories of ${DB_ROOT} are used to keep ADNL cache data, recent blocks and states, and so on. They are not relevant to the purposes of this document.
4. Global configuration of the TON Blockchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order to set up your Full Node, you'll need a special JSON file called the "global configuration (file)". It is called this because it is the same for all full nodes, and even nodes participating in different instances of the TON Blockchain (e.g., "testnet" vs. "mainnet") share an almost identical global configuration.
The "testnet" global configuration can be downloaded at https://test.ton.org/ton-global.config.json as follows:
$ wget https://test.ton.org/ton-global.config.json
You may wish to put this file into /var/ton-work/etc/ton-global.config.json .
We'll discuss the structure of this file later in more detail. For now, let us remark that the bulk of this file consists of a list of known TON DHT nodes required for the bootstrapping of the TON Network. A smaller section near the end of this file describes the particular instance of the TON Blockchain that we wish to connect to.
All instances of the TON Blockchain use the same "global" TON Network (i.e., the TON Network is not fragmented into several instances for each blockchain instance). While the global network configuration is therefore independent of the particular TON Blockchain instance chosen, the Full Nodes belonging to different instances will later connect to different overlay subnetworks inside the TON Network.
It is important to distinguish this "global configuration file", used for setting up a TON Blockchain Full Node, and the "local" or "automatic configuration file", automatically updated by validator-engine and usually stored in ${DB_ROOT}/config.json. The global configuration is used only once to generate the initial automatic configuration file, which is thereafter updated by validator-engine itself (e.g., by adding new DHT nodes or storing hashes of newer masterchain blocks).
5. Initializing the local configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once the global configuration file is downloaded, it can be used to create the initial local configuration in ${DB_ROOT}/config.json. To do this, execute validator-engine once:
$ validator-engine -C /var/ton-work/etc/ton-global.config.json --db /var/ton-work/db/ --ip <IP>:<PORT> -l /var/ton-work/log
Here `/var/ton-work/log` is the log directory of `validator-engine`, where it will create its log files. The argument to the `-C` command-line option is the global configuration file downloaded from test.ton.org as explained above, and `/var/ton-work/db/` is the working directory ${DB_ROOT}. Finally, <IP>:<PORT> are the global IP address of this full node (you need to indicate a public IPv4 address here) and the UDP port used to run TON Network protocols such as ADNL and RLDP. Make sure that your firewall is configured to pass UDP packets with source or destination <IP>:<PORT> at least for the `validator-engine` binary.
When validator-engine is invoked as above, and ${DB_ROOT}/config.json does not exist, it creates a new local configuration file ${DB_ROOT}/config.json using the information from the global configuration file and from the command-line options such as --ip, and then exits. If ${DB_ROOT}/config.json already exists, it is not rewritten; instead the global configuration is ignored, and validator-engine starts up as a daemon using the local configuration.
If you need to change the local configuration afterwards, you'll need to either delete this file and regenerate it from the global configuration (potentially forgetting other important information accumulated in the local configuration), or edit the local configuration in a text editor (when validator-engine is not running).
6. Setting up remote control CLI
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You will almost surely want to enable validator-engine-console in the local configuration, to be able to control your Full Node (i.e., validator-engine daemon) when it is running. For this, you'll need to generate two keypairs, one for the server (validator-engine) and one for the client (validator-engine-console). In the examples below we assume that validator-engine-console runs on the same machine and connects to validator-engine through the loopback network interface. (This is not necessarily so; you can use validator-engine-console for remote control as well.)
As a first step, use the `generate-random-id` executable to create two keypairs, one for the server (on the machine running `validator-engine`) and one for the client (on the machine running `validator-engine-console`):
$ ./generate-random-id -m keys -n server
6E9FD109F76E08B5710445C72D2C5FEDE04A96357DAA4EC0DDAEA025ED3AC3F7 bp/RCfduCLVxBEXHLSxf7eBKljV9qk7A3a6gJe06w/c=
This utility generates a new keypair and saves the private key into file `server` and the public key into `server.pub`. The hexadecimal (6E9F...F7) and the base64 ("bp/RC...6wc/=") representations of the public key are displayed in the standard output, and are used henceforth to identify this key.
We have to install the private key `server` into the keyring of the Full Node (validator-engine):
$ mv server /var/ton-work/db/keyring/6E9FD109F76E08B5710445C72D2C5FEDE04A96357DAA4EC0DDAEA025ED3AC3F7
Notice that the file name to store this private key inside the keyring equals the hexadecimal identifier (which essentially is a hash of the public key) of this key.
Next, we generate the client keypair:
$ ./generate-random-id -m keys -n client
8BBA4F8FCD7CC4EF573B9FF48DC63B212A8E9292B81FC0359B5DBB8719C44656 i7pPj818xO9XO5/0jcY7ISqOkpK4H8A1m127hxnERlY=
We obtain a client keypair, saved into files `client` and `client.pub`. This second operation should be run in the working directory of the client (validator-engine-console), possibly on another machine.
Now we have to list the client's public key in the server's local configuration file ${DB_ROOT}/config.json. To do this, open the local configuration file in a text editor (after terminating validator-engine if it was running) and find the empty "control" section:
"control": [
]
Replace it with the following:
"control" : [
{ "id" : "bp/RCfduCLVxBEXHLSxf7eBKljV9qk7A3a6gJe06w/c=",
"port" : <CONSOLE-PORT>,
"allowed" : [
{ "id" : "i7pPj818xO9XO5/0jcY7ISqOkpK4H8A1m127hxnERlY=",
"permissions" : 15
}
]
}
],
`control.0.id` is set to the base64 identifier of the server's public key, and `control.0.allowed.0.id` is the base64 identifier of the client's public key. <CONSOLE-PORT> is the UDP port the server will listen to for console commands.
7. Running the Full Node
~~~~~~~~~~~~~~~~~~~~~~~~
To run the full node, simply run the validator-engine binary in a console:
$ validator-engine --db ${DB_ROOT} -l /var/ton-work/log
It will read the local configuration from ${DB_ROOT}/config.json, and continue running silently. You should write suitable scripts for invoking validator-engine as a daemon (so that it does not terminate when the console is closed), but we'll skip these considerations for simplicity. (Using "nohup" is a cheap way of achieving this on some Linux systems.)
If the configuration is invalid, validator-engine will terminate immediately and, in most cases, output nothing. You'll have to study the log files under /var/ton-work/log to find out what went wrong. Otherwise, validator-engine will keep working silently. Again, you can understand what's going on by inspecting the log files, and by looking into subdirectories of the ${DB_ROOT} directory.
If everything works as expected, validator-engine will locate other full nodes participating in the same instance of the TON Blockchain, and download recent blocks of the masterchain and all shardchains. (You can actually control the number of recent blocks to be downloaded, or even download them all starting from the zerostate---but this topic is outside the scope of this document; try running validator-engine with command-line option `-h` to find out the list of all available options with brief explanations).
8. Using the Console CLI
~~~~~~~~~~~~~~~~~~~~~~~~
If the validator-engine-console has been set up as explained in Section 6., you can use it to connect to the running validator-engine (i.e., your Full Node) and run simple status and key management queries:
$ ./validator-engine-console -k client -p server.pub -a <IP>:<CLIENT-PORT>
connecting to [<IP>:<CLIENT-PORT>]
local key: 8BBA4F8FCD7CC4EF573B9FF48DC63B212A8E9292B81FC0359B5DBB8719C44656
remote key: 6E9FD109F76E08B5710445C72D2C5FEDE04A96357DAA4EC0DDAEA025ED3AC3F7
conn ready
> gettime
received validator time: time=1566568904
The `gettime` command obtains the current Unix time at the validator. If everything has been configured properly, you'll see an output similar to the one above. Notice that you need both the client's private key ("client") and the server's public key ("server.pub") for the console to work. You might wish to move them (especially the client's private key) into a separate directory with suitable permissions.
Other console commands are available in validator-engine-console. For instance, `help` displays a list of all console commands with short descriptions. In particular, they are used to set up the Full Node as a Validator for the TON Blockchain, as explained in the separate document Validator-HOWTO.
9. Setting up the Full Node as a Lite Server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can set up your Full Node to function as a Lite Server, so that you can use the Lite Client to connect to it from the same or a remote host. For instance, sending the command `last` in a Lite Client connected to your Full Node will display the identifier of the most recent masterchain block known to your Full Node, so that you can inspect the progress of block downloading. You can also inspect the state of all smart contracts, send external messages (e.g., wallet queries), and so on as explained in the Lite Client HOWTO.
In order to set up your Full Node as a Lite Server, you have to generate another keypair and install the private key into the server's keyring, similarly to what we did to enable the remote console:
$ utils/generate-random-id -m keys -n liteserver
BDFEA84525ADB3B16D0192488837C04883C10FF1F4031BB6DEECDD17544F5347 vf6oRSWts7FtAZJIiDfASIPBD/H0Axu23uzdF1RPU0c=
mv liteserver ${DB_ROOT}/keyring/BDFEA84525ADB3B16D0192488837C04883C10FF1F4031BB6DEECDD17544F5347
After that, stop validator-engine if it is running and open the local configuration file ${TON_DB}/config.json in a text editor. Find the empty section
"liteservers" : [
]
and replace it with a record containing the TCP port for listening to inbound Lite Client connections and the lite server's public key:
"liteservers" : [
{
"id" : "vf6oRSWts7FtAZJIiDfASIPBD/H0Axu23uzdF1RPU0c=",
"port" : <TCP-PORT>
}
],
Now start `validator-engine` again. If it does not terminate immediately, it is likely that you have re-configured it properly. Now you can use the lite-client binary (usually located as "lite-client/lite-client" with respect to the build directory) to connect to the Lite Server running as a part of your Full Node:
$ lite-client -a <IP>:<TCP-PORT> -p liteserver.pub
Again, `help` lists all commands available in the Lite Client. The Lite Client HOWTO contains some examples of what can be done with the Lite Client.

485
doc/LiteClient-HOWTO Normal file
View file

@ -0,0 +1,485 @@
The aim of this document is to provide step-by-step instructions for compiling and creating a simple smart contract (a simple wallet) in the TON Blockchain Test Network using the TON Blockchain Lite Client and associated software.
Download and installation instructions may be found in README. We assume here that the Lite Client is already properly downloaded, compiled and installed.
1. Smart-contract addresses
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Smart-contract addresses in the TON Network consist of two parts: (a) the workchain ID (a signed 32-bit integer) and (b) the address inside the workchain (64-512 bits depending on the workchain). Currently, only the masterchain (workchain_id=-1) and occasionally the basic workchain (workchain_id=0) are running in the TON Blockchain Test Network. Both of them have 256-bit addresses, so we henceforth assume that workchain_id is either 0 or -1 and that the address inside the workchain is exactly 256-bit.
Under the conditions stated above, the smart-contract address can be represented in the following forms:
A) "Raw": <decimal workchain_id>:<64 hexadecimal digits with address>
B) "User-friendly", which is obtained by first generating:
- one tag byte (0x11 for "bounceable" addresses, 0x51 for "non-bounceable"; add +0x80 if the address should not be accepted by software running in the production network)
- one byte containing a signed 8-bit integer with the workchain_id (0x00 for the basic workchain, 0xff for the masterchain)
- 32 bytes containing 256 bits of the smart-contract address inside the workchain (big-endian)
- 2 bytes containing CRC16-CCITT of the previous 34 bytes
In case B), the 36 bytes thus obtained are then encoded using base64 (i.e., with digits, upper- and lowercase Latin letters, '/' and '+') or base64url (with '_' and '-' instead of '/' and '+'), yielding 48 printable non-space characters.
Example:
The "test giver" (a special smart contract residing in the masterchain of the Test Network that gives up to 20 test Grams to anybody who asks) has the address
-1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
in the "raw" form (notice that uppercase Latin letters 'A'..'F' may be used instead of 'a'..'f')
and
Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb (base64) or
Ef-BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb (base64url)
in the "user-friendly" form (to be displayed by user-friendly clients). Notice that both forms (base64 and base64url) are valid and must be accepted.
2. Inspecting the state of a smart contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Inspecting the state of smart contracts with the aid of the TON Lite Client is easy. For the sample smart contract described above, you would run the Lite Client and enter the following commands:
> last
...
> getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
or
> getaccount Ef-BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb
You will see something like this:
------------------------------------
got account state for -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D with respect to blocks (-1,8000000000000000,1645):4D5E1B928490BE34A4F03C9BC996661B8AD4E988F1460DB69BE3634B1E843DAF:9D85BB116D1DA4BFD999E9092BBD43B3A9F96EC9C9ED7AFDC81B27B08D3861A0 and (-1,8000000000000000,1645):4D5E1B928490BE34A4F03C9BC996661B8AD4E988F1460DB69BE3634B1E843DAF:9D85BB116D1DA4BFD999E9092BBD43B3A9F96EC9C9ED7AFDC81B27B08D3861A0
account state is (account
addr:(addr_std
anycast:nothing workchain_id:-1 address:x8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:3)
bits:(var_uint len:2 value:539)
public_cells:(var_uint len:0 value:0)) last_paid:0
due_payment:nothing)
storage:(account_storage last_trans_lt:0
balance:(currencies
grams:(nanograms
amount:(var_uint len:7 value:1000000000000000))
other:(extra_currencies
dict:hme_empty))
state:(account_active
(
split_depth:nothing
special:nothing
code:(just
value:(raw@^Cell
x{}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
))
data:(just
value:(raw@^Cell
x{}
x{00000000}
))
library:hme_empty))))
x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C0000000000000000000000001C0E35FA931A000134_}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
x{00000000}
------------------------------------
The first information line "got account state ... for ..." shows the account address and the masterchain block identifier with respect to which the account state has been dumped. Notice that even if the account state changes in a subsequent block, the `getaccount xxx` command will return the same result until the reference block is updated to a newer value by a `last` command. In this way one can study the state of all accounts and obtain consistent results.
The "account state is (account ... " line begins the pretty-printed deserialized view of the account state. It is a deserialization of TL-B data type Account, used to represent account states in the TON Blockchain as explained in the TON Blockchain documentation. (You can find the TL-B scheme used for deserialization in the source file crypto/block/block.tlb; notice that if the scheme is out of date, the deserialization may break down.)
Finally, the last several lines beginning with x{CFF538... (the "raw dump") contain the same information displayed as a tree of cells. In this case, we have one root cell containing the data bits CFF...134_ (the underscore means that the last binary one and all subsequent binary zeroes are to be removed, so hexadecimal "4_" corresponds to binary "0"), and two cells that are its children (displayed with one-space indentation).
We can see that x{FF0020DDA4F260...} is the code of this smart contract. If we consult the Appendix A of the TON Virtual Machine documentation, we can even disassemble this code: FF00 is SETCP 0, 20 is DUP, DD is IFNOTRET, A4 is INC, F260 is THROWIF 32, and so on. (Incidentally, you can find the source code of this smartcontract in the source file crypto/block/mc0.fif .)
We can also see that x{00000000} (the actual value you see may be different) is the persistent data of this smart contract. It is actually an unsigned 32-bit integer, used by the smart contract as the counter of operations performed so far. Notice that this value is big-endian (i.e., 3 is encoded as x{00000003}, not as x{03000000}), as are all integers inside the TON Blockchain.
The current balance of the smart contract is easily seen in the pretty-printed portion of the output. In this case, we see ... balance:(currencies:(grams:(nanograms:(... value:1000000000000000...)))), which is the balance of the account in (test) nanograms (a million test Grams in this example; the actual number you see may be smaller). If you study the TL-B scheme provided in crypto/block/scheme.tlb, you will be able to find this number (10^15) in binary big-endian form in the raw dump portion as well (it is located near the end of the data bits of the root cell).
3. Compiling a new smart contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before uploading a new smart contract into the TON Blockchain, you need to determine its code and data and save them in serialized form into a file (called a "bag-of-cells" or BOC file, usually with a .boc suffix). Let us consider the case of a simple wallet smart contract, which stores a 32-bit operations counter and a 256-bit Ed25519 public key of its owner in its persistent data.
Obviously, you'll need some tools for developing smart contracts - namely, a TON smart contract compiler. Basically, a TON smart contract compiler is a program that reads the source of a smart contract in a specialized high-level programming language and creates a .boc file from this source.
One such tool is the Fift interpreter, which is included in this distribution and can help create simple smart contracts. You may wish to develop more sophisticated tools. However, Fift is sufficient for demonstration purposes.
Create the file `new-wallet.fif` containing the source of our new smart contract:
------------------------------------
#!/usr/bin/fift -s
"Asm.fif" include
0 constant wc // create a wallet in workchain 0 (basechain)
// Create new simple wallet
<{ SETCP0 DUP IFNOTRET INC 32 THROWIF // return if recv_internal, fail unless recv_external
512 INT LDSLICEX DUP 32 PLDU // sign cs cnt
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk
s1 s2 XCPU // sign cs cnt pubk cnt' cnt
EQUAL 33 THROWIFNOT // ( seqno mismatch? )
s2 PUSH HASHSU // sign cs cnt pubk hash
s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk
CHKSIGNU // pubk cs cnt ?
34 THROWIFNOT // signature mismatch
ACCEPT
SWAP 32 LDU NIP
DUP SREFS IF:<{
8 LDU LDREF // pubk cnt mode msg cs
s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent )
}>
ENDS
INC NEWC 32 STU 256 STU ENDC c4 POPCTR
}>c
// code
<b 0 32 u,
newkeypair swap dup constant wallet_pk
"new-wallet.pk" B>file
B,
b> // data
// no libraries
<b b{00110} s, rot ref, swap ref, b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hash dup constant wallet_addr
."new wallet address = " wc . .": " dup x. cr
wc over 7 smca>$ type cr
256 u>B "new-wallet.addr" B>file
<b 0 32 u, b>
dup ."signing message: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint rot
<b b{1000100} s, wc 8 i, wallet_addr 256 u, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
dup ."External message for initialization is " <s csr. cr
2 boc+>B dup Bx. cr
"new-wallet-query.boc" tuck B>file
."(Saved to file " type .")" cr
--------------------------------------------
Incidentally, you can find a more sophisticated version of this sample file in crypto/smartcont/new-wallet.fif. It accepts command-line arguments, so you don't need to edit the source file each time you want to create a new wallet.
Now, provided that you have compiled Fift binary (usually located as "crypto/fift" with respect to the build directory), you can run
crypto/fift -I<source-directory>/crypto/fift/lib new-wallet.fif
assuming that you have copied new-wallet.fif into the current directory. Alternatively, you might skip the source editing phase and simply run
crypto/fift -I<source-directory>/crypto/fift/lib -s <source-directory>/smartcont/new-wallet.fif 0 my_wallet_name
where 0 is the workchain to contain the new wallet (0 = basechain, -1 = masterchain), `my_wallet_name` is any identifier you wish to be associated with this wallet. The address of the new wallet will be saved into file `my_wallet_name.addr`, its newly-generated private key will be saved to `my_wallet_name.pk` (unless this file already exists; then the key will be loaded from this file instead), and the external message will be saved into my_new_wallet-query.boc. If you do not indicate the name of your wallet (`my_wallet_name` in the example above), the default name `new-wallet` is used.
You may wish to set the FIFTPATH environment variable to <source-directory>/crypto/fift/lib, the directory containing Fift.fif and Asm.fif library files; then you can omit the -I argument to the Fift interpreter.
If everything worked, you'll see something like the following
--------------------------------------------
StateInit: x{34_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54}
x{00000000F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA3}
new wallet address = -1 : 60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0
0f9gwEFBxqe5bWhhXnqR0mWtDzqaki6a6ckB1PqD9dPA0EKD
signing message: x{00000000}
External message for initialization is x{89FEC18082838D4F72DAD0C2BCF523A4CB5A1E7535245D35D39203A9F507EBA781A0119401748E6F89C1BA026A363C9F58765508DFF6854475357210D0D69F07C3A5453CEEDF1A0383FC405B57FF10CE060C2377BDD954A336DE5161F0AC1C61084180E00000001_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54}
x{00000000F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA3}
B5EE9C724104030100000000DA0002CF89FEC18082838D4F72DAD0C2BCF523A4CB5A1E7535245D35D39203A9F507EBA781A0119401748E6F89C1BA026A363C9F58765508DFF6854475357210D0D69F07C3A5453CEEDF1A0383FC405B57FF10CE060C2377BDD954A336DE5161F0AC1C61084180E0000000100102008CFF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54004800000000F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA32DB9BE63
(Saved to file new-wallet-query.boc)
--------------------------------------------
In a nutshell, the Fift assembler (loaded by the "Asm.fif" include line) is used to compile the source code of the smart contract (contained in <{ SETCP0 ... c4 POPCTR }> lines) into its internal representation. The initial data of the smart contract is also created (by <b 0 32 u, ... b> lines), containing a 32-bit sequence number (equal to zero) and a 256-bit public key from a newly-generated Ed25519 keypair. The corresponding private key is saved into the file `new-wallet.pk` (be careful not to run this code twice in the same directory, otherwise this private key file will be overwritten).
The code and data for the new smart contract are combined into a StateInit structure (in the next lines), the address of the new smart contract (equal to the hash of this StateInit structure) is computed and output, and then an external message with a destination address equal to that of the new smart contract is created. This external message contains both the correct StateInit for the new smart contract and a non-trivial payload (signed by the correct private key).
Finally, the external message is serialized into a bag of cells (represented by B5EE...BE63) and saved into the file `new-wallet-query.boc`. Essentially, this file is your compiled smart contract with all additional information necessary to upload it into the TON Blockchain.
4. Transferring some funds to the new smart contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You might try to upload the new smart contract immediately by running the Lite Client and typing
> sendfile new-wallet-query.boc
or
> sendfile my_new_wallet-query.boc
if you chose to name your wallet `my_new_wallet`.
Unfortunately, this won't work, because smart contracts must have a positive balance to be able to pay for storing and processing their data in the blockchain. So you have to transfer some funds to your new smart contract address first, displayed during its generation as -1:60c0...c0d0 (in raw form) and 0f9..EKD (in user-friendly form).
In a real scenario, you would either transfer some Grams from your already existing wallet, ask a friend to do so, or buy some Grams at a cryptocurrency exchange, indicating 0f9...EKD as the account to transfer the new Grams to.
In the Test Network, you have another option: you can ask the "test giver" to give you some test Grams (up to 20). Let us explain how to do it.
5. Using the test giver smart contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You need to know the address of the test giver smart contract. We'll assume that it is -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d, or, equivalently, Ef-BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb, as indicated in one of the previous examples. You inspect the state of this smart contract in the Lite Client by typing
> last
> getaccount Ef-BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb
as explained above in Section 2. The only number you need from the output is the 32-bit sequence number stored in the smart contract data (it is zero in the example above, but generally it will be non-zero).
Next, you create an external message to the test giver asking it to send another message to your (uninitialized) smart contract carrying a specified amount of test Grams. There is a special Fift source file for generating this external message, a more sophisticated version of which is located at crypto/smartcont/testgiver.fif:
--------------------------------------------
// "testgiver.addr" file>B 256 B>u@
0x8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
dup constant wallet_addr ."Test giver address = " x. cr
0x60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0
constant dest_addr
-1 constant wc
0 constant seqno
1000000000 constant Gram
{ Gram swap */ } : Gram*/
6.666 Gram*/ constant amount
// b x --> b' ( serializes a Gram amount )
{ -1 { 1+ 2dup 8 * ufits } until
rot over 4 u, -rot 8 * u, } : Gram,
// create a message (NB: 01b00.., b = bounce)
<b b{010000100} s, wc 8 i, dest_addr 256 u, amount Gram, 0 9 64 32 + + 1+ 1+ u, "GIFT" $, b>
<b seqno 32 u, 1 8 u, swap ref, b>
dup ."enveloping message: " <s csr. cr
<b b{1000100} s, wc 8 i, wallet_addr 256 u, 0 Gram, b{00} s,
swap <s s, b>
dup ."resulting external message: " <s csr. cr
2 boc+>B dup Bx. cr
"wallet-query.boc" B>file
---------------------------------------------
In general, you will need to edit the line containing the destination address (0x60c0...c0d0 in our example) and the sequence number of the test giver (the "0" in the "0 constant seqno" line). Alternatively, you can pass the required parameters as command-line arguments to
fift -I<include-path> -s <path-to-testgiver-fif> <dest-addr> <seqno> <amount> [<savefile>]
For instance,
fift -I<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont -s testgiver.fif 0f9gwEFBxqe5bWhhXnqR0mWtDzqaki6a6ckB1PqD9dPA0EKD 6.666 wallet-query
(Again, setting FIFTPATH to <source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont and installing fift binary as /usr/bin/fift is a good idea.)
The newly-created message to the new smart contract must have its bounce bit clear, otherwise the transfer will be "bounced" to its sender.
This Fift code creates an internal message from the test giver smart contract to the address of our new smart contract carrying 6.666 test Grams (you can enter any other amount here up to approximately 20 Grams). Then this message is enveloped into an external message addressed to the test giver; this external message must also contain the correct sequence number of the test giver. When the test giver receives such an external message, it checks whether the sequence number matches the one stored in its persistent data, and if it does, sends the embedded internal message with the required amount of test Grams to its destination (our smart contract in this case).
The external message is serialized and saved into the file `wallet-query.boc`. Some output is generated in the process:
---------------------------------------------
Test giver address = 8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
enveloping message: x{0000000001}
x{427FB06020A0E353DCB6B430AF3D48E932D6879D4D49174D74E480EA7D41FAE9E068280C6A98B4000000000000000000000000000047494654}
resulting external message: x{89FEA71F4F9849FF1D54203B094BE356FD065FC3B0966139BFDE9DD286E755901EFA00000000000C_}
x{427FB06020A0E353DCB6B430AF3D48E932D6879D4D49174D74E480EA7D41FAE9E068280C6A98B4000000000000000000000000000047494654}
B5EE9C7241040201000000006600014F89FEA71F4F9849FF1D54203B094BE356FD065FC3B0966139BFDE9DD286E755901EFA00000000000C010072427FB06020A0E353DCB6B430AF3D48E932D6879D4D49174D74E480EA7D41FAE9E068280C6A98B40000000000000000000000000000474946545D6254A9
---------------------------------------------
6. Uploading the external message to the test giver smart contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now we can invoke the Lite Client, check the state of the test giver (if the sequence number has changed, our external message will fail), and then type
> sendfile wallet-query.boc
We will see some output:
... external message status is 1
which means that the external message has been delivered to the collator pool. Afterwards one of the collators might choose to include this external message in a block, creating a transaction for the test giver smart contract to process this external message. We can check whether the state of the test giver has changed:
> last
> getaccount Ef-BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb
(If you forget to type `last`, you are likely to see the unchanged state of the test giver smart contract.) The resulting output would be:
---------------------------------------------
got account state for -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D with respect to blocks (-1,8000000000000000,10441):2DCFB7F734913261B85B8866DFF7CBEF07205EAA0769F4EE15E242AB520A2CC5:4F96F417BCD74D5DAE0CC3CCC285E513B85652002D9AD8CC884781D8465E3591 and (-1,8000000000000000,10441):2DCFB7F734913261B85B8866DFF7CBEF07205EAA0769F4EE15E242AB520A2CC5:4F96F417BCD74D5DAE0CC3CCC285E513B85652002D9AD8CC884781D8465E3591
account state is (account
addr:(addr_std
anycast:nothing workchain_id:-1 address:x8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:3)
bits:(var_uint len:2 value:539)
public_cells:(var_uint len:0 value:0)) last_paid:0
due_payment:nothing)
storage:(account_storage last_trans_lt:10697000003
balance:(currencies
grams:(nanograms
amount:(var_uint len:7 value:999993280210000))
other:(extra_currencies
dict:hme_empty))
state:(account_active
(
split_depth:nothing
special:nothing
code:(just
value:(raw@^Cell
x{}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
))
data:(just
value:(raw@^Cell
x{}
x{00000001}
))
library:hme_empty))))
x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C00000000000000009F65D110DC0E35F450FA914134_}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
x{00000001}
---------------------------------------------
You may notice that the sequence number stored in the persistent data has changed (in our example, to one), and the last_trans_lt field (the logical time of the last transaction of this account) has been increased.
Now we can inspect the state of our new smart contract:
> getaccount 0f9gwEFBxqe5bWhhXnqR0mWtDzqaki6a6ckB1PqD9dPA0EKD
or
> getaccount -1:60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0
Now we see
---------------------------------------------
got account state for -1:60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D0 with respect to blocks (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB and (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB
account state is (account
addr:(addr_std
anycast:nothing workchain_id:-1 address:x60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D0)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:1)
bits:(var_uint len:1 value:111)
public_cells:(var_uint len:0 value:0)) last_paid:1553210152
due_payment:nothing)
storage:(account_storage last_trans_lt:16413000004
balance:(currencies
grams:(nanograms
amount:(var_uint len:5 value:6666000000))
other:(extra_currencies
dict:hme_empty))
state:account_uninit))
x{CFF60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D02025BC2E4A0D9400000000F492A0511406354C5A004_}
---------------------------------------------
Our new smart contract has some positive balance (of 6.666 test Grams), but has no code or data (reflected by `state:account_uninit`).
7. Uploading the code and data of the new smart contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now you can finally upload the external message with the StateInit of the new smart contract, containing its code and data:
---------------------------------------------
> sendfile new-wallet-query.boc
... external message status is 1
> last
...
> getaccount -1:60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0
...
got account state for -1:60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D0 with respect to blocks (-1,8000000000000000,16709):D223B25D8D68401B4AA19893C00221CF9AB6B4E5BFECC75FD6048C27E001E0E2:4C184191CE996CF6F91F59CAD9B99B2FD5F3AA6F55B0B6135069AB432264358E and (-1,8000000000000000,16709):D223B25D8D68401B4AA19893C00221CF9AB6B4E5BFECC75FD6048C27E001E0E2:4C184191CE996CF6F91F59CAD9B99B2FD5F3AA6F55B0B6135069AB432264358E
account state is (account
addr:(addr_std
anycast:nothing workchain_id:-1 address:x60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D0)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:3)
bits:(var_uint len:2 value:963)
public_cells:(var_uint len:0 value:0)) last_paid:1553210725
due_payment:nothing)
storage:(account_storage last_trans_lt:16625000002
balance:(currencies
grams:(nanograms
amount:(var_uint len:5 value:5983177000))
other:(extra_currencies
dict:hme_empty))
state:(account_active
(
split_depth:nothing
special:nothing
code:(just
value:(raw@^Cell
x{}
x{FF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54}
))
data:(just
value:(raw@^Cell
x{}
x{00000001F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA3}
))
library:hme_empty))))
x{CFF60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D020680F0C2E4A0EB280000000F7BB57909405928024A134_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54}
x{00000001F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA3}
---------------------------------------------
You will see that the smart contract has been initialized using code and data from the StateInit of the external message, and its balance has been slightly decreased because of the processing fees. Now it is up and running, and you can activate it by generating new external messages and uploading them to the TON Blockchain using the "sendfile" command of the Lite Client.
8. Using the simple wallet smart contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Actually, the simple wallet smart contract used in this example can be used to transfer test Grams to any other accounts. It is in this respect similar to the test giver smart contract discussed above, with the difference that it processes only external messages signed by the correct private key (of its owner). In our case, it is the private key saved into the file "new-wallet.pk" during the compilation of the smart contract (see Section 3).
An example of how you might use this smart contract is provided in sample file crypto/smartcont/wallet.fif :
--------------------------------------------------------
#!/usr/bin/fift -s
def? $1 { "new-wallet" =: $1 } ifnot
$1 $len { "new-wallet" =: $1 } ifnot
$1 +".addr" file>B 256 B>u@ dup constant wallet_addr
."Wallet address = " x. cr
$1 +".pk" file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long"
constant wallet_pk
// 0x1111111122222222333333334444444455555555666666667777777788888888 constant dest_addr
0x13CB612A00A7C092C7DFD2EA45D603A9B54591BA4C88F71E707E009B879F0FB2 constant dest_addr
-1 constant wc
0 constant seqno
1000000000 constant Gram
{ Gram swap */ } : Gram*/
.666 Gram*/ constant amount
// b x --> b' ( serializes a Gram amount )
{ -1 { 1+ 2dup 8 * ufits } until
rot over 4 u, -rot 8 * u, } : Gram,
// create a message
<b b{011000100} s, wc 8 i, dest_addr 256 u, amount Gram, 0 9 64 32 + + 1+ 1+ u, "TEST" $, b>
<b seqno 32 u, 1 8 u, swap ref, b>
dup ."signing message: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint
<b b{1000100} s, wc 8 i, wallet_addr 256 u, 0 Gram, b{00} s,
swap B, swap <s s, b>
dup ."resulting external message: " <s csr. cr
2 boc+>B dup Bx. cr
$1 +"-query.boc" B>file
-------------------------------------
You can hard-code the address of your smart contract here by changing the fourth line to, say,
0x60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0 dup constant wallet_addr
You will also need to change the destination address, the name of the file with the private key, the sequence number (it will be 1 immediately after the smart contract is initialized; the actual value can always be retrieved by inspecting the current account state) and the Gram amount to be transferred. The payload of the internal message contains 32 bits with the string "TEST"; it can be changed to something more useful if necessary.
Alternatively, you can use the more sophisticated version of this script provided in crypto/smartcont/wallet.fif in the source tree, and pass all the required parameters as command-line arguments:
fift -I<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont -s wallet.fif <your-wallet-id> <destination-addr> <your-wallet-seqno> <gram-amount>
For example,
fift -I<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont -s wallet.fif my_new_wallet kf8Ty2EqAKfAksff0upF1gOptUWRukyI9x5wfgCbh58Pss9j 1 .666
Here `my_new_wallet` is the identifier of your wallet used before with new-wallet.fif; the address and the private key of your test wallet will be loaded from files `my_new_wallet.addr` and `my_new_wallet.pk` in the current directory.
When you run this code (by invoking the Fift interpreter), you create an external message with a destination equal to the address of your wallet smart contract, containing a correct Ed25519 signature, a sequence number, and an enveloped internal message from your wallet smart contract to the smart contract indicated in dest_addr, with an arbitrary value attached and an arbitrary payload. When your smart contract receives and processes this external message, it first checks the signature and the sequence number. If they are correct, it accepts the external message, sends the embedded internal message from itself to the intended destination, and increases the sequence number in its persistent data (this is a simple measure to prevent replay attacks, in case this sample wallet smart contract code ends up used in a real wallet application).
Of course, a true TON Blockchain wallet application would hide all the intermediate steps explained above. It would first communicate the address of the new smart contract to the user, asking them to transfer some funds to the indicated address (displayed in its non-bounceable user-friendly form) from another wallet or a cryptocurrency exchange, and then would provide a simple interface to display the current balance and to transfer funds to whatever other addresses the user wants. (The aim of this document is to explain how to create new non-trivial smart contracts and experiment with the TON Blockchain Test Network, rather than to explain how one could use the Lite Client instead of a more user-friendly wallet application.)
One final remark: The above examples used smart contracts in the basic workchain (workchain 0). They would work in exactly the same way in the masterchain (workchain -1), if one changes the "0 constant wc" to "-1 constant wc" in relevant places. The only difference is that the processing and storage fees in the basic workchain are 10-100 times lower than in the masterchain. Some smart contracts (such as the validator election smart contract) accept transfers only from masterchain smart contracts, so you'll need a wallet in the masterchain if you wish to make stakes on behalf of your own validator and participate in the elections.

234
doc/Validator-HOWTO Normal file
View file

@ -0,0 +1,234 @@
The aim of this document is to provide step-by-step instructions for setting up a full node for the TON Blockchain as a validator. We assume that a TON Blockchain Full Node is already up and running as explained in FullNode-HOWTO. We also assume some familiarity with the TON Blockchain Lite Client.
Notice that a validator must be run on a dedicated high-performance server with high network bandwidth installed in a reliable datacenter, and that you'll need a large amount of Grams (test Grams, if you want to run a validator in the "testnet") as stakes for your validator. If your validator works incorrectly or is not available for prolonged periods of time, you may lose part or all of your stake, so it makes sense to use high-performance reliable servers.
0. Downloading and compiling
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The basic instructions are the same as for a TON Blockchain Full Node, as explained in FullNode-HOWTO. In fact, any Full Node will automatically work as a validator if it discovers that the public key corresponding to its private key appears as a member of the current validator set for the currently selected TON Blockchain instance. In particular, the Full Node and the Validator use the same binary file validator-engine, and are controlled by means of the same validator-engine-console.
1. Controlling smart contract of a validator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order to run a Validator, you'll need a Full Node that is already up and running (and completely synchronized with the current blockchain state), and a wallet in the masterchain holding large amounts of Grams (or test Grams, if you want to run a validator in the "testnet" TON Blockchain instance). Typically you'll need at least 100,000 Grams.
Each validator is identified by its (Ed25519) public key. During the validator elections, the validator (or rather its public key) is also associated with a smart contract residing in the masterchain. For simplicity, we say that the validator is "controlled" by this smart contract (e.g., a wallet smart contract). Stakes are accepted on behalf of this validator only if they arrive from its associated smart contract, and only that associated smart contract is entitled to collect the validator stake after it is unfrozen, along with the validator's share of bonuses (block mining fees, transaction and message forwarding fees collected from the users of the TON Blockchain by the validator pool). Typically the bonuses are distributed proportionally to the (effective) stakes of the validators. On the other hand, validators with higher stakes get a larger amount of work to perform (have to create and validate blocks for more shardchains), so it is important not to get too greedy.
Notice that each validator (identified by its public key) can be associated with at most one controlling smartcontract (residing in the masterchain), but the same controlling smart contract may be associated with several validators. In this way you can run several validators (on different physical servers) and make stakes for them from the same smart contract. If one of these validators breaks down and you lose its stake, at least the other validators will continue working and will keep their stakes and receive bonuses.
2. Creating the controlling smart contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you don't have a controlling smart contract, you can simply create a wallet in the masterchain. A simple wallet can be created with the aid of the script new-wallet.fif, located in the subdirectory crypto/smartcont of the source tree. In what follows, we assume that you have configured the environment variable FIFTPATH to include <source-root>/crypto/fift/lib:<source-root>/crypto/smartcont, and that your PATH includes a directory with the Fift binary (located as <build-directory>/crypto/fift). Then you can simply run
$ fift -s new-wallet.fif -1 my_wallet_id
where "my_wallet_id" is any identifier you want to assign to your new wallet, and -1 is the workchain identifier for the masterchain. If you have not set up FIFTPATH and PATH, then you'll have to run a longer version of this command
$ crypto/fift -I <source-dir>/crypto/fift/lib:<source-dir>/crypto/smartcont -s new-wallet.fif -1 my_wallet_id
in your build directory.
Once you run this script, the address of the new smart contract is displayed:
...
new wallet address = -1:af17db43f40b6aa24e7203a9f8c8652310c88c125062d1129fe883eaa1bd6763
(Saving address to file my_wallet_id.addr)
Non-bounceable address (for init): 0f-vF9tD9Atqok5yA6n4yGUjEMiMElBi0RKf6IPqob1nYzqK
Bounceable address (for later access): kf-vF9tD9Atqok5yA6n4yGUjEMiMElBi0RKf6IPqob1nY2dP
...
(Saved wallet creating query to file my_wallet_id-query.boc)
Now my_wallet_id.pk is a new file containing the private key for controlling this wallet (you must keep it secret), and my_wallet_id.addr is a (not so secret) file containing the address of this wallet. Once this is done, you have to transfer some (test) Grams to the non-bounceable address of your wallet, and run "sendfile my_wallet_id-query.boc" in the Lite Client to finish creating the new wallet. This process is explained in more detail in the LiteClient-HOWTO.
If you are running a validator in the "mainnet", it is a good idea to use more sophisticated wallet smart contracts, e.g., a multi-signature wallet. For the "testnet", the simple wallet should be enough.
3. Elector smart contract
~~~~~~~~~~~~~~~~~~~~~~~~~
The elector smart contract is a special smart contract residing in the masterchain. Its full address is -1:xxx..xxx, where -1 is the workchain identifier (-1 corresponds to the masterchain), and xxx..xxx is the hexadecimal representation of its 256-bit address inside the masterchain. In order to find out this address, you have to read the configuration parameter #1 from a recent state of the blockchain. This is easily done by means of the command "getconfig 1" in the Lite Client:
> getconfig 1
ConfigParam(1) = ( elector_addr:xA4C2C7C05B093D470DE2316DBA089FA0DD775FD9B1EBFC9DC9D04B498D3A2DDA)
x{A4C2C7C05B093D470DE2316DBA089FA0DD775FD9B1EBFC9DC9D04B498D3A2DDA}
In this case, the complete elector address is -1:A4C2C7C05B093D470DE2316DBA089FA0DD775FD9B1EBFC9DC9D04B498D3A2DDA
We assume familiarity with the Lite Client and that you know how to run it and how to obtain a global configuration file for it. Notice that the above command can be run in batch mode by using '-c' command line option of the Lite Client:
$ lite-client -C <global-config-file> -c 'getconfig 1'
...
ConfigParam(1) = ( elector_addr:xA4C2C7C05B093D470DE2316DBA089FA0DD775FD9B1EBFC9DC9D04B498D3A2DDA)
x{A4C2C7C05B093D470DE2316DBA089FA0DD775FD9B1EBFC9DC9D04B498D3A2DDA}
$
The elector smart contract is used for several things. Most importantly, you can participate in validator elections or collect unfrozen stakes and bonuses by sending messages from the controlling smart contract of your validator to the elector smart contract. You can also learn about current validator elections and their participants by invoking the so-called "get methods" of the elector smart contract.
Namely, running
> runmethod -1:A4C2C7C05B093D470DE2316DBA089FA0DD775FD9B1EBFC9DC9D04B498D3A2DDA active_election_id
...
arguments: [ 86535 ]
result: [ 1567633899 ]
(or lite-client -C <global-config> -c "runmethod -1:<elector-addr> active_election_id" in batch mode) will return the identifier of the currently active elections (a non-zero integer, typically the unixtime of the start of the service term of the validator group being elected), or 0, if no elections are currently active. In this example, the identifier of the active elections is 1567633899.
You can also recover the list of all active participants (pairs of 256-bit validator public keys and their corresponding stakes expressed in nanograms) by running the method "participant_list" instead of "active_election_id".
4. Creating a validator public key and ADNL address
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order to participate in validator elections, you need to know the elections identifier (obtained by running get-method "active_elections_id" of the elector smart contract), and also the public key of your validator. The public key is created by running validator-engine-console (as explained in FullNode-HOWTO) and running the following commands:
$ validator-engine-console ...
...
conn ready
> newkey
created new key BCA335626726CF2E522D287B27E4FAFFF82D1D98615957DB8E224CB397B2EB67
> exportpub BCA335626726CF2E522D287B27E4FAFFF82D1D98615957DB8E224CB397B2EB67
got public key: xrQTSIQEsqZkWnoADMiBnyBFRUUweXTvzRQFqb5nHd5xmeE6
> addpermkey BCA335626726CF2E522D287B27E4FAFFF82D1D98615957DB8E224CB397B2EB67 1567633899 1567733900
success
Now the full node (validator-engine) has generated a new keypair, exported the Base64 representation of the public key (xrQT...E6), and registered it as a persistent key for signing blocks starting from unixtime 1567633899 (equal to the election identifier) until 1567733900 (equal to the previous number plus the term duration of the validator set to be elected, available in configuration parameter #15, which can be learned by typing "getconfig 15" in the Lite Client, plus a safety margin in case elections actually happen later than intended).
You also need to define a temporary key to be used by the validator to participate in the network consensus protocol. The simplest way (sufficient for testing purposes) is to set this key equal to the persistent (block signing) key:
> addtempkey BCA335626726CF2E522D287B27E4FAFFF82D1D98615957DB8E224CB397B2EB67 BCA335626726CF2E522D287B27E4FAFFF82D1D98615957DB8E224CB397B2EB67 1567733900
success
It is also a good idea to create a dedicated ADNL address to be used exclusively for validator purposes:
> newkey
created new key C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C
> addadnl C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C 0
success
> addvalidatoraddr BCA335626726CF2E522D287B27E4FAFFF82D1D98615957DB8E224CB397B2EB67 C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C 1567733900
success
Now C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C is a new ADNL address, which will be used by the Full Node for running as a validator with the public key BCA...B67, with expiration time set to 1567733900.
5. Creating an election participation request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The special script validator-elect-req.fif (located in <source-dir>/crypto/smartcont) is used to create a message that has to be signed by the validator in order to participate in the elections. It is run as follows:
$ fift -s validator-elect-req.fif <wallet-addr> <elect-utime> <max-factor> <adnl-addr> [<savefile>]
For example,
$ fift -s validator-elect-req.fif kf-vF9tD9Atqok5yA6n4yGUjEMiMElBi0RKf6IPqob1nY2dP 1567633899 2.7 C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C
or, if you have created the controlling wallet by means of new-wallet.fif, you can use @my_wallet_id.addr instead of copying the wallet address kf-vF...dP:
---------------------------------------
$ fift -s validator-elect-req.fif @my_wallet_id.addr 1567633899 2.7 C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C
Creating a request to participate in validator elections at time 1567633899 from smart contract Uf+vF9tD9Atqok5yA6n4yGUjEMiMElBi0RKf6IPqob1nY4EA = -1:af17db43f40b6aa24e7203a9f8c8652310c88c125062d1129fe883eaa1bd6763 with maximal stake factor with respect to the minimal stake 176947/65536 and validator ADNL address c5c2b94529405fb07d1ddfb4c42bfb07727e7ba07006b2db569fbf23060b9e5c
654C50745D7031EB0002B333AF17DB43F40B6AA24E7203A9F8C8652310C88C125062D1129FE883EAA1BD6763C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C
ZUxQdF1wMesAArMzrxfbQ_QLaqJOcgOp-MhlIxDIjBJQYtESn-iD6qG9Z2PFwrlFKUBfsH0d37TEK_sHcn57oHAGsttWn78jBgueXA==
---------------------------------------
Here <max-factor> = 2.7 is the maximal allowed ratio of your stake with respect to the minimal validator stake in the elected validator group. In this way you can be sure that your stake will be not more than 2.7 times the smallest stake, so the workload of your validator is at most 2.7 times the minimal one. If your stake is too large with respect to the stakes of other validators, then it will be clipped to this value (2.7 times the minimal stake), and the remainder will be returned to you (i.e., to the controlling smart contract of your validator) immediately after elections.
Now you obtain a binary string in hexadecimal (654C...9E5C) and base64 form to be signed by the validator. This can be done in validator-engine-console:
> sign BCA335626726CF2E522D287B27E4FAFFF82D1D98615957DB8E224CB397B2EB67 654C50745D7031EB0002B333AF17DB43F40B6AA24E7203A9F8C8652310C88C125062D1129FE883EAA1BD6763C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C
got signature ovf9cmr2J/speJEtMU+tZm6zH/GBEyZCPpaukqL3mmNH9Wipyoys63VFh0yR386bARHKMPpfKAYBYslOjdSjCQ
Here BCA...B67 is the identifier of the signing key of our validator, and 654...E5C is the message generated by validator-elect-req.fif. The signature is ovf9...jCQ (this is the Base64 representation of 64-byte Ed25519 signature).
Now you have to run another script validator-elect-signed.fif, which also requires the public key and the signature of the validator:
------------------------------------
$ fift -s validator-elect-signed.fif @my_wallet_id.addr 1567633899 2.7 C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C xrQTSIQEsqZkWnoADMiBnyBFRUUweXTvzRQFqb5nHd5xmeE6 ovf9cmr2J/speJEtMU+tZm6zH/GBEyZCPpaukqL3mmNH9Wipyoys63VFh0yR386bARHKMPpfKAYBYslOjdSjCQ==
Creating a request to participate in validator elections at time 1567633899 from smart contract Uf+vF9tD9Atqok5yA6n4yGUjEMiMElBi0RKf6IPqob1nY4EA = -1:af17db43f40b6aa24e7203a9f8c8652310c88c125062d1129fe883eaa1bd6763 with maximal stake factor with respect to the minimal stake 176947/65536 and validator ADNL address c5c2b94529405fb07d1ddfb4c42bfb07727e7ba07006b2db569fbf23060b9e5c
String to sign is: 654C50745D7031EB0002B333AF17DB43F40B6AA24E7203A9F8C8652310C88C125062D1129FE883EAA1BD6763C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C
Provided a valid Ed25519 signature A2F7FD726AF627FB2978912D314FAD666EB31FF1811326423E96AE92A2F79A6347F568A9CA8CACEB7545874C91DFCE9B0111CA30FA5F28060162C94E8DD4A309 with validator public key 8404B2A6645A7A000CC8819F20454545307974EFCD1405A9BE671DDE7199E13A
query_id set to 1567632790
Message body is x{4E73744B000000005D702D968404B2A6645A7A000CC8819F20454545307974EFCD1405A9BE671DDE7199E13A5D7031EB0002B333C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C}
x{A2F7FD726AF627FB2978912D314FAD666EB31FF1811326423E96AE92A2F79A6347F568A9CA8CACEB7545874C91DFCE9B0111CA30FA5F28060162C94E8DD4A309}
Saved to file validator-query.boc
-----------------------
Alternatively, if you are running validator-engine-console on the same machine as your wallet, you can skip the above steps and instead use "createelectionbid" to create a file with the external message to be sent by the lite client. For this command to work, you have to run validator-engine with `-f <fift-dir>` command line option, where <fift-dir> is a directory containing copies of all required Fift source files (such as Fift.fif, TonUtil.fif, validator-elect-req.fif, and validator-elect-signed.fif), even though these files normally reside in different source directories (<source-dir>/crypto/fift/lib and <source-dir>/crypto/smartcont).
Now you have a message body containing your elections participation request. You have to send it from the controlling smart contract, carrying the stake as its value (plus one extra Gram for sending confirmation). If you use the simple wallet smart contract, this can be done by using -B command line argument to wallet.fif:
--------------------------------------------
$ fift -s wallet.fif my_wallet_id -1:A4C2C7C05B093D470DE2316DBA089FA0DD775FD9B1EBFC9DC9D04B498D3A2DDA 1 100001. -B validator-query.boc
Source wallet address = -1:af17db43f40b6aa24e7203a9f8c8652310c88c125062d1129fe883eaa1bd6763
kf-vF9tD9Atqok5yA6n4yGUjEMiMElBi0RKf6IPqob1nY2dP
Loading private key from file my_wallet_id.pk
Transferring GR$100001. to account kf-kwsfAWwk9Rw3iMW26CJ-g3Xdf2bHr_J3J0EtJjTot2lHQ = -1:a4c2c7c05b093d470de2316dba089fa0dd775fd9b1ebfc9dc9d04b498d3a2dda seqno=0x1 bounce=-1
Body of transfer message is x{4E73744B000000005D702D968404B2A6645A7A000CC8819F20454545307974EFCD1405A9BE671DDE7199E13A5D7031EB0002B333C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C}
x{A2F7FD726AF627FB2978912D314FAD666EB31FF1811326423E96AE92A2F79A6347F568A9CA8CACEB7545874C91DFCE9B0111CA30FA5F28060162C94E8DD4A309}
signing message: x{0000000101}
x{627FD26163E02D849EA386F118B6DD044FD06EBBAFECD8F5FE4EE4E825A4C69D16ED32D79A60A8500000000000000000000000000001}
x{4E73744B000000005D702D968404B2A6645A7A000CC8819F20454545307974EFCD1405A9BE671DDE7199E13A5D7031EB0002B333C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C}
x{A2F7FD726AF627FB2978912D314FAD666EB31FF1811326423E96AE92A2F79A6347F568A9CA8CACEB7545874C91DFCE9B0111CA30FA5F28060162C94E8DD4A309}
resulting external message: x{89FF5E2FB687E816D5449CE40753F190CA4621911824A0C5A2253FD107D5437ACEC6049CF8B8EA035B0446E232DB8C1DFEA97738076162B2E053513310D2A3A66A2A6C16294189F8D60A9E33D1E74518721B126A47DA3A813812959BD0BD607923B010000000080C_}
x{627FD26163E02D849EA386F118B6DD044FD06EBBAFECD8F5FE4EE4E825A4C69D16ED32D79A60A8500000000000000000000000000001}
x{4E73744B000000005D702D968404B2A6645A7A000CC8819F20454545307974EFCD1405A9BE671DDE7199E13A5D7031EB0002B333C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C}
x{A2F7FD726AF627FB2978912D314FAD666EB31FF1811326423E96AE92A2F79A6347F568A9CA8CACEB7545874C91DFCE9B0111CA30FA5F28060162C94E8DD4A309}
B5EE9C7241040401000000013D0001CF89FF5E2FB687E816D5449CE40753F190CA4621911824A0C5A2253FD107D5437ACEC6049CF8B8EA035B0446E232DB8C1DFEA97738076162B2E053513310D2A3A66A2A6C16294189F8D60A9E33D1E74518721B126A47DA3A813812959BD0BD607923B010000000080C01016C627FD26163E02D849EA386F118B6DD044FD06EBBAFECD8F5FE4EE4E825A4C69D16ED32D79A60A85000000000000000000000000000010201A84E73744B000000005D702D968404B2A6645A7A000CC8819F20454545307974EFCD1405A9BE671DDE7199E13A5D7031EB0002B333C5C2B94529405FB07D1DDFB4C42BFB07727E7BA07006B2DB569FBF23060B9E5C030080A2F7FD726AF627FB2978912D314FAD666EB31FF1811326423E96AE92A2F79A6347F568A9CA8CACEB7545874C91DFCE9B0111CA30FA5F28060162C94E8DD4A309062A7721
(Saved to file wallet-query.boc)
----------------------------------
Now you just have to send wallet-query.boc from the Lite Client (not the Validator Console):
> sendfile wallet-query.boc
or you can use LiteClient in the batch mode
$ lite-client -C <config-file> -c "sendfile wallet-query.boc"
This is an external message signed by your private key (which controls your wallet), that instructs your wallet smart contract to send an internal message to the elector smart contract with the prescribed payload (containing the validator bid and signed by its key) and transfer the specified amount of grams. When the elector smart contract receives this internal message, it registers your bid (with the stake equal to the specified amount of grams minus one), and sends you (i.e., the wallet smart contract) a confirmation (carrying 1 gram back) or a rejection message with an error code (carrying back almost all of the original stake amount minus processing fees).
You can check whether your stake has been accepted by running get-method "participant_list" of the elector smart contract.
6. Recovering stakes and bonuses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If your stake is only partially accepted (because of max-factor) during the elections, or after your stake is unfrozen (this happens some time after the expiration of the term of the validator group to which your validator has been elected), you may want to collect back all or part of your stake, along with whatever share of bonuses is due to your validator. The elector smart contract does not send the stake and bonuses to you (i.e., the controlling smart contract) in a message. Instead, it credits the amount to be returned to you inside a special table, which can be inspected with the aid of "compute_returned_stake" get-method (which expects the address of the controlling smart contract as an argument):
$ lite-client -C ton-lite-client-test1.config.json -rc 'runmethod -1:A4C2C7C05B093D470DE2316DBA089FA0DD775FD9B1EBFC9DC9D04B498D3A2DDA compute_returned_stake 0xaf17db43f40b6aa24e7203a9f8c8652310c88c125062d1129fe883eaa1bd6763'
arguments: [ 79196899299028790296381692623119733846152089453039582491866112477478757689187 130944 ]
result: [ 0 ]
If the result is zero, nothing is due to you. Otherwise, you'll see part or all of your stake, maybe with some bonuses. In that case, you can create a stake recovery request by using recover-stake.fif:
-----------------------------
$ fift -s recover-stake.fif
query_id for stake recovery message is set to 1567634299
Message body is x{47657424000000005D70337B}
Saved to file recover-query.boc
-----------------------------
Again, you have to send recover-query.boc as the payload of a message from the controlling smart contract (i.e., your wallet) to the elector smart contract:
$ fift -s wallet.fif my_wallet_id <dest-addr> <my-wallet-seqno> <gram-amount> -B recover-query.boc
For example,
$ fift -s wallet.fif my_wallet_id -1:A4C2C7C05B093D470DE2316DBA089FA0DD775FD9B1EBFC9DC9D04B498D3A2DDA 2 1. -B recover-query.boc
...
(Saved to file wallet-query.boc)
Notice that this message carries a small value (one Gram) just to pay the message forwarding and processing fees. If you indicate a value equal to zero, the message will not be processed by the election smart contract (a message with exactly zero value is almost useless in the TON Blockchain context).
Once wallet-query.boc is ready, you can send it from the Lite Client:
$ liteclient -C <config> -c 'sendfile wallet-query.boc'
If you have done everything correctly (in particular indicated the correct seqno of your wallet instead of "2" in the example above), you'll obtain a message from the elector smart contract containing the change from the small value you sent with your request (1. Gram in this example) plus the recovered portion of your stake and bonuses.
7. Participating in the next elections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Notice that even before the term of the validator group containing your elected validator finishes, new elections for the next validator group will be announced. You'll probably want to participate in them as well. For this, you can use the same validator, but you must generate a new validator key and new ADNL address. You'll also have to make a new stake before your previous stake is returned (because your previous stake will be unfrozen and returned only some time after the next validator group becomes active), so it does not make sense to stake more than half of your Grams.

2173
doc/fiftbase.tex Normal file

File diff suppressed because it is too large Load diff

2317
doc/tblkch.tex Normal file

File diff suppressed because it is too large Load diff

6133
doc/ton.tex Normal file

File diff suppressed because it is too large Load diff

3138
doc/tvm.tex Normal file

File diff suppressed because it is too large Load diff