如何理解区块链上的经济模型(FeesGas)

qtum 发布在 链圈子 2 2682

11

以太坊和Qtum中的Gas表示的是智能合约中具体操作的经济开销。不同的操作对应不同的开销,有些操作开销很小,有些则不然。这种机制的工作原理其实是在价格上限制某些操作,同时使得针对区块链的攻击变得更加昂贵。在图灵完备的系统中,Gas的概念是必不可少的,否则的任何人都可以用一个简单的无限循环语句让整个区块链崩溃。

今天我将会具体说说Gas模型的设计初衷鼓励哪些行为,以及由此带来的与其设计初衷相违背的结果。

存储开销

众所周知,在以太坊区块链上的永久存储空间是最昂贵的资源。按照目前的网络状况,大概1Kb数据需要花费2.5美元,这还取决于你想要实现多快的确认速度。而存储1Mb的数据需要花费超过2,000美元。这就意味着在区块链上存储任何稍微多一点的数据都是不太现实的。然而,这一结论仅仅是针对常规的存储方式。实际上在以太坊上还有多种不同的数据存储方式,下面我将具体介绍五种不同的数据存储方法,并介绍其不同的行为,开销以及优势等:

外部代码

这指的是你在以太坊上使用CREATE opcode创建合约时的一种数据存储方式。你可以把任何数据当做新创建合约的opcode进行存储。这或许是最浪费资源的方式了,因为一旦存储了这些数据,即使这些数据不再需要了,用户也没有动力再去销毁数据并回收资源。

这可以看做是一个半易失性存储方式。一旦数据被保存,它将不会更新,直到重新创建。这种方式比较适用于那种几乎不需要更新的比较大的数据区块。值得注意的是,这种方式在加载数据时开销非常低,这方面和接下来介绍几种方法相比具有压倒性的优势。

内部代码

这种方式是存储Solidity内部生成bytecode用到数据的首选。当你可以从构造函数或是缓存中生成某些数据的情况下,一般来说重新计算数据并直接存储到所部署合约的bytecode中,要比在创建合约交易时传入大量数据要来的便宜得多。如果这些数据不需要更新,那么最好采用这种存储方式,同时在加载数据时的开销也很低。

常规存储

这是以太坊提供的常规数据存储方式。它是一种非连续的存储,允许每32字节字长为单位的存储空间进行更新,这比直接写到新的存储空间要便宜。同时这种方式也能激励用户释放不需要的空间,因为这样做将获得Gas奖励。这部分存储区域可以很容易地进行更新和读取的操作,但是对其进行写操作的开销却是这几种存储方式中最昂贵的。

合约交易存储

这是一种完全易失性存储方式,尽管它并没有存储任何合约能够直接获取的永久数据。这种方式实际上将数据存储在EVM中,数据只有通过发送合约数据才能够传递给具体的合约。合约会对这些数据进行hash操作以确保正确的合约状态。这种数据存储方式有不少弊端,其中最大的问题是合约交易数据往往是非常昂贵的。这是目前为止获取数据最为昂贵的一种方式,因为它需要将数据放到调用合约的交易中。然而,对于写数据或更新数据来说,它却是除了内部代码外最便宜的方式,因为它并没有在区块链上存储任何数据。这种存储方式可以扩展成类似hash数的结构,这样能进一步节省宝贵的交易存储空间。这种数据存储方式还有一个弊端就是数据竞争(race)问题。一旦某个用户调用合约导致数据更新,这一操作将导致所有未完成的交易的数据失效。这一弊端再加上之前提到的高昂的获取数据的开销,极大的限制了这种存储方式的应用场景。

那么,这几种存储方法的总体开销到底有多大呢?接下来进行定量比较。

注意,下面图中的“actual transaction cost”上文中没有详述,它指的是给定传输数据大小的情况下实际调用合约的开销。

22

334455

通过这些图中的结果,我们可以得到如下结论:

  • 外部代码适用于任何大于128字节且不需要更新的数据存储;
  • 内部代码适用于任何部署时已知的常量数据存储;
  • 常规存储方式主要适用于需要经常更新的数据;
  • 交易数据在除了竞争情况的一些场景中也适用,但当合约调用超过一次后,总体花费的开销将会急速增长;

