以太坊学习 1 remixd -s D:\GoCode\以太坊 ##连接本地
合约结构 状态变量 变量值会永久存储在合约的存储空间
函数 智能合约中的一个可执行单元
函数修饰符 在函数声明时,增强函数语义,满足条件时才会执行函数
事件 用于日志输出,便于跟踪调试
结构体类型 用户自定义的将几个变量组合在一起形成的类型
枚举类型 特殊的自定义类型,类型的所有值可枚举的情况
类型 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 pragma solidity ^0.4 .20 ; contract Student{ uint _score = 80 ; uint _num = 0 ; function exectue (uint score ) public { changeScore(score); } function changeScore (uint score ) private { _score = score; _num = 90 ; } function getScore ( ) constant returns (uint ) { return _score; } function getNum ( ) constant returns (uint ) { return _num; } }
2.引用类型 不定长字节数组、字符串、数组、结构体
memory:相当于值拷贝
storage:引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pragma solidity ^0.4 .20 ; contract Student{ string _name = "ck" ; function exectue ( ) public { changeName(_name); } function changeName (string storage name ) private { bytes(name)[0 ] = "C" ; } function getName ( ) constant returns (string ) { return _name; } }
3.布尔类型 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 pragma solidity ^0.4 .20 ; contract BoolTest{ uint v1 = 10 ; uint v2 = 20 ; bool b1 = true ; bool b2 = false ; function f1 ( ) constant returns (bool ) { return !b1; } function f2 ( ) constant returns (bool ) { return b1 && b2; } function f3 ( ) constant returns (bool ) { return b1 || b2; } function f4 ( ) constant returns (bool ) { return v1 == v2; } function f5 ( ) constant returns (bool ) { return v1 != v2; } }
4.整型 int(有负号) , uint(无负号),支持int8 到int256,uint同理,int默认为int256,uint默认为uint256
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 pragma solidity ^0.4 .21 ; contract IntegerTest { uint8 a = 10 ; uint8 b = 4 ; uint8 c = 3 ; function f1 ( ) constant public returns (bool ) { return a & b == 0 ; } function f2 ( ) constant public returns (bool ) { return a | b == 14 ; } function f3 ( ) constant public returns (bool ) { return a ^ b == 14 ; } function f4 ( ) constant public returns (bool ) { return ~c == 252 ; } }
5.地址类型 以太坊地址的长度,大小为20个字节,160位,可以用一个uint160编码。地址是所有合约的基础,所有的合约都会继承地址对象,也可以随时用一个地址串得到对应代码进行调用。
地址类型的成员:
属性:balance
方法:send( )、transfer( )、 call( ) 、delegatecall( ) 、callcode( )
1. 地址类型与整型转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 pragma solidity ^0.4 .24 ; contract AddressTest{ address _add1 = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 ; address _add2 = 0x7b96aF9Bd211cBf6BA5b0dd53aa61Dc5806b6AcE ; uint160 public _num = 0 ; address public _add; function addressTOuint160 () public returns (uint160) { _num = uint160(_add1); return _num; } function uint160TOaddress () public returns (address) { _add = address(_num); return _add; } }
2. balance获取余额 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 pragma solidity ^0.4 .24 ; contract addressSender{ address public _owner ; function senderTest ( ) public { _owner = msg.sender; } function getOwnerBalance ( )constant public returns (uint ) { return _owner.balance; } function getInvoker ( )constant public returns (address ) { return msg.sender; } }
4. This和payable转账 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 pragma solidity ^0.4 .24 ; contract addressThisTest{ uint _money; function addressThis ( ) payable public { _money=msg.value; } function getThis ( ) constant public returns (address ) { return this ; } function getBalance ( ) constant public returns (uint256 ) { return this .balance; } function getMoney ( ) constant public returns (uint ) { return _money; } }
5. 转账transfer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pragma solidity ^0.4 .20 ; contract TransferTest{ address CKNO2 = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2 ; uint _money; function transfer ( ) payable public { CKNO2.transfer(msg.value); } function getThis ( ) constant public returns (address ) { return msg.sender; } function getBalance ( ) constant public returns (uint256 ) { return CKNO2.balance; } }
6. 转账send Send在转账时不会向transfer一样抛出异常,而是返回一个false。
1 2 3 4 5 6 7 8 9 10 11 pragma solidity ^0.4 .20 ; contract TransferTest{ address CKNO2 = 0xCF208DD52bB63b08616c1a1bC498b48c544858A8 ; bool res; function transfer ( ) payable public returns (bool ) { res = CKNO2.send(msg.value); return res; } }
6.枚举类型 枚举类型是一种用户自定义类型。他可以显式的转换与整数进行转换,但不能进行隐式转换。显示的转换会在运行时检查数值范围,如果不匹配,将会引起异常。枚举类型应该至少由一名成员,枚举元素默认为uint8,当元素数量足够多时,会自动变为uint16,第一个元素默认为0,使用超出范围的数值时会报错。
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 pragma solidity ^0.4 .0 ; contract enumTest { enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } ActionChoices _choice; ActionChoices defaultChoice = ActionChoices.GoStraight; function setGoStraight (ActionChoices choice ) public { _choice = choice; } function getChoice ( ) constant public returns (ActionChoices ) { return _choice; } function getDefaultChoice ( ) constant public returns (uint ) { return uint(defaultChoice); } function isGoLeft (ActionChoices choice ) constant public returns (bool ) { if (choice == ActionChoices.GoLeft){ return true ; } return false ; } }
7.函数类型 函数本身也是一个特殊的变量,它可以当作变量赋值,也可以作为函数参数进行传递。它包括:
内部类型(internal): 仅能在合约内部调用,如当前代码块中、内部库函数中、继承的函数中。
外部类型(external): 仅能在合约外部调用,由地址和函数签名两部分组成,可作为外部函数调用的参数,或者由外部函数调用返回。
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 pragma solidity ^0.4 .5 ; contract FuntionTest{ uint public v1; uint public v2; function internalFunc ( ) internal { v1 = 10 ; } function externalFunc ( ) external returns (uint res ) { v2 = 20 ; return v2; } function resetV2 ( ) public { v2 = 0 ; } function callFunc ( ) { internalFunc(); this .externalFunc(); } } contract Son is FuntionTest { function callInternalFunc ( ) public { internalFunc(); } } contract FunctionTest1{ uint public v3; function externalCall (FuntionTest ft ) { v3 = ft.externalFunc(); } function resetV3 ( ) public { v3 = 0 ; } }
8.数组 1. 定长数组 bytes1….bytes32
字节不可修改
长度不可修改
支持length(只读)
支持下标索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 pragma solidity ^0.4 .20 ; contract fixedArray { bytes2 public b2 = "ck" ; bytes3 public b3 = "ck" ; uint public len = b3.length; bytes1 public b4 = b2[0 ]; }
2.不定长数组 内容和长度均可修改,包括以下三种:
类型[长度]
动态数组
可以修改
可以改变长度
支持length、push、pop方法
可以使用new关键字创建一个memory的数组,与stroage数组不同的是,不能通过length,push,pop的长度来修改数组大小属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 pragma solidity ^0.4 .20 ; contract unfixedTypeLenTest { uint public len2; function memoryfunc ( ) { uint[] memory value = new uint[](10 ); value[0 ] = 1 ; } uint[] public value2; function storagefunc ( ) { value2 = new uint[](10 ); value2[0 ] = 1 ; value2.length = 20 ; value2.push(2 ); len2 = value2.length; } }
bytes
动态字节数组
引用类型
支持下标索引
支持length、push方法
可以修改
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 pragma solidity ^0.4 .20 ; contract unFixedBytes { bytes public _name = new bytes(1 ); bytes public _name2; function setLength (uint length ) { _name.length = length; } function getLength ( ) constant returns (uint ) { return _name.length; } function setName (bytes name ) { _name = name; } function changeName (bytes1 name ) { _name[0 ] = name; } function setInside ( ) { _name = "helloWorld" ; _name2 = "HELLOWORLD" ; } }
string
动态尺寸的UTF-8编码字符串,是特殊的可变字节数组
引用类型
不支持下标索引
可以修改(需通过bytes转换),bytes和string可以自由转换
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 pragma solidity ^0.4 .20 ; contract unfixedStringTest { string public _name = "lily" ; function nameBytes ( ) constant returns (bytes ) { return bytes(_name); } function nameLength ( ) constant returns (uint ) { return bytes(_name).length; } function changeName ( ) public { bytes(_name)[0 ] = "L" ; } function changeLength ( ) { bytes(_name).length = 15 ; bytes(_name)[14 ] = "x" ; } }
bytes与string之间的转换:
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 pragma solidity ^0.4 .20 ; contract Test { bytes10 b10 = 0x68656c6c6f776f726c64 ; bytes public bs10 = new bytes(b10.length); function fixedBytesToBytes ( ) public { for (uint i = 0 ; i< b10.length; i++){ bs10[i] = b10[i]; } } string greeting = "helloworld" ; bytes public b1; function StringToBytes ( ) public { b1 = bytes(greeting); } string public str3; function BytesToString ( ) public { str3 = string(bs10); } function FiexBytesToString ( ) { } }
9.结构体 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 pragma solidity ^0.4 .20 ; contract Test{ struct Student{ string name; uint age; uint score; string sex; } Student public stu1= Student("CK" , 22 , 100 , "boy" ); Student public stu2 = Student({ name : "YX" , age :23 , score : 99 , sex : "girl" }); Student [] public Students; function assign ( ) public { Students.push(stu1); Students.push(stu2); } }
10.字典 字典(映射)是一对一的键值关系,关键字mapping
语法:
1 mapping(_keyType => _valueType)
键的类型允许除映射外的所有类型,如数组、合约、枚举、结构体。值的类型无限制。
字典可以被视作为一个哈希表,其中所有可能的键已被虚拟化的创建,被映射到一个默认值(二进制表示的零)。在映射表中,我们并不存储键的数据,而是存储它的哈希值用来查找时使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pragma solidity ^0.4 .20 ; contract test { mapping(uint => string) id_names; constructor ( ) public { id_names[0x001 ] = "lily" ; id_names[0x002 ] = "Jim" ; id_names[0x002 ] = "Lily" ; } function getNameById (uint id ) constant public returns (string ) { string storage name = id_names[id]; return name; } }
11.自动推导类型 通过赋值的类型来进行推断。类似于go语言。
函数的参数不可以使用var
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pragma solidity ^0.4 .20 ; contract varTest { string str1 = "hello!" ; uint256 public count = 0 ; function loopTest ( ) public { for (var i = 0 ; i< 2000 ; i++){ count++; if (count > 257 ){ break ; } } var str2 = str1; } }
storage和memory的区别
storage: 成员变量,可以跨函数调用,有点类似于硬盘;
memory: 临时数据存储,类似于电脑的存储,函数的参数可以理解为memory类型;
全局函数/变量 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 pragma solidity ^0.4 .21 ; contract Test { bytes32 public blockhash; address public coinbase; uint public difficulty; uint public gaslimit; uint public blockNum; uint public timestamp; bytes public calldata; uint public gas; address public sender; bytes4 public sig; uint public msgValue; uint public _now; uint public gasPrice; address public txOrigin; function tt ( ) payable public { blockhash = block.blockhash(block.number - 1 ); coinbase = block.coinbase ; difficulty = block.difficulty; gaslimit = block.gaslimit; blockNum = block.number; timestamp = block.timestamp; calldata = msg.data; gas = gasleft(); sender = msg.sender; sig = msg.sig; msgValue = msg.value; _now = now; gasPrice = tx.gasprice; txOrigin = 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 pragma solidity ^0.4 .0 ; contract EthUnit{ uint a = 1 ether; uint b = 10 ** 18 wei; uint c = 1000 finney; uint d = 1000000 szabo; function f1 ( ) constant public returns (bool ) { return a == b; } function f2 ( ) constant public returns (bool ) { return a == c; } function f3 ( ) constant public returns (bool ) { return a == d; } function f4 ( ) pure public returns (bool ) { return 1 ether == 100 wei; } } contract TimeUnit{ function f1 ( ) pure public returns (bool ) { return 1 == 1 seconds; } function f2 ( ) pure public returns (bool ) { return 1 minutes == 60 seconds; } function f3 ( ) pure public returns (bool ) { return 1 hours == 60 minutes; } function f4 ( ) pure public returns (bool ) { return 1 days == 24 hours; } function f5 ( ) pure public returns (bool ) { return 1 weeks == 7 days; } function f6 ( ) pure public returns (bool ) { return 1 years == 365 days; } }
constant、view和pure 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 40 41 42 43 44 45 46 47 pragma solidity ^0.4 .20 ; contract Test { uint public v1 = 10 ; uint constant v2 = 10 ; string str1 = "hello!" ; string constant str2 = "test!" ; function f1 ( ) public { v1 = 20 ; str1 = "Hello!" ; } struct Person { string name; uint age; } function f2 ( ) constant public { v1 = 20 ; } function f3 ( ) pure public returns (uint ) { } }
错误处理 传统方法: if…throw和throw
新方法:require()、assert()、revert()
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 pragma solidity ^0.4 .20 ; contract HasAnOwner { address public owner; constructor ( ) public { owner = msg.sender; } function useSuperPowers ( ) public { require (msg.sender == owner); } }
delete delete操作符可以用于任何变量,将其设置成默认值,而不是销毁
如果对动态数组delete,则删除所有元素,长度变为0
如果对静态数组delete,则重置所有索引的值
如果对map类型使用delete,什么都不会发送
如果对map的一个键进行delete,则会删除与该键相关的值
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 pragma solidity ^0.4 .20 ; contract deleteTest { string public str1 = "hello" ; function delStr ( ) public { delete str1; } function setStr ( ) public { str1 = "HELLO" ; } uint[10 ] public staticArray = [4 ,2 ,4 ,5 ,6 ,7 ,8 ]; uint[] public dynamicArray = new uint[](10 ); function intDynamicArray ( ) public { for (uint i = 0 ; i< 10 ; i++) { dynamicArray.push(i); } } function delStaticArray ( ) public { delete staticArray; } function delDynamicArray ( ) public { delete dynamicArray; } function getArrayLength ( ) view public returns (uint, uint ) { return (staticArray.length, dynamicArray.length); } mapping(uint => bool) public map1; function initMap ( ) public { map1[1 ] = true ; map1[2 ] = true ; map1[3 ] = false ; } function deleMapByKey (uint key ) public { delete map1[key]; } struct Person { string name; mapping(string => uint) nameScore; } Person public p1; function initP1 ( ) public { p1.name = "duke" ; p1.nameScore["duke" ] = 80 ; } function returnP1 ( ) view public returns (string, uint ) { return (p1.name, p1.nameScore["duke" ]); } function deleteP1name ( ) public { delete p1; } function deleteP1Score ( ) public { delete p1.nameScore["duke" ]; } }
modifier修饰器 modifier可以用来改变一个函数的行为,比如用于在函数执行前检查某种前置条件。修饰器是一种合约的属性,可以被继承,同时还可以被派生的合约重写(override)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 pragma solidity ^0.4 .20 ; contract HasAnOwner { address public owner; uint public a ; constructor ( ) public { owner = msg.sender; } modifier ownerOnly (address addr ) { require (addr == owner); _; } function useSuperPowers ( ) ownerOnly (msg.sender ) public { a = 10 ; } }
投票案例 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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 pragma solidity ^0.4 .20 ; pragma experimental ABIEncoderV2; contract VoteContent{ struct Voter{ uint voteNumber; bool isVoild; uint weight; address agent; } struct Candidate{ string name; uint voteCount; } address public admin; Candidate [] public candidates; mapping(address => Voter) public voters; constructor (string[] candidatesNames ) public { admin = msg.sender; for (uint i = 0 ; i < candidatesNames.length ; i++){ Candidate memory tmp = Candidate({ name : candidatesNames[i], voteCount : 0 }); candidates.push(tmp); } } modifier adminOnly ( ) { require (admin == msg.sender); _; } function giveVoteRightTo (address addr ) adminOnly public { if (voters[addr].weight > 0 ){ revert(); } voters[addr].weight = 1 ; } function agentFunc (address to ) public { Voter storage voter = voters[msg.sender]; if (voter.weight <= 0 || voter.isVoild){ revert(); } while (voters[to].agent != address(0 ) && voters[to].agent != msg.sender){ to = voters[to].agent; } require (msg.sender != to); voter.isVoild = true ; voter.agent = to; Voter storage finalAgent = voters[to]; if (finalAgent.isVoild){ candidates[finalAgent.voteNumber].voteCount += voter.weight; }else { finalAgent.weight += voter.weight; } } function vote (uint voteNum ) public { Voter storage voter = voters[msg.sender]; if (voter.weight <= 0 || voter.isVoild){ revert(); } voter.isVoild = true ; voter.voteNumber = voteNum; candidates[voteNum].voteCount += voter.weight; } function whoWin ( ) public returns (string , uint ) { string winner; uint winnerVoteCount; for (uint i = 0 ; i < candidates.length ; i++){ if (candidates[i].voteCount > winnerVoteCount){ winnerVoteCount = candidates[i].voteCount; winner = candidates[i].name; } } return (winner,winnerVoteCount); } }