Gánh nặng của (các) bằng chứng: Code Merkleization

Rate this post

Một lưu ý về sáng kiến ​​Ethereum không trạng thái:

Hoạt động nghiên cứu (có thể hiểu được) đã chậm lại vào nửa cuối năm 2020 vì tất cả những người đóng góp đã điều chỉnh để sống theo dòng thời gian kỳ lạ. Nhưng khi hệ sinh thái ngày càng tiến gần đến Serenity và hợp nhất Eth1 / Eth2, hoạt động của Ethereum không trạng thái sẽ ngày càng trở nên phù hợp và có tác động. Mong đợi một đợt hồi cứu Ethereum không trạng thái vào cuối năm đáng kể hơn trong những tuần tới.

Hãy xem qua giới hạn lại một lần nữa: Mục tiêu cuối cùng của Ethereum không trạng thái là loại bỏ yêu cầu của một nút Ethereum để giữ bản sao đầy đủ của trie trạng thái được cập nhật mọi lúc và thay vào đó để cho phép thay đổi trạng thái dựa trên một phần dữ liệu (nhỏ hơn nhiều) chứng minh một giao dịch cụ thể đang thực hiện một thay đổi hợp lệ. Làm điều này giải quyết một vấn đề lớn đối với Ethereum; một vấn đề cho đến nay chỉ được đẩy ra bởi phần mềm máy khách được cải tiến: Tăng trưởng nhà nước.

Bằng chứng Merkle cần thiết cho Ethereum không trạng thái được gọi là ‘nhân chứng’ và nó chứng thực sự thay đổi trạng thái bằng cách cung cấp tất cả không thay đổi các hàm băm trung gian được yêu cầu để đến gốc trạng thái hợp lệ mới. Nhân chứng về mặt lý thuyết nhỏ hơn rất nhiều so với trạng thái Ethereum đầy đủ (mất 6 giờ tốt nhất để đồng bộ hóa), nhưng họ vẫn lớn hơn rất nhiều hơn một khối (cần truyền tới toàn bộ mạng chỉ trong vài giây). Do đó, giảm bớt quy mô nhân chứng là điều tối quan trọng để đưa Ethereum không trạng thái đến tiện ích khả thi tối thiểu.

Cũng giống như chính trạng thái Ethereum, rất nhiều trọng lượng (kỹ thuật số) bổ sung trong các nhân chứng đến từ mã hợp đồng thông minh. Nếu một giao dịch thực hiện một cuộc gọi đến một hợp đồng cụ thể, theo mặc định, nhân chứng sẽ cần phải bao gồm mã bytecode của hợp đồng toàn bộ với nhân chứng. Mã Merkelization là một kỹ thuật chung để giảm bớt gánh nặng của mã hợp đồng thông minh trong các nhân chứng, để các lệnh gọi hợp đồng chỉ cần bao gồm các bit mã mà họ ‘chạm vào’ để chứng minh tính hợp lệ của chúng. Chỉ với kỹ thuật này, chúng ta có thể thấy lượng nhân chứng giảm đáng kể, nhưng có rất nhiều chi tiết cần xem xét khi chia mã hợp đồng thông minh thành các phần có kích thước byte.

Bytecode là gì?

Có một số sự đánh đổi cần cân nhắc khi chia nhỏ mã bytecode của hợp đồng. Câu hỏi cuối cùng chúng ta sẽ cần đặt ra là “các đoạn mã sẽ lớn như thế nào?” – nhưng hiện tại, hãy xem xét một số bytecode thực sự trong một hợp đồng thông minh rất đơn giản, chỉ để hiểu nó là gì:

pragma solidity >=0.4.22 <0.7.0;

contract Storage {

    uint256 number;

    function store(uint256 num) public {
        number = num;
    }

    function retrieve() public view returns (uint256){
        return number;
    }
}

Khi hợp đồng lưu trữ đơn giản này được biên dịch, nó sẽ biến thành mã máy dùng để chạy ‘bên trong’ EVM. Tại đây, bạn có thể thấy cùng một hợp đồng lưu trữ đơn giản được hiển thị ở trên, nhưng được tuân thủ theo các hướng dẫn EVM riêng lẻ (mã opcodes):

PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x32 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x2E64CEC1 EQ PUSH1 0x37 JUMPI DUP1 PUSH4 0x6057361D EQ PUSH1 0x53 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x3D PUSH1 0x7E JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x7C PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x20 DUP2 LT ISZERO PUSH1 0x67 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP PUSH1 0x87 JUMP JUMPDEST STOP JUMPDEST PUSH1 0x0 DUP1 SLOAD SWAP1 POP SWAP1 JUMP JUMPDEST DUP1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 DUP13 PUSH7 0x1368BFFE1FF61A 0x29 0x4C CALLER 0x1F 0x5C DUP8 PUSH18 0xA3F10C9539C716CF2DF6E04FC192E3906473 PUSH16 0x6C634300060600330000000000000000

Như đã giải thích trong một bài trướccác hướng dẫn opcode này là các hoạt động cơ bản của kiến ​​trúc ngăn xếp của EVM. Họ xác định hợp đồng lưu trữ đơn giản và tất cả các chức năng mà nó chứa. Bạn có thể tìm thấy hợp đồng này là một trong những hợp đồng rắn ví dụ trong Phối lại IDE (Lưu ý rằng mã máy ở trên là một ví dụ về storage.sol sau khi nó đã được triển khaivà không phải là đầu ra của trình biên dịch Solidity, trình biên dịch này sẽ có thêm một số opcodes ‘bootstrapping’). Nếu bạn không tập trung mắt của mình và tưởng tượng một máy xếp chồng vật lý đang chạy cùng với tính toán từng bước trên thẻ opcode, trong phần mờ của ngăn xếp chuyển động, bạn gần như có thể nhìn thấy đường nét của các chức năng được trình bày trong hợp đồng Solidity.

Bất cứ khi nào hợp đồng nhận được một cuộc gọi tin nhắn, mã này sẽ chạy bên trong mỗi nút Ethereum xác thực các khối mới trên mạng. Để gửi một giao dịch hợp lệ trên Ethereum ngày nay, người ta cần một bản sao đầy đủ của mã bytecode của hợp đồng, bởi vì chạy mã đó từ đầu đến cuối là cách duy nhất để có được trạng thái đầu ra (xác định) và hàm băm liên quan.

Ethereum không trạng thái, hãy nhớ rằng, nhằm mục đích thay đổi yêu cầu này. Giả sử rằng tất cả những gì bạn muốn làm là gọi hàm lấy lại() và không có gì hơn. Logic mô tả chức năng đó chỉ là một tập hợp con của toàn bộ hợp đồng và trong trường hợp này, EVM chỉ thực sự cần hai khối cơ bản hướng dẫn opcode để trả về giá trị mong muốn:

PUSH1 0x0 DUP1 SLOAD SWAP1 POP SWAP1 JUMP,

JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN

Trong mô hình Không trạng thái, giống như nhân chứng cung cấp các hàm băm còn thiếu của trạng thái chưa được chạm vào, nhân chứng cũng phải cung cấp các hàm băm còn thiếu cho các đoạn mã máy chưa được thực thi, để một khách hàng không trạng thái chỉ yêu cầu phần hợp đồng mà nó đang thực thi .

Nhân chứng của Code

Các hợp đồng thông minh trong Ethereum tồn tại ở cùng một nơi mà các tài khoản thuộc sở hữu bên ngoài làm: như các nút lá trong bộ ba trạng thái gốc đơn khổng lồ. Theo nhiều cách, hợp đồng không khác gì so với các tài khoản thuộc sở hữu bên ngoài mà con người sử dụng. Họ có địa chỉ, có thể gửi giao dịch và giữ số dư Ether cũng như bất kỳ mã thông báo nào khác. Nhưng tài khoản hợp đồng là đặc biệt vì chúng phải chứa logic chương trình (mã) của riêng chúng, hoặc hàm băm của nó. Merkle-Patricia Trie liên quan khác, được gọi là StorageTrie giữ mọi biến hoặc trạng thái liên tục mà một hợp đồng đang hoạt động sử dụng để thực hiện hoạt động kinh doanh của nó trong quá trình thực thi.

nhân chứng

