0 参考来源

文档主要参考了https://cryptozombies.io的游戏dapp开发网站,一步步通过游戏来讲解solidity。

同时一些细节参考了网上的博客:
https://www.colabug.com/2416565.html
https://www.cnblogs.com/StephenWu/p/7096550.html
https://blog.csdn.net/liyuechun520/article/details/78408588
等等

附一些solidity和以太坊的参考资料:
official docs: http://www.ethdocs.org/en/latest
以太坊wiki:https://github.com/ethereum/wiki/wiki

以及github上的solidity指南合集:https://github.com/bkrem/awesome-solidity

1 solidity支持的数据类型

1.1 关键词罗列

string//字符串
uint //默认为uint256,还有uint8,uint128等等
int//默认为int256
[] //数组
struct //结构体
view //修饰函数,函数只读取数据不更改数据
pure //修饰函数,函数不访问应用中的数据,返回值完全取决于它的输入参数
mapping//映射:存储和查找数据所用的键-值对
address//地址
bytesN//N取1到32的任意整数值,默认的byte表示bytes1
require//限制条件,require使得函数在执行过程中,当不满足某些条件时抛出错误,并停止执行,个人感觉类似assert断言
inheritance//继承,合约可以访问被继承合约中定义的public函数
storage//永久存储在区块链中的变量
memory//临时变量,当外部函数对某合约调用完成时,内存型变量即被移除
//大多数时候你都用不到这些关键字,默认情况下 Solidity 会自动处理它们。 状态变量(在函数之外声明的变量)默认为“存储”形式,//并永久写入区块链;而在函数内部声明的变量是“内存”型的,它们函数调用结束后消失。
//有时,也需要手动声明存储类型,主要用于处理函数内的 _ 结构体 _ 和 _ 数组 _ ,用以标识指针指向的是原始上链数据还是副本internal//类似private,但是合约可以访问父合约中定义的internal函数
external//与public类似,但是这些函数只能在合约之外调用
constructor()//0.4.22及以后采用此作为构造函数
modifier//函数修饰符,修饰函数,它不能像函数那样被直接调用,只能被添加到函数定义的末尾,用以改变函数的行为,在其他语句执行前,先检查先验条件,给继承这个函数的modifier的对应函数加一个特定约束,在继承的函数体将在_特殊符号的位置被插入
now//当前unix时间戳(自1970年1月1日以来经过的秒数)

Solidity 定义的函数的属性默认为公共。 这就意味着任何一方 (或其它合约) 都可以调用你合约里的函数。

在 Solidity 中,有一些全局变量可以被所有函数调用, msg.sender,它指的是当前调用者(或智能合约)的 address

Solidity 并不支持原生的字符串比较, 我们只能通过比较两字符串的 keccak256 哈希值来进行判断

1.2 参数类型细节

1.2.1 数组

https://www.jianshu.com/p/5ca861cce245

1.2.2 memory storage

引用类型的变量在传值时需要memory或storage关键词,一般用于结构体struct、数组或者mapping当中,具体而言,就是传入的值是一个副本还是原始参数。
例如:

struct Zombie {uint a;bool b;
}
function modify(Zombie storage _zombie) internal {_zombie.a = 6;//这样就修改了传入对象的原始值
}
function fake_modify(Zombie memory _zombie) internal {_zombie.a = 5;//这样只是修改了临时变量的值
}

注:函数参数如果为storage类型时,函数的类型必须为internal或者private。

1.2.3 函数修饰符

可以带参数

// 存储用户年龄的映射
mapping (uint => uint) public age;// 限定用户年龄的修饰符
modifier olderThan(uint _age, uint _userId) {require(age[_userId] >= _age);_;
}// 必须年满16周岁才允许开车 (至少在美国是这样的).
// 我们可以用如下参数调用`olderThan` 修饰符:
function driveCar(uint _userId) public olderThan(16, _userId) {// 其余的程序逻辑
}

1.2.4 view

当玩家从外部调用一个view函数,是不需要支付一分 gas 的。这是因为 view 函数不会真正改变区块链上的任何数据 - 它们只是读取。

