func (tp *TransactionProcessor) GetLayerStateRoot(layer types.LayerID) (types.Hash32, error) {
	bts, err := tp.processorDb.Get(getStateRootLayerKey(layer))
	if err != nil {
		return types.Hash32{}, fmt.Errorf("get from DB: %w", err)
	}
	var x types.Hash32
	x.SetBytes(bts)
	return x, nil
}
GetLayerStateRoot returns the state root at a given layer.
package state
import (
	"container/list"
	"context"
	"fmt"
	"sync"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/spacemeshos/ed25519"
	"github.com/spacemeshos/go-spacemesh/common/types"
	"github.com/spacemeshos/go-spacemesh/database"
	"github.com/spacemeshos/go-spacemesh/events"
	"github.com/spacemeshos/go-spacemesh/log"
	"github.com/spacemeshos/go-spacemesh/mempool"
	"github.com/spacemeshos/go-spacemesh/mesh"
	"github.com/spacemeshos/go-spacemesh/p2p/pubsub"
	"github.com/spacemeshos/go-spacemesh/trie"
)
const IncomingTxProtocol = "TxGossip"
type PreImages struct {
	rootHash  types.Hash32
	preImages []*types.Transaction
}
type Projector interface {
	GetProjection(addr types.Address, prevNonce, prevBalance uint64) (nonce, balance uint64, err error)
}
type TransactionProcessor struct {
	log.Log
	*DB
	pool         *mempool.TxMempool
	processorDb  database.Database
	currentLayer types.LayerID
	rootHash     types.Hash32
	stateQueue   list.List
	projector    Projector
	trie         *trie.Database
	mu           sync.Mutex
	rootMu       sync.RWMutex
}
const newRootKey = "root"
func NewTransactionProcessor(allStates, processorDb database.Database, projector Projector, txPool *mempool.TxMempool, logger log.Log) *TransactionProcessor {
	stateDb, err := New(types.Hash32{}, NewDatabase(allStates))
	if err != nil {
		log.With().Panic("cannot load state db", log.Err(err))
	}
	root := stateDb.IntermediateRoot(false)
	logger.With().Info("started processor", log.FieldNamed("state_root", root))
	return &TransactionProcessor{
		Log:         logger,
		DB:          stateDb,
		processorDb: processorDb,
		rootHash:    root,
		stateQueue:  list.List{},
		projector:   projector,
		trie:        stateDb.TrieDB(),
		pool:        txPool,
		mu:          sync.Mutex{},  
		rootMu:      sync.RWMutex{},
	}
}
func PublicKeyToAccountAddress(pub ed25519.PublicKey) types.Address {
	var addr types.Address
	addr.SetBytes(pub)
	return addr
}
func (tp *TransactionProcessor) AddressExists(addr types.Address) bool {
	return tp.Exist(addr)
}
func (tp *TransactionProcessor) GetLayerApplied(txID types.TransactionID) *types.LayerID {
	layerIDBytes, err := tp.processorDb.Get(txID.Bytes())
	if err != nil {
		return nil
	}
	layerID := types.BytesToLayerID(layerIDBytes)
	return &layerID
}
func (tp *TransactionProcessor) ValidateNonceAndBalance(tx *types.Transaction) error {
	origin := tx.Origin()
	nonce, balance, err := tp.projector.GetProjection(origin, tp.GetNonce(origin), tp.GetBalance(origin))
	if err != nil {
		return fmt.Errorf("failed to project state for account %v: %v", origin.Short(), err)
	}
	if tx.AccountNonce != nonce {
		return fmt.Errorf("incorrect account nonce! Expected: %d, Actual: %d", nonce, tx.AccountNonce)
	}
	if (tx.Amount + tx.Fee) > balance {  
		return fmt.Errorf("insufficient balance! Available: %d, Attempting to spend: %d[amount]+%d[fee]=%d",
			balance, tx.Amount, tx.Fee, tx.Amount+tx.Fee)
	}
	return nil
}
func (tp *TransactionProcessor) ApplyTransactions(layer types.LayerID, txs []*types.Transaction) ([]*types.Transaction, error) {
	if len(txs) == 0 {
		err := tp.addStateToHistory(layer, tp.GetStateRoot())
		return make([]*types.Transaction, 0), err
	}
	tp.mu.Lock()
	defer tp.mu.Unlock()
	remaining := txs
	remainingCount := len(remaining)
	for {
		tp.With().Debug("applying transactions", log.Int("count_remaining", remainingCount))
		remaining = tp.Process(remaining, layer)
		if remainingCount == len(remaining) {
			break
		}
		remainingCount = len(remaining)
	}
	newHash, err := tp.Commit()
	if err != nil {
		return remaining, fmt.Errorf("failed to commit global state: %w", err)
	}
	if err = tp.addStateToHistory(layer, newHash); err != nil {
		return remaining, fmt.Errorf("add state to history: %w", err)
	}
	return remaining, nil
}
func (tp *TransactionProcessor) addStateToHistory(layer types.LayerID, newHash types.Hash32) error {
	tp.trie.Reference(newHash, types.Hash32{})
	err := tp.trie.Commit(newHash, false)
	if err != nil {
		return fmt.Errorf("commit trie: %w", err)
	}
	err = tp.saveStateRoot(newHash, layer)
	if err != nil {
		return fmt.Errorf("save state root: %w", err)
	}
	tp.Log.With().Info("new state root", layer, log.FieldNamed("state_root", newHash))
	return nil
}
func getStateRootLayerKey(layer types.LayerID) []byte {
	return append([]byte(newRootKey), layer.Bytes()...)
}
func (tp *TransactionProcessor) saveStateRoot(stateRoot types.Hash32, layer types.LayerID) error {
	if err := tp.processorDb.Put(getStateRootLayerKey(layer), stateRoot.Bytes()); err != nil {
		return fmt.Errorf("put into DB: %w", err)
	}
	tp.rootMu.Lock()
	tp.rootHash = stateRoot
	tp.rootMu.Unlock()
	return nil
}