Тестов клиент TON (Telegram Open Network) и нов език Fift за смарт-контракти

Преди повече от година стана известно за плановете на месинджъра Telegram да пусне собствена децентрализирана мрежа Telegram Open Network. Тогава стана достъпен обемен технически документ, който, предполагам, е написан от Николай Дуров и описва структурата на бъдещата мрежа. За тези, които пропуснаха — препоръчвам да се запознаят с моя преразказ на този документ (част 1, част 2; третата част, за съжаление, все още прахосва в черновите).

Оттогава няма значителни новини относно статуса на разработката на TON, докато преди няколко дни (в един от неофициалните канали) не се появи линк към страницата https://test.ton.org/download.html, където са разположени:
ton-test-liteclient-full.tar.xz — изходния код на лек клиент за тестовата мрежа TON;
ton-lite-client-test1.config.json — конфигурационен файл за свързване с тестовата мрежа;
README — информация за компилирането и стартирането на клиента;
HOWTO — стъпка по стъпка инструкция за създаване на смарт-контракт с помощта на клиента;
ton.pdf — обновен документ (от 2 март 2019 г.) с технически преглед на мрежата TON;
tvm.pdf — техническо описание на TVM (TON Virtual Machine, виртуална машина на TON);
tblkch.pdf — техническо описание на блокчейна TON;
fiftbase.pdf — описание на новия език Fift, предназначен за създаване на смарт-контракти в TON.

Да повторя, официални потвърждения на страницата и всички тези документи от страна на Telegram не е имало, но обемът на тези материали ги прави достатъчно правдоподобни. Стартирайте публикувания клиент на собствен риск.

Сглобяване на тестовия клиент

За начало да опитаме да сглобим и стартираме тестовия клиент — все пак, README подробно описва този несложен процес. Аз ще го направя на примера на macOS 10.14.5, за успешността на сглобяването на други системи не мога да дам гаранции.

  1. Сваляме и разархивираме архива с изходния код. Важно е да сваляте последната версия, тъй като обратно съвместимост на този етап не се гарантира.

  2. Убедете се, че в системата са инсталирани последните версии на make, cmake (версии 3.0.2 или по-високи), OpenSSL (включително заглавни файлове C), g++ или clang. На мен не ми се наложи да инсталирам нищо допълнително, всичко се компилира веднага.

  3. Да предположим, че изходният код е разархивиран в папката ~\/lite-client. Отделно от нея създаваме празна папка за сглобения проект (например, ~\/liteclient-build), и от нея (cd ~\/liteclient-build) извикваме командите:

    cmake ~\/lite-client
    cmake --build . --target test-lite-client

    Тестов клиент TON (Telegram Open Network) и нов език Fift за смарт-контракти

    За сглобяване на интерпретатора на езика Fift за смарт-контракти (за него по-долу), също извикваме

    cmake --build . --target fift

  4. Сваляме актуалния конфигурационен файл за свързване с тестовата мрежа и го поставяме в папката с компилирания клиент.

  5. Готово, можете да стартирате клиента:

    ./test-lite-client -C ton-lite-client-test1.config.json

Ако всичко е направено правилно, трябва да видите нещо такова:

Тестов клиент TON (Telegram Open Network) и нов език Fift за смарт-контракти

Достъпните команди, както виждаме, са малко:
help — да изведем този списък с команди;
quit — да излезем;
time — да покажем текущото време на сървъра;
статус — да покажем състоянието на свързването и локалната БД;
last — да обновим състоянието на блокчейна (да заредим последния блок). Тази команда е важна да се изпълни преди всякакви заявки, за да сме сигурни, че виждаме актуалното състояние на мрежата.
sendfile <filename> — да заредим локален файл в мрежата TON. Така става взаимодействието с мрежата — включително, например, създаването на нови смарт-контракти и заявки за прехвърляне на средства между акаунти;
getaccount

— да покажем текущото (в момента на изпълнение на командата last) състояние на акаунта с указания адрес;
privkey <filename> — да заредим личния ключ от локален файл.

Ако при стартиране на клиента му предадем папка с опцията -D, той ще складира последния блок на мастерчейна в нея:

