[BEB] Section3 : 스마트 컨트랙트의 통신 / 인프라 아키텍쳐 CAP 이론과 PACELC 이론 그리고 샤딩

jsg_ko·2022년 1월 13일
0
post-custom-banner

스마트 컨트랙트의 통신

🔥스마트 컨트랙트 생성, 배포, 접근

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

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

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

스마트 컨트랙트 코딩

다음은 스마트 컨트랙트의 간단한 예시이다. 다음처럼 코딩하면, 공용 변수에 값을 변경하고 읽는 기능을 수행할 수 있다.

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;
 }
}

소스 코드 컴파일

위 코드를 솔리티티 컴파일러로 컴파일 하면 아래와 같은 EVM 바이트 코드가 생성된다. 컴파일은 remix나 geth의 solidity 컴파일러로 할 수 있다. Remix와 Geth에 대한 부분은 추후 포스팅 에서 다룰예정이다.

0x6060604052341561000f57600080fd5b60405161046d38038061046d833981016040528080519091019050600081805161003d929160200190610044565b50506100df565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061008557805160ff19168380011785556100b2565b828001600101855582156100b2579182015b828111156100b2578251825591602001919060010190610097565b506100be9291506100c2565b5090565b6100dc91905b808211156100be57600081556001016100c8565b90565b61037f806100ee6000396000f3006060604052600436106100565763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663954ab4b2811461005b578063a4136862146100e5578063ef690cc014610138575b600080fd5b341561006657600080fd5b61006e61014b565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156100aa578082015183820152602001610092565b50505050905090810190601f1680156100d75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100f057600080fd5b61013660046024813581810190830135806020601f820181900481020160405190810160405281815292919060208401838380828437509496506101f495505050505050565b005b341561014357600080fd5b61006e61020b565b6101536102a9565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101e95780601f106101be576101008083540402835291602001916101e9565b820191906000526020600020905b8154815290600101906020018083116101cc57829003601f168201915b505050505090505b90565b60008180516102079291602001906102bb565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102a15780601f10610276576101008083540402835291602001916102a1565b820191906000526020600020905b81548152906001019060200180831161028457829003601f168201915b505050505081565b60206040519081016040526000815290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102fc57805160ff1916838001178555610329565b82800160010185558215610329579182015b8281111561032957825182559160200191906001019061030e565b50610335929150610339565b5090565b6101f191905b80821115610335576000815560010161033f5600a165627a7a72305820352cec017ed93c8351ac6fbc835eda354ea6dbc9e672ae6b60c16f29c49a5cd30029

컴파일된 코드라 기계만 이해할 수 있는 코드이니 모르는게 당연하다. 이 컴파일 된 부분이 코드블록에 배포된다는 것만 알고 있으면 충분하다.

스마트 컨트랙트 배포

컴파일된 바이트 코드를 블록체인에 배포하는 절차이다. Remix로 소스 코드를 컴파일하면, 바이트 코드와 ABI도 자동으로 얻을 수 있다. Geth를 사용한다면, 바이트 코드와 ABI를 일일이 생성해야 한다.

  • ABI 얻기
    ABI는 Application Binary Interface의 약자로, 스마트 컨트랙트에서 사용할 인터페이스의 바이너리 형태를 말한다. Geth의 경우, 이 ABI를 생성해야 한다


    [Geth console 예시]
    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"
    }]
    Geth에서는 이런 과정을 통해 컨트랙트의 ABI를 얻을 수 있다.

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

    [Geth console에서 스마트 컨트랙트 객체 생성 예시]
    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()
    }
  • 스마트 컨트랙트 배포
    앞서 생성한 스마트 컨트랙트 객체를 실제로 블록 내용에 포함시키고, 채굴되어 블록체인에 포함되도록 하는 과정이다. 이 과정이 종료되어야 비로소, 생성한 컨트랙트에 접근할 수 있는 주소가 생성된다. 앞서 생성한 스마트 컨트랙스 객체(객체라고 하지만 클래스와 같이 아직 실제 데이터에 접근할 수 있는 데이터가 없는 상태)로부터 하나의 인스턴스를 만든다. 이 때, 스마트 컨트랙트 최초 배포시에만 생성자에 입력될 매개변수를 넣어준다.
    _greeting="Hello, World"
    그 다음, 매개변수와 함께, 스마트 컨트랙트를 하나의 트랙잭션처럼 생성한다. 이 때, 트랜잭션의 송신자, 바이트 코드, 사용될 예상 gas양을 같이 입력한다. [Geth console에서 스마트 컨트랙트 배포 예시]
    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"
    }

