Skip to content

Commit e9e96c4

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/version-gt-release-beats-prerelease
2 parents c6f48d9 + 3f1efe9 commit e9e96c4

7 files changed

Lines changed: 213 additions & 11 deletions

File tree

‎.github/CODEOWNERS‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @ancazamfir @romac @ZhiyuCircle @sergio-mena

‎crates/execution-config/src/defaults.rs‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use reth_cli_commands::download::DownloadDefaults;
2323
use reth_node_core::args::DefaultRpcServerArgs;
2424
use std::borrow::Cow;
2525

26-
// FIXME: Update this to the actual snapshot URL.
2726
/// Default snapshot URL for Arc Network testnet (chain ID 5042002).
2827
pub(crate) const DEFAULT_DOWNLOAD_URL: &str = "https://snapshots.arc.network/5042002";
2928

@@ -36,7 +35,6 @@ pub const RPC_MAX_SUBSCRIPTIONS_PER_CONNECTION: u32 = 32;
3635
fn init_download_urls() {
3736
let download_defaults = DownloadDefaults {
3837
available_snapshots: vec![
39-
// FIXME: Update this to the actual snapshot URL.
4038
Cow::Borrowed("https://snapshots.arc.network/5042002 (testnet)"),
4139
Cow::Borrowed("https://snapshots.arc.network/5042001 (devnet)"),
4240
],

‎docs/running-an-arc-node.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ rm -rf ~/.arc
405405

406406
> [!WARNING]
407407
> Running EL and CL on separate hosts requires the RPC/HTTP Engine API transport,
408-
> which will be deprecated in `v0.8.0` and will be removed in `v0.9.0`.
408+
> which is deprecated as of `v0.8.0` and will be removed in `v0.9.0`.
409409
> Run both layers on the same host and use IPC instead (see the
410410
> [Binaries](#binaries) section).
411411
> The consensus layer logs a startup warning when any RPC option is set.

‎scripts/genesis/ProtocolConfig.ts‎

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ const PROTOCOL_CONFIG_CONTROLLER_STORAGE_LOCATION = 0x958f8fec699b51a1249f513ece
4444
const PAUSABLE_STORAGE_LOCATION = 0x0642d7922329a434cf4fd17a3c95eb692c24fd95f9f94d0b55420a5d895f4a00n
4545

4646
const maxUint64 = 18446744073709551615n
47+
const maxUint16 = 65535n
48+
// Packed into one storage slot as 8×uint16. Runtime updateConsensusParams also
49+
// requires each timeout (except targetBlockTimeMs) to be > 0.
50+
const schemaConsensusTimeoutMs = schemaBigInt.min(1n).max(maxUint16)
51+
const schemaTargetBlockTimeMs = schemaBigInt.min(0n).max(maxUint16)
4752

4853
export const schemaProtocolConfig = z
4954
.object({
@@ -75,14 +80,14 @@ export const schemaProtocolConfig = z
7580
}),
7681
consensusParams: z
7782
.object({
78-
timeoutProposeMs: schemaBigInt,
79-
timeoutProposeDeltaMs: schemaBigInt,
80-
timeoutPrevoteMs: schemaBigInt,
81-
timeoutPrevoteDeltaMs: schemaBigInt,
82-
timeoutPrecommitMs: schemaBigInt,
83-
timeoutPrecommitDeltaMs: schemaBigInt,
84-
timeoutRebroadcastMs: schemaBigInt,
85-
targetBlockTimeMs: schemaBigInt,
83+
timeoutProposeMs: schemaConsensusTimeoutMs,
84+
timeoutProposeDeltaMs: schemaConsensusTimeoutMs,
85+
timeoutPrevoteMs: schemaConsensusTimeoutMs,
86+
timeoutPrevoteDeltaMs: schemaConsensusTimeoutMs,
87+
timeoutPrecommitMs: schemaConsensusTimeoutMs,
88+
timeoutPrecommitDeltaMs: schemaConsensusTimeoutMs,
89+
timeoutRebroadcastMs: schemaConsensusTimeoutMs,
90+
targetBlockTimeMs: schemaTargetBlockTimeMs,
8691
})
8792
.optional(),
8893
})
@@ -93,6 +98,15 @@ export const schemaProtocolConfig = z
9398
{ key: 'controller', value: data.controller },
9499
{ key: 'pauser', value: data.pauser },
95100
])
101+
102+
// Match updateFeeParams: minBaseFee must not exceed maxBaseFee.
103+
if (data.feeParams.minBaseFee > data.feeParams.maxBaseFee) {
104+
ctx.addIssue({
105+
code: z.ZodIssueCode.custom,
106+
path: ['feeParams', 'minBaseFee'],
107+
message: 'minBaseFee must be <= maxBaseFee',
108+
})
109+
}
96110
})
97111