Hình ảnh nhân chứng này cung cấp một cảm giác tốt về mức độ quan trọng của sự hợp nhất mã trong việc giảm quy mô nhân chứng. Hãy xem khối hình vuông màu khổng lồ đó và nó lớn hơn bao nhiêu phần tử khác trong bộ ba? Đó là một phân phối đầy đủ của mã byte hợp đồng thông minh.

Bên cạnh nó và một chút bên dưới là các phần của trạng thái dai dẳng trong StorageTriechẳng hạn như ánh xạ số dư ERC20 hoặc bản kê khai quyền sở hữu mặt hàng kỹ thuật số ERC721. Vì đây là ví dụ về một nhân chứng chứ không phải ảnh chụp nhanh trạng thái đầy đủ, chúng cũng được thực hiện chủ yếu bằng các hàm băm trung gian và chỉ bao gồm các thay đổi mà một ứng dụng không trạng thái sẽ yêu cầu để chứng minh khối tiếp theo.

Hợp nhất mã nhằm mục đích tách đoạn mã khổng lồ đó và thay thế trường codeHash trong tài khoản Ethereum có gốc của Merkle Trie khác, được đặt tên một cách khéo léo là codeTrie.

Giá trị trọng lượng của nó tính bằng Hashes

Hãy xem một ví dụ từ video này của Nhóm Kỹ thuật Ethereumphân tích một số phương pháp phân tích mã bằng cách sử dụng Mã thông báo ERC20 hợp đồng. Vì nhiều mã thông báo mà bạn đã nghe nói đến được làm theo tiêu chuẩn ERC-20, đây là một bối cảnh thực tế tốt để hiểu việc hợp nhất mã.

Bởi vì mã bytecode dài và không cầu kỳ, chúng ta hãy sử dụng một cách viết tắt đơn giản là thay thế bốn byte mã (8 ký tự thập phân) bằng một . hoặc X với ký tự thứ hai đại diện cho mã bytecode cần thiết để thực thi một chức năng cụ thể (trong ví dụ, ERC20.transfer () chức năng được sử dụng xuyên suốt).

Trong ví dụ ERC20, việc gọi chuyển khoản() hàm sử dụng ít hơn một nửa của toàn bộ hợp đồng thông minh:

XXX.XXXXXXXXXXXXXXXXXX..........................................
.....................XXXXXX.....................................
............XXXXXXXXXXXX........................................
........................XXX.................................XX..
......................................................XXXXXXXXXX
XXXXXXXXXXXXXXXXXX...............XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX..................................
.......................................................XXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXX..................................X
XXXXXXXX........................................................
....

Nếu chúng tôi muốn chia đoạn mã đó thành các phần 64 byte, chỉ có 19 trong số 41 phần được yêu cầu để thực thi một mã không trạng thái chuyển khoản() giao dịch, với phần còn lại của dữ liệu bắt buộc đến từ một nhân chứng.

|XXX.XXXXXXXXXXXX|XXXXXX..........|................|................
|................|.....XXXXXX.....|................|................
|............XXXX|XXXXXXXX........|................|................
|................|........XXX.....|................|............XX..
|................|................|................|......XXXXXXXXXX
|XXXXXXXXXXXXXXXX|XX..............|.XXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXX
|XXXXXXXXXXXXXXXX|XXXXXXXXXXXXXX..|................|................
|................|................|................|.......XXXXXXXXX
|XXXXXXXXXXXXXXXX|XXXXXXXXXXXXX...|................|...............X
|XXXXXXXX........|................|................|................
|....

So sánh nó với 31 trong số 81 khối trong một lược đồ phân đoạn 32 byte:

|XXX.XXXX|XXXXXXXX|XXXXXX..|........|........|........|........|........
|........|........|.....XXX|XXX.....|........|........|........|........
|........|....XXXX|XXXXXXXX|........|........|........|........|........
|........|........|........|XXX.....|........|........|........|....XX..
|........|........|........|........|........|........|......XX|XXXXXXXX
|XXXXXXXX|XXXXXXXX|XX......|........|.XXXXXXX|XXXXXXXX|XXXXXXXX|XXXXXXXX
|XXXXXXXX|XXXXXXXX|XXXXXXXX|XXXXXX..|........|........|........|........
|........|........|........|........|........|........|.......X|XXXXXXXX
|XXXXXXXX|XXXXXXXX|XXXXXXXX|XXXXX...|........|........|........|.......X
|XXXXXXXX|........|........|........|........|........|........|........
|....

