mirror of https://github.com/poanetwork/gecko.git
152 lines
3.5 KiB
Go
152 lines
3.5 KiB
Go
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||
|
// See the file LICENSE for licensing terms.
|
||
|
|
||
|
// Package state manages the meta-data required by consensus for an avalanche
|
||
|
// dag.
|
||
|
package state
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
|
||
|
"github.com/ava-labs/gecko/cache"
|
||
|
"github.com/ava-labs/gecko/database"
|
||
|
"github.com/ava-labs/gecko/database/versiondb"
|
||
|
"github.com/ava-labs/gecko/ids"
|
||
|
"github.com/ava-labs/gecko/snow"
|
||
|
"github.com/ava-labs/gecko/snow/choices"
|
||
|
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
|
||
|
"github.com/ava-labs/gecko/utils/hashing"
|
||
|
"github.com/ava-labs/gecko/utils/math"
|
||
|
|
||
|
avacon "github.com/ava-labs/gecko/snow/consensus/avalanche"
|
||
|
avaeng "github.com/ava-labs/gecko/snow/engine/avalanche"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
dbCacheSize = 10000
|
||
|
idCacheSize = 1000
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
errUnknownVertex = errors.New("unknown vertex")
|
||
|
errWrongChainID = errors.New("wrong ChainID in vertex")
|
||
|
)
|
||
|
|
||
|
// Serializer manages the state of multiple vertices
|
||
|
type Serializer struct {
|
||
|
ctx *snow.Context
|
||
|
vm avaeng.DAGVM
|
||
|
state *prefixedState
|
||
|
db *versiondb.Database
|
||
|
edge ids.Set
|
||
|
}
|
||
|
|
||
|
// Initialize implements the avalanche.State interface
|
||
|
func (s *Serializer) Initialize(ctx *snow.Context, vm avaeng.DAGVM, db database.Database) {
|
||
|
s.ctx = ctx
|
||
|
s.vm = vm
|
||
|
|
||
|
vdb := versiondb.New(db)
|
||
|
dbCache := &cache.LRU{Size: dbCacheSize}
|
||
|
rawState := &state{
|
||
|
serializer: s,
|
||
|
dbCache: dbCache,
|
||
|
db: vdb,
|
||
|
}
|
||
|
s.state = newPrefixedState(rawState, idCacheSize)
|
||
|
s.db = vdb
|
||
|
|
||
|
s.edge.Add(s.state.Edge()...)
|
||
|
}
|
||
|
|
||
|
// ParseVertex implements the avalanche.State interface
|
||
|
func (s *Serializer) ParseVertex(b []byte) (avacon.Vertex, error) {
|
||
|
vtx, err := s.parseVertex(b)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if err := vtx.Verify(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
uVtx := &uniqueVertex{
|
||
|
serializer: s,
|
||
|
vtxID: vtx.ID(),
|
||
|
}
|
||
|
if uVtx.Status() == choices.Unknown {
|
||
|
uVtx.setVertex(vtx)
|
||
|
}
|
||
|
|
||
|
s.db.Commit()
|
||
|
return uVtx, nil
|
||
|
}
|
||
|
|
||
|
// BuildVertex implements the avalanche.State interface
|
||
|
func (s *Serializer) BuildVertex(parentSet ids.Set, txs []snowstorm.Tx) (avacon.Vertex, error) {
|
||
|
parentIDs := parentSet.List()
|
||
|
ids.SortIDs(parentIDs)
|
||
|
sortTxs(txs)
|
||
|
|
||
|
height := uint64(0)
|
||
|
for _, parentID := range parentIDs {
|
||
|
parent, err := s.getVertex(parentID)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
height = math.Max64(height, parent.v.vtx.height)
|
||
|
}
|
||
|
|
||
|
vtx := &vertex{
|
||
|
chainID: s.ctx.ChainID,
|
||
|
height: height + 1,
|
||
|
parentIDs: parentIDs,
|
||
|
txs: txs,
|
||
|
}
|
||
|
|
||
|
bytes, err := vtx.Marshal()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
vtx.bytes = bytes
|
||
|
vtx.id = ids.NewID(hashing.ComputeHash256Array(vtx.bytes))
|
||
|
|
||
|
uVtx := &uniqueVertex{
|
||
|
serializer: s,
|
||
|
vtxID: vtx.ID(),
|
||
|
}
|
||
|
// It is possible this vertex already exists in the database, even though we
|
||
|
// just made it.
|
||
|
if uVtx.Status() == choices.Unknown {
|
||
|
uVtx.setVertex(vtx)
|
||
|
}
|
||
|
|
||
|
s.db.Commit()
|
||
|
return uVtx, nil
|
||
|
}
|
||
|
|
||
|
// GetVertex implements the avalanche.State interface
|
||
|
func (s *Serializer) GetVertex(vtxID ids.ID) (avacon.Vertex, error) { return s.getVertex(vtxID) }
|
||
|
|
||
|
// Edge implements the avalanche.State interface
|
||
|
func (s *Serializer) Edge() []ids.ID { return s.edge.List() }
|
||
|
|
||
|
func (s *Serializer) parseVertex(b []byte) (*vertex, error) {
|
||
|
vtx := &vertex{}
|
||
|
if err := vtx.Unmarshal(b, s.vm); err != nil {
|
||
|
return nil, err
|
||
|
} else if !vtx.chainID.Equals(s.ctx.ChainID) {
|
||
|
return nil, errWrongChainID
|
||
|
}
|
||
|
return vtx, nil
|
||
|
}
|
||
|
|
||
|
func (s *Serializer) getVertex(vtxID ids.ID) (*uniqueVertex, error) {
|
||
|
vtx := &uniqueVertex{
|
||
|
serializer: s,
|
||
|
vtxID: vtxID,
|
||
|
}
|
||
|
if vtx.Status() == choices.Unknown {
|
||
|
return nil, errUnknownVertex
|
||
|
}
|
||
|
return vtx, nil
|
||
|
}
|