Skip to content

Commit 225a6bc

Browse files
authored
refactor(Contracts): set non-zero .head during initialize (#32)
1 parent b4f693e commit 225a6bc

File tree

5 files changed

+54
-21
lines changed

5 files changed

+54
-21
lines changed

contracts/Caravan.vy

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,24 @@ def eip712Domain() -> (
9393
)
9494

9595

96+
@view
97+
def _DOMAIN_SEPARATOR() -> bytes32:
98+
return keccak256(
99+
abi_encode(EIP712_DOMAIN_TYPEHASH, NAMEHASH, VERSIONHASH, chain.id, self)
100+
)
101+
102+
103+
@view
104+
@external
105+
def DOMAIN_SEPARATOR() -> bytes32:
106+
return self._DOMAIN_SEPARATOR()
107+
108+
109+
@view
110+
def _hash_typed_data_v4(struct_hash: bytes32) -> bytes32:
111+
return keccak256(concat(x"1901", self._DOMAIN_SEPARATOR(), struct_hash))
112+
113+
96114
@external
97115
def initialize(signers: DynArray[address, 11], threshold: uint256):
98116
assert self.IMPLEMENTATION != empty(address) # dev: only Proxy can initialize
@@ -102,6 +120,23 @@ def initialize(signers: DynArray[address, 11], threshold: uint256):
102120
self._signers = signers
103121
self.threshold = threshold
104122

123+
# NOTE: Initialize head to non-zero, network-specific value as if this action was performed
124+
self.head = self._hash_typed_data_v4(
125+
keccak256(
126+
abi_encode(
127+
MODIFY_TYPEHASH,
128+
empty(bytes32),
129+
ICaravan.ActionType.ROTATE_SIGNERS,
130+
# NOTE: Per EIP712, Dynamic structures are encoded as the hash of their contents
131+
keccak256(
132+
abi_encode(
133+
signers, empty(DynArray[address, 11]), threshold
134+
)
135+
),
136+
)
137+
)
138+
)
139+
105140

106141
@view
107142
@external
@@ -148,24 +183,6 @@ def _verify_signatures(msghash: bytes32, signatures: DynArray[Bytes[65], 11]):
148183
already_approved.append(signer)
149184

150185

151-
@view
152-
def _DOMAIN_SEPARATOR() -> bytes32:
153-
return keccak256(
154-
abi_encode(EIP712_DOMAIN_TYPEHASH, NAMEHASH, VERSIONHASH, chain.id, self)
155-
)
156-
157-
158-
@view
159-
@external
160-
def DOMAIN_SEPARATOR() -> bytes32:
161-
return self._DOMAIN_SEPARATOR()
162-
163-
164-
@view
165-
def _hash_typed_data_v4(struct_hash: bytes32) -> bytes32:
166-
return keccak256(concat(x"1901", self._DOMAIN_SEPARATOR(), struct_hash))
167-
168-
169186
def _rotate_signers(
170187
signers_to_add: DynArray[address, 11],
171188
signers_to_rm: DynArray[address, 11],

sdk/py/caravan/factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def __init__(self, address: AddressType | None = None):
3333
self._cached_releases: dict[Version, "ContractInstance"] = {
3434
# NOTE: This is the deterministic deployment address for v1 via CreateX
3535
Version("1"): PackageType.SINGLETON("1").at(
36-
"0xf7AC37e8A31Da4D2Fbe7687118c142dd46e63517"
36+
"0xB810c65972596d213DCdf0A73b27fa7be59Ef3E2"
3737
),
3838
}
3939

sdk/py/caravan/queue.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ class QueueManager(BaseModel):
107107

108108
# QueueItem => list[QueueItem.hash]
109109
queue: dict[QueueItem, list[HexBytes]] = dict()
110-
base: HexBytes = HexBytes("00" * 32)
110+
base: HexBytes # NOTE: Must always provide head!
111111

112112
@model_validator(mode="after")
113113
def ensure_base(self) -> Self:

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def new_van(
8585

8686
van = factory.new(owners, threshold, version=version, **txn_args)
8787
# NOTE: Make sure to use empty queue for testing
88-
van.queue = QueueManager()
88+
van.queue = QueueManager(base=van.head)
8989
return van
9090

9191
return new_van

tests/test_operation.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import ape
22
import pytest
33

4+
from caravan.messages import ActionType
5+
46

57
def test_configuration(networks, van, VERSION, THRESHOLD, owners):
68
assert set(van.signers) == set(o.address for o in owners)
@@ -20,6 +22,20 @@ def test_configuration(networks, van, VERSION, THRESHOLD, owners):
2022
assert salt == b"\x00" * 32
2123
assert extensions == []
2224

25+
# NOTE: Initial head should be as if it did a ROTATE_SIGNERS modification
26+
assert (
27+
van.head
28+
== ActionType.ROTATE_SIGNERS(
29+
[o.address for o in owners],
30+
[],
31+
THRESHOLD,
32+
address=address,
33+
chain_id=chain_id,
34+
version=version,
35+
parent=b"\x00" * 32,
36+
).hash
37+
)
38+
2339

2440
def test_initialize(singleton, van, THRESHOLD, owners):
2541
assert van.contract.IMPLEMENTATION() == singleton

0 commit comments

Comments
 (0)