@@ -68,6 +68,9 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
6868 */
6969 event ShardsUnregistered (TssKey[] keys );
7070
71+ // number of signing sessions per batch
72+ mapping (uint64 => uint16 ) internal _signingSessions;
73+
7174 // GMP message status
7275 mapping (bytes32 => GmpStatus) public messages;
7376
@@ -247,7 +250,7 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
247250 /**
248251 * @dev Dispatch a single GMP message.
249252 */
250- function _gmpCommand (bytes calldata params ) private returns (bytes32 operationHash ) {
253+ function _gmpCommand (bytes calldata params , bool dry ) private returns (bytes32 operationHash ) {
251254 require (params.length >= 256 , "invalid GmpMessage " );
252255 GmpMessage calldata gmp;
253256 assembly {
@@ -266,6 +269,10 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
266269 GmpCallback memory callback = gmp.toCallback ();
267270 operationHash = callback.opHash;
268271
272+ if (dry) {
273+ return operationHash;
274+ }
275+
269276 // Verify if this GMP message was already executed
270277 bytes32 msgId = callback.messageId ();
271278 require (messages[msgId] == GmpStatus.NOT_FOUND, "message already executed " );
@@ -325,14 +332,18 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
325332 /**
326333 * @dev Register a single shard and returns the GatewayOp hash.
327334 */
328- function _registerShardCommand (bytes calldata params ) private returns (bytes32 operationHash ) {
335+ function _registerShardCommand (bytes calldata params , bool dry ) private returns (bytes32 operationHash ) {
329336 require (params.length == 64 , "invalid TssKey " );
330337 TssKey calldata publicKey;
331338 assembly {
332339 publicKey := params.offset
333340 }
334341 operationHash = PrimitiveUtils.hash (publicKey.yParity, publicKey.xCoord);
335342
343+ if (dry) {
344+ return operationHash;
345+ }
346+
336347 bool isSuccess = ShardStore.getMainStorage ().register (publicKey);
337348 if (isSuccess) {
338349 TssKey[] memory keys = new TssKey [](1 );
@@ -344,14 +355,18 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
344355 /**
345356 * @dev Removes a single shard from the set.
346357 */
347- function _unregisterShardCommand (bytes calldata params ) private returns (bytes32 operationHash ) {
358+ function _unregisterShardCommand (bytes calldata params , bool dry ) private returns (bytes32 operationHash ) {
348359 require (params.length == 64 , "invalid TssKey " );
349360 TssKey calldata publicKey;
350361 assembly {
351362 publicKey := params.offset
352363 }
353364 operationHash = PrimitiveUtils.hash (publicKey.yParity, publicKey.xCoord);
354365
366+ if (dry) {
367+ return operationHash;
368+ }
369+
355370 bool isSuccess = ShardStore.getMainStorage ().revoke (publicKey);
356371 if (isSuccess) {
357372 TssKey[] memory keys = new TssKey [](1 );
@@ -363,7 +378,7 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
363378 /**
364379 * Cast the command function into a uint256.
365380 */
366- function fnToPtr (function (bytes calldata ) internal returns (bytes32 ) fn) private pure returns (uint256 ptr ) {
381+ function fnToPtr (function (bytes calldata , bool ) internal returns (bytes32 ) fn) private pure returns (uint256 ptr ) {
367382 assembly {
368383 ptr := fn
369384 }
@@ -393,7 +408,7 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
393408 function _cmdTableLookup (CommandsLookUpTable lut , Command command )
394409 private
395410 pure
396- returns (function (bytes calldata ) internal returns (bytes32 ) fn)
411+ returns (function (bytes calldata , bool ) internal returns (bytes32 ) fn)
397412 {
398413 unchecked {
399414 // Extract the function pointer from the table using the `Command` as index.
@@ -421,7 +436,7 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
421436 * increase the cost exponentially.
422437 * @return (uint256, bytes32) Returns a tuple containing the maximum amount of memory used in bytes and the operations root hash.
423438 */
424- function _executeCommands (GatewayOp[] calldata operations ) private returns (uint256 , bytes32 ) {
439+ function _executeCommands (GatewayOp[] calldata operations , bool dry ) private returns (uint256 , bytes32 ) {
425440 // Track the free memory pointer, to reset the memory after each command executed.
426441 uint256 freeMemPointer = PrimitiveUtils.readAllocatedMemory ();
427442 uint256 maxAllocatedMemory = freeMemPointer;
@@ -434,10 +449,11 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
434449 GatewayOp calldata operation = operations[i];
435450
436451 // Lookup the command function pointer
437- function (bytes calldata ) internal returns (bytes32 ) commandFN = _cmdTableLookup (lut, operation.command);
452+ function (bytes calldata , bool ) internal returns (bytes32 ) commandFN =
453+ _cmdTableLookup (lut, operation.command);
438454
439455 // Execute the command
440- bytes32 operationHash = commandFN (operation.params);
456+ bytes32 operationHash = commandFN (operation.params, dry );
441457
442458 // Update the operations root hash
443459 operationsRootHash =
@@ -462,12 +478,24 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
462478 function execute (Signature calldata signature , Batch calldata batch ) external {
463479 uint256 initialGas = gasleft ();
464480
481+ uint16 numSigningSessions = _signingSessions[batch.batchId];
482+ bool dry;
483+ if (numSigningSessions == 0 ) {
484+ numSigningSessions = batch.numSigningSessions;
485+ } else if (numSigningSessions == 1 ) {
486+ revert ("batch already executed " );
487+ } else if (numSigningSessions > 1 ) {
488+ numSigningSessions -= 1 ;
489+ dry = true ;
490+ }
491+ _signingSessions[batch.batchId] = numSigningSessions;
492+
465493 // Execute the commands and compute the operations root hash
466- (, bytes32 rootHash ) = _executeCommands (batch.ops);
494+ (, bytes32 rootHash ) = _executeCommands (batch.ops, dry );
467495 emit BatchExecuted (batch.batchId);
468496
469497 // Compute the Batch signing hash
470- rootHash = PrimitiveUtils.hash (batch.version, batch.batchId, uint256 (rootHash));
498+ rootHash = PrimitiveUtils.hash (batch.version, batch.batchId, batch.numSigningSessions, uint256 (rootHash));
471499 bytes32 signingHash = keccak256 (
472500 abi.encodePacked ("Analog GMP v2 " , networkId (), bytes32 (uint256 (uint160 (address (this )))), rootHash)
473501 );
@@ -484,7 +512,7 @@ contract Gateway is IGateway, UUPSUpgradeable, OwnableUpgradeable {
484512 // Refund the chronicle gas
485513 unchecked {
486514 // Extra gas overhead used to execute the refund logic + selector overhead
487- uint256 gasUsed = 2964 ;
515+ uint256 gasUsed = 2890 ;
488516
489517 // Compute the gas used + base cost + proxy overhead
490518 gasUsed += GasUtils.txBaseGas ();
0 commit comments