CIP No. | Title | Author | Discussions | Status | Type | Created |
---|---|---|---|---|---|---|
13 |
Employ Big-Endian MPT Keys |
Thegaram <th307q@gmail.com> |
Final |
Spec Breaking |
2020-07-27 |
Simple Merkle Patricia Tries (or just MPTs) are used to calculate the transactions_root
and deferred_receipts_root
header fields and to provide the corresponding light node proofs. Currently, these keys are encoded as little-endian. This PR proposes to switch to a big-endian encoding.
A block's transactions are stored in an MPT where the key is the block index and the value is the transaction hash. The root of this MPT is stored in the transactions_root
header field. A similar approach is used for receipts.
The keys of this MPT are encoded as follows. We first find the smallest number of bytes B
that can accommodate all keys. Then, we encode all keys as little-endian byte sequences of length B
.
Example: If a block contains 300 transactions, then the keys are 0, 1, ..., 299. We need 2 bytes to represent 299 (B = 2
). The keys are encoded as follows:
0 --> [0x00, 0x00]
1 --> [0x01, 0x00]
...
255 --> [0xff, 0x00]
256 --> [0x00, 0x01]
257 --> [0x01, 0x01]
...
299 --> [0x2b, 0x01]
This approach is counter-intuitive and not consistent with storage MPT (delta, snapshot) key encoding.
Little- and big-endian key encodings are both reasonable and do not affect the correctness of the protocol. Different encodings might result in different proof sizes, given that the number of transactions in a block is limited.
As these implementation details might be exposed to node developers and potentially to dApp developers as well (if we add a cfx_getProofs
RPC), it is better to aim for an intuitive and consistent encoding, which in this case is the big-endian one.
Going with the previous example of 300 transactions in a block, the encoding would work like this:
0 --> [0x00, 0x00]
1 --> [0x00, 0x01]
...
255 --> [0x00, 0xff]
256 --> [0x01, 0x00]
257 --> [0x01, 0x01]
...
299 --> [0x01, 0x2b]
As little-endian keys work just fine, there might not be a consensus about the necessity of this change. The main use here is to make the protocol easier to understand. As we are in phase-2, with a planned hard fork and a new phase launch both on the horizon, I propose we do make this change.
This change updates the definition of transactions_root
and deferred_receipts_root
for blocks with more than 256 transactions. As the correctness of the transaction_root
is a prerequisite of block validity (see 3.4.3 Well-formedness and 3.4.4 Block Header Validity in the spec), this is a breaking change. Please note that the intended behavior is not reflected in the current version of the specification.
We will enable this change in a hard fork of Oceanus.
The existing unit tests [1] and [2] demonstrate the current behavior. Along with the fix, these are updated as well.
The fix is a single-line change. See the corresponding PR here.
There are no obvious security implications of this change.
Copyright and related rights waived via CC0.