Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл

Дараа нь бид Move хэлний үндсэн шинж чанар, ухаалаг гэрээний аль хэдийн түгээмэл хэрэглэгддэг хэл болох Solidity (Ethereum платформ дээр) -аас гол ялгаа нь юу болохыг нарийвчлан авч үзэх болно. Энэхүү материал нь онлайнаар 26 хуудас бүхий цагаан хуудсыг судлахад үндэслэсэн болно.

Танилцуулга

Move нь хэрэглэгчийн гүйлгээ болон ухаалаг гэрээг гүйцэтгэхэд хэрэглэгддэг байт код хэл юм. Хоёр зүйлийг анхаарна уу:

  1. Move нь Move виртуал машин дээр шууд ажиллуулах боломжтой байт кодын хэл боловч Solidity (Ethereum-ийн ухаалаг гэрээний хэл) нь EVM (Ethereum Virtual Machine) дээр ажиллахаасаа өмнө байт код руу хөрвүүлэгддэг дээд түвшний хэл юм.
  2. Move-ийг зөвхөн ухаалаг гэрээг хэрэгжүүлэхэд төдийгүй захиалгат гүйлгээнд ашиглах боломжтой (энэ тухай дараа дэлгэрэнгүй үзэх болно), харин Solidity бол зөвхөн гэрээнд зориулагдсан ухаалаг хэл юм.


Орчуулгыг INDEX Protocol төслийн баг гүйцэтгэсэн. Бид аль хэдийн орчуулсан Libra төслийг тодорхойлсон том материал, одоо Move хэлийг бага зэрэг нарийвчлан үзэх цаг болжээ. Орчуулгыг Хабраузертай хамтран хийсэн сэрүүн

Move-ийн гол онцлог нь шугаман логик дээр суурилсан семантикийн тусламжтайгаар захиалгат нөөцийн төрлийг тодорхойлох чадвар юм: нөөцийг хэзээ ч хуулж эсвэл далд устгаж болохгүй, зөвхөн зөөж болно. Функциональ хувьд энэ нь Rust хэлний чадамжтай төстэй юм. Rust дахь утгыг нэг удаад зөвхөн нэг нэрээр оноож болно. Өөр нэрэнд утга оноох нь өмнөх нэрээр ашиглах боломжгүй болгодог.

Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл

Жишээлбэл, дараах кодын хэсэг нь алдаа гаргах болно: 'X' шилжүүлсэн утгыг ашиглах. Учир нь Зэв хотод хог цуглуулах газар байдаггүй. Хувьсагчид хамрах хүрээнээс гарах үед тэдний дурдсан санах ой ч бас суллагдана. Энгийнээр хэлэхэд өгөгдлийн ганц "эзэмшигч" байж болно. Энэ жишээнд x нь анхны эзэн бөгөөд дараа нь y шинэ эзэн болно. Энэ зан үйлийн талаар эндээс уншина уу.

Нээлттэй систем дэх тоон хөрөнгийн төлөөлөл

Биет хөрөнгийн тоон хэлбэрээр илэрхийлэхэд хэцүү хоёр шинж чанар байдаг.

  • Ховор байдал (Хомсдол, анхандаа хомсдол). Систем дэх хөрөнгийн тоо (ялгаруулалт) -ийг хянах ёстой. Одоо байгаа хөрөнгийг хуулбарлахыг хориглох ёстой бөгөөд шинээр бий болгох нь давуу эрхтэй үйл ажиллагаа юм.
  • Хандалтыг хянах... Системийн оролцогч хандалтын хяналтын бодлогыг ашиглан хөрөнгөө хамгаалах чадвартай байх ёстой.

Биет хөрөнгийн хувьд байгалиас заяасан эдгээр хоёр шинж чанарыг дижитал объектод зориулан хэрэгжүүлэх ёстой. Жишээлбэл, ховор метал нь байгалийн хомсдолтой байдаг бөгөөд зөвхөн та үүнийг авах боломжтой (жишээ нь гартаа бариад) зарах эсвэл зарцуулах боломжтой.

Эдгээр хоёр үл хөдлөх хөрөнгийг хэрхэн олж авснаа харуулахын тулд дараах өгүүлбэрүүдээс эхэлье.