.\/test-lite-client -C ton-lite-client-test1.config.json -D ~\/ton-db-dir

Сега можем да преминем към по-интересни неща — да изучим езика Fift, да опитаме да компилираме смарт-контракт (например, да създадем тестов портфейл), да го заредим в мрежата и да опитаме прехвърляне на средства между акаунти.

Езикът Fift

От документа fiftbase.pdf можем да научим, че за създаване на смарт-контракти екипът на Telegram е създал нов стеков език Fift (очевидно, от числителното fifth, по аналогия с Forth — езика, с който Fift има много общо).

Документът е доста обемен, с 87 страници, и не смятам да разказвам подробно съдържанието му в рамките на тази статия (понеже самият не съм го завършил : ). Ще се спра на основните моменти и ще дам няколко примера на код на този език.

На базово ниво синтаксисът на Fift е доста прост: кодът му се състои от думи, обикновено разделени с интервали или нови редове (специален случай: някои думи не изискват разделител след тях). Всяко думата — това е регистрирани последователности от символи, на които съответстват определени определения (грубо казано, това, което интерпретаторът трябва да направи, когато срещне тази дума). Ако не съществува определение на думата, интерпретаторът се опитва да я парсне като число и да я постави на стека. Между другото, числата тук — внезапно — са 257-битови цели числа, а дробни не съществуват изобщо — по-точно, те веднага се превръщат в двойка цели числа, които образуват числител и знаменател на рационалната дроб.

