336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
지난 포스트에선 이더리움 스마트 컨트랙트의 출력 수단 중 하나인 이벤트에 대해 알아 보았습니다. 호출된 이벤트는 로그라는 이름으로 Transaction Receipt에 남아 이더리움 블록체인에 기록됩니다. 블록체인에 기록된 것은 블록을 다운로드 받을 수만 있다면 항상 접근할 수 있기 때문에 일종의 스토리지라고 생각할 수도 있습니다. 그러나 이 스토리지는 무한정 쓸 수는 없습니다. Gas라고 불리는 것에 의해 쓰는 만큼 비용을 내야 하고, 트랜잭션 당 쓸 수 있는 양에 제한이 있기 때문입니다.


Gas란?
Gas(이하 가스)는 이더리움이라는 거대한 컴퓨터를 쓰기 위한 일종의 연료입니다. 수수료는 가스 사용량과 가스 가격의 곱으로 결정됩니다. 이전 포스트에서 Solidity가 EVM이라는 스택머신의 바이트코드로 컴파일되고, 이더리움은 그 바이트코드를 실행한다고 언급했었습니다. EVM Opcode 마다 정해진 가스 사용량이 있습니다. 예를 들면 ADD 연산에 3가스, 곱하기 연산에 5가스, 트랜잭션에 21000가스, log 출력 시 바이트당 8가스 등등 입니다. 트랜잭션에서 발생한 모든 Opcode의 가스 사용량을 합하고 사용한 양에 1가스에 지불할 이더를 곱하면 해당 트랜잭션의 수수료가 계산됩니다.

트랜잭션 수수료 = 가스 사용량 * 가스 가격



Opcode에 따른 가스 사용량은 버전에 따라 조금씩 다르지만 중요한 것은 블록에 무언가 기록하는 Opcode가 비싸다는 것입니다. 블록에 기록된 내용은 이더리움의 모든 풀 노드가 저장해야 하기 때문입니다. 따라서 Solidity를 EVM 바이트코드로 변환할 때 중요한 것은 실행 속도가 아닙니다. 실행이 느리게 되더라도 바이트코드 사이즈를 줄여서 배포 시 발생하는 수수료를 줄이고, 가스 비용이 비싼 특정 Opcode들을 최소화하는 것이 좋겠지요.


Gas 가격의 결정과 지불
Gas 가격은 트랜잭션을 생성하는 사람이 1가스 당 지불할 wei를 결정합니다(wei는 현재 이더의 최소 단위입니다). 마이너가 트랜잭션을 처리하고 블록을 마이닝하면 그 블록에 수수료 지불내역이 남습니다. 

현재 가스의 적정 시세는 1 가스 당 40 Gwei(= 40000000000 wei = 0.00000004 ETH) 입니다. 트랜잭션에 21000 가스가 발생하니 기본 트랜잭션에 0.00084 ETH가 필요합니다. 1 ETH를 100만원으로 계산하면 기본 트랜잭션에 840원이 수수료로 발생하는 셈입니다. 송금앱들이 500원 수수료를 요구하는 것에 비해 더 비싸지만 비트코인 트랜잭션 수수료에 비하면 훨씬 합리적이네요.

가스 시세를 보여주는 ethgasstation 사이트에 따르면 1 가스당 40 Gwei를 지불하면 블록에 포함되는 시간이 1분 이내인 반면에 38 Gwei를 지불하면 약 30분 정도 걸릴 수 있다고 합니다.
https://ethgasstation.info/

트랜잭션을 생성할 때 가스 가격(GasPrice)과 같이 가스 제한(GasLimit)을 정해두어야 합니다. EVM은 튜링머신이기 때문에 실행해보기 전까지는 트랜잭션 처리가 종료될 지 알 수 없습니다. 가스 제한이 없다면 무한 루프 등으로 처리가 끝나지 않는 트랜잭션을 발생시켜 마이너들에게 DoS 공격을 가할 수 있습니다. 만약 트랜잭션이 제한된 가스 사용량을 넘기게 되면 그 트랜잭션은 실패된 채로 수수료가 지불됩니다. 가스 제한은 트랜잭션 뿐만 아니라 블록에도 존재하기 때문에 누군가 수수료를 많이 낼 수 있다고 해서 가스를 마음껏 쓸 수 있는 것은 아닙니다.

비트코인의 스크립트 언어가 튜링 완전하지 않기 때문에 종료가 보장되어 이러한 Gas 개념이 필요하지 않습니다.


Solidity 프로그래밍 내 Gas 제어 이슈
스마트 컨트랙트 프로그래밍에선 이 Gas 개념 때문에 고려해야 할 점들이 있습니다.

Solidity 프로그래밍 (1) 포스트에서 언급한 것과 같이 이더를 보낼 때 다른 Contract의 함수를 호출하게 되는 경우도 있습니다. 이더를 보낼 때 call을 호출해서 현재 내가 쓰고 남은 가스를 보낼 수 있는 방식과 send 혹은 transfer를 호출해서 남은 가스를 보내지 않는 방식을 구분해서 써야 합니다. 외부의 임의의 Contract가 악의적인 코드일 가능성을 염두에 두어 Gas를 필요한 만큼만 주는 것이 바람직합니다. 아래 링크는 Solidity 공식 문서의 re-entrancy 예제입니다.


6번 라인의 msg.sender.call.value(x)()는 x만큼 msg.sender에게 이더를 보내고 남은 가스를 넘겨줍니다. 만약 msg.sender가 악의적으로 작성된 스마트 컨트랙트일 경우 withdraw()를 다시 호출하는 re-entracy 공격을 할 수 있는데, 이 경우 코드에 따르면 msg.sender에게 계속 이더를 보내게 되고, 가스가 부족해지거나 Fund에 잔고가 다 떨어졌다고 판단되면 더 이상 withdraw()를 호출하지 않고 정상 종료하여 트랜잭션을 성공시킬 수 있습니다. 360만 이더가 탈취된 DAO 해킹이 이 취약점이 공격된 사례입니다.



