Solidity 智能合约入门

存储合约示例

将一个数据放置在链上

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;contract SimpleStorage {uint storedData;function set(uint x) public {storedData = x;}function get() public view returns (uint) {return storedData;}
}

我们对代码进行逐行分析,首先第一行

第一行表明此段代码是被GPL-3.0所授权。默认情况下,在发布源代码时加入机器可读许可证说明是很重要的。

GPL(GNU General Public License Versions)

GPL协议一般还可以分为GPL2.0和GPL3.0两种,而GPL3.0是更新一代的开源标准,在对用户专利的保护和DRM的限制方面有所更改。GPL协议同其它的自由软件许可证一样,许可社会公众享有:运行、复制软件的自由,发行传播软件的自由,获得软件源码的自由,改进软件并将自己作出的改进版本向社会发行传播的自由。 而GPL协议就像一种开源“病毒”,任何一款沾染上他的软件都不得不保持开源和免费。

下一行是告诉编译器源代码所适用的Solidity版本为>=0.4.16 及 <0.9.0 。这样的说明是为了确保合约不会在新的编译器版本中发生异常的行为。关键字 pragma是告知编译器如何处理源代码的通用指令

Solidity中智能合约的含义就是一组代码(它的 功能 )和数据(它的 状态 )的集合,并且它们是位于以太坊区块链的一个特定地址上的

uint storedData; 这一行代码声明了一个名为storedData的状态变量,其类型为 uint (256位无符号整数)。 你也可以认为它是数据库里的一个插槽,并且可以通过调用管理数据库代码的函数进行查询和更改。在这个例子中,上述的合约定义了setget 函数,可以用来修改或检索变量的值。

要访问当前合约的成员(如:状态变量),通常不需要像添加 this. 这样的前缀,你只需要通过名字就可以直接访问它。 与其他一些语言不同的是,省略它不仅仅是一个风格问题,因为它是一种完全不同的访问成员的方式

该合约能完成的事情并不多(由于以太坊构建的基础架构的原因):它能允许任何人在合约中存储一个单独的数字,并且这个数字可以被世界上任何人访问,且没有可行的办法阻止你发布这个数字。当然,任何人都可以再次调用 set ,传入不同的值,覆盖你的数字,但是这个数字仍会被存储在区块链的历史记录中。随后,我们会看到怎样施加访问限制,以确保只有你才能改变这个数字。

所有的标识符(合约名称,函数名称和变量名称)都只能使用ASCII字符集。UTF-8编码的数据可以用字符串变量的形式存储。

小心使用Unicode文本,因为有些字符虽然长得相像(甚至一样),但其字符码是不同的,其编码后的字符数组也会不一样。

货币合约(Subcurrency)示例

下面的合约实现了一个最简单的加密货币。这里,币确实可以无中生有地产生,但是只有创建合约的人才能做到(实现一个不同的发行计划也不难)。而且,任何人都可以给其他人转币,不需要注册用户名和密码 —— 所需要的只是以太坊密钥对

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;contract Coin {// 关键字“public”让这些变量可以从外部读取address public minter;mapping (address => uint) public balances;// 轻客户端可以通过事件针对变化作出高效的反应event Sent(address from, address to, uint amount);// 这是构造函数,只有当合约创建时运行constructor() {minter = msg.sender;}function mint(address receiver, uint amount) public {require(msg.sender == minter);balances[receiver] += amount;}// Errors allow you to provide information about// why an operation failed. They are returned// to the caller of the function.error InsufficientBalance(uint requested, uint available);function send(address receiver, uint amount) public {if (amount > balances[msg.sender])revert InsufficientBalance({requested: amount,available: balances[msg.sender]});balances[msg.sender] -= amount;balances[receiver] += amount;emit Sent(msg.sender, receiver, amount);}
}

这个合约引入了一些新的概念,让我们逐一解读。

address public minter; 这一行声明了一个可以被公开访问的 address 类型的状态变量。 address 类型是一个160位的值,且不允许任何算数操作。这种类型适合存储合约地址或外部人员的密钥对。关键字 public 自动生成一个函数,允许你在这个合约之外访问这个状态变量的当前值。如果没有这个关键字,其他的合约没有办法访问这个变量。由编译器生成的函数的代码大致如下所示(暂时忽略 external 和 view):

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

