[CrytoZombie] basic - 좀비 공장 만들기

해당 글은 CrytoZombie를 공부하면서 정리한 내용입니다.

Ch1. 개요

"좀비 공장"을 만듬

  • 우리 공장은 군대 내 모든 좀비의 데이터베이스를 유지
  • 우리 공장은 새로운 좀비를 생성하는 함수를 가짐
  • 각 좀비는 랜덤하고 독특한 외모를 가짐

기본적인 기능을 가진 좀비 생성

좀비 DNA가 활용되는 방법

  • 좀비 DNA에 따라 외모가 달라짐
  • 좀비 DNA는 16자리 정수
  • 이 숫자의 각 부분은 좀비가 가진 개별 특성과 매핑된다.

Ch2. 컨트렉트

  • 솔리디티 코드는 컨트랙트 안에 싸여 있다.
  • 컨트랙트
    • 이더리움 애플리케이션의 기본적인 구성 요소
    • 모든 변수와 함수는 어느 한 컨트랙트에 속함
    • 모든 프로젝트의 시작 지점

Version Pragma

  • 모든 솔리디티 소스 코드는 "version pragma"로 시작
  • Version Pragma : 해당 코드가 이용해야 하는 솔리디티 버전을 선언하는 것
  • 새로운 컴파일러 버전이 나와도 기존 코드가 깨지지 않도록 예방
pragma solidity ^0.4.19;

contract HelloWorld {

}

Ch3. 상태 변수 & 정수

  • 상태 변수
    • 컨트랙트 저장소에 영구적으로 저장되어 이더리움 블록체인에 기록된다
    • 데이터베이스에 데이터를 쓰는 것과 동일하다.
contract Example {
  // 이 변수는 블록체인에 영구적으로 저장된다
  uint myUnsignedInteger = 100;
}

부호 없는 정수: uint

  • uint 자료형
    • 부호 없는 정수
    • 값이 음수가 아니어야 한다는 의미
  • int 자료형 : 부호 있는 정수
  • 참고: 솔리디티에서 uint는 실제로 uint256, 즉 256비트 부호 없는 정수의 다른 표현이다. uint8, uint16, uint32 등과 같이 uint를 더 적은 비트로 선언할 수도 있다.

Ch4. 수학 연산

대부분의 프로그래밍 언어의 수학 연산과 동일

  • 덧셈: x + y
  • 뺄셈: x - y,
  • 곱셈: x * y
  • 나눗셈: x / y
  • 모듈로 / 나머지: x % y (이를테면, 13 % 5는 3이다. 왜냐면 13을 5로 나누면 나머지가 3이기 때문이다)

솔리디티는 지수 연산도 지원한다.
ex) "x의 y승"= x^y
uint x = 5 ** 2; // 즉, 5^2 = 25


Ch5. 구조체

  • 구조체: 여러 특성을 가진, 보다 복잡한 자료형을 생성
struct Person {
  uint age;
  string name;
}

Ch6. 배열

  • 솔리디티에는 정적배열, 동적배열 2가지가 있다.
  • 상태 변수가 블록체인에 영구적으로 저장할 수 있어, 구조체의 동적 배열을 생성하면 마치 데이터베이스처럼 컨트랙트에 구조화된 데이터를 저장하는데 유용하다.
/* 정적 배열*/
// 2개의 원소를 담을 수 있는 고정 길이의 배열:
uint[2] fixedArray;
// 또다른 고정 배열으로 5개의 스트링을 담을 수 있다:
string[5] stringArray;

/*동적 배열*/
// 동적 배열은 고정된 크기가 없으며 계속 크기가 커질 수 있다:
uint[] dynamicArray;
// 구조체를 배열로 
Person[] people; // 이는 동적 배열로, 원소를 계속 추가할 수 있다.

Public 배열

  • public으로 배열을 선언
  • 솔리디티는 이런 배열을 위해 getter 메소드를 자동적으로 생성
Person[] public people;

Ch7. 함수 선언

  • function eatHamburgers(string _name, uint _amount) {} : 이 함수는 eatHamburgers라는 함수로, string과 uint 2개의 인자를 전달받는다.
  • 함수 인자명을 언더스코어(_)로 시작해서 전역 변수와 구별하는 것이 관례이지 의무는 아니다.

Ch8. 구조체와 배열 활용

struct Person {
  uint age;
  string name;
}

Person[] public people;

// 새로운 사람 생성
Person ella = Person(11,'ella')
// 이 사람을 배열에 추가
people.push(ella);

// 한줄로 표현
people.push(Person(100,'juily'))

Ch9. Private / Public 함수

  • 솔리디티에서 함수는 기본적으로 public으로 선언된다.
  • public Function : 누구나 (혹은 다른 어느 컨트랙트가) 자네 컨트랙트의 함수를 호출하고 코드를 실행할 수 있다
  • private Function : 컨트랙트 내의 다른 함수들만이 이 함수를 호출하고 실행 할 수 있다.
  • 보안에 취약하므로 기본적으로 함수를 private으로 선언하고, 공개할 함수만 public으로 선언하는 것을 추천한다.
uint[] numbers;

function _addToArray(uint _number) private {
  numbers.push(_number);
}
  • private 키워드는 함수명 다음에 쓴다.
  • 함수 인자명과 마찬가지로 private 함수명도 언더바(_)로 시작하는 것이 관례이다.

10. 함수 심화

반환값

  • 솔리디티에서 함수 선언은 반환값 종류를 포함
