创建一个创世块
IBFT 工具可以自动创建创世块。同时,它还会生成节点密钥、从节点密钥生成的地址和 static-nodes.json
文件。
运行以下命令以生成所有这些内容:
./istanbul setup --num 4 --nodes --verbose • 1
现在,你会得到类似的输出:
validators { "Address": "0x05a6245732c2350ba2ed64e840394c2239f8ad1f", "Nodekey": "eae5093e524bf14ba6e95c13591d6a785be9ea486b9e8e9c1281314f75a3d4f9", "NodeInfo": "enode://bd1049d796f1b71bef17d428ce8db5f22e478ecbeb9513c57e90d93ca1e9ec107f4f4b43585556ca8bb3ab630f1f6543d0d4147f5d890e1fde301b2af1fd7a08@0.0.0.0:30303?discport=0" } { "Address": "0x97a80dc7a7e27f41ae006fa1253f1f105f77335c", "Nodekey": "decc1787fda1f4079511bcff92e83f868755c8e06636303c42cfb3cce554919e", "NodeInfo": "enode://6344e12a9b3f4fd5c154ee13ebe5351a5460a44302fd493a5e742adf8a294b6dc112fab1fa8ff19dde0027373c96c51ab6254153877c9fadabfc057624e522f0@0.0.0.0:30303?discport=0" } { "Address": "0xf69faf33e8690e82b0043e9131e09bbbc394cbed", "Nodekey": "7e1a7660f4ec525096ebea34a7a3b78803138fbaaa3f61b7dc13439ce3e08c95", "NodeInfo": "enode://0955966accd8f36256e876790c9b66098675f7ac6bfc10b805d7356d66844cf696902b8dadb62c44cdb783db69197ebacc709ab1908229fe7e13be3f1eae35fe@0.0.0.0:30303?discport=0" } { "Address": "0x68795d3e326b553dc8b2c5739b87a9cb827037c8", "Nodekey": "9f0e0b268671c29c43a0976faa7e08fd20aae24219ad1db6dfc7e645413600c1", "NodeInfo": "enode://a76bf5be8ddd1b1b9bd8d46e5947ccef9c1ce492d4e8fe800e234e61be67a0dbd586e33afb4e17998dc53fa2ea5c72a8a0544c7baae45fc4c16c401c1de90a22@0.0.0.0:30303?discport=0" } static-nodes.json [ "enode://bd1049d796f1b71bef17d428ce8db5f22e478ecbeb9513c57e90d93ca1e9ec107f4f4b43585556ca8bb3ab630f1f6543d0d4147f5d890e1fde301b2af1fd7a08@0.0.0.0:30303?discport=0", "enode://6344e12a9b3f4fd5c154ee13ebe5351a5460a44302fd493a5e742adf8a294b6dc112fab1fa8ff19dde0027373c96c51ab6254153877c9fadabfc057624e522f0@0.0.0.0:30303?discport=0", "enode://0955966accd8f36256e876790c9b66098675f7ac6bfc10b805d7356d66844cf696902b8dadb62c44cdb783db69197ebacc709ab1908229fe7e13be3f1eae35fe@0.0.0.0:30303?discport=0", "enode://a76bf5be8ddd1b1b9bd8d46e5947ccef9c1ce492d4e8fe800e234e61be67a0dbd586e33afb4e17998dc53fa2ea5c72a8a0544c7baae45fc4c16c401c1de90a22@0.0.0.0:30303?discport=0" ] genesis.json { "config": { "chainId": 2017, "homesteadBlock": 1, "eip150Block": 2, "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", "eip155Block": 3, "eip158Block": 3, "istanbul": { "epoch": 30000, "policy": 0 } }, "nonce": "0x0", "timestamp": "0x5a213583", "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000f89af8549405a6245732c2350ba2ed64e840394c2239f8ad1f9497a80dc7a7e27f41ae006fa1253f1f105f77335c94f69faf33e8690e82b0043e9131e09bbbc394cbed9468795d3e326b553dc8b2c5739b87a9cb827037c8b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0", "gasLimit": "0x47b760", "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", "alloc": { "05a6245732c2350ba2ed64e840394c2239f8ad1f": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" }, "68795d3e326b553dc8b2c5739b87a9cb827037c8": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" }, "97a80dc7a7e27f41ae006fa1253f1f105f77335c": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" }, "f69faf33e8690e82b0043e9131e09bbbc394cbed": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" } }, "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" } • 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
你会看到不同的地址、enode 等。现在,创建 static-nodes.json
、genesis.json
和 enode
密钥文件,并将前述内容放入其中。将节点密钥文件名设为 enode_id_1
、enode_id_2
、enode_id_3
和 enode_id_1
。将 enode URL 中的端口更改为 23000
、23001
、23002
和 23003
。
现在,让我们生成一个以太坊账户,并在创世块中分配一些以太币给它。以太币不是动态生成的,因此我们需要预先提供。使用以下命令生成以太坊账户:
./geth --datadir ./accounts account new • 1
现在,将 accounts/keystore
目录中的文件名更改为 key1
。然后将地址复制,放入 genesis
文件中,并分配一些余额。例如,如果我新生成的账户地址是 0x65d8c00633404140986e5e23aa9de8ea689c1d05
,那么我的 genesis
文件内容将如下所示:
{ "config": { "chainId": 2017, "homesteadBlock": 1, "eip150Block": 2, "eip150Hash": "0x000000000000000000000000000000000000000000 0000000000000000000000", "eip155Block": 3, "eip158Block": 3, "istanbul": { "epoch": 30000, "policy": 0 } }, "nonce": "0x0", "timestamp": "0x5a213583", "extraData": "0x00000000000000000000000000000000000000000000 00000000000000000000f89af8549405a6245732c2350ba2ed64e840 394c2239f8ad1f9497a80dc7a7e27f41ae006fa1253f1f105f77 335c94f69faf33e8690e82b0043e9131e09bbbc394cbed9468795 d3e326b553dc8b2c5739b87a9cb827037c8b841000000000000000 0000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000 00000000000000000000000c0", "gasLimit": "0x47b760", "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7 420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", "alloc": { "05a6245732c2350ba2ed64e840394c2239f8ad1f": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" }, "68795d3e326b553dc8b2c5739b87a9cb827037c8": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" }, "97a80dc7a7e27f41ae006fa1253f1f105f77335c": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" }, "f69faf33e8690e82b0043e9131e09bbbc394cbed": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" }, "65d8c00633404140986e5e23aa9de8ea689c1d05": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" } }, "number": "0x0", "gasUsed": "0x0", "parentHash": "0x00000000000000000000000000000 00000000000000000000000000000000000" } • 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
起始节点
现在,在我们启动节点之前,我们需要初始化它们:为每个节点创建数据目录,将账户密钥复制到数据目录,复制验证者的 enode 密钥,并使用创世块引导区块链。
以下是为所有六个节点实现这些的命令:
#Configuring Node 1 mkdir -p qdata/node1/{keystore,geth} cp accounts/keystore/key1 qdata/node1/keystore cp static-nodes.json qdata/node1 cp enode_id_1 qdata/node1/geth/nodekey ./geth --datadir qdata/node1 init genesis.json #Configuring Node 2 mkdir -p qdata/node2/geth cp static-nodes.json qdata/node2 cp enode_id_2 qdata/node2/geth/nodekey ./geth --datadir qdata/node2 init genesis.json #Configuring Node 3 mkdir -p qdata/node3/geth cp static-nodes.json qdata/node3 cp enode_id_3 qdata/node3/geth/nodekey ./geth --datadir qdata/node3 init genesis.json #Configuring Node 4 mkdir -p qdata/node4/geth cp static-nodes.json qdata/node4 cp enode_id_4 qdata/node4/geth/nodekey ./geth --datadir qdata/node4 init genesis.json #Configuring Node 5 mkdir -p qdata/node5/geth cp static-nodes.json qdata/node5 ./geth --datadir qdata/node5 init genesis.json #Configuring Node 6 mkdir -p qdata/node6/geth cp static-nodes.json qdata/node6 ./geth --datadir qdata/node6 init genesis.json • 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
上述命令是不言自明的。对于最后两个节点,我们没有生成任何 enode 密钥,因为 geth
如果不存在,则会自动生成一个。现在,运行以下命令启动 Quorum 节点。在新的 shell 窗口中运行每个命令:
./geth --datadir qdata/node1 --mine --port 23000 --ipcpath "./geth.ipc" --istanbul.requesttimeout 5000 --istanbul.blockperiod 1 --istanbul.blockpausetime 20 console ./geth --datadir qdata/node2 --mine --port 23001 --ipcpath "./geth.ipc" --istanbul.requesttimeout 5000 --istanbul.blockperiod 1 --istanbul.blockpausetime 20 console ./geth --datadir qdata/node3 --mine --port 23002 --ipcpath "./geth.ipc" --istanbul.requesttimeout 5000 --istanbul.blockperiod 1 --istanbul.blockpausetime 20 console ./geth --datadir qdata/node4 --mine --port 23003 --ipcpath "./geth.ipc" --istanbul.requesttimeout 5000 --istanbul.blockperiod 1 --istanbul.blockpausetime 20 console ./geth --datadir qdata/node5 --port 23004 --ipcpath "./geth.ipc" console ./geth --datadir qdata/node6 --port 23005 --ipcpath "./geth.ipc" console • 1 • 2 • 3 • 4 • 5 • 6 • 7
下面是我们刚刚传递的不同选项的含义:
- 在运行验证者时需要
--mine
。 --istanbul.requesttimeout
是最大区块时间(默认值:10000ms
)。--istanbul.blockperiod
是最小区块时间(默认值:1s
)。--istanbul.blockpausetime
是前一个区块中没有交易时的暂停时间。值应大于istanbul.blockperiod
(默认值:2s
)。
要获取网络中所有验证者的列表,您可以使用 istanbul.getValidators()
API。
动态添加或移除验证者
让我们首先看看如何动态添加新的验证节点。要添加验证节点,我们首先需要生成新验证节点的节点密钥和地址。运行以下命令生成它:
./istanbul setup --num 1 --nodes --verbose • 1 • 2
这是我们之前使用的相同命令。现在,我们不需要 genesis
文件或 static-nodes.json
文件。我们只需要节点密钥和地址。创建一个名为 node_id_5
的文件,并将节点密钥放入其中。运行以下命令初始化新的验证者:
#Configuring Node 7 mkdir -p qdata/node7/geth cp static-nodes.json qdata/node7 cp enode_id_5 qdata/node7/geth/nodekey ./geth --datadir qdata/node7 init genesis.json • 1 • 2 • 3 • 4 • 5
现在,在上述命令成功运行后,是时候让 (2F+ 1) 其他验证者同意插入新的验证者了。为此,在所有其他验证者中运行以下命令:
istanbul.propose("0x349ec6eefe8453a875c4905f5581ea792806a3e5", true) • 1
将第一个参数替换为您获得的新验证节点地址。现在,使用以下命令启动新的验证节点:
./geth --datadir qdata/node7 --mine --port 23006 --ipcpath "./geth.ipc" --istanbul.requesttimeout 5000 --istanbul.blockperiod 1 --istanbul.blockpausetime 20 console • 1
现在,您可以运行 istanbul.getValidators()
来检查网络中所有验证者的列表。现在应该有五个。让我们从网络中移除一个验证者。假设我们想要移除第一个验证者。在第一个验证者的控制台中运行 eth.coinbase
找到其唯一地址。然后,在 (2F + 1) 个验证者中运行以下命令以从网络中移除第一个验证者:
istanbul.propose("0x05a6245732c2350ba2ed64e840394c2239f8ad1f", false) • 1 • 2
在此处,使用您生成的第一个验证节点的地址替换第一个参数。
在移除或添加验证节点时,如果某个验证节点宕机,那么一旦它重新运行起来,它将自动了解到这些更改。
概要
在本章中,我们从以太坊区块链的基础知识开始,然后深入探讨了 Quorum 的特性和共识协议。然后,通过设置星座、Raft 和 IBFT 网络,我们第一次实践了 Quorum。现在,您应该对设置网络的过程感到满意了。下一步是学习编写智能合约,并部署我们的第一个智能合约。我们将在下一章中实现这一点。
第三章:编写智能合约
在上一章中,我们了解了 Quorum 的工作原理以及各种共识协议是如何保护它的。现在我们了解了 Quorum 的工作原理,让我们继续编写智能合约。Quorum 智能合约可以使用许多语言编写;最流行的是Solidity。在本章中,我们将学习 Solidity,并构建一个企业可以用来数字签署文件的 DApp。
在本章中,我们将涵盖以下主题:
- Solidity 源文件的布局
- 理解 Solidity 数据类型
- 特殊变量和合约函数
- 控制结构
- 合约的结构和特性
- 编译和部署合约
本章与作者之前的书籍项目区块链中的章节相同。这不是第二版的书籍,它被用来向读者解释基本概念。
Solidity 源文件
Solidity 源文件的识别方法是通过 .sol
扩展名。它有各种版本,就像通常的编程语言一样。在撰写本书时,最新版本是 0.4.17
。
在源文件中,您可以使用 pragma Solidity
指令来指定编写代码的编译器版本。例如:
pragma Solidity ⁰.4.17; • 1
需要注意的是,源文件不会在早于 0.4.17
或晚于 0.5.0
(此第二个条件使用 ^
添加)的编译器版本下编译。编译器版本在 0.4.17
和 0.5.0
之间的情况最有可能包含 bug 修复,并且不太可能破坏任何内容。
我们可以为编译器版本指定更复杂的规则;表达式遵循 npm
使用的规则。
智能合约的结构
A 类似于一个类。它可以有函数、修改器、状态变量、事件、结构体和枚举。合约也支持继承。您可以通过在编译时复制代码来实现继承。智能合约也可以是多态的。
以下是一个智能合约的示例:
contract Sample { //state variables uint256 data; address owner; //event definition event logData(uint256 dataToLog); //function modifier modifier onlyOwner() { if (msg.sender != owner) throw; _; } //constructor function Sample(uint256 initData, address initOwner){ data = initData; owner = initOwner; } //functions function getData() returns (uint256 returnedData){ return data; } function setData(uint256 newData) onlyOwner{ logData(newData); data = newData; } } • 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
让我们看看上述代码如何工作:
- 首先,我们使用
contract
关键字声明了一个合约。 - 接下来,我们声明了两个状态变量:
data
保存一些数据;owner
保存了他们的以太坊钱包的地址,也就是合约部署的地址。状态变量构成智能合约的状态,并存储在智能合约的存储中。智能合约的存储位于数据库中。 - 然后,我们定义了事件。事件用于客户端通知。我们的事件将在数据更改时触发。所有事件都保留在区块链中。
- 接下来,我们定义了一个修改器函数。修改器在执行函数之前自动检查条件。我们的修改器检查合约所有者是否是调用函数的人。如果不是,则会抛出异常。
- 在此之后,我们有了合约构造函数。它在部署合约时调用。构造函数用于初始化状态变量。
- 最后,我们定义了两种方法。第一种方法获取数据状态变量的值,第二种方法更改数据值。
在更深入研究智能合约功能之前,我们必须学习与 Solidity 相关的一些重要事项。之后,我们将回到合约。
Solidity 中的数据位置
与其他编程语言不同,Solidity 的变量根据上下文存储在内存和数据库中。
总是有一个默认位置,但可以通过附加 storage 或 memory 来覆盖复杂类型的数据,例如字符串、数组和结构体。Memory 是函数参数(包括 return
参数)的默认值,而 storage 适用于局部和状态变量(显然)。
数据位置很重要,因为它们会改变赋值的行为:
- 在存储变量和内存变量之间的赋值中,始终会创建独立的副本。但是,从一个内存存储的复杂类型赋值给另一个内存存储的复杂类型时,不会创建副本。
- 对状态变量进行赋值时,始终会创建独立的副本(即使来自其他状态变量)。
- 存储在内存中的复杂类型不能赋值给局部存储变量。
- 如果状态变量赋给局部存储变量,那么局部存储变量将指向状态变量;基本上,局部存储变量充当指针。
不同类型的数据
Solidity 是一种静态类型语言;变量持有的数据类型需要预定义。所有变量的位默认都被赋值为零。在 Solidity 中,变量是在函数范围内生效;也就是说,无论在函数的任何地方声明的变量都将在整个函数范围内生效。
Solidity 提供了以下数据类型:
- 最简单的数据类型是
bool
。它可以存储true
或false
。 uint8
、uint16
、uint24
,一直到uint256
用于存储 8 位、16 位、24 位,一直到 256 位的无符号整数。同样地,int8
、int16
一直到int256
用于存储 8 位、16 位,一直到 256 位的有符号整数。uint
和int
是uint256
和int256
的别名。ufixed
和fixed
代表分数。ufixed0x8
、ufixed0x16
,一直到ufixed0x256
用于存储 8 位、16 位,一直到 256 位的无符号分数。类似地,fixed0x8
、fixed0x16
,一直到fixed0x256
用于存储 8 位、16 位,一直到 256 位的有符号分数。如果我们有一个需要超过 256 位的数字,那么将使用 256 位数据类型,此时将存储数字的近似值。- 地址(Address)用于存储最多 20 字节的值,通过分配十六进制字面量。它用于存储以太坊地址。您可以在 Solidity 中使用
0x
前缀,将十六进制编码的值赋给变量。
数组
Solidity 支持通用和字节数组,固定大小和动态数组,以及多维数组。
bytes1
、bytes2
、bytes3
,一直到 bytes32
都是字节数组的类型。我们将使用字节表示 bytes1
。
这是一些通用数组语法的示例:
contract sample{ //dynamic size array //wherever an array literal is seen a new array is created. If the //array literal is in state, then it's stored in storage and if it's //found inside function, then its stored in memory //Here myArray stores [0, 0] array. The type of [0, 0] is decided based //on its values. //Therefore, you cannot assign an empty array literal. int[] myArray = [0, 0]; function sample(uint index, int value){ //index of an array should be uint256 type myArray[index] = value; //myArray2 holds pointer to myArray int[] myArray2 = myArray; //a fixed size array in memory //here we are forced to use uint24 because 99999 is the max value and //24 bits is the max size required to hold it. //This restriction is applied to literals in memory because memory is //expensive. As [1, 2, 99999] is of type uint24, myArray3 also has to //be the same type to store pointer to it. uint24[3] memory myArray3 = [1, 2, 99999]; //array literal //throws exception while compiling as myArray4 cannot be assigned to //complex type stored in memory uint8[2] myArray4 = [1, 2]; } } • 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 9 • 10 • 11 • 12 • 13 • 14 • 15 • 16 • 17 • 18 • 19 • 20 • 21 • 22
以下是您应该了解的一些关于数组的重要事项:
- 数组还具有
length
属性,可用于查找数组的长度。还可以将value
分配给length
属性以更改数组大小。但是,内存中的数组或非动态数组无法调整大小。 - 如果尝试访问动态数组的未设置的
index
,则会引发异常。
字符串
在 Solidity 中,可以通过两种方式创建字符串:使用 bytes
和 string
。bytes
用于创建原始字符串,而 string
用于创建 UTF-8 字符串。字符串的长度始终是动态的。
以下是显示 string
语法的示例:
contract sample { //wherever a string literal is seen, a new string is created. If the //string literal is in state, then it's stored in storage and if it's //found inside function, then its stored in memory //Here myString stores "" string. string myString = ""; //string literal bytes myRawString; function sample(string initString, bytes rawStringInit){ myString = initString; //myString2 holds a pointer to myString string myString2 = myString; //myString3 is a string in memory string memory myString3 = "ABCDE"; //here the length and content changes myString3 = "XYZ"; myRawString = rawStringInit; //incrementing the length of myRawString myRawString.length++; //throws exception while compiling string myString4 = "Example"; //throws exception while compiling string myString5 = initString; } } • 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
结构体
Solidity 结构体。以下是 struct
语法的示例:
contract sample{ struct myStruct { bool myBool; string myString; } myStruct s1; //wherever a struct method is seen, a new struct is created. If //the struct method is in state, then it's stored in storage //and if it's found inside function, then its stored in memory myStruct s2 = myStruct(true, ""); //struct method syntax function sample(bool initBool, string initString){ //create an instance of struct s1 = myStruct(initBool, initString); //myStruct(initBool, initString) creates an instance in memory myStruct memory s3 = myStruct(initBool, initString); } } • 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 9 • 10 • 11 • 12 • 13 • 14 • 15 • 16 • 17 • 18 • 19 • 20 • 21
枚举
Solidity 枚举。以下是 enum
语法的示例:
contract sample { //The integer type which can hold all enum values and is the //smallest is chosen to hold enum values enum OS { Windows, Linux, OSX, UNIX } OS choice; function sample(OS chosen){ choice = chosen; } function setLinuxOS(){ choice = OS.Linux; } function getChoice() returns (OS chosenOS){ return choice; } } • 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 9 • 10 • 11 • 12 • 13 • 14 • 15 • 16 • 17 • 18 • 19
映射
哈希表 是一种映射数据类型。由于映射只能存在于存储中,因此它们被声明为状态变量。您可以将映射看作具有 key
和 value
对的数据结构。key
实际上不会被存储;相反,将 key
的 keccak256 哈希用于查找 value
。映射没有长度,并且无法分配给另一个映射。
以下是创建和使用 mapping
的示例:
contract sample{ mapping (int => string) myMap; function sample(int key, string value){ myMap[key] = value; //myMap2 is a reference to myMap mapping (int => string) myMap2 = myMap; } } • 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 9 • 10
delete 运算符
delete
运算符可以应用于任何变量以将其重置为其默认值。默认值是所有位都分配为零。
如果我们对动态数组应用 delete
,它将删除所有元素并使长度变为零。如果我们对静态数组应用 delete
,它的所有索引都会被重置。我们也可以对特定的索引应用 delete
,以重置它们。
然而,如果您将 delete
应用于映射类型,则不会发生任何事情。但是,如果您将 delete
应用于映射的 key
,则与 key
关联的值将被删除。
让我们看看 delete
运算符的工作原理,如下所示:
contract sample { struct Struct { mapping (int => int) myMap; int myNumber; } int[] myArray; Struct myStruct; function sample(int key, int value, int number, int[] array) { //maps cannot be assigned so while constructing struct we // ignore the maps myStruct = Struct(number); //here set the map key/value myStruct.myMap[key] = value; myArray = array; } function reset(){ //myArray length is now 0 delete myArray; //myNumber is now 0 and myMap remains as it is delete myStruct; } function deleteKey(int key){ //here we are deleting the key delete myStruct.myMap[key]; } } • 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