区块和交易属性是Solidity中访问区块链信息的重要全局变量。本文详细介绍block、msg、tx等全局对象的属性,以及如何在实际开发中使用这些信息。

一、什么是区块和交易属性
一、1 基本概念
Solidity提供了多个全局对象来访问当前区块和交易的信息,包括block、msg、tx等。这些属性在智能合约中非常常用,用于获取时间戳、调用者地址、Gas价格等信息。
二、2 全局对象
- 包含当前区块信息
- 时间戳、区块号、难度等
- 只读属性
- 所有节点相同
- 包含当前消息/调用信息
- 发送者、值、数据等
- 函数调用相关
- 可能在不同调用中不同
- 包含交易信息
- Gas价格、原始发送者
- 整个交易相关
- 交易级别信息
二、区块属性
三、1 基本属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| pragma solidity ^0.8.0;
contract BlockInfo { function getCurrentTime() public view returns (uint256) { return block.timestamp; // 当前区块时间戳(秒) } function isAfter(uint256 targetTime) public view returns (bool) { return block.timestamp >= targetTime; } `block.number:`solidity function getCurrentBlock() public view returns (uint256) { return block.number; // 当前区块号 }
function getBlocksSince(uint256 startBlock) public view returns (uint256) { return block.number - startBlock; } `block.difficulty:`solidity function getDifficulty() public view returns (uint256) { return block.difficulty; // 当前区块难度 } `block.gaslimit:`solidity function getGasLimit() public view returns (uint256) { return block.gaslimit; // 当前区块Gas限制 } `block.coinbase:`solidity function getMiner() public view returns (address) { return block.coinbase; // 当前区块矿工地址 }
|
四、2 区块哈希
1 2 3 4 5
| blockhash函数: function getBlockHash(uint256 blockNumber) public view returns (bytes32) { require(blockNumber < block.number && blockNumber >= block.number - 256, "Block out of range"); return blockhash(blockNumber);
|
}
1 2
| function getPreviousBlockHash() public view returns (bytes32) { return blockhash(block.number - 1);
|
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| 限制说明: - 只能查询最近256个区块 - 超出范围返回0 - 用于验证历史区块 - 注意范围限制
## 三、消息属性
### 五、1 基本属性
msg.sender: contract MessageInfo { address public owner = msg.sender; // 部署者地址 function getCaller() public view returns (address) { return msg.sender; // 函数调用者地址 } function onlyOwner() public view { require(msg.sender == owner, "Not owner"); // 只有owner可以调用 } `msg.value:`solidity function deposit() public payable { balances[msg.sender] += msg.value; // 发送的以太币数量(wei) }
function getValue() public view returns (uint256) { return msg.value; // 当前调用发送的以太币 } `msg.data:`solidity function getCallData() public view returns (bytes memory) { return msg.data; // 完整的调用数据 }
function getFunctionSelector() public view returns (bytes4) { return msg.sig; // 函数选择器(前4字节) }
|
六、2 函数选择器
1 2 3 4
| msg.sig: function transfer(address to, uint256 amount) public { bytes4 selector = msg.sig;
|
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ## 四、交易属性
### 七、1 基本属性
tx.gasprice: function getGasPrice() public view returns (uint256) { return tx.gasprice; // 交易的Gas价格 }
function calculateFee(uint256 gasUsed) public view returns (uint256) { return gasUsed * tx.gasprice; // 计算交易费用 } `tx.origin:`solidity function getOrigin() public view returns (address) { return tx.origin; // 交易的原始发起者 }
// 注意:tx.origin 和 msg.sender 的区别 // tx.origin: 交易的原始发起者(可能是合约调用链的起点) // msg.sender: 直接调用者(可能是中间合约)
|
八、2 区别说明
1 2 3 4
| msg.sender vs tx.origin: contract A { function callB(address b) public { B(b).someFunction();
|
}
1 2 3 4 5
| contract B { function someFunction() public view returns (address, address) { return (msg.sender, tx.origin);
|
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 安全提示: - 通常使用msg.sender - tx.origin可能导致安全问题 - 避免使用tx.origin做权限检查 - 使用msg.sender更安全
## 五、Gas相关
### 九、1 gasleft函数
剩余Gas: function checkGas() public view returns (uint256) { return gasleft(); // 返回剩余Gas }
function measureGas() public { uint256 gasStart = gasleft(); // 执行一些操作 uint256 gasUsed = gasStart - gasleft(); // gasUsed 是执行操作的Gas消耗 } `Gas优化:`solidity function optimizedFunction() public { uint256 gasBefore = gasleft(); // 执行操作 if (gasleft() < gasBefore - 100000) { revert("Gas limit reached"); }
|
六、应用场景
十、1 时间锁
1 2 3 4
| 基于时间戳: contract TimeLock { mapping(address => uint256) public lockTime; mapping(address => uint256) public lockedAmount;
|
1 2 3 4
| function lock(uint256 duration) public payable { require(msg.value > 0, "Must send ether"); lockTime[msg.sender] = block.timestamp + duration; lockedAmount[msg.sender] = msg.value;
|
}
1 2 3
| function withdraw() public { require(block.timestamp >= lockTime[msg.sender], "Still locked"); require(lockedAmount[msg.sender] > 0, "No locked amount");
|
1 2 3
| uint256 amount = lockedAmount[msg.sender]; lockedAmount[msg.sender] = 0; lockTime[msg.sender] = 0;
|
1
| payable(msg.sender).transfer(amount);
|
}
1 2
| function isLocked(address account) public view returns (bool) { return block.timestamp < lockTime[account];
|
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ### 十一、2 随机数生成
基于区块信息: contract RandomGenerator { function generateRandom(uint256 max) public view returns (uint256) { return uint256(keccak256(abi.encodePacked( block.timestamp, block.difficulty, msg.sender ))) % max; } // 注意:这不是真正的随机数,矿工可以操纵 // 仅用于非关键场景 }
|
十二、3 权限控制
1 2 3 4
| 基于调用者: contract AccessControl { address public owner = msg.sender; mapping(address => bool) public isAdmin;
|
_;
}
1 2
| modifier onlyAdmin() { require(isAdmin[msg.sender] || msg.sender == owner, "Not admin");
|
_;
}
1 2
| function grantAdmin(address user) public onlyOwner { isAdmin[user] = true;
|
}
1 2
| function revokeAdmin(address user) public onlyOwner { delete isAdmin[user];
|
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ### 十三、4 支付处理
处理以太币: contract PaymentProcessor { function processPayment(address recipient) public payable { require(recipient != address(0), "Invalid recipient"); uint256 fee = msg.value / 100; // 1% 手续费 uint256 amount = msg.value - fee; payable(recipient).transfer(amount); payable(msg.sender).transfer(fee); // 退回手续费(示例) } function getPaymentInfo() public view returns (uint256, address) { return (msg.value, msg.sender); }
|
七、最佳实践
十四、1 时间戳使用
- 矿工可以操纵时间戳(±15秒)
- 不要用于关键随机数
- 适合时间锁和过期检查
- 考虑时间窗口
1 2 3 4 5 6
| 安全使用: function safeTimeCheck(uint256 targetTime) public view returns (bool) { uint256 timeWindow = 300; return block.timestamp >= targetTime - timeWindow && block.timestamp <= targetTime + timeWindow;
|
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ### 十五、2 区块号使用
应用场景: - 计算区块间隔 - 实现基于区块的时间锁 - 记录特定区块的状态 - 验证区块历史
示例: mapping(uint256 => bool) public blockProcessed;
function processBlock() public { require(!blockProcessed[block.number], "Already processed"); blockProcessed[block.number] = true; // 处理逻辑 }
|
十六、3 安全考虑
1 2 3 4
| 避免tx.origin:
function badCheck() public { require(tx.origin == owner, "Not owner");
|
}
1 2 3
| function goodCheck() public { require(msg.sender == owner, "Not owner");
|
}
Gas限制:solidity
1 2
| function gasLimitedFunction() public { require(gasleft() > 50000, "Insufficient gas");
|
}
## 八、常见问题
### 十七、1 时间戳精度
问题:
- 时间戳精度为秒
- 矿工可以操纵
- 不适合精确时间
解决:
- 使用时间窗口
- 考虑区块间隔
- 不用于关键随机数
- 适合相对时间
### 十八、2 区块哈希限制
- 检查范围
- 使用require验证
- 考虑替代方案
- 记录历史哈希
## 九、总结
区块和交易属性是Solidity中访问区块链信息的重要工具。关键要点:
全局对象:
- block:区块信息
- msg:消息/调用信息
- tx:交易信息
- gasleft:剩余Gas
- 时间锁
- 权限控制
- 支付处理
- 状态验证
最佳实践:
- 注意时间戳精度
- 避免使用tx.origin
- 检查区块哈希范围
- 合理使用Gas信息
通过深入理解这些全局属性,可以编写更灵活、更安全的智能合约,实现各种基于区块链信息的业务逻辑。
本文标题: Solidity区块和交易属性
发布时间: 2023年12月08日 00:00
最后更新: 2025年12月30日 08:54
原始链接: https://haoxiang.eu.org/4744933f/
版权声明: 本文著作权归作者所有,均采用CC BY-NC-SA 4.0许可协议,转载请注明出处!