Docker-镜像、容器与仓库
Docker 的三个基本概念是镜像(Image)、容器(Container)和仓库(Repository),理解了这三个概念基本就理解了 Docker。
Docker 的三个基本概念是镜像(Image)、容器(Container)和仓库(Repository),理解了这三个概念基本就理解了 Docker。
项目进行过程中知识点的总结
翻译自:Fravoll-String Equality Comparison
比较两个给定字符串是否相等,是 Solidity 编程中最常见的一种情况,但语言本身并没有提供内置函数用于字符串比较,本文介绍两种可用方法并分析 Gas 消耗情况。
第一种方法是使用 Ethereum 基金会提供的 StringUtils 库,它对每个字符进行成对比较,如果有一个字符对不匹配,则返回false。这种办法可以返回正确的结果,对于短字符串和字符不同发生在字符串前面的情况仅消耗少量 Gas。但是对于相等的字符串和长字符串,这种方法的 Gas 消耗较高,因为必须做很多比较才能得到正确结果。因此,字符串比较的两个可衡量的因素是字符串平均长度和正确率。
作者提出使用哈希函数进行比较,同时检查所提供的字符串的长度,从一开始就剔除长度不匹配的字符串。其步骤如下
bytes 类型完成,因为 bytes 类型有内置长度函数。如果相同进入第2步,如果不相同返回结果;keccak256() 函数对两个字符串求哈希,然后比较计算得到的哈希值,从而确定是否相等。一个示例代码如下
| |
abi.encodePacket(...) returns (bytes) 用于对给定参数执行紧打包编码,官方文档中不推荐使用 keccak256(...) 直接计算哈希,而是使用 keccak256(abi.encodePacked(...))
在 Remix 编写代码测试了三种不同情况的字符串比较的 Gas 消耗
结果如下表所示,输入列为输入的待比较字符串,输出列的单位为 Wei
| Input A | Input B | Hash | Character + Length | Hash + Length |
|---|---|---|---|---|
| abcdefghijklmnopqrstuvwxyz | abcdefghijklmnopqrstuvwxyz | 1225 | 7062 | 1261 |
| abcdefghijklmnopqrstuvwxyX | abcdefghijklmnopqrstuvwxyz | 1225 | 7012 | 1261 |
| Xbcdefghijklmnopqrstuvwxyz | abcdefghijklmnopqrstuvwxyz | 1225 | 912 | 1261 |
| aXcdefghijklmnopqrstuvwxyz | abcdefghijklmnopqrstuvwxyz | 1225 | 1156 | 1261 |
| abXdefghijklmnopqrstuvwxyz | abcdefghijklmnopqrstuvwxyz | 1225 | 1400 | 1261 |
| abcdefghijkl | abcdefghijklmnopqrstuvwxyz | 1225 | 690 | 707 |
| a | a | 1225 | 962 | 1261 |
| ab | ab | 1225 | 1156 | 1261 |
| abc | abc | 1225 | 1450 | 1261 |
可以看出,哈希+字符串长度 的比较方式 Gas 消耗更加稳定,这种方式比较高效。
Solidity 有两种函数调用:内部调用(Internal Function Calls)和外部调用(External Function Calls)。前者指直接或递归地调用合约内部函数,不会产生实际的 EVM 调用,因此也被称为「消息嗲用」,后者指从合约外部调用合约中的函数,会产生一个 EVM 调用。
因此,函数和状态变量有四种可见性。函数可以指定为 external,public,internal 或 private,对于状态变量, 默认是 internal 且不能设置为 external。
external:外部函数作为合约接口的一部分,意味着我们可以从其他合约和交易中调用。 一个外部函数 f 不能从内部调用(即 f 不起作用,但 this.f()可以)。 当收到大量数据的时候,外部函数有时候会更有效率,因为数据不会从calldata复制到内存.public:public 函数是合约接口的一部分,可以在内部或通过消息调用。对于 public 状态变量, 会自动生成一个 getter 函数(见下面)。internal:这些函数和状态变量只能是内部访问(即从当前合约内部或从它派生的合约访问),不使用 this 调用。private:private 函数和状态变量仅在当前定义它们的合约中使用,并且不能被派生合约使用。合约中的所有内容对外部观察者都是可见的。设置一些
private类型只能阻止其他合约访问和修改这些信息, 但是对于区块链外的整个世界它仍然是可见的。
可见性标识符的定义位置,对于状态变量来说是在类型后面,对于函数是在参数列表和返回关键字中间,如下例
| |
在下面的例子中,D 可以调用 c.getData() 来获取状态存储中 data 的值,但不能调用 f 。 合约 E 继承自 C ,因此可以调用 compute。
| |
编译器自动为所有 public 状态变量创建 getter 函数。对于下面给出的合约,编译器会生成一个名为 data 的函数, 该函数没有参数,返回值是一个 uint 类型,即状态变量 data 的值。 状态变量的初始化可以在声明时完成。
| |
getter 函数具有外部(external)可见性。如果在内部访问 getter(即没有 this. ),它被认为一个状态变量。 如果使用外部访问(即用 this. ),它被认作为一个函数。
| |
如果你有一个数组类型的 public 状态变量,那么你只能通过生成的 getter 函数访问数组的单个元素。 这个机制以避免返回整个数组时的高成本gas。 可以使用如 data(0) 用于指定参数要返回的单个元素。 如果要在一次调用中返回整个数组,则需要写一个函数,例如:
| |
现在可以使用 getArray() 获得整个数组,而 myArray(i) 是返回单个元素。
下一个例子稍微复杂一些:
| |
这将会生成以下形式的函数
| |
请注意,因为没有好的方法来提供映射的键,所以结构中的映射被省略。
之前的实验合约间的调用没有成功,这次就仔细地研究一下合约间地调用机制。分为两种情况
本文提到的合约调用方法的实质是抽象合约的使用。
下面的智能合约中,Main和Add两个合约定义在一个Main.sol文件中,可以同时编译,然后逐个部署。
| |
以使用Remix为例,点击编译按钮编译Main.sol文件,将会同时编译Main和Add两个合约。

