CryptoZombie 1단계

2. 컨트랙트

pragma solidity ^0.4.19;

contract ZombieFactory {
    
}

3. 상태 변수 & 정수

3-1. 상태 변수

컨트랙트 저장소에 영구적으로 저장된다. 즉, 이더리움 블록체인에 기록이 된다.

3-2. uint: 부호 없는 정수

uint 자료형은 부호 없는 정수로, 값이 음수가 아니어야 한다.
정수에 부호를 쓰려면 int 형을 쓰면 된다.

uint는 실제로 uint256 (부호 없는 256비트 정수) 의 다른 표현이다.
uint8, uint16, uint32 등과 같이 uint를 더 적은 비트로 선언할 수도 있다.

pragma solidity ^0.4.19;

contract ZombieFactory {

    uint dnaDigits = 16;

}

4. 수학 연산

4-1. 지수 연산

uint x = 5 ** 2;

4-2. 정답

좀비 DNA가 16자리 숫자가 되도록 또다른 uint 형 변수를 선언하고, 10^16값을 배정한다. 이로써 이 값을 이후 모듈로 연산자 %와 함께 사용하여 16자리보다 큰 수를 16자리 숫자로 줄일 수 있다.

pragma solidity ^0.4.19;

contract ZombieFactory {

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

}

5. 구조체

5-1. 구조체

복잡한 자료형을 필요로 할 때 사용한다.

struct Person {
	uint age;
    string name;
}

5-2. 정답

pragma solidity ^0.4.19;

contract ZombieFactory {

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

    struct Zombie{
        string name;
        uint dna;
    }

}

6. 배열(1)

배열의 종류: 정적 배열, 동적 배열

6-1. 정적 배열

uint[2] fixedArray;
2개의 정수를 담을 수 있는 고정길이의 배열
string[5] stringArray;
5개의 스트링을 담을 수 있는 고정 길이의 배열

6-2. 동적 배열

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

6-3. 구조체 배열

구조체의 배열을 생성할 수도 있다. 이전의 Person구조체를 사용하면
Person[] people;
위의 내용은 동적 배열로, 원소를 계속 추가할 수 있다.
상태 변수는 블록체인에 영구적으로 저장될 수 있듯이 구조체의 동적 배열을 생성하면 마치 DB처럼 컨트랙트에 구조화된 데이터를 저장하는데 유용하다.

6-4. public배열

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

Person[] public people

public으로 선언함으로써 다른 컨트랙트들이 이 배열을 읽을 수 있게 된다. 이는 컨트랙트에 공개 데이터를 저장할 때 유용하게 사용할 수 있다.

6-5. 정답

좀비 군대를 저장하고, 다른 앱에 좀비들을 자랑하기 위해 좀비 군대 저장소를 public으로 한다.

pragma solidity ^0.4.19;

contract ZombieFactory {

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

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

}

7. 함수 선언

7-1. 함수 선언

function eatHamburgers(string _name, uint _amount) { }
위의 내용은 eatHamburgers라는 함수를 선언하고, string과 uint 형식의 2개의 인자를 전달받고 있다.

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

위의 함수를 다음과 같이 호출할 수 있다.
eatHamburgers("vitalik",100);

7-2. 정답

좀비를 생성한다. string형의 name과 uint형의 dna라는 2개의 인자값을 받는 함수를 생성한다.

pragma solidity ^0.4.19;

contract ZombieFactory {

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

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    function createZombie(string _name, uint _dna){
        
    }

}

8. 구조체와 배열 활용하기

8-1. 새로운 구조체 생성

새로운 Person을 생성하고 people배열에 추가해보자.

  1. 새로운 Person 생성하기
    Person satoshi = Person(172, "Satoshi");

2.만든 Person을 배열에 추가한다.
people.push(satoshi);

위의 1,2의 내용을 한 줄로 표현하기
people.push(Person(172,"Satoshi"));

array.push()는 무언가를 배열의 에 추가해서 순서를 유지하도록 한다.

8-2. 정답

createZombie의 내용을 적어보자.
1. 함수에 코드를 넣어 새로운 Zombie를 생성하여 zombie에 추가하도록 한다.

pragma solidity ^0.4.19;

contract ZombieFactory {

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

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    function createZombie(string _name, uint _dna) {
        zombies.push(Zombie(_name,_dna));
    }

}

9. Private/Public 함수

9-1. Private 함수