채굴

스마트 컨트랙트 배포 예시의 마지막에는, address: undefined 라는 부분을 볼 수 있다. 이 트랜잭션이 채굴을 통해 블록체인에 등록되지 않았기 때문에, 컨트랙트의 주소값이 비어있는 상태이다. 마이너가 트랜잭션이 포함된 블록을 채굴하게 되면 비로소 스마트 컨트랙트 주소가 생성된다.

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

위 예시에서 address: "0x6f9c338bb987f1baf619697784c9457b9afa119c" 이 부분이, 생성한 스마트 컨트랙트의 주소값이다.

위에서 생성한 스마트 컨트랙트의 트랜잭션 내용을 살펴보겠습니다. 위에서 생성한 트랜잭션의 해시값은 transactionHash:"0x99ddfd763478ce7a0d328fbc67f3c10fec377efa18a8e9c41f61321feb836cd1" 이다. 이 트랜잭션의 내용을, Geth 콘솔에서 확인할 수 있다. 트랜잭션 해시와 스마트 컨트랙트의 주소값은 전혀 다른 값 이다.

아래의 예시를 보면 일반적인 송금과는 달리, 트랜잭션의 input:에 값이 있다. 이 것이 바로 스마트 컨트랙트 소스 코드를 컴파일한 바이트 코드이다. 일반적인 거래에서는 바이트 코드가 필요없기 때문에 input: 0x 로 나타난다. 그리고 수신자가 없다. 즉, 송신자만 있고, 수신자는 없다. 스마트 컨트랙트는 이것을 생성한 송신자가 특정 수신을 지정하지 않고 자신이 생성한계약 코드를 블록체인에 올린 것이기 때문에 당연한 현상이다

> 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
}

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

[Geth console에서 배포된 스마트 컨트랙트가 포함된 블록 정보 예시]

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: []
}

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

앞서 생성한 스마트 컨트랙트가 블록체인에 올라갔으니, 이제 스마트 컨트랙트 주소를 이용하여 정보를 읽고 쓰고 할 수 있다. 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"

🔥스마트 컨트랙트 호출

이더리움에서 스마트 컨트랙트는 새로운 스마트 컨트랙트를 생성하거나, 특정 스마트 컨트랙트상의 함수를 실행하거나, 이더를 전송하는 방식중의 하나로 실행이 된다. 또한 사용자 어카운트(EOA)에 의해서 발생한 트랜잭션이나 다른 컨트랙트에 의해서만 실행된다.

또 무한반복같은 악의적인 코드를 막고, 데이터의 무결성를 지키기 위해 모든 트랜잭션을 실행할 때 해당 트랜잭션의 실행 비용을 지급하도록 규정하고 있다. 그리고 모든 트랜잭션의 기본 실행 비용은 21,000 가스이다. 트랜잭션 실행 비용에는 발송자 어카운트 주소에 대한 ECDSA(Elliptic Curve Digital Signature Algorithm)를 위한 비용과, 트랜잭션 저장을 위한 스토리지 비용, 네트워크 대역폭 비용이 포함된다. 이렇게 스마트 컨트랙트 실행시 비용을 지불하도록 정의하여, 디도스 공격 같은 무한 실행 공격처럼, 악의적인 의도로 컨트랙트를 실행하는 걸 방지할 수 있다.

