[구조의 이해] ‘동일한 거래내역’을 기존 구조와 블록체인 구조를 비교한다면?
1.
기존의 데이터  구조
INDEX  | 판매자  | 구매자  | 개수  | 시간  | 
0  | 파랑불  | 김민수  | 3개  | 1990년 1월 1일 00시 00분 00초  | 
1  | 김민수  | 김영수  | 3개  | 1990년 1월 2일 01시 02분 03초  | 
2  | 김영수  | 박영수  | 3개  | 1990년 1월 3일 02시 03분 04초  | 
3  | 박영수  | 이미래  | 3개  | 1990년 1월 4일 02시 03분 04초  | 
4  | 이미래  | 손영호  | 3개  | 1990년 1월 5일 02시 03분 04초  | 
5  | 손영호  | 수영호  | 3개  | 1990년 1월 6일 02시 03분 04초  | 
2.
블록체인의 데이터 구조
┌───────────────┐
│ 블록1                                   │
│---------------                    │
│ 'INDEX' : 0                         │
│ '판매자' : 파이썬                │
│ '구매자' : 김민수               │
│ '개수'  : 3개                        │
│ '시간'  : 1990-01-01 00:00:00 │
└───────────────┘
│
▼
┌───────────────┐
│ 블록2                                  │
│---------------                    │
│ 'INDEX' : 1                          │
│ '판매자' : 김민수                │
│ '구매자' : 김영수                │
│ '개수'  : 3개                        │
│ '시간'  : 1990-01-01 00:00:00 │
└───────────────┘
│
▼
┌───────────────┐
│ 블록3                                  │
│---------------                    │
│ 'INDEX' : 3                         │
│ '판매자' : 김영수               │
│ '구매자' : 박명수               │
│ '개수'  : 3개                       │
│ '시간'  : 1990-01-01 00:00:00 │
└───────────────┘
1.파이썬으로 만드는 블록체인
previous_block이 chain역할을 함
block1 = {
    'INDEX': 0,
    '판매자': '파이썬',
    '구매자': '김민수',
    '개수': '3개',
    '시간': '2022년 5월 11일 16시 25분 16초',
    'previous_block': None
}
block2 = {
    'INDEX': 1,
    '판매자': '김민수',
    '구매자': '김영수',
    '개수': '3개',
    '시간': '2022년 5월 12일 13시 25분 16초',
    'previous_block': block1
}
block3 = {
    'INDEX': 2,  # 수정: 3 → 2 (순서대로)
    '판매자': '김영수',
    '구매자': '박명수',
    '개수': '3개',
    '시간': '2022년 5월 13일 16시 25분 16초',
    'previous_block': block2
}
block4 = {
    'INDEX': 3,  # 수정: 4 → 3 (순서대로)
    '판매자': '박명수',
    '구매자': '이미래',
    '개수': '3개',
    '시간': '2022년 5월 14일 16시 25분 16초',
    'previous_block': block3
}
block5 = {
    'INDEX': 4,  # 수정: 5 → 4 (순서대로)
    '판매자': '이미래',
    '구매자': '최용수',
    '개수': '3개',
    '시간': '2022년 5월 15일 16시 25분 16초',
    'previous_block': block4
}
block6 = {
    'INDEX': 5,  # 수정: 6 → 5 (순서대로)
    '판매자': '최용수',
    '구매자': '강영희',
    '개수': '3개',
    '시간': '2022년 5월 16일 16시 25분 16초',  # 수정: 공백 추가
    'previous_block': block5
}
Python
복사
•
위와 같은 형태로 블록이 체인으로 연결되며 거래가 계속 진행되면 어떻게 될까?
그래서 필요한 것이 바로 ‘암호해시’
2.블록체인의 핵심: 암호’해시’가 필요한 이유
[해시 함수의 특징]
: 어떤 재료를 넣어도 항상 똑같은 크기의 스무디가 나오는데, 한 번 갈아버리면 원래 재료가 무엇인지 절대 알 수 없고, 재료가 조금만 달라져도 완전히 다른 맛의 스무디가 나오는 신기한 믹서기
2-1. 해시함수의 가지 핵심 특징
 결정적 (Deterministic)
같은 입력 → 항상 같은 출력
특징:
•
동일한 데이터를 해시하면 언제나 동일한 해시값 생성
•
컴퓨터가 달라도, 시간이 달라도 결과는 항상 같음
 일방향성 (One-way Function)
