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;
}
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]++;
NewZombie(id, _name, _dna);
}
function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(_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);
}
}
일단 추가 기능을 위한 변수를 선언하였다.
level
, readyTime
또한 cooldownTime
이라는 지역변수를 선언하여 날짜를 부여 하였다.
이러한 추가된 변수는 좀비가 공격을 할수 있는 시간을 의미한다.
기본적으로 uint256이나 uint32이나 메모리는 똑같다.
하지만 struct안에 적어주는 값은 다르다.
솔리디티에도 시간이 있다
now같은 경우에는 1970년 1월 1일부터 지금까지의 초를 반환한다.
참고로 솔리디티 시간 언어에는 seconds, minutes, hours, days, weeks, year
등이 있고 모두 초로 계산이 된다.
또한 기본적으로 now는 uint256을 반환하기 떄문에 구조체에 맞춰서 uint32로 설정해 주는 것이다.
now + cooldownTime을 적용함으로써 하루가 지나면 공격이 가능하게 구현을 해놓은 것이다.
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function Ownable() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
권한을 부여하기 위한 컨트랙트이다.
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;
function setKittyContractAddress(address _address) external onlyOwner {
kittyContract = KittyInterface(_address);
}
function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
_targetDna = _targetDna % dnaModulus;
uint newDna = (myZombie.dna + _targetDna) / 2;
if (keccak256(_species) == keccak256("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");
}
}
일단 좀비에게 먹이로줄 데이터를 따로 받아서 사용을 하였다.
이런 경우에는 만약 데이터의 주소값이 변경되거나, 오류가 발생을 하게 되면 사용이 불가능해지는 단점이 있다.
그러기 떄문에 일단 setKittyContractAddress
함수를 통해서 값을 받아오는 것이다.
이떄 onlyOwner
을 사용을 하였는데 그 이유는 다른 악의적인 사용자가 이상한 데이터를 줄 수도 있기 떄문이다.
external
을 추가해 주는 것이다.pragma solidity ^0.4.19;
import "./zombiefeeding.sol";
contract ZombieHelper is ZombieFeeding {
modifier aboveLevel(uint _level, uint _zombieId) {
require(zombies[_zombieId].level >= _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(let i=0; i<zombies.length; i++){
if(zombieToOwner[i] == _owner){
result[counter] = i;
counter++
}
}
return result;
}
}
오늘은 거의 이곳을 작업하면서 학습을 하였다.
이 곳에서는 좀비의 능력을 작성하는 코드이다.
기본적으로 특정 레벨에 도달하였을떄만 함수를 작동할 것이기 때문에 조건을 걸어준다.
aboveLevel
특정 레벨에 도달하면 해당 함수를 실행 가능하고 반드시 사용자만 실행이 가능하다는 특징이 있다.
그후 함수의 기능에 따라 이름을 바꾸거나, 새로운 DNA를 설정 가능하다.
getZombiesByOwner
사용자가 가지고 있는 좀비를 반환하는 함수이다.
만약 전역으로 선언되어있는 배열을 활용하게 되면 메모리 소모가 심하다.
왜냐하면 배열에는 다른 사용자들의 좀비도 들어있을 곳이고 특정 좀비를 뺴오면 해당 공간은 빈공간이 되어서 모든 배열을 이동해야 한다.
그러기 떄문에 함수 내에서 따로 배열을 만드는 것이다.
이 과정은 비효율적인 과정이지만 메모리적으로는 효율적인 작업이다.
storage
연산은 메모리 적으로 매우 비싸다.
storage
연산은 블록체인 생태계에 영구적으로 기록이 되기 떄문에 생태계를 변경해야 하는 점에서 더 많은 비용이 든다.
그러기 떄문에 떄로는 비효율적이더라도 storage
보다는 호출될 떄마다 momory
에 다시 만드는 것이 좋다.
이번 글은 나도 개인적으로 집중을 제대로 하지 못해서 많이 전문성이 떨어지는 것같다...
다음 과정도 천천히 집중해서 진행을 해볼 것이며
한번 다시 작성을 해보면서 복습하는 시간을 가져봐야 겠다.