IT.About/라이트닝 네트워크
[Mastering the Lightning Network] 10 - Onion Routing
zNine
2022. 4. 21. 11:35
728x90
반응형
- 마지막 10장에서는 라이트닝 네트워크의 Onion 라우팅 매커니즘에 대해 설명한다.
- Onion 라우팅
- 미 해군 연구원들이 통신 보안 프로토콜로 발명, 모든 사람이 인터넷을 익명으로 사용할 수 있도록 하며,
- Tor에서 가장 많이 사용됨.
- 메시지 발신자가 의도한 수신자에게 가장 안쪽 레이어가 전달될 때까지 각 중간 노드에 의해 "벗겨지는" 연속된 중첩 암호화 레이어를 구축하는 암호화된 통신 방식.
- 양파 껍질처럼 한 번에 한층씩 벗겨지는 계층화된 암호화.
- 중간의 각 노드는 한 레이어만 "벗길" 수 있고 통신 경로상 다음 전달할 곳을 볼수 있음.
- 발신자 외에는 통신 경로, 목적지, 길이를 알 수 없고, 각 중개자는 이전/다음 홉만 알고 있음.
- 라이트닝 네트워크에서는 2009년에 Sphinx 기반의 Onion 라우팅 프로토콜을 사용. (BOLT #4 에 정의)
GitHub - lightning/bolts: BOLT: Basis of Lightning Technology (Lightning Network Specifications)
BOLT: Basis of Lightning Technology (Lightning Network Specifications) - GitHub - lightning/bolts: BOLT: Basis of Lightning Technology (Lightning Network Specifications)
github.com
- Onion 라우팅의 실사용 예
- Alice가 Dina에게 비밀 편지를 밀봉된 봉투에 넣어 전달한다고 가정,
- 봉투는 암호화 계층을 나타내며, 지정된 수신자만 봉투를 열고 내용을 읽을 수 있다.
- 경로 선택
- 라이트닝에서 경로는 보내는 사람이 선택.
- Alice는 Bob과 Chan을 거쳐 Dina에게 전달되는 경로를 선택
- 레이어 만들기
- A는 D에게 비밀 편지를 쓰고, 봉투안에 편지를 봉해 바깥쪽에 "To Dina"라고 표기.
- 봉투는 D만이 봉투를 열어 편지를 읽을 수 있도록 D의 공개키로 암호화.
- D에게 전달될 봉투는 C가 전달해주어야 하므로,
- C에게 보내는 봉투안에 D의 봉투를 넣고, (C는 "To Dina"만 볼 수 있음) C만 볼수 있도록 C의 공개키로 암호화.
- 또, C의 봉투는 B가 전달해주어야 하므로,
- B에게 보내는 봉투안에 C의 봉투를 넣고, B만 봉투안의 "To Chan"을 볼 수 있도록 B의 공개키로 암호화.
- 봉투안의 내부를 들여다 보면,
- 레이어 필링(벗기기)
- A는 "To Bob"이 적힌 B의 공개키로 암호화된 봉투를 B에게 전달.
- B은 전달자가 A라는 것은 알지만, 발신자인지 전달자인지는 모르며,
- 봉투를 열어 "To Chan"이 적힌 봉투를 찾고, C에게 전달…
- 최종적으로 D는 C에게 봉투를 받고, 열어 안의 편지를 발견.
- HTLC의 Onion 라우팅
- A는 모든 중개 노드에게 경로의 다음 노드로 설정할 HTLC를 알려준다.
- 발신자 A를 원본 노드(origin node), 수신자 D를 최종 노드(final node), 중개자 B와 C를 홉(hop) 이라 함.
- 모든 홉은 다음 홉으로 나가는 HTLC를 설정해야하며, A가 각 홉에 전달한 정보를 홉 페이로드 또는 홉 데이터 라고 함.
- A에서 D로 라우팅되는 메세지를 양파(Onion)라고 하며, 각 홉에 암호화면 홉 페이로드 혹은 홉 데이터로 구성 된다.
- A는 홉 데이터로 양파를 구성하고, 각 홉에 최종 노드에 지불을 보내기위해 outgoing HTLC를 구성하는 방법을 알려야 함.
- Alice의 경로 선택
- 채널에 대한 공지와 업데이트를 통해 A는 B, C, D 간의 채널에 대한 다음 정보를 알고 있음.
- 경로를 구성할 때 채널을 참고하는데 사용할 수 있는 각 채널의 short_channel_id
- 각 HTLC의 만료 시간에 추가할 수 있는 cltv_expiry_delta(timelock delta)
- 총 라우팅 요금을 계산하는데 사용할 수 있는 free_base_msat 및 fee_proportional_millionths
- Alice의 페이로드 구성
- 각 홉에 전달되는 정보에 사용할 수 있는 두 가지 형식이 있음.
- hop data - 레거시 고정-길이 형식
- hop payload - 유연한 TLV(Type-Length-Value) 형식
- Dina의 최종 노드 페이로드
- A는 D에게 전달할 페이로드를 가장 먼저 구성하며,
- 최종 노드(수신자)이므로, outgoing HTLC는 구성하지 않음.
- D의 홉 페이로드는 Dina for Alice에서 생성한 인보이스의 정보와 일치해야하며, TLV형식의 다음 필드를 포함.
- amt_to_forward
- 지불 금액, 여러 페이로드로 지불된 경우 총액보다 작고, 그렇지 않을 경우 total_msat와 일치해야 함.
- outgoing_cltv_value
- 지불 만료시간 잠금, 인보이스내 min_final_cltv_expiry 값으로 설정.
- payment_secret
- 인보이스에 전달된 256-bit 비밀 값.
- total_msat
- 인보이스와 일치하는 총액.
- amt_to_forward
- A가 D에게서 받은 인보이스에는 금액이 50,000sat, D는 min_final_cltv_expiry를 18블록(약 3시간)으로 지정 함.
- A가 결제를 시도할 때 비트코인 블록체인의 높이가 700,000블록이었다고 가정하면,
- A는 outgoing_cltv_value를 최소 700,018로 설정해야 함.
- D에 대한 홉 페이로드를 다음과 같이 구성하고, 이를 TLV 형식으로 직렬화 한다
-
amt_to_forward : 50,000,000 outgoing_cltv_value: 700,018 payment_secret: fb53d94b7b65580f75b98f10...03521bdab6d519143cd521d1b3826 total_msat: 50,000,000
-
- Chan의 홉 페이로드
- C에 대한 홉 페이로드를 구성하고, 이것으로 D에 대한 HTLC를 설정하는 방법을 알려준다.
-
short_channel_id: 010002010a42be amt_to_forward: 50,000,000 outgoing_cltv_value: 700,018
-
- C에 대한 홉 페이로드를 구성하고, 이것으로 D에 대한 HTLC를 설정하는 방법을 알려준다.
- Bob의 홉 페이로드
-
short_channel_id: 000004040a61f0 amt_to_forward: 50,100,000 outgoing_cltv_value: 700,038
- 라우팅 수수료에 대한 100sat, 20블록의 타임록 델타를 반영.
-
- 각 홉에 전달되는 정보에 사용할 수 있는 두 가지 형식이 있음.
- Onion 레이어 포장하기 (Wrapping the Onion Layers)
- https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md#packet-construction 에 자세히 설명되어 있다.
- 고정 길이 Onion (Fixed-Length Onions)
- Onion 패킷의 길이를 모든 단계에서 동일하게 유지하므로써 홉 노드가 경로의 길이나 경로의 위치를 알지 못하게 하는데 사용.
- Onion 페이로드는 1,300 바이트이며, 각 홉의 페이로드는 65바이트 이하(65보다 작으면 65에 맞게 채워짐).
- 따라서 최대 라우팅 경로는 20홉이며, 모든 홉은 20개의 홉 중 자신이 첫번째 홉으로 착각하게 끔 패킷 뒷 부분이 채워짐.
- Onion의 크기는 1,366 바이트이며, 아래와 같이 구성
- 1 byte - 버전 바이트
- 33 bytes - 압축된 공개 세션 키(A의 센셔 키이며, 이것으로 A의 신원을 숨기면서 홉당 shared secret를 생성 할수 있음)
- 1,300 bytes - 실제 페이로드
- 32 bytes - HMAC 무결성 체크섬
- 포장하기 (Outlined)
- 각 홉에 대해 A에서 동일한 프로세스를 반복.
- 홉 별로 공유 비밀과 rho, mu 및 pad 키를 생성.
- 1,300 바이트의 필러를 생성하고, 1,300바이트의 어니언 페이로드 필드를 필러로 채움.
- 페이로드에 대한 HMAC 계산(최종 노드의 경우 0)
- 홉 페이로드의 길이 + HMAC + 길이 자체를 저장할 공간의 길이를 계산.
- 홉 페이로드의 길이 만큼 Onion 페이로드를 오른쪽으로 shift. 오른쪽 필러가 삭제되어 왼쪽에 페이로드를 위한 공간이 생긴다.
- 페이로드 필드의 앞쪽에 홉 페이로드의 길이 + 홈 페이로드 + HMAC 을 insert.
- rho 키를 사용해 1,300 바이트의 일회용 패드를 생성.
- rho키에서 생성된 바이트를 XOR하여 전체 Onion 페이로드를 난독화.
- mu키를 사용해 Onion 페이로드의 HMAC을 계산.
- 세션 공개 키를 추가.(홉이 공유 비밀을 계산할 수 있도록)
- 버전 번호를 추가
- 공유 비밀과 이전 홉의 공개키를 해시하여 파생된 값을 사용해 세션 키를 re-blind.
- Dina의 홉 페이로드 래핑
- 1,300바이트의 빈 필드인 고정 길이 Onion 페이로드로 시작.
- pad 키에서 생성된 pseudo 랜덤 바이트 스트림 "filler"로 페이로드를 채움.
- 랜덤 바이트 스트림 생성은 ChaCha20 알고리즘을 암호화 pseudo 난숙 생성기(CSPRNG)로 사용.
- D의 홉 페이로드를 왼쪽에 삽입, 오른쪽 오버플로되는 것은 버림.
- D만 읽을 수 있도록 전체 페이로드를 난독화.
- 난독화를 위해 rho 키(D도 알고 있는)를 사용해 바이트 스트림을 생성, 페이로드 비트와 바이트 스트림을 XOR.
- XOR은 두번 적용 시 원래대로 복구(ex: XOR(a,b), b) = a)
- mu 키를 초기화 키로 사용하는 D의 페이로드에 대한 해시 기반 메시지 인증 코드(HMAC)을 계산하고, D의 홈 페이로드에 추가.
- HMAC은 보안 체크섬 역할 및 D가 홉 페이로드의 무결성을 확인하는데 도움.
- Chan의 홉 페이로드 래핑
- D를 위해 생성된 1,300 바이트의 Onion 페이로드로 시작.
- D를 위해 생성된 1,300 바이트의 Onion 페이로드로 시작.
- Bob의 홉 페이로드 래핑
- 마지막 Onion 패킷
- B에게 보낼 준비 완료.
- Onion 페이로드에 대한 HMAC 계산하여 B이 확인할 수 있는 체크섬 암호화 방식을 보호.
- shared secret, rho, mu, pad 키를 생성하기 위해 각 홉에서 사용할 33바이트 공개 센셔 키를 추가.
- 끝으로 버전 번호(현재 0)을 맨 앞에 추가
- Onion 패킷 보내기
- 패킷이 전달되는 방식과 HTLC가 경로를 따라 배포되는 방식을 살펴본다.
- updateadd_htlc 메시지
- Onion 패킷을 update_add_htlc 메세지의 일부로 전송
-
[channel_id:channel_id] [u64:id] [u64:amount_msat] [sha256:payment_hash] [u32:cltv_expiry] [1366*byte:onion_routing_packet]
- Alice가 Bob에게 Onion을 보냄
- channel_id - A와 B 채널ID
- ID - 채널에 있는 HTLC의 ID, 0 ~
- amount_msat - 밀리 사토시, 50,200
- payment_hash - 지불 해시
- cltv_expiry - HTLC의 만료 시간 잠금, 700,058
- onion_routing_packet - 모든 홉의 페이로드로 구성된 Onion packet from Alice
- Bob의 Onion 패킷 확인
- B은 HTLC를 커밋 TX에 추가하고, A로 채널 상태를 업데이트.
- Onion을 다음과 같이 벗긴다.
- 패킷에서 세션 키를 가져와 A-B 공유 비밀을 도출.
- 공유 비밀에서 mu키를 생성해, 이것으로 HMAC 체크섬 확인.
- HMAC을 확인했으므로 패킷내에서 1,300바이트 페이로드의 래핑 해제를 시작할 수 있음.
- 목표는 B이 자신의 홈 페이로드를 검색하고, 나머지 패킷을 다음 홉으로 전달하는 것
- Bob의 Filler 생성
- A와 약간 다른 방식으로 필러를 생성하지만 동일한 규칙을 따름.
- Onion 페이로드를 1,300바이트 추가로 확장하고 0으로 값을 채움. (2,600바이트가 됨)
- Bob의 홉 페이로드 난독화
- A-B 공유 키에서 rho 키 생성. 이것을 ChaCha20 알고리즘을 사용해 2,600바이트를 생성하는데 사용.
- XOR 연산,
- 이때 페이로드 첫 1,300바이트에 대해서는 A가 적용한 동일한 작업이므로, XOR에 의해 난독화가 해제되고,
- 0으로 채운 1,300바이트는 임의의 필러 데이터로 바뀜.
- Bob의 다음 홉을 위한 외부 HMAC 추출
- 내부 HMAC은 각 홉에 포함되고, 이는 다음 홉의 외부 HMAC이 됨.
- B은 내부 HMAC을 추출하고, C가 자신의 암호화된 패킷의 HMAC을 확인할 수 있도록 난독 해제된 패킷에 추가할 것이지 때문에 따로 보관한다.
- Bob은 자신의 페이로드를 제거하고, Onion을 Left-Shifts
- B의 제거된 페이로드 만큼 추간 필러(1,300바이트)가 채워지는 것.
- B의 제거된 페이로드 만큼 추간 필러(1,300바이트)가 채워지는 것.
- Bob의 새로운 Onion 패킷 구성
- 페이로드를 Onion 패킷에 복사하고, C에 대한 외부 HMAC을 추가하고, 세션 키를 re-randomize하고, 버전 바이트를 추가한다.
- 세션 키를 무작위화하기위해 B는 노드 공개 키와 파생된 공유 비밀을 사용해 홉에 대한 블라이드 요소를 계산.
-
b_bob = SHA-256(P_bob || shared_secret_bob)
-
- 세션 키와 블라이드 팩터를 사용해 EC 곱셈을 수행, 세션 키를 무작위화.
-
session_key_chan = session_key_bob * b_bob
-
- C에 의해 처리 될 session_key_chan 공개 키는 onion 패킷 앞에 추가된다.
- Bob은 HTLC 세부정보를 확인
- B의 홉 페이로드에는 C를 위한 HTLC를 만드는데 필요한 지침이 포함.
- short_channel_id, amt_to_forward, cltv_expiry를 검색,
- short_channel_id를 가진 채널이 있는지 확인하고, C와의 채널이 있다는 것 확인.
- 나가는 금액(50,100)이 들어오는 금액(50,200)보다 적으므로 수수료가 충족되는지 확인.
- 나가는 cltv_expiry가 들어오는 cltv_expiry보다 작은지확인하여, 위반이 있는 경우 B에게들어오는 HTLC를 요청할 수 있는 충분한 시간을 제공.
- Bob이 Chan에게 update_add_htlc를 보냄
- C에게 보낼 HTLC 구성
- channel_id
- id
- amount_msat - 50,100
- payment_hash
- cltv_expiry - 700,038
- onion_routing_packet
- C에게 보낼 HTLC 구성
- Chan의 Onion 전달
- B와 똑같은 과정을 반복
- update_add_htlc 수신, HTLC 요청을 처리하여 Commit TX에 추가.
- Alice-Chan 공유 키와 mu 하위 키 생성.
- 패킷 HMAC을 확인하고, 다음 1,300바이트 페이로드 추출.
- 페이로드를 1,300바이트 추가 확장하고, 0으로 채움.
- rho 키 생성하여 2,600 바이트 생성
- XOR하여 1,300바이트 어니언 페이로드에 대한 난독화를 해제하고, 추가 1,300바이트를 난독화하여 필로러 바꿈.
- 페이로드에서 내부 HMAC을 추출, 이는 D의 외부 HMAC이 됨.
- C의 홉 페이로드를 제거하고, 페이로드를 왼쪽으로 이동.
- Onion 페이로드를 사용해 D에 대한 패킷을 구성.
- D에 대한 update_add_htlc 메세지를 작성하고, Onion 패킷을 삽입.
- update_add_htlc를 D에게 보냄.
- C는 B이 D에 대해 이전 홉에서 했던 것과 같이 세션 키를 re-randomize.
- Dina의 최종 페이로드 수신
- C로 부터 update_add_htlc를 받으면 자신을 위한 지불임을 알게되고, 마지막 홉이라는 것도 알게 됨.
- Onion을 까서 확인하고, HTLC를 사용하기 위해 update_fulfill_htlc로 C에게 응답.
- update_fulfill_htlc를 A까지 역방향으로 전달되며, 모든 HTLC가 사용되고 채널 잔액이 업데이트 된다. 결제 완료!!
- 오류 반환 (Returning Errors)
- https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md#returning-errors 에 정의.
- 지불, Onion, 홉에 문제가 있는 경우 에러를 역방향으로 전파하여모든 노드에 실패를 알리고 HTLC를 해제해야 함.
- 아래와 같이 세 가지 범주로 나뉘며, 영구/일시적인 오류로 세분화 됨.
- onion failure
- node failure
- channel failure
- 에러는 아래와 같이 에러를 발견한 반환 노드에 의해 인코딩 됨
-
[32*byte:hmac] [u16:failure_len] [failure_len*byte:failuremsg] [u16:pad_len] [pad_len*byte:pad]
-
- hmac
- 반환 패킷 HMAC 체크섬은 Onion에서 설정된 shared secret 에서 생성된 um 키로 계산.
- 반환 노드는 ammag(감마의 반대) 키를 생성하고, ammag에서 생성된 바이트 스트림과 XOR 연산을 사용해 반환 패킷을 난독화.
- Onion 패킷을 받은 홉으로 전달.
- error를 수신한 각 홉은 ammag 키를 생성ㅗ, ammag의 바이트 스트림과 XOR하여 패킷을 다시 난독화.
- 원본 노드가 패킷을 받으면, 각 홉에 대햇 ammag 및 um 키를 생성해 반환 패킷이 나올때까지 XOR연산 반복.
- Failure Messages
- https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md#failure-messages 에 정의.
- 2바이트 실패 코드와 실패 유형에 적용할 수 있는 데이터로 구성.
- failure_code
- 0x8000 (BADONION) - 구문 분석을 할 수 없는 onion
- 0x4000 (PERM) - 영구 실패(아니면 일시적)
- 0x2000 (Node) - 노드 장애(아니면 채널)
- 0x1000 (UPDATE) - 새 채널 업데이트가 동봉됨
- Stuck payment
- LN의 현재 구현에서는 결제 시도가 중단 될 가능성이 있음. 이때 HTLC가 만료될 때까지 해결할 수 없음.
- 해당 HTLC에 약정된 자금은 HTLC가 만료될때 까지 사용할 수 없고, 동일한 지불을 재시도할 수도 없다. (실패하거나 완료되어야 끝남)
- Stuckless payments
- 위 문제에 대해 제안된 솔루션 중 하나.
- HTLC와 다른 암호화 형식을 사용하는 PTLC(Point Time-Locked Contracts).
- ECDSA를 사용하면 복잡하지만, 비트코인의 Taproot와 Schnorr 서명 기능을 사용하면 휠씬 쉬움
- Bitcoint 에서 기능이 활설화된 후 LN에서 PTLC가 구현될 것으로 예상.
728x90
반응형