이더리움에서 활용하고 있는 "머클 패트리샤 트리"를 이해하려다가 주화입마에 빠지신 분들이 꽤 있는거 같은데요. 사실 그것은 당신들의 탓이 아닙니다. 비탈릭이 쓴 머클링 in 이더리움 등의 블로그 글 및 각종 이더리움 책에서 설명된 내용을 읽고 금방 이해하는게 사실상 쉽지 않습니다. 남을 이해시키기 위한 글이 아니라는 생각이 들기도 하는데요.

이 글에서 저는 "이해시켜 드리기 위한 글"을 써보려고 합니다. 만약 또 실패한다면 역시 제가 모자라서 그런것이 겠지요. "개념을 이해시켜 드리기 위한 글" 이기 때문에 아주 디테일한 부분(트리에 대한 구체적 요소,추가,삭제,업데이트 및 블록상세등)을 과감히 생략하였습니다. 지엽적인 오류도 있을 수 있습니다. OTL

머클패트리샤트리를 이해하기 위해서는 먼저 몇가지 짚고 넘어가야 할 것이 있습니다. 먼저 용어의 재정의 인데요.
보다 선명한 이해를 위해서는 비트코인의 "머클 트리"는 "바이너리 머클 트리" 라고 말해야 하며, 이더리움의 "머클 패트리샤 트리" 는 "상태전이 일반 머클 확장 페트리샤 트리" 라고 말해야 합니다. 각각의 단어마다 중요한 의미를 가지고 있으며 본 글을 통해 차차 설명드릴 예정입니다.

이제 시작해 볼까요?

 "상태전이"  "바이너리 머클 트리" "상태전이 일반 머클 트리"  "확장 페트리샤 트리"  각각의 어떤 의미를 가지는지 봅시다. 마지막엔 모두 합쳐서~"상태전이 일반 머클 확장 페트리샤 트리" !!!

1. "상태전이"

먼저 여기 아이언맨이 있습니다. 

"어벤져스 인피니티 워" 의 한장면인데요. 현재 아이언맨의 체력수치는 "100" 입니다.  아이언맨 역할을 한 로버트 다우니 주니어는 체력 "100"으로 역할을 수행하고 있습니다.

하지만 우주가 파멸되는 것을 볼 수 없었던 "타노스" 의 주먹 한방에 체력 50이 고갈됩니다.
이때, 이전의 체력 "100" 이었던 로버트다우니주니어를 영화에서 제외해서 구경시키고, 체력"50"인 이윤석을 투입해서 아이언맨의 역할을 수행하게 합니다. 좀 말이 안되는거 같나요? 즉 어떤 변화가 일어 났을때 "교체" 합니다. 이것은 상태 전이(변화)가 아닙니다. 

프로그래밍적으로 보면 immutable 속성이라고 하며,  

a = 100; 

이런 a 를 100에서 50으로 값을 변경 할 경우 그냥 기존 a 에 50 을 넣어서 변경하는것이 아니라, 기존 100은 놔준채 50을 가진 새로운 a를 만들게 되는 방식입니다. 딱 봐도 먼가 불필요한 공간을 낭비하는것 처럼 보이죠? 하지만 장점도 크기 때문에 현재 프로그래밍 언어에서 주목받는 방식이기도 합니다.

이것이 "비트코인"에서 처리하는 방식입니다. "교체" , "기존 것은 놔두고 새로운것을 만든다" 

근데 이더리움에서는 그냥 상식적인(?)인 방식을 사용합니다. 그냥 체력이 줄어든 기존 아이언맨이 계속 타노스와 싸우는 거죠. 즉 상태가 변경된다는 것이죠. 이것을 고상하게 "상태 전이"라고 합니다. 아무것도 아니죠 사실.

블록체인(비트코인)틱한 예로 들어 볼까요? (디테일은 이렇지 않습니다. 예를 위한 것임) 

철수가 이전에는 100원 이었고 현재는 60원입니다. (영희는 120원, 위에 그림 잘못;) 이전블록에도 있고 현재 블록에도 기록되어 있네요. 다른 사람들 기록도 마찬가지입니다. 먼가 공간을 낭비하는 듯한 느낌입니다.

좀더 구체적으로 보면 

위의 그림에서는 철수의 80이 50으로 교체되고, 영희는 10을 40으로 상태가 변이된 것이 아니라, 30이 새로 생성되었습니다. 즉 비트코인은 "추가,삭제" 방식으로 이루어지고, 이더리움은 아주 상식적인 "변이" 방식으로 이루어집니다. (이더리움의 모든것이 "상태변이"로 이루어지는 것은 아닙니다)

이제 이더리움식 상태전이를 보겠습니다.

33블록과 34블록은 하나를 가르키고 있으며, 철수와 영희에 대한 상태가 변경(전이)되었을 뿐입니다. 공간의 낭비가 줄어 들었습니다.
(* 위는 이해를 위한 예이고 정확히는 이더리움의 state 저장은 항상 최신본만 유지하는게 아니라, history 또한 유지합니다.  과거 데이터를 유지할 이유가 없으면 그냥 update 해도 되지만, 이더리움은 불안요소를 가지고 있습니다. 따라서 실제는 변하지 않은 데이터는 계속 유지하되, 변한 데이터는 update 하지 않고 append 합니다. 남은 데이터는 나중에 삭제합니다. 가지치기(prunning) 이라고 하는데 비탈릭이 한번 개념을 제시했었는데 문제가 있다고 하며, 현재 정확히 어떻게 구현되어 있는지는 확인 안해봤습니다) 

2. "바이너리 머클 트리"

이제 비트코인에서 사용되는 바이너리 머클 트리에 대해서 알아보죠. (디테일은 좀 다릅니다)
일단 바이너리 머클 트리는 누군가는 조금 공간을 더 쓰지만 전체적으로 보면  "공간을 절약" 하기 위해 만들어진 놈입니다. 특히 무엇이 포함되어 있는가? 에 대한 질문에 전체를 가지고 있지 않으면서도 대답을 할 수 있게 해줍니다.

바이너리 머클트리는 구체적인 정보(여기서는 철수100, 영희50등등) 를 가지고, 두개씩 쌍을 이뤄서 축약시키고, 그것을 다시 축약시켜서 하나로 만드는 것을 말합니다. 그림에서 보다시피 2개씩 쌍으로 되어서 바이너리이구요. 반드시 2개씩 쌍을 만들지 않아도 됩니다. (비트코인에서만 2개씩 쌍인 것이구요)

즉 구체적인 정보 + 축약정보의 트리인데요. 결국 공간을 더 낭비하게 됩니다. 잉? 공간을 절약한다매? 
네 비트코인에는 노드들이 다양하게 있는데, 이 구체적인 정보 + 축약정보트리를 모두 가지고 있는 노드에게는 낭비가 맞습니다만, 라이트 노드는 단지 블록헤더만 가질수 있게 되어 전체적으로 보면  획기적인 절약이 됩니다. 

보통 거래를 검증하기 위해서는 위의 그림을 예로들면 정은이가 30을 가지고 있음을 확인해야 하는데요. 블록헤더만 가지고서 그것을 가능하게 합니다. 어떻게 할까요? 새 그림을 보시죠.

실제는 이렇습니다. 위의 그림에서 거래0 이라고만 적혀있지만 데이터가 꽤 많아요. 그 큰 거래데이터를 해쉬하여 해쉬0,1,2,3을 만들고 그것을 또 해쉬 하여 루트해시를 만드는데요.

루트해시만을 가지고 있는 라이트노드는 거래3이 이 블록에 있는지 확인을 받기 위해서 모든것을 가지고 있는 풀노드에게 요청하면 해쉬2와 해시01에 대한 정보만 전달해주는데, 이것을 가지고 순서대로 재계산하여 루트해시를 만들수 있게  되며, 그 루트해시 값이 자신이 가지고 있는 루트해시값과 같으면 "아~ 거래3는 이 블록에 있는게 맞구나" 하고 검증성공하게 합니다.

이게 비트코인에서 사용되는 바이너리 머클 트리인데요. 이제 중요한 포인트가 나옵니다. 위의 "상태전이" 에서 설명했듯이 비트코인 같은 경우는 이 바이너리 머클 트리를 구성하는 내용들이 모두 새로 만들어 집니다. 즉 업데이트(전이) 가 아니라 새롭게 창조됩니다. 따라서 아래와 같은 모습을 띄게 됩니다.

블록마다 새로운 내용을 담고 있습니다. (위의 상태전이에서 설명한 것처럼 상태가 전이되는 방식이 아니라, 새로 생성되는 방식. 아이언맨 100이 아이언맨60이 되는게 아니라, 아이언맨 60이 새로 생성되는 방식)

그럼 상태를 전이 시키려면 어떻게 해야 할까요? 매우 단순합니다. 어찌보면 이게 더 자연스럽구요.

3. "상태전이 일반(non-binary)머클 트리"

(아직까지 "패트리샤" 에 대한 내용은 하나도 안나왔으며, 위의 그림도 그것과는 무관합니다.)

위의 그림을 보면 2개의 블록이 있으며, 두 블록이 동일한 머클 트리를 공유하고 있습니다. 여기서 보시면 트리가 2개씩 뻗어나간게 아니죠? 그래서 일반(non-binary)머클트리라고 칭했으며, 상태전이가 일어나고 있습니다. 즉 블록마다 새로운것만 가지고 있는게 아니라, 공통된것은 공유하고 있으며, 변경(전이) 된 것만 마지막 트리에서는 변경(정확히는 추가)하여 사용하고 있습니다.

이렇기 때문에 공간을 엄청나게 절약할 수 있음을 알수 있습니다. 

(* 다시 말하지만 정확히는 이더리움의 state 저장은 항상 최신본만 유지하는게 아니라, history 또한 유지합니다. 위 그림에서 27이 없어져야 하는데, 사실 일정동안 남겨 두긴 합니다. 이유는 합의가 이루어져서 블록이 만들어 졌다고 해도 확정이 되지 않는 불안 요소가 비트코인이나 이더리움에 있기 때문입니다. bifurcated 이 생기면 state rollback 을 해야 하기 때문. 따라서 시간이 어느정도 흐르면 pruning 과정을 통해 이 history 를 삭제하여 저장 공간을 줄여 줍니다. 실제는 update가 아니라 append 라는 점이죠.) 

4. "패트리샤 트리"

이제 패트리샤 트리에 대한 설명을 시작합니다. 이것도 역시 공간을 절약하기 위함입니다.

인터넷에서 젤 흔히 볼수 있는 그림이며, 이 자체로 아주 명쾌하게 설명해 주고 있습니다.  1번에서 7번까지의 단어를 모두 기록하는 것보다 각 공통되는 부분은 공유하는게 공간을 절약할 수 있게 되겠지요.  r,om, ub, an 이나 ic 처럼 공통되는 부분을 하나의 노드로 공유합니다. 이더리움에서 사용되는 Account 가 State tree, Transactions Tree등에 주렁주렁 달릴텐데...저럴게 매달아 놓으면 중복이 많이 없어지는거죠. 

