Starknet 交易批量处理程序

抽象的

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

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

建筑学

image description

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

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。请注意,为了更好的可读性,代码已被简化(完整代码):

// 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 loopfunc (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.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  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)        }    }}

登录后复制

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

发件人

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

// actual send actor event loopfunc (b *batcher) runsendactor(txndatapairchan <-chan txndatapair) {    oldnonce := new(felt.felt).setuint64(0)    for {        // receive the batch transaction        txndatapair, ok := <-txndatapairchan        if !ok {            ...        }        txn := txndatapair.txn        data := txndatapair.data        // get the current nonce of the sender account        nonce, err := b.accnt.nonce(            context.background(),            rpc.blockid{tag: "latest"},            b.accnt.accountaddress,        )        if err != nil {            ...        }        // it might happen that the nonce is not directly updated if another transaction was sent just before. therefore, we manually increment it to make sure this new transaction is sent with the correct nonce        if nonce.cmp(oldnonce) <= 0 {            nonce.add(oldnonce, new(felt.felt).setuint64(1))        }        txn.invoketxnv1.nonce = nonce        // sign the transaction        err = b.accnt.signinvoketransaction(            context.background(),            &txn.invoketxnv1,        )        if err != nil {            ...        }        // send the transaction to the starknet network        resp, err := b.accnt.addinvoketransaction(            context.background(),            &txn,        )        if err != nil {            ...        }        // monitor the transaction status    statusloop:        for {            // wait a bit before checking the status            time.sleep(time.second * 5)            // get the transaction status            txstatus, err := b.accnt.gettransactionstatus(                context.background(),                resp.transactionhash,            )            if err != nil {                ...            }            // check the execution status            switch txstatus.executionstatus {            case rpc.txnexecutionstatussucceeded:                oldnonce = nonce                break statusloop            case rpc.txnexecutionstatusreverted:                // a reverted transaction consumes the nonce                oldnonce = nonce                ...                break statusloop            default:            }            // check the finality status            switch txstatus.finalitystatus {            case rpc.txnstatus_received:                continue            case rpc.txnstatus_accepted_on_l2, rpc.txnstatus_accepted_on_l1:                oldnonce = nonce                break statusloop            case rpc.txnstatus_rejected:                ...            default:            }            // loop until the transaction status is determined        }    }}

登录后复制

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

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

        fee, err := b.accnt.EstimateFee(            context.Background(),            []rpc.BroadcastTxn{txn},            []rpc.SimulationFlag{},            rpc.WithBlockTag("latest"),        )        if err != nil {            ...        }

登录后复制

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

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

迈向通用批处理机

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

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

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

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

命令行工具

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

结论

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

以上就是Starknet 交易批量处理程序的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2477175.html

(0)
上一篇 2025年3月4日 19:19:49
下一篇 2025年3月4日 19:20:13

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 每个后端开发人员都应该知道的热门库

    Go语言凭借其简洁性、性能和并发优势,已成为后端开发的热门选择。虽然Go标准库功能强大,但许多第三方库能显著提升开发效率和代码质量。 本文将介绍五个必备的Go语言库,助您高效构建API、管理数据库、完善日志记录等。 1. Gin 高性能HT…

    2025年3月4日
    200
  • 使用 OpenAI、Go 和 PostgreSQL (pgvector) 构建语义搜索引擎

    近年来,向量嵌入技术已成为自然语言处理(NLP)和语义搜索的核心。与传统的关键词搜索不同,向量数据库通过比较文本的向量表示(嵌入)来理解文本的语义含义。本示例展示如何结合OpenAI嵌入、Go语言和PostgreSQL数据库(以及pgvec…

    2025年3月4日
    200
  • 微服务中的事务:SAGA 模式概述部分

    构建强大的分布式系统是一项极具挑战性的任务,尤其是在保证多个服务数据一致性方面。在微服务架构中,传统的数据库事务往往力不从心。这时,分布式事务便成为关键解决方案。 分布式事务能够协调多个服务间的操作,并优雅地处理各种故障。而SAGA模式是实…

    2025年3月4日
    200
  • Docker 卷

    容器化应用的关键在于数据持久化。docker容器默认情况下,删除后其内部所有数据都会丢失。解决方法是使用docker卷。它们允许数据在容器生命周期中持续存在,为任何应用提供隔离和可扩展性。 为何使用Docker卷? 持久性:创建或链接卷到容…

    2025年3月4日
    200
  • 如何使用Golang,Gin和Postgresql构建CRUD应用程序

    本教程演示如何使用Go语言、Gin框架和PostgreSQL数据库构建一个简单的CRUD (创建、读取、更新、删除) 应用。最终,您将得到一个可以管理PostgreSQL数据库中数据的基本应用。 目录 简介前提条件项目结构项目设置创建数据库…

    2025年3月4日
    200
  • Golang文本/模板中的SQL查询

    在使用Go的text/template包动态生成SQL查询构建后端API时,提高开发效率的同时,务必注意SQL注入的风险。本文将演示如何避免这种风险。 text/template的SQL注入漏洞示例 以下代码片段展示了如何通过字符串插值构建…

    2025年3月4日
    200
  • GO中的数据处理管道(Golang)

    Go语言数据处理管道详解 Go语言中的数据处理管道是一种将数据处理流程分解成一系列阶段或步骤的模式。每个阶段对数据执行特定操作,前一阶段的输出作为下一阶段的输入。这种模式广泛应用于ETL(提取、转换、加载)、流处理和批处理等场景。Go语言利…

    2025年3月4日
    200
  • 不要浪费时间 – 立即消除Golang!

    还在犹豫是否学习Golang?作为开发者,您可能已经错过了很多机会!近年来,行业对更快、更简洁、更可靠代码的需求日益增长。虽然JavaScript、Java和Python各有优势,但Golang凭借其速度、简洁性和并发性脱颖而出,让您在竞争…

    2025年3月4日
    200
  • 如何解密JWT Token并提取用户ID?

    轻松提取JWT Token中的用户ID 本文介绍如何从JWT (JSON Web Token) 字符串中提取用户ID。JWT由三个部分组成,用点号(.)分隔:头部、有效载荷和签名。有效载荷包含用户信息,例如用户ID、过期时间等,并以Base…

    2025年3月4日
    200
  • Go语言中如何高效查找字符串中第一个出现的指定字符?

    Go语言高效查找字符串中第一个指定字符的方法 在Go语言中,查找字符串中第一个出现的特定字符,并非只能依赖索引比较。 更高效的方法是直接遍历字符串的字符范围。 以下Go代码片段展示了这种高效的查找方法: func findFirstChar…

    2025年3月4日
    200

发表回复

登录后才能评论