98112
export type ProtocolConfigConfig = z.infer<typeof schemaProtocolConfig>

‎scripts/genesis/ValidatorManager.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ export const schemaValidatorManager = z
100100
})
101101
.strict()
102102
.superRefine((data, ctx) => {
103+
if (!data.validators.some((validator) => validator.votingPower > 0n)) {
104+
ctx.addIssue({
105+
code: z.ZodIssueCode.custom,
106+
path: ['validators'],
107+
message: 'At least one validator must have positive voting power',
108+
})
109+
}
103110
// Verify the public keys are unique.
104111
const publicKeySet = new Set()
105112
for (const validator of data.validators) {
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright 2026 Circle Internet Group, Inc. All rights reserved.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
import { expect } from 'chai'
18+
import { schemaProtocolConfig } from '../../scripts/genesis/ProtocolConfig'
19+
20+
const proxyAdmin = '0x0000000000000000000000000000000000000001'
21+
const owner = '0x0000000000000000000000000000000000000002'
22+
const controller = '0x0000000000000000000000000000000000000003'
23+
const pauser = '0x0000000000000000000000000000000000000004'
24+
25+
const validConsensusParams = {
26+
timeoutProposeMs: 3000n,
27+
timeoutProposeDeltaMs: 500n,
28+
timeoutPrevoteMs: 1000n,
29+
timeoutPrevoteDeltaMs: 500n,
30+
timeoutPrecommitMs: 1000n,
31+
timeoutPrecommitDeltaMs: 500n,
32+
timeoutRebroadcastMs: 2000n,
33+
targetBlockTimeMs: 1000n,
34+
}
35+
36+
const baseConfig = {
37+
proxy: {
38+
admin: proxyAdmin,
39+
},
40+
owner,
41+
controller,
42+
pauser,
43+
feeParams: {
44+
alpha: 1n,
45+
kRate: 1n,
46+
inverseElasticityMultiplier: 1n,
47+
minBaseFee: 0n,
48+
maxBaseFee: 1_000_000n,
49+
blockGasLimit: 30_000_000n,
50+
},
51+
consensusParams: validConsensusParams,
52+
}
53+
54+
describe('ProtocolConfig genesis schema', () => {
55+
it('accepts a valid config', () => {
56+
expect(() => schemaProtocolConfig.parse(baseConfig)).to.not.throw()
57+
})
58+
59+
it('rejects zero consensus timeouts that updateConsensusParams also rejects', () => {
60+
const timeoutKeys = [
61+
'timeoutProposeMs',
62+
'timeoutProposeDeltaMs',
63+
'timeoutPrevoteMs',
64+
'timeoutPrevoteDeltaMs',
65+
'timeoutPrecommitMs',
66+
'timeoutPrecommitDeltaMs',
67+
'timeoutRebroadcastMs',
68+
] as const
69+
70+
for (const key of timeoutKeys) {
71+
expect(
72+
() =>
73+
schemaProtocolConfig.parse({
74+
...baseConfig,
75+
consensusParams: {
76+
...validConsensusParams,
77+
[key]: 0n,
78+
},
79+
}),
80+
`${key} should reject 0`,
81+
).to.throw()
82+
}
83+
})
84+
85+
it('rejects consensus params above uint16 max to prevent packed-slot overflow', () => {
86+
expect(() =>
87+
schemaProtocolConfig.parse({
88+
...baseConfig,
89+
consensusParams: {
90+
...validConsensusParams,
91+
timeoutProposeMs: 65536n,
92+
},
93+
}),
94+
).to.throw()
95+
})
96+
97+
it('rejects minBaseFee greater than maxBaseFee', () => {
98+
expect(() =>
99+
schemaProtocolConfig.parse({
100+
...baseConfig,
101+
feeParams: {
102+
...baseConfig.feeParams,
103+
minBaseFee: 1000n,
104+
maxBaseFee: 1n,
105+
},
106+
}),
107+
).to.throw()
108+
})
109+
})
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2026 Circle Internet Group, Inc. All rights reserved.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
import { expect } from 'chai'
6+
import { schemaValidatorManager } from '../../scripts/genesis/ValidatorManager'
7+
8+
const REGISTRY_ADMIN = '0x1111111111111111111111111111111111111111'
9+
const PVM_ADMIN = '0x2222222222222222222222222222222222222222'
10+
const OWNER = '0x3333333333333333333333333333333333333333'
11+
const PAUSER = '0x4444444444444444444444444444444444444444'
12+
const REGISTERER = '0x5555555555555555555555555555555555555555'
13+
const CONTROLLER_A = '0x6666666666666666666666666666666666666666'
14+
const CONTROLLER_B = '0x7777777777777777777777777777777777777777'
15+
16+
const PUBLIC_KEY_A = `0x${'11'.repeat(32)}`
17+
const PUBLIC_KEY_B = `0x${'22'.repeat(32)}`
18+
19+
const validator = (publicKey: string, controller: string, votingPower: bigint) => ({
20+
publicKey,
21+
votingPower,
22+
controllers: [
23+
{
24+
address: controller,
25+
votingPowerLimit: 100n,
26+
},
27+
],
28+
})
29+
30+
const configWithValidators = (validators: ReturnType<typeof validator>[]) => ({
31+
proxy: {
32+
admin: REGISTRY_ADMIN,
33+
},
34+
validators,
35+
PermissionedValidatorManager: {
36+
proxy: {
37+
admin: PVM_ADMIN,
38+
},
39+
owner: OWNER,
40+
pauser: PAUSER,
41+
validatorRegisterers: [REGISTERER],
42+
},
43+
})
44+
45+
describe('ValidatorManager genesis validator-set validation', () => {
46+
it('rejects an empty validator set', () => {
47+
const result = schemaValidatorManager.safeParse(configWithValidators([]))
48+
49+
expect(result.success).to.be.false
50+
})
51+
52+
it('rejects a validator set with no positive voting power', () => {
53+
const result = schemaValidatorManager.safeParse(
54+
configWithValidators([validator(PUBLIC_KEY_A, CONTROLLER_A, 0n), validator(PUBLIC_KEY_B, CONTROLLER_B, 0n)]),
55+
)
56+
57+
expect(result.success).to.be.false
58+
})
59+
60+
it('accepts a validator set with positive voting power', () => {
61+
const result = schemaValidatorManager.safeParse(configWithValidators([validator(PUBLIC_KEY_A, CONTROLLER_A, 20n)]))
62+
63+
expect(result.success).to.be.true
64+
})
65+
66+
it('accepts zero-power validators when another validator has positive power', () => {
67+
const result = schemaValidatorManager.safeParse(
68+
configWithValidators([validator(PUBLIC_KEY_A, CONTROLLER_A, 0n), validator(PUBLIC_KEY_B, CONTROLLER_B, 20n)]),
69+
)
70+
71+
expect(result.success).to.be.true
72+
})
73+
})

0 commit comments

Comments
 (0)