当然,加一个和上面完全一样的函数是行不通的,因为我们会有同名的一个函数和一个变量,这里,主要是希望你能明白——编译器已经帮你实现了。

下一行, mapping (address => uint) public balances; 也创建一个公共状态变量,但它是一个更复杂的数据类型。 该类型将address映射为无符号整数。 Mappings 可以看作是一个 哈希表 它会执行虚拟初始化,以使所有可能存在的键都映射到一个字节表示为全零的值。 但是,这种类比并不太恰当,因为它既不能获得映射的所有键的列表,也不能获得所有值的列表。 因此,要么记住你添加到mapping中的数据(使用列表或更高级的数据类型会更好),要么在不需要键列表或值列表的上下文中使用它,就如本例。 而由 public 关键字创建的getter函数 getter function 则是更复杂一些的情况, 它大致如下所示:

function balances(address account) external view returns (uint) {return balances[account];
}

正如你所看到的,你可以通过该函数轻松地查询到账户的余额。

event Sent(address from, address to, uint amount); 这行声明了一个所谓的“事件(event)”,它会在 send 函数的最后一行被发出。用户界面(当然也包括服务器应用程序)可以监听区块链上正在发送的事件,而不会花费太多成本。一旦它被发出,监听该事件的listener都将收到通知。而所有的事件都包含了 fromtoamount 三个参数,可方便追踪交易。 为了监听这个事件,你可以使用如下JavaScript代码(假设 Coin 是已经通过 web3.js 创建好的合约对象 ):

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 函数是如何从用户界面调用的。

特殊函数 constructor 是仅在创建合约期间运行的构造函数,不能在创建之后调用。 在 Coin 合约中,构造函数永久存储创建合约的人的地址: msg (类似的还有 txblock ) 是一个特殊的全局变量, 参考 特殊变量和函数 ,这些变量允许我们访问区块链的属性。 msg.sender 始终记录当前(外部)函数调用是来自于哪一个地址。

最后,真正被用户或其他合约所调用的,以完成本合约功能的方法是 mintsend

mint 函数用来新发行一定数量的币到一个地址。 require 用来检查某些条件,如果不满足这些条件就会回推所有的状态变化。 在这个例子中, require(msg.sender == minter); 确保只有合约的创建者可以调用 mint。 一般来说,创建者可以随心所欲地铸造代币,但在某些时候,这将导致一种叫做 “溢出” 的现象。

请注意,由于默认的 算术检查模式 ,如果表达式 balances[receiver] += amount; 溢出交易将被还原。 即当任意精度算术中的 balances[receiver]+ amount 大于 uint (2**256 - 1)。同样在在函数 send 中的 balances[receiver] += amount; 这对语句来说也是如此。

Errors 用来向调用者描述错误信息。Error与 revert 语句 一起使用。 revert 语句无条件地中止执行并回退所有的变化,类似于 require 函数,它也同样允许你提供一个错误的名称和额外的数据,这些额外数据将提供给调用者(并最终提供给前端应用程序或区块资源管理器),这样就可以更容易地调试或应对失败。

任何人(已经拥有一些代币)都可以使用 send 函数来向其他人发送代币。如果发送者没有足够的代币可以发送, if 条件为真 revert 将触发失败,并通过 InsufficientBalance 向发送者提供错误细节

如果你用这个合约向一个地址发送代币,当你在区块链浏览器上看这个地址时,你不会看到任何东西,因为你发送代币的记录和余额的变化只存储在这个特定的Coin合约的数据存储中。 通过使用事件,你可以让 “区块链浏览器”,跟踪代币的交易和余额变化,但你必须查看代币合约地址(下的交易记录),而不是持有人的地址

如果 mint 被合约创建者外的其他人调用则什么也不会发生。 另一方面, send 函数可被任何人用于向他人发送币 (当然,前提是发送者拥有这些币)。记住,如果你使用合约发送币给一个地址,当你在区块链浏览器上查看该地址时是看不到任何相关信息的。因为,实际上你发送币和更改余额的信息仅仅存储在特定合约的数据存储器中。通过使用事件,你可以非常简单地为你的新币创建一个“区块链浏览器”来追踪交易和余额。

