Exit roots
An exit tree is a binary, append-only, sparse Merkle tree (SMT) with a maximum depth of 32, where leaf nodes store bridging data.
The Merkle root of an exit tree is known as the exit tree root, and it is the fingerprint of all the information recorded in the exit tree’s leaf nodes.
The global exit tree root of the L1 info tree is, therefore, the source of truth for the whole network.
Rollup local exit trees¶
The L2 bridge contract manages a special Merkle tree called a local exit tree for each network that participates in bridging and claiming which is updated by the PolygonZkEVMGlobalExitRootL2.sol contract.
Data from bridgeAsset()
and bridgeMessage()
calls on the bridge is stored in leaf nodes on the local exit trees.
Important
The following exit tree structures are managed by:
- The PolygonRollupManager.sol.
- The L1 PolygonZkEVMBridgeV2.sol contract.
- ThePolygonZkEVMGlobalExitRootV2.sol.
Exit tree for rollups¶
The roots of the L2 local exit trees feed into a single exit tree that manages state from all participating L2 rollups. The state lives in the L1 realm and is accessed at sequencing time.
The L2 local exit root is accessible on the rollup manager by calling the getRollupExitRoot()
method.
L1 local exit tree¶
Every time there is a call to bridgeAsset()
and bridgeMessage()
on the bridge at the L1 Ethereum level, the data is stored in a leaf node on the L1 local exit tree.
L1 info tree¶
The L1 info tree is stored in the PolygonZkEVMGlobalExitRootV2.sol contract also known as the global exit root manager.
All subtrees exit roots feed into the leaves of the L1 info tree, which contains the global exit root (GER).
The GER is the fingerprint of the information stored in all trees, and thus represents the global state of the system.
Exit leaves¶
Two constants define transaction leaf types in the bridge contract.
// Leaf type asset
uint8 private constant _LEAF_TYPE_ASSET = 0;
// Leaf type message
uint8 private constant _LEAF_TYPE_MESSAGE = 1;
Data in a leaf contains a Keccak256 hash of the metadata (ABI encoded metadata if any) and the following parameters (matched by publicly available transaction data as seen in the bridge L1 to L2 documentation):
_addLeaf(
getLeafValue(
_LEAF_TYPE_ASSET, // or _LEAF_TYPE_MESSAGE_
originNetwork,
originTokenAddress,
destinationNetwork,
destinationAddress,
leafAmount,
keccak256(metadata)
)
);
Leaf parameters
int32 originNetwork
: Origin network ID, where the original asset belongs.address originTokenAddress
: IfleafType = 0
, Origin network token address (0x0000...0000
) is reserved for ether. IfleafType = 1
,msg.sender
of the message.uint32 destinationNetwor
k: Bridging destination network ID.address destinationAddress
: Address that receives the bridged asset in the destination network.uint256 leafAmount
: Amount of tokens/ether to bridge.bytes32 keccak256(metadata)
: Hash of the metadata. This metadata contains information about assets transferred or the message payload.
Updating system state¶
The system uses a set of exit tree roots to manage system state. Leaves of the trees point to transaction data such as detailed above.
Adding a new leaf to the tree triggers an update to the exit tree root which then propagates to an update on the global exit tree root.
Using Merkle tree exit roots in this way, referenced by the bridge contracts and accessible to the PolygonRollupManager
contract with getters, the bridge contract triggers data synchronization across L1 and L2, including at the sequencer and state db level.
The use of two distinct global exit root manager contracts for L1 and L2, as well as separate logic for the sequencing flow and the bridge contract, allows for extensive network interoperability. Meanwhile, all asset transfers can be validated by any L1 and L2 node due to the accessibility of state data.
The exit roots are modified in two key flows; sequencing and bridging.
Sequencing flow¶
The PolygonZkEVMGlobalExitRootV2
contract manages updates to the exit roots on sequencing. The contract calls updateExitRoot(...)
on the GlobalExitRootManager
during the sequencing flow to add an exit leaf to the relevant exit tree.
- Initiate update:
PolygonZkEVMEtrog
initiates the update process by callingupdateExitRoots
onPolygonRollupBaseEtrog
. - Retrieve current roots:
PolygonRollupBaseEtrog
retrieves the current local and global exit roots fromPolygonZkEVMGlobalExitRootL2
andPolygonZkEVMGlobalExitRootV2
respectively. - Compute new exit root:
PolygonRollupBaseEtrog
computes the new exit root based on the retrieved local and global exit roots. - Update local exit root:
PolygonRollupBaseEtrog
updates the local exit root inPolygonZkEVMGlobalExitRootL2
. - Update global exit root:
PolygonRollupBaseEtrog
updates the global exit root inPolygonZkEVMGlobalExitRootV2
. - Verify updated exit root:
PolygonRollupBaseEtrog
callsgetRollupExitRoot
onPolygonRollupManager
to verify the updated exit root.
L1 or L2 update
- If
msg.sender
is the bridge contract, the L1 local exit root is updated. - If
msg.sender
is the rollup manager, the L2 local exit root is updated.
Bridging flow¶
When bridging, the global exit root is updated if the forceUpdateGlobalExitRoot
variable is set to true
.
- The user interacts with the
PolygonZkEVMBridgeV2
contract by calling thebridge()
function. PolygonZkEVMBridgeV2
callsupdateLocalExitRoot()
onPolygonZkEVMGlobalExitRootL2
, which updates the local exit root.- If
forceUpdateGlobalExitRoot
is set to true,PolygonZkEVMBridgeV2
callsupdateGlobalExitRoot()
onPolygonZkEVMGlobalExitRootV2
, which updates the global exit root.