




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
以太坊基础及应用开发主讲:XXX辅导:XXX课程简介01.以太坊详解03.以太坊智能合约开发02.以太坊开发环境04.应用系统开发实例05.总结1以太坊详解一以太坊详解什么是以太坊?以太坊的本质就是一个基于交易的状态机(transaction-basedstatemachine)。全球就存在一台以分布式形式存在的单机,其系统状态在不停的改变;这台单机的系统状态主要由区块链组成,区块链上保存着状态和交易;当用户与以太坊交互时,其实就是在执行交易、改变系统状态。一以太坊详解如图所示,以太坊架构分为7层,由下至上依次是存储层、数据层、网络层、协议层、共识层、合约层、应用层:1.以太坊体系结构一以太坊详解
(1)存储层主要用于存储以太坊系统运行中的日志数据及区块链元数据,存储技术主要使用文件系统和LevelDB。(2)数据层主要用于处理以太坊交易中的各类数据,如将数据打包成区块,将区块维护成链式结构,区块中内容的加密与哈希计算,区块内容的数字签名及增加时间戳印记,将交易数据构建成Merkle树,并计算Merkle树根节点的hash值等。与比特币的不同之处在于以太坊引入了交易和交易池的概念。交易指的是一个账户向另一个账户发送被签名的数据包的过程。而交易池则存放通过节点验证的交易,这些交易会放在矿工挖出的新区块里。以太坊的Event(事件)指的是和以太坊虚拟机提供的日志接口,当事件被调用时,对应的日志信息被保存在日志文件中。
(3)与比特币一样,以太坊的系统也是基于P2P网络的,在网络中每个节点既有客户端角色,又有服务端角色。1.以太坊体系结构一以太坊详解
(4)协议层是以太坊提供的供系统各模块相互调用的协议支持,主要有HTTP、RPC协议、LES、ETH协议、Whipser协议等。以太坊基于HTTPClient实现了对HTTP的支持,实现了GET、POST等HTTP方法。外部程序通过JSONRPC调用以太坊的API时需通过RPC(远程过程调用)协议。Whisper协议用于DApp间通信。LES的全称是轻量级以太坊子协议(LightEthereumSub-protocol),允许以太坊节点同步获取区块时仅下载区块的头部,在需要时再获取区块的其他部分。(5)共识层在以太坊系统中有PoW(ProofofWork)和PoS(ProofofStake)两种共识算法。(6)合约层分为两层,底层是EVM(EthereumVirtualMachine,即以太坊虚拟机),上层的智能合约运行在EVM中。智能合约是运行在以太坊上的代码的统称,一个智能合约往往包含数据和代码两部分。智能合约系统将约定或合同代码化,由特定事件驱动触发执行。因此,在原理上适用于对安全性、信任性、长期性的约定或合同场景。在以太坊系统中,智能合约的默认编程语言是Solidity,一般学过JavaScript语言的读者很容易上手Solidity。(7)应用层有DApp(DecentralizedApplication,分布式应用)、以太坊钱包等多种衍生应用,是目前开发者最活跃的一层。1.以太坊体系结构一以太坊详解2.以太坊工作流程及运行原理①以太坊区块链的范式阐述
以太坊的本质就是一个基于交易的状态机(transaction-basedstatemachine)。在计算机科学中,状态机是指可以读取一系列的输入,然后根据这些输入,会转换成一个新的状态出来的东西,如图所示。
根据以太坊的状态机,我们从创世纪状态(genesisstate)开始。这差不多类似于一片空白的石板,在网络中还没有任何交易的产生状态。当交易被执行后,这个创世纪状态就会转变成最终状态。在任何时刻,这个最终状态都代表着以太坊当前的状态。一以太坊详解2.以太坊工作流程及运行原理
以太坊的状态有数百万个交易,这些交易都被打包到以太坊区块中。一个区块包含了一系列的交易,每个区块都与它的前一个区块链接起来。
为了让一个状态转换成下一个状态,交易必须是有效的。为了让一个交易被认为是有效的,它必须要经过一个验证过程,此过程也就是挖矿。挖矿就是一组节点(即电脑)用它们的计算资源来创建一个包含有效交易的区块出来。任何在网络上宣称自己是矿工的节点都可以尝试创建和验证区块。世界各地的很多矿工都在同一时间创建和验证区块。每个矿工在提交一个区块到区块链上的时候都会提供一个数学机制的“证明”,这个证明就像一个保证:如果这个证明存在,那么这个区块一定是有效的。为了让一个区块添加到主链上,一个矿工必须要比其他矿工更快的提供出这个“证明”。通过矿工提供的一个数学机制的“证明”来证实每个区块的过程称之为工作量证明(proofofwork)。证实了一个新区块的矿工都会被奖励一定价值的奖赏。以太坊使用一种内在数字代币--以太币(Ether)作为奖赏。每次矿工证明了一个新区块,那么就会产生一个新的以太币并被奖励给矿工。一以太坊详解2.以太坊工作流程及运行原理
前面,我们定义了区块链就是一个具有共享状态的交易单机。使用这个定义,我们可以知道正确的当前状态是一个全球真相,所有人都必须要接受它。拥有多个状态(或多个链)会摧毁这个系统,因为它在哪个是正确状态的问题上不可能得到统一结果。如果链分叉了,你有可能在一条链上拥有10个币,一条链上拥有20个币,另一条链上拥有40个币。在这种场景下,是没有办法确定哪个链才是最“有效的”。
不论什么时候只要多个路径产生了,一个“分叉”就会出现。我们通常都想避免分叉,因为它们会破坏系统,强制人们去选择哪条链是他们相信的链。一以太坊详解2.以太坊工作流程及运行原理为了确定哪个路径才是最有效的以及防止多条链的产生,以太坊使用了一个叫做“GHOST协议”(GHOSTprotocol)的数学机制。简单来说,GHOST协议就是让我们必须选择一个在其上完成计算最多的路径。一个方法确定路径就是使用最近一个区块(叶子区块)的区块号,区块号代表着当前路径上总的区块数(不包含创世纪区块)。区块号越大,路径就会越长,就说明越多的挖矿算力被消耗在此路径上以达到叶子区块。使用这种推理就可以允许我们赞同当前状态的权威版本。一以太坊详解2.以太坊工作流程及运行原理②以太坊系统的主要组件以太坊系统的主要组件包括:账户、账户状态、世界状态、gas和费用、交易、区块、交易执行、挖矿、PoW等。
(1)账户:
以太坊的全球“共享状态”是由很多小的对象(账户)组成,这些账户通过消息传递框架实现彼此交互。每个账户都有一个与之关联的状态以及一个20字节的地址。以太坊中的地址是160位比特的标识符,用于标识任何账户。有两种类型的账户:外部账户(由私钥控制,没有与之关联的代码)和合约账户(由合约代码控制,有与之关联的代码)。一以太坊详解2.以太坊工作流程及运行原理
理解外部账户和合约账户之间的根本区别是非常重要的。通过创建及使用其私钥签名一个交易,外部账户能够给其他外部账户或其他合约账户发送消息。两个外部账户之间的消息只是简单的价值传输。但从外部账户发送到合约账户的消息可以激活合约账户的代码,允许它执行各种操作(例如,转移代币、写入内部存储、发新币、执行计算、创建新合约等。)
与外部账户不同,合约账户无法自行启动新的交易。相反,合约账户仅能够通过响应它们收到的交易来触发自身的交易,比如从外部账户或从其他的合约账户的交易来触发。因此,任何以太坊区块链上发生的操作始终由外部账户所触发的交易来启动。一以太坊详解2.以太坊工作流程及运行原理(2)账户状态:账户状态由四个部分组成:1)Nonce随机数:如果该账户是外部账户,这个数代表从这个账户地址发出来的交易数。如果该账户是合约账户,则该nonce是该账户创建的合约数,每创建一次会加1。2)Balance账户余额:该地址拥有的Wei数,每个Ether有10^18Wei。3)StorageRoot:MerklePatricia树的根节点的哈希值。Merkletree对该账户的存储内容的哈希进行编码,默认情况下为空。4)CodeHash:该账户EVM代码的哈希。对于合约账户,就是被Hash的代码并作为codeHash保存。对于外部账户而言,Codehash字段是空字符串的哈希值。一以太坊详解2.以太坊工作流程及运行原理(3)世界状态:以太坊的全局状态就是由账户地址和账户状态的一个映射组成。这个映射被保存在一个叫做MerklePatricia树的数据结构中。MerkleTree是一种由一系列节点组成的二叉树,这些节点包括:1)在树底的包含了源数据的大量叶子节点。2)一系列的中间的节点,这些节点是两个子节点的Hash值。3)一个根节点,同样是两个子节点的Hash值,代表着整棵树。一以太坊详解2.以太坊工作流程及运行原理树底的数据是通过分开我们想要保存到chunks的数据产生的,然后将chunks分成buckets,再然后再获取每个bucket的hash值并一直重复直到最后只剩下一个Hash:根Hash。一以太坊详解2.以太坊工作流程及运行原理这棵树要求存在里面的值(value)都有一个对应的key。从树的根节点开始,key会告诉你顺着哪个子节点可以获得对应的值,这个值存在叶子节点。在以太坊中,key/value是地址和与地址相关联的账户之间状态的映射,包括每个账户的balance,nonce,codeHash和storageRoot(storageRoot自己就是一颗树)。一以太坊详解2.以太坊工作流程及运行原理同样的树结构也用来存储交易和收据。更具体的说,每个块都有一个头(header),保存了三个不同Merkletrie结构的根节点的Hash,包括:1)状态树。2)交易树。3)收据树。一以太坊详解2.以太坊工作流程及运行原理区块链是靠一群节点来维持的,有两种节点类型:全节点和轻节点。全节点通过下载整条链来进行同步,一个全节点包含了整个链,从创世纪块到当前块,执行其中包含的所有交易。通常,矿工会存储全节点,因为他们在挖矿过程中需要全节点。一个节点如果不需要执行所有的交易或轻松访问历史数据,就没必要保存整条链,这就是轻节点概念的来源。比起下载和存储整个链以及执行其中所有的交易,轻节点仅仅下载链的头,从创世纪块到当前块的头,不执行任何的交易或检索任何相关联的状态。由于轻节点可以访问块的头,而头中包含了3个tries的Hash,所有轻节点依然可以很容易生成和接收关于交易、事件、余额等可验证的答案,因为在Merkle树中hash值是向上传播的。如果一个恶意用户试图用一个假交易来交换Merkle树底的交易,这个会改变它上面节点的hash值,而它上面节点的值的改变也会导致上上一个节点Hash值的改变,以此类推,一直到树的根节点。如图。一以太坊详解2.以太坊工作流程及运行原理任何节点想要验证一些数据都可以通过Merkle证明来进行验证,Merkle证明的组成如下:1)一块需要验证的数据。2)树的根节点Hash。3)一个“分支”(从chunk到根这个路径上所有的hash值)。任何可以读取证明的人都可以验证分支的hash是连贯的,因此给出的块在树中实际的位置就是在此处。使用MerklePatricia树的好处就是该结构的根节点加密取决于存储在树中的数据,而且根据点的hash还可以作为该数据的安全标识。由于块的头包含了状态、交易、收据树的根hash,所有任何节点都可以验证以太坊的一小部分状态而不用保存整个状态,而这整个状态的大小可能是非常大的。一以太坊详解2.以太坊工作流程及运行原理(4)Gas和支付:以太坊有一个非常重要的概念是费用的概念。在以太坊网络上交易而消耗的计算都会产生费用,支付的费用以“gas”来计算。gas是用于衡量特定计算所需费用的单位。gas价格是你愿意花费在每单位gas上的Ether总量,用“gwei”来衡量。“Wei”是Ether的最小单位,1018Wei代表1Ether。1gwei是1,000,000,000Wei。一以太坊详解2.以太坊工作流程及运行原理每次交易,交易发送人(转账人)都会设置gas的limit和gas价格。gas价格和gaslimit代表了发送人愿意为交易支付的最大数量的Wei。例如,我们假设发送人设置gaslimit是50,000,gas价格是20gwei。这意味着交易发送者愿意支付最多50,000*20gwei=1,000,000,000,000,000Wei,也就是0.001Ether用来执行该交易。gaslimit代表了交易发送人愿意支付的最大费用。如果交易发送人账户余额可以覆盖这个最大值,就不会有问题。交易结束时,发送人会收到未被使用的gas资金退款,并按最初价格交易。如果交易发送人没有提供足够的gas来执行交易,交易会用光gas,并且该交易无效。在这种情况下,交易过程中止,发生的任何状态更改都会被逆转,这样交易会结束,并回到交易前的以太坊状态。此外,还会记录交易失败,显示什么交易试图发起并在哪里失败。同时,既然在用光gas之前,机器已经花费了努力进行计算,逻辑上来说,这些花费的gas不会再退还给交易发送人。交易发送人花费的所有gas资金都被发送到“受益人”地址,这通常是矿工的地址。既然矿工花费努力来计算和验证交易,矿工收取gas费用作为奖励。通常,交易发送人愿意支付的gas价格越高,矿工从交易中获得价值越大。因此,矿工也会选择价格高的交易。这样,矿工自由选择他们愿意验证的交易。为了引导交易发送者设置gas价格,矿工可以选择宣传他们会执行交易的最低gas价格。一以太坊详解2.以太坊工作流程及运行原理通常,交易发送人愿意支付的gas价格越高,矿工从交易中获得价值越大。因此,矿工也会选择价格高的交易。这样,矿工自由选择他们愿意验证的交易。为了引导交易发送者设置gas价格,矿工可以选择宣传他们会执行交易的最低gas价格。gas不仅用于支付计算步骤,也用于支付存储费用。存储所需的总体费用跟使用32字节的最小倍数成正比。存储的费用有一些细微差别。比如,既然不断增加的存储增大了所有节点的以太坊状态数据库的大小,那么有动机来保持小的数据存储量。因此,如果交易具有可以清除存储中的条目的步骤,则免除执行该操作的费用,并且为了释放存储空间还可以退还费用。以太坊是图灵完备的语言,智能合约引入了循环语句,这使得以太坊容易受到停顿问题的影响,可能会使程序无限运行下去。如果没有费用,恶意行为者可以通过在交易中执行无限循环,而轻易地破坏网络。因此,计费机制可以保护网络免受恶意攻击。以太坊网络上的存储也有成本,因此存储也是需要付费的。一以太坊详解2.以太坊工作流程及运行原理(5)交易和消息:以太坊是基于交易的状态机,发生在不同账户之间的交易推动着以太坊的全球状态从一个状态转换到另外一个状态。从最根本的意义上来说,交易是加密签名的指令,它由外部账户生成,并序列化,然后提交到区块链上。有两类交易:消息调用和合约创建(即创建新的以太坊合约的交易)。一以太坊详解2.以太坊工作流程及运行原理所有类型的交易均包含以下部分:1)Nonce(随机数):交易发送人发送的交易数量的计数。(跟比特币的Nonce概念不同。)2)Gasprice:交易发送人愿意为执行交易所需的每单位gas支付的Wei的数量。3)Gaslimit:交易发送人愿意为执行交易支付的最大gas数量。数量是设置并预付的,在任何计算完成之前确定。4)To:接收人的地址。如是创建合约的交易,合约账户地址还不存在,所以使用的是空值。5)Value:从发送人转移到接收人的Wei总量。在创建合约的交易中,这个值作为新创建合约账户的初始余额。6)v,r,s:用于生成签名,该签名可以标识交易的发送人。7)Init:仅用于创建合约的交易。它是EVM代码片段,可用来初始化新的合约账户。Init只允许一次,然后被抛弃。首次运行init时,它会返回账户代码的正文,这段代码与合约账户产生永久关联关系。8)数据:仅用于消息调用的可选字段。它是指消息调用的输入数据(即参数)。比如,如果智能合约充当域名注册的服务,对合约的调用可能需要输入字段如域名或IP地址。一以太坊详解2.以太坊工作流程及运行原理(6)区块:区块就是交易的集合,公链或者联盟链将交易打包成区块以后会进行持久化存储。(7)交易执行:所有交易的执行必须满足以下条件:1)交易必须是格式正确的RLP。“RLP”代表“递归长度前缀”,是一种数据格式,它用于编码二进制数据的嵌套数组。RLP是以太坊用于序列化对象的格式。2)有效的交易签名。3)有效交易nonce。一个账户的nonce是从该账户发送过来交易计数。为了有效,交易nonce必须等于发送者账户的nonce。一以太坊详解2.以太坊工作流程及运行原理4)交易的gaslimit必须等于或大于交易使用的固有gas,发送人的账户余额必须有足够的Ether覆盖“预定”gas成本。固有gas包括:为执行交易预先定义的成本gas;与交易一起发送的数据的gas费用(每字节数据或代码相当于零时则是4gas的费用,每非零字节的数据或代码是68gas费用);如果交易是创建合约的交易,则额外增加32000gas。“预定”gas成本包括两个方面:最大的gas成本(交易的gaslimit乘以交易的gas价格);从发送者转移到接收者的总价值(总值)。如果交易满足以上4个条件,交易才会继续执行。在交易执行之前,先从发送人的余额中扣除预定的执行成本,并将发送人的账户的nonce增加1以计入当前的交易。此时,可以算出剩余的gas,它们作为交易的总gaslimit减去使用过的固有gas。一以太坊详解2.以太坊工作流程及运行原理接下来交易开始执行。在交易的执行过程中,以太坊跟踪“子状态”,该子状态是记录交易过程中产生的信息的方法,主要包括:1)自毁集:在交易完成后被抛弃的一组账户。2)日志系列:虚拟机代码执行的归档及可索引的检查点。3)退还余额:交易后退还给发送人账户的金额。接下来,处理交易要求的各种计算。一旦交易要求的所有步骤都被处理完毕,假定没有无效状态,则通过确定要退还给发送人的未使用的gas金额来实现最终状态。除了未使用的gas,发送人还可以从上面提到的“退款余额”中获得一些补贴。一旦发送人获得退款:1)gas的Ether已经给到矿工。2)交易使用的gas被添加到区块gas计数器(它跟踪区块中所有交易使用的总gas,并在验证区块时使用)。3)在自毁集中的所有账户都将被删除。最后,留下新的状态和一组交易创建的日志。一以太坊详解2.以太坊工作流程及运行原理③运行原理(1)合约创建:为了创建新的合约账户,首先使用特殊公式声明新账户地址,然后通过如下方式初始化新账户:1)把nonce(随机数)设置为零。2)如果发送人用交易发送一定量的Ether作为价值,则设置该价值为账户余额。3)从发送人的余额中扣除发送给新账户的价值。4)把存储设置为空。5)把合约的codeHash设置为空字符串的哈希。一旦初始化账户,使用跟随交易发送的init代码实际上能够创建账户。在执行这个初始化代码时会产生多样的变化。根据合约的构造函数,它可能会升级账户的存储、创建其他合约账户、进行其他消息调用等。一以太坊详解2.以太坊工作流程及运行原理当执行初始化合约的代码时,它使用gas。交易不允许使用比剩余gas更多的gas。如果遇到这样的情况,执行导致gas不足的异常,然后退出。如果交易因为gas不足异常而退出,那么,状态会立刻复原到交易之前到那个点,而发送人不会收到gas退款,该gas已在之前执行中用完。但是,如果发送人用交易发送任何Ether价值,即使合约创建失败,Ether价值也会被退回。如果初始化代码执行成功,会支付最终的成功创建合约的成本。这是存储成本,支付费用跟所创建合约代码的大小成正比。如果没有足够的剩余gas来支付最终的成本,交易再次出现gas不足的异常,并中止。如果在交易执行过程中没有发生异常,则剩余的未使用的gas会退还给交易的最初发送人,由此被改变的状态也允许持续存在。一以太坊详解2.以太坊工作流程及运行原理(2)消息调用:消息调用的执行跟合约创建的执行类似,不过存在一些差异。消息调用执行并不包括任何init代码,因为没有创建新的账户。如果数据由交易发送人提供,则它可以包含输入数据。一旦执行,消息调用还有额外的组件,组件包含了输出数据,如果接下来的执行需要该数据,它会被使用。跟合约创建一样,如果因为gas不足或交易无效(如堆栈溢出、无效跳转目标或无效指令),消息调用退出,并且所使用的gas不会退还给最初的调用者。相反,所有剩余的未使用的gas被消耗,并且状态会复原到余额转移之前的点。一以太坊详解2.以太坊工作流程及运行原理(3)执行模式:交易是在VM上执行的,实际处理交易流程的部分协议是以太坊自己的虚拟机,也就是所谓的EVM。EVM是图灵完备的虚拟机,跟其他图灵完备的机器相比,EVM唯一的限制是它跟gas是内在绑定的。也就是说,它的计算量是受gas量内在约束的。EVM有基于堆栈的架构,堆栈计算机使用后进先出堆栈来保存临时值。EVM中每个堆栈项的大小是256位,堆栈最大的大小为1024。EVM有内存,其中的item存储为字寻址字节数组。内存不稳定,意味着它不是持久的。EVM还有存储空间。跟内存不同,存储是稳定的,同时它作为系统状态的一部分进行维持。EVM在虚拟ROM中分别存储程序代码,这些代码只能通过特别指令访问。通过这种方式,EVM跟经典的冯·诺依曼架构不同,其中的程序代码存储在内存或存储器中。EVM也有它自己的语言:“EVM字节代码”。当一个程序员编写以太坊智能合约时,一般来说,会使用高阶语言如Solidity。之后,可以将其编译为EVM可以理解的EVM字节代码。一以太坊详解3.以太坊区块结构及链结构区块就是交易的集合,公链或者联盟链将交易打包成区块以后会进行持久化存储。如图是经典的以太坊区块结构,从中可以看到以太坊区块结构由两部分组成,分别是区块头(header)和区块体(body)两部分。一以太坊详解3.以太坊区块结构及链结构区块头(header):区块头存储了区块的元信息,用来对区块内容进行一些标识、校验、说明等。(1)通用字段:ParentHash:父区块的哈希值。Root:世界状态的哈希,stateDB的RLP编码后的哈希值。TxHash(transactionroothash):交易字典树的根哈希,由本区块所有交易的交易哈希算出。ReceptHash:收据树的哈希。Time:区块产生出来的Unix时间戳。Number:区块号。Bloom:布隆过滤器,快速定位日志是否在这个区块中。一以太坊详解3.以太坊区块结构及链结构(2)公链场景:Coinbase:挖出这个块的矿工地址,挖出区块所奖励的ETH就会发放到这个地址。Difficulty:当前工作量证明算法的复杂度。GasLimit:每个区块Gas的消耗上线。GasUsed:当前区块所有交易使用的Gas之和。MixDigest:挖矿得到的Pow算法证明的摘要,也就是挖矿的工作量证明。nonce:挖矿找到的满足条件的值。Uncle:叔块,和以太坊的共识算法相关。一般而言一个类以太坊的联盟链是需要上面介绍的通用字段的,但是也不绝对,还可能与选择的共识算法,隐私保护策略,设计偏好有关。区块体(Body):区块体包括这个区块打包的所有交易,在一些链的设计中,并不像以太坊区分header和body,而是整合在一起。(1)区块头存储以太坊通过如下方式将区块头转换成键值对存储在LevelDB中:headerPrefix+num+hash->rlp(header)(2)区块体存储区块体的存储方式也是类似:bodyPrefix+num+hash->rlp(block)最初系统产生一个空白的区块(无交易信息),每个新产生的区块的ParentHash指向上一块区块,这样就形成了区块链。2以太坊开发环境二以太坊开发环境当开发者使用Solidity语言编写智能合约时,智能合约实际上就是由Solidity代码编译后的程序,也就是说,智能合约的编译环境就是Solidity的编译环境。而这段程序(智能合约)的执行环境就是EVM。在以太坊中,Solidity编写的智能合约经过编译后会生成一串十六进制字节码,创建后进行调用时,也需要将调用的函数(function)名称和参数转化成一串十六进制字节码写进交易中。当用户通过发起ethsendTransaction或者ethcall创建或者调用智能合约时,就要在交易(Transaction)的data字段填入这个十六进制码。创建智能合约时,EVM会将这段字节码解析成相应的指令符序列,存储到一个新建的智能合约地址下。当用户调用这个智能合约时,以太坊本身会根据交易里的to宇段先获取到这个智能合约的信息,EVM先根据data字段里解析出的具体函数和参数生成具体的指令,再依次执行这些指令得到执行结果,这些操作会涉及对账户状态数据进行更改。二以太坊开发环境1.以太坊开发工具及框架流行的以太坊开发框架:(1)Truffle:最为流行的智能合约开发、测试和部署框架,经常与Ganache(也是由Truffle团队开发)一起搭配使用。(2)Embark:和广泛使用的Truffle类似,Embark也是一个功能强大的DApp开发框架,帮助开发者快速构建和部署DApp。Embark不单可以与以太坊区块链通信,还集成了IPFS/Swarm去中心化存储和Whisper网络通信功能。(3)Populus:用Python语言写的智能合约开发框架。(4)Etherlime:基于ethers.js的DApp开发框架。二以太坊开发环境1.以太坊开发工具及框架集成开发环境(IDE):(1)Remix:一个基于solidity语言的在线智能合约开发IDE,它提供从编译,调试到部署的全流程支持。(2)AtomAtom:AtomAtom编辑器可以结合AtomSolidityLinter,Etheratom等插件进行智能合约开发。(3)Pragma:一个非常简单的solidity合约在线IDE,提供合约的编译、部署与调用支持。(4)SuperblocksStudio:SuperblocksStudio可以帮助你在线编写、编译与部署智能合约,目前处于beta版本。(5)Vimsolidity:有了这个,使用vim也可以写愉快地写solidity了。(6)VisualStudioCode:VSCode是日常用的最多的工具。(7)IntellijSolidityPlugin:JetBrainsIntelliJIdeaIDE上用的solidity插件,支持语法高亮,格式化与代码自动补全。本文选择Truffle这个最为流行的智能合约开发、测试和部署框架进行介绍。本节介绍用于启动以太坊的以太坊客户端Geth、编译智能合约的Solidity编译器和流行的智能合约开发框架Truffle。二以太坊开发环境1.以太坊开发工具及框架1.GethGeth是GoEthereum开源项目的简称,它是使用Go语言编写且实现了Ethereum协议的客户端软件,也是目前用户最多,使用最广泛的客户端。通过Geth客户端与以太坊网络进行连接和交互可以实现账户管理、合约部署、挖矿等众多有趣且实用的功能。Geth启动时需要对创世区块文件和相关参数进行配置。创世区块文件genesis.json是区块链最重要的识别标志之一,每一条区块链都有唯一识别的创世区块文件,如果两台机器启动Geth时所选用的创世区块文件不同,就无法被识别为同一条区块链的成员。因此,同一条联盟链中的所有节点必须使用同一份创世区块文件进行初始化配置。二以太坊开发环境1.以太坊开发工具及框架下面是创世区块文件genesis.json的一个示例:{"config":{"chainId":11,"homesteadBlock":0,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"ethash":{}},"nonce":"0x0","timestamp":"0x5d5cdc87","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x47b760","difficulty":"0x80000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0000000000000000000000000000000000000000":{"balance":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}二以太坊开发环境1.以太坊开发工具及框架根据配置用途可分为三大类:(1)链配置。config项是定义链配置,会影响共识协议,虽然链配置对创世影响不大,但新区块的出块规则均依赖链配置。(2)创世区块头信息配置。nonce:随机数,对应创世区块Nonce字段。timestamp:UTC时间戳,对应创世区块Time字段。extraData:额外数据,对应创世区块Extra字段。gasLimit:必填,燃料上限,对应创世区块GasLimit字段。difficulty:必填,难度系数,对应创世区块Difficulty字段。搭建私有链时,需要根据情况选择合适的难度值,以便调整出块。minHash:一个哈希值,对应创世区块的MixDigest字段。和nonce值一起证明在区块上已经进行了足够的计算。coinbase:一个地址,对应创世区块的Coinbase字段。(3)初始账户资产配置。alloc项是创世中初始账户资产配置。在生成创世区块时,将此数据集中的账户资产写入区块中,相当于预挖矿。这对开发测试和私有链非常好用,不需要挖矿就可以直接为任意多个账户分配资产。二以太坊开发环境1.以太坊开发工具及框架Geth常用命令:(1)初始化创世区块:geth--datadir/path/to/datadirinit/path/to/genesis.json(2)启动私有链:geth--identity"TestNode"--rpc--rpcport"8545"--datadir/path/to/datadir--port "30303"--nodiscoverconsole--identity:指定节点ID。--rpc:表示开启HTTP-RPC服务。--rpcport:指定HTTP-RPC服务监听端口号(默认为8545)。--datadir:指定区块链数据的存储位置。--port:指定和其他节点连接所用的端口号(默认为30303)。--nodiscover:关闭节点发现机制,防止加入同样配置的陌生节点。(3)快速同步模式:geth--fastconsole2>network_sync.log(4)浏览日志:tail-fnetwork_sync.log二以太坊开发环境1.以太坊开发工具及框架(5)账户操作:创建账户:gethaccountnew查看账户:gethaccountlist查看账户余额:eth.getBalance(eth.accounts[])解锁账户:personal.unlockAccount(eth.accounts[],<password>)转账:eth.sendTransaction({from:sender,to:receiver,value:amount,gas:gasAmount})(6)挖矿:设置挖矿进程数目:geth--mine--minerthreads=4开始挖矿:miner.start(8)结束挖矿:miner.stop()查看挖矿速率:miner.getHashrate()查看区块高度:eth.blockNumber查看挖矿账户:eth.coinbase设置挖矿账户:miner.setEtherbase(eth.accounts[0])二以太坊开发环境1.以太坊开发工具及框架(7)检查连接状态:检查是否连接:net.listening连接到的节点个数:net.peerCount添加节点:admin.addPeer()查看添加的节点的信息:admin.peers(8)查看:查看区块信息:eth.getBlock(n)交易信息:eth.getTransaction()查看正在等待确认的交易:eth.getBlock("pending",true).transactions二以太坊开发环境1.以太坊开发工具及框架2.Solidity编译器以太坊官方社区提供了Solidity语言的编译开发工具Solidity项目(/ethereum/solidity)。该项目是用C++编写的,使用者可以根据自己的操作系统下载相应发布版的二进制可执行文件。如果想使用最新的版本,可以同步最新的代码自行编译生成可执行文件。Solidity项目还提供了一个命令行工具:Solc。Solc不仅提供将Solidity编译成字节码的功能,也提供一些智能合约相关的信息,比如可以生成函数的签名(调用智能合约各个函数时的依据)、估算每个函数消耗的Gas等。下面我们展示一些常用的Sole使用方法。Solc命令行工具基本的使用模板:solc[options][input_file...]其中options可以是各种参数,用于指定输出的文件的格式和输出路径等。二以太坊开发环境1.以太坊开发工具及框架Solc最常使用的例子如下:solc--bin-otmp/solcoutputcontract.sol其中,--bin是指将Solidity智能合约编译成十六进制字节码。执行完以上命令后,会在/tmp/solcoutput目录下生成一个以代码中定义的合约名(非文件名)命名的.bin的文件,里面存着编译出来的字节码。除了--bin,Solc也支持其他各种相关格式的输出:--ast:所有源文件的抽象语法树。--ast-json:json格式的抽象语法树。--ast-compact-on:压缩(去空格、空行)过后的json格式抽象语法树。--asm:EVM的汇编语言。--asm-json:json格式的EVM汇编语言。--opcodes:操作码(和--asm作用类似,区别在于asm会有一些对应到源文件的注释,而opcodes只有操作码)。--bin:十六进制字节码。--bin-runtime:运行时部分的十六进制码(没有构造函数部分)。二以太坊开发环境1.以太坊开发工具及框架--clone-bin:克隆合约的十六进制字节码。--abi:应用程序二进制接口规范。--hashes:各个函数的签名(十六进制名称,用于调用智能合约时识别指定的函数)。--userdoc:用户使用说明文档。--devdoc:开发者文档。--metadata:编译源文件的元数据(包括编译器版本、abi、userdoc、devdoc、设置、源文件hash等,以json格式组织在一起)。例子里的-o是用来指定输出文件路径的。还有一些常用的选项:--optimize:编译字节码时进行优化。--optimize-runsn(=200)在激活优化功能时,为了进行优化,试执行合约的次数--add-std:添加标准合约。--librarieslibs:指定合约依赖的库。--overwrite:在指定目录里覆盖已有的输出文件。--pretty-json:当用户指定输出的格式为json时,以可读性更好的形式输出。--gas:执行编译时打印出各个函数估测消耗的Gas数量。二以太坊开发环境1.以太坊开发工具及框架3.TruffleTruffle(/)是现在比较流行的Solidity智能合约开发框架,功能十分强大,可以帮助开发者迅速搭建起一个DApp。Truffle具体的特性有:(1)内建智能合约编译、链接、部署和二进制包管理功能;(2)支持对智能合约的自动测试;(3)支持自动化部署、移植;(4)支持公有链和私有链,可以轻松部署到不同的网络中;(5)支持访问外部包,可以方便地基于NPM和EthPM引入其他智能合约的依赖;(6)可以使用Truffle命令行工具执行外部脚本。二以太坊开发环境2.以太坊开发环境搭建本节介绍以太坊客户端Geth、编程语言Solidity和智能合约开发框架Truffle的安装。1.Geth安装Go-ethereum又称Geth,是当前最为成熟的以太坊客户端,采用Go语言编写,以下是Geth的安装部署过程。(1)在Windows系统中一键安装Geth安装包下载地址为:/downloads/。下载完成后打开并安装,默认路径为C:\ProgramFiles\Geth。安装完成后,打开命令行,输入如命令gethhelp显示如图所示表示安装成功。二以太坊开发环境2.以太坊开发环境搭建(2)在Linux系统中安装Geth在Linux系统中,Geth可以从指定源直接安装,也可以下载源代码井编译安装。下面以Ubuntu16.04为例说明其安装部署过程。需要注意的是,在Linux环境下部分命令需要root权限执行,如apt命令等,本章的命令代码中以“#”开头的命令表示需要root权限执行,以“$”开头表示仅需在普通用户权限下执行。使用PPA安装:#apt-getinstall-ysoftware-properties-common#add-apt-repository-yppa:ethereum/ethereum#add-apt-repository-yppa:ethereum/ethereum-dev#apt-getupdate#apt-getinstallethereum编译完成后的二进制文件为./build/bin/geth。二以太坊开发环境2.以太坊开发环境搭建2.Solidity安装(1)Windows系统中安装Solidity一键安装Nodejs。Nodejs官方长期支持的版本为8.10.0LTS,其下载地址为/dist/v8.10.0/node-v8.10.0-x64.msi。安装完成后,在命令行或者终端中使用命令:npminstallsolc进行安装。(2)在Linux系统中安装Solidity使用NPM进行安装:使用命令#apt-getinstallnodejs安装nodejs。安装完成后使用命令npminstallsolc进行安装,再按如下方法进行源代码编译。首先使用如下命令加入相关的包依赖:#sudoapt-get-yupdate#sudoapt-get-yinstalllanguage-pack-en-base#sudodpkg-reconfigurelocales#sudoapt-get-yinstallsoftware-properties-common#sudoadd-apt-repository-yppa:ethereum/ethereum#sudoadd-apt-repository-yppa:ethereum/ethereum-dev#sudoapt-get-yupdate#sudoapt-get-yupgrade二以太坊开发环境2.以太坊开发环境搭建然后使用如下命令下载并编译Solidity:#gitclone--recursive/ethereum/webthree-umbrella.git#cdwebthree-umbrella#更新Solidity库#./webthree-helpers/scripts/ethupdate.sh--no-push--simple-pull--projectsolidity#编译Solidity#./webthree-helpers/scripts/ethbuild.sh--no-git--projectsolidity--all--cores4-DEVMJIT=0二以太坊开发环境2.以太坊开发环境搭建3.安装Truffle(1)Windows系统中安装Truffle在命令行或者终端中使用命令:#npminstall--global--productionwindows-build-tools安装依赖,安装完成后使用#npminstall-gtruffle安装Truffle。(2)Linux系统中安装Truffle在命令行或者终端中使用命令:#npminstall-gtruffle。3以太坊智能合约开发三以太坊智能合约开发1.智能合约运行环境
以太坊虚拟机(EVM)是以太坊中智能合约的运行环境,它不仅被沙箱封装起来,事实上它被完全隔离运行,也就是说运行在EVM内部的代码不能接触到网络、文件系统或者其它进程,甚至智能合约之间也只有有限的调用。1.业务流程以太坊虚拟机,简称EVM,是用来执行以太坊上的交易的。业务流程如图所示:输入一笔交易,内部会转换成一个Message对象,传入EVM执行。如果是一笔普通转账交易,那么直接修改StateDB中对应的账户余额即可。如果是智能合约的创建或者调用,则通过EVM中的解释器加载和执行字节码,执行过程中可能会查询或者修改StateDB。三以太坊智能合约开发1.智能合约运行环境2.固定油费(IntrinsicGas)
对于每笔交易,先需要收取一笔固定油费,计算方法如下:如果交易不带额外数据(Payload),比如普通转账,那么需要收取21000的油费。如果交易携带额外数据,那么额外携带的数据也是需要收费的。具体来说是按字节收费:字节为0的收4块,字节不为0收68块,所以会看到很多做合约优化的,目的就是减少数据中不为0的字节数量,从而降低油费gas消耗。三以太坊智能合约开发1.智能合约运行环境3.生成Contract对象
交易会被转换成一个Message对象传入EVM,而EVM则会根据Message生成一个Contract对象以便后续执行:可以看到,Contract中会根据合约地址,从StateDB中加载对应的代码,然后送入解释器执行。执行合约能够消耗的油费有一个上限,就是节点配置的每个区块能够容纳的GasLimit。三以太坊智能合约开发1.智能合约运行环境4.送入解释器执行代码跟输入都有了,就可以送入解释器执行了。EVM是基于栈的虚拟机,解释器中需要操作四大组件:PC:类似于CPU中的PC寄存器,指向当前执行的指令。Stack:执行堆栈,位宽为256bits,最大深度为1024。Memory:内存空间。Gas:油费池,耗光油费则交易执行失败。三以太坊智能合约开发1.智能合约运行环境具体解释执行的流程参见:EVM的每条指令称为一个OpCode,占用一个字节,所以指令集最多不超过256,具体描述参见:https://ethervm.io。首先PC会从合约代码中读取一个OpCode,然后从一个JumpTable中检索出对应的operation,也就是与其相关联的函数集合。接下来会计算该操作需要消耗的油费,如果油费耗光则执行失败,返回ErrOutOfGas错误。如果油费充足,则调用execute()执行该指令,根据指令类型的不同,会分别对Stack、Memory或者StateDB进行读写操作。三以太坊智能合约开发1.智能合约运行环境5.调用合约函数
前面分析完了EVM解释执行的主要流程,那么EVM怎么知道交易想调用的是合约里的哪个函数呢?前面提到跟合约代码一起送到解释器里的还有一个Input,而这个Input数据是由交易提供的。三以太坊智能合约开发1.智能合约运行环境Input数据通常分为两个部分:前面4个字节被称为“4-bytesignature”,是某个函数签名的Keccak哈希值的前4个字节,作为该函数的唯一标识。后面跟的就是调用该函数需要提供的参数了,长度不定。例如:在部署完A合约后,调用add(1)对应的Input数据是:0x87db03b70000000000000000000000000000000000000000000000000000000000000001。而在编译智能合约的时候,编译器会自动在生成的字节码的最前面增加一段函数选择逻辑:首先通过CALLDATALOAD(把输入数据加载到Stack中)指令将“4-bytesignature”压入堆栈中,然后依次跟该合约中包含的函数进行比对,如果匹配则调用JUMPI指令跳入该段代码继续执行。三以太坊智能合约开发1.智能合约运行环境6.合约调用合约合约内部调用另外一个合约,有4种调用方式:1)CALL。2)CALLCODE。3)DELEGATECALL。4)STATICALL。这里先以最简单的CALL为例,调用流程如下图所示:三以太坊智能合约开发1.智能合约运行环境6.合约调用合约合约内部调用另外一个合约,有4种调用方式:1)CALL。2)CALLCODE。3)DELEGATECALL。4)STATICALL。三以太坊智能合约开发1.智能合约运行环境这里先以最简单的CALL为例,调用流程如下图所示:可以看到,调用者把调用参数存储在内存中,然后执行CALL指令。CALL指令执行时会创建新的Contract对象,并以内存中的调用参数作为其Input。解释器会为新合约的执行创建新的Stack和Memory,从而不会破环原合约的执行环境。新合约执行完成后,通过RETURN指令把执行结果写入之前指定的内存地址,然后原合约继续向后执行。三以太坊智能合约开发1.智能合约运行环境7.创建合约如果某一笔交易的to地址为nil,则表明该交易是用于创建智能合约的。首先需要创建合约地址,采用下面的计算公式:Keccak(RLP(call_addr,nonce))[:12]。也就是说,对交易发起人的地址和nonce进行RLP编码,再算出Keccak哈希值,取后20个字节作为该合约的地址。下一步就是根据合约地址创建对应的stateObject,然后存储交易中包含的合约代码。该合约的所有状态变化会存储在一个storagetrie中,最终以Key-Value的形式存储到StateDB中。代码一经存储则无法改变,而storagetrie中的内容则是可以通过调用合约进行修改的,比如通过SSTORE指令。三以太坊智能合约开发2.智能合约开发语言1结构Solidity中的合约与面向对象编程语言中的类(Class)很相似,在一个合约中可以声明多种成员,包括状态变量、函数、函数修改器、事件等。同时,一个合约可以继承另一个合约。本节将简单介绍各种成员的形式和作用。状态变量是永久存储在合约账户存储中的值,用于保存合约的状态。Solidity语言提供了多种类型的变量,下面的代码在合约中声明了一个无符号整数类型的状态变量:contractSimpleStorage{uintsomeData;//状态变量}函数是合约代码的执行单位,一个合约中可能包含许许多多提供各种功能的函数,它们相互调用,共同组成合约的工作逻辑。合约中还有一些特殊的函数,比如合约创建时执行的构造函数、想要调用一个不存在的函数时自动执行的fallback函数等。下面的代码在合约中声明了一个不做任何操作的函数:contractSimpleAction{functiondoNothing(){//函数}}三以太坊智能合约开发2.智能合约开发语言函数修改器可用于改变函数的行为,在函数执行前或执行后插入其他逻辑,比如在函数执行前进行参数检查等。下面的代码演示了如何使用一个函数修改器确保一个函数只能被合约的创建者调用:contractSimpleContract{addresspubliccreater;functionSimpleContract(){creater=msg.sender;//构造函数中记录合约创建者}modifieronlyCreater(){//函数修改器require(msg.sender==creater);_//使用下划线指代原函数代码}functionabort()onlyCreater{//使用函数修改器}}三以太坊智能合约开发2.智能合约开发语言事件是以太坊日志协议的高层次抽象,用于记录合约执行过程中发生的各种事件和状态变化。在下面的代码中,当donate函数被调用时会自动记录调用者的地址和以太币数量,以供将来查看:contractFunding{eventDeposit(addressfrom,uintamount);//事件functiondonate()payable{Deposit(msg.sender,msg.value);//触发事件}}三以太坊智能合约开发2.智能合约开发语言2变量类型在计算机程序中需要使用变量来存储值。值有多种类型,比如整数、小数、字符串等,不同类型的值需要存储在不同类型的变量中。Solidity是一门静态类型语言,每一个变量都必须指定变量的类型,否则无法正确编译。Solidity提供了一些基础的变量类型,可以使用这些基础类型组合形成复杂的类型。变量类型根据参数传递方式的不同可以分为两类:值类型和引用类型。值类型在每次赋值或者作为参数传递时都会创建一份拷贝,引用类型则有两种存储地点,即账户存储和内存。状态变量与部分类型的局部变量(数组、结构体等复杂类型)是默认保存在账户存储中的,而函数的参数和其他简单类型的局部变量则保存在内存中。必要时还可以在声明变量时加上memory或者storage修饰词来强制限定变量的存储地点。数据的存储地点非常重要,引用类型在不同的存储位置赋值,其产生的结果完全不同。值类型包括布尔类型、整数类型、地址类型、固定长度字节数组等,引用类型包括数组、结构体等。三以太坊智能合约开发2.智能合约开发语言1.值类型(1)布尔类型布尔类型(bool)可能的取值是常量true和false。支持!(逻辑非)、&&(逻辑与)、||(逻辑或)、==(等于)、!=(不等于)等运算符。(2)整数类型int表示有符号整数,uint表示元符号整数。变量支持通过后缀指明变量使用多少位进行存储,后缀必须是8~256范围内8的整数倍,比如int8、intl6、int256。如果没有显式指明后缀,int默认表示int256,uint默认表示uint256。(3)枚举类型枚举类型(enums)是一种用户自定义的类型,用于声明一些命名的常数。下面的代码演示了如何声明和使用枚举类型。枚举类型可以与整数类型之间显式地进行类型转换,但是不能自动进行隐式转换。枚举类型的成员默认从0开始,依次递增,在下面的例子中DEFAULT、ONE、TWO分别对应整数0、1、2。contractSirnpleEnurn{enurnSorneEnurn{DEFAULT,ONE,TWO};//声明一个枚举类型}三以太坊智能合约开发2.智能合约开发语言(4)地址类型地址类型(address)的长度为20字节(与以太坊账户地址长度一致),其是合约的基类,拥有-些成员方法和变量。从Solidity0.5.0版本开始,合约不再继承自地址类型,但是开发者仍可以通过显式类型转换将合约转换为地址类型。<address>.balance:类型为uint,表示账户的余额,单位是wei。<address>.transfer(uint256amount):发送amount数量的以太币给address表示的账户,单位是wei,失败会抛出异常。<address>.send(uint256amount)returns(bool):与<address>.transfer类似,同样是进行以太币的转账。两者的区别是如果执行失败<address>.transfer会抛出异常并且终止代码,<address>.send则是返回false,代码继续执行。需要注意的是,以太币的转账会有失败的风险,为了确保以太币转账的安全,一定要检查<address>.send的返回值,或者使用<address>.transfer。三以太坊智能合约开发2.智能合约开发语言<address>.call(...)returns(bool):发起底层的CALL指令,失败将返回false。<address>.callcode(...)returns(bool):发起底层的CALLCODE指令,失败将返回false。<address>.delegatecall(...)returns(bool):发起底层的DELETECALL指令,失败将返回false。以上三个函数提供了一个底层的、灵活的方式与合约进行交互,<address>.call(...)可以接受任何长度、任何类型的参数,每个参数将被填充到32字节并拼接在一起。但有一种例外情况,当第一个参数的长度恰好是4字节时,该参数不会被打包成32字节,而是被作为指定函数的签名。在下面的代码中,第一个参数bytes4(keccak256("fun(uint256)"))为长度4字节的函数签名,表示调用一个函数签名为fun(uint256)的函数,4则是实际传给fun函数的参数:addressnameReg=0x72ba7d8e73fe8eb666ea66babc8116a4lbfb10e2;nameReg.call(bytes4(keccak256("fun(uint256)")),4);函数签名使用基本类型的典型格式定义,如果有多个参数要使用逗号隔开,并且要去掉表达式中的所有空格。<address>.delegatecall与<address>.call的区别是调用delegatecall时仅执行代码,而诸如账户存储、余额等其他方面都是用当前合约的数据,这是为了使用另一个合约中的库代码。为了代码能够顺利执行,调用者必须确保本合约中的存储变量与delegatecall执行的代码相兼容。<address>.callcode是早期使用的接口,比起call拥有更低的权限,无法访问msg.sender、msg.value等变量。以上三个函数是非常底层的函数调用。在实际情况中建议开发者还是尽量不要使用,因为它们破坏了Solidity语言的类型安全。三以太坊智能合约开发2.智能合约开发语言2.引用类型(1)数组Solidity中的数组包括固定长度的数组,以及运行时可动态改变长度的动态数组。对于账户存储中的数组,数组元素可以是任何类型,而内存中的数组,其数组元素不可以是映射。一个包含固定k个T类型数据的数组可以用T[k]语句来声明,一个动态长度的数组用T[]来声明。下面来了解数组的成员变量和函数。1)length:数组可以通过访问length成员获取数组的长度。对于账户存储中的数组,可以通过修改数组的length成员动态地改变数组的长度,而内存中的数组在创建之后,其length成员就已经完全确定了,无法修改。2)push:账户存储中的动态数组以及bytes类型的变量,可以通过调用push方法在数组尾部添加元素,返回值为数组新的长度。3)bytes和string:一种特殊的数组。bytes通常用于表示任意长度的字节数据,而string用于表示任意长度的字符数据(UTF-8编码)。但是string不支持length成员和下标访问。两者之间可以互相转换,比如bytes(s)可以将字符串s转换成bytes类型。但是需要注意字符串中的字符是以UTF-8编码保存的,转换成bytes类型时会将多字节的字符展开,此时如果使用下标的方式访问bytes,有可能只访问到一个字符的部分编码。如果想要访问一个字符串的长度,可以使用bytes(s).length,但是要注意这样获取到的长度同样是以UTF-8编码计算的长度,而不是以单个字符计算的长度。还有一点需要注意,因为EVM的限制,外部函数调用无法返回一个动态长度的数组,唯一的做法是将需要返回的内容放在一个足够长的定长数组中。三以太坊智能合约开发2.智能合约开发语言(2)结构体Solidity语言中的结构体(struct)与C语言中很相似,允许开发者根据需要自定义变量类型。下面的代码展示了如何声明一个结构体:contractTest{structStudent{stringname;uintage;uintscore;stringsex;}}结构体可以作为映射或者数组中的元素,其本身也可以包含映射和数组等类型,但是不能将一个结构体用作其本身的成员,因为结构体嵌套自身会导致无限循环。三以太坊智能合约开发2.智能合约开发语言(3)映射映射(Mapping)是一种键值对映射关系的存储结构,我们使用mapping(KeyType=>ValueType)来声明一个映射,其中KeyType可以是除了映射、动态数组、合约、枚举类型、结构体以外的任何类型,ValueType则可以是任意类型,包括映射本身。映射可以看作一个散列表,其中所有可能存在的键都有一个默认值,默认值的二进制编码全为0,所以映射并没有长度的概念。同时,映射并不存储键的数据,而仅仅存储它的Keccak-256散列值。三以太坊智能合约开发2.智能合约开发语言3.类型转换如果一个运算符作用于两个类型不同的变量,编译器会自动尝试将一个变量类型转换为另一个变量的类型,这是隐式类型转换。通常,在语义合理并且不会造成信息损失的情况下允许进行隐式类型转换,比如uint8转换为uint16或者uint32,但是int8不能转换成uintl6,这是因为uintl6不能表示负数。任何可以转换为uintl6的变量都可以转换为address类型。有时在编译器不能进行隐式类型转换的情况下可以强行进行类型转换,这叫做显式类型转换。但是请注意,进行显式类型转换前必须知道你在进行什么操作并且确定操作的结果是你想要的,否则会造成很多异常情况。uint32a=0x12345678;uintl6b=uintl6(a);以上代码将uint32类型转换为uint16类型,这导致了数值的高16位被截断。三以太坊智能合约开发2.智能合约开发语言4.运算符Solidity语言中也包括算术运算符、比较运算符、位运算等。运算符的优先顺序如下图所示。三以太坊智能合约开发2.智能合约开发语言在以上运算符中,需要特别注意delete运算符。在其他编程语言中,delete经常作为一种与new相反的内存管理操作,用于释放内存。但是在Solidity中,delete仅仅是一项赋值运算,它用作给变量赋初始值。例如,deletea与a=0是等效的;delete用于数组表示将该数据变成一个长度为0的空数组;当作用于固定长度数组时,该数组将变为一个长度不变但每个元素都被赋值为默认值的数组;当作用于结构体时,delete将递归作用于除映射外的所有成员;delete对映射无效。下面的代码展示了delete对复杂类型变量的效果:contractDeleteExample{functiondeleteArray(){uint[]memorya=newuint[](3);a[0]=l;a[l]=2;a[2]=3;deletea[l];//数组将变为[1,0,3]deletea;//a.length将变为0}structS{uinta;stringb;bytesc;};functiondeleteStruct(){Ss=S(1,"hello","world");deletes; //删除s中的所有元素,a、b、c分别被赋值为0、空串、0x0}}三以太坊智能合约开发2.智能合约开发语言5.类型推断Solidity语言中,var关键字和C++语言中的auto关键字类似,用于类型推断。uint24x=0xl23;vary=x;var声明的变量将会拥有第一个赋值变量的类型,比如上面一段代码中,y的类型是uint24。在使用中有时需要小心,比如for(vari=0;i<2000;i++){…}将是一个无限循环,因为根据类型推断i的类型为unit8,所有i永远都不会满足跳出循环的条件(i>=2000)。三以太坊智能合约开发2.智能合约开发语言3内置单位、全局变量和函数Solidity包含一些内置单位、全局变量和函数以供使用。(1)货币单位一个字面量的数字可以使用wei、finney、szabo和ether等后缀表示不同的额度,不加任何后缀则默认单位为wei。比如“2ether==2000finney”的结果是true。(2)时间单位与货币单位相似,不同的时间单位可以以秒为基本单位进行转换,不同的单位定义如下:1==1seconds1minutes==60seconds1hours==60minutes1days==24hours1weeks==7days1years==365days特别注意,如果想要使用这些单位进行时间计算必须特别小心,因为一年并不总是有365天;同时因为闰秒的存在,一天也并不总是24小时。因为闰秒的计算难以预测,因此为保证日历库的精确性,需要由外部供应商定期更新。三以太坊智能合约开发2.智能合约开发语言(3)区块和交易属性有一些方法和变量可以用于获取区块和交易的属性。block.blockhash(uintblo
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 全方位解读2024年公共事业管理试题答案
- 【成都】2025年四川成都天府国际竞技训练中心公开招聘运动员4人笔试历年典型考题及考点剖析附带答案详解
- 小自考设计项目计划书精要试题及答案
- 逐步深入的小自考汉语言试题及答案
- 六年级语文上册 第二单元 6《狼牙山五壮士》教学设计 新人教版
- 第二单元 时代乐章 第三课自然之美 教学设计 2024-2025学年人教版(2024)初中美术七年级上册
- 行政决策与行政执行的协调性研究试题及答案
- 小自考汉语言文学理解与表达练习试题及答案
- 雅保销售面试题及答案
- 北京市保安证试题及答案
- 三年级数学两位数乘两位数笔算题综合考核训练题大全附答案
- NB-T20307-2014核电厂冷却塔环境影响评价技术规范
- 2024年普通高等学校招生全国统一考试(北京卷)语文含答案
- 中国保险行业协会机动车商业保险示范条款(2020版)
- 天然气管网大数据分析与预测
- (正式版)G-B- 21257-2024 烧碱、聚氯乙烯树脂和甲烷氯化物单位产品能源消耗限额
- 2024年中考语文复习:人性光辉主题阅读练习题(含答案)
- 老年人排泄照护(老年护理学课件)
- 公厕保洁服务服务承诺及质量保障措施
- 湖北省武汉市部分重点中学(六校)2023-2024学年高二下学期期中生物试题
- 《火力发电建设工程机组调试技术规范》
评论
0/150
提交评论