SECURITY/Blockchain

[SmartContract] Truffle ๋กœ Solidity ์ปดํŒŒ์ผ ๋ฐ Ganache ์— ๋ฐฐํฌ

\b\t 2022. 1. 11. 14:51

 

* ์ด์ „ ํฌ์ŠคํŒ…์— ์ด์–ด์ง€๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

 

SmartContract ๋ฅผ Solidity ๋กœ ์ž‘์„ฑํ•˜๊ณ , truffle ๋กœ ์ปดํŒŒ์ผํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ์‚ดํŽด๋ด…์‹œ๋‹ค.

๋˜ํ•œ, ์ปดํŒŒ์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ง€๊ณ  ๋ฐฐํฌ๊นŒ์ง€ ์ง„ํ–‰ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 


Truffle ์—์„œ Solidity ์ปดํŒŒ์ผํ•˜๊ธฐ

์ง€๋‚œ ํฌ์ŠคํŒ…์—์„œ์ฒ˜๋Ÿผ, truffle ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ๋‹ค์Œ์˜ ๊ตฌ์กฐ๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค.

  • contracts: solidity ์†Œ์ŠคํŒŒ์ผ (.sol)
  • migrations: ๋ฐฐํฌ script (.js)
  • test: ํ…Œ์ŠคํŠธ script (.js or .sol)
  • truffe.js: ์„ค์ • ํŒŒ์ผ

 

truffle ์€ ์ด ๊ตฌ์กฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ solidity ํŒŒ์ผ์„ ์ปดํŒŒ์ผํ•˜๊ณ , ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

์ง€๊ธˆ์€ ์–ด๋–ป๊ฒŒ ์ปดํŒŒ์ผ ๋ฐ ๋ฐฐํฌ๋ฅผ ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๋Š” ๋‹จ๊ณ„์ด๋ฏ€๋กœ, truffle ์˜ ๊ธฐ๋ณธ ์ œ๊ณต ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•ด ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๋‹ค์Œ์€ truffle ์˜ Migrations.sol ์ž…๋‹ˆ๋‹ค.

 

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

contract Migrations {
  address public owner = msg.sender;
  uint public last_completed_migration;

  modifier restricted() {
    require(
      msg.sender == owner,
      "This function is restricted to the contract's owner"
    );
    _;
  }

  function setCompleted(uint completed) public restricted {
    last_completed_migration = completed;
  }
}

 

 

truffle ์—์„œ solidity ๋ฅผ ์ปดํŒŒ์ผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” contracts ํด๋”์— sol ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜๊ณ , ํ„ฐ๋ฏธ๋„์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž…๋ ฅํ•ด์ค๋‹ˆ๋‹ค.

$ truffle compile

๊ทธ๋Ÿผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ปดํŒŒ์ผ์ด ์ง„ํ–‰๋˜๊ณ , ์„ฑ๊ณต์ ์œผ๋กœ ๋๋‚˜๋ฉด build ํด๋”์— ์ปดํŒŒ์ผ ๊ฒฐ๊ณผ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

solidity ์ปดํŒŒ์ผ ๊ฒฐ๊ณผ๋Š” 'abi' ์™€ 'bytecode' ๋กœ, ํ•ด๋‹น ๋‚ด์šฉ์ด ๋“ค์–ด์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

* abi: application binary interface (contract ์˜ ๋ช…์„ธ ์—ญํ• )

 

 

 

 

๋ฐฐํฌ script ์ž‘์„ฑ

smart contract ๋ฅผ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐฐํฌ script ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

deploy (๋˜๋Š” migrate) ๋ฅผ ์œ„ํ•ด์„  ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ๋ฅผ migrations ํด๋”์— ์ž‘์„ฑํ•˜๋ฉด ๋˜๋Š”๋ฐ, ํŒŒ์ผ๋ช… ํ˜•์‹์— ์ •ํ•ด์ง„ ๊ทœ์น™์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

{์ˆซ์ž}_{์ด๋ฆ„}.js

 

์ˆซ์ž๋Š” truffle ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” '๋ฐฐํฌ ์ˆœ์„œ' ์ž…๋‹ˆ๋‹ค.

์ฒ˜์Œ truffle ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค๋ฉด 1_initial_migration.js ๋ผ๋Š” ํŒŒ์ผ์ด ์žˆ๋Š”๋ฐ, ์ด ์นœ๊ตฌ๊ฐ€ 1๋ฒˆ์ด๊ณ  ์šฐ๋ฆฌ๋Š” 2 ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ž‘์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ex. 2_HelloWorld_migration.js

 

๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

const Migrations = artifacts.require("Migrations");

