流行的区块链游戏确实是一个庞氏骗局。区块链奥运会潜在的危机是什么?
流行的区块链游戏确实是一个庞氏骗局。区块链奥运会潜在的危机是什么?如果你在2017年开始关注以太坊区块链,你应该知道早期的智能合约有庞氏骗局。就像传统的庞氏骗局。这些游戏旨在吸引玩家加入,以便让游戏继续下去。虽然这些合同会戛然而止,但有些人会发现是因为其他原因而结束的。本文列举了对此类契约可能的攻击。
攻击#1:异常障碍
当攻击者利用合同中的漏洞返回异常错误时,就会发生异常障碍攻击。。当合同能够';t成功调用address.send()或address.call.value()之类的函数。错误本身不会被标记,胖契约引导它这样做;异常错误不会自动生成。
攻击示例
2016年2月6日,KotET游戏智能合约部署完成。在KotET游戏中,玩家需要向契约中发送一些飘渺的硬币来获得"王座"。只要拿到皇位,玩家就会被加进朝廷。,并被永远记录在区块链上。更重要的是,后来的国王有权得到新国王';以太坊。随着国外数量的增加,成为王者的代价会越来越贵。如果14天过去了,没有新的继任者,那么王位将被重置,游戏将重新开始。。这个游戏的理想是新的外国要支付一定的费用才能获得王位,同时不断有新人继续玩这个游戏,这就导致了庞氏陷阱。
代码示例
以下是初始KotET契约代码的简化版本。。注意,当玩家向契约发送msg.value时,会触发返回功能。返回函数将首先检查国王是否发出了足够的以太坊来获得王位。否则,需求将被丢弃,代码将返回。如果有足够的以太币。那么现任国王将获得足够的补偿(认购价减去服务费),发行资金的人将成为新国王。然后,计算新的地王价格。
合同KotET{
地址公王;
uint公开索赔价格=100;
地址所有者;
//constructor,assigningownership
constructor(){
owner=msg.sender;
king=msg.sender;
[XY001]}[XY002][XY001]//Forthecontractortowithdrawthecommission[XY002]函数佣金(单位金额){
所有者。发送(金额);
}
//回退函数
function(){
if(msg。valueclaimPrice)还原;
uint补偿=计算补偿();king.send(补偿);
king=msg。发件人;
claimPrice=calculatenewprice();
}
}
kotetcontract的漏洞在于它使用了address.send(),当调用不成功时,它可以';t检查异常错误。如前所述。,地址。发送()和地址。转移()都受限于2300的油费。虽然这对于防止再入攻击很有用,但气体燃料的限制会导致向国王发送资金的失败';的地址。如果国王';的合同有退款功能,它将花费2300多气体燃料。这就是KotET的情况。付给国王的钱将被送到&";合同钱包"以太坊而不是"合同账户",这需要更多的气体燃料来完成转移。。最后的结果是转移不成功,以太坊还给国王';的账户,所以新国王可以';不加冕,所以契约永远卡。
解决方案
KotET可以通过以下两种方式解决问题:
1。丢弃异常,调用将继续——我们可以通过在函数中添加revert来实现这一点。这将防止合同停止,但也需要额外的步骤来启动支付转移。有两种方案。一种是让用户自己发出多次支付转账(过于集中)。二是实行批量支付,保证支付到"一等奖"。
2。用提现代替直接发送调用,可以结构化合约,然后玩家可以让自己提现失败,代替合约中的剩余资金。。现金提取算法的唯一缺点是它不是自动的,需要大量的用户交互。让';让我们看看如何更新合同来实现这些变化。
合同KotET{
addressmaleking;
uintpublicclaimprice=100;
CorporatePublicResolutionFund
地址所有者;
映射(地址=
uint)贷记资金;
//constructor,assigningownership
constructor(){
owner=msg.sender;
king=msg.sender;
[XY001]}[XY002][XY001]//Forthecontractortowithdrawthecommission[XY002]函数佣金(单位金额){
所有者。发送(金额);
}
//Usedtoallocatethenewkingandcreditbalance
函数becomeKing()公共应付款返回(bool){
if(msg。值
claimPrice){
贷记资金[最富有]=消息。价值;
king=msg。发件人;
返回true
}else{
返回false
}
}
函数撤回()公共{
uintamount=creditedfunds[msg.sender];
//Clearthebalancebeforesendingthecreditedfunds.
//防止重入攻击
等待撤销。发件人]=0;
msg.sender.transfer(金额);
}
}现在契约不再依赖返回函数来执行新外国的加冕,可以直接给下一任国王发资金。。该契约现在是安全的,可以抵御任何可能攻击该契约的后退/再入攻击。
攻击#2:调用栈攻击
在使用EIP150之前,以太坊虚拟机的调用栈深度是1024。也就是说,有人可以调用1023次合同,然后自动使用第1024次调用。攻击者最终会到达第1023个契约,导致下一次调用失败,让他们自己窃取契约的资金,控制契约。
攻击示例
类似于KotET等庞氏游戏,用户会向合约发送飘渺币来加入游戏。每轮游戏的获胜者可以获得奖池的金额。游戏规则如下:
你必须至少发送1个ETH到契约,然后你会得到10%的利息。
如果"政府"(合约)12小时内没有收到新的资金,最后一个人将赢得所有奖池,所有人都将失去自己的资金。
送合约的以太坊分配如下:奖池5%,合约主5%。按照支付顺序,90%支付给发送资金的人
当奖池满(10000等)时,95%的资金发送给支付人。[XY002][XY001]奖励:付款人可以使用推荐链接邀请其他人。。如果朋友为这个合同付费,邀请人可以获得5%,5%给合同所有人,5%进入奖池,剩下的85%用于支付利息。
合约的书写需要保证用户及其资金记录在两个数组中。,address[]publiccredaddr和int[]publiccredAmt。这两个数组会在游戏结束时重置。政府已经非常成功,因为阵列已经变得非常大。,需要清除他们的燃料费用已经超过了每次转移的限制。最后的结局是奖池永久冻结,共1100以太币左右。最后,过了两个月,资金终于解冻,发给了打电话的人。
政府虽然没有受到恶意用户的攻击,但也是一个很好的例子。这种灾难将由调用栈攻击引起。这也表明,在处理大型数据库时,您需要格外小心。
代码
以下是政府智能合同的完整代码,其中也包含短变量。我已经完整地包含了真实的合同,因为通过逐行检查合同,我可以学到很多东西,包括合同是如何构造的。。有些人可以看到函数lendGovernmentMoney(),它代表资金发送方的地址,以及需要重置或添加到现有数据中的以太坊数量。应该注意的是,在相同的功能中12小时结束时,合同所有者和最后一个发行者之间的资金是如何分配的?,credAddr[credAddr.length1].发送(profitfromrash);以及腐败的生活。发(这个。平衡)。
契约政府{
//全局变量
uint32publiclastpayed;
uintpubliclastimeofnewcredit;
unit公众从崩溃中获利;
address[]公共信用地址;
单位[]公共信用;
处理公共腐败;
map(address=
uint)friends;
uintconstanttwelvehours=43200;
uint8公共回合;
//constructor
constructor(){
profitfromrash=msg。价值;
损坏lite=msg。发件人;
lastimeofnewcredit=block。时间戳;
}
函数lendGovernmentMoney(地址好友)返回(bool){
uintamount=msg。价值;
//Checkwhetherthesystemhascrashed.
//Ifthereisnonewcreditorwithin12hours,
//Thesystemwillstoprunning.Average
//12h=60*60*12/12.5=3456
.if(lastimeofnewcredit十二小时块。时间戳)
//向发送方退款
msg.sender.send(amount);
//Sendallcontractmoneytothelastcreditor
信用地址。长度-1].发送(profitfromrash);
瓦楞原纸。发(这个。平衡);
//重置合同状态
lastpayed=0;
lastimeofnewcredit=block。时间戳;
profitfromrash=0;
//Thisiswherethearrayiscleared
credAddr=新地址[](0);
信用金额=新单位[](0);
round=1;
返回错误的
}
else{
//Thesystemneedstobecollectedat
.//If(amount
=10**18){
,getatleast1%profitfromthecollapsetosurvive.
//Thesystemhasreceivednewmoney,
//Itcansurviveforatleast12hours
.lastimeofnewcredit=block。时间戳;
//注册新的债权人及其
//利率10%的金额
credaddr。推(msg。发件人);
credAmt.push(amount*110/100);
//Nowthemoneyhasbeensent
//First,thecorruptelitetakes5%-thieves!
corruptelite.send(amount*5/100);
//5%willentertheeconomy(theywillincrease
//Valueofpeoplewhoseethecollapsecoming]
If(profitfromcrash10000*10**18)
profitfromrash=金额*5/100;
}
//Ifyouhaveabuddyinthegovernment(heis
//inthecreditorlistcanget5%ofyour
//creditor'srights.Makeadealwithhim.
if(buddies[buddy]
=amount){
buddy。发送(金额*5/100);
}
Friend[msg.sender]=amount*110/100;
//90%ofthefundsareusedtorepaytheoldcreditors.
if(信用金额[上次付款人]
信用地址[上次付款人]).发送(信用金额[上次付款]);
好友[信用地址[上次付款]]-=信用金额[上次付款];
lastpayed=1;
}
返回真实的
}
else{
msg。发件人。发送(金额);
返回false
}
}
}
//fallbackfunction
function(){
lendgovermentmoney(0);
}
函数债务总额()返回(uint债务){
for(uintI=最后支付;我
债务=信用额[I];
}
}
函数totalPayedOut()返回(uint支出){
for(uintI=0;我
payment=creditamount[i];
}
}
//direction'Government'
DonationfunctioninvestInTheSystem(){
profitfromscrash=msg。价值;
}
//Thecorruptelite
//Fromtimetotime,thepowerispassedontothenextgeneration
函数inheritToNextGeneration(地址下一代){
if(消息。sender==corruptElite){
corruptElite=下一代;
}
}
函数getCreditorAddresses()returns(address[]){
returncredAddr;
}
函数getCreditorAmounts()returns(uint[]){
返回信用金额;
}
}
让';假设攻击者编写了下面的契约来恶意攻击契约政府{}。合同攻击政府{
functionattackGov(addresstarget,uintcount){
if(0
.这个。攻击政府燃气(燃气左()-2000)(目标,计数1);
}
else{
attackGov(target)LendOvernmentmoney
}
}
Theattackercalledthecontractattackgovernment{}function.,直到堆栈的大小为1023。当堆栈达到1022时,将在第1023个堆栈上执行1022.lendGovernmentMoney()函数。因为第1024次呼叫失败了。而send()函数不会检查返回的代码,而credaddr[credaddr。长度-1]。发送(profitfromcrash)合同的代码也将无效。合同到期后会重置。下一轮已经开始了。因为支付失败,合同现在会得到上一轮的奖池,下一轮结束后,合同主会得到所有资金,corruptlite.send(this.balance)。
解决方案
那么如何才能避免全栈攻击呢?好在EIP150标准已经更新,堆栈调用深度几乎不可能达到1024。按照规则,分呼不能花掉主呼63/64的油费。。为了达到堆栈调用的极限,攻击者需要花费难以想象的开销,所以很少有人会这么做。
另一方面,对于大量数据的处理方法包括:
写合同时,数据清理工作要分散在多次传输中。,而不是专注于一个,或者
通过使用户能够独立地处理数据集来编写合同。
攻击#3——不可撤销的经理缺陷
是什么让智能合约如此特殊?它们是不可改变的。。是什么创造了智能合约的噩梦?它们是不可改变的。现在,很遗憾的是,在编写智能合同时,经常会出现错误。在激活合同之前,有必要审查整体功能、参数和合同结构。
如果在以太坊的历史上,有一个因为整体架构问题最终失败的智能合约,毫无疑问就是Rubixi。Rubixi是另一种庞氏游戏,玩家需要将以太币送入合约中,获得更多以太币。但是Rubixi开发过程中,车主随意更改合同名称,但查车没有不一致。毫无疑问,卢比西远远称不上"成功>;
攻击示例
由于Solidityv0.4.24算法,契约的管理器函数是construct()。但是,在创建Rubixi契约时,管理器功能是由以太坊虚拟机和同名契约共享的。。Rubixi的问题是,当契约中部署的管理器被命名为functionDynamicPyramid()而不是functionRubixi()时,这意味着卢比西';的原名是"动态金字塔"。由于这种不一致,在创建合同时没有指定所有者,所以城堡的钥匙被拿走了。任何人都可以将自己定义为合同的所有者。,然后获得参与者加入的签约费。
代码示例
如果我们取出合约代码的前几行,你会发现合约名称和指定的经理职能之间的区别。
合同约定
//Declarethatthestoragevariableuintprivatebalance=0;iscrucialtothecontract
;
uint私人收取的费用=0;
uint私人费用百分比=10;
uint私有金字塔乘数=300;
单位私人付款人=0;
privatecreatorofaddress;
//Setthecreator
functionDynamicPyramid(){
creator=msg.sender;
}
现在你应该明白了,攻击者需要做的是创建一个名为functionDynamicPyramid()的契约,然后获得所有权。。然后,攻击者可以集体调用函数Feeds()并提取现金。虽然这种攻击很直接,但是Rubixi就是一个很好的例子,告诉我们一定要彻底检查合同。
合同提取卢比Xi{
地址所有者;
Rubixir=Rubixi(0xe82.);
构造函数()public{
owner=msg。发件人;
}
函数setAndGrab()public{
r.动态金字塔();
r.collectallfees();
}
}
解决方案
很幸运地是Solidity语言已经更新,因此管理器函数被定义为constructor()而不是contractName()。我们从中可以学到的是多次检查我们的合同代码,并确保你在整个开发过程中,以保持一致性。没有什么比部署一个不可变更的契约,发现它有问题更糟糕的了。
以上是流行的区块链游戏确实是一个庞氏骗局。区块链奥运会的潜在危机是什么?对…的详细介绍庞氏区块链游戏可能已经成为过去,但乔治桑塔亚纳曾经说过那些能';不吸取历史教训就会重蹈覆辙。。"通过从KotET,GovernMental和Rubixi等错误中吸取教训,我们可以防止自己在错误的道路上越走越远。