gecko/vms/components/core/block.go

104 lines
3.0 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package core
import (
"errors"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/choices"
"github.com/ava-labs/gecko/snow/consensus/snowman"
"github.com/ava-labs/gecko/vms/components/missing"
)
var (
errBlockNil = errors.New("block is nil")
errRejected = errors.New("block is rejected")
)
// Block contains fields and methods common to block's in a Snowman blockchain.
// Block is meant to be a building-block (pun intended).
// When you write a VM, your blocks can (and should) embed a core.Block
// to take care of some bioler-plate code.
// Block's methods can be over-written by structs that embed this struct.
type Block struct {
Metadata
PrntID ids.ID `serialize:"true"` // parent's ID
VM *SnowmanVM
}
// Initialize sets [b.bytes] to [bytes], sets [b.id] to hash([b.bytes])
// Checks if [b]'s status is already stored in state. If so, [b] gets that status.
// Otherwise [b]'s status is Unknown.
func (b *Block) Initialize(bytes []byte, vm *SnowmanVM) {
b.VM = vm
b.Metadata.Initialize(bytes)
status := b.VM.State.GetStatus(vm.DB, b.ID())
b.SetStatus(status)
}
// ParentID returns [b]'s parent's ID
func (b *Block) ParentID() ids.ID { return b.PrntID }
// Parent returns [b]'s parent
func (b *Block) Parent() snowman.Block {
parent, err := b.VM.GetBlock(b.ParentID())
if err != nil {
return &missing.Block{BlkID: b.ParentID()}
}
return parent
}
// Accept sets this block's status to Accepted and sets lastAccepted to this
// block's ID and saves this info to b.vm.DB
// Recall that b.vm.DB.Commit() must be called to persist to the DB
func (b *Block) Accept() {
b.SetStatus(choices.Accepted) // Change state of this block
b.VM.State.PutStatus(b.VM.DB, b.ID(), choices.Accepted) // Persist data
b.VM.State.PutLastAccepted(b.VM.DB, b.ID())
b.VM.lastAccepted = b.ID() // Change state of VM
}
// Reject sets this block's status to Rejected and saves the status in state
// Recall that b.vm.DB.Commit() must be called to persist to the DB
func (b *Block) Reject() {
b.SetStatus(choices.Rejected)
b.VM.State.PutStatus(b.VM.DB, b.ID(), choices.Rejected)
}
// Status returns the status of this block
func (b *Block) Status() choices.Status {
// See if [b]'s status field already has a value
if status := b.Metadata.Status(); status != choices.Unknown {
return status
}
// If not, check the state
status := b.VM.State.GetStatus(b.VM.DB, b.ID())
b.SetStatus(status)
return status
}
// Verify returns:
// 1) true if the block is accepted
// 2) nil if this block is valid
func (b *Block) Verify() (bool, error) {
if b == nil {
return false, errBlockNil
}
// Check if [b] has already been accepted/rejected
switch status := b.Status(); status {
case choices.Accepted:
return true, nil
case choices.Rejected:
return false, errRejected
}
return false, nil
}
// NewBlock returns a new *Block
func NewBlock(parentID ids.ID) *Block {
return &Block{PrntID: parentID}
}