注意:如果一个 view 函数在另一个函数的内部被调用,而调用函数与 view 函数的不属于同一个合约,也会产生调用成本。这是因为如果主调函数在以太坊创建了一个事务,它仍然需要逐个节点去验证。所以标记为 view 的函数只有在外部调用时才是免费的。

1.2.5 数组

    uint[] a = new uint[](3);//注意在括号里写lengthint b;constructor() public {a.push(1);//可以通过push动态扩大长度a.push(2);a.push(3);a.pop();//弹出a.length;//数组长度}function seta(uint _a) internal pure {_a = 3;}function query() public view returns(uint) {return a[2];}

1.2.6 事件

事件 是合约和区块链通讯的一种机制。前端应用“监听”某些事件,并做出反应。
比如:

// 这里建立事件
event IntegersAdded(uint x, uint y, uint result);function add(uint _x, uint _y) public {uint result = _x + _y;//触发事件,通知appIntegersAdded(_x, _y, result);return result;
}

app 前端可以监听这个事件。JavaScript 实现如下:

YourContract.IntegersAdded(function(error, result) { // 干些事
}

1.2.7 modifier函数修饰符

modifier onlyOwner() {require(msg.sender == owner);_;
}contract MyContract is Ownable {event LaughManiacally(string laughter);//注意! `onlyOwner`上场 :function likeABoss() external onlyOwner {LaughManiacally("Muahahahaha");}
}

当你调用 likeABoss 时,首先执行 onlyOwner 中的代码, 执行到 onlyOwner 中的 _; 语句时,程序再返回并执行 likeABoss 中的代码。

思考:直接在函数中添加require(…)不是一样吗?意义在于什么?
简化一部分通用权限处理的编写数量?

1.2.8 时间单位

秒(seconds),分钟(minutes),小时(hours),天(days),周(weeks) 和 年(years)

1 days//也需要用复数

now 返回类型 uint256

2 一个小栗子

pragma solidity ^0.4.24;//定义版本import "./ownable.sol";//可以调用其他合约contract ZombieFactory is Ownable { //is 继承event NewZombie(uint zombieId, string name, uint dna);//事件uint dnaDigits = 16;uint dnaModulus = 10 ** dnaDigits;uint cooldownTime = 1 days;struct Zombie {//结构体string name;uint dna;uint32 level;uint32 readyTime;}Zombie[] public zombies;mapping (uint => address) public zombieToOwner;//映射mapping (address => uint) ownerZombieCount;function _createZombie(string _name, uint _dna) internal {uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime))) - 1;zombieToOwner[id] = msg.sender;ownerZombieCount[msg.sender]++;NewZombie(id, _name, _dna);//触发事件,高版本需要添加emit}function _generateRandomDna(string _str) private view returns (uint) {uint rand = uint(keccak256(_str));return rand % dnaModulus;}function createRandomZombie(string _name) public {require(ownerZombieCount[msg.sender] == 0);uint randDna = _generateRandomDna(_name);randDna = randDna - randDna % 100;_createZombie(_name, randDna);}}

3 开发环境搭建

3.1 在线环境(推荐)

remix:https://remix.ethereum.org

缺点:不稳定,时常打不开。

优点:功能强大,包含编译、调试、生成abi等功能,可以选择各种版本的编译器。


可以看到,当选择编译器版本过高时,构造函数的问题被解析出来。

3.2 离线环境

3.2.1 remix离线

代码仓库:https://github.com/ethereum/remix-ide/

安装:npm安装

npm install remix-ide -g
remix-ide

or git下载代码仓库安装

git clone https://github.com/ethereum/remix-ide.git
git clone https://github.com/ethereum/remix.git # only if you plan to link remix and remix-ide repositories and develop on it.
cd remix-ide
npm install
npm run setupremix  # only if you plan to link remix and remix-ide repositories and develop on it.
npm start

报错:

 fatal: unable to connect to github.com:
npm ERR! github.com[0: 13.229.188.59]: errno=No such file or directory
npm ERR! github.com[1: 13.250.177.223]: errno=No such file or directory
npm ERR! github.com[2: 52.74.223.119]: errno=No such file or directory

可以在本地跑一个remix:在http://127.0.0.1:8080开启本地remix窗口。

缺点:只包含了最新版本的编译器,不支持老版本。

3.2.2 vscode

安装Juan Blanco的solidity插件。

优点:本地编译。

缺点:无法调试。且如果需要更换低版本编译器,需要自己动手修改。

4 部署与调试

4.1 运行

remix可以通过javascript虚拟环境来模拟客户端连入一个虚构的区块链网络,从而进行合约测试。

会预先提供一些含有ether的账户供测试使用,足够合约运行。

和真实以太坊一样,部署运行合约需要消耗gas。

部署运行成功后,浏览器会自动生成可以外部调用的方法栏,输入参数即可观察合约方法运行结果。

4.2 调试

???

5 多源文件

solidity支持多源文件的编写。

多个源文件的合约部署:管理平台能够做到吗?

行内wifi,使用不了cita的js sdk!!!

6 与其他合约的交互

如果我们的合约需要和区块链上的其他的合约会话,则需先定义一个 interface (接口)。

假设在区块链上有这么一个合约:

contract LuckyNumber {mapping(address => uint) numbers;function setNum(uint _num) public {numbers[msg.sender] = _num;}function getNum(address _myAddress) public view returns (uint) {return numbers[_myAddress];}
}

假设我们有一个外部合约,使用 getNum 函数可读取其中的数据。

首先,我们定义 LuckyNumber 合约的 interface :

contract NumberInterface {function getNum(address _myAddress) public view returns (uint);
}

我们可以在合约中这样使用:

contract MyContract {address NumberInterfaceAddress = 0xab38...;// ^ 这是FavoriteNumber合约在以太坊上的地址NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);// 现在变量 `numberContract` 指向另一个合约对象function someFunction() public {// 现在我们可以调用在那个合约中声明的 `getNum`函数:uint num = numberContract.getNum(msg.sender);// ...在这儿使用 `num`变量做些什么}
}

7 外部常用库

7.1 OpenZeppelin库 Ownable合约(禁止第三方修改自己的合约,同时留有后门函数可以自己修改)

设置合约中函数的所有权(执行权)

大多数人开发自己的 Solidity DApps,都是从复制/粘贴 Ownable 开始的,从它再继承出的子类,并在之上进行功能开发。

附合约:

/*** @title Ownable* @dev The Ownable contract has an owner address, and provides basic authorization control* functions, this simplifies the implementation of "user permissions".*/
contract Ownable {address public owner;event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);/*** @dev The Ownable constructor sets the original `owner` of the contract to the sender* account.*/function Ownable() public {owner = msg.sender;}/*** @dev Throws if called by any account other than the owner.*/modifier onlyOwner() {require(msg.sender == owner);_;}/*** @dev Allows the current owner to transfer control of the contract to a newOwner.* @param newOwner The address to transfer ownership to.*/function transferOwnership(address newOwner) public onlyOwner {require(newOwner != address(0));OwnershipTransferred(owner, newOwner);owner = newOwner;}}

