Blockchain 개발

이더리움 스마트 컨트랙트 동작방식의 이해

스마트 컨트랙트 동작 방식

이전에 올린 참고 서적(블록체인 애플리케이션 개발 실전 입문)을 보면서 이더리움의 핵심 개념이 스마트 컨트랙트의 개념 및
동작방식이 종결되었습니다!

최근에 @feyee95님도 스마트 컨트랙의 개념에 대해서 올려주셨습니다.
[이더리움] 블록과 계약의 연결(스마트 컨트랙트)

내용을 쉬운 예를 들어 설명해 주셨는데, 한가지 스마트 컨트랙트가 블락체인 상에서 어떻게 동작하는지는 모호한 면이 있었습니다. 저는 그동안 스마트 컨트랙트는 개인 계정(Externally Owned Account)과 달리 Smart Contact의 계정이 있다고 하니 하나의 노드 처럼 인식을 하고 있었습니다.

그러나 그것은 사실이 아닙니다! 스마트 컨트랙트의 코드 자체가 블락체인의 한 블락에 포함되어 있습니다!

 

아래 그림으로 한 번 설명해 보겠습니다.

[출처: 블록체인 애플리케이션 개발 실전 입문]

위 그림은 단순히 스마트 컨트랙트 설명만을 위한 그림이 아닙니다. 블락체인 데이터를 가지고 있지 않은 일반 사용자들이 어떻게 블락체인에 접근하고, 스마트 컨트랙트를 사용하는지 설명해 주는 매우 소중한 그림입니다.

1. 일반 사용자

위 그림 왼편을 보면 일반적인 사용자는 이더리움 블락체인 데이터를 가지고 있지 않습니다. 이더리움 블락체인 데이터는 수백 GB데이터 이며, 블락체인의 노드로 동작하기 위해서는 계속 블락데이터를 다운로드 받아 싱크를 맞춰야 합니다. 그래서 일반 사용자는 블락체인 데이터를 직접 저장하지 않고, 일종의 서버와 같은 역할을 하는 노드에 연결합니다. 이 때 웹 브라우저나 ssh와 같은 서비스를 이용하게 되는 것이죠.

2. 이더리움 클라이언트

그림 중앙에 나타난 이더리움 클라이언트는 이더리움 블락체인에 참여하는 노드입니다. 즉 블락이 생성되면 그 정보를 전파 받는 역할을 하는 것이죠. 따라서, 싱크된 블락체인 데이터를 가지고 있어야 합니다. 이더리움 클라이언트는 블락체인 네트워크의 노드이면서, 일반 사용자의 접속을 허용하고 블락체인과 연결시켜주는 역할도 담당합니다. 그래서 일반 사용자는 이더리움 클라이언트, 즉 노드에 접속하여 geth와 같은 명령어로 블락체인 정보를 얻거나, 스마트 컨트랙트를 사용할 수 있습니다.

이더리움 클라이언트는 블락체인 데이터를 모두 가지고 있습니다. 한 사용자가 생성한 스마트 컨트랙트는 이더리움 클라이언트를 통해서 블락에 포함되고, 결국 블락체인에 연결됩니다.

스마트 컨트랙트가 블락체인에 포함된다는 얘기는 블락체인 네트워크 상의 모든 노드들이 동일한 스마트 컨트랙트(엄밀히는 스마트 컨트랙트 코드)를 가지고 있는 것입니다!

위 내용이 위 그림의 오른편에 나타나 있습니다. 한 사용자가 생성한 스마트 컨트랙트 코드가 블락에 기록되어 다른 노드에서도 코드가 복사된 것이죠. 그래서 각 노드의 EVM에서 코드를 실행시킬 수가 있습니다!


위 내용을 실제 예를 들어서 좀 더 구체적으로 알아보겠습니다. 자 따라오시면, 이더리움 스마트 컨트랙트 개념을 종결시킬 수 있습니다!

스마트 컨트랙트를 만드는 순서는 다음과 같습니다.

  1. 스마트 컨트랙트 코딩
    : 구현하고자 하는 내용을 솔리디티나 다른 언어로 코딩합니다.
  2. 구현한 소스 코드를 컴파일
    : 컴파일 결과 EVM 바이트 코드가 생성됩니다.
  3. 스마트 컨트랙트 배포
    : 스마트 컨트랙트를 배포한다는 것은 컴파일된 EVM 코드를 하나의 트랜잭션 처럼 블락에 추가시켜 블락체인에 등록시키는 작업입니다.
    : 소스 컴파일 -> EVM 바이트 코드 -> 구체적인 작업은 ABI 취득 -> ABI로부터 컨트랙트 객체 생성 -> 트랜잭션 생성하여 블락에 추가
    : 마이너가 해당 블락을 채굴하게 되면 블락체인에 포함됨

