Cryptozombies_Lesson2. 좀비가 희생물을 공격하다
Lesson2 목표
좀비를 랜덤으로만 생성하지 않고, 좀비가 다른 생명체를 "먹도록"해서 새로운 좀비를 생성해보자!
새로운 좀비의 DNA는 이전 좀비의 DNA와 먹이의 DNA를 활용하여 계산될 것.
매핑과 주소
주소
- 이더리움 블록체인은 은행 계좌와 같은 계정들로 이루어져 있다.
- 계정은 이더리움 블록체인 상의 통화인 "_이더_"의 잔액을 가진다.
- 계정을 통해 다른 계정과 이더를 주고 받을 수 있다.
- 각 계정은 은행 계좌번호와 같은 "주소"를 가지고 있다.
- 주소는 특정 계정을 가리키는 "고유 식별자"이다.
- 주소는 "특정 유저(또는 스마트 컨트랙트)"가 소유한다.
- 여기서 주소는 좀비들에 대한 소유권을 나타내는 고유ID로 활용할 수 있다. 즉, 유저가 새로운 좀비를 생성하면, 좀비를 생성하는 함수를 호출한 이더리움 주소에 그 좀비에 대한 소유권을 부여한다.
매핑
- 솔리디티에서 구조화된 데이터를 저장하는 방법이다.
- 기본적으로 키-값저장소로, 데이터를 저장하고 검색하는 데 이용된다.
- 예1) mapping (address => uint) public accountBalance; ----> 키 : address, 값 : uint // 계좌 잔액을 보유하는 uint를 저장
- 예2) mapping (uiunt => string) userIdToName; //userID로 유저 이름을 저장
*실습*
- 좀비 소유자를 추적하는 매핑 만들기
Msg.sender
전역 변수
- Msg.sender는 현재 함수를 호출한 사람(또는 스마트컨트랙트)의 "주소"를 가리킨다.
- 참고로, 솔리디티에서 함수 실행은 항상 외부 호출자가 실행한다. 따라서 컨트랙트는 누군가 컨트랙트의 함수를 호출할 때까지 블록체인에서 아무 것도 안 하고 있어야 한다. 그래서 항상 msg.sender가 있어야 한다.
*실습*
1) 새로운 좀비의 id가 반환된 후, zombieToOwner 매핑을 업데이트해서 id에 대하여 msg.sender가 저장되도록 만들기.
2) 저장된 msg.sender를 고려하여 ownerZombieCount를 증가시키기
Require
- require함수를 활용하면, 특정 조건이 참이 아닐 때 함수가 에러 메시지를 발생하고 실행을 멈춘다.
- require 함수를 실행하기 전에, 참이어야 하는 특정 조건을 확인하는 데 유용하다.
- cryptozombie에서는 require 함수를 활용해서 각 플레이어가 한 번만 createRandomZombie 함수를 호출하도록 만들 것이다. 즉, 새로운 플레이어들이 게임을 처음 시작할 때 좀비 군대를 위한 첫 좀비를 생성하기 위해 createRandomZombie 함수를 호출하게 된다.
*실습*
- require를 활용해 유저들이 첫 좀비를 만들 때 이 함수가 유저 당 한 번만 호출되도록 해보기.
상속
- 여러 컨트랙트에 코드 로직을 관리하기 쉽게 나눌 수 있다.
- 상속을 받는 컨트랙트는 상위 컨트랙트의 함수에 접근 가능하다.
- 그러나 상위 컨트랙트에서 어떤 함수를 private로 설정하면, 해당 컨트랙트를 상속하는 어떤 컨트랙트도 해당 함수에 접근할 수 없다.
*실습*
- ZombieFeeding 컨트랙트는 ZombieFactory 컨트랙트를 상속받음.
Import
- 다수의 파일이 있고, 어떤 파일을 다른 파일로 불러오고 싶을 때 import라는 키워드를 사용한다.
*실습*
- zombiefeeding.sol에 zombiefactory.sol 불러오기
Storage vs Memory
- 솔리디티에서는 변수를 저장하는 공간으로 storage와 memory가 있다.
- storage는 블록체인 상에 영구적으로 저장되는 변수이다.
- memory는 임시적으로 저장되는 변수로, 컨트랙트 함수에 대한 외부 호출들이 일어나는 사이에 지워진다.
- 하지만 솔리디티가 알아서 처리해주기 때문에, 이런 키워드를 우리가 이용할 필요는 없다.
- 상태 변수(함수 외부에 선언된 변수)는 초기 설정 상, storage로 선언된다.
- 함수 내에 선언된 변수는 memory에 자동선언되어 함수 호출이 종료되면 사라진다.
- 그러나, 함수 내의 "구조체"와 "배열"을 처리할 때 해당 키워드를 사용해야 한다.
*실습*
먹이를 먹고 번식하는 능력을 좀비들에게 부여하려고 한다.
좀비가 어떤 다른 생명체를 잡아 먹을 떄, 좀비 DNA가 생명체의 DNA와 혼합되어 새로운 좀비가 생성될 것이다.
그 함수를 feedAndMultiply라고 정의한다.
1) 주인만이 좀비에게 먹이를 줄 수 있도록 한다. 따라서 require문을 추가하여 msg.sender가 좀비 주인과 동일하도록 한다.
2) 먹이를 먹는 좀비 DNA를 얻어야 한다.
좀비 DNA
새로운 좀비의 DNA를 계산하는 공식은 다음과 같다.
먹이를 먹는 좀비의 DNA와 먹이의 DNA 평균을 계산한다.
*실습*
1) targetDna가 16자리보다 크지 않도록 설정한다.
2) 새로운 좀비의 DNA를 계산한다.
3) 새로운 DNA 값을 얻게 되면, _createZombie 함수를 호출한다.
함수 접근 제어자 더 알아보기 (Internal과 External)
- 함수를 internal로 설정할 경우, 해당 컨트랙트를 상속하는 컨트랙트에서도 접근이 가능하다. 이 점을 제외하곤 private와 동일하다. 참고로 private는 해당 컨트랙트를 상속하는 컨트랙트에서 접근이 불가능했다.
- external은 함수가 컨트랙트 밖에서만 호출될 수 있고, 컨트랙트 내의 다른 함수에 호출될 수 없다. 이 점을 제외하곤 public과 동일하다. 참고로 public은 컨트랙트 밖이든 외부든 모두 접근 가능하다.
interface 정의 : 다른 컨트랙트와 상호작용하기 (크립토 키티 )
- 블록체인 상에 있으면서, 우리가 소유하지 않은 컨트랙트와 우리 컨트랙트가 상호작용을 하려면, 인터페이스를 정의해야 한다.
- 인터페이스를 정의하는 것은, 컨트랙트를 정의하는 것과 유사하다.
- 다른 컨트랙트와 상호작용하고자 하는 함수만을 선언할 뿐, 다른 함수나 상태 변수를 언급하진 않는다.
- 함수 몸체를 정의하지 않는다. 즉, 중괄호를 쓰지 않고 함수 선언을 세미콜론으로 끝낸다.
- 예) contract NumberInterface { function getNum(address _myAddress) public view returns (uint); }
- 따라서 인터페이스는 컨트랙트 뼈대처럼 보인다고 할 수 있다.
*실습*
좀비의 먹이는 크립토키티이다.
좀비에게 크립토 키티를 먹이로 주려면, 크립토키티 스마트 컨트랙트에서 키티의 dna를 읽어와야 한다.
KittyInterface라는 인터페이스를 정의하여, 인터페이스 내에 getKitty 함수를 선언한다.
인터페이스 활용하기
*실습*
정의된 KittyInterface를 활용해보기.
ckAddress는 크립토키티의 컨트랙트 주소이다.
1) KittyContract라는 이름으로 KittyInterface를 생성한다. 그리고 ckAddress를 이용하여 초기화한다.