Mais de um ano atrás, ficou sabendo dos planos do mensageiro Telegram de lançar sua própria rede descentralizada Telegram Open Network. Na época, um extenso documento técnico foi disponibilizado, que supostamente foi escrito por Nikolai Durov e descrevia a estrutura da futura rede. Para aqueles que perderam, recomendo dar uma olhada na minha síntese desse documento (, ; a terceira parte, infelizmente, ainda está engavetada nos rascunhos).
Desde então, não houve notícias significativas sobre o status do desenvolvimento do TON, até que, há alguns dias (em um dos ) surgiu um link para a página , onde estão disponíveis:
◦ — código-fonte do cliente leve para a rede de teste TON;
◦ — arquivo de configuração para conexão à rede de teste;
◦ — informações sobre compilação e execução do cliente;
◦ — instruções passo a passo sobre a criação de um contrato inteligente usando o cliente;
◦ — um documento atualizado (de 2 de março de 2019) com uma visão técnica da rede TON;
◦ — descrição técnica do TVM (Máquina Virtual TON);
◦ — descrição técnica da blockchain TON;
◦ — descrição da nova linguagem Fift, destinada à criação de contratos inteligentes em TON.
Reitero que não houve confirmações oficiais da página e de todos esses documentos por parte do Telegram, mas a quantidade desses materiais os torna bastante plausíveis. Ao lançar o cliente publicado, faça-o por sua própria conta e risco.
Compilando o cliente de teste
Para começar, vamos tentar compilar e executar o cliente de teste — felizmente, descreve esse processo simples em detalhes. Farei isso usando o exemplo do macOS 10.14.5, não posso garantir o sucesso da compilação em outros sistemas.
Baixamos e descompactamos . É importante baixar a versão mais recente, pois a compatibilidade retroativa não é garantida nesta fase.
Verificamos se as versões mais recentes do make, cmake (versão 3.0.2 ou superior), OpenSSL (incluindo arquivos de cabeçalho C), g++ ou clang estão instaladas no sistema. Eu não precisei instalar nada adicional, tudo compilou na hora.
Suponhamos que os códigos-fonte estejam descompactados na pasta
~\/lite-client. Separadamente, criamos uma pasta vazia para o projeto compilado (por exemplo,~\/liteclient-build), e a partir dela (cd ~\/liteclient-build) chamamos os comandos:cmake ~\/lite-client\ncmake --build . --target test-lite-client
Para compilar o interpretador da linguagem Fift para contratos inteligentes (sobre isso abaixo), também chamamoscmake --build . --target fiftBaixamos o atual para se conectar à rede de teste e o colocamos na pasta com o cliente compilado.
Concluído, podemos iniciar o cliente:
./test-lite-client -C ton-lite-client-test1.config.json
Se tudo foi feito corretamente, você deve ver algo assim:

