当前位置:   article > 正文

以太坊环境以及Solidity学习笔记_function name ( ) public pure returns ( string ) {

function name ( ) public pure returns ( string ) { return name ; }

一、以太坊环境搭建

以太坊 私有链搭建 Geth+Mist钱包

以太坊 链私有链环境搭建(windows)

S1:下载安装Geth、Mist客户端

S2:初始化创世纪节点

定义一个配置文件genesis.json

  1. {
  2. "config": {
  3. "chainId": 666,
  4. "homesteadBlock": 0,
  5. "eip150Block": 0,
  6. "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  7. "eip155Block": 0,
  8. "eip158Block": 0,
  9. "byzantiumBlock": 0,
  10. "constantinopleBlock": 0,
  11. "petersburgBlock": 0,
  12. "istanbulBlock": 0,
  13. "ethash": {}
  14. },
  15. "nonce": "0x0",
  16. "timestamp": "0x5ddf8f3e",
  17. "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
  18. "gasLimit": "0x47b760",
  19. "difficulty": "0x00002",
  20. "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  21. "coinbase": "0x0000000000000000000000000000000000000000",
  22. "alloc": { },
  23. "number": "0x0",
  24. "gasUsed": "0x0",
  25. "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
  26. }

通过如下指令,利用配置文件初始化创世块:

geth  -datadir data0 init genesis.json

上面的命令,会读取genesis.json文件,根据其中的内容,将创世区块写入到区块链中。如果看到log信息中含有Successfully wrote genesis state字样,说明初始化成功。

在系统文件夹中生成data0,geth?keystore?

S3:启动私有链节点

通过如下指令,即可启动:

geth -datadir data0 console

成功出现如下提示:

注:这里后面INFO输出Looking for peers,不影响操作!

S4:体验挖矿

启动挖矿指令:

miner.start()

可以看到DAG percentage逐渐增大

之后不想挖矿后,停止挖矿指令:

miner.stop()

挖矿收入检查:

eth.getBalance(eth.accounts[0])

二、智能合约学习资料

智能合约概述 — Solidity develop 文档 (solidity-cn.readthedocs.io)

基于以太坊的智能合约开发教程【Solidity】_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili

Remix - Solidity IDE - 中文版 (hubwiz.com)

三、Solidity初级篇

3.1 pragma+view+pure

  1. pragma solidity ^0.4.0;
  2. //^0.4.0代表向上版本兼容,编译器版本最高到0.5.0但不包括0.5.0
  3. contract Helloworld{
  4. string Myname="tyl";
  5. //view 修饰函数只能读取状态变量,不消耗gas
  6. function getName() public view returns(string){
  7. return Myname;
  8. }
  9. function changeName(string _newName) public{
  10. Myname=_newName;
  11. }
  12. //pure 修饰函数不能改也不能读状态变量,不消耗gas
  13. function pureTest(string _newName) public pure returns(string){
  14. return _newName;
  15. }
  16. }
1、Solidity 旧版本只有constant,新版本将constant拆成了view和pure。view的作用和constant一模一样,可以读取状态变量但是不能改;pure则更为严格,pure修饰的函数不能改也不能读状态变量,否则编译通不过。
2、pragma solidity ^0.4.0; //^0.4.0代表向上版本兼容,编译器版本最高到0.5.0但不包括0.5.0

3.2 数据类型:bool、int、uint 、bytes、bytes1-bytes32

  1. pragma solidity ^0.4.0;
  2. contract dataClass{
  3. //布尔类型 bool 数据操作:逻辑与、逻辑或、逻辑非 ||、 && 、!
  4. int num1=100;
  5. int num2=200;
  6. function boolcheck() public view returns(bool){
  7. return num1==num2;
  8. }//返回false
  9. function boolcheck1() public view returns(bool){
  10. return num1!=num2;
  11. } //返回true
  12. //整型 int=int256 uint=uint256 数据操作:加减乘除求余、求平方 +-*/%、**
  13. function intcheck(uint a,uint b) public pure returns(uint){
  14. return a**b;
  15. } //计算a的b次方
  16. //位运算:按位与、按位或、按位取反、按位异或、左移、右移 &、|、~、^、<<>>
  17. uint8 a=3; uint8 b=4;
  18. function weicheck() public view returns(uint8){
  19. return a|b;
  20. }//按位或,结果为7
  21. //整型字面量:在solidity里面运算是计算出结果再赋值
  22. function IntergerTest() public pure returns(uint){
  23. uint num=1/2*1000;
  24. return num;
  25. }//返回500
  26. //字节(数组)类型:bytes1(byte)、bytes2...bytes32,长度固定且内部数据不可修改
  27. //属性:length 可以进行比较,位运算
  28. bytes9 name=0xe69d8ee79fa5e681a9;
  29. function byteTest() spublic pure returns(uint){
  30. byte num=0x7a;
  31. return num.length;
  32. }//返回1
  33. function getIndex(uint index) public view returns(byte){
  34. return name[index];
  35. }//按照index获得字节数组的值
  36. }
动态字节数组:bytes num=new bytes();长度、内部数据均可修改,push方法和修改长度,均是在数组末尾变化
  1. pragma solidity ^0.4.0;
  2. contract DynamicByte{
  3. bytes public num=new bytes(2);//创建动态字节数组
  4. function InitNum() public{
  5. num[0]=0x12;
  6. num[1]=0x34;
  7. }//初始化数组
  8. function getlength() public view returns(uint){
  9. return num.length;
  10. }//获取数组长度
  11. function setlength() public{
  12. num.length=5;
  13. }//修改数组长度
  14. function pushTest() public{
  15. num.push(0x56);
  16. }//push方法在数组末尾追加数据
  17. }

3.3 字符串类型

  1. pragma solidity ^0.4.0;
  2. contract StringTest{
  3. string name='tyl';//0x74796c
  4. string name1="李知恩";//0xe69d8ee79fa5e681a9
  5. function getlength() view returns(uint){
  6. return bytes(name).length;
  7. }//获得字符串的字节长度
  8. function getstrValue() view returns(string){
  9. return name;
  10. }//获得字符串的str值
  11. function getValue() view returns(bytes){
  12. return bytes(name);
  13. }//获得字符串的bytes值
  14. function getValue1() view returns(bytes){
  15. return bytes(name1);
  16. }//获得字符串的bytes值
  17. function getfirst() view returns(byte){
  18. return bytes(name)[0];
  19. }//获得字符串首个字节存储的值
  20. function setstrValue(){
  21. bytes(name)[0]='T';
  22. bytes(name)[1]='Y';
  23. bytes(name)[2]='L';
  24. }//按字节修改字符串的值
  25. }
字符串变量无法直接获得长度和修改字符串的值,需要进行bytes(字符串)强制转换后才能操作前面的操作。

3.3.1固定长度字节数组之间的转换

  1. pragma solidity ^0.4.0;
  2. contract StrTest{
  3. bytes9 name= 0xe69d8ee79fa5e681a9;
  4. function change1() public view returns(bytes1){
  5. return bytes1(name);
  6. }//0xe6
  7. function change2() public view returns(bytes10){
  8. return bytes10(name);
  9. }//0xe69d8ee79fa5e681a900
  10. }
固定长度字符节数组长的变短,截取前面部分;短的变长的,在后面添0。

3.3.2 固定长度字节数组转换为动态长度字节数组

  1. pragma solidity ^0.4.0;
  2. contract StrTest1{
  3. bytes9 name=0xe69d8ee79fa5e681a9;
  4. function fixByte2dynamicByte() public view returns(bytes){
  5. bytes memory newByte=new bytes(name.length);
  6. for(uint i=0;i<name.length;i++){//细节问题:for循环里要求是uint变量
  7. newByte[i]=name[i];
  8. }
  9. return newByte;
  10. }
  11. }

3.3.3 bytes to string

  1. pragma solidity ^0.4.0;
  2. contract StrTest1{
  3. bytes9 name=0xe69d8ee79fa5e681a9;
  4. function fixByte2dynamicByte() public view returns(bytes){
  5. bytes memory newByte=new bytes(name.length);
  6. for(uint i=0;i<name.length;i++){
  7. newByte[i]=name[i];
  8. }
  9. return newByte;
  10. }
  11. function bytes2string() public view returns(string){
  12. bytes memory IU=fixByte2dynamicByte();
  13. return string(IU);
  14. }//返回"李知恩"
  15. }
小结:bytes与string可互相转换。string(字节数组)、bytes(字符串)

3.3.4 固定字节数组转换为string

  1. pragma solidity ^0.4.0;
  2. contract StrTest2{
  3. function bytes32Tostring(bytes32 _newname) public pure returns(string){
  4. uint count=0;
  5. for(uint i=0;i<_newname.length;i++){
  6. bytes1 char=_newname[i];
  7. if(char!=0){
  8. count++;
  9. }
  10. }//找到字节数组中有用数据个数
  11. bytes memory newname=new bytes(count);
  12. for(uint j=0;j<count;j++){
  13. newname[j]=_newname[j];
  14. }//固定字节数组转换为动态字节数组
  15. return string(newname);//最终实现固定字节数组转换为string
  16. }
  17. }

3.4 数组

3.4.1 固定数组

  1. pragma solidity ^0.4.0;
  2. contract ArrayTest{
  3. //固定数组初始化
  4. uint[5] arr=[1,2,3,4,5];
  5. //获取数组元素并修改
  6. function Init() public{
  7. arr[0]=100;
  8. arr[1]=200;
  9. }
  10. //获取数组元素内容
  11. function getArrayContent() public view returns(uint[5]){
  12. return arr;
  13. }
  14. //对数组元素求和
  15. function getGrade() public view returns(uint){
  16. uint grade=0;
  17. for(uint i=0;i<arr.length;i++){
  18. grade+=arr[i];
  19. }
  20. return grade;
  21. }
  22. }
固定数组没有push方法,也无法修改数组的length

3.4.2 可变数组

  1. pragma solidity ^0.4.0;
  2. contract ArrayTest1{
  3. // 可变数组初始化
  4. uint[] grade=[1,2,3,4,5];
  5. //获取数组元素内容
  6. function getContent() public view returns(uint[]) {
  7. return grade;
  8. }
  9. //获取可变数据长度
  10. function getLength() public view returns(uint){
  11. return grade.length;
  12. }
  13. //获取可变数组元素并修改
  14. function changeContent() public{
  15. grade[0]=100;
  16. grade[1]=200;
  17. }
  18. //对数组元素求和
  19. function add() public view returns(uint){
  20. uint sum=0;
  21. for(uint i=0;i<grade.length;i++){
  22. sum+=grade[i];
  23. }
  24. return sum;
  25. }
  26. //改变可变数组长度(缩短,只保留前面的数组元素)
  27. function changLength() public{
  28. grade.length=2;
  29. }
  30. //改变可变数组长度(增长,在数组后面添0
  31. function changLength1() public{
  32. grade.length=10;
  33. }
  34. //push方法在数组后面追加
  35. function pushContent() public{
  36. grade.push(6);
  37. }
  38. }

3.4.3 二维数组

定义静态二维数组时,行数和列数和其他语言不同,与其他语言刚好相反:数据类型[列数][行数]=[…….]。但在数组操作时,依旧是一样。静态二维数组同样不能改变数组长度
  1. pragma solidity ^0.4.0;
  2. contract ArrayTest2{
  3. //静态数组定义行数和列数刚好相反!!!
  4. uint[2][3] grade=[[1,2],[3,4],[5,6]];
  5. //获取数组内容
  6. function getContent() public view returns(uint[2][3]){
  7. return grade;
  8. }
  9. function getRowLength() public view returns(uint){
  10. return grade.length;
  11. }//3
  12. function getColumnsLength() public view returns(uint){
  13. return grade[0].length;
  14. }//2
  15. function add() public view returns(uint){
  16. uint sum=0;
  17. for(uint i=0;i<grade.length;i++){//
  18. for(uint j=0;j<grade[0].length;j++){//
  19. sum+=grade[i][j];//取数组元素依旧和之前的语言一样
  20. }
  21. }
  22. return sum;
  23. }
  24. }
动态二维数组,可修改数组长度,暂时还无法支持动态二维数组作为返回值,获得数组内容
  1. pragma solidity ^0.4.0;
  2. contract ArrayTest3{
  3. //定义动态二维数组
  4. uint[][] grade=[[1,2],[3,4],[5,6]];
  5. function getRowLength() public view returns(uint){
  6. return grade.length;
  7. }//3
  8. function getColumnsLength() public view returns(uint){
  9. return grade[0].length;
  10. }//2
  11. //改变行数
  12. function setRowLength() public{
  13. grade.length=4;
  14. }
  15. //改变列数,可针对任一行修改列数。
  16. function setColumnsLength() public {
  17. grade[0].length=4;
  18. }
  19. function add() public view returns(uint){
  20. uint sum=0;
  21. for(uint i=0;i<grade.length;i++){
  22. for(uint j=0;j<grade[i].length;j++){//注意和静态数组差别,每行的列数不一定相同
  23. sum+=grade[i][j];
  24. }
  25. }
  26. return sum;
  27. }
  28. }

3.4.4 数组字面量

一般用于输入需求,返回数组字面量时,返回参数列表以数组字面量最小类型为准。
  1. pragma solidity ^0.4.0;
  2. contract ArrayTest3{
  3. function getArray1() public pure returns(uint8[4]){
  4. return [1,2,3,4];
  5. }//最小类型为uint8
  6. function getArray2() public pure returns(uint16[4]){
  7. return [256,2,3,4];
  8. }//最小类型为uint16
  9. function getArray3() public pure returns(uint[4]){
  10. return [uint(1),2,3,4];
  11. }//可以强制将字面量转换为来适应返回参数列表类型
  12. function add(uint[4] grade) public pure returns(uint){
  13. uint sum=0;
  14. for(uint i=0;i<grade.length;i++){
  15. sum+=grade[i];
  16. }
  17. return sum;
  18. } //数组字面量用于输入
  19. }

四、Solidity 进阶篇

4.1 地址 address

address 在存储上和uint160一样,且二者可以互相转换,地址之间也可以进行比较大小
  1. pragma solidity ^0.4.0;
  2. contract arrayTest{
  3. address public account=0xca35b7d915458ef540ade6068dfe2f44e8fa733c;
  4. function changeIt() public view returns(uint160){
  5. return uint160(account);
  6. }//地址转换为uint1601154414090619811796818182302139415280051214250812
  7. function Itchange() public view returns(address){
  8. return address(changeIt());
  9. }//uint160转换为地址:0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c
  10. }
地址分为外部账户地址和合约账户地址,合约是由外部账户发布的合约,每个合约都有自己的地址。和外部账户一样,合约账户也可以有钱(以太币)
  1. pragma solidity ^0.4.0;
  2. contract arrayTest1{
  3. //payable关键字,允许转账操作(这里是外部账户转账给合约账户)
  4. function pay() public payable{
  5. }
  6. //返回合约账户地址
  7. function getAddress() public view returns(address){
  8. return this;
  9. }//0x038f160ad632409bfb18582241d9fd88c1a072ba
  10. //this关键字代表合约账户,账户都有balance(钱),这里“钱”单位默认是wei,返回合约账户的钱
  11. function getBalance() public view returns(uint){
  12. return address(this).balance;
  13. }
  14. //可以获得任意账户的钱
  15. function getBalance_by_addr(address account) public view returns(uint){
  16. return account.balance;
  17. }
  18. }

4.2 转账操作

外部账户之间进行转账: account.transfer(msg.value);
外部账户向合约账户转账:address(this).transfer(msg.value);+回滚函数
其中msg.value是全局变量,代表转账的数量
  1. pragma solidity ^0.4.0;
  2. contract arrayTest2{
  3. //运行该合约的账户向外部account账户进行转账
  4. function transfer() public payable{
  5. address account=0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
  6. account.transfer(msg.value);
  7. }
  8. //按照地址获得账户余额
  9. function getBlance_by_addr(address account) public view returns(uint){
  10. return account.balance;
  11. }
  12. //外部账户向改合约账户进行转账,必须要有回滚函数
  13. function transfer1() public payable{
  14. address(this).transfer(msg.value);
  15. }
  16. //必须要有的回滚函数!!!
  17. function() public payable{
  18. }
  19. }

4.3 全局变量

account.transfer(msg.value)中msg.value可以直接用相应的值替换,e.g. account.transfer(10 ether);

4.4 send方法与transfer方法

  1. function sendMoney() public payable returns(bool){
  2. address account=0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
  3. return account.send(10 ether);
  4. }//send方法比较底层,如果不传入相应的转账金额,不会报错,只会返回false
  5. function transfer() public payable{
  6. address account=0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
  7. account.transfer(msg.value);
  8. }//transfer方法,如果不传入相应的转账金额,会进行报错提示

4.5 mapping类型

  1. pragma solidity ^0.4.0;
  2. contract mappingTest{
  3. //定义两个mapping类型的变量
  4. mapping(address=>uint) public idmapping;
  5. mapping(uint=>string) public namemapping;
  6. //用于代表注册的人数
  7. uint sum=0;
  8. //注册,主要是合约调用者的地址=>id;id=>姓名两组映射关系
  9. function register(string name) public{
  10. address account=msg.sender;
  11. sum++;
  12. idmapping[account]=sum;
  13. namemapping[sum]=name;
  14. }
  15. //根据地址来获得id
  16. function getIdbyAddr(address account) public view returns(uint){
  17. return idmapping[account];
  18. }
  19. //根据id来获得姓名
  20. function getNamebyId(uint id) public view returns(string){
  21. return namemapping[id];
  22. }
  23. }

4.6 函数重载

函数一般类型:

function 函数名(参数列表){private|internal|external|public}[pure|constant|view|payable][returns(返回值类型)]

  • 1、函数名字相同
  • 2、函数的参数列表不同(类型、数量)
  • 3、不考虑函数的返回值类型
函数重载会遇到一个函数匹配的问题,例如add(1)与函数add(uint8)和add(uint)两个都匹配,发生冲突故报错!
其次参数列表类型是uint160和address时,无论输入的参数是什么类型,都会发生冲突,因为这两个本身机器是无法区分的。

4.7 关于函数命名参数说明

  1. pragma solidity ^0.4.0;
  2. contract Test{
  3. string public name;
  4. uint public id;
  5. function set(string _name,uint _id) public{
  6. name=_name;
  7. id=_id;
  8. }
  9. function test1() public{
  10. set("李知恩",123);
  11. }
  12. //通过这种给参数直接赋值的方式可以不考虑函数列表的先后顺序
  13. function test2() public{
  14. set({_name:"李知恩",_id:123});
  15. }
  16. }

4.8 关于函数返回值说明

  1. pragma solidity ^0.4.0;
  2. contract Test{
  3. //函数返回值可以有名称
  4. function test1() public pure returns(uint mul){
  5. return 123;
  6. }
  7. //函数返回值可以不用return返回,直接给返回值赋值
  8. function test2() public pure returns(uint mul){
  9. mul=123;
  10. }
  11. //函数返回值最终以return为主
  12. function test3() public pure returns(uint mul){
  13. mul=123;
  14. uint a=100;
  15. return a;
  16. }
  17. //函数可有多个返回值
  18. function test4() public pure returns(uint a, uint b){
  19. return(10,20);
  20. }
  21. }

4.9 生命周期与作用域

全局变量名称在局部变量里可以出现重名的,此时修改局部变量不会影响全局变量。同时在函数里面,也不能出现重复定义的变量!
  1. pragma solidity ^0.4.0;
  2. contract test{
  3. //全局变量
  4. uint public a=100;
  5. function test0() public view returns(uint){
  6. return a;
  7. }//返回100
  8. function test1() public pure returns(uint){
  9. uint a=200;
  10. return a;
  11. }//重新定义局部变量a,返回200,但全局变量a依旧是100
  12. function test2(uint a) public pure returns(uint){
  13. // uint a=300;无法重复定义a,在参数列表内出现了重复
  14. a=300;
  15. return a;
  16. }//返回300
  17. }

4.10 函数修饰符

  1. pragma solidity ^0.4.0;
  2. //public修饰符,可以在合约内部,继承合约内部,外部调用
  3. contract father{
  4. //public修饰函数
  5. function dahan() public pure returns(string){
  6. return "dahan";
  7. }
  8. //合约内部调用
  9. function publictest() public pure returns(string){
  10. return dahan();
  11. }
  12. }
  13. contract son is father{
  14. //public修饰 继承合约可继承还可直接调用
  15. function test() public pure returns(string){
  16. return dahan();
  17. }
  18. }
  19. //=========================================================
  20. //private修饰符,函数只能被本合约内部调用
  21. contract father1{
  22. function dahan() private pure returns(string){
  23. return "dahan";
  24. }
  25. //合约内部调用
  26. function privatetest() public pure returns(string){
  27. return dahan();
  28. }
  29. }
  30. contract son1 is father1{
  31. //private修饰符,函数不能被继承合约继承和调用
  32. /* function test() public pure returns(string){
  33. return dahan();
  34. }*/
  35. }
  36. //==========================================================
  37. //internal修饰符,函数只能被合约内部调用和继承合约内部调用
  38. contract father2{
  39. function dahan() internal pure returns(string){
  40. return "dahan";
  41. }
  42. //合约内部调用
  43. function internaltest() public pure returns(string){
  44. return dahan();
  45. }
  46. }
  47. contract son2 is father2{
  48. //继承合约内部调用
  49. function test() public pure returns(string){
  50. return dahan();
  51. }
  52. }
  53. //==========================================================
  54. //external修饰符,只能在外部调用,或者在合约内部和继承合约内部间接调用(实际也是外部调用)
  55. contract father3{
  56. function dahan() external pure returns(string){
  57. return "dahan";
  58. }
  59. //无法直接在合约内部调用
  60. /* function externaltest() public pure returns(string){
  61. return dahan();
  62. }*/
  63. //间接调用
  64. function externaltest() public view returns(string){
  65. return this.dahan();
  66. }
  67. }
  68. contract son3 is father3{
  69. //无法直接在继承合约内部调用
  70. /* function test() public pure returns(string){
  71. return dahan();
  72. }*/
  73. //间接调用
  74. function test() public view returns(string){
  75. return this.dahan();
  76. }
  77. }
  78. //通过另外不相关的合约,new一个合约对象,也可以间接调用
  79. contract externaltest{
  80. father3 f=new father3();
  81. function test() public view returns(string){
  82. return f.dahan();
  83. }
  84. }

4.11 值传递与副本拷贝

变量赋值给变量,是值传递的方式;形参传入的是副本拷贝,修改它的值不会影响原来的变量。
  1. pragma solidity ^0.4.0;
  2. contract test{
  3. uint public a=100;
  4. uint public b=a;//变量赋值给变量,值传递
  5. function changeIt() public returns(uint){
  6. b=200;
  7. return b;//修改b不会影响原来的变量a
  8. }
  9. function changeIt1(uint m) public pure returns(uint){
  10. m++;
  11. return m;
  12. }
  13. function changeIt1test() public view{
  14. changeIt1(a);//形参传入的是变量的副本拷贝,不会影响原来的变量。
  15. }
  16. }

4.12 constant修饰符

作用与view差不多,只能读取变量,没法修改变量,目前支持int、uint、string、bytes1-32
  1. pragma solidity ^0.4.0;
  2. contract constantTest{
  3. // 测试可以支持的数据类型
  4. uint constant num=100;
  5. uint public num1=100;
  6. int constant num2=100;
  7. string constant num3="100";
  8. bytes1 constant num4=0x11;
  9. bytes constant public num5=new bytes(2);//编译没有报错,但无法修改,只能是0x0000;
  10. //constan全局变量无法修改,编译报错!
  11. /* function changeIt() public{
  12. num=0;
  13. }*/
  14. function change() public pure returns(string){
  15. num5[0]=0x12;
  16. num5[1]=0x12;
  17. return string(num5);
  18. }//并没有修改成功!
  19. function changeIt() public constant returns(uint){
  20. num1=200;
  21. return num1;
  22. }//返回200,但是全局变量num1依旧是100
  23. }

4.13 构造函数

新版本用 constructor(…) {…}作为构造函数
  1. pragma solidity ^0.4.0;
  2. contract Test{
  3. uint public a;
  4. function Test() public{
  5. a=100;
  6. }
  7. }
  8. contract Test1{
  9. uint public b;
  10. function Test1(uint _b) public{
  11. b=_b;
  12. }
  13. }
  14. contract Test2{
  15. address public owner;
  16. constructor() public{
  17. owner=msg.sender;
  18. }
  19. }

4.14 modifier

函数修饰符函数modifier 函数名(参数列表){…}
  1. pragma solidity ^0.4.0;
  2. contract modifierTest{
  3. address public owner;
  4. uint public num=0;
  5. constructor() public{
  6. owner=msg.sender;
  7. }
  8. modifier Onlyowner() {
  9. require(msg.sender==owner);
  10. _;
  11. }//定义的函数修饰符函数,_;在满足require的条件时,会被替换成num=100;否则函数回滚,不会执行num=100;
  12. function change() Onlyowner public{
  13. num=100;
  14. }//用Onlyowner修饰
  15. }
modifier修饰函数作用举例1
  1. contract mappingTest{
  2. //定义两个mapping类型的变量
  3. mapping(address=>uint) public idmapping;
  4. mapping(uint=>string) public namemapping;
  5. //用于代表注册的人数
  6. uint sum=0;
  7. modifier control(){
  8. require(idmapping[msg.sender]==0);
  9. _;
  10. }//通过这个修饰,可以确保每个账户只能注册一次!
  11. //注册,主要是合约调用者的地址=>id;id=>姓名两组映射关系
  12. function register(string name) control public{
  13. address account=msg.sender;
  14. sum++;
  15. idmapping[account]=sum;
  16. namemapping[sum]=name;
  17. }
  18. //根据地址来获得id
  19. function getIdbyAddr(address account) public view returns(uint){
  20. return idmapping[account];
  21. }
  22. //根据id来获得姓名
  23. function getNamebyId(uint id) public view returns(string){
  24. return namemapping[id];
  25. }
  26. }
modifier修饰函数作用举例2
  1. contract modifierTest2{
  2. uint public level=9;
  3. string public name="李知恩";
  4. uint public age=20;
  5. //modifier可以有参数列表
  6. modifier controlLevel(uint _level){
  7. require(level>_level);
  8. _;
  9. }
  10. //不同level才能有不同的权限!
  11. function changename() controlLevel(10) public{
  12. name="IU";
  13. }
  14. function changeage() controlLevel(5) public{
  15. age=18;
  16. }
  17. }
一个函数可以被多个modifier函数修饰符修饰,且执行顺序非常关键。(实际上就是按照修饰符顺序,依次执行,遇到_;会将后面的modifier函数,直接嵌入替换即可!)
  1. contract modifierTest3{
  2. uint public a;
  3. modifier mod1 {
  4. a=1;
  5. _;
  6. a=2;
  7. }
  8. modifier mod2{
  9. a=3;
  10. _;
  11. a=4;
  12. }
  13. //执行顺序:a=1,a=3,a=100,a=4,a=2
  14. function change() mod1 mod2 public{
  15. a=100;
  16. }//最终a=2
  17. //执行顺序:a=3,a=1,a=100,a=2,a=4
  18. function change1() mod2 mod1 public{
  19. a=100;
  20. }//最终a=4
  21. }

4.15 继承

一、合约支持连续继承:儿子继承父亲,父亲继承祖父

  1. contract father is grandfater{...}
  2. contract son is father{...}
1、修饰变量的修饰符在继承中作用:默认、public、internal 可以被继承,private不能被继承,且修饰变量没有external
2、修饰函数的修饰符在继承中的作用:public internal、external可以被继承,private不能被继承

二、合约支持多重继承:儿子继承父亲和母亲

  1. contract son is mom,father{
  2. /*1、继承的属性如果mon和father里面都有,那么按照继承的顺序(这里是mom,father),选择最后一个继承者(这里是father)覆盖掉其他的合约属性*/
  3. //2、自身的属性如果和is后面的父辈合约重复了,那么自身属性会覆盖掉其他合约属性
  4. //3、继承时发生的函数重载以自身为准,父辈合约之间发生函数冲突,也是和属性一样,按继承顺序的最后一个继承者为准
  5. }

4.16 合约销毁

selfdestruct(合约发布者的地址)
  1. pragma solidity ^0.4.0;
  2. contract selfdestructTest{
  3. uint public money;
  4. address owner;
  5. constructor() public{
  6. owner=msg.sender;
  7. }
  8. function increase() public returns(uint){
  9. money++;
  10. return money;
  11. }
  12. //销毁合约
  13. function kill() public{
  14. if(msg.sender==owner){
  15. selfdestruct(owner);
  16. }
  17. }
  18. }

函数知识小结

1、private 不能被继承,不能在外部被调用,可以在内部被调用

2、internal可以在内部被调用,不能在外部被调用,可以被继承

3、external 不能在内部被调用,只能够在外部调用,可以被继承,如果强行执行,通过”地址.“

4、public权限最大,可以在外部和内部调用,可以被继承

5、pure不会读取全局变量,更不会修改全局变量,一个固定的输入就会有一个固定的输出

6、constant在函数中,和view相同,在全局变量中,只用于bytes1-32,uint,int,string代表数据不能被修改

7、view只读取全局变量的值,不修改它,不消耗gas

8、payable转账的时候必须要加的关键字

9、命名参数{形参1名字:值1,形参2名字:值2}

10、函数可以有多个返回值

public 修饰变量时,会默认生成一个getter方法
  1. pragma solidity ^0.4.0;
  2. contract getter{
  3. uint public num=100;
  4. //相当于生成了下面注释的getter方法,external属性的
  5. /* function num() external view returns(uint){
  6. return num;
  7. }*/
  8. function getnum() public view returns(uint){
  9. return this.num();
  10. }
  11. mapping(uint=>string) public map;
  12. constructor() public{
  13. map[0]="李知恩";
  14. map[1]="IU";
  15. mapx[0][1][2]="IU";
  16. }
  17. //mapping类型比较特殊,生成的getter方法带有输入参数
  18. /* function map(uint key) external view returns(string){
  19. return map[key];
  20. }*/
  21. function getmap(uint key) public view returns(string){
  22. return this.map(key);
  23. }
  24. //复杂一点的mapping原理也是如上面所述的原理,这里带三个输入参数
  25. mapping(uint=>mapping(uint=>mapping(uint=>string))) public mapx;
  26. }

五、solidity 高级篇

5.1 memor与storage

规则1 – 状态变量

状态变量总是存储在存储区storage中。

  1. pragma solidity ^0.5.0;
  2. contract DataLocation {
  3. // storage
  4. uint stateVariable;
  5. uint[] stateArray;
  6. }

此外,不能显式地标记状态变量的位置。

  1. pragma solidity ^0.5.0;
  2. contract DataLocation {
  3. uint storage stateVariable; // 错误
  4. uint[] memory stateArray; // 错误
  5. }

规则2 – 函数参数与返回值

函数参数包括返回参数都存储在内存memory中。

  1. pragma solidity ^0.5.0;
  2. contract DataLocation {
  3. // storage
  4. uint stateVariable;
  5. uint[] stateArray;
  6. function calculate(uint num1, uint num2) public pure returns (uint result) {
  7. return num1 + num2
  8. }
  9. }

此处,函数参数 uint num1 与 uint num2,返回值 uint result 都存储在内存中。

规则3 – 局部变量

值类型的局部变量存储在内存中。但是,对于引用类型,需要显式地指定数据位置。

  1. pragma solidity ^0.5.0;
  2. contract Locations {
  3. /* 此处都是状态变量 */
  4. // 存储在storage中
  5. bool flag;
  6. uint number;
  7. address account;
  8. function doSomething() public {
  9. /* 此处都是局部变量 */
  10. // 值类型
  11. // 所以它们被存储在内存中
  12. bool flag2;
  13. uint number2;
  14. address account2;
  15. // 引用类型,需要显示指定数据位置,此处指定为内存
  16. uint[] memory localArray;
  17. }
  18. }

不能显式覆盖具有值类型的局部变量。

  1. function doSomething() public {
  2. /* 此处都是局部变量 */
  3. // 值类型
  4. bool memory flag2; // 错误
  5. uint Storage number2; // 错误
  6. address account2;
  7. }

规则4 – 外部函数的参数

外部函数的参数(不包括返回参数)存储在Calldata中。

举例说明
  1. pragma solidity ^0.4.0;
  2. contract memoryTest{
  3. //状态变量,默认是storage,且不能显示说明!
  4. uint[] public arrx;
  5. /* uint[] memory public arrx;
  6. uint[] storage public arrx;*/
  7. function test(uint[] array) public returns(uint){
  8. //array存储在memory内,将值传递给存储在storage内的arrx
  9. arrx=array;
  10. //这里arrx由于是数组,属于引用变量,因此存储都在storage里的Z和arrx等同,且数组在storage内可以修改长度
  11. uint[] storage Z=arrx;
  12. Z[0]=10;
  13. Z.length=10;
  14. //由于Y是存储在memory的,它和arrx除了赋值的关系外,并没有等价的关系,且不能修改长度,Y修改对arrx不影响
  15. uint[] memory Y=arrx;
  16. //Y.length=20;
  17. Y[1]=20;
  18. }
  19. function test2() public view returns(uint[]){
  20. return arrx;
  21. }//返回整个数组内容
  22. function test3() public view returns(uint){
  23. return arrx.length;
  24. }//返回数组长度
  25. }

5.2 变量类型

Solidity中,变量类型有以下几大类:

  • 值类型
  • 地址类型
  • 引用类型

值类型

地址类型

地址类型表示以太坊地址,长度为20字节。地址可以使用.balance方法获得余额,也可以使用.transfer方法将余额转到另一个地址。

  1. address x = 0x212;
  2. address myAddress = this;
  3. if (x.balance < 10 && myAddress.balance >= 10)
  4. x.transfer(10);

引用类型/复合数据类型

Solidity中,有一些数据类型由值类型组合而成,相比于简单的值类型,这些类型通常通过名称引用,被称为引用类型。

引用类型包括:

  • 数组 (字符串与bytes是特殊的数组,所以也是引用类型)
  • struct (结构体)
  • map (映射)

5.2 struct

定义与初始化

  1. pragma solidity ^0.4.0;
  2. contract structTest{
  3. //结构体的定义
  4. struct student{
  5. uint grade;
  6. string name;
  7. }
  8. //结构体的定义
  9. struct student1{
  10. uint grade;
  11. string name;
  12. // student1 stu;结构体内部不能包含自己本身,但是可以是动态长度的数组,也可以是映射
  13. student1[] stu;
  14. mapping(uint=>student1) hahah;
  15. }
  16. //结构体的初始化
  17. function init() public pure returns(uint,string){
  18. /*struct在函数体内默认是storage,而具体画的结构体student(100, "IU");则存储在memory中,因此需要显示定义结构体指针s在memory*/
  19. student memory s=student(100, "IU");
  20. return(s.grade,s.name);
  21. }
  22. //初始化的第二种方式
  23. function init2() public pure returns(uint,string){
  24. student memory s =student({grade: 100, name: "IU1"});
  25. return(s.grade,s.name);
  26. }
  27. }

结构体内的mapping

初始化结构体时不能初始化mapping,且操作结构体内的mapping需要storage类型
  1. pragma solidity ^0.4.0;
  2. contract structTest{
  3. //结构体的定义
  4. struct student{
  5. uint grade;
  6. string name;
  7. mapping(uint=>string) map;
  8. }
  9. student IU;//1、默认是storage类型,只有storage类型才能操作结构体内的mapping
  10. //结构体的初始化
  11. function init() public returns(uint,string,string){
  12. //结构体初始化不需要初始化mapping
  13. student memory s=student(100, "IU");
  14. //2、memory对象不能操作结构体内的mapping
  15. //s.map[0]="李知恩";编译出错
  16. //3、通过将memory对象s赋值给storage对象,才能操作结构体内的mapping
  17. IU=s;
  18. IU.map[0]="李知恩";
  19. return(s.grade,s.name,IU.map[0]);
  20. // return(IU.grade,IU.name,IU.map[0]);返回值一样
  21. }
  22. }

结构体作为形参

前提:1、函数必须是internal 2、不能直接将形参赋值给storage指针
  1. pragma solidity ^0.4.0;
  2. contract structTest{
  3. //结构体的定义
  4. struct student{
  5. uint grade;
  6. string name;
  7. }
  8. /*1、必须是internal修饰 2、结构体变量stu是storage指针,不能和memory指针直接赋值(这就好像两个指向不同区域的指针是没法赋值一样的意思)*/
  9. function test1(student memory s) internal{
  10. student stu=s;
  11. }//编译不通过
  12. //默认就是memory,编译不通过
  13. /* function test1(student s)internal{
  14. student stu=s;
  15. }*/
  16. //编译通过了
  17. /* function test1(student storage s)internal{
  18. student stu=s;
  19. }*/
  20. }

情况1:storage转storage

  1. pragma solidity ^0.4.0;
  2. contract structTest{
  3. //结构体的定义
  4. struct student{
  5. uint grade;
  6. string name;
  7. }
  8. //stu是状态变量,存储在storage区域
  9. student stu=student(100,"李知恩");
  10. function test1(student storage s)internal{
  11. student storage IU=s;//内存中的s指针通过test1(stu)后,指向了storage区域的stu对象
  12. IU.name="IU";//通过IU指针间接操作了storage区域的stu对象
  13. }
  14. function call() public returns(string){
  15. test1(stu);//传递的实际是指向stu对象的指针
  16. return stu.name;
  17. }//返回IU
  18. }

情形2:memory转storage

  1. pragma solidity ^0.4.0;
  2. contract structTest{
  3. //结构体的定义
  4. struct student{
  5. uint grade;
  6. string name;
  7. }
  8. //stu是状态变量,存储在storage区域
  9. student stu=student(10,"李知恩");
  10. function test1(student memory s)internal{
  11. stu=s;//将s的值赋给了stu,stu变为student(100,"IU")
  12. s.name="love";//修改memory上的s,不会影响storage上的stu
  13. }
  14. function call() public returns(uint,string){
  15. student memory IU=student(100,"IU");//IU是局部变量,存储在memory中
  16. test1(IU);//值传递,将IU的值传递给了形参s(也存储在memory当中)
  17. return (stu.grade,stu.name);//返回100,"IU"
  18. }
  19. }

情形3:storage转memory

  1. pragma solidity ^0.4.0;
  2. contract structTest{
  3. //结构体的定义
  4. struct student{
  5. uint grade;
  6. string name;
  7. }
  8. //stu是状态变量,存储在storage区域
  9. student stu=student(10,"李知恩");
  10. function test1(student storage s) pure internal returns(uint,string){
  11. student memory IU=s;//在memory中,s根据传递过来的stu指针,对stu对象进行了副本copy,赋值给了IU
  12. IU.name="IU";//在memory内的IU进行修改,不会影响存储在storage中的stu
  13. return(IU.grade,IU.name);
  14. }
  15. function call() public view returns(uint,string){
  16. test1(stu);//传递的是指向stu的指针
  17. return (stu.grade,stu.name);
  18. }//返回(10,"李知恩")
  19. //由于internal外部无法调用,因此采用这种方式在外部间接调用test1,结果说明内存中的IU的值发生了改变
  20. function test() public view returns(uint,string){
  21. return test1(stu);
  22. }//返回(10,"IU")
  23. }

情形4:memory转memory

  1. pragma solidity ^0.4.0;
  2. contract structTest{
  3. //结构体的定义
  4. struct student{
  5. uint grade;
  6. string name;
  7. }
  8. function test1(student memory s) pure internal {
  9. student memory IU=s;//s和IU都指向了stu的存储的memory区域
  10. IU.name="IU";//通过IU间接修改了stu对象
  11. IU.grade=18;
  12. }
  13. function call() public pure returns(uint,string){
  14. student memory stu=student(100,"李知恩");
  15. test1(stu);//memory上的对象stu,传递的是指向它的指针(这里比较特殊,属于本身对内存的一个优化操作)
  16. return (stu.grade,stu.name);
  17. }//返回(18,"IU")
  18. }

5.3 enum

enum 枚举体名字{枚举1,枚举2…} 枚举1-N不能有汉字,本质上枚举1数值为0,枚举2数值为1,以此类推
枚举体主要用于状态的切换,例如从状态1切换到状态2,让代码具有可读性!
  1. pragma solidity ^0.4.0;
  2. contract enumTest{
  3. enum state{s1,s2,s3,s4,s5}
  4. state step=state.s1;
  5. function getEnum() public view returns(state){
  6. return step;
  7. }//返回uint8:0(也是看枚举个数,按最小标准存储)
  8. function firstStep() public returns(string){
  9. require(step==state.s1);//必须完成s1才能切换到s2
  10. step=state.s2;
  11. return "s1 over!";
  12. }
  13. function sencondStep() public returns(string){
  14. require(step==state.s2);//必须完成s2才能切换到s3
  15. step=state.s3;
  16. return "s1,s2 are over!";
  17. }
  18. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/768846
推荐阅读
相关标签
  

闽ICP备14008679号