Skip to content

Commit 406295c

Browse files
committed
Merge pull request #443 from derekpierre/more-testing
More testing of Signing Contracts
2 parents 7588451 + 98c3f91 commit 406295c

File tree

5 files changed

+450
-0
lines changed

5 files changed

+450
-0
lines changed

contracts/contracts/coordination/SigningCoordinatorDispatcher.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ contract SigningCoordinatorDispatcher is Initializable, OwnableUpgradeable {
3434
require(chainId != 0, "Invalid chain ID");
3535
if (chainId != block.chainid) {
3636
require(l1Sender != address(0), "Invalid L1 sender");
37+
} else {
38+
// same chain so no L1 sender needed
39+
require(l1Sender == address(0), "L1 sender not needed for same chain");
3740
}
3841
require(signingCoordinatorChild != address(0), "Invalid target");
3942
dispatchMap[chainId] = DispatchTarget(l1Sender, signingCoordinatorChild);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+
pragma solidity ^0.8.0;
4+
5+
import "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol";
6+
import "../contracts/coordination/SigningCoordinatorDispatcher.sol";
7+
import "../contracts/coordination/ISigningCoordinatorChild.sol";
8+
9+
/**
10+
* @notice Mock contract for testing SigningCoordinatorDispatcher
11+
*/
12+
contract SigningCoordinatorMock is Initializable {
13+
SigningCoordinatorDispatcher public signingCoordinatorDispatcher;
14+
15+
function setDispatcher(SigningCoordinatorDispatcher dispatcher) external {
16+
require(address(dispatcher).code.length > 0, "Dispatcher must be contract");
17+
signingCoordinatorDispatcher = dispatcher;
18+
}
19+
20+
function callDispatch(uint256 chainId, bytes calldata callData) external {
21+
signingCoordinatorDispatcher.dispatch(chainId, callData);
22+
}
23+
}
24+
25+
contract SigningCoordinatorChildMock is ISigningCoordinatorChild {
26+
function deployCohortMultiSig(uint32 cohortId, address[] calldata, uint16) external {
27+
emit CohortMultisigDeployed(cohortId, address(0));
28+
}
29+
30+
function updateMultiSigParameters(
31+
uint32 cohortId,
32+
address[] calldata signers,
33+
uint16 threshold,
34+
bool clearSigners
35+
) external {
36+
emit CohortMultisigUpdated(cohortId, address(0), signers, threshold, clearSigners);
37+
}
38+
}
39+
40+
contract L1SenderMock {
41+
function sendData(address target, bytes calldata data) external {
42+
// solhint-disable-next-line avoid-low-level-calls
43+
(bool success, ) = target.call(data);
44+
require(success, "Execution failed");
45+
}
46+
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import os
2+
3+
import ape
4+
import pytest
5+
from eth_utils import to_checksum_address
6+
7+
NUM_SIGNERS = 5
8+
THRESHOLD = 2
9+
10+
11+
@pytest.fixture(scope="module")
12+
def signers(accounts):
13+
_accounts = [acc.address for acc in accounts[:NUM_SIGNERS]]
14+
return _accounts
15+
16+
17+
@pytest.fixture(scope="module")
18+
def deployer(accounts):
19+
deployer = accounts[NUM_SIGNERS + 1]
20+
return deployer
21+
22+
23+
@pytest.fixture(scope="module")
24+
def allowed_caller(accounts):
25+
return accounts[NUM_SIGNERS + 2]
26+
27+
28+
@pytest.fixture(scope="module")
29+
def unauthorized_caller(accounts):
30+
return accounts[NUM_SIGNERS + 3]
31+
32+
33+
@pytest.fixture()
34+
def signing_coordinator_child(
35+
project, oz_dependency, deployer, allowed_caller, threshold_signing_multisig_impl
36+
):
37+
contract = project.SigningCoordinatorChild.deploy(
38+
sender=deployer,
39+
)
40+
proxy = oz_dependency.TransparentUpgradeableProxy.deploy(
41+
contract.address,
42+
deployer,
43+
b"",
44+
sender=deployer,
45+
)
46+
proxy_contract = project.SigningCoordinatorChild.at(proxy.address)
47+
48+
signing_factory_contract = project.ThresholdSigningMultisigCloneFactory.deploy(
49+
threshold_signing_multisig_impl.address,
50+
proxy_contract.address,
51+
sender=deployer,
52+
)
53+
54+
proxy_contract.initialize(signing_factory_contract.address, allowed_caller, sender=deployer)
55+
assert proxy_contract.allowedCaller() == allowed_caller.address
56+
assert proxy_contract.signingMultisigFactory() == signing_factory_contract.address
57+
assert proxy_contract.owner() == deployer.address
58+
59+
return proxy_contract
60+
61+
62+
@pytest.fixture(scope="module")
63+
def threshold_signing_multisig_impl(project, deployer):
64+
threshold_signing_multisig_impl = project.ThresholdSigningMultisig.deploy(
65+
sender=deployer,
66+
)
67+
return threshold_signing_multisig_impl
68+
69+
70+
def test_set_multisig_factory(
71+
project,
72+
deployer,
73+
allowed_caller,
74+
unauthorized_caller,
75+
signing_coordinator_child,
76+
threshold_signing_multisig_impl,
77+
):
78+
new_factory_contract = project.ThresholdSigningMultisigCloneFactory.deploy(
79+
threshold_signing_multisig_impl.address,
80+
signing_coordinator_child.address,
81+
sender=deployer,
82+
)
83+
84+
# must be owner
85+
with ape.reverts(f"account={unauthorized_caller.address}"):
86+
signing_coordinator_child.setMultisigFactory(
87+
new_factory_contract.address, sender=unauthorized_caller
88+
)
89+
90+
with ape.reverts(f"account={allowed_caller.address}"):
91+
signing_coordinator_child.setMultisigFactory(
92+
new_factory_contract.address, sender=allowed_caller
93+
)
94+
95+
signing_coordinator_child.setMultisigFactory(new_factory_contract.address, sender=deployer)
96+
assert signing_coordinator_child.signingMultisigFactory() == new_factory_contract.address
97+
98+
99+
def test_set_allowed_caller(
100+
deployer, allowed_caller, unauthorized_caller, signing_coordinator_child
101+
):
102+
# must be owner
103+
with ape.reverts(f"account={unauthorized_caller.address}"):
104+
signing_coordinator_child.setAllowedCaller(
105+
unauthorized_caller.address, sender=unauthorized_caller
106+
)
107+
108+
with ape.reverts(f"account={allowed_caller.address}"):
109+
signing_coordinator_child.setAllowedCaller(allowed_caller.address, sender=allowed_caller)
110+
111+
assert signing_coordinator_child.owner() == deployer.address
112+
signing_coordinator_child.setAllowedCaller(unauthorized_caller.address, sender=deployer)
113+
assert signing_coordinator_child.allowedCaller() == unauthorized_caller.address
114+
115+
116+
def test_deploy_cohort_multisig(
117+
project, deployer, allowed_caller, signers, signing_coordinator_child
118+
):
119+
cohort_id = 42
120+
121+
# must be allowed caller
122+
with ape.reverts("Unauthorized caller"):
123+
signing_coordinator_child.deployCohortMultiSig(
124+
cohort_id, signers, THRESHOLD, sender=deployer
125+
)
126+
127+
signing_coordinator_child.deployCohortMultiSig(
128+
cohort_id, signers, THRESHOLD, sender=allowed_caller
129+
)
130+
cohort_42_multisig_address = signing_coordinator_child.cohortMultisigs(cohort_id)
131+
cohort_42_multisig_contract = project.ThresholdSigningMultisig.at(cohort_42_multisig_address)
132+
assert cohort_42_multisig_contract.getSigners() == signers
133+
assert cohort_42_multisig_contract.threshold() == THRESHOLD
134+
135+
# deploying again for the same cohort should fail
136+
with ape.reverts("Multisig already deployed"):
137+
signing_coordinator_child.deployCohortMultiSig(
138+
cohort_id, signers, THRESHOLD, sender=allowed_caller
139+
)
140+
141+
# deploy for other cohort
142+
other_cohort_id = 43
143+
cohort_43_threshold = 1
144+
cohort_43_signers = [
145+
to_checksum_address(os.urandom(20)) for _ in range(cohort_43_threshold + 1)
146+
]
147+
signing_coordinator_child.deployCohortMultiSig(
148+
other_cohort_id, cohort_43_signers, cohort_43_threshold, sender=allowed_caller
149+
)
150+
cohort_43_multisig_address = signing_coordinator_child.cohortMultisigs(other_cohort_id)
151+
# different address from 1st one
152+
assert cohort_43_multisig_address != cohort_42_multisig_address
153+
154+
cohort_43_multisig_contract = project.ThresholdSigningMultisig.at(cohort_43_multisig_address)
155+
assert cohort_43_multisig_contract.getSigners() == cohort_43_signers
156+
assert cohort_43_multisig_contract.threshold() == cohort_43_threshold
157+
158+
159+
def test_update_multisig_parameters(
160+
project, deployer, allowed_caller, unauthorized_caller, signers, signing_coordinator_child
161+
):
162+
cohort_id = 100
163+
signing_coordinator_child.deployCohortMultiSig(
164+
cohort_id, signers, THRESHOLD, sender=allowed_caller
165+
)
166+
cohort_multisig_address = signing_coordinator_child.cohortMultisigs(cohort_id)
167+
cohort_multisig_contract = project.ThresholdSigningMultisig.at(cohort_multisig_address)
168+
169+
new_threshold = 3
170+
new_signers = [to_checksum_address(os.urandom(20)) for _ in range(NUM_SIGNERS + 2)]
171+
172+
# must be allowed caller
173+
with ape.reverts("Unauthorized caller"):
174+
signing_coordinator_child.updateMultiSigParameters(
175+
cohort_id, signers, new_threshold, True, sender=deployer
176+
)
177+
178+
# must have already been deployed
179+
with ape.reverts("Multisig not deployed"):
180+
signing_coordinator_child.updateMultiSigParameters(
181+
11, signers, new_threshold, True, sender=allowed_caller
182+
)
183+
184+
# don't replace, bulk add and update
185+
signing_coordinator_child.updateMultiSigParameters(
186+
cohort_id, new_signers, new_threshold, False, sender=allowed_caller
187+
)
188+
assert cohort_multisig_contract.threshold() == new_threshold
189+
assert len(cohort_multisig_contract.getSigners()) == len(signers) + len(new_signers)
190+
assert set(cohort_multisig_contract.getSigners()) == set(signers).union(set(new_signers))
191+
192+
# totally replace
193+
signing_coordinator_child.updateMultiSigParameters(
194+
cohort_id, new_signers, new_threshold, True, sender=allowed_caller
195+
)
196+
assert cohort_multisig_contract.threshold() == new_threshold
197+
assert cohort_multisig_contract.getSigners() == new_signers

0 commit comments

Comments
 (0)