Skip to content

Commit a8233f0

Browse files
tac0turtlemergify[bot]
authored andcommitted
feat: make validator key injectable by application developers (#21608)
(cherry picked from commit f1dd03f) # Conflicts: # schema/diff/diff_test.go
1 parent 2174a3d commit a8233f0

File tree

7 files changed

+376
-8
lines changed

7 files changed

+376
-8
lines changed

runtime/app.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"slices"
77

88
abci "github.com/cometbft/cometbft/api/cometbft/abci/v1"
9+
cmtcrypto "github.com/cometbft/cometbft/crypto"
10+
cmted25519 "github.com/cometbft/cometbft/crypto/ed25519"
911
"google.golang.org/grpc"
1012

1113
runtimev1alpha1 "cosmossdk.io/api/cosmos/app/runtime/v1alpha1"
@@ -30,6 +32,9 @@ import (
3032
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
3133
)
3234

35+
// KeyGenF is a function that generates a private key for use by comet.
36+
type KeyGenF = func() (cmtcrypto.PrivKey, error)
37+
3338
// App is a wrapper around BaseApp and ModuleManager that can be used in hybrid
3439
// app.go/app config scenarios or directly as a servertypes.Application instance.
3540
// To get an instance of *App, *AppBuilder must be requested as a dependency
@@ -308,3 +313,10 @@ var _ servertypes.Application = &App{}
308313
type hasServicesV1 interface {
309314
RegisterServices(grpc.ServiceRegistrar) error
310315
}
316+
317+
// ValidatorKeyProvider returns a function that generates a private key for use by comet.
318+
func (a *App) ValidatorKeyProvider() KeyGenF {
319+
return func() (cmtcrypto.PrivKey, error) {
320+
return cmted25519.GenPrivKey(), nil
321+
}
322+
}

schema/diff/diff_test.go

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
package diff
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"cosmossdk.io/schema"
8+
)
9+
10+
func TestCompareModuleSchemas(t *testing.T) {
11+
tt := []struct {
12+
name string
13+
oldSchema schema.ModuleSchema
14+
newSchema schema.ModuleSchema
15+
diff ModuleSchemaDiff
16+
hasCompatibleChanges bool
17+
empty bool
18+
}{
19+
{
20+
name: "no change",
21+
oldSchema: requireModuleSchema(t, schema.StateObjectType{
22+
Name: "object1",
23+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
24+
}),
25+
newSchema: requireModuleSchema(t, schema.StateObjectType{
26+
Name: "object1",
27+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
28+
}),
29+
diff: ModuleSchemaDiff{},
30+
hasCompatibleChanges: true,
31+
empty: true,
32+
},
33+
{
34+
name: "object type added",
35+
oldSchema: requireModuleSchema(t),
36+
newSchema: requireModuleSchema(t, schema.StateObjectType{
37+
Name: "object1",
38+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
39+
}),
40+
diff: ModuleSchemaDiff{
41+
AddedStateObjectTypes: []schema.StateObjectType{
42+
{
43+
Name: "object1",
44+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
45+
},
46+
},
47+
},
48+
hasCompatibleChanges: true,
49+
},
50+
{
51+
name: "object type removed",
52+
oldSchema: requireModuleSchema(t, schema.StateObjectType{
53+
Name: "object1",
54+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
55+
}),
56+
newSchema: requireModuleSchema(t),
57+
diff: ModuleSchemaDiff{
58+
RemovedStateObjectTypes: []schema.StateObjectType{
59+
{
60+
Name: "object1",
61+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
62+
},
63+
},
64+
},
65+
hasCompatibleChanges: false,
66+
},
67+
{
68+
name: "object type changed, key field added",
69+
oldSchema: requireModuleSchema(t, schema.StateObjectType{
70+
Name: "object1",
71+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
72+
}),
73+
newSchema: requireModuleSchema(t, schema.StateObjectType{
74+
Name: "object1",
75+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}, {Name: "key2", Kind: schema.StringKind}},
76+
}),
77+
diff: ModuleSchemaDiff{
78+
ChangedStateObjectTypes: []StateObjectTypeDiff{
79+
{
80+
Name: "object1",
81+
KeyFieldsDiff: FieldsDiff{
82+
Added: []schema.Field{
83+
{Name: "key2", Kind: schema.StringKind},
84+
},
85+
},
86+
},
87+
},
88+
},
89+
hasCompatibleChanges: false,
90+
},
91+
{
92+
name: "object type changed, nullable value field added",
93+
oldSchema: requireModuleSchema(t, schema.StateObjectType{
94+
Name: "object1",
95+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
96+
}),
97+
newSchema: requireModuleSchema(t, schema.StateObjectType{
98+
Name: "object1",
99+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
100+
ValueFields: []schema.Field{{Name: "value1", Kind: schema.StringKind, Nullable: true}},
101+
}),
102+
diff: ModuleSchemaDiff{
103+
ChangedStateObjectTypes: []StateObjectTypeDiff{
104+
{
105+
Name: "object1",
106+
ValueFieldsDiff: FieldsDiff{
107+
Added: []schema.Field{{Name: "value1", Kind: schema.StringKind, Nullable: true}},
108+
},
109+
},
110+
},
111+
},
112+
hasCompatibleChanges: true,
113+
},
114+
{
115+
name: "object type changed, non-nullable value field added",
116+
oldSchema: requireModuleSchema(t, schema.StateObjectType{
117+
Name: "object1",
118+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
119+
}),
120+
newSchema: requireModuleSchema(t, schema.StateObjectType{
121+
Name: "object1",
122+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}},
123+
ValueFields: []schema.Field{{Name: "value1", Kind: schema.StringKind}},
124+
}),
125+
diff: ModuleSchemaDiff{
126+
ChangedStateObjectTypes: []StateObjectTypeDiff{
127+
{
128+
Name: "object1",
129+
ValueFieldsDiff: FieldsDiff{
130+
Added: []schema.Field{{Name: "value1", Kind: schema.StringKind}},
131+
},
132+
},
133+
},
134+
},
135+
hasCompatibleChanges: false,
136+
},
137+
{
138+
name: "object type changed, fields reordered",
139+
oldSchema: requireModuleSchema(t, schema.StateObjectType{
140+
Name: "object1",
141+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.StringKind}, {Name: "key2", Kind: schema.StringKind}},
142+
}),
143+
newSchema: requireModuleSchema(t, schema.StateObjectType{
144+
Name: "object1",
145+
KeyFields: []schema.Field{{Name: "key2", Kind: schema.StringKind}, {Name: "key1", Kind: schema.StringKind}},
146+
}),
147+
diff: ModuleSchemaDiff{
148+
ChangedStateObjectTypes: []StateObjectTypeDiff{
149+
{
150+
Name: "object1",
151+
KeyFieldsDiff: FieldsDiff{
152+
OldOrder: []string{"key1", "key2"},
153+
NewOrder: []string{"key2", "key1"},
154+
},
155+
},
156+
},
157+
},
158+
hasCompatibleChanges: false,
159+
},
160+
{
161+
name: "enum type added, nullable value field added",
162+
oldSchema: requireModuleSchema(t, schema.StateObjectType{
163+
Name: "object1",
164+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.Int32Kind}},
165+
}),
166+
newSchema: requireModuleSchema(t, schema.StateObjectType{
167+
Name: "object1",
168+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.Int32Kind}},
169+
ValueFields: []schema.Field{
170+
{
171+
Name: "value1",
172+
Kind: schema.EnumKind,
173+
ReferencedType: "enum1",
174+
Nullable: true,
175+
},
176+
},
177+
},
178+
schema.EnumType{Name: "enum1", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}, {Name: "b", Value: 2}}}),
179+
diff: ModuleSchemaDiff{
180+
ChangedStateObjectTypes: []StateObjectTypeDiff{
181+
{
182+
Name: "object1",
183+
ValueFieldsDiff: FieldsDiff{
184+
Added: []schema.Field{
185+
{
186+
Name: "value1",
187+
Kind: schema.EnumKind,
188+
ReferencedType: "enum1",
189+
Nullable: true,
190+
},
191+
},
192+
},
193+
},
194+
},
195+
AddedEnumTypes: []schema.EnumType{
196+
{Name: "enum1", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}, {Name: "b", Value: 2}}},
197+
},
198+
},
199+
hasCompatibleChanges: true,
200+
},
201+
{
202+
name: "enum type removed",
203+
oldSchema: requireModuleSchema(t,
204+
schema.StateObjectType{
205+
Name: "object1",
206+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.Int32Kind}},
207+
ValueFields: []schema.Field{
208+
{
209+
Name: "value1",
210+
Kind: schema.EnumKind,
211+
ReferencedType: "enum1",
212+
},
213+
},
214+
},
215+
schema.EnumType{Name: "enum1", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}, {Name: "b", Value: 2}}}),
216+
newSchema: requireModuleSchema(t, schema.StateObjectType{
217+
Name: "object1",
218+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.Int32Kind}},
219+
}),
220+
diff: ModuleSchemaDiff{
221+
ChangedStateObjectTypes: []StateObjectTypeDiff{
222+
{
223+
Name: "object1",
224+
ValueFieldsDiff: FieldsDiff{
225+
Removed: []schema.Field{
226+
{
227+
Name: "value1",
228+
Kind: schema.EnumKind,
229+
ReferencedType: "enum1",
230+
},
231+
},
232+
},
233+
},
234+
},
235+
RemovedEnumTypes: []schema.EnumType{
236+
{Name: "enum1", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}, {Name: "b", Value: 2}}},
237+
},
238+
},
239+
hasCompatibleChanges: false,
240+
},
241+
{
242+
name: "enum value added",
243+
oldSchema: requireModuleSchema(t,
244+
schema.EnumType{Name: "enum1", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}}},
245+
),
246+
newSchema: requireModuleSchema(t,
247+
schema.EnumType{Name: "enum1", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}, {Name: "b", Value: 2}}},
248+
),
249+
diff: ModuleSchemaDiff{
250+
ChangedEnumTypes: []EnumTypeDiff{
251+
{
252+
Name: "enum1",
253+
AddedValues: []schema.EnumValueDefinition{{Name: "b", Value: 2}},
254+
},
255+
},
256+
},
257+
hasCompatibleChanges: true,
258+
},
259+
{
260+
name: "enum value removed",
261+
oldSchema: requireModuleSchema(t,
262+
schema.EnumType{Name: "enum1", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}, {Name: "b", Value: 2}, {Name: "c", Value: 3}}},
263+
),
264+
newSchema: requireModuleSchema(t,
265+
schema.EnumType{Name: "enum1", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}, {Name: "b", Value: 2}}},
266+
),
267+
diff: ModuleSchemaDiff{
268+
ChangedEnumTypes: []EnumTypeDiff{
269+
{
270+
Name: "enum1",
271+
RemovedValues: []schema.EnumValueDefinition{{Name: "c", Value: 3}},
272+
},
273+
},
274+
},
275+
hasCompatibleChanges: false,
276+
},
277+
{
278+
name: "object type and enum type name switched",
279+
oldSchema: requireModuleSchema(t,
280+
schema.StateObjectType{
281+
Name: "foo",
282+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.EnumKind, ReferencedType: "bar"}},
283+
},
284+
schema.EnumType{Name: "bar", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}}},
285+
),
286+
newSchema: requireModuleSchema(t,
287+
schema.StateObjectType{
288+
Name: "bar",
289+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.EnumKind, ReferencedType: "foo"}},
290+
},
291+
schema.EnumType{Name: "foo", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}}},
292+
),
293+
diff: ModuleSchemaDiff{
294+
RemovedStateObjectTypes: []schema.StateObjectType{
295+
{
296+
Name: "foo",
297+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.EnumKind, ReferencedType: "bar"}},
298+
},
299+
},
300+
AddedStateObjectTypes: []schema.StateObjectType{
301+
{
302+
Name: "bar",
303+
KeyFields: []schema.Field{{Name: "key1", Kind: schema.EnumKind, ReferencedType: "foo"}},
304+
},
305+
},
306+
RemovedEnumTypes: []schema.EnumType{
307+
{Name: "bar", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}}},
308+
},
309+
AddedEnumTypes: []schema.EnumType{
310+
{Name: "foo", Values: []schema.EnumValueDefinition{{Name: "a", Value: 1}}},
311+
},
312+
},
313+
hasCompatibleChanges: false,
314+
},
315+
}
316+
317+
for _, tc := range tt {
318+
t.Run(tc.name, func(t *testing.T) {
319+
got := CompareModuleSchemas(tc.oldSchema, tc.newSchema)
320+
if !reflect.DeepEqual(got, tc.diff) {
321+
t.Errorf("CompareModuleSchemas() = %v, want %v", got, tc.diff)
322+
}
323+
hasCompatibleChanges := got.HasCompatibleChanges()
324+
if hasCompatibleChanges != tc.hasCompatibleChanges {
325+
t.Errorf("HasCompatibleChanges() = %v, want %v", hasCompatibleChanges, tc.hasCompatibleChanges)
326+
}
327+
if tc.empty != got.Empty() {
328+
t.Errorf("Empty() = %v, want %v", got.Empty(), tc.empty)
329+
}
330+
})
331+
}
332+
}
333+
334+
func requireModuleSchema(t *testing.T, types ...schema.Type) schema.ModuleSchema {
335+
t.Helper()
336+
s, err := schema.CompileModuleSchema(types...)
337+
if err != nil {
338+
t.Fatal(err)
339+
}
340+
return s
341+
}

server/start.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,7 @@ func startCmtNode(
374374
return nil, cleanupFn, err
375375
}
376376

377-
pv, err := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile(), func() (cmtcrypto.PrivKey, error) {
378-
return cmted25519.GenPrivKey(), nil
379-
}) // TODO: make this modular
377+
pv, err := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile(), app.ValidatorKeyProvider())
380378
if err != nil {
381379
return nil, cleanupFn, err
382380
}

0 commit comments

Comments
 (0)