해시값으로 원본 데이터를 추측하는 것은 현실적으로 불가능
특징:
•
해시값에서 원본을 역산하는 것은 계산적으로 불가능
•
"계란을 스크램블로 만들면 다시 온전한 계란으로 되돌릴 수 없다"와 같은 개념
 눈사태 효과 (Avalanche Effect)
입력이 1비트만 바뀌어도 출력이 완전히 달라짐
특징:
•
아주 작은 변화도 완전히 다른 해시값 생성
•
50% 이상의 비트가 변경되는 것이 이상적
입력값이 텍스트이든, 숫자이든, 텍스트의 길이가 길든 짧든 간에 모두 64개의 문자가 조합된 동일 양식의 리턴값을 확인
2-2. 해시함수의 공굴리기 효과 실습
[조작이 불가능한 이유]
import hashlib
# 변경전
genesis_block =   {'INDEX':0,
                    '판매자' : '파공블',
                    '구매자' : '김민수',
                    '개수' : '3개',
                    '시간' : '2022년 5월 11일 16시 25분 16초',
                    'previous_block' : None
                  }
hash_data = hashlib.sha256(str(genesis_block).encode()).hexdigest()
print(hash_data)
Python
복사
import hashlib
# 변경후
genesis_block =   {'INDEX':0,
                    '판매자' : '파공블',
                    '구매자' : '김민수',
                    '개수' : '3개',
                    '시간' : '2022년 5월 11일 16시 23분 16초',
                    'previous_block' : None
                  }
hash_data = hashlib.sha256(str(genesis_block).encode()).hexdigest()
print(hash_data)
Python
복사
따라서 블록체인 내의 과거 이력을 수정하려면, 이후 모든 블록의 암호 해시값이 바꿔야 하는 노가다 of 노가다, 심지어 동시에 바꿔주어야하므로, 과거의 거래 내겨을 수정하는 것은 불가능!!
2-3. 해시함수의 역사
SHA: Secure Hash Algorithm
1.
1993년 미국의 국가안보구NSA이 제작
2.
이 후 위험성을 줄이고, 안정성을 높여가며 SHA-2, SHA-3으로 발전해오고 있음
2-4. 해시함수의 구성(2단계)
단계  | 설명  | 
1. 전처리(Preprocessing)  | 입력 데이터를 고정된 규격에 맞추기 위해 인코딩(UTF-8), 패딩(padding), 블록 분할 등의 과정을 수행함  | 
2. 해싱(Hashing)  | 전처리된 데이터를 해시 함수에 입력하여 고정 길이의 해시값(hash value)을 생성함  | 
 1단계: 전처리 (Preprocessing)
 패딩 (Padding)
목적: 입력 데이터를 알고리즘이 처리할 수 있는 고정 크기 블록으로 만들기
패딩 규칙 (SHA-256 기준)
1.
메시지 끝에 '1' 비트 추가
2.
0 비트들을 적절히 추가
3.
원본 메시지 길이를 64비트로 표현하여 맨 끝에 추가
4.
최종 크기: 512비트의 배수
원본: "Hello" (40비트)
↓
Step 1: "Hello" + 1 (41비트)
↓  
Step 2: "Hello" + 1 + 0...0 (448비트까지 0 채움)
↓
Step 3: 앞의 448비트 + 원본길이(40을 64비트로) = 512비트
Python
복사
왜 원본 길이를 추가할까?
•
서로 다른 메시지가 같은 패딩 결과를 갖는 것을 방지
•
예: "a" + 패딩과 "aa" + 패딩이 같아지는 것을 방지
보안상 의미:
•
길이 확장 공격 방지
•
충돌 공격 저항성 강화
 2단계: 해싱 (Hashing)
해싱은 다시 2단계로 구분 가능
 1.  1단계: H, K, W 값 준비
2.
2단계: H+K+W 조합으로 64라운드 압축
 SHA-256의 3가지 핵심 값
