【CryptoZombies - 2 Solidity 进阶】002 Gas Time Units

2023-12-11 20:39

本文主要是介绍【CryptoZombies - 2 Solidity 进阶】002 Gas Time Units,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、前言

二、Gas - 驱动以太坊DApps的能源

1、讲解

1.以太币

2.Gas是啥

3.Gas能干嘛?

4.如何节省Gas

2、实战

1.要求

2.代码

三、Time Units 

1、讲解

2、实战1

1.要求

2.代码

3、实战2——僵尸冷却

1.要求

2.代码


一、前言

看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。

本次要为大家讲解有关于Gas的相关知识。

如果你想了解更多有关于机器学习、深度学习、区块链、计算机视觉等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们吧!

二、Gas - 驱动以太坊DApps的能源

1、讲解

Gas是solidity编程语言中非常与众不同的特征,我猜想,Gas是受到了比特币的启发。用户每次执行你的DAPP都需要支付一定的Gas,Gas可以使用以太币购买。

1.以太币

在讲Gas之前,我们先来简单了解一下什么是以太币。

以太币(ETH)是以太坊(Ethereum)的一种数字代币,被视为“比特币2.0版”,以太币涉及到的技术是与比特币不同的区块链技术“以太坊”(Ethereum),以太坊是一个开源的有智能合约成果的民众区块链平台。

在此,膜拜V神!

在此,膜拜V神!

在此,膜拜V神!

但是我们只是在此做个简单了解,因为我们这个博客是为了做CryptoZombies的学习记录,以后,我会逐步完善有关区块链和以太坊的内容。

2.Gas是啥

Gas是测量以太坊网络中运行交易或智能合约的计算工作的单位。该系统类似于使用千瓦(kW)来测量房屋内的电力;您使用的电力不是以美元和美分计量,而是以每小时千瓦时或千瓦计。

Gas用来衡量执行某些动作需要多少“工作量”,这些“工作量”就是为了执行该动作支付给网络的费用额。通俗理解,Gas是给矿工的佣金,以ETH 支付,无论是交易、执行智能合约并启动 DApps,还是支付数据存储费用,都需要用到 Gas。

3.Gas能干嘛?

Gas常用来作为激励矿工,那为什么要用Gas来作为激励呢?

区块链中有很多矿工,他们耗费大量的算力进行挖矿,无非就是为了抢夺nonce值,谁最先计算出来得到全网认可的nonce值,谁就获得了记账权,并能够获得对应的比特币。

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

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

注:如果你使用侧链,倒是不一定需要付费,比如咱们在 Loom Network 上构建的 CryptoZombies 就免费。你不会想要在以太坊主网上玩儿“魔兽世界”吧? - 所需要的 gas 可能会买到你破产。但是你可以找个算法理念不同的侧链来玩它。我们将在以后的课程中咱们会讨论到,什么样的 DApp 应该部署在太坊主链上,什么又最好放在侧链。

更多详细的有关Gas的内容,我们在介绍以太坊相关概念的时候,会着重介绍,在这里我们主要还是来聊一下技术吧!

4.如何节省Gas

我们了解到用户执行DAPP就会消耗Gas,那我们肯定是希望Gas消耗的越少越好,那我们如何节省Gas呢?

在第1课中,我们提到除了基本版的 uint 外,还有其他变种 uintuint8uint16uint32等。

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

如果一个 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放一起了。

 

2、实战

1.要求

为 Zombie 结构体添加两个属性:leveluint32)和readyTimeuint32)。因为希望同类型数据打成一个包,所以把它们放在结构体的末尾。32位足以保存僵尸的级别和时间戳了,这样比起使用普通的uint(256位),可以更紧密地封装数据,从而为我们省点 gas。

2.代码

pragma solidity >=0.5.0 <0.6.0;import "./ownable.sol";contract ZombieFactory is Ownable {event NewZombie(uint zombieId, string name, uint dna);uint dnaDigits = 16;uint dnaModulus = 10 ** dnaDigits;struct Zombie {string name;uint dna;// Add new data hereuint32 level;uint32 readyTime;}Zombie[] public zombies;mapping (uint => address) public zombieToOwner;mapping (address => uint) ownerZombieCount;function _createZombie(string memory _name, uint _dna) internal {uint id = zombies.push(Zombie(_name, _dna)) - 1;zombieToOwner[id] = msg.sender;ownerZombieCount[msg.sender]++;emit NewZombie(id, _name, _dna);}function _generateRandomDna(string memory _str) private view returns (uint) {uint rand = uint(keccak256(abi.encodePacked(_str)));return rand % dnaModulus;}function createRandomZombie(string memory _name) public {require(ownerZombieCount[msg.sender] == 0);uint randDna = _generateRandomDna(_name);randDna = randDna - randDna % 100;_createZombie(_name, randDna);}}

三、Time Units 

1、讲解

在上面的例题中,我们添加了两个uint32类型的数据level和readyTime。

level属性表示僵尸的级别。以后,在我们创建的战斗系统中,打胜仗的僵尸会逐渐升级并获得更多的能力。

重点是readyTime。

我们希望增加一个“冷却周期”,表示僵尸在两次猎食或攻击之之间必须等待的时间。如果没有它,僵尸每天可能会攻击和繁殖1,000次,这样游戏就太简单了。

为了记录僵尸在下一次进击前需要等待的时间,我们使用了 Solidity 的时间单位。

Solidity 使用自己的本地时间单位。

变量 now 将返回当前的unix时间戳(自1970年1月1日以来经过的秒数)。

注:Unix时间传统用一个32位的整数进行存储。这会导致“2038年”问题,当这个32位的unix时间戳不够用,产生溢出,使用这个时间的遗留系统就麻烦了。所以,如果我们想让我们的 DApp 跑够20年,我们可以使用64位整数表示时间,但为此我们的用户又得支付更多的 gas。真是个两难的设计啊!

Solidity 还包含秒(seconds)分钟(minutes)小时(hours)天(days)周(weeks) 和 年(years) 等时间单位。它们都会转换成对应的秒数放入 uint 中。所以 1分钟 就是 601小时是 3600(60秒×60分钟),1天86400(24小时×60分钟×60秒),以此类推。

下面是一些使用时间单位的实用案例:

uint lastUpdated;// 将‘上次更新时间’ 设置为 ‘现在’
function updateTimestamp() public {lastUpdated = now;
}// 如果到上次`updateTimestamp` 超过5分钟,返回 'true'
// 不到5分钟返回 'false'
function fiveMinutesHavePassed() public view returns (bool) {return (now >= (lastUpdated + 5 minutes));
}

 

2、实战1

1.要求

给DApp添加一个“冷却周期”的设定,让僵尸两次攻击或捕猎之间必须等待 1天

1.声明一个名为 cooldownTime 的uint,并将其设置为 1 days

2.因为在上一章中我们给 Zombie 结构体中添加 level 和 readyTime 两个参数,所以现在创建一个新的 Zombie 结构体时,需要修改 _createZombie(),在其中把新旧参数都初始化一下。

修改 zombies.push 那一行, 添加加2个参数:1(表示当前的 level )和uint32(now + cooldownTime)(现在+冷却时间,表示下次允许攻击的时间 readyTime)。

注:必须使用 uint32(...) 进行强制类型转换,因为 now 返回类型 uint256。所以我们需要明确将它转换成一个 uint32 类型的变量

2.代码

pragma solidity >=0.5.0 <0.6.0;import "./ownable.sol";contract ZombieFactory is Ownable {event NewZombie(uint zombieId, string name, uint dna);uint dnaDigits = 16;uint dnaModulus = 10 ** dnaDigits;// 1. Define `cooldownTime` hereuint 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 memory _name, uint _dna) internal {// 2. Update the following line:uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime))) - 1;zombieToOwner[id] = msg.sender;ownerZombieCount[msg.sender]++;emit NewZombie(id, _name, _dna);}function _generateRandomDna(string memory _str) private view returns (uint) {uint rand = uint(keccak256(abi.encodePacked(_str)));return rand % dnaModulus;}function createRandomZombie(string memory _name) public {require(ownerZombieCount[msg.sender] == 0);uint randDna = _generateRandomDna(_name);randDna = randDna - randDna % 100;_createZombie(_name, randDna);}}

3、实战2——僵尸冷却

冷却用于防止僵尸无限制地繁殖和捕猎,也会在以后用于限制僵尸之间的打频率。

1.要求

给DApp添加一个“冷却周期”的设定,让僵尸两次攻击或捕猎之间必须等待 1天

1.先定义一个 _triggerCooldown 函数。它要求一个参数,_zombie,表示一某个Zombie的storage指针。这个函数可见性设置为 internal

2.在函数中,把 _zombie.readyTime 设置为 uint32(now + cooldownTime)

3.创建一个名为 _isReady 的函数。这个函数的参数也是名为 _zombie 的 Zombie storage。这是一个 internal view 函数,并返回一个 bool 值。。

4.函数计算返回(_zombie.readyTime <= now),值为 true 或 false。这个功能的目的是判断下次允许猎食的时间是否已经到了。

 

2.代码

pragma solidity >=0.5.0 <0.6.0;import "./zombiefactory.sol";contract KittyInterface {function getKitty(uint256 _id) external view returns (bool isGestating,bool isReady,uint256 cooldownIndex,uint256 nextActionAt,uint256 siringWithId,uint256 birthTime,uint256 matronId,uint256 sireId,uint256 generation,uint256 genes);
}contract ZombieFeeding is ZombieFactory {KittyInterface kittyContract;function setKittyContractAddress(address _address) external onlyOwner {kittyContract = KittyInterface(_address);}// 1. Define `_triggerCooldown` function herefunction _triggerCooldown(Zombie storage _zombie) internal {_zombie.readyTime = uint32(now + cooldownTime);}// 2. Define `_isReady` function herefunction _isReady(Zombie storage _zombie) internal view returns(bool) {return (_zombie.readyTime <= now);}function feedAndMultiply(uint _zombieId, uint _targetDna, string memory _species) public {require(msg.sender == zombieToOwner[_zombieId]);Zombie storage myZombie = zombies[_zombieId];_targetDna = _targetDna % dnaModulus;uint newDna = (myZombie.dna + _targetDna) / 2;if (keccak256(abi.encodePacked(_species)) == keccak256(abi.encodePacked("kitty"))) {newDna = newDna - newDna % 100 + 99;}_createZombie("NoName", newDna);}function feedOnKitty(uint _zombieId, uint _kittyId) public {uint kittyDna;(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);feedAndMultiply(_zombieId, kittyDna, "kitty");}}

这篇关于【CryptoZombies - 2 Solidity 进阶】002 Gas Time Units的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/482046

相关文章

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

MySQL中时区参数time_zone解读

《MySQL中时区参数time_zone解读》MySQL时区参数time_zone用于控制系统函数和字段的DEFAULTCURRENT_TIMESTAMP属性,修改时区可能会影响timestamp类型... 目录前言1.时区参数影响2.如何设置3.字段类型选择总结前言mysql 时区参数 time_zon

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

如何使用 Bash 脚本中的time命令来统计命令执行时间(中英双语)

《如何使用Bash脚本中的time命令来统计命令执行时间(中英双语)》本文介绍了如何在Bash脚本中使用`time`命令来测量命令执行时间,包括`real`、`user`和`sys`三个时间指标,... 使用 Bash 脚本中的 time 命令来统计命令执行时间在日常的开发和运维过程中,性能监控和优化是不

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

[MySQL表的增删改查-进阶]

🌈个人主页:努力学编程’ ⛅个人推荐: c语言从初阶到进阶 JavaEE详解 数据结构 ⚡学好数据结构,刷题刻不容缓:点击一起刷题 🌙心灵鸡汤:总有人要赢,为什么不能是我呢 💻💻💻数据库约束 🔭🔭🔭约束类型 not null: 指示某列不能存储 NULL 值unique: 保证某列的每行必须有唯一的值default: 规定没有给列赋值时的默认值.primary key:

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。