《【WAX链游】EOS网络第三方代付CPU资源【原理】》
在上一篇文章中,我们介绍了EOS网络的【ONLY_BILL_FIRST_AUTHORIZER】特性,我们知道如何用A账户来支付B账户提交的交易的CPU/NET资源费用。本文,我们讲给出基于【eosjs】【waxjs】【eospy】的示例代码。
首先贴出示例代码github仓库地址,所有的示例代码都在这里,有一定基础的同学可以简单看一下就立马上手:
https://github.com/encoderlee/eos_demo
我们的大部分测试用例运行在EOS测试网络【Jungle3.0】之上,这当然也适用于EOS主网和WAX主网,只需修改RPC端点即可。
首先参考:https://github.com/encoderlee/eos_demo/blob/main/eosjs_demo/normal_transfer.html
这是使用eosjs实现的一笔转账交易,我们将 0.0001 个 EOS代币从账号【consumer1111】转给账号【consumer2222】,关键代码如下:
const consumer_name = "consumer1111";const consumer_private_key = "5KWxgG4rPEXzHnRBaiVRCCE6WAfnqkRpTu1uHzJoQRzixqBB1k3";const rpc = new eosjs_jsonrpc.JsonRpc("https://jungle3.greymass.com");const provider = new eosjs_jssig.JsSignatureProvider([consumer_private_key]);const api = new eosjs_api.Api({ rpc:rpc, signatureProvider: provider });const result = await api.transact({ actions: [{ account: 'eosio.token', name: 'transfer', authorization: [ { actor: consumer_name, permission: "active", }, ], data: { from: consumer_name, to: "consumer2222", quantity: '0.0001 EOS', memo: 'by eosjs', }, }] }, { blocksBehind: 3, expireSeconds: 90, });
代币的转移本质上是调用合约【eosio.token】的【transfer】方法,【data】是传递给【transfer】方法的参数,【authorization】指明该笔交易由谁来签名。正常情况下,这笔交易由【consumer1111】来支付CPU/NET资源费用。
那么根据上文的原理,如果要让第三个账号来支付该笔交易的CPU/NET资源费用,只需在【authorization】中增加第三个账号即可。
参考:https://github.com/encoderlee/eos_demo/blob/main/eosjs_demo/only_bill_first_authorizer.html
关键代码:
const consumer_name = "consumer1111";const consumer_private_key = "5KWxgG4rPEXzHnRBaiVRCCE6WAfnqkRpTu1uHzJoQRzixqBB1k3";const payer_name = "payer2222222";const payer_private_key = "5KAskRRbqYVCRhZxLXqeg9yvWYQQHifDtf7BPceZUDw6zybjaQh";const rpc = new eosjs_jsonrpc.JsonRpc("https://jungle3.greymass.com");const provider = new eosjs_jssig.JsSignatureProvider([consumer_private_key, payer_private_key]);const api = new eosjs_api.Api({ rpc:rpc, signatureProvider: provider });const result = await api.transact({ actions: [{ account: 'eosio.token', name: 'transfer', authorization: [ { actor: payer_name, permission: "active", }, { actor: consumer_name, permission: "active", }, ], data: { from: consumer_name, to: "consumer2222", quantity: '0.0001 EOS', memo: 'by eosjs', }, }] }, { blocksBehind: 3, expireSeconds: 90, });
可以看到,我们在【authorization】中增加了【payer2222222】这个账号,并且把【payer2222222】放到了前面,这时他是第一授权人,最后这笔交易的CPU/NET资源费用将会由【payer2222222】来买单。当然,授权人增加了【payer2222222】后,我们也需要提供【payer2222222】的私钥,该笔交易将分别用【consumer1111】和【payer2222222】的私钥进行签名后发送。
执行效果:
https://jungle3.bloks.io/transaction/64f1c43328b6790c7bacdeb06a832bb6d0b918d253a996091b344cc04b6b2a3f
有同学就问了,不对呀,如果是自行创建的EOS/WAX原生账号,私钥在自己手上,确实可以直接用EOSJS来这样发起交易,但是我的WAX账号是用【WAX云钱包】注册的呀,私钥不在我手上,托管在WAX云钱包云端,我无法提供私钥呀,该怎么办。
其实不用担心,仔细阅读【eosjs】和【waxjs】的代码就会发现,【waxjs】实际上就是在【eosjs】上套了一层壳,【waxjs】其实也是遵循【eosjs】的API规范来开发的,它也实现了一个SignatureProvider,和【eosjs】的JsSignatureProvider不同的是,【eosjs】的JsSignatureProvider是在本地使用私钥直接签名,而【waxjs】实现的SignatureProvider是在浏览器弹出一个授权小窗口,向【WAX云钱包】服务器发送一个HTTP请求来签署交易,感兴趣的同学可以在浏览器抓包研究授权小窗口发送了什么数据,得到的签名是什么样子的,其实都是按照eosjs的数据格式来的,它并没有做复杂的事情。当然,弄清楚原理的同学,完全可以使用python或java等其它语言,直接发送HTTP请求到 https://public-wax-on.wax.io/wam/sign 完成交易签名,这样就可以脱离浏览器环境实现链游脚本,占用更少的系统资源,一台电脑多开更多号。
本质上我们的目的是为一笔交易设置两个授权人,并且用两个授权人的私钥进行签名,那么只要能【签名】就可以了,私钥未必要在我们手上,所以针对【waxjs】的实现代码是这样的:
参考:https://github.com/encoderlee/eos_demo/blob/main/waxjs_demo/only_bill_first_authorizer.html
//waxjs rpc实例,注意需要先login()const wax = new waxjs.WaxJS({ rpcEndpoint: 'https://wax.greymass.com', });const payer_name = "fuckpayforit"; //这里改成你支付CPU的账号const payer_private_key = "这里填写该账号的私钥"//eosjs rpc实例const rpc = new eosjs_jsonrpc.JsonRpc('https://wax.greymass.com');const provider = new eosjs_jssig.JsSignatureProvider([payer_private_key]);const api = new eosjs_api.Api({rpc: rpc, signatureProvider: provider});//注意broadcast: false,我们并没有发送交易,只是将交易序列化let transcation_args = await api.transact({ actions: [{ account: 'eosio.token', name: 'transfer', authorization: [ { actor: payer_name, permission: "active", }, { actor: wax.user.account, permission: "active", }, ], data: { from: wax.user.account, to: "consumer1111", quantity: '0.00010000 WAX', memo: 'by waxjs', },}],}, { blocksBehind: 3, expireSeconds: 90, sign: false, broadcast: false,});//首先借助eosjs用【fuckpayforit】的私钥签名let available_keys = await api.signatureProvider.getAvailableKeys();let sign_args = { chainId: api.chainId, requiredKeys: available_keys, serializedTransaction: transcation_args.serializedTransaction,};transcation_args = await api.signatureProvider.sign(sign_args);const payer_signatures = transcation_args.signatures;//然后,借助waxjs用登录的wax账号对该笔交易进行签名available_keys = await wax.api.signatureProvider.getAvailableKeys();sign_args = { chainId: api.chainId, requiredKeys: available_keys, serializedTransaction: transcation_args.serializedTransaction,};transcation_args = await wax.api.signatureProvider.sign(sign_args);//合并签名transcation_args.signatures = payer_signatures.concat(transcation_args.signatures)//最后,发送该笔交易,用eosjs或者用waxjs来发送都行,一样的const result = await api.pushSignedTransaction(transcation_args);
在上面的代码中,我们用登录的wax账号给【consumer1111】这个账号转账0.0001个WAX,但是该笔交易的CPU/NET费用由【fuckpayforit】来支付。
执行效果:
https://wax.bloks.io/transaction/02a069aac945d62d5a91e913dce7e96862235bc5c382bc7da78b9da088430a62
注意【fuckpayforit】这个账号是我们使用【Anchor】创建的WAX原生账号,私钥在我们手上,并非WAX云钱包账号,而发起交易的账号【m45yy.wam】则是托管的WAX云钱包账号。
以此为例,我们只需要创建一个用于支付CPU/NET资源的账号A,并且质押够足够多的CPU资源,然后我们的几百个WAX云钱包账号用来玩链游,这几百个账号可以完全0质押,在玩游戏,调用智能合约,发送交易的时候,让账号A也来签一下名,就可以实现所有账号的所有操作所需的CPU/NET资源都由账号A来买单。
当然这个买单的账号A也可以是一个WAX云钱包账号,但我觉得没必要了,原生账号更好管理。
EOSPY实现 至于使用Python如何实现,这里不再赘述,直接看代码即可:
https://github.com/encoderlee/eos_demo
关于【ONLY_BILL_FIRST_AUTHORIZER】,这里有其它的一些好的文章和开源项目可以参考:
https://cmichel.io/eosio-how-to-pay-for-users-cpu
https://github.com/MrToph/eos-pay-for-user-cpu-example
https://github.com/pudrikrete/only_bill_first_authorizer
https://github.com/jan-gogogo/only-bill-first-authorizer