H값: 해시 상태 (Hash State)
8개 워킹 레지스터 - 계속 변화하는 "상태값"
# 초기값 (소수의 제곱근에서 유도)
H₀ = 0x6a09e667  # √2
H₁ = 0xbb67ae85  # √3  
H₂ = 0x3c6ef372  # √5
H₃ = 0xa54ff53a  # √7
H₄ = 0x510e527f  # √11
H₅ = 0x9b05688c  # √13
H₆ = 0x1f83d9ab  # √17
H₇ = 0x5be0cd19  # √19
# 라운드마다 a,b,c,d,e,f,g,h 레지스터에 복사되어 변화
Python
복사
K값: 라운드 상수 (Round Constants)
64개 고정값 - 각 라운드마다 다른 "양념"
# 처음 64개 소수의 세제곱근에서 유도
K[0] = 0x428a2f98   # ∛2
K[1] = 0x71374491   # ∛3
K[2] = 0xb5c0fbcf   # ∛5
K[3] = 0xe9b5dba5   # ∛7
# ... 
K[63] = 0xc67178f2  # ∛311 (64번째 소수)
# 목적: 각 라운드를 고유하게 만듦
Python
복사
W값: 메시지 스케줄 (Message Schedule)
64개 확장 워드 - 원본 메시지에서 생성된 "재료"
•
이 복잡한걸 하는 이유? 
 원본 메시지는 16개 워드밖에 없는데, 64라운드를 돌려야 하기 때문
•
결론적으로 16개 워드를 64개로 "늘려서" 각 라운드마다 각기 다른 W값을 사용하기 위함
# 1. 원본 16개 워드 (512비트 블록을 32비트씩 분할)
W[0] ~ W[15] = 원본 메시지 워드
# 2. 48개 워드 확장 생성
for i in range(16, 64):
    W[i] = σ₁(W[i-2]) + W[i-7] + σ₀(W[i-15]) + W[i-16]
# σ₀, σ₁: 비선형 함수 (비트 회전 + XOR 조합)
Python
복사
2단계
H+K+W 조합으로 64라운드 압축
for i in range(64):  # 각 라운드마다
    # 3가지 값을 조합하여 계산
    T₁ = h + Σ₁(e) + Ch(e,f,g) + K[i] + W[i]
    #    ↑     ↑        ↑       ↑      ↑
    #   H값   H값기반   H값기반   K값   W값
    
    T₂ = Σ₀(a) + Maj(a,b,c)
    #    ↑         ↑
    #   H값기반    H값기반
    
    # H값(상태) 업데이트
    h, g, f, e, d, c, b, a = g, f, e, d+T₁, c, b, a, T₁+T₂
    
    # 결과: H, K, W가 모두 섞여서 새로운 H값 생성
Python
복사
3.코드로 이해하기
 기본 해싱 확인
import hashlib
def basic_hash_demo():
    """기본적인 SHA-256 해싱 확인"""
    
    messages = ["Hello", "hello", "Hello!", ""]
    
    print("🔐 SHA-256 해싱 결과")
    print("=" * 60)
    
    for msg in messages:
        hash_result = hashlib.sha256(msg.encode()).hexdigest()
        print(f"'{msg:8}' → {hash_result}")
    
    print("\n✅ 특징:")
    print("- 출력 길이: 항상 64자 (256비트)")
    print("- 작은 변화도 완전히 다른 결과")
    print("- 빈 문자열도 고정된 해시값")
basic_hash_demo()
Python
복사
 H, K, W 값 확인하기
def hkw_values_demo():
    """SHA-256의 H, K, W 값들을 실제로 확인"""
    
    message = "Hello"
    
    print("🔍 H, K, W 값 확인하기")
    print("=" * 50)
    
    # H값 (초기 해시 상태)
    print("1️⃣ H값 - 초기 해시 상태 (8개 워드)")
    H_initial = [
        0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
        0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
    ]
    for i, h in enumerate(H_initial):
        print(f"   H{i} = 0x{h:08x}")
    
    # K값 (라운드 상수) - 일부만 표시
    print(f"\n2️⃣ K값 - 라운드 상수 (64개 중 처음 8개)")
    K_constants = [
        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
        0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5
    ]
    for i, k in enumerate(K_constants):
        print(f"   K[{i}] = 0x{k:08x}")
    print("   ... (총 64개)")
    
    # W값 시뮬레이션 (실제로는 복잡한 계산)
    print(f"\n3️⃣ W값 - 메시지 스케줄")
    message_bytes = message.encode('utf-8').hex()
    print(f"   원본: '{message}' → {message_bytes}")
    print(f"   W[0] = 0x48656c6c ('Hell')")
    print(f"   W[1] = 0x6f800000 ('o' + 패딩)")
    print(f"   W[2~15] = 패딩으로 채움")
    print(f"   W[16~63] = 수학적 확장 (σ₀, σ₁ 함수 사용)")
    
    # 최종 해시
    final_hash = hashlib.sha256(message.encode()).hexdigest()
    print(f"\n4️⃣ 최종 결과 - H+K+W 조합")
    print(f"   64라운드 후: {final_hash}")
