/
blockchain.go
216 lines (179 loc) · 5.9 KB
/
blockchain.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
package beacon
import (
"errors"
"time"
"github.com/prysmaticlabs/prysm/shared/ssz"
"github.com/sirupsen/logrus"
"github.com/phoreproject/synapse/beacon/config"
"github.com/phoreproject/synapse/beacon/db"
"github.com/phoreproject/synapse/bls"
"github.com/phoreproject/synapse/primitives"
"github.com/phoreproject/synapse/utils"
"github.com/phoreproject/synapse/chainhash"
)
var zeroHash = chainhash.Hash{}
// Blockchain handles the communication between 3 main components.
// 1. BlockchainView stores block nodes as a chain and an index to allow
// easy access. All block nodes are stored in memory and only store
// information needed often.
// 2. DB keeps track of the persistent data. It stores large block
// files, a representation of the chain through block nodes, and
// important information about the state of the chain.
// 3. StateManager stores information about the state of certain blocks.
// The state manager stores the state and derived states of every block
// after the finalized block.
type Blockchain struct {
View *BlockchainView
DB db.Database
config *config.Config
stateManager *StateManager
Notifees []BlockchainNotifee
}
// GetEpochBoundaryHash gets the Hash of the parent block at the epoch boundary.
func (b *Blockchain) GetEpochBoundaryHash(slot uint64) (chainhash.Hash, error) {
epochBoundaryHeight := slot - (slot % b.config.EpochLength)
bl, err := b.View.Chain.GetBlockBySlot(epochBoundaryHeight)
if err != nil {
return chainhash.Hash{}, err
}
return bl.Hash, nil
}
func (b *Blockchain) getLatestAttestationTarget(validator uint32) (*BlockNode, error) {
att, err := b.DB.GetLatestAttestation(validator)
if err != nil {
return nil, err
}
bl, err := b.DB.GetBlockForHash(att.Data.BeaconBlockHash)
if err != nil {
return nil, err
}
h, err := ssz.TreeHash(bl)
if err != nil {
return nil, err
}
node := b.View.Index.GetBlockNodeByHash(h)
if node == nil {
return nil, errors.New("couldn't find block attested to by validator in index")
}
return node, nil
}
// NewBlockchainWithInitialValidators creates a new blockchain with the specified
// initial validators.
func NewBlockchainWithInitialValidators(db db.Database, config *config.Config, validators []InitialValidatorEntry, skipValidation bool, genesisTime uint64) (*Blockchain, error) {
b := &Blockchain{
DB: db,
config: config,
View: NewBlockchainView(),
}
sm, err := NewStateManager(config, genesisTime, b, db)
if err != nil {
return nil, err
}
b.stateManager = sm
initialState, err := InitializeState(config, validators, genesisTime, skipValidation)
if err != nil {
return nil, err
}
stateRoot, err := ssz.TreeHash(initialState)
if err != nil {
return nil, err
}
block0 := primitives.Block{
BlockHeader: primitives.BlockHeader{
SlotNumber: 0,
StateRoot: stateRoot,
ParentRoot: zeroHash,
RandaoReveal: bls.EmptySignature.Serialize(),
Signature: bls.EmptySignature.Serialize(),
},
BlockBody: primitives.BlockBody{
ProposerSlashings: []primitives.ProposerSlashing{},
CasperSlashings: []primitives.CasperSlashing{},
Attestations: []primitives.Attestation{},
Deposits: []primitives.Deposit{},
Exits: []primitives.Exit{},
},
}
blockHash, err := ssz.TreeHash(block0)
if err != nil {
return nil, err
}
logrus.WithField("genesisHash", chainhash.Hash(blockHash)).Info("initializing blockchain with genesis block")
err = b.DB.SetBlock(block0)
if err != nil {
return nil, err
}
// this is a new database, so let's populate it with default values
node, err := b.View.Index.AddBlockNodeToIndex(&block0, blockHash, stateRoot)
if err != nil {
return nil, err
}
// check if the block index exists in the database
_, err = b.DB.GetBlockNode(blockHash)
if err != nil {
// if it doesn't, initialize the database
if err := b.initializeDatabase(node, *initialState); err != nil {
return nil, err
}
} else {
// if it does, load everything needed from disk
if err := b.loadBlockchainFromDisk(blockHash); err != nil {
return nil, err
}
}
return b, nil
}
// GetUpdatedState gets the tip, but with processed slots/epochs as appropriate.
func (b *Blockchain) GetUpdatedState(upTo uint64) (*primitives.State, error) {
tip := b.View.Chain.Tip()
view := NewChainView(tip)
tipState, err := b.stateManager.GetStateForHashAtSlot(tip.Hash, upTo, &view, b.config)
if err != nil {
return nil, err
}
tipStateCopy := tipState.Copy()
return &tipStateCopy, nil
}
// GetNextSlotTime returns the timestamp of the next slot.
func (b *Blockchain) GetNextSlotTime() time.Time {
return time.Unix(int64((b.View.Chain.Tip().Slot+1)*uint64(b.config.SlotDuration)+b.stateManager.GetGenesisTime()), 0)
}
// InitialValidatorEntry is the validator entry to be added
// at the beginning of a blockchain.
type InitialValidatorEntry struct {
PubKey [96]byte
ProofOfPossession [48]byte
WithdrawalShard uint32
WithdrawalCredentials chainhash.Hash
DepositSize uint64
}
// Height returns the height of the chain.
func (b *Blockchain) Height() uint64 {
h := b.View.Chain.Height()
if h < 0 {
return 0
}
return uint64(h)
}
// GetBlockByHash gets a block by Hash.
func (b *Blockchain) GetBlockByHash(h chainhash.Hash) (*primitives.Block, error) {
block, err := b.DB.GetBlockForHash(h)
if err != nil {
return nil, err
}
return block, nil
}
// GetConfig returns the config used by this blockchain
func (b *Blockchain) GetConfig() *config.Config {
return b.config
}
// GetCurrentSlot gets the current slot according to the time.
func (b *Blockchain) GetCurrentSlot() uint64 {
currentTime := uint64(utils.Now().Unix())
timeSinceGenesis := currentTime - b.stateManager.GetGenesisTime()
return timeSinceGenesis / uint64(b.config.SlotDuration)
}
// GenesisHash gets the genesis hash for the chain.
func (b *Blockchain) GenesisHash() chainhash.Hash {
return b.View.Chain.GetBlockByHeight(0).Hash
}