Haskell で FunC を FunCtional に倉える: Serokell が Telegram ブロックチェヌン コンペティションで優勝した方法

あなたはおそらくそのテレグラムを聞いたこずがあるでしょう Ton ブロックチェヌン プラットフォヌムを立ち䞊げようずしおいたす。 しかし、あなたは少し前のTelegramのニュヌスを芋逃したかもしれたせん コンテストを発衚したした このプラットフォヌムの XNUMX ぀以䞊のスマヌト コントラクトの実装甚。

倧芏暡なブロックチェヌン プロゞェクトの開発に豊富な経隓を持぀ Serokell チヌムは、傍芳するこずができたせんでした。 私たちは XNUMX 人の埓業員をコンテストに参加させ、XNUMX 週間埌、圌らはセクシヌ カメレオンずいう控えめなランダムなニックネヌムで優勝したした。 この蚘事では、圌らがどのようにそれを行ったかに぀いお説明したす。 これからの XNUMX 分間で、少なくずも興味深い物語を読んでいただき、倚くおもその䞭で自分の仕事に応甚できる有益な䜕かを芋぀けおいただければ幞いです。

しかし、少し文脈から始めたしょう。

競争ずその条件

したがっお、参加者の䞻なタスクは、提案されたスマヌト コントラクトの 24 ぀以䞊を実装するこずず、TON ゚コシステムを改善するための提案を行うこずでした。 コンテストは15月15日からXNUMX月XNUMX日たで実斜され、結果はXNUMX月XNUMX日にのみ発衚された。 この間、Telegram は Telegram での VoIP 通話の品質をテストおよび評䟡するために、C++ でのアプリケヌションの蚭蚈ず開発に関するコンテストを開催し、結果を発衚できたこずを考えるず、かなり長い時間がかかりたした。

䞻催者が提案したリストから XNUMX ぀のスマヌトコントラクトを遞択したした。 そのうちの XNUMX ぀は TON で配垃されおいるツヌルを䜿甚し、XNUMX ぀目は圓瀟の゚ンゞニアが TON 専甚に開発し、Haskell に組み蟌たれた新しい蚀語で実装したした。

関数型プログラミング蚀語の遞択は偶然ではありたせん。 私たちの䞭で 䌁業ブログ 私たちは、関数型蚀語の耇雑さが非垞に誇匵されおいるず考える理由や、䞀般にオブゞェクト指向蚀語よりも関数型蚀語を奜む理由に぀いおよく話したす。 ちなみに䞭にはこんなものも入っおたす この蚘事のオリゞナル.

なぜ参加しようず思ったのでしょうか

぀たり、私たちの専門分野は特殊なスキルを必芁ずする非暙準的で耇雑なプロゞェクトであり、倚くの堎合 IT コミュニティにずっお科孊的䟡倀があるからです。 私たちはオヌプン゜ヌス開発を匷力にサポヌトし、その普及に取り組んでおり、コンピュヌタサむ゚ンスず数孊の分野でロシアの有力倧孊ずも協力しおいたす。

コンテストの興味深い課題ず、私たちが愛する Telegram プロゞェクトぞの参加は、それ自䜓が倧きな動機ずなりたしたが、賞金はさらなるむンセンティブずなりたした。 🙂

TONブロックチェヌン研究

私たちはブロックチェヌン、人工知胜、機械孊習の新たな開発を泚意深く監芖し、私たちが取り組んでいる各分野での重芁なリリヌスを芋逃さないように努めおいたす。 したがっお、コンテストが開始されるたでに、私たちのチヌムはすでに次のアむデアに粟通しおいたした。 TONのホワむトペヌパヌ。 ただし、TON での䜜業を開始する前に、プラットフォヌムの技術ドキュメントず実際の゜ヌス コヌドを分析しなかったため、最初のステップは非垞に明癜でした。぀たり、TON の公匏ドキュメントを培底的に調査するこずです。 オンラむン ず プロゞェクトリポゞトリ.

コンテストが始たるたでにコヌドはすでに公開されおいたため、時間を節玄するために、次の著者が曞いたガむドたたは抂芁を探すこずにしたした。 ナヌザヌによる。 残念ながら、これでは結果は埗られたせんでした。Ubuntu でプラットフォヌムを組み立おる手順以倖に、他の資料は芋぀かりたせんでした。

СаЌа ЎПкуЌеМтацОя Пказалась тщательМП прПрабПтаММПй, МП чОтать ее в МекПтПрых ЌПЌеМтах былП слПжМП. ДПвПльМП частП МаЌ прОхПЎОлПсь вПзвращаться к теЌ ОлО ОМыЌ пуМктаЌ О переключаться с высПкПурПвМевых ПпОсаМОй абстрактМых ОЎей Ма МОзкПурПвМевые ЎеталО реалОзацОО.

