Skip to content

Moonbeam共识和确定性

概览

虽然Moonbeam致力于兼容以太坊Web3 API和EVM,但开发者仍需了解Moonbeam在共识和确定性方面与以太坊之间的主要差异。

简而言之,共识是不同主体就共享状态达成一致的一种方式。当创建区块时,网络的节点需要决定哪个区块将会代表下一个有效状态。而确定性则负责定义该有效状态何时无法被替代或是逆转。

以太坊最开始是使用基于工作量证明((PoW)的共识协议提供概率确定性。然而,从2022开始,以太坊不再使用PoW,而是改用权益证明(PoS)提供最终确定性。与其相反的是,Moonbeam使用基于委托权益证明(DPoS)的混合共识协议提供最终确定性。DPoS是Polkadot的提名权益证明 (NPoS)概念的演变,它允许委托人选择他们想要支持的候选收集人以及支持的比例,从而将更多权力交给Token持有者。

本教程将概述关于共识和确定性的一些主要差异,以及首次使用Moonbeam时需要了解的事项。

以太坊共识和确定性

如上所述,以太坊目前使用PoS共识协议,其中验证人将ETH质押在网络中并负责生产区块和检查新区块的有效性。区块生产的时间是固定的,分为12秒slots和32 slot epochs。每个slot随机选择一个验证人生产区块并传播至网络。每个slot都有一个随机选择的验证人委员会,负责确定区块的有效性。网络中的权益越大,验证人被选择生成或验证区块的机会就越大。

在以太坊的PoS共识协议中,最终确定性是通过“checkpoint”区块来实现的。验证者人就特定checkpoint区块的区块状态达成一致,这些checkpoint区块始终是一个epoch中的第一个区块,如果三分之二的验证人同意,则该区块被最终确定。区块确定性可以恢复,但是由于有强大的经济激励措施,因此验证人不会试图串通恢复区块。您可以在Vitalik的On Settlement Finality博客文章的Casper部分获取关于确定性的更多信息 。

Moonbeam共识和确定性

在Polkadot运行的机制中具有收集人和验证人,收集人负责通过收集用户端的交易记录并为中继链验证人生产状态交易证明来维持平行链(本示例中为Moonbeam)的运作。而收集人集(产生区块的节点)是根据其网络上获得的质押量来选择的。

在确定性方面,Polkadot和Kusama依赖GRANDPA运作。GRANDPA为任何指定交易(区块)提供确定性的功能。换句话说,当区块/交易被标志为结束后,除非通过链上治理和分叉,将无法被恢复。Moonbeam遵循这样的最终确定性。

PoS和DPoS的主要差异

在共识方面,Moonbeam主要基于委托权益证明(DPoS)模式,而以太坊遵循权益证明(PoS)模式,两者略有不同。尽管这两种机制都依赖于使用权益来验证和创建新区块,但仍存在一些关键差异。

通过以太坊上的PoS,验证人被选择根据自己在网络中的权益来生成和验证区块。只要验证人存入了验证人保证金,就可以选择他们来生成和验证区块。 然而,如前所述,网络中的质押量越大,选择验证者来生成和验证区块的机会就越大。

另一方面,通过Moonbeam上的DPoS,收集人有资格根据自身质押量加上网络中的委托人的质押量来生成区块。任何Token持有者都可以选择将其Token委托给候选收集人。质押量排名靠前的候选收集人(包括社区代表)将加入活跃收集人集。活跃收集人集中的候选收集人数量受治理的约束。一旦进入活跃收集人集,收集人将被随机选择以使用Nimbus共识框架生成区块。请注意,一旦收集人进入活跃收集人集,其总质押量不会影响他们被选择生产区块的机会。

就确定性而言,由于其使用的checkpoint确定性系统,以太坊上的区块可能比Moonbeam上的区块需要更长的时间才能完成。在以太坊中,验证人确定checkpoint区块的最终性,checkpoint区块始终是一个epoch中的第一个区块。由于一个epoch有32个slot,每个slot为12秒,因此一个区块的最终确定至少需要384秒,即6.4分钟。

Moonbeam不使用checkpoint区块,而是依赖Polkadot的GRANDPA确定性小工具,其中确定性过程与区块生产并行完成。此外,最终确定过程结合了区块链的结构,允许中继链验证人对他们认为有效的最高区块进行投票。在这种情况下,投票将适用于最终确定的所有区块,从而加快最终确定过程。当一个区块被包含在中继链后,一个区块就可以在Moonbeam上的一个区块内最终确定。

通过Ethereum RPC检查交易确定性

尽管确定性的小工具有所不同,但您可以使用相同的、相对简单的策略来检查以太坊和Moonbeam上的交易确定性:

  1. 您可以询问网络获取最新最终确定区块的哈希
  2. 使用哈希检索区块号
  3. 将其与交易的区块号进行对比。如果交易被包含在之前的区块中,则该交易已完成
  4. 为保证检查的安全性,按编号检索区块并验证给定的交易哈希是否在区块中

下方代码片段遵循这一策略来检查交易确定性。其使用默认块参数finalized选项来获取最新最终确定的区块。

要在Moonbeam或Moonriver网络上测试本指南中的示例,您可以从受支持的网络端点提供商之一获取您自己的端点和API密钥。

注意事项

下方教程中提供的代码片段不适用于生产环境。请确保针对每个用例进行调整。

import { ethers } from 'ethers';

// Define the transaction hash to check finality
const txHash = 'INSERT_TX_HASH';

// Define the RPC of the provider for Moonbeam
// This can be adapted for Moonriver or Moonbase Alpha
const providerRPC = {
  moonbeam: {
    name: 'moonbeam',
    rpc: 'INSERT_RPC_API_ENDPOINT',
    chainId: 1284,
  }
};

// Define the Web3 provider
const web3Provider = new ethers.JsonRpcProvider(providerRPC.moonbeam.rpc, {
  chainId: providerRPC.moonbeam.chainId,
  name: providerRPC.moonbeam.name,
});

const main = async () => {
  // Get the last finalized block
  const finalizedBlockHeader = await web3Provider.getBlock('finalized');
  const finalizedBlockNumber = finalizedBlockHeader.number;

  // Get the transaction receipt of the given transaction hash
  const txReceipt = await web3Provider.getTransactionReceipt(txHash);

  // If block number of receipt is not null, compare it against finalized head
  if (txReceipt) {
    const txBlockNumber = txReceipt.blockNumber;

    // As a safety check, get given block to check if transaction is included
    const txBlock = await web3Provider.getBlock(txBlockNumber);

    console.log(`Current finalized block number is ${finalizedBlockNumber}`);
    console.log(
      `Your transaction in block ${txBlockNumber} is finalized? ${
        finalizedBlockNumber >= txBlockNumber
      }`
    );
    console.log(
      `Your transaction is indeed in block ${txBlockNumber}? ${txBlock.transactions.includes(
        txHash
      )}`
    );
  } else {
    console.log(
      'Your transaction has not been included in the canonical chain'
    );
  }
};

main();
import { Web3 } from 'web3';

// Define the transaction hash to check finality
const txHash = 'INSERT_TX_HASH';

// Define the Web3 provider for Moonbeam
// This can be adapted for Moonriver or Moonbase Alpha
const web3Provider = new Web3('INSERT_RPC_API_ENDPOINT');

const main = async () => {
  // Get the last finalized block
  const finalizedBlockHeader = await web3Provider.eth.getBlock('finalized');
  const finalizedBlockNumber = finalizedBlockHeader.number;

  // Get the transaction receipt of the given transaction hash
  const txReceipt = await web3Provider.eth.getTransactionReceipt(txHash);

  // If block number of receipt is not null, compare it against finalized head
  if (txReceipt) {
    const txBlockNumber = txReceipt.blockNumber;

    // As a safety check, get given block to check if transaction is included
    const txBlock = await web3Provider.eth.getBlock(txBlockNumber);

    console.log(`Current finalized block number is ${finalizedBlockNumber}`);
    console.log(
      `Your transaction in block ${txBlockNumber} is finalized? ${
        finalizedBlockNumber >= txBlockNumber
      }`
    );
    console.log(
      `Your transaction is indeed in block ${txBlockNumber}? ${txBlock.transactions.includes(
        txHash
      )}`
    );
  } else {
    console.log(
      'Your transaction has not been included in the canonical chain'
    );
  }
};

main();
from web3 import Web3

# Define the transaction hash to check finality
tx_hash = "INSERT_TX_HASH"

# Define the Web3 provider for Moonbeam
# This can be adapted for Moonriver or Moonbase Alpha
web3_provider = Web3(Web3.HTTPProvider("INSERT_RPC_API_ENDPOINT"))

if __name__ == "__main__":
    # Get the latest finalized block
    finalized_block_header = web3_provider.eth.get_block("finalized")
    finalized_block_number = finalized_block_header.number

    # Get the transaction receipt of the given transaction hash
    tx_receipt = web3_provider.eth.get_transaction_receipt(tx_hash)

    # If block number of receipt is not null, compare it against finalized head
    if tx_receipt is not None:
        tx_block_number = tx_receipt.blockNumber

        # As a safety check, get given block to check if transaction is included
        tx_block = web3_provider.eth.get_block(tx_block_number)
        is_in_block = False
        for tx in tx_block.transactions:
            if tx_hash == web3_provider.to_hex(tx):
                is_in_block = True

        print(f"Current finalized block number is { str(finalized_block_number) }")
        print(
            f"Your transaction in block { str(tx_block_number) } is finalized? { str(finalized_block_number >= tx_block_number) }"
        )
        print(
            f"Your transaction is indeed in block { str(tx_block_number) }? { is_in_block }"
        )
    else:
        print("Your transaction has not been included in the canonical chain")

使用Moonbeam RPC端点检查交易确定性

Moonbeam添加了对两个自定义RPC端点moon_isBlockFinalizedmoon_isTxFinalized的支持,可用于检查链上事件是否已完成。这两个方法都十分直接,您不需要对比区块数字来确保一个交易的完成性。

您可以前往Moonbeam自定义API页面的确定性RPC端点部分获取更多信息。

您可以修改这些脚本以使用moon_isBlockFinalizedmoon_isTxFinalized。为此,您可以使用Web3.jsEthers.jssend方法对Substrate JSON-RPC进行自定义调用。自定义RPC请求也可以使用 Web3.pymake_request方法。您需要将方法名称和参数传递给自定义请求,您可以在Moonbeam自定义API页面上找到该请求。

moon_isBlockFinalized
import { ethers } from 'ethers';

// Define the block hash to check finality
const blockHash = 'INSERT_BLOCK_HASH';

// Define the RPC of the provider for Moonbeam
// This can be adapted for Moonriver or Moonbase Alpha
const providerRPC = {
  moonbeam: {
    name: 'moonbeam',
    rpc: 'INSERT_RPC_API_ENDPOINT',
    chainId: 1284,
  },
};

// Define the Web3 provider
const web3Provider = new ethers.JsonRpcProvider(providerRPC.moonbeam.rpc, {
  chainId: providerRPC.moonbeam.chainId,
  name: providerRPC.moonbeam.name,
});

// Define the function for the custom web3 request
const customWeb3Request = async (web3Provider, method, params) => {
  try {
    return await web3Provider.send(method, params);
  } catch (error) {
    throw new Error(error.body);
  }
};

const main = async () => {
  // Check if the block has been finalized
  const isFinalized = await customWeb3Request(
    web3Provider,
    'moon_isBlockFinalized',
    [blockHash]
  );
  console.log(`Block is finalized? ${isFinalized}`);
};

main();
import { Web3 } from 'web3';

// Define the block hash to check finality
const blockHash = 'INSERT_BLOCK_HASH';

// Define the Web3 provider for Moonbeam
// This can be adapted for Moonriver or Moonbase Alpha
const web3Provider = new Web3('INSERT_RPC_API_ENDPOINT');

// Define the function for the custom Web3 request
const customWeb3Request = async (web3Provider, method, params) => {
  try {
    return await requestPromise(web3Provider, method, params);
  } catch (error) {
    throw new Error(error);
  }
};

// In Web3.js you need to return a promise
const requestPromise = async (web3Provider, method, params) => {
  return new Promise((resolve, reject) => {
    web3Provider.send(
      {
        jsonrpc: '2.0',
        id: 1,
        method,
        params,
      },
      (error, result) => {
        if (error) {
          reject(error.message);
        } else {
          if (result.error) {
            reject(result.error.message);
          }
          resolve(result);
        }
      }
    );
  });
};

const main = async () => {
  // Check if the block has been finalized
  const isFinalized = await customWeb3Request(
    web3Provider.currentProvider,
    'moon_isBlockFinalized',
    [blockHash]
  );

  console.log(JSON.stringify(isFinalized));
  console.log(`Block is finalized? ${isFinalized.result}`);
};

main();
from web3 import Web3

# Define the block hash to check finality
block_hash = 'INSERT_BLOCK_HASH'

# Set the RPC_address for Moonbeam
# This can also be adapted for Moonriver or Moonbase Alpha
RPC_address = 'INSERT_RPC_API_ENDPOINT'

# Define the Web3 provider
web3_provider = Web3(Web3.HTTPProvider(RPC_address))

# Asynchronous JSON-RPC API request
def custom_web3_request(method, params):
    response = web3_provider.provider.make_request(method, params)
    return response

if __name__ == "__main__":
    # Check if the block has been finalized
    is_finalized = custom_web3_request(
       'moon_isBlockFinalized', [block_hash])
    print(
        f'Block is finalized? { is_finalized["result"] }')
moon_isTxFinalized
import { ethers } from 'ethers';

// Define the transaction hash to check finality
const txHash = 'INSERT_TRANSACTION_HASH';

// Define the RPC of the provider for Moonbeam
// This can be adapted for Moonriver or Moonbase Alpha
const providerRPC = {
  moonbeam: {
    name: 'moonbeam',
    rpc: 'INSERT_RPC_API_ENDPOINT',
    chainId: 1284,
  },
};

// Define the Web3 provider
const web3Provider = new ethers.JsonRpcProvider(providerRPC.moonbeam.rpc, {
  chainId: providerRPC.moonbeam.chainId,
  name: providerRPC.moonbeam.name,
});

// Define the function for the custom web3 request
const customWeb3Request = async (web3Provider, method, params) => {
  try {
    return await web3Provider.send(method, params);
  } catch (error) {
    throw new Error(error.body);
  }
};

const main = async () => {
  // Check if the transaction has been finalized
  const isFinalized = await customWeb3Request(
    web3Provider,
    'moon_isTxFinalized',
    [txHash]
  );
  console.log(`Transaction is finalized? ${isFinalized}`);
};

main();
import Web3 from 'web3';

// Define the transaction hash to check finality
const txHash = 'INSERT_TRANSACTION_HASH';

// Define the Web3 provider for Moonbeam
// This can be adapted for Moonriver or Moonbase Alpha
const web3Provider = new Web3('INSERT_RPC_API_ENDPOINT');

// Define the function for the custom Web3 request
const customWeb3Request = async (web3Provider, method, params) => {
  try {
    return await requestPromise(web3Provider, method, params);
  } catch (error) {
    throw new Error(error);
  }
};

// In Web3.js you need to return a promise
const requestPromise = async (web3Provider, method, params) => {
  return new Promise((resolve, reject) => {
    web3Provider.send(
      {
        jsonrpc: '2.0',
        id: 1,
        method,
        params,
      },
      (error, result) => {
        if (error) {
          reject(error.message);
        } else {
          if (result.error) {
            reject(result.error.message);
          }
          resolve(result);
        }
      }
    );
  });
};

const main = async () => {
  // Check if the transaction has been finalized
  const isFinalized = await customWeb3Request(
    web3Provider.currentProvider,
    'moon_isTxFinalized',
    [txHash]
  );

  console.log(JSON.stringify(isFinalized));
  console.log(`Transaction is finalized? ${isFinalized}`);
};

main();
from web3 import Web3

# Define the transaction hash to check finality
tx_hash = 'INSERT_BLOCK_HASH'

# Set the RPC_address for Moonbeam
# This can also be adapted for Moonriver or Moonbase Alpha
RPC_address = 'INSERT_RPC_API_ENDPOINT'

# Define the Web3 provider
web3_provider = Web3(Web3.HTTPProvider(RPC_address))

# Asynchronous JSON-RPC API request
def custom_web3_request(method, params):
    response = web3_provider.provider.make_request(method, params)
    return response

if __name__ == "__main__":
    # Check if the transaction has been finalized
    is_finalized = custom_web3_request(
       'moon_isTxFinalized', [tx_hash])
    print(
        f'Transaction is finalized? { is_finalized["result"] }')

使用Substrate库检查交易确定性

利用以下三个Substrate JSON-RPC的RPC请求,您能够获取当前最终确定的区块并将其与您提供的交易区块号进行比较:

  • chain_getFinalizedHead - 第一个请求将获取最后的最终确定区块的区块哈希
  • chain_getHeader - 第二个请求将获取给定区块哈希的区块头
  • eth_getTransactionReceipt - 第三个请求将检索给定区块哈希的交易收据

Polkadot.js API packagePython Substrate Interface package为开发者提供一种使用JavaScript和Python与Substrate链交互的方式。

您可以在Polkadot.js官方文档网站获取关于Polkadot.js和Substrate JSON-RPC的更多信息,并在PySubstrate官方文档网站获取关于Python Substrate接口的更多信息。

import { ApiPromise, WsProvider } from '@polkadot/api';
import { types } from 'moonbeam-types-bundle';

// Define the transaction hash to check finality
const txHash = 'INSERT_TX_HASH';

// Define the provider for Moonbeam
// This can be adapted for Moonriver or Moonbase Alpha
const wsProvider = new WsProvider('INSERT_WSS_API_ENDPOINT');

const main = async () => {
  // Create the provider using Moonbeam types
  const polkadotApi = await ApiPromise.create({
    provider: wsProvider,
    typesBundle: types,
  });
  await polkadotApi.isReady;

  // Get the latest finalized block of the Substrate chain
  const finalizedHeadHash = (
    await polkadotApi.rpc.chain.getFinalizedHead()
  ).toJSON();

  // Get finalized block header to retrieve number
  const finalizedBlockHeader = (
    await polkadotApi.rpc.chain.getHeader(finalizedHeadHash)
  ).toJSON();

  // Get the transaction receipt of the given tx hash
  const txReceipt = (
    await polkadotApi.rpc.eth.getTransactionReceipt(txHash)
  ).toJSON();

  // You can not verify if the tx is in the block because polkadotApi.rpc.eth.getBlockByNumber
  // does not return the list of tx hashes

  // If block number of receipt is not null, compare it against finalized head
  if (txReceipt) {
    console.log(
      `Current finalized block number is ${finalizedBlockHeader.number}`
    );
    console.log(
      `Your transaction in block ${txReceipt.blockNumber} is finalized? ${
        finalizedBlockHeader.number >= txReceipt.blockNumber
      }`
    );
  } else {
    console.log(
      'Your transaction has not been included in the canonical chain'
    );
  }

  polkadotApi.disconnect();
};

main();
from substrateinterface import SubstrateInterface

# Define the Ethereum transaction hash to check finality
tx_hash = "INSERT_TX_HASH"

# Point API provider to Moonbeam
# This can be adapted for Moonriver or Moonbase Alpha
moonbeam_API_provider = SubstrateInterface(
    url="INSERT_WSS_API_ENDPOINT",
)

if __name__ == "__main__":
    # Get the latest finalized block header of the chain
    finalized_block_header = moonbeam_API_provider.get_block_header(finalized_only=True)
    # Get the finalized block number from the block header
    finalized_block_number = finalized_block_header["header"]["number"]
    # Get the transaction receipt of the given transaction hash through a
    # custom RPC request
    tx_receipt = moonbeam_API_provider.rpc_request(
        "eth_getTransactionReceipt", [tx_hash]
    )

    # Check if tx_receipt is null
    if tx_receipt is None:
        print("The transaction hash cannot be found in the canonical chain.")
    else:
        # Get the block number of the transaction
        tx_block_number = int(tx_receipt["result"]["blockNumber"], 16)
        # Get the transaction block through a custom RPC request
        tx_block = moonbeam_API_provider.rpc_request(
            "eth_getBlockByNumber", [tx_block_number, False]
        )

        print(f"Current finalized block number is { str(finalized_block_number) }")
        print(
            f"Your transaction in block { str(tx_block_number) } is finalized? { str(finalized_block_number >= tx_block_number) }"
        )
        print(
            f'Your transaction is indeed in block { str(tx_block_number) }? { str(tx_hash in tx_block["result"]["transactions"]) }'
        )
本网站的所有信息由第三方提供,仅供参考之用。Moonbeam文档网站(https://docs.moonbeam.network/)上列出和描述的任何项目与Moonbeam立场无关。Moonbeam Foundation不保证网站信息的准确性、完整性或真实性。如使用或依赖本网站信息,需自行承担相关风险,Moonbeam Foundation不承担任何责任和义务。这些材料的所有陈述和/或意见由提供方个人或实体负责,与Moonbeam Foundation立场无关,概不构成任何投资建议。对于任何特定事项或情况,应寻求专业权威人士的建议。此处的信息可能会包含或链接至第三方提供的信息与/或第三方服务(包括任何第三方网站等)。这类链接网站不受Moonbeam Foundation控制。Moonbeam Foundation对此类链接网站的内容(包括此类链接网站上包含的任何信息或资料)概不负责也不认可。这些链接内容仅为方便访客而提供,Moonbeam Foundation对因您使用此信息或任何第三方网站或服务提供的信息而产生的所有责任概不负责。
Last update: January 23, 2024
| Created: October 1, 2021