All hashing is done with SHA-2-256 (also known as SHA-256), defined in FIPS 180-4 .
Three Merkle tree structures are used: a Binary Merkle Tree (to commit to bytecode), a Binary Merkle Sum Tree (to commit to transactions and collected fees) and a Sparse Merkle Tree (to commit to contract storage, i.e. state).
A specification for the Binary Merkle Tree is here .
The Binary Merkle Sum Tree is an extension of the tree defined in RFC-6962 .
The root pair (fee, digest)
of an empty tree is:
(0x0000000000000000, hash()) = (0x0000000000000000, 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855)
The root pair of a tree with one leaf:
(leaf.fee, hash(0x00 ++ leaf.fee ++ serialize(leaf)))
The root pair of a tree with two or more leaves is defined recursively:
(left.fee + right.fee, hash(0x01 ++ left.fee ++ left.digest ++ right.fee ++ right.digest))
In other words, the root pair is 40 bytes (8 for fee sum, 32 for hash digest).
A specification for the Sparse Merkle Tree is here .
A specification describing a suite of test vectors and outputs of a Sparse Merkle Tree is here.
Before insertion of the key-value pair, each key of the Sparse Merkle Tree should be hashed with sha256
to prevent tree structure manipulations.
During the proof verification, the original leaf key should be hashed similarly. Otherwise, the root will not match.
Consensus-critical data is authenticated using ECDSA , with the curve secp256k1 . A highly-optimized library is available in C (https://github.com/bitcoin-core/secp256k1 ), with wrappers in Go (https://pkg.go.dev/github.com/ethereum/go-ethereum/crypto/secp256k1 ) and Rust (https://docs.rs/crate/secp256k1 ).
Public keys are encoded in uncompressed form, as the concatenation of the x
and y
values. No prefix is needed to distinguish between encoding schemes as this is the only encoding supported.
Deterministic signatures (RFC-6979 ) should be used when signing, but this is not enforced at the protocol level as it cannot be.
Signatures are represented as the r
and s
(each 32 bytes), and v
(1-bit) values of the signature. r
and s
take on their usual meaning (see: SEC 1, 4.1.3 Signing Operation ), while v
is used for recovering the public key from a signature more quickly (see: SEC 1, 4.1.6 Public Key Recovery Operation ). Only low-s
values in signatures are valid (i.e. s <= secp256k1.n//2
); s
can be replaced with -s mod secp256k1.n
during the signing process if it is high. Given this, the first bit of s
will always be 0
, and can be used to store the 1-bit v
value.
v
represents the parity of the Y
component of the point, 0
for even and 1
for odd. The X
component of the point is assumed to always be low, since the possibility of it being high is negligible .
Putting it all together, the encoding for signatures is:
| 32 bytes || 32 bytes |
[256-bit r value][1-bit v value][255-bit s value]
This encoding scheme is derived from EIP 2098: Compact Signature Representation .