솔리디티에서 함수는 기본적으로 public으로 선언된다. 즉, 누구나 컨트랙트의 함수를 호출하고, 코드를 실행할 수 있는 것이다. 그러나 이는 공격에 취약하도록 할 수 있다. 그래서 기본적으로 함수를 private로 선언하고 공개할 함수만 public으로 선언하는 것이 좋다.

private는 컨트랙트 내의 다른 함수들만이 이 함수를 호출할 수 있다는 것을 의미한다. private 키워드는 함수명 다음에 적는다. 함수 인자명과 마찬가지로 private 함수명도 언더바_로 시작하는 것이 관례이다.

9-1-1. private 함수 선언

uint[] numbers;

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

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

9-2. 정답

createZombie 함수를 private로 선언한다.

pragma solidity ^0.4.19;

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));
    }

}

10. 함수의 반환값 & 함수 제어자

10-1. 함수의 반환값

string greeting = "What's up dog";

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

10-2. 함수 제어자

10-2-1. view

위에서 살펴본 함수 sayHello()는 솔리디티에서 상태를 변화시키지 않는다. 어떤 값을 변경하거나 무언가를 쓰지 않는다.

view는 함수가 데이터를 보기만 하고 변경하지 않는것을 의미한다.

따라서 위의 함수는 다음과 같이 쓰여도 된다.

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

10-2-2. pure

pure은 함수가 앱에서 어떤 데이터를 접근하지 않음을 의미한다.

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

이 함수는 앱에서 읽는 것도 하지 않고, 반환값이 함수에 전달된 인자값에 따라서 달라진다.

10-3. 정답

스트링으로부터 랜덤 DNA 숫자를 생성하데 도움을 주는 함수가 필요하다.

  1. _generateRandomDna라는 private 함수를 만든다. 이 함수는 string형 _str을 인자로 전달받고 uint를 반환해야 한다.
  2. 이 함수는 컨트랙트의 변수를 보지만 변경하지는 않을 것이기에 view로 선언한다.
pragma solidity ^0.4.19;

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) {

    }

}

11. Keccak256 & 형변환

11-1. keccak256

이더리움은 SHA3의 레거시 버전인 keccak256을 내장 해시 함수로 가지고 있다. 해시 함수는 기본적으로 입력 string을 랜덤 256비트 16진수로 매핑한다.
keccak256("aaaab");
의 값은 6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5

keccak256("aaaac");
의 값은 b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
으로 조금만 변경이 되도 해시값은 크게 달라진다.

11-2. 형변환

uint8 a = 5;
uint b = 6;

다음과 같이 변수를 선언했다고 치자.

uint8 c = a * b;
다음과 같이 작성하면 a * b는 uint를 반환하기에 에러메세지가 난다.

uint8 c = a * uint8(b);
b를 uint8로 형변환해서 코드가 제대로 작동하도록 한다.

11-3. 정답

_generateRandomDna 함수 내용 채우기
1. 코드 첫 줄에는 _str을 이용항 keccak256 해시값을 받아서 16진수 난수를 생성한 다음 이를uint로 형변환한다. 그리고 rand라는 uint형 변수에 결과값을 저장한다.
2. 좀비의 DNA는 16자리 숫자여야 하므로 (dnaMoculus) 두번째 줄에서는 위의 결과값을 모듈로(%) dnaMoculus로 연산한 값을 변환한다.

pragma solidity ^0.4.19;

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; 
    }

}

12. 종합하기

이제 랜덤 좀시 생성기를 거의 다 완성해 간다. 모든 내용을 종합하는 public 함수를 생성해본다.

12-1. 정답

createZombie라는 public 함수를 생성한다.
1.이 함수는 string형 _name인자를 하나 전달받는다.
2. _name을 전달받은 _generateRandomDna 함수를 호출하고 이 함수의 반환값을 randDna라는 uint형 변수에 저장한다.
3. 두번째 줄에서는 createZombie 함수를 호출하고, _name과 rnadDna를 전달한다.

pragma solidity ^0.4.19;

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);
    }

}

13. 이벤트

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

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

function add (uint _x, uint _y) public {
	uint result = _x + _y;
    IntegersAdded(_x, _y, result);
    return result;
}

위의 코드의 내용은 먼저 add가 실행이 되면 IntergersAdded라는 이벤트를 브로드캐스팅하는 것이다. 그리고 result를 반환하는 순서로 되어 있다.

13-1. 정답

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);
    }
}

14. 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
}

15. cryptoZombie완성!

https://share.cryptozombies.io/ko/lesson/1/share/nara7875

좋은 웹페이지 즐겨찾기