mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
318 lines
7.2 KiB
Go
318 lines
7.2 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package state
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/luxfi/database"
|
|
"github.com/luxfi/database/memdb"
|
|
"github.com/luxfi/database/versiondb"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/metric"
|
|
"github.com/luxfi/node/upgrade"
|
|
"github.com/luxfi/node/vms/xvm/block"
|
|
"github.com/luxfi/node/vms/xvm/fxs"
|
|
"github.com/luxfi/node/vms/xvm/txs"
|
|
lux "github.com/luxfi/utxo"
|
|
|
|
"github.com/luxfi/utxo/secp256k1fx"
|
|
)
|
|
|
|
const trackChecksums = false
|
|
|
|
var (
|
|
parser block.Parser
|
|
populatedUTXO *lux.UTXO
|
|
populatedUTXOID ids.ID
|
|
populatedTx *txs.Tx
|
|
populatedTxID ids.ID
|
|
populatedBlk block.Block
|
|
populatedBlkHeight uint64
|
|
populatedBlkID ids.ID
|
|
)
|
|
|
|
func init() {
|
|
var err error
|
|
parser, err = block.NewParser(
|
|
[]fxs.Fx{
|
|
&secp256k1fx.Fx{},
|
|
},
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
populatedUTXO = &lux.UTXO{
|
|
UTXOID: lux.UTXOID{
|
|
TxID: ids.GenerateTestID(),
|
|
},
|
|
Asset: lux.Asset{
|
|
ID: ids.GenerateTestID(),
|
|
},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 1,
|
|
},
|
|
}
|
|
populatedUTXOID = populatedUTXO.InputID()
|
|
|
|
populatedTx = &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: lux.BaseTx{
|
|
BlockchainID: ids.GenerateTestID(),
|
|
}}}
|
|
err = populatedTx.Initialize()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
populatedTxID = populatedTx.ID()
|
|
|
|
// Native-ZAP blocks store each tx's own wire bytes, so a block's txs must
|
|
// be Initialized (bytes populated) before the block is built — the same
|
|
// contract production uses (mempool/parse hand the builder initialized txs).
|
|
blkTx := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: lux.BaseTx{
|
|
BlockchainID: ids.GenerateTestID(),
|
|
}}}
|
|
if err = blkTx.Initialize(); err != nil {
|
|
panic(err)
|
|
}
|
|
populatedBlk, err = block.NewStandardBlock(
|
|
ids.GenerateTestID(),
|
|
1,
|
|
time.Now(),
|
|
[]*txs.Tx{blkTx},
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
populatedBlkHeight = populatedBlk.Height()
|
|
populatedBlkID = populatedBlk.ID()
|
|
}
|
|
|
|
type versions struct {
|
|
chains map[ids.ID]Chain
|
|
}
|
|
|
|
func (v *versions) GetState(blkID ids.ID) (Chain, bool) {
|
|
c, ok := v.chains[blkID]
|
|
return c, ok
|
|
}
|
|
|
|
func TestState(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
db := memdb.New()
|
|
vdb := versiondb.New(db)
|
|
s, err := New(vdb, parser, metric.NewNoOp().Registry(), trackChecksums)
|
|
require.NoError(err)
|
|
|
|
s.AddUTXO(populatedUTXO)
|
|
s.AddTx(populatedTx)
|
|
s.AddBlock(populatedBlk)
|
|
require.NoError(s.Commit())
|
|
|
|
s, err = New(vdb, parser, metric.NewNoOp().Registry(), trackChecksums)
|
|
require.NoError(err)
|
|
|
|
ChainUTXOTest(t, s)
|
|
ChainTxTest(t, s)
|
|
ChainBlockTest(t, s)
|
|
}
|
|
|
|
func TestDiff(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
db := memdb.New()
|
|
vdb := versiondb.New(db)
|
|
s, err := New(vdb, parser, metric.NewNoOp().Registry(), trackChecksums)
|
|
require.NoError(err)
|
|
|
|
s.AddUTXO(populatedUTXO)
|
|
s.AddTx(populatedTx)
|
|
s.AddBlock(populatedBlk)
|
|
require.NoError(s.Commit())
|
|
|
|
parentID := ids.GenerateTestID()
|
|
d, err := NewDiff(parentID, &versions{
|
|
chains: map[ids.ID]Chain{
|
|
parentID: s,
|
|
},
|
|
})
|
|
require.NoError(err)
|
|
|
|
ChainUTXOTest(t, d)
|
|
ChainTxTest(t, d)
|
|
ChainBlockTest(t, d)
|
|
}
|
|
|
|
func ChainUTXOTest(t *testing.T, c Chain) {
|
|
require := require.New(t)
|
|
|
|
fetchedUTXO, err := c.GetUTXO(populatedUTXOID)
|
|
require.NoError(err)
|
|
|
|
// Compare IDs because [fetchedUTXO] isn't initialized
|
|
require.Equal(populatedUTXO.InputID(), fetchedUTXO.InputID())
|
|
|
|
utxo := &lux.UTXO{
|
|
UTXOID: lux.UTXOID{
|
|
TxID: ids.GenerateTestID(),
|
|
},
|
|
Asset: lux.Asset{
|
|
ID: ids.GenerateTestID(),
|
|
},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 1,
|
|
},
|
|
}
|
|
utxoID := utxo.InputID()
|
|
|
|
_, err = c.GetUTXO(utxoID)
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
|
|
c.AddUTXO(utxo)
|
|
|
|
fetchedUTXO, err = c.GetUTXO(utxoID)
|
|
require.NoError(err)
|
|
require.Equal(utxo, fetchedUTXO)
|
|
|
|
c.DeleteUTXO(utxoID)
|
|
|
|
_, err = c.GetUTXO(utxoID)
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
}
|
|
|
|
func ChainTxTest(t *testing.T, c Chain) {
|
|
require := require.New(t)
|
|
|
|
fetchedTx, err := c.GetTx(populatedTxID)
|
|
require.NoError(err)
|
|
|
|
// Compare IDs because [fetchedTx] differs between nil and empty fields
|
|
require.Equal(populatedTx.ID(), fetchedTx.ID())
|
|
|
|
// Pull again for the cached path
|
|
fetchedTx, err = c.GetTx(populatedTxID)
|
|
require.NoError(err)
|
|
require.Equal(populatedTx.ID(), fetchedTx.ID())
|
|
|
|
tx := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: lux.BaseTx{
|
|
BlockchainID: ids.GenerateTestID(),
|
|
}}}
|
|
require.NoError(tx.Initialize())
|
|
txID := tx.ID()
|
|
|
|
_, err = c.GetTx(txID)
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
|
|
// Pull again for the cached path
|
|
_, err = c.GetTx(txID)
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
|
|
c.AddTx(tx)
|
|
|
|
fetchedTx, err = c.GetTx(txID)
|
|
require.NoError(err)
|
|
require.Equal(tx, fetchedTx)
|
|
}
|
|
|
|
func ChainBlockTest(t *testing.T, c Chain) {
|
|
require := require.New(t)
|
|
|
|
fetchedBlkID, err := c.GetBlockIDAtHeight(populatedBlkHeight)
|
|
require.NoError(err)
|
|
require.Equal(populatedBlkID, fetchedBlkID)
|
|
|
|
fetchedBlk, err := c.GetBlock(populatedBlkID)
|
|
require.NoError(err)
|
|
require.Equal(populatedBlk.ID(), fetchedBlk.ID())
|
|
|
|
// Pull again for the cached path
|
|
fetchedBlkID, err = c.GetBlockIDAtHeight(populatedBlkHeight)
|
|
require.NoError(err)
|
|
require.Equal(populatedBlkID, fetchedBlkID)
|
|
|
|
fetchedBlk, err = c.GetBlock(populatedBlkID)
|
|
require.NoError(err)
|
|
require.Equal(populatedBlk.ID(), fetchedBlk.ID())
|
|
|
|
inlineTx := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: lux.BaseTx{
|
|
BlockchainID: ids.GenerateTestID(),
|
|
}}}
|
|
require.NoError(inlineTx.Initialize())
|
|
blk, err := block.NewStandardBlock(
|
|
ids.GenerateTestID(),
|
|
10,
|
|
time.Now(),
|
|
[]*txs.Tx{inlineTx},
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
blkID := blk.ID()
|
|
blkHeight := blk.Height()
|
|
|
|
_, err = c.GetBlockIDAtHeight(blkHeight)
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
|
|
_, err = c.GetBlock(blkID)
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
|
|
// Pull again for the cached path
|
|
_, err = c.GetBlockIDAtHeight(blkHeight)
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
|
|
_, err = c.GetBlock(blkID)
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
|
|
c.AddBlock(blk)
|
|
|
|
fetchedBlkID, err = c.GetBlockIDAtHeight(blkHeight)
|
|
require.NoError(err)
|
|
require.Equal(blkID, fetchedBlkID)
|
|
|
|
fetchedBlk, err = c.GetBlock(blkID)
|
|
require.NoError(err)
|
|
require.Equal(blk, fetchedBlk)
|
|
}
|
|
|
|
func TestInitializeChainState(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
db := memdb.New()
|
|
vdb := versiondb.New(db)
|
|
s, err := New(vdb, parser, metric.NewNoOp().Registry(), trackChecksums)
|
|
require.NoError(err)
|
|
|
|
stopVertexID := ids.GenerateTestID()
|
|
genesisTimestamp := upgrade.InitiallyActiveTime
|
|
require.NoError(s.InitializeChainState(stopVertexID, genesisTimestamp))
|
|
|
|
lastAcceptedID := s.GetLastAccepted()
|
|
genesis, err := s.GetBlock(lastAcceptedID)
|
|
require.NoError(err)
|
|
require.Equal(stopVertexID, genesis.Parent())
|
|
require.Equal(genesisTimestamp.UnixNano(), genesis.Timestamp().UnixNano())
|
|
|
|
childBlock, err := block.NewStandardBlock(
|
|
genesis.ID(),
|
|
genesis.Height()+1,
|
|
genesisTimestamp,
|
|
nil,
|
|
)
|
|
require.NoError(err)
|
|
|
|
s.AddBlock(childBlock)
|
|
s.SetLastAccepted(childBlock.ID())
|
|
require.NoError(s.Commit())
|
|
|
|
require.NoError(s.InitializeChainState(stopVertexID, genesisTimestamp))
|
|
|
|
lastAcceptedID = s.GetLastAccepted()
|
|
lastAccepted, err := s.GetBlock(lastAcceptedID)
|
|
require.NoError(err)
|
|
require.Equal(genesis.ID(), lastAccepted.Parent())
|
|
}
|