루벤 솜센 지음
출처: https://gist.github.com/RubenSomsen/a61a37d14182ccd78760e477c78133cd
어떤 출력이 사용되지 않을지에 대한 힌트를 사용하여 비트코인 블록체인을 거의 무상태로, 완벽하게 병렬화하여 검증합니다. 다른 모든 입력/출력은 효과적으로 단일 집계 해시 값으로 해시됩니다. 검증이 성공하고 힌트가 정확하다면 이 집계 값은 결국 0이 됩니다.
소개
검증은 비트코인의 핵심입니다. 검증 속도가 향상되면 시스템(및 이를 기반으로 구축된 모든 것)의 확장성에 직접적인 영향을 미칩니다. 이러한 이유로 검증 성능을 개선하는 것은 아마도 우리가 할 수 있는 가장 중요한 일 중 하나일 것입니다.
SwiftSync에 대한 일반적인 관찰은 두 개의 세트(여기서는 입력과 출력)가 있고 그 둘(UTXO 세트)의 차이점을 알고 싶을 때, 이 질문에 대한 주어진 답변(힌트가 주어졌을 때)을 검증하는 것이 그 자체로 답을 계산하는 것보다 훨씬 쉽다는 것입니다. 제가 아는 한 통찰력 있는 점은 위의 관찰 결과를 이 컬렉션에 적용하는 방법입니다. 이러한 관찰은 다른 블록체인 관련 맥락에서도 유용한 것으로 보이며, 어쩌면 그 이상의 맥락에서도 유용할 수 있습니다.
현재, Bitcoin Core에서 발생하는 정기적인 검증은 블록체인을 순서대로 탐색하고(몇 가지 사소한 컨텍스트 독립 검사를 실행하면서) UTXO 세트에 출력을 추가한 다음 (지출된) 출력을 검색한 다음 제거하는 것을 요구합니다. 종종 UTXO 세트가 메모리에 맞지 않아 디스크 읽기 및 쓰기로 인해 속도가 더욱 느려집니다.
SwiftSync는 보안 가정을 변경 하지 않고도 이러한 패러다임을 완전히 바꿔 놓습니다. 더 이상 데이터베이스를 검색할 필요가 없고, 메모리 사용량은 거의 0으로 줄어들며, 모든 검증은 병렬로 수행할 수 있습니다. 이렇게 하면 메모리, 디스크 읽기/쓰기, 단일 스레드 성능이 병목 현상 목록에서 제거됩니다. 남은 병목 현상은 CPU와 대역폭이며, 이 두 가지가 모두 능숙할수록 더 빠르게 검증할 수 있습니다.
프로토콜의 무상태성은 블록체인 검증, 제로 지식 증명 구축 또는 다른 작업(예: 슈노르 서명의 일괄 검증)을 위한 메모리 확보 등 메모리가 제한된 환경에 적합할 수도 있습니다. 또한 여러 기기에 걸쳐 인증 부담을 쉽게 분산할 수 있습니다.
SwiftSync가 얼마나 빠른지 정확히 확인하려면 추가적인 벤치마킹이 필요하지만, 아직 병렬화가 이루어지지 않은 매우 예비적인 개념 증명에 따르면 무려 5.28배의 속도 향상이 나타났습니다 .
프로토콜 개요
온체인 각 출력에 대해 블록체인 끝에서 해당 출력이 아직 사용되지 않았는지 여부를 나타내는 힌트가 필요하지만 힌트에는 1비트( 압축 시 100MB 미만 )만 필요합니다.
프롬프트가 올바르지 않으면 검증이 실패합니다(이것은 DoS 인터페이스입니다). 이러한 프롬프트는 신뢰할 수 있는 소스(그렇지 않으면 전체 노드 소프트웨어 바이너리)에서 나와야 합니다 [1] .
우리는 특정 검사를 건너뛰기 위해 "assumevalid "에 의존할 것입니다. 반드시 필요한 것은 아니지만, 이를 통해 더욱 우아하고 대역폭 효율적인 프로토콜이 만들어집니다. 나중에 assumeavlid 의 구체적인 결과 , assumevalid 버전이 아닌 버전 , 그리고 SwiftSync와 "assumeutxo"의 관계에 대해 설명하겠습니다.
우리는 모든 입력과 출력을 순차적으로 독립적인(완전히 병렬화 가능한) 방식으로 처리합니다. 우리에게 필요한 유일한 상태는 32바이트 필드의 해시된 집계 값 입니다(메모리 사용 없음, 디스크에 대한 무작위 읽기 또는 쓰기 없음). 이 프로토콜의 뇌물 수수는 사용 가능한 CPU와 대역폭에 따라 선형적으로 증가합니다.
각 출력에 대해:
- 프롬프트에서 체인의 맨 위에서도 출력이 사용되지 않을 것이라고 표시되는 경우:
- 그런 다음 UTXO 세트에 씁니다(즉, 추가 전용 - SwiftSync가 완료되고 일반 유효성 검사 모드로 돌아갈 때까지 이 데이터가 필요하지 않음)
- 그 반대로:
- 출력 포인트를 해시한 다음 해시된 집계 값 에 추가합니다.
각 입력에 대해:
- 출력 지점(입력 자체가 전달하는)을 해시한 다음 해시된 집계 값 에서 제거합니다.
SwiftSync를 완료하면 해시 집계 값은 0이 되어야 하며, 이는 UTXO 세트에 쓴 출력이 모두 사용되지 않았고 다른 모든 출력은 한 번만 사용되었음을(이중 지출 없음) 증명합니다.
아래에 더 자세한 내용이 있지만, 이것이 계약의 핵심입니다.
세부 사항
해시 집계 값에 대하여
우리에게 필요한 것은 A 세트(소비되는 출력)가 B 세트(입력)와 어느 순서로든 일관성이 있는지 아는 것입니다. 이러한 순서 독립성 속성은 요소를 추가하기 전에 제거해도 문제가 발생하지 않도록 보장하므로 완전히 병렬화가 가능합니다.
수학적으로는 단순히 원시 이미지를 해시한 다음 결과를 더하거나 빼는 것(모듈러 산술)으로 충분 합니다. 또한 외부에서 데이터를 조작할 수 없도록 비밀 값 솔트도 추가해야 합니다(일반화된 생일 문제). [2]
예
소비된 출력: outpoint_A , outpoint_B
입력: outpoint_C , outpoint_D
hash(outpoint_A||salt) + hash(outpoint_B||salt) - hash(outpoint_C||salt) - hash(outpoint_D||salt) == 0 이면 사용된 출력 목록은 입력 목록과 동일합니다(즉, (A==C && B==D) || (A==D && B==C) ). 나머지는 UTXO 집합과 동일합니다.
assumevalid에 대하여
현재 구현된 대로, assumevalid는 스크립트 유효성 검사를 건너뛰는 데만 사용됩니다. 이를 뒷받침하는 보안 주장은 사용자가 이미 소프트웨어가 정확하다고 신뢰하고 있으므로 체인의 유효성에 대한 진술도 신뢰할 수 있다는 것입니다. 스크립트 외에도 건너뛸 수 있는 다른 것들이 있지만, 남아 있는 검사 대부분은 비용이 많이 들지 않거나 제거하기 어렵기 때문에 그대로 두는 것 같습니다. assumevalid의 사용법을 약간 확장하면 입력을 검증하는 데 필요한 데이터 양을 크게 줄일 수 있습니다. 살펴보세요.
UTXO 세트의 완전한 항목에는 다음 데이터가 포함됩니다.
- 출력 지점
- 출력 스크립트
- 코인베이스 태그
- 블록 높이
- 양
단순 케이스
assumevalid가 없으면 입력을 확인하기 위해 모든 데이터를 사용할 수 있어야 합니다. SwiftSync의 assumevalid 버전에서는 출력 지점(위 목록의 (1))만 사용합니다. 이 방법은 각 입력란에 이미 데이터가 들어 있으므로 편리합니다. (2)가 생략된 이유는 쉽게 이해할 수 있습니다. 스크립트는 어쨌든 이를 assumevalid에서 확인하지 않기 때문입니다. 하지만 나머지 데이터는 어떨까요?
(3) 코인베이스 태그를 건너뛰면 코인베이스에서 생성된 출력이 허용된 사용 시간보다 일찍 사용되었는지 더 이상 확인할 수 없습니다. 이러한 출력은 기본적으로 최대 100개 블록의 암묵적 상대적 시간 잠금을 갖습니다. 이러한 상대적 시간 잠금을 확인하려면 (4) 블록 높이도 알아야 합니다. 마찬가지로, 트랜잭션의 nsequence 필드는 상대적 시간 잠금을 활성화하는 데에도 사용될 수 있습니다(여기서는 살펴보지 않겠습니다).
블록 높이는 병렬 검증 사례에서만 발생하는 문제에 간접적으로 도움이 됩니다. 즉, 출력이 소비되기 전에 생성되도록 보장합니다(소비된 후가 아님). 블록 높이만으로는 충분하지 않을 수 있습니다. 출력이 생성된 블록에서 사용될 수도 있기 때문입니다. 이에 대해서는 나중에 더 자세히 논의하겠습니다. 왜냐하면 이 문제는 assumvalid가 아닌 버전 에서 명시적으로 처리해야 하기 때문입니다.
우리가 나중에 다룰 또 다른 문제는 BIP30 인데, 이는 현재 UTXO 세트에 대한 전체 액세스가 필요합니다. UTXO 집합은 완전히 주소 지정이 가능하지만, assumevalid 버전에서는 이것이 필요하지 않습니다.
위의 검사를 assumevalid 모드에서 건너뛰는 것은 스크립트 검사(ascemvalid가 건너뛰는 것)만큼 중요한 검사가 없기 때문에 전혀 논란의 여지가 없어야 합니다. [3]
더 어려운 상황
(5) 금액 확인을 건너뛰는 것은 논란의 여지가 있습니다. 금액 확인이 없으면 인플레이션 버그에 대한 활성 확인이 더 이상 없기 때문입니다. 다행히도 이에 대한 해결책이 있습니다. UTXO 집합에서 금액을 합산하여 총 통화 공급량을 초과하는지 확인하는 것입니다. 여기에는 OP_RETURN 출력의 양도 포함되어야 합니다.
이 접근 방식의 한계 중 하나는 거래 계산을 암묵적으로 건너뛰기 때문에 채굴자가 가져가지 않은 자금을 계산하는 쉬운 방법이 없다는 것입니다. 이론상으로는 누군가가 청구되지 않은 돈을 가져가더라도 우리는 눈치채지 못할 것입니다. 희소성의 관점에서 보면 이는 assumevalid의 최악의 시나리오, 즉 잘못된 스크립트를 통해 부적절하게 자금을 얻는 것보다 나쁘지 않습니다. 더욱이 이런 방식으로 빼돌릴 수 있는 자금의 양은 매우 적습니다. [4]
마지막으로, 의심할 여지 없이 단점이기는 하지만 그렇게 크지는 않은 결과가 하나 있습니다. 블록의 입력을 검사할 때 이전 출력 데이터의 전체 세트가 없기 때문에 블록체인 재구성 시 블록체인을 롤백하는 데 필요한 데이터인 소위 "실행 취소" 데이터도 생성할 수 없습니다. 즉, assumevalid SwiftSync 지점보다 더 깊은 롤백은 불가능하다는 의미입니다. Bitcoin Core를 정리 모드로 실행하면 정확히 동일한 제한 사항이 있습니다. 이 문제를 해결할 수 있는 실용적인 방법이 있습니다. 최신 X 블록에 대해 assumevalid SwiftSync를 사용하지 마세요. X는 재편이 일어날 가능성이 낮다고 생각하는 깊이이면 됩니다.
간단히 말해, 최소한으로 확장된 assumevalid 정의에 의존하고 (제거된 노드에서와 같이) 심각한 재구성이 발생할 경우 재동기화 요구 사항을 수용하면 매우 깔끔하고 간단한 프로토콜을 얻을 수 있습니다. 물론, non-assumevalid 모드에서 검증하는 기능도 중요하므로, 이에 대해서는 다음에서 논의하겠습니다.
유효하다고 가정할 필요가 없습니다
assumevalid 없이도 SwiftSync는 모든 합의 규칙을 완전히 검증할 수 있지만, 몇 가지 추가 단계가 필요합니다. 해시된 출력 포인트를 더하고 빼는 것 외에도 해시 값에서 입력을 검사하는 데 필요한 모든 것을 포함해야 합니다( 위의 5개 데이터 목록 참조).
이것을 해시 집계 값에 추가하는 것은 쉽습니다. 왜냐하면 출력을 처리할 때 필요한 데이터가 이미 있기 때문입니다. 하지만 이를 제거하고 싶을 때(입력을 처리할 때), 출력 포인트만 있기 때문에 누락된 데이터를 다시 다운로드해야 합니다. 기존의 전체 검증과 비교했을 때 이 방법은 15% 더 많은 데이터가 필요할 수 있으며, 이는 효율적인 인코딩을 위한 대량 여지를 남겨줍니다 [5] .
사용자가 외부에서 이 데이터를 다운로드하도록 허용할 수도 있지만, P2P 네트워크를 통해 이 데이터를 제공하는 것도 고려할 수 있습니다. 다행히도 대부분의 노드는 이미 이 데이터를 가지고 있습니다. 앞서 언급했듯이 노드가 블록체인을 재구성할 수 있도록 하는 실행 취소 데이터가 바로 우리에게 필요한 것입니다 [6] . 또한 임의의 피어가 잘못된 데이터를 제공하여 검증이 실패하지 않도록 하기 위해 취소 데이터의 해시를 제공하는 힌트를 제공하는 동일한 소스도 필요하다는 점에 유의하십시오 [7] .
이제 금액을 확인하고 있으므로 UTXO 집합에 대한 별도의 인플레이션 확인은 필요하지 않지만, 이미 이전 섹션에서 언급한 몇 가지 새로운 문제가 발생합니다.
블록 내 거래 순서
SwiftSync에서는 병렬화가 기본적으로 활성화되어 있으므로 출력이 생성되기 전에 사용될지 반드시 알 수 있는 것은 아닙니다. 예를 들어, 출력이 생성된 후 같은 블록에서 사용될 수도 있습니다(다른 모든 경우에는 블록 높이를 확인하는 것으로 충분합니다).
이 문제의 존재는 부인할 수 없지만, 주의 깊게 생각해 보면 그 결과가 그렇게 큰 문제가 아니라는 것을 알 수 있다 [8] . 하지만 우리는 이 문제를 확실히 다룰 수 있으며, 일단 이 문제를 다루면 안전에 대해 논의하기가 더 쉬워질 것입니다.
가장 직접적이고 실용적인 방법은 블록별 캐시를 사용하는 것입니다. 이를 블록 단위의 마이크로 UTXO 세트(기술적으로는 순서를 증명할 수 있는 데이터만 필요함)로 생각할 수 있으며, 순서대로 구성됩니다. 현재 블록과 (생성) 블록 높이가 같을 때만 입력을 검색해야 하지만 더 많은 작업을 수행해야 할 이유가 있을 수 있습니다 [9] . 이렇게 하면 일부 상태를 추가하고 순차적 검색을 하게 되어 프로토콜이 덜 우아해지지만, 단일 블록 컨텍스트로 제한됩니다. 여전히 모든 블록을 병렬로 처리할 수 있는 완전한 자유를 누릴 수 있습니다.
캐시 없음 전략을 고수하고 검색 및 순차적 검사를 완전히 피하려는 경우 더 많은 옵션이 있지만 [10] 일반 하드웨어에서 실행되는 전체 노드의 경우 블록 캐시가 충분할 것입니다.
BIP30 검증
SwiftSync가 일반적인 전체 검증과 동등해지려면 UTXO 세트에 액세스할 수 없더라도 BIP30 의 요구 사항도 충족해야 합니다. BIP30은 2012년 3월까지 활성화되지 않았지만, 구현 범위는 제네시스 블록부터 BIP34가 활성화되는 순간(블록 높이 227931, 2013년 3월)까지입니다. BIP30은 기존에 사용되지 않은 출력을 생성하는 블록을 무효화하여 중복 출력을 방지합니다. 이론상으로는 BIP30은 중복 거래를 완벽하게 방지할 수 없습니다(BIP34는 가능하지만 해결되지 않은 몇 가지 문제가 있습니다 ). 여전히 중복 거래를 생성하고, 지출하고, 재생성하는 것이 가능하기 때문입니다. 하지만 현실적으로는 그런 일이 일어난 적이 없습니다.
BIP30이 활성화되기 전에는 동일한 코인베이스 거래 출력이 두 번 생성되는 중복 출력이 두 개 있었습니다. 두 번째 출력이 첫 번째 출력을 덮어쓰므로 실제로는 첫 번째 출력을 사용할 수 없게 됩니다. 이러한 사례는 BIP30에서 예외로 처리됩니다. 자세한 내용은 여기에서 확인하세요.
SwiftSync에는 UTXO 집합이라는 개념이 없으므로 출력이 이미 존재하는지 추적할 수 없습니다. 다행히도, 같은 결과를 얻을 수 있는 또 다른 방법을 고안해낼 수 있습니다. 각 코인베이스 거래 ID가 포함된 데이터베이스를 직접 저장하여 고유성을 확인할 수 있습니다. 이는 227931 * 32바이트 ~= 7MB의 메모리만 차지하며, 원한다면 이를 더욱 최적화하고 무상태로 만들 수도 있습니다 [11] . 이는 BIP30과 동일할 뿐만 아니라, 현재 우리가 하는 방식보다 더 빠르게 대체할 수 있는 방식입니다.
2013년으로 거슬러 올라가는 폭력적인 재편이 발생하여 BIP34가 결코 활성화되지 않고 BIP30이 영원히 지속되는 이론적인 시나리오도 있습니다. 이런 '있거나 없거나' 상황을 처리하는 것보다, 더 실용적인 해결책은 이런 상황이 발생했을 때 SwiftSync가 아닌 인증을 사용하는 것입니다.
SwiftSync와 assumeutxo
Assumeutxo 와 SwiftSync는 완전히 직교적입니다. Assumeutxo는 UTXO 집합이 올바르다는 가정에서 시작하여, 이 가정이 맞는지 확인하기 위해 백그라운드에서 검증을 수행합니다. 후반부에서는 SwiftSync를 사용할 수 있습니다.
assumeutxo로 인해 발생하는 복잡성 중 하나는 두 세트의 상태가 필요하다는 것입니다. 하나는 assumeutxo 지점에서 현재 체인 끝까지의 상태이고, 다른 하나는 genesis 블록에서 assumeutxo 지점까지의 상태입니다. 백엔드 검증이 완료되면 다시 설정 상태로 돌아갑니다. SwiftSync는 거의 상태 비저장이므로 백그라운드 검증에 사용하면 작업이 더 쉬울 수 있습니다.
중요하지만 쉽게 해결할 수 있는 차이점은 더 이상 UTXO 집합을 구성할 필요는 없지만 시작점으로 사용되는 assumeutxo의 UTXO 집합이 SwiftSync 프롬프트에서 파생될 수 있는 UTXO 집합과 정확히 동일한지 확인해야 한다는 것입니다. 우리는 동일한 해시 집계 기법을 사용하여 이를 수행할 수 있습니다.
UTXO 세트에 남아 있을 것으로 예상되는 출력을 처리할 때마다 이를 해시 집계 값에 추가합니다(이것은 출력 지점만이 아니라 전체 UTXO 세트 데이터의 해시가 됩니다). 마찬가지로, 우리는 assumeutxo의 UTXO 세트에 있는 각 항목을 해시한 다음 해시된 집계 값에서 해당 항목을 제거합니다. 둘 다 사실이라면, 결국 0이 됩니다.
SwiftSync의 non-assumevalid 버전을 사용하는 경우 각 출력에 대해 1비트 힌트가 필요하지 않다는 점이 좋습니다. 출력이 UTXO 세트에 포함되는지 여부와 관계없이 해시 집계에 추가하는 데이터가 정확히 동일하기 때문입니다(즉,输出- 输入- UTXO == 0 ). 그런 우아함에 나는 취해 있다.
감사의 말
검증 과정의 최적화는 수년간 논의되어 온 주제입니다. 많은 사람들이 저보다 먼저 나아가서 관련된 아이디어를 탐구했습니다. 코리 필즈의 UHS는 아마도 이 프로토콜과 가장 유사할 것이며, 이는 브램 코헨의 비트필드 아이디어보다 개선된 것입니다. Tadge Dryja의 utreexo 도 상태 감소, 병렬화, 캐시 관련 초기화 블록 동기화(IBD) 힌트 측면에서 이 프로토콜과 겹칩니다.
초기 벤치마크 와 통계를 제공해 준 theStack 과 0xb10c 에게 감사드립니다. 또한 IBD 최적화에 적극적으로 참여해 온 Bitcoin Core 개발자( davidgumberg , l0rinc , andrewtoth )와 유익한 토론을 제공해 주신 분들, 그리고 피드백을 제공해 주신 많은 분들께도 감사드리고 싶습니다.
각주
1. 알림을 받을 수 있는 다른 방법이 있나요? 소프트웨어에 저장된 값에 의존하는 것의 한 가지 단점은 소프트웨어가 몇 달 전에 출시되었을 수 있다는 것입니다. 프롬프트를 사용한 후에는 검증 과정이 상당히 느려집니다. 또 다른 가능성은 신뢰할 수 있는 제3자로부터 [non-assumevalid SwiftSync] 프롬프트를 받는 것입니다. 최악의 경우, 제3자가 거짓말을 하고 결국 귀하의 검증은 실패하게 될 것입니다. 따라서 유효하지 않은 체인이 유효하다고 가정하지 않는 것이 중요합니다. 두 가지 접근 방식을 혼합하는 것도 가능합니다(즉, 노드가 오프라인일 때도 따라잡는 것). 하지만 이 경우 SwiftSync 세션 간 컬렉션에서 UTXO를 제거하기 위해 몇 가지 추가 논리를 추가해야 합니다. 마지막으로, 팁은 온체인 커밋될 수도 있지만, 이를 위해서는 소프트 포크 필요합니다. ↩
2. MuHash와 XOR 스태킹이 더 나은가요? 아니면 비밀 솔트를 사용한 해시 집계가 더 나은가요? 클라이언트 간 상태 비교를 허용하는 등 비밀 salt 요구 사항을 제거하고 싶다면 MuHash 가 실행 가능한 대안이지만, 우리의 사용 사례에는 과도한 것 같습니다. XOR만으로는 충분하지 않습니다. 요소가 두 번 나타나면 서로 상쇄되기 때문입니다(한 번은 입력에, 한 번은 출력에). 추가적인 점검을 통해 이를 보상하는 것도 가능하지만, 이는 여기서 제안하는 접근 방식과 매우 유사할 것입니다. ↩
3. 시간 잠금과 거래 순서 무시하면 최악의 경우는 어떻게 될까요? 시행되지 않은 시간 잠금이나 순서가 잘못된 거래는 단순히 일부 자금이 조기에 사용되었거나 거래 순서에 이상한 일이 발생했음을 의미합니다(자금이 먼저 존재하지 않는 출력에서 사용되어 인플레이션이 발생하고, 그 출력이 나중에 생성되어 인플레이션을 상쇄함). 그리고 이러한 인플레이션이 결국 수정되지 않으면 SwiftSync 검증이 실패합니다. 중요한 점은 이것이 잘못된 스크립트로 인해 출력이 낭비되는 최악의 assumevalid 사례보다 나쁘지 않다는 것입니다. ↩
4. 채굴자들이 가져가지 않은 자금을 좀 더 정확하게 확인하고 싶다면 어떻게 해야 할까? 이론상으로는 채굴자가 받아야 할 모든 수수료를 받지 않은 블록의 모든 입력에 금액 힌트를 추가할 수 있고, 해당 블록에서 어떤 출력이 사용되었는지에 대한 힌트를 제공할 수 있습니다. 이러한 특정한 경우에는 해시에 금액을 포함할 수 있습니다. 채굴자는 항상 1사토시를 남겨서 괴롭힘을 시작할 수 있습니다. 전체 수수료 약정을 요구하거나 기존 수수료를 허용하지 않는 소프트 포크 더 근본적인 해결책이 될 것입니다. 그럼에도 불구하고, 최악의 시나리오가 이미 assumevalid가 가정한 것보다 나쁘지 않다는 점을 감안하면 이러한 제한을 수용하는 것은 완전히 합리적입니다. 관심 있는 사람들은 assumevalid를 비활성화하는 것을 선택해야 합니다. ↩
5. non-assumevalid 버전에 필요한 데이터를 어떻게 압축할 수 있나요? 여기에는 효율적인 압축 및 중복 제거를 위한 대량 기회가 있습니다. 반복되는 패턴(예: 표준 거래), 원시 이미지를 알고 있는 해시(예: P2SH), 자주 발생하는 출력(예: 최근 블록 높이에서 발생하는 대부분의 입력)이 많이 있습니다. BIP337 의 일부 아이디어도 재사용될 수 있습니다. ↩
6. P2P 네트워크를 통해 실행 취소 데이터를 제공하는 것이 다른 목적에 유용할 수 있을까요? 이 기능을 사용하면 가지치기된 노드가 가지치기된 실행 취소 데이터의 해시를 저장하는 한 자신의 가지치기 지점보다 더 깊이 롤백할 수 있습니다(안타깝게도, assumevalid가 적용된 SwiftSync에서는 이를 수행할 수 없음). 또한 피어는 자신이 포크 블록에 대한 실행 취소 데이터를 제공할 의향이 있습니다. 실행 취소 데이터를 사용하면 컨텍스트 없이 블록을 완전히 검증할 수 있으므로 " PoW 사기 방지 "(포크 포크 블록을 선택적으로 검증하여 저대역폭 블록체인 검증)와 같은 프로토콜도 이로부터 이점을 얻을 수 있습니다. ↩
7. DoS 문제를 일으키지 않고 실행 취소 데이터를 제공하는 다른 방법이 있습니까? 가장 직접적인 방법은 이 데이터를 해시하여 각 블록에 넣은 다음 이를 합의의 일부로 만드는 것입니다. 하지만 소프트 포크 어렵죠. 또한, 피어 노드 간의 실행 취소 데이터 상태를 비교하는 이론적인 피어 투 피어 방식도 있습니다. 차이가 있다면 어느 피어 노드가 거짓말을 하고 있는지 찾아내세요(적어도 하나의 피어 노드가 정직하다면 작동할 겁니다). 하지만 이 역시 비교적 복잡합니다. (다른 맥락에서이긴 하지만) 이를 수행하는 예는 여기에서 찾을 수 있습니다. ↩
8. 블록 내에서 거래 순서 확인하지 않는다고 가정하면 공격자는 어떻게 이를 악용할 수 있을까? 잘못된 거래 순서 포함된 체인을 수락하더라도 펀드 소유권이나 인플레이션에 영향 을 미치지 않습니다. 유일한 문제는 정기적으로 완전히 검증하는 노드가 이러한 대체 순서 거부하므로 하드 포크 발생한다는 것입니다. 그러나 이러한 포크 이러한 대체 순서 실제로 가장 많은 작업 증명을 가진 온체인 나타나지 않는 한 발생하지 않을 것입니다. 공격자가 이를 실질적으로 악용할 수 있는 상황을 상상하는 것은 매우 어렵습니다. 다음은 시도입니다. (1) 작업 증명이 가장 많은 현재 체인을 전복하고 이 대체 순서 사용하여 블록을 채굴합니다. (2) 피해자에게 체인의 맨 위까지 SwiftSync 검증을 수행하게 하여 대체 순서 확신시킵니다(아마도 먼저 소프트웨어를 오염시키는 것일 수도 있음). (3) 피해자가 이 "잘못된" 온체인 에서 당신에게서 비트코인을 구매하도록 합니다. 결국에는 "좋은" 체인이 "나쁜" 체인을 다시 이길 것입니다. 결론적으로, 이 공격은 비트코인을 판매한 다음 거래를 재구성하는 것보다 더 효율적이지 않은 것으로 보입니다. 공격자의 목표가 단순히 혼란을 조성하는 것이라 하더라도, 대규모 조직 개편이 그러한 공격과 어떻게 다른지 말할 수는 없습니다. ↩
9. 모든 UTXO를 다시 다운로드하는 대신 캐싱하면 대역폭을 절약할 수 있을까요 ? 네, 이를 처리하는 좋은 방법이 있지만 복잡성이 더 커지고 전체 검증 프로세스가 직렬화되어 병렬화가 어렵습니다. 캐시가 여러 블록을 포함하도록 할 수 있으며, 한 걸음 더 나아가 어떤 출력이 사용될지에 대한 힌트를 제공할 수도 있습니다. 이로 인해 대역폭 등의 일부 병목 현상이 다시 발생할 수 있습니다. ↩
10. 캐시가 없다면 블록 내의 순서 어떻게 확인할 수 있나요? UTXO 집합에 추가 요소인 정식 거래 번호를 추가할 수 있습니다(출력 번호도 허용되고 다른 이유로 매력적이지만, 그 정밀도가 우리의 필요를 초과합니다). 이제 출력을 사용해야 하고 해당 제네시스 블록 높이가 현재 높이와 같은 경우, 평가 중인 거래 보다 먼저 생성되었는지 확인하기 위해 거래 ID를 확인합니다. 이 접근 방식의 단점은 전체 UTXO 세트의 크기(항목당 2바이트)가 커진다는 것입니다. 더 흥미로운 접근 방식은 같은 블록에서 사용될 각 출력에 대해 추가 힌트를 추가하는 것일 수 있습니다. 구체적으로, 우리는 그것을 사용할 거래 번호를 힌트로 줍니다(이 번호는 그것을 출력으로 포함하는 거래의 거래 번호보다 커야 합니다. 우리는 단지 두 번호의 차이만 인코딩합니다). 그런 다음 이 힌트를 해시 값에 포함합니다. 이제 현재 높이와 일치하는 생성 높이를 가진 입력을 만나면 해당 거래 번호를 해시에 포함한 다음 집계 값에서 제거합니다. 안타깝게도 출력이 생성되자마자 같은 블록에서 사용되는 경우가 매우 흔하므로 필요한 힌트 데이터가 상당히 증가할 수 있습니다. 캐싱이 더 명확한 접근 방식이기는 하지만, 캐싱을 사용하지 않는 대안과 비교해 보는 것도 흥미로울 수 있습니다. ↩
11. BIP30은 캐싱이 사용되는 또 다른 곳입니다. 어떻게 최적화할 수 있을까요? txid를 잘라내면 데이터 세트를 줄일 수 있습니다. 일반적으로 충돌에 대해 걱정하지만, 보존해야 할 특정 비트를 미리 계산하여 충돌이 발생하지 않도록 보장할 수 있으며, 이를 통해 온체인 충돌 없는 결과를 보장할 수 있습니다(소트를 사용하여 다시 해싱하는 것도 가능하지만 계산 속도가 느립니다). 정보 이론적으로는 각 txid를 18비트로 줄일 수 있지만, 계산적으로는 불가능합니다. 실제로는 4바이트 이내(심지어 3바이트 이내)에서 처리할 수 있어야 합니다. 이런 방식으로 데이터 크기는 1MB 이하로 줄어듭니다. 한 걸음 더 나아가 이 상태를 버리고 해시된 집계 값을 이용해 검색할 수도 있습니다. 이를 위해 잘린 해시 값의 순서 있는 목록을 제공합니다. 우리는 목록을 반복하면서 각 요소가 이전 요소보다 큰지 확인하고(따라서 고유성을 증명함) 이를 해시 집계 값에 추가할 것입니다. 블록에서 해당 값을 만나면 해시 집계 값에서 제거합니다(0이 나오면 집합 동등성이 증명됩니다). ↩