Санал No1: Хямдралгүй, хандалтын хяналтгүй хамгийн энгийн дүрэм

Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл

  • G [K]: = n түлхүүрээр хандах боломжтой дугаарын шинэчлэлтийг илэрхийлнэ К блокчэйний дэлхийн төлөв байдалд, шинэ утгатай n.
  • гүйлгээ, Алис, 100 Алисын дансны үлдэгдлийг 100 болгож тохируулна гэсэн үг.

Дээрх шийдэл нь хэд хэдэн гол бэрхшээлтэй байдаг.

  • Алис зүгээр л илгээх замаар хязгааргүй тооны зоос авах боломжтой гүйлгээ "Алис, 100".
  • Алис Боб руу илгээсэн зоос нь ашиггүй, учир нь Боб өөрөө ижил техник ашиглан хязгааргүй тооны зоос илгээх боломжтой байв.

Зөвлөмж No2: Алдагдлыг харгалзан үзнэ

Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл

Одоо бид нөхцөл байдлыг хянаж байгаа бөгөөд ингэснээр зоосны тоо нэмэгдэх болно Ka наад зах нь тэнцүү байсан n шилжүүлэх гүйлгээ хийхээс өмнө. Гэсэн хэдий ч энэ нь хомсдолын асуудлыг шийдэж байгаа боловч Алисын зоосыг хэн илгээх боломжтой талаар мэдээлэл алга байна (одоогоор хэн ч үүнийг хийж чадна, гол зүйл бол хэмжээг хязгаарлах дүрмийг зөрчихгүй байх явдал юм).

Санал No3: Хомсдол ба хандалтын хяналтыг хослуулах

Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл

Бид энэ асуудлыг тоон гарын үсгийн механизмаар шийддэг шалгана уу үлдэгдлийг шалгахаас өмнө Алис хувийн түлхүүрээ ашиглан гүйлгээнд гарын үсэг зурж, зоосны эзэн гэдгээ батална гэсэн үг юм.

Блокчейн програмчлалын хэл

Одоо байгаа блокчейн хэлүүд дараахь асуудлуудтай тулгарч байна (бүгдийг нь Move дээр шийдсэн (анхаарна уу: Харамсалтай нь, нийтлэл зохиогч нь харьцуулалт хийхдээ зөвхөн Ethereum -т ханддаг тул тэдгээрийг зөвхөн энэ хүрээнд авч үзэх нь зүйтэй юм. Жишээлбэл, дараахь зүйлсийн ихэнхийг EOS дээр шийддэг.)))

Хөрөнгийн шууд бус дүрслэл. Хөрөнгийг бүхэл тоо ашиглан кодчилдог боловч бүхэл тоо нь хөрөнгөтэй адил биш юм. Үнэн хэрэгтээ, Bitcoin/Ether/<Ямар ч зоос>-ыг төлөөлөх ямар ч төрөл, утга байхгүй! Энэ нь хөрөнгийг ашигладаг програм бичихэд хүндрэлтэй, алдаа гаргахад хүргэдэг. Процедураас хөрөнгө шилжүүлэх, бүтцэд хөрөнгийг хадгалах гэх мэт загварууд хэлний тусгай дэмжлэг шаарддаг.

Алдагдлыг өргөтгөх боломжгүй... Хэл бол хомсдолтой ганцхан хөрөнгийг илэрхийлдэг. Нэмж дурдахад хомсдолын эсрэг арга хэмжээг хэлний семантикт шууд тусгасан болно. Хөгжүүлэгч нь хэрэв хувийн өмч бий болгохыг хүсч байвал нөөцийн бүх талыг сайтар хянаж байх ёстой. Эдгээр нь Ethereum ухаалаг гэрээний асуудлууд юм.

Хэрэглэгчид үнэ, нийлүүлэлтийн аль алиныг тодорхойлохын тулд бүх хөрөнгийн тоог ашиглан ERC-20 жетоныг гаргадаг. Шинэ жетон үүсгэх бүрт ухаалаг гэрээний код нь ялгаралтын дүрмийг дагаж мөрдөхийг бие даан шалгах ёстой. Нэмж дурдахад хөрөнгийг шууд бусаар танилцуулах нь зарим тохиолдолд давхардал, хоёр дахин зарцуулалт эсвэл бүр хөрөнгөө бүрэн алдах зэрэг ноцтой алдаа гаргахад хүргэдэг.

