Filecoin GAS费的设置

简介

与许多区块链的传统情况一样,GAS是一个度量单位,用来衡量为了执行上链消息操作消耗了多少存储和/或计算资源,它的工作原理如下:为了消息被执行并包含在一个块中(即为了消息能成功上链),消息发送方会指定他们愿意支付的最大金额,这是根据所有的GAS单位数(GasLimit)(它通常预期高于实际的使用GAS的单位(GasUsed))和每单位GAS的费用(GasFeeCap)规定的。
传统上,GasUsed * GasFeeCap作为奖励给区块生产矿工,该计算公式的结果被视为消息包含的优先费,也就是说,消息按递减顺序排序,且GasUsed * GasFeeCap最高的消息优先,因为它们给矿工带来了更多的利润。
然而,由于一些原因,这种支付GAS的策略(GasUsed * GasFeeCap)对区块开采矿工来说是有问题的。首先,一个区块生成矿机可能免费上链一条高成本的消息(就所需的链资源而言),在这种情况下,链本身需要承担成本。其次,消息发送方可以为低成本的消息设置任意高的价格(同样,就链资源而言),从而导致DoS漏洞。
为了应对这种情况,Filecoin区块链定义了一个每条消息都需要燃烧的基准费用(BaseFee)。他们给出的理由是:GAS是链上资源消耗的一种衡量标准,燃烧掉比奖励给矿工更有意义。这样就避免了矿商的收费操纵。BaseFee是动态的,根据网络拥塞情况自动调整。这使网络应对spam攻击时更具有弹性。在SPAM攻击期间,网络负载会增加,由于基准费用的增加,攻击者不可能长期保持整个块都是SPAM信息。
最后,GasPremium是发送方支付的优先费用,以激励矿工挑选最有利可图的信息。换句话说,如果消息发送者希望更快的上链其消息,他们可以设置更高的GasPremium

参数

  • GasUsed是为了执行消息而消耗的资源数量(或GAS单位)的度量。每个单位的GAS是用attoFIL来测量的,因此,GasUsed是一个代表能量消耗单位的数字。GasUsed与消息是否正确执行或失败无关。
  • BaseFee是每条消息执行时燃烧的每单位GAS的设定价格(以attoFIL/gas单位衡量)(发送到一个不可恢复的地址)。BaseFee的值是动态的,根据当前的网络拥塞参数进行调整。例如,当网络中 GAS 限用量超过5B时,BaseFee增加,当GAS限用量低于5B时,BaseFee会减少。
    应用于每个块的BaseFee都应该包含在块本身中。链的头部必须要包含当前BaseFee的值。BaseFee适用于每个GasUsed的单位,因此,一条消息燃烧的GAS总量为 BaseFee * GasUsed。注意,每个消息都会触发BaseFee,但是它的值对于同一块中的所有消息都是相同的。
  • GasLimit以GAS为单位的度量,并由消息发送方设置。它对一条消息在执行上链操作时应该被允许消耗的gas的数量(即gas的单位数)施加了严格的限制。消息对于它所触发的每个基本操作都会消耗gas,而耗尽gas的消息将失败。当一条消息失败时,由于该消息的执行而发生的对状态的每一次修改都将恢复到它之前的状态。不管消息执行是否成功,矿机都将获得执行消息所消耗资源的奖励(参见下面的GasPremium)。
  • GasFeeCap是信息发送方愿意支付的每单位GAS的最高价格(以attoFIL/gas单位衡量)。与GasLimit一起,GasFeeCap设置了发送方为消息支付的FIL的最大金额:发送方保证消息的成本不会超过GasLimit * GasFeeCap attoFIL(不包括消息为接收方支付的任何费用)。
  • GasPremium是消息发送方为了消息上链愿意支付的“小费”(在基准费之上)的每单位GAS的价格(以attoFIL/gas衡量)。一条消息通常可以被赚取的利润是GasLimit * GasPremium attoFIL,通常GasPremium = GasFeeCap - BaseFee。请注意,GasPremium应用于GasLimit,而不是GasUsed,以便让矿工的消息选择更简单。