hkw_values_demo()
Python
복사
 조합 과정 시뮬레이션
def combination_simulation():
    """H, K, W가 어떻게 조합되는지 시뮬레이션"""
    
    print("⚙️ H+K+W 조합 과정 시뮬레이션")
    print("=" * 40)
    
    # 가상의 라운드 0 계산
    print("📊 라운드 0 예시:")
    
    # H값 (현재 상태)
    h = 0x5be0cd19
    e = 0x510e527f
    print(f"   H값 사용: h=0x{h:08x}, e=0x{e:08x}")
    
    # K값 (라운드 상수)
    K0 = 0x428a2f98
    print(f"   K값 사용: K[0]=0x{K0:08x}")
    
    # W값 (메시지 스케줄)
    W0 = 0x48656c6c  # "Hell"
    print(f"   W값 사용: W[0]=0x{W0:08x}")
    
    # 조합 계산 (단순화)
    print(f"\n   조합 공식:")
    print(f"   T₁ = h + Σ₁(e) + Ch(e,f,g) + K[0] + W[0]")
    print(f"      = 0x{h:08x} + [복잡한 함수들] + 0x{K0:08x} + 0x{W0:08x}")
    print(f"      = [새로운 32비트 값]")
    
    print(f"\n   결과: H, K, W가 모두 섞여서 새로운 해시 상태 생성")
    print(f"         이 과정을 64번 반복 → 최종 해시값")
combination_simulation()
Python
복사
 초기값 검증
def verify_initial_values():
    """SHA-256 초기값이 정말 소수의 제곱근인지 확인"""
    
    import math
    
    print("🔬 초기값 검증: 소수의 제곱근")
    print("=" * 40)
    
    primes = [2, 3, 5, 7, 11, 13, 17, 19]
    sha256_initials = [
        0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
        0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
    ]
    
    for i, prime in enumerate(primes):
        # 제곱근 계산
        sqrt_val = math.sqrt(prime)
        fractional_part = sqrt_val - int(sqrt_val)
        
        # 32비트로 변환
        hex_val = int(fractional_part * (2**32))
        
        print(f"√{prime:2d} = {sqrt_val:.8f}")
        print(f"     소수부분 × 2³² = 0x{hex_val:08x}")
        print(f"     SHA-256 H{i} = 0x{sha256_initials[i]:08x}")
        print(f"     일치여부: {'✅' if hex_val == sha256_initials[i] else '❌'}")
        print()
verify_initial_values()
Python
복사
 H, K, W 값 역할 확인
def hkw_role_experiment():
    """H, K, W 각각의 역할을 실험으로 확인"""
    
    print("🧪 H, K, W 역할 실험")
    print("=" * 40)
    
    # 같은 메시지, 다른 초기 H값이라면? (가정)
    print("실험 1: H값의 역할")
    print("- H값이 바뀌면 → 완전히 다른 해시")
    print("- 초기 상태가 최종 결과를 좌우")
    
    messages = ["A", "B", "A"]  # 같은 입력도 다시 해싱
    for i, msg in enumerate(messages):
        hash_val = hashlib.sha256(msg.encode()).hexdigest()
        print(f"   '{msg}' → {hash_val[:16]}...")
    
    print(f"\n실험 2: K값의 역할")
    print("- 라운드마다 다른 K값 → 각 라운드가 고유함")
    print("- 만약 모든 K값이 0이라면 → 보안 취약")
    
    print(f"\n실험 3: W값의 역할") 
    print("- 입력 메시지가 W값으로 변환됨")
    print("- 메시지가 바뀌면 → W값 변화 → 해시 변화")
    
    # 메시지 변화 확인
    test_msgs = ["Hello", "Hello!", "Hell", "Hellp"]
    print("   메시지 변화에 따른 해시 변화:")
    for msg in test_msgs:
        hash_val = hashlib.sha256(msg.encode()).hexdigest()
        print(f"   '{msg:6}' → {hash_val[:16]}...")
hkw_role_experiment()
Python
복사

