【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

相关文章

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

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

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

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

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

智能交通(二)——Spinger特刊推荐

特刊征稿 01  期刊名称: Autonomous Intelligent Systems  特刊名称: Understanding the Policy Shift  with the Digital Twins in Smart  Transportation and Mobility 截止时间: 开放提交:2024年1月20日 提交截止日

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

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

Flutter 进阶:绘制加载动画

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