// 반환값 string
string greeting = "What's up dog";

function sayHello() public returns (string) {
  return greeting;
}

함수 제어자

  • view 함수 : 함수가 데이터를 보기만하고 변경하지 않는다.
  • pure 함수 : 함수가 앱에서 어떤 데이터도 접근하지 않는 것을 의미한다.
  • 솔리디티 컴파일러는 어떤 제어자를 경고메세지로 알려준다.
// view 함수 
function sayHello() public view returns (string){}

// pure 함수 : 이 함수는 읽는 것도 하지 않고, 다만 반환값이 함수에 전달된 인자값에 따라서 달라진다.
function _multiply(uint a, uint b) 	private pure returns (uint) {
  return a * b;
}

11. Keccak256과 형 변환

Keccak256

  • 의사 난수 발생기로 Keccak256를 이용
  • 이더리움은 SHA3의 한 버전인 keccak256를 내장 해시 함수로 가지고 있다.
  • 해시 함수 : 기본적으로 입력 스트링을 랜덤 256비트 16진수로 매핑한다.
  • 입력값이 약간의 변화하면 해시 값은 달라진다.
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");
  • 블록체인에서 안전한 의사 난수 발생기는 매우 어려운 문제다. 여기서 활용한 방법은 안전하지는 않다.

형변환

uint8 a = 5;
uint b = 6;
// a * b가 uint8이 아닌 uint를 반환하기 때문에 에러 메시지가 난다:
uint8 c = a * b; 
// b를 uint8으로 형 변환해서 코드가 제대로 작동하도록 해야 한다:
uint8 c = a * uint8(b); 
  • a * b는 uint를 반환하는데 우리는 이 반환값을 uint8에 저장하려고 하니 잠재적으로 문제를 야기할 수 있다.
  • b의 반환값을 uint8으로 형 변환하면 코드가 제대로 작동하고 컴파일러도 에러 메시지를 주지 않는다.

12. 이벤트

  • 이벤트 : 컨트랙트가 블록체인 상에서 앱의 사용자 단에서 무언가 액션이 발생했을 때 의사소통하는 방법
  • 컨트랙트는 특정 이벤트가 일어나는지 "귀를 기울이고" 그 이벤트가 발생하면 행동한다.
  • 앱의 사용자 단은 해당 이벤트가 일어나는지 귀를 기울인다.
// 이벤트를 선언한다
event IntegersAdded(uint x, uint y, uint result);

function add(uint _x, uint _y) public {
  uint result = _x + _y;
  // 이벤트를 실행하여 앱에게 add 함수가 실행되었음을 알린다:
  IntegersAdded(_x, _y, result);
  return result;
}

// js로 변환
YourContract.IntegersAdded(function(error, result) {
  // 결과와 관련된 행동을 취한다
})


Smart Contract

pragma solidity ^0.4.19;

contract ZombieFactory {
	// 이벤트
    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        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 {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}

Web3.js

// 여기에 우리가 만든 컨트랙트에 접근하는 방법을 제시한다:
var abi = /* abi generated by the compiler */
var ZombieFactoryContract = web3.eth.contract(abi)
var contractAddress = /* our contract address on Ethereum after deploying */
var ZombieFactory = ZombieFactoryContract.at(contractAddress)
// `ZombieFactory`는 우리 컨트랙트의 public 함수와 이벤트에 접근할 수 있다.

// 일종의 이벤트 리스너가 텍스트 입력값을 취한다:
$("#ourButton").click(function(e) {
  var name = $("#nameInput").val()
  // 우리 컨트랙트의 `createRandomZombie`함수를 호출한다:
  ZombieFactory.createRandomZombie(name)
})

// `NewZombie` 이벤트가 발생하면 사용자 인터페이스를 업데이트한다
var event = ZombieFactory.NewZombie(function(error, result) {
  if (error) return
  generateZombie(result.zombieId, result.name, result.dna)
})

// 좀비 DNA 값을 받아서 이미지를 업데이트한다
function generateZombie(id, name, dna) {
  let dnaStr = String(dna)
  // DNA 값이 16자리 수보다 작은 경우 앞 자리를 0으로 채운다
  while (dnaStr.length < 16)
    dnaStr = "0" + dnaStr

  let zombieDetails = {
    // 첫 2자리는 머리의 타입을 결정한다. 머리 타입에는 7가지가 있다. 그래서 모듈로(%) 7 연산을 하여
    // 0에서 6 중 하나의 값을 얻고 여기에 1을 더해서 1에서 7까지의 숫자를 만든다. 
    // 이를 기초로 "head1.png"에서 "head7.png" 중 하나의 이미지를 불러온다:
    headChoice: dnaStr.substring(0, 2) % 7 + 1,
    // 두번째 2자리는 눈 모양을 결정한다. 눈 모양에는 11가지가 있다:
    eyeChoice: dnaStr.substring(2, 4) % 11 + 1,
    // 셔츠 타입에는 6가지가 있다:
    shirtChoice: dnaStr.substring(4, 6) % 6 + 1,
    // 마지막 6자리는 색깔을 결정하며, 360도(degree)까지 지원하는 CSS의 "filter: hue-rotate"를 이용하여 아래와 같이 업데이트된다:
    skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360),
    eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360),
    clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360),
    zombieName: name,
    zombieDescription: "A Level 1 CryptoZombie",
  }
  return zombieDetails
}

좋은 웹페이지 즐겨찾기