2018-10-25笔记
这篇笔记是对官方文档一些内容的提取。顺序是从Introduction to Smart Contracts开始到Solidity In Depth章节结束。
访问状态变量
The keyword public automatically generates a function that allows you to access the current value of the state variable from outside of the contract.
在声明状态变量的时候,使用public关键字,编译器会自动帮你生成getter函数帮助你在外部访问这个变量。如果自己定义一个如下函数,
function minter() external view returns (address) {
return minter;
}
因为状态变量跟方法名一样,因此这样做是没有用的。
Mapping的概念
Mappings can be seen as hash tables which are virtually initialized such that every possible key exists from the start and is mapped to a value whose byte-representation is all zeros.
对于这段话我的理解是,solidity作为一个静态语言,在编译阶段就要确定变量的数据类型,为其分配内存空间。因此映射类型可以看作是一个很大的哈希表,已经包含了几乎所有可能存在的键,然后每一个键映射到0。
交易上链
Transactions are not guaranteed to be included in the next block or any specific future block, since it is not up to the submitter of a transaction, but up to the miners to determine in which block the transaction is included.
If you want to schedule future calls of your contract, you can use the alarm clock or a similar oracle service.
交易上链时间取决于矿工的打包的时间,而不是交易发起人的交易发起时间,如果要知道交易几时上链,可以通过使用回调的方式来获取(之前开发智能合约的时候,用来打印合约地址的提示语)
计算合约地址
the address of a contract is determined at the time the contract is created (it is derived from the creator address and the number of transactions sent from that address, the so-called “nonce”).
合约地址可以通过交易的from
与交易编号计算出来,黄皮书好像有这个算法。
账户的storage
空间
Every account has a persistent key-value store mapping 256-bit words to 256-bit words called storage.
无论是外部账户(即我们平时用的,存有Eth的账户),还是合约账户都有storage
内存空间。
文件导入
import "filename"
;
This statement imports all global symbols from “filename” into the current global scope。- The following example creates a new global symbol symbolName whose members are all the global symbols from "filename".
import * as symbolName from "filename"
;
第一种做法,会将filename中的所有符号导入当前命名空间,存在命名空间污染的问题,类似于Python的from module import *
。第二种做法,将文件中的符号导入到一个该文件的一个新的命名空间中。
整数的相反数
- The expression -x is equivalent to (T(0) - x) where T is the type of x. This means that -x will not be negative if the type of x is an unsigned integer type.
- There is another caveat also resulting from two’s complement representation:
int x = -2**255;
assert(-x == x);
- Exponentiation is only available for unsigned types.
无符号整数的相反数一定也是非负数;
符号整数中,正数相反数一定是负数,但负数相反数不一定是正数。以uint8
为例,其中最小的负数是-128
,最大的正数是127
,那么-(-128)=128
,128
导致溢出到最小值,也就是-128
(从二进制表示的角度看,也可以很容易理解,-127
是10000000
,127
是01111111
,则127+1
就是b01111111 + b00000001 = b10000000
),因此上面会有assert(-x == x)
;
科学记数法只用于无符号整数。
地址变量
The idea behind this distinction is that address payable is an address you can send Ether to, while a plain address cannot be sent Ether.
Address literals can be implicitly converted to address payable.
It might very well be that you do not need to care about the distinction between address and address payable and just use address everywhere.
Solidity0.5有两种地址变量,一种是address
,另一种是address payable
,后者带有transfer()
和send()
可以用来发送比特币。地址字面值(相当于硬编码时候用的字符串,如0x123
)可以隐式转换成address payable
类型。编程时使用address
即可,不用理会他们的区别。
授权调用
the function
delegatecall
can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, …) are taken from the current contract.
使用delegatecall
的时候,代码是从别的合约拿的,其他的(storage
、balance
等)都是从当前合约中获取。delegatecall
主要用于调用library
中的代码。
字面值的运算精度
Number literal expressions retain arbitrary precision until they are converted to a non-literal type.
((2**800 + 1) - 2**800) == 1
(.5 * 8) == 4
使用字面值进行计算的时候,可以获得任意的精度,直到将他们赋值给未字面值的变量。意思是,uint a=2**800
的时候,精度就会损失,因为a
的字节已经限定好了为32字节。
整数除法
Division on integer literals used to truncate in Solidity prior to version 0.4.0, but it now converts into a rational number, i.e.
5 / 2
is not equal to2
, but to2.5
.
在0.5版本前,整数除法是直接截断小数部分,0.5之后会保留小数。
隐式转换
Disregarding types, the value of the expression assigned to b below evaluates to an integer. Because a is of type uint128, the expression
2.5 + a
has to have a proper type, though. Since there is no common type for the type of2.5
anduint128
, the Solidity compiler does not accept this code.uint128 a = 1; uint128 b = 2.5 + a + 0.5;
只有数据类型一致才可以进行算术运算,因此第二行的代码,因为在进行2.5 + a
的时候,需要将2.5
与uint128
之间进行转换,但是小数与整数之间有没有一个通用类型可以进行隐式转换,因此会导致编译不通过。
带符号整型 <=> 无符号整型
In general, an implicit conversion between value-types is possible if it makes sense semantically and no information is lost:
uint8
is convertible touint16
andint128
toint256
, butint8
is not convertible touint256
(because uint256 cannot hold e.g. -1).
这里说明了,无符号整数可以通过隐式转换成带符号整数,但是带符号整数不可以隐式转换成无符号整数,比如无符号整数没有-1
。
数值字面值 => 整型
Decimal and hexadecimal number literals can be implicitly converted to any integer type that is large enough to represent it without truncation.
十进制数与十六进制数的字面值,如果要转换成任何一种整数类型,必须要长度合适,如下所示:
uint8 a = 12; // fine
uint32 b = 1234; // fine
uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456
数值字面值 => bytes
Decimal number literals cannot be implicitly converted to fixed-size byte arrays.
Hexadecimal number literals can be, but only if the number of hex digits exactly fits the size of the bytes type.
As an exception both decimal and hexadecimal literals which have a value of zero can be converted to any fixed-size bytes type.
要想从数值字面值转换成bytes
,需要符合bytes
的长度,其中十进制数只有0
可以转换,举例如下:
bytes2 a = 54321; // 不可以使用非0十进制
bytes2 b = 0x12; // 字节长度不对应,0x12是1个字节
bytes2 c = 0x123; // 字节长度不对应,0x123算是1个半字节
bytes2 d = 0x1234; // fine
bytes2 e = 0x0012; // fine
bytes4 f = 0; // fine
bytes4 g = 0x0; // fine
数组访问越界
Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion. Increasing the length adds new zero-initialised elements to the array. Reducing the length performs an implicit :ref:delete on each of the removed elements.
越界访问动态数组,不会使数组自动增长,反而会导致异常使交易回滚。调节数组长度要通过修改.length
值来设置数组大小。增大.length
会增加初始化为0
的元素,减小.length
相当于对末尾元素调用delete
。
delete
delete a
assigns the initial value for the type toa
.
I.e. for integers it is equivalent toa = 0
, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset.For structs, it assigns a struct with all members reset.
对一个变量使用delete
,实际上就是他赋值为初始值(因此对于指针类型要注意,他只会把指针重置,但是不会对其所指向的元素调用delete
)。比如uint a
,他的默认初始值使0
,那么delete a
等价于a = 0
。delete arr
则将数组长度设置为0
,对于元素就是逐个地调用delete
。
对Mapping使用delete
delete
has no effect on mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted: Ifa
is a mapping, thendelete a[x]
will delete the value stored atx
.
对Mapping
调用delete
是没有任何影响的,但是对Mapping
的某个键值对delete
,则发生的与上面所述的一致。对struct
调用delete
的时候,会遍历所有的非Mapping
元素,并且会递归的执行。
struct不允许自包含
It is not possible for a struct to contain a member of its own type, although the struct itself can be the value type of a mapping member or it can contain a dynamically-sized array of its type.
Bytes与Strings
As a rule of thumb, use bytes for arbitrary-length raw byte data and string for arbitrary-length string (UTF-8) data. If you can limit the length to a certain number of bytes, always use one of bytes1 to bytes32 because they are much cheaper.
Bytes与Strings的区别就相当于,Python3中Bytes和String的区别。
数组字面值
The type of an array literal is a memory array of fixed size whose base type is the common type of the given elements.The type of
[1, 2, 3]
isuint8[3] memory
, because the type of each of these constants isuint8
. Because of that, it is necessary to convert the first element in the example above touint
.
Note that currently, fixed size memory arrays cannot be assigned to dynamically-sized memory arrays, i.e. the following is not possible:// The next line creates a type error because uint[3] memory // cannot be converted to uint[] memory. uint[] memory x = [uint(1), 3, 4];
首先一点是数组字面值是输入memory
的;第二,数组中的整型字面值默认是uint8
;第三,数组字面值不可以赋值给动态数组变量。
函数返回值
Return parameters can be used as any other local variable and they are zero-initialized; if they are not explicitly set, they stay zero.
函数返回值可以作为函数的局部变量使用,并且默认初始化为0
。
IF判断
Note that there is no type conversion from non-boolean to boolean types as there is in C and JavaScript, so
if (1) { ... }
is not valid Solidity.
非布尔类型不会隐式转换成布尔类型!因此条件判断的时候,要通过逻辑操作符等返回布尔值。
0.5版本的FOR循环
Variables declared in the initialization part of a for-loop are only visible until the end of the for-loop.
For初始化部分声明的变量,仅在For循环中可见,For循环结束后,变量就不存在了。
pragma solidity >0.4.99 <0.6.0;
contract hello {
function f() pure public {
int j;
int k;
for(int i=0; i < 10; i++) {
j++;
}
k = i + 1;
}
}