首页 >后端开发 >Golang >Starknet 交易批量处理程序

Starknet 交易批量处理程序

Linda Hamilton
Linda Hamilton原创
2024-12-31 17:54:14602浏览

抽象的

本文介绍了 Metacube 中使用的交易批处理程序,用于即时发送玩家赚取的 NFT。它解释了批处理程序基于参与者的可扩展架构,并提供了 Go 中的详细实现。

所有代码片段都可以在关联的 GitHub 存储库中找到。

建筑学

A Starknet transactions batcher

巴彻由两位主要演员组成:

  • Builder 接收交易,将它们批处理成单个多调用交易,并将其发送给 Sender actor。
  • 发送者使用适当的字段(随机数、最大费用等)完成交易,对其进行签名,将其发送到 Starknet 网络,并监控其状态。

这种参与者分离可以实现可扩展且高效的批处理程序。构建器在发送者发送交易时准备交易,从而实现连续且高效的交易流。

执行

以下实现特定于 Go,但这些概念可以轻松适应其他语言,因为功能保持不变。

此外,请注意,此实现特定于从同一合约发送 NFT。然而,本文后面提到了一种更通用的方法。

最后,代码基于Nethermind开发的starknet.go库。

批处理机

让我们从 Batcher 本身开始:

type Batcher struct {
    accnt           *account.Account
    contractAddress *felt.Felt
    maxSize         int
    inChan          <-chan []string
    failChan        chan<- []string
}

账户(accnt)是持有NFT的账户,它将用于签署转移NFT的交易。这些 NFT 是同一合约的一部分,因此有合约地址字段。 maxSize 字段是批次的最大大小,inChan 是将事务发送到 Batcher 的通道。 failChan 用于发回发送失败的交易。

请注意,在这个实现中,后面所说的交易数据([]string)是一个由两个元素组成的数组:接收者地址和 NFT ID。

Batcher 同时运行 Builder 和 Sender Actor:

type TxnDataPair struct {
    Txn  rpc.BroadcastInvokev1Txn
    Data [][]string
}

func (b *Batcher) Run() {
    txnDataPairChan := make(chan TxnDataPair)

    go b.runBuildActor(txnDataPairChan)
    go b.runSendActor(txnDataPairChan)
}

定义的通道 txnDataPairChan 将交易数据对从 Builder 发送到 Sender。每个交易数据对都包含批量交易,每个交易的数据都嵌入其中。每笔交易的数据都与批量交易一起发送,以便失败的交易可以被发送回实例化 Batcher 的实体。

建设者

让我们分析一下Build actor。请注意,为了更好的可读性,代码已被简化(完整代码):

type Batcher struct {
    accnt           *account.Account
    contractAddress *felt.Felt
    maxSize         int
    inChan          <-chan []string
    failChan        chan<- []string
}

runBuildActor 函数是 Builder actor 的事件循环。它等待事务发送到批处理程序,并在批处理已满或达到超时时构建批处理事务。然后批量交易被发送到 Sender actor。

发件人

现在让我们分析一下Sender actor。请注意,为了更好的可读性,代码已被简化(完整代码):

type TxnDataPair struct {
    Txn  rpc.BroadcastInvokev1Txn
    Data [][]string
}

func (b *Batcher) Run() {
    txnDataPairChan := make(chan TxnDataPair)

    go b.runBuildActor(txnDataPairChan)
    go b.runSendActor(txnDataPairChan)
}

runSendActor 函数是发送者 Actor 的事件循环。它等待 Builder 发送批量交易,对它们进行签名,将它们发送到 Starknet 网络,并监控它们的状态。

关于费用估算的说明:可以在发送之前估算批量交易的费用成本。交易签名后可添加以下代码:

// This function builds a function call from the transaction data.
func (b *Batcher) buildFunctionCall(data []string) (*rpc.FunctionCall, error) {
    // Parse the recipient address
    toAddressInFelt, err := utils.HexToFelt(data[0])
    if err != nil {
        ...
    }

    // Parse the NFT ID
    nftID, err := strconv.Atoi(data[1])
    if err != nil {
        ...
    }

    // The entry point is a standard ERC721 function
    // https://docs.openzeppelin.com/contracts-cairo/0.20.0/erc721
    return &rpc.FunctionCall{
        ContractAddress: b.contractAddress,
        EntryPointSelector: utils.GetSelectorFromNameFelt(
            "safe_transfer_from",
        ),
        Calldata: []*felt.Felt{
            b.accnt.AccountAddress, // from
            toAddressInFelt, // to
            new(felt.Felt).SetUint64(uint64(nftID)), // NFT ID
            new(felt.Felt).SetUint64(0), // data -> None
            new(felt.Felt).SetUint64(0), // extra data -> None
        },
    }, nil
}