메시지 구조체

스마트 컨트랙트간의 호출은 메시지라는 특별한 구조체를 사용하여 호출된다. 메시지는 외부 어카운트(EOA)가 아니라 컨트랙트 어카운트(CA)에 의해서만 생성되며, 함수 호출시에 다른 컨트랙트로 전달된다. 메시지는 트랜잭션과는 달리 EVM 내부에서만 존재하기 때문에 가스비용이 발생하지 않는다.

다음은 메시지의 구조이다. 메시지는 EVM내에서 컨트랙트를 실행하기 위해서 Call , CallCode , DelegateCall , StaticCall 등이 호출될때에 생성된다. 이들 Call 코드들은 공통적으로 컨트랙트 주소를 매개변수로 전달받아 이를 실행하고 처리한다.

[코드] Message 구조 예시

type Message struct {
	to         *common.Address // 메시지 수신처
	from       common.Address // 메시지 발신처
	nonce      uint64 // 거래 실행시 수행되도록 허용된 최대 트랜잭션 수행횟수
	amount     *big.Int // 메시지와 함께 전달되는 이더(wei 단위)
	gasLimit   uint64 // 트랜잭션 수행시 소비될 총 가스량에 대한 추정치
	gasPrice   *big.Int // 가스 가격
	data       []byte // 매개변수 전달시 사용되는 데이타 필드(Optional)
	checkNonce bool
}

다음과 같이 Call , CallCode , DelegateCall 이 호출될때에 메시지가 만들어진다

contract D {
  uint public n;
  address public sender;
  function callSetN(address _e, uint _n) {
    _e.call(bytes4(sha3("setN(uint256)")), _n); // E's storage is set, D is not modified
  }
  function callcodeSetN(address _e, uint _n) {
    _e.callcode(bytes4(sha3("setN(uint256)")), _n); // D's storage is set, E is not modified
  }
  function delegatecallSetN(address _e, uint _n) {
    _e.delegatecall(bytes4(sha3("setN(uint256)")), _n); // D's storage is set, E is not modified
  }
}
contract E {
  uint public n;
  address public sender;
  function setN(uint _n) {
    n = _n;
    sender = msg.sender;
    // msg.sender is D if invoked by D's callcodeSetN. None of E's storage is updated
    // msg.sender is C if invoked by C.foo(). None of E's storage is updated
    // the value of "this" is D, when invoked by either D's callcodeSetN or C.foo()
  }
}
contract C {
    function foo(D _d, E _e, uint _n) {
        _d.delegatecallSetN(_e, _n);
    }
}

이더리움에서 스마트 컨트랙트는 Solidity 언어로 프로그래밍한다. solidity 언어로 프로그래밍된 스마트 컨트랙트는 컴파일러(solc)에 의해 바이트 코드로 컴파일되고, 컴파일된 바이트코드는 블록에 포함되어, EVM(Ethereum Virtual Machine)에 의해 실행된다.

EVM

이전 포스팅에서 다뤘던 바와같이 EVM(Ethereum Virtual Machine)은 이더리움 스마트 컨트랙트의 바이트 코드를 실행하는 32바이트 스택 기반의 실행환경으로 스택의 최대 크기는 1024바이트이다. 이더리움의 각 노드는 EVM을 포함하고 있으며, EVM을 통해 바이트 코드를 OP코드로 변환하고 스택기반으로 각각의 OP코드를 실행한다.

EVM은 휘발성, 비휘발성 메모리로 구성되어 있으며, 여기에 바이트 배열 형태로 스택의 항목들을 저장한다.

비휘발성 메모리(non-volatile)

  • storage : 상태(state)가 저장
  • code : 스마트 컨트랙트의 컴파일된 바이트 코드가 저장

