-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathEthscriptionsProver.sol
More file actions
134 lines (113 loc) · 5.45 KB
/
Copy pathEthscriptionsProver.sol
File metadata and controls
134 lines (113 loc) · 5.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./Ethscriptions.sol";
import "./L2/L2ToL1MessagePasser.sol";
import "./L2/L1Block.sol";
import "./libraries/Predeploys.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
/// @title EthscriptionsProver
/// @notice Proves Ethscription ownership and token balances to L1 via OP Stack
/// @dev Uses L2ToL1MessagePasser to send provable messages to L1
contract EthscriptionsProver {
using EnumerableSet for EnumerableSet.Bytes32Set;
/// @notice Info stored when an ethscription is queued for proving
struct QueuedProof {
uint256 l2BlockNumber;
uint256 l2BlockTimestamp;
bytes32 l1BlockHash;
uint256 l1BlockNumber;
}
/// @notice Set of all ethscription transaction hashes queued for proving
EnumerableSet.Bytes32Set private queuedEthscriptions;
/// @notice Mapping from ethscription tx hash to its queued proof info
mapping(bytes32 => QueuedProof) private queuedProofInfo;
/// @notice L1Block contract address for access control
address public constant L1_BLOCK = Predeploys.L1_BLOCK_ATTRIBUTES;
/// @notice L2ToL1MessagePasser predeploy address on OP Stack
L2ToL1MessagePasser constant L2_TO_L1_MESSAGE_PASSER =
L2ToL1MessagePasser(Predeploys.L2_TO_L1_MESSAGE_PASSER);
/// @notice The Ethscriptions contract (pre-deployed at known address)
Ethscriptions public constant ethscriptions = Ethscriptions(Predeploys.ETHSCRIPTIONS);
/// @notice Struct for ethscription data proof
struct EthscriptionDataProof {
bytes32 ethscriptionTxHash;
bytes32 contentSha;
bytes32 contentUriHash;
address creator;
address currentOwner;
address previousOwner;
uint256 ethscriptionNumber;
bool esip6;
bytes32 l1BlockHash;
uint256 l1BlockNumber;
uint256 l2BlockNumber;
uint256 l2Timestamp;
}
/// @notice Events for tracking proofs
event EthscriptionDataProofSent(
bytes32 indexed ethscriptionTxHash,
uint256 indexed l2BlockNumber,
uint256 l2Timestamp
);
/// @notice Queue an ethscription for proving
/// @dev Only callable by the Ethscriptions contract
/// @param txHash The transaction hash of the ethscription
function queueEthscription(bytes32 txHash) external virtual {
require(msg.sender == address(ethscriptions), "Only Ethscriptions contract can queue");
// Add to the set (deduplicates automatically)
if (queuedEthscriptions.add(txHash)) {
// Only store info if this is the first time we're queueing this txHash
// Capture the L1 block hash and number at the time of queuing
L1Block l1Block = L1Block(L1_BLOCK);
queuedProofInfo[txHash] = QueuedProof({
l2BlockNumber: block.number,
l2BlockTimestamp: block.timestamp,
l1BlockHash: l1Block.hash(),
l1BlockNumber: l1Block.number()
});
}
}
/// @notice Flush all queued proofs
/// @dev Only callable by the L1Block contract at the start of each new block
function flushAllProofs() external {
require(msg.sender == L1_BLOCK, "Only L1Block can flush");
uint256 count = queuedEthscriptions.length();
// Process and remove each ethscription from the set
// We iterate backwards to avoid index shifting during removal
for (uint256 i = count; i > 0; i--) {
bytes32 txHash = queuedEthscriptions.at(i - 1);
// Create and send proof for current state with stored block info
_createAndSendProof(txHash, queuedProofInfo[txHash]);
// Clean up: remove from set and delete the proof info
queuedEthscriptions.remove(txHash);
delete queuedProofInfo[txHash];
}
}
/// @notice Internal function to create and send proof for an ethscription
/// @param ethscriptionTxHash The transaction hash of the ethscription
/// @param proofInfo The queued proof info containing block data
function _createAndSendProof(bytes32 ethscriptionTxHash, QueuedProof storage proofInfo) internal {
// Get ethscription data including previous owner
Ethscriptions.Ethscription memory etsc = ethscriptions.getEthscription(ethscriptionTxHash);
address currentOwner = ethscriptions.currentOwner(ethscriptionTxHash);
// Create proof struct with all ethscription data
EthscriptionDataProof memory proof = EthscriptionDataProof({
ethscriptionTxHash: ethscriptionTxHash,
contentSha: etsc.content.contentSha,
contentUriHash: etsc.content.contentUriHash,
creator: etsc.creator,
currentOwner: currentOwner,
previousOwner: etsc.previousOwner,
ethscriptionNumber: etsc.ethscriptionNumber,
esip6: etsc.content.esip6,
l1BlockHash: proofInfo.l1BlockHash,
l1BlockNumber: proofInfo.l1BlockNumber,
l2BlockNumber: proofInfo.l2BlockNumber,
l2Timestamp: proofInfo.l2BlockTimestamp
});
// Encode and send to L1 with zero address and gas (only for state recording)
bytes memory proofData = abi.encode(proof);
L2_TO_L1_MESSAGE_PASSER.initiateWithdrawal(address(0), 0, proofData);
emit EthscriptionDataProofSent(ethscriptionTxHash, proofInfo.l2BlockNumber, proofInfo.l2BlockTimestamp);
}
}