2 Commits

Author SHA1 Message Date
Andrew Morris
92f6ec8467 Fix getInitializeData 2022-09-21 13:29:06 +10:00
Andrew Morris
2e68505b2e Draft 2022-09-20 15:54:02 +10:00
6 changed files with 120 additions and 19 deletions

View File

@@ -57,6 +57,7 @@ export default class BlsWalletWrapper {
const initFunctionParams =
BLSWallet__factory.createInterface().encodeFunctionData("initialize", [
verificationGatewayAddress,
ethers.constants.AddressZero, // TODO: 4337 EntryPoint address
]);
return ethers.utils.getCreate2Address(

View File

@@ -6,7 +6,7 @@ pragma abicoder v2;
//To avoid constructor params having forbidden evm bytecodes on Optimism
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "./interfaces/IWallet.sol";
import "./interfaces/UserOperation4337.sol";
/** Minimal upgradable smart contract wallet.
Generic calls can only be requested by its trusted gateway.
@@ -23,6 +23,7 @@ contract BLSWallet is Initializable, IWallet
// BLS variables
address public trustedBLSGateway;
address public trustedEntryPoint;
address pendingBLSGateway;
uint256 pendingGatewayTime;
@@ -49,10 +50,12 @@ contract BLSWallet is Initializable, IWallet
);
function initialize(
address blsGateway
address blsGateway,
address entryPoint
) external initializer {
nonce = 0;
trustedBLSGateway = blsGateway;
trustedEntryPoint = entryPoint;
}
receive() external payable {}
@@ -144,7 +147,7 @@ contract BLSWallet is Initializable, IWallet
*/
function performOperation(
IWallet.Operation calldata op
) public payable onlyTrustedGateway thisNonce(op.nonce) returns (
) public payable onlyTrusted thisNonce(op.nonce) returns (
bool success,
bytes[] memory results
) {
@@ -221,6 +224,25 @@ contract BLSWallet is Initializable, IWallet
nonce++;
}
// --- <4337> ---
function validateUserOp(
UserOperation4337 calldata userOp,
bytes32 requestId,
address aggregator,
uint256 missingWalletFunds
) external view {
require(aggregator == trustedBLSGateway);
require(userOp.nonce == nonce);
require(missingWalletFunds == 0);
}
function getAggregator() public view returns (address) {
return trustedBLSGateway;
}
// --- </4337> ---
modifier onlyThis() {
require(msg.sender == address(this), "BLSWallet: only callable from this");
_;
@@ -234,6 +256,15 @@ contract BLSWallet is Initializable, IWallet
_;
}
modifier onlyTrusted() {
require(
msg.sender == trustedBLSGateway ||
msg.sender == trustedEntryPoint,
"BLSWallet: only callable from trusted gateway or 4337 entry point"
);
_;
}
modifier thisNonce(uint256 opNonce) {
require(opNonce == nonce, "BLSWallet: only callable with current nonce");
_;

View File

@@ -8,6 +8,7 @@ import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "./interfaces/IWallet.sol";
import "./interfaces/UserOperation4337.sol";
/**
A non-upgradable gateway used to create BLSWallets and call them with
@@ -26,7 +27,7 @@ contract VerificationGateway
ProxyAdmin public immutable walletProxyAdmin;
address public immutable blsWalletLogic;
mapping(bytes32 => IWallet) public walletFromHash;
mapping(IWallet => bytes32) public hashFromWallet;
mapping(IWallet => uint256[BLS_KEY_LEN]) public blsKeyFromWallet;
//mapping from an existing wallet's bls key hash to pending variables when setting a new BLS key
mapping(bytes32 => uint256[BLS_KEY_LEN]) public pendingBLSPublicKeyFromHash;
@@ -100,6 +101,16 @@ contract VerificationGateway
require(verified, "VG: Sig not verified");
}
function hashFromWallet(IWallet wallet) public view returns (bytes32) {
uint256[BLS_KEY_LEN] memory blsKey = blsKeyFromWallet[wallet];
if (blsLib.isZeroBLSKey(blsKey)) {
return bytes32(0);
}
return keccak256(abi.encodePacked(blsKey));
}
/**
If an existing wallet contract wishes to be called by this verification
gateway, it can directly register itself with a simple signed msg.
@@ -115,7 +126,7 @@ contract VerificationGateway
) public {
require(blsLib.isZeroBLSKey(publicKey) == false, "VG: publicKey must be non-zero");
IWallet wallet = IWallet(msg.sender);
bytes32 existingHash = hashFromWallet[wallet];
bytes32 existingHash = hashFromWallet(wallet);
if (existingHash == bytes32(0)) { // wallet does not yet have a bls key registered with this gateway
// set it instantly
safeSetWallet(messageSenderSignature, publicKey, wallet);
@@ -130,7 +141,7 @@ contract VerificationGateway
function setPendingBLSKeyForWallet() public {
IWallet wallet = IWallet(msg.sender);
bytes32 existingHash = hashFromWallet[wallet];
bytes32 existingHash = hashFromWallet(wallet);
require(existingHash != bytes32(0), "VG: hash does not exist for caller");
if (
(pendingBLSPublicKeyTimeFromHash[existingHash] != 0) &&
@@ -299,7 +310,7 @@ contract VerificationGateway
address(walletProxyAdmin),
getInitializeData()
)));
updateWalletHashMappings(publicKeyHash, blsWallet);
updateWalletHashMappings(publicKey, blsWallet);
emit WalletCreated(
address(blsWallet),
publicKey
@@ -328,29 +339,26 @@ contract VerificationGateway
blsLib.verifySingle(wallletAddressSignature, publicKey, addressMsg),
"VG: Signature not verified for wallet address."
);
bytes32 publicKeyHash = keccak256(abi.encodePacked(
publicKey
));
emit BLSKeySetForWallet(publicKey, wallet);
updateWalletHashMappings(publicKeyHash, wallet);
updateWalletHashMappings(publicKey, wallet);
}
/** @dev Only to be called on wallet creation, and in `safeSetWallet` */
function updateWalletHashMappings(
bytes32 publicKeyHash,
uint256[BLS_KEY_LEN] memory blsKey,
IWallet wallet
) private {
// remove reference from old hash
bytes32 oldHash = hashFromWallet[wallet];
bytes32 oldHash = hashFromWallet(wallet);
walletFromHash[oldHash] = IWallet(address(0));
// update new hash / wallet mappings
walletFromHash[publicKeyHash] = wallet;
hashFromWallet[wallet] = publicKeyHash;
walletFromHash[keccak256(abi.encodePacked(blsKey))] = wallet;
blsKeyFromWallet[wallet] = blsKey;
}
function getInitializeData() private view returns (bytes memory) {
return abi.encodeWithSignature("initialize(address)", address(this));
return abi.encodeWithSignature("initialize(address,address)", address(this), address(0));
}
modifier onlyWallet(bytes32 hash) {
@@ -387,4 +395,49 @@ contract VerificationGateway
);
}
}
// --- <4337> ---
// These functions seem to exist to allow clients (aka nodes) to rely on logic defined on-chain
// so that they don't need to implement anything specific to any signature aggregation scheme.
// For our prototype we can do this specific implementation and make this work without these
// functions. Later, it might be necessary to add these for compatibility with clients relying
// on this generic technique.
// function validateUserOpSignature(
// UserOperation4337 calldata userOp,
// bool offChainSigCheck
// ) external view returns (
// bytes memory sigForUserOp,
// bytes memory sigForAggregation,
// bytes memory offChainSigInfo
// ) {}
// function aggregateSignatures(
// bytes[] calldata sigsForAggregation
// ) external view returns (bytes memory aggregatesSignature) {}
function validateSignatures(
UserOperation4337[] calldata userOps,
bytes calldata signature
) view external {
uint256[2][] memory messages = new uint256[2][](userOps.length);
uint256[BLS_KEY_LEN][] memory senderPublicKeys = new uint256[BLS_KEY_LEN][](userOps.length);
for (uint256 i = 0; i < userOps.length; i++) {
messages[i] = blsLib.hashToPoint(
BLS_DOMAIN,
abi.encode(userOps[i])
);
senderPublicKeys[i] = blsKeyFromWallet[IWallet(userOps[i].sender)];
}
bool verified = blsLib.verifyMultiple(
abi.decode(signature, (uint256[2])),
senderPublicKeys,
messages
);
require(verified, "VG: Sig not verified");
}
// --- </4337> ---
}

View File

@@ -17,7 +17,7 @@ interface IWallet {
bytes encodedFunction;
}
function initialize(address gateway) external;
function initialize(address gateway, address entryPoint) external;
function nonce() external returns (uint256);
function performOperation(

View File

@@ -0,0 +1,16 @@
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4 <0.9.0;
struct UserOperation4337 {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
uint256 callGasLimit;
uint256 verificationGasLimit;
uint256 preVerificationGas;
uint256 maxFeePerGas;
uint256 maxPriorityFeePerGas;
bytes paymasterAndData;
bytes signature;
}

View File

@@ -107,7 +107,7 @@ const config: HardhatUserConfig = {
hardhat: {
initialBaseFeePerGas: 0, // workaround from https://github.com/sc-forks/solidity-coverage/issues/652#issuecomment-896330136 . Remove when that issue is closed.
accounts,
blockGasLimit: 30_000_000,
blockGasLimit: 200_000_000,
},
gethDev: {
url: `http://localhost:8545`,