5. "확장 패트리샤 트리"

이더리움 같은 경우 패트리샤 트리를 좀 다르게 사용하는데요. 이것 역시 그림 그 자체로 이해 할 수 있을 것 입니다.

3개의 특징을 가진 노드가 있습니다.

첫째. 리프노드 

 - 이 노드는 키값에 대한 패스가 종결되었을 때 value 에 해당 주소에 대한 정보들을 포함하고 있게 됩니다.
   위의 그림에서는 a77d337 는 value 로 1.00WEI 를 가지고있네요.(실제 이더리움 구조에서는 더 복잡한 value 를 갖게 됩니다)

둘째. 브랜치노드

- 이 노드는 16개의 16진수 값과 value 를 가지고 있게 됩니다. 이 노드를 통해서 가지치기가 시작됩니다.

셋째, 확장노드

- 이 노드를 통해서 공유되어질 키값들이 저장되게 됩니다. 이 노드 자체로는 종결되지 않기 때문에 value 로는 이후의 키 값을 책임질 브랜치 노드를 갖게 됩니다.

참고로 이 그림에서는 "머클" 과 "상태전이" 의 모습은 없습니다. 

6. "상태전이 일반 머클 확장 페트리샤 트리

"상태전이" + "머클트리" + "확장패트리샤 트리""상태전이 일반 머클 확장 패트리샤 트리" 

그림을 다시 봅시다. (다시 언급하지만 정확한 디테일을 담고 있지 않습니다.) 

보다시피 확장 패트리샤 구조를 가지고 있으며, 그 구조에서 각 패트리샤 노드들의 해쉬가 합쳐져서 위쪽을 향하고 있습니다. 즉 머클링을 하고 있는거죠. 비트코인처럼 2개씩 쌍을 지을 필요는 없습니다. 결국 이 머클링이 이어지면 루트해시가 만들어지게 되며, 자식에 어떤 변화가 생기면 루트해시의 값도 바뀝니다. 이 그림에서 "상태전이"가 표현되지 않았지만, 2e: 200원이 이전 블록에서는 2e: 300원이 었으며 300->200으로 "상태전이"가 일어난 후의 모습이라고 생각하시면 될 거 같습니다. 이 상태전이 때문에 새로운 블록의 상태트리의 루트해시 값도 바뀌게 될 겁니다.

* 참고로 이더리움은 1개의 "상태전이 머클 패트리샤 트리(어카운트별 상태(코인 및 코드) 저장용)" 와  2개의 상태전이는 빠진 "머클 패트리샤 트리(트랜잭션,영수증용)" 로 이루어져 있다.


이쯤에서 글을 마칠까 합니다. 이 글을 읽고 전반적으로 "아하~~" 라는 느낌을 가질 수 있다면


1) https://steemit.com/cryptocurrency/@nadifsd/merkle-patricia-tree 
2) https://medium.com/coinmonks/data-structure-in-ethereum-episode-3-patricia-trie-b7b0ccddd32f

을 통해서 디테일한 부분은 금방 해소 될 수 있으리라 보여집니다. ^^ 

또한  이더리움은 PBFT 와는 다르게 "Safety" (분산 시스템에서 말하는 Safety vs Liveness)가 약하며 그러기 때문에 history 를 관리해야 하고 pruning 이라는 주제도 나옵니다. 본문에는 모든게 생략되어 있습니다. 다음 여행지를 찾아 떠나세요~~ 

 

 

- 발표시간 : 2018년 4월14일 (토요일) 3시~5시  

- 발표주제 :

은행/사이버보안/선거관리/웹호스팅/도매,소매공급망/엔터네이먼트등 블록체인은 예상치 못한 방식으로 수많은 산업에 영향을 미칠 엄청난 미래 기술이 될 것이다라는 다소 과장됬을지도 모르는 글들이 쏟아져 나오는 현재 있어서, 우리 개발자들은 어떻게 보면 복받았을지도 모른다고 생각합니다. 그런 엄청난 기술에 대해서 일반인들 처럼 가쉽성 기사들만 읽고 지나가는게 아니라, 그 실체 기술이 무엇인지에 대한 호기심을 비교적 쉽게 풀 수 있기 때문인데요. 세상이 바뀌는 것에 함께 한다는 것은 꽤 멋진 일인거 같습니다. 
그리고 블록체인을 공부하는 것은 우리 소프트웨어 개발자에겐 일석이조의 효과를 가져오는데요. 블록체인 자체를 알게 되는 것과 동시에, 개발자로써의 내공을 쌓는 하나의 교육 과정이 될 수 도 있습니다. 오픈소스 리딩과 함께 여러 알고리즘을 공부하게 되니까요.여러분의 실무에 써먹을수도 있는 그런 기술일지도 모릅니다. 

블록체인에 대해서 짧게 소개하자면 블록체인은 데이터를 블록이라는 개념으로 연결지어 분산 노드를 통해서 관리되고 서로 통신하며, 해당 노드는 누구나 될 수 있습니다. 이러한 누구나 가지고 있을 수 있는 데이터를 어떻게 신뢰 할 수 있을까? 그것에 대한 해답으로 나온 기술로써, 이  블록체인(비트코인,이더리움)이라는 거대한 아이디어의 기반이 되는 다양한  요소 기술들에 대해서 간단히 살펴보며 마지막으로는 전체에 대한 조망을 하면서 마무리 짓겠습니다.



1.블럭체인(비트코인) 인사이드 

발표 순서

- 책 소개 (마스터링 비트코인, 마스터링 이더리움) 
- 신뢰란 무엇인가 
- 분산컴퓨팅
- 비잔틴 장군 문제
- 블록체인과 비트코인
- 블록체인 세대별 기술
- 대칭키/공개키 암호화/서명이란
- 해시 
- 가쉽 네트워킹 및 비트코인 네트워크 

- 분산 데이터베이스

- 주소 만들기 
- 머클트리 
- 블룸필터 
- 스크립트 
- 거래검증 
- 거래생성
- 채굴 및 작업증명 
- 블록생성
- 블록검증
- 블록선택 및 전파
- 전체 조망  <-- 이번 세미나는 여기까지 



2.이더리움 코어 및 기타 스토리

- 작업증명(PoW, Proof-of-work)과 지분증명(PoS, Proof-of-stake) 
- state,balance,accounts  vs UTXO
- RLP ,HP 엔코딩  & 굳이 왜? 
- 이더리움 머클 패트리샤 트리 및 상태전이 증명 (거래 검증)
- 상태트리, 거래트리, 영수증 트리 (굳이 왜 영수증 트리?)
- 이더리움 스마트 컨트랙트
- 이더리움 ERC20 & ERC721
- 이더리움 스크립트 
이더리움 네트워킹
   ㅁ TCP/UDP/NAT/홀펀칭/UPnP   : 이더리움 nas 옵션 인사이드
   ㅁ Kademlia (Consistent Hashing & DHT)
   ㅁ RLPx/devP2P
   ㅁ ethereum wire protocol 과 블록전파 
- 이더리움 스웜 
   ㅁ bitTorrent
   ㅁ Filecoin ( IPFS)
- 이더리움 플라즈마
- 이더리움 샤딩
- 이더리움 위스퍼
- 이더리움 캐스퍼 POW 와 POA 의 조합 
Multi-sig ,오라클,prunning,등등
- 라이트닝 네트워크,Raiden 네트워크
- 패리티를 통한 콘소시엄 블록체인 구성하기  
- 사이드체인&two way peg
- 사이드체인을 통한 이더리움 DApp 확장 (loom network DAppChains)
- DEVP2P (GO,PYTHON) 코드리딩

3. ColoredCoin,zcash,모네로,하이퍼레저,CORDA스트라티스,리플,스텔라,카르다노,스팀,EOS,,IOTA,ICON,코스모스

-  각 플랫폼의 특성 (비트코인 확장,BaaS,프라이빗,금융,커뮤니티,범용,사물인터넷,인터커넥션등) 
-  비트코인(家) 의 확장:  알트코인/사이드체인 (단순화 화폐에 자산등록,자산발생,자산중심 플랫폼으로)
-  익명성 : 모네로의 링시그니쳐 와 zcach 의 영지식증명 
-  리플에서 집중하는 이체 시장 ( PG,은행간 국제송금, 기업간 국제송금, 은행 지점간 내부송금)
-  다른 플랫폼들을 Private 하게 사용하는것과 스트라티스,하이퍼레저,CORDA 의 차이점
-  Private 블록체인이 필요한가? 
-  하이퍼레저,CORDA 은 블록체인인가?  분산원장 VS 블록체인
-  금융업계와 블록체인 : 리플 vs CORDA
-  Hyperledger Fabric vs CORDA
-  POW,POS 말고 POA : 확률적 투표 이론, PBFT : 그리고 이들의 조합 캐스퍼  FFG
-  PBFT -> SIEVE
-  리플은 실제 사용되고 있는가? 리플과 스텔라의 차이점
-  스팀은 데이터를 어디에 저장하나? 비용은?  그리고 Graphene 네트워킹 
-  스팀,EOS 는 뭘 믿고 거래비용을 사용자에게서 해방시켰나?  수수료 vs  비율제한
-  ICON 은 분산합의 시스템 인가? 아닌가?
-  ICON 과 코스모스가 과연 inter-connection 역할을 할 수 있을까? 한계는?
-  IOTA 블록체인이 없다고?  마코프 체인 몬테카를로 알고리즘이란
-   ....


4. 다양한 블록체인에 사용되는 기술들 
- 네트워크 샤딩(Network Sharding), 트랜잭션 샤딩(Transaction Sharding), 연산 샤딩(Computational Sharding)
- 다중서명/집계된 서명(aggregated signature)
- 암호화 (ECDSA , ECIES , ECDH)
- 토큰 이코노미 & 암호 이코노미 
 PBTF 를  2단계 실행으로 변경한다면? (즉 브로드캐스팅을 한번만 한다면?) 
- ...


5. 블록체인에 관련된 비기술 이슈들 
-  거래소 이슈
-  ICO 이슈 
- 국가별,기관별 규제 이슈 
- 비트코인(家) 이야기 
- ...

6.이더리움 Dapp 응용 개발

- 분산 어플리케이션 이란?
- 장,단점
- ERC
- 로직과 저장소
- 오프체인&오라클  
- 솔리디티 프로그래밍
- Web3.js & Electron 
- 존재증명 및 크라우드펀딩 개발 

7. 하이퍼레저 패브릭 이란?