1. 스마트 컨트랙트 코딩

간단한 스마트 컨트랙트의 예로 아라왜 같이 코딩합니다. 단순히 공용 변수에 값을 변경하고, 읽는 기능에 대한 구현입니다.

pragma solidity ^0.4.8;     // (1) 버전 프라그마

// (2) 계약 선언
contract HelloWorld {
  // (3) 상태 변수 선언
  string public greeting;
  // (4) 생성자
  function HelloWorld(string _greeting) {
    greeting = _greeting;
  }
  // (5) 메서드 선언
  function setGreeting(string _greeting) {
    greeting = _greeting;
  }
  function say() constant returns (string) {
    return greeting;
  }
}

2. 소스 코드 컴파일

위 코드를 솔리티티 컴파일러로 컴파일 하면 아래와 같은 EVM 바이트 코드가 생성됩니다. 컴파일은 remix나 geth의 solidity 컴파일러로 할 수 있습니다. 이 부분은 생략합니다.
0x6060604052341561000f57600080fd5b60405161046d38038061046d833981016040528080519091019050600081805161003d929160200190610044565b50506100df565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061008557805160ff19168380011785556100b2565b828001600101855582156100b2579182015b828111156100b2578251825591602001919060010190610097565b506100be9291506100c2565b5090565b6100dc91905b808211156100be57600081556001016100c8565b90565b61037f806100ee6000396000f3006060604052600436106100565763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663954ab4b2811461005b578063a4136862146100e5578063ef690cc014610138575b600080fd5b341561006657600080fd5b61006e61014b565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156100aa578082015183820152602001610092565b50505050905090810190601f1680156100d75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100f057600080fd5b61013660046024813581810190830135806020601f820181900481020160405190810160405281815292919060208401838380828437509496506101f495505050505050565b005b341561014357600080fd5b61006e61020b565b6101536102a9565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101e95780601f106101be576101008083540402835291602001916101e9565b820191906000526020600020905b8154815290600101906020018083116101cc57829003601f168201915b505050505090505b90565b60008180516102079291602001906102bb565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102a15780601f10610276576101008083540402835291602001916102a1565b820191906000526020600020905b81548152906001019060200180831161028457829003601f168201915b505050505081565b60206040519081016040526000815290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102fc57805160ff1916838001178555610329565b82800160010185558215610329579182015b8281111561032957825182559160200191906001019061030e565b50610335929150610339565b5090565b6101f191905b80821115610335576000815560010161033f5600a165627a7a72305820352cec017ed93c8351ac6fbc835eda354ea6dbc9e672ae6b60c16f29c49a5cd30029

전혀 뭔지 모르겠죠? 컴파일된 코드라 기계만 이해할 수 있는 코드이니 모르는게 당연합니다~

3. 스마트 컨트랙트 배포

그러면 컴파일된 바이트 코드를 블락체인에 배포하는 절차입니다.
remix를 쓰면 소스 코드를 컴파일하면, 바이트 코드와 ABI도 자동으로 얻어집니다. geth를 사용한다면 일일이 생성해야 합니다.

3.1 ABI 얻기

geth 콘솔의 경우 컴파일된 바이트 코드로 부터 ABI, 즉 스마트 컨트랙트에서 사용될 인터페이스 부분을 얻어와야 합니다.

contractAbiDefinition=sourceCompiled['/tmp/geth-compile-solidity602335484:HelloWorld'].info.abiDefinition
[{
    constant: true,
    inputs: [],
    name: "say",
    outputs: [{
        name: "",
        type: "string"
    }],
    payable: false,
    stateMutability: "view",
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "_greeting",
        type: "string"
    }],
    name: "setGreeting",
    outputs: [],
    payable: false,
    stateMutability: "nonpayable",
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "greeting",
    outputs: [{
        name: "",
        type: "string"
    }],
    payable: false,
    stateMutability: "view",
    type: "function"
}, {
    inputs: [{
        name: "_greeting",
        type: "string"
    }],
    payable: false,
    stateMutability: "nonpayable",
    type: "constructor"
}]