Хандалтын уян хатан хяналт байхгүй байна... Өнөөдөр ашиглагдаж буй хандалтыг хянах цорын ганц бодлого бол тэгш бус криптограф ашиглан гарын үсгийн схем юм. Хязгаарлагдмал байдлын эсрэг хамгаалалтын нэгэн адил хандалтын хяналтын бодлого нь хэлний семантикт гүн гүнзгий шингэсэн байдаг. Гэхдээ програмистуудад хандалтын хяналтын бодлогоо өөрсдөө тодорхойлох боломжийг олгохын тулд хэлийг хэрхэн өргөжүүлэх нь ихэвчлэн маш хэцүү ажил юм.

Энэ нь Ethereum-д бас үнэн бөгөөд ухаалаг гэрээ нь хандалтын хяналтын эх криптографийн дэмжлэггүй байдаг. Хөгжүүлэгчид хандалтын хяналтыг гараар тохируулах ёстой, жишээлбэл, onlyOwner өөрчлөгчийг ашиглан.

Би Ethereum-ийн том шүтэн бишрэгч хэдий ч аюулгүй байдлын үүднээс хөрөнгийн шинж чанаруудыг хэлээр дэмжих ёстой гэдэгт би итгэдэг. Ялангуяа Ether-ийг ухаалаг гэрээнд шилжүүлэх нь динамик диспетчерийг багтаадаг бөгөөд энэ нь дахин нэвтрэх сул тал гэж нэрлэгддэг шинэ ангиллын алдааг нэвтрүүлсэн. Энд динамик диспетч гэдэг нь кодын гүйцэтгэлийн логикийг хөрвүүлэх үед (статик) биш харин ажиллах үед (динамик) тодорхойлно гэсэн үг юм.

Тиймээс Solidity-д А гэрээ В гэрээнд функцийг дуудах үед В гэрээ нь А гэрээний хөгжүүлэгчийн төлөвлөөгүй кодыг ажиллуулж болох бөгөөд энэ нь дахин нэвтрэх эмзэг байдал (А гэрээ нь дансны үлдэгдэл бодитоор хасагдахаас өмнө мөнгө авах Б гэрээний үүрэг гүйцэтгэсэн).

Move хэлний дизайны үндэс

Анхны захиалгын нөөц

Өндөр түвшинд, Move хэл дээрх модулиуд / нөөцүүд / процедуруудын харилцан үйлчлэл нь OOP хэл дээрх анги / объект, аргуудын хоорондын харилцаатай маш төстэй юм.
Хөдөлгөөнт модулиуд нь бусад блокчэйн дэх ухаалаг гэрээнүүдтэй төстэй юм. Модуль нь зарласан нөөцийг бий болгох, устгах, шинэчлэх дүрмийг тодорхойлдог нөөцийн төрөл, процедурыг тунхагладаг. Гэхдээ эдгээр нь бүгд уламжлал юм ("үг хэллэг") Хөдөлж байна. Хэсэг хугацааны дараа бид энэ санааг тайлбарлах болно.

Уян хатан байдал

Move нь скриптээр дамжуулан Libra-д уян хатан байдлыг нэмдэг. Libra дахь гүйлгээ бүр нь скрипт агуулдаг бөгөөд энэ нь үндсэндээ гүйлгээний үндсэн журам юм. Скрипт нь тодорхой нэг үйлдлийг гүйцэтгэх боломжтой, жишээлбэл, хүлээн авагчдын тодорхой жагсаалтад төлбөр хийх эсвэл бусад нөөцийг дахин ашиглах боломжтой - жишээлбэл, ерөнхий логикийг тодорхойлсон процедурыг дуудах замаар. Ийм учраас Move гүйлгээний скрипт нь илүү уян хатан байдлыг санал болгодог. Скрипт нь нэг удаагийн болон давтагдах зан үйлийг хоёуланг нь ашиглах боломжтой бол Ethereum нь зөвхөн давтагдах скриптүүдийг гүйцэтгэх боломжтой (ухаалаг гэрээний арга дээр нэг аргыг дуудах). Үүнийг "дахин ашиглах боломжтой" гэж нэрлэсэн шалтгаан нь ухаалаг гэрээний функцуудыг олон удаа гүйцэтгэх боломжтой байдаг. (тэмдэглэл: Энд байгаа санаа нь маш нарийн юм. Нэг талаас, псевдобайт код хэлбэрээр гүйлгээний скриптүүд Bitcoin-д бас байдаг. Нөгөөтэйгүүр, миний ойлгож байгаагаар Move нь энэ хэлийг, үнэндээ, бүрэн хэмжээний ухаалаг гэрээний хэлний түвшинд хүртэл өргөжүүлдэг.).

