---
title: "データ精度：Web3における知られざる構造的な問題"
description: "Web3インフラにおけるデータ精度の重要性、既存ソリューションの欠点、そしてAlchemyのアプローチについて解説します。"
---

# データ精度：Web3における知られざる構造的な問題

<ExternalVideo
  url="https://vimeo.com/728999085"
  provider="vimeo"
  providerUid="728999085"
/>

あるNFTチームは、ユーザーに矛盾する情報を表示していたために\(_上記の例_\)、数万人のユーザーを失いました。

最大級のDeFiプロトコルの一つは、フロントエンドでユーザーにエラーを発生させていたバグの調査に追われ、200万ドルを失いました。プロトコル側が根本原因を特定できなかったため、問題は数週間解決されませんでした。

#### この2つのシナリオは同じ問題に起因しています。それはノードインフラにおける正確性の欠如であり、Web3で頻発する問題です。

エンジニアであれ、創業者であれ、プロダクトマネージャーであれ、あるいはユーザーであれ、ノードの正確性をまだ意識していないなら、アプリを成功させるために最優先事項にする必要があります。

## 背景

ブロックチェーンは、ピアツーピアネットワークで通信するノードによって運営されています。この構造的な要素が[分散化](https://sgrlaw.com/blockchain-decentralized-ledgers/)を可能にしており、これはWeb3の基本的な価値提案の一つです。中央の仲介者に頼る代わりに、[数千のノード](https://etherscan.io/nodetracker)がトラストレスに情報を伝播し合うことでトランザクションを記録します。

ユーザーに正確で一貫性のある結果を提供するためには、ノードのネットワークがブロックチェーンの最新状態について合意する必要があります。これは[ピアツーピアネットワーク](https://medium.com/coinmonks/what-is-a-peer-to-peer-p2p-network-in-blockchain-3fe4417dfba2)では非常に難しいことです。なぜなら、情報が各ノードに同時に届くわけではないからです。

<ExternalVideo
  url="https://vimeo.com/729065053"
  provider="vimeo"
  providerUid="729065053"
/>

つまり、あるユーザーがノードに依存してデータを提供するdAppを操作するとき:

- ノードは常に同じデータを返すとは限りません ➡️
- ユーザーは常に期待通りの結果を得られるとは限りません

<ImageBlock
  src="https://media.alchemy.com/1704793834-nfts-not-showing-up-in-wallets-on-time.png"
  alt="NFTがウォレットに時間通り反映されない"
  width={1068}
  height={557}
  caption="NFTがウォレットに時間通り反映されない"
/>

ユーザーが予期しない、あるいは矛盾する情報を受け取り、それを理屈で説明できない場合、たいていは使用しているノード間の**正確性**の欠如が原因です。

Alchemyでは、**正確性**を**ユーザーがdAppを操作した際に、正しく一貫性のあるデータを受け取る状態**と定義しています。

この記事では、以下を説明します:

- なぜ正確性がWeb3特有の問題なのか
- 単一ノードで正確性を解決することの限界
- ロードバランサーで正確性を解決することの限界
- 正確性の欠如がアプリにどのような広範な問題を引き起こすか
- AlchemyのCortexエンジンがどのようにブロックチェーンの正確性を解決するか

## 正確性とは、ユーザーがdAppを操作した際に正しく一貫性のあるデータを受け取る状態のこと

Web2では、[中央集権的なシステムが正確性を解決しやすい問題にしています](https://ethereum.org/en/developers/docs/web2-vs-web3/#:~:text=Web2 refers to the version,that run on the blockchain)。情報が単一のソースからしか来ないからです。しかしWeb3の分散システムは、以下のような多数の新しく複雑な問題を引き起こします:

- 実装の複雑さ
- ネットワーク内の最も遠い参加者に情報を届ける難しさ\(ネットワーク内の遠いノードに情報が到達するには文字通り時間がかかります\)
- ネットワーク調整の難しさ

#### これらの課題は、ノード群の間で正確性の欠如を引き起こし、以下につながる可能性があります:

- 壊れた体験の提供
- 顧客の喪失
- 問題を事後的に修正するための時間とコストの損失

<CalloutBlock>

"[CryptoKittiesで考えられるあらゆる状態の問題が起きました](https://www.alchemy.com/case-studies/dapper)。ノードが同期していないと、ユーザー体験もdApp全体もめちゃくちゃになりました。" - Eric Lin, Dapper

</CalloutBlock>

## 単一ノードを運用することの問題

複数ノード間の通信からデータの正確性の問題が生じているのであれば、自分で単一ノードを運用しようと考えるかもしれません。

自分で単一ノードを運用していて、ユーザーがdAppにコールを送ったとします:

- ユーザーは最新のブロックを尋ねます
- あなたのノードは最新のブロックが4であると答えます

<ImageBlock
  src="https://media.alchemy.com/1704794016-the-problems-with-running-a-single-node.png"
  alt="シングルノードで動作するdAppに対してユーザーが最新ブロックをリクエストする様子の図"
  width={1920}
  height={1081}
  caption="ユーザーがdAppを呼び出す"
/>

✅ うまくいきます!

しかし、落とし穴があります。

これはある程度までしかうまくいきません。

#### 単一ノードにトラフィックを流すと、アプリのスケーラビリティと信頼性が大幅に損なわれます:

- **スケーラビリティ**: あなたのdAppが今日100人のユーザーを抱えていて、利用が10倍に拡大した場合、単一ノードでは増加したリクエスト負荷を効果的に処理できません
- **信頼性**: 単一ノードに依存していると、そのノードがダウンした際にdAppもダウンします。そしてノードは概して信頼性が低いため、単一ノードだけではあなたが必要とするアップタイムより大幅に低くなります\(実際に計測すると単一ノードのアップタイムが72%程度まで低下するケースもあります\)。

ノードの保守に費やす時間は、[顧客のためのプロダクトや体験の構築に使えたはずの時間](https://www.alchemy.com/case-study/zerion)です。

<CalloutBlock>

"Alchemyと協働することで、フルタイムエンジニア3人分に相当する工数を節約できました。彼らは本来、常時インフラ保守に張り付かなければならなかったはずです。" - **Evgeny Yurtaev, CEO & Co-Founder at [Zerion](https://www.alchemy.com/dapps/zerion)**

</CalloutBlock>

さて、単一ノードでは事業ニーズを満たせないと気づいたとします。次に何をすればいいでしょうか?

## ロードバランサーを使うことの問題

複数ノードにまたがるトラフィックをロードバランサーで管理するインフラプロバイダーを試したくなるかもしれません。これはWeb3において非常に一般的なインフラ設計です。

ロードバランシングは、もともとWeb2で使われていた水平スケーリングの仕組みです。ロードバランシングは、スーパーで一番短い列を選ぶようなものだと考えてください。ロードバランサーは、あなたのdAppのトラフィックを一番短い列のノードに振り分けます。

<ExternalVideo
  url="https://vimeo.com/729065065"
  provider="vimeo"
  providerUid="729065065"
/>

以下の例を見てみましょう。

ユーザーがあなたのdAppにコールを送ります:

- ユーザーは最新のブロックを尋ねます
- Node A経由で、あなたのdAppは最新のブロックが2であると答えます

次に、ユーザーは確認のためもう一度尋ねます:

- 今度はクエリがNode Bに振り分けられます
- Node B経由で、あなたのdAppは最新のブロックが4であると答えます

<ExternalVideo
  url="https://vimeo.com/729065080"
  provider="vimeo"
  providerUid="729065080"
/>

システムが依然として各リクエストを単一のノードに通しており、どの時点でもそのノードが最新情報を持っているとは限らないため、結果はしばしば不正確になります。ユーザーには理屈で説明できない矛盾した結果が返されることになります。

**ロードバランシングは単一ノードよりも高いスケールと信頼性を生み出しますが、それは正確性を犠牲にして得られるものです。**

#### トラフィックがロードバランサーを通じて振り分けられると、ノードは以下の情報について異なる見解を持つ可能性があります:

- 最新ブロック
- 直近のトランザクション
- 保留中のトランザクション

これらの問題は、以下を引き起こす可能性があります:

- [開発者が一貫性のないデータを経験する](https://community.infura.io/t/how-does-the-load-balancer-work/1090)
- リクエストの失敗
- アプリケーションの障害
- 一人のユーザーが同じリクエストに対して矛盾した結果を見る
- 複数のユーザーが同じリクエストに対して矛盾した結果を見る

まだこれが大きな問題だと納得できませんか? 続けて読んでください。

## 正確性の欠如が重大な問題を引き起こす仕組み

Isaacが DAOに参加したいとして、そのためにはトークンを購入する必要があるとします。

- Isaacは分散型取引所\(DEX\)に尋ねます: "DAOトークンはまだ買えますか?"
- DEXのインフラプロバイダーは、Isaacのリクエストを Node Aに振り分けます。
- Node A経由で、DEXは答えます: "はい、トークンはまだ買えます。"

✅ トークンは買えます!

Isaacは購入しようとします。

- DEXのインフラプロバイダーは、Isaacのリクエストを Node Bに振り分けます。
- Node B経由で、DEXは答えます: "トランザクション失敗。"

<ExternalVideo
  url="https://vimeo.com/729065088"
  provider="vimeo"
  providerUid="729065088"
/>

DEXはノード間でロードバランシングを行うインフラプロバイダー\(Web3で非常によくあるスケーリングの仕組み\)を使っていたため、Isaacがトークンがまだ買えることを確認した際、Node Aはこのケースでは古くなった情報を返していました。

**ここで重要な点が浮き彫りになります。ある瞬間において、個々のノードから得られる情報、つまりロードバランサーを使用している際に常に情報の出所となるものは、頼りにできないということです。**

そのノードが最新の情報を持っていない可能性があり、そのノードがあなたのリクエストに応答した場合、あなたのトランザクション\(あるいはその後のトランザクション\)は失敗するか、予期しない挙動を示す可能性が高くなります。

<EbookCta
  eyebrow="無料ガイド"
  title="Build vs. Buy: Blockchain Infrastructure"
  description="インフラチームを雇う前に読んでください：自社構築は、本番リクエストを1件も処理しないうちから年間$850K〜$1M以上かかることがあります。コストの内訳全体はこちら。"
  ctaLabel="ガイドをダウンロード"
  ctaHref="/resources/build-vs-buy"
  coverImage={{
    src: "https://media.alchemy.com/ebooks/cta-build-vs-buy.png",
    alt: "Build vs. Buy ebook cover",
  }}
/>

#### 正確性の問題は幅広い種類の障害を引き起こす

Isaacに起きたことは、ノードの正確性の欠如によって引き起こされる多くの障害状態の一つに過ぎません。

もう少し他の潜在的な障害状態を見てみましょう:

1. 本来一つしかないはずの事実に対して、スマートコントラクトの実行が矛盾する答えを返す。例えば:

- ユーザーが尋ねます: "このBored Apeの所有者は誰ですか?"
- APIが返します: "Alice"
- 次にユーザーが尋ねます: "Aliceが所有しているApeを教えてください?"
- APIが返します: "Aliceは何も所有していません"

<ExternalVideo
  url="https://vimeo.com/728999085"
  provider="vimeo"
  providerUid="728999085"
/>

なぜこれが起きるのでしょうか? ロードバランサーに依存したインフラでは、スマートコントラクトの状態、つまり実行した際に何が起きるかは、どのノードがリクエストを処理するかによって異なります。

2. dAppが不正確な"ノンス"\(任意のウォレットアドレスからの各トランザクションに対する一意の識別子\)を返し、その後のトランザクションが失敗する。例えば:

- ユーザーがノンス100でトランザクション1を完了する
- ユーザーが尋ねます: "次のノンスは何ですか?"
- APIが返します: 100\(ノンス100は既に完了しているため、これはあり得ないはずです\)
- 次のトランザクションは自動的に拒否されます

<ExternalVideo
  url="https://vimeo.com/729065102"
  provider="vimeo"
  providerUid="729065102"
/>

_なぜこれが起きるのでしょうか? 不正確なインフラでは、_[_保留中のトランザクション_](https://www.alchemy.com/docs/ethereum-transactions-pending-mined-dropped-replaced)_が単一のノードにしか存在しない状態になることがあります。そのため、それらの保留中トランザクションの状態や、それを送信したウォレットについての情報を取得しようとすると、データが欠落していたり誤解を招くものだったりすることがよくあります。_

#### これらの障害状態は目に見えにくく、理屈で説明できず、アプリの健全性に深刻な影響を与えます

起こりうることの例:

不正確なデータを取り込んでしまい、データベースが破損し、修正に時間と労力がかかることがあります。

例えば[Origin](https://www.alchemy.com/case-study/origin)を見てみましょう。彼らの最初のインフラプロバイダーではRPCエラーやデバッグが頻発しており、[エラーのデバッグ](https://www.alchemy.com/build)にはチームの多くの時間が費やされていました。

彼らは、あるノードがリクエストに応答したものの、そのノードが別のリクエストに応答した別のノードより数ブロック遅れていたことを発見しました。

これらの状態の問題はトラブルシューティングがほぼ不可能で、根本原因を特定できたときには、システム全体が既に破損していたことが判明しました。

**これらの問題にはエラーメッセージや失敗コードが存在しないため、a\) コード内で事前に対処することができず、b\) 発生した際にはデバッグに何時間も費やすことになります。ユーザーにとっても同様に混乱するものです:**

<ImageBlock
  src="https://media.alchemy.com/1704794424-error-transferring-nft-from-bakery-swap.png"
  alt="Bakery swapからのNFT転送エラー"
  width={942}
  height={359}
  caption="Bakery swapからのNFT転送エラー"
/>

**ユーザーは古いデータに基づいてトランザクションを送信し、それが必ず失敗する結果、ガス代を失うことがあります:**

<ImageBlock
  src="https://media.alchemy.com/1704794521-nfts-missing-in-user-wallet.png"
  alt="ウォレットからNFTが消えたというユーザー報告"
  width={1227}
  height={336}
  caption="NFTの消失"
/>

**ユーザーはあなたのdApp上で矛盾するUIを見ることがあり、トランザクションが成功したか失敗したかを交互に示す結果になります。ユーザーはこの失敗を、あなたのdAppのフロントエンド体験のせいだと考えるでしょう:**

<ImageBlock
  src="https://media.alchemy.com/1704794600-nfts-not-showing-up-in-user-wallet.png"
  alt="4時間経ってもユーザーのウォレットにNFTが反映されない"
  width={1224}
  height={552}
  caption="4時間経ってもユーザーのウォレットにNFTが反映されない"
/>

**ユーザーは、トランザクションが正常にマイニングされなかったと思い込んで再度実行することで、過払いをすることがあります。実際には元のトランザクションは成功していたものの、正しく反映されていなかったのです:**

<ImageBlock
  src="https://media.alchemy.com/1704794692-user-paying-gas-fees-twice-for-failed-transaction.png"
  alt="失敗したトランザクションに対してガス代を二重に支払うユーザー"
  width={1232}
  height={399}
  caption="失敗したトランザクションに対してガス代を二重に支払うユーザー"
/>

## 正確性を前提に構築する

ここまでで明らかなように、ブロックチェーンネットワークにおいて、不正確なインフラはあなたのdAppとユーザーに非常に悪い影響を及ぼす可能性があります。

以下を満たすインフラプロバイダーと組む必要があります:

- 正確性を基本的な最優先事項として考えていること
- あなたのdAppがスケールする際に正確性を維持するための、ロードバランシングとは異なる持続可能な仕組みを持っていること

#### ノードプロバイダーのベンチマーク

いくつかの著名なノードインフラプロバイダー間で正確性をベンチマークしたデータをまとめました。このツールを使って、自分たちでも正確性をベンチマークしてみてください。

正確性はプロバイダー評価の一部にすぎません。Alchemyの[RPCプロバイダーベンチマーク](https://www.alchemy.com/benchmarks)では、現在のレイテンシ、成功率、失敗リクエストも比較できます。

私たちのテストでは、24時間の期間中に、各プロバイダーから最新のブロック番号を取得するために、JSON RPCメソッドの_eth_blockNumber_を合計約1,072,000回クエリしました。これはデータの不正確性をベンチマークするための単純な一例です。他にも、より頻繁に、より深刻に失敗する例が数多くあります。

<ImageBlock
  src="https://media.alchemy.com/1704794854-benchmarking-node-providers.png"
  alt="24時間にわたるノードインフラプロバイダー間のデータ精度を比較したチャート"
  width={3250}
  height={1500}
  caption="ノードプロバイダーのベンチマーク"
/>

_注記_ このサンプルダッシュボードのスナップショットでは、1日分のデータ正確性をクエリし記録しています。

\*完全な手法の詳細は付録に記載しています。

#### 正確なデータを得るために何ができるか?

<CalloutBlock>

"Alchemyは、これまで顕在化していた一貫性の問題を解決し、ユーザーからの苦情の98%を取り除き、[Augur](https://www.alchemy.com/dapps/augur)のユーザー体験と採用率を大幅に改善しました。" - [**Augur CTO Alex Chapman**](https://www.alchemy.com/case-studies/augur)

</CalloutBlock>

あなたの[アプリ](https://www.alchemy.com/dapps/top/defi-dapps)とその顧客のために正確性を確保することを目指し、Alchemyは数十万時間のエンジニアリング時間を投じ、数百に及ぶ独自のイノベーションを開発して[AlchemyのCortexエンジン](https://www.alchemy.com/rpc-api)を構築しました。これは、マルチノードインフラのスケーラビリティと信頼性の利点を持ちながら、一貫したデータの正確性を確保するものです。

Cortexは、単につながったノードの集まりではありません。実際、それはCortexエンジンのほんの一部に過ぎません。Cortexは、カスタムでスケーラブルかつ分散されたシステムを組み合わせることで、私たちのAPIが実質的に単一ノードのように振る舞うことを可能にし、これまで議論してきた正確性の問題をすべて解決します。

これはどのように機能するのでしょうか?

<ExternalVideo
  url="https://vimeo.com/730080026"
  provider="vimeo"
  providerUid="730080026"
/>

私たちはVox Nodi\(ノードの声\)と呼ばれる明示的な一貫性レイヤーを構築しました。Vox Nodiの役割は、私たちが処理を助けているブロックチェーンリクエストが、一貫した結果を返すことを保証することです。

これは、実質的に私たちのインフラ全体にわたってコンセンサスアルゴリズムを実行することで機能します。インフラの各部分が、ブロックチェーンの正しい状態について投票できる仕組みです。

クエリを正しくルーティングし調整することで、このシステムは、様々なノードがある瞬間にトランザクションデータについて異なる見方をしていても、結果が常に一貫して正確であることを保証します。

簡単に言えば、Vox Nodiは、私たちのAPIへのあらゆるリクエストが、迅速かつ確実に、そして100%正確なデータとともに返されることを保証します。

そしてCortexは、開発者が無制限かつ確実にスケールできることも引き続き可能にします。なぜなら、個々のノードではなく、完全な分散型ブロックチェーンエンジンが各リクエストに応答しているからです。

要するに、これは**ロードバランサーではありません。**

## 影響と結論

dAppをブロックチェーンに接続する方法を選ぶ際に考慮すべき側面は数多くありますが、**プロバイダーが正確性を提供できるかどうかは、リストの最優先事項であるべきです。**

Alchemyでは、正確性を最優先事項としています。今すぐAlchemyで無料で[始めましょう](https://dashboard.alchemy.com/signup/?a=f79ab530c4)。

-

## 付録

#### ベンチマーク手法

正確性のベンチマークを検討するにあたり、私たちは第一原理から出発し、最も基本的なEthereum RPCコールの一つであるeth_blockNumberをテストしました。

おさらいすると、eth_blockNumberはEthereumネットワークの最新のブロック番号を返します。分散型ネットワークでは、最も高いブロック番号を持つブロックが最も新しいトランザクションを含んでいます。

このため、通常のネットワーク状態下では、単一のノード/ノードプロバイダーからeth_blockNumberを呼び出すと、値が**昇順**になるブロック番号のみが返されるはずです。昇順データにおける不整合は、リバージョンの兆候であるか、それがより頻繁に起きる場合は、プロバイダーのデータ不正確性の可能性を示しています。

これは、既存のノード上のデータを新しい最長チェーンが置き換える'[リオーグ](https://www.alchemy.com/overviews/what-is-a-reorg)'とは異なることに注意してください。'リオーグ'は常に、ノード上に既にあるデータと_少なくとも同数の_ブロックを持ちます。そのため、ブロック番号が後退するのを見た場合、それは壊れたインフラが原因であると断定できます。

不整合なデータをどのように測定するかを理解するために、eth_blockNumberを合計13回ポーリングした後、以下のようなブロック番号のシーケンスがあったと仮定しましょう。

<EmbeddedTable
  table={{
    columns: [
      { key: "1", width: 200, title: "eth_blockNumber", dataType: "object" },
      { key: "2", width: 200, title: "Response Sequence", dataType: "object" },
    ],
    data: [
      {
        "1": { title: "<p>1</p>", tooltip: "", icon: "" },
        "2": { title: true, tooltip: "", icon: "" },
        id: 0,
      },
      {
        "1": { title: "<p>1</p>", tooltip: "", icon: "" },
        "2": { title: true, tooltip: "", icon: "" },
        id: 1,
      },
      {
        "1": { title: "<p>1</p>", tooltip: "", icon: "" },
        "2": { title: true, tooltip: "", icon: "" },
        id: 2,
      },
      {
        "1": { title: "<p>1</p>", tooltip: "", icon: "" },
        "2": { title: true, tooltip: "", icon: "" },
        id: 3,
      },
      {
        "1": { title: "<p>1</p>", tooltip: "", icon: "" },
        "2": { title: true, tooltip: "", icon: "" },
        id: 4,
      },
      {
        "1": { title: "<p>2</p>", tooltip: "", icon: "" },
        "2": { title: true, tooltip: "", icon: "" },
        id: 5,
      },
      {
        "1": { title: "<p>1</p>", tooltip: "", icon: "" },
        "2": { title: false, tooltip: "", icon: "" },
        id: 6,
      },
      {
        "1": { title: "<p>1</p>", tooltip: "", icon: "" },
        "2": { title: false, tooltip: "", icon: "" },
        id: 7,
      },
      {
        "1": { title: "<p>3</p>", tooltip: "", icon: "" },
        "2": { title: true, tooltip: "", icon: "" },
        id: 8,
      },
      {
        "1": { title: "<p>2</p>", tooltip: "", icon: "" },
        "2": { title: false, tooltip: "", icon: "" },
        id: 9,
      },
      {
        "1": { title: "<p>1</p>", tooltip: "", icon: "" },
        "2": { title: false, tooltip: "", icon: "" },
        id: 10,
      },
      {
        "1": { title: "<p>1</p>", tooltip: "", icon: "" },
        "2": { title: false, tooltip: "", icon: "" },
        id: 11,
      },
      {
        "1": { title: "<p>3</p>", tooltip: "", icon: "" },
        "2": { title: true, tooltip: "", icon: "" },
        id: 12,
      },
    ],
  }}
/>

このシーケンスから、ブロックインデックス6, 7, 9, 10, 11の合計5件のエラーが見つかります。これは、ブロック6, 7, 9, 10, 11において、返されたブロックが期待される昇順のシーケンスに従っておらず、何らかの根本的なデータ正確性の問題を示していることを意味します。

注: ノードプロバイダーがブロック番号2にいると記録した時点で、それ以降に返される1という値はすべて不正確であり、プロバイダーが時間を遡って1ブロック分古い_現在の_ブロック値を提供していることに相当すると見なします。同様に、ノードプロバイダーがブロック番号3にいると記録した時点で、その後にブロック1または2が現れる場合はすべて_不整合_となります。このベンチマークテストを単純化するため、私たちは個々のプロバイダー自身からのデータ正確性を明示的に測定しており、異なるプロバイダー間でブロックデータを相互比較することはしていません。