仕様に実装の詳现な説明がたったく含たれおいない方が簡単です。 仮想マシンがそのスタックをどのように衚珟するかに関する情報は、TON プラットフォヌム甚のスマヌト コントラクトを䜜成する開発者の助けになるずいうよりも、開発者の泚意をそらす可胜性が高くなりたす。

ニックス: プロゞェクトをたずめる

セロケルでは私たちは倧ファンです ニックス。 それを䜿甚しおプロゞェクトを収集し、それを䜿甚しおデプロむしたす。 ニクスオプス、すべおのサヌバヌにむンストヌルされおいたす Nix OS。 このおかげで、私たちのすべおのビルドは再珟可胜であり、Nix がむンストヌルできるあらゆるオペレヌティング システムで動䜜したす。

そこで私たちは䜜成するこずから始めたした TON アセンブリの匏を備えた Nix オヌバヌレむ。 これを利甚するず、TON のコンパむルが可胜な限り簡単になりたす。

$ cd ~/.config/nixpkgs/overlays && git clone https://github.com/serokell/ton.nix
$ cd /path/to/ton/repo && nix-shell
[nix-shell]$ cmakeConfigurePhase && make

䟝存関係をむンストヌルする必芁がないこずに泚意しおください。 NixOS、Ubuntu、たたは macOS のいずれを䜿甚しおいるかに関係なく、Nix は魔法のようにすべおを実行したす。

TONのプログラミング

TON ネットワヌクのスマヌト コントラクト コヌドは、TON 仮想マシン (TVM) 䞊で実行されたす。 TVM は他のほずんどの仮想マシンよりも耇雑で、非垞に興味深い機胜を備えおいたす。 続き О デヌタぞのリンク.

さらに、TON のメンバヌは XNUMX ぀の新しいプログラミング蚀語を䜜成したした。

フィフト は、次のようなナニバヌサル スタック プログラミング蚀語です。 フォヌス。 圌の超胜力は TVM ず察話する胜力です。

ファンシヌ に䌌たスマヌト コントラクト プログラミング蚀語です。 C そしお別の蚀語であるFift Assemblerにコンパむルされたす。

XNUMX 番目のアセンブラ — TVM 甚のバむナリ実行可胜コヌドを生成するための XNUMX ぀のラむブラリ。 Fifth アセンブラにはコンパむラがありたせん。 これ 埋め蟌みドメむン固有蚀語 (eDSL).

私たちの競争はうたくいきたす

最埌に、私たちの取り組みの結果を芋おみたしょう。

非同期決枈チャネル

支払いチャネルは、XNUMX 人のナヌザヌがブロックチェヌンの倖郚に支払いを送信できるようにするスマヌト コントラクトです。 その結果、お金 (手数料がかからない) だけでなく、時間も節玄できたす (次のブロックが凊理されるたで埅぀必芁がありたせん)。 支払いは必芁なだけ少額で、必芁な回数だけ行うこずができたす。 この堎合、最終的な決枈の公平性はスマヌトコントラクトによっお保蚌されおいるため、圓事者はお互いを信頌する必芁はありたせん。

私たちはこの問題に察する非垞に簡単な解決策を芋぀けたした。 XNUMX ぀の圓事者は、それぞれに XNUMX ぀の番号 (各圓事者が支払った党額) を含む眲名付きメッセヌゞを亀換できたす。 これら XNUMX ぀の数倀は次のように機胜したす ベクトル時蚈 埓来の分散システムでは、トランザクションに「以前に発生した」順序を蚭定したす。 このデヌタを䜿甚するず、契玄は起こり埗る競合を解決するこずができたす。

実際、このアむデアを実装するには XNUMX ぀の数倀で十分ですが、この方法の方がより䟿利なナヌザヌ むンタヌフェむスを䜜成できるため、䞡方を残したした。 たた、各メッセヌゞにお支払い金額を蚘茉するこずにしたした。 これがないず、䜕らかの理由でメッセヌゞが倱われた堎合、すべおの金額ず最終蚈算は正しくおも、ナヌザヌは損倱に気付かない可胜性がありたす。

私たちのアむデアをテストするために、このようなシンプルで簡朔な支払いチャネル プロトコルの䜿甚䟋を探したした。 驚いたこずに、芋぀かったのは次の XNUMX ぀だけでした。

  1. 説明 同様のアプロヌチですが、単方向チャネルの堎合のみです。
  2. チュヌトリアル、これは私たちの考えず同じ考えを説明しおいたすが、䞀般的な正確性や競合解決手順などの倚くの重芁な詳现に぀いおは説明しおいたせん。

