solidity 语法学习

341 阅读6分钟

提前了解

位(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

  1. assert(bool condition):如果条件不满足,则抛出,消耗掉剩余的gas - 用于内部错误。
  2. require(bool condition):如果条件不满足,则抛出,返回剩余的gas - 用于输入或外部组件中的错误。
  3. revert():中止执行并恢复状态更改,返回gas
if(msg.sender != owner) { revert(); }
assert(msg.sender == owner);
require(msg.sender == owner);
  1. 允许返回一个数值

看起来像这样,第一个参数是判断条件,第二个参数是如果判断条件不满,则返回一个数值:

revert('Something bad happened');

或者

require(condition, 'Something bad happened');

  1. 将剩余 gas 返还调用者

目前的合约处理 throws 后会消耗剩余的 gas。尽管可以视为对矿工的慷慨捐助,但是往往会消耗用户大量金钱。

一旦 REVERT 在 EVM 中实现,将会抛弃旧方式转而将剩余 gas 返还用户。

3、两者在使用场景上是等价的,但当一些判断逻辑比较复杂的时候,使用Revert()更合适。

  1. View 视图函数 修改状态变量。

    产生事件。

    创建其它合约。

    使用 selfdestruct。

    通过调用发送以太币。

    调用任何没有标记为 view 或者 pure 的函数。

    使用低级调用。

    使用包含特定操作码的内联汇编。

  2. 状态变量总是存储在存储区中。此外,不能显式地标记状态变量的位置

pragma solidity ^0.5.0;  

contract DataLocation {  

   uint storage stateVariable; // 错误  
   uint[] memory stateArray; // 错误  
} 

函数参数包括返回参数都存储在内存中 www.qikegu.com/docs/4928

  1. 值类型的局部变量存储在内存中。但是,对于引用类型,需要显式地指定数据位置。
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

www.cnblogs.com/x-poior/p/1…

call、callcode、delegatecall调用的自由度极大,并且 call 会发生 msg 值的改变,需要谨慎的使用这些底层的函数;同时在使用时,需要对调用的合约地址、可调用的函数做严格的限制。

call 与 callcode 调用会改变 msg 的值,会修改 msg.sender 为调用者合约的地址,所以在合约中不能轻易将合约本身的地址作为可信地址。

delegatecall 与 callcode 会拷贝目标代码到自己的环境中执行,所以调用的函数应该做严格的限制,避开调用任意函数的隐患。 智能合约在部署前必须通过严格的审计和测试