mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
144 lines
3.5 KiB
Go
144 lines
3.5 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package state
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/luxfi/database"
|
|
"github.com/luxfi/database/linkeddb"
|
|
"github.com/luxfi/database/prefixdb"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/node/vms/platformvm/status"
|
|
"github.com/luxfi/node/vms/platformvm/txs"
|
|
lux "github.com/luxfi/utxo"
|
|
)
|
|
|
|
func (s *state) GetTx(txID ids.ID) (*txs.Tx, status.Status, error) {
|
|
if tx, exists := s.addedTxs[txID]; exists {
|
|
return tx.tx, tx.status, nil
|
|
}
|
|
if tx, cached := s.txCache.Get(txID); cached {
|
|
if tx == nil {
|
|
return nil, status.Unknown, database.ErrNotFound
|
|
}
|
|
return tx.tx, tx.status, nil
|
|
}
|
|
|
|
txBytes, err := s.txDB.Get(txID[:])
|
|
if err == database.ErrNotFound {
|
|
s.txCache.Put(txID, nil)
|
|
return nil, status.Unknown, database.ErrNotFound
|
|
}
|
|
if err != nil {
|
|
return nil, status.Unknown, err
|
|
}
|
|
|
|
stx, err := parseTxStatus(txBytes)
|
|
if err != nil {
|
|
return nil, status.Unknown, err
|
|
}
|
|
|
|
tx, err := txs.Parse(stx.Tx)
|
|
if err != nil {
|
|
return nil, status.Unknown, err
|
|
}
|
|
|
|
ptx := &txAndStatus{
|
|
tx: tx,
|
|
status: stx.Status,
|
|
}
|
|
|
|
s.txCache.Put(txID, ptx)
|
|
return ptx.tx, ptx.status, nil
|
|
}
|
|
|
|
func (s *state) AddTx(tx *txs.Tx, status status.Status) {
|
|
s.addedTxs[tx.ID()] = &txAndStatus{
|
|
tx: tx,
|
|
status: status,
|
|
}
|
|
}
|
|
|
|
func (s *state) GetRewardUTXOs(txID ids.ID) ([]*lux.UTXO, error) {
|
|
if utxos, exists := s.addedRewardUTXOs[txID]; exists {
|
|
return utxos, nil
|
|
}
|
|
if utxos, cached := s.rewardUTXOsCache.Get(txID); cached {
|
|
return utxos, nil
|
|
}
|
|
|
|
rawTxDB := prefixdb.New(txID[:], s.rewardUTXODB)
|
|
txDB := linkeddb.NewDefault(rawTxDB)
|
|
it := txDB.NewIterator()
|
|
defer it.Release()
|
|
|
|
utxos := []*lux.UTXO(nil)
|
|
for it.Next() {
|
|
utxo, err := lux.ParseUTXO(it.Value())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
utxos = append(utxos, utxo)
|
|
}
|
|
if err := it.Error(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.rewardUTXOsCache.Put(txID, utxos)
|
|
return utxos, nil
|
|
}
|
|
|
|
func (s *state) AddRewardUTXO(txID ids.ID, utxo *lux.UTXO) {
|
|
s.addedRewardUTXOs[txID] = append(s.addedRewardUTXOs[txID], utxo)
|
|
}
|
|
|
|
func (s *state) writeTXs() error {
|
|
for txID, txStatus := range s.addedTxs {
|
|
stx := txBytesAndStatus{
|
|
Tx: txStatus.tx.Bytes(),
|
|
Status: txStatus.status,
|
|
}
|
|
|
|
// We serialize the [txBytesAndStatus] envelope natively (see
|
|
// statewire.go). The embedded tx keeps its own self-delimiting
|
|
// signed bytes so its TxID is preserved on re-parse.
|
|
txBytes, err := marshalTxStatus(stx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to serialize tx: %w", err)
|
|
}
|
|
|
|
delete(s.addedTxs, txID)
|
|
// Note: Evict is used rather than Put here because stx may end up
|
|
// referencing additional data (because of shared byte slices) that
|
|
// would not be properly accounted for in the cache sizing.
|
|
s.txCache.Evict(txID)
|
|
if err := s.txDB.Put(txID[:], txBytes); err != nil {
|
|
return fmt.Errorf("failed to add tx: %w", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *state) writeRewardUTXOs() error {
|
|
for txID, utxos := range s.addedRewardUTXOs {
|
|
delete(s.addedRewardUTXOs, txID)
|
|
s.rewardUTXOsCache.Put(txID, utxos)
|
|
rawTxDB := prefixdb.New(txID[:], s.rewardUTXODB)
|
|
txDB := linkeddb.NewDefault(rawTxDB)
|
|
|
|
for _, utxo := range utxos {
|
|
utxoBytes, err := utxo.WireBytes()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to serialize reward UTXO: %w", err)
|
|
}
|
|
utxoID := utxo.InputID()
|
|
if err := txDB.Put(utxoID[:], utxoBytes); err != nil {
|
|
return fmt.Errorf("failed to add reward UTXO: %w", err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|