두 번째는 Solidity 공식 문서의 Ballot 예제를 일부 따왔습니다. 코드 전체를 이해할 필요는 없고, 동적 길이의 배열과 그 배열 크기만큼 반복하는 for문에 대한 얘기입니다. winningProposal()이 view function 이여서 적합한 예제는 아니지만, 어느 정도 상상력을 가지고 보시면 되겠습니다. 누군가 ProposalNames를 길게 하여 여러번 Ballot을 호출했다고 가정해봅시다. 그 다음 winningProposal()을 호출하면 상당히 긴 for문을 돌아야 할 지도 모릅니다. 이 때 문제는 proposals의 길이를 줄일 수 있는 코드가 없고, 이미 길어진 proposals를 처리하는데 들어가는 가스가 블록 가스 제한을 넘어버리면, winningProposal()은 더 이상 호출할 수 없는 코드가 되어버립니다. DoS 공격이 가능해지는 것이지요.





이더리움의 가스와 관련된 내용에 대해 알아봤습니다. EVM이 가진 튜링완전 특성으로 인해 불가피하게 도입되었을텐데, 스마트 컨트랙트 프로그래밍 입장에선 상당히 저수준의 내용을 염두에 두고 있어야 해서 불편한 부분이기도 합니다. 다음 포스팅에서는 MetaCoin이라는 예제를 기반으로 이더리움 기반의 코인이란 어떤 것인지 알아보도록 하겠습니다.


336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


지난 포스트에서는 이더리움이 추상적으로 어떻게 동작하는지와 컨트랙트의 생성, 함수 호출에 대해 알아보았습니다. 이번 포스팅에서는 이벤트의 개념과 사용, 블룸 필터 자료구조를 소개하겠습니다. P2P 네트워크 상에는 나와는 무관한 트랜잭션들이 떠돌아(?) 다니기 때문에 내가 관심있는 트랜잭션만 효과적으로 식별하기 위해 블룸 필터를 사용하고, 비트코인에서도 마찬가지의 이유로 사용됩니다.


이벤트

이전 예제에서는 이더를 주고 받을 수 있는 컨트랙트 예제를 설명드렸습니다. 프로그래밍을 하는 관점에서 봤을 때, 스마트 컨트랙트의 출력(output)은 이더입니다. 그러나 이더를 보내는 것은 제한된 용도의 출력입니다. 오늘 설명드릴 내용은 Event라고 하여 스마트 컨트랙트의 다른 출력 수단입니다.


Event(이하 이벤트)란 트랜잭션 내에서 호출될 수 있는 일종의 리턴값이 없는 함수입니다. 이벤트를 호출하면 그 호출한 기록이 Transaction Receipt라 불리는 트랜잭션 결과에 저장됩니다. 일종의 로그(log)입니다. 기술적인 상세는 뒤에서 설명하고 우선 코드 예제를 보겠습니다.



이더를 받는 폴백 함수에서 누가 얼마를 보냈는지를 기록하는 컨트랙트입니다. 지난 시간에 소개한 것과 같이 위와 같이 한 번 배포된 컨트랙트는 내용이 바뀔 수 없습니다. 위의 컨트랙트에 돈을 보내면 태웠다는 로그만 남으며 보낸 이더는 꺼낼 수 있는 방법이 없습니다. 돈이 태워진 것입니다.


위의 로그가 발생했다는 사실은 아래처럼 읽을 수 있습니다. 아래는 web3.js 라이브러리를 사용한 자바스크립트 예제입니다.




코드에 대해 부연설명을 하면 web3.js는 이더리움 노드의 RPC 함수들을 자바스크립트로 제어하기 위한 라이브러리입니다. abi 변수는 컨트랙트의 인터페이스 데이터입니다. 한 번 컴파일되어 배포된 코드와 그 입출력은 결국 ByteArray이기 때문에 그 데이터를 해석할 수 있는 스키마가 필요합니다. ABI에 대한 자세한 내용은 아래 링크에서 확인할 수 있습니다.


https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI


저 코드를 이용해서 누가 제일 많이 태웠는지 보여주는 웹 페이지를 만드는 등을 상상해볼 수 있겠죠. :) 그런데 이더리움 네트워크에는 하루에도 수만 개의 트랜잭션이 발생하는데, 이 트랜잭션에서 내가 원하는 이벤트가 발생했다는 사실을 어떻게 알고 콜백을 받을 수 있을까요? 모든 트랜잭션의 로그를 들여다보는 것은 매우 비효율적입니다.


Topic과 Bloom Filter

이벤트 호출을 Topic으로 변환하고 그 토픽을 블룸 필터로 만듭니다. 현재 프로토콜 상 토픽은 4개로 제한되고, 첫 번째 토픽은 이벤트 시그니처를 해시하여 만듭니다.


SHA-3("burned(address,uint)") = 0x0970ce1235167a71...


이렇게 생성된 토픽들을 블룸 필터로 만듭니다.




블룸 필터에 대해 위의 그림 예제로 간단히 설명하겠습니다. x를 삽입할 때 x를 서로 다른 3개의 해시 함수로 얻은 값을 포인터로 사용해 해당 비트를 1로 표시합니다(파란색 화살표). 이렇게 한 뒤 x의 삽입 여부를 알고 싶다면 x를 다시 해시하여 해당 비트가 모두 1인지 여부를 확인하면 됩니다. 물론 해시 충돌로 인해서 False Positive 응답이 나올 수 있습니다.


블룸 필터에 데이터 w가 있었는지 여부를 물으면 대답은 두 가지입니다.