module.exports = function (deployer) {
  deployer.deploy(Migrations);
};
  • artifacts.require("CONTRACT_NAME"): contract name (๋˜๋Š” ํŒŒ์ผ๋ช…) ์„ ์ ์–ด์ค๋‹ˆ๋‹ค.
  • deployer.deploy(Migrations)
    • ๋‘ ๋ฒˆ์งธ parameter ๋กœ ํ•ด๋‹น contract ์˜ ์ƒ์„ฑ์ž ์ธ์ˆ˜๋ฅผ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, contract ์˜ consrtuctor ์— ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์žˆ์—ˆ๋‹ค๋ฉด, ํ•ด๋‹น ๋‚ด์šฉ์„ ์ฑ„์›Œ์ค๋‹ˆ๋‹ค.
    • ex. deployer.deploy(Migrations, "Hello");
    • * ์ด๋Š” 'argv' ์„ ๋„ฃ์–ด์ค€๋‹ค๊ณ  ์ดํ•ดํ•˜๋ฉด ํŽธํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

๋ฐฐํฌ๋ฅผ ์œ„ํ•ด์„œ๋Š” contract ์˜ ์ปดํŒŒ์ผ ๊ฒฐ๊ณผ์ธ abi ์™€ bytecode ๋ฅผ ์•Œ์•„์•ผ ํ•˜๋Š”๋ฐ, truffle ์—์„œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ์ € ์ •๋ณด์— ์ ‘๊ทผํ•˜์ง€ ์•Š๊ณ ๋„ ์†์‰ฝ๊ฒŒ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค.

 

 

 

Ganache test net ์— ๋ฐฐํฌํ•˜๊ธฐ

์ด์ œ contract ๋„ ์ž‘์„ฑํ•ด์„œ ์ปดํŒŒ์ผํ–ˆ๊ณ , ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ๋„ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

 

๋‚จ์€ ๊ฒƒ์€ contract ๋ฅผ ์–ด๋””์— ๋ฐฐํฌํ•˜๋Š๋ƒ, ์ฆ‰ ๋ฐฐํฌ ํƒ€๊ฒŸ์„ ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

'๋ฐฐํฌ ํƒ€๊ฒŸ'์€ contract ๋ฅผ ์–ด๋””์— ๋ฐฐํฌํ•˜๊ฒ ๋ƒ๋Š” ๊ฒƒ์œผ๋กœ, ๋กœ์ปฌ (Ganache, geth ๋“ฑ), ์ด๋”๋ฆฌ์›€ ํ…Œ์ŠคํŠธ๋„ท, ์ด๋”๋ฆฌ์›€ ๋ฉ”์ธ๋„ท(๋ฉ”์ธ ์„œ๋ฒ„) ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • Ganache, geth ๋ฅผ ์ด์šฉํ•ด์„œ ๋กœ์ปฌ ํ…Œ์ŠคํŠธ๋„ท์— ๋ฐฐํฌ
  • geth ๋กœ ๋ฉ”์ธ๋„ท ๋˜๋Š” ํ…Œ์ŠคํŠธ๋„ท์— ๋ฐฐํฌ
  • infura ๋ผ๋Š” ํšŒ์‚ฌ๊ฐ€ ์ œ๊ณตํ•˜๋Š” provider ๋ฅผ ํ†ตํ•ด ๋ฉ”์ธ๋„ท ๋˜๋Š” ํ…Œ์ŠคํŠธ๋„ท์— ๋ฐฐํฌ

 

์ด๋Š” truffle-config.js ์—์„œ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

* ์ด์ „ truffle ์—์„œ๋Š” ์šด์˜์ฒด์ œ์— ๋”ฐ๋ผ tuffle.js ๋˜๋Š” truffle-config.js ๋กœ ์„ค์ • ํŒŒ์ผ์ด ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์—ˆ๋Š”๋ฐ, ์ตœ์‹  truffle ์—์„œ๋Š” truffle-config.js ๋กœ ํ†ต์ผ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

truffle-config.js ์—์„œ๋Š” ๋””ํดํŠธ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋˜์–ด์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. (์ฃผ์„์œผ๋กœ ์ž‘์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค)

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",     // Localhost (default: none)
      port: 8545,            // Standard Ethereum port (default: none)
      network_id: "*",       // Any network (default: none)
    }
};