이렇게 스마트 컨트랙트의 ABI가 얻어집니다.

3.2 ABI로 부터 스마트 컨트랙트 객체 생성

geth 콘솔에서 eth.contract 함수를 이용하면 ABI로 부터 스마트 컨트랙트 객체를 만들 수 있습니다. 이 단계는 일반적 코딩에서 클래스를 정의한 것과 유사합니다. 실제 스마트 컨트랙트의 주소가 생성된게 아닙니다.

sourceCompiledContract= eth.contract(contractAbiDefinition)
{
  abi: [{
      constant: true,
      inputs: [],
      name: "say",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      constant: false,
      inputs: [{...}],
      name: "setGreeting",
      outputs: [],
      payable: false,
      stateMutability: "nonpayable",
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "greeting",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      inputs: [{...}],
      payable: false,
      stateMutability: "nonpayable",
      type: "constructor"
  }],
  eth: {
    accounts: ["0x9751574414138b22986eb80ce2713cd2f5508c5c", "0xe37aa5cd578bb1ac568298e5621e11b8a8a113eb", "0xc94593e5164b6f4b5a2f9f0165c1b520858438de", "0x7a1809177f225053ed413743d7321fba8413a7b5"],
    blockNumber: 1179,
    coinbase: "0x9751574414138b22986eb80ce2713cd2f5508c5c",
    compile: {
      lll: function(),
      serpent: function(),
      solidity: function()
    },
    defaultAccount: undefined,
    defaultBlock: "latest",
    gasPrice: 20000000000,
    hashrate: 131062,
    mining: true,
    pendingTransactions: [],
    syncing: false,
    call: function(),
    contract: function(abi),
    estimateGas: function(),
    filter: function(fil, callback),
    getAccounts: function(callback),
    getBalance: function(),
    getBlock: function(),
    getBlockNumber: function(callback),
    getBlockTransactionCount: function(),
    getBlockUncleCount: function(),
    getCode: function(),
    getCoinbase: function(callback),
    getCompilers: function(),
    getGasPrice: function(callback),
    getHashrate: function(callback),
    getMining: function(callback),
    getNatSpec: function(),
    getPendingTransactions: function(callback),
    getRawTransaction: function(),
    getRawTransactionFromBlock: function(),
    getStorageAt: function(),
    getSyncing: function(callback),
    getTransaction: function(),
    getTransactionCount: function(),
    getTransactionFromBlock: function(),
    getTransactionReceipt: function(),
    getUncle: function(),
    getWork: function(),
    iban: function(iban),
    icapNamereg: function(),
    isSyncing: function(callback),
    namereg: function(),
    resend: function(),
    sendIBANTransaction: function(),
    sendRawTransaction: function(),
    sendTransaction: function(),
    sign: function(),
    signTransaction: function(),
    submitTransaction: function(),
    submitWork: function()
  },
  at: function(address, callback),
  getData: function(),
  new: function()
}

3.3 스마트 컨트랙트 배포

이부분이 스마트 컨트랙트의 개념 종결에 가장 중요합니다.
3.2에서 생성한 스마트 컨트랙트 객체를 실제로 블락 내용에 포함시켜서, 채굴되어 블락체인에 포함되도록 하는 과정입니다. 이 때 비로소, 우리가 생성한 스마트 컨트랙트에 접근할 수 있는 주소가 생성됩니다.
3.2에서 만든 스마트 컨트랙스 객체(객체라고 하지만 클래스와 같이 아직 실제 데이터에 접근할 수 있는 데이터가 없는 상태)로부터 하나의 인스턴스를 만듭니다. 이 때, 스마트 컨트랙트 최초 배포시에만 생성자에 입력될 매개변수를 넣어줍니다.
> _greeting="Hello, World"
그런 다음 매개변수와 함께, 스마트 컨트랙트를 하나의 트랙잭션처럼 생성합니다. 이 때, 트랜잭션의 송신자, 바이트 코드, 사용될 예상 gas량을 같이 입력합니다.

> contract= sourceCompiledContract.new( _greeting, {from:eth.accounts[0], data:sourceCompiled['/tmp/geth-compile-solidity602335484:HelloWorld'].code gas:'4700000'})
{
  abi: [{
      constant: true,
      inputs: [],
      name: "say",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      constant: false,
      inputs: [{...}],
      name: "setGreeting",
      outputs: [],
      payable: false,
      stateMutability: "nonpayable",
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "greeting",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      inputs: [{...}],
      payable: false,
      stateMutability: "nonpayable",
      type: "constructor"
  }],
  address: undefined,
  transactionHash: "0x99ddfd763478ce7a0d328fbc67f3c10fec377efa18a8e9c41f61321feb836cd1"
}

