/
genesis.go
333 lines (310 loc) · 11.8 KB
/
genesis.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
// Copyright (c) 2019 IoTeX
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
// License 2.0 that can be found in the LICENSE file.
package genesis
import (
"flag"
"math/big"
"sort"
"time"
"github.com/golang/protobuf/proto"
"github.com/pkg/errors"
"go.uber.org/config"
"go.uber.org/zap"
"github.com/iotexproject/iotex-core/address"
"github.com/iotexproject/iotex-core/pkg/hash"
"github.com/iotexproject/iotex-core/pkg/log"
"github.com/iotexproject/iotex-core/pkg/unit"
"github.com/iotexproject/iotex-core/protogen/iotextypes"
"github.com/iotexproject/iotex-core/test/identityset"
)
var (
// Default contains the default genesis config
Default Genesis
genesisPath string
)
func init() {
flag.StringVar(&genesisPath, "genesis-path", "", "Genesis path")
initDefaultConfig()
}
func initDefaultConfig() {
Default = Genesis{
Blockchain: Blockchain{
Timestamp: 1546329600,
BlockGasLimit: 20000000,
ActionGasLimit: 5000000,
BlockInterval: 10 * time.Second,
NumSubEpochs: 2,
NumDelegates: 24,
NumCandidateDelegates: 36,
TimeBasedRotation: false,
},
Account: Account{
InitBalanceMap: make(map[string]string),
},
Poll: Poll{
EnableGravityChainVoting: false,
},
Rewarding: Rewarding{
InitAdminAddrStr: identityset.Address(0).String(),
InitBalanceStr: unit.ConvertIotxToRau(1200000000).String(),
BlockRewardStr: unit.ConvertIotxToRau(36).String(),
EpochRewardStr: unit.ConvertIotxToRau(400000).String(),
NumDelegatesForEpochReward: 100,
},
}
for i := 0; i < identityset.Size(); i++ {
addr := identityset.Address(i).String()
value := unit.ConvertIotxToRau(100000000).String()
Default.InitBalanceMap[addr] = value
if uint64(i) < Default.NumDelegates {
Default.Delegates = append(Default.Delegates, Delegate{
OperatorAddrStr: addr,
RewardAddrStr: addr,
VotesStr: value,
})
}
}
}
type (
// Genesis is the root level of genesis config. Genesis config is the network-wide blockchain config. All the nodes
// participating into the same network should use EXACTLY SAME genesis config.
Genesis struct {
Blockchain `yaml:"blockchain"`
Account `ymal:"account"`
Poll `yaml:"poll"`
Rewarding `yaml:"rewarding"`
}
// Blockchain contains blockchain level configs
Blockchain struct {
// Timestamp is the timestamp of the genesis block
Timestamp int64
// BlockGasLimit is the total gas limit could be consumed in a block
BlockGasLimit uint64 `yaml:"blockGasLimit"`
// ActionGasLimit is the per action gas limit cap
ActionGasLimit uint64 `yaml:"actionGasLimit"`
// BlockInterval is the interval between two blocks
BlockInterval time.Duration `yaml:"blockInterval"`
// NumSubEpochs is the number of sub epochs in one epoch of block production
NumSubEpochs uint64 `yaml:"numSubEpochs"`
// NumDelegates is the number of delegates that participate into one epoch of block production
NumDelegates uint64 `yaml:"numDelegates"`
// NumCandidateDelegates is the number of candidate delegates, who may be selected as a delegate via roll dpos
NumCandidateDelegates uint64 `yaml:"numCandidateDelegates"`
// TimeBasedRotation is the flag to enable rotating delegates' time slots on a block height
TimeBasedRotation bool `yaml:"timeBasedRotation"`
}
// Account contains the configs for account protocol
Account struct {
// InitBalanceMap is the address and initial balance mapping before the first block.
InitBalanceMap map[string]string `yaml:"initBalances"`
}
// Poll contains the configs for poll protocol
Poll struct {
// EnableGravityChainVoting is a flag whether read voting from gravity chain
EnableGravityChainVoting bool `yaml:"enableGravityChainVoting"`
// GravityChainStartHeight is the height in gravity chain where the init poll result stored
GravityChainStartHeight uint64 `yaml:"gravityChainStartHeight"`
// RegisterContractAddress is the address of register contract
RegisterContractAddress string `yaml:"registerContractAddress"`
// StakingContractAddress is the address of staking contract
StakingContractAddress string `yaml:"stakingContractAddress"`
// VoteThreshold is the vote threshold amount in decimal string format
VoteThreshold string `yaml:"voteThreshold"`
// ScoreThreshold is the score threshold amount in decimal string format
ScoreThreshold string `yaml:"scoreThreshold"`
// SelfStakingThreshold is self-staking vote threshold amount in decimal string format
SelfStakingThreshold string `yaml:"selfStakingThreshold"`
// Delegates is a list of delegates with votes
Delegates []Delegate `yaml:"delegates"`
}
// Delegate defines a delegate with address and votes
Delegate struct {
// OperatorAddrStr is the address who will operate the node
OperatorAddrStr string `yaml:"operatorAddr"`
// RewardAddrStr is the address who will get the reward when operator produces blocks
RewardAddrStr string `yaml:"rewardAddr"`
// VotesStr is the score for the operator to rank and weight for rewardee to split epoch reward
VotesStr string `yaml:"votes"`
}
// Rewarding contains the configs for rewarding protocol
Rewarding struct {
// InitAdminAddrStr is the address of the initial rewarding protocol admin in encoded string format
InitAdminAddrStr string `yaml:"initAdminAddr"`
// InitBalanceStr is the initial balance of the rewarding protocol in decimal string format
InitBalanceStr string `yaml:"initBalance"`
// BlockReward is the block reward amount in decimal string format
BlockRewardStr string `yaml:"blockReward"`
// EpochReward is the epoch reward amount in decimal string format
EpochRewardStr string `yaml:"epochReward"`
// NumDelegatesForEpochReward is the number of top candidates that will share a epoch reward
NumDelegatesForEpochReward uint64 `yaml:"numDelegatesForEpochReward"`
}
)
// New constructs a genesis config. It loads the default values, and could be overwritten by values defined in the yaml
// config files
func New() (Genesis, error) {
opts := make([]config.YAMLOption, 0)
opts = append(opts, config.Static(Default))
if genesisPath != "" {
opts = append(opts, config.File(genesisPath))
}
yaml, err := config.NewYAML(opts...)
if err != nil {
return Genesis{}, errors.Wrap(err, "error when constructing a genesis in yaml")
}
var genesis Genesis
if err := yaml.Get(config.Root).Populate(&genesis); err != nil {
return Genesis{}, errors.Wrap(err, "failed to unmarshal yaml genesis to struct")
}
return genesis, nil
}
// Hash is the hash of genesis config
func (g *Genesis) Hash() hash.Hash256 {
gbProto := iotextypes.GenesisBlockchain{
Timestamp: g.Timestamp,
BlockGasLimit: g.BlockGasLimit,
ActionGasLimit: g.ActionGasLimit,
BlockInterval: g.BlockInterval.Nanoseconds(),
NumSubEpochs: g.NumSubEpochs,
NumDelegates: g.NumDelegates,
NumCandidateDelegates: g.NumCandidateDelegates,
TimeBasedRotation: g.TimeBasedRotation,
}
initBalanceAddrs := make([]string, 0)
for initBalanceAddr := range g.InitBalanceMap {
initBalanceAddrs = append(initBalanceAddrs, initBalanceAddr)
}
sort.Strings(initBalanceAddrs)
initBalances := make([]string, 0)
for _, initBalanceAddr := range initBalanceAddrs {
initBalances = append(initBalances, g.InitBalanceMap[initBalanceAddr])
}
aProto := iotextypes.GenesisAccount{
InitBalanceAddrs: initBalanceAddrs,
InitBalances: initBalances,
}
dProtos := make([]*iotextypes.GenesisDelegate, 0)
for _, d := range g.Delegates {
dProto := iotextypes.GenesisDelegate{
OperatorAddr: d.OperatorAddrStr,
RewardAddr: d.RewardAddrStr,
Votes: d.VotesStr,
}
dProtos = append(dProtos, &dProto)
}
pProto := iotextypes.GenesisPoll{
EnableGravityChainVoting: g.EnableGravityChainVoting,
GravityChainStartHeight: g.GravityChainStartHeight,
RegisterContractAddress: g.RegisterContractAddress,
StakingContractAddress: g.StakingContractAddress,
VoteThreshold: g.VoteThreshold,
ScoreThreshold: g.ScoreThreshold,
SelfStakingThreshold: g.SelfStakingThreshold,
Delegates: dProtos,
}
rProto := iotextypes.GenesisRewarding{
InitAdminAddr: g.InitAdminAddrStr,
InitBalance: g.InitBalanceStr,
BlockReward: g.BlockRewardStr,
EpochReward: g.EpochRewardStr,
NumDelegatesForEpochReward: g.NumDelegatesForEpochReward,
}
gProto := iotextypes.Genesis{
Blockchain: &gbProto,
Account: &aProto,
Poll: &pProto,
Rewarding: &rProto,
}
b, err := proto.Marshal(&gProto)
if err != nil {
log.L().Panic("Error when marshaling genesis proto", zap.Error(err))
}
return hash.Hash256b(b)
}
// InitBalances returns the address that have initial balances and the corresponding amounts. The i-th amount is the
// i-th address' balance.
func (a *Account) InitBalances() ([]address.Address, []*big.Int) {
// Make the list always be ordered
addrStrs := make([]string, 0)
for addrStr := range a.InitBalanceMap {
addrStrs = append(addrStrs, addrStr)
}
sort.Strings(addrStrs)
addrs := make([]address.Address, 0)
amounts := make([]*big.Int, 0)
for _, addrStr := range addrStrs {
addr, err := address.FromString(addrStr)
if err != nil {
log.L().Panic("Error when decoding the account protocol init balance address from string.", zap.Error(err))
}
addrs = append(addrs, addr)
amount, ok := big.NewInt(0).SetString(a.InitBalanceMap[addrStr], 10)
if !ok {
log.S().Panicf("Error when casting init balance string %s into big int", a.InitBalanceMap[addrStr])
}
amounts = append(amounts, amount)
}
return addrs, amounts
}
// OperatorAddr is the address of operator
func (d *Delegate) OperatorAddr() address.Address {
addr, err := address.FromString(d.OperatorAddrStr)
if err != nil {
log.L().Panic("Error when decoding the poll protocol operator address from string.", zap.Error(err))
}
return addr
}
// RewardAddr is the address of rewardee, which is allowed to be nil
func (d *Delegate) RewardAddr() address.Address {
if d.RewardAddrStr == "" {
return nil
}
addr, err := address.FromString(d.RewardAddrStr)
if err != nil {
log.L().Panic("Error when decoding the poll protocol rewardee address from string.", zap.Error(err))
}
return addr
}
// Votes returns the votes
func (d *Delegate) Votes() *big.Int {
val, ok := big.NewInt(0).SetString(d.VotesStr, 10)
if !ok {
log.S().Panicf("Error when casting votes string %s into big int", d.VotesStr)
}
return val
}
// InitAdminAddr returns the address of the initial rewarding protocol admin
func (r *Rewarding) InitAdminAddr() address.Address {
addr, err := address.FromString(r.InitAdminAddrStr)
if err != nil {
log.L().Panic("Error when decoding the rewarding protocol init admin address from string.", zap.Error(err))
}
return addr
}
// InitBalance returns the init balance of the rewarding fund
func (r *Rewarding) InitBalance() *big.Int {
val, ok := big.NewInt(0).SetString(r.InitBalanceStr, 10)
if !ok {
log.S().Panicf("Error when casting init balance string %s into big int", r.InitBalanceStr)
}
return val
}
// BlockReward returns the block reward amount
func (r *Rewarding) BlockReward() *big.Int {
val, ok := big.NewInt(0).SetString(r.BlockRewardStr, 10)
if !ok {
log.S().Panicf("Error when casting block reward string %s into big int", r.BlockRewardStr)
}
return val
}
// EpochReward returns the epoch reward amount
func (r *Rewarding) EpochReward() *big.Int {
val, ok := big.NewInt(0).SetString(r.EpochRewardStr, 10)
if !ok {
log.S().Panicf("Error when casting epoch reward string %s into big int", r.EpochRewardStr)
}
return val
}