存储方式的经济模型

从博弈论和经济模型的角度看,将整个区块链的非易失性数据都存储到合约bytecode中是否比存储到常规存储数据库中要便宜呢?我想这取决于多方面因素。随着账户数量的增加,其获取数据的开销是否会随之增大呢?我无从得知。但有一点我可以确定,那就是如果用这种方法存储易失性数据,代价会非常昂贵,并且非常浪费区块链资源。我所列的图标包含了部署一个新合约所需的开销,但要减去销毁旧合约数据返还的大约19000gas。这确实会在一定程度上影响图表左半边的结果。但我们可以看到,在大约数据大小达到320字节以上时,采用以太坊推荐的常规存储将是最好的方式。

那么合约数据存储代价如此高昂的弊端是什么呢?其最大的弊端就是会鼓励使用相对便宜的通过计算得到数据的行为,而这往往需要更长的运行时间。

这种昂贵的存储方式还有可能会鼓励用户通过交易发送数据。这或许是我们最不愿意看到的情况。从区块链协议层角度看来,合约数据的数据库对于整个区块链来说是免费的存储空间。除了Qtum的SPV或是以太坊的“fast”同步方式外,区块链本身并不会关心这个数据库的实际大小或是需要同步的时间。这个数据库最终会压缩为256位hash值,称为stateRootHash。我们可以验证,实际上整个区块链最消耗资源的并不是这个数据库,而是所有附加于交易中的数据。出于安全性考虑,这部分交易数据无法被丢弃或忽略。

对于轻钱包或者是Qtum的节点来说,将数据存储在账户bytecode中的方式对所有用户的体验有非常负面的影响。虽然对于单个用户来说获取和验证数据非常简单,但如果合约数据同时被多个账户调用,将有可能导致很严重的延时。如果不下载所有的合约bytecode,用户无法立刻获取和验证数据。因此用户必须先运行合约,然后回到网络中,获取另一个账户信息并验证,诸如此类繁琐的过程之后,才能真正执行目标合约。如果只有一个这样的合约数据,总体影响或许不大,但由于这种方式获取数据的开销非常小,而且采用多重调用合约的方式并没有什么明显的弊端(除了创建时需要固定的32,000gas手续费之外),所以实际上在达到一定条件后,用户会大量的采用这种方式进行存储,特别是针对那些半易失性数据。这样一来,延时的问题就会越来越严重了。

因此,以太坊设置高昂的存储代价的初衷是为了更好地和轻钱包进行交互。然而,合约开发者为了减小经济开销可以有不同的方式绕过这一限制,最终反而造成了比常规存储更加不利的结果,对轻钱包的不利影响尤其大。

运算开销

接下来我们以太坊gas模型中比较容易被忽视的运算开销。几乎所有运算操作的opcode都要消耗一些gas。这些开销比起数据存储或是直接的资源访问来说,看起来并不是很多。但当在EVM上实现某些基本操作需要各种opcode的组合时,整个操作的整体开销增长会非常快。这里举一个我个人最喜欢的关于字符串比较的智能合约的例子:

contract GasTest{
    using strings for *;
    function StringUtilsEqual(string tmp1, string tmp2) constant returns (bool){
        return StringUtils.equal(tmp1, tmp2);
    }
    function SliceEquals(string tmp1, string tmp2) constant returns (bool){
        return tmp1.toSlice().equals(tmp2.toSlice());
    }
    function NaiveEquals(string tmp1, string tmp2) constant returns (bool){
        if(bytes(tmp1).length != bytes(tmp2).length) return false;
        uint256 max=bytes(tmp1).length;
        for(uint256 i=0;i<max;i++){ 
            if(bytes(tmp1)[i] != bytes(tmp2)[i]){
                return false;
            }
        }
        return true;
    }
    function Sha3Equals(string tmp1, string tmp2) constant returns (bool){
        return sha3(tmp1) == sha3(tmp2);
    }
}

这个例子比较了一下四种字符串比较方法的开销:

  • 调用StringUtils库
  • 调用StringSlice库
  • 采用原生循环字符比较的方式比较字符串
  • 对字符串进行SHA3运算,并比较hash值是否相等

