Skip to content

Commit

Permalink
feat: Add SMA Replay-Safe Hash (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
Zer0dot authored Aug 22, 2024
1 parent f475848 commit a1510cd
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 15 deletions.
33 changes: 30 additions & 3 deletions src/account/SemiModularAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ contract SemiModularAccount is UpgradeableModularAccount {
uint256 internal constant _SEMI_MODULAR_ACCOUNT_STORAGE_SLOT =
0x5b9dc9aa943f8fa2653ceceda5e3798f0686455280432166ba472eca0bc17a32;

// keccak256("EIP712Domain(uint256 chainId,address verifyingContract)")
bytes32 private constant _DOMAIN_SEPARATOR_TYPEHASH =
0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;

// keccak256("ReplaySafeHash(bytes32 hash)")
bytes32 private constant _REPLAY_SAFE_HASH_TYPEHASH =
0x294a8735843d4afb4f017c76faf3b7731def145ed0025fc9b1d5ce30adf113ff;

ModuleEntity internal constant _FALLBACK_VALIDATION = ModuleEntity.wrap(bytes24(type(uint192).max));

uint256 internal constant _SIG_VALIDATION_PASSED = 0;
Expand Down Expand Up @@ -88,6 +96,11 @@ contract SemiModularAccount is UpgradeableModularAccount {
return "erc6900/reference-semi-modular-account/0.8.0";
}

function replaySafeHash(bytes32 hash) public view virtual returns (bytes32) {
return
MessageHashUtils.toTypedDataHash({domainSeparator: _domainSeparator(), structHash: _hashStruct(hash)});
}

function _execUserOpValidation(
ModuleEntity userOpValidationFunction,
PackedUserOperation memory userOp,
Expand Down Expand Up @@ -120,9 +133,9 @@ contract SemiModularAccount is UpgradeableModularAccount {
if (msg.sender != fallbackSigner) {
revert FallbackSignerMismatch();
}
return;
} else {
super._execRuntimeValidation(runtimeValidationFunction, callData, authorization);
}
super._execRuntimeValidation(runtimeValidationFunction, callData, authorization);
}

function _exec1271Validation(ModuleEntity sigValidation, bytes32 hash, bytes calldata signature)
Expand All @@ -134,7 +147,7 @@ contract SemiModularAccount is UpgradeableModularAccount {
if (sigValidation.eq(_FALLBACK_VALIDATION)) {
address fallbackSigner = _getFallbackSigner();

if (SignatureChecker.isValidSignatureNow(fallbackSigner, hash, signature)) {
if (SignatureChecker.isValidSignatureNow(fallbackSigner, replaySafeHash(hash), signature)) {
return _1271_MAGIC_VALUE;
}
return _1271_INVALID;
Expand Down Expand Up @@ -176,11 +189,25 @@ contract SemiModularAccount is UpgradeableModularAccount {
return address(uint160(bytes20(appendedData)));
}

function _domainSeparator() internal view returns (bytes32) {
return keccak256(abi.encode(_DOMAIN_SEPARATOR_TYPEHASH, block.chainid, address(this)));
}

function _getSemiModularAccountStorage() internal pure returns (SemiModularAccountStorage storage) {
SemiModularAccountStorage storage _storage;
assembly ("memory-safe") {
_storage.slot := _SEMI_MODULAR_ACCOUNT_STORAGE_SLOT
}
return _storage;
}

function _hashStruct(bytes32 hash) internal pure virtual returns (bytes32) {
bytes32 res;
assembly ("memory-safe") {
mstore(0x00, _REPLAY_SAFE_HASH_TYPEHASH)
mstore(0x20, hash)
res := keccak256(0, 0x40)
}
return res;
}
}
10 changes: 8 additions & 2 deletions src/modules/ReplaySafeWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ abstract contract ReplaySafeWrapper is ModuleEIP712 {
});
}

function _hashStruct(bytes32 hash) internal view virtual returns (bytes32) {
return keccak256(abi.encode(_REPLAY_SAFE_HASH_TYPEHASH, hash));
function _hashStruct(bytes32 hash) internal pure virtual returns (bytes32) {
bytes32 res;
assembly ("memory-safe") {
mstore(0x00, _REPLAY_SAFE_HASH_TYPEHASH)
mstore(0x20, hash)
res := keccak256(0x00, 0x40)
}
return res;
}
}
14 changes: 4 additions & 10 deletions test/account/UpgradeableModularAccount.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -402,17 +402,11 @@ contract UpgradeableModularAccountTest is AccountTestBase {
function test_isValidSignature() public {
bytes32 message = keccak256("hello world");

uint8 v;
bytes32 r;
bytes32 s;
bytes32 replaySafeHash = vm.envOr("SMA_TEST", false)
? SemiModularAccount(payable(account1)).replaySafeHash(message)
: singleSignerValidationModule.replaySafeHash(address(account1), message);

if (vm.envOr("SMA_TEST", false)) {
// todo: implement replay-safe hashing for SMA
(v, r, s) = vm.sign(owner1Key, message);
} else {
bytes32 replaySafeHash = singleSignerValidationModule.replaySafeHash(address(account1), message);
(v, r, s) = vm.sign(owner1Key, replaySafeHash);
}
(uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, replaySafeHash);

bytes memory signature = _encode1271Signature(_signerValidation, abi.encodePacked(r, s, v));

Expand Down

0 comments on commit a1510cd

Please sign in to comment.