본문 바로가기
개발 일지/블록체인

S4: Credential 발급/조회 + 추가기능

by StelthPark 2021. 12. 3.

전체코드

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.10;

abstract contract OwnerHelper { //추상컨트랙트로 Owner에 의한 처리를 위한 컨트랙트
    address private owner; //owner는 private로 주소타입
  
  	event OwnerTransferPropose(address indexed _from, address indexed _to);
    //OwnerTransferPropose가 emit되면 log가 기록된다.
    //인자로 현재 owner주소, 새owner주소가 들어간다.
  	modifier onlyOwner { //onlyOwner가 실행되는 함수는 아래 require를 처리한뒤 작동된다.
		require(msg.sender == owner); //함수실행자가 현재 owner주소이다.
		_; //require 결과 문제가 없을시 함수 실행
  	}

  	constructor() {
		owner = msg.sender; //초기 owner는 contract 실행자이다.
  	}

  	function transferOwnership(address _to) onlyOwner public {
    //owner변경함수로 새 owner주소를 _to로 받는다.
        require(_to != owner); //_to가 현재 owner가 아니여야한다.
        require(_to != address(0x0)); //_to 주소가 0이 아니여야한다.
        address _from = owner; //_from에 현재 owner를 담는다.
    	owner = _to; //owner를 새 owner인 _to로 변경한다.
    	emit OwnerTransferPropose(_from, _to); //이전 owner와 새 owner주소를 emit하여 event 기록한다.
  	}
}

abstract contract IssuerHelper is OwnerHelper { //credential 발급자인 issure를 Owner만 관리하도록한다.
    mapping(address => bool) public issuers; //issures로 address 키와 bool타입 값으로 매핑한다.

    event AddIssuer(address indexed _issuer); //추가된 issure를 기록한다.
    event DelIssuer(address indexed _issuer); //삭제된 issure를 기록한다.

    modifier onlyIssuer { //onlyIssure를 상속받은 함수는 아래 require를 검사한뒤 함수를 실행한다.
        require(isIssuer(msg.sender) == true); //isIssure함수를 통해 함수실행자가 issure인지 검사한다.
        _; //위 require에 문제가 없다면 정상적으로 아래 로직 실행
    }

    constructor() {f
        issuers[msg.sender] = true; //issures에 현재 콘트랙트 실행자는 issure이다.
    }

    function isIssuer(address _addr) public view returns (bool) {
    //인자로 받은 _addr이 issure인지 확인하는 함수이다.
        return issuers[_addr]; //맵핑한 issures에 확인할 주소를 넣어 bool값을 리턴한다.
    }

    function addIssuer(address _addr) onlyOwner public returns (bool) {
    //issure를 추가하는 함수로 오직 owner인 주소만 실행 할 수 있다.
        require(issuers[_addr] == false); //issure가 false로 아닌지 확인한다.
        issuers[_addr] = true; //require를 문제없이 넘기면 issure에 해당 _addr주소를 issure로 변경
        emit AddIssuer(_addr); /AddIssuer 함수로 해당 _addr 주소를 보내 emit 하여 기록한다.
        return true; //정상적으로 처리됨을 bool로 리턴한다.
    }

    function delIssuer(address _addr) onlyOwner public returns (bool) {
    //issure를 제거하는 함수로 오직 owner인 주소만 실행 할 수 있다.
        require(issuers[_addr] == true); //issure가 true로 맞는지 확인한다.
        issuers[_addr] = false; //require를 문제없이 넘기면 issure에 해당하는 _addr주소를 삭제한다.
        emit DelIssuer(_addr); //해당 _addr주소를 보내 emit 하여 기록한다.
        return true; //정상적으로 처리됨을 bool로 리턴한다.
    }
}

contract CredentialBox is IssuerHelper {
    uint256 private idCount; //credential이 만들어지는 index이다.
    mapping(uint8 => string) private alumniEnum; //alumn(졸업증명서)타입을 uint8키와 string값으로 매핑
    mapping(uint8 => string) private statusEnum; //status타입을 uint8키와 string값으로 매핑

    struct Credential{ //Credential구조이다
        uint256 id; 
        address issuer;
        uint8 alumniType;
        uint8 statusType;
        string value;
        uint256 createDate;
    }

    mapping(address => Credential) private credentials;
    //credentials로 address키와 Credential의 구조체 값으로 매핑

    constructor() {
        idCount = 1; //credential 순서를 위해 1부터시작
        alumniEnum[0] = "SEB"; //alumni타입(졸업증명서)을 할당
        alumniEnum[1] = "BEB";
        alumniEnum[2] = "AIB";
    }

    function claimCredential(address _alumniAddress, uint8 _alumniType, string calldata _value) onlyIssuer public returns(bool){
    //credential 만드는 함수로 홀더주소, 졸업증명서타입, 서명이나 크리덴셜에 저장될 정보등을 인자로 넘긴다
    //onlyIssuer에 의해 issuer만 함수를 실행 할 수 있다.
        Credential storage credential = credentials[_alumniAddress];
        //storage 즉, 데이터를 영구적으로 올린다
        //credentials에 홀더주소를 키값으로 Credential 구조체를 사용해credential을 만든다.
        require(credential.id == 0); //credential의 id값(생성순서)이 0일경우에 처리된다.
        credential.id = idCount; //idCount는 1부터이므로 credential 순서는 1부터 된다.
        credential.issuer = msg.sender;
        credential.alumniType = _alumniType;
        credential.statusType = 0;
        credential.value = _value; 
        credential.createDate = block.timestamp; //credential을 클레임한 시간을 저장한다.

        idCount += 1; //다음credential을 순서를 1올린다.

        return true;
    }

    function getCredential(address _alumniAddress) public view returns (Credential memory){
    // credential들중 특정 _alumniAddress 인 홀더주소가 가진 credential을 리턴하여 보여준다.
        return credentials[_alumniAddress];
    }

    function addAlumniType(uint8 _type, string calldata _value) onlyIssuer public returns (bool) {
    //AlumniType, 졸업증명서 타입을 추가하는 함수로 issuer만 실행 할 수 있다.
    //인자로 type과 string 값을 받는다.
        require(bytes(alumniEnum[_type]).length == 0); //해당하는 타입이 없는지 확인한다.
        alumniEnum[_type] = _value; //새로운 타입을 키로 값을 할당한다.
        return true;
    }

    function getAlumniType(uint8 _type) public view returns (string memory) {
    //AlumniType, 졸업증명서 타입 키에 있는 값을 불러온다.
        return alumniEnum[_type];
    }

    function addStatusType(uint8 _type, string calldata _value) onlyIssuer public returns (bool){
    //Status, 상태 타입을 추가하는 함수로 issure만 실행 할 수 있다.
        require(bytes(statusEnum[_type]).length == 0); //해당하는 상태가 없는지 확인한다.
        statusEnum[_type] = _value; //새로운 타입을 키로 값을 할당한다.
        return true;
    }

    function getStatusType(uint8 _type) public view returns (string memory) {
    //Status, 상태 타입 키에 있는 값을 불러온다.
        return statusEnum[_type];
    }

    function changeStatus(address _alumni, uint8 _type) onlyIssuer public returns (bool) {
    //저장되어 있는 credential의 상태타입의 값을 바꾸는 함수로 issuer만 실행 할 수 있다.
        require(credentials[_alumni].id != 0); 
        //credentials의 특정 홀더 주소의 id가 0이아니여야한다.
        //즉 뭐라도 credential.id가 1이상으로 데이터가 있어야한다.
        require(bytes(statusEnum[_type]).length != 0); //status타입들 중에 넣으려면 _type이 존재해야한다.
        credentials[_alumni].statusType = _type; //credentials에 해당 홀더의 주소를 넣은 상태값을 _type으로 할당
        return true;
    }

}

 

기존 코드의 문제점 (1)

  	function transferOwnership(address _to) onlyOwner public {
        require(_to != owner);
        require(_to != address(0x0));
    	owner = _to;
    	emit OwnerTransferPropose(owner, _to);
  	}
}

Owner를 변경하는 함수로 owner = _to를 하게되면 기존 owner가 새로 인자로 받은 _to로 바뀌는게 맞다.

하지만 아래 emit에서 OwnerTransferPropose의 인자로 결국 새 owner와 새 owner 를 똑같이 넣는것과 같게된다.

owner= _to로 할당하기전에 이전의 owner주소를 다른변수에 할당하고 해당 변수를 OwnerTransferPropose의 첫인자로 넣어야 이전owner와 새owner가 정상적으로 들어가 event 기록이 된다.

 

기존 코드의 문제점 (2)

    function addIssuer(address _addr) onlyOwner public returns (bool) {
        require(issuers[_addr] == true);
        issuers[_addr] = true;
        emit AddIssuer(_addr);
        return true;
    }

issuer를 추가하는 함수로 issuers에 새로 issure가 될 _addr을 키값으로 하게 되면 require에서 해당값은 false로 되어있다. 만들어서 true로 할당한 적이 없기때문이다. false인 상태에서 true로 바꾸는 과정이 addIssuer라고 할수 있다. 해당 코드아래에 delIssuer도 같은 방법으로 접근해야한다.

기존코드의 의문점 (1)

    function claimCredential(address _alumniAddress, uint8 _alumniType, string calldata _value) onlyIssuer public returns(bool){
        Credential storage credential = credentials[_alumniAddress];
        require(credential.id == 0);
        credential.id = idCount;
        credential.issuer = msg.sender;
        credential.alumniType = _alumniType;
        credential.statusType = 0;
        credential.value = _value;
        credential.createDate = block.timestamp;

        idCount += 1;

        return true;
    }

credential 생성하는 함수로 require에서 credential.id가 0일때 시작하게 되며 이후에 idCount인 1을 할당하고 차례로 idCount가 올라가게 된다. 당연 한번더 credential을 생성하게 되면 credential.id가 1이 되었으므로 require를 통과하지 못할것이다. 여기서 credential.id가 어떻게 0으로 돼있었는지, 의문이다.

   function addIssuer(address _addr) onlyOwner public returns (bool) {
        require(issuers[_addr] == true);
        issuers[_addr] = true;
        emit AddIssuer(_addr);
        return true;
    }

또한 해당 함수에서도 issures에 _addr의 키값을 넣었을때 처음 접근하지만 false로 할당되어 있다는것은 어디서 값을 할당했는지, 초기값 설정없이 값을 선언하면 데이터 타입에 따라 0또는 false로만 만들어지는것인지 의문이다.

댓글