提前了解
位(bit):1位表示一个二进制数。 位是计算机中存储数据的最小单位,其值为“0”或“1”。一个二进制数就是一位(1bit)
字节(byte):1字节表示8个二进制数。字节是计算机存储容量的基本单位。在计算机内部,一个字节可以表示一个数据,也可以表示一个英文字母,两个字节可以表示一个汉字。1B=8bit 2B = 16bit
字(word):计算机进行数据处理时,一次存取、加工和传送的数据长度称为字。一个字通常由一个或多个(一般是字节的整数位)字节构成。
在16位的系统中(比如8086微机) 1字 (word)= 2字节(byte)= 16(bit)
在32位的系统中(比如win32) 1字(word)= 4字节(byte)=32(bit)
在64位的系统中(比如win64)1字(word)= 8字节(byte)=64(bit)
进制:
一个二进制数 = 1bit
一个八进制数07 = 3bit(二进制表示为111)
一个十六进制数0xf = 4bit(二进制表示为1111)
两个十六进制数0xff= 8bit = 1个字节
理解概念:
布署之后的合约状态变量会永久存在链上, 就类似于全局变量. 调用时需要用合约名称 和 合约里定义的方法来操作.
全局变量
msg.data(bytes):完整的通话数据
msg.sender():消息的发件人(当前通话)address payable
msg.value(uint):随消息一起发送的wei数
assert(bool condition):如果条件为中止,则中止执行并还原状态更改false(用于内部错误)
require(bool condition):如果条件为条件,则中止执行并还原状态更改false(用于格式错误的输入或外部组件中的错误)
keccak256(bytes memory) returns (bytes32):计算输入的Keccak-256哈希
sha256(bytes memory) returns (bytes32):计算输入的SHA-256哈希
ripemd160(bytes memory) returns (bytes20):计算输入的RIPEMD-160哈希
函数可见性
1. public: visible externally and internally (creates a getter function for storage/state variables)
2. private: only visible in the current contract
3. external: only visible externally (only for functions) - i.e. can only be message-called (via this.func)
4. internal: only visible internally
修饰符
1. pure for <font color='#00ff00' size=7 >functions</font>: Disallows modification or access of state.
2. view for functions: Disallows modification of state.
3. payable for functions: Allows them to receive Ether together with a call.
4. constant for state variables: Disallows assignment (except initialisation), does not occupy storage slot.
5. immutable for state variables: Allows exactly one assignment at construction time and is constant afterwards. Is stored in code.
6. anonymous for events: Does not store event signature as topic.
7. indexed for event parameters: Stores the parameter as topic.
8. virtual for functions and modifiers: Allows the function’s or modifier’s behaviour to be changed in derived contracts.
9. override: States that this function, modifier or public state variable changes the behaviour of a function or modifier in a base contract.
pure 修饰函数时:不允许修改或访问状态——但目前并不是强制的。
view 修饰函数时:不允许修改状态——但目前不是强制的。
payable 修饰函数时:允许从调用中接收 以太币Ether 。
constant 修饰状态变量时:不允许赋值(除初始化以外),不会占据 存储插槽storage slot 。
constant 修饰函数时:与 view 等价。
anonymous 修饰事件时:不把事件签名作为 topic 存储。
indexed 修饰事件时:将参数作为 topic 存储。
modifier:被 modifier 关键字声明的关键字所修饰的函数只能在满足 modifier 关键字声明的关键字的要求后才会被执行,比如声明某函数只有管理员有权限,则可以参考以下实现:
modifier onlyAdmin() {
require(msg.sender == admin, "Permission denied");
_;
}
function set(uint a) public onlyAdmin returns(uint) {
.....
}
constant:被声明为 constant 的状态变量只能使用那些在编译时有确定值的表达式来给它们赋值。任何通过访问 内存、链数据(例如 now,this.balance 或 block.number)或执行数据(msg.gas)或对外部合约的调用来给它们赋值都是不允许的。不是所有类型的状态变量都支持用 constant 来修饰,当前支持的仅有值类型和字符串。
view:被该关键字修饰的状态变量只能读取其值,不能对该状态变量的值进行修改。
pure:被该关键字修饰的状态变量既不能读取变量,也不能修改该变量。
Storage:Storage 变量是指永久存储在链中的变量。
Memory:Memory 变量则是临时的,当外部函数对某合约调用完成时,内存型变量即被移除。
错误处理: Assert, Require, Revert
- assert(bool condition):如果条件不满足,则抛出,消耗掉剩余的gas - 用于内部错误。
- require(bool condition):如果条件不满足,则抛出,返回剩余的gas - 用于输入或外部组件中的错误。
- revert():中止执行并恢复状态更改,返回gas
if(msg.sender != owner) { revert(); }
assert(msg.sender == owner);
require(msg.sender == owner);
- 允许返回一个数值
看起来像这样,第一个参数是判断条件,第二个参数是如果判断条件不满,则返回一个数值:
revert('Something bad happened');
或者
require(condition, 'Something bad happened');
- 将剩余 gas 返还调用者
目前的合约处理 throws 后会消耗剩余的 gas。尽管可以视为对矿工的慷慨捐助,但是往往会消耗用户大量金钱。
一旦 REVERT 在 EVM 中实现,将会抛弃旧方式转而将剩余 gas 返还用户。
3、两者在使用场景上是等价的,但当一些判断逻辑比较复杂的时候,使用Revert()更合适。
-
View 视图函数 修改状态变量。
产生事件。
创建其它合约。
使用 selfdestruct。
通过调用发送以太币。
调用任何没有标记为 view 或者 pure 的函数。
使用低级调用。
使用包含特定操作码的内联汇编。
-
状态变量总是存储在存储区中。此外,不能显式地标记状态变量的位置
pragma solidity ^0.5.0;
contract DataLocation {
uint storage stateVariable; // 错误
uint[] memory stateArray; // 错误
}
函数参数包括返回参数都存储在内存中 www.qikegu.com/docs/4928
- 值类型的局部变量存储在内存中。但是,对于引用类型,需要显式地指定数据位置。
pragma solidity ^0.5.0;
contract Locations {
/* 此处都是状态变量 */
// 存储在storage中
bool flag;
uint number;
address account;
function doSomething() public {
/* 此处都是局部变量 */
// 值类型
// 所以它们被存储在内存中
bool flag2;
uint number2;
address account2;
// 引用类型,需要显示指定数据位置,此处指定为内存
uint[] memory localArray;
}
}
规则4 – 外部函数的参数
外部函数的参数(不包括返回参数)存储在Calldata中。
规则1 – 存储变量赋值给存储变量
将一个状态(存储)变量赋值给另一个状态(存储)变量,将创建一个新的副本。
规则2 – 内存变量复制到存储变量
call、callcode、delegatecall
call、callcode、delegatecall调用的自由度极大,并且 call 会发生 msg 值的改变,需要谨慎的使用这些底层的函数;同时在使用时,需要对调用的合约地址、可调用的函数做严格的限制。
call 与 callcode 调用会改变 msg 的值,会修改 msg.sender 为调用者合约的地址,所以在合约中不能轻易将合约本身的地址作为可信地址。
delegatecall 与 callcode 会拷贝目标代码到自己的环境中执行,所以调用的函数应该做严格的限制,避开调用任意函数的隐患。 智能合约在部署前必须通过严格的审计和测试