TIPS & TRICKS

SPLでjoinを使わない方法

このブログは、イーサリアムエコシステムが仮想空間の土地をこれからオークションに出そうとしている絶好のタイミングで書いています。私の知るところによると、一部の土地には特別なキャラクターがいて、土地の価格が上がっているようです。とても魅力的な話です

Splunkとしては捨て置くわけにいきません。というわけでさっそく手を打ち、Splunkはイーサリアムに対応しました。これで、ブロックやトランザクションに関する情報を取り込み、ブロックチェーン上のイベントをデコードできます

しかしそれに飽き足らず、より多くの知識を求めて、ブロックチェーン上の要素の資産価値を示すマップを作成しました。Chainlink社のオラクルで提供されるデータを活用して、各ブロックのイーサリアムの価値を特定します。

さらにNFTの売上に注目して、資産のIDを販売価格にマッピングしようとしたときに、問題に直面しました。NFTの売上は、同じトランザクションの2つの異なるイベントの一部としてトリガーされるためです。1つは販売を確定して貨幣額を示すOrderMatchedイベント、もう1つは電子トークンをアカウント間で転送するTransferイベントです。  この2つは、交換が初めて行われる際に発生する別々のイベントです。リエントランシー攻撃を防ぐため、まず注文を作成して照合してから、転送が行われるのです。

両方のイベントをマッチングするために、まずは2つのサーチ(各イベントに1つずつ)を作成し、トランザクションのハッシュを基準にjoinしてみました。

index=mainnet address=0x960b7a6bcd451c9968473f7bbfd9be826efd549a sourcetype="ethereum:transaction:event"  "event.name"=Transfer
|  join left=e1 right=e2 where e1.transactionHash=e2.transactionHash
 [search index="mainnet" sourcetype="ethereum:transaction:event" "event.name"=OrdersMatched]

実行した結果、このサーチは非常にコストが高いことがわかりました。トークンのコントラクトアドレス(0x960b7a6bcd451c9968473f7bbfd9be826efd549a)を1つ目のサーチでのみ使用し、2つ目のサーチでは使用していない点に注意してください。これは、OrdersMatchedでは交換するアイテムのコントラクトアドレスが使用されないためです。

私たちのチームにはベテランのメンバーがいます。彼は「決してjoinを使わないように。もし使ったらSantana Rowオフィスの4階にいるトラに食べられるかもしれない」と私たちを日頃から脅しています。ですから、このサーチは絶対に使えません。

4階に居座るトラ

そこでまず、2つのサーチを1つにまとめ、両方のクエリーに一致するすべてのエントリーを返すサーチを作成しました。

index=mainnet sourcetype="ethereum:transaction:event" ((address=0x960b7a6bcd451c9968473f7bbfd9be826efd549a Transfer) OR OrdersMatched)

次に、各レコードをもう少し掘り下げます。返されるイベントにはTransferとOrdersMatchedがあります。そこからサーチしたいデータを取り出して、新しいフィールドを作成します。最初に、イベントの内容を解析します。

index=mainnet sourcetype="ethereum:transaction:event" ((address=0x960b7a6bcd451c9968473f7bbfd9be826efd549a Transfer) OR OrdersMatched)
| spath "event.params{}.value"
| spath "event.name"
| rename "event.name" AS eventName
| rename "event.params{}.value" AS eventValues

次に、Transferの値を取り出して新しいフィールドに代入します。

| eval transfer_from=if(eventName="Transfer",mvindex(eventValues,0),null())
| eval transfer_to=if(eventName="Transfer",mvindex(eventValues,1),null())
| eval transfer_index=if(eventName="Transfer",mvindex(eventValues,2),null())

さらに、OrdersMatchedの値を取り出して新しいフィールドに代入します。

| eval ordersMatched_buyhash=if(eventName="OrdersMatched",mvindex(eventValues,0),null())
| eval ordersMatched_sellHash=if(eventName="OrdersMatched",mvindex(eventValues,1),null())
| eval ordersMatched_maker=if(eventName="OrdersMatched",mvindex(eventValues,2),null())
| eval ordersMatched_taker=if(eventName="OrdersMatched",mvindex(eventValues,3),null())
| eval ordersMatched_price=if(eventName="OrdersMatched",mvindex(eventValues,4),null())
| eval ordersMatched_metadata=if(eventName="OrdersMatched",mvindex(eventValues,5),null())

これで、joinを使わず、statsコマンドを使って、新しく作成したすべてのフィールドの最初の値を一意のトランザクションハッシュごとに取得できます。

| stats first(ordersMatched*) first(transfer_*) by transactionHash

この変更のおかげで、結果を瞬時に生成できるようになりました。

サーチ結果

このサーチには重要な前提があります。それは、各トランザクションにTransferイベントとOrdersMatchedイベントが1つずつしかないということです。これらのイベントを生成する既存のコードをチェックしたところ、この前提は今のところ有効です。

お読みいただき、ありがとうございました。joinを避けたことで、どうやらトラ(のぬいぐるみ)に食べられずに済みそうです。こちらのプレゼンテーションではさらに多くのヒントを紹介しています。ぜひお読みください。ブロックチェーンデータの扱いでお困りのことがありましたら、ブロックチェーンチーム(blockchain@splunk.com)までお問い合わせください。

このブログはこちらの英語ブログの翻訳、山村 悟史によるレビューです。

Antoine Toulme
Posted by

Antoine Toulme

Antoine Toulme is the engineering manager of the blockchain and DLT team at Splunk. He is also a member of the Apache Software Foundation and a committer for Hyperledger Besu.