@@ -28,18 +28,179 @@ import (
28
28
)
29
29
30
30
const (
31
- defaultEncoding = formatting .Hex
32
- codecVersion = 0
31
+ defaultEncoding = formatting .Hex
32
+ codecVersion = 0
33
+ configChainIDAlias = "X"
33
34
)
34
35
36
+ // validateInitialStakedFunds ensures all staked
37
+ // funds have allocations and that all staked
38
+ // funds are unique.
39
+ //
40
+ // This function assumes that NetworkID in *Config has already
41
+ // been checked for correctness.
42
+ func validateInitialStakedFunds (config * Config ) error {
43
+ if len (config .InitialStakedFunds ) == 0 {
44
+ return errors .New ("initial staked funds cannot be empty" )
45
+ }
46
+
47
+ allocationSet := ids.ShortSet {}
48
+ initialStakedFundsSet := ids.ShortSet {}
49
+ for _ , allocation := range config .Allocations {
50
+ // It is ok to have duplicates as different
51
+ // ethAddrs could claim to the same avaxAddr.
52
+ allocationSet .Add (allocation .AVAXAddr )
53
+ }
54
+
55
+ for _ , staker := range config .InitialStakedFunds {
56
+ if initialStakedFundsSet .Contains (staker ) {
57
+ avaxAddr , err := formatting .FormatAddress (
58
+ configChainIDAlias ,
59
+ constants .GetHRP (config .NetworkID ),
60
+ staker .Bytes (),
61
+ )
62
+ if err != nil {
63
+ return fmt .Errorf (
64
+ "unable to format address from %s" ,
65
+ staker .String (),
66
+ )
67
+ }
68
+
69
+ return fmt .Errorf (
70
+ "address %s is duplicated in initial staked funds" ,
71
+ avaxAddr ,
72
+ )
73
+ }
74
+ initialStakedFundsSet .Add (staker )
75
+
76
+ if ! allocationSet .Contains (staker ) {
77
+ avaxAddr , err := formatting .FormatAddress (
78
+ configChainIDAlias ,
79
+ constants .GetHRP (config .NetworkID ),
80
+ staker .Bytes (),
81
+ )
82
+ if err != nil {
83
+ return fmt .Errorf (
84
+ "unable to format address from %s" ,
85
+ staker .String (),
86
+ )
87
+ }
88
+
89
+ return fmt .Errorf (
90
+ "address %s does not have an allocation to stake" ,
91
+ avaxAddr ,
92
+ )
93
+ }
94
+ }
95
+
96
+ return nil
97
+ }
98
+
99
+ // validateConfig returns an error if the provided
100
+ // *Config is not considered valid.
101
+ func validateConfig (networkID uint32 , config * Config ) error {
102
+ if networkID != config .NetworkID {
103
+ return fmt .Errorf (
104
+ "networkID %d specified but genesis config contains networkID %d" ,
105
+ networkID ,
106
+ config .NetworkID ,
107
+ )
108
+ }
109
+
110
+ initialSupply , err := config .InitialSupply ()
111
+ switch {
112
+ case err != nil :
113
+ return fmt .Errorf ("unable to calculate initial supply: %w" , err )
114
+ case initialSupply == 0 :
115
+ return errors .New ("initial supply must be > 0" )
116
+ }
117
+
118
+ startTime := time .Unix (int64 (config .StartTime ), 0 )
119
+ if time .Since (startTime ) < 0 {
120
+ return fmt .Errorf (
121
+ "start time cannot be in the future: %s" ,
122
+ startTime ,
123
+ )
124
+ }
125
+
126
+ // We don't impose any restrictions on the minimum
127
+ // stake duration to enable complex testing configurations
128
+ // but recommend setting a minimum duration of at least
129
+ // 15 minutes.
130
+ if config .InitialStakeDuration == 0 {
131
+ return errors .New ("initial stake duration must be > 0" )
132
+ }
133
+
134
+ if len (config .InitialStakers ) == 0 {
135
+ return errors .New ("initial stakers must be > 0" )
136
+ }
137
+
138
+ offsetTimeRequired := config .InitialStakeDurationOffset * uint64 (len (config .InitialStakers )- 1 )
139
+ if offsetTimeRequired > config .InitialStakeDuration {
140
+ return fmt .Errorf (
141
+ "initial stake duration is %d but need at least %d with offset of %d" ,
142
+ config .InitialStakeDuration ,
143
+ offsetTimeRequired ,
144
+ config .InitialStakeDurationOffset ,
145
+ )
146
+ }
147
+
148
+ if err := validateInitialStakedFunds (config ); err != nil {
149
+ return fmt .Errorf ("initial staked funds validation failed: %w" , err )
150
+ }
151
+
152
+ if len (config .CChainGenesis ) == 0 {
153
+ return errors .New ("C-Chain genesis cannot be empty" )
154
+ }
155
+
156
+ return nil
157
+ }
158
+
35
159
// Genesis returns the genesis data of the Platform Chain.
36
160
//
37
161
// Since an Avalanche network has exactly one Platform Chain, and the Platform
38
162
// Chain defines the genesis state of the network (who is staking, which chains
39
163
// exist, etc.), defining the genesis state of the Platform Chain is the same as
40
164
// defining the genesis state of the network.
41
165
//
42
- // The ID of the new network is [networkID].
166
+ // Genesis accepts:
167
+ // 1) The ID of the new network. [networkID]
168
+ // 2) The location of a custom genesis config to load. [filepath]
169
+ //
170
+ // If [filepath] is empty or the given network ID is Mainnet, Testnet, or Local, loads the
171
+ // network genesis state from predefined configs. If [filepath] is non-empty and networkID
172
+ // isn't Mainnet, Testnet, or Local, loads the network genesis data from the config at [filepath].
173
+ //
174
+ // Genesis returns:
175
+ // 1) The byte representation of the genesis state of the platform chain
176
+ // (ie the genesis state of the network)
177
+ // 2) The asset ID of AVAX
178
+ func Genesis (networkID uint32 , filepath string ) ([]byte , ids.ID , error ) {
179
+ config := GetConfig (networkID )
180
+ if len (filepath ) > 0 {
181
+ switch networkID {
182
+ case constants .MainnetID , constants .TestnetID , constants .LocalID :
183
+ return nil , ids.ID {}, fmt .Errorf (
184
+ "cannot override genesis config for standard network %s (%d)" ,
185
+ constants .NetworkName (networkID ),
186
+ networkID ,
187
+ )
188
+ }
189
+
190
+ customConfig , err := GetConfigFile (filepath )
191
+ if err != nil {
192
+ return nil , ids.ID {}, fmt .Errorf ("unable to load provided genesis config at %s: %w" , filepath , err )
193
+ }
194
+
195
+ config = customConfig
196
+ }
197
+
198
+ if err := validateConfig (networkID , config ); err != nil {
199
+ return nil , ids.ID {}, fmt .Errorf ("genesis config validation failed: %w" , err )
200
+ }
201
+
202
+ return FromConfig (config )
203
+ }
43
204
44
205
// FromConfig returns:
45
206
// 1) The byte representation of the genesis state of the platform chain
@@ -313,20 +474,8 @@ func splitAllocations(allocations []Allocation, numSplits int) [][]Allocation {
313
474
return append (allNodeAllocations , currentNodeAllocation )
314
475
}
315
476
316
- // Genesis returns:
317
- // 1) The byte representation of the genesis state of the platform chain
318
- // (ie the genesis state of the network)
319
- // 2) The asset ID of AVAX
320
- func Genesis (networkID uint32 ) ([]byte , ids.ID , error ) {
321
- return FromConfig (GetConfig (networkID ))
322
- }
323
-
324
477
// VMGenesis ...
325
- func VMGenesis (networkID uint32 , vmID ids.ID ) (* platformvm.Tx , error ) {
326
- genesisBytes , _ , err := Genesis (networkID )
327
- if err != nil {
328
- return nil , err
329
- }
478
+ func VMGenesis (genesisBytes []byte , vmID ids.ID ) (* platformvm.Tx , error ) {
330
479
genesis := platformvm.Genesis {}
331
480
if _ , err := platformvm .GenesisCodec .Unmarshal (genesisBytes , & genesis ); err != nil {
332
481
return nil , fmt .Errorf ("couldn't unmarshal genesis bytes due to: %w" , err )
0 commit comments