[Blockchain] 크립토좀비 lesson1 (Solidity)

공효은·2022년 2월 24일
0

Blockchain

목록 보기
6/10

크립토 좀비


https://cryptozombies.io/ko/

lesson1에서는 좀비의 dna를 랜덤으로 생성해서 좀비를 만든다!

상태 변수 & 정수

상태변수

컨트랙트 저장소에 영구적으로 저장된다. 즉, 이더리움 블록체인에 기록된다. 데이터베이스에 데이터를 쓰는 것과 동일하다.

uint: uint 자료형은 부호 없는 정수로, 값이 음수가 아니어야한다는 의미. 부호있는 정수를 위한 int도 있다.

uint256 - 256비트 부호 없는 정수
uint8, uint16, uint32 더 적은 비트로 선언 가능

string: 임의의 길이를 가진 UTF-8 데이터를 위해 활용

구조체

struct Person {
	uint age;
    string name;
}

배열

정적 배열

배열: 정적 배열과, 동적배열이 있음

두개의 원소를 담을 수 있는 고정 길이의 배열

uint[2] fixedArray; 

5개의 스트링을 담을 수 있는 고정 길이의 배열

string[5] stringArray;

동적 배열

동적 배열은 고정된 크기가 없으며 계속 크기가 커질 수 있다.

uint[] dynamicArray;

구조체 배열

Person[] people;

Public 배열

public 으로 배열을 선언할 수 있다. 솔리디티는 이런 배열을 위해 getter 메소드를 자동적으로 생성한다.

Persion[] public people;

그러면 다른 컨트랙트들이 이 배열을 읽을 수 있게 된다.(쓸수는 없다) 이는 컨트랙트에 공개 데이터를 저장할 때 유용한 패턴이다.

함수선언

솔리디티에서 함수 선언은 다음과 같이 한다.

function eatHamburgers(string _name, uint _amount){
}

이 함수는 eatHamburgers라는 함수로 string과 uint 2개의 인자를 전달받고 있다.

함수 인자명을 언더스코어(_)로 시작해서 전역 변수와 구별하는 것이 관례이다.

이 함수를 다음과 같이 호출 할 수 있다.

eatHamburgers("vitalik", 100);

구조체와 배열 활용하기

struct Persion {
  uint age;
  string name;
 }
 
 Persion[] public people;

새로운 Person를 생성하고 people 배열에 추가한다.

Persion satoshi = Person(172, "Satoshi");
people.push(satoshi)
//깔끔하게 한 줄로 표현
people.push(Persion(172, "Satoshi"))

Private/Public 함수

솔리디티에서는 함수는 기본적으로 public으로 선언된다. 즉, 누구나(혹은 다른 컨트랙트가) 내 컨트랙트의 함수를 호출하고 코드를 실행할 수 있다.

이는 컨트랙트를 공격에 취약게 만들 수 있다. 따라서 기본적으로 함수를 private로 선언하고, 공개할 함수만 public으로 선언하는 것이 좋다.

private 함수를 선언하는 방법

uint[] numbers;

function _addToArray(uint _number) private {
  numbers.push(_number);
}

private 는 컨트랙트 내의 다른 함수들만이 이 함수를 호출하여 numbers 배열로 무언가를 추가할 수 있다.

private 함수명도 언더바(_)로 시작하는 것이 관례다.

함수 더 알아보기

함수 반환값과 함수 제어자

반환값

함수에서 어떤 값을 반환 받으려면 다음과 같이 선언해야한다.

string greeting = "What's up dog";

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

솔리디티에서 함수 선언은 반환값 종류를 포함한다. (이 경우에는 string)

함수 제어자

위에서 살펴 본 함수 sayHello()는 솔리디티에서 상태를 변화시키지 않는다. 즉, 어떤 값을 변경하거나 무언가를 쓰지 않는다.
이 경우에는 함수를 view로 선언한다. 이는 함수가 데이터를 보기만 하고 변경하지 않는다는 뜻이다.

function sayHello() public view returns (string) {

솔리디티는 pure 함수도 갖고 있는데, 이는 함수가 앱에서 어떤 데이터도 접근하지 않는 것을 의미한다.

function _multiply(uint a, uint b) private pure returns (uint){
  return a * b;
}

이 함수는 앱에서 읽는 것도 하지 않고, 다만 반환값이 함수에 전달된 인자값에 따라서 달라진다. 이 경우에 함수를 pure 로 선언한다.

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에 저장하려고 하니 잠재적으로 문제를 야기할 수 있다. 반환값을 uint8으로 형 변환 하면 코드가 제대로 작동하고 컴파일에러도 나지 않는다.

이벤트

컨트랙트가 블록체인 상에서 앱의 사용자 단에서 무언가 액션이 발생했을때 의사 소통하는 방법이다. 컨트랙트는 특정 이벤트가 일어나는지 "귀를 기울이고" 그 이벤트가 발생하면 행동을 취한다.

예시

event IntegersAdded(uint x, uint y, uint result);

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

앱의 사용자 단은 해당 이벤트가 일어나는 지 귀를 기울인다. 자바스크립트로 이를 구현하면 다음과 같다.

MyContract.IntegersAdded(function(error,result){
  //결과와 관련된 행동을 취한다.
})

크립토 좀비 lesson1 에서 만든 컨트랙트!

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 {
        zombies.push(Zombie(_name, _dna));
        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

컨트랙트와 상호작용하는 사용자 다느이 자바스크립트 코드를 작성해야한다.

이더리움은 Web3.js 라고 하는 자바스크립트 라이브러리를 가지고 있다.

Web3.js 가 구축된 컨트랙트와 어떤 방식으로 상호작용하는지에 대한 샘플 코드를 살펴보자

var api 
var ZombieFactoryContract = web3.eth.contract(abi)
var contractAddress= 
var ZombileFactory = ZombieFactoryContract.at(contractAddress)
// ZombileFactory 는 우리 컨트랙트의 public 함수와 벤트에 접근할 수 있다.
 
//일종의 이벤트 리스너가 텍스트 입력값을 취한다.
$("outButton").click(function(e){
  var name = ${"#nameInput").val()
  ZombieFactory.createRandomZombie(name)
 })


// NewZombile 이벤트가 발생하면 사용자 인터페이스를 업데이트 한다.


var event = ZombieFactory.NewZombie(function(error, result){
  if(error) return
  generateZombie(result.zombieId, result.name, result.dna)
})


//좀비 DNA 값으 받아서 이미지를 업데이트 한다.
function generageZombie(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.pnt"에서 "head7,png" 중 하나의 이미지를 불러온다
   
    headChoice: dnaSr.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"를 이용하여 아래와 같이 업데이트된다:
    skingColorChoice: 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
}
                               

내일도 열심히 !!

profile
잼나게 코딩하면서 살고 싶어요 ^O^/

0개의 댓글