Аюулгүй байдал

Move executable формат нь байт код бөгөөд энэ нь нэг талаас ассемблер хэлээс өндөр түвшний хэл боловч эх кодоос доогуур түвшний хэл юм. Байт кодыг байт код баталгаажуулагч ашиглан нөөц, төрөл, санах ойн аюулгүй байдлыг шалгах (гинжин хэлхээнд) хугацаанд шалгаж, дараа нь орчуулагч гүйцэтгэнэ. Энэ арга нь Move-д эх кодын аюулгүй байдлыг хангах боломжийг олгодог боловч эмхэтгэх процессгүйгээр, системд хөрвүүлэгч нэмэх шаардлагагүй. Move-г байт код хэл болгох нь үнэхээр сайн шийдэл юм. Үүнийг Solidity-тэй адил эх сурвалжаас эмхэтгэх шаардлагагүй бөгөөд хөрвүүлэгчийн дэд бүтцэд гарч болзошгүй алдаа, халдлагын талаар санаа зовох шаардлагагүй болно.

Баталгаажуулалт

Энэ бүгдийг гинжин хэлхээнд хийдэг тул бид шалгалтыг аль болох хялбар хийхийг зорьж байна (санамж: Гүйлгээ бүрийг гүйцэтгэх явцад онлайнаар хийгддэг тул аливаа саатал нь сүлжээг бүхэлд нь удаашруулахад хүргэдэг), гэхдээ эхэндээ хэлний загвар нь сүлжээнээс гадуурх статик баталгаажуулах хэрэгслийг ашиглахад бэлэн байна. Хэдийгээр энэ нь илүү тохиромжтой боловч одоохондоо баталгаажуулах хэрэгслийг (тусдаа хэрэгсэл болгон) ирээдүйд хойшлуулсан бөгөөд одоо зөвхөн ажиллах хугацаандаа (гинжин хэлхээнд) динамик баталгаажуулалтыг дэмжиж байна.

Модульчлал

Хөдөлгөөнт модулиуд нь өгөгдлийн хийсвэрлэлийг хангаж, нөөцийн чухал үйлдлүүдийг нутагшуулдаг. Модулийн өгсөн бүрхүүл нь Move төрлийн системээр хангагдсан хамгаалалттай хослуулан модулийн төрлүүд дээр суурилуулсан шинж чанаруудыг модулийн гаднах кодоор зөрчих боломжгүй болно. Энэ бол нэлээд сайн бодож боловсруулсан хийсвэр хийц бөгөөд гэрээний доторх өгөгдөл нь зөвхөн гэрээний хүрээнд өөрчлөгдөж болох боловч гадуур биш юм.

Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл

Ерөнхий тоймыг шилжүүлэх

Гүйлгээний скриптийн жишээ нь програмистын модулийн гаднах хортой эсвэл болгоомжгүй үйлдэл нь модулийн нөөцийн аюулгүй байдлыг алдагдуулж чадахгүй байгааг харуулж байна. Дараа нь бид Libra блокчэйнийг програмчлахад модуль, нөөц, процедурыг хэрхэн ашигладаг жишээг авч үзэх болно.

Үе тэнгийн төлбөр

Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл

Тоогоор заасан зоосны тоог илгээгчийн үлдэгдэлээс хүлээн авагч руу шилжүүлнэ.
Энд хэд хэдэн шинэ зүйл байна (улаан өнгөөр ​​тодруулсан):

  • 0x0: модуль хадгалагдаж буй дансны хаяг
  • Валютын: модулийн нэр
  • Маркны: нөөцийн төрөл
  • Процедурын дагуу буцааж өгсөн зоос нь 0x0.Currency.Coin төрлийн нөөцийн утга юм
  • хөдлөх (): утгыг дахин ашиглах боломжгүй
  • хуулбарлах (): утгыг дараа нь ашиглаж болно

Кодыг задлан шинжилнэ үү: эхний алхам дээр илгээгч нэртэй процедурыг дууддаг илгээгчээс_харах -д хадгалагдсан модулаас 0x0. Валют. Хоёрдахь алхамд илгээгч нь зоосны нөөцийн үнэ цэнийг модулийн хадгаламжийн журамд шилжүүлснээр хүлээн авагч руу мөнгө шилжүүлдэг. 0x0. Валют.

Шалгалтаар няцаагдах кодын алдааны гурван жишээ энд байна:
Дуудлагыг өөрчлөх замаар мөнгийг хуулбарлах хөдлөх (зоос) тухай хуулбар (зоос). Зөвхөн нөөцийг зөөх боломжтой. Нөөцийн хэмжээг хуулбарлахыг оролдох (жишээ нь, дуудлага хийх замаар хуулбар (зоос) дээрх жишээн дээр) байт кодыг шалгах явцад алдаа гарна.

Тодорхойлох замаар мөнгийг дахин ашиглах хөдлөх (зоос) хоёр удаа . Мөр нэмж байна 0x0.Currency.deposit (хуулбарлах (бусад_төлбөр авагч), зөөх (зоос)) жишээлбэл, дээрх зүйл нь илгээгчид зоосыг хоёр удаа "зарцуулах" боломжийг олгоно - эхний удаа төлбөр авагчтай, хоёр дахь нь бусад_төлбөр авагч. Энэ нь биет хөрөнгөөр ​​байж боломгүй хүсээгүй зан үйл юм. Аз болоход, Move энэ програмаас татгалзах болно.

Татгалзсаны улмаас хөрөнгөө алдах хөдлөх (зоос). Хэрэв та нөөцийг шилжүүлэхгүй бол (жишээлбэл, агуулсан мөрийг устгах замаар хөдлөх (зоос)), байт код баталгаажуулах алдаа гарна. Энэ нь Move программистуудыг санамсаргүй эсвэл хорлонтойгоор мөнгө алдахаас хамгаалдаг.

Валютын модуль

Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл

Бүртгэл бүр 0 ба түүнээс дээш модуль (тэгш өнцөгт хэлбэрээр харуулсан) ба нэг буюу хэд хэдэн нөөцийн утгыг (цилиндрээр харуулсан) агуулж болно. Жишээлбэл, данс 0x0 модулийг агуулдаг 0x0. Валют ба нөөцийн төрлийн үнэ цэнэ 0x0.Currency.Coin. Хаяг дээрх данс 0x1 хоёр нөөц, нэг модультай; Хаяг дээрх данс 0x2 хоёр модуль, нэг нөөцийн утгатай.

Зарим мөчүүд:

  • Гүйлгээний скрипт нь атом юм - энэ нь бүрэн гүйцэтгэсэн эсвэл огт хэрэгждэггүй.
  • Модуль бол дэлхий даяар ашиглах боломжтой урт хугацааны кодын хэсэг юм.
  • Глобал төлөв нь хэш хүснэгт хэлбэрээр хийгдсэн бөгөөд гол нь дансны хаяг юм
  • Бүртгэлд өгөгдсөн төрлийн нэгээс илүүгүй эх үүсвэрийн утгыг, мөн өгөгдсөн нэртэй нэгээс илүүгүй модулийг агуулж болно. 0x0 нэмэлт нөөцийг агуулж болохгүй 0x0.Currency.Coin эсвэл өөр нэртэй модуль Валютын)
  • Зарлагдсан модулийн хаяг нь төрөл (0x0.Currency.Coin и 0x1.Currency.Coin сольж ашиглах боломжгүй тусдаа төрлүүд)
  • Программистууд өөрсдийн захиалгат нөөцийг тодорхойлсноор энэ төрлийн нөөцийн олон тохиолдлыг дансанд хадгалах боломжтой - (нөөц TwoCoins {c1: 0x0.Currency.Coin, c2: 0x0.Currency.Coin})
  • Та эх сурвалжийг ямар ч зөрчилгүйгээр нэрээр нь дурдаж болно, жишээ нь та хоёр эх сурвалжийг ашиглаж болно TwoCoins.c1 и TwoCoins.c2.