Nhìn bề ngoài, có vẻ như các phần nhỏ hơn hiệu quả hơn các phần lớn hơn, bởi vì gần như trống rỗng các khối ít thường xuyên hơn. Nhưng ở đây chúng ta cần nhớ rằng mã không sử dụng cũng có giá: mỗi đoạn mã chưa được thực thi được thay thế bằng một hàm băm kích thước cố định. Các đoạn mã nhỏ hơn có nghĩa là số lượng băm lớn hơn cho mã không sử dụng và các mã băm đó có thể lớn tới 32 byte mỗi đoạn (hoặc nhỏ nhất là 8 byte). Tại thời điểm này, bạn có thể kêu lên “Hol ‘lên! Nếu mã băm có kích thước tiêu chuẩn là 32 byte, thì việc thay thế 32 byte mã bằng 32 byte băm sẽ hữu ích như thế nào !?”.

Nhớ lại rằng mã hợp đồng là vui vẻnghĩa là tất cả các hàm băm được liên kết với nhau trong codeTrie – mã băm gốc mà chúng ta cần xác thực một khối. Trong cấu trúc đó, bất kỳ tuần tự các khối chưa được thực thi chỉ yêu cầu một băm, bất kể có bao nhiêu. Điều đó có nghĩa là, một hàm băm có thể thay thế một chi lớn tiềm năng chứa đầy các hàm băm phân đoạn tuần tự trên bộ ba mã được hợp nhất hóa, miễn là không có mã nào trong số chúng được yêu cầu để thực thi mã hóa.

Chúng tôi phải thu thập dữ liệu bổ sung

Kết luận mà chúng tôi đang xây dựng có chút gì đó không phù hợp: Không có sơ đồ ‘tối ưu’ về mặt lý thuyết cho việc ghép mã. Các lựa chọn thiết kế như cố định kích thước của các đoạn mã và hàm băm phụ thuộc vào dữ liệu được thu thập về ‘thế giới thực’. Mỗi hợp đồng thông minh sẽ kết hợp với nhau theo cách khác nhau, do đó, các nhà nghiên cứu phải chọn định dạng mang lại lợi ích hiệu quả lớn nhất cho hoạt động mạng chính được quan sát. Điều đó chính xác có nghĩa là gì?

trên không

Một điều có thể cho thấy một chương trình hợp nhất mã hiệu quả như thế nào Merkleization chi phícâu trả lời cho câu hỏi “có bao nhiêu thông tin bổ sung ngoài mã được thực thi được đưa vào nhân chứng này?”

Chúng tôi đã có một số kết quả đầy hứa hẹnđược thu thập bằng cách sử dụng một công cụ được xây dựng có mục đích được phát triển bởi Horacio Mijail từ nhóm nghiên cứu TeamX của Consensys, cho thấy tổng chi phí nhỏ tới 25% – không tệ chút nào!

Nói tóm lại, dữ liệu cho thấy rằng kích thước chunk nhỏ hơn theo kích thước lớn hơn sẽ hiệu quả hơn kích thước lớn hơn, đặc biệt nếu sử dụng hàm băm nhỏ hơn (8 byte). Nhưng những con số ban đầu này không có nghĩa là toàn diện, vì chúng chỉ đại diện cho khoảng 100 khối gần đây. Nếu bạn đang đọc bài này và muốn đóng góp vào sáng kiến ​​Ethereum không trạng thái bằng cách thu thập thêm dữ liệu hợp nhất mã đáng kể, hãy tự giới thiệu bản thân trên diễn đàn ethresear.ch hoặc kênh # code-merkleization trên nghiên cứu bất hòa Eth1x / 2!

Và như mọi khi, nếu bạn có câu hỏi, phản hồi hoặc yêu cầu liên quan đến “Tệp 1.X” và Ethereum, DM hoặc @gichiba không trạng thái trên twitter.

Thuc Quyen

Leave a Reply

Your email address will not be published. Required fields are marked *