debug v param. sigs now work

This commit is contained in:
Max Wolff
2023-06-16 02:04:58 -07:00
parent c95e0c1782
commit 17bbb929b7
4 changed files with 259 additions and 19 deletions

View File

@@ -1,13 +1,16 @@
#/bin/sh
set -uex
PID=$(lsof -t -i:1234)
echo $PID
kill $PID
export L2_DEPLOYER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
PORT=1234
# deploys a local instance of the contracts
anvil --port $PORT &
P1=$!
while ! lsof -i :$PORT
do
@@ -18,6 +21,6 @@ echo "started anvil"
forge script ./foundry/DeployL2AdminContracts.s.sol:DeployL2AdminContracts --rpc-url http://localhost:1234 --legacy --broadcast -vvvv
./encode.sh
npx ts-node ./encode.ts
echo "deployment success"

View File

@@ -1,6 +1,9 @@
#/bin/sh
set -uex
# does not work due to V recovery bit being off
L2_COUNCIL_SAFE_ADDR=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
L2_COUNCIL_TIMELOCK_ADDR=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
L2_SCROLL_SAFE_ADDR=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
@@ -22,6 +25,9 @@ $L2_SCROLL_TIMELOCK_ADDR 0 $TIMELOCK_SCHEDULE_CALLDATA 0 0 0 0 $ZERO_BYTES $ZERO
SAFE_SIG=$(cast wallet sign --private-key $L2_DEPLOYER_PRIVATE_KEY $SAFE_TX_HASH | awk '{print $2}')
# echo $SAFE_SIG
# echo $SAFE_TX_HASH
# send safe tx to schedule the call
cast send -c 31337 --legacy --private-key $L2_DEPLOYER_PRIVATE_KEY -r http://localhost:1234 --gas-limit 1000000 $L2_SCROLL_SAFE_ADDR "execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes)" \
$L2_SCROLL_TIMELOCK_ADDR 0 $TIMELOCK_SCHEDULE_CALLDATA 0 0 0 0 $ZERO_BYTES $ZERO_BYTES $SAFE_SIG
@@ -53,23 +59,6 @@ $L2_SCROLL_TIMELOCK_ADDR 0 $TIMELOCK_SCHEDULE_CALLDATA 0 0 0 0 $ZERO_BYTES $ZERO
exit 0
# /////////////// 2nd tx ///////////////
# sign tx hash for execute call

View File

@@ -0,0 +1,36 @@
import { ethers } from "ethers";
import { SafeAbi__factory, SafeAbi } from "../safeAbi";
/*
to get safe abi
* forge build
* cat artifacts/src/Safe.sol/Safe.json| jq .abi >> safeabi.json
* mkdir safeAbi
* npx typechain --target=ethers-v5 artifacts/src/Safe.sol/Safe.json --out-dir safeAbi
*/
async function main() {
const provider = new ethers.providers.JsonRpcProvider("http://localhost:1234");
const safeAddress = "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853";
const safe = SafeAbi__factory.connect(safeAddress, provider);
const wallet = new ethers.Wallet("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80");
const message = "Hello, world!";
const dataHash = ethers.utils.hashMessage(message);
console.log(dataHash);
const sigRaw = await wallet.signMessage(ethers.utils.arrayify(dataHash));
const sig = editSig(sigRaw);
await safe.checkNSignatures(dataHash, ethers.utils.arrayify("0x00"), sig, 1);
}
// add 4 to the v byte at the end of the signature
function editSig(sig: string) {
const v = parseInt(sig.slice(-2), 16);
const newV = v + 4;
const newSig = sig.slice(0, -2) + newV.toString(16);
return newSig;
}
main();

View File

@@ -0,0 +1,212 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {DSTestPlus} from "solmate/test/utils/DSTestPlus.sol";
import "forge-std/Vm.sol";
// import {Vm, VmSafe} from "./Vm.sol";
import "forge-std/Test.sol";
import "forge-std/console.sol";
import {Safe} from "safe-contracts/Safe.sol";
import {SafeProxy} from "safe-contracts/proxies/SafeProxy.sol";
import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";
import {Forwarder} from "../../src/misc/Forwarder.sol";
import {MockTarget} from "../../src/mocks/MockTarget.sol";
interface ISafe {
// enum
enum Operation {
Call,
DelegateCall
}
function setup(
address[] calldata _owners,
uint256 _threshold,
address to,
bytes calldata data,
address fallbackHandler,
address paymentToken,
uint256 payment,
address payable paymentReceiver
) external;
function execTransaction(
address to,
uint256 value,
bytes calldata data,
Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes memory signatures
) external returns (bool success);
function checkNSignatures(
bytes32 dataHash,
bytes memory data,
bytes memory signatures,
uint256 requiredSignatures
) external;
}
// scratchpad
contract Temp is DSTestPlus {
address scroll_safe;
// function setUp() external {
// hevm.prank(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266);
// address council_safe = deploySafe();
// // deploy timelock with no delay, just to keep council and scroll admin flows be parallel
// address council_timelock = deployTimelockController(council_safe, 0);
// // logAddress("L2_COUNCIL_SAFE_ADDR", address(council_safe));
// // logAddress("L2_COUNCIL_TIMELOCK_ADDR", address(council_timelock));
// address scroll_safe = deploySafe();
// // TODO: get timelock delay from env. for now just use 0
// address scroll_timelock = deployTimelockController(scroll_safe, 0);
// // logAddress("L2_SCROLL_SAFE_ADDR", address(scroll_safe));
// // logAddress("L2_SCROLL_TIMELOCK_ADDR", address(scroll_timelock));
// address forwarder = deployForwarder(address(council_safe), address(scroll_safe));
// // logAddress("L1_FORWARDER_ADDR", address(forwarder));
// MockTarget target = new MockTarget();
// // logAddress("L2_TARGET_ADDR", address(target));
// // vm.stopBroadcast();
// }
function testEcrecover() external {
bytes32 dataHash = 0xb453bd4e271eed985cbab8231da609c4ce0a9cf1f763b6c1594e76315510e0f1;
// (uint8 v, bytes32 r, bytes32 s) = signatureSplit(
// hex"078461ca16494711508b8602c1ea3ef515e5bfe11d67fc76e45b9217d42059f57abdde7cb9bf83b094991e2b6e61fd8b1146de575fd12080d65eaedd2e0c74da1c",
// 0
// );
bytes
memory signatures = hex"078461ca16494711508b8602c1ea3ef515e5bfe11d67fc76e45b9217d42059f57abdde7cb9bf83b094991e2b6e61fd8b1146de575fd12080d65eaedd2e0c74da1c";
uint256 requiredSignatures = 1;
uint8 v;
bytes32 r;
bytes32 s;
uint256 i;
for (i = 0; i < requiredSignatures; i++) {
(v, r, s) = signatureSplit(signatures, i);
emit log_uint(v);
emit log_bytes32(r);
emit log_bytes32(s);
address currentOwner = ecrecover(
keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)),
v,
r,
s
);
assertEq(address(0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf), currentOwner);
}
}
function testEcrecover1() external {
bytes
memory sig = hex"078461ca16494711508b8602c1ea3ef515e5bfe11d67fc76e45b9217d42059f57abdde7cb9bf83b094991e2b6e61fd8b1146de575fd12080d65eaedd2e0c74da1c";
uint8 v;
bytes32 r;
bytes32 s;
(v, r, s) = signatureSplit(sig, 0);
emit log_uint(v);
emit log_bytes32(r);
emit log_bytes32(s);
require(r == 0x078461ca16494711508b8602c1ea3ef515e5bfe11d67fc76e45b9217d42059f5, "r");
require(s == 0x7abdde7cb9bf83b094991e2b6e61fd8b1146de575fd12080d65eaedd2e0c74da, "s");
require(v == 28, "v");
}
function testSigVerify() external {
address currentOwner = ecrecover(
keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
bytes32(0xb453bd4e271eed985cbab8231da609c4ce0a9cf1f763b6c1594e76315510e0f1)
)
),
28,
0x078461ca16494711508b8602c1ea3ef515e5bfe11d67fc76e45b9217d42059f5,
0x7abdde7cb9bf83b094991e2b6e61fd8b1146de575fd12080d65eaedd2e0c74da
);
require(currentOwner == 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf, "SIG FAIL ABC");
}
function signatureSplit(bytes memory signatures, uint256 pos)
public
returns (
uint8 v,
bytes32 r,
bytes32 s
)
{
// solhint-disable-next-line no-inline-assembly
assembly {
let signaturePos := mul(0x41, pos)
r := mload(add(signatures, add(signaturePos, 0x20)))
s := mload(add(signatures, add(signaturePos, 0x40)))
/**
* Here we are loading the last 32 bytes, including 31 bytes
* of 's'. There is no 'mload8' to do this.
* 'byte' is not working due to the Solidity parser, so lets
* use the second best option, 'and'
*/
v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)
}
}
function deployForwarder(address admin, address superAdmin) internal returns (address) {
Forwarder forwarder = new Forwarder(admin, superAdmin);
return address(forwarder);
}
function deploySafe() internal returns (address) {
address owner = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
// TODO: get safe signers from env
Safe safe = new Safe();
SafeProxy proxy = new SafeProxy(address(safe));
address[] memory owners = new address[](1);
owners[0] = owner;
// deployer 1/1. no gas refunds for now
ISafe(address(proxy)).setup(
owners,
1,
address(0),
new bytes(0),
address(0),
address(0),
0,
payable(address(0))
);
return address(proxy);
}
function deployTimelockController(address safe, uint256 delay) internal returns (address) {
address deployer = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
address[] memory proposers = new address[](1);
proposers[0] = safe;
// add SAFE as the only proposer, anyone can execute
TimelockController timelock = new TimelockController(delay, proposers, new address[](0));
bytes32 TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE");
// make safe admin of timelock, then revoke deployer's rights
timelock.grantRole(TIMELOCK_ADMIN_ROLE, address(safe));
timelock.revokeRole(TIMELOCK_ADMIN_ROLE, deployer);
return address(timelock);
}
}