筹备

随着区块链技术的逐步推广,区块链平安也逐步成为钻研的热点。在其中,又以智能智能合约平安最为突出。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界面能够通过playercontract别离查看用户以后账户和被创立合约实例。player代表用户钱包账户地址,而contract则蕴含合约实例abiaddress、以及办法信息。


依照提醒要求输出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的原理。