- 개념(network,identity,membership,peers,private data,ledger 등)
- 네트워크 만들기
- 체인코드 만들기
- 미들웨어 만들기
- 웹어플리케이션 만들기
- 하이퍼레저 컴포저
- IBM Bluemix & AWS Blockchain template 
- 하이퍼레저 sawtooth 는 무엇이며 fabric 과의 차이점은?


8. Corda 란?  


- 개념(네트워크,ledger,state,notary,contracts,Tranaction,Flow 등)
- 네트워크 만들기
- CorDapp 만들기

9 .EOS  & STEEM 응용 개발
 
10. DAppChains (loom) 응용 개발 

11. COSMOS 응용 개발 

12. 블록체인 기본/응용 서비스들

- 현재 존재하는..
- 앞으로 존재 할..
- 기록물/저작권 보관 (등록) 
- 커뮤니티 보상 서비스 
- 엔터테이먼트 서비스 (음악,영상,카툰등) 
- 게임,가상현실에서의 자산으로써의 가치
- 유통,운송,무역
- 정치,공공재 
- ...

발표자료 

개발자를 위한 비트코인(블록체인)_ver발표.pdf





scriptSig 스크립트의 시작에 나오는 sig 가 어떻게 만들어지는지 궁금하진 않는가? 즉 무엇을 서명한 것인가? 
그에 대한 답이 아래 코드에 있다. 이렇게 복잡하게 만들어 질 줄이야 ㅎㅎ 

관련 레퍼런스: http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html
코드출처: https://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-tx

트랜잭션이 만들어지는 단계 (19단계) :

  1. 4바이트의 버전 필드 추가 : 01000000
  2. 1바이트의 몇번째 input 인지 추가: 01
  3. 우리가 소비하고자 하는 지난번 트랜잭션의 32-byte 해시: 
    eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
  4. 4바이트의 3번의 트랜잭션에서의 output 인덱스의 UTXO (output number 2 = output index 1):
     01000000
  5. 트랜잭션을 서명할 목적으로 임시 scriptSig 를 만드는데, 1바이트의 길이 (0x19=25bytes) 를 채운다 :
     19
  6.  우리가 사용 할 UXTO의  scriptPubKey 를 이용해서 임시 scriptSig 를 만든다:
     76a914010966776006953d5567439e5e39f86a0d273bee88ac
  7. 4바이트 시퀀스 필드를 채운다: ffffffff
  8. 1바이트의 새로운 트랜잭션의 아웃풋 넘버를 채운다 : 01
  9. 거래 할 금액에 대해 8바이트 필드를 쓴다. 우리가 소비할 수 있는 총 UTXO 에서 fee 를 위한 0.001BTC 를 제외한다. (0.999 BTC, or 99900000 Satoshis): 605af40500000000
  10. output 스크립트의 길이를 1바이트로 채운다.(0x19 or 25 bytes): 19
  11. 실제 아웃풋 스크립트를 작성한다(
    대략  OP_DUP OP_HASH160 pubKeyHash  OP_EQUALVERIFY OP_CHECKSIG 이런것이 들어간다.pubKeyHash 는 public key 를 해쉬한 값인데, 내가 돈을 보내는 사람의 주소에서 생성 될 수 있다.):
     76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
  12. 4바이트 "lock time" 필드를 채운다: 00000000
  13. 4바이트의 "hash code type" 를 쓴다.(우리 경우는 1이며 SIGHASH_ALL): 01000000

    (일단 여기까지 완전한 트랜잭션이 만들어 졌다. 이후에는 이 트랜잭션에 대한 서명을 할 차례이다.) 

    01000000
    01
    eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
    01000000
    19
    76a914010966776006953d5567439e5e39f86a0d273bee88ac
    ffffffff
    01
    605af40500000000
    19
    76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
    00000000
    01000000
    
  14. 이중-SHA256 해시로 이 전체 구조에 대해서 해쉬한다.:
     9302bda273a887cb40c13e02a50b4071a31fd3aae3ae04021b0b843dd61ad18e

  15. 개인키로 private/public 키 쌍을 생성한다. 그리고 14번의 해시를 서명한다:
     30460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc06 이 서명에 뒤에 1바이트 해시코드를 넣는다. 01. 공개키는 다음과 같다: 0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
  16. 진짜 scriptSig 를 구축한다: <One-byte script OPCODE containing the length of the DER-encoded signature plus 1 (the length of the one-byte hash code type)>|< The actual DER-encoded signature plus the one-byte hash code type>|< One-byte script OPCODE containing the length of the public key>|<The actual public key>
  17. 스텝 5에서의 scriptSig 길이를 진짜 scriptSig 의 길이로 변경한다 : 8c
  18. 스텝 6에서 생성한 임시 scriptSig 를 스텝16의 진짜 scriptSig 로 변경한다:
     4930460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc0601410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
  19. 마지막으로 스텝 13에서 추가한 4바이트 해시 코드타입을 제거한다. 

    01000000
    01
    eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
    01000000
    8c
    4930460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc0601410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
    ffffffff
    01
    605af40500000000
    19
    76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
    00000000

https://hamait.tistory.com/1003  <-- 여기로 가면 위의 과정을 좀 더 비쥬얼하게 만들어 놓았다.

위 19단계에 해당하는 파이썬 코드는 다음과 같다.

#bitcointools
from deserialize import parse_Transaction, opcodes
from BCDataStream import BCDataStream
from base58 import bc_address_to_hash_160, b58decode, public_key_to_bc_address, hash_160_to_bc_address

import ecdsa_ssl

import Crypto.Hash.SHA256 as sha256
import Crypto.Random

#transaction, from which we want to redeem an output
HEX_TRANSACTION="010000000126c07ece0bce7cda0ccd14d99e205f118cde27e83dd75da7b141fe487b5528fb000000008b48304502202b7e37831273d74c8b5b1956c23e79acd660635a8d1063d413c50b218eb6bc8a022100a10a3a7b5aaa0f07827207daf81f718f51eeac96695cf1ef9f2020f21a0de02f01410452684bce6797a0a50d028e9632be0c2a7e5031b710972c2a3285520fb29fcd4ecfb5fc2bf86a1e7578e4f8a305eeb341d1c6fc0173e5837e2d3c7b178aade078ffffffff02b06c191e010000001976a9143564a74f9ddb4372301c49154605573d7d1a88fe88ac00e1f505000000001976a914010966776006953d5567439e5e39f86a0d273bee88ac00000000"

#output to redeem. must exist in HEX_TRANSACTION
OUTPUT_INDEX=1

#address we want to send the redeemed coins to.
#REPLACE WITH YOUR OWN ADDRESS, unless you're feeling generous
SEND_TO_ADDRESS="1L4xtXCdJNiYnyqE6UsB8KSJvqEuXjz6aK"

#fee we want to pay (in BTC)
TX_FEE=0.001

#constant that defines the number of Satoshis per BTC
COIN=100000000

#constant used to determine which part of the transaction is hashed.
SIGHASH_ALL=1

#private key whose public key hashes to the hash contained in scriptPubKey of output number *OUTPUT_INDEX* in the transaction described in HEX_TRANSACTION
PRIVATE_KEY=0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725

def dsha256(data):
return sha256.new(sha256.new(data).digest()).digest()

tx_data=HEX_TRANSACTION.decode('hex_codec')
tx_hash=dsha256(tx_data)

#here we use bitcointools to parse a transaction. this gives easy access to the various fields of the transaction from which we want to redeem an output
stream = BCDataStream()
stream.write(tx_data)
tx_info = parse_Transaction(stream)

if len(tx_info['txOut']) < (OUTPUT_INDEX+1):
raise RuntimeError, "there are only %d output(s) in the transaction you're trying to redeem from. you want to redeem output index %d" % (len(tx_info['txOut']), OUTPUT_INDEX)

#this dictionary is used to store the values of the various transaction fields
# this is useful because we need to construct one transaction to hash and sign
# and another that will be the final transaction
tx_fields = {}

##here we start creating the transaction that we hash and sign
sign_tx = BCDataStream()

##first we write the version number, which is 1
tx_fields['version'] = 1
sign_tx.write_int32(tx_fields['version'])

##then we write the number of transaction inputs, which is one
tx_fields['num_txin'] = 1
sign_tx.write_compact_size(tx_fields['num_txin'])

##then we write the actual transaction data
#'prevout_hash'
tx_fields['prevout_hash'] = tx_hash
sign_tx.write(tx_fields['prevout_hash']) #hash of the the transaction from which we want to redeem an output
#'prevout_n'
tx_fields['output_index'] = OUTPUT_INDEX
sign_tx.write_uint32(tx_fields['output_index']) #which output of the transaction with tx id 'prevout_hash' do we want to redeem?

##next comes the part of the transaction input. here we place the script of the *output* that we want to redeem
tx_fields['scriptSigHash'] = tx_info['txOut'][OUTPUT_INDEX]['scriptPubKey']
#first write the size
sign_tx.write_compact_size(len(tx_fields['scriptSigHash']))
#then the data
sign_tx.write(tx_fields['scriptSigHash'])

#'sequence'
tx_fields['sequence'] = 0xffffffff
sign_tx.write_uint32(tx_fields['sequence'])

##then we write the number of transaction outputs. we'll just use a single output in this example
tx_fields['num_txout'] = 1
sign_tx.write_compact_size(tx_fields['num_txout'])

##then we write the actual transaction output data
#we'll redeem everything from the original output minus TX_FEE
tx_fields['value'] = tx_info['txOut'][OUTPUT_INDEX]['value']-(TX_FEE*COIN)
sign_tx.write_int64(tx_fields['value'])

##this is where our scriptPubKey goes (a script that pays out to an address)
#we want the following script:
#"OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG"
address_hash = bc_address_to_hash_160(SEND_TO_ADDRESS)

#chr(20) is the length of the address_hash (20 bytes or 160 bits)
scriptPubKey = chr(opcodes.OP_DUP) + chr(opcodes.OP_HASH160) + \
chr(20) + address_hash + chr(opcodes.OP_EQUALVERIFY) + chr(opcodes.OP_CHECKSIG)

#first write the length of this lump of data
tx_fields['scriptPubKey'] = scriptPubKey
sign_tx.write_compact_size(len(tx_fields['scriptPubKey']))

#then the data
sign_tx.write(tx_fields['scriptPubKey'])

#write locktime (0)
tx_fields['locktime'] = 0
sign_tx.write_uint32(tx_fields['locktime'])

#and hash code type (1)
tx_fields['hash_type'] = SIGHASH_ALL
sign_tx.write_int32(tx_fields['hash_type'])

#then we obtain the hash of the signature-less transaction (the hash that we sign using our private key)
hash_scriptless = dsha256(sign_tx.input)

##now we begin with the ECDSA stuff.
## we create a private key from the provided private key data, and sign hash_scriptless with it
## we also check that the private key's corresponding public key can actually redeem the specified output