一般来说,在普通的计算机上最耗时的方法应该是SHA3运算。SHA3运算非常便宜,但即使是调用优化的非常好的库并采用最快的机器运行,其对100字节数据进行一次hash运算的时间也需要0.028秒,也就是28ms。而进行两次hash操作则需要将近64ms。从gas开销来看,采用传统的字符串比较方法显然会比较便宜。

66

接下来大家一定以为我要吐槽以太坊的gas调度是多么不合理,然而我想说的是,问题不仅仅在于这些gas值的设置,而在于整个gas模型本身。当然,我也不会装作自己已经有一个完美的解决方案,因为这确实是一个非常复杂的问题。

如果你曾经阅读过Intel的用户手册,其中会有很多关于在x86处理器上各种opcode操作的时间和性能等方面的描述,并且会对实际性能和时间进行测量比较等。之所以需比较理论性能和实际性能,主要原因是现代的处理器都是一些极度复杂且高度优化的半导体器件结合的产物。这当中应用了诸如多管道,多级缓存,分支预测,寄存器集合等众多技术。这些技术的引入,使得某项需要耗时1ms的操作,循环100次只需要2ms,而不是100ms。然而,这些优化在区块链的虚拟机上则很难实现。gas模型需要兼容不同的处理器,包括不同架构的处理器,不同位宽的处理器等等。

尽管这个问题很难有完美的解决方案,但我个人对于改善这些问题有不少新的想法,我在设计Qtum的x86虚拟机的gas模型过程中将会重点研究以下几个问题。(Qtum x86虚拟机预计将在2018年和大家见面!)

丰富多样的标准库,且可以预测开销

提供丰富的标准库,并且设置合理的gas价格或许是一个快速有效的方法,并且不会有太大风险。这个标准库并不需要在虚拟机上运行,当然,如果虚拟机本身足够高效的话,在虚拟机上运行也不会有什么坏处。重要的是调用标准库的gas消耗应该被设定为一个合理的值。这样一来,类似分支预测和多级缓存的技术就可以计算出操作所需的gas价格。以太坊在标准库方面只实现了非常非常少的一部分,比如SHA3.SHA3操作的基本开销是30gas,之后每个32字节的数据hash需要3gas。内置的StringEqual操作需要5 gas的基本开销,随后比较每个字节需要1 gas。

这种方法在以太坊中最大的问题在于,必须调用整个预编译好的智能合约或是增加新的EVM opcode。而我设想的x86虚拟机则可以调用原生的x86代码,并且可以检测到这块特殊的存储区域。当然这还仅仅处于设想阶段。增加或修改这些操作可能会引起分叉,而Qtum在防止分叉方面有很大优势,就是Qtum的分布式自治协议,该技术可以比较容易地修改库函数基本开销等参数。然而,在这种特殊的gas消耗模型中增加新函数的问题还需要进一步解决。

计算分支和内存开销,取代opcode开销

从分支和内存的角度计算开销,而不是原来的根据opcode计算开销。这一点的主要原因是,目前大部分的opcode操作都非常快,时间上没有太大的区别,操作本身开销非常低。因此,更明显的开销其实主要是分支和内存分配方面。对于现代处理器来说,最昂贵的操作并不是opcode,而是缓存miss以及分支预测错误。因此,这个新模型应该在这些方面进行计费。这样做可以极大地鼓励更加高效的循环展开(loop-unrolling)。当过多的采用循环展开的话,有可能会影响缓存性能,但未展开的额外的存储开销可能会比这个问题更严重。

这个方法的可行性还需要更深入的研究,并且一些边缘用例也需要考虑。这种方法最大好处在于所有操作的gas消耗都可以比较准确预测。仅仅这一条可能并不是一个完整的解决方案,但结合其他的gas预测方案可以为智能合约开发者提供极大地便利。

在gas模型中引入宏操作

现代的编译器往往会生成许多相同的代码,这样一来,除了在gas模型中引入原始opcode外,引入宏操作或许会带来不少好处。当然对这些宏操作的定价需要非常谨慎,否则有可能导致DoS攻击。

存在的问题