3.4 채굴

3.3의 마지막 내용을 보면 해당 트랜잭션이 아직 채굴되지 않은 상태라서 address: undefined,라고 표시됩니다. 즉, 우리가 만든 스마트 컨트랙트의 계정 주소가 아직 생성 전입니다. 마이너가 트랜잭션이 포함된 블락을 채굴하게 되면 비로소 스마트 컨트랙트 주소가 생성됩니다. 아래와 같이요.

  address: "0x6f9c338bb987f1baf619697784c9457b9afa119c",
  transactionHash: "0x99ddfd763478ce7a0d328fbc67f3c10fec377efa18a8e9c41f61321feb836cd1",
  allEvents: function(),
  greeting: function(),
  say: function(),
  setGreeting: function()

address: "0x6f9c338bb987f1baf619697784c9457b9afa119c" 이것이 바로 우리가 생성한 스마트 컨트랙트 계정 주소입니다.


그러면 스마트 컨트랙트를 배포하는 트랙잭션 내용을 살펴보겠습니다. 3.3에서 생성한트랜잭션의 해쉬값은 이렇습니다.
transactionHash: "0x99ddfd763478ce7a0d328fbc67f3c10fec377efa18a8e9c41f61321feb836cd1"
이 트랜잭션의 내용을 geth 콘솔에서 확인할 수 있습니다. 주의할 점은 트랜잭션 해쉬와 스마트 컨트랙트 계정 주소는 다른 것입니다.!

> eth.getTransaction("0x99ddfd763478ce7a0d328fbc67f3c10fec377efa18a8e9c41f61321feb836cd1")
{
  blockHash: "0x2691dcfbd09febcd6d60537386974ba2147ef2ada4e7693a541000442aa5d91d",
  blockNumber: 1230,
  from: "0x9751574414138b22986eb80ce2713cd2f5508c5c",
  gas: 4700000,
  gasPrice: 20000000000,
  hash: "0x99ddfd763478ce7a0d328fbc67f3c10fec377efa18a8e9c41f61321feb836cd1",
  input: "0x6060604052341561000f57600080fd5b60405161046d38038061046d833981016040528080519091019050600081805161003d929160200190610044565b50506100df565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061008557805160ff19168380011785556100b2565b828001600101855582156100b2579182015b828111156100b2578251825591602001919060010190610097565b506100be9291506100c2565b5090565b6100dc91905b808211156100be57600081556001016100c8565b90565b61037f806100ee6000396000f3006060604052600436106100565763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663954ab4b2811461005b578063a4136862146100e5578063ef690cc014610138575b600080fd5b341561006657600080fd5b61006e61014b565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156100aa578082015183820152602001610092565b50505050905090810190601f1680156100d75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100f057600080fd5b61013660046024813581810190830135806020601f820181900481020160405190810160405281815292919060208401838380828437509496506101f495505050505050565b005b341561014357600080fd5b61006e61020b565b6101536102a9565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101e95780601f106101be576101008083540402835291602001916101e9565b820191906000526020600020905b8154815290600101906020018083116101cc57829003601f168201915b505050505090505b90565b60008180516102079291602001906102bb565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102a15780601f10610276576101008083540402835291602001916102a1565b820191906000526020600020905b81548152906001019060200180831161028457829003601f168201915b505050505081565b60206040519081016040526000815290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102fc57805160ff1916838001178555610329565b82800160010185558215610329579182015b8281111561032957825182559160200191906001019061030e565b50610335929150610339565b5090565b6101f191905b80821115610335576000815560010161033f5600a165627a7a72305820352cec017ed93c8351ac6fbc835eda354ea6dbc9e672ae6b60c16f29c49a5cd300290000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c48656c6c6f2c20576f726c640000000000000000000000000000000000000000",
  nonce: 1,
  r: "0x7e7bbc34563f0a6e7d5113344eaea0761d4edf815d41ac752d3cb33b4f65fb43",
  s: "0x61a968fcab4935d0f17b67a5274c31ed2a499ccbc5aa605a9080192a940834e9",
  to: null,
  transactionIndex: 0,
  v: "0x1b",
  value: 0
}

