Rust로 만든 비트코인 엔진을 WebAssembly로 컴파일해 브라우저에서 직접 돌려보는 학습 도구
아래 6개 부품이 어떻게 맞물리는지 차례대로 직접 만져보면, "비트코인이 왜 위조가 안 되는가, 주인 없이도 왜 돌아가는가"가 손에 잡혀요.
src/ 의 Rust 엔진이 합니다.cargo run 으로 돌던 그 엔진이 그대로 웹에서 돕니다.block.rs(해시·채굴) · transaction.rs(거래) · blockchain.rs(체인·잔액·검증) · utxo.rs(UTXO·입력별 서명) · wallet.rs(키·ECDSA) · merkle.rs(머클트리) · header.rs(80B 헤더) · network.rs(P2P 합의) · attack.rs(이중지불) · wasm_api.rs(브라우저 다리)아무 글자나 입력해 보세요. 입력이 1바이트든 1메가든, 결과는 항상 64자리 16진수(256비트)입니다.
한 글자만 바꿔도 결과의 절반 가까운 비트가 뒤집힙니다. 두 입력을 비교해 보세요.
비트코인의 핵심 아이디어예요. 각 블록은 자기 데이터뿐 아니라 바로 앞 블록의 해시까지 함께 SHA-256에 넣습니다. 그래서 블록들이 사슬(chain)처럼 이어져요 — 이게 블록체인이라는 이름의 유래죠.
👉 아래 블록의 데이터를 고치거나 ⛏ 채굴을 눌러보세요. 그 블록 해시가 바뀌면, 그걸 품고 있던 뒤 블록들의 해시가 줄줄이 다시 계산돼요(주황색 반짝임). 채굴해둔 뒤 블록이 있었다면 0으로 시작하던 게 깨지는 것도 보입니다.
자주 나오는 얘기예요. 근데 두 가지 위협을 나눠서 봐야 정확합니다.
그러니까 양자 컴퓨터가 노리는 건 "해시 연결"보다 "서명" 쪽이에요. 대비책으로 양자내성 서명(post-quantum)으로 갈아타자는 논의가 이미 진행 중이고, 무엇보다 그만한 규모의 양자 컴퓨터는 아직 존재하지 않습니다.
블록 데이터는 그대로 두고 nonce(숫자)만 0,1,2,… 바꿔가며 해시를 다시 계산해요. 해시가 0이 N개로 시작하면 성공. 정답을 찾는 빠른 방법은 없고 오직 무차별 대입뿐입니다.
한 줄 = 한 번의 시도예요. 데이터 + nonce 를 SHA-256에 넣어 본 결과죠. 앞에 0이 모자라면 ❌, 목표만큼 0으로 시작하면 ✅. 정답을 미리 알 방법이 없어서 0번부터 하나씩 무작정 대입하는 게 전부입니다.
여긴 브라우저에서 즉시 체감하려고 난이도를 확 낮췄어요(1~6). 실제 비트코인은 이렇게 다릅니다:
그래서 이 실험실 속도(초당 수만~수십만)로는 우주 나이로도 못 찾아요. 전 세계 채굴기를 합치면 특수 장비(ASIC)로 초당 약 10²⁰번(= 1억 × 1조) 규모의 해시를 돌려서, 그걸로 평균 10분마다 블록 하나를 캡니다.
→ target · double SHA-256 · 80바이트 헤더의 정밀한 버전은 탭 5 · 블록 해부에서 실제 hex까지 직접 볼 수 있어요.
프리셋을 고르거나 값을 직접 바꾼 뒤 엔진을 시작하세요. (제네시스 블록이 채굴되며 시작됩니다)
보내는 사람의 잔액이 부족하면 엔진이 거부해요(이중지불 방지).
채굴 보상(코인베이스)과 거래로 잔액이 바뀝니다.
멤풀의 거래들을 모아 새 블록을 채굴합니다. 맨 앞에는 채굴 보상(코인베이스) 거래가 자동으로 들어가요.
위에서 블록을 조작하면 검증에 걸리죠. 그럼 공격자가 조작한 블록부터 다시 채굴하면 되지 않을까요? 문제는 그러는 동안에도 정직한 네트워크는 계속 블록을 쌓는다는 거예요. 그래서 공격자는 따라잡는 게 아니라 추월해야 합니다.
CLI에서 println! 으로 찍히던 그 메시지를 브라우저로 그대로 가져온 거예요.
3번 탭은 계좌식(Alice=20, Bob=10처럼 잔액을 기억)이었어요. 하지만 진짜 비트코인은 잔액을 저장하지 않고, "아직 안 쓴 거래 출력(UTXO)" 조각들만 둡니다. 지갑이 그걸 다 더해 잔액을 계산할 뿐이죠.
발행/송금에 처음 등장하는 이름마다 개인키·공개키 한 쌍이 자동 생성돼요. 주소는 공개키를 해시해 만든 값이고, 동전은 이 주소에 잠깁니다. 개인키는 비밀(엔진 내부에만 존재).
공개키·개인키는 타원곡선 위에서 하는 "점 점프 놀이"예요. 시작점 G에서 정해진 규칙(선 긋고 → 만나는 점 → 위아래 뒤집기)으로 점프하는데 — 몇 번 점프했는지 = 개인키(비밀), 최종 착지점 = 공개키(공개). 버튼을 눌러 한 번씩 더해보세요.
UTXO가 있어야 보낼 수 있어요. 먼저 누군가에게 새 코인을 만들어 줍니다.
큰 동전부터 골라 목표+수수료 이상이 될 때까지 소비하고, 거스름돈을 돌려받아요.
공격자가 피해자의 UTXO를 자기 앞으로 보내는 거래를 만들어 봅니다. 하지만 공격자는 피해자의 개인키가 없어요. 자기 키로 서명할 수밖에 없고, 그 키의 주소는 피해자 주소와 다르므로 검증에서 거부됩니다.
주소별로 묶었어요. 각 칩 = UTXO 한 개. 파란 테두리는 거스름돈으로 생긴 UTXO예요. 잔액은 칩들의 합으로 계산됩니다(저장 아님).
2·3번 탭에선 설명을 위해 블록 전체를 문자열로 이어붙여 해시했어요. 하지만 진짜 비트코인은 다릅니다. 거래 수천 건은 머클트리로 32바이트 머클루트 하나로 요약하고, 채굴자는 딱 80바이트 헤더만 double SHA-256으로 반복 해시하죠. 그리고 난이도는 "0의 개수"가 아니라 target이라는 256비트 숫자보다 작은가로 판정해요.
거래를 한 줄에 하나씩 적어요(내용은 자유). 각 거래를 double SHA-256해 잎(leaf)을 만들고, 둘씩 이어붙여 해시하길 반복해 꼭대기의 루트 하나로 압축합니다. 노드 수가 홀수면 마지막 노드를 복제해 짝을 맞춰요.
머클루트와 직전 블록 해시, 난이도를 넣어 헤더를 만듭니다. 헤더는 정확히 6필드 · 80바이트예요.
지금까지의 탭은 전부 노드 1대의 내부 동작이었어요. 하지만 진짜 비트코인은 수만 대의 노드가 각자 자기 체인을 들고, 서로 블록을 주고받으며 합의합니다. 중앙 서버도, 심판도 없어요. 대신 딱 하나의 규칙으로 정리되죠 — 가장 길고 유효한 체인이 이긴다.
각 노드에서 채굴해 블록을 붙이고, 방송으로 이웃에게 퍼뜨려 보세요. 두 노드에서 각각 캐면 포크가 생기고, 한쪽을 더 길게 만든 뒤 방송하면 재구성(reorg)이 일어납니다.
한마디로 "돈 내고 물건 받은 뒤, 그 결제 기록을 몰래 지워버리는" 사기예요. 같은 코인을 두 번 쓰는 거죠.
아래 시뮬레이터에서 공개 체인(모두가 아는 진짜 기록)과 공격자의 비밀 체인을 직접 경쟁시켜, 결제가 어떻게 증발하는지 볼 수 있어요.
컨펌 = 내 결제가 담긴 블록 위에 쌓인 블록 수. 판매자가 물건을 보내기 전에 이걸 더 기다릴수록, 공격자가 결제를 뒤집으려고 따라잡아야 할 거리가 멀어져요. 공격자의 비밀 체인이 정직한 체인보다 더 길어져야 뒤집을 수 있는데, 기다리는 동안 정직한 체인이 계속 앞서 나가기 때문이죠.
51% 공격에도 한계가 있어요. 결론부터: 갓 결제된 최근 몇 블록은 노려볼 수 있어도, 깊은 과거(제네시스 근처)는 사실상 절대 불가능합니다. 아래에서 공격자 해시파워와 컨펌 수를 바꿔보면 왜 그런지 숫자로 보여요. (모든 계산은 Rust 엔진이 합니다.)
결론부터: "하루아침에 붕괴"는 아니지만, 특정 조건의 코인엔 실질 위협이에요. 핵심은 딱 두 알고리즘이에요 — 쇼어(Shor)는 서명을 깨고(치명적), 그로버(Grover)는 해시를 조금 빠르게 할 뿐(제한적). 이 사이트에서 본 secp256k1 서명(탭 4)이 쇼어의 표적, SHA-256(탭 1·2·5)이 그로버의 표적입니다.
가르는 기준은 딱 하나 — 공개키가 체인에 드러나 있느냐. 일반 주소는 공개키를 해시 뒤에 숨겨 두기 때문에, "한 번도 출금 안 한" 주소는 (현재로선) 안전해요.