mirror of https://github.com/poanetwork/gecko.git
179 lines
4.2 KiB
Go
179 lines
4.2 KiB
Go
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package state
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/ava-labs/gecko/ids"
|
|
"github.com/ava-labs/gecko/snow/choices"
|
|
"github.com/ava-labs/gecko/snow/consensus/avalanche"
|
|
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
|
|
"github.com/ava-labs/gecko/utils/formatting"
|
|
)
|
|
|
|
// uniqueVertex acts as a cache for vertices in the database.
|
|
//
|
|
// If a vertex is loaded, it will have one canonical uniqueVertex. The vertex
|
|
// will eventually be evicted from memory, when the uniqueVertex is evicted from
|
|
// the cache. If the uniqueVertex has a function called again afther this
|
|
// eviction, the vertex will be re-loaded from the database.
|
|
type uniqueVertex struct {
|
|
serializer *Serializer
|
|
|
|
vtxID ids.ID
|
|
v *vertexState
|
|
}
|
|
|
|
func (vtx *uniqueVertex) refresh() {
|
|
if vtx.v == nil {
|
|
vtx.v = &vertexState{}
|
|
}
|
|
if !vtx.v.unique {
|
|
unique := vtx.serializer.state.UniqueVertex(vtx)
|
|
prevVtx := vtx.v.vtx
|
|
if unique == vtx {
|
|
vtx.v.status = vtx.serializer.state.Status(vtx.ID())
|
|
vtx.v.unique = true
|
|
} else {
|
|
// If someone is in the cache, they must be up to date
|
|
*vtx = *unique
|
|
}
|
|
|
|
switch {
|
|
case vtx.v.vtx == nil && prevVtx == nil:
|
|
vtx.v.vtx = vtx.serializer.state.Vertex(vtx.ID())
|
|
case vtx.v.vtx == nil:
|
|
vtx.v.vtx = prevVtx
|
|
}
|
|
}
|
|
}
|
|
|
|
func (vtx *uniqueVertex) Evict() {
|
|
if vtx.v != nil {
|
|
vtx.v.unique = false
|
|
}
|
|
}
|
|
|
|
func (vtx *uniqueVertex) setVertex(innerVtx *vertex) {
|
|
vtx.refresh()
|
|
if vtx.v.vtx == nil {
|
|
vtx.v.vtx = innerVtx
|
|
vtx.serializer.state.SetVertex(innerVtx)
|
|
vtx.setStatus(choices.Processing)
|
|
}
|
|
}
|
|
|
|
func (vtx *uniqueVertex) setStatus(status choices.Status) {
|
|
vtx.refresh()
|
|
if vtx.v.status != status {
|
|
vtx.serializer.state.SetStatus(vtx.ID(), status)
|
|
vtx.v.status = status
|
|
}
|
|
}
|
|
|
|
func (vtx *uniqueVertex) ID() ids.ID { return vtx.vtxID }
|
|
|
|
func (vtx *uniqueVertex) Accept() {
|
|
vtx.setStatus(choices.Accepted)
|
|
|
|
vtx.serializer.edge.Add(vtx.vtxID)
|
|
for _, parent := range vtx.Parents() {
|
|
vtx.serializer.edge.Remove(parent.ID())
|
|
}
|
|
|
|
vtx.serializer.state.SetEdge(vtx.serializer.edge.List())
|
|
|
|
// Should never traverse into parents of a decided vertex. Allows for the
|
|
// parents to be garbage collected
|
|
vtx.v.parents = nil
|
|
|
|
vtx.serializer.db.Commit()
|
|
}
|
|
|
|
func (vtx *uniqueVertex) Reject() {
|
|
vtx.setStatus(choices.Rejected)
|
|
|
|
// Should never traverse into parents of a decided vertex. Allows for the
|
|
// parents to be garbage collected
|
|
vtx.v.parents = nil
|
|
|
|
vtx.serializer.db.Commit()
|
|
}
|
|
|
|
func (vtx *uniqueVertex) Status() choices.Status { vtx.refresh(); return vtx.v.status }
|
|
|
|
func (vtx *uniqueVertex) Parents() []avalanche.Vertex {
|
|
vtx.refresh()
|
|
|
|
if len(vtx.v.parents) != len(vtx.v.vtx.parentIDs) {
|
|
vtx.v.parents = make([]avalanche.Vertex, len(vtx.v.vtx.parentIDs))
|
|
for i, parentID := range vtx.v.vtx.parentIDs {
|
|
vtx.v.parents[i] = &uniqueVertex{
|
|
serializer: vtx.serializer,
|
|
vtxID: parentID,
|
|
}
|
|
}
|
|
}
|
|
|
|
return vtx.v.parents
|
|
}
|
|
|
|
func (vtx *uniqueVertex) Txs() []snowstorm.Tx {
|
|
vtx.refresh()
|
|
|
|
if len(vtx.v.vtx.txs) != len(vtx.v.txs) {
|
|
vtx.v.txs = make([]snowstorm.Tx, len(vtx.v.vtx.txs))
|
|
for i, tx := range vtx.v.vtx.txs {
|
|
vtx.v.txs[i] = tx
|
|
}
|
|
}
|
|
|
|
return vtx.v.txs
|
|
}
|
|
|
|
func (vtx *uniqueVertex) Bytes() []byte { return vtx.v.vtx.Bytes() }
|
|
|
|
func (vtx *uniqueVertex) Verify() error { return vtx.v.vtx.Verify() }
|
|
|
|
func (vtx *uniqueVertex) String() string {
|
|
sb := strings.Builder{}
|
|
|
|
parents := vtx.Parents()
|
|
txs := vtx.Txs()
|
|
|
|
sb.WriteString(fmt.Sprintf(
|
|
"Vertex(ID = %s, Status = %s, Number of Dependencies = %d, Number of Transactions = %d)",
|
|
vtx.ID(),
|
|
vtx.Status(),
|
|
len(parents),
|
|
len(txs),
|
|
))
|
|
|
|
parentFormat := fmt.Sprintf("\n Parent[%s]: ID = %%s, Status = %%s",
|
|
formatting.IntFormat(len(parents)-1))
|
|
for i, parent := range parents {
|
|
sb.WriteString(fmt.Sprintf(parentFormat, i, parent.ID(), parent.Status()))
|
|
}
|
|
|
|
txFormat := fmt.Sprintf("\n Transaction[%s]: ID = %%s, Status = %%s",
|
|
formatting.IntFormat(len(txs)-1))
|
|
for i, tx := range txs {
|
|
sb.WriteString(fmt.Sprintf(txFormat, i, tx.ID(), tx.Status()))
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
type vertexState struct {
|
|
unique bool
|
|
|
|
vtx *vertex
|
|
status choices.Status
|
|
|
|
parents []avalanche.Vertex
|
|
txs []snowstorm.Tx
|
|
}
|