휘발성 메모리(volatile)

  • stack : OP 코드를 실행하기위한 스택영역
  • args : 컨트랙트 호출시에 넘어오는 인자를 저장
  • memory : word 단위로 아이템을 저장하는 바이트 배열

EVM은 바이트코드를 내부 OP 코드로 재해석한다. 즉, solidity 언어로 개발된 스마트 컨트랙트의 컴파일된 바이트 코드는 EVM에서 OP 코드로 치환하여 실행한다.

EVM 동작 예시

1+2 를 계산하는 바이트 코드를 가지고 EVM의 동작을 알아보도록 하자.1+2 를 계산하는 코드의 바이트코드는 6001600201 이다. 이 바이트코드를 치환하여 OP 코드로 분리하면 0x60, 0x01, 0x60, 0x02, 0x01 이다.

OP 코드표를 참고로 하면, 0x60 은 PUSH OPCODE를 의미하고, 0x01, 0x02는 값 1, 2를 의미하고, 마지막 0x01ADD OPCODE 를 의미한다. 즉 EVM의 스택에 1,2를 Push하고 Add 연산을 수행하라는 의미이다.

🔥노드와 통신

이 챕터에선 콘텐츠에서는 노드끼리의 통신을 다룬다.

P2P(Peer to Peer)

P2P는 인터넷에 연결된 다수의 개별 사용자들이 중개기관을 거치지 않고 직접 데이터를 주고받는 것으로, 네트워크에 중앙 컨트롤러가 없고, 모든 참여자가 서로 직접 소통한다. P2P 방식은 기존의 서버-클라이언트 방식의 데이터 전송과는 본질적으로 다른 구조를 가지고 있다. 서버-클라이언트 구조에서 개별적인 참여자는 우선 서버에 데이터를 올려야 하고, 다른 참여자가 해당 서버로부터 데이터를 받아오는 방식으로 작동한다. 그러나 P2P에서는 모든 노드가 클라이언트이면서, 동시에 서버의 역할을 수행한다.

💡 블록체인은 P2P 방식으로 운영된다.

  • P2P 구조의 확장성

기존의 클라이언트-서버 모델은, 종단의 노드의 수가 늘어날수록 최소 분배 시간이 선형적으로 증가한다. 기존의 모델과 비교하여, P2P 파일 시스템은 노드가 확장되더라도 최소 분배 시간이 로그함수 형태로 증가합니다.

분배 시간(distribution time)은 모든 N개의 피어들이 파일의 복사본을 얻는 데 걸리는 시간이다. 최소 분배 시간은 모든 피어가 파일의 복사본을 얻는 데에 필요한 최소한의 값을 말한다. 분배 시간이 작을수록, 모든 피어가 동일한 파일의 복사본을 가지는 시간이 적게 들고, 따라서 하나의 파일을 모든 피어가 가지는 속도가 빨라진다.

P2P 파일 시스템으로는 비트토렌트가 가장 대표적이다.

노드와 클라이언트간 통신 JSON - RPC

RPC(Remote Procedure call)

RPC(Remote Procedure call)란, 별도의 원격 제어를 위한 코딩 없이 다른 주소 공간에서 리모트의 함수나 프로시저를 실행 할 수 있게 해주는 프로세스간 통신이다.

RPC 모델은 분산컴퓨팅 환경에서 많이 사용되었으며, 현재에는 MSA(Micro Software Archtecture)에서 마이크로 서비스 간에도 많이 사용되는 방식이다. 서로 다른 환경이지만 서비스간의 프로시저 호출을 가능하게 해줌에 따라, 언어에 구애받지 않고 환경에 대한 확장이 가능하며, 좀 더 비지니스 로직에 집중하여 생산성을 증가시킬 수 있다.

JSON-RPC API

