<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>CWLog - blockchain</title>
    <subtitle>개발 &amp; 해킹 좋아하는 사람의 블로그</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blog.devcw.xyz/tags/blockchain/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blog.devcw.xyz/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-05-11T00:00:00+00:00</updated>
    <id>https://blog.devcw.xyz/tags/blockchain/atom.xml</id>
    <entry xml:lang="en">
        <title>머클 트리 (Merkle Tree)</title>
        <published>2026-05-11T00:00:00+00:00</published>
        <updated>2026-05-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.devcw.xyz/posts/merkle-tree/"/>
        <id>https://blog.devcw.xyz/posts/merkle-tree/</id>
        
        <content type="html" xml:base="https://blog.devcw.xyz/posts/merkle-tree/">&lt;p&gt;블록체인 L1 레이어에선 블록이 정상적인지 판별하기 위해 머클 트리를 이용해서 만든 트랜잭션 해시값을 대조한다.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;..&#x2F;..&#x2F;images&#x2F;merkle-2.png&quot; alt=&quot;merkle-2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;위와 같이 각 트랜잭션들의 해시를 트리 형태로 합해서(H(L1 | L2)) 머클 루트를 만든다. 해시 특성상 1 바이트라도 바뀌면 트리 전체가 무너지기 때문에 정합성을 검증할 수 있다. 머클 트리 자체의 방식은 이게 끝이다. 설명할게 따로 없다. 그냥 해시 이어붙여서 또 해시하기, 그걸 올리기다.&lt;&#x2F;p&gt;
&lt;p&gt;중요한 부분은 이게 실제 블록체인에서 어떻게 사용되는지이다. 보통 일반적으로 블록의 형태는 다음과 같다.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Hash&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 32&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; BlockHeader&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    version&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; u32&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    prev_block_hash&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Hash&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    merkle_root&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Hash&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    timestamp&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; u64&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Block&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    header&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; BlockHeader&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    transactions&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Transaction&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;beulrogceini-meokeul-ruteureul-sseuneun-sijeom&quot;&gt;블록체인이 머클 루트를 쓰는 시점&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#beulrogceini-meokeul-ruteureul-sseuneun-sijeom&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;크게 세 군데가 있는데,&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;블록 생성: 블록 생성자(PoW면 마이너, PoS면 프로포저)는 멤풀에서 트랜잭션을 골라 머클 트리를 계산하고, 그 루트를 &lt;code&gt;merkle_root&lt;&#x2F;code&gt;에 박는다. 이 시점부터 본문은 사실상 동결이다. 트랜잭션 하나만 바꿔도 루트가 바뀌고, 그 위에서 진행한 봉인 작업(PoW 해싱이든 PoS 서명이든)이 전부 무효가 된다.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;블록 검증: 풀노드는 본문 트랜잭션 전부로 머클 루트를 재계산해서 헤더의 &lt;code&gt;merkle_root&lt;&#x2F;code&gt;와 대조한다. 일치하지 않으면 거부 처리된다. 트랜잭션 위변조나 누락은 여기서 잡힌다.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;풀노드 없이 검증할 때 (라이트 클라이언트): 32바이트 해시 하나가 본문 전체를 대표하니까, 헤더만 따라가도 체인 상태를 검증할 수 있다. 본문(MB 단위)은 필요할 때만 받는다. 모바일 지갑이 이 방식으로 동작한다.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;그럼 라이트 클라이언트는 내 트랜잭션이 이 블록에 진짜 들어있다는 걸 어떻게 검증할까. 여기서 머클 증명이 나온다.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;meokeul-jeungmyeong-merkle-proof&quot;&gt;머클 증명 (Merkle Proof)&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#meokeul-jeungmyeong-merkle-proof&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;트리에서 잎부터 루트까지 올라가는 경로의 형제(sibling) 해시만 있으면 된다. 트랜잭션이 N개일 때 필요한 해시는 log₂(N)개이다.&lt;&#x2F;p&gt;
&lt;p&gt;Tx1~Tx4 네 트랜잭션이 잎으로 있는 트리(&lt;code&gt;L1 = hash(H1|H2)&lt;&#x2F;code&gt;, &lt;code&gt;L2 = hash(H3|H4)&lt;&#x2F;code&gt;, &lt;code&gt;root = hash(L1|L2)&lt;&#x2F;code&gt;)에서 Tx3이 들어있다는 걸 증명한다고 치자.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언특 가지고 있는 것: Tx3&lt;&#x2F;li&gt;
&lt;li&gt;밸리데이터가 줘야 할 것: H4, L1&lt;&#x2F;li&gt;
&lt;li&gt;검증:
&lt;ol&gt;
&lt;li&gt;H3 = hash(Tx3)&lt;&#x2F;li&gt;
&lt;li&gt;L2&#x27; = hash(H3 | H4)&lt;&#x2F;li&gt;
&lt;li&gt;root&#x27; = hash(L1 | L2&#x27;)&lt;&#x2F;li&gt;
&lt;li&gt;root&#x27;가 헤더의 &lt;code&gt;merkle_root&lt;&#x2F;code&gt;와 같으면 끝&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;이게 끝이다. 별 거 없어 보이지만 이 검증 단계 개수에 머클 증명의 효율성이 다 들어있다.&lt;&#x2F;p&gt;
&lt;p&gt;트리 구조는 한 층 올라갈 때마다 노드 수가 절반이 되니까, 잎이 N개라면 루트까지 깊이는 log₂(N)이고, 각 층에서 형제 해시 하나씩만 받으면 위로 합쳐 올라갈 수 있다. 필요한 해시 개수도 결국 log₂(N)개가 된다.&lt;&#x2F;p&gt;
&lt;p&gt;숫자로 보면 얼추 감이 잡히는데,&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;트랜잭션 4개 → 해시 &lt;strong&gt;2개&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;트랜잭션 1,024개 → 해시 &lt;strong&gt;10개&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;트랜잭션 100만 개 → 해시 &lt;strong&gt;20개&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;트랜잭션 10억 개 → 해시 &lt;strong&gt;30개&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;가 된다. 본문은 O(N)으로 늘어나는데 증명은 O(log N)로만 자란다. 트랜잭션을 1000배로 늘려도 해시는 10개 더 받으면 끝인 것이다.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;enum&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Direction&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Left&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Right&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; MerkleProof&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    leaf&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Hash&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    siblings&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Hash&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Direction&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; verify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;proof&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;MerkleProof&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; root&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Hash&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; mut&lt;&#x2F;span&gt;&lt;span&gt; acc&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; proof&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;leaf&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;sibling&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; dir&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;proof&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;siblings &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        acc&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; match&lt;&#x2F;span&gt;&lt;span&gt; dir&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;            Direction&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Left&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;  =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; hash_pair&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;sibling&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;acc&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;            Direction&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Right&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; hash_pair&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;acc&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; sibling&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;acc&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span&gt; root&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;방향이 필요한 이유는 &lt;code&gt;hash(A|B) ≠ hash(B|A)&lt;&#x2F;code&gt;이기 때문이다. (문자열을 앞뒤로 단순히 붙이기 때문) 형제가 왼쪽인지 오른쪽인지 모르면 다른 루트가 나온다.&lt;&#x2F;p&gt;
&lt;p&gt;이 구조의 안전성은 결국 해시 함수의 충돌 저항성에 기대는데 공격자가 &lt;code&gt;hash(X | H4) = hash(H3 | H4)&lt;&#x2F;code&gt;인 가짜 &lt;code&gt;X&lt;&#x2F;code&gt;를 만들 수 있다면 변조가 가능하겠지만.. SHA-256 같은 함수에선 확률적으로 불가능하다.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dareun-goseseodo-sseuinda&quot;&gt;다른 곳에서도 쓰인다&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#dareun-goseseodo-sseuinda&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;해시 트리 자체는 블록체인 전용 자료구조가 아니라서 곳곳에 박혀있다.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Git: 객체 저장소가 머클 DAG다. 커밋 해시 하나가 트리 전체를 대표한다.&lt;&#x2F;li&gt;
&lt;li&gt;Certificate Transparency: TLS 인증서 발급 로그&lt;&#x2F;li&gt;
&lt;li&gt;이더리움: 상태(state)까지 머클로 박는 Merkle Patricia Trie를 쓴다&lt;&#x2F;li&gt;
&lt;li&gt;ZK 시스템: 상태 커밋먼트로 머클 루트 사용&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;내부 구조 까보면 해시 묶어서 트리 쌓고 루트만 들고 다니기가 전부인데 의외로 응용 폭이 넓은 자료구조다.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Consensus Engine (합의 엔진) 이해하기</title>
        <published>2026-01-15T00:00:00+00:00</published>
        <updated>2026-01-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.devcw.xyz/posts/consensuses/"/>
        <id>https://blog.devcw.xyz/posts/consensuses/</id>
        
        <content type="html" xml:base="https://blog.devcw.xyz/posts/consensuses/">&lt;p&gt;근래에 블록체인에 꽤 관심이 생겼다. 생태계를 쭉 둘러봤는데, 대략적으로 dApp이나 Consensus 엔진 정도가 눈에 먼저 보인 것 같다.&lt;&#x2F;p&gt;
&lt;p&gt;차차 전반적으로 이해한 걸 바탕으로 글을 쓸 예정이다. 다만 우선 첫글은 가장 low-level 레이어인 합의 엔진 (Consensus Engine)을 이론이랑 구현체를 까보며 이해해보려고 한다.&lt;&#x2F;p&gt;
&lt;p&gt;이런 게 왜 필요할까? 그리고 구현은 왜 그렇게 돼있을까? 이걸 알기 위해선 우선 분산 시스템을 이해할 필요가 있다.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bunsan-siseutem&quot;&gt;분산 시스템&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#bunsan-siseutem&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;일단 사전적인 정의로는, 여러 독립적인 노드(컴퓨터)들이 네트워크로 연결되어 하나의 공동 목표를 수행하기 위한 시스템이다.&lt;&#x2F;p&gt;
&lt;p&gt;예를 들어..&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Redis에서 샤딩 기능을 이용해서 여러 노드가 연결되고 캐싱을 수행하면 분산 시스템이다. (장애 복구)&lt;&#x2F;li&gt;
&lt;li&gt;인프라에서 de facto인 쿠버네티스같은 경우도 동일하다.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;결국 여러 노드가 &lt;strong&gt;동일한 순서의 로그를 통해 동일한 State&lt;&#x2F;strong&gt;를 갖게 만드는 것이 핵심이다. 이걸 학술적으로는 **상태 머신 전파(State Machine Replication)**라고 부른다.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;raft-habyi-algorijeum&quot;&gt;Raft 합의 알고리즘&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#raft-habyi-algorijeum&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;대표적으로 흔히 사용되고 있는 것은 &lt;code&gt;Raft&lt;&#x2F;code&gt;다.&lt;&#x2F;p&gt;
&lt;p&gt;쿠버네티스의 &lt;code&gt;etcd&lt;&#x2F;code&gt;에서 사용하고, &lt;code&gt;Redis&lt;&#x2F;code&gt;도 &lt;code&gt;Sentinel&lt;&#x2F;code&gt;이나 &lt;code&gt;Cluster&lt;&#x2F;code&gt;의 리더 선출 과정에서 &lt;code&gt;Raft-like&lt;&#x2F;code&gt;를 사용한다.&lt;&#x2F;p&gt;
&lt;p&gt;함축적으로 플로우를 설명하자면 다음과 같다:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;리더가 없을 시 리더 투표를 한다.&lt;&#x2F;li&gt;
&lt;li&gt;투표가 정족수, 즉 과반수가 넘으면 해당 노드를 리더로 승격시킨다.&lt;&#x2F;li&gt;
&lt;li&gt;리더가 상태 전파를 담당한다.&lt;&#x2F;li&gt;
&lt;li&gt;위 과정 반복&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;이건 모든 노드들이 &#x27;착하다&#x27; 라는 전제이다. 만약 악의적인 노드가 있다고 하고, 해당 노드가 리더가 되면 잘못된 로그를 줘서 시스템을 붕괴시킬 수도 있다.&lt;&#x2F;p&gt;
&lt;p&gt;위의 예시들은 내부 시스템에서 사용하고 있기 때문에 (물론 요새 Zero-Trust로 얼추 방어한다) 이런 경우가 많지 않겠지만, &lt;strong&gt;블록체인&lt;&#x2F;strong&gt;은 다르다. &lt;strong&gt;누구나&lt;&#x2F;strong&gt; 참여할 수 있기 때문에 악의적인 노드가 있을 확률이 존재한다.&lt;&#x2F;p&gt;
&lt;p&gt;이를 해결하기 위해 고안된 것이 &lt;strong&gt;비잔틴 장애 허용(Byzantine Fault Tolerance, BFT)&lt;&#x2F;strong&gt; 알고리즘이다.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bijantin-jangae-heoyong-algorijeum&quot;&gt;비잔틴 장애 허용 알고리즘&lt;a class=&quot;anchor&quot; aria-hidden=&quot;true&quot; href=&quot;#bijantin-jangae-heoyong-algorijeum&quot; hidden=&quot;&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;알고리즘을 알기 전 배경적으로 알아야 할 딜레마적 개념이 있는데, &lt;strong&gt;비잔틴 장군 문제&lt;&#x2F;strong&gt;이다.&lt;&#x2F;p&gt;
&lt;p&gt;배경은 이렇다:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;비잔틴 제국의 장군들이 적의 도시를 포위하고 있다.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;해당 전투에서 이기려면 양자택일을 해야한다: 모든 장군이 전부 공격하거나, 모든 장군이 전부 후퇴하거나&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;현실적인 제약은 다음과 같다:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- 장군들끼리는 전령을 통해서만 소통 가능하다.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- 장군들 중에서 배신자가 몇몇 섞여있다. 예를 들어, 이런식으로: 몇몇 장군들에게는 후퇴하라고하고 몇몇 장군들에게는 공격하라고 한다.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;이 상황에서 어떻게 배신자들을 뚫고 승리할 수 있을까?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;위 내용을 해결하기 위한 여러 합의 이론들이 존재한다. 크게 두가지로 나눌 수 있는데&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;PoW: 연산력 기반 합의 (메세지에 이어진 체인이 가장 긴 게 진실로 합의됨)&lt;&#x2F;li&gt;
&lt;li&gt;BFT: 메세지들 던지면 2&#x2F;3 노드들이 검증 후 메세지를 합의함&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;와 같이 나눌 수 있다.&lt;&#x2F;p&gt;
&lt;p&gt;여기까지가 백서 내용이다. 백서 자체도 장황하지만.. 분산 시스템이 다 그렇듯 엣지케이스 처리를 엄청나게 해줘야 하기 때문에 구현체 내부 동작도 중요하다.&lt;&#x2F;p&gt;
&lt;p&gt;현재 공부중인 내용이라 부족한 부분이 존재할 수 있다. 여러가지 엣지케이스가 생각나는데, 다음 글에서 어떻게 핸들링하는지 알아보도록 하겠다.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