k = ecdsa_ssl.KEY()
k.generate(('%064x' % PRIVATE_KEY).decode('hex'))

#here we retrieve the public key data generated from the supplied private key
pubkey_data = k.get_pubkey()

#then we create a signature over the hash of the signature-less transaction
sig_data=k.sign(hash_scriptless)

#a one byte "hash type" is appended to the end of the signature (https://en.bitcoin.it/wiki/OP_CHECKSIG)
sig_data = sig_data + chr(SIGHASH_ALL)

#let's check that the provided privat key can actually redeem the output in question
if (bc_address_to_hash_160(public_key_to_bc_address(pubkey_data)) != tx_info['txOut'][OUTPUT_INDEX]['scriptPubKey'][3:-2]):
bytes = b58decode(SEND_TO_ADDRESS, 25)
raise RuntimeError, "The supplied private key cannot be used to redeem output index %d\nYou need to supply the private key for address %s" % \
(OUTPUT_INDEX, hash_160_to_bc_address(tx_info['txOut'][OUTPUT_INDEX]['scriptPubKey'][3:-2], bytes[0]))

##now we begin creating the final transaction. this is a duplicate of the signature-less transaction,
## with the scriptSig filled out with a script that pushes the signature plus one-byte hash code type, and public key from above, to the stack

final_tx = BCDataStream()
final_tx.write_int32(tx_fields['version'])
final_tx.write_compact_size(tx_fields['num_txin'])
final_tx.write(tx_fields['prevout_hash'])
final_tx.write_uint32(tx_fields['output_index'])

##now we need to write the actual scriptSig.
## this consists of the DER-encoded values r and s from the signature, a one-byte hash code type, and the public key in uncompressed format
## we also need to prepend the length of these two data pieces (encoded as a single byte
## containing the length), before each data piece. this length is a script opcode that tells the
## Bitcoin script interpreter to push the x following bytes onto the stack

scriptSig = chr(len(sig_data)) + sig_data + chr(len(pubkey_data)) + pubkey_data

#first write the length of this data
final_tx.write_compact_size(len(scriptSig))

#then the data
final_tx.write(scriptSig)

##and then we simply write the same data after the scriptSig that is in the signature-less transaction,
# leaving out the four-byte hash code type (as this is encoded in the single byte following the signature data)

final_tx.write_uint32(tx_fields['sequence'])
final_tx.write_compact_size(tx_fields['num_txout'])
final_tx.write_int64(tx_fields['value'])
final_tx.write_compact_size(len(tx_fields['scriptPubKey']))
final_tx.write(tx_fields['scriptPubKey'])
final_tx.write_uint32(tx_fields['locktime'])

#prints out the final transaction in hex format (can be used as an argument to bitcoind's sendrawtransaction)
print final_tx.input.encode('hex')





 (A,B) -> B 보다 A -> (B->B) 가 더 좋다?


사실 명령형 파라다임에 적폐인 나로써는, 함수형 파라다임의 체득이란 살아 생전 가능할까 싶다. ㅎㅎ 특히 함수형 파라다임을 체득(머리로 이해가 아닌) 하기 위해서 제일 먼저 만나는 관문을 "커링"이라고 보는데...왜 저 짓을 하냐? 저게 진짜 더 좋긴해? 를 넘어서서 저렇게 하는게 너무 당연하게 느껴지는 퀀텀점프의 경험을 하고 싶다.

* 이 글에서는 부분적용함수와의 차이는 무시한다. 부분적용함수도 커링이라고 친다.

함수형 언어와 함수형 프로그래밍이 다르듯이 파이썬이나 자바스크립트로도 충분히 커링이 가능하다.

// 파이썬
def curry(func, var):
y = var
def f(x):
return func(x, y)
return f

커링을 사용한것 과 안 한 소스부터 일단 보면. (포인트의 이동과 회전에 관한 함수로 이루어졌다)

C 소스 (커링 사용안함) 

typedef point (*converter_t) (point);

point trans(double dx, double dy, point pt) {
point result = pt;
result.x += dx;
result.y += dy;
return result;
}

// 원래 sin,cos 이용해야하지만 그냥 이렇다고 치자.
point rotate(double theta, point pt) {
point result = {0,0}
result.x = pt.x + theta
result.y = pt.y + theta
return result
}
point trans_by_config (config_t config, point pt){
return trans(config.x, config.y, point pt);
}

point rotate_by_config (config_t config, point pt){
return rotate(config.theta, point pt);
}

point convert_by_config(config_t config, point pt){
return trans_by_config(config, rotate_by_config(config, pt));
}
void map_to_points(converter_t conv, size_t n, point * in, point * out) {
unsigned int i = 0;
for (i = 0 ; i < n ; i++) out[i] = conv(in[i]);
}

Javascript 소스 (커링 사용)  

function compose(f, g) { return function(x) { return f(g(x))}}

var trans = function(dx,dy) {
return function(point) {
var result = clone(point)
result.x += dx;
result.y += dy;
return result;
}
}

var transByConfig = function(config){
return trans(config.x , config.y);
}

//rotate 생략

var convertByConfig = function(config) {
return compose(transByConfig(config), rotateByConfig(config));
}

var converted_rect = unit_rect.map(convertByConfig(config));

하스켈 소스 (커링 사용, 타입이 맞아야 한다. 강력한 제한을 둠으로써 완전하게 한다) 

type Coord = (Double, Double)

data Config = Config { rotAt :: Coord
, theta :: Double
, ofs :: (Double, Double)
}

type CoordConverter = Coord -> Coord

trans :: (Double, Double) -> CoordConverter
trans (dx, dy) = \(x,y) -> (x+dx, y+dy)

rotate :: Double -> CoordConverter
rotate t = \(x, y) -> ( x + t, y + t)

transByConfig :: Config -> CoordConverter
transByConfig config = trans (ofs config)

rotateByConfig :: Config -> CoordConverter
rotateByConfig config = postTrans . rotate (theta config) . preTrans where
rotateAt = rotAt config
preTrans = trans (rotate pi $ rotateAt)
postTrans = trans rotateAt

convertByConfig :: Config -> CoordConverter
convertByConfig config = transByConfig config . rotateByConfig config
main :: IO ()

main = do
let config = Config { rotAt = (0.5,0.5), theta = pi / 4, ofs = (-0.5, -0.5) }
let unitRect = [(0,0),(0,1),(1,1),(1,0)]
let convertedRect = map (convertByConfig config) unitRect

"하스켈로 배우는 함수형 프로그래밍"이라는 책에서 발췌한 코드인데, 해당 책에는 이렇게 쓰여져 있다.
"무엇인가와 좌표를 받아서 좌표를 반환하는 것보다, 무엇인가를 받아서 좌표를 받아 좌표를 반환하는 함수를 반환하는 것이 보다 일반적이고 간단한 조합 방법을 도입할 수 있게 된다" 라고 나온다. 

?? 그냥 위의 C 코드에서 함수포인터 만들때 conifg 매개변수도 시그니처로 넣어주면 되는 거 아냐? 라고 반발심도 생기며, 그냥 함수 만들면 되지, 굳이 실행 시점에 만들어지는 함수가 무슨 의미가 있는지, 뭐가 그렇게 좋은지에 대한 "아~~~하" 체험을 하기가 힘들다. 그냥 " 아 그렇군요" 정도는 가능하지만..

대부분의 커링에 관한 블로그에서도 인자수를 줄여주는 마법(?)을 보여주며, 커링 좋지? 라고 말하고 끝난다. 
이건 그냥 그 사람이 커링이 먼지 안 것이지, 그게 정말 좋은지에 대한 설명으로는 부족하다. 

이럴 때~~~

하스켈을 해 봐야 한다. 명령형 언어의 경우 그냥 함수를 몇개 더 만들면 된다. add2 함수 까짓거 만들어도 되지만 그건 추상이 아니다. 그것은 구체적이다. 따라서 커리 만드는데 행사코드가 덕지덕지 붙는다면 그냥 add2, add3 만드는게 낫다.즉 애초에 그런언어로 커리를 공부하면 맘에 와닿을리 없다.

add 에 add2,add3 등의 함수들을 직접만드는 것은 재활용이 아니다. 함수 시그니처가 재각각인 것들을 합성하는 것은 불가능하다. 함수를 합성 하기 위해서는 매개변수가 작고 타입이 같을 수록 좋다. 즉 x -> x 인 함수들 끼리는 합성하기가 너무 좋을 것이다. 존재하는 함수를 최대한 재활용하는것이 함수형이다. 하스켈에서는 아예 매개변수를 1개로 강제하였다. 즉 하스켈에서는 함수가 아예 커링화 되어있다. 하스켈은 2개의 입력값을 받는 함수가 없으며 만들 수도 없다. 함수합성에 대한 강력한 의지가 보인다. (콜스택에 매개변수를 항상 넘겨서 호출하는 것보다, 미리 내부에 정해져있으면 효율적이기도 할 것이다)

let add x y = x + y

이러한 함수도 2개의 x y 를 받는 함수가 아니다. 따라서 아래와 같은 식이 개발자의 추가 노력없이 가능하다.

let add x y = x + y
map (add 1) [1,2,3,4]

 x 를 1로 먼저 입력되어서 만들어진 함수 add y = 1 + y 를 통해 리스트가 입력되어진다.

결국 함수를 재활용하는 함수 합성을 많이 해 봐야 합수합성의 기본도구인 커링,부분함수가 납득(체득)이 되는것 같다.. 이 글을 썼다고 혹은 읽는다고 체득이 되지 않을 것이다. 백날 "자바/파이썬/자바스크립트/스칼라를 통한 함수형 프로그래밍" 류의 책을 읽는 것 보다, 가장 빠른 길은 하스켈를 공부하고, 하스켈 코드를 짜 보는 것 같다. 프로젝트를 하나 해보면 더 좋고~

다음 글은 Why Sum Types Matter in Haskell  글을 번역한 글임을 밝힙니다.

Why Sum Types Matter in Haskell

이 글에서는 Haskell의 Algebraic Data Types 자세히 살펴볼 것 입니다. Algebraic Data Types를 이해하는 열쇠는 기존 타입을 결합하여 새로운 타입을 생성하는 방법을 이해하는 것입니다. 고맙게도 두 가지 방법 밖에 없는데요. 다른 타입을 'and'과 결합하여 만든 타입을 Product types라고합니다. 'or'을 사용하여 결합 된 타입을 Sum types이라고합니다. 대부분의 프로그래밍 언어는 Product 타입을 사용합니다. Sum types은 개념적으로 단순하지만 모델링 데이터의 경우 매우 강력한 도구임을 입증합니다. 이 글의 끝 부분에서는 Sum types을 사용하여 복잡한 데이터의 디자인을 극적으로 단순화하는 방법을 살펴 보겠습니다.

Product Types — Combing types with ‘and’

