【CryptoZombies - 2 Solidity 进阶】001 智能合约的不可篡改性与Ownable

本文主要是介绍【CryptoZombies - 2 Solidity 进阶】001 智能合约的不可篡改性与Ownable,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、前言

二、不可篡改性与外部依赖

1、讲解

1.不可篡改

2.外部依赖关系

2、实战

1.要求

2.代码

三、Ownable 

1、讲解

1.构造函数

2.函数修饰符

2、实战

1.要求

2.代码


一、前言

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

终于要更换新标题了,这次命名为进阶,也就是说之前的是基础教程,因为从这一次的内容开始,我们将通过这个项目实战,更加深入了解solidity。也更能区分solidity和其他的一些编程语言的区别。当然,我们会在后面详细说明。

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

二、不可篡改性与外部依赖

1、讲解

1.不可篡改

我们学完了solidity的基础,但是我们发现,我们讲的 Solidity 和其他语言没有质的区别,它长得也很像 JavaScript。但是以太坊solidity开发的是去中心化的应用DAPP,这个和普通的应用是有很大的差别的。

我们都知道,以太坊是区块链2.0,我们在以太坊上部署合约,就满足区块链中的一个非常重要的特性,不可篡改

所以如果我们将我们写好的合约上传到以太坊,就不能再修改了。合约就会永远保存在以太坊上,这个特性保证了合约的安全性。但是我们的合约编写的一旦有漏洞,我们就只能放弃这个合约,然后转移到新的修复后的合约上。

总结一下,这个特性的优点如下:

因为其不可篡改,一个合约定义并部署好后,所有的程序会按照既定的代码一丝不苟的执行,其结果不会被中途恶意用户篡改。从而极大的保证了合约的安全性。也更具公平性。

 也不是说这个特性是没有缺点的:

因为其不可篡改,一旦发现合约存在问题,那这个合约就作废了,并且一直留存在以太坊中,久而久之,无用的协议增多,白白浪费很多内存。就像你电脑中一旦有了垃圾文件,一直删不掉,着实挺难受,强迫症更难受。

2.外部依赖关系

我们之前调用加密小猫,在地址中使用的地址是固定的,也就是硬编码到DAPP中的,一旦加密小猫出现问题,那我们的DAPP也就会出现问题了,所以我们在真实的使用中是不能使用硬编码的,我们要通过一个函数来获取其地址,这样我们就可以便于在DAPP的关键部分中使用参数进行数据的修改了。

在这个过程中我们对外部的合约有依赖,涉及到了外部依赖关系,这种依赖,我们应该是灵活可动态修正的依赖。

2、实战

1.要求

我们来定义一个函数,从 kitty 合约中获取它的基因:

1.删除采用硬编码 方式的 ckAddress 代码行。

2.之前创建 kittyContract 变量的那行代码,修改为对 kittyContract 变量的声明 -- 暂时不给它指定具体的实例。

3.创建名为 setKittyContractAddress 的函数, 它带一个参数 _addressaddress类型), 可见性设为external。

4.在函数内部,添加一行代码,将 kittyContract 变量设置为返回值:KittyInterface(_address)

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 {// 1. Remove this:address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;// 2. Change this to just a declaration:KittyInterface kittyContract = KittyInterface(ckAddress);// 3. Add setKittyContractAddress method herefunction 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");}}

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 {// 1. Remove this:// 2. Change this to just a declaration:KittyInterface kittyContract;// 3. Add setKittyContractAddress method herefunction setKittyContractAddress(address _address) external {kittyContract = KittyInterface(_address);}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");}}

三、Ownable 

1、讲解

在上面的例题中,我们将函数声明为外部。我想大家应该还记得外部的特点:

定义为外部的函数只能在合约之外调用 - 它们不能被合约内的其他函数调用。

如果我们这样定义函数,函数之外的合约都可以调用。也就是说任何可以调用该函数的人都可以修改加密猫的地址。我们确实希望这个地址能够在合约中修改,但是我们并不希望所有人都可以修改,所以我们还要继续完善权限。

在这里我们使用Ownable合约。首先我们先举一个示例,然后再逐步讲解。

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;}
}

在这里,我们涉及到如下知识点:

1.构造函数

这个名词,想必大家都很熟悉了,在这里的构造函数是:

  function Ownable() public {owner = msg.sender;}

在以太坊中,构造函数不是必须的,但是一旦我们使用,就要遵守相关规则:

1.与合约同名。

2.构造函数只在合约最初被创建的时候执行一次。

 

2.函数修饰符

函数修饰符跟函数很类似,不过函数修饰符是用来修饰其他已有函数的,在这里的函数修饰符是:

  modifier onlyOwner() {require(msg.sender == owner);_;}

函数修饰符在这里的作用是:

在其他语句执行前,为它检查下先验条件。

在我们的这个样例中,我们使用函数修饰符检查调用者,确保只有合约的主人才能运行本函数。

函数修饰符看起来跟函数没什么不同,不过关键字modifier 告诉编译器,这是个modifier(修饰符),而不是个function(函数)。它不能像函数那样被直接调用,只能被添加到函数定义的末尾,用以改变函数的行为

接下来我们来看一下修饰符如何使用。

contract MyContract is Ownable {event LaughManiacally(string laughter);//注意! `onlyOwner`上场 :function likeABoss() external onlyOwner {LaughManiacally("Muahahahaha");}
}

对于上述代码执行过程如下:

1.调用likeABoss 函数时,首先执行 onlyOwner 中的代码。

2.执行到 onlyOwner 中的 _; 语句时,程序再返回并执行 likeABoss 中的代码。

注:一般修饰符常用于添加require检查

除了上面这两个还有很多,我们以后再慢慢讲到。

所以一个Ownable合约基本流程如下:

1.合约创建,构造函数先行,将其 owner 设置为msg.sender(其部署者)。

2.为它加上一个修饰符 onlyOwner,它会限制陌生人的访问,将访问某些函数的权限锁定在 owner 上。

3.允许将合约所有权转让给他人

 

2、实战

1.要求

在僵尸代码中实现小猫的基因。:

1.在程序中导入 ownable.sol 的内容。 。

2.修改 ZombieFactory 合约, 让它继承自 Ownable。

3.将 onlyOwner 函数修饰符添加到 setKittyContractAddress 中

2.代码

pragma solidity >=0.5.0 <0.6.0;// 1. Import here
import "./ownable.sol";// 2. Inherit here:
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;}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);}}
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;// Modify this function:function setKittyContractAddress(address _address) external onlyOwner {kittyContract = KittyInterface(_address);}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 进阶】001 智能合约的不可篡改性与Ownable的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

使用Python实现表格字段智能去重

《使用Python实现表格字段智能去重》在数据分析和处理过程中,数据清洗是一个至关重要的步骤,其中字段去重是一个常见且关键的任务,下面我们看看如何使用Python进行表格字段智能去重吧... 目录一、引言二、数据重复问题的常见场景与影响三、python在数据清洗中的优势四、基于Python的表格字段智能去重

Spring AI集成DeepSeek三步搞定Java智能应用的详细过程

《SpringAI集成DeepSeek三步搞定Java智能应用的详细过程》本文介绍了如何使用SpringAI集成DeepSeek,一个国内顶尖的多模态大模型,SpringAI提供了一套统一的接口,简... 目录DeepSeek 介绍Spring AI 是什么?Spring AI 的主要功能包括1、环境准备2

Spring AI与DeepSeek实战一之快速打造智能对话应用

《SpringAI与DeepSeek实战一之快速打造智能对话应用》本文详细介绍了如何通过SpringAI框架集成DeepSeek大模型,实现普通对话和流式对话功能,步骤包括申请API-KEY、项目搭... 目录一、概述二、申请DeepSeek的API-KEY三、项目搭建3.1. 开发环境要求3.2. mav

MySQL进阶之路索引失效的11种情况详析

《MySQL进阶之路索引失效的11种情况详析》:本文主要介绍MySQL查询优化中的11种常见情况,包括索引的使用和优化策略,通过这些策略,开发者可以显著提升查询性能,需要的朋友可以参考下... 目录前言图示1. 使用不等式操作符(!=, <, >)2. 使用 OR 连接多个条件3. 对索引字段进行计算操作4

Python3脚本实现Excel与TXT的智能转换

《Python3脚本实现Excel与TXT的智能转换》在数据处理的日常工作中,我们经常需要将Excel中的结构化数据转换为其他格式,本文将使用Python3实现Excel与TXT的智能转换,需要的可以... 目录场景应用:为什么需要这种转换技术解析:代码实现详解核心代码展示改进点说明实战演练:从Excel到

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读取修改概述在现实中,很多工作都需要与数据打交

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

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