JSON-RPC는 무상태성(stateless)의 경량 RPC(원격 프로시저 호출) 프로토콜이다. Spec(사양; specification)에는 여러 데이터 구조와 처리 규칙을 정의하고 있다. 소켓 통신이나 HTTP 등 다양한 메시지 전달 환경에서 사용할 수 있도록 추상화되어 있다. 이 프로토콜은 JSON(RFC 4627)의 데이터 형식으로 사용한다.

JSON-RPC API를 통해 이더리움 노드와 직접 상호작용할 수도 있다. 그러나 dapp 개발자를 위한 더 편리한 옵션도 있다.이미 자바스크립트와 백엔드를 위한 API 라이브러리가 많이 존재한다. 이런 라이브러리를 통해 개발자는 (원리를 잘 모르더라도), 직관적인 한 줄의 메서드로 JSON-RPC API를 사용할 수 있다.

Web3.js는 대표적인 이더리움 자바스크립트 API 이다.

인프라 아키텍처

인프라란?

일상생활에서 사용되는 구글, 유튜브 등등 이런한 서비스를 어디에서 방대한 데이터를 주고 받을까? 그리고 이런 데이터는 어떻게 처리해서 우리에게 보여줄까? 이런 것들을 처리하고 보여주고 관리하는 환경을 인프라라고 한다.

아키텍처란?

아키텍처란 무엇일까? 구조를 뜻한다. 위에서 언급한 인프라에서 데이터를 관리하고 처리하는 구조에는 무엇이 있을까?4가지로 나눌 수 있다.

https://velog.io/@ssdii44/1장.-인프라-아키텍처#인프라란

🔥CAP 이론과 PACELC 이론

CAP 이론은 분산 데이터베이스 시스템을 바탕으로, 네트워크 분할 허용(Network Partition)이 이루어지는 경우에 일관성(Consistency), 가용성(Availability)을 동시에 만족하는 것은 불가능하며, 이들 사이에 양자택일해야 한다는 이론이다.

CAP 이론에 따르면 모든 분산 네트워크는 일관성을 지키는 상황 (CP)과 가용성을 지키는 상황(AP)으로 나눌 수 있다고 보았다.

모든 노드는 데이터를 업데이트하는 과정에서 다른 작업이 불가능하다. 따라서 업데이트가 진행되는 시간에는 일시적으로 데이터베이스에 접속할 수 없게 되어 서비스가 중지된다. 모든 노드에 정보를 업데이트하여 일관성을 지키되 가용성을 포기하는 게 맞을까? 혹은 일부 노드에만 정보를 업데이트하여 가용성을 지키되 일관성를 포기할 것인가? 에 대한 문제가 CAP 이론이다.