Product types은 두 개 이상의 기존 타입을 'and'로 결합하여 생성됩니다. 몇 가지 일반적인 예는 다음과 같습니다.

  • 분수는 분자 (정수)와 분모 (또 다른 정수)로 정의 할 수 있습니다.
  • 거리 주소는 숫자 (Int) 및 거리 이름 (String) 일 수 있습니다.
  • 우편 주소는 거리 주소 및 도시 (문자열) 및 상태 (문자열) 및 우편 번호 (Int) 일 수 있습니다.

"Product type"이라는 이름은 좀 복잡하게 들릴 수 있지만 타입을 정의하는 가장 일반적인 방법입니다. 거의 모든 프로그래밍 언어가 Product type을 지원합니다. 가장 간단한 예제는 C의 구조체입니다. 다음은 author_name에 다른 구조체를 사용 하는 책의 C 구조체 예제입니다 .

struct author_name {
char *first_name;
char *last_name;
};
struct book {
author_name author;
char *isbn;
char *title;
int year_published;
double price;
};

이 예제에서는 author_name 타입이 두 개의 String을 결합하여 만들어 짐을 알 수 있습니다. 그 다음에 있는 book 타입은AUTHOR_NAME , 두 개의 문자열, int 형과 double 형을 결합합니다. author_name 과 book 은 모두 'and'로 다른 유형을 결합하여 만듭니다. C의 구조체는 클래스 및 JSON을 비롯한 거의 모든 언어에서 유사한 타입들의 조상이죠. Haskell에서 우리의 book 예제는 다음과 같이 보일 것입니다.

data AuthorName = AuthorName String String
data Book = Author String String Int

Record Syntax를 사용하여 C 구조체를 더 연상시키는 Book 버전을 작성하겠습니다.

data Book = Book 
{author :: AuthorName
,isbn :: String
,title :: String
,year :: Int
,price :: Double }

Book 과 AuthorName 은 모두 product types의 예이며 거의 모든 최신 프로그래밍 언어에서 analog를 사용합니다. 매혹적인 것은 대부분의 프로그래밍 언어에서 타입을 'and'로 결합하는 것이 종종 새로운 타입을 만드는 유일한 방법이라는 것입니다.

The curse of product types: Hierarchical design.

"and"를 사용하여 기존 타입을 결합하여 새로운 타입을 만드는 것은 소프트웨어 설계의 흥미로운 모델로 연결됩니다. 아이디어를 추가하여 확장 할 수 있다는 제한 때문에 우리는 상상할 수 있는 타입의 가장 추상적인 표현부터 시작하여 상향식 디자인으로 제한됩니다. 이것은 클래스 계층의 관점에서 소프트웨어를 설계하기위한 기초입니다.

예를 들어 Java를 작성 중이며 Book Store에 대한 데이터 모델링을 시작한다고 가정합니다. 의 Book 예제로 시작 합니다 ( Author 클래스가 이미 있다고 가정 )

public class Book {
Author author;
String isbn;
String title;
int yearPublished;
double price;
}

이것은 우리가 서점에서 레코드판을 판매하기를 원한다는 것을 알기 전까지 훌륭합니다. VinylRecord 의 기본 구현은 다음과 같습니다.

public class VinylRecord {
String artist;
String title;
int yearPublished;
double price;
}

VinylRecord은 book과 비슷하지만 몇가지 문제를 야기 시킵니다처음에는 Author 타입을 다시 사용할 수 없습니다 그 이유는 일부 아티스트는 이름이 없기 때문입니다. 우리는 "Elliott Smith" 의 저자 타입을 사용할 수 있지만 "The Smiths" 는 사용할 수 없습니다. 전통적인 계층적 디자인에서는 제작자 와 아티스트의 불일치 에 대해 이 문제에 대한 좋은 해답이 없습니다 또 다른 문제는 VinylRecords 에 ISBN 번호가 없다는 것입니다.

디자인 관점에서 우리는 검색 가능한 인벤토리를 지원할 VinylRecords 및 Books 를 나타내는 단일 타입을 원합니다 "and" 만을 사용하여 타입을 작성해야 하는 필요성 때문에 우리는 레코드와 서적이 공통으로 가지고있는 모든 것을 설명하는 추상화를 개발해야합니다. 그런 다음 개별 클래스 의 차이점 만 구현합니다 이것이 상속 의 기본 아이디어이죠. 다음으로 VinylRecord 와 Book 의 수퍼클래스 인 StoreItem 클래스를 만듭니다 . 다음은 리팩터링된 Java 클래스입니다.

public class StoreItem {
String title;
int yearPublished;
double price;
}
public class Book extends StoreItem{
Author author;
String isbn;
}
public class VinylRecord extends StoreItem{
String artist;
}

이 솔루션은 작동은 합니다. StoreItems 로 작업 할 코드를 생성 한 다음 조건문을 사용하여 Book 및 VinylRecord 를 처리 할 수 ​​있습니다 .

이제 판매 할 수 있는 다양한 종류의 장난감 인형을 주문 했다고 가정합니다. 다음은 기본적인 CollectibleToy 클래스입니다.

public class CollectibleToy {
String name;
String description;
double price;
}

모든 것을 작동 시키려면 모든 코드를 다시 리팩토링해야합니다! 이제 StoreItem 은 모든 항목이 공통으로 공유하는 유일한 값이기 때문에 가격 속성 만 공통적으로 가질 수 있습니다 즉, VinylRecords와 Books 사이의 일반적인 속성은 해당 클래스로 돌아 가야합니다. 또는 StoreItem 에서 상속받은 새로운 클래스를 만들 수 있겠지요. 즉 VinylRecord 및 Book 의 수퍼 클래스말 입니다 근데 CollectibleToy 의 name 속성은 title 과 무엇이 다른가요? 어쩌면 우리는 인터페이스를 만들어야 할 것입니다. 대신 우리의 모든 항목! 핵심은 비교적 단순한 경우임에도 엄격하게 제품 타입을 설계하는 것이 꽤나 복잡해질 수 있다는 것입니다.

이론적으로 클래스 계층 구조를 생성하는 것은 우아하고 세계의 모든 것이 어떻게 상호 연관되어 있는지에 대한 추상을 포착합니다. 실제로는 사소한 클래스 계층 구조를 만드는 일은 디자인 문제로 가득합니다. 이러한 모든 도전의 근원은 대부분의 언어에서 타입을 결합하는 유일한 방법은 "and" 이라는것 입니다. 이로 인해 우리는 극단적인 추상화에서 시작하여 아래로 이동해야합니다. 불행히도, 실생활은 우리가 원하는 것보다 더 복잡한 이상한 요소들로 가득차 있습니다

(역주: 누구나 저런 경우를 만나왔을 것이고 고민 또한 했을 거 같습니다. 제 경우 자바나 파이썬 할 때 저런 지경이면 먼가 깨름칙 한 기분을 앉고서는 그냥 다 따로 만듭니다. 특히 요즘은 마이크로서비스로 작은 사이즈의 서버에서 돌아가는 프로그램을 짜 왔기 때문인지 별 고민없이 따로 만들었던거 같습니다.  계층적 설계는 많은 경우에 오버스펙이 될 여지가 크기 때문이죠.) 

Sum types: Combining Types with ‘or’

Sum 타입 은 두 가지 타입을 결합한다는 점에서 놀랍도록 강력한 도구입니다. 타입을 "or" ( 둘 중 하나 !!!) 로써 결합하는 예는 다음과 같습니다. 

  • 주사위는 6면 다이 또는 20면 다이입니다.
  • 논문은 사람 (String) 또는 사람 그룹 ([String])으로 authored 됩니다.
  • 목록은 빈 목록 ([])이거나 다른 목록 (a : [a])에 추가 된 항목입니다.

가장 간단한 Sum 타입은 Bool입니다 .

data Bool = False | True

BOOL 의 인스턴스는 False 데이터 생성자 또는 True  데이터 생성자 둘 중 하나입니다. 이것은 Sum타입이 많은 다른 프로그래밍 언어에 존재 하는 열거 타입 생성 방법의 하스켈 버전이라는 잘못된 인상을 줄 수도 있습니다. 다음은 Sum타입을 사용하여 Name 타입을 생성 할 수있는 예입니다 중간 이름을 가진 이름의 가능성과 그렇지 않은 이름의 가능성을 모델링 할 수 있음을 주목하십시오. 대부분의 다른 언어 에서는 가운데 이름이 없는 경우 Null 값을 지정해야 합니다 . Sum 타입을 사용하여 각 사례를 명확하게 모델링 할 수 있습니다.

type FirstName = String
type LastName = String
type MiddleName = String
data Name = Name FirstName LastName 
| NameWithMiddle FirstName MiddleName LastName

이 예제에서는 두 개의 String으로 구성된 Name 또는 세 개의 String으로 구성된 NameWithMiddle 중 하나가 될 수있는 두 개의 다른 타입 생성자를 사용할 수 있습니다 여기서 두 가지 타입 중 "or"을 사용하면 타입의 의미를 표현할 수 있습니다. 우리가 타입을 결합하는 데 사용하는 도구에 "or"을 추가함으로써, Sum 타입이 없는 다른 프로그래밍 언어에서는 사용할 수 없는 가능성의 세계를 하스켈에서는 해내지 말입니다. 이전 섹션의 몇 가지 문제를 해결할 수 있는 강력한 Sum 타입을 확인 하십시오.

흥미로운 지점은 author와 artist의 차이입니다. 이 예제에서는 책 저자의 이름을 이름과 성으로 가정 할 수 있기 때문에 두 가지 타입이 필요했지만 레코드를 만드는 아티스트는 사람의 이름이나 밴드 이름이 될 수 있습니다. Product 타입만으로 이 문제를 해결하는 것은 까다로운 일입니다. Sum타입을 사용하면 이 문제를 쉽게 처리 할 수 ​​있습니다. 우리는 Author 또는 Artist 이기도 한 Creator 타입을 만들 수 있습니다.

data Creator = AuthorCreator Author | ArtistCreator Artist

우리는 이미 Author를 이름으로 정의 할 수 있는 Name 형식을 가지고 있습니다.

data Author = Author Name

아티스트는 사람의 이름이나 밴드 이름이 될 수 있기 때문에 아티스트가 더 까다 롭습니다. 이 문제를 해결하기 위해 다른 합계타입을 사용합니다.

data Artist = Person Name | Band String

이것은 좋은 해결책이지만 실생활에서 나타나는 더 까다로운 경우는 어떨까요? 예를 들어, H.P. Lovecraft와 같은 저자를 잊었습니다! 우리는 "하워드 필립스 러브 크래프트"를 사용하도록 강요 할 수 있지만, 왜 우리 자신이 우리의 데이터 모델에 의해 강요를 당해야 하는지요. 유연해야합니다. Name에 또 다른 데이터 생성자를 추가하여 쉽게 이 문제를 해결할 수 있습니다.

