2 Commits

Author SHA1 Message Date
Andrew Morris
eaa09dc917 Reduce gas by pruning recovery 2022-09-21 13:25:59 +10:00
Andrew Morris
2e68505b2e Draft 2022-09-20 15:54:02 +10:00
9 changed files with 767 additions and 662 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,148 +101,158 @@ contract VerificationGateway
require(verified, "VG: Sig not verified");
}
/**
If an existing wallet contract wishes to be called by this verification
gateway, it can directly register itself with a simple signed msg.
NB: this is independent of the proxyAdmin, and if desired can be changed
via the corresponding call.
@dev overrides previous wallet address registered with the given public key
@param messageSenderSignature signature of message containing only the calling address
@param publicKey that signed the caller's address
*/
function setBLSKeyForWallet(
uint256[2] memory messageSenderSignature,
uint256[BLS_KEY_LEN] memory publicKey
) public {
require(blsLib.isZeroBLSKey(publicKey) == false, "VG: publicKey must be non-zero");
IWallet wallet = IWallet(msg.sender);
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);
}
else { // wallet already has a key registered, set after delay
pendingMessageSenderSignatureFromHash[existingHash] = messageSenderSignature;
pendingBLSPublicKeyFromHash[existingHash] = publicKey;
pendingBLSPublicKeyTimeFromHash[existingHash] = block.timestamp + 604800; // 1 week from now
emit PendingBLSKeySet(existingHash, publicKey);
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));
}
function setPendingBLSKeyForWallet() public {
IWallet wallet = IWallet(msg.sender);
bytes32 existingHash = hashFromWallet[wallet];
require(existingHash != bytes32(0), "VG: hash does not exist for caller");
if (
(pendingBLSPublicKeyTimeFromHash[existingHash] != 0) &&
(block.timestamp > pendingBLSPublicKeyTimeFromHash[existingHash])
) {
safeSetWallet(
pendingMessageSenderSignatureFromHash[existingHash],
pendingBLSPublicKeyFromHash[existingHash],
wallet
);
pendingMessageSenderSignatureFromHash[existingHash] = [0,0];
pendingBLSPublicKeyTimeFromHash[existingHash] = 0;
pendingBLSPublicKeyFromHash[existingHash] = [0,0,0,0];
}
}
// /**
// If an existing wallet contract wishes to be called by this verification
// gateway, it can directly register itself with a simple signed msg.
// NB: this is independent of the proxyAdmin, and if desired can be changed
// via the corresponding call.
// @dev overrides previous wallet address registered with the given public key
// @param messageSenderSignature signature of message containing only the calling address
// @param publicKey that signed the caller's address
// */
// function setBLSKeyForWallet(
// uint256[2] memory messageSenderSignature,
// uint256[BLS_KEY_LEN] memory publicKey
// ) public {
// require(blsLib.isZeroBLSKey(publicKey) == false, "VG: publicKey must be non-zero");
// IWallet wallet = IWallet(msg.sender);
// 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);
// }
// else { // wallet already has a key registered, set after delay
// pendingMessageSenderSignatureFromHash[existingHash] = messageSenderSignature;
// pendingBLSPublicKeyFromHash[existingHash] = publicKey;
// pendingBLSPublicKeyTimeFromHash[existingHash] = block.timestamp + 604800; // 1 week from now
// emit PendingBLSKeySet(existingHash, publicKey);
// }
// }
/**
Calls to proxy admin, exclusively from a wallet. Must be called twice.
Once to set the function in the wallet as pending, then again after the recovery time.
@param hash calling wallet's bls public key hash
@param encodedFunction the selector and params to call (first encoded param must be calling wallet)
*/
function walletAdminCall(
bytes32 hash,
bytes memory encodedFunction
) public onlyWallet(hash) {
IWallet wallet = walletFromHash[hash];
// function setPendingBLSKeyForWallet() public {
// IWallet wallet = IWallet(msg.sender);
// bytes32 existingHash = hashFromWallet(wallet);
// require(existingHash != bytes32(0), "VG: hash does not exist for caller");
// if (
// (pendingBLSPublicKeyTimeFromHash[existingHash] != 0) &&
// (block.timestamp > pendingBLSPublicKeyTimeFromHash[existingHash])
// ) {
// safeSetWallet(
// pendingMessageSenderSignatureFromHash[existingHash],
// pendingBLSPublicKeyFromHash[existingHash],
// wallet
// );
// pendingMessageSenderSignatureFromHash[existingHash] = [0,0];
// pendingBLSPublicKeyTimeFromHash[existingHash] = 0;
// pendingBLSPublicKeyFromHash[existingHash] = [0,0,0,0];
// }
// }
// ensure first parameter is the calling wallet address
bytes memory encodedAddress = abi.encode(address(wallet));
uint8 selectorOffset = 4;
for (uint256 i=0; i<32; i++) {
require(
(encodedFunction[selectorOffset+i] == encodedAddress[i]),
"VG: first param to proxy admin is not calling wallet"
);
}
// /**
// Calls to proxy admin, exclusively from a wallet. Must be called twice.
// Once to set the function in the wallet as pending, then again after the recovery time.
// @param hash calling wallet's bls public key hash
// @param encodedFunction the selector and params to call (first encoded param must be calling wallet)
// */
// function walletAdminCall(
// bytes32 hash,
// bytes memory encodedFunction
// ) public onlyWallet(hash) {
// IWallet wallet = walletFromHash[hash];
wallet.setAnyPending();
// // ensure first parameter is the calling wallet address
// bytes memory encodedAddress = abi.encode(address(wallet));
// uint8 selectorOffset = 4;
// for (uint256 i=0; i<32; i++) {
// require(
// (encodedFunction[selectorOffset+i] == encodedAddress[i]),
// "VG: first param to proxy admin is not calling wallet"
// );
// }
// ensure wallet has pre-approved encodedFunction
bytes32 approvedFunctionHash = wallet.approvedProxyAdminFunctionHash();
bytes32 encodedFunctionHash = keccak256(encodedFunction);
bool matchesApproved = encodedFunctionHash == approvedFunctionHash;
// wallet.setAnyPending();
if (matchesApproved == false) {
// prepare for a future call
wallet.setProxyAdminFunctionHash(encodedFunctionHash);
}
else {
// call approved function
(bool success, ) = address(walletProxyAdmin).call(encodedFunction);
require(success, "VG: call to proxy admin failed");
wallet.clearApprovedProxyAdminFunctionHash();
}
}
// // ensure wallet has pre-approved encodedFunction
// bytes32 approvedFunctionHash = wallet.approvedProxyAdminFunctionHash();
// bytes32 encodedFunctionHash = keccak256(encodedFunction);
// bool matchesApproved = encodedFunctionHash == approvedFunctionHash;
/**
Recovers a wallet, setting a new bls public key.
@param walletAddressSignature signature of message containing only the wallet address
@param blsKeyHash calling wallet's bls public key hash
@param salt used in the recovery hash
@param newBLSKey to set as the wallet's bls public key
*/
function recoverWallet(
uint256[2] memory walletAddressSignature,
bytes32 blsKeyHash,
bytes32 salt,
uint256[BLS_KEY_LEN] memory newBLSKey
) public {
IWallet wallet = walletFromHash[blsKeyHash];
bytes32 recoveryHash = keccak256(
abi.encodePacked(msg.sender, blsKeyHash, salt)
);
if (recoveryHash == wallet.recoveryHash()) {
safeSetWallet(walletAddressSignature, newBLSKey, wallet);
wallet.recover();
}
}
// if (matchesApproved == false) {
// // prepare for a future call
// wallet.setProxyAdminFunctionHash(encodedFunctionHash);
// }
// else {
// // call approved function
// (bool success, ) = address(walletProxyAdmin).call(encodedFunction);
// require(success, "VG: call to proxy admin failed");
// wallet.clearApprovedProxyAdminFunctionHash();
// }
// }
/**
Wallet can migrate to a new gateway, eg additional signature support
*/
function setTrustedBLSGateway(
bytes32 hash,
address blsGateway
) public onlyWallet(hash) {
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(blsGateway) }
require(
(blsGateway != address(0)) && (size > 0),
"BLSWallet: gateway address param not valid"
);
// /**
// Recovers a wallet, setting a new bls public key.
// @param walletAddressSignature signature of message containing only the wallet address
// @param blsKeyHash calling wallet's bls public key hash
// @param salt used in the recovery hash
// @param newBLSKey to set as the wallet's bls public key
// */
// function recoverWallet(
// uint256[2] memory walletAddressSignature,
// bytes32 blsKeyHash,
// bytes32 salt,
// uint256[BLS_KEY_LEN] memory newBLSKey
// ) public {
// IWallet wallet = walletFromHash[blsKeyHash];
// bytes32 recoveryHash = keccak256(
// abi.encodePacked(msg.sender, blsKeyHash, salt)
// );
// if (recoveryHash == wallet.recoveryHash()) {
// safeSetWallet(walletAddressSignature, newBLSKey, wallet);
// wallet.recover();
// }
// }
IWallet wallet = walletFromHash[hash];
// /**
// Wallet can migrate to a new gateway, eg additional signature support
// */
// function setTrustedBLSGateway(
// bytes32 hash,
// address blsGateway
// ) public onlyWallet(hash) {
// uint256 size;
// // solhint-disable-next-line no-inline-assembly
// assembly { size := extcodesize(blsGateway) }
// require(
// (blsGateway != address(0)) && (size > 0),
// "BLSWallet: gateway address param not valid"
// );
require(
VerificationGateway(blsGateway).walletFromHash(hash) == wallet,
"Not recognized"
);
// IWallet wallet = walletFromHash[hash];
// getProxyAdmin fails if not called by the current proxy admin, so this
// enforces that the wallet's proxy admin matches the one in the new
// gateway.
VerificationGateway(blsGateway).walletProxyAdmin().getProxyAdmin(
TransparentUpgradeableProxy(payable(address(wallet)))
);
// require(
// VerificationGateway(blsGateway).walletFromHash(hash) == wallet,
// "Not recognized"
// );
wallet.setTrustedGateway(blsGateway);
}
// // getProxyAdmin fails if not called by the current proxy admin, so this
// // enforces that the wallet's proxy admin matches the one in the new
// // gateway.
// VerificationGateway(blsGateway).walletProxyAdmin().getProxyAdmin(
// TransparentUpgradeableProxy(payable(address(wallet)))
// );
// wallet.setTrustedGateway(blsGateway);
// }
/**
Base function for verifying and processing BLS-signed transactions.
@@ -299,7 +310,7 @@ contract VerificationGateway
address(walletProxyAdmin),
getInitializeData()
)));
updateWalletHashMappings(publicKeyHash, blsWallet);
updateWalletHashMappings(publicKey, blsWallet);
emit WalletCreated(
address(blsWallet),
publicKey
@@ -328,29 +339,30 @@ 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) // TODO: 4337 EntryPoint address
);
}
modifier onlyWallet(bytes32 hash) {
@@ -387,4 +399,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`,

View File

@@ -1,251 +1,251 @@
import { expect } from "chai";
import { BigNumber } from "ethers";
import { solidityPack } from "ethers/lib/utils";
import { ethers, network } from "hardhat";
// import { expect } from "chai";
// import { BigNumber } from "ethers";
// import { solidityPack } from "ethers/lib/utils";
// import { ethers, network } from "hardhat";
import { BlsWalletWrapper, Signature } from "../clients/src";
import Fixture from "../shared/helpers/Fixture";
import deployAndRunPrecompileCostEstimator from "../shared/helpers/deployAndRunPrecompileCostEstimator";
import { defaultDeployerAddress } from "../shared/helpers/deployDeployer";
import { BLSWallet, VerificationGateway } from "../typechain";
// import { BlsWalletWrapper, Signature } from "../clients/src";
// import Fixture from "../shared/helpers/Fixture";
// import deployAndRunPrecompileCostEstimator from "../shared/helpers/deployAndRunPrecompileCostEstimator";
// import { defaultDeployerAddress } from "../shared/helpers/deployDeployer";
// import { BLSWallet, VerificationGateway } from "../typechain";
const signWalletAddress = async (
fx: Fixture,
senderAddr: string,
signerPrivKey: string,
): Promise<Signature> => {
const addressMessage = solidityPack(["address"], [senderAddr]);
const wallet = await BlsWalletWrapper.connect(
signerPrivKey,
fx.verificationGateway.address,
fx.provider,
);
return wallet.signMessage(addressMessage);
};
// const signWalletAddress = async (
// fx: Fixture,
// senderAddr: string,
// signerPrivKey: string,
// ): Promise<Signature> => {
// const addressMessage = solidityPack(["address"], [senderAddr]);
// const wallet = await BlsWalletWrapper.connect(
// signerPrivKey,
// fx.verificationGateway.address,
// fx.provider,
// );
// return wallet.signMessage(addressMessage);
// };
describe("Recovery", async function () {
this.beforeAll(async function () {
// deploy the deployer contract for the transient hardhat network
if (network.name === "hardhat") {
// fund deployer wallet address
const fundedSigner = (await ethers.getSigners())[0];
await (
await fundedSigner.sendTransaction({
to: defaultDeployerAddress(),
value: ethers.utils.parseEther("1"),
})
).wait();
// describe("Recovery", async function () {
// this.beforeAll(async function () {
// // deploy the deployer contract for the transient hardhat network
// if (network.name === "hardhat") {
// // fund deployer wallet address
// const fundedSigner = (await ethers.getSigners())[0];
// await (
// await fundedSigner.sendTransaction({
// to: defaultDeployerAddress(),
// value: ethers.utils.parseEther("1"),
// })
// ).wait();
// deploy the precompile contract (via deployer)
console.log("PCE:", await deployAndRunPrecompileCostEstimator());
}
});
// // deploy the precompile contract (via deployer)
// console.log("PCE:", await deployAndRunPrecompileCostEstimator());
// }
// });
const safetyDelaySeconds = 7 * 24 * 60 * 60;
let fx: Fixture;
let vg: VerificationGateway;
let wallet1: BlsWalletWrapper;
let wallet2: BlsWalletWrapper;
let walletAttacker: BlsWalletWrapper;
let blsWallet: BLSWallet;
let recoverySigner;
let hash1, hash2, hashAttacker;
let salt;
let recoveryHash;
beforeEach(async function () {
fx = await Fixture.create();
vg = fx.verificationGateway;
// const safetyDelaySeconds = 7 * 24 * 60 * 60;
// let fx: Fixture;
// let vg: VerificationGateway;
// let wallet1: BlsWalletWrapper;
// let wallet2: BlsWalletWrapper;
// let walletAttacker: BlsWalletWrapper;
// let blsWallet: BLSWallet;
// let recoverySigner;
// let hash1, hash2, hashAttacker;
// let salt;
// let recoveryHash;
// beforeEach(async function () {
// fx = await Fixture.create();
// vg = fx.verificationGateway;
wallet1 = await fx.lazyBlsWallets[0]();
wallet2 = await fx.lazyBlsWallets[1]();
walletAttacker = await fx.lazyBlsWallets[2]();
blsWallet = await ethers.getContractAt("BLSWallet", wallet1.address);
recoverySigner = (await ethers.getSigners())[1];
// wallet1 = await fx.lazyBlsWallets[0]();
// wallet2 = await fx.lazyBlsWallets[1]();
// walletAttacker = await fx.lazyBlsWallets[2]();
// blsWallet = await ethers.getContractAt("BLSWallet", wallet1.address);
// recoverySigner = (await ethers.getSigners())[1];
hash1 = wallet1.blsWalletSigner.getPublicKeyHash(wallet1.privateKey);
hash2 = wallet2.blsWalletSigner.getPublicKeyHash(wallet2.privateKey);
hashAttacker = wallet2.blsWalletSigner.getPublicKeyHash(
walletAttacker.privateKey,
);
salt = "0x1234567812345678123456781234567812345678123456781234567812345678";
recoveryHash = ethers.utils.solidityKeccak256(
["address", "bytes32", "bytes32"],
[recoverySigner.address, hash1, salt],
);
});
// hash1 = wallet1.blsWalletSigner.getPublicKeyHash(wallet1.privateKey);
// hash2 = wallet2.blsWalletSigner.getPublicKeyHash(wallet2.privateKey);
// hashAttacker = wallet2.blsWalletSigner.getPublicKeyHash(
// walletAttacker.privateKey,
// );
// salt = "0x1234567812345678123456781234567812345678123456781234567812345678";
// recoveryHash = ethers.utils.solidityKeccak256(
// ["address", "bytes32", "bytes32"],
// [recoverySigner.address, hash1, salt],
// );
// });
it("should update bls key", async function () {
expect(await vg.hashFromWallet(wallet1.address)).to.eql(hash1);
// it("should update bls key", async function () {
// expect(await vg.hashFromWallet(wallet1.address)).to.eql(hash1);
const addressSignature = await signWalletAddress(
fx,
wallet1.address,
wallet2.privateKey,
);
// const addressSignature = await signWalletAddress(
// fx,
// wallet1.address,
// wallet2.privateKey,
// );
await fx.call(
wallet1,
vg,
"setBLSKeyForWallet",
[addressSignature, wallet2.PublicKey()],
1,
);
// await fx.call(
// wallet1,
// vg,
// "setBLSKeyForWallet",
// [addressSignature, wallet2.PublicKey()],
// 1,
// );
await fx.advanceTimeBy(safetyDelaySeconds + 1);
await fx.call(wallet1, vg, "setPendingBLSKeyForWallet", [], 2);
// await fx.advanceTimeBy(safetyDelaySeconds + 1);
// await fx.call(wallet1, vg, "setPendingBLSKeyForWallet", [], 2);
expect(await vg.hashFromWallet(wallet1.address)).to.eql(hash2);
});
// expect(await vg.hashFromWallet(wallet1.address)).to.eql(hash2);
// });
it("should NOT override public key hash after creation", async function () {
let walletForHash = await vg.walletFromHash(hash1);
expect(BigNumber.from(walletForHash)).to.not.equal(BigNumber.from(0));
expect(walletForHash).to.equal(wallet1.address);
// it("should NOT override public key hash after creation", async function () {
// let walletForHash = await vg.walletFromHash(hash1);
// expect(BigNumber.from(walletForHash)).to.not.equal(BigNumber.from(0));
// expect(walletForHash).to.equal(wallet1.address);
let hashFromWallet = await vg.hashFromWallet(wallet1.address);
expect(BigNumber.from(hashFromWallet)).to.not.equal(BigNumber.from(0));
expect(hashFromWallet).to.equal(hash1);
// let hashFromWallet = await vg.hashFromWallet(wallet1.address);
// expect(BigNumber.from(hashFromWallet)).to.not.equal(BigNumber.from(0));
// expect(hashFromWallet).to.equal(hash1);
await fx.call(wallet1, vg, "setPendingBLSKeyForWallet", [], 1);
// await fx.call(wallet1, vg, "setPendingBLSKeyForWallet", [], 1);
walletForHash = await vg.walletFromHash(hash1);
expect(walletForHash).to.equal(wallet1.address);
// walletForHash = await vg.walletFromHash(hash1);
// expect(walletForHash).to.equal(wallet1.address);
hashFromWallet = await vg.hashFromWallet(wallet1.address);
expect(hashFromWallet).to.equal(hash1);
});
// hashFromWallet = await vg.hashFromWallet(wallet1.address);
// expect(hashFromWallet).to.equal(hash1);
// });
it("should set recovery hash", async function () {
// set instantly from 0 value
await fx.call(wallet1, blsWallet, "setRecoveryHash", [recoveryHash], 1);
expect(await blsWallet.recoveryHash()).to.equal(recoveryHash);
// it("should set recovery hash", async function () {
// // set instantly from 0 value
// await fx.call(wallet1, blsWallet, "setRecoveryHash", [recoveryHash], 1);
// expect(await blsWallet.recoveryHash()).to.equal(recoveryHash);
// new value set after delay from non-zero value
salt = "0x" + "AB".repeat(32);
const newRecoveryHash = ethers.utils.solidityKeccak256(
["address", "bytes32", "bytes32"],
[recoverySigner.address, hash1, salt],
);
await fx.call(wallet1, blsWallet, "setRecoveryHash", [newRecoveryHash], 2);
expect(await blsWallet.recoveryHash()).to.equal(recoveryHash);
await fx.advanceTimeBy(safetyDelaySeconds + 1);
await (await blsWallet.setAnyPending()).wait();
expect(await blsWallet.recoveryHash()).to.equal(newRecoveryHash);
});
// // new value set after delay from non-zero value
// salt = "0x" + "AB".repeat(32);
// const newRecoveryHash = ethers.utils.solidityKeccak256(
// ["address", "bytes32", "bytes32"],
// [recoverySigner.address, hash1, salt],
// );
// await fx.call(wallet1, blsWallet, "setRecoveryHash", [newRecoveryHash], 2);
// expect(await blsWallet.recoveryHash()).to.equal(recoveryHash);
// await fx.advanceTimeBy(safetyDelaySeconds + 1);
// await (await blsWallet.setAnyPending()).wait();
// expect(await blsWallet.recoveryHash()).to.equal(newRecoveryHash);
// });
it("should recover before bls key update", async function () {
await fx.call(wallet1, blsWallet, "setRecoveryHash", [recoveryHash], 1);
// it("should recover before bls key update", async function () {
// await fx.call(wallet1, blsWallet, "setRecoveryHash", [recoveryHash], 1);
const attackSignature = await signWalletAddress(
fx,
wallet1.address,
walletAttacker.privateKey,
);
// const attackSignature = await signWalletAddress(
// fx,
// wallet1.address,
// walletAttacker.privateKey,
// );
// Attacker assumed to have compromised wallet1 bls key, and wishes to reset
// the gateway wallet's bls key to their own.
await fx.call(
wallet1,
vg,
"setBLSKeyForWallet",
[attackSignature, walletAttacker.PublicKey()],
1,
);
// // Attacker assumed to have compromised wallet1 bls key, and wishes to reset
// // the gateway wallet's bls key to their own.
// await fx.call(
// wallet1,
// vg,
// "setBLSKeyForWallet",
// [attackSignature, walletAttacker.PublicKey()],
// 1,
// );
await fx.advanceTimeBy(safetyDelaySeconds / 2); // wait half the time
await fx.call(wallet1, vg, "setPendingBLSKeyForWallet", [], 2);
// await fx.advanceTimeBy(safetyDelaySeconds / 2); // wait half the time
// await fx.call(wallet1, vg, "setPendingBLSKeyForWallet", [], 2);
const addressSignature = await signWalletAddress(
fx,
wallet1.address,
wallet2.privateKey,
);
const safeKey = wallet2.PublicKey();
// const addressSignature = await signWalletAddress(
// fx,
// wallet1.address,
// wallet2.privateKey,
// );
// const safeKey = wallet2.PublicKey();
await (
await fx.verificationGateway
.connect(recoverySigner)
.recoverWallet(addressSignature, hash1, salt, safeKey)
).wait();
// await (
// await fx.verificationGateway
// .connect(recoverySigner)
// .recoverWallet(addressSignature, hash1, salt, safeKey)
// ).wait();
// key reset via recovery
expect(await vg.hashFromWallet(wallet1.address)).to.eql(hash2);
expect(await vg.walletFromHash(hash2)).to.eql(wallet1.address);
// // key reset via recovery
// expect(await vg.hashFromWallet(wallet1.address)).to.eql(hash2);
// expect(await vg.walletFromHash(hash2)).to.eql(wallet1.address);
await fx.advanceTimeBy(safetyDelaySeconds / 2 + 1); // wait remainder the time
// await fx.advanceTimeBy(safetyDelaySeconds / 2 + 1); // wait remainder the time
// check attacker's key not set after waiting full safety delay
await fx.call(
walletAttacker,
vg,
"setPendingBLSKeyForWallet",
[],
await walletAttacker.Nonce(),
);
await fx.call(
wallet2,
vg,
"setPendingBLSKeyForWallet",
[],
await wallet2.Nonce(),
);
// // check attacker's key not set after waiting full safety delay
// await fx.call(
// walletAttacker,
// vg,
// "setPendingBLSKeyForWallet",
// [],
// await walletAttacker.Nonce(),
// );
// await fx.call(
// wallet2,
// vg,
// "setPendingBLSKeyForWallet",
// [],
// await wallet2.Nonce(),
// );
expect(await vg.walletFromHash(hash1)).to.not.equal(blsWallet.address);
expect(await vg.walletFromHash(hashAttacker)).to.not.equal(
blsWallet.address,
);
expect(await vg.walletFromHash(hash2)).to.equal(blsWallet.address);
// expect(await vg.walletFromHash(hash1)).to.not.equal(blsWallet.address);
// expect(await vg.walletFromHash(hashAttacker)).to.not.equal(
// blsWallet.address,
// );
// expect(await vg.walletFromHash(hash2)).to.equal(blsWallet.address);
// // verify recovered bls key can successfully call wallet-only function (eg setTrustedGateway)
const res = await fx.callStatic(
wallet2,
fx.verificationGateway,
"setTrustedBLSGateway",
[hash2, fx.verificationGateway.address],
3,
);
expect(res.successes[0]).to.equal(true);
});
// // // verify recovered bls key can successfully call wallet-only function (eg setTrustedGateway)
// const res = await fx.callStatic(
// wallet2,
// fx.verificationGateway,
// "setTrustedBLSGateway",
// [hash2, fx.verificationGateway.address],
// 3,
// );
// expect(res.successes[0]).to.equal(true);
// });
// https://github.com/jzaki/bls-wallet/issues/141
it("should NOT be able to recover to another wallet", async function () {
const attackerWalletContract = await ethers.getContractAt(
"BLSWallet",
walletAttacker.address,
);
// // https://github.com/jzaki/bls-wallet/issues/141
// it("should NOT be able to recover to another wallet", async function () {
// const attackerWalletContract = await ethers.getContractAt(
// "BLSWallet",
// walletAttacker.address,
// );
// Attacker users recovery signer to set their recovery hash
const attackerRecoveryHash = ethers.utils.solidityKeccak256(
["address", "bytes32", "bytes32"],
[recoverySigner.address, hashAttacker, salt],
);
await fx.call(
walletAttacker,
attackerWalletContract,
"setRecoveryHash",
[attackerRecoveryHash],
1,
);
// // Attacker users recovery signer to set their recovery hash
// const attackerRecoveryHash = ethers.utils.solidityKeccak256(
// ["address", "bytes32", "bytes32"],
// [recoverySigner.address, hashAttacker, salt],
// );
// await fx.call(
// walletAttacker,
// attackerWalletContract,
// "setRecoveryHash",
// [attackerRecoveryHash],
// 1,
// );
// Attacker waits out safety delay
await fx.advanceTimeBy(safetyDelaySeconds + 1);
await (await attackerWalletContract.setAnyPending()).wait();
expect(await attackerWalletContract.recoveryHash()).to.equal(
attackerRecoveryHash,
);
// // Attacker waits out safety delay
// await fx.advanceTimeBy(safetyDelaySeconds + 1);
// await (await attackerWalletContract.setAnyPending()).wait();
// expect(await attackerWalletContract.recoveryHash()).to.equal(
// attackerRecoveryHash,
// );
const addressSignature = await signWalletAddress(
fx,
walletAttacker.address,
walletAttacker.privateKey,
);
const wallet1Key = await wallet1.PublicKey();
// const addressSignature = await signWalletAddress(
// fx,
// walletAttacker.address,
// walletAttacker.privateKey,
// );
// const wallet1Key = await wallet1.PublicKey();
// Attacker attempts to overwrite wallet 1's hash in the gateway and fails
await expect(
fx.verificationGateway
.connect(recoverySigner)
.recoverWallet(addressSignature, hashAttacker, salt, wallet1Key),
).to.be.rejectedWith("VG: Signature not verified for wallet address");
});
});
// // Attacker attempts to overwrite wallet 1's hash in the gateway and fails
// await expect(
// fx.verificationGateway
// .connect(recoverySigner)
// .recoverWallet(addressSignature, hashAttacker, salt, wallet1Key),
// ).to.be.rejectedWith("VG: Signature not verified for wallet address");
// });
// });

View File

@@ -1,350 +1,350 @@
import { expect } from "chai";
import { BigNumber } from "ethers";
import { solidityPack } from "ethers/lib/utils";
import { ethers, network } from "hardhat";
// import { expect } from "chai";
// import { BigNumber } from "ethers";
// import { solidityPack } from "ethers/lib/utils";
// import { ethers, network } from "hardhat";
import { BLSOpen, ProxyAdmin } from "../typechain";
import { ActionData, BlsWalletWrapper } from "../clients/src";
import Fixture from "../shared/helpers/Fixture";
import deployAndRunPrecompileCostEstimator from "../shared/helpers/deployAndRunPrecompileCostEstimator";
import { defaultDeployerAddress } from "../shared/helpers/deployDeployer";
import {
proxyAdminBundle,
proxyAdminCall,
} from "../shared/helpers/callProxyAdmin";
import Create2Fixture from "../shared/helpers/Create2Fixture";
// import { BLSOpen, ProxyAdmin } from "../typechain";
// import { ActionData, BlsWalletWrapper } from "../clients/src";
// import Fixture from "../shared/helpers/Fixture";
// import deployAndRunPrecompileCostEstimator from "../shared/helpers/deployAndRunPrecompileCostEstimator";
// import { defaultDeployerAddress } from "../shared/helpers/deployDeployer";
// import {
// proxyAdminBundle,
// proxyAdminCall,
// } from "../shared/helpers/callProxyAdmin";
// import Create2Fixture from "../shared/helpers/Create2Fixture";
describe("Upgrade", async function () {
this.beforeAll(async function () {
// deploy the deployer contract for the transient hardhat network
if (network.name === "hardhat") {
// fund deployer wallet address
const fundedSigner = (await ethers.getSigners())[0];
await (
await fundedSigner.sendTransaction({
to: defaultDeployerAddress(),
value: ethers.utils.parseEther("1"),
})
).wait();
// describe("Upgrade", async function () {
// this.beforeAll(async function () {
// // deploy the deployer contract for the transient hardhat network
// if (network.name === "hardhat") {
// // fund deployer wallet address
// const fundedSigner = (await ethers.getSigners())[0];
// await (
// await fundedSigner.sendTransaction({
// to: defaultDeployerAddress(),
// value: ethers.utils.parseEther("1"),
// })
// ).wait();
// deploy the precompile contract (via deployer)
console.log("PCE:", await deployAndRunPrecompileCostEstimator());
}
});
// // deploy the precompile contract (via deployer)
// console.log("PCE:", await deployAndRunPrecompileCostEstimator());
// }
// });
const safetyDelaySeconds = 7 * 24 * 60 * 60;
let fx: Fixture;
beforeEach(async function () {
fx = await Fixture.create();
});
// const safetyDelaySeconds = 7 * 24 * 60 * 60;
// let fx: Fixture;
// beforeEach(async function () {
// fx = await Fixture.create();
// });
it("should upgrade wallet contract", async function () {
const MockWalletUpgraded = await ethers.getContractFactory(
"MockWalletUpgraded",
);
const mockWalletUpgraded = await MockWalletUpgraded.deploy();
// it("should upgrade wallet contract", async function () {
// const MockWalletUpgraded = await ethers.getContractFactory(
// "MockWalletUpgraded",
// );
// const mockWalletUpgraded = await MockWalletUpgraded.deploy();
const wallet = await fx.lazyBlsWallets[0]();
// const wallet = await fx.lazyBlsWallets[0]();
// prepare call
await proxyAdminCall(fx, wallet, "upgrade", [
wallet.address,
mockWalletUpgraded.address,
]);
// // prepare call
// await proxyAdminCall(fx, wallet, "upgrade", [
// wallet.address,
// mockWalletUpgraded.address,
// ]);
// Advance time one week
const latestTimestamp = (await ethers.provider.getBlock("latest"))
.timestamp;
await network.provider.send("evm_setNextBlockTimestamp", [
BigNumber.from(latestTimestamp)
.add(safetyDelaySeconds + 1)
.toHexString(),
]);
// // Advance time one week
// const latestTimestamp = (await ethers.provider.getBlock("latest"))
// .timestamp;
// await network.provider.send("evm_setNextBlockTimestamp", [
// BigNumber.from(latestTimestamp)
// .add(safetyDelaySeconds + 1)
// .toHexString(),
// ]);
// make call
await proxyAdminCall(fx, wallet, "upgrade", [
wallet.address,
mockWalletUpgraded.address,
]);
// // make call
// await proxyAdminCall(fx, wallet, "upgrade", [
// wallet.address,
// mockWalletUpgraded.address,
// ]);
const newBLSWallet = MockWalletUpgraded.attach(wallet.address);
await (await newBLSWallet.setNewData(wallet.address)).wait();
expect(await newBLSWallet.newData()).to.equal(wallet.address);
});
// const newBLSWallet = MockWalletUpgraded.attach(wallet.address);
// await (await newBLSWallet.setNewData(wallet.address)).wait();
// expect(await newBLSWallet.newData()).to.equal(wallet.address);
// });
it("should register with new verification gateway", async function () {
// Deploy new verification gateway
const create2Fixture = Create2Fixture.create();
const bls = (await create2Fixture.create2Contract("BLSOpen")) as BLSOpen;
const ProxyAdmin = await ethers.getContractFactory("ProxyAdmin");
const proxyAdmin2 = (await ProxyAdmin.deploy()) as ProxyAdmin;
await proxyAdmin2.deployed();
// it("should register with new verification gateway", async function () {
// // Deploy new verification gateway
// const create2Fixture = Create2Fixture.create();
// const bls = (await create2Fixture.create2Contract("BLSOpen")) as BLSOpen;
// const ProxyAdmin = await ethers.getContractFactory("ProxyAdmin");
// const proxyAdmin2 = (await ProxyAdmin.deploy()) as ProxyAdmin;
// await proxyAdmin2.deployed();
const blsWalletImpl = await create2Fixture.create2Contract("BLSWallet");
const VerificationGateway = await ethers.getContractFactory(
"VerificationGateway",
);
const vg2 = await VerificationGateway.deploy(
bls.address,
blsWalletImpl.address,
proxyAdmin2.address,
);
await (await proxyAdmin2.transferOwnership(vg2.address)).wait();
// const blsWalletImpl = await create2Fixture.create2Contract("BLSWallet");
// const VerificationGateway = await ethers.getContractFactory(
// "VerificationGateway",
// );
// const vg2 = await VerificationGateway.deploy(
// bls.address,
// blsWalletImpl.address,
// proxyAdmin2.address,
// );
// await (await proxyAdmin2.transferOwnership(vg2.address)).wait();
// Recreate hubble bls signer
const walletOldVg = await fx.lazyBlsWallets[0]();
const walletAddress = walletOldVg.address;
const blsSecret = walletOldVg.privateKey;
// // Recreate hubble bls signer
// const walletOldVg = await fx.lazyBlsWallets[0]();
// const walletAddress = walletOldVg.address;
// const blsSecret = walletOldVg.privateKey;
const wallet = await BlsWalletWrapper.connect(
blsSecret,
fx.verificationGateway.address,
fx.provider,
);
// Sign simple address message
const addressMessage = solidityPack(["address"], [walletAddress]);
const addressSignature = wallet.signMessage(addressMessage);
// const wallet = await BlsWalletWrapper.connect(
// blsSecret,
// fx.verificationGateway.address,
// fx.provider,
// );
// // Sign simple address message
// const addressMessage = solidityPack(["address"], [walletAddress]);
// const addressSignature = wallet.signMessage(addressMessage);
const proxyAdmin2Address = await vg2.walletProxyAdmin();
// Get admin action to change proxy
const bundle = await proxyAdminBundle(fx, walletOldVg, "changeProxyAdmin", [
walletAddress,
proxyAdmin2Address,
]);
const changeProxyAction = bundle.operations[0].actions[0];
// const proxyAdmin2Address = await vg2.walletProxyAdmin();
// // Get admin action to change proxy
// const bundle = await proxyAdminBundle(fx, walletOldVg, "changeProxyAdmin", [
// walletAddress,
// proxyAdmin2Address,
// ]);
// const changeProxyAction = bundle.operations[0].actions[0];
// prepare call
await proxyAdminCall(fx, walletOldVg, "changeProxyAdmin", [
walletAddress,
proxyAdmin2Address,
]);
// // prepare call
// await proxyAdminCall(fx, walletOldVg, "changeProxyAdmin", [
// walletAddress,
// proxyAdmin2Address,
// ]);
// Advance time one week
await fx.advanceTimeBy(safetyDelaySeconds + 1);
// // Advance time one week
// await fx.advanceTimeBy(safetyDelaySeconds + 1);
const hash = walletOldVg.blsWalletSigner.getPublicKeyHash(
walletOldVg.privateKey,
);
// const hash = walletOldVg.blsWalletSigner.getPublicKeyHash(
// walletOldVg.privateKey,
// );
const setExternalWalletAction: ActionData = {
ethValue: BigNumber.from(0),
contractAddress: vg2.address,
encodedFunction: vg2.interface.encodeFunctionData("setBLSKeyForWallet", [
addressSignature,
walletOldVg.PublicKey(),
]),
};
// const setExternalWalletAction: ActionData = {
// ethValue: BigNumber.from(0),
// contractAddress: vg2.address,
// encodedFunction: vg2.interface.encodeFunctionData("setBLSKeyForWallet", [
// addressSignature,
// walletOldVg.PublicKey(),
// ]),
// };
const setTrustedBLSGatewayAction: ActionData = {
ethValue: BigNumber.from(0),
contractAddress: fx.verificationGateway.address,
encodedFunction: fx.verificationGateway.interface.encodeFunctionData(
"setTrustedBLSGateway",
[hash, vg2.address],
),
};
// const setTrustedBLSGatewayAction: ActionData = {
// ethValue: BigNumber.from(0),
// contractAddress: fx.verificationGateway.address,
// encodedFunction: fx.verificationGateway.interface.encodeFunctionData(
// "setTrustedBLSGateway",
// [hash, vg2.address],
// ),
// };
// Upgrading the gateway requires these three steps:
// 1. register external wallet in vg2
// 2. change proxy admin to that in vg2
// 3. lastly, set wallet's new trusted gateway
//
// If (1) or (2) are skipped, then (3) should fail, and therefore the whole
// operation should fail.
// // Upgrading the gateway requires these three steps:
// // 1. register external wallet in vg2
// // 2. change proxy admin to that in vg2
// // 3. lastly, set wallet's new trusted gateway
// //
// // If (1) or (2) are skipped, then (3) should fail, and therefore the whole
// // operation should fail.
{
// Fail if setExternalWalletAction is skipped
// {
// // Fail if setExternalWalletAction is skipped
const { successes } =
await fx.verificationGateway.callStatic.processBundle(
walletOldVg.sign({
nonce: BigNumber.from(2),
actions: [
// skip: setExternalWalletAction,
changeProxyAction,
setTrustedBLSGatewayAction,
],
}),
);
// const { successes } =
// await fx.verificationGateway.callStatic.processBundle(
// walletOldVg.sign({
// nonce: BigNumber.from(2),
// actions: [
// // skip: setExternalWalletAction,
// changeProxyAction,
// setTrustedBLSGatewayAction,
// ],
// }),
// );
expect(successes).to.deep.equal([false]);
}
// expect(successes).to.deep.equal([false]);
// }
{
// Fail if changeProxyAction is skipped
// {
// // Fail if changeProxyAction is skipped
const { successes } =
await fx.verificationGateway.callStatic.processBundle(
walletOldVg.sign({
nonce: BigNumber.from(2),
actions: [
setExternalWalletAction,
// skip: changeProxyAction,
setTrustedBLSGatewayAction,
],
}),
);
// const { successes } =
// await fx.verificationGateway.callStatic.processBundle(
// walletOldVg.sign({
// nonce: BigNumber.from(2),
// actions: [
// setExternalWalletAction,
// // skip: changeProxyAction,
// setTrustedBLSGatewayAction,
// ],
// }),
// );
expect(successes).to.deep.equal([false]);
}
// expect(successes).to.deep.equal([false]);
// }
{
// Succeed if nothing is skipped
// {
// // Succeed if nothing is skipped
const { successes } =
await fx.verificationGateway.callStatic.processBundle(
walletOldVg.sign({
nonce: BigNumber.from(2),
actions: [
setExternalWalletAction,
changeProxyAction,
setTrustedBLSGatewayAction,
],
}),
);
// const { successes } =
// await fx.verificationGateway.callStatic.processBundle(
// walletOldVg.sign({
// nonce: BigNumber.from(2),
// actions: [
// setExternalWalletAction,
// changeProxyAction,
// setTrustedBLSGatewayAction,
// ],
// }),
// );
expect(successes).to.deep.equal([true]);
}
// expect(successes).to.deep.equal([true]);
// }
expect(await vg2.walletFromHash(hash)).not.to.equal(walletAddress);
// expect(await vg2.walletFromHash(hash)).not.to.equal(walletAddress);
// Now actually perform the upgrade so we can perform some more detailed
// checks.
await (
await fx.verificationGateway.processBundle(
fx.blsWalletSigner.aggregate([
walletOldVg.sign({
nonce: BigNumber.from(2),
actions: [
setExternalWalletAction,
changeProxyAction,
setTrustedBLSGatewayAction,
],
}),
]),
)
).wait();
// // Now actually perform the upgrade so we can perform some more detailed
// // checks.
// await (
// await fx.verificationGateway.processBundle(
// fx.blsWalletSigner.aggregate([
// walletOldVg.sign({
// nonce: BigNumber.from(2),
// actions: [
// setExternalWalletAction,
// changeProxyAction,
// setTrustedBLSGatewayAction,
// ],
// }),
// ]),
// )
// ).wait();
// Create required objects for data/contracts for checks
const proxyAdmin = await ethers.getContractAt(
"ProxyAdmin",
await vg2.walletProxyAdmin(),
);
// // Create required objects for data/contracts for checks
// const proxyAdmin = await ethers.getContractAt(
// "ProxyAdmin",
// await vg2.walletProxyAdmin(),
// );
// Direct checks corresponding to each action
expect(await vg2.walletFromHash(hash)).to.equal(walletAddress);
expect(await vg2.hashFromWallet(walletAddress)).to.equal(hash);
expect(await proxyAdmin.getProxyAdmin(walletAddress)).to.equal(
proxyAdmin.address,
);
// // Direct checks corresponding to each action
// expect(await vg2.walletFromHash(hash)).to.equal(walletAddress);
// expect(await vg2.hashFromWallet(walletAddress)).to.equal(hash);
// expect(await proxyAdmin.getProxyAdmin(walletAddress)).to.equal(
// proxyAdmin.address,
// );
const blsWallet = await ethers.getContractAt("BLSWallet", walletAddress);
// New verification gateway pending
expect(await blsWallet.trustedBLSGateway()).to.equal(
fx.verificationGateway.address,
);
// Advance time one week
await fx.advanceTimeBy(safetyDelaySeconds + 1);
// set pending
await (await blsWallet.setAnyPending()).wait();
// Check new verification gateway was set
expect(await blsWallet.trustedBLSGateway()).to.equal(vg2.address);
// const blsWallet = await ethers.getContractAt("BLSWallet", walletAddress);
// // New verification gateway pending
// expect(await blsWallet.trustedBLSGateway()).to.equal(
// fx.verificationGateway.address,
// );
// // Advance time one week
// await fx.advanceTimeBy(safetyDelaySeconds + 1);
// // set pending
// await (await blsWallet.setAnyPending()).wait();
// // Check new verification gateway was set
// expect(await blsWallet.trustedBLSGateway()).to.equal(vg2.address);
// Check new gateway has wallet via static call through new gateway
const bundleResult = await vg2.callStatic.processBundle(
fx.blsWalletSigner.aggregate([
walletOldVg.sign({
nonce: BigNumber.from(3),
actions: [
{
ethValue: 0,
contractAddress: vg2.address,
encodedFunction: vg2.interface.encodeFunctionData(
"walletFromHash",
[hash],
),
},
],
}),
]),
);
const walletFromHashAddress = ethers.utils.defaultAbiCoder.decode(
["address"],
bundleResult.results[0][0], // first and only operation/action result
)[0];
expect(walletFromHashAddress).to.equal(walletAddress);
});
// // Check new gateway has wallet via static call through new gateway
// const bundleResult = await vg2.callStatic.processBundle(
// fx.blsWalletSigner.aggregate([
// walletOldVg.sign({
// nonce: BigNumber.from(3),
// actions: [
// {
// ethValue: 0,
// contractAddress: vg2.address,
// encodedFunction: vg2.interface.encodeFunctionData(
// "walletFromHash",
// [hash],
// ),
// },
// ],
// }),
// ]),
// );
// const walletFromHashAddress = ethers.utils.defaultAbiCoder.decode(
// ["address"],
// bundleResult.results[0][0], // first and only operation/action result
// )[0];
// expect(walletFromHashAddress).to.equal(walletAddress);
// });
it("should change mapping of an address to hash", async function () {
const vg1 = fx.verificationGateway;
// it("should change mapping of an address to hash", async function () {
// const vg1 = fx.verificationGateway;
const lazyWallet1 = await fx.lazyBlsWallets[0]();
const lazyWallet2 = await fx.lazyBlsWallets[1]();
// const lazyWallet1 = await fx.lazyBlsWallets[0]();
// const lazyWallet2 = await fx.lazyBlsWallets[1]();
const wallet1 = await BlsWalletWrapper.connect(
lazyWallet1.privateKey,
vg1.address,
fx.provider,
);
// const wallet1 = await BlsWalletWrapper.connect(
// lazyWallet1.privateKey,
// vg1.address,
// fx.provider,
// );
const wallet2 = await BlsWalletWrapper.connect(
lazyWallet2.privateKey,
vg1.address,
fx.provider,
);
// const wallet2 = await BlsWalletWrapper.connect(
// lazyWallet2.privateKey,
// vg1.address,
// fx.provider,
// );
const hash1 = wallet1.blsWalletSigner.getPublicKeyHash(wallet1.privateKey);
// const hash1 = wallet1.blsWalletSigner.getPublicKeyHash(wallet1.privateKey);
expect(await vg1.walletFromHash(hash1)).to.equal(wallet1.address);
expect(await vg1.hashFromWallet(wallet1.address)).to.equal(hash1);
// expect(await vg1.walletFromHash(hash1)).to.equal(wallet1.address);
// expect(await vg1.hashFromWallet(wallet1.address)).to.equal(hash1);
// wallet 2 bls key signs message containing address of wallet 1
const addressMessage = solidityPack(["address"], [wallet1.address]);
const addressSignature = wallet2.signMessage(addressMessage);
// // wallet 2 bls key signs message containing address of wallet 1
// const addressMessage = solidityPack(["address"], [wallet1.address]);
// const addressSignature = wallet2.signMessage(addressMessage);
const setExternalWalletAction: ActionData = {
ethValue: BigNumber.from(0),
contractAddress: vg1.address,
encodedFunction: vg1.interface.encodeFunctionData("setBLSKeyForWallet", [
addressSignature,
wallet2.PublicKey(),
]),
};
// const setExternalWalletAction: ActionData = {
// ethValue: BigNumber.from(0),
// contractAddress: vg1.address,
// encodedFunction: vg1.interface.encodeFunctionData("setBLSKeyForWallet", [
// addressSignature,
// wallet2.PublicKey(),
// ]),
// };
// wallet 1 submits a tx
{
const { successes } = await vg1.callStatic.processBundle(
wallet1.sign({
nonce: BigNumber.from(1),
actions: [setExternalWalletAction],
}),
);
// // wallet 1 submits a tx
// {
// const { successes } = await vg1.callStatic.processBundle(
// wallet1.sign({
// nonce: BigNumber.from(1),
// actions: [setExternalWalletAction],
// }),
// );
expect(successes).to.deep.equal([true]);
}
// expect(successes).to.deep.equal([true]);
// }
await (
await fx.verificationGateway.processBundle(
fx.blsWalletSigner.aggregate([
wallet1.sign({
nonce: BigNumber.from(1),
actions: [setExternalWalletAction],
}),
]),
)
).wait();
// await (
// await fx.verificationGateway.processBundle(
// fx.blsWalletSigner.aggregate([
// wallet1.sign({
// nonce: BigNumber.from(1),
// actions: [setExternalWalletAction],
// }),
// ]),
// )
// ).wait();
// wallet 1's hash is pointed to null address
// wallet 2's hash is now pointed to wallet 1's address
const hash2 = wallet2.blsWalletSigner.getPublicKeyHash(wallet2.privateKey);
// // wallet 1's hash is pointed to null address
// // wallet 2's hash is now pointed to wallet 1's address
// const hash2 = wallet2.blsWalletSigner.getPublicKeyHash(wallet2.privateKey);
await fx.advanceTimeBy(safetyDelaySeconds + 1);
await fx.call(wallet1, vg1, "setPendingBLSKeyForWallet", [], 2);
// await fx.advanceTimeBy(safetyDelaySeconds + 1);
// await fx.call(wallet1, vg1, "setPendingBLSKeyForWallet", [], 2);
expect(await vg1.walletFromHash(hash1)).to.equal(
ethers.constants.AddressZero,
);
expect(await vg1.walletFromHash(hash2)).to.equal(wallet1.address);
expect(await vg1.hashFromWallet(wallet1.address)).to.equal(hash2);
});
});
// expect(await vg1.walletFromHash(hash1)).to.equal(
// ethers.constants.AddressZero,
// );
// expect(await vg1.walletFromHash(hash2)).to.equal(wallet1.address);
// expect(await vg1.hashFromWallet(wallet1.address)).to.equal(hash2);
// });
// });

View File

@@ -58,7 +58,7 @@ describe("WalletActions", async function () {
const initFunctionParams = BLSWallet.interface.encodeFunctionData(
"initialize",
[fx.verificationGateway.address],
[fx.verificationGateway.address, ethers.constants.AddressZero],
);
const calculatedAddress = ethers.utils.getCreate2Address(