Solidity 官方文档中文版 2_Ethereum 智能合约介绍

一个简单的智能合约

先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。

Storage

contract SimpleStorage {uint storedData;function set(uint x) {storedData = x;}function get() constant returns (uint retVal) {return storedData;}
}

在Solidity中,一个合约由一组代码(合约的函数)和数据(合约的状态)组成。合约位于以太坊区块链上的一个特殊地址。*uint storedData*; 这行代码声明了一个状态变量,变量名为storedData,类型为 uint (256bits无符号整数)。你可以认为它就像数据库里面的一个存储单元,跟管理数据库一样,可以通过调用函数查询和修改它。在以太坊中,通常只有合约的拥有者才能这样做。在这个例子中,函数 set 和 get 分别用于修改和查询变量的值。

跟很多其他语言一样,访问状态变量时,不需要在前面增加 this. 这样的前缀。

这个合约还无法做很多事情(受限于以太坊的基础设施),仅仅是允许任何人储存一个数字。而且世界上任何一个人都可以来存取这个数字,缺少一个(可靠的)方式来保护你发布的数字。任何人都可以调用set方法设置一个不同的数字覆盖你发布的数字。但是你的数字将会留存在区块链的历史上。稍后我们会学习如何增加一个存取限制,使得只有你才能修改这个数字。

代币的例子

接下来的合约将实现一个形式最简单的加密货币。空中取币不再是一个魔术,当然只有创建合约的人才能做这件事情(想用其他货币发行模式也很简单,只是实现细节上的差异)。而且任何人都可以发送货币给其他人,不需要注册用户名和密码,只要有一对以太坊的公私钥即可。

Note

对于在线solidity环境来说,这不是一个好的例子。如果你使用在线solidity环境 来尝试这个例子。调用函数时,将无法改变from的地址。所以你只能扮演铸币者的角色,可以铸造货币并发送给其他人,而无法扮演其他人的角色。这点在线solidity环境将来会做改进。

contract Coin {
//关键字“public”使变量能从合约外部访问。address public minter;mapping (address => uint) public balances;//事件让轻客户端能高效的对变化做出反应。event Sent(address from, address to, uint amount);//这个构造函数的代码仅仅只在合约创建的时候被运行。function Coin() {minter = msg.sender;}function mint(address receiver, uint amount) {if (msg.sender != minter) return;balances[receiver] += amount;}function send(address receiver, uint amount) {if (balances[msg.sender] < amount) return;balances[msg.sender] -= amount;balances[receiver] += amount;Sent(msg.sender, receiver, amount);}
}

这个合约引入了一些新的概念,让我们一个一个来看一下。

address public minter; 这行代码声明了一个可公开访问的状态变量,类型为address。address类型的值大小为160 bits,不支持任何算术操作。适用于存储合约的地址或其他人的公私钥。public关键字会自动为其修饰的状态变量生成访问函数。没有public关键字的变量将无法被其他合约访问。另外只有本合约内的代码才能写入。自动生成的函数如下:

function minter() returns (address) { return minter; }

当然我们自己增加一个这样的访问函数是行不通的。编译器会报错,指出这个函数与一个状态变量重名。

下一行代码 mapping (address => uint) public balances; 创建了一个public的状态变量,但是其类型更加的复杂。该类型将一些address映射到无符号整数。mapping可以被认为是一个哈希表,每一个可能的key对应的value被虚拟的初始化为全0.这个类比不是很严谨,对于一个mapping,无法获取一个包含其所有key或者value的链表。所以我们得自己记着添加了哪些东西到mapping中。更好的方式是维护一个这样的链表,或者使用其他更高级的数据类型。或者只在不受这个缺陷影响的场景中使用mapping,就像这个例子。在这个例子中由public关键字生成的访问函数将会更加复杂,其代码大致如下:

function balances(address _account) returns (uint balance) {return balances[_account];
}

我们可以很方便的通过这个函数查询某个特定账号的余额。

event Sent(address from, address to, uint value); 这行代码声明了一个“事件”。由send函数的最后一行代码触发。客户端(服务端应用也适用)可以以很低的开销来监听这些由区块链触发的事件。事件触发时,监听者会同时接收到from,to,value这些参数值,可以方便的用于跟踪交易。为了监听这个事件,你可以使用如下代码:

Coin.Sent().watch({}, '', function(error, result) {if (!error) {console.log("Coin transfer: " + result.args.amount +" coins were sent from " + result.args.from +" to " + result.args.to + ".");console.log("Balances now:\n" +"Sender: " + Coin.balances.call(result.args.from) +"Receiver: " + Coin.balances.call(result.args.to));}
}

注意在客户端中是如何调用自动生成的 balances 函数的。

这里有个比较特殊的函数 Coin。它是一个构造函数,会在合约创建的时候运行,之后就无法被调用。它会永久得存储合约创建者的地址。msg(以及tx和block)是一个神奇的全局变量,它包含了一些可以被合约代码访问的属于区块链的属性。msg.sender 总是存放着当前函数的外部调用者的地址。

最后,真正被用户或者其他合约调用,用来完成本合约功能的函数是mint和send。如果合约创建者之外的其他人调用mint,什么都不会发生。而send可以被任何人(拥有一定数量的代币)调用,发送一些币给其他人。注意,当你通过该合约发送一些代币到某个地址,在区块链浏览器中查询该地址将什么也看不到。因为发送代币导致的余额变化只存储在该代币合约的数据存储中。通过事件我们可以很容易创建一个可以追踪你的新币交易和余额的“区块链浏览器”。

区块链基础

对于程序员来说,区块链这个概念其实不难理解。因为最难懂的一些东西(挖矿,哈希,椭圆曲线加密,点对点网络等等)只是为了提供一系列的特性和保障。你只需要接受这些既有的特性,不需要关心其底层的技术。就像你如果仅仅是为了使用亚马逊的AWS,并不需要了解其内部工作原理。

交易/事务

区块链是一个全局共享的,事务性的数据库。这意味着参与这个网络的每一个人都可以读取其中的记录。如果你想修改这个数据库中的东西,就必须创建一个事务,并得到其他所有人的确认。事务这个词意味着你要做的修改(假如你想同时修改两个值)只能被完完全全的实施或者一点都没有进行。

此外,当你的事务被应用到这个数据库的时候,其他事务不能修改该数据库。

举个例子,想象一张表,里面列出了某个电子货币所有账号的余额。当从一个账户到另外一个账户的转账请求发生时,这个数据库的事务特性确保从一个账户中减掉的金额会被加到另一个账户上。如果因为某种原因,往目标账户上增加金额无法进行,那么源账户的金额也不会发生任何变化。

此外,一个事务会被发送者(创建者)进行密码学签名。这项措施非常直观的为数据库的特定修改增加了访问保护。在电子货币的例子中,一个简单的检查就可以确保只有持有账户密钥的人,才能从该账户向外转账。

区块

区块链要解决的一个主要难题,在比特币中被称为“双花攻击”。当网络上出现了两笔交易,都要花光一个账户中的钱时,会发生什么?一个冲突?

简单的回答是你不需要关心这个问题。这些交易会被排序并打包成“区块”,然后被所有参与的节点执行和分发。如果两笔交易相互冲突,排序靠后的交易会被拒绝并剔除出区块。

这些区块按时间排成一个线性序列。这也正是“区块链”这个词的由来。区块以一个相当规律的时间间隔加入到链上。对于以太坊,这个间隔大致是17秒。

作为“顺序选择机制”(通常称为“挖矿”)的一部分,一段区块链可能会时不时被回滚。但这种情况只会发生在整条链的末端。回滚涉及的区块越多,其发生的概率越小。所以你的交易可能会被回滚,甚至会被从区块链中删除。但是你等待的越久,这种情况发生的概率就越小。

以太坊虚拟机

总览

以太坊虚拟机(EVM)是以太坊中智能合约的运行环境。它不仅被沙箱封装起来,事实上它被完全隔离,也就是说运行在EVM内部的代码不能接触到网络、文件系统或者其它进程。甚至智能合约与其它智能合约只有有限的接触。

账户

以太坊中有两类账户,它们共用同一个地址空间。外部账户,该类账户被公钥-私钥对控制(人类)。合约账户,该类账户被存储在账户中的代码控制。

外部账户的地址是由公钥决定的,合约账户的地址是在创建该合约时确定的(这个地址由合约创建者的地址和该地址发出过的交易数量计算得到,地址发出过的交易数量也被称作"nonce")

合约账户存储了代码,外部账户则没有,除了这点以外,这两类账户对于EVM来说是一样的。

每个账户有一个key-value形式的持久化存储。其中key和value的长度都是256比特,名字叫做storage.

另外,每个账户都有一个以太币余额(单位是“Wei"),该账户余额可以通过向它发送带有以太币的交易来改变。

交易

一笔交易是一条消息,从一个账户发送到另一个账户(可能是相同的账户或者零账户,见下文)。交易可以包含二进制数据(payload)和以太币。

如果目标账户包含代码,该代码会执行,payload就是输入数据。

如果目标账户是零账户(账户地址是0),交易将创建一个新合约。正如上文所讲,这个合约地址不是零地址,而是由合约创建者的地址和该地址发出过的交易数量(被称为nonce)计算得到。创建合约交易的payload被当作EVM字节码执行。执行的输出做为合约代码被永久存储。这意味着,为了创建一个合约,你不需要向合约发送真正的合约代码,而是发送能够返回真正代码的代码。

Gas

以太坊上的每笔交易都会被收取一定数量的gas,gas的目的是限制执行交易所需的工作量,同时为执行支付费用。当EVM执行交易时,gas将按照特定规则被逐渐消耗。

gas price(以太币计)是由交易创建者设置的,发送账户需要预付的交易费用 = gas price * gas amount。 如果执行结束还有gas剩余,这些gas将被返还给发送账户。

无论执行到什么位置,一旦gas被耗尽(比如降为负值),将会触发一个out-of-gas异常。当前调用帧所做的所有状态修改都将被回滚。

存储,主存和栈

每个账户有一块持久化内存区域被称为存储。其形式为key-value,key和value的长度均为256比特。在合约里,不能遍历账户的存储。相对于另外两种,存储的读操作相对来说开销较大,修改存储更甚。一个合约只能对它自己的存储进行读写。

第二个内存区被称为主存。合约执行每次消息调用时,都有一块新的,被清除过的主存。主存可以以字节粒度寻址,但是读写粒度为32字节(256比特)。操作主存的开销随着其增长而变大(平方级别)。

EVM不是基于寄存器,而是基于栈的虚拟机。因此所有的计算都在一个被称为栈的区域执行。栈最大有1024个元素,每个元素256比特。对栈的访问只限于其顶端,方式为:允许拷贝最顶端的16个元素中的一个到栈顶,或者是交换栈顶元素和下面16个元素中的一个。所有其他操作都只能取最顶的两个(或一个,或更多,取决于具体的操作)元素,并把结果压在栈顶。当然可以把栈上的元素放到存储或者主存中。但是无法只访问栈上指定深度的那个元素,在那之前必须要把指定深度之上的所有元素都从栈中移除才行。

指令集

EVM的指令集被刻意保持在最小规模,以尽可能避免可能导致共识问题的错误实现。所有的指令都是针对256比特这个基本的数据类型的操作。具备常用的算术,位,逻辑和比较操作。也可以做到条件和无条件跳转。此外,合约可以访问当前区块的相关属性,比如它的编号和时间戳。

消息调用

合约可以通过消息调用的方式来调用其它合约或者发送以太币到非合约账户。消息调用和交易非常类似,它们都有一个源,一个目标,数据负载,以太币,gas和返回数据。事实上每个交易都可以被认为是一个顶层消息调用,这个消息调用会依次产生更多的消息调用。

一个合约可以决定剩余gas的分配。比如内部消息调用时使用多少gas,或者期望保留多少gas。如果在内部消息调用时发生了out-of-gas异常(或者其他异常),合约将会得到通知,一个错误码被压在栈上。这种情况只是内部消息调用的gas耗尽。在solidity中,这种情况下发起调用的合约默认会触发一个人工异常。这个异常会打印出调用栈。就像之前说过的,被调用的合约(发起调用的合约也一样)会拥有崭新的主存并能够访问调用的负载。调用负载被存储在一个单独的被称为calldata的区域。调用执行结束后,返回数据将被存放在调用方预先分配好的一块内存中。

调用层数被限制为1024,因此对于更加复杂的操作,我们应该使用循环而不是递归。

代码调用和库

存在一种特殊类型的消息调用,被称为callcode。它跟消息调用几乎完全一样,只是加载自目标地址的代码将在发起调用的合约上下文中运行。

这意味着一个合约可以在运行时从另外一个地址动态加载代码。存储,当前地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。

这使得Solidity可以实现”库“。可复用的库代码可以应用在一个合约的存储上,可以用来实现复杂的数据结构。

日志

在区块层面,可以用一种特殊的可索引的数据结构来存储数据。这个特性被称为日志,Solidity用它来实现事件。合约创建之后就无法访问日志数据,但是这些数据可以从区块链外高效的访问。因为部分日志数据被存储在布隆过滤器(Bloom filter) 中,我们可以高效并且安全的搜索日志,所以那些没有下载整个区块链的网络节点(轻客户端)也可以找到这些日志。

创建

合约甚至可以通过一个特殊的指令来创建其他合约(不是简单的向零地址发起调用)。创建合约的调用跟普通的消息调用的区别在于,负载数据执行的结果被当作代码,调用者/创建者在栈上得到新合约的地址。

自毁

只有在某个地址上的合约执行自毁操作时,合约代码才会从区块链上移除。合约地址上剩余的以太币会发送给指定的目标,然后其存储和代码被移除。

注意,即使一个合约的代码不包含自毁指令,依然可以通过代码调用(callcode)来执行这个操作。

 

参考文献: 
http://solidity.readthedocs.io/en/latest/installing-solidity.html 

http://wiki.jikexueyuan.com/project/solidity-zh/installing-solidity.html

 

转载于:https://www.cnblogs.com/felixzuo/p/8821851.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/398297.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为手机云闪付付款码如何截图_云闪付乘车码,它带着优惠又来了

云闪付乘车码又来了&#xff01;想必这段时间大家有些(甚是)想念薅羊毛的日子想起那快乐时光嘴角总是不经意的偷偷乐不多说了&#xff0c;直接上优惠特别提醒&#xff1a;1分钱乘公交5折乘地铁......详戳▼▼▼银联信用卡1分钱乘公交活动时间&#xff1a;2019年12月6日—2019年…

读《构建之法》1-5章

《构建之法》第一至五章&#xff0c;我了解了软件工程的重要性&#xff0c;它就像楼房的建设&#xff0c;是一项大工程&#xff0c;必须有设计&#xff0c;步骤&#xff0c;工具&#xff0c;有能力&#xff0c;有计划&#xff0c;还要有团队合作才有可能实现这么一个工程。 第一…

ChemDraw教程:如何查看和删除俗名

化学范畴里&#xff0c;允许用俗名表示ChemDraw原子标记或原子标记的一部分&#xff0c;可以定义俗名的快捷键也可以自由查看或删除俗名&#xff0c;熟练掌握可以提高ChemDraw软件使用效率&#xff0c;下面将具体介绍此部分内容。 一、查看俗名 查看已定义俗名的方法有两种&…

850是什么意思_850是什么意思

1. 850的近义词1. The United Nations says more than eight hundred fifty million people do not have enough food.联合国公布了有超过850&#xff0c;000&#xff0c;000的人食不果腹。2. danci.911cha.com2. The results show that the TiO_2 thin films are composed of …

[HNOI2012]矿场搭建

题解&#xff1a; 首先显然这是要缩点的 缩点双 直接对割点之间的联通块判断一下连着几个割点 连0个 cnt*&#xff08;cnt-1&#xff09;/2 连1个 cnt 连2个 0 代码&#xff1a; #include <bits/stdc.h> using namespace std; #define ll long long #define N 2000 bool …

地图与定位之定位

今天开始总结一下地图与定位。首先要使用定位就要引用框架CoreLocation.framework,在类文件中加入#import <CoreLocation/CoreLocation.h>,其次在IOS8中调用的时候要在Info.plist中加两个Key,NSLocationAlwaysUsageDescription和NSLocationWhenInUseUsageDescription,具体…

merge into ORA-30926

ORA-30926: 无法在源表中获得一组稳定的行 同时要求在一张表中添加一个字段&#xff0c;由于数据量较大&#xff0c;想使用merge into update的方式将数据 meger into xxx1 t1 using(select * from xxx2 )t2 on(t1.xmt2.xm and t1.dzt2.dz) when matched then update set tra…

rocketmq 如何保证高可用_RocketMq容灾、高可用方案

一、实现分布式集群多副本的三种方式1.1 M/S模式即Master/Slaver模式。该模式在过去使用的最多&#xff0c;RocketMq之前也是使用这样的主从模式来实现的。主从模式分为同步模式和异步模式&#xff0c;区别是在同步模式下只有主从复制完毕才会返回给客户端&#xff1b;而在异步…

Debian 9 strech 安装 ROS lunar

1. 配置源 按照我以前的博客配置或者按照wiki上的配置。 2. sudo sh -c echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list3. sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:…

谈Find指令

Linux下find命令在目录结构中搜索文件&#xff0c;并执行指定的操作。Linux下find命令提供了相当多的查找条件&#xff0c;功能很强大。由于find具有强大的功能&#xff0c;所以它的选项也很多&#xff0c;其中大部分选项都值得我们花时间来了解一下。即使系统中含有网络文件系…

数据库原理归档——对于数据库原理最全的总结

恭喜你来到新天地&#xff01; 以下的笔记是我复习了三次总结出来的&#xff0c;属于一个归档。目前还在对以往的知识做一个更加详细地总结&#xff0c;所以仍在更新中。这份笔记对于新手入门数据库来说十分友好&#xff0c;语言通俗易懂。在数据库中&#xff0c;还涉及到很多…

k8s部署tomcat及web应用_部署 Spring Boot 应用到 K8S 教程

点击上方 Java后端&#xff0c;选择 设为星标优质文章&#xff0c;及时送达作者&#xff1a;qingmuqingmu.io/2020/04/08/Spring-Boot-Operator-User-Guide/前言在Kubernetes中部署spring boot应用整体上来说是一件比较繁琐的事情&#xff0c;而Spring Boot Operator则能带给你…

Web性能优化分析

如果你的网站在1000ms内加载完成&#xff0c;那么会有平均一个用户停留下来。2014年&#xff0c;平均网页的大小是1.9MB。看下图了解更多统计信息。 直击现场 《HTML开发MacOSApp教程》 http://pan.baidu.com/s/1jG1Q58M 网站的核心内容需要在1000ms内呈现出来。如果失败了&a…

Ionic 自定义组件中使用 Ionic 官方组件

问题起因&#xff1a; 在项目目录中使用 ionic g component SearchBar 加入一个新的自定义组件并在 html 中插入代码&#xff1a; 1 <div> 2 <ion-searchbar 3 [(ngModel)]"myInput" 4 showCancelButton"true" 5 placeholder"…

scp构造端口_指定端口号的scp

指定端口号的scp我正在尝试将文件从远程服务器scp到我的本地计算机。 只有80端口可以访问。我试过了&#xff1a;scp -p 80 usernamewww.myserver.com:/root/file.txt .但得到了这个错误&#xff1a;cp: 80: No such file or directory如何在scp命令中指定端口号&#xff1f;On…

闭包/Block当成函数参数实现回调

2019独角兽企业重金招聘Python工程师标准>>> Swift // MARK:// MARK: 获取数据/// 获取数据class func toolLoadData(completion:(finish: String) -> ()? ,content: String) {self .nextStepRequest({ (finish) -> ()? in// 接收到数据 调用 block 传递数据…

利用find同时查找多种类型文件

find . -name "*.c" -o -name "*.cpp" -o -name "*.h" 就可以列出当前目录下面所有的c,cpp,h文件转载于:https://www.cnblogs.com/baizx/p/4438905.html

JS动态插入HTML后不能执行后续JQUERY操作

通过js追加的html 发现 不能点击 执行函数 普通绑定事件&#xff1a;$(.btn1).click(function(){}绑定 事件委托 解决方法&#xff1a; $("body").delegate(".class","click",function(){}); on绑定事件&#xff1a;$(document).on(click,.btn2,…

linux重装eclipse_Linux环境安装Eclipse工具开发

2、上传到虚拟机进行解压缩操作&#xff1a;[hadoopslaver1 package]$ tar -zxvf apache-maven-3.5.3-bin.tar.gz -C /home/hadoop/soft/3、配置Maven的环境变量&#xff1a;[hadoopslaver1 soft]$ sudo vim /etc/profileexport MAVEN_HOME/home/hadoop/soft/apache-maven-3.5.…

js中如何判断一个DOM对象是否存在?

<div id"div1"></div> if(!!document.getElementById("div1"))转载于:https://www.cnblogs.com/AmayaWang/p/5528320.html