블록체인2017.07.07 17:49


토큰 기능 개선


암호화토큰은 코드를 만지지도 않고 바로 만들어 배포할 수 있다. 하지만 진짜 마술은 커스터마이징하면서 발생한다. 이번 장에서는 토큰에 추가 할 수있는 함수에 대한 제안을 제공하여 사용자의 필요에 더 적합하게 만든다.


중앙화된 관리자


dapp이 기본적으로 분산되어 있지만 원한다면 중앙화된 관리자를 가질수 없다는것을 의미하지는 않는다. 중앙에서 어떤 제약조건을 강제하고 싶을 수 있다. 이런 기능을 추가할 수 있으며 시작시에 추가할 수 있다 이는 모든 토큰 소유자는 게임을 소유하기로 결정하기전에 게임의 규칙을 정확하게 알 수 있다.


그러려면 화폐에 대한 중안컨트롤러가 필요하다. 이는 단순한 계정이 될수 있지만 또 한 컨트랙트로 가능하며 토큰을 생성하는데 이 컨트랙트에 따라서 결정이 되게 된다. 투표권을 부여할 수 있는 민주적단체이거나 토큰 주인에게 제한적인 능력만을 부여 할 수 있다.



contract owned {

    address public owner;


    function owned() {

        owner = msg.sender;

    }


    modifier onlyOwner {

        if (msg.sender != owner) throw;

        _;

    }


    function transferOwnership(address newOwner) onlyOwner {

        owner = newOwner;

    }

}


이것은 소유할 수 있는 계약에 대한 아주 기본적인 코드이가. 다음 단계로 컨트렉트에 추가한다.


contract MyToken is owned {

    /* the rest of the contract as usual */


이는 MyToken의 내부 어디에서든 owner와 modifier onlyOwner에 접근이 가능하다는 것을 의미한다. transferOwnership이라는 함수를 가지고 있다. 이 정보가 아주 중요하기 때문에 컨트랙트 시작시 셋업할 수 있다.


function MyToken(

    uint256 initialSupply,

    string tokenName,

    uint8 decimalUnits,

    string tokenSymbol,

    address centralMinter

    ) {

    if(centralMinter != 0 ) owner = centralMinter;


CENTERAL MINT


회전되고 있는 동전의 양이 바뀌길 원한다고 가정하자. 이것은 실제로 금괴인증서나 정부통화같은 블록체인이 아닌 자산(off blockchain asset)를 나타내며 가상 재고가 실제 재고를 반영하도록 하려는 경우이다.이는 통화보유자가 토큰 가격을 통제하기를 원하고 회전되고 있는 통화를 더 발행하거나 제거하려는 경우도 해당된다.


먼저 해야할것은 totalSuplly를 추가하고 초기화하는것이다.


contract MyToken {

    uint256 public totalSupply;


    function MyToken(...) {

        totalSupply = initialSupply;

        ...

    }

    ...

}


이제 새함수를 추가하여 소유자가 새로운 토큰을 만들수 있게 한다.


function mintToken(address target, uint256 mintedAmount) onlyOwner {

    balanceOf[target] += mintedAmount;

    totalSupply += mintedAmount;

    Transfer(0, owner, mintedAmount);

    Transfer(owner, target, mintedAmount);

}


함수명 뒤에 onlyOwner를 주의깊게 봐야한다. 이는 앞서 정의한 mofifier onlyOwner에서 상속되어 컴파일되어 재작성될것이라는것을 의미한다. 이 함수의 코드는 mofifier함수의 밑줄 부분에 삽입될것이다. 이는 이 함수가 해당 계정에 의해서만 호출되고 owner로 셋팅될것을 의미한다. 이 코드만 추가하면 더 많은 코인을 생성할 수 있다.


자산의 동결


토큰의 사용유형에 따라 사용할 수 있는 사람과 할수 없는 사람에 대한 규칙적인 제한이 필요할 수 있다. 그러려면 자산을 동결하거나 해재하는 파라메터를 컨트랙트에 추가해야 한다.


아래 변수와 함수를 아무데나 컨트랙트내에 추가한다. 아무데나 가능하지만 다른 이벤트나 매핑과 함께 배치하는게 좋다.


mapping (address => bool) public frozenAccount;

event FrozenFunds(address target, bool frozen);


function freezeAccount(address target, bool freeze) onlyOwner {

    frozenAccount[target] = freeze;

    FrozenFunds(target, freeze);

}


이 코드로 기본적으로 미동결상태인 모든 계좌를 Freeze Account를 호출함으로써 동결할 수 있다. 불행히도 동결은 아무런 효과가 없다. 왜냐하면 transfer함수에 아무것도 추가하지 않았기 때문에, 그래서 다음과 같이 해야 한다.


function transfer(address _to, uint256 _value) {

    if (frozenAccount[msg.sender]) throw;


이제 동결된 계좌는 자금은 있지만 송금은 할  수 없다. 디폴트는 미동결계좌이나 원한다면 화이트리스트를 관리하면서 원할 때 계좌를 동결할 수 있다. frozenAccount를 approvedAccount로 변경하여 적용이 가능하다.


if (!approvedAccount[msg.sender]) throw;


자동 구매 및 판매


지금까지 토큰을 평가하기 위해 유틸리티와 신뢰에 의존했다. 하지만 원한다면 자동으로 시장의 가치를 기준으로 구입하는 펀드를 만들어 토큰의 가치를 이더(또는 다른 토큰)로 뒷받침 할 수 있다.


먼저 구매와 판매가격을 셋팅한다.


uint256 public sellPrice;

uint256 public buyPrice;


function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {

    sellPrice = newSellPrice;

    buyPrice = newBuyPrice;

}


이것은 가격에 접근가능하게 하며 자주 변하지 않는다. 이를 변경하려면 트랜잭션을 싱행하면서 이더를 소비하게 되기 때문이다. 변동가격을 일정하게 유지하려면 표준데이터피드를 참고하라.


다음은 구매와 판매함수를 작성한다.


// 살때

// msg.sender => 구매자

function buy() payable returns (uint amount){

    amount = msg.value / buyPrice;                     // msg.value는 구매금액(eth), 구매금액을 토큰가격으로 나눠서 토큰개수 계산

    if (balanceOf[this] < amount) throw;               // 현재 토큰잔액 확인

    balanceOf[msg.sender] += amount;                   // 구매자 토큰개수 추가

    balanceOf[this] -= amount;                         // 판매자 토큰개수 차감

    Transfer(this, msg.sender, amount);                // 전송 수행

    return amount;                                     // 토큰개수 반환

}


// 팔때

// msg.sender => 판매자

function sell(uint amount) returns (uint revenue){

    if (balanceOf[msg.sender] < amount ) throw;   // 판매자 토큰 잔액 확인

    balanceOf[this] += amount;                              // 구매자 토큰잔액 추가

    balanceOf[msg.sender] -= amount;                   // 판매자 토큰 잔액 차감 subtracts the amount from seller's balance

    revenue = amount * sellPrice;                // 판매금액 계산(eth)

    if (!msg.sender.send(revenue)) {                   // 판매자에게 이더 전송, 아주 중요함

        throw;                                         // to do this last to prevent recursion attacks

    } else {

        Transfer(msg.sender, this, amount);             // 전송 수행

        return revenue;                                 // 판매금액 반환

    }

}


이게 새로운 토큰을 생성하지 않고 컨트렉트 주체의 잔액을 변경한다는것을 주의깊게 보아야 한다. 컨트랙트는 토큰과 이더 모두 그리고 컨트랙트 소유자를 보유할 수 있으나 가격을 설정하거나 유효한경우 일부 토큰을 생성한다. 이는 은행의 토큰이나 이더를 건드릴수는 없다. 이 컨트랙트에서 펀드를 이동시킬수 있는 유일한 방법은 구매나 판매을 하는것 뿐이다.


가치를 구매 및 판매하는것은 이더내에서 설정되지 않고 유로나 달라의 cent나 비트코인의 satoshi같은 단위인 wei가 있다. 1 이더는 1000000000000000000 wei(10경)이다. 따라서 이더로 토큰을 설정할때 18개의 0을 넣어야 한다. 무쟈게 많다. 10경이니..


컨트랙트를 생성할 때 시장에있는 모든 토큰을 다시 구매할 수 있도록 충분한 양의 이더를 보내야 한다.그렇지 않으면 컨트랙트가 파산하여 사용자가 토큰을 판매 할 수 없게 된니다.


위 샘플에서는 단일 중앙구매자와 판매자간의 컨트랙트를 설명했지만 좀더 흥미롭게 다른 사람이 다른 가격을 입찰하거나 외부에서 가격을 직접로드할 수 있습니다.


자동리필


이더리움에서 트랜잭셕이 일어날때마다 채굴자에게 일정비율의 요금을 지불하게 되는데 이 값은 컨트랙트에서 계산되어진다. 순간적으로 수수료는 이더로만 지불되므로 토큰의 모든 사용자는 이더를 필요로 한다. 소유자가 필요한 비용이 지불할 수 있을 때 까지 수수료보다 적은 잔액을 가진 계좌의 토큰은 정지(stick)된다. 하지만 어떤 경우에는 유저가 이더리움, 블록체인 또는 이더리움을 어떻게 얻을 수 있는지 몰라도 되기를 원할 수 있다. 이 경우 한가지 가능한 접근방법은 잔액이 너무 적게되면 자동으로 채워질 수 있게 하는것이다.


이렇게 하기 위해서는 먼저 임계치값을 변수로 생성해야 하며 변경할 수 있게 해야 한다. 5 finney(0.005 ether)로 설정해보자


uint minBalanceForAccounts;


function setMinBalance(uint minimumBalanceInFinney) onlyOwner {

     minBalanceForAccounts = minimumBalanceInFinney * 1 finney;

}


그리고 transfer함수를 수정한다.


/* Send coins */

function transfer(address _to, uint256 _value) {

    ...

    if(msg.sender.balance<minBalanceForAccounts)

        sell((minBalanceForAccounts-msg.sender.balance)/sellPrice);

}


송금인이 수취인에게 수수료를 지불할 수 있도록 변경할수도 있다.


/* Send coins */

function transfer(address _to, uint256 _value) {

    ...

    if(_to.balance<minBalanceForAccounts)

        _to.send(sell((minBalanceForAccounts-_to.balance)/sellPrice));

}


이러면 토큰을 받는 계좌가 수수료를 지불하는데 필요한 이더보다 적게 수수료를 지불하지 않습니다.


작업검증


수학공식을 코인과 엮는 몇가지 방안이 있다. 가장 간단한 방법은 이더로 "병합된 광산(채굴)"로 만드는것이다. 다시 말해 이더리움에서 블록을 발견한 사람은 누구나 그 불록에 대한 보상 기능을 호출하면 코인에서 보상을 받을것이다. 블록을 찾은 광부를 지칭하는 coinbase 키워드로 이를 수행할 수 있다.


function giveBlockReward() {

    balanceOf[block.coinbase] += 1;

}


이것은 또한 수식을 추가할 수 있는데 누구든 이를 수행하면 보상에서 승리할 수 있다. 다음 예에서 당신은 포인트를 얻는 현재 도전의 큐빅루트를 계산해야 하고 다음 도전을 설정할 수 있는 권한을 얻는다.


uint currentChallenge = 1; // Can you figure out the cubic root of this number?


function rewardMathGeniuses(uint answerToCurrentReward, uint nextChallenge) {

    if (answerToCurrentReward**3 != currentChallenge) throw; // If answer is wrong do not continue

    balanceOf[msg.sender] += 1;         // Reward the player

    currentChallenge = nextChallenge;   // Set the next challenge

}


물론 계산을 누군가의 머리로 하는것은 많이 어렵지만 계산기는 아주 쉽다.그래서 이 게임은 컴퓨터로는 금방 끝난다. 또한 마지막 승자가 다음 도전을 고를수 있기 때문에 다른 플레이어에게는 공정하지 않다. 인간에게는 쉽지만 기게로는 매우 어렵지만 스크립트로 코딩하는것을 매우 어렵다. 대신에 더 공정한 시스템은 기계가 할 수 있는것은 매우 어렵지만 기계가 검증하기가 그리 어렵지는 않다. 가장 적절한 후보는 도전자가 주어진 난이도보다 낮은 해시를 찾을때 까지 여러 숫자의 해시를 생성해야 하는 해시 도전을 만드는것이다.

이 프로세스는 1997년 Adam Back에 의해 hashcash로 처음 제안되었고 2008년에 satosi nakamoto의 비트코인에서 구현되었습니다. 이더리움은 보안모델을 위해 이러한 시스템을 사용하여 출시되었지만 작업증명보안모델에서 혼합된 모델(링크탭하여 더 알아보세요)로 이동하려고 계획하고 있습니다.

해싱을 코인의 랜덤발행형태로 하려는 경우 작업방행증명이 있는 자신의 이더기반 통화를 만들수 있다.


bytes32 public currentChallenge;                         // The coin starts with a challenge

uint public timeOfLastProof;                             // Variable to keep track of when rewards were given

uint public difficulty = 10**32;                         // Difficulty starts reasonably low


function proofOfWork(uint nonce){

    bytes8 n = bytes8(sha3(nonce, currentChallenge));    // Generate a random hash based on input

    if (n < bytes8(difficulty)) throw;                   // Check if it's under the difficulty


    uint timeSinceLastProof = (now - timeOfLastProof);  // Calculate time since last reward was given

    if (timeSinceLastProof <  5 seconds) throw;         // Rewards cannot be given too quickly

    balanceOf[msg.sender] += timeSinceLastProof / 60 seconds;  // The reward to the winner grows by the minute


    difficulty = difficulty * 10 minutes / timeSinceLastProof + 1;  // Adjusts the difficulty


    timeOfLastProof = now;                              // Reset the counter

    currentChallenge = sha3(nonce, currentChallenge, block.blockhash(block.number-1));  // Save a hash that will be used as the next proof

}


생성자함수도 변경해야 한다.


    timeOfLastProof = now;


컨트랙트가 온란인상태가 되면 "작업 증명"기능을 선택하고 nonce필드에 원하는 번호르 추가하여 실행해라. 확인 창이 "데이터를 실행할 수 없습니다"라는 빨간색 경고 메시지가 나타나면 거래가 진행될때까지 다른 번호를 선택하라. 이 프로세스는 임의적이다. 하나를 찾으면 마지막 보상이 주어지기 때문에 매 순간마다 1토큰을 받게되며 도전난이도는 보상당 평균 10분을 목표로 위아래로 조정된다.

보상을 제공하는 번호를 찾으려는 이 과정을 채굴이라고 부른다. 난이도가 올라가면 숫자를 찾기가 어려울 수 있지만 발견한것을 확인하는것은 항상 쉽다.



Posted by 삼스