Os comandos disponíveis, como podemos ver, são poucos:
◦ ajuda — listar essa lista de comandos;
◦ quit — sair;
◦ time — mostrar a hora atual no servidor;
◦ status — mostrar o estado da conexão e do banco de dados local;
◦ último — atualizar o estado da blockchain (baixar o último bloco). Este comando é importante para ser executado antes de qualquer solicitação, para ter certeza de que você está vendo o estado mais atual da rede.
◦ sendfile <filename> — carregar um arquivo local na rede TON. Isso ocorre durante a interação com a rede — incluindo, por exemplo, a criação de novos contratos inteligentes e solicitações para transferências de fundos entre contas;
◦ getaccount — mostrar o estado atual (no momento da execução do comando último) da conta com o endereço especificado;
◦ privkey <filename> — carregar a chave privada de um arquivo local.
Se você passar uma pasta para o cliente ao iniciá-lo usando a opção -D, ele irá armazenar o último bloco do masterchain nela:
./test-lite-client -C ton-lite-client-test1.config.json -D ~/ton-db-dirAgora podemos passar para coisas mais interessantes — estudar a linguagem Fift, tentar compilar um contrato inteligente (por exemplo, criar uma carteira de teste), carregá-lo na rede e tentar transferir fundos entre contas.
Linguagem Fift
Do documento podemos descobrir que, para criar contratos inteligentes, a equipe do Telegram criou uma nova linguagem de pilha Fift (aparentemente, do numeral fifth, por analogia com Forth — uma linguagem que tem muito em comum com Fift).
O documento é bastante extenso, com 87 páginas, e eu não vou resumir seu conteúdo detalhadamente neste artigo (pelo menos, porque ainda não terminei a leitura :). Vou me concentrar nos principais pontos e fornecer alguns exemplos de código nessa linguagem.
Em um nível básico, a sintaxe de Fift é bastante simples: seu código consiste em palavras, geralmente separadas por espaços ou quebras de linha (caso específico: algumas palavras não exigem um delimitador após si). Qualquer uma palavra — é uma sequência de símbolos sensível a maiúsculas e minúsculas que corresponde a alguma definição (de forma grosseira, o que o interpretador deve fazer quando encontra essa palavra). Se não houver definição para a palavra, o interpretador tentará analisá-la como um número e colocá-la na pilha. A propósito, os números aqui são - surpreendentemente - inteiros de 257 bits, enquanto não há frações - mais precisamente, eles se transformam imediatamente em um par de inteiros que formam o numerador e o denominador de uma fração racional.
As palavras, geralmente, interagem com os valores que estão no topo da pilha. Um tipo separado de palavras é prefixo — utiliza não uma pilha, mas os próximos símbolos dos arquivos de origem. Por exemplo, assim são implementados os literais de string — o símbolo «aspas» (") é uma palavra prefixada que busca a próxima (fechando) aspas e coloca a string entre elas na pilha. Comentários de uma linha (//) e de várias linhas (/*) se comportam de maneira semelhante.
Aqui termina a maior parte da estrutura interna da linguagem. Todo o restante (incluindo estruturas de controle) é definido como palavras (ou internas, como operações aritméticas e definição de novas palavras; ou definidas na «biblioteca padrão» Fift.fif, que está na pasta crypto/fift nos códigos-fonte).
Um exemplo simples de programa em Fift:
{ dup =: x dup * =: y } : setxy
3 setxy x . y . x y + .
7 setxy x . y . x y + .Na primeira linha, definimos uma nova palavra setxy (note o prefixo {, que cria um bloco até a aspa de fechamento } e o prefixo :, que essencialmente define a palavra). setxy pega um número do topo da pilha, o define (ou redefine) como uma constante global x, e o quadrado desse número — como uma constante y (considerando que os valores das constantes podem ser redefinidos, eu preferiria chamá-los de variáveis, mas sigo a nomenclatura da linguagem).
Nas próximas duas linhas, um número é colocado na pilha, chamado setxy, em seguida, os valores das constantes são exibidos x, y (para exibir, é utilizada a palavra .), ambas as constantes são colocadas na pilha, somadas e o resultado também é exibido. Como resultado, veremos:
3 9 12 ok
7 49 56 ok(A linha «ok» é exibida pelo interpretador quando termina de processar a linha atual no modo interativo de entrada)
E agora um exemplo completo de código:
"Asm.fif" inclui
-1 constante wc
// cria uma carteira na workchain -1 (masterchain)
// Cria nova carteira simples
<{ SETCP0 DUP IFNOTRET INC 32 THROWIF
512 INT LDSLICEX DUP 32 PLDU
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS
s1 s2 XCPU
EQUAL 33 THROWIFNOT
s2 PUSH HASHSU
s0 s4 s4 XC2PU
CHKSIGNU
34 THROWIFNOT
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
// código
<b 0 32 u, newkeypair swap dup constant wallet_pk "new-wallet.pk" b>arquivo
B,
b> // dados
// sem bibliotecas
<b b{00110} s, rot ref, swap b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hash dup constant wallet_addr
."new wallet address = " wc . .": " x. cr
wc over 7 smca>$ tipo cr
256 u>B "new-wallet.addr" B>arquivo
<b 0 32 u, b>
dup ."assinando mensagem: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint rot
<b b{1000100} s, wc 8 i, wallet_addr 256 u, b{000010} swap <s b{0} b, b>
dup ."Mensagem externa para inicialização é " <s csr. cr
2 boc+>B dup Bx. cr
"new-wallet-query.boc" tuck B>arquivo
."(Salvo no arquivo " tipo .")" crEste arquivo que parece assustador destina-se à criação de um contrato inteligente — ele será colocado no arquivo new-wallet-query.boc após a execução. Observe que aqui é utilizado outro idioma, assembler, para a TON Virtual Machine (não vou entrar em detalhes sobre isso), cujas instruções serão colocadas na blockchain.
Assim, o assembler para TVM é escrito em Fift — os códigos-fonte desse assembler estão no arquivo crypto/fift/Asm.fif e são incluídos no início do trecho de código acima.
O que posso dizer, aparentemente, Nikolai Durov simplesmente ama criar novas linguagens de programação 🙂
Criando um contrato inteligente e interagindo com o TON
Então, suponha que reunimos o cliente TON e o interpretador Fift, conforme descrito acima, e nos familiarizamos com a linguagem. Como agora criar um contrato inteligente? Isso é discutido no arquivo , anexado aos códigos-fonte.
Contas no TON
Como descrevi na , essa rede contém mais de uma blockchain — existe uma comum, chamada de "masterchain", além de um número arbitrário de "workchains" adicionais, identificados por um número de 32 bits. A masterchain tem o identificador -1, além dela, também pode ser utilizado um "workchain" base com o identificador 0. Cada workchain pode ter sua própria configuração. Internamente, cada workchain é subdividido em shardchains, mas isso já é um detalhe de implementação que não precisamos ter em mente.
Dentro de um workchain, existem várias contas, cada uma com seus identificadores account_id. Para a masterchain e o workchain zero, eles têm 256 bits de comprimento. Assim, o identificador da conta é escrito, por exemplo, assim:
-1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0dEste é o formato "cru": primeiro o identificador do workchain, depois dois pontos, e o identificador da conta em notação hexadecimal.
Além disso, existe um formato encurtado — o número do workchain e o endereço da conta são codificados em binário, a eles é acrescentado um checksum e tudo isso é codificado em Base64:
Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkbSabendo esse formato de gravação, podemos solicitar o estado atual de uma conta através do cliente de teste usando o comando
getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0dReceberemos uma resposta semelhante a esta:
[ 3][t 2][1558746708.815218925][test-lite-client.cpp:631][!testnode] solicitando estado da conta para -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D
[ 3][t 2][1558746708.858564138][test-lite-client.cpp:652][!testnode] obteve estado da conta para -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D em relação aos blocos (-1,8000000000000000,72355):F566005749C1B97F18EDE013EBA7A054B9014961BC1AD91F475B9082919A2296:1BD5DE54333164025EE39D389ECE2E93DA2871DA616D488253953E52B50DC03F e (-1,8000000000000000,72355):F566005749C1B97F18EDE013EBA7A054B9014961BC1AD91F475B9082919A2296:1BD5DE54333164025EE39D389ECE2E93DA2871DA616D488253953E52B50DC03F
o estado da conta é (conta
addr:(addr_std
anycast:nada workchain_id:-1 endereço:x8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D)
storage_stat:(storage_info
usado:(storage_used
células:(var_uint len:1 value:3)
bits:(var_uint len:2 value:539)
public_cells:(var_uint len:0 value:0)) último_pago:0
pagamento_devido:nada)
storage:(account_storage last_trans_lt:74208000003
saldo:(currencies
gramas:(nanograms
amount:(var_uint len:7 value:999928362430000))
outros:(extra_currencies
dict:hme_empty))
estado:(account_active
(
split_depth:nada
special:nada
código:(just
value:(raw@^Cell
x{}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
))
dados:(just
value:(raw@^Cell
x{}
x{0000000D}
))
biblioteca:hme_empty))))
x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C000000000000000451C90E00DC0E35B7DB5FB8C134_}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
x{0000000D}Vemos a estrutura armazenada no DHT do workchain especificado. Por exemplo, no campo storage.balance encontramos o saldo atual da conta, em storage.state.code — o código do contrato inteligente, e em storage.state.data — seus dados atuais. Vale ressaltar que o armazenamento de dados do TON — Cell, as células — é hierárquico, onde cada célula pode conter seus próprios dados e células filhas. Isso é mostrado como recuos nas últimas linhas.
Montagem do contrato inteligente
Agora vamos criar tal estrutura (chamada BOC — bag of cells) usando a linguagem Fift. Felizmente, não precisaremos escrever o contrato inteligente manualmente — na pasta crypto/block no arquivo com os códigos fonte, há um arquivo new-wallet.fif, que nos ajudará a criar uma nova carteira. Vamos copiá-lo para a pasta do cliente compilado (~\/liteclient-build, se você seguiu as instruções acima). O conteúdo deste arquivo eu já apresentei acima como exemplo de código em Fift.
Executamos este arquivo da seguinte forma:
.\/crypto\/fift -I"\/crypto\/fift" new-wallet.fifAqui <source-directory> deve ser substituído pelo caminho para os códigos fonte descompactados (infelizmente, o símbolo «~» não pode ser usado aqui, é necessário um caminho completo). Em vez de usar a chave -I você pode definir a variável de ambiente FIFTPATH e colocar esse caminho nela.
Como o Fift foi iniciado com o nome do arquivo new-wallet.fif, ele será executado e encerrado. Se o nome do arquivo for omitido, você pode brincar com o interpretador em modo interativo.
Após a execução, algo assim deve ser exibido no console:
StateInit: x{34_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
x{0000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B}
novo endereço da carteira = -1 : 4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2
0f9PzVILj8yglrVn1zS-NSjtxr7QBfaTCp7JrBqnFPIR8nhZ
mensagem de assinatura: x{00000000}
Mensagem externa para inicialização é x{89FEE120E20C7E953E31546F64C23CD654002C1AA919ADD24DB12DDF85C6F3B58AE41198A28AD8DAF3B9588E7A629252BA3DB88F030D00BC1016110B2073359EAC3C13823C53245B65D056F2C070B940CDA09789585935C7ABA4D2AD4BED139281CFA1200000001_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
x{0000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B}
B5EE9C724104030100000000D60002CF89FEE120E20C7E953E31546F64C23CD654002C1AA919ADD24DB12DDF85C6F3B58AE41198A28AD8DAF3B9588E7A629252BA3DB88F030D00BC1016110B2073359EAC3C13823C53245B65D056F2C070B940CDA09789585935C7ABA4D2AD4BED139281CFA1200000001001020084FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED5400480000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B6290698B
(Salvo no arquivo new-wallet-query.boc)Isso significa que a carteira com o identificador -1:4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2 (ou, o mesmo que, 0f9PzVILj8yglrVn1zS-NSjtxr7QBfaTCp7JrBqnFPIR8nhZ) foi criada com sucesso. O código correspondente estará no arquivo new-wallet-query.boc, seu endereço estará em new-wallet.addr, e a chave privada estará em new-wallet.pk (cuidado — reiniciar o script sobrescreverá esses arquivos).
Claro, a rede TON ainda não conhece esta carteira, ela só está armazenada nesses arquivos. Agora você precisa carregá-la na rede. No entanto, o problema é que para criar um contrato inteligente você precisa pagar uma taxa, e o saldo da sua conta ainda é zero.
Em modo de operação, esse problema será resolvido comprando gramas na bolsa (ou transferindo de outra carteira). Mas no atual modo de teste, existe um contrato inteligente especial, do qual você pode solicitar até 20 gramas gratuitamente.
Formação de uma solicitação para um contrato inteligente externo
Fazemos uma solicitação ao contrato inteligente que distribui gramas à esquerda e à direita desta forma. Na mesma pasta crypto/block encontramos o arquivo testgiver.fif:
// "testgiver.addr" file>B 256 B>u@
0x8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
dup constant wallet_addr ."Test giver address = " x. cr
0x4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2
constant dest_addr
-1 constant wc
0x00000011 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+ "gift" $, b>
<b seqno 32 u, 1 8 swap ref, b>
dup ."mensagem de envelopamento: " <s csr. cr
<b b{1000100} s, wc 8 i, wallet_addr 256 u, 0 gram, b{00} s,
swap <s b>
dup ."mensagem externa resultante: " <s csr. cr
2 boc+>B dup Bx. cr
"wallet-query.boc" B>arquivoNós também o salvaremos na pasta com o cliente compilado, mas ajustaremos a quinta linha — antes da linha "dest_addr constante". Trocaremos por o endereço da carteira que você criou anteriormente (completo, não abreviado). Não é necessário colocar "-1:" no início, em vez disso, comece com "0x".
Você também pode alterar a linha 6.666 Gramas* / quantidade constante — é a quantidade em gramas que você solicita (não mais que 20). Mesmo que você indique um número inteiro, mantenha o ponto decimal.
Por fim, é necessário corrigir a linha 0x00000011 seqno constante. O primeiro número aqui é o sequence number atual, que é armazenado na conta que concede os gramas. De onde obtê-lo? Como mencionado anteriormente, inicie o cliente e execute:
last
getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0dNo final dos dados do smart contract, haverá
...
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
x{0000000D}O número 0000000D (o seu será maior) é o sequence number que deve ser inserido em testgiver.fif.
Tudo bem, salvamos o arquivo e executamos (./crypto/fift testgiver.fif). A saída será o arquivo wallet-query.boc. Este é o solicitado a ocorrência para um smart contract externo — pedido "transfira tanto gramas para tal conta".
Com o cliente, carregamos na rede:
> sendfile wallet-query.boc
[ 1][t 1][1558747399.456575155][test-lite-client.cpp:577][!testnode] enviando consulta do arquivo wallet-query.boc
[ 3][t 2][1558747399.500236034][test-lite-client.cpp:587][!query] status da mensagem externa é 1Se agora chamarmos último, e então pedirmos novamente o status da conta que solicitamos os gramas, devemos ver que seu sequence number aumentou em um — isso significa que ele enviou dinheiro para nossa conta.
Resta um último passo — carregamos o código da nossa carteira (seu saldo já foi creditado, mas sem o código do smart contract, não conseguiremos gerenciá-la). Executamos sendfile new-wallet-query.boc — e tudo, você tem sua própria carteira na rede TON (mesmo que ainda seja apenas uma de teste).
Criando transações de saída
Para transferir dinheiro do saldo da conta criada, há um arquivo crypto/block/wallet.fif, que também precisa ser colocado na pasta com o cliente compilado.
De maneira semelhante aos passos anteriores, nele você deve corrigir a quantidade que está transferindo, o endereço do destinatário (dest_addr) e o seqno da sua carteira (que é 1 após a inicialização da carteira e aumenta em 1 após cada transação de saída — você poderá vê-lo ao consultar o estado de sua conta). Para testes, você pode usar, por exemplo, minha carteira — 0x4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2.
Ao executar (./crypto/fift wallet.fif), o script pegará o endereço da sua carteira (de onde você está transferindo) e sua chave privada dos arquivos new-wallet.addr e new-wallet.pk, e a mensagem obtida será gravada em new-wallet-query.boc.
Como antes, para realizar a transação diretamente, chamamos sendfile new-wallet-query.boc no cliente. Depois disso, não esqueça de atualizar o estado da blockchain (último) e verificar se o saldo e o seqno da nossa carteira mudaram (getaccount).

E é isso, agora sabemos como criar contratos inteligentes no TON e enviar solicitações para eles. Como podemos ver, a funcionalidade atual já é suficiente para, por exemplo, criar uma carteira mais amigável com uma interface gráfica (no entanto, espera-se que ela esteja disponível como parte do mensageiro).
Apenas usuários registrados podem participar da pesquisa. , por favor.
Você está interessado em continuar com artigos sobre TON, TVM, Fift?
Sim, estou aguardando a conclusão do ciclo de artigos com uma visão geral do TON
Sim, gostaria de ler mais sobre a linguagem Fift
Sim, quero saber mais sobre a TON Virtual Machine e o assembler para ela
Não, não tenho interesse por nada disso
39 usuários votaram. 12 usuários se abstiveram.
Qual é a sua opinião sobre os planos do Telegram de lançar o TON?
Tenho grandes esperanças para este projeto
Estou apenas acompanhando seu desenvolvimento por interesse
Estou cético, duvido do seu sucesso
Tendo a considerar esta iniciativa como fracassada, desnecessária para as massas
Votaram 47 usuários. 12 usuários se abstiveram.
Fonte: habr.com

