Solidity學習記錄
第一章 創建生產僵尸的工廠
第二章 設定僵尸的攻擊功能
第三章 撰寫DAPP所需的基礎理論
第四章 完善僵尸功能
文章目錄
- Solidity學習記錄
- 前言
- 一、本章主要目的
- 二、學習程序
- 1.本節課程知識點
- 2.最終代碼
- 總結
前言
國慶假期,事情比較多,更新拖了兩天,請見諒,一、本章主要目的
在之前的課程,我們學習了Solidity的大部分知識,本章我們學習 payable 函式,學習如何開發可以接收其他玩家付款的DApp,并學習通過函式修飾符來完善我們的程式,
二、學習程序
1.本節課程知識點
1、payable 修飾符
payable 方法是讓 Solidity 和以太坊變得如此酷的一部分 —— 它們是一種可以接收以太的特殊函式,在以太坊中, 因為錢 (Ether), 資料 (transaction payload), 以及合約代碼本身都存在于以太坊,你可以在同時呼叫函式 并付錢給另外一個合約,如果一個函式沒標記為payable, 當用戶嘗試發送以太時,函式將拒絕事務,
2、msg.value 是一種可以查看向合約發送了多少以太的方法,常用msg.value == XXX 來表示,如果把事務想象成一個信封,你發送到函式的引數就是信的內容, 添加一個 value 很像在信封里面放錢 —— 信件內容和錢同時發送給了接收者,
3、我們可以通過 transfer 向任何以太坊地址付錢,提現寫法:
contract GetPaid is Ownable {
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
}
4、Solidity 中最好的亂數生成器是 keccak256 哈希函式,但這個方法很容易被不誠實的節點攻擊,
5、Solidity 的 keccak256 被攻擊原理:
在以太坊上, 當你在和一個合約上呼叫函式的時候, 你會把它廣播給一個節點或者在網路上的 transaction 節點們, 網路上的節點將收集很多事務, 試著成為第一個解決計算密集型數學問題的人,作為“作業證明”,然后將“作業證明”(Proof of Work, PoW)和事務一起作為一個 block 發布在網路上,
一旦一個節點解決了一個PoW, 其他節點就會停止嘗試解決這個 PoW, 并驗證其他節點的事務串列是有效的,然后接受這個節點轉而嘗試解決下一個節點,
這就讓我們的亂數函式變得可利用了
如果運行一個節點,可以 只對我自己的節點 發布一個事務,且 不分享它 , 如果不是我想要的結果,我就不把這個事務包含進我要解決的下一個區塊中去,我可以一直運行這個方法,直到獲得了我想要的結果(類似于手游弱聯網(并非實時監測是否聯網)游戲,通過斷網的方式來不斷重繪出自己想要的東西,如:抽卡,掉落的稀有的物品等),
2.最終代碼
代碼如下:
ownable.sol
//this is ownable.sol
pragma solidity ^0.4.19;
/**
* @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 private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns(address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns(bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @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 {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
zombiefactory.sol
//this is zombiefactory.sol
pragma solidity ^0.4.19;
import "./ownable.sol";
contract ZombieFactory is Ownable {
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;
uint16 winCount;
uint16 lossCount;
}
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), 0, 0)) - 1;
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender]++;
emit NewZombie(id, _name, _dna);
}
function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(abi.encodePacked(_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);
}
}
zombiefeeding.sol
//this is zombiefeeding.sol
pragma solidity ^0.4.19;
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;
modifier ownerOf(uint _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
_;
}
function setKittyContractAddress(address _address) external onlyOwner {
kittyContract = KittyInterface(_address);
}
function _triggerCooldown(Zombie storage _zombie) internal {
_zombie.readyTime = uint32(now + cooldownTime);
}
function _isReady(Zombie storage _zombie) internal view returns (bool) {
return (_zombie.readyTime <= now);
}
function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal ownerOf(_zombieId) {
Zombie storage myZombie = zombies[_zombieId];
require(_isReady(myZombie));
_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);
_triggerCooldown(myZombie);
}
function feedOnKitty(uint _zombieId, uint _kittyId) public {
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
feedAndMultiply(_zombieId, kittyDna, "kitty");
}
}
zombiehelper.sol
//this is zombiehelper.sol
pragma solidity ^0.4.19;
import "./zombiefeeding.sol";
contract ZombieHelper is ZombieFeeding {
uint levelUpFee = 0.001 ether;
modifier aboveLevel(uint _level, uint _zombieId) {
require(zombies[_zombieId].level >= _level);
_;
}
function withdraw() external onlyOwner {
address _owner = owner();
_owner.transfer(address(this).balance);
}
function setLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}
function levelUp(uint _zombieId) external payable {
require(msg.value == levelUpFee);
zombies[_zombieId].level++;
}
function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) ownerOf(_zombieId) {
zombies[_zombieId].name = _newName;
}
function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) ownerOf(_zombieId) {
zombies[_zombieId].dna = _newDna;
}
function getZombiesByOwner(address _owner) external view returns(uint[]) {
uint[] memory result = new uint[](ownerZombieCount[_owner]);
uint counter = 0;
for (uint i = 0; i < zombies.length; i++) {
if (zombieToOwner[i] == _owner) {
result[counter] = i;
counter++;
}
}
return result;
}
}
zombieattack.sol
//this is zombiehelper.sol
pragma solidity ^0.4.19;
import "./zombiehelper.sol";
contract ZombieAttack is ZombieHelper {
uint randNonce = 0;
uint attackVictoryProbability = 70;
function randMod(uint _modulus) internal returns(uint) {
randNonce++;
return uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % _modulus;
}
function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) {
Zombie storage myZombie = zombies[_zombieId];
Zombie storage enemyZombie = zombies[_targetId];
uint rand = randMod(100);
if (rand <= attackVictoryProbability) {
myZombie.winCount++;
myZombie.level++;
enemyZombie.lossCount++;
feedAndMultiply(_zombieId, enemyZombie.dna, "zombie");
} else {
myZombie.lossCount++;
enemyZombie.winCount++;
_triggerCooldown(myZombie);
}
}
}
總結
本章知識點比較少,大部分是使用之前的知識內容,本章重點知識只有兩個:
1.可以使用 payable 來付款,value 用來表示付多少錢,transfer 用來轉賬(可以轉給任何人),
2.keccak256 函式是不安全的,有一個亂數方法是利用 oracle 來訪問以太坊區塊鏈之外的亂數函式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/305758.html
標籤:區塊鏈
上一篇:以太坊理解
