블록체인의 스마트 컨트랙트 공부를 위해 크립토좀비를 시작했다.(22.07.07)
챕터 1부터 아주 쉽고 간편하게 가이드하며 알려준다. 프로그래밍 언어나 문법을 전혀 모른다면 살짝은 이해가 안되는 부분들이 있을 수 있겠다라고 생각했다.
크립토 좀비는 좀비라는 소재로 이더리움과 솔리디티에 대해 알려준다. 첫번째 레슨에서는 좀비 군대를 위한 좀비 공장 기능을 만드는 단계이다.
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
}
function _createZombie(string _name, uint _dna) private {
zombies.push(Zombie(_name, _dna));
// 여기서 이벤트 실행
}
function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(_str));
return rand % dnaModulus;
}
function createRandomZombie(string _name) public {
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
솔리디티에서 함수의 파라미터에는 _
를 사용한다고 한다. 이는 전역 변수와 구별하기 위함이고 의무는 아니지만 관례라고 한다. 또한 public
과 private
로 설정할 수 있는데 private
로 함수를 설정할 시에는 함수명에도 _
를 사용해야 한다.
event NewZombie(uint zombieId, string name, uint dna); //이벤트 선언
function _createZombie(string _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna)) - 1;
NewZombie(id, _name, _dna); //이벤트 실행
}
레슨 2에서는 좀비에게 먹이를 주는 것과 먹이를 먹어서 좀비가 변하는 기능을 구현한다.
mapping (uint => address) public zombieToOwner;
mapping (address => uint) ownerZombieCount;
주소는 특정 계정을 가리키는 교유 식별자이며 매핑은 구조화된 데이터를 저장하는 한가지 방법이다. 매핑은 key-value 구조로 이루어져 있다.
function _createZombie(string _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna)) - 1;
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender]++;
NewZombie(id, _name, _dna);
}
새로운 좀비의 id에 대해 msg.sender가 저장되도록 하고 msg.sender에 대해 좀비 카운트를 증가 시킨다.
이를 통해 함수 실행 전 여러 조건을 확인하는 기능에 유용하다.
function createRandomZombie(string _name) public {
require(ownerZombieCount[msg.sender] == 0);
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
좀비를 무제한 생성하지 못하도록 제한하기 위해 require로 좀비 카운트를 비교한다.
import "./zombiefactory.sol";
contract ZombieFeeding is ZombieFactory {
}
같은 파일안에서도 상속할 수 있지만 파일을 나누어 상속받는 것도 코드가 깔끔해지고 관리가 용이해진다. 파일을 나눌 때에는 import로 상속받을 파일을 불러와야 한다.
internal은 함수가 정의된 컨트랙트를 상속하는 컨트랙트에서도 접근이 가능하고, external은 함수가 컨트랙트 바깥에서만 호출될 수 있고 컨트랙트 내의 다른 함수에 의해 호출될 수 없도록 한다. 본 프로그램에서는 internal을 적용하여야 한다.
function _createZombie(string _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna)) - 1;
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender]++;
NewZombie(id, _name, _dna);
}
인터페이스로 상호작용 하고자 하는 컨트랙트와 추상메서드를 정의해야 한다.
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
);
}
솔리디티는 자바나 파이썬과는 다르게 리턴값을 여러개줄 수 있다.
function multipleReturns() internal returns(uint a, uint b, uint c) {
return (1, 2, 3);
}
function processMultipleReturns() external {
uint a;
uint b;
uint c;
// 다음과 같이 다수 값을 할당한다:
(a, b, c) = multipleReturns();
}
// 혹은 단 하나의 값에만 관심이 있을 경우:
function getLastReturnValue() external {
uint c;
// 다른 필드는 빈칸으로 놓기만 하면 된다:
(,,c) = multipleReturns();
}
리턴 값이 여러개일 때에는 위와 같이 사용할 수 있다.