有些时候,在区块链共识系统中加入越来越多的计算资源消耗的代码有可能导致你最终花在预测的时间远大于你真正运行合约的时间。这对于gas模型来说是最困难的一个问题。我们往往即希望共识机制在不同实现中保持一致,又希望避免攻击,并且保持对不同虚拟机的兼容性。这些复杂问题糅杂在一起,就成了这个问题最复杂的部分。这也是为什么至今它还是一个开放性问题的原因。对于图灵完备的系统来说,完全预测一个只能合约的开销是不可能的,除非你完全运行这个合约。我们最多能做的就是测量一些特殊代码段的开销,这样我们至少可以部分预测其开销。这对于x86虚拟机智能合约来说其实是一个软肋。在x86虚拟机中,代码也是数据,随时可能会被改变或重写。所以在最终的实现中,比较有可能的方式是采用混合gas模型,即对于内部常量opcode采用更加高效的gas模型,而对于动态加载和运行的代码则采用更加保守的模型。

小结

Gas经济模型是一个非常复杂问题,目前还没有一个完美的解决方法。相信在整个区块链行业开发者的共同努力下,我们最终能攻克这个难题。

附录

gas开销的计算方法如下:

memory(size) = 3 * (size/32) + (size/32) * (size/32) / 512 

# Gas costs of loading existing data in EVM (assuming memory is already allocated)
(not accounting for memory costs to store the data in)
## external code (semi-volatile)
200 + 700 + (3 * size)
sload + extcodecopy + (copy * size)
## internal code (constant)
3 * size
copy * size
## storage (mutable)
200 * size
sload * size
## Transaction (mutable)
200 + (68 * (size * 32)) + 30 + (6 * size) ## note: zero data is only 4 gas
sload + (txnonzeroByte * (size * 32)) + sha3 + (sha3word * size)

# Gas costs of storing data initially to EVM
(not accounting for memory costs data is initially stored in)
## external code (semi-volatile)
32000 + 20000 + (200 * size * 32) + memory(size) + 10
create + sstore + (createByte * (size * 32)) + memory(size) + overhead
## internal code (constant)
(200 * (size * 32))
(createByte * (size * 32))
## storage (mutable)
20000 * size
sstore * size
## Transaction (mutable)
20000 + 30 + (6 * size) ## note: zero data is only 4 gas
sstore + sha3 + (sha3word * size)

# Gas costs of updating data in EVM
(not accounting for memory costs of updated data to store)
## external code (semi-volatile)
32000 + 5000 + (200 * size * 32) + memory(size) + 10
create + sstoreReset + (createByte * (size * 32)) + memory(size) + overhead
## internal code (constant)
N/A (technically could be possible, but would greatly complicated contract design)
## storage (mutable)
5000 * size
sstoreReset * size
## Transaction (mutable)
5000 + 30 + (6 * size) ## note: zero data is only 4 gas
sstoreReset + sha3 + (sha3word * size)

Test contract bytecode including libraries etc:

6060604052341561000f57600080fd5b610f488061001e6000396000f30060606040523615610081576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633a96fdd71461008657806346bdca9a1461013a5780635d62fdf5146101f2578063674ff51b146102aa57806388ee0b2a146103625780638a0807b71461041a578063ef5a8374146104ce575b600080fd5b341561009157600080fd5b610124600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610586565b6040518082815260200191505060405180910390f35b341561014557600080fd5b6101d8600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061084a565b604051808215151515815260200191505060405180910390f35b34156101fd57600080fd5b610290600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610860565b604051808215151515815260200191505060405180910390f35b34156102b557600080fd5b610348600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610874565b604051808215151515815260200191505060405180910390f35b341561036d57600080fd5b610400600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506108a1565b604051808215151515815260200191505060405180910390f35b341561042557600080fd5b6104b8600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506109eb565b6040518082815260200191505060405180910390f35b34156104d957600080fd5b61056c600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610d17565b604051808215151515815260200191505060405180910390f35b6000610590610eee565b610598610eee565b6000808693508592508351915081835110156105b357825191505b600090505b818110156107f65782818151811015156105ce57fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916848281518110151561064957fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191610156106e4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450610840565b82818151811015156106f257fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916848281518110151561076d57fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191611156107e95760019450610840565b80806001019150506105b8565b825184511015610828577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450610840565b82518451111561083b5760019450610840565b600094505b5050505092915050565b6000806108578484610586565b14905092915050565b600061086c838361084a565b905092915050565b600061089961088283610df0565b61088b85610df0565b610e1e90919063ffffffff16565b905092915050565b6000806000835185511415156108ba57600092506109e3565b84519150600090505b818110156109de5783818151811015156108d957fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916858281518110151561095457fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415156109d157600092506109e3565b80806001019150506108c3565b600192505b505092915050565b60006109f5610eee565b6109fd610eee565b600080869350859250600184511080610a17575060018351105b80610a23575083518351115b15610a50577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450610d0d565b6fffffffffffffffffffffffffffffffff84511115610a91577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450610d0d565b60009150600090505b8351811015610ce957826000815181101515610ab257fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168482815181101515610b2d57fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415610cdc57600191505b825182108015610bb757508351828201105b8015610cb857508282815181101515610bcc57fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191684838301815181101515610c4957fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b15610cca578180600101925050610ba5565b8251821415610cdb57809450610d0d565b5b8080600101915050610a9a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b5050505092915050565b6000816040518082805190602001908083835b602083101515610d4f5780518252602082019150602081019050602083039250610d2a565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916836040518082805190602001908083835b602083101515610db65780518252602082019150602081019050602083039250610d91565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191614905092915050565b610df8610f02565b600060208301905060408051908101604052808451815260200182815250915050919050565b600080610e2b8484610e34565b14905092915050565b60008060008060008060008060008a6000015197508a600001518a600001511015610e6157896000015197505b8a60200151965089602001519550600094505b87851015610ed25786519350855192508284141515610ebb57600185896020030160080260020a03199150818316828516039050600081141515610eba57809850610ee0565b5b602087019650602086019550602085019450610e74565b89600001518b600001510398505b505050505050505092915050565b602060405190810160405280600081525090565b6040805190810160405280600081526020016000815250905600a165627a7a72305820fcb471f7522e763de8c06e6eba885f81d32bc8128e364dc46a35fd7e6c960bb50029
Raw string gas measurements:

Gas used for string "x" 1
StringUtilsEqual 23884
SliceEquals 23928
NaiveEquals 23433
Sha3Equals 23708

Gas used for string "foobar123" 9
StringUtilsEqual 28268
SliceEquals 24952
NaiveEquals 26385
Sha3Equals 24732

Gas used for string "foobarxxx124529898453ifdf" 25
StringUtilsEqual 37036
SliceEquals 27000
NaiveEquals 32289
Sha3Equals 26780

Gas used for string "jhkfdfdsfsdgfdsgdsffgfdsgrsehsfrhfrdggewrrtrewgfdsaffvdsfgdsf" 61
StringUtilsEqual 57032
SliceEquals 32006
NaiveEquals 45841
Sha3Equals 31859

Gas used for string "hjfhjdskhjglkhdsjlkghjlkdshjkglhjdsfkhjglfdsjhgjhldsfhgjlkdsfhjkghjfdsklhgjlkfdsjhgfdsgfdshgfds" 95
StringUtilsEqual 75932
SliceEquals 36756
NaiveEquals 58655
Sha3Equals 36682

Gas used for string "hgjkghjdskhgjlserijhlktjiurewihuygtiuserhjkgfhfdsjkvgfhjklfdshjghfdsjghufdshgjlkfdshjlkghfdsjlkghjlkfdshgjlkhfdseulkghfdslkjhgulkijfdshg" 136
StringUtilsEqual 98936
SliceEquals 42801
NaiveEquals 74320
Sha3Equals 42872

发文时比特币价格 ¥35845
稿源:巴比特资讯( http://www.8btc.com/feesgas-qtum-1013) 版权声明: by nc" sa 作者保留权利。文章为作者独立观点,不代表巴比特立场。

评论:2

您需要登录后才可以回复 登录|注册
    Author Image
    gohomeman 8 天前

    作者是Qtum的核心开发人员么
    这个看不懂呢

    +1
    +1
    我要点评
    ICO观察员
    ICO观察员 8 天前

    #今日早报#如何理解区块链上的经济模型(FeesGas)_巴比特_服务于区块链创新者http://t.cn/RONjqxH ​

    +1
    +1
    我要点评