Solidity 智能合约入门相关推荐

  1. 分享实录|区块链技术与智能合约入门(开发实例)

    2019独角兽企业重金招聘Python工程师标准>>> 1 什么是区块链 1.1白话讲解区块链 现在区块链特别火,可能大家都听说过区块链,听说过比特币,那到底什么是区块链? 前几天和 ...

  2. 区块链之智能合约入门

    区块链之智能合约入门 第一步 安装环境 首先这里写的合约是指solidity合约,使用Remix IDE.所以我们第一步就是安装Remix IDE.remix ide是开发以太坊智能合约的神器,支持网 ...

  3. web3j用于solidity智能合约maven插件

    web3j maven插件用于基于solidity智能合约文件创建java类. 用法 插件的基本配置将从src/main/resources获取solidity文件,并将java类生成到src/mai ...

  4. 区块链智能合约入门:Hello world(2)

    这次的基础合约Hello world 有所升级,增加了updateMessage,让你可以随时更新自己想说的话. 其实在区块链智能合约入门:Hello world(1)这篇文章里已经介绍过,Hello ...

  5. Solidity智能合约库:区块链工程师的随身工具箱

    编者荐语: Solidity使用起来不如其他语言那般丝滑顺畅?安全事故难以避免?社区迎来适用于FISCO BCOS的Solidity智能合约库,轻松破解合约的各种小难题,让合约开发更加快速高效.省时省 ...

  6. 微众银行Solidity智能合约库:区块链工程师的随身工具箱

    区块链技术在经历了十余年的发展后,渐呈"燎原之势",不断在各行业落地生根.但同时,从技术的角度看,区块链应用开发仍然有着较高的门槛,存在不少痛点.为了提升应用开发各环节的用户体验, ...

  7. 以太坊solidity智能合约-生成随机数

    Solidity随机数生成 在以太坊的只能合约中,没有提供像其他面向对象编程一样的生成随机数的工具类或方法.其实,所谓的随机数也是伪随机的,没有哪一种语言能够真正的生成随机数. 对于solidity来 ...

  8. solidity智能合约implicit conversion异常

    问题场景 在使用^0.5.10版本的solidity时,如果使用this关键字会出现以下问题. 代码: require(tokenContract.balanceOf(this) >= _num ...

  9. solidity智能合约中tx.origin的正确使用场景

    简介 tx.origin是Solidity的一个全局变量,它遍历整个调用栈并返回最初发送调用(或事务)的帐户的地址.在智能合约中使用此变量进行身份验证会使合约容易受到类似网络钓鱼的攻击. 但针对tx. ...

最新文章

  1. Unrecognised tag: #39;encoding#39; (position: START_TAG seen ...lt;/versiongt;\r\n\t\t\t\tlt;en...
  2. Windows Azure HandBook (5) Azure混合云解决方案
  3. 不同页面之间实现参数传递的几种方式
  4. 快速学习C语言的途径,让你少走弯路!
  5. python移动平均线绘图_对python pandas 画移动平均线的方法详解
  6. 清屏函数 mysql,mysql中的常用函数总结
  7. ubuntu 20 kvm 安装macos
  8. eduline php5.3,index.php
  9. 【论文笔记】Don’t Stop Pretraining: Adapt Language Models to Domains and Tasks
  10. tcl android tv系统,TCL to launch motion-controlled Android TV first in China
  11. Mac制作windows10安装U盘
  12. bat的使用技巧汇总
  13. 计算机硬件系统组装的论文,有关计算机硬件组装论文.docx
  14. 解除IIS文件下载限制
  15. 怎么旋转PDF文件中的页面
  16. 数据科学家处理小数据的7个技巧。
  17. 同时安装 TensorflowPytorch
  18. TexturePacker 命令行方式调用参数
  19. Translation idea插件
  20. 苏宁11.11:系统拆分的一些经验谈

热门文章

  1. 电池-外部DC双电源供电设备电源自动切换电路分享(上)
  2. 爱上我的“手机女友”( 教你全面认识智能手机)
  3. 摩尔斯码(Morse Code)
  4. 易地推拓客团队分享没有利润,就没有品质和服务
  5. wordcloud词云报错ValueError: Only supported for TrueType fonts
  6. Java工具包:小写金额转换成大写金额
  7. 2012华东架构师大会将于2012年11月18日在上海举办
  8. 挂机空调加氟移机注意哪些问题?
  9. Proteus.Pro. 8.9.SP2安装教程及下载
  10. javase知识点大全总结