일반적은 송금 거래와 달리 트랜잭션의 input:에 값이 있습니다. 이 것이 바로 스마트 컨트랙트 소스 코드를 컴파일한 바이트 코드입니다. 일반적인 거래에서는 바이트 코드가 필요없기 때문에 input: 0x로 나타납니다. 이 점이 스마트 컨트랙트와 일반 거래와 다른 점입니다. 그리고 수신자가 없습니다. 즉, 송신자만 있고, 수신자는 없습니다. 이것은 당연합니다. 스마트 컨트랙트는 이것을 생성한 송신자가 특정 수신을 지정하지 않고 자신이 생성한계약 코드를 블락체인에 올린 것이기 때문입니다.

그러면, 이 스마트 컨트랙트가 포함된 블락(blockNumber: 1230)이 채굴된 상태에서 블락 정보를 살펴 보겠습니다.

> eth.getBlock(1230)
{
  difficulty: 234190,
  extraData: "0xd783010505846765746887676f312e362e32856c696e7578",
  gasLimit: 40355674,
  gasUsed: 293248,
  hash: "0x2691dcfbd09febcd6d60537386974ba2147ef2ada4e7693a541000442aa5d91d",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0x9751574414138b22986eb80ce2713cd2f5508c5c",
  mixHash: "0xdd2c7606df86047b0c769a375074f4630a850345a63a8236a8b0f8628a9ba43e",
  nonce: "0x1f1450b847e902d0",
  number: 1230,
  parentHash: "0xf72c4cc5ec6c6ce1ad477396cc1dfcf81a5028844d0b21aa563fb651cf9c62e1",
  receiptsRoot: "0xff4331768f55966c79f25c49800b70572d62856ab00960371b411430a022d05e",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 1858,
  stateRoot: "0xf39ba07306e185b8fc5a66fd54de9aef311fd00e59f268453cdde7db4d121f29",
  timestamp: 1515859762,
  totalDifficulty: 218484208,
  transactions: ["0x99ddfd763478ce7a0d328fbc67f3c10fec377efa18a8e9c41f61321feb836cd1"],
  transactionsRoot: "0x63e1a02b6d3a89fe118f481e5ab23093d52e89d475a9ff83e4dbb37c3ce3c7a2",
  uncles: []
}

위에서 컨트랙트 배포할 때, 설정한 gas량인 4700000보다 적은 gasUsed: 293248가 소모된 것을 알 수 있습니다.

스마트 컨트랙스 접근 및 사용

이제 우리가 생성한 스마트 컨트랙트가 블락체인에 올라갔으니 이제 스마트 컨트랙트 주소를 이용하여 정보를 읽고 쓰고 할 수 있습니다. geth 콘솔에서 새로운 컨트랙트 객체를 만드는데 이 때는 기존에 존재하는 컨트랙트의 주소를 이용합니다. 여기서는 앞에서 생성한 스마트 컨트랙트의 계정 주소인 0x6f9c338bb987f1baf619697784c9457b9afa119c를 이용하여 contract2라는 객체를 만듭니다.

> contract2= eth.contract(contractAbiDefinition).at("0x6f9c338bb987f1baf619697784c9457b9afa119c")
{
  abi: [{
      constant: true,
      inputs: [],
      name: "say",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      constant: false,
      inputs: [{...}],
      name: "setGreeting",
      outputs: [],
      payable: false,
      stateMutability: "nonpayable",
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "greeting",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      inputs: [{...}],
      payable: false,
      stateMutability: "nonpayable",
      type: "constructor"
  }],
  address: "0x6f9c338bb987f1baf619697784c9457b9afa119c",
  transactionHash: null,
  allEvents: function(),
  greeting: function(),
  say: function(),
  setGreeting: function()
}

그럼 contract2를 이용하여 스마트 컨트랙트의 공유 변수 값을 바꿔 보겠습니다.

> contract2.setGreeting.sendTransaction( "Hello, Ethereum", {from:eth.accounts[0], gas:1000000})
"0xa6b71f81b5d6d5c71248afb0e89f34aa2e0e52f98e353899bf80166b072fed36"

contract2를 통해서 스마트 컨트랙트의 공유 변수 값을 바꾸는 것도 하나의 트랙잭션을 생성합니다. 위 명령의 결과로 트랜잭션 0xa6b71f81b5d6d5c71248afb0e89f34aa2e0e52f98e353899bf80166b072fed36"이 발생하였습니다. 이 트랙잭션이 포함된 블락이 채굴되면 스마트 컨트랙트의 공유 변수 값이 바뀌게 됩니다.

