mirror of
https://github.com/getwax/bls-wallet.git
synced 2026-01-11 14:57:56 -05:00
Compare commits
2 Commits
tx-data-en
...
4337-explo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92f6ec8467 | ||
|
|
2e68505b2e |
@@ -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(
|
||||
|
||||
@@ -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");
|
||||
_;
|
||||
|
||||
@@ -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> ---
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
16
contracts/contracts/interfaces/UserOperation4337.sol
Normal file
16
contracts/contracts/interfaces/UserOperation4337.sol
Normal 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;
|
||||
}
|
||||
@@ -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`,
|
||||
|
||||
Reference in New Issue
Block a user