symbol-sdkソースコードのクラス構造概観

こんにちは。

この記事はnem Advent Calendar 2020の記事です。

今回はSymbol SDKのソースコードを読んでわかる範囲で、Symbol SDKで使う(主要な)クラスの構造を紹介します。

https://github.com/nemtech/symbol-sdk-typescript-javascript

書くテーマとしてSymbol用Cosmos Pegzoneの設計とか考え方でもよかったんですが、これはNem Group Limitedと連携して実際に進めていくので、もうちょい練ってからがいいかなと思いました。

クラス構造を見ることで、手っ取り早く論理的な構造やブロックチェーンの設計などがちょっとだけ見えてきたりするので、やっていきます。

account

アカウント関係のフォルダです。継承関係は以下です(箇条書きで右にインデントしてたらそれが最も直前のインデントしてない項目の子だということです)。

  • Account
  • AccountInfo
  • AccountLinkPublicKey(実質ただのstring)
  • AccountLinkVotingKey(実質ただのstring)
  • AccountNames
  • ActivityBucket
  • Address
  • MultisigAccountGraphInfo
  • PublicAccount
  • SupplementalPublicKeys

つまり、継承関係は一つもありません。

注目ポイントとして、NEM1用のSDK(nem-library)では、Account(秘密鍵がわかっているアカウント)はPublicAccount(秘密鍵がわからず公開鍵しかわかってないアカウント)の派生クラスでした。つまりPublicAccountが必要なところには、Accountをそのまま代入しても使えたのです。しかしSymbolではこれらが別々のクラスとなりました。Account内のメソッドで自身をPublicAccountに変換してやる必要があります。

Addressクラスは、PublicAccountクラスから作ることができます。秘密鍵→公開鍵→アドレスは一方通行に限り計算で求められるので当然ですね。

message

継承関係は以下です。

  • Message
    • EncryptedMessage
    • PersistemtHarvestingDelegationMessage
    • PlainMessage

目新しいのはPersistentHarvestingDelegationMessageですね。たぶん適当な解説してもろくなことにならないのでしゃしゃり出るのは控えておきますが、まあ名前からなにしてるかのイメージは湧くと思います。

PlainとEncryptedは相変わらず変わってません。暗号化は、ECDH(以下記事参照)をするつまり”Aさんの秘密鍵とBさんの公開鍵”から暗号化鍵を生成してそれで暗号化するわけですが、この鍵が”Bさんの秘密鍵とAさんの公開鍵”から生成しても同じものが作れるというカラクリです。

コードを読めばわかりますが、PersistentHarvestingDelegationMessageでは、その場で生成した即席インスタントのアカウントの秘密鍵とノードの公開鍵を使ってECDHで対称鍵を作り、それで署名用秘密鍵とVRF(Verifiable Random Function)秘密鍵の文字列を暗号化するという離れ業をやっているようです。

EncryptedMessageだけでなくPersistentHarvestingDelegationMessageにもMessageTypeという番号が割り当てられており、これは拡張(ほかの種類の付けたし)可能としたうえでメッセージの設計をプロトコル内に取り込んだという形のようですね。

いままでだと暗号化メッセージはプロトコル外(メッセージはバイナリデータが載せられるということだけがプロトコルで決まっており、ECDH使った暗号化自体はプロトコルで決まってるものではない)でしたが、そうではなくなったということのようです。

mosaic

継承関係は以下です。

  • Mosaic
    • NetworkCurrencyLocal
    • NetworkCurrencyPublic
    • NetworkHarvestLocal
  • MosaicFlags
  • MosaicId
  • MosaicInfo
  • MosaicNames
  • MosaicNonce
  • ResolvedMosaic
  • UnresolvedMosaicId

NetworkCurrencyLocalは、cat.currencyというネームスペースフルネームを持つ、ローカルテストネット用のモザイクです。無視で構いません。NetworkHarvestLocalも、cat.harvestというネームスペースフルネームをもつ、ローカルテストネットのハーベスティング用モザイクです。

NetworkCurrencyPublicにて、symbol.xymというネームスペースフルネームを持つモザイクが定義されています。

それ以外に継承関係はないですね。

注目点は、UnresolvedMosaicIdですね。これはMosaicId | NamespaceIdという交差型になっています。

ややこしいですが、

  • “実際のモザイクの識別子”はMosaicIdもしくはNamespaceIdのどちらかを用いて識別することができる
    • Namespaceはオプショナルである
  • Namespaceをモザイクの識別子として指定された場合、それを解決するまでMosaicIdを得ることができていないので、UnresolvedMosaicIdとして扱う
    • 解決(つまりNamespaceIdをもとにMosaicIdを取得)できたら、MosaicIdを”実際のモザイクの識別子”として利用できる

という使い方になります。MosaicIdは4バイトの乱数で表されます。

namespace

継承関係は以下です。

  • Alias
    • AddressAlias
    • EmptyAlias
    • MosaicAlias
  • NamespaceId
  • NamespaceInfo
  • NamespaceName

継承関係があるのは、Aliasですね。

基底クラスであるAliasクラスでは、AliasType を保持しています。0がNone,1がMosaic,2がAddressになっています。

NoneというのがEmptyAliasで、ようするにネームスペースを確保するけどなにもしません。

NEM1のようなアドレスに紐づける使い方は、AddressAliasになります。

NamespaceIdは、Namespaceフルネーム(symbol.xymなど)をハッシュ関数に入れて出た値をゴニョゴニョ変形させて作ります。

wallet

これはSymbol SDKにて秘密鍵をナマで保管せずいい感じに保管するためのクラスが入ったフォルダになります。

継承関係は以下です。

  • Wallet
    • SimpleWallet
  • Password(文字数制限のバリデーション機能を持っているだけでデータとしてはただのstring)

SimpleWalletですが、これはnem1と変わらず、パスワードで秘密鍵を暗号化して保管するシンプルなやり方です。BIP39などを利用したHierarchy DeterministicなWalletと対比する意味でSimpleと言ってるのだと思われます。

おわりに

これらの継承関係を見るだけで、素早くSymbolの構造が理解できたのではないでしょうか?(私はできましたキリッ)

理解のためのコスパがいいので継承関係あたりを重点的に読んで正解だったなと思っています。