contract, contract2에서 공유 변수를 접근해 보면 값이 바뀐 값이 동일하게 읽히게 됩니다. 즉, 복수의 사용자가 각각 스마트 컨트랙트 객체를 만들고 해당 스마트 컨트랙트에 접근하여 값을 변경하거나 읽어 올 수 있게 됩니다. 이 때, 스마트 컨트랙트에 쓰는 것는 것은 트랜잭션을 발생시키지만, 값을 읽어 오는 것은 트랙잭션을 발생시키지 않습니다. 아래와 같이 공유 변수 값을 읽기만 하는 경우는 트랜잭션이 발생하지 않습니다. 읽는 작업은 딱히 거래라고 보기 어렵겠죠.

> contract.say.call()
"Hello, Ethereum"
> contract.setGreeting.sendTransaction( "Hello, Bitcoin", {from:eth.accounts[0], gas:1000000})
"0xe3878aa2689efc199f1159eb8c839882206405c5fb9d3c5eebfbe719a6b49d44"

다시 그림으로 돌아와서, 스마트 컨트랙트가 블락체인에 올라가면 블락체인 상의 모든 노드는 해당 스마트 컨트랙트의 바이트 코드를 가지고 있게 됩니다. 따라서, 각 노드, 이더리움 클라이언트는 해당 스마트 컨트랙트를 자신의 EVM에서 실행시킬 수가 있습니다. 한 노드에서 스마트 컨트랙트의 내용을 변경시키는 명령은 트랜잭션을 발생시켜 내용 변경이 이루어져, 다른 모드 노드가 컨트랙트에 접근하면 바뀐 내용을 얻게 됩니다.


스마트 컨트랙트의 동작 방식이 종결되었나요? 최근에 이더리움 개발서들이 나오다 보니 확실히 이해하는데 좋네요. 자 모두 다같이 이더리움 개발 가즈아~

오늘의 실습: 스마트폰에서는 블락체인 데이터를 모두 저장하긴 어렵습니다. 그렇다면 일종의 서버인 이더리움 클라이언트를 통해서 블락체인 서비스를 이용해야 하는데, 이것은 탈중앙화에 반하는 것일까요?

댓글

댓글 본문
  1. Hyungju Moon
    상태 변수 값을 지우고 고쳐서 바꾸는게 아니라 그 값을 이렇게 수정하겠다는 내용을 새 블록에 남기는것 뿐입니다. (append only) 처음에 설정한 값이 뭐였다는건 이전 해당 블록에 영원히 남아요. 블록에 올라간 정보는 바뀌지 않죠.
    대화보기
    • 테드
      너어어어어어무 감사합니다 진짜로
    • 지니
      여러 책들을 봐도 이해가 어려웠는데 설명의 흐름이 초보자가 이해하기에 딱이에요 ㅠ감사합니다
    • 김경환
      와 이런 면에서는 한국인이라서 너무 좋습니다.
    • 이승호
      블록체인에 직접 기록되는건 변하지 않습니다. 스마트컨트렉트와 상수변수는 초기값이 기록되어 불변하나 스마트컨트렉트 멤버로 포함된 변수의 값은 변경이 가능합니다. 그러나 값을 변경하기 위해 값을 집어넣으면 트랜잭션 코스트와 실행 코스트가 발생합니다.
      대화보기
      • 블록에 포함된 원본의 정보를 수정하는 것이 아니라 원본에 대한 수정 기록을 추가함으로써 수정하는 것으로 보는 것입니다.
        이미 블록에 포함된 원본을 수정하려면 해당 블록부터 새로 마이닝을 해서 현재 마이닝 중인 체인을 따라잡아야 하는데(51% 공격), 이것은 거의 불가능에 가깝기 때문에 수정 기록을 추가하는 형식으로 수정을 허용하는 것입니다.
        대화보기
        • BRuner
          잘 읽었습니다. 그런데 컨트랙트가 블록체인에 올라간 상태에서 설명하신 것처럼 상태 변수 값이 바뀔 수 있잖아요? 이것과 "블록체인에 올라간 정보는 절대로 바뀌지 않는다"는 말이 어떻게 양립될 수 있는지요?
        • 한량관
          감사합니다! 헷갈리던 부분들이 명쾌하게 정리되네요
        버전 관리
        이타인
        현재 버전
        선택 버전
        graphittie 자세히 보기