Зоосны нөөцийн мэдэгдэл

Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл
Модуль нэрлэсэн Валютын болон нэрлэсэн нөөцийн төрөл Маркны

Зарим мөчүүд:

  • Маркны нь нэг төрлийн талбар бүхий бүтэц юм u64 (64 битийн тэмдэггүй бүхэл тоо)
  • Зөвхөн модулийн процедур Валютын төрлийн утгыг үүсгэх эсвэл устгах боломжтой Маркны.
  • Бусад модулиуд болон скриптүүд нь зөвхөн модулийн өгсөн нийтийн процедураар дамжуулан утгын талбарыг бичих эсвэл лавлах боломжтой.

Хадгаламж худалдах

Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл

Энэ процедур нь нөөцийг хүлээн авдаг Маркны орц болгон ашиглаж, нөөцтэй нэгтгэдэг Маркныхүлээн авагчийн дансанд хадгалагдсан:

  1. Зоосын оролтын нөөцийг устгаж, үнэ цэнийг нь бүртгэж байна.
  2. Хүлээн авагчийн дансанд хадгалагдсан зоосны өвөрмөц нөөцийн холбоосыг хүлээн авах.
  3. Процедурыг дуудах үед Зоосны тооны утгыг параметрт дамжуулсан утгаар өөрчлөх.

Зарим мөчүүд:

  • BorrowGlobal, задлах - суурилуулсан журам
  • Савыг задлах Энэ нь T төрлийн нөөцийг устгах цорын ганц арга юм. Процедур нь нөөцийг оролт болгон авч, устгаж, нөөцийн талбаруудтай холбоотой утгыг буцаана.
  • BorrowGlobal хаягийг оролт болгон авч, тухайн хаягаар нийтлэгдсэн (эзэмшсэн) T-ийн өвөрмөц жишээний лавлагааг буцаана
  • &mut Coin Энэ бол эх сурвалжийн холбоос юм Маркны

Илгээгчээс_салах үйлдлийг хэрэгжүүлэх

Dive into Move - Facebook-ийн Libra блокчэйн програмчлалын хэл

Энэ журам:

  1. Өвөрмөц нөөцийн холбоосыг авдаг Маркны, илгээгчийн данстай холбогдсон
  2. Нөөцийн үнэ цэнийг бууруулдаг Маркны холбоосоор дамжуулан заасан хэмжээгээр
  3. Шинэ нөөц үүсгэж, буцаана Маркны шинэчлэгдсэн үлдэгдэлтэй.

Зарим мөчүүд:

  • хадгаламжийн хэн ч үүсгэж болно, гэхдээ илгээгчээс_харах зөвхөн дуудлага хийх дансны зоос руу хандах эрхтэй
  • GetTxnSenderAddress тэй төстэй msg.sender Solidity-д
  • Үгүй бол татгалзах тэй төстэй шаарддаг Solidity-д. Хэрэв энэ шалгалт амжилтгүй болвол гүйлгээг зогсоож, бүх өөрчлөлтийг буцаана.
  • Багц Энэ нь мөн T төрлийн шинэ нөөцийг бий болгодог суурилуулсан процедур юм.
  • Түүнчлэн Савыг задлах, Багц зөвхөн эх сурвалжийг тодорхойлсон модуль дотор дуудаж болно T

дүгнэлт

Бид Move хэлний үндсэн шинж чанаруудыг судалж, Ethereum-тай харьцуулж, мөн скриптүүдийн үндсэн синтакстай танилцсан. Эцэст нь би шалгахыг зөвлөж байна анхны цагаан цаас. Энэ нь програмчлалын хэлний дизайны зарчмуудын талаархи дэлгэрэнгүй мэдээлэл, түүнчлэн олон хэрэгтэй холбоосуудыг агуулдаг.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх