DreamFactory 수정

웹페이지 구성하다 보니 중요한 한 가지를 DreamFactory에서 빼먹었네요. 바로 DreamStory의 핵심인 스토리입니다. 사용자가 스토리를 DApp을 통해서 올리는데 DreamStory에 이 스토리를 저장하는 상태변수를 선언하지 않았습니다. 컨트랙트 작성 시 많은 고민을 안 하다보니 이렇게 컨트랙트를 수정하게 되네요. 테스트넷에 배포까지 했는데 말이죠. 그래도 수정할 건 수정해야죠.


수정 사항

  • DreamStory 상태변수에 string story_titlestring story 추가
  • DreamStory 생성자에 매개변수 title, story 추가
  • DreamFactory의 createDreamStory 함수 인자에 title, story 추가
  • DreamStory의 getSummary 함수에 story_title, story 반환 추가
  • 컴파일(compile.js) 실행
  • 배포(deploy.js) 실행
  • 배포된 주소를 코드에 반영

사실 이것 이외도 지금 신경쓰지 못한 게 있습니다. contributor가 어떤 story 다운로드했을 때, 사용 권한을 줘야 합니다. 아직 어떻게 구현해야 할지 생각을 못했습니다. 임시로 생각한 것이 해당 story를 다운로드 한 사용자의 계정을 출력하는 정도입니다. 작업하며 좋은 방법이 있다면 수정하도록 하겠습니다.


수정된 코드 전문

pragma solidity ^0.4.17;

// dream story factory contract
contract DreamFactory {
    // array of addresses of deployed dream stories
    address[] public deployed_dream_stories;

    /*
     * Create a new dream story
     * @param min_down_price minimum download price in wei
     * @param story dream story
     */
    function createDreamStory( uint _min_down_price, string _title, string _story ) public {
        // create a new dream story
        address new_story= new DreamStory( _min_down_price, msg.sender, _title, _story );
        // save the deployed address
        deployed_dream_stories.push( new_story );
    }

    /*
     * Get the deployed dream stories
     * @return addresses of the deployed dream stories
     */
    function getDeployedDreamStories() public view returns (address[]) {
        return deployed_dream_stories;
    }
}

