筹备
随着区块链技术的逐步推广,区块链平安也逐步成为钻研的热点。在其中,又以智能智能合约平安最为突出。Ethernaut正是入门钻研区块链智能合约平安的好工具。
- 首先,应确保装置Metamask,如果能够应用Google Extension能够间接装置,否则能够应用FireFox装置
- 新建账号,并连贯到RinkeBy Test Network(须要在Setting - Advanced里启用Show test networks,并在网络中进行切换)
- 拜访Faucet并获取测试币,每天都有0.1Eth的额度
当初就能够开始Ethernaut的探索之旅了!
0. Hello Ethernaut
本节比较简单,所以我将更关注整体过程,介绍Ethernaut的实例创立等等,本人也梳理一下,所以会更具体一些。
筹备工作
进入Hello Ethernaut,会主动提醒连贯Metamask钱包,连贯后,示意图如下:
按F12关上开发者工具,在console界面就能够进行智能合约的交互。
创立实例并剖析
单击 Get New Instance 以创立新的合约实例。
能够看出咱们实际上是通过与合约0xD991431D8b033ddCb84dAD257f4821E9d5b38C33
交互以创立实例。在辅导参数中,调用0xdfc86b17
办法,附带地址为0x4e73b858fd5d7a5fc1c3455061de52a53f35d966
作为参数。实际上,所有关卡创立实例时都会向0xD991431D8b033ddCb84dAD257f4821E9d5b38C33
,附带的地址则是用来表明所处的关卡,如本例URL地址也为https://ethernaut.openzeppelin.com/level/0x4E73b858fD5D7A5fc1c3455061dE52a53F35d966
。
实例曾经胜利生成,主合约交易截图如下:
进入交易详情,查看外部交易,发现合约之间产生了调用。第一笔是由主合约调用关卡合约,第二笔是由关卡合约创立合约实例,其中实例地址为0x87DeA53b8cbF340FAa77C833B92612F49fE3B822
。
回到页面来看,能够确认生成实例确实为0x87DeA53b8cbF340FAa77C833B92612F49fE3B822
上面咱们将进行合约的交互以实现本关卡。
合约交互
此时,在console界面能够通过player
和contract
别离查看用户以后账户和被创立合约实例。player
代表用户钱包账户地址,而contract
则蕴含合约实例abi
、address
、以及办法信息。
依照提醒要求输出await contract.info()
,失去后果'You will find what you need in info1().'
。
输出await contract.info1()
,失去后果'Try info2(), but with "hello" as a parameter.'
。
输出await contract.info2('hello')
,失去后果'The property infoNum holds the number of the next info method to call.
。
输出await contract.infoNum()
,失去infoNum参数值为42
(Word中的首位)。这就是下一步要调用的函数(info42
)。
输出await contract.info42()
,失去后果'theMethodName is the name of the next method.
,即下一步该当调用theMethodName
。
输出await contract.theMethodName()
,失去后果'The method name is method7123949.
。
输出await contract.method7123949()
,失去后果'If you know the password, submit it to authenticate().
。
所以通过password()
能够获取明码ethernaut0
,并将其提交到authenticate(string)
。
留神当在进行authenticate()
函数时,Metamask会弹出交易确认,这是因为该函数扭转了合约外部的状态(以实现对关卡胜利的查看工作),而其余先前调用的函数却没有(为View)。
此时,本关卡曾经实现。能够抉择Sumbit Instance进行提交,同样要签名实现交易
在此之后,Console页面弹出胜利提醒,本关卡实现!
总结
本题比较简单,更多的是要相熟ethernaut的操作和原理。
1. Fallback
创立实例并剖析
依据先前的步骤,创立合约实例,其合约地址为0xe0D053252d87F16F7f080E545ef2F3C157EA8d0E
。
本关卡要求取得合约的所有权并清空余额。
察看其源代码,找到合约所有权变更的入口。找到两个,别离是contribute()
及receive()
,其代码如下:
function contribute() public payable { require(msg.value < 0.001 ether); contributions[msg.sender] += msg.value; if(contributions[msg.sender] > contributions[owner]) { owner = msg.sender; } } receive() external payable { require(msg.value > 0 && contributions[msg.sender] > 0); owner = msg.sender; }
依照contribute()
的逻辑,当用户随调用发送小于0.001 ether
,其总奉献额超过了owner
,即可取得合约的所有权。这个过程看似简略,然而通过以下constructor()函数能够看出,在创立时,owner
的创立额为1000 ether
,所以这种办法不是很实用。
constructor() public { owner = msg.sender; contributions[msg.sender] = 1000 * (1 ether); }
再思考receive()
函数,依据其逻辑,当用户发送任意ether
,且在此之前已有奉献(已调用过contribute()
函数),即可取得合约所有权。receive()
相似于fallback()
,当用户发送代币但没有指定函数对应时(如sendTransaction()
),会调用该办法。
在获取所有权后,再调用withdraw
函数既能够清空合约余额。
合约交互
应用contract
命令,查看合约abi及对外函数状况。
调用await contract.contribute({value:1})
,向合约发送1单位Wei。
此时,调用await contract.getContribution()
查看用户奉献,发现贡献度为1,满足调用receiver()
默认函数的最低要求。
应用await contract.sendTransaction({value:1})
结构转账交易发送给合约,
调用await contract.owner() === player
确认合约所有者曾经变更。
最初调用await contract.withdraw()
取出余额。
提交实例,显示关卡胜利!
总结
本关卡也算比较简单,次要须要剖析代码外部的逻辑,了解fallback()
及receive
的原理。