/
generate_genesis_state.go
210 lines (197 loc) · 8.59 KB
/
generate_genesis_state.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
// Package interop contains deterministic utilities for generating
// genesis states and keys.
package interop
import (
"context"
"sync"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/async"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
coreState "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
statenative "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/container/trie"
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
"github.com/prysmaticlabs/prysm/v4/crypto/hash"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/time"
)
var (
// This is the recommended mock eth1 block hash according to the Ethereum consensus interop guidelines.
// https://github.com/ethereum/eth2.0-pm/blob/a085c9870f3956d6228ed2a40cd37f0c6580ecd7/interop/mocked_start/README.md
mockEth1BlockHash = []byte{66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66}
)
// GenerateGenesisState deterministically given a genesis time and number of validators.
// If a genesis time of 0 is supplied it is set to the current time.
func GenerateGenesisState(ctx context.Context, genesisTime, numValidators uint64) (*ethpb.BeaconState, []*ethpb.Deposit, error) {
privKeys, pubKeys, err := DeterministicallyGenerateKeys(0 /*startIndex*/, numValidators)
if err != nil {
return nil, nil, errors.Wrapf(err, "could not deterministically generate keys for %d validators", numValidators)
}
depositDataItems, depositDataRoots, err := DepositDataFromKeys(privKeys, pubKeys)
if err != nil {
return nil, nil, errors.Wrap(err, "could not generate deposit data from keys")
}
return GenerateGenesisStateFromDepositData(ctx, genesisTime, depositDataItems, depositDataRoots)
}
// GenerateGenesisStateFromDepositData creates a genesis state given a list of
// deposit data items and their corresponding roots.
func GenerateGenesisStateFromDepositData(
ctx context.Context, genesisTime uint64, depositData []*ethpb.Deposit_Data, depositDataRoots [][]byte,
) (*ethpb.BeaconState, []*ethpb.Deposit, error) {
t, err := trie.GenerateTrieFromItems(depositDataRoots, params.BeaconConfig().DepositContractTreeDepth)
if err != nil {
return nil, nil, errors.Wrap(err, "could not generate Merkle trie for deposit proofs")
}
deposits, err := GenerateDepositsFromData(depositData, t)
if err != nil {
return nil, nil, errors.Wrap(err, "could not generate deposits from the deposit data provided")
}
root, err := t.HashTreeRoot()
if err != nil {
return nil, nil, errors.Wrap(err, "could not hash tree root of deposit trie")
}
if genesisTime == 0 {
genesisTime = uint64(time.Now().Unix())
}
beaconState, err := coreState.GenesisBeaconState(ctx, deposits, genesisTime, ðpb.Eth1Data{
DepositRoot: root[:],
DepositCount: uint64(len(deposits)),
BlockHash: mockEth1BlockHash,
})
if err != nil {
return nil, nil, errors.Wrap(err, "could not generate genesis state")
}
pbState, err := statenative.ProtobufBeaconStatePhase0(beaconState.ToProtoUnsafe())
if err != nil {
return nil, nil, err
}
return pbState, deposits, nil
}
// GenerateDepositsFromData a list of deposit items by creating proofs for each of them from a sparse Merkle trie.
func GenerateDepositsFromData(depositDataItems []*ethpb.Deposit_Data, trie *trie.SparseMerkleTrie) ([]*ethpb.Deposit, error) {
deposits := make([]*ethpb.Deposit, len(depositDataItems))
results, err := async.Scatter(len(depositDataItems), func(offset int, entries int, _ *sync.RWMutex) (interface{}, error) {
return generateDepositsFromData(depositDataItems[offset:offset+entries], offset, trie)
})
if err != nil {
return nil, errors.Wrap(err, "failed to generate deposits from data")
}
for _, result := range results {
if depositExtent, ok := result.Extent.([]*ethpb.Deposit); ok {
copy(deposits[result.Offset:], depositExtent)
} else {
return nil, errors.New("extent not of expected type")
}
}
return deposits, nil
}
// generateDepositsFromData a list of deposit items by creating proofs for each of them from a sparse Merkle trie.
func generateDepositsFromData(depositDataItems []*ethpb.Deposit_Data, offset int, trie *trie.SparseMerkleTrie) ([]*ethpb.Deposit, error) {
deposits := make([]*ethpb.Deposit, len(depositDataItems))
for i, item := range depositDataItems {
proof, err := trie.MerkleProof(i + offset)
if err != nil {
return nil, errors.Wrapf(err, "could not generate proof for deposit %d", i+offset)
}
deposits[i] = ðpb.Deposit{
Proof: proof,
Data: item,
}
}
return deposits, nil
}
// DepositDataFromKeys generates a list of deposit data items from a set of BLS validator keys.
func DepositDataFromKeys(privKeys []bls.SecretKey, pubKeys []bls.PublicKey) ([]*ethpb.Deposit_Data, [][]byte, error) {
type depositData struct {
items []*ethpb.Deposit_Data
roots [][]byte
}
depositDataItems := make([]*ethpb.Deposit_Data, len(privKeys))
depositDataRoots := make([][]byte, len(privKeys))
results, err := async.Scatter(len(privKeys), func(offset int, entries int, _ *sync.RWMutex) (interface{}, error) {
items, roots, err := depositDataFromKeys(privKeys[offset:offset+entries], pubKeys[offset:offset+entries], 0)
return &depositData{items: items, roots: roots}, err
})
if err != nil {
return nil, nil, errors.Wrap(err, "failed to generate deposit data from keys")
}
for _, result := range results {
if depositDataExtent, ok := result.Extent.(*depositData); ok {
copy(depositDataItems[result.Offset:], depositDataExtent.items)
copy(depositDataRoots[result.Offset:], depositDataExtent.roots)
} else {
return nil, nil, errors.New("extent not of expected type")
}
}
return depositDataItems, depositDataRoots, nil
}
// DepositDataFromKeysWithExecCreds generates a list of deposit data items from a set of BLS validator keys.
func DepositDataFromKeysWithExecCreds(privKeys []bls.SecretKey, pubKeys []bls.PublicKey, numOfCreds uint64) ([]*ethpb.Deposit_Data, [][]byte, error) {
return depositDataFromKeys(privKeys, pubKeys, numOfCreds)
}
func depositDataFromKeys(privKeys []bls.SecretKey, pubKeys []bls.PublicKey, numOfCreds uint64) ([]*ethpb.Deposit_Data, [][]byte, error) {
dataRoots := make([][]byte, len(privKeys))
depositDataItems := make([]*ethpb.Deposit_Data, len(privKeys))
for i := 0; i < len(privKeys); i++ {
withCred := uint64(i) < numOfCreds
data, err := createDepositData(privKeys[i], pubKeys[i], withCred)
if err != nil {
return nil, nil, errors.Wrapf(err, "could not create deposit data for key: %#x", privKeys[i].Marshal())
}
h, err := data.HashTreeRoot()
if err != nil {
return nil, nil, errors.Wrap(err, "could not hash tree root deposit data item")
}
dataRoots[i] = h[:]
depositDataItems[i] = data
}
return depositDataItems, dataRoots, nil
}
// Generates a deposit data item from BLS keys and signs the hash tree root of the data.
func createDepositData(privKey bls.SecretKey, pubKey bls.PublicKey, withExecCreds bool) (*ethpb.Deposit_Data, error) {
depositMessage := ðpb.DepositMessage{
PublicKey: pubKey.Marshal(),
WithdrawalCredentials: withdrawalCredentialsHash(pubKey.Marshal()),
Amount: params.BeaconConfig().MaxEffectiveBalance,
}
if withExecCreds {
newCredentials := make([]byte, 12)
newCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
execAddr := bytesutil.ToBytes20(pubKey.Marshal())
depositMessage.WithdrawalCredentials = append(newCredentials, execAddr[:]...)
}
sr, err := depositMessage.HashTreeRoot()
if err != nil {
return nil, err
}
domain, err := signing.ComputeDomain(params.BeaconConfig().DomainDeposit, nil, nil)
if err != nil {
return nil, err
}
root, err := (ðpb.SigningData{ObjectRoot: sr[:], Domain: domain}).HashTreeRoot()
if err != nil {
return nil, err
}
di := ðpb.Deposit_Data{
PublicKey: depositMessage.PublicKey,
WithdrawalCredentials: depositMessage.WithdrawalCredentials,
Amount: depositMessage.Amount,
Signature: privKey.Sign(root[:]).Marshal(),
}
return di, nil
}
// withdrawalCredentialsHash forms a 32 byte hash of the withdrawal public
// address.
//
// The specification is as follows:
//
// withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX_BYTE
// withdrawal_credentials[1:] == hash(withdrawal_pubkey)[1:]
//
// where withdrawal_credentials is of type bytes32.
func withdrawalCredentialsHash(pubKey []byte) []byte {
h := hash.Hash(pubKey)
return append([]byte{blsWithdrawalPrefixByte}, h[1:]...)[:32]
}