8 DApp

8.1

部署在以太坊上的 DApp,并不能保证它真正做到去中心,你需要阅读并理解它的源代码,才能防止其中没有被部署者恶意植入后门。作为开发人员,如何做到既要给自己留下修复 bug 的余地,又要尽量地放权给使用者,以便让他们放心你,从而愿意把数据放在你的 DApp 中,这确实需要个微妙的平衡。

8.2 与众不同的特征——Gas驱动

一个 DApp 收取多少 gas 取决于功能逻辑的复杂程度。每个操作背后,都在计算完成这个操作所需要的计算资源,(比如,存储数据就比做个加法运算贵得多), 一次操作所需要花费的 gas 等于这个操作背后的所有运算花销的总和。

以太坊就像一个巨大、缓慢、但非常安全的电脑。当你运行一个程序的时候,网络上的每一个节点都在进行相同的运算,以验证它的输出 —— 这就是所谓的“去中心化” 由于数以千计的节点同时在验证着每个功能的运行,这可以确保它的数据不会被被监控,或者被刻意修改。

可能会有用户用无限循环堵塞网络,抑或用密集运算来占用大量的网络资源,为了防止这种事情的发生,以太坊的创建者为以太坊上的资源制定了价格,想要在以太坊上运算或者存储,你需要先付费。