// This function builds the batch transaction from the function calls.
func (b *Batcher) buildBatchTransaction(functionCalls []rpc.FunctionCall) (rpc.BroadcastInvokev1Txn, error) {
    // Format the calldata (i.e., the function calls)
    calldata, err := b.accnt.FmtCalldata(functionCalls)
    if err != nil {
        ...
    }

    return rpc.BroadcastInvokev1Txn{
        InvokeTxnV1: rpc.InvokeTxnV1{
            MaxFee:        new(felt.Felt).SetUint64(MAX_FEE),
            Version:       rpc.TransactionV1,
            Nonce:         new(felt.Felt).SetUint64(0), // Will be set by the send actor
            Type:          rpc.TransactionType_Invoke,
            SenderAddress: b.accnt.AccountAddress,
            Calldata:      calldata,
        },
    }, nil
}

// Actual Build actor event loop
func (b *Batcher) runBuildActor(txnDataPairChan chan<- TxnDataPair) {
    size := 0
    functionCalls := make([]rpc.FunctionCall, 0, b.maxSize)
    currentData := make([][]string, 0, b.maxSize)

    for {
        // Boolean to trigger the batch building
        trigger := false

        select {
        // Receive new transaction data
        case data, ok := <-b.inChan:
            if !ok {
                ...
            }

            functionCall, err := b.buildFunctionCall(data)
            if err != nil {
                ...
            }

            functionCalls = append(functionCalls, *functionCall)
            size++
            currentData = append(currentData, data)

            if size >= b.maxSize {
                // The batch is full, trigger the building
                trigger = true
            }

        // We don't want a smaller batch to wait indefinitely to be full, so we set a timeout to trigger the building even if the batch is not full
        case <-time.After(WAITING_TIME):
            if size > 0 {
                trigger = true
            }
        }

        if trigger {
            builtTxn, err := b.buildBatchTransaction(functionCalls)
            if err != nil {
                ...
            } else {
                // Send the batch transaction to the Sender
                txnDataPairChan <- TxnDataPair{
                    Txn:  builtTxn,
                    Data: currentData,
                }
            }

            // Reset variables
            size = 0
            functionCalls = make([]rpc.FunctionCall, 0, b.maxSize)
            currentData = make([][]string, 0, b.maxSize)
        }
    }
}

这可能有助于确保在发送交易之前费用不会太高。如果估计费用高于预期,则可能还需要重新调整交易的最大费用字段。但请注意,当交易发生任何更改时,必须重新签名!

但是请注意,如果交易吞吐量相当高,您在估算费用时可能会遇到一些问题。这是因为当给定的交易刚刚被批准时,更新帐户的随机数会有一点延迟。因此,在估计下一笔交易的费用时,它可能会失败,认为随机数仍然是前一笔交易。因此,如果您仍然想估算费用,那么您可能需要在每笔交易之间提供一些睡眠以避免此类问题。

迈向通用批处理机

所提供的批处理程序专门用于从同一合约发送 NFT。然而,该架构可以轻松适应发送任何类型的交易。

首先,发送到 Batcher 的交易数据必须更加通用,因此包含更多信息。它们必须包含合约地址、入口点选择器和调用数据。然后必须调整 buildFunctionCall 函数来解析此信息。

还可以更进一步,将发件人帐户设为通用。这将需要更多的重构,因为必须针对每个发送者帐户对交易进行批处理。然而,它是可行的,并且可以实现更通用的批处理机。

但是,请记住,过早的优化是万恶之源。因此,如果您只需要发送 NFT 或特定代币(例如 ETH 或 STRK),那么提供的批处理程序就足够了。

命令行工具

存储库代码可以用作 CLI 工具来批量发送一堆 NFT。该工具易于使用,阅读本文后您应该能够根据您的需要进行调整。请参阅 README 了解更多信息。

结论

我希望这篇文章可以帮助您更好地了解Metacube如何向其玩家发送NFT。批处理程序是一个关键的基础设施组件,我们很高兴与社区分享它。如果您有任何问题或反馈,请随时发表评论或与我联系。感谢您的阅读!

以上是Starknet 交易批量处理程序的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn