solidity编程

Solidity源文件布局

  • Pragma( 版本杂注)
    1,源文件可以被版本咋住pragma所注解,表明要求的编译器版本
    2,例如;pragma solidity 0.4.0。及所要求版本不能低于0.4.0版本的编译器编译,也不允许高于(包含)0.5.0版本的编译器编译(第二个条件因使用被添加)
    3,Import(导入其他源文件)solidity所支持的导入语句import,语法同javascript非常类似。
  • Import
    1,import “filename”; 从filename中导入所有的全局符号到当前全局作用域中
    2,import * as symbolName from “filename”; 创建一个全局符号symbolName,其成员均来自filename中的全局变量
    3,import {symbol1 as alias, symbol2} from “filename”; 创建全局符号alias和symbol2,分别从filename中引用symbol1和symbol2
    4,import “filename” as symbolName; 这条语句等同于import * as symbolName from “filename”

Solidity值类型

1,布尔(boll):可以取值为字符常量值true和false
2,整型(int/uint):分别表示有符号和无符号的不同位数的整形变量;支持关键字uint8到uint256(无符号,从8位到256位)以及int8到int256,以8位为步长增长
3,定长浮点型(fixed/ufixed):表示各种大小有符号和无符号的定长浮点型;在关键字ufixedMxN中,M表示该类型占用的位数,N表示可用的小数位数
4,地址(address):存储一个20字节的只(以太坊地址大小)
5,定长字节数组:关键字有bytes1,bytes2,bytes3,…,bytes32
6,枚举(enum):一种可以定义类型的方法,与c语言类似,默认从0开始递增,一般用来模拟合约的状态
7,函数(function):一种表示函数的类型
Solidity引用类型

数组(array)

1,数组在声明时指定长度(定长数组),也可以调整大小(变长数组,动态数组)
2,对于存储类型(storage)的数组来说,元素类型可以是任意的(及元素可以是数组类型,映射类型或者结构体类型);对于内存型(memory)来说,元素类型不能是映射型

结构(struct)

Solidity支持通过构造结构体的形式定义新的类型

映射(mapping)

映射可视作哈希表,在实际的初始化过程中创建每个可能的key,并将其映射到字节形式全是0的值(类型默认)

Solidity地址类型

  • address:地址类型存储一个20字节的值(以太坊地址的大小);地址类型也有成员变量,并作为所有合约的基础
  • Address payable(v0.5.0引入):与地址类型基本相同,不过多出来transfer和send两个成员变量。
  • 两者区别与转换
    1,Payable地址是可以发送ether的地址,而普通的address不能
    2,允许从payable address到address的隐式转换,而反过来的直接转换是不可能的(唯一方法是通过int160来进行中间转换)
    3,从0.5.0版本起,合约不再是从地址派生出来,但如果他有payable的回退函数,那样可以显示转换为address或者address payable类型
  • 地址类型成员变量
    1,
    .balance(uint256) 改地址的ether余额,以wei为单位
    2,
    .transfer(uint256 amount)向指定地址发送数量为amount的ether(以wei为单位),失败时抛出异常,发送2300gas的矿工费,不可调节
    3,
    .send(uint256 amount) returns(boll)向指定地址发送数量为amount的ether(以wei为单位),失败时返回false,发送2300gas的矿工费,不可调节
    4,
    .call(bytes money) returns(boll,bytes money)发出底层函数CALL,失败时返回false,放松所有可用gas,可调节
    5,
    .delegatecall(bytes money) returns(boll,bytes money)发出底层函数DELEGATECALL,失败时返回false,放松所有可用gas,可调节
    6,
    .staticcall(bytes money) returns(boll,bytes money)发出底层函数STATICCALL,失败时返回false,放松所有可用gas,可调节
  • 地址成员变量用法
    1,balance和transfer:可以使用balance属性来查询一个地址的余额,可以使用transfer函数向一个payable地址发送以太币ether(以wei为单位)
    2,Send:send是transfer的低级版本。如果执行失败,当前合约不会因为异常而终止,但send会返回false
    3,call :也可以用来实现转币操作,通过添加.gas()和.value()修饰器;nameReg.call.gas(1000000).value(1 ether)(abi.encodeWithSignature(“register(string)”,”MyName”))

字符数组(Byte Arrays)

  • 定长字符数组
    1,属于值类型,bytes1,bytes2,bytes3, …,bytes32分别代表了长度为1到32的字节节序
    2,有一个.length属性返回数组长度(只读)
    变长字节数组
  • 属于引用类型包括bytes和string,不同的是bytes是Hex字符串,而string是utf-8编码字符串
    枚举(enum)
  • 枚举类型用来用户自定义一组常量值
  • 与c语言的枚举类型相似,对应整数值

数组(array)

1,固定大小k和元素类型为T的数组被写为T[k],动态大小的数组为T[]。例如,一个有5个动态数组组成的数组是uint[][5]
2,要访问第三个动态数组中的第二个uint,可以使用x[2][1]
3,越界访问数组,会导致调用失败回退
4,如果要添加新元素,则必须用.push()或将.length增大
5,变长的storage数组和bytes(不包括string)有一个push()方法。可以将一个新元素附加到数组末端,返回值为当前长度。

结构(struct)

1,结构类型可以在映射和数组中使用,他们本身可以包含映射和数组。
2,结构不能包含自己类型的成员,但可以作为自己数组成员的类型,也可以作为自己映射成员的值类型。

映射(mapping)

1,声明一个映射:mapping(_KeyType => _ValueType)
2,_KeyType 可以是任何基本类型,这就意味着它可以使任何内置类型加上字节和字符串。不允许使用用户定义的或复杂的类型,如枚举,映射,结构以及除bytes和string之外的任何数组类型。
3,_ValueType可以使任何类型,包括映射
Solidity数据位置
1,所有的复杂类型,即数组、结构和映射类型,都有一个额外属性,“数据位置”,用来说明数据是保存在内存memory中还是存储在storage中
2,根据上下文不同,大多时候数据有默认的位置,但也可以通过在类型后面添加关键字storage或memory进行修改
3,函数参数(包括返回的参数)的数据位置默认为memory,局部变量的数据位置默认为storage,状态变脸的数据位置强制是storage
4,另外还存在第三种数据位置,calldata,这是一块只读的,且不会永久存储的位置,用来存储函数参数。外部函数的参数(非返回参数)的数据位置被强制指定为calldata,效果跟memory差不多。

强制指定的数据位置

  • 外部函数的参数(不包括返回参数):calldata;
  • 状态变量:storage
  • 默认数据位置
  • 函数参数(包括返回参数):memory;
  • 引用类型的局部变量:storage
  • 值类型的局部变量:栈(stack)

特别要求

  • 公开可见(public visible)的函数参数一定是memory类型,如果要求是storage类型 则必须是private或者internal函数,这是为了防止随意的公开调用占用资源

蜜罐合约

pragma solidity ^0.4.22;
contract HoneyPot{
uint public luckDate = 52;
struct Guess{
address player;
uint num;
}
Guess[] public guessHistory;
function guess(uint _num) public payable{
Guess newGuess;
newGuess.player = msg.sender;
guessHistory.push(newGuess);
if(_num == luckDate){
msg.sender.transfer(msg.value * 2);
}
}
function ()public payable{

}

}
该合约利用局部指针未初始化直接指向初始化空间第一个位置来骗取比特币。
首先newGuess先指向luckDate为52,执行赋值操作后,newGuess指向msg.sender的地址,与实际值不一样,导致竞猜错误。

Solidity函数可见性

  • 函数的可见性可以指定为external,public,internal,private;对于状态变量,不能设置为external,默认为internal。
  • External:外部函数作为合约的一部分,意味着我们可以从其他合约和交易中调用。
  • Public:public函数是合约接口的一部分,意味着可以在内部或通过消息调用。
  • Internal:这些函数和状态变量只能内部访问,不能使用this调用。
  • Private:private函数和状态变量仅在当前定义的合约中使用,并且不能被派生合约使用。
  • pragma solidity ^0.4.22;
    contract C{
    uint private date;
    function f(uint a) private pure returns(uint b){return a + 1;}
    function setDate(uint a)public{date =a;}
    function getData() public view returns(uint){return date;}
    function compute(uint a, uint b) internal pure returns(uint){
    return a + b;
    }
    }

contract D{
function readDate() public{
C c = new C();
uint local = c.f(7); //f 为私有,不可为外部函数调用
c.setDate(3);
c.getData();
c.compute(3, 5);//compute为internal,不可调用
}
}

contract E is C{
function g()public pure{
//继承后直接调用内部函数
//C c = new C();
//uint val = c.compute(2, 3);
compute(4, 5);
}
}`

Solidity函数状态可见性

  • Pure:纯函数,不允许修改或访问状态
  • View:不允许修改状态
  • Payable:允许从消息调用中接受以太币ether
  • Constant:与view相同,一般只修饰状态变量,不许赋值(除初始化外)

Solidity函数状态可变性

以下情况被认为修改状态:

  • 修改状态变量。
  • 产生事件。
  • 创建其他合约。
  • 使用self destruct。
  • 通过调用发送以太币。
  • 调用任何没有标记的view和pure的函数。
  • 使用低级调用。
  • 使用包含包含特殊操作码的内联汇编。

以下被认为是从状态中进行读取:

  • 读取状态变量。
  • 访问this.balance或者
    .balance。
  • 访问block,tx,msg中的任意成员(除msg.sig,msg.data)。
  • 调用任意未标记为pure的函数。
  • 使用包含某些操作码的内联汇编。

函数修饰器(modifier)

  • 使用函数编辑器modifier可以轻松改变函数的行为。例如,它可以在执行函数前自动检查某个条件。修饰器modifier是合约的可继承属性,并可能被派生合约覆盖。
  • 如果同一个函数有多个修饰器modifier,它们之间可以空格隔开,修饰器modifier会依次检查执行。
    pragma solidity ^0.4.22;
    contract Purchase{
    address public seller;
    constructor ()public{
    seller = msg.sender;
    }
    modifier onlySeller(){
    require(msg.sender == seller, “Only Seller”);//require要求如果条件为真则执行,部位真输出信息。
    _;//_表示占位符,_的位置在哪表示函数在那个位置先执行。
    }
    function f()public view onlySeller returns(uint){
    return 200;
    }
    }

回退函数(fallback)

  • 回退函数是合约中的特殊函数;没有名字,不能有参数也不能有返回值。
    如果一个在合约的调用中,没有其它函数与指定的标识符匹配(或没有提供调用数据),那么这个(fallback函数)会被执行。
  • 每当合约收到以太币(没有任何数据),回退函数就会执行。此外,为了接收以太币,fallback函数必须标记为payable。如果不存在这样的函数,则合约不能通过常规交易接收以太币。
    在上下文中通常只有很少的gas可以用来完成回退函数的调用,所以使用fallback函数的调用尽量廉价很重要。

事件(event)

  • 事件是以太坊EVM提供的一种日志基础设施。时间可以用来操作记录,存储为日志。也可以用来实现一些交互功能,比如通知UI,返回函数调用结果等等。
  • 当定义的事件触发时,我们将事件存储到EVM的交易日志中,日志是区块链中一种特殊的数据结构;日志与合约关联,与合约的存储合并存入区块链中;只要某个区块可以访问,其相关的日志就可以访问;但在合约中,我们不能直接访问日志和事件数据。
  • 可以通过日志实现简单支付验证SPV(Simplified Payment Verification),如果一个外部实体提供了一个这种带有证明的合约,他就可以检查日志是否真实存在于区块链中。
    Solidity异常处理
  • Solidity使用“状态恢复异常”来处理异常。这样的异常将撤销对当前调用(以及所有子调用)中的状态所做的所有更改,并且香调用着返回错误。
  • 函数assert和require可用于判断条件,并且在不满足条件时抛出异常。
  • Assert()一般只应用与测试内部错误,并检查常量。
  • Require()应用于确保满足有效条件(如输入或合约状态变量),或验证调用外部合约的返回值。
  • Revert()用于抛出异常,他可以用于标记一个错误并将当前调用回退。

你可能感兴趣的:(编程语言)