简体   繁体   English

在 Solidity 中,`msg.sender` 优于 `tx.origin`

[英]`msg.sender` preferred over `tx.origin` in Solidity

I am new to solidity but I have read at many times that tx.origin should be avoided & msg.sender should be used.我是刚接触 Solidity 的新手,但我多次读过应该避免使用tx.origin并且应该使用msg.sender There is a given demo on this in solidity page .在solidity page中有一个给定的演示。 It says like:-它说: -

Never use tx.origin for authorization.切勿使用 tx.origin 进行授权。 Let's say you have a wallet contract like this:假设你有一个这样的钱包合约:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract TxUserWallet {
    address owner;

    constructor() {
        owner = msg.sender;
    }

    function transferTo(address payable dest, uint amount) public {
        // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin
        require(tx.origin == owner);
        dest.transfer(amount);    // .transfer is a global variable
    }
}

Now someone tricks you into sending Ether to the address of this attack wallet:现在有人欺骗你将以太币发送到这个攻击钱包的地址:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
interface TxUserWallet {
    function transferTo(address payable dest, uint amount) external;
}

contract TxAttackWallet {
    address payable owner;

    constructor() {
        owner = payable(msg.sender);
    }

    receive() external payable {
        TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);   //  **LINE 1**
    }
}

Now, I want to know that how line 1 will drain entire funds from TxUserWallet .现在,我想知道第 1 行将如何从TxUserWallet耗尽全部资金。 I think transfer() is a global variable which will just transfer the amount to address in dest which is of attack wallet.我认为transfer()是一个全局变量,它只会将金额转移到攻击钱包的dest中的地址。 How .transfer() will trigger receive() function of contract TxAttackWallet . .transfer()将如何触发合约TxAttackWalletreceive() function。 Secondly, In line TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);其次,在行TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance); , why are we writing it as TxUserWallet(msg.sender) , like why are we adding (msg.sender) after contract name and also what values is being passed by writing msg.sender.balance ? ,为什么我们把它写成TxUserWallet(msg.sender) ,就像我们为什么在合约名称之后添加 (msg.sender) 以及通过写msg.sender.balance传递什么值?

you should avoid using tx.origin to prevent the Reentrancy attack.你应该避免使用tx.origin来防止重入攻击。 solidity re-entrancy attack explanation solidity 重入攻击解释

Basically, the attacker is withdrawing money from the contract recursively till the balance is 0. So there is a chain of function calls.基本上,攻击者递归地从合约中取钱,直到余额为 0。所以有一个 function 调用链。 A reentrancy attack involves two smart contracts.重入攻击涉及两个智能合约。 A vulnerable contract and an untrusted attacker's contract.易受攻击的合约和不受信任的攻击者的合约。 ( fallback function is called under multiple conditions and this makes writing code for each condition hard. That is why receive is added. receive is called only contract receives ether so that fallback should be executed only if a function signature is not implemented within a contract.) fallback function 在多个条件下被调用,这使得为每个条件编写代码变得困难。这就是添加receive的原因。 receive仅被称为合约接收以太,因此只有在合约中未实现 function 签名时才应执行后备。 )

在此处输入图像描述

msg.sender is the last caller while tx.origin is the original caller of those recursive function chain. msg.sender是最后一个调用者,而tx.origin是那些递归 function 链的原始调用者。

funcA => funcB => funcC函数A => 函数B => 函数C

msg.sender is the caller of funcC whereas tx.origin is the caller of funcA msg.senderfuncC的调用者,而tx.originfuncA的调用者

Now to prevent the Reentrancy attack we have to make sure that there is no chain of function calls.现在为了防止重入攻击,我们必须确保没有 function 调用链。

require(tx.origin == msg.sender)

In your question, story is a little different.在你的问题中,故事有点不同。 Imagine the workflow.想象一下工作流程。 You are saying你是说

Now someone tricks you into sending Ether to the address of this attack wallet:现在有人欺骗你将以太币发送到这个攻击钱包的地址:

You are sending money to TxAttackWallet , so you are initiating the function chains.您正在向TxAttackWallet ,因此您正在启动 function 链。 tx.origin is you, the owner, tx.origin == owner . tx.origin是您,所有者, tx.origin == owner Once you send money to TxAttackWallet , automatically receive of TxAttackWallet will be called so this code will be executed recursively.一旦您向TxAttackWallet汇款,将调用TxAttackWallet的自动receive ,因此此代码将递归执行。 You are in a loop你在一个循环中

  TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);

You keep transferring funds from your contract to TxUserWallet .你不断将资金从你的合约转移到TxUserWallet

Who is the tx.origin ?谁是tx.origin It is you because you started this chain of functions so this will always pass:是你,因为你启动了这个函数链,所以它总是会通过:

 require(tx.origin == owner); 

If you were using the msg.sender==owner , msg.sender would be the TxUserWallet so require condition would not pass and no funds would be transferred.如果您使用的是msg.sender==ownermsg.sender将是TxUserWallet因此要求条件不会通过并且不会转移任何资金。

How .transfer() will trigger receive() function of contract TxAttackWallet .transfer()将如何触发合约TxAttackWalletreceive() function

The receive() function is invoked automatically when you're sending ETH to the contract address without specifying the data field.当您在未指定data字段的情况下向合约地址发送 ETH 时,会自动调用receive() function。

It is not possible to specify the data field with the .transfer() function, you'd need to use the low-level .call() for that.无法使用.transfer() function 指定数据字段,您需要为此使用低级 .call .call()

Docs:文件:


why are we writing it as TxUserWallet(msg.sender) , like why are we adding (msg.sender) after contract name and also what values is being passed by writing msg.sender.balance ?为什么我们将它写为TxUserWallet(msg.sender) ,就像我们为什么在合约名称之后添加 (msg.sender) 以及通过编写msg.sender.balance传递的值是什么?

TxUserWallet(msg.sender) is a contract type pointer to the msg.sender address (in your case the TxUserWallet contract address), expecting it to implement all public and external functions of TxUserWallet . TxUserWallet(msg.sender)是指向msg.sender地址(在您的情况下为TxUserWallet合约地址)的合约类型指针,期望它实现TxUserWallet的所有publicexternal功能。

This way, the TxAttackWallet contract expects to be able to call the transferTo() function on msg.sender and expects to receive empty response, as the transferTo() function doesn't return any params.这样, TxAttackWallet合约期望能够在 msg.sender 上调用transferTo() msg.sender并期望收到空响应,因为transferTo() function 不返回任何参数。

msg.sender.balance returns the native balance (ETH on Ethereum, BNB on Binance Smart Chain, ...) of msg.sender , in your case that's the TxUserWallet contract address. msg.sender.balance返回 msg.sender 的本机余额(以太坊上的 ETH,Binance 智能链上的 BNB,...),在您的情况下TxUserWallet msg.sender地址。

Docs:文件:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Solidity 基础知识:“msg.sender”代表什么 - Solidity basics: what "msg.sender" stands for 如何在solidity 0.5.0中将etherenum发送到msg.sender - How to send etherenum to msg.sender in solidity 0.5.0 Solidity:tx.origin == address1 不匹配,即使两个地址相同 - Solidity : tx.origin == address1 does not match, even when both address are same Solidity - 我如何从另一个合约转移到 msg.sender - Solidity - how do i transfer from another contract to msg.sender ParserError:应为“;” 但得到了“事件”——solidity 0.8 address payable(msg.sender) - ParserError: Expected ';' but got 'event' -- solidity 0.8 address payable(msg.sender) OpenZeppelin 的可拥有合约 vs 需要 msg.sender - OpenZeppelin's ownable contract vs require msg.sender Msg.sender在“视图”功能中不起作用,为什么? 有解决方法吗? - Msg.sender does not work inside a “view” function, why? Is there a workaround? 无法从智能合约向 msg.sender 发送以太币 - Not able to send ether to msg.sender from smart contract require(msg.sender == owner(), 'not owner') 抛出异常,尽管在事件中检查 msg.sender 等于 owner() - require(msg.sender == owner(), 'not owner') throwing exception although msg.sender equals owner() when inspected in event onERC721Received 中出现意外的 msg.sender - Unexpected msg.sender in onERC721Received
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM