Skip to content

Commit e061658

Browse files
RonTuretzkygithub-actions[bot]
authored andcommitted
chore(flat): auto-flatten solidity sources 🤖
1 parent 0c5329b commit e061658

File tree

3 files changed

+5545
-103
lines changed

3 files changed

+5545
-103
lines changed

‎flat/GasKillerSDK.flat.sol‎

Lines changed: 147 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,94 +2264,6 @@ library SafeCastUpgradeable {
22642264
}
22652265
}
22662266

2267-
// src/StateChangeHandlerLib.sol
2268-
2269-
enum StateUpdateType {
2270-
STORE,
2271-
CALL,
2272-
LOG0,
2273-
LOG1,
2274-
LOG2,
2275-
LOG3,
2276-
LOG4
2277-
}
2278-
2279-
library StateChangeHandlerLib {
2280-
/// @notice Decodes and executes a series of state updates
2281-
/// @dev This function processes an array of state updates, executing them in sequence. Each update can be one of:
2282-
/// - STORE: Direct storage writes using assembly
2283-
/// - CALL: External contract calls with value transfer
2284-
/// - LOG0-LOG4: Event emission with 0-4 indexed topics
2285-
/// @param types Array of StateUpdateType enums indicating the type of each state update operation
2286-
/// @param args Array of ABI-encoded arguments corresponding to each operation type
2287-
/// @dev types and args arrays must be equal length, with args[i] containing the encoded parameters for types[i]
2288-
function _runStateUpdates(StateUpdateType[] memory types, bytes[] memory args) internal {
2289-
require(types.length == args.length, InvalidArguments());
2290-
for (uint256 i = 0; i < types.length; i++) {
2291-
StateUpdateType stateUpdateType = types[i];
2292-
bytes memory arg = args[i];
2293-
2294-
if (stateUpdateType == StateUpdateType.STORE) {
2295-
(bytes32 slot, bytes32 value) = abi.decode(arg, (bytes32, bytes32));
2296-
assembly {
2297-
sstore(slot, value)
2298-
}
2299-
} else if (stateUpdateType == StateUpdateType.CALL) {
2300-
(address target, uint256 value, bytes memory callargs) = abi.decode(arg, (address, uint256, bytes));
2301-
bool success;
2302-
// TOOD: might need better gas handling
2303-
uint256 callgas = gasleft();
2304-
assembly {
2305-
success := call(callgas, target, value, add(callargs, 0x20), mload(callargs), 0, 0)
2306-
}
2307-
// TODO: this section needs heavy testing
2308-
if (!success) {
2309-
uint256 _returndatasize;
2310-
assembly {
2311-
_returndatasize := returndatasize()
2312-
}
2313-
bytes memory revertData = new bytes(_returndatasize);
2314-
assembly {
2315-
returndatacopy(add(revertData, 0x20), 0, _returndatasize)
2316-
}
2317-
revert RevertingContext(i, target, revertData, callargs);
2318-
}
2319-
} else if (stateUpdateType == StateUpdateType.LOG0) {
2320-
// NOTE: For consistency I decode an abi encoding of bytes from bytes, but technically it's redundant
2321-
(bytes memory data) = abi.decode(arg, (bytes));
2322-
assembly {
2323-
log0(add(data, 0x20), mload(data))
2324-
}
2325-
} else if (stateUpdateType == StateUpdateType.LOG1) {
2326-
(bytes memory data, bytes32 topic1) = abi.decode(arg, (bytes, bytes32));
2327-
assembly {
2328-
log1(add(data, 0x20), mload(data), topic1)
2329-
}
2330-
} else if (stateUpdateType == StateUpdateType.LOG2) {
2331-
(bytes memory data, bytes32 topic1, bytes32 topic2) = abi.decode(arg, (bytes, bytes32, bytes32));
2332-
assembly {
2333-
log2(add(data, 0x20), mload(data), topic1, topic2)
2334-
}
2335-
} else if (stateUpdateType == StateUpdateType.LOG3) {
2336-
(bytes memory data, bytes32 topic1, bytes32 topic2, bytes32 topic3) =
2337-
abi.decode(arg, (bytes, bytes32, bytes32, bytes32));
2338-
assembly {
2339-
log3(add(data, 0x20), mload(data), topic1, topic2, topic3)
2340-
}
2341-
} else if (stateUpdateType == StateUpdateType.LOG4) {
2342-
(bytes memory data, bytes32 topic1, bytes32 topic2, bytes32 topic3, bytes32 topic4) =
2343-
abi.decode(arg, (bytes, bytes32, bytes32, bytes32, bytes32));
2344-
assembly {
2345-
log4(add(data, 0x20), mload(data), topic1, topic2, topic3, topic4)
2346-
}
2347-
}
2348-
}
2349-
}
2350-
2351-
error InvalidArguments();
2352-
error RevertingContext(uint256 index, address target, bytes revertData, bytes callargs);
2353-
}
2354-
23552267
// src/StateTracker.sol
23562268

23572269
/**
@@ -5447,6 +5359,12 @@ interface IBLSSignatureChecker is IBLSSignatureCheckerErrors, IBLSSignatureCheck
54475359

54485360
// src/interface/IGasKillerSDK.sol
54495361

5362+
/// @notice Represents an external storage slot access (address + slot)
5363+
struct ExternalStorageSlot {
5364+
address contractAddress;
5365+
bytes32 slot;
5366+
}
5367+
54505368
/**
54515369
* @title IGasKillerSDK
54525370
* @notice Interface for GasKillerSDK contracts
@@ -5461,13 +5379,15 @@ interface IGasKillerSDK is IERC165 {
54615379
error InsufficientQuorumThreshold();
54625380
error StaleBlockNumber();
54635381
error FutureBlockNumber();
5382+
error ExternalStorageSlotMismatch(address contractAddress, bytes32 slot);
54645383

54655384
/**
54665385
* @notice Function to verify if a signature is valid and contains correct storage updates
54675386
* @param msgHash The hash of the message to verify
54685387
* @param quorumNumbers The quorum numbers to check signatures for
54695388
* @param referenceBlockNumber The block number to use as reference for operator set
54705389
* @param storageUpdates The storage updates to verify
5390+
* @param expectedExternalSlots Array of external storage slots that were read during execution (as proven in ZK proof)
54715391
* @param transitionIndex The transition index
54725392
* @param anchorHash The block hash anchoring the execution to a specific Ethereum state
54735393
* @param callerAddress The address that initiated the original call (msg.sender)
@@ -5479,6 +5399,7 @@ interface IGasKillerSDK is IERC165 {
54795399
bytes calldata quorumNumbers,
54805400
uint32 referenceBlockNumber,
54815401
bytes calldata storageUpdates,
5402+
ExternalStorageSlot[] calldata expectedExternalSlots,
54825403
uint256 transitionIndex,
54835404
bytes32 anchorHash,
54845405
address callerAddress,
@@ -5487,6 +5408,126 @@ interface IGasKillerSDK is IERC165 {
54875408
) external;
54885409
}
54895410

5411+
// src/StateChangeHandlerLib.sol
5412+
5413+
enum StateUpdateType {
5414+
STORE,
5415+
CALL,
5416+
LOG0,
5417+
LOG1,
5418+
LOG2,
5419+
LOG3,
5420+
LOG4
5421+
}
5422+
5423+
library StateChangeHandlerLib {
5424+
/// @notice Decodes and executes a series of state updates with external storage slot verification
5425+
/// @dev This function processes an array of state updates, executing them in sequence. Each update can be one of:
5426+
/// - STORE: Direct storage writes using assembly
5427+
/// - CALL: External contract calls with value transfer (verified against expectedExternalSlots)
5428+
/// - LOG0-LOG4: Event emission with 0-4 indexed topics
5429+
/// @param types Array of StateUpdateType enums indicating the type of each state update operation
5430+
/// @param args Array of ABI-encoded arguments corresponding to each operation type
5431+
/// @param expectedExternalSlots Array of external storage slots that were read during execution (as proven in ZK proof)
5432+
/// @dev types and args arrays must be equal length, with args[i] containing the encoded parameters for types[i]
5433+
function _runStateUpdates(
5434+
StateUpdateType[] memory types,
5435+
bytes[] memory args,
5436+
ExternalStorageSlot[] calldata expectedExternalSlots
5437+
) internal {
5438+
require(types.length == args.length, InvalidArguments());
5439+
5440+
// Track which external slots have been verified
5441+
uint256 externalSlotIndex = 0;
5442+
5443+
for (uint256 i = 0; i < types.length; i++) {
5444+
StateUpdateType stateUpdateType = types[i];
5445+
bytes memory arg = args[i];
5446+
5447+
if (stateUpdateType == StateUpdateType.STORE) {
5448+
(bytes32 slot, bytes32 value) = abi.decode(arg, (bytes32, bytes32));
5449+
assembly {
5450+
sstore(slot, value)
5451+
}
5452+
} else if (stateUpdateType == StateUpdateType.CALL) {
5453+
(
5454+
address target,
5455+
uint256 value,
5456+
bytes memory callargs,
5457+
bytes32[] memory externalSlotsAccessed
5458+
) = abi.decode(arg, (address, uint256, bytes, bytes32[]));
5459+
5460+
// Verify each external storage slot accessed matches the expected list
5461+
for (uint256 j = 0; j < externalSlotsAccessed.length; j++) {
5462+
require(
5463+
externalSlotIndex < expectedExternalSlots.length,
5464+
ExternalStorageSlotMismatch(target, externalSlotsAccessed[j])
5465+
);
5466+
5467+
ExternalStorageSlot calldata expectedSlot = expectedExternalSlots[externalSlotIndex];
5468+
require(
5469+
expectedSlot.contractAddress == target && expectedSlot.slot == externalSlotsAccessed[j],
5470+
ExternalStorageSlotMismatch(target, externalSlotsAccessed[j])
5471+
);
5472+
5473+
externalSlotIndex++;
5474+
}
5475+
5476+
bool success;
5477+
// TOOD: might need better gas handling
5478+
uint256 callgas = gasleft();
5479+
assembly {
5480+
success := call(callgas, target, value, add(callargs, 0x20), mload(callargs), 0, 0)
5481+
}
5482+
// TODO: this section needs heavy testing
5483+
if (!success) {
5484+
uint256 _returndatasize;
5485+
assembly {
5486+
_returndatasize := returndatasize()
5487+
}
5488+
bytes memory revertData = new bytes(_returndatasize);
5489+
assembly {
5490+
returndatacopy(add(revertData, 0x20), 0, _returndatasize)
5491+
}
5492+
revert RevertingContext(i, target, revertData, callargs);
5493+
}
5494+
} else if (stateUpdateType == StateUpdateType.LOG0) {
5495+
// NOTE: For consistency I decode an abi encoding of bytes from bytes, but technically it's redundant
5496+
(bytes memory data) = abi.decode(arg, (bytes));
5497+
assembly {
5498+
log0(add(data, 0x20), mload(data))
5499+
}
5500+
} else if (stateUpdateType == StateUpdateType.LOG1) {
5501+
(bytes memory data, bytes32 topic1) = abi.decode(arg, (bytes, bytes32));
5502+
assembly {
5503+
log1(add(data, 0x20), mload(data), topic1)
5504+
}
5505+
} else if (stateUpdateType == StateUpdateType.LOG2) {
5506+
(bytes memory data, bytes32 topic1, bytes32 topic2) = abi.decode(arg, (bytes, bytes32, bytes32));
5507+
assembly {
5508+
log2(add(data, 0x20), mload(data), topic1, topic2)
5509+
}
5510+
} else if (stateUpdateType == StateUpdateType.LOG3) {
5511+
(bytes memory data, bytes32 topic1, bytes32 topic2, bytes32 topic3) =
5512+
abi.decode(arg, (bytes, bytes32, bytes32, bytes32));
5513+
assembly {
5514+
log3(add(data, 0x20), mload(data), topic1, topic2, topic3)
5515+
}
5516+
} else if (stateUpdateType == StateUpdateType.LOG4) {
5517+
(bytes memory data, bytes32 topic1, bytes32 topic2, bytes32 topic3, bytes32 topic4) =
5518+
abi.decode(arg, (bytes, bytes32, bytes32, bytes32, bytes32));
5519+
assembly {
5520+
log4(add(data, 0x20), mload(data), topic1, topic2, topic3, topic4)
5521+
}
5522+
}
5523+
}
5524+
}
5525+
5526+
error InvalidArguments();
5527+
error RevertingContext(uint256 index, address target, bytes revertData, bytes callargs);
5528+
error ExternalStorageSlotMismatch(address contractAddress, bytes32 slot);
5529+
}
5530+
54905531
// src/GasKillerSDK.sol
54915532

54925533
/**
@@ -5529,12 +5570,13 @@ abstract contract GasKillerSDK is StateTracker, IGasKillerSDK {
55295570
/**
55305571
* @notice Function to verify if a signature is valid and contains correct storage updates
55315572
* @dev The message hash must be computed as:
5532-
* sha256(abi.encode(transitionIndex, address(this), anchorHash, callerAddress, contractCalldata, storageUpdates))
5573+
* sha256(abi.encode(transitionIndex, address(this), anchorHash, callerAddress, contractCalldata, storageUpdates, expectedExternalSlots))
55335574
* This format enables slashing by including all inputs needed to reproduce execution.
55345575
* @param msgHash The hash of the message to verify
55355576
* @param quorumNumbers The quorum numbers to check signatures for
55365577
* @param referenceBlockNumber The block number to use as reference for operator set
55375578
* @param storageUpdates The storage updates to verify
5579+
* @param expectedExternalSlots Array of external storage slots that were read during execution (as proven in ZK proof)
55385580
* @param transitionIndex The transition index
55395581
* @param anchorHash The block hash anchoring the execution to a specific Ethereum state
55405582
* @param callerAddress The address that initiated the original call (msg.sender)
@@ -5546,6 +5588,7 @@ abstract contract GasKillerSDK is StateTracker, IGasKillerSDK {
55465588
bytes calldata quorumNumbers,
55475589
uint32 referenceBlockNumber,
55485590
bytes calldata storageUpdates,
5591+
ExternalStorageSlot[] calldata expectedExternalSlots,
55495592
uint256 transitionIndex,
55505593
bytes32 anchorHash,
55515594
address callerAddress,
@@ -5560,21 +5603,23 @@ abstract contract GasKillerSDK is StateTracker, IGasKillerSDK {
55605603

55615604
// Verify transition index and message hash
55625605
require(transitionIndex + 1 == stateTransitionCount(), InvalidTransitionIndex());
5563-
5606+
55645607
// Compute expected hash with all slashing-required fields:
55655608
// - transitionIndex: replay protection
55665609
// - address(this): target contract
55675610
// - anchorHash: block hash for state anchoring (enables slashing verification)
55685611
// - callerAddress: msg.sender (affects execution via access control, balances)
55695612
// - contractCalldata: full calldata with arguments (enables execution reproduction)
55705613
// - storageUpdates: the claimed storage changes
5614+
// - expectedExternalSlots: external storage slots that were read during execution
55715615
bytes32 expectedHash = sha256(abi.encode(
55725616
transitionIndex,
55735617
address(this),
55745618
anchorHash,
55755619
callerAddress,
55765620
contractCalldata,
5577-
storageUpdates
5621+
storageUpdates,
5622+
expectedExternalSlots
55785623
));
55795624
require(expectedHash == msgHash, InvalidSignature());
55805625

@@ -5591,8 +5636,8 @@ abstract contract GasKillerSDK is StateTracker, IGasKillerSDK {
55915636
);
55925637
}
55935638

5594-
// Apply the state changes
5595-
_stateChangeHandler(storageUpdates);
5639+
// Apply the state changes, verifying external storage accesses match expected
5640+
_stateChangeHandler(storageUpdates, expectedExternalSlots);
55965641
}
55975642

55985643
/**
@@ -5613,22 +5658,25 @@ abstract contract GasKillerSDK is StateTracker, IGasKillerSDK {
56135658
* @param callerAddress The caller address (msg.sender)
56145659
* @param contractCalldata The full contract calldata
56155660
* @param storageUpdates The storage updates
5661+
* @param expectedExternalSlots The expected external storage slots that were read
56165662
* @return bytes32 The expected message hash
56175663
*/
56185664
function getMessageHash(
56195665
uint256 transitionIndex,
56205666
bytes32 anchorHash,
56215667
address callerAddress,
56225668
bytes calldata contractCalldata,
5623-
bytes calldata storageUpdates
5669+
bytes calldata storageUpdates,
5670+
ExternalStorageSlot[] calldata expectedExternalSlots
56245671
) external view returns (bytes32) {
56255672
return sha256(abi.encode(
56265673
transitionIndex,
56275674
address(this),
56285675
anchorHash,
56295676
callerAddress,
56305677
contractCalldata,
5631-
storageUpdates
5678+
storageUpdates,
5679+
expectedExternalSlots
56325680
));
56335681
}
56345682

@@ -5657,12 +5705,13 @@ abstract contract GasKillerSDK is StateTracker, IGasKillerSDK {
56575705
}
56585706

56595707
/**
5660-
* @notice Function to apply storage updates
5708+
* @notice Function to apply storage updates with external slot verification
56615709
* @param storageUpdates The storage updates to apply
5710+
* @param expectedExternalSlots The expected external storage slots that were read during execution
56625711
*/
5663-
function _stateChangeHandler(bytes calldata storageUpdates) internal {
5712+
function _stateChangeHandler(bytes calldata storageUpdates, ExternalStorageSlot[] calldata expectedExternalSlots) internal {
56645713
(StateUpdateType[] memory types, bytes[] memory args) = abi.decode(storageUpdates, (StateUpdateType[], bytes[]));
5665-
StateChangeHandlerLib._runStateUpdates(types, args);
5714+
StateChangeHandlerLib._runStateUpdates(types, args, expectedExternalSlots);
56665715
}
56675716

56685717
/**

0 commit comments

Comments
 (0)