Chuyển đến nội dung chính
Change page

Ethash

Ethash từng là thuật toán khai thác bằng chứng công việc (PoW) của Ethereum. Bằng chứng công việc hiện đã bị tắt hoàn toàn và Ethereum hiện được bảo mật bằng bằng chứng cổ phần (PoS). Đọc thêm về The Merge, bằng chứng cổ phầnviệc đặt cọc. Trang này chỉ dành cho mục đích tham khảo lịch sử!

Ethash là một phiên bản sửa đổi của thuật toán Dagger-Hashimoto. Bằng chứng công việc Ethash đòi hỏi nhiều bộ nhớ (memory hard) (opens in a new tab), điều này được cho là giúp thuật toán có khả năng kháng ASIC. Các máy ASIC cho Ethash cuối cùng cũng được phát triển nhưng việc khai thác bằng GPU vẫn là một lựa chọn khả thi cho đến khi bằng chứng công việc bị tắt. Ethash vẫn được sử dụng để khai thác các đồng tiền khác trên các mạng lưới bằng chứng công việc không phải Ethereum khác.

Ethash hoạt động như thế nào?

Tính chất đòi hỏi nhiều bộ nhớ đạt được nhờ một thuật toán bằng chứng công việc yêu cầu chọn các tập con của một tài nguyên cố định phụ thuộc vào nonce và tiêu đề block. Tài nguyên này (có kích thước vài gigabyte) được gọi là DAG. DAG được thay đổi sau mỗi 30000 khối, một khoảng thời gian ~125 giờ được gọi là một kỷ nguyên (khoảng 5,2 ngày) và mất một khoảng thời gian để tạo ra. Vì DAG chỉ phụ thuộc vào chiều cao khối, nó có thể được tạo trước, nhưng nếu không, client cần phải đợi cho đến khi quá trình này kết thúc để tạo ra một khối. Nếu các client không tạo trước và lưu trữ DAG vào bộ nhớ đệm (cache) từ trước, mạng lưới có thể gặp phải sự chậm trễ khối lớn ở mỗi lần chuyển đổi kỷ nguyên. Lưu ý rằng DAG không cần phải được tạo ra để xác minh bằng chứng công việc, về cơ bản cho phép xác minh với cả CPU thấp và bộ nhớ nhỏ.

Quy trình chung mà thuật toán thực hiện như sau:

  1. Có một seed (hạt giống) có thể được tính toán cho mỗi khối bằng cách quét qua các tiêu đề block cho đến thời điểm đó.
  2. Từ seed, người ta có thể tính toán một bộ nhớ đệm giả ngẫu nhiên 16 MB. Các light client lưu trữ bộ nhớ đệm này.
  3. Từ bộ nhớ đệm, chúng ta có thể tạo ra một tập dữ liệu 1 GB, với đặc tính là mỗi mục trong tập dữ liệu chỉ phụ thuộc vào một số lượng nhỏ các mục từ bộ nhớ đệm. Các full client và thợ đào lưu trữ tập dữ liệu này. Tập dữ liệu tăng trưởng tuyến tính theo thời gian.
  4. Việc khai thác liên quan đến việc lấy các phần ngẫu nhiên của tập dữ liệu và băm chúng lại với nhau. Việc xác minh có thể được thực hiện với bộ nhớ thấp bằng cách sử dụng bộ nhớ đệm để tạo lại các phần cụ thể của tập dữ liệu mà bạn cần, vì vậy bạn chỉ cần lưu trữ bộ nhớ đệm.

Tập dữ liệu lớn được cập nhật một lần sau mỗi 30000 khối, vì vậy phần lớn nỗ lực của thợ đào sẽ là đọc tập dữ liệu, chứ không phải thực hiện các thay đổi đối với nó.

Định nghĩa

Chúng tôi sử dụng các định nghĩa sau:

Việc sử dụng 'SHA3'

Sự phát triển của Ethereum diễn ra cùng lúc với sự phát triển của tiêu chuẩn SHA3, và quá trình tiêu chuẩn hóa đã thực hiện một thay đổi muộn trong phần đệm (padding) của thuật toán băm đã chung cuộc, do đó các mã băm "sha3_256" và "sha3_512" của Ethereum không phải là các mã băm sha3 tiêu chuẩn, mà là một biến thể thường được gọi là "Keccak-256" và "Keccak-512" trong các ngữ cảnh khác. Xem thảo luận, ví dụ: tại đây (opens in a new tab), tại đây (opens in a new tab), hoặc tại đây (opens in a new tab).

Vui lòng ghi nhớ điều đó vì các mã băm "sha3" được đề cập trong phần mô tả thuật toán bên dưới.

Các tham số

Các tham số cho bộ nhớ đệm và tập dữ liệu của Ethash phụ thuộc vào số khối. Kích thước bộ nhớ đệm và kích thước tập dữ liệu đều tăng trưởng tuyến tính; tuy nhiên, chúng tôi luôn lấy số nguyên tố cao nhất dưới ngưỡng tăng trưởng tuyến tính để giảm rủi ro về các quy luật ngẫu nhiên dẫn đến hành vi tuần hoàn.

Các bảng giá trị kích thước tập dữ liệu và bộ nhớ đệm được cung cấp trong phần phụ lục.

Tạo bộ nhớ đệm