- "w가 있을 수도 있다"

- "w는 없다"


이더리움 노드는 트랜잭션을 실행한 결과를 블룸 필터를 만들고, 수십 개의 트랜잭션을 모아서 블록으로 만들 때에도 각 트랜잭션의 블룸 필터를 모아 하나로 만듭니다. 새로운 블록이 만들어졌을 때 내가 관심있는 이벤트가 발생했는지 여부를 알고 싶으면 해당 이벤트의 Topic을 블록 헤더의 블룸 필터에 조회해보면 됩니다. 만약 없다면 해당 블록에 이벤트가 발생하지 않았음을 바로 알 수 있습니다.


앞서 토픽은 4개로 제한되어 있다고 했습니다. 이벤트 시그니처를 제외한 나머지 토픽은 Indexed Parameter로 지정할 수 있습니다. 위의 예제를 확장하여 다시 코드를 보여드리겠습니다.



파라메터 타입 뒤에 "indexed" 키워드가 추가되었습니다.



web3.js 코드에는 callback 인자 앞에 { amount: 10 } 을 넘겨주고 있습니다. 이는 amount 값이 10인 로그만 콜백으로 받겠다는 의미입니다. 마찬가지로 who 에도 indexed를 주었기 때문에 특정 어카운트가 돈을 태웠는지를 이벤트로 받을 수도 있습니다.


Solidity의 기본형들은 32바이트인 토픽 사이즈 내에 들어오기 때문에 해시를 하지 않고, 배열의 경우 해시값이 토픽으로 들어갑니다. 일반적으로 배열이 토픽이 되는 경우는 없지만, 배열이 토픽으로 들어갈 경우 트랜잭션 결과(Receipt)에 로그가 저장될 때 배열이 복사되는게 아닌 해시값이 들어갑니다. 이 경우 원본 데이터가 날아가는 것은 아닙니다. 해당 트랜잭션을 내 이더리움 노드가 실행하여 재생산할 수 있기 때문입니다.


Topic은 4개로 제한되어 있기 때문에 indexed는 3개까지만 지정할 수 있습니다. 예외적으로 이벤트 시그니처를 Topic으로 취급하지 않는 익명(anonymous) 이벤트는 indexed 파라메터를 4개까지 가질 수 있습니다.


정리

지금까지 Solidity의 이벤트에 대해 다소 자세히 살펴봤습니다. 이더리움 네트워크는 스마트 컨트랙트를 실행하기 위한 단 한 대의 컴퓨터라고 생각할 수 있습니다. 그 컴퓨터의 출력은 이더를 송금하거나, 이벤트를 발생시키는 것입니다. 단 한 대만 존재하는 컴퓨터이기 때문에 이벤트를 무한히 발생시켜서는 안되기 때문에 트랜잭션 실행 비용을 받습니다. 다음 포스팅에선 Gas에 대해 알아보도록 하겠습니다.





336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

비트코인 포크에 대해 이야기 해보려 합니다.




비트코인 포크가 발생하는 이유는 다양하게 얽혀져 있습니다만, 의도를 구분하자면 두 가지로 나눌 수 있을 것 같습니다.


- 더 나은 프로토콜이 필요해서

- 경제적 이득을 취하기 위해


비트코인이란 것은 결국 프로토콜입니다. 예를 들어 비트코인 발행량이 2100만개로 제한되어 있다는 사실을 들어보셨을 겁니다. 이것은 기술적 제약이나 암호학적 증명이 아닌 비트코인에 참여하는 사람들 간의 약속입니다. 이 약속대로 동작하는 구현 중에는 C++로 작성된 Bitcoin Core(이하 코어 구현체)가 있으며, 이 코어 구현체는 2100만개만 생성하도록 프로그래밍 되어 있습니다. 코어 구현체는 오픈소스로 개발되고 있지만 비트코인 재단에서 주도하고 있으며 자연히 프로토콜의 개선에도 중요한 역할을 하고 있습니다. 만약 비트코인 재단에서 발행량을 2배 늘리기로 하여 프로토콜 개선 논의를 주도하고 구현체를 수정하고, 기존 참여자들이 그 구현체를 계속 쓴다면 비트코인의 발행량은 바뀌는 것입니다.


비트코인은 현재 여러 가지 문제를 가지고 있습니다. 그 중 하나만 짚고 넘어가면 채굴의 중앙화 문제가 있습니다. 비트코인은 채굴 연산에 특화된 ASIC으로 채굴이 가능합니다. 문제는 이 장비가 GPU보다 효율이 좋고 가격이 비싸다는 점에서 비롯됩니다. 이더리움의 경우 ASIC-resistant 채굴 방식을 채택하여서 GPU가 가장 합리적인 채굴 장비입니다. 이 경우 GPU를 가진 사람이라면 누구나 채굴에 참여할 동기가 생깁니다. 그러나 비트코인의 경우 GPU가 있어도 ASIC 채굴자의 경쟁에서 밀리기 때문에 채굴 할 동기가 떨어집니다. 특정 소수가 채굴을 독점하게 되면 여러 문제가 발생할 수 있습니다. 이런 문제를 해결하기 위해 채굴 방식을 바꾸고 싶은 사람들도 어딘가에 있을 것입니다.


비트코인 골드는 ASIC-resistant 채굴 방식을 채택한 프로토콜입니다. 기존의 비트코인과 호환되지 않습니다. 기존 비트코인과 호환되지는 않지만 비트코인의 특정 시점을 스냅샷 찍은 효과를 내기 때문에 기존의 비트코인을 가진 사람은 같은 양의 비트코인 골드가 추가로 생깁니다. 해피포인트를 전자화폐로 생각하여 예를 들어보면, 해피포인트 가맹점 중 하나가 해피포인트 정책에 반발하면서 새로운 포인트 제도를 만듭니다. 해피포인트 골드를요. 그러면서 특정 시점의 해피포인트 내역을 그대로 해피포인트 골드로 가져온다고 가정합시다. 실제로 일어나지는 않겠지만요. 그 특정 시점에 해피포인트 1000점을 가지고 있던 사람은 해피포인트 골드 1000점이 추가로 생긴 것입니다.


누군가 비트코인 1억원 어치를 가지고 있었는데 비트코인 골드가 나와서 그것이 1000만원이 되어 그 사람의 자산이 1억 1천만원이 되면, 복사 붙여넣기로 재화 및 서비스가 생산된 것이나 다름이 없습니다. 실제로 경제적 이득이 발생한 이유는 투기 수요가 있었기 때문입니다. 비트코인 골드 자체가 투기 심리를 조장한 면도 있을 것이고요. 이렇게 투기 수요가 증가하면 이득을 보는 곳이 하나 더 있습니다. 바로 거래소입니다.




오늘 갑자기 이 이야기를 하게 된 이유는... 비트코인 플래티넘을 빼고 말할 수 없을 것 같습니다. 한 1-2주 전 쯤에 비트코인 플래티넘에 대해 듣고, 홈페이지와 GitHub 저장소를 찾아봤을 때, 의도를 어느 정도는 추측할 수 있었습니다. 일련의 사태를 보면서 드는 생각은... 대중의 관심에 비해 기술적 검증 정보가 전무하다는 것입니다. 이는 관련 개발자나 거래소 등에서 침묵하는 점이 있다고 생각합니다. 자신의 경제적 이득과도 연관이 있을 수 있으니까요.

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

이더리움 스마트 프로그래밍 언어인 Solidity 언어 예제를 기반으로 이더리움 스마트 컨트랙트의 생성과 함수 호출에 대해 설명하는 글입니다.


Solidity 프로그래밍에 앞서 Solidity의 런타임 환경인 이더리움이 추상적으로 무엇을 하고, 어떻게 프로그램이 동작하는지 이해할 필요가 있습니다. 우선 블록체인 기술을 P2P 분산 스토리지로 생각할 수 있습니다. 블록체인 자체에 대한 내용은 다른 포스팅으로 소개하겠습니다. 이더리움은 비트코인과 마찬가지로 어떤 주소로 이더(Ether, ETH)를 주고 받을 수 있고, 주고 받은 내역이 스토리지에 됩니다. 이더리움의 차별점은 거래내역만 저장할 뿐 아니라 이더를 입출금할 수 있는 코드와 그 실행 결과를 저장합니다. 즉, 이더리움 네트워크에서 이더를 주고 받을 수 있는 주소는 2가지 종류가 있습니다. 이더리움에서는 주소라는 말 대신 어카운트(Account)라는 말을 사용하기에 아래부턴 어카운트라고 지칭하겠습니다.


- 이더리움의 어카운트 종류


이더리움에는 두 가지 종류의 어카운트가 있습니다. 하나는 Externally Owned Account, 줄여서 EOA라고도 부르며, 공개키/비밀키 쌍을 만들고 공개키를 공개하는 원리를 생각하면 되며 만들기 쉽습니다. 이 공개키로 만든 주소를 이더를 보내줄 사람에게 알려주면 이더를 받을 수 있고, 받은 이더를 다른 사람에게 보내려면 비밀키로 서명하여 네트워크에 전파하면 됩니다. 서명된 거래내역(트랜잭션)이 어떻게 다른 노드들에 의해 받아들여지고 위변조가 불가능해지는지는 비트코인 화이트페이퍼(https://bitcoin.org/bitcoin.pdf)를 참고하는 것을 추천합니다. 디테일로 들어가면 이더리움과 비트코인은 다르지만 키 아이디어를 이해하는데에는 효과적입니다. 다른 어카운트 종류 하나는 Contract 입니다. 저번 포스트에 이어 코드를 우선 보겠습니다.


위의 Contract는 bet()을 호출할 때 받은 돈을 prize_money로 누적시켜 기억해뒀다가 10번째 호출하는 사람에게 몰아주는 복권입니다. 실질적으로는 누구나 count 값을 알 수 있기 때문에 실제로 쓰이긴 어려운 예제입니다. 저번 예제과 다르게 payable이나 msg.value, msg.sender.transfer 등이 나타나면서 설명이 필요할 것 같습니다. payable은 이 함수를 호출할 때 이더를 보낼 수 있다는 것을 의미합니다. payable이 아닌 경우 이 함수를 호출할 때 이더를 보낼 수 없습니다. msg는 특수한 객체인데, 이 함수가 호출된 컨텍스트 정보를 얻어올 수 있습니다. msg.value는 전달된 이더 값이고, msg.sender는 이더를 보낸 어카운트의 주소입니다. Solidity에서는 주소 타입이 있어 balance()나 transfer() 등을 통해 해당 어카운트의 잔고를 확인하거나 이더를 보내는 함수를 호출할 수 있습니다.


사실, 위의 예제를 더 정확히 이해하기 위해서는 실행 환경에 대해 더 이해할 필요가 있어 그 얘기를 하려 합니다.


- Contract 인스턴스의 특정 함수를 실행시키는 방법

- Contract 인스턴스를 생성하는 방법


이더리움의 Contract 객체 생성이나 함수 호출은 트랜잭션에 의해 이루어집니다. 트랜잭션은 아래와 같은 정보를 포함합니다.


- 보내는 사람의 주소

- 받는 사람의 주소

- 이더

- 입력 데이터

- 가스 제한

- 가스 가격


가스에 관련된 내용은 이어질 포스팅에서 다루도록 하겠습니다. 보내는 주소/받는 주소/이더 외에 데이터 항목이 있다는 것에 주목하시면 됩니다. 함수를 호출하기 위해선 함수를 지정하는 Function Selector와 함수에 넘길 인자를 인코딩하여 전달해야 합니다. 기술적인 상세한 내용은 ABI 스펙 문서(https://solidity.readthedocs.io/en/develop/abi-spec.html)를 보면 됩니다. 만약 유효한 Function Selector가 아닌 경우 Contract에 정의되어 있는 Fallback 함수가 호출됩니다. Fallback 함수가 없으면 트랜잭션은 실패합니다. 아래는 위의 예제를 bet() 함수 대신 Fallback 함수로 바꾼 것입니다. Fallback 함수는 해당 Contract 내의 이름이 없는 유일한 함수이며 함수 인자를 받거나 값을 리턴하지 않습니다. Fallback 함수로 바꾸면 이 Contract에 그냥 이더를 보내는 트랜잭션으로 함수를 호출한 것과 같은 효과를 낼 수 있습니다.

스마트 컨트랙트의 보안에서 신경써야 할 점 중 하나는 msg.sender를 포함해서 어떤 address가 Contract 일 수 있다는 점입니다. msg.sender.transfer()를 호출하는 순간 단순히 내가 이더를 보내는 것이 아닌 다른 사람이 작성한 Fallback 함수가 실행될 수 있다는 점입니다.


다음은 Contract의 생성(혹은 deploy)인데, 이더리움은 Contract의 생성을 위한 프로토콜을 가지고 있습니다. 받는 사람의 주소를 0x0으로 주고 입력 데이터에 Solidity 코드를 컴파일한 EVM Bytecode를 넘겨주는 것입니다. Constructor에 전달해줘야 하는 인자가 있다면 역시 ABI 인코딩하여 넘겨줘야 합니다. 인스턴스의 파괴는 해당 인스턴스 내에서 selfdestruct 함수를 호출해야 합니다. 인스턴스의 주의해야 할 특징으로는 한 번 deploy 된 코드는 바꿀 수 없다는 점입니다. 코드를 업데이트 해야 하는 요구사항이 있을 경우 업데이트된 Contract의 인스턴스를 deploy 한 뒤 기존 인스턴스의 변수를 새 인스턴스로 이전하거나 Proxy 역할을 하는 Contract를 써야 하는 등의 문제가 있습니다. 만약 selfdestruct를 호출할 수 없는 Contract는 이더리움 프로토콜이 바뀌지 않는 한 영원히 파괴되지 않습니다. 


여기까지 이더를 주고 받을 수 있는 스마트 컨트랙트 예제와 컨트랙트의 생성과 함수 호출에 대해 살펴봤습니다. 설명을 위해 간단한 예제만 보여드렸지만 응용은 다양하게 있을 수 있습니다. 대표적인 예제로는 투표 시스템, 크라우드 펀딩, 경매 시스템 등이 있습니다. 이런 응용들을 블록체인으로 하면서 얻게 되는 장점으로는 암호학적으로 증명되는 투명성과 거래의 보장입니다. 크라우드 펀딩을 예로 설명하면 환불과 관련된 규정을 적고 고객을 설득 할 필요없이 코드로 작성하여 공개하면 됩니다. 단점으로는 한번 배포된 계약은 사업 주체의 통제에서 벗어나 해커의 공격 대상이 될 수 있다는 점입니다.

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
비트코인과 이더리움, 그리고 기타 블록체인에 기반한 코인들의 거래 가격이 폭등하면서 이젠 지나가는 할아버지도 비트코인 얘기하는 것을 어렵지 않게 볼 수 있는 것 같습니다. 이 포스트는 투자와 관련하여 쓰는 글은 아닙니다. 저는 개발자이고, 블록체인 기술 스타트업에 조인해서 일을 하고 있습니다. 제 친구들 역시 대부분이 개발자입니다. 최근에 제 친구들에게 제가 하는 일을 소개하기 위해 구구절절 설명하게 되는 일이 많아졌음을 느꼈고, 뭔가 정리된 상태로 전달할 수 있으면 좋을 것 같아서 개발자를 위해 간단한 소개 글을 쓰고 싶어졌습니다. 다른 관점에서 보면, 개발자들이 암호화폐에 가지는 관심에 비해 그 기반 기술에 대한 이해는 부족한 것 같습니다.

이를 위한 첫 시리즈는 Solidity 코드로 들여다보는 이더리움 스마트 컨트랙트 프로그래밍입니다. 앞서 간단히 말하자면, 블록체인을 일종의 분산 스토리지로 생각할 수 있고 이더리움은 그 스토리지에 코드와 그 실행 결과를 저장하는 시스템이라고 볼 수 있습니다. 이더리움의 코드 실행은 EVM이라고 불리는 스택 머신에 의해 실행되고, Solidity는 EVM 바이트코드로 컴파일되는 언어 중 하나입니다. 이 시리즈에선 여러분들에게 이더리움 클라이언트를 설치하거나 코드를 실행해보는 것을 요구하진 않습니다. 그저 눈으로 코드를 훑어보고 그 특성을 이해하는 정도로 넘어갈 것이며 따라서 엄밀한 예제를 제공하진 않을 것입니다.


위는 간단한 Solidity 코드입니다. 기존 class 기반의 OOP 프로그래밍 경험이 있다면, contract 대신 class를 대입해보면 간단히 이해할 수 있을 것이라 생각합니다. 이어지는 포스팅에서는 위의 코드처럼 다른 프로그래밍 언어와 비슷한 부분은 설명하지 않을 것입니다. 그런 부분은 지루하고, 또 공식 문서나 다른 글에서 이미 잘 설명하고 있기 때문입니다. 따라서 위의 코드를 보고 어떻게 동작하는지 짐작가는 바가 없는 분에게는 이해가 어려울 수도 있습니다.


다음 포스트




336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Windows 10 1주년 업데이트에 WSL (Windows Subsystem for Linux) 베타가 추가되었다. 감격...

개발자용이고, 쉘 환경이 아직 온전히 돌아가지는 않는다. 한글 입력이 안되는 것은 빨리 알아차릴 수 있다..


openssh-server를 열어서 bash.exe가 아닌 SSH 클라이언트로 붙어보라는 친구의 아이디어 시행.



$ apt install openssh-server


는 잘 수행된다.



어째선지 22번 포트에 이미 다른 SSH 서버가 떠있다. Git Bash나 MINGW 돌리는 과정에서 파악되지 않았는데 일단 무시하고


/etc/ssh/sshd_config


파일을 수정해서 포트를 바꿨으나 접속하는 즉시 Connection Closed 가 발생한다.


Github의 이슈를 찾아보니 역시 있다.

https://github.com/Microsoft/BashOnWindows/issues/300


/etc/ssh/sshd_config 에서 아래 항목을 수정한다.


ListenAddress 0.0.0.0

UsePrivilegeSeparation no

PasswordAuthentication yes


putty로 접속이 잘 되고, 한글 입력도 잘 된다.

문제는 bash.exe를 끄면 ssh 서버도 내려가는데, 백그라운드에서 자동 시작하는 방법은 좀 더 알아봐야 할 것 같다.



깃허브 스레드에선 bash.exe -c "sudo service ssh start" 커맨드를 수행하는 방식을 얘기하는데,

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Github에 한글로 자료를 정리하고 있었는데 몇 가지 문제점을 느끼고 Tistory로 옮기는 것을 시도해보려 한다. 한글로 정리한 이유는 영어를 잘 못하는 한국 학생들을 위함이었는데, 생각해보니 영어와 친숙하지 않은 학생이 Github로 유입되길 기대하기는 힘들다고 판단된다.


---


이항 힙 (Binomial Heap)


아래 설명은 읽는 사람이 트리 이론 기초와 이진 힙(Binary Heap)을 이해하고 있다는 가정 하에 쓰여있다.


- 왜 이항 힙을 쓰는가?


이진 힙(Binary Heap)보다 삽입 연산이 더 빠른 것을 기대할 수 있다. 이진 힙이 삽입의 시간복잡도 O(logN)인데 비해 이항 힙은 시간복잡도 O(1)이다. 단, 이는 분할상환 시간복잡도(Amortized Time Complexity)를 고려한 것이고, 최악의 경우를 따진다면 이항 힙의 삽입 역시 O(logN)의 시간복잡도를 가진다. 분할상환 시간복잡도는 언젠가 따로 포스팅을 해야겠지만, 이항 힙의 분할상환 계산은 비교적 직관적으로 설명이 가능하여 삽입을 설명하는 부분에서 이해를 도울 것이다.


- 이항 힙, 이진 힙 형태 비교


이진 힙은 이진 트리이고, 트리 1개로 구성된다.

이항 힙은 어떤 노드의 자식이 3개 이상일 수 있고, 트리 여러 개로 구성된다. 즉 루트 노드가 여러 개일 수 있다.


이진 힙은 부모-자식 간의 값을 비교한다면, 이항 힙은 트리의 루트 노드 간의 값을 비교한다.


- 이항 힙의 형태


이항 힙은 여러 개의 트리로 구성된다고 앞서 말했는데, 이항 힙의 모든 트리는 노드의 개수가 2의 멱수이다. 그리고 모든 자연수는 2의 멱수의 합으로 나타낼 수 있다.


이항 힙에 20개의 데이터가 있다면 아래와 같이 이해할 수 있다.

 * 16개의 노드를 가진 트리와 4개의 노드를 가진 트리, 총 2개의 트리로 구성된다.

   * (20 = 16+4)

 * 힙에서 1개의 데이터를 빼면, 노드 16개 트리, 노드 2개 트리, 노드 1개 트리, 총 3개의 트리로 구성된다.

   * (20-1 = 19 = 16+2+1)


데이터가 N개라면 N을 이진수로 나타내어 보자. 이진수에서 1의 개수가 트리의 개수이고, 1이 표현하는 수가 해당 트리가 가진 노드의 개수이다.


이항 힙은 그 이름에 맞게 이항 계수와 연관이 있다. 잠시 파스칼의 삼각형을 보자.


1  = 1

2  = 1 + 1

4  = 1 + 2 + 1

8  = 1 + 3 + 3 + 1

16 = 1 + 4 + 6 + 4 + 1


오른쪽이 파스칼의 삼각형이고, 왼쪽은 각 줄을 모두 합한 수이며 이는 2의 거듭제곱 수(=멱수)이다.


최소 힙이라고 가정하고 아래 노드가 각각 2개인 트리 2개를 생각해보자.


 > 3 - 4 (루트 노드 3, 자식 노드 4)

 > 1 - 2 (루트 노드 1, 자식 노드 2)


2개 트리를 병합해보자.


 - 트리의 병합


 > 1 - 2

      - 3 - 4 (루트 노드 1, 1의 자식 2와 3 그리고 3의 자식 4)


루트 노드 2개를 비교해서, 더 작은 값이 루트가 되도록 병합하였다.


또 다른 노드 4개인 트리가 있었다고 가정해보자.


 > 5 - 6

      - 7 - 8


트리 2개를 병합하면 아래와 같다.


 > 1 - 2

      - 3 - 4

      - 5 - 6

           - 7 - 8


이런 식으로 병합되는 트리는 힙의 조건을 만족한다. 이 경우 최소 힙이므로 모든 노드에 대해 부모 노드가 자식 노드보다 값이 작은 것을 알 수 있다.


트리의 레벨에 따른 노드 개수는 이항 계수를 따른다. 예제의 경우 노드가 8개이면 파스칼의 삼각형 4번째 줄에 해당한다. 루트 노드를 레벨 0으로 두었을때 

 * 레벨 0 노드 수: 1

 * 레벨 1 노드 수: 3

 * 레벨 2 노드 수: 3

 * 레벨 3 노드 수: 1


2개의 힙 트리를 병합하는 작업의 시간복잡도는 O(1)이다.


 - 이항 힙의 삽입


트리의 병합에 대해 배웠으므로, 이제 이항 힙을 구성할 수 있다.


데이터가 7개인 이항 힙이 있다면 트리 3개로 구성될 것이다. 앞으로 이항 힙의 트리들을 포레스트라고 부르겠다. 이를 이진수로 표현하면 111 이다. 여기에 데이터 1개를 추가하면 이진수에 값을 1 더하는 것만으로도 포레스트의 형태가 어떻게 변하는지 정확하게 알 수 있다.

 > 111 + 1 = 1000


이항 힙에 대해 공부할 정도라면 눈치챘으리라 예상한다. LSB를 포함해 연속된 1의 개수만큼 트리의 병합이 발생한다. 데이터 개수가 7(111)이었으면 트리의 병합은 3번 일어나고, 6(110)이었으면 트리 간의 병합은 일어나지 않는다.


데이터 개수가 N이라면 연속된 1의 최대 개수는 logN 근방임을 알 수 있으며 삽입의 시간복잡도가 O(logN)임을 짐작할 수 있다. 그런데 글 처음에 언급했듯 이항 힙은 이진 힙보다 삽입 연산이 빠른 것을 기대할 수 있다. 데이터 8개를 연속으로 삽입하는 상황을 생각해보자.


- 0000 : 0

- 0001 : 1

- 0010 : 0

- 0011 : 2

- 0100 : 0

- 0101 : 1

- 0110 : 0

- 0111 : 3

- 1000


콜론 오른쪽의 숫자는 트리 병합이 발생하는 횟수를 적은 것이다. 트리 병합이 발생하는 횟수를 모두 합쳐보면 7이다. 8개를 삽입하는 과정 중 절반이 트리 병합을 한 번도 필요로 하지 않는다. 간단히 계산해보면 삽입을 N번 수행하는데 시간복잡도가 O(N)임을 유추할 수 있고, 이를 근거로 삽입 1회에 걸리는 시간복잡도를 O(1)이라 하는 대신 분할상환(Amotized)이라는 조건이 붙는 것이다. 이는 어디까지나 총계를 근거로 산정한 것이며, 최악의 경우 logN에 비례하는 연산이 일어난다는 사실은 변하지 않는다. 분할상환 시간복잡도의 계산/증명은 필자도 엄밀히 알지는 못하여 당장 상세한 설명은 어려움을 양해바란다.


 - 이항 힙의 최솟값 찾기


포레스트의 루트 노드를 전부 비교해보면 된다. 이렇게 되면 시간복잡도가 O(logN)인데, 트리의 삽입 과정에서 병합할 때 미리 최솟값을 기억해두면 O(1)에 최솟값을 알 수 있다.


 - 이항 힙의 최솟값 제거


포레스트 중에서 최솟값을 가진 트리를 생각해보자. 위의 예에서 가져와보면


 > 1 - 2

      - 3 - 4

      - 5 - 6

           - 7 - 8


인데, 최솟값인 1을 제외한 서브 트리들이 이항 힙의 포레스트가 될 수 있음이 보이는가?


> 2

> 3 - 4

> 5 - 6

     - 7 - 8


이렇게 나온 3개의 트리를 기존 이진 힙 포레스트에 병합시키면 된다. 트리 병합을 최소로 하는 병합 순서를 생각해보고, 이에 관한 시간복잡도 계산은 각자의 몫으로 남겨두려 한다.


---


이해가 잘 가지 않는 부분 혹은 오류, 누락이 있다면 댓글이나 이메일(jjgjoojis@gmail.com)로 알려주시면 도움이 될 수 있도록 글을 보충하겠습니다.


본문과 별도로 국문 자료를 찾기 힘든 자료구조 및 알고리즘에 대한 설명을 요청해주셔도 좋습니다.

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

쓰고 있는 루분투 내장 크로미움 버전이 22인데, Medium에서 지원하지 않습니다. 젠장!

따라서, 티스토리에 글을 일단 쓰고 나중에 옮겨 써 볼 예정.


어떤 리눅스 배포판을 설치하느냐에 따라 다르지만, 일단 제가 세팅하고 있는 lubuntu 에서 세팅이 필요했던 부분들을 정리하자면,


한글 미지원

글꼴 설치 후 시스템 글꼴 설정, 브라우저 글꼴 설정, 터미널 언어 설정, 한글 키보드 설치가 필요합니다. 한글 키보드는 uim 벼루를 자주 써서 이것으로 셋팅을 하는데 별 이슈는 없습니다.


디스플레이

일단 큐비트럭은 HDMI와 VGA 포트를 가지고 있는데, 대부분의 배포판 이미지들이 두 개 이미지를 따로 다운로드 받을 수 있도록 하거나, 아니면 하나만 지원하던가(... 호스팅 비용을 아끼기 위해?), 처음 부팅할 때 연결된 포트를 인식해서 부트 옵션을 생성하는 이미지가 있습니다. 부트 파티션 세팅 옵션에 clone도 있지만, 일단 간단하게 셋팅값만 보고 해봤는데 안됩니다. 디스플레이가 안되면 부팅디스크를 수정이 불가피하니 주의하시기 바랍니다. (Cubian은 IP를 모스부호로 전환해서 LED로 표시해주는 기능이 있지만...)


고주파음

제가 베어본 대신에 큐비트럭을 구매한 이유 중 하나는 팬리스에 특별히 알려진 발열 이슈가 없다는 것입니다.(무거운 어댑터도 없다.. 출력만 된다면 USB로도 전원 공급이 가능하다..) 다만 여기에 함정이 있었으니 고주파음 발생.

일단 고주파음은 보드마다 격차가 있고, 제 보드가 불량일 수도 있으니 모든 큐비트럭에서 아래 현상이 나타나는 것은 아닙니다.

- 유무선 마우스 연결 시 발생

네. USB 꽂는 순간 고주파음이 지속적으로 발생합니다. 제가 가진 유선 키보드는 그런 현상은 없었습니다.

- SATA에 SSD 연결 시 발생

SSD를 꽂으면 보드에서 고주파음 발생합니다.


마우스는 뭐 안 쓸때 뽑으면 되지.. 라고 생각하며 안일하게 넘어가고, SSD는 전원 관리 옵션을 좀 만져보려는데, 가벼운 데스크톱 환경이다 보니 전원 관리 툴 따윈 없고 커맨드로 열심히 유틸리티 깔아서 조작해줘야 합니다. 처음에 봤던 hdparm


hdparm -I /dev/sdx 하면 정보가 쭉 나오는데, 제가 연결한 SSD에 PM(Power Management) feature set enable 확인.


hdparm -B 255 /dev/sdx 로 PM Disable 시도 했으나

 HDIO_DRIVE_CMD failed: Input/output error

 APM_level = not supported


에러 발생. ARM_level이 not supported로 나와서, 스스디나 커널이 지원이 안되는 것인지 확인은 못해보고 방법을 찾다가 laptop-mode-tools 라는 패키지가 있어서 설치해봤는데 고주파음이 상시 발생하는게 아니라 아주 가끔 조금씩 발생하는 것으로 개선되었습니다.
덩달아 마우스 고주파음도 개선, 적어도 지금은 소음이 하나도 발생하지 않습니다.


블루투스/와이파이
시도해봤는데 둘 다 잘 안됩니다. 저한테 급한 옵션은 아니어서 우선순위가 낮습니다.


336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

빨콩 달린 무선키보드가 있으면 쓰기 편할 것 같아서, TPT2 블루투스 키보드(0B47361)를 구매했다.

블루투스 키보드로 인식하더라도 트랙포인트 시스템이 무난하게 동작한다. 다만, 트랙포인트 전용 드라이버가 설치되지 않아 가운데 버튼을 제대로 활용할 수 없는 문제가 있다.

일단 자주 사용하는 Ubuntu 13.10, Xwindow 시스템에서는 해결 방법을 찾아 스크립트로 작성해두었다.


#!/bin/sh


ID=`xinput list --id-only "ThinkPad Keyboard"` || (echo "Failed to find ThinkPad Keyboard"; exit 1;)

echo "Found ThinkPad Keyboard ID: $ID"


echo "Setting Evdev Wheel Emulation ... ";

# Originally 0

EVDEV_WHEEL_EMULATION=1

xinput set-prop $ID "Evdev Wheel Emulation" $EVDEV_WHEEL_EMULATION


echo "Setting Evdev Wheel Emulation Button ... ";

# Originally 4

EVDEV_WHEEL_EMULATION_BUTTON=2

xinput set-prop $ID "Evdev Wheel Emulation Button" $EVDEV_WHEEL_EMULATION_BUTTON


PS. 광학식 빨콩 괜찮다. 손가락이 덜 아프다. 다만, TPT2 블루투스 키보드 자체가 컴팩트한 크기에 한계가 있어 개발용으로 쓰기엔 사람에 따라 다소 불편할 수 있다.

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

MySQL 원격 접속에 관한 설정은 검색하면 관련 문서가 많으니 생략하겠다. 기본적으로 데이터베이스 서버를 원격에서 접속하려면 서버 측에서 허용하는 설정이 필요하다.


R command 에서

> install.package('RODBC')

install에 성공했다면 이제 해당 모듈을 불러들이고 접속을 시도하면 된다.

> library(RODBC)

> odbcConnect("<something>", uid="<database user name>", pwd="<password>");


<something> 에 들어가야 할 문자열은 DSN 이다.

DSN이란, Data Source Name 의 약자로 ODBC에서 접속할 데이타베이스 정보에 대한 별칭 같은 것 같다.

Windows 에서의 설정은 적당한 검색으로 찾아보면 나온다.

필자의 환경인 Fedora 17 에서는 unixODBC-devel을 설치하면서 설정 파일인

/etc/odbc.ini, /etc/odbcinst.ini 이 생겨있었다.

/etc/odbcinst.ini 에는

#Example driver definitions

...(생략)

[MySQL]

Description = ODBC for MySQL

...(생략)


의 내용이 있었다.


/etc/odbc.ini 는 빈 파일이었다.


빈 파일에 다음과 같은 내용을 추가했다.

<> 로 감싸진 것은 자신의 환경에 맞는 것을 써 넣으면 된다.


[<My Data Source Name>]

Description = <My Description>

Driver = MySQL

ServerName = <My Server Name or My Server IP>

Port = <My Server Port, MySQL Server default port number is 3306>

Database = <My Database Name>


자. 이제 DSN을 설정해두었으니 다시 접속을 시도해보자.

> odbcConnect("<My Data Source Name>", uid="<database user name>", pwd="<password>");


여기서 접속이 잘 되면 잘 된거고.. 필자는 다음과 같은 문제가 발생했다.

MySQL Driver인 libmyodbc5.so 파일을 찾을 수 없다는 문제였다.


그래서 이래저래 삽질한 결과

# yum install mysql-connector-odbc


위의 패키지를 설치한 뒤 커넥트 하니 잘 되었다.

+ Recent posts