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.
183 lines
4.8 KiB
Go
183 lines
4.8 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package proposervm
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/luxfi/ids"
|
|
chain "github.com/luxfi/vm/chain"
|
|
|
|
statelessblock "github.com/luxfi/node/vms/proposervm/block"
|
|
)
|
|
|
|
var _ chain.BatchedChainVM = (*VM)(nil)
|
|
|
|
// containerLenPrefix is the 4-byte length prefix each block occupies in an
|
|
// ancestors response container; counted toward the response size budget.
|
|
const containerLenPrefix = 4
|
|
|
|
func (vm *VM) GetAncestors(
|
|
ctx context.Context,
|
|
blkID ids.ID,
|
|
maxBlocksNum int,
|
|
maxBlocksSize int,
|
|
maxBlocksRetrievalTime time.Duration,
|
|
) ([][]byte, error) {
|
|
if vm.batchedVM == nil {
|
|
return nil, chain.ErrRemoteVMNotImplemented
|
|
}
|
|
if maxBlocksNum <= 0 || maxBlocksSize <= 0 || maxBlocksRetrievalTime <= 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
res := make([][]byte, 0, maxBlocksNum)
|
|
currentByteLength := 0
|
|
startTime := vm.Clock.Time()
|
|
|
|
// hereinafter loop over proposerVM cache and DB, possibly till chain++
|
|
// fork is hit
|
|
for {
|
|
blk, err := vm.getStatelessBlk(blkID)
|
|
if err != nil {
|
|
// maybe we have hit the proposerVM fork here?
|
|
break
|
|
}
|
|
|
|
blkBytes := blk.Bytes()
|
|
|
|
// Ensure response size isn't too large. Include containerLenPrefix
|
|
// because the size of the message is included with each container, and
|
|
// the size is repr. by an int.
|
|
currentByteLength += containerLenPrefix + len(blkBytes)
|
|
elapsedTime := vm.Clock.Time().Sub(startTime)
|
|
if len(res) > 0 && (currentByteLength >= maxBlocksSize || maxBlocksRetrievalTime <= elapsedTime) {
|
|
return res, nil // reached maximum size or ran out of time
|
|
}
|
|
|
|
res = append(res, blkBytes)
|
|
blkID = blk.ParentID()
|
|
if len(res) >= maxBlocksNum {
|
|
return res, nil
|
|
}
|
|
}
|
|
|
|
// chain++ fork may have been hit.
|
|
preMaxBlocksNum := maxBlocksNum - len(res)
|
|
preMaxBlocksSize := maxBlocksSize - currentByteLength
|
|
preMaxBlocksRetrivalTime := maxBlocksRetrievalTime - time.Since(startTime)
|
|
innerBytes, err := vm.batchedVM.GetAncestors(
|
|
ctx,
|
|
blkID,
|
|
preMaxBlocksNum,
|
|
preMaxBlocksSize,
|
|
preMaxBlocksRetrivalTime,
|
|
)
|
|
if err != nil {
|
|
if len(res) == 0 {
|
|
return nil, err
|
|
}
|
|
return res, nil // return what we have
|
|
}
|
|
res = append(res, innerBytes...)
|
|
return res, nil
|
|
}
|
|
|
|
func (vm *VM) BatchedParseBlock(ctx context.Context, blks [][]byte) ([]chain.Block, error) {
|
|
// Handle empty input
|
|
if len(blks) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
if vm.batchedVM == nil {
|
|
return nil, chain.ErrRemoteVMNotImplemented
|
|
}
|
|
|
|
type partialData struct {
|
|
index int
|
|
block statelessblock.Block
|
|
}
|
|
var (
|
|
blocksIndex int
|
|
blocks = make([]chain.Block, len(blks))
|
|
|
|
innerBlocksIndex int
|
|
statelessBlockDescs = make([]partialData, 0, len(blks))
|
|
innerBlockBytes = make([][]byte, 0, len(blks))
|
|
)
|
|
|
|
parsingResults := statelessblock.ParseBlocks(blks, vm.rt.ChainID)
|
|
|
|
for ; blocksIndex < len(blks); blocksIndex++ {
|
|
statelessBlock, err := parsingResults[blocksIndex].Block, parsingResults[blocksIndex].Err
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
blkID := statelessBlock.ID()
|
|
if block, exists := vm.cachedVerifiedBlock(blkID); exists {
|
|
blocks[blocksIndex] = block
|
|
continue
|
|
}
|
|
|
|
statelessBlockDescs = append(statelessBlockDescs, partialData{
|
|
index: blocksIndex,
|
|
block: statelessBlock,
|
|
})
|
|
innerBlockBytes = append(innerBlockBytes, statelessBlock.Block())
|
|
}
|
|
innerBlockBytes = append(innerBlockBytes, blks[blocksIndex:]...)
|
|
|
|
// parse all inner blocks at once
|
|
innerBlks, err := vm.batchedVM.BatchedParseBlock(ctx, innerBlockBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(innerBlks) != len(innerBlockBytes) {
|
|
return nil, fmt.Errorf(
|
|
"inner VM returned %d parsed blocks for %d inputs",
|
|
len(innerBlks),
|
|
len(innerBlockBytes),
|
|
)
|
|
}
|
|
for ; innerBlocksIndex < len(statelessBlockDescs); innerBlocksIndex++ {
|
|
statelessBlockDesc := statelessBlockDescs[innerBlocksIndex]
|
|
statelessBlk := statelessBlockDesc.block
|
|
|
|
if statelessSignedBlock, ok := statelessBlk.(statelessblock.SignedBlock); ok {
|
|
blocks[statelessBlockDesc.index] = &postForkBlock{
|
|
SignedBlock: statelessSignedBlock,
|
|
postForkCommonComponents: postForkCommonComponents{
|
|
vm: vm,
|
|
innerBlk: innerBlks[innerBlocksIndex],
|
|
},
|
|
}
|
|
} else {
|
|
blocks[statelessBlockDesc.index] = &postForkOption{
|
|
Block: statelessBlk,
|
|
postForkCommonComponents: postForkCommonComponents{
|
|
vm: vm,
|
|
innerBlk: innerBlks[innerBlocksIndex],
|
|
},
|
|
}
|
|
}
|
|
}
|
|
for ; blocksIndex < len(blocks); blocksIndex, innerBlocksIndex = blocksIndex+1, innerBlocksIndex+1 {
|
|
blocks[blocksIndex] = &preForkBlock{
|
|
Block: innerBlks[innerBlocksIndex],
|
|
vm: vm,
|
|
}
|
|
}
|
|
return blocks, nil
|
|
}
|
|
|
|
func (vm *VM) getStatelessBlk(blkID ids.ID) (statelessblock.Block, error) {
|
|
if currentBlk, exists := vm.cachedVerifiedBlock(blkID); exists {
|
|
return currentBlk.getStatelessBlk(), nil
|
|
}
|
|
return vm.State.GetBlock(blkID)
|
|
}
|