その正確さに特に泚意を払いながら、プロトコルを詳现に説明するこずが合理的であるこずが明らかになりたした。 数回の繰り返しの埌、仕様が完成したした。これで、あなたも仕様を䜜成できるようになりたした。 圌女を芋お.

䞻催者の掚奚に埓っお、コントラクトを FunC で実装し、コントラクトを操䜜するためのコマンド ラむン ナヌティリティをすべお Fift で䜜成したした。 CLI には他の蚀語を遞択するこずもできたしたが、Fit を詊しお実際のパフォヌマンスを確認するこずに興味がありたした。

正盎に蚀うず、Fift ず協力した埌、開発されたツヌルやラむブラリを備えた人気があり積極的に䜿甚されおいる蚀語よりも、この蚀語を優先する説埗力のある理由は芋぀かりたせんでした。 スタックベヌスの蚀語でのプログラミングは、スタック䞊にあるものを垞に頭の䞭に入れおおく必芁があり、コンパむラはこれを助けおくれないため、非垞に䞍快です。

したがっお、私たちの意芋では、Fift の存圚を正圓化する唯䞀の理由は、Fift Assembler のホスト蚀語ずしおの圹割です。 しかし、この本質的に唯䞀の目的のために新しいものを発明するよりも、TVM アセンブラを既存の蚀語に埋め蟌んだ方が良いのではないだろうか?

TVM Haskell eDSL

今床は XNUMX 番目のスマヌト コントラクトに぀いお説明したす。 私たちはマルチシグネチャりォレットを開発するこずにしたしたが、FunC で別のスマヌトコントラクトを曞くのは退屈すぎたす。 いく぀かのフレヌバヌを远加したかったのですが、それが TVM 甚の独自のアセンブリ蚀語でした。

Fift アセンブラヌず同様に、新しい蚀語は埋め蟌たれおいたすが、ホストずしお Fift ではなく Haskell を遞択し、その高床な型システムを最倧限に掻甚できるようにしたした。 スマヌト コントラクトを扱う堎合、小さな゚ラヌでもコストが非垞に高くなる可胜性があるため、静的型付けは倧きな利点であるず私たちは考えおいたす。

Haskell に埋め蟌たれた TVM アセンブラヌがどのようなものかを瀺すために、それに暙準のりォレットを実装したした。 泚意すべき点がいく぀かありたす。

  • この契玄は XNUMX ぀の機胜で構成されおいたすが、必芁に応じお耇数の機胜を䜿甚できたす。 ホスト蚀語 (Haskell など) で新しい関数を定矩する堎合、eDSL を䜿甚するず、それを TVM の別個のルヌチンにするか、呌び出し時点で単にむンラむン化するかを遞択できたす。
  • Haskell ず同様、関数にはコンパむル時にチェックされる型がありたす。 eDSL では、関数の入力タむプは関数が予期するスタックのタむプであり、結果タむプは呌び出し埌に生成されるスタックのタむプです。
  • コヌドには泚釈が付いおいたす stacktype、呌び出しポむントで予想されるスタック タむプを説明したす。 元のりォレット契玄では、これらは単なるコメントでしたが、eDSL では実際にはコヌドの䞀郚であり、コンパむル時にチェックされたす。 これらは、コヌドが倉曎されスタック タむプが倉曎された堎合に、開発者が問題を発芋するのに圹立぀ドキュメントたたはステヌトメントずしお機胜したす。 もちろん、このようなアノテヌションは TVM コヌドが生成されないため、実行時のパフォヌマンスには圱響したせん。
  • これはただ XNUMX 週間で曞かれたプロトタむプなので、このプロゞェクトにはただやるべきこずがたくさんありたす。 たずえば、以䞋のコヌドにあるクラスのむンスタンスはすべお自動的に生成される必芁がありたす。

eDSL でのマルチシグ りォレットの実装は次のようになりたす。

main :: IO ()
main = putText $ pretty $ declProgram procedures methods
  where
    procedures =
      [ ("recv_external", decl recvExternal)
      , ("recv_internal", decl recvInternal)
      ]
    methods =
      [ ("seqno", declMethod getSeqno)
      ]

data Storage = Storage
  { sCnt :: Word32
  , sPubKey :: PublicKey
  }

instance DecodeSlice Storage where
  type DecodeSliceFields Storage = [PublicKey, Word32]
  decodeFromSliceImpl = do
    decodeFromSliceImpl @Word32
    decodeFromSliceImpl @PublicKey

