Bởi Ruben Somsen
Nguồn: https://gist.github.com/RubenSomsen/a61a37d14182ccd78760e477c78133cd
Xác minh gần như không có trạng thái, có thể song song hóa hoàn toàn blockchain Bitcoin bằng cách sử dụng các gợi ý về đầu ra nào sẽ không được sử dụng. Tất cả các đầu vào/đầu ra khác đều được băm nhỏ thành một giá trị tổng hợp duy nhất; nếu xác minh thành công và các gợi ý là chính xác, giá trị tổng hợp này cuối cùng sẽ bằng không.
giới thiệu
Xác thực là cốt lõi của Bitcoin. Bất kỳ cải tiến nào về tốc độ xác minh đều có tác động trực tiếp đến mở rộng của hệ thống (và mọi thứ được xây dựng trên đó). Vì lý do này, cải thiện hiệu suất xác minh có lẽ là một trong những điều quan trọng nhất chúng ta có thể làm.
Nhận xét chung về SwiftSync là nếu chúng ta có hai tập hợp (ở đây là đầu vào và đầu ra) và muốn biết sự khác biệt giữa chúng (các tập hợp UTXO), thì việc xác minh câu trả lời cho câu hỏi này (có gợi ý) sẽ dễ hơn nhiều so với việc tính toán chính câu trả lời. Điều sâu sắc (theo như tôi biết) là cách áp dụng những quan sát trên vào bộ sưu tập này. Quan sát này có vẻ cũng hữu ích trong hoàn cảnh liên quan đến blockchain khác, và có lẽ còn hơn thế nữa.
Hiện tại, xác thực thường xuyên diễn ra trong Bitcoin Core yêu cầu phải duyệt blockchain theo thứ tự (trong khi chạy một số kiểm tra nhỏ không phụ thuộc vào ngữ cảnh), thêm đầu ra vào bộ UTXO, sau đó truy xuất đầu ra (đã chi) rồi xóa chúng. Bộ UTXO thường không vừa với bộ nhớ, làm chậm hơn nữa quá trình đọc và ghi đĩa.
SwiftSync thay đổi hoàn toàn mô hình này mà không làm thay đổi các giả định về bảo mật. Không còn phải truy xuất cơ sở dữ liệu nữa, việc sử dụng bộ nhớ được giảm xuống gần bằng không và mọi xác thực có thể được thực hiện song song. Điều này loại bỏ bộ nhớ, khả năng đọc/ghi đĩa và hiệu suất luồng đơn khỏi danh sách các điểm nghẽn có thể xảy ra. Những nút thắt còn lại là CPU và băng thông, bạn càng giỏi cả hai thì có thể xác minh càng nhanh.
Tính không trạng thái của giao thức cũng có thể khiến nó phù hợp với hoàn cảnh hạn chế về bộ nhớ, chẳng hạn như sử dụng phần cứng chuyên dụng (FPGA và ASIC) để xác minh blockchain, xây dựng bằng chứng không kiến thức hoặc chỉ đơn giản là giải phóng thêm bộ nhớ cho các hoạt động khác (chẳng hạn như xác minh hàng loạt chữ ký Schnorr). Bạn cũng có thể dễ dàng phân bổ gánh nặng xác minh trên nhiều thiết bị.
Chúng tôi sẽ cần đánh giá chuẩn sâu hơn để xác định chính xác tốc độ của SwiftSync, nhưng một bằng chứng khái niệm rất sơ bộ (chưa có bất kỳ sự song song hóa nào) cho thấy tốc độ tăng đáng kinh ngạc lên tới 5,28 lần .
Tổng quan về giao thức
Đối với mỗi đầu ra trên Chuỗi, chúng ta cần một gợi ý để chỉ ra liệu đầu ra đó có vẫn chưa được sử dụng ở đầu blockchain hay không, nhưng một gợi ý chỉ cần một bit ( ít hơn 100 MB sau khi nén ).
Nếu lời nhắc không chính xác, quá trình xác minh sẽ không thành công (đây là giao diện DoS) và những lời nhắc như vậy phải đến từ một nguồn đáng tin cậy (nếu không, thì là phần mềm nhị nút đầy đủ của bạn) [1] .
Chúng tôi sẽ dựa vào "assumevalid " để bỏ qua các bước kiểm tra cụ thể. Điều này không bắt buộc nhưng nó tạo ra một giao thức thanh lịch hơn và tiết kiệm băng thông hơn. Chúng tôi sẽ giải thích những hậu quả cụ thể của assumeavlid , phiên bản không phải là sumevalid và mối quan hệ giữa SwiftSync và "assumeutxo" sau.
Chúng tôi xử lý tất cả đầu vào và đầu ra theo cách độc lập tuần tự (hoàn toàn có thể song song hóa). Trạng thái duy nhất chúng tôi yêu cầu là giá trị tổng hợp được băm của trường 32 byte (không sử dụng bộ nhớ, không đọc hoặc ghi ngẫu nhiên vào đĩa). Sự hối lộ của giao thức này mở rộng theo tỷ lệ thuận với CPU và băng thông hiện có.
Đối với mỗi đầu ra:
- Nếu lời nhắc chỉ ra rằng đầu ra sẽ không được chi tiêu ngay cả ở đầu Chuỗi:
- Sau đó ghi nó vào bộ UTXO (tức là chỉ thêm vào - chúng ta không cần dữ liệu này cho đến khi SwiftSync hoàn tất và trở về chế độ xác thực bình thường)
- ngược lại:
- Băm các điểm đầu ra của nó và sau đó thêm chúng vào giá trị tổng hợp đã băm
Đối với mỗi đầu vào:
- Băm điểm đầu ra của nó (mà chính đầu vào mang theo), sau đó xóa nó khỏi giá trị tổng hợp đã băm
Khi chúng ta hoàn tất SwiftSync, giá trị tổng hợp băm sẽ là 0, chứng tỏ rằng các đầu ra chúng ta đã ghi vào bộ UTXO đều chưa được sử dụng và tất cả các đầu ra khác chỉ được sử dụng một lần (không có chi tiêu kép).
Có thêm thông tin chi tiết bên dưới, nhưng đây là nội dung cốt lõi của thỏa thuận.
chi tiết
Về giá trị tổng hợp băm
Tất cả những gì chúng ta cần là biết liệu tập A (các đầu ra được chi tiêu) có nhất quán với tập B (các đầu vào) theo bất kỳ thứ tự nào hay không. Thuộc tính độc lập về thứ tự này đảm bảo rằng việc xóa một phần tử trước khi thêm nó vào sẽ không gây ra vấn đề gì, nghĩa là nó hoàn toàn có thể song song hóa được.
Về mặt toán học, chỉ cần băm tiền ảnh rồi cộng/trừ kết quả (số học mô-đun) là đủ - chúng ta cũng cần thêm giá trị muối bí mật để đảm bảo dữ liệu không thể bị thao túng từ bên ngoài (bài toán sinh nhật tổng quát). [2]
ví dụ
Đầu ra đã sử dụng: outpoint_A , outpoint_B
Đầu vào: outpoint_C , outpoint_D
Nếu hash(outpoint_A||salt) + hash(outpoint_B||salt) - hash(outpoint_C||salt) - hash(outpoint_D||salt) == 0 , thì danh sách đầu ra đã sử dụng của chúng ta giống hệt với danh sách đầu vào (tức là (A==C && B==D) || (A==D && B==C) ), và phần còn lại giống hệt với tập UTXO.
Giới thiệu về assumevalid
Theo cách triển khai hiện tại, assumevalid chỉ được sử dụng để bỏ qua xác thực tập lệnh. Lập luận bảo mật đằng sau điều này là người dùng đã tin tưởng phần mềm của họ là chính xác, vì vậy họ cũng có thể tin tưởng vào tuyên bố về tính hợp lệ của một Chuỗi. Có những thứ khác ngoài tập lệnh có thể bỏ qua, nhưng hầu hết các kiểm tra còn lại đều không tốn kém và/hoặc khó loại bỏ, đó có lẽ là lý do tại sao chúng được giữ lại. Nếu chúng ta mở rộng việc sử dụng assumevalid một chút, chúng ta có thể giảm đáng kể lượng dữ liệu cần thiết để xác thực đầu vào. Xin hãy nhìn xem.
Một mục hoàn chỉnh trong bộ UTXO chứa dữ liệu sau:
- Điểm đầu ra
- Tập lệnh đầu ra
- Thẻ Coinbase
- Block Height
- Số lượng
Trường hợp đơn giản
Nếu không có assumevalid, tất cả dữ liệu này phải có sẵn để kiểm tra dữ liệu đầu vào. Trong phiên bản assumevalid của SwiftSync, chúng tôi chỉ sử dụng điểm đầu ra (tức là (1) trong danh sách ở trên). Điều này rất tiện lợi vì dữ liệu này đã có sẵn trong mỗi đầu vào. Thật dễ hiểu tại sao (2) bị bỏ qua -- tập lệnh không kiểm tra nó trong assumevalid -- nhưng dữ liệu còn lại thì sao?
Việc bỏ qua thẻ (3) coinbase có nghĩa là chúng ta không thể kiểm tra xem đầu ra được tạo từ coinbase có được chi tiêu sớm hơn thời điểm được phép chi tiêu hay không - về cơ bản, các đầu ra này có khóa thời gian tương đối ngầm định lên tới 100 khối. Để kiểm tra khóa thời gian tương đối như vậy, chúng ta cũng cần biết (4) Block Height. Tương tự như vậy, trường nsequence trong giao dịch cũng có thể được sử dụng để kích hoạt khóa thời gian tương đối (chúng ta sẽ không xem xét điều này).
Block Height cũng gián tiếp giúp ích cho một vấn đề chỉ gặp phải trong dữ liệu xác thực song song: nó đảm bảo rằng đầu ra được tạo trước khi chúng được chi tiêu (không phải sau đó). Lưu ý rằng Block Height có thể không đủ vì đầu ra có thể được sử dụng trong cùng khối mà nó được tạo ra. Chúng ta sẽ thảo luận chi tiết hơn về vấn đề này sau , vì chúng ta cần xử lý vấn đề này một cách rõ ràng trong phiên bản không phải là sumevalid .
Một vấn đề khác mà chúng ta sẽ giải quyết sau là BIP30 , hiện tại yêu cầu quyền truy cập đầy đủ vào bộ UTXO. Bộ UTXO có thể được định địa chỉ đầy đủ, nhưng đối với phiên bản assumevalid thì điều này không cần thiết.
Việc bỏ qua các kiểm tra trên trong chế độ assumevalid sẽ hoàn toàn không gây tranh cãi, vì không có kiểm tra nào quan trọng bằng kiểm tra tập lệnh (là những gì assumevalid bỏ qua) [3] .
Một tình huống khó khăn hơn
Có thể tranh luận về việc bỏ qua kiểm tra số tiền (5), vì nếu không kiểm tra số tiền, bạn sẽ không còn kiểm tra lỗi lạm phát nữa. May mắn thay, có một giải pháp cho vấn đề này - chúng ta có thể cộng tổng số tiền từ bộ UTXO và kiểm tra xem nó có vượt quá tổng nguồn cung tiền tệ hay không. Lưu ý rằng điều này cũng bao gồm số lượng đầu ra OP_RETURN.
Một trong những hạn chế của cách tiếp cận này là không có cách dễ dàng nào để tính toán số tiền mà thợ đàokhông lấy, vì chúng ta bỏ qua việc tính toán giao dịch. Về lý thuyết, nếu ai đó lấy những khoản tiền chưa được nhận này, chúng ta sẽ không để ý. Theo quan điểm về sự khan hiếm, điều này không tệ hơn trường hợp xấu nhất là assumevalid - tiền được lấy không đúng cách thông qua một tập lệnh không hợp lệ. Hơn nữa, số tiền có thể huy động được theo cách này là rất nhỏ. [4]
Cuối cùng, có một hậu quả chắc chắn là bất lợi , nhưng không quá lớn. Bởi vì chúng ta không có bộ dữ liệu đầu ra trước đó đầy đủ khi kiểm tra dữ liệu đầu vào của khối, nên chúng ta cũng không thể tạo ra cái gọi là dữ liệu "hoàn tác" — dữ liệu cần thiết để khôi phục lại blockchain trong trường hợp tổ chức lại blockchain . Điều này có nghĩa là chúng ta không thể khôi phục sâu hơn điểm SwiftSync assumevalid. Chạy Bitcoin Core ở chế độ cắt giảm cũng có những hạn chế tương tự. Có một cách thực tế để giải quyết vấn đề này: đối với các khối X mới nhất, không sử dụng assumevalid SwiftSync; X chỉ cần là độ sâu bạn cho rằng việc tái tổ chức khó có thể xảy ra.
Tóm lại, bằng cách dựa vào định nghĩa mở rộng tối thiểu của assumevalid và chấp nhận (như với nút được cắt tỉa) yêu cầu đồng bộ hóa lại trong trường hợp tổ chức lại độ sâu, chúng ta sẽ có được một giao thức rất rõ ràng và đơn giản. Tất nhiên, khả năng xác thực ở chế độ không phải giả định cũng quan trọng, vì vậy chúng ta sẽ thảo luận về điều đó sau.
Không cần phải giả định hợp lệ
SwiftSync không có assumevalid có thể xác thực đầy đủ mọi quy tắc đồng thuận, nhưng chúng ta cần thêm một số bước. Ngoài việc thêm và trừ các điểm đầu ra đã băm, chúng ta cũng cần đưa mọi thứ cần thiết để kiểm tra đầu vào (xem danh sách 5 dữ liệu ở trên ) vào giá trị băm.
Việc thêm những giá trị này vào giá trị tổng hợp băm rất dễ dàng vì chúng ta đã có dữ liệu tại thời điểm cần thêm (khi xử lý đầu ra). Nhưng khi chúng ta muốn xóa chúng (khi xử lý dữ liệu đầu vào), chúng ta chỉ còn lại các điểm đầu ra - do đó chúng ta phải tải lại dữ liệu bị thiếu. So với việc xác minh đầy đủ thông thường, điều này có thể yêu cầu nhiều dữ liệu hơn 15%, tạo ra lượng lớn không gian cho việc mã hóa hiệu quả [5] .
Chúng tôi có thể cho phép người dùng tải xuống dữ liệu này bên ngoài, nhưng chúng tôi cũng có thể cân nhắc việc sử dụng mạng ngang hàng để cung cấp dữ liệu này. May mắn thay, hầu hết nút đã có dữ liệu này - dữ liệu hoàn tác đã đề cập trước đó cho phép nút sắp xếp lại blockchain là thứ chúng ta cần [6] . Lưu ý rằng chúng ta cũng cần cùng một nguồn cung cấp cho chúng ta gợi ý để cung cấp cho chúng ta một hàm băm của dữ liệu hoàn tác, để đảm bảo rằng một nút ngẫu nhiên không cung cấp cho chúng ta dữ liệu không hợp lệ, khiến quá trình xác minh không thành công [7] .
Vì hiện tại chúng ta đang kiểm tra số lượng nên không cần kiểm tra lạm phát riêng trên bộ UTXO, nhưng chúng ta gặp phải một số vấn đề mới (đã đề cập ở phần trước).
Thứ tự các giao dịch trong một khối
Vì tính năng song song hóa được bật theo mặc định trong SwiftSync, nên chúng ta không nhất thiết phải biết liệu đầu ra có được sử dụng trước khi nó được tạo hay không, ví dụ, đầu ra có thể được tạo rồi sử dụng trong cùng một khối (trong mọi trường hợp khác, chỉ cần kiểm tra Block Height).
Mặc dù sự tồn tại của vấn đề này là không thể phủ nhận, nhưng nếu bạn suy nghĩ kỹ, bạn sẽ thấy rằng hậu quả của nó không phải là vấn đề lớn [8] . Nhưng chúng ta chắc chắn có thể giải quyết vấn đề này và khi giải quyết được, việc thảo luận về các vấn đề an toàn sẽ dễ dàng hơn.
Cách trực tiếp và thiết thực nhất là sử dụng bộ nhớ đệm theo từng khối. Bạn có thể cho rằng nó như một loại tập hợp micro-UTXO từng khối (về mặt kỹ thuật, nó chỉ cần dữ liệu có thể chứng minh thứ tự) được xây dựng theo thứ tự. Chúng ta chỉ cần lấy lại một đầu vào khi Block Height(genesis) của nó giống với khối hiện tại, mặc dù có thể có lý do để làm nhiều hơn [9] . Điều này làm cho giao thức kém thanh lịch hơn vì chúng ta thêm lại một số trạng thái và thực hiện tìm kiếm tuần tự, nhưng nó giới hạn chúng ta trong một hoàn cảnh khối duy nhất - chúng ta vẫn có toàn quyền xử lý tất cả các khối song song.
Nếu chúng ta muốn sử dụng chiến lược không có bộ nhớ đệm và tránh hoàn toàn việc tìm kiếm và kiểm tra tuần tự, thì có nhiều lựa chọn hơn [10] , nhưng đối với nút đầy đủ chạy trên phần cứng thông thường, bộ nhớ đệm khối sẽ đủ tốt.
Xác minh BIP30
Để SwiftSync tương đương với xác thực đầy đủ thông thường, nó cũng cần đáp ứng các yêu cầu của BIP30 , mặc dù nó không có quyền truy cập vào bộ UTXO. BIP30 không được kích hoạt cho đến tháng 3 năm 2012, nhưng phạm vi triển khai của nó kéo dài từ Khối Genesis đến thời điểm BIP34 được kích hoạt (Block Height 227931, tháng 3 năm 2013). BIP30 ngăn chặn các đầu ra trùng lặp bằng cách vô hiệu hóa các khối tạo ra các đầu ra chưa sử dụng đã tồn tại từ trước. Về mặt lý thuyết, BIP30 không thể ngăn chặn hoàn toàn các giao dịch trùng lặp (BIP34 có thể, nhưng nó cũng mang đến một số vấn đề chưa được giải quyết ), vì vẫn có thể tạo, chi tiêu rồi tạo lại, mặc dù trên thực tế điều này chưa bao giờ xảy ra.
Trước khi BIP30 được kích hoạt, có hai đầu ra trùng lặp trong đó cùng một đầu ra giao dịch coinbase được tạo lần. Đầu ra thứ hai ghi đè lên đầu ra thứ nhất, khiến đầu ra thứ nhất không thể sử dụng được. Những trường hợp này được coi là ngoại lệ trong BIP30. Có thể tìm thấy thêm thông tin tại đây .
SwiftSync không có khái niệm về bộ UTXO, do đó chúng tôi không thể theo dõi liệu đầu ra đã tồn tại hay chưa. May mắn thay, chúng ta có thể nghĩ ra một phương pháp khác mang lại cho chúng ta kết quả tương tự. Chúng ta có thể trực tiếp lưu cơ sở dữ liệu chứa từng ID giao dịch coinbase để kiểm tra tính duy nhất của chúng. Điều này chỉ chiếm 227931 * 32 byte ~= 7MB bộ nhớ và chúng ta có thể tối ưu hóa nó hơn nữa và thậm chí làm cho nó không có trạng thái nếu chúng ta muốn [11] . Nó không chỉ tương đương với BIP30 mà còn là giải pháp thay thế nhanh hơn cho cách chúng ta làm hiện nay.
Ngoài ra còn có một kịch bản lý thuyết, đó là một sự tổ chức lại dữ dội diễn ra từ năm 2013, khiến BIP34 không bao giờ được kích hoạt và BIP30 sẽ tiếp tục mãi mãi. Thay vì giải quyết tình huống này, một giải pháp thực tế hơn là quay lại xác thực không phải SwiftSync nếu tình huống này xảy ra.
SwiftSync và assumeutxo
Assumeutxo và SwiftSync hoàn toàn độc lập với nhau. Assumeutxo bắt đầu bằng giả định rằng tập hợp UTXO là đúng, sau đó thực hiện xác minh ở chế độ nền để xác minh tính đúng đắn của giả định này. Phần sau là nơi chúng ta có thể sử dụng SwiftSync.
Một sự phức tạp do assumeutxo gây ra là nó yêu cầu hai tập trạng thái: một từ điểm assumeutxo đến đầu Chuỗi hiện tại và một từ Khối Genesis đến điểm assumeutxo. Khi quá trình xác thực ở phía sau hoàn tất, chúng ta sẽ quay lại trạng thái đã thiết lập. Vì SwiftSync gần như không có trạng thái nên việc sử dụng nó để xác thực nền có thể giúp mọi việc dễ dàng hơn.
Một sự khác biệt quan trọng nhưng dễ giải quyết là mặc dù chúng ta không còn cần phải xây dựng bộ UTXO nữa, chúng ta vẫn cần xác minh rằng bộ UTXO của assumeutxo được sử dụng làm điểm khởi đầu hoàn toàn giống với bộ UTXO có thể được suy ra từ dấu nhắc SwiftSync. Chúng ta có thể thực hiện điều này bằng thủ thuật tổng hợp băm tương tự.
Lần khi chúng tôi xử lý một đầu ra được chỉ định nằm trong tập hợp UTXO, chúng tôi sẽ thêm nó vào giá trị tổng hợp băm (đây sẽ là băm của toàn bộ dữ liệu tập hợp UTXO, không chỉ là điểm đầu ra). Tương tự như vậy, chúng tôi băm từng mục trong tập UTXO của assumeutxo rồi xóa chúng khỏi giá trị tổng hợp đã băm. Nếu cả hai đều đúng thì kết quả cuối cùng cũng là 0.
Điều tuyệt vời là nếu bạn đang sử dụng phiên bản SwiftSync không phải là sumevalid, bạn thậm chí không cần gợi ý một bit cho mỗi đầu ra, vì dữ liệu bạn thêm vào tổng hợp băm sẽ hoàn toàn giống nhau bất kể đầu ra có nằm trong tập hợp UTXO hay không (tức là输出- 输入- UTXO == 0 ). Sự thanh lịch như vậy làm tôi say đắm.
Lời cảm ơn
Tối ưu hóa quy trình xác minh là chủ đề đã được thảo luận trong nhiều năm. Nhiều người đã đi trước tôi và khám phá những ý tưởng liên quan. UHS của Cory Fields có lẽ giống với giao thức này nhất, bản thân nó là một cải tiến dựa trên ý tưởng bitfield của Bram Cohen . Utreexo của Tadge Dryja cũng trùng lặp với giao thức này về mặt giảm trạng thái, song song hóa và gợi ý đồng bộ khối khởi tạo liên quan đến bộ nhớ đệm (IBD).
Cảm ơn theStack và 0xb10c đã cung cấp số liệu thống kê và chuẩn mực ban đầu. Tôi cũng muốn cảm ơn các nhà phát triển Bitcoin Core đã tích cực làm việc để tối ưu hóa IBD — davidgumberg , l0rinc và andrewtoth — cũng như những người đã cung cấp các cuộc thảo luận hữu ích và nhiều người khác đã cung cấp phản hồi.
chú thích
1. Chúng tôi có lựa chọn nào khác để nhận thông báo không? Một bất lợi khi dựa vào các giá trị được lưu trữ trong phần mềm của bạn là phần mềm đó có thể đã được phát hành cách đây nhiều tháng. Sau khi sử dụng trong đó, quá trình xác minh sẽ chậm lại đáng kể. Một khả năng khác là nhận được lời nhắc [non-assumevalid SwiftSync] từ bên thứ ba đáng tin cậy. Trong trường hợp tệ nhất, bên thứ ba này sẽ nói dối và quá trình xác minh của bạn cuối cùng sẽ thất bại. Do đó, điều quan trọng là bạn không nên cho rằng một Chuỗi không hợp lệ là hợp lệ. Có thể kết hợp cả hai phương pháp(tức là bắt kịp ngay cả khi nút của bạn ngoại tuyến), nhưng trong trường hợp này, chúng ta sẽ phải thêm một số logic bổ sung để xóa UTXO khỏi bộ sưu tập giữa các phiên SwiftSync. Cuối cùng, tiền boa cũng có thể được cam kết trên Chuỗi, nhưng điều này sẽ yêu cầu một soft fork. ↩
2. Cái nào tốt hơn, MuHash và xếp chồng XOR hay tổng hợp băm với muối bí mật? MuHash là một giải pháp thay thế khả thi nếu chúng ta muốn loại bỏ yêu cầu salt bí mật (ví dụ: để cho phép so sánh trạng thái giữa máy trạm), nhưng có vẻ như nó quá mức cần thiết cho trường hợp sử dụng của chúng ta. XOR không đủ, vì các phần tử xuất hiện lần sẽ hủy lẫn nhau (một lần ở đầu vào và một lần ở đầu ra). Có thể khắc phục điều này bằng các biện pháp kiểm tra bổ sung, nhưng cách đó khá gần với phương pháp chúng tôi đề xuất ở đây. ↩
3. Nếu chúng ta bỏ qua khóa thời gian và sắp xếp giao dịch, điều gì sẽ xảy ra trong trường hợp xấu nhất? Khóa thời gian không được thực thi hoặc các giao dịch không theo thứ tự chỉ có nghĩa là một số tiền đã được chi trước thời hạn hoặc có điều gì đó kỳ lạ đã xảy ra với thứ tự các giao dịch (tiền đầu tiên được chi từ một đầu ra không tồn tại, tạo ra lạm phát, sau đó đầu ra đó được tạo ra sau, bù đắp cho lạm phát) — và nếu lạm phát đó cuối cùng không được khắc phục, xác minh SwiftSync sẽ không thành công. Quan trọng là, điều này không tệ hơn trường hợp tệ nhất của assumevalid -- khi một tập lệnh không hợp lệ sẽ lãng phí đầu ra. ↩
4. Thế còn nếu chúng ta thực sự muốn kiểm tra chính xác hơn số tiền mà thợ đào không lấy thì sao? Về mặt lý thuyết, chúng ta có thể thêm gợi ý về số tiền vào tất cả các đầu vào của khối mà thợ đào không lấy hết toàn bộ phí được hưởng và cung cấp gợi ý về đầu ra nào đã được chi trong các khối đó. Đối với những trường hợp cụ thể này, chúng ta có thể bao gồm số tiền trong hàm băm. Thợ đào có thể bắt đầu quấy rối bằng cách luôn để lại 1 satoshi. Soft fork yêu cầu cam kết tổng phí hoặc không cho phép phí cũ sẽ là giải pháp triệt để hơn. Tuy nhiên, việc chấp nhận hạn chế này là hoàn toàn hợp lý, vì kịch bản tệ nhất cũng không tệ hơn những gì assumevalid đã giả định. Những người quan tâm nên chọn tắt assumevalid. ↩
5. Làm thế nào chúng ta có thể nén dữ liệu cần thiết cho phiên bản không phải là sumevalid? Có lượng lớn cơ hội để nén và loại bỏ trùng lặp hiệu quả ở đây. Có nhiều mẫu lặp lại (ví dụ: giao dịch chuẩn), hàm băm có ảnh trước mà chúng ta biết (ví dụ: P2SH) và đầu ra thường xuyên xảy ra (ví dụ: phần lớn các đầu vào xảy ra ở Block Height gần đây). Một số ý tưởng từ BIP337 cũng có thể được sử dụng lại. ↩
6. Việc cung cấp dữ liệu hoàn tác thông qua mạng P2P có thể hữu ích cho mục đích khác không? Nó cho phép nút đã cắt tỉa quay lại sâu hơn điểm cắt tỉa của chúng, miễn là chúng lưu một hàm băm của dữ liệu hoàn tác đã cắt tỉa (thật không may, SwiftSync với assumevalid không thể thực hiện điều này) và nút sẵn sàng cung cấp dữ liệu hoàn tác cho khối mà chúng fork . Vì hoàn tác dữ liệu về cơ bản cho phép bạn xác thực đầy đủ các khối ngoài hoàn cảnh , nên các giao thức như " bằng chứng gian lận PoW " (xác thực blockchain băng thông thấp bằng cách xác thực có chọn lọc các khối fork ) cũng có thể được hưởng lợi từ nó. ↩
7. Có cách nào khác để cung cấp dữ liệu hoàn tác mà không gây ra sự cố DoS không? Cách trực tiếp nhất là băm dữ liệu này, đưa nó vào từng khối và sau đó đưa nó vào sự đồng thuận; nhưng soft fork thì khó. Ngoài ra còn có một phương pháp ngang hàng lý thuyết, đó là so sánh trạng thái dữ liệu hoàn tác giữa nút hàng. Nếu có sự khác biệt, hãy tìm ra nút hàng nào đang nói dối (miễn là có ít nhất một nút hàng trung thực thì sẽ hoạt động), nhưng điều này cũng tương đối phức tạp. Một ví dụ về cách thực hiện điều này (mặc dù trong hoàn cảnh khác) có thể được tìm thấy tại đây . ↩
8. Giả sử chúng ta không kiểm tra sắp xếp giao dịch trong một khối, kẻ tấn công có thể khai thác điều này như thế nào? Việc chấp nhận một Chuỗi có sắp xếp giao dịch không chính xác sẽ không ảnh hưởng đến quyền sở hữu quỹ hoặc lạm phát. Vấn đề duy nhất là nút xác thực đầy đủ thông thường sẽ từ chối sắp xếp thay thế này, do đó nó sẽ trở thành một hard fork. Tuy nhiên, fork này sẽ không xảy ra trừ khi sắp xếp thay thế này thực sự xuất hiện trên Chuỗi có nhiều Bằng chứng công việc nhất. Rất khó để hình dung ra tình huống mà kẻ tấn công có thể khai thác đáng kể điều này. Đây là một nỗ lực: (1) Sự lật đổ Chuỗi hiện tại bằng Bằng chứng công việc cao nhất và khai thác một khối bằng cách sử dụng sắp xếp thay thế này; (2) Yêu cầu nạn nhân thực hiện xác minh SwiftSync cho đến tận đầu Chuỗi, qua đó thuyết phục họ về sắp xếp thay thế của bạn (có lẽ bằng cách làm nhiễm phần mềm của họ trước?); (3) Yêu cầu nạn nhân mua Bitcoin từ bạn trên Chuỗi “sai” này. Cuối cùng, Chuỗi"tốt" sẽ lại đánh bại Chuỗi"xấu" của bạn. Nhìn chung, có vẻ như cuộc tấn công này sẽ không hiệu quả hơn việc bán Bitcoin của bạn rồi tổ chức lại các giao dịch. Ngay cả khi mục tiêu của kẻ tấn công chỉ đơn giản là tạo ra sự hỗn loạn thì cũng không thể nói rằng một cuộc tổ chức lại quy mô lớn sẽ khác biệt như thế nào so với một cuộc tấn công như vậy. ↩
9. Có lẽ chúng ta có thể tiết kiệm băng thông bằng cách lưu vào bộ nhớ đệm thay vì tải lại mọi UTXO ? Có, và có nhiều phương pháp tốt để xử lý vấn đề này, nhưng nó sẽ làm tăng thêm độ phức tạp và khiến toàn bộ quá trình xác minh mang tính tuần tự hơn (khó có thể song song hóa). Bạn có thể để bộ nhớ đệm bao phủ nhiều khối và thậm chí tiến thêm một bước nữa là cung cấp gợi ý về đầu ra nào sẽ được sử dụng. Điều này có thể gây ra một số vấn đề như băng thông. ↩
10. Nếu chúng ta không có bộ nhớ đệm, làm sao chúng ta có thể kiểm tra sắp xếp trong một khối? Chúng ta có thể thêm một phần tử bổ sung vào bộ UTXO: số giao dịch chuẩn (số đầu ra cũng được chấp nhận và hấp dẫn vì những lý do khác, nhưng độ chính xác của chúng vượt quá nhu cầu của chúng ta). Bây giờ, nếu một đầu ra cần được chi tiêu và Block Height genesis của nó giống với chiều cao hiện tại, chúng ta sẽ kiểm tra ID giao dịch để xác minh rằng nó được tạo trước giao dịch mà chúng ta đang đánh giá. Nhược điểm của phương pháp này là nó làm tăng kích thước của toàn bộ bộ UTXO (2 byte cho mỗi mục). Một phương pháp thú vị hơn có thể là thêm một gợi ý bổ sung cho mỗi đầu ra mà chúng ta biết sẽ được sử dụng trong cùng một khối. Cụ thể, chúng tôi gợi ý về số giao dịch sẽ chi tiêu số đó (phải lớn hơn số giao dịch của giao dịch bao gồm số đó làm đầu ra - chúng tôi chỉ mã hóa sự khác biệt giữa hai số đó) và sau đó đưa gợi ý này vào giá trị băm. Bây giờ, khi chúng ta gặp một đầu vào có chiều cao ban đầu khớp với chiều cao hiện tại, chúng ta sẽ đưa số giao dịch của đầu vào đó vào hàm băm rồi xóa nó khỏi giá trị tổng hợp. Thật không may, đầu ra thường được sử dụng trong cùng một khối với khối tạo ra nó, do đó dữ liệu gợi ý cần thiết có thể tăng lên đáng kể. Mặc dù lưu trữ đệm là một phương pháp rõ ràng hơn, nhưng có thể sẽ thú vị khi so sánh nó với phương án không lưu trữ đệm. ↩
11. BIP30 là một nơi khác sử dụng bộ nhớ đệm - làm thế nào để tối ưu hóa nó? Chúng ta có thể giảm dữ liệu bằng cách cắt bớt txid. Thông thường chúng ta lo lắng về va chạm, nhưng chúng ta có thể đảm bảo không có va chạm nào bằng cách tính toán trước các bit cụ thể cần giữ lại, do đó đảm bảo kết quả không va chạm trên Chuỗi cụ thể này (cũng có thể băm lại bằng muối, nhưng tính toán chậm hơn). Về mặt lý thuyết thông tin, điều này có thể giảm mỗi txid xuống còn 18 bit, mặc dù điều này là không thể thực hiện được về mặt tính toán. Trên thực tế, chúng ta có thể thực hiện việc này trong vòng 4 byte (hoặc thậm chí trong vòng 3 byte). Bằng cách này, kích thước dữ liệu được giảm xuống dưới 1 MB. Chúng ta cũng có thể tiến thêm một bước nữa là loại bỏ trạng thái này và tìm kiếm với sự trợ giúp của các giá trị tổng hợp được băm. Để thực hiện điều này, chúng tôi cung cấp một danh sách có thứ tự các giá trị băm bị cắt bớt. Chúng ta sẽ lặp lại danh sách, kiểm tra xem mỗi phần tử có lớn hơn phần tử trước đó hay không (do đó chứng minh tính duy nhất) và thêm chúng vào giá trị tổng hợp băm. Khi chúng ta gặp chúng trong khối, chúng ta xóa chúng khỏi giá trị tổng hợp băm (nếu kết quả là 0, chúng ta đã chứng minh được tính tương đương của tập hợp). ↩