์œ„์˜ ์˜ˆ์‹œ์—์„œ์ฒ˜๋Ÿผ ํƒ€๊ฒŸ networks ๋ฅผ ์ง€์ •ํ•ด์ฃผ๋ฉด ๋˜๋Š”๋ฐ, s ๊ฐ€ ๋ถ™์€ ๊ฒƒ์œผ๋กœ ์ง์ž‘ํ•  ์ˆ˜ ์žˆ๋“ฏ์ด ํ•œ๋ฒˆ์— ์—ฌ๋Ÿฌ network ๋ฅผ ์ง€์ •ํ•˜์—ฌ ๋‹ค์ค‘ ๋ฐฐํฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ 'network' ์˜ ์ด๋ฆ„์€ ๋งˆ์Œ๋Œ€๋กœ ์ •ํ•ด์ค˜๋„ ๋˜์ง€๋งŒ, 'development' ๋Š” tuffle ์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋„คํŠธ์›Œํฌ์ž„์„ ์ฃผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ, network ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” 'host', 'port', 'network_id' ์ด ์„ธ ๊ฐ€์ง€ ์š”์†Œ๋ฅผ ๋ฐ˜๋“œ์‹œ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ด๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด, tuffle command ๋ฅผ ํ†ตํ•ด ํŠน์ • network ๋ฅผ ์ •ํ•ด์„œ ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

$ truffle test --network <NETWORK_NAME>

 

๋‹ค์Œ์€ ๋‹ค์ค‘ ๋„คํŠธ์›Œํฌ ์„ค์ •์— ๋Œ€ํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

 

module.exports = {
  networks: {
    development: { host: "127.0.0.1", port: 8545, network_id: "*", },
    dev-ganache: { host: "127.0.0.1", port: 7545, netword_id: "777", },
    advanced: { host:"192.168.0.x", port: 8777, network_id: 1342, gas: 8500000, gasPrice: 20000000000, },
};

 

 

 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” Ganache ๋กœ ๋กœ์ปฌ ํ…Œ์ŠคํŠธ๋„ท์— ๋ฐฐํฌ๋ฅผ ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๋จผ์ € ์ด์ „์— ์„ค์น˜ํ•ด๋‘” Ganache ๋ฅผ ์‹คํ–‰ํ•ด์ฃผ๊ณ , test net ์„ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค. (์ €์˜ ๊ฒฝ์šฐ, QuickStart ๋กœ ๋งŒ๋“ค์–ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค)

 

 

Auto mining ์œผ๋กœ ๋Œ์•„๊ฐ€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ ์•„๋ž˜๋กœ๋Š” Ganache ๊ฐ€ ์‹คํ–‰ ์‹œ ๋งŒ๋“ค์–ด์ฃผ๋Š” 10๊ฐœ์˜ ํ…Œ์ŠคํŠธ ๊ณ„์ •์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

truffle-config.js ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ด์ค๋‹ˆ๋‹ค.

(์ž์‹ ์˜ Ganache ๋ฒ„์ „์— ๋งž๊ฒŒ port, network_id ๋ฅผ ๋ณ€๊ฒฝํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค)

 

module.exports = {
    networks: {
        development: {
            host: "127.0.0.1",     // Localhost (default: none)
            port: 7545,            // Standard Ethereum port (default: none)
            network_id: 5777,       // Any network (default: none)
        }
    },
};

 

์ด์ œ ํ„ฐ๋ฏธ๋„์„ ์—ด์–ด์„œ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž…๋ ฅํ•ด์ฃผ๋ฉด ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ฐจ๋ก€๋กœ ์ง„ํ–‰๋˜๋ฉฐ ๋ฐฐํฌ๋œ ์ฃผ์†Œ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.

 

$ truffle migrate --network development
  • migrate: Run migrations to deply contracts

(ํ˜น์‹œ, ์–ด๋– ํ•œ ์ด์œ ๋กœ ์ž˜ ์•ˆ๋œ๋‹ค๋ฉด, power shell ์—์„œ ์‹œ๋„ํ•ด๋ณด์„ธ์š”.)

 

๋ฐฐํฌ๋Š” ์ปดํŒŒ์ผ ํ›„ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

 

 

contract address ๋ฅผ ์ด์šฉํ•ด์„œ ๋ฐฐํฌ๋œ contract ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ, Ganache ๋Š” local test net ์ด๋ฏ€๋กœ, ์ข…๋ฃŒ์‹œํ‚ค๋ฉด ๋ฐฐํฌํ–ˆ๋˜ ๋‚ด์šฉ์ด ๋ชจ๋‘ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

 

Ganache ๋ฅผ ๋ณด๋ฉด ํ•œ ๊ณ„์ •์˜ ์ด๋”๊ฐ€ ๊ฐ์†Œํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 


์ด๋ ‡๊ฒŒ ๊ธฐ๋ณธ Solidity ์ฝ”๋“œ๋ฅผ Truffle ์„ ์ด์šฉํ•ด์„œ ์ปดํŒŒ์ผํ•˜๊ณ , Ganache ์— ๋ฐฐํฌํ•˜๋Š” ๊ฒƒ๊นŒ์ง€ ํ•ด๋ดค์Šต๋‹ˆ๋‹ค.