注意

  • GasFeeCap 应该始终高于网络的BaseFee,如果消息的GasFeeCap低于BaseFee,则其余部分将来自矿工作为惩罚。这个惩罚适用于矿工,因为他们选择了一条支付低于网络BaseFee(即不能支付网络成本)的消息。然而,如果同一个发送方在消息池中有另一条消息,其GasFeeCap远大于BaseFee,则矿工可能会选择其GasFeeCap小于BaseFee的消息(同时也会选择另一条GasFeeCap远大于BaseFee的消息)。因为如果存在多个消息,则矿机应该从消息池中挑选发送方的所有消息。原因是,第二条消息增加的费用将弥补第一条消息损失。
  • 如果BaseFee + GasPremium > GasFeeCap,那么矿工可能不会获得整个GasLimit * GasPremium作为他们的奖励。
  • 一条消息的花费被严格限制在GasFeeCap * GasLimit之内。从这笔金额中,首先支付(烧掉)BaseFee。之后,最高GasLimit * GasPremium将给予矿工作为奖励。
  • 一条消息会在GAS耗尽时失败,退出代码为“out of gas”。GasUsed * BaseFee将仍然燃烧(在这种情况下GasUsed = GasLimit),矿工将仍然奖励GasLimit * GasPremium,前提是GasFeeCap > BaseFee + GasPremium
  • GasFeeCap的值太低,很可能会导致消息被困在消息池中,因为从利润的角度来说它不太有吸引力,任何矿工都不愿意打包上链这条消息。当这种情况发生时,需要更新GasFeeCap,从而使消息对矿工更具吸引力。消息的发送方可以推送一个新的消息到消息池(默认情况下,将传播到其他矿工的消息池),这条新消息与旧信息的标识符必须是相同的(例如,相同的Nonce),然后更新这条新消息的GasPremium,至少增加至少25%。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个比较省gas的ERC721合约示例代码: ``` pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import "@openzeppelin/contracts/utils/Address.sol"; contract MyNFT is IERC721 { using Address for address; mapping (uint256 => address) private _owners; mapping (address => uint256) private _balances; mapping (uint256 => address) private _tokenApprovals; mapping (address => mapping (address => bool)) private _operatorApprovals; function balanceOf(address owner) public view override returns (uint256) { require(owner != address(0), "ERC721: balance query for the zero address"); return _balances[owner]; } function ownerOf(uint256 tokenId) public view override returns (address) { address owner = _owners[tokenId]; require(owner != address(0), "ERC721: owner query for nonexistent token"); return owner; } function safeTransferFrom(address from, address to, uint256 tokenId) public override { safeTransferFrom(from, to, tokenId, ""); } function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public override { require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); _safeTransfer(from, to, tokenId, _data); } function transferFrom(address from, address to, uint256 tokenId) public override { require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); _transfer(from, to, tokenId); } function approve(address to, uint256 tokenId) public override { address owner = ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require(msg.sender == owner || isApprovedForAll(owner, msg.sender), "ERC721: approve caller is not owner nor approved for all" ); _tokenApprovals[tokenId] = to; emit Approval(owner, to, tokenId); } function setApprovalForAll(address operator, bool approved) public override { require(operator != msg.sender, "ERC721: approve to caller"); _operatorApprovals[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function isApprovedForAll(address owner, address operator) public view override returns (bool) { return _operatorApprovals[owner][operator]; } function getApproved(uint256 tokenId) public view override returns (address) { require(_owners[tokenId] != address(0), "ERC721: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } function _transfer(address from, address to, uint256 tokenId) internal virtual { require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); require(to != address(0), "ERC721: transfer to the zero address"); _owners[tokenId] = to; _balances[from] -= 1; _balances[to] += 1; emit Transfer(from, to, tokenId); } function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { require(_owners[tokenId] != address(0), "ERC721: operator query for nonexistent token"); address owner = ownerOf(tokenId); return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); } function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) private returns (bool) { if (!to.isContract()) { return true; } bytes memory returndata = to.functionCall(abi.encodeWithSelector( IERC721Receiver(to).onERC721Received.selector, msg.sender, from, tokenId, _data ), "ERC721: transfer to non ERC721Receiver implementer"); bytes4 retval = abi.decode(returndata, (bytes4)); return (retval == _ERC721_RECEIVED); } bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; } ``` 这个合约代码只包含了ERC721标准规定的基本函数,没有额外的复杂逻辑,因此省去了一些不必要的计算和存储操作,从而降低了gas用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值