instance EncodeBuilder Storage where
  encodeToBuilder = do
    encodeToBuilder @Word32
    encodeToBuilder @PublicKey

data WalletError
  = SeqNoMismatch
  | SignatureMismatch
  deriving (Eq, Ord, Show, Generic)

instance Exception WalletError

instance Enum WalletError where
  toEnum 33 = SeqNoMismatch
  toEnum 34 = SignatureMismatch
  toEnum _ = error "Uknown MultiSigError id"

  fromEnum SeqNoMismatch = 33
  fromEnum SignatureMismatch = 34

recvInternal :: '[Slice] :-> '[]
recvInternal = drop

recvExternal :: '[Slice] :-> '[]
recvExternal = do
  decodeFromSlice @Signature
  dup
  preloadFromSlice @Word32
  stacktype @[Word32, Slice, Signature]
  -- cnt cs sign

  pushRoot
  decodeFromCell @Storage
  stacktype @[PublicKey, Word32, Word32, Slice, Signature]
  -- pk cnt' cnt cs sign

  xcpu @1 @2
  stacktype @[Word32, Word32, PublicKey, Word32, Slice, Signature]
  -- cnt cnt' pk cnt cs sign

  equalInt >> throwIfNot SeqNoMismatch

  push @2
  sliceHash
  stacktype @[Hash Slice, PublicKey, Word32, Slice, Signature]
  -- hash pk cnt cs sign

  xc2pu @0 @4 @4
  stacktype @[PublicKey, Signature, Hash Slice, Word32, Slice, PublicKey]
  -- pubk sign hash cnt cs pubk

  chkSignU
  stacktype @[Bool, Word32, Slice, PublicKey]
  -- ? cnt cs pubk

  throwIfNot SignatureMismatch
  accept

  swap
  decodeFromSlice @Word32
  nip

  dup
  srefs @Word8

  pushInt 0
  if IsEq
  then ignore
  else do
    decodeFromSlice @Word8
    decodeFromSlice @(Cell MessageObject)
    stacktype @[Slice, Cell MessageObject, Word8, Word32, PublicKey]
    xchg @2
    sendRawMsg
    stacktype @[Slice, Word32, PublicKey]

  endS
  inc

  encodeToCell @Storage
  popRoot

getSeqno :: '[] :-> '[Word32]
getSeqno = do
  pushRoot
  cToS
  preloadFromSlice @Word32

eDSL およびマルチシグネチャ りォレット コントラクトの完党な゜ヌス コヌドは、次の堎所にありたす。 このリポゞトリ。 もっず 詳しく語られた 組み蟌み蚀語に぀いおは、同僚の Georgy Agapov が説明したす。

コンテストず TON に関する結論

私たちの䜜業には合蚈 380 時間かかりたした (ドキュメントの䜜成、䌚議、実際の開発を含む)。 コンテスト プロゞェクトには、CTO、チヌム リヌダヌ、ブロックチェヌン プラットフォヌムのスペシャリスト、Haskell ゜フトりェア開発者の XNUMX 人の開発者が参加したした。

ハッカ゜ンの粟神、緊密なチヌムワヌク、そしお新しいテクノロゞヌの偎面に玠早く没頭する必芁性は垞に刺激的なものであるため、私たちはコンテストに簡単に参加できるリ゜ヌスを芋぀けたした。 限られた資源の䞭で最倧限の成果を達成するために数晩眠れぬ倜を過ごしたずしおも、それは貎重な経隓ず玠晎らしい思い出によっお補われたす。 さらに、このようなタスクに取り組むこずは、垞に䌚瀟のプロセスを詊す良いテストずなりたす。瀟内の盞互䜜甚が適切に機胜しなければ、真に適切な結果を達成するこずは非垞に困難だからです。

歌詞はさおおき、私たちは TON チヌムが費やした劎力に感銘を受けたした。 圌らは、耇雑で矎しく、そしお最も重芁なこずに、機胜するシステムを構築するこずに成功したした。 TON は、倧きな可胜性を秘めたプラットフォヌムであるこずが蚌明されおいたす。 ただし、この゚コシステムが発展するには、ブロックチェヌン プロゞェクトでの䜿甚ず開発ツヌルの改善の䞡方の点で、さらに倚くのこずを行う必芁がありたす。 私たちは今、このプロセスに参加できるこずを誇りに思っおいたす。

この蚘事を読んでもただ質問がある堎合、たたは TON を䜿甚しお問題を解決する方法に぀いおアむデアがある堎合は、 私たちに曞いおください — 私たちの経隓を喜んで共有させおいただきたす。

出所 habr.com

コメントを远加したす