Bây giờ, chúng ta chỉ định hàm để tạo ra một bộ nhớ đệm:

Quá trình tạo bộ nhớ đệm bao gồm việc đầu tiên là lấp đầy tuần tự 32 MB bộ nhớ, sau đó thực hiện hai lượt chạy thuật toán RandMemoHash của Sergio Demian Lerner từ Strict Memory Hard Hashing Functions (2014) (opens in a new tab). Đầu ra là một tập hợp gồm 524288 giá trị 64-byte.

Hàm tổng hợp dữ liệu

Chúng tôi sử dụng một thuật toán lấy cảm hứng từ mã băm FNV (opens in a new tab) trong một số trường hợp như một sự thay thế không kết hợp cho XOR. Lưu ý rằng chúng tôi nhân số nguyên tố với toàn bộ đầu vào 32-bit, trái ngược với thông số kỹ thuật FNV-1 nhân số nguyên tố với từng byte (octet) một.

FNV_PRIME = 0x01000193

def fnv(v1, v2):
    return ((v1 * FNV_PRIME) ^ v2) % 2**32

Xin lưu ý, ngay cả khi sách vàng chỉ định fnv là v1*(FNV_PRIME ^ v2), tất cả các triển khai hiện tại đều nhất quán sử dụng định nghĩa ở trên.

Tính toán toàn bộ tập dữ liệu

Mỗi mục 64-byte trong toàn bộ tập dữ liệu 1 GB được tính toán như sau:

Về cơ bản, chúng tôi kết hợp dữ liệu từ 256 nút bộ nhớ đệm được chọn giả ngẫu nhiên và băm dữ liệu đó để tính toán nút tập dữ liệu. Toàn bộ tập dữ liệu sau đó được tạo ra bởi:

def calc_dataset(full_size, cache):
    return [calc_dataset_item(cache, i) for i in range(full_size // HASH_BYTES)]

Vòng lặp chính

Bây giờ, chúng ta chỉ định vòng lặp chính giống "hashimoto", nơi chúng ta tổng hợp dữ liệu từ toàn bộ tập dữ liệu để tạo ra giá trị cuối cùng cho một tiêu đề và nonce cụ thể. Trong đoạn mã bên dưới, header đại diện cho mã băm SHA3-256 của biểu diễn RLP của một tiêu đề block bị cắt bớt, nghĩa là của một tiêu đề loại trừ các trường mixHashnonce. nonce là tám byte của một số nguyên không dấu 64 bit theo thứ tự Big-endian. Vì vậy, nonce[::-1] là biểu diễn little-endian tám byte của giá trị đó:

Về cơ bản, chúng tôi duy trì một "mix" rộng 128 byte, và liên tục tìm nạp tuần tự 128 byte từ toàn bộ tập dữ liệu và sử dụng hàm fnv để kết hợp nó với mix. 128 byte truy cập tuần tự được sử dụng để mỗi vòng của thuật toán luôn tìm nạp một trang đầy đủ từ RAM, giảm thiểu các lỗi trượt bộ đệm ẩn biên dịch (translation lookaside buffer misses) mà về mặt lý thuyết các máy ASIC có thể tránh được.

Nếu đầu ra của thuật toán này thấp hơn mục tiêu mong muốn, thì nonce đó hợp lệ. Lưu ý rằng việc áp dụng thêm sha3_256 ở cuối đảm bảo rằng tồn tại một nonce trung gian có thể được cung cấp để chứng minh rằng ít nhất một lượng nhỏ công việc đã được thực hiện; việc xác minh PoW vòng ngoài nhanh chóng này có thể được sử dụng cho mục đích chống DDoS. Nó cũng phục vụ để cung cấp sự đảm bảo về mặt thống kê rằng kết quả là một số 256-bit không bị sai lệch.

Khai thác

Thuật toán khai thác được định nghĩa như sau:

def mine(full_size, dataset, header, difficulty):
    # đệm số 0 vào target để so sánh với mã băm trên cùng chữ số
    target = zpad(encode_int(2**256 // difficulty), 64)[::-1]
    from random import randint
    nonce = randint(0, 2**64)
    while hashimoto_full(full_size, dataset, header, nonce) > target:
        nonce = (nonce + 1) % 2**64
    return nonce

Định nghĩa mã băm seed

Để tính toán mã băm seed sẽ được sử dụng để khai thác trên một khối nhất định, chúng tôi sử dụng thuật toán sau:

 def get_seedhash(block):
     s = '\x00' * 32
     for i in range(block.number // EPOCH_LENGTH):
         s = serialize_hash(sha3_256(s))
     return s

Lưu ý rằng để việc khai thác và xác minh diễn ra suôn sẻ, chúng tôi khuyên bạn nên tính toán trước các mã băm seed và tập dữ liệu trong tương lai ở một luồng (thread) riêng biệt.

Đọc thêm

Bạn biết một tài nguyên cộng đồng nào đó đã giúp ích cho bạn? Hãy chỉnh sửa trang này và thêm nó vào!

Phụ lục

Đoạn mã sau nên được thêm vào đầu nếu bạn quan tâm đến việc chạy thông số kỹ thuật Python ở trên dưới dạng mã.

Kích thước dữ liệu

Các bảng tra cứu sau đây cung cấp khoảng 2048 kỷ nguyên được lập bảng về kích thước dữ liệu và kích thước bộ nhớ đệm.