Skip to content

Commit 0accdc9

Browse files
committed
Improve prover setup
1 parent 23f1c6f commit 0accdc9

File tree

5 files changed

+203
-72
lines changed

5 files changed

+203
-72
lines changed

contracts/src/Ethscriptions.sol

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,6 @@ contract Ethscriptions is ERC721EthscriptionsUpgradeable {
131131
bytes revertData
132132
);
133133

134-
/// @notice Emitted when a prover operation fails but ethscription continues
135-
event ProverFailed(
136-
bytes32 indexed transactionHash,
137-
bytes revertData
138-
);
139134

140135

141136
/// @notice Modifier to emit pending genesis events on first real creation
@@ -407,12 +402,8 @@ contract Ethscriptions is ERC721EthscriptionsUpgradeable {
407402
}
408403
}
409404

410-
// Use try-catch to prevent prover failures from reverting operations
411-
try prover.proveEthscriptionData(txHash) {} catch (bytes memory revertData) {
412-
// Proving failed, but the operation should continue
413-
// The prover can be called again later if needed
414-
emit ProverFailed(txHash, revertData);
415-
}
405+
// Queue ethscription for batch proving at block boundary
406+
prover.queueEthscription(txHash);
416407
}
417408

418409
/// @notice Get ethscription details (returns struct to avoid stack too deep)

contracts/src/EthscriptionsProver.sol

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,28 @@ import "./TokenManager.sol";
66
import "./EthscriptionsERC20.sol";
77
import "./L2/L2ToL1MessagePasser.sol";
88
import "./libraries/Predeploys.sol";
9+
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
910

1011
/// @title EthscriptionsProver
1112
/// @notice Proves Ethscription ownership and token balances to L1 via OP Stack
1213
/// @dev Uses L2ToL1MessagePasser to send provable messages to L1
1314
contract EthscriptionsProver {
15+
using EnumerableSet for EnumerableSet.Bytes32Set;
16+
17+
/// @notice Info stored when an ethscription is queued for proving
18+
struct QueuedProof {
19+
uint256 blockNumber;
20+
uint256 blockTimestamp;
21+
}
22+
23+
/// @notice Set of all ethscription transaction hashes queued for proving
24+
EnumerableSet.Bytes32Set private queuedEthscriptions;
25+
26+
/// @notice Mapping from ethscription tx hash to its queued proof info
27+
mapping(bytes32 => QueuedProof) private queuedProofInfo;
28+
29+
/// @notice L1Block contract address for access control
30+
address public constant L1_BLOCK = Predeploys.L1_BLOCK_ATTRIBUTES;
1431
/// @notice L2ToL1MessagePasser predeploy address on OP Stack
1532
L2ToL1MessagePasser constant L2_TO_L1_MESSAGE_PASSER =
1633
L2ToL1MessagePasser(Predeploys.L2_TO_L1_MESSAGE_PASSER);
@@ -60,6 +77,9 @@ contract EthscriptionsProver {
6077
uint256 indexed l2BlockNumber,
6178
uint256 l2Timestamp
6279
);
80+
81+
/// @notice Emitted when a batch of proofs is flushed
82+
event ProofBatchFlushed(uint256 count, uint256 blockNumber);
6383

6484
/// @notice Prove token balance for an address
6585
/// @param holder The address to prove balance for
@@ -92,20 +112,64 @@ contract EthscriptionsProver {
92112
emit TokenBalanceProofSent(holder, tokenInfo.tick, block.number, block.timestamp);
93113
}
94114

95-
/// @notice Prove ethscription existence and metadata
115+
/// @notice Queue an ethscription for proving
116+
/// @dev Only callable by the Ethscriptions contract
117+
/// @param txHash The transaction hash of the ethscription
118+
function queueEthscription(bytes32 txHash) external virtual {
119+
require(msg.sender == address(ethscriptions), "Only Ethscriptions contract can queue");
120+
121+
// Add to the set (deduplicates automatically)
122+
if (queuedEthscriptions.add(txHash)) {
123+
// Only store info if this is the first time we're queueing this txHash
124+
queuedProofInfo[txHash] = QueuedProof({
125+
blockNumber: block.number,
126+
blockTimestamp: block.timestamp
127+
});
128+
}
129+
}
130+
131+
/// @notice Flush all queued proofs
132+
/// @dev Only callable by the L1Block contract at the start of each new block
133+
function flushAllProofs() external {
134+
require(msg.sender == L1_BLOCK, "Only L1Block can flush");
135+
136+
uint256 count = queuedEthscriptions.length();
137+
138+
// Process and remove each ethscription from the set
139+
// We iterate backwards to avoid index shifting during removal
140+
for (uint256 i = count; i > 0; i--) {
141+
bytes32 txHash = queuedEthscriptions.at(i - 1);
142+
143+
// Get the stored proof info to know which block this was from
144+
QueuedProof memory proofInfo = queuedProofInfo[txHash];
145+
146+
// Create and send proof for current state with stored block info
147+
_createAndSendProof(txHash, proofInfo.blockNumber, proofInfo.blockTimestamp);
148+
149+
// Clean up: remove from set and delete the proof info
150+
queuedEthscriptions.remove(txHash);
151+
delete queuedProofInfo[txHash];
152+
}
153+
154+
emit ProofBatchFlushed(count, block.number - 1);
155+
}
156+
157+
/// @notice Internal function to create and send proof for an ethscription
96158
/// @param ethscriptionTxHash The transaction hash of the ethscription
97-
function proveEthscriptionData(bytes32 ethscriptionTxHash) external virtual {
159+
/// @param blockNumber The L2 block number being proved
160+
/// @param blockTimestamp The timestamp of the L2 block being proved
161+
function _createAndSendProof(bytes32 ethscriptionTxHash, uint256 blockNumber, uint256 blockTimestamp) internal {
98162
// Get ethscription data including previous owner
99163
Ethscriptions.Ethscription memory etsc = ethscriptions.getEthscription(ethscriptionTxHash);
100164
address currentOwner = ethscriptions.currentOwner(ethscriptionTxHash);
101-
165+
102166
// Check if it's a token item
103167
bool isToken = tokenManager.isTokenItem(ethscriptionTxHash);
104168
uint256 tokenAmount = 0;
105169
if (isToken) {
106170
tokenAmount = tokenManager.getTokenAmount(ethscriptionTxHash);
107171
}
108-
172+
109173
// Create proof struct with previous owner
110174
EthscriptionDataProof memory proof = EthscriptionDataProof({
111175
ethscriptionTxHash: ethscriptionTxHash,
@@ -116,14 +180,15 @@ contract EthscriptionsProver {
116180
ethscriptionNumber: etsc.ethscriptionNumber,
117181
isToken: isToken,
118182
tokenAmount: tokenAmount,
119-
l2BlockNumber: block.number,
120-
l2Timestamp: block.timestamp
183+
l2BlockNumber: blockNumber,
184+
l2Timestamp: blockTimestamp
121185
});
122-
186+
123187
// Encode and send to L1 with zero address and gas (only for state recording)
124188
bytes memory proofData = abi.encode(proof);
125189
L2_TO_L1_MESSAGE_PASSER.initiateWithdrawal(address(0), 0, proofData);
126-
127-
emit EthscriptionDataProofSent(ethscriptionTxHash, block.number, block.timestamp);
190+
191+
emit EthscriptionDataProofSent(ethscriptionTxHash, blockNumber, blockTimestamp);
128192
}
193+
129194
}

contracts/src/L2/L1Block.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
pragma solidity 0.8.24;
33

44
import { Constants } from "../libraries/Constants.sol";
5+
import { Predeploys } from "../libraries/Predeploys.sol";
6+
7+
interface IEthscriptionsProver {
8+
function flushAllProofs() external;
9+
}
510

611
/// @custom:proxied
712
/// @custom:predeploy 0x4200000000000000000000000000000000000015
@@ -64,6 +69,10 @@ contract L1Block {
6469
/// 8. _hash L1 blockhash.
6570
/// 9. _batcherHash Versioned hash to authenticate batcher by.
6671
function setL1BlockValuesEcotone() external {
72+
// Flush all queued ethscription proofs before updating to new block
73+
// Each proof includes its own block number and timestamp from when it was queued
74+
IEthscriptionsProver(Predeploys.ETHSCRIPTIONS_PROVER).flushAllProofs();
75+
6776
address depositor = DEPOSITOR_ACCOUNT();
6877
assembly {
6978
// Revert if the caller is not the depositor account.

contracts/test/EthscriptionsFailureHandling.t.sol

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,9 @@ contract FailingProver is EthscriptionsProver {
5454
failMessage = _message;
5555
}
5656

57-
function proveEthscriptionData(bytes32 transactionHash) external override {
58-
if (shouldFail) {
59-
revert(failMessage);
60-
}
61-
// Otherwise do nothing
57+
function queueEthscription(bytes32 txHash) external override {
58+
// For testing, always succeed
59+
// In the new design, queueing doesn't fail and doesn't emit events
6260
}
6361
}
6462

@@ -72,11 +70,6 @@ contract EthscriptionsFailureHandlingTest is TestSetup {
7270
bytes revertData
7371
);
7472

75-
event ProverFailed(
76-
bytes32 indexed transactionHash,
77-
bytes revertData
78-
);
79-
8073
function setUp() public override {
8174
super.setUp();
8275

@@ -138,12 +131,12 @@ contract EthscriptionsFailureHandlingTest is TestSetup {
138131
}
139132

140133
function testCreateEthscriptionWithProverFailure() public {
141-
// Configure Prover to fail
142-
FailingProver(Predeploys.ETHSCRIPTIONS_PROVER).setShouldFail(true);
143-
FailingProver(Predeploys.ETHSCRIPTIONS_PROVER).setFailMessage("Proving failed");
134+
// Note: With the new batched proving design, the prover doesn't fail immediately
135+
// during creation. Instead, ethscriptions are queued for batch proving.
136+
// This test now verifies that creation succeeds and the ethscription is queued.
144137

145138
bytes32 txHash = keccak256("test_tx_2");
146-
string memory dataUri = "data:,Hello World with failing prover";
139+
string memory dataUri = "data:,Hello World with batched prover";
147140

148141
Ethscriptions.CreateEthscriptionParams memory params = createTestParams(
149142
txHash,
@@ -152,14 +145,7 @@ contract EthscriptionsFailureHandlingTest is TestSetup {
152145
false
153146
);
154147

155-
// Expect the ProverFailed event
156-
vm.expectEmit(true, false, false, true);
157-
emit ProverFailed(
158-
txHash,
159-
abi.encodeWithSignature("Error(string)", "Proving failed")
160-
);
161-
162-
// Create ethscription - should succeed despite Prover failure
148+
// Create ethscription - should succeed and queue for proving silently (no event)
163149
uint256 tokenId = ethscriptions.createEthscription(params);
164150

165151
// Verify the ethscription was created successfully

0 commit comments

Comments
 (0)