CAP 요소

  • 네트워크 분할 허용(P; Partition Tolerance)
    노드 간의 통신이 실질적으로 어려운 상태에 도달하여 네트워크가 사실상 분할(Partition)되었을 때도 네트워크가 정상적으로 작동할 수 있는 경우를 의미한다

    네트워크 분할 허용을 전제하는 네트워크는 노드 간의 통신이 완전히 불가능하거나 비동기성 네트워크 (asynchronous network)에서 메시지가 유실될 수 있고, 현실성이 떨어지는 시간에 메시지가 전달될 수 있다. 이러한 상황에서도 사용자입장에서는 마치 하나의 분산 네트워크가 여러 가지의 노드로 나뉘었다는 사실을 인지못하는것 처럼 문제 없이 네트워크를 이용할 수 있어야 한다. 분산 네트워크가 단일 네트워크처럼 보여야 유의미하다고 말할 수 있다.

  • 일관성(C; Consistency)
    모든 요청이 모든 시스템에서 하나의 동일한 상태(the same state)로서 같은 논리적 시간(logical time)에 업데이트되어 최신 데이터 또는 에러를 응답 받는다면 일관성이 지켜졌다고 볼 수 있다.

    즉, 한 노드에서 특정 데이터의 처리가 완료되었을 때 다른 노드가 동시 다발적으로 업데이트된 정보에 접근할 수 있어야 한다는 것이다. 따라서 Lineraizability(선형화가능성) 는 매우 강력한 형태의 일관성을 요구한다.

    💡 이해를 돕기 위한 Not Linearizable한 시스템을 시나리오
    철수는 근처 펍에 가서 2018 월드컵 조별리그, 한국 대 독일의 경기를 보고있다.
    영희는 한국이 이기지 못할 것으로 생각하고, 학교에서 시험 공부를 하고있다.
    경기가 끝나고 한국이 2:0 으로 이겼다.
    철수는 감격한 나머지 포털 사이트에 접속해서 경기 결과를 재확인했다.
    철수는 영희에게 카카오톡으로 메시지를 보내 경기 결과를 알려주었다.

    영희는 포털 사이트에 접속했지만.영희가 접속한 포털 사이트에는 여전히 게임이 진행되고 있다라는 표시가 나타난다. 철수는 최신 업데이트가 된 정보를 받을 수 있었다. 하지만 영희는 최신 업데이트가 된 정보를 받지 못했다.
    즉, 가용성을 우선시하고 Linearizable 한 일관성을 포기한 것이다.

      
  • 가용성(A; Availability)
    분산 시스템이 지속해서 가용하기 위해서 장애 없는(non-failing) 노드에 수신된 모든 요청은 반드시 응답돼야 한다.

    즉, 오류가 없는 노드가 발송한 메시지는 오류가 없는 모든 노드로부터 응답을 받을 수 있어야 한다. 데이터 저장소에 대한 모든 동작이 오류가 없는 모든 노드로부터 성공적으로 리턴을 받아야 한다는 것 이다. 따라서 오류가 있는 노드로부터는 응답을 받을 필요가 없다. 정상 노드만 응답해도 시스템은 가용이 가능하다고(Available) 말할 수 있기 때문이다.

    💡 CAP 이론에서 의미하는 가용성은 일반적인 가용성과 의미가 다르다
    CAP 이론에서는 모든 노드로부터 응답을 받을 수 있어야 하는 반면에 일반적인 가용성에선 정상적으로 작동하고 있는 노드 중 일부만 응답을 받을 수 있어도 가용성이 있다고 본다.
    또 일반적인 가용성은 현실적인 응답시간(realistic response time)을 고려하지만 CAP 이론의 가용성은 응답시간에 대한 제한을 걸어두지 않는다는 문제가 있다. 아무리 늦게 응답을 보내도 받기만 하면 충분하다는 것 이다.

CAP의 상관관계

  • CA: 일관성과 가용성을 충족하지만 분할 허용을 충족하지 못한다. 데이터에 대한 강한 신뢰를 부여할 수 있지만, 네트워크 문제가 시스템의 중단을 야기할 수 있다. 시스템 중단은 치명적인 문제이기때문에 분할허용은 무조건 충족해야한다
  • CP(ConsistencyPartition Tolerance): 일관성과 분할 허용을 충족하지만 가용성을 충족하지 못한다. 높은 확장성으로부터 성능을 확보할 수 있지만 일부 데이터가 비가용할 수 있다.
  • AP(Availability-Partition Tolerance): 가용성과 분할 허용을 충족하지만 일관성을 충족하지 못한다. 부정확한 응답을 허용하는 고성능 시스템에 적합하다.

CAP 관점에서의 블록체인

블록체인에서 네트워크 분할은 분기라는 논리적 사건을 이유로 발생하기도 한다.

비슷한 시점에 같은 높이의 블록이 채굴되거나 새로운 원장이 제시될 경우 분기가 발생함

그러나 시간이 지남에 따라 포크 선택 규칙(합의알고리즘)에 의해 하나로 합의하게 된다 이 점을 미루어 보아 CAP 관점에서의 블록체인은 가용성과 분할 허용을 충족하는 AP 시스템이다.