然后首先部署Add合约,因为Main合约的部署需要Add的合约地址作为参数。切换到部署和运行选项卡,选择Add合约,点击Deploy,成功部署后,复制合约地址。

然后重新选择Main合约,填入Add合约地址作为参数,点击部署按钮。

测试合约间调用,由合约内容可知,Main合约中的Addnumber函数调用了Add合约的add5函数,传入参数为10,得到的结果应为15。展开左侧的Deployed Contracts,点击Addnumber进行调用,结果如下。

这一次我们测试不同sol文件的智能合约调用,来一个复杂一点的,两个合约分别是Add.sol和Main.sol。
Add.sol使用了一个结构体来定义数值,并通过映射定义查找表来寻找这个值。文件中定义了两个函数,numRegister用来向表中添加数值,addValue用来将从表中查到的指定值+5返回。之所以用这个结构是因为我们的项目里用到了,这里来测试一下可不可行。
| |
Main.sol没有多大变化
| |
仍然是先编译部署Add合约,部署后调用numRegister函数写入数值5,并调用addValue函数测试返回。

接着编译部署Main合约,复制Add合约地址作为初始化参数,部署后调用Addnumber函数测试

合约内的调用方法是相同的,都要先实例化,然后传入被调合约地址,接着才能调用。而写在不同sol文件中时,需要额外声明被调合约的抽象合约,有些文章中说使用call,callcode或delegatecall,但并不建议,因为这三个函数都是非常底层的函数,破坏了类型的安全,只能作为最后的手段使用。
详细的解释参考了StackExchange-Calling function from deployed contract
这两个函数修饰词的作用是告诉编译器函数是否会读取/修改状态,view 表示保证不修改状态,pure 表示保证不读取也不修改状态。Solidity v0.4.17 之前没有这两个修饰词,而是使用 constant 关键字,和 view 的含义相同,不过在 v0.5.0 之后被移除,现在只能使用这两个 view 和 pure。
Getter 方法会被自动标记为 view,除此之外,一个 view 修饰的例子如下
| |
view 保证函数不修改状态,以下操作会被认为是修改状态
selfdestruct。view 或者 pure 的函数。pure 保证不读取也不修改状态,不修改的定义上面已经提到,下面的操作被认为是读取状态
address(this).balance 或者 .balance。block,tx, msg 中任意成员 (除 msg.sig 和 msg.data 之外)。pure 的函数。一个 pure 修饰的例子如下
| |
首先声明,Solidity 中支持浮点数定义,但无法赋值和进行计算。文档中对其描述是「目前还不完全支持」,虽然这意味着以后可能会完全支持,但等不及了,下面记录几个可参考的资料。
在智能合约中显式传入地址类型时,可能会出现如下错误
Address checksum
This looks like an address but has an invalid checksum. If this is not used as an address, please prepend ‘00’.
关于该问题的一个讨论见 https://github.com/ethereum/EIPs/issues/55
这是因为合约中现在使用地址类型必须做一个转换,不是简单的全部大写字母或小写字母,而是遵循一定的规则,这个规则见 ethereum/EIPs#55
但是网上提供的解决方案一般是使用JS库中的转换函数,在智能合约中无法直接解决,好在,web3提供了一个在线API接口,可以调用其checkAddressChecksum函数对地址进行转换,然后将转换后的结果直接用于合约代码。
在搭建的以太坊私链上进行智能合约部署时,出现了以下问题
| |
出现该错误的原因如错误描述,是当前合约所需的gas超过了区块的最大gas。这可能与参数gasLimit有关。在创世区块的配置文件中,我们使用了默认的配置值,为0x2fefd8,转换为10进制即3141592。
注:在线转换工具