data Name = Name FirstName LastName
| NameWithMiddle FirstName MiddleName LastName
| TwoInitialsWithLast Char Char LastName

이제 다른 종류의 방법에 대해 걱정 할 필요가 없는 타입들이 정의되어 있습니다 Artist 및 Author 타입 모두에서 이름을 한 곳에서 정의 함으로써 이익을 얻으므로 동시에 코드 재사용의 이점을 누릴 수 있습니다 이 모든 것의 예로 HP Lovecraft Creator가 있습니다.

hpLovecraft :: Creator
hpLovecraft = AuthorCreator(Author
(TwoInitialsWithLast
'H' 'P' "Lovecraft"))

우리의 데이터 생성자는 약간 장황 할 수 있지만 실제로는 이 부분을 '추상화'하는 기능을 사용하게 될 것입니다. 이 솔루션이 제품 타입별로 강제된 계층 적 디자인을 사용하여 생성하는 솔루션과 어떻게 비교되는지 생각해보십시오. 계층적 디자인 관점에서 이름 속성이 세 개인 이름 수퍼 클래스가 있어야합니다. 세 가지 타입의 이름 만 공유 할 수있는 유일한 속성이기 때문입니다. 사용된 세 가지 데이터 생성자 각각에 대해 별도의 하위 클래스가 필요합니다. 그때도 성 (姓)이 "Louis CK"와 같은 이름이 우리 모델을 완전히 깨뜨릴 것입니다. Sum 타입을 사용하면 쉽게 해결할 수 있습니다.

data Name = Name FirstName LastName
| NameWithMiddle FirstName MiddleName LastName
| TwoInitialsWithLast Char Char LastName
| FirstNameWithTwoInits FirstName Char Char

Product 타입 관점에서의 유일한 솔루션은 사용되지 않는 특성 목록이 늘어나는 한심한 Name 클래스 를 만드는 것입니다 .

public class Name {
String firstName;
String lastName;
String middleName;
char firstInitial;
char middleInitial;
char lastInitial;
}

이것은 모든 것이 올바르게 작동하도록 하기 위해 많은 추가 코드가 필요합니다. 또한, 우리는 이름 이 유효한 상태에 있음을 보장하지 않습니다. 이러한 모든 속성이 값을 가지고 있으면 어떻게 될까요? 자바 타입 검사기는 Name 객체가 이름에 대해 지정한 제약 조건을 충족하는지 확인할 수 없습니다 하스켈에서는 정의한 명시적 타입만 존재한다는 것을 알고 있습니다.

Putting together our book store

이제 서점 문제를 다시 살펴보고 Sum 타입으로 생각하는 것이 어떻게 도움이되는지 확인해 봅시다. 강력한 Creator 타입을 사용하여 책을 다시 쓸 수 있습니다.

data Book = Book 
{author :: Creator
, isbn :: String
, bookTitle :: String
, bookYear :: Int
, bookPrice :: Double}

VinylRecord 타입 도 정의 할 수 있습니다.

data VinylRecord = VinylRecord 
{ artist :: Creator
, recordTitle :: String
, recordYear :: Int
, recordPrice :: Double}

이제 우리는 쉽게 StoreItem 타입을 생성 할 수 있습니다.

data StoreItem = BookItem Book | RecordItem VinylRecord

다시 한번 우리는 CollectibleToy 에 대해 잊어 버렸습니다 Sum 타입으로 인해이 데이터 타입을 추가하고 StoreItem 타입을 확장하여 쉽게 포함 할 수 있습니다.

data CollectibleToy = CollectibleToy 
{ name :: String
, descrption :: String
, toyPrice :: Double}

우리는 쉽게 할 수 리팩토링 STOREITEM을 하나 더 '또는'추가

data StoreItem = BookItem Book
| RecordItem VinylRecord
| ToyItem CollectibleToy

마지막으로, 우리는 모든 항목의 가격을 가져 오는 가격 함수를 작성하여 이러한 타입 모두에서 작동하는 함수를 빌드하는 방법을 보여줍니다.

price :: StoreItem -> Double
price (BookItem book) = bookPrice book
price (RecordItem record) = recordPrice record
price (ToyItem toy) = toyPrice toy

합계 타입을 사용하면 타입에 따라 표현을 크게 표현할 수 있지만 유사한 타입의 그룹을 만드는 편리한 방법을 제공합니다.


C / C++ 
:  극한의 상황에서도 돌아가야해. 메모리관리,쓰레드관리,리소스관리를 마이크로 컨트롤하여 솔루션을 만드는게 진정한 개발자라고 할 수 있지
자바 : C++ 은 반쪽짜리 객체지향에다가 오류의 대부분이 포인터 관련인 어처구니 없는 적폐의 언어. 보통 사람의 능력으로는 항상 문제가 발생 할 소지가 많은 언어. 이제 누구나 개발하는 시대이지. 기본적인 것은 플랫폼에서 해주고, 대규모어플리케이션도 편하면서도 강력하게 만들게 해주는 생산성 높은 자바. 대세의 언어가 된데에는 이유가 있는법
파이썬: 얘들아 쉽고 빠르게 개발하는 시대가 왔어. 니들이 진짜 만들고 싶은게 뭐야? 그거에 집중하라고. 초딩들처럼 유치하게 로우레벨 가지고 투닥대지말고~
하스켈,스칼라: C++/JAVA는 그냥 그저그런 지적능력을 가진 애들이나 하는 대중적이고 비효율적인 방식. 지능수준이 높다면 완전한 솔루션을 만들 수 있는 함수형을 선택하는 것은 진리.

라고 주장하는 사람들이 틀렸다고 말하지 않겠습니다. 그냥 자신의 생각일 뿐이니 (과격하지만) 그려려니 하고 싶습니다.  모든 사람이 같은 생각을 하는게 더 무서우니까요.



"리소스,속도 관리 시대"  ->  "생산성, 대중성,적합성 시대" -> "끌리는 것을 선택하는 시대" 


그래도 전체적으로는 언어에 대한 강박관념이 점점 옅어지고 있는거 같습니다."리소스와 속도를 짜내기 위한 언어" 선택의 시대에서  "생산성,대중성,적합성의 시대"를 지나서  "그냥 끌리는 언어를 선택. 오~ 이거 쿨한데" 하는 시대로 이제 접어 들지 않았나 생각합니다. (마이크로 서비스가 한몫을 하며...음 당연히 예외도 있을 겁니다)  "생산성의 시대" 에 진입 하였을 당시에 " 직접 모든것을 다뤄야지. 너 VM에서 하는 것을 어떻게 믿냐?" 라던 사람들이 이제 대부분 찌그러졌습니다. "끌리는 언어를 선택" 하는 시대로 진입하는 현재 "니 맘대로 하냐? 적합해그냥 대세를 따르는건 어때?  "라며 딴지 거는 사람들이 아직은 많을 겁니다만 앞으로는 이것도 점차 희미해 지리라 봅니다.

즉 스칼라,클로저,러스트,Golang,얼랭,코틀린을 개인의 흥미로 선택해도 이해해 주는 시대. 그 아름다운 시대에 접어 들었습니다. 

기대합니다.


* 이 글은 누군가를 이해 시키기 위한 글이 아닙니다. 블록체인을 처음 만난 개발자가 비트코인의 핵심이라 할 수 있는 거래(검증)에 대한 공부/삽질을 하며 겪는 여정으로써 굉장히 불친절한 글이라는 것을 말씀드립니다. 그냥 제 삽질일기~




그림 7-5에서는 단지 32바이트 크기의 해시 4개의 길이(총 128바이트)인 머클 경로를 생성함으로써 거래 K 가 블록 내에 포함되어 있다는 사실을 노드가 입증할 수 있다는 것을 보여준다. 머클패스는 HL .......(그림에서 파랑) 등등 이렇게 4개의 해시로 구성되어 있다. 인증 경로로 제공된 이 4개의 해시를 가지고 어떤 노드라도 4개의 해쉬에 대응하는 해시쌍인 H .... (그림에서 점선) 와 머클트리 루트를 계산함으로써 HK가 머클 루트에 포함되어 있다는 사실을 증명 할 수 있다.


마스터링 비트코인의 243p 나온 글이다. 해당 챕터를 읽어보면 머클트리가 왜 필요한지, 머클패스를 이용해서 검증하는 기술에 대해서는 이해하기 어렵지 않지만, 그럼 머클 패스를 던져주는 주체는 누구 인가에 대해서는 아리송 할 것이다. 즉 단위 기술의 이해 보다는 전반적인 스토리 이해의 부재를 느낄 것이다. (사실 저 책의 내용중 오역도 많은듯 하고, 실 저자가 친철하게 쓰지 못한 내용도 있다는 핑계를 대고 싶다. 뭐 책 두께가 한 800페이지는 되야 가능하겠지만.. 블록체인의 성경과 같은 보석같은 책에 대해 불손한 자세를 보인 것 같아서 자진 검열함) 

즉 저 책으로 처음 블록체인(비트코인)을 마주친다면 "HK가 검증 대상이면,그 쌍이 HL임을 어떻게  아는지? 이미 머클트리 상에 HK, HL 이 같이 있는 것을 확신 한다면 보낼 필요도 없는게 아닌지..?? 모든 노드가 참여하는지? 누가 보내는지?? 언제 보내는지? HK 거래는 현재 거래인가, 아니면 현재거래를 검증하기 위한 모든 과거 트랜잭션 중 하나 인가" 라는 합당한 의문들이 생기기 마련이다.

이에 대해서는 아래와 같이 추측을 했다. (사실이 아니다. or 아닐 수 도 있다.) 

"주변의 p2p의 상대 노드 중 풀노드는 거래 데이터를 받고 자신의 전체 블록체인과 대조하여 해당 거래의 유효성 확인하지만  p2p 상대가 SPV 이면 거래 데이터와 함께 "머클패스"를 보내서 검증에 참여 시킨다.

하나더

책에 있는 거래K도  현재 발생된 거래 그 자체라고 한참 오해했는데, 아니라고 추측 했다.

"현재 거래에 사용된 UTXO or 입력값 의 근거 과거 거래들 중 하나" 일 것이다.  

지금 거래의 입력값(or UTXO) 으로 사용된 코인이 합당한 것인지 어떻게 검증 할 수 있는지가 궁금해졌다.일단 현재 거래의 입력값을 검증을 위해서, 해당 입력값이 어떤 입력값에서 부터 나왔는지에 대한 내용을 블록체인에서 찾아야 한다고 짐작 할 수 있는데 아래의 두가지 궁금즘을 해결하기 위해서 먼저 비트코인을 구성하는 것들 중  4가지 구조를 한눈에 살펴보는것으로 시작하자.