// dream story contract
contract DreamStory {
    //// state variables
    // dream story title
    string public story_title;
    // dream story
    string public story;
    // download struct
    struct Download {
        // address of the downloder
        address downloader;
        // download price in wei
        uint price_wei;
        // download date
        uint date;
    }
    // download history
    Download[] public downloads;
    // author of a dream story
    address public author;
    // list of contributors as mapping
    mapping( address => bool ) public contributors;
    // number of votes which is the number of contributors
    uint public votes_count;
    // list of approvers
    mapping( address => bool ) public approvers;
    // number of approvers
    uint public approvers_count;
    // minimum download price in wei
    uint public min_down_price_wei;
    // list of downloaders as mapping
    mapping( address => bool ) public downloaders;

    //// modifier
    // only for author
    modifier onlyAuthor() {
        require( msg.sender == author );
        _;
    }
    // only for contributors
    modifier onlyContributor() {
        // the sender should be in the contributors list
        require( contributors[msg.sender] );
        _;
    }

    //// functions
    /*
     * Constructor
     @param min_down_price minimum download price in wei
     @param creator address of the creator of this story
     @param title dream story title
     @param story dream story
     */
    constructor( uint _min_down_price, address _creator, string _title, string _story ) public {
        // set author to message sender who is the creator of this dream story
        author= _creator;
        // set minimum download price
        min_down_price_wei= _min_down_price;
        // set story title
        story_title= _title;
        // set story
        story= _story;
    }

    /*
     * A contributor donates some money for a dream story.
     * So the money will be transfered to this contract address, resulted in increasing the balance
     * @note this function can receive some money, which is msg.value
     */
    function contribute() public payable {
        // check if the money is greater than zero
        require( msg.value > 0 );
        // increase the vote counts
        votes_count++;
        // set contributor address to true
        contributors[ msg.sender ]= true;
    }

    /*
     * Download (license of) the dream story
     * @note this function can receive some money, which is msg.value
     */
    function download() public payable onlyContributor {
        // check if the contributor has downloaded before.
        // if so, no need to download again
        require( !downloaders[msg.sender] );
        // check if the input price is bigger than the min_down_price_wei
        require( msg.value >= min_down_price_wei );
        // local variable is basically stored in storage,
        // and literal such as struct is created in memory since it is temporary.
        // storage variable references the original variable
        // memory variable copies the original variable
        // memory is temporary, storage is global.
        Download memory new_download= Download({
           downloader: msg.sender,
           price_wei: msg.value,
           date: now
        });

        // add it to the downloads array
        downloads.push( new_download );
        // set the download address to true
        downloaders[ msg.sender ]= true;
    }


    /*
     * Approve the payment to the author by a contributor
     * @note a contributor who already approved the withdrawl cannot call it again
     */
    function approveWithdrawal() public onlyContributor {
        // check whether this contributor approved the withdrawl already
        require( !approvers[msg.sender] );
        // set the account as an approver
        approvers[ msg.sender ]= true;
        // increase the counts
        approvers_count++;
    }

    /*
     * Execute the withdrawal by the author
     */
    function executeWithdrawal() public onlyAuthor {
        // half of contributors should approve the withdrawal
        require( approvers_count > ( votes_count/2 ) );
        // transfer the balance to the author
        author.transfer( address(this).balance );
    }

    /*
     * Get summary of the dream story
     * @return
     */
    function getSummary() public view returns ( uint, uint, uint, uint, uint, address, string, string )
    {
        return (
          address(this).balance,
          votes_count,
          downloads.length,
          min_down_price_wei,
          approvers_count,
          author,
          story_title,
          story
        );
    }
}


컨트랙트 컴파일

컨트랙트 소스 코드가 바뀌었기 때문에 컴파일을 다시 해야 하고, 테스트넷에 배포도 다시 해야 합니다. 주소도 달라지기 때문에 주소가 달라진 부분도 수정해야 합니다.

comile.js가 있는 폴더로 이동하여 다음 명령을 실행합니다.

$ node compile.js


컨트랙트 배포

테스트넷에 배포하는 게 이걸로 두 번째네요. 역시 개발 중에는 문제가 터지기 마련입니다. 테스트넷이 없었으면 이래 저래 손해에 자원 낭비가 될 뻔했습니다. 아마 두 번째로 안 끝나고 몇 번 갈지 모르겠네요. 컨트랙트 소스 변경되면 해야 할 것이 이제 익숙해 질거 같습니다. 배포 또한 단순히 deploy.js가 있는 폴더로 이동해서 다음 스크립트를 실행하면 됩니다.

$ node deploy.js


새로 배포된 DreamFactory의 주소입니다! 0xf6faBCa0A6F00eC2E6E8A5726c593905f2c3874b. 이것을 파일로 저장하고, 주소가 사용된 모든 곳을 업데이트 합니다. 물론 여러분이 작성한 컨트랙트 주소를 사용해야 합니다.


컨트랙트 주소 변경

dream_factory.js에서 주소를 변경합니다.

// import the web3.js
import web3 from './web3';
// import interface, which is abi
import DreamFactory from './build/DreamFactory.json';

// create dream factory instance
const factory_instance= new web3.eth.Contract(
  // set contract interface from the pre-built json file
  JSON.parse( DreamFactory.interface ),
  // deployed DreamFactory contract address
//  '0x75A31f56efEba84D7A1D99ac1b29Bb062cCD57d9'
    '0x7860cBb15cC42993456e09159590D5a61b4F329e'
);

// export the factory instance
export default factory_instance;


오늘의 실습: 실수를 두려워 하지 마세요. 테스트넷에서 충분히 하세요!



 [한빛미디어 블록체인 도서 보러가기]