Skip to content

Latest commit

 

History

History
348 lines (278 loc) · 12.2 KB

battle-04.md

File metadata and controls

348 lines (278 loc) · 12.2 KB
title actions requireLogin material
Logica di refactoring generale
checkAnswer
hints
true
editor
language startingCode answer
sol
zombiefeeding.sol zombieattack.sol zombiehelper.sol zombiefactory.sol ownable.sol
pragma solidity ^0.4.25; 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; // 1. Crea il modificatore qui 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); } // 2. Aggiungi il modificatore nella definizione della funzione: function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal { // 3. Rimuovi questa linea require(msg.sender == zombieToOwner[_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"); } }
pragma solidity ^0.4.25; 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 { } }
pragma solidity ^0.4.25; 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) { require(msg.sender == zombieToOwner[_zombieId]); zombies[_zombieId].name = _newName; } function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) { require(msg.sender == zombieToOwner[_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; } }
pragma solidity ^0.4.25; 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; } 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]++; 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); } }
pragma solidity ^0.4.25; /** * @title Ownable * @dev Il contratto di proprietà ha un indirizzo del proprietario e fornisce funzioni di controllo * delle autorizzazioni di base, ciò semplifica l'implementazione delle "autorizzazioni dell'utente". */ contract Ownable { address private _owner; event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev Il costruttore di proprietà imposta il `proprietario` originale del contratto sull'account del mittente. */ constructor() internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return l'indirizzo del proprietario. */ function owner() public view returns(address) { return _owner; } /** * @dev Genera se chiamato da qualsiasi account diverso dal proprietario. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return vero se `msg.sender` è il proprietario del contratto. */ function isOwner() public view returns(bool) { return msg.sender == _owner; } /** * @dev Consente all'attuale proprietario di rinunciare al controllo del contratto. * @notice La rinuncia alla proprietà lascerà il contratto senza un proprietario * Non sarà più possibile chiamare le funzioni con il modificatore `onlyOwner`. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Consente all'attuale proprietario di trasferire il controllo del contratto a un nuovo proprietario. * @param newOwner L'indirizzo a cui trasferire la proprietà. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Trasferisce il controllo del contratto a un nuovo proprietario. * @param newOwner L'indirizzo a cui trasferire la proprietà. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } }
pragma solidity ^0.4.25; 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"); } }

Chiunque può chiamare la nostra funzione attack - vogliamo assicurarci che l'utente possieda effettivamente lo zombi con cui sta attaccando. Sarebbe un problema di sicurezza se potessi attaccare con lo zombi di qualcun altro!

Riesci a pensare a come aggiungere un controllo per vedere se la persona che chiama questa funzione è il proprietario dello _zombieId che sta passando?

Pensaci e vedi se riesci a trovare la risposta da solo.

Prenditi un momento... Fai riferimento ad alcune delle nostre lezioni precedenti...

La risposta è qua sotto, non continuare fino a quando non ci hai pensato.

La Risposta

We've done this check multiple times now in previous lessons. In changeName(), changeDna(), and feedAndMultiply(), we used the following check: Abbiamo eseguito questo controllo più volte nelle lezioni precedenti. In changeName(), changeDna() e feedAndMultiply(), abbiamo usato il seguente controllo:

require(msg.sender == zombieToOwner[_zombieId]);

This is the same logic we'll need for our attack function. Since we're using the same logic multiple times, let's move this into its own modifier to clean up our code and avoid repeating ourselves. Questa è la stessa logica di cui avremo bisogno per la nostra funzione di attacco. Dato che stiamo usando la stessa logica più volte, spostiamola nel suo modifier per ripulire il nostro codice ed evitare di ripeterci.

Facciamo una prova

Torniamo a zombiefeeding.sol poiché questo è il primo posto in cui abbiamo usato quella logica. Facciamo un refactor nel suo modifier.

  1. Creare un modifier chiamato ownerOf. Ci vorrà 1 argomento: _zombieId (a uint).

Il body dovrebbe richiedere che msg.sender sia uguale a zombieToOwner[_zombieId] per continuare con la funzione. Puoi fare riferimento a zombiehelper.sol se non ricordi la sintassi di un modifier.

  1. Modificare la definizione della funzione di feedAndMultiply in modo che utilizzi il modificatore ownerOf.

  2. Ora che stiamo usando un modifier, puoi rimuovere la riga request(msg.sender == zombieToOwner[_zombieId]);