8.3 如何节省Gas

通常情况下我们不会考虑使用 uint 变种,因为无论如何定义 uint的大小,Solidity 为它保留256位的存储空间。例如,使用 uint8 而不是uint(uint256)不会为你节省任何 gas。除非,把uint绑定到struct里面。

如果一个 struct 中有多个 uint,则尽可能使用较小的 uint, Solidity 会将这些 uint 打包在一起,从而占用较少的存储空间。

struct NormalStruct {uint a;uint b;uint c;
}struct MiniMe {uint32 a;uint32 b;uint c;
}// 因为使用了结构打包,`mini` 比 `normal` 占用的空间更少
NormalStruct normal = NormalStruct(10, 20, 30);
MiniMe mini = MiniMe(10, 20, 30);

所以,当 uint 定义在一个 struct 中的时候,尽量使用最小的整数子类型以节约空间。 并且把同样类型的变量放一起(即在 struct 中将把变量按照类型依次放置),这样 Solidity 可以将存储空间最小化。例如,有两个 struct:

uint c; uint32 a; uint32 b; 和 uint32 a; uint c; uint32 b;

前者比后者需要的gas更少,因为前者把uint32放一起了。

9 开发注意事项

9.1 public external仔细排查

需要仔细地检查所有声明为 public 和 external的函数,一个个排除用户滥用它们的可能,谨防安全漏洞。请记住,如果这些函数没有类似 onlyOwner 这样的函数修饰符,用户能利用各种可能的参数去调用它们。

9.2 利用’view’函数节省gas

外部调用一个view函数,是不需要支付一分 gas 的。

9.3 storage慎用!

使用storage(存储)是相当昂贵的,”写入“操作尤其贵。

这是因为,无论是写入还是更改一段数据, 这都将永久性地写入区块链。”永久性“啊!需要在全球数千个节点的硬盘上存入这些数据,随着区块链的增长,拷贝份数更多,存储量也就越大。这是需要成本的!

9.4 反人类的做法

为了节省gas,当需要查询的时候,不建议使用mapping即类似哈希的方式,因为这样会多余写入。

反而需要使用遍历的方法,因为这样通过view虽然查询变慢,但是花费gas也较少。

其他概念

1 侧链

问题:

1 address(0)

来源:

ownable合约function transferOwnership(address newOwner) public onlyOwner {require(newOwner != address(0)); //这里的address(0)是什么意思?如果是任意新地址,那不是怎么都通不过了?OwnershipTransferred(owner, newOwner);owner = newOwner;}

solidity中:是一个初始化的地址或者就是0x0?

以太坊交易中:

transaction = {nonce: '0x0', gasLimit: '0x6acfc0', // 7000000gasPrice: '0x4a817c800', // 20000000000to: '0x0',value: '0x0',data: '0xfffff'
};

a new contract will be created by executing the code in data (this is what is meant by “code that returns the code”). The address of the newly created contract is technically known beforehand as it’s based on the address of the sender and it’s current nonce. That address becomes the official address of the contract after mining.

2 bytesN表示什么?

3 数组可以作为函数参数吗?

solidity基础学习相关推荐

  1. 【转】oracle PLSQL基础学习

    [转]oracle PLSQL基础学习 --oracle 练习: /**************************************************PL/SQL编程基础****** ...

  2. python创建对象的格式为_Python入门基础学习(面向对象)

    python基础学习笔记(四) 面向对象的三个基本特征: 封装:把客观事物抽象并封装成对象,即将属性,方法和事件等集合在一个整体内 继承:允许使用现有类的功能并在无须重新改写原来的类情况下,对这些功能 ...

  3. 虚幻引擎虚拟现实开发基础学习教程

    流派:电子学习| MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz 语言:英语+中英文字幕(根据原英文字幕机译更准确)|大小解压后:3.93 GB |时长:5h 15m 了 ...

  4. 动画产业基础学习教程 Rad How to Class – Animation Industry Fundamentals

    如何分类--动画产业基础 大小解压后:6.2G 含课程素材 1920X1080 mp4 语言:英语+中英文字幕(根据原英文字幕机译更准确) 信息: 绘画技巧.解剖学.角色设计.透视和整体讲故事--这门 ...

  5. Blender纹理基础学习视频教程 CGCookie – Fundamentals of Texturing in Blender

    Blender纹理基础学习视频教程 CGCookie – Fundamentals of Texturing in Blender Blender纹理基础学习视频教程 CGCookie – Funda ...

  6. ue5新手零基础学习教程 Unreal Engine 5 Beginner Tutorial - UE5 Starter Course

    ue5新手零基础学习教程 Unreal Engine 5 Beginner Tutorial - UE5 Starter Course! 教程大小解压后:4.96G 语言:英语+中英文字幕(机译)时长 ...

  7. 0基础学好python难不难_零基础学习Python难不难?Python有什么优势?

    原标题:零基础学习Python难不难?Python有什么优势? Python是一种计算机程序设计语言.首先,我们普及一下编程语言的基础知识.用任何编程语言来开发程序,都是为了让计算机干活,比如下载一个 ...

  8. 计算机一级ps2019,2019年计算机一级考试PS基础学习点子:PS菜单中英文对照表.docx...

    2019 年计算机一级考试 PS 基础学习点子: PS 菜单中英文对照表 PS菜单中英文对照表 一.File New 2.Open 3.Open As 4.Open Recent Close 6.Sa ...

  9. Java零基础学习难吗

    java编程是入行互联网的小伙伴们大多数的选择,那么对于零基础的小伙伴来说Java零基础学习难吗?如果你是初学者,你可以很好的理解java编程语言.并不困难.如果你的学习能力比较高,那么你对Java的 ...

最新文章

  1. 在实践中深入理解IP协议
  2. ffmpeg+ffserver搭建流媒体服务器
  3. Echart常用效果(一)
  4. java为什么打不开jar_带你上手阿里开源的 Java 诊断利器:Arthas
  5. easyui表格编辑事件_Unity手游开发札记——从Odin插件聊基于元数据的编辑器实现
  6. hashMap和hashTable的区别(个人总结)
  7. how to handdraw a web UI for applications? Figma
  8. 国内油价今日将迎九连跌 出租车燃油费望调整
  9. 同步 GIT@OSC 实现MARKDOWN文件发布或更新到CSDN博客中
  10. php怎么取随机3位数字,使用php怎么从指定数字中获取随机组合
  11. [react] react16的reconciliation和commit分别是什么?
  12. 几个功能强大的系统源码(机票分销、机票预订、OA、手机充值、wifi营销、网络超市、体检平台)...
  13. 树莓派 编译live555_树莓派raspberry4B入坑指南 part-6 docker编译古灵币钱包
  14. C#项目中的bin目录和obj目录的区别,以及Debug版本和Release版本的区别(转载)...
  15. 8000401a 错误 ,检索 COM 类工厂中 CLSID 为 的组件时失败,原因是出现以下错误: 8000401a。...
  16. logo计算机编程简单指令,完整版PCLOGO编程技巧
  17. 中国石油燃气蓄能器市场深度研究分析报告(2021)
  18. 九宫怎么排列和使用_奇门遁甲九宫数字 九宫数字的排列和算法
  19. MEM/MBA 写作-论证有效性分析(03) 论证有效性分析 写作模板
  20. 树莓派用c语言pwm控制电机,树莓派学习笔记之PWM控制直流电机转速

热门文章

  1. 字符及字符串(数字串)输入输出字符串处理函数
  2. baidumap vue 判断范围_vue--百度地图点覆盖和区域划分
  3. Expression is not assignable
  4. mp4文件播放不了怎么办?
  5. mysql workbench 主从_MySQL Workbench 使用教程 (四)
  6. 软件配置管理中三个基线概念
  7. ScriptManager的简单用法
  8. 这里整理了基于java平台的常用资源
  9. 如何下载tomcat历史版本
  10. 2007年生日祝福:宝贝,谢谢您陪伴我走过人生风风雨雨,祝您2007年生日快乐!