mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
The module already called itself github.com/luxfi/node while containing no node in Go: cmd/luxd was twenty-one lines that printed the size of a few types, and the Go column of the matrix measured a VM plugin. A repo that fetches the thing it claims to be has not replaced it, so the Go sources move in — 1515 files, the daemon and everything under it — and the build reaches outside this module for nothing. Rust and C++ sit beside it unchanged and are still measured against it by the differential. That is the point of one repo: the reference implementation and the two ports are the same checkout, so parity is a thing you can run rather than a thing you assert. Verified: builds, and the closure carries no lux-private — checked at build time rather than believed. The daemon starts twelve chains on a local network (P X C Q Z A B G K M F, plus D through its plugin) and reports healthy.
192 lines
4.3 KiB
Go
192 lines
4.3 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package chain
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/luxfi/database"
|
|
"github.com/luxfi/database/versiondb"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/math/set"
|
|
"github.com/luxfi/node/vms/example/xsvm/execute"
|
|
"github.com/luxfi/runtime"
|
|
|
|
xsblock "github.com/luxfi/node/vms/example/xsvm/block"
|
|
smblock "github.com/luxfi/vm/chain"
|
|
)
|
|
|
|
const maxClockSkew = 10 * time.Second
|
|
|
|
var (
|
|
_ Block = (*block)(nil)
|
|
|
|
errMissingParent = errors.New("missing parent block")
|
|
errMissingChild = errors.New("missing child block")
|
|
errParentNotVerified = errors.New("parent block has not been verified")
|
|
errFutureTimestamp = errors.New("future timestamp")
|
|
errTimestampBeforeParent = errors.New("timestamp before parent")
|
|
errWrongHeight = errors.New("wrong height")
|
|
)
|
|
|
|
type Block interface {
|
|
smblock.Block
|
|
smblock.WithVerifyRuntime
|
|
|
|
// Timestamp returns the block's timestamp
|
|
Timestamp() time.Time
|
|
|
|
// State intends to return the new chain state following this block's
|
|
// acceptance. The new chain state is built (but not persisted) following a
|
|
// block's verification to allow block's descendants verification before
|
|
// being accepted.
|
|
State() (database.Database, error)
|
|
}
|
|
|
|
type block struct {
|
|
*xsblock.Stateless
|
|
|
|
chain *chain
|
|
|
|
id ids.ID
|
|
bytes []byte
|
|
|
|
state *versiondb.Database
|
|
verifiedChildrenIDs set.Set[ids.ID]
|
|
}
|
|
|
|
func (b *block) ID() ids.ID {
|
|
return b.id
|
|
}
|
|
|
|
func (b *block) Parent() ids.ID {
|
|
return b.Stateless.ParentID
|
|
}
|
|
|
|
func (b *block) ParentID() ids.ID {
|
|
return b.Stateless.ParentID
|
|
}
|
|
|
|
func (b *block) Bytes() []byte {
|
|
return b.bytes
|
|
}
|
|
|
|
func (b *block) Height() uint64 {
|
|
return b.Stateless.Height
|
|
}
|
|
|
|
func (b *block) Timestamp() time.Time {
|
|
return b.Time()
|
|
}
|
|
|
|
func (b *block) Status() uint8 {
|
|
// Return processing status (implementation detail for consensus)
|
|
return 1
|
|
}
|
|
|
|
func (b *block) Verify(ctx context.Context) error {
|
|
return b.VerifyWithRuntime(ctx, nil)
|
|
}
|
|
|
|
func (b *block) Accept(context.Context) error {
|
|
if err := b.state.Commit(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Following this block's acceptance, make sure that it's direct children
|
|
// point to the base state, which now also contains this block's changes.
|
|
for childID := range b.verifiedChildrenIDs {
|
|
child, exists := b.chain.verifiedBlocks[childID]
|
|
if !exists {
|
|
return errMissingChild
|
|
}
|
|
if err := child.state.SetDatabase(b.chain.acceptedState); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
b.chain.lastAcceptedID = b.id
|
|
delete(b.chain.verifiedBlocks, b.ParentID())
|
|
b.state = nil
|
|
return nil
|
|
}
|
|
|
|
func (b *block) Reject(context.Context) error {
|
|
delete(b.chain.verifiedBlocks, b.id)
|
|
b.state = nil
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *block) ShouldVerifyWithRuntime(context.Context) (bool, error) {
|
|
return execute.ExpectsContext(b.Stateless)
|
|
}
|
|
|
|
func (b *block) VerifyWithRuntime(ctx context.Context, blockContext *runtime.Runtime) error {
|
|
timestamp := b.Time()
|
|
if time.Until(timestamp) > maxClockSkew {
|
|
return errFutureTimestamp
|
|
}
|
|
|
|
// parent block must be verified or accepted
|
|
parent, exists := b.chain.verifiedBlocks[b.Stateless.ParentID]
|
|
if !exists {
|
|
return errMissingParent
|
|
}
|
|
|
|
if b.Stateless.Height != parent.Stateless.Height+1 {
|
|
return errWrongHeight
|
|
}
|
|
|
|
parentTimestamp := parent.Time()
|
|
if timestamp.Before(parentTimestamp) {
|
|
return errTimestampBeforeParent
|
|
}
|
|
|
|
parentState, err := parent.State()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// This block's state is a versionDB built on top of it's parent state. This
|
|
// block's changes are pushed atomically to the parent state when accepted.
|
|
blkState := versiondb.New(parentState)
|
|
err = execute.Block(
|
|
ctx,
|
|
b.chain.chainRuntime,
|
|
blkState,
|
|
false, // not bootstrapping - chainState is an interface, not a status enum
|
|
blockContext,
|
|
b.Stateless,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Make sure to only state the state the first time we verify this block.
|
|
if b.state == nil {
|
|
b.state = blkState
|
|
parent.verifiedChildrenIDs.Add(b.id)
|
|
b.chain.verifiedBlocks[b.id] = b
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *block) State() (database.Database, error) {
|
|
if b.id == b.chain.lastAcceptedID {
|
|
return b.chain.acceptedState, nil
|
|
}
|
|
|
|
// If this block isn't processing, then the child should never have had
|
|
// verify called on it.
|
|
if b.state == nil {
|
|
return nil, errParentNotVerified
|
|
}
|
|
|
|
return b.state, nil
|
|
}
|