통상의 AP 시스템은 고성능을 위하지만 블록체인 시스템은 자가 제한을 통해 성능을 제한한다는 점이 특이한 점이다. 대신 이를 통해 과거 블록의 위/변조를 어렵게 만들고 궁극적 일관성을 확보했다.

PACELC 이론

CAP 이론에 따르자면 일관성과 가용성은 완전한 대립 관계에 있지만, 사실 완벽한(perfect) CP 시스템과 AP 시스템은 쓸모가 없다. 그리고 완벽한 CA 시스템은 가질 수 없습니다.

  • 완벽한 CP: 완벽한 일관성을 갖는 분산 시스템에서는 하나의 트랜잭션에 대한 복사본을 모든 노드가 가져야 한다. 시스템의 전체 성능은 네트워크에서 가장 낮은 성능의 노드에도 종속되며, 만일 단 하나의 노드라도 문제가 발생하면 트랜잭션 처리는 실패한다. 또한, 노드의 수가 많아질수록 지연시간이 길어진다.
  • 완벽한 AP: 완벽한 가용성을 갖는 분산 시스템은 네트워크 분할로 인해 고립된 노드가 발생하더라도 서비스를 제공한다. 고립된 노드는 업데이트를 반영하지 못해 일관성이 결여된 잘못된 정보를 가지고 있지만, 완벽한 AP 시스템에서는 어찌 됐건 가용하다. 사용자는 잘못된 정보를 받고 활용하게 된다.
  • 완벽한 CA: 완벽한 CA 시스템을 위해선 절대로 장애가 발생하지 않는 네트워크를 구성해야 한다. 앞서 살펴봤듯이 이는 불가능한 일이다.

실제로는 일관성과 가용성의 절충안에 해당하는 연속적인 중간 영역이 존재한다. 이러한 중간 영역을 고려하고 네트워크의 정상 상황과 분할 상황을 모두 담아낼 수 있는 표현법으로 Daniel Abadi가 제시한 PACELC 이론이 있다.

[그림] 네트워크 상황을 중심으로 한 PACELC 이론의 다이어그램. 붉은색 선은 시스템의 정도를 나타낸 것이다.

  • 네트워크 분할 상황: 분할 상황(P)에서는 단절된 노드에 접근할 수 없기 때문에 일관성을 포기하고 가용성을 제공할지(PA),가용성을 포기하고 일관성을 유지할지(PC)의 정도를 결정해야 한다.
  • 네트워크 정상 상황: 정상 상황(E)에서는 모든 노드에 업데이트를 반영해 일관성을 유지하기 위한 긴 대기 및 응답시간을 가질지(EC), 일관성을 포기하고 짧은 지연시간을 가질지(EL)의 정도를 결정해야 한다.

🔥샤딩

💡 샤딩은 조각내다"라는 뜻

샤딩(sharding) 데이터 저장방법중 하나로, 단일의 데이터를 다수의 데이터로 쪼개어 나누는 걸 말한다, 단일의 데이터베이스에서 저장하기 너무 클 때 사용하여 데이터를 구간별로 쪼개어 나눈다. 이를 통해 노드에 무겁게 가지고 있던 데이터를 빠르게 검증할 수 있어, 트랜잭션 속도를 향상시킬 수 있다. 샤딩을 통해 나누어진 블록들의 구간(epoch)을 샤드(shard)라고 부른다.

블록체인에서는 검증자를 소규모 그룹으로로 분리해 각 그룹이 서로 다른 트랜잭션을 동시다발적으로 처리하는 방식이다. 즉, 하나의 데이터를 샤드라는 단위로 조각내고, 네트워크를 통해 분산하여 저장해(수평 분할) 이를 병렬적으로 처리하여 블록체인에 확장성을 부여한다.

만약 10만큼의 데이터와 10명의 노드가 참여했다고 가정한다면 기존의 블록체인은 10명의 노드 개개인은 10만큼을 모두 가지고 있으면서 공유한다. 그러나 샤딩은 10을 조각내서 10명의 노드 개개인은 1만큼씩만 보관함으로써 보관 데이터가 가벼워져 거래처리 속도가 크게 향상된다.

샤딩의 문제점

블록체인 시스템의 확장성 문제를 해결하기 위해 등장한 기술이지만, 샤딩을 적용할 경우 한 샤드 내에서의 전송이 아닌 여러 샤드 간의 전송은 절차가 훨씬 복잡하고 느려진다. 여러 샤드로 쪼갤수록 인터샤드 트랜잭션은 확률적으로 많아지는데 각 샤드는 자기 샤드의 데이터만 있고, 다른 샤드의 데이터는 가지고 있지 않으므로 샤드간 데이터를 어떻게 참조할 것인지, 어떻게 검증할 것인지 문제가 생겨서 알고리즘이 복잡해진다.

그리고 샤드마다(분할된 네트워크=노드 그룹) 트랜잭션의 빈도, 노드의 수, 밸리데이터의 비율 등에서 차이가 나기 때문에 한 번 샤드가 정해진 다음에도, 샤드의 구성원을 재배치하여 샤드간 균형을 맞추는 알고리즘이 필요하다. 뿐만 아니라, 예상했던 것보다 전체 트래픽이 높아질 경우, 처음 설정해둔 샤드의 수를 늘릴 필요가 있다. 한 번 쓰여진 데이터를 해쉬나 서명으로 묶어 위변조나 조작을 하지 못하도록 만들어진 분산 원장 구조에서는 샤드를 중간에 추가하는 등의 동적 샤딩 기술을 구현하기가 매우 어렵다.

블록체인에서 샤딩의 특징

공개형 블록체인의 맥락에서, 네트워크에 올려진 트랜잭션은 네트워크상의 서로 다른 노드들로 이루어진 여러 샤드(shards)로 분할된다. 각각의 노드는 들어오는 트랜잭션들의 일부만을 처리할 수 있게 되고, 네트워크상에서 병렬식으로 다른 노드들에서도 똑같이 실행된다. 따라서, 네트워크를 여러 샤드로 쪼개면 동시에 더 많은 트랜잭션을 처리하고 증명할 수 있어 네트워크가 커질수록 점점 더 많은 트랜잭션 처리가 가능하게 되는 속성을 가진다. 이 속성을 병렬식 확장(horizontal scaling)이라고도 한다.

네트워크가 확장할수록 처리량이 증가하면, 샤딩의 이 독특한 특성은 블록체인의 빠른 채택을 촉진하는 이상적인 촉매제가 될 수 있다.

이더리움에 샤딩 적용

  • 이더리움 샤딩
    이더리움 2.0에서 도입될 예정인 이더리움 샤딩은 메인 체인이 처리해야 할 블록들을 조각내어 샤드(Shard)라고 불리는 오프 체인(off-chain)에 할당하는데 오프 체인들은 주어진 조각에 대해서만 유효성을 검증하게 되고, 샤드들이 각자 할당된 조각들의 검증을 모두 끝내면 다시 묶어 이러한 개념을 통해 블록을 검증하는 데 걸리는 시간을 단축한다.

    이더리움의 블록 생성 속도는 13초 이상 걸리고 있지만, 샤딩이 도입된다면 약 2초에서 8초 사이에 생성될 것이다. 샤드별로 Merkel Patricia tree를 만들고 그 샤드의 root들로 만들어진 Merkel Patricia tree의 root만을 블록체인에 올리는 것으로 모든 miner가 모든 트랜잭션을 실행할 필요 없이 샤드별로 miner를 분산 시켜 실행할 수 있기 때문에 전체 실행 속도가 올라간다.
profile
디버깅에서 재미를 추구하면 안되는 걸까
post-custom-banner

0개의 댓글