@ 입력값과 UTXO 와의 구조적 차이점 (UTXO를 가지고 입력값 만들기? 출력값에서 UTXO 생성하기?) 
@ 기술적으로 입력값 or UTXO가 완성된 블록안의 머클트리에 존재하는 거래 해쉬와 어떻게 연관되는지


1. UTXO 구조

https://github.com/zoinofficial/zoin/blob/master/src/coins.h 

 CTransaction 의 잘라내진 (Pruned) 버전: 오직 메타데이터 및 UTXO 용으로 유지 

 시리얼라이즈 포맷:

  - VARINT(nVersion)

  - VARINT(nCode)

  - unspentness bitvector, for vout[2] and further; least significant byte first

  - the non-spent CTxOuts (via CTxOutCompressor)

  - VARINT(nHeight)

nCode 는 다음으로 이루어져있다.

  - bit 1: IsCoinBase()

  - bit 2: vout[0] is not spent

  - bit 4: vout[1] is not spent

  - The higher bits encode N, the number of non-zero bytes in the following bitvector.

  - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at

        least one non-spent output).

Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e

                 <><><--------------------------------------------><---->

                   |       \                            |                             /

              version   code             vout[1]                  height

 

     - version = 1

     - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow)

     - unspentness bitvector: as 0 non-zero bytes follow, it has length 0

     - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35

                * 8358: compact amount representation for 60000000000 (600 BTC)

                * 00: special txout type pay-to-pubkey-hash

                * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160

     - height = 203998

 Example:                    

                01 09 0440 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b

           <><><--><---------------------------------------------------------><---------------------------------------------------><---->

             /    \      \                                |                                                                                    |                       /

    version  code  unspentness       vout[4]                                                                       vout[16]           height

 

   - version = 1

   - code = 9 (coinbase, neither vout[0] or vout[1] are unspent,

                 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow)

   - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent

   - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee

              * 86ef97d579: compact amount representation for 234925952 (2.35 BTC)

              * 00: special txout type pay-to-pubkey-hash

              * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160

   - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4

               * bbd123: compact amount representation for 110397 (0.001 BTC)

               * 00: special txout type pay-to-pubkey-hash

               * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160

   - height = 120891

 

- UTXO 는 블록과 별개로 LevelDB에 저장된다. 사용자의 지갑에 남은 돈이 얼마인지 빠르게 알 수 있게 해 준다.
입력값에 사용된 UTXO 는 데이타베이스에서 삭제될 것이고, 출력값은 새로운 UTXO를 데이터베이스에 입력 할 것이다.
- 아직 잘 모르겠다.


gettxout "txid" n ( include_mempool )

Returns details about an unspent transaction output.

Arguments:
1. "txid"             (string, required) The transaction id
2. "n"                (numeric, required) vout number
3. "include_mempool"  (boolean, optional) Whether to include the mempool. Default: true.     Note that an unspent output that is spent in the mempool won't appear.

Result:
{
  "bestblock" : "hash",    (string) the block hash
  "confirmations" : n,       (numeric) The number of confirmations
  "value" : x.xxx,           (numeric) The transaction value in BTC
  "scriptPubKey" : {         (json object)
     "asm" : "code",       (string) 
     "hex" : "hex",        (string) 
     "reqSigs" : n,          (numeric) Number of required signatures
     "type" : "pubkeyhash", (string) The type, eg pubkeyhash
     "addresses" : [          (array of string) array of bitcoin addresses
        "address"     (string) bitcoin address
        ,...
     ]
  },
  "coinbase" : true|false   (boolean) Coinbase or not
}

Examples:

Get unspent transactions
> bitcoin-cli listunspent 

View the details
> bitcoin-cli gettxout "txid" 1

As a json rpc call
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "gettxout", "params": ["txid", 1] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/

- txid (거래아이디)와 vout 숫자(해당거래에서 돈 줄 사람중 몇번째)로 출력정보(UTXO)를 얻을 수 있구나.
- 출력정보안의 scriptPubKey 를 얻는군
- 블럭해쉬도 기본적으로 리턴해주네.

2. 거래 구조 (인용 : 책 '비트코인, 블록체인과 금융의 혁신')

 거래 구조안에는 입력값/출력값/기타등등이 있다.

입력값안에는 해제스크립트가 있고 (근데 출력값 인덱스는 뭐지?)

출력값안에는 잠금 스크립트가 있다.

하나의 거래안의  잠금 스크립트를 해제스크립트로 푸는 건가?  (아니다) 


오호~이전 UTXO를 잠그고 있군? 그럼 이전 거래를 잠그고 현재 거래에서 해제시킨다는 건가?

근데 스크립트가 뭐지?  @.@

쉽게 설명하는 블록체인, 스크립트란 뭔가요? [비트코인의 언어]
쉽게 설명하는 블록체인, 비트코인 거래의 원리와 과정 - 1
쉽게 설명하는 블록체인, 비트코인 거래의 원리와 과정 - 2 [거래의 입력값과 출력값]
비트코인 시스템에서 사용하는 암호 기술(II) - 거래 검증

gettransaction "txid" ( include_watchonly )

Get detailed information about in-wallet transaction <txid>

Arguments:
1. "txid"                  (string, required) The transaction id
2. "include_watchonly"     (bool, optional, default=false) Whether to include watch-only addresses in balance calculation and details[]