Думите по правило взаимодействат със стойностите, лежащи на върха на стека. Отделен тип думи — префиксна — използва не стека, а следващите след тях символи от входния файл. Например, така са реализирани стринговите литерали — символът "кавычка" (") е префиксна дума, която търси следващата (затваряща) кавичка и поставя стринга между тях на стека. По подобен начин се държат еднострочни (//) и многострочни (/*) коментари.

Тук почти всичко вътрешно устройство на езика приключва. Всичко останало (включително управляващите конструкции) е определено като думи (или вътрешни, като аритметични операции и определяне на нови думи; или определени в "стандартната библиотека" Fift.fif, която се намира в папката crypto/fift в изходния код).

Прост пример на програма на Fift:

{ dup =: x dup * =: y } : setxy
3 setxy x . y . x y + .
7 setxy x . y . x y + .

В първия ред се определя нова дума setxy (обърнете внимание на префикса {, който създава блок до затварящия } и префикса :, който всъщност определя думата). setxy взема число от върха на стека, определя (или преназначава) го като глобална константа x, а квадратът на това число — като константа y (имайки предвид, че стойностите на константите могат да се преназначават, по-скоро бих ги нарекъл променливи, но следвам именуването в езика).

В следващите две реда на стека се поставя число, извиква се setxy, след това се извеждат стойностите на константите x, y (за извеждане се използва думата .), двете константи се поставят на стека, събират се и резултатът също се извежда. В резултат ще видим:

3 9 12 ok
7 49 56 ok

(Редът "ok" се извежда от интерпретатора, когато приключи с обработката на текущия ред в интерактивен режим на въвеждане)

И ето един пълен пример за код:

"Asm.fif" включва

-1 константа wc  // създаване на портфейл в работната верига -1 (главна верига)

// Създаване на нов прост портфейл
&lt;{  SETCP0 DUP IFNOTRET INC 32 THROWIF  // връщане, ако recv_internal, провал освен ако recv_external
    512 INT LDSLICEX DUP 32 PLDU  // подписване на cs cnt
    c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS  // подписване на cs cnt cnt&#039; pubk
    s1 s2 XCPU            // подписване на cs cnt pubk cnt&#039; cnt
    EQUAL 33 THROWIFNOT   // ( несъответствие в seqno? )
    s2 PUSH HASHSU        // подписване на cs cnt pubk hash
    s0 s4 s4 XC2PU        // pubk cs cnt hash подпис pubk
    CHKSIGNU              // pubk cs cnt ?
    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
}&gt;c
// код
<b 0 32 u, newkeypair swap dup constant wallet_pk "new-wallet.pk" b>файл
   B, 
b&gt;  // данни
// без библиотеки
<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>$ тип cr
256 u&gt;B "new-wallet.addr" B&gt;файл
<b 0 32 u, b>
dup ."подписващо съобщение: " <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 ."Външно съобщение за инициализация е " <s csr. cr
2 boc+>B dup Bx. cr
"new-wallet-query.boc" tuck B&gt;файл
."(Запазено в файл " тип .")" cr

Този леко стряскащ файл е предназначен за създаване на смарт-контракт — той ще бъде поставен в файл new-wallet-query.boc след изпълнението. Обърнете внимание, че тук се използва още един, асемблерен език за TON Virtual Machine (който няма да разглеждам подробно), чийто инструкции ще бъдат поставени в блокчейна.

Така че, асемблерът за TVM е написан на Fift — изходният код на този асемблер се намира в файла crypto/fift/Asm.fif и се включва в началото на горепосочения код.

Какво мога да кажа, очевидно Николай Дуров просто обожава да създава нови програмни езици 🙂

Създаване на смарт-контракт и взаимодействие с TON

И така, да предположим, че сме събрали клиент TON и интерпретатор Fift, както е описано по-горе, и сме се запознали с езика. Как вървим напред със създаването на смарт-контракт? Това е описано в файла HOWTO, приложен към изходния код.

Акаунти в TON

Както описах в преглед на TON, тази мрежа съдържа повече от един блокчейн — има един общ, т.н. „мастерчейн“, както и произволен брой допълнителни „воркчейнове“, идентифицирани чрез 32-битово число. Мастерчейнът има идентификатор -1, освен него може да се ползва и „базов“ воркчейн с идентификатор 0. Всеки воркчейн може да има своя конфигурация. Вътрешно всеки воркчейн се разделя на шардчейнове, но това е детайл от реализацията, който не е необходимо да помним.

В рамките на един воркчейн се съхраняват множество акаунти, които имат свои идентификатори account_id. За мастерчейна и нулевия воркчейн те имат дължина 256 бита. Така идентификаторът на акаунта може да бъде записан, например, така:

-1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d

Това е „суров“ формат: най-напред идентификаторът на воркчейна, след това двоеточие и идентификаторът на акаунта в шестнадесетичен формат.

Освен това, има съкратен формат — номерът на воркчейна и адресът на акаунта са кодирани в двоичен вид, към тях е добавена контролна сума и всичко това е кодирано в Base64:

Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb

Знаейки този формат на записа, можем да заявим текущото състояние на някой акаунт чрез тестов клиент с команда

getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d

Приблизително ще получим такъв отговор:

[ 3][t 2][1558746708.815218925][test-lite-client.cpp:631][!testnode]    искам състоянието на акаунта за -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D
[ 3][t 2][1558746708.858564138][test-lite-client.cpp:652][!testnode]    получих състоянието на акаунта за -1:8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D относно блокове (-1,8000000000000000,72355):F566005749C1B97F18EDE013EBA7A054B9014961BC1AD91F475B9082919A2296:1BD5DE54333164025EE39D389ECE2E93DA2871DA616D488253953E52B50DC03F и (-1,8000000000000000,72355):F566005749C1B97F18EDE013EBA7A054B9014961BC1AD91F475B9082919A2296:1BD5DE54333164025EE39D389ECE2E93DA2871DA616D488253953E52B50DC03F
състоянието на акаунта е (акаунт
  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:74208000003
    balance:(currencies
      grams:(nanograms
        amount:(var_uint len:7 value:999928362430000))
      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{0000000D}
            ))
        library:hme_empty))))
x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C000000000000000451C90E00DC0E35B7DB5FB8C134_}
 x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
 x{0000000D}

Виждаме структура, която се съхранява в DHT на посочената работна верига. Например, в полето storage.balance се намира текущият баланс на акаунта, в storage.state.code — код на смарт договора, а в storage.state.data — неговите текущи данни. Обърнете внимание, че хранилището на данни в TON — Cell, клетките — е дървовидно, като всяка клетка може да има както свои данни, така и дъщерни клетки. Това е показано чрез отстъпите в последните редове.

Сглобяване на смарт договора

Сега нека създадем такава структура (тя се нарича BOC — bag of cells) с помощта на езика Fift. За щастие, не е нужно сами да пишем смарт договор — в папката crypto/block в архива с изходните кодове има файл new-wallet.fif, който ще ни помогне да създадем нов портфейл. Ще го копираме в папката с компилирания клиент (~\/liteclient-build, ако сте действали по инструкциите по-горе). Съдържанието му вече беше предоставено по-горе като пример за код на Fift.

Изпълняваме този файл по следния начин:

./crypto/fift -I"/crypto/fift" new-wallet.fif

Тук <source-directory> трябва да се замени с пътя до разархивираните изходни кодове (символ „~“ тук, за съжаление, не може да се използва, нужен е пълен път). Вместо да използвате флага -I можете да определите променлива на средата FIFTPATH и да поставите този път в нея.

Тъй като Fift стартирахме с името на файла new-wallet.fif, той ще го изпълни и ще приключи. Ако името на файла не бъде определено, можете да играете с интерпретатора в интерактивен режим.

В конзолата след изпълнение трябва да се изведе нещо такова:

StateInit: x{34_}
 x{FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
 x{0000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B}

нов адрес на портфейл = -1 : 4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2 
0f9PzVILj8yglrVn1zS-NSjtxr7QBfaTCp7JrBqnFPIR8nhZ
подписващо съобщение: x{00000000}

Външно съобщение за инициализация е x{89FEE120E20C7E953E31546F64C23CD654002C1AA919ADD24DB12DDF85C6F3B58AE41198A28AD8DAF3B9588E7A629252BA3DB88F030D00BC1016110B2073359EAC3C13823C53245B65D056F2C070B940CDA09789585935C7ABA4D2AD4BED139281CFA1200000001_}
 x{FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
 x{0000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B}

B5EE9C724104030100000000D60002CF89FEE120E20C7E953E31546F64C23CD654002C1AA919ADD24DB12DDF85C6F3B58AE41198A28AD8DAF3B9588E7A629252BA3DB88F030D00BC1016110B2073359EAC3C13823C53245B65D056F2C070B940CDA09789585935C7ABA4D2AD4BED139281CFA1200000001001020084FF0020DDA4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED5400480000000055375F730EDC2292E8CB15C42E8036EE9C25AA958EE002D2DE48A205E3A3426B6290698B
(Записано в файла new-wallet-query.boc)

Това означава, че портфейлът с идентификатор -1:4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2 (или, което е равно на 0f9PzVILj8yglrVn1zS-NSjtxr7QBfaTCp7JrBqnFPIR8nhZ) е успешно създаден. Съответният му код ще бъде в файла new-wallet-query.boc, адресът му — в new-wallet.addr, а частният ключ — в new-wallet.pk (внимавайте — повторното стартиране на скрипта ще презапише тези файлове).

Разбира се, мрежата TON все още не знае за този портфейл, той се съхранява само като тези файлове. Сега трябва да бъде качен в мрежата. Проблемът е, че за създаването на смарт-контракт е необходимо да се заплати такса, а балансът на вашия акаунт все още е нулев.

В работен режим този проблем ще се реши с покупка на грамове на борсата (или превод от друг портфейл). А в настоящия тестови режим е въведен специален смарт-контракт, от който може да поискате до 20 грама просто така.

Формиране на заявка към чужд смарт-контракт

Заявка към смарт-контракта, който раздава грамове наляво и надясно, правим така. В същата папка crypto/block намираме файла 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 ."обгръщащо съобщение: " <s csr. cr
<b b{1000100} s, wc 8 i, wallet_addr 256 u, 0 gram, b{00} s,
 swap <s b>
dup ."допълващо външно съобщение: " <s csr. cr
2 boc+>B dup Bx. cr
"wallet-query.boc" B&gt;file

И него ще запазим в папката с компилирания клиент, но ще поправим петия ред — преди реда "constant dest_addr". Заменяме го с адреса на онзи портфейл, който създадохте преди това (пълният, не съкратен). "-1:" в началото не е нужно да се пише, вместо това в началото поставете "0x".

Освен това можете да промените реда 6.666 Грам* / постоянна сума — това е сумата в грамове, която искате (не повече от 20). Дори и да посочите цяло число, оставете десетичната запетая.

Накрая, трябва да коригирате реда 0x00000011 постоянен seqno. Първото число тук е текущият sequence number, който се съхранява в акаунта, издаващ грамовете. Как да го получите? Както беше споменато по-горе, стартирайте клиента и изпълнете:

last
getaccount -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d

В края на данните на смарт-контракта ще бъде

...
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
 x{0000000D}

Числото 0000000D (при вас ще е по-голямо) е sequence number, който трябва да поставите в testgiver.fif.

Всичко, запазваме файла и стартираме (. /crypto /fift testgiver.fif). На изхода ще получим файла wallet-query.boc. Това е формираната съобщението към чужд смарт-контракт — молба "преведи толкова грам на такъв акаунт".

С помощта на клиента го зареждаме в мрежата:

> sendfile wallet-query.boc
[ 1][t 1][1558747399.456575155][test-lite-client.cpp:577][!testnode]    изпраща запитване от файл wallet-query.boc
[ 3][t 2][1558747399.500236034][test-lite-client.cpp:587][!query]   статус на външното съобщение е 1

Ако сега извикате last, а след това отново поискате статус на акаунта, от който поискахме грамовете, тогава трябва да видим, че неговият sequence number се е увеличил с единица — това значи, че той е изпратил парите на нашия акаунт.

Остана последната стъпка — зареждаме кода на нашия портфейл (балансът му вече е попълнен, но без кода на смарт-контракта не можем да го управляваме). Изпълняваме sendfile new-wallet-query.boc — и всичко, имате собствен портфейл в мрежата TON (макар и само тестов).

Създаване на изходящи транзакции

За да преведете пари от баланса на създадения акаунт, има файл crypto /block /wallet.fif, който също трябва да бъде поставен в папката със събран клиента.

По аналогия с предишните стъпки, в него трябва да коригирате сумата, която превеждате, адреса на получателя (dest_addr) и seqno на вашия портфейл (той е равен на 1 след инициализацията на портфейла и се увеличава с 1 след всяка изходяща транзакция — можете да го видите, като поискате състоянието на вашия акаунт). За тестове можете да използвате, например, моя портфейл — 0x4fcd520b8fcca096b567d734be3528edc6bed005f6930a9ec9ac1aa714f211f2.

При стартиране (. /crypto /fift wallet.fif) скриптът ще вземе адреса на вашия портфейл (откъдето превеждате) и неговия частен ключ от файловете new-wallet.addr и new-wallet.pk, а полученото съобщение ще запише в new-wallet-query.boc.

Както и преди, за да изпълните транзакцията, извикайте sendfile new-wallet-query.boc в клиента. След това не забравяйте да актуализирате състоянието на блокчейна (last) и проверяваме дали балансът и seqno на нашия портфейл са се променили (getaccount).

Тестов клиент TON (Telegram Open Network) и нов език Fift за смарт-контракти

И това е всичко, сега умеем да създаваме смарт-контракти в TON и да изпращаме запитвания към тях. Както виждаме, настоящата функционалност вече е достатъчна, за да създадем по-достъпен портфейл с графичен интерфейс (въпреки че се очаква, че той така или иначе ще бъде наличен като част от месинджъра).

Само регистрирани потребители могат да участват в анкетата. Влезте, моля.

Интересувате ли се от продължението на статиите с анализ на TON, TVM, Fift?

  • Да, очаквам завършването на цикъла статии с общ преглед на TON

  • Да, интересно ми е да прочета повече за езика Fift

  • Да, искам да науча повече за TON Virtual Machine и асемблера за него

  • Не, не ме интересува нищо от това

Гласували 39 потребители. Въздържали се 12 потребители.

Как се отнасяте към плановете на Telegram за стартиране на TON?

  • Имам големи надежди за този проект

  • Просто с интерес следя развитието му

  • Настроен съм скептично, съмнявам се в успеха му

  • Склонен съм да считам тази инициатива за неуспешна и ненужна за широката публика

Гласували 47 потребители. Въздържали се 12 потребители.

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster