doublezero-serviceability 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,145 @@
1
+ """Mainnet compatibility tests.
2
+
3
+ These tests fetch live mainnet-beta data and verify that our struct
4
+ deserialization works against real on-chain accounts.
5
+
6
+ Run with:
7
+ SERVICEABILITY_COMPAT_TEST=1 cd sdk/serviceability/python && uv run pytest -k compat -v
8
+
9
+ Requires network access to Solana mainnet RPC.
10
+ """
11
+
12
+ import os
13
+ import struct
14
+
15
+ import pytest
16
+ from solders.pubkey import Pubkey # type: ignore[import-untyped]
17
+
18
+ from serviceability.config import PROGRAM_IDS, LEDGER_RPC_URLS
19
+ from serviceability.pda import (
20
+ derive_global_config_pda,
21
+ derive_global_state_pda,
22
+ derive_program_config_pda,
23
+ )
24
+ from serviceability.state import GlobalConfig, GlobalState, ProgramConfig
25
+
26
+
27
+ def skip_unless_compat() -> None:
28
+ if not os.environ.get("SERVICEABILITY_COMPAT_TEST"):
29
+ pytest.skip("set SERVICEABILITY_COMPAT_TEST=1 to run compatibility tests against mainnet")
30
+
31
+
32
+ def _rpc_url() -> str:
33
+ return os.environ.get("SOLANA_RPC_URL", LEDGER_RPC_URLS["mainnet-beta"])
34
+
35
+
36
+ def _program_id() -> Pubkey:
37
+ return Pubkey.from_string(PROGRAM_IDS["mainnet-beta"])
38
+
39
+
40
+ def fetch_raw_account(addr: Pubkey) -> bytes:
41
+ from serviceability.rpc import new_rpc_client
42
+
43
+ rpc = new_rpc_client(_rpc_url())
44
+ resp = rpc.get_account_info(addr)
45
+ assert resp.value is not None, f"account not found: {addr}"
46
+ return bytes(resp.value.data)
47
+
48
+
49
+ def read_u8(raw: bytes, offset: int) -> int:
50
+ return raw[offset]
51
+
52
+
53
+ def read_u16(raw: bytes, offset: int) -> int:
54
+ return struct.unpack_from("<H", raw, offset)[0]
55
+
56
+
57
+ def read_u32(raw: bytes, offset: int) -> int:
58
+ return struct.unpack_from("<I", raw, offset)[0]
59
+
60
+
61
+ def read_pubkey(raw: bytes, offset: int) -> Pubkey:
62
+ return Pubkey.from_bytes(raw[offset : offset + 32])
63
+
64
+
65
+ class TestCompatProgramConfig:
66
+ def test_deserialize(self) -> None:
67
+ skip_unless_compat()
68
+
69
+ program_id = _program_id()
70
+ addr, _ = derive_program_config_pda(program_id)
71
+ raw = fetch_raw_account(addr)
72
+
73
+ pc = ProgramConfig.from_bytes(raw)
74
+
75
+ # ProgramConfig layout (all fixed-size):
76
+ # offset 0: AccountType (u8)
77
+ # offset 1: BumpSeed (u8)
78
+ # offset 2: Version.Major (u32)
79
+ # offset 6: Version.Minor (u32)
80
+ # offset 10: Version.Patch (u32)
81
+ # offset 14: MinCompatVersion.Major (u32)
82
+ # offset 18: MinCompatVersion.Minor (u32)
83
+ # offset 22: MinCompatVersion.Patch (u32)
84
+ assert pc.account_type == read_u8(raw, 0), "AccountType"
85
+ assert pc.bump_seed == read_u8(raw, 1), "BumpSeed"
86
+ assert pc.version.major == read_u32(raw, 2), "Version.Major"
87
+ assert pc.version.minor == read_u32(raw, 6), "Version.Minor"
88
+ assert pc.version.patch == read_u32(raw, 10), "Version.Patch"
89
+ assert pc.min_compat_version.major == read_u32(raw, 14), "MinCompatVersion.Major"
90
+ assert pc.min_compat_version.minor == read_u32(raw, 18), "MinCompatVersion.Minor"
91
+ assert pc.min_compat_version.patch == read_u32(raw, 22), "MinCompatVersion.Patch"
92
+
93
+ assert pc.account_type == 9
94
+
95
+
96
+ class TestCompatGlobalConfig:
97
+ def test_deserialize(self) -> None:
98
+ skip_unless_compat()
99
+
100
+ program_id = _program_id()
101
+ addr, _ = derive_global_config_pda(program_id)
102
+ raw = fetch_raw_account(addr)
103
+
104
+ gc = GlobalConfig.from_bytes(raw)
105
+
106
+ # GlobalConfig layout (all fixed-size):
107
+ # offset 0: AccountType (u8)
108
+ # offset 1: Owner (32 bytes)
109
+ # offset 33: BumpSeed (u8)
110
+ # offset 34: LocalASN (u32)
111
+ # offset 38: RemoteASN (u32)
112
+ # offset 57: NextBGPCommunity (u16)
113
+ assert gc.account_type == read_u8(raw, 0), "AccountType"
114
+ assert gc.owner == read_pubkey(raw, 1), "Owner"
115
+ assert gc.bump_seed == read_u8(raw, 33), "BumpSeed"
116
+ assert gc.local_asn == read_u32(raw, 34), "LocalASN"
117
+ assert gc.remote_asn == read_u32(raw, 38), "RemoteASN"
118
+ assert gc.next_bgp_community == read_u16(raw, 57), "NextBGPCommunity"
119
+
120
+ assert gc.account_type == 2
121
+ assert gc.local_asn > 0, "LocalASN should be > 0 on mainnet"
122
+
123
+
124
+ class TestCompatGlobalState:
125
+ def test_deserialize(self) -> None:
126
+ skip_unless_compat()
127
+
128
+ program_id = _program_id()
129
+ addr, _ = derive_global_state_pda(program_id)
130
+ raw = fetch_raw_account(addr)
131
+
132
+ gs = GlobalState.from_bytes(raw)
133
+
134
+ # GlobalState fixed layout (first 18 bytes before variable-length vecs):
135
+ # offset 0: AccountType (u8)
136
+ # offset 1: BumpSeed (u8)
137
+ assert gs.account_type == read_u8(raw, 0), "AccountType"
138
+ assert gs.bump_seed == read_u8(raw, 1), "BumpSeed"
139
+
140
+ assert gs.account_type == 1
141
+
142
+ # Sanity checks.
143
+ assert gs.activator_authority_pk != Pubkey.default(), "ActivatorAuthorityPK is zero"
144
+ assert gs.sentinel_authority_pk != Pubkey.default(), "SentinelAuthorityPK is zero"
145
+ # health_oracle_pk may be zero on mainnet
@@ -0,0 +1,110 @@
1
+ """Test that enum __str__ outputs match the shared fixture file."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ import pytest
7
+
8
+ from serviceability.state import (
9
+ AccessPassStatus,
10
+ AccessPassTypeTag,
11
+ ContributorStatus,
12
+ CyoaType,
13
+ DeviceDesiredStatus,
14
+ DeviceDeviceType,
15
+ DeviceHealth,
16
+ DeviceStatus,
17
+ ExchangeStatus,
18
+ InterfaceStatus,
19
+ InterfaceType,
20
+ LinkDesiredStatus,
21
+ LinkHealth,
22
+ LinkLinkType,
23
+ LinkStatus,
24
+ LocationStatus,
25
+ LoopbackType,
26
+ MulticastGroupStatus,
27
+ UserStatus,
28
+ UserUserType,
29
+ )
30
+
31
+ FIXTURE_PATH = Path(__file__).resolve().parent.parent.parent.parent / "testdata" / "enum_strings.json"
32
+
33
+ ENUM_MAP = {
34
+ "LocationStatus": LocationStatus,
35
+ "ExchangeStatus": ExchangeStatus,
36
+ "DeviceDeviceType": DeviceDeviceType,
37
+ "DeviceStatus": DeviceStatus,
38
+ "DeviceHealth": DeviceHealth,
39
+ "DeviceDesiredStatus": DeviceDesiredStatus,
40
+ "InterfaceStatus": InterfaceStatus,
41
+ "InterfaceType": InterfaceType,
42
+ "LoopbackType": LoopbackType,
43
+ "LinkLinkType": LinkLinkType,
44
+ "LinkStatus": LinkStatus,
45
+ "LinkHealth": LinkHealth,
46
+ "LinkDesiredStatus": LinkDesiredStatus,
47
+ "ContributorStatus": ContributorStatus,
48
+ "UserUserType": UserUserType,
49
+ "CyoaType": CyoaType,
50
+ "UserStatus": UserStatus,
51
+ "MulticastGroupStatus": MulticastGroupStatus,
52
+ "AccessPassTypeTag": AccessPassTypeTag,
53
+ "AccessPassStatus": AccessPassStatus,
54
+ }
55
+
56
+
57
+ def _load_fixture() -> dict:
58
+ return json.loads(FIXTURE_PATH.read_text())
59
+
60
+
61
+ def _enum_test_cases() -> list[tuple[str, int, str]]:
62
+ """Yield (enum_name, int_value, expected_string) tuples."""
63
+ fixture = _load_fixture()
64
+ cases = []
65
+ for enum_name, mappings in fixture.items():
66
+ if enum_name not in ENUM_MAP:
67
+ continue
68
+ for str_value, expected in mappings.items():
69
+ cases.append((enum_name, int(str_value), expected))
70
+ return cases
71
+
72
+
73
+ @pytest.mark.parametrize(
74
+ "enum_name,int_value,expected",
75
+ _enum_test_cases(),
76
+ ids=lambda x: str(x),
77
+ )
78
+ def test_enum_str(enum_name: str, int_value: int, expected: str) -> None:
79
+ enum_cls = ENUM_MAP[enum_name]
80
+ try:
81
+ instance = enum_cls(int_value)
82
+ except ValueError:
83
+ # Unknown value not defined as a member; skip since Python IntEnum
84
+ # cannot instantiate undefined members. Go/TS tests cover this case.
85
+ pytest.skip(f"{enum_name}({int_value}) is not a valid member")
86
+ return
87
+ assert str(instance) == expected, (
88
+ f"{enum_name}({int_value}): expected {expected!r}, got {str(instance)!r}"
89
+ )
90
+
91
+
92
+ def test_all_enum_members_in_fixture() -> None:
93
+ """Every Python IntEnum member must appear in the fixture.
94
+
95
+ This catches the case where a variant is added in Python but not in
96
+ enum_strings.json. Since the fixture is shared across Go, Python, and
97
+ TypeScript, updating it will cause the other languages' tests to fail
98
+ until they add the new variant too.
99
+ """
100
+ fixture = _load_fixture()
101
+ missing = []
102
+ for enum_name, enum_cls in ENUM_MAP.items():
103
+ fixture_values = fixture.get(enum_name, {})
104
+ for member in enum_cls:
105
+ if str(member.value) not in fixture_values:
106
+ missing.append(f"{enum_name}.{member.name} ({member.value})")
107
+ assert not missing, (
108
+ "Enum members missing from enum_strings.json fixture — add them so "
109
+ "other languages stay in sync:\n " + "\n ".join(missing)
110
+ )
@@ -0,0 +1,330 @@
1
+ """Fixture-based compatibility tests."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from solders.pubkey import Pubkey # type: ignore[import-untyped]
7
+
8
+ from serviceability.state import (
9
+ AccessPass,
10
+ Contributor,
11
+ Device,
12
+ Exchange,
13
+ GlobalConfig,
14
+ GlobalState,
15
+ Link,
16
+ Location,
17
+ MulticastGroup,
18
+ ProgramConfig,
19
+ User,
20
+ )
21
+
22
+ FIXTURES_DIR = Path(__file__).resolve().parent.parent.parent.parent / "testdata" / "fixtures"
23
+
24
+
25
+ def _load_fixture(name: str) -> tuple[bytes, dict]:
26
+ bin_data = (FIXTURES_DIR / f"{name}.bin").read_bytes()
27
+ meta = json.loads((FIXTURES_DIR / f"{name}.json").read_text())
28
+ return bin_data, meta
29
+
30
+
31
+ def _assert_fields(expected_fields: list[dict], got: dict) -> None:
32
+ for f in expected_fields:
33
+ name = f["name"]
34
+ if name not in got:
35
+ continue
36
+ typ = f["typ"]
37
+ raw = f["value"]
38
+ actual = got[name]
39
+ if typ in ("u8", "u16", "u32", "u64"):
40
+ assert actual == int(raw), f"{name}: expected {raw}, got {actual}"
41
+ elif typ == "pubkey":
42
+ expected = Pubkey.from_string(raw)
43
+ assert actual == expected, f"{name}: expected {expected}, got {actual}"
44
+ elif typ == "string":
45
+ assert actual == raw, f"{name}: expected {raw!r}, got {actual!r}"
46
+ elif typ == "bool":
47
+ expected = raw == "true"
48
+ assert actual == expected, f"{name}: expected {expected}, got {actual}"
49
+ elif typ == "ipv4":
50
+ import ipaddress
51
+
52
+ expected_bytes = ipaddress.IPv4Address(raw).packed
53
+ assert actual == expected_bytes, f"{name}: expected {raw}, got {actual}"
54
+ elif typ == "networkv4":
55
+ import ipaddress
56
+
57
+ net = ipaddress.IPv4Network(raw)
58
+ expected_bytes = net.network_address.packed + bytes([net.prefixlen])
59
+ assert actual == expected_bytes, f"{name}: expected {raw}, got {actual}"
60
+ elif typ == "u128":
61
+ assert actual == int(raw), f"{name}: expected {raw}, got {actual}"
62
+
63
+
64
+ class TestFixtureGlobalState:
65
+ def test_deserialize(self):
66
+ data, meta = _load_fixture("global_state")
67
+ gs = GlobalState.from_bytes(data)
68
+ _assert_fields(
69
+ meta["fields"],
70
+ {
71
+ "AccountType": gs.account_type,
72
+ "BumpSeed": gs.bump_seed,
73
+ "ContributorAirdropLamports": gs.contributor_airdrop_lamports,
74
+ "UserAirdropLamports": gs.user_airdrop_lamports,
75
+ "ActivatorAuthorityPk": gs.activator_authority_pk,
76
+ "SentinelAuthorityPk": gs.sentinel_authority_pk,
77
+ "HealthOraclePk": gs.health_oracle_pk,
78
+ },
79
+ )
80
+
81
+
82
+ class TestFixtureGlobalConfig:
83
+ def test_deserialize(self):
84
+ data, meta = _load_fixture("global_config")
85
+ gc = GlobalConfig.from_bytes(data)
86
+ _assert_fields(
87
+ meta["fields"],
88
+ {
89
+ "AccountType": gc.account_type,
90
+ "Owner": gc.owner,
91
+ "BumpSeed": gc.bump_seed,
92
+ "LocalAsn": gc.local_asn,
93
+ "RemoteAsn": gc.remote_asn,
94
+ "NextBgpCommunity": gc.next_bgp_community,
95
+ },
96
+ )
97
+
98
+
99
+ class TestFixtureLocation:
100
+ def test_deserialize(self):
101
+ data, meta = _load_fixture("location")
102
+ loc = Location.from_bytes(data)
103
+ _assert_fields(
104
+ meta["fields"],
105
+ {
106
+ "AccountType": loc.account_type,
107
+ "Owner": loc.owner,
108
+ "BumpSeed": loc.bump_seed,
109
+ "LocId": loc.loc_id,
110
+ "Status": loc.status,
111
+ "ReferenceCount": loc.reference_count,
112
+ },
113
+ )
114
+
115
+
116
+ class TestFixtureExchange:
117
+ def test_deserialize(self):
118
+ data, meta = _load_fixture("exchange")
119
+ ex = Exchange.from_bytes(data)
120
+ _assert_fields(
121
+ meta["fields"],
122
+ {
123
+ "AccountType": ex.account_type,
124
+ "Owner": ex.owner,
125
+ "BumpSeed": ex.bump_seed,
126
+ "BgpCommunity": ex.bgp_community,
127
+ "Status": ex.status,
128
+ "ReferenceCount": ex.reference_count,
129
+ "Device1Pk": ex.device1_pk,
130
+ "Device2Pk": ex.device2_pk,
131
+ },
132
+ )
133
+
134
+
135
+ class TestFixtureDevice:
136
+ def test_deserialize(self):
137
+ data, meta = _load_fixture("device")
138
+ dev = Device.from_bytes(data)
139
+ _assert_fields(
140
+ meta["fields"],
141
+ {
142
+ "AccountType": dev.account_type,
143
+ "Owner": dev.owner,
144
+ "Index": dev.index,
145
+ "BumpSeed": dev.bump_seed,
146
+ "DeviceType": dev.device_type,
147
+ "PublicIp": dev.public_ip,
148
+ "Status": dev.status,
149
+ "Code": dev.code,
150
+ "MgmtVrf": dev.mgmt_vrf,
151
+ "ReferenceCount": dev.reference_count,
152
+ "UsersCount": dev.users_count,
153
+ "MaxUsers": dev.max_users,
154
+ "DeviceHealth": dev.device_health,
155
+ "DesiredStatus": dev.device_desired_status,
156
+ "MetricsPublisherPk": dev.metrics_publisher_pub_key,
157
+ "ContributorPk": dev.contributor_pub_key,
158
+ },
159
+ )
160
+ # Verify interfaces
161
+ assert len(dev.interfaces) == 2
162
+ assert dev.interfaces[0].name == "Loopback0"
163
+ assert dev.interfaces[1].name == "Ethernet1"
164
+ # Verify dz_prefixes
165
+ import ipaddress
166
+
167
+ assert len(dev.dz_prefixes) == 1
168
+ net = ipaddress.IPv4Network("10.10.0.0/24")
169
+ expected_prefix = net.network_address.packed + bytes([net.prefixlen])
170
+ assert dev.dz_prefixes[0] == expected_prefix
171
+ # Verify code, mgmt_vrf, public_ip, index
172
+ assert dev.code == "dz1"
173
+ assert dev.mgmt_vrf == "mgmt"
174
+ assert dev.public_ip == ipaddress.IPv4Address("203.0.113.1").packed
175
+ assert dev.index == 7
176
+
177
+
178
+ class TestFixtureLink:
179
+ def test_deserialize(self):
180
+ data, meta = _load_fixture("link")
181
+ lk = Link.from_bytes(data)
182
+ _assert_fields(
183
+ meta["fields"],
184
+ {
185
+ "AccountType": lk.account_type,
186
+ "Owner": lk.owner,
187
+ "BumpSeed": lk.bump_seed,
188
+ "LinkType": lk.link_type,
189
+ "Bandwidth": lk.bandwidth,
190
+ "Mtu": lk.mtu,
191
+ "DelayNs": lk.delay_ns,
192
+ "JitterNs": lk.jitter_ns,
193
+ "TunnelId": lk.tunnel_id,
194
+ "Status": lk.status,
195
+ "ContributorPk": lk.contributor_pub_key,
196
+ "DelayOverrideNs": lk.delay_override_ns,
197
+ "LinkHealth": lk.link_health,
198
+ "DesiredStatus": lk.link_desired_status,
199
+ },
200
+ )
201
+
202
+
203
+ class TestFixtureUser:
204
+ def test_deserialize(self):
205
+ data, meta = _load_fixture("user")
206
+ u = User.from_bytes(data)
207
+ _assert_fields(
208
+ meta["fields"],
209
+ {
210
+ "AccountType": u.account_type,
211
+ "Owner": u.owner,
212
+ "BumpSeed": u.bump_seed,
213
+ "UserType": u.user_type,
214
+ "TenantPk": u.tenant_pub_key,
215
+ "DevicePk": u.device_pub_key,
216
+ "CyoaType": u.cyoa_type,
217
+ "TunnelId": u.tunnel_id,
218
+ "Status": u.status,
219
+ "ValidatorPubkey": u.validator_pub_key,
220
+ },
221
+ )
222
+
223
+
224
+ class TestFixtureMulticastGroup:
225
+ def test_deserialize(self):
226
+ data, meta = _load_fixture("multicast_group")
227
+ mg = MulticastGroup.from_bytes(data)
228
+ _assert_fields(
229
+ meta["fields"],
230
+ {
231
+ "AccountType": mg.account_type,
232
+ "Owner": mg.owner,
233
+ "BumpSeed": mg.bump_seed,
234
+ "TenantPk": mg.tenant_pub_key,
235
+ "MaxBandwidth": mg.max_bandwidth,
236
+ "Status": mg.status,
237
+ "PublisherCount": mg.publisher_count,
238
+ "SubscriberCount": mg.subscriber_count,
239
+ },
240
+ )
241
+
242
+
243
+ class TestFixtureContributor:
244
+ def test_deserialize(self):
245
+ data, meta = _load_fixture("contributor")
246
+ c = Contributor.from_bytes(data)
247
+ _assert_fields(
248
+ meta["fields"],
249
+ {
250
+ "AccountType": c.account_type,
251
+ "Owner": c.owner,
252
+ "BumpSeed": c.bump_seed,
253
+ "Status": c.status,
254
+ "ReferenceCount": c.reference_count,
255
+ "OpsManagerPk": c.ops_manager_pk,
256
+ },
257
+ )
258
+
259
+
260
+ class TestFixtureProgramConfig:
261
+ def test_deserialize(self):
262
+ data, meta = _load_fixture("program_config")
263
+ pc = ProgramConfig.from_bytes(data)
264
+ _assert_fields(
265
+ meta["fields"],
266
+ {
267
+ "AccountType": pc.account_type,
268
+ "BumpSeed": pc.bump_seed,
269
+ "VersionMajor": pc.version.major,
270
+ "VersionMinor": pc.version.minor,
271
+ "VersionPatch": pc.version.patch,
272
+ },
273
+ )
274
+
275
+
276
+ class TestFixtureAccessPass:
277
+ def test_deserialize(self):
278
+ data, meta = _load_fixture("access_pass")
279
+ ap = AccessPass.from_bytes(data)
280
+ _assert_fields(
281
+ meta["fields"],
282
+ {
283
+ "AccountType": ap.account_type,
284
+ "Owner": ap.owner,
285
+ "BumpSeed": ap.bump_seed,
286
+ "AccessPassType": ap.access_pass_type_tag,
287
+ "UserPayer": ap.user_payer,
288
+ "LastAccessEpoch": ap.last_access_epoch,
289
+ "ConnectionCount": ap.connection_count,
290
+ "Status": ap.status,
291
+ "Flags": ap.flags,
292
+ },
293
+ )
294
+
295
+
296
+ class TestFixtureAccessPassValidator:
297
+ def test_deserialize(self):
298
+ data, meta = _load_fixture("access_pass_validator")
299
+ ap = AccessPass.from_bytes(data)
300
+ _assert_fields(
301
+ meta["fields"],
302
+ {
303
+ "AccountType": ap.account_type,
304
+ "Owner": ap.owner,
305
+ "BumpSeed": ap.bump_seed,
306
+ "AccessPassType": ap.access_pass_type_tag,
307
+ "AccessPassTypeValidatorPubkey": ap.validator_pub_key,
308
+ "ClientIp": ap.client_ip,
309
+ "UserPayer": ap.user_payer,
310
+ "LastAccessEpoch": ap.last_access_epoch,
311
+ "ConnectionCount": ap.connection_count,
312
+ "Status": ap.status,
313
+ "Flags": ap.flags,
314
+ },
315
+ )
316
+ assert ap.account_type == 11
317
+ assert ap.bump_seed == 243
318
+ assert ap.access_pass_type_tag == 1
319
+ assert ap.validator_pub_key == Pubkey.from_string(
320
+ "BuP3jEYfnTCfB4UqQk9L37k2vaXsNuVsbWxrYbGDmL6s"
321
+ )
322
+ import ipaddress
323
+
324
+ assert ap.client_ip == ipaddress.IPv4Address("10.0.0.50").packed
325
+ assert ap.last_access_epoch == 1000
326
+ assert ap.connection_count == 1
327
+ assert ap.status == 1
328
+ assert len(ap.mgroup_pub_allowlist) == 1
329
+ assert len(ap.mgroup_sub_allowlist) == 1
330
+ assert ap.flags == 3
@@ -0,0 +1,36 @@
1
+ from solders.pubkey import Pubkey # type: ignore[import-untyped]
2
+
3
+ from serviceability.pda import (
4
+ derive_global_config_pda,
5
+ derive_global_state_pda,
6
+ derive_program_config_pda,
7
+ )
8
+
9
+ PROGRAM_ID = Pubkey.from_string("ser2VaTMAcYTaauMrTSfSrxBaUDq7BLNs2xfUugTAGv")
10
+
11
+
12
+ def test_derive_global_state_pda():
13
+ addr, bump = derive_global_state_pda(PROGRAM_ID)
14
+ assert addr != Pubkey.default()
15
+ addr2, bump2 = derive_global_state_pda(PROGRAM_ID)
16
+ assert addr == addr2
17
+ assert bump == bump2
18
+
19
+
20
+ def test_derive_global_config_pda():
21
+ addr, _ = derive_global_config_pda(PROGRAM_ID)
22
+ assert addr != Pubkey.default()
23
+
24
+
25
+ def test_derive_program_config_pda():
26
+ addr, _ = derive_program_config_pda(PROGRAM_ID)
27
+ assert addr != Pubkey.default()
28
+
29
+
30
+ def test_pdas_are_different():
31
+ gs, _ = derive_global_state_pda(PROGRAM_ID)
32
+ gc, _ = derive_global_config_pda(PROGRAM_ID)
33
+ pc, _ = derive_program_config_pda(PROGRAM_ID)
34
+ assert gs != gc
35
+ assert gs != pc
36
+ assert gc != pc