Result:
{
  "amount" : x.xxx,        (numeric) The transaction amount in BTC
  "fee": x.xxx,            (numeric) The amount of the fee in BTC. This is negative and only available for the 
                              'send' category of transactions.
  "confirmations" : n,     (numeric) The number of confirmations
  "blockhash" : "hash",  (string) The block hash
  "blockindex" : xx,       (numeric) The index of the transaction in the block that includes it
  "blocktime" : ttt,       (numeric) The time in seconds since epoch (1 Jan 1970 GMT)
  "txid" : "transactionid",   (string) The transaction id.
  "time" : ttt,            (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)
  "timereceived" : ttt,    (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)
  "bip125-replaceable": "yes|no|unknown",  (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);
                                                   may be unknown for unconfirmed transactions not in the mempool
  "details" : [
    {
      "account" : "accountname",      (string) DEPRECATED. The account name involved in the transaction, can be "" for the default account.
      "address" : "address",          (string) The bitcoin address involved in the transaction
      "category" : "send|receive",    (string) The category, either 'send' or 'receive'
      "amount" : x.xxx,                 (numeric) The amount in BTC
      "label" : "label",              (string) A comment for the address/transaction, if any
      "vout" : n,                       (numeric) the vout value
      "fee": x.xxx,                     (numeric) The amount of the fee in BTC. This is negative and only available for the 
                                           'send' category of transactions.
      "abandoned": xxx                  (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 
                                           'send' category of transactions.
    }
    ,...
  ],
  "hex" : "data"         (string) Raw data for transaction
}

Examples:
> bitcoin-cli gettransaction "1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d"
> bitcoin-cli gettransaction "1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d" true
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "gettransaction", "param=
getrawtransaction "txid" ( verbose )

NOTE: By default this function only works for mempool transactions. If the -txindex option is
enabled, it also works for blockchain transactions.
DEPRECATED: for now, it also works for transactions with unspent outputs.

Return the raw transaction data.

If verbose is 'true', returns an Object with information about 'txid'.
If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.

Arguments:
1. "txid"      (string, required) The transaction id
2. verbose       (bool, optional, default=false) If false, return a string, otherwise return a json object

Result (if verbose is not set or set to false):
"data"      (string) The serialized, hex-encoded data for 'txid'

Result (if verbose is set to true):
{
  "hex" : "data",       (string) The serialized, hex-encoded data for 'txid'
  "txid" : "id",        (string) The transaction id (same as provided)
  "hash" : "id",        (string) The transaction hash (differs from txid for witness transactions)
  "size" : n,             (numeric) The serialized transaction size
  "vsize" : n,            (numeric) The virtual transaction size (differs from size for witness transactions)
  "version" : n,          (numeric) The version
  "locktime" : ttt,       (numeric) The lock time
  "vin" : [               (array of json objects)
     {
       "txid": "id",    (string) The transaction id
       "vout": n,         (numeric) 
       "scriptSig": {     (json object) The script
         "asm": "asm",  (string) asm
         "hex": "hex"   (string) hex
       },
       "sequence": n      (numeric) The script sequence number
       "txinwitness": ["hex", ...] (array of string) hex-encoded witness data (if any)
     }
     ,...
  ],
  "vout" : [              (array of json objects)
     {
       "value" : x.xxx,            (numeric) The value in BTC
       "n" : n,                    (numeric) index
       "scriptPubKey" : {          (json object)
         "asm" : "asm",          (string) the asm
         "hex" : "hex",          (string) the hex
         "reqSigs" : n,            (numeric) The required sigs
         "type" : "pubkeyhash",  (string) The type, eg 'pubkeyhash'
         "addresses" : [           (json array of string)
           "address"        (string) bitcoin address
           ,...
         ]
       }
     }
     ,...
  ],
  "blockhash" : "hash",   (string) the block hash
  "confirmations" : n,      (numeric) The confirmations
  "time" : ttt,             (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)
  "blocktime" : ttt         (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)
}

Examples:
> bitcoin-cli getrawtransaction "mytxid"
> bitcoin-cli getrawtransaction "mytxid" true
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getrawtransaction", "params": ["mytxid", true] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/

- 개발 트랜잭션안에는 그 트랜잭션과 연관된 block hash가 있다. 결국 UTXO는 이전 TXID 를 가르키기 때문에 UTXO로 블럭을 찾을 수 있다는 것이고, 해당 블럭이 그 UTXO를 가지고있는지는 merkle path를 통해서 알 수 있을 것이다.


3. 블록구조 

- 채굴로 만들어진 블록의 최종 구조
- 이전 블록 해쉬,머클루트등의 블록헤더와 개별거래들을 모아둔 블록바디가 있다.
- 블록해쉬(ID)는 자체 헤더 정보를 이용해서 만들어 진다.
- 그래서  nounce 와 bits 기반으로 블럭마다 채굴을 첨부터 다시 시작해야하지.


getblock "blockhash" ( verbosity ) 

If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.
If verbosity is 1, returns an Object with information about block <hash>.
If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. 

Arguments:
1. "blockhash"          (string, required) The block hash
2. verbosity              (numeric, optional, default=1) 0 for hex encoded data, 1 for a json object, and 2 for json object with transaction data

Result (for verbosity = 0):
"data"             (string) A string that is serialized, hex-encoded data for block 'hash'.

Result (for verbosity = 1):
{
  "hash" : "hash",     (string) the block hash (same as provided)
  "confirmations" : n,   (numeric) The number of confirmations, or -1 if the block is not on the main chain
  "size" : n,            (numeric) The block size
  "strippedsize" : n,    (numeric) The block size excluding witness data
  "weight" : n           (numeric) The block weight as defined in BIP 141
  "height" : n,          (numeric) The block height or index
  "version" : n,         (numeric) The block version
  "versionHex" : "00000000", (string) The block version formatted in hexadecimal
  "merkleroot" : "xxxx", (string) The merkle root
  "tx" : [               (array of string) The transaction ids
     "transactionid"     (string) The transaction id
     ,...
  ],
  "time" : ttt,          (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)
  "mediantime" : ttt,    (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)
  "nonce" : n,           (numeric) The nonce
  "bits" : "1d00ffff", (string) The bits
  "difficulty" : x.xxx,  (numeric) The difficulty
  "chainwork" : "xxxx",  (string) Expected number of hashes required to produce the chain up to this block (in hex)
  "previousblockhash" : "hash",  (string) The hash of the previous block
  "nextblockhash" : "hash"       (string) The hash of the next block
}

Result (for verbosity = 2):
{
  ...,                     Same output as verbosity = 1.
  "tx" : [               (array of Objects) The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 "tx" result.
         ,...
  ],
  ,...                     Same output as verbosity = 1.
}

Examples:
> bitcoin-cli getblock "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getblock", "params": [


4. 블록구조내의 바디 

- 3번 그림의 transaction 이 스택 형태가 아니라 해쉬값의 트리형태로 구성됨. 아래 그림은 뒤짚어 놓은 것이다.
- 2번 거래구조가 아래 그림의 입력값 A 이고, 이것을 해싱해서 나온 값들로 머클트리를 만들어서 블록구조바디에 추가
- 즉 거래 자체 데이터가 블록에 추가되는 것이 아니다.
- 원시거래구조(2번항목) 포맷은 TxID로 만들어지기 위해 이중해쉬된다. 이 txid 로 부터 머클 트리가 구성된다. 

비트코인 개발자 가이드 및 남이 그린 그림들을 토대로 전체적으로 훑어보아도 예상외로 UTXO 와 Transaction 간의 연결고리를 찾기가 힘들다.Transaction 를 위해 UTXO 는 찾을 수 있는데, 그 반대방향은 안보이는데..다시 찾아보자. 내가 TxID 에 집중해서 그런거 같은데 아마도 Address 라는 공통 분모가 있는거 같기도 하고...(이미 주화입마 상태이다.아는 것 같은데 잘 모르겠다.) 

즉 UTXO의 주인의 address 를 얻고, 그 address 를 통해서 과거 거래정보를 얻은 후에 그 TxID 를 통해 머클패스를 구성 할 것이라고 추측할 수도 있겠다 싶었는데... 웬걸 ~

이미 위에서 UTXO - TXID - 블럭해시 간에 연결고리를 발견해 놓고 먼 딴 생각을 한거냐 


위의 이미지를 보듯이, vin (입력값)에는 "이전 거래"와 "몇번째 UTXO인가" 를 가르키는 txid,vout 이 있다. 이 txid 를 매개변수로 GetRawTransaction API 를 호출하여 이전 거래를 찾거나, 그 거래(txid)를 포함하는 블록의 머클트리를 검증한다는 추론이 더 그럴싸 하다. 근데 이렇다고치면 머클패스/머클트리로 검증하는 것은 얼마나 깊이 검증해야 한다는 거냐? 즉 내가 채굴자인데 이 거래를 내 임시풀에 넣기 전에 검증 하는 용도면 채굴자 선에서 끝나는거 아닌가? 만약 임시풀에 넣고 있다가, 채굴에 성공한후에 제대로 된 블록에 넣었다고 치고 이걸 다른 노드들에게 전파 한다고 칠 때, 그 블록의 검증을 채굴,풀노드 말고 SPV 도 하나? SPV 가 하니깐 머클패스가 필요한거니 당연한건가? SPV 한데 머클패스를 보내나? 아니면 SPV 가 트랜잭션을 받으면 주변 노드한테 머클패스 달라고 요청하나?

아직 전체적인 비트코인 와꾸가 선명히 스케치 되기엔 스크립트의 이해도가 떨어져서 그런듯 하니..좀 더 힘을 내보자.
반복 만큼 좋은 것은 없다. 머리를 정돈하기 위해 아주 쉬운 기초적인 내용부터 다시 시작해보자. 


쉽게 설명하는 블록체인, 스크립트란 뭔가요? [비트코인의 언어]
쉽게 설명하는 블록체인, 비트코인 거래의 원리와 과정 - 1
쉽게 설명하는 블록체인, 비트코인 거래의 원리와 과정 - 2 [거래의 입력값과 출력값]
비트코인 시스템에서 사용하는 암호 기술(II) - 거래 검증

그리고 나서 이 글의 애니매이션을 통해 스크립트의 스택연산에 대해 감잡고 Bitcoin protocolfor developer
트랜잭션의 로우포맷에 대해 익힌후 Bitcoins the hard way: Using the raw Bitcoin protocol

비트코인 개발자 가이드의 거래 부분 을 참고로 수동으로 가상거래를 해보는 아래 멋진 글로 스크립트에 대한 이해를 끝장내자.

Part 1: Transaction Basics
Part 2: Building a Transaction By Hand

다시 한번 읽어보니, 이제 감이 잡힌거 같기도 하다. 장님 코끼리 다리 만지기를 했던 것 같다. 


Validation

비트코인에서의 검증 및 확정은 아래와 같다. - 3가지

1. 거래가 일어나고 그 거래에 대한 검증 (노드가 거래풀에 넣기 위함)  - 거래검증
   ㅁ 공개키 검증 ( A 에게 돈을 보낸 것이 맞나)
   ㅁ 공캐기로 서명 검증 (그 거래에 사용된 UTXO의 출처가 확실히 전 UTXO 부터로 인가? 혹시 내용이 변경되지 않았는가? 
   ㅁ coinbase 까지 이전 UTXO 전체 혹은 옵셔널하게 깊이를 체크한다.(라고 누군가 말하던데, 생각해 보면 깊이 1이면 충분하다고 추론한다. 마스터링 비트코인에도 이거에 대한 언급이 없다.) 

2. 노드에 의해 블록이 완성되고 그 블록에 대한 검증  -블럭검증

   ㅁ 채굴하면서 노드들은 다른 곳에서 날라온 블록에 대한 검증을 한다.블록해쉬의 정확성과 깊이를 조사한다.

3. 블록의 높이에 따른 거래 확정 
   

   ㅁ 거래가 포함된 블록이 길이(높이)를 체크한다.  
   

검증시 사용되는 것들 

 A.  LevelDB에 저장된 UTXO set 과 메타데이터 이용
 B.  블록체인 자체 이용

자 마지막 결과 !!! 여기서 머클패스를 던저주는 과정 1,2,3 중 어디일까?

당연히 거래검증 부분이다. UTXO 들에 대한 검증시 이전 UTXO 는 어떤 블록에 들어 있을 것이고, 블록은 단단히 고정되어있다.단단히 고정되어 있는 블록안에 그 UTXO가 포함된것이 확실하다면 그 UTXO 는 신뢰 할 수 있을 것인데, 풀노드라면 그냥 자신의 정보로 검증하고, SPV 일 경우 머클패스를 받기 위해 요청(Push or Pull) 할 것이다.

참 먼길을 걸어왔다. 이러한 여정을 통해서 막힌 속은 어느정도 해소 할 수 있게 되었다. 하지만 이 글은 모두 추측에 기반 하며 틀린 내용도 있을 것이다. 본 글을 사실로 받아드리지 말자. 추후에 내가 직접 C++ 소스를 까보며 정확히 확인 해야 할거 같다. 개발자적인 소명이랄까? 나는 오로지 실제 소스에 기반한 것만 사실이라고 생각한다. 조만간 C++,Golang,Scala 소스등 확인 할 것이며 관련 내용은 블로그에 작성할 것이다.


p.s

* 나중에 발견한 관련 동영상. 

Blockchain/Bitcoin for beginners 7: Blockchain header: Merkle roots and SPV transaction verification




* 마지막으로 머클트리에 먼가 문제가 있다고 느끼는 사람이 있을지도 모르겠다. 그런 사람이 실제 있다.

이 사람인데 --> 


다음 링크를 참조하자.

 https://blog.ethereum.org/2015/11/15/merkling-in-ethereum/




소프웨어 엔니지어링에서 가장 교조주의적인 덕목은 인터페이스를 위시한 유연성,확장성이 아닐까 싶다. (추가적으로 제네릭과 폴리모피즘의 환상 떡칠은 가독성을 최악으로 치닫게 만든다.) 그게 절실히 필요한 코드는 5%나 될까 싶은데 주로 공용 라이브러리겠지.나머지 95%의 코드를 만드는 사람들이 모든 책에 적혀져있는 "확장가능하게 만들어야한다" 를 맹목적으로 받아드리고 있는거 같기도 하며,교조적인 덕목에 따라 혹시 자신은 유연하게 만들지 않았는지에 대한 죄책감이 남아 있을지도 모르겠다. 물론 그 정도 고민을 하는 사람이라면 그것만으로도 훌륭하다고 볼 수 있겠다. 까놓고 말해보자. 자신이 만든 클래스가 100개 있다면, 그 중에 1년 후에 형제 클래스를 만들어야 했던 경우가 얼마나 되었는지. 내 경우는 하나 밖에 없다. 솔직히 말하면 오히려 안쓰던 클래스를 줄였다.일반 응용프로그래밍 작성시 확장성 있게 만들지 않아서 고통받는 코드는 거의 별로 없다. 성능문제와 마찬가지로 확장성이 필요하면 그때 가서 고쳐도 충분한 코드(팀내에서 조율 가능한)가 훨씬 더 많다.짐을 내려 놓아도 된다.


*

특히 대형 솔루션을 만들지 않는 마이크로 서비스 형태의 스타트업이라면 철저하게 빠른 구현 위주로 가야 하며, 뜯어 고치는거 & 더 좋게 새로 만드는 것은 눈감고도 기민하게 해야 한다.즉 비지니스 로직(예를들어 MVC 경우 Controlller 단, Service 단) 따위는 유연성을 따지는 것은 오바고, 다양한 외부인터페이싱이 요구될 가능성이 있는 DB 접속 (예를들어 MVC 경우 Repository단) 같은 경우에도 PostgreSQL 을 사용하다가 InfluxDB 를 추가시 기존에 공통 인터페이스가 없더라도 후딱 만들어 넣어야 한다는 야그~

'소프트웨어 사색' 카테고리의 다른 글

왜 커링 (currying) 이 더 좋아?  (1) 2018.03.28
언어 선택의 기준  (0) 2018.03.20
쓰레스세이프하다? 란 과연 무엇인가?  (0) 2018.03.02
블럭체인/비트코인  (1) 2018.01.31
소프트웨어는 유기물  (0) 2018.01.15

+ Recent posts