func (db *DB) GetAtxHeader(id types.ATXID) (*types.ActivationTxHeader, error) {
	if id == *types.EmptyATXID {
		return nil, errors.New("trying to fetch empty atx id")
	}
	if atxHeader, gotIt := db.atxHeaderCache.Get(id); gotIt {
		return atxHeader, nil
	}
	atxHeaderBytes, err := db.atxs.Get(getAtxHeaderKey(id))
	if err != nil {
		return nil, fmt.Errorf("get ATXs from DB: %w", err)
	}
	var atxHeader types.ActivationTxHeader
	err = types.BytesToInterface(atxHeaderBytes, &atxHeader)
	if err != nil {
		return nil, fmt.Errorf("parse ATX header: %w", err)
	}
	atxHeader.SetID(&id)
	db.atxHeaderCache.Add(id, &atxHeader)
	return &atxHeader, nil
}
GetAtxHeader returns the ATX header by the given ID. This function is thread safe and will return an error if the ID
is not found in the ATX DB.
package activation
import (
	"bytes"
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"sync"
	"time"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/spacemeshos/post/shared"
	"github.com/spacemeshos/go-spacemesh/common/types"
	"github.com/spacemeshos/go-spacemesh/common/util"
	"github.com/spacemeshos/go-spacemesh/database"
	"github.com/spacemeshos/go-spacemesh/events"
	"github.com/spacemeshos/go-spacemesh/log"
	"github.com/spacemeshos/go-spacemesh/mesh"
	"github.com/spacemeshos/go-spacemesh/p2p/pubsub"
	"github.com/spacemeshos/go-spacemesh/signing"
	"github.com/spacemeshos/go-spacemesh/system"
)
const (
	namespaceAtx       = "a"
	namespaceEpoch     = "e"
	namespaceTop       = "p"
	namespaceHeader    = "h"
	namespaceTimestamp = "t"
)
func getNodeAtxKey(nodeID types.NodeID, targetEpoch types.EpochID) []byte {
	b := getNodeAtxPrefix(nodeID)
	b.Write(util.Uint64ToBytesBigEndian(uint64(targetEpoch)))
	return b.Bytes()
}
func getNodeAtxPrefix(nodeID types.NodeID) *bytes.Buffer {
	var b bytes.Buffer
	b.WriteString(namespaceAtx)
	b.WriteString(nodeID.Key)
	return &b
}
func getNodeAtxEpochKey(epoch types.EpochID, nodeID types.NodeID) []byte {
	b := getEpochPrefix(epoch)
	b.WriteString(nodeID.Key)
	b.Write(nodeID.VRFPublicKey)
	return b.Bytes()
}
func getEpochPrefix(epoch types.EpochID) *bytes.Buffer {
	var b bytes.Buffer
	b.WriteString(namespaceEpoch)
	b.Write(epoch.ToBytes())
	return &b
}
func getAtxHeaderKey(atxID types.ATXID) []byte {
	var b bytes.Buffer
	b.WriteString(namespaceHeader)
	b.Write(atxID.Bytes())
	return b.Bytes()
}
func getAtxBodyKey(atxID types.ATXID) []byte {
	return atxID.Bytes()
}
func getAtxTimestampKey(atxID types.ATXID) []byte {
	var b bytes.Buffer
	b.WriteString(namespaceTimestamp)
	b.Write(atxID.Bytes())
	return b.Bytes()
}
var (
	errInvalidSig   = errors.New("identity not found when validating signature, invalid atx")
	errGenesisEpoch = errors.New("tried to retrieve miner weights for target epoch 0")
)
type atxChan struct {
	ch        chan struct{}
	listeners int
}
type DB struct {
	sync.RWMutex
	idStore
	atxs            database.Database
	atxHeaderCache  AtxCache
	meshDb          *mesh.DB
	LayersPerEpoch  uint32
	goldenATXID     types.ATXID
	nipostValidator nipostValidator
	log             log.Log
	processAtxMutex sync.Mutex
	atxChannels     map[types.ATXID]*atxChan
	fetcher system.Fetcher
}
func NewDB(dbStore database.Database, fetcher system.Fetcher, idStore idStore, meshDb *mesh.DB, layersPerEpoch uint32, goldenATXID types.ATXID, nipostValidator nipostValidator, log log.Log) *DB {
	db := &DB{
		idStore:         idStore,
		atxs:            dbStore,
		atxHeaderCache:  NewAtxCache(600),
		meshDb:          meshDb,
		LayersPerEpoch:  layersPerEpoch,
		goldenATXID:     goldenATXID,
		nipostValidator: nipostValidator,
		log:             log,
		atxChannels:     make(map[types.ATXID]*atxChan),
		fetcher:         fetcher,
	}
	return db
}
var closedChan = make(chan struct{})
func init() {
	close(closedChan)
}
func (db *DB) AwaitAtx(id types.ATXID) chan struct{} {
	db.Lock()
	defer db.Unlock()
	if _, err := db.atxs.Get(getAtxHeaderKey(id)); err == nil {
		return closedChan
	}
	ch, found := db.atxChannels[id]
	if !found {
		ch = &atxChan{
			ch:        make(chan struct{}),
			listeners: 0,
		}
		db.atxChannels[id] = ch
	}
	ch.listeners++
	return ch.ch
}
func (db *DB) UnsubscribeAtx(id types.ATXID) {
	db.Lock()
	defer db.Unlock()
	ch, found := db.atxChannels[id]
	if !found {
		return
	}
	ch.listeners--
	if ch.listeners < 1 {
		delete(db.atxChannels, id)
	}
}
func (db *DB) ProcessAtx(ctx context.Context, atx *types.ActivationTx) error {
	db.processAtxMutex.Lock()
	defer db.processAtxMutex.Unlock()
	existingATX, _ := db.GetAtxHeader(atx.ID())
	if existingATX != nil {  
		return nil
	}
	epoch := atx.PubLayerID.GetEpoch()
	db.log.WithContext(ctx).With().Info("processing atx",
		atx.ID(),
		epoch,
		log.FieldNamed("atx_node_id", atx.NodeID),
		atx.PubLayerID)
	if err := db.ContextuallyValidateAtx(atx.ActivationTxHeader); err != nil {
		db.log.WithContext(ctx).With().Error("atx failed contextual validation",
			atx.ID(),
			log.FieldNamed("atx_node_id", atx.NodeID),
			log.Err(err))
	} else {
		db.log.WithContext(ctx).With().Info("atx is valid", atx.ID())
	}
	if err := db.StoreAtx(ctx, epoch, atx); err != nil {
		return fmt.Errorf("cannot store atx %s: %w", atx.ShortString(), err)
	}
	if err := db.StoreNodeIdentity(atx.NodeID); err != nil {
		db.log.WithContext(ctx).With().Error("cannot store node identity",
			log.FieldNamed("atx_node_id", atx.NodeID),
			atx.ID(),
			log.Err(err))
	}
	return nil
}
func (db *DB) createTraversalFuncForMinerWeights(minerWeight map[string]uint64, targetEpoch types.EpochID) func(b *types.Block) (bool, error) {
	return func(b *types.Block) (stop bool, err error) {
		if b.ActiveSet == nil {
			return false, nil
		}
		for _, id := range *b.ActiveSet {
			atx, err := db.GetAtxHeader(id)
			if err != nil {
				log.Panic("error fetching atx %v from database -- inconsistent state", id.ShortString())  
				return false, fmt.Errorf("error fetching atx %v from database -- inconsistent state", id.ShortString())
			}
			if atx.TargetEpoch() != targetEpoch {
				db.log.With().Debug("atx found in relevant layer, but target epoch doesn't match requested epoch",
					atx.ID(),
					log.FieldNamed("atx_target_epoch", atx.TargetEpoch()),
					log.FieldNamed("requested_epoch", targetEpoch))
				continue
			}
			minerWeight[atx.NodeID.Key] = atx.GetWeight()
		}
		return false, nil
	}
}
func (db *DB) GetMinerWeightsInEpochFromView(targetEpoch types.EpochID, view map[types.BlockID]struct{}) (map[string]uint64, error) {
	if targetEpoch == 0 {
		return nil, errGenesisEpoch
	}
	firstLayerOfPrevEpoch := (targetEpoch - 1).FirstLayer()
	minerWeight := make(map[string]uint64)
	traversalFunc := db.createTraversalFuncForMinerWeights(minerWeight, targetEpoch)
	startTime := time.Now()
	err := db.meshDb.ForBlockInView(view, firstLayerOfPrevEpoch, traversalFunc)
	if err != nil {
		return nil, fmt.Errorf("traverse blocks in view: %w", err)
	}
	db.log.With().Info("done calculating miner weights",
		log.Int("numMiners", len(minerWeight)),
		log.String("duration", time.Now().Sub(startTime).String()))
	return minerWeight, nil
}
func (db *DB) SyntacticallyValidateAtx(ctx context.Context, atx *types.ActivationTx) error {
	events.ReportNewActivation(atx)
	pub, err := ExtractPublicKey(atx)
	if err != nil {
		return fmt.Errorf("cannot validate atx sig atx id %v err %v", atx.ShortString(), err)
	}
	if atx.NodeID.Key != pub.String() {
		return fmt.Errorf("node ids don't match")
	}
	if atx.PositioningATX == *types.EmptyATXID {
		return fmt.Errorf("empty positioning atx")
	}
	if atx.PrevATXID != *types.EmptyATXID {
		err = db.ValidateSignedAtx(*pub, atx)
		if err != nil {  
			return fmt.Errorf("no id found %v err %v", atx.ShortString(), err)
		}
		prevATX, err := db.GetAtxHeader(atx.PrevATXID)
		if err != nil {
			return fmt.Errorf("validation failed: prevATX not found: %v", err)
		}
		if prevATX.NodeID.Key != atx.NodeID.Key {
			return fmt.Errorf("previous atx belongs to different miner. atx.ID: %v, atx.NodeID: %v, prevAtx.NodeID: %v",
				atx.ShortString(), atx.NodeID.Key, prevATX.NodeID.Key)
		}
		prevEp := prevATX.PubLayerID.GetEpoch()
		curEp := atx.PubLayerID.GetEpoch()
		if prevEp >= curEp {
			return fmt.Errorf(
				"prevAtx epoch (%v, layer %v) isn't older than current atx epoch (%v, layer %v)",
				prevEp, prevATX.PubLayerID, curEp, atx.PubLayerID)
		}
		if prevATX.Sequence+1 != atx.Sequence {
			return fmt.Errorf("sequence number is not one more than prev sequence number")
		}
		if atx.InitialPost != nil {
			return fmt.Errorf("prevATX declared, but initial Post is included")
		}
		if atx.InitialPostIndices != nil {
			return fmt.Errorf("prevATX declared, but initial Post indices is included in challenge")
		}
	} else {
		if atx.Sequence != 0 {
			return fmt.Errorf("no prevATX declared, but sequence number not zero")
		}
		if atx.InitialPost == nil {
			return fmt.Errorf("no prevATX declared, but initial Post is not included")
		}
		if atx.InitialPostIndices == nil {
			return fmt.Errorf("no prevATX declared, but initial Post indices is not included in challenge")
		}
		if !bytes.Equal(atx.InitialPost.Indices, atx.InitialPostIndices) {
			return errors.New("initial Post indices included in challenge does not equal to the initial Post indices included in the atx")
		}
		initialPostMetadata := *atx.NIPost.PostMetadata
		initialPostMetadata.Challenge = shared.ZeroChallenge
		if err := db.nipostValidator.ValidatePost(pub.Bytes(), atx.InitialPost, &initialPostMetadata, atx.NumUnits); err != nil {
			return fmt.Errorf("invalid initial Post: %v", err)
		}
	}
	if atx.PositioningATX != db.goldenATXID {
		posAtx, err := db.GetAtxHeader(atx.PositioningATX)
		if err != nil {
			return fmt.Errorf("positioning atx not found")
		}
		if !atx.PubLayerID.After(posAtx.PubLayerID) {
			return fmt.Errorf("atx layer (%v) must be after positioning atx layer (%v)",
				atx.PubLayerID, posAtx.PubLayerID)
		}
		if d := atx.PubLayerID.Difference(posAtx.PubLayerID); d > db.LayersPerEpoch {
			return fmt.Errorf("expected distance of one epoch (%v layers) from pos atx but found %v",
				db.LayersPerEpoch, d)
		}
	} else {
		publicationEpoch := atx.PubLayerID.GetEpoch()
		if !publicationEpoch.NeedsGoldenPositioningATX() {
			return fmt.Errorf("golden atx used for atx in epoch %d, but is only valid in epoch 1", publicationEpoch)
		}
	}
	expectedChallengeHash, err := atx.NIPostChallenge.Hash()
	if err != nil {
		return fmt.Errorf("failed to compute NIPost's expected challenge hash: %v", err)
	}
	db.log.WithContext(ctx).With().Info("validating nipost", log.String("expected_challenge_hash", expectedChallengeHash.String()), atx.ID())
	pubKey := signing.NewPublicKey(util.Hex2Bytes(atx.NodeID.Key))
	if err = db.nipostValidator.Validate(*pubKey, atx.NIPost, *expectedChallengeHash, atx.NumUnits); err != nil {
		return fmt.Errorf("invalid nipost: %v", err)
	}
	return nil
}
func (db *DB) ContextuallyValidateAtx(atx *types.ActivationTxHeader) error {
	if atx.PrevATXID != *types.EmptyATXID {
		lastAtx, err := db.GetNodeLastAtxID(atx.NodeID)
		if err != nil {
			db.log.With().Error("could not fetch node last atx", atx.ID(),
				log.FieldNamed("atx_node_id", atx.NodeID),
				log.Err(err))
			return fmt.Errorf("could not fetch node last atx: %v", err)
		}
		if lastAtx != atx.PrevATXID {
			return fmt.Errorf("last atx is not the one referenced")
		}
	} else {
		lastAtx, err := db.GetNodeLastAtxID(atx.NodeID)
		if _, ok := err.(ErrAtxNotFound); err != nil && !ok {
			db.log.With().Error("fetching atx ids failed", log.Err(err))
			return err
		}
		if err == nil {  
			return fmt.Errorf("no prevATX reported, but other atx with same nodeID (%v) found: %v",
				atx.NodeID.ShortString(), lastAtx.ShortString())
		}
	}
	return nil
}
func (db *DB) StoreAtx(ctx context.Context, ech types.EpochID, atx *types.ActivationTx) error {
	db.Lock()
	defer db.Unlock()
	if _, err := db.atxs.Get(getAtxHeaderKey(atx.ID())); err == nil {
		return nil
	}
	err := db.storeAtxUnlocked(atx)
	if err != nil {
		return err
	}
	err = db.updateTopAtxIfNeeded(atx)
	if err != nil {
		return err
	}
	err = db.addAtxToNodeID(atx.NodeID, atx)
	if err != nil {
		return err
	}
	err = db.addNodeAtxToEpoch(ctx, atx.PubLayerID.GetEpoch(), atx.NodeID, atx)
	if err != nil {
		return err
	}
	err = db.addAtxTimestamp(ctx, time.Now(), atx)
	if err != nil {
		return err
	}
	db.log.WithContext(ctx).With().Info("finished storing atx in epoch", atx.ID(), ech)
	return nil
}
func (db *DB) storeAtxUnlocked(atx *types.ActivationTx) error {
	atxHeaderBytes, err := types.InterfaceToBytes(atx.ActivationTxHeader)
	if err != nil {
		return fmt.Errorf("serialize ATX: %w", err)
	}
	err = db.atxs.Put(getAtxHeaderKey(atx.ID()), atxHeaderBytes)
	if err != nil {
		return fmt.Errorf("put ATX in DB: %w", err)
	}
	atxBytes, err := types.InterfaceToBytes(atx)
	if err != nil {
		return fmt.Errorf("parse ATX: %w", err)
	}
	err = db.atxs.Put(getAtxBodyKey(atx.ID()), atxBytes)
	if err != nil {
		return fmt.Errorf("put ATX in DB: %w", err)
	}
	if ch, found := db.atxChannels[atx.ID()]; found {
		close(ch.ch)
		delete(db.atxChannels, atx.ID())
	}
	return nil
}
type atxIDAndLayer struct {
	AtxID   types.ATXID
	LayerID types.LayerID
}
func (db *DB) updateTopAtxIfNeeded(atx *types.ActivationTx) error {
	currentTopAtx, err := db.getTopAtx()
	if err != nil && !errors.Is(err, database.ErrNotFound) {
		return fmt.Errorf("failed to get current atx: %v", err)
	}
	if err == nil && !currentTopAtx.LayerID.Before(atx.PubLayerID) {
		return nil
	}
	newTopAtx := atxIDAndLayer{
		AtxID:   atx.ID(),
		LayerID: atx.PubLayerID,
	}
	topAtxBytes, err := types.InterfaceToBytes(&newTopAtx)
	if err != nil {
		return fmt.Errorf("failed to marshal top atx: %v", err)
	}
	err = db.atxs.Put([]byte(namespaceTop), topAtxBytes)
	if err != nil {
		return fmt.Errorf("failed to store top atx: %v", err)
	}
	return nil
}
func (db *DB) getTopAtx() (atxIDAndLayer, error) {
	topAtxBytes, err := db.atxs.Get([]byte(namespaceTop))
	if err != nil {
		if errors.Is(err, database.ErrNotFound) {
			return atxIDAndLayer{
				AtxID: db.goldenATXID,
			}, nil
		}
		return atxIDAndLayer{}, fmt.Errorf("failed to get top atx: %v", err)
	}
	var topAtx atxIDAndLayer
	err = types.BytesToInterface(topAtxBytes, &topAtx)
	if err != nil {
		return atxIDAndLayer{}, fmt.Errorf("failed to unmarshal top atx: %v", err)
	}
	return topAtx, nil
}
func (db *DB) getAtxTimestamp(id types.ATXID) (time.Time, error) {
	b, err := db.atxs.Get(getAtxTimestampKey(id))
	if err != nil {
		return time.Time{}, fmt.Errorf("get ATXs from DB: %w", err)
	}
	ts := time.Unix(0, int64(binary.LittleEndian.Uint64(b)))
	return ts, nil
}
func (db *DB) addAtxToNodeID(nodeID types.NodeID, atx *types.ActivationTx) error {
	err := db.atxs.Put(getNodeAtxKey(nodeID, atx.PubLayerID.GetEpoch()), atx.ID().Bytes())
	if err != nil {
		return fmt.Errorf("failed to store atx ID for node: %v", err)
	}
	return nil
}
func (db *DB) addNodeAtxToEpoch(ctx context.Context, epoch types.EpochID, nodeID types.NodeID, atx *types.ActivationTx) error {
	db.log.WithContext(ctx).Info("added atx %v to epoch %v", atx.ID().ShortString(), epoch)
	err := db.atxs.Put(getNodeAtxEpochKey(atx.PubLayerID.GetEpoch(), nodeID), atx.ID().Bytes())
	if err != nil {
		return fmt.Errorf("failed to store atx ID for node: %v", err)
	}
	return nil
}
func (db *DB) addAtxTimestamp(ctx context.Context, timestamp time.Time, atx *types.ActivationTx) error {
	db.log.WithContext(ctx).Info("added atx %v timestamp %v", atx.ID().ShortString(), timestamp)
	b := make([]byte, 8)
	binary.LittleEndian.PutUint64(b, uint64(timestamp.UnixNano()))
	err := db.atxs.Put(getAtxTimestampKey(atx.ID()), b)
	if err != nil {
		return fmt.Errorf("failed to store atx timestamp for node: %v", err)
	}
	return nil
}
type ErrAtxNotFound error
func (db *DB) GetNodeLastAtxID(nodeID types.NodeID) (types.ATXID, error) {
	it := db.atxs.Find(getNodeAtxPrefix(nodeID).Bytes())
	defer it.Release()
	if exists := it.Last(); !exists {
		err := ErrAtxNotFound(fmt.Errorf("atx for node %v does not exist", nodeID.ShortString()))
		return *types.EmptyATXID, fmt.Errorf("find ATX in DB: %w", err)
	}
	if it.Error() != nil {
		return *types.EmptyATXID, fmt.Errorf("iterator error: %w", it.Error())
	}
	return types.ATXID(types.BytesToHash(it.Value())), nil
}
func (db *DB) GetEpochAtxs(epochID types.EpochID) (atxs []types.ATXID, err error) {
	it := db.atxs.Find(getEpochPrefix(epochID).Bytes())
	defer it.Release()
	for it.Next() {
		if it.Key() == nil {
			break
		}
		var a types.ATXID
		copy(a[:], it.Value())
		atxs = append(atxs, a)
	}
	db.log.With().Debug("returned epoch atxs", epochID,
		log.Int("count", len(atxs)),
		log.String("atxs", fmt.Sprint(atxs)))
	err = it.Error()
	return
}
func (db *DB) GetNodeAtxIDForEpoch(nodeID types.NodeID, publicationEpoch types.EpochID) (types.ATXID, error) {
	id, err := db.atxs.Get(getNodeAtxKey(nodeID, publicationEpoch))
	if err != nil {
		return *types.EmptyATXID, fmt.Errorf("atx for node %v with publication epoch %v: %v",
			nodeID.ShortString(), publicationEpoch, err)
	}
	return types.ATXID(types.BytesToHash(id)), nil
}
func (db *DB) GetPosAtxID() (types.ATXID, error) {
	idAndLayer, err := db.getTopAtx()
	if err != nil {
		return *types.EmptyATXID, err
	}
	return idAndLayer.AtxID, nil
}
func (db *DB) GetAtxTimestamp(atxid types.ATXID) (time.Time, error) {
	ts, err := db.getAtxTimestamp(atxid)
	if err != nil {
		return time.Time{}, err
	}
	return ts, nil
}
func (db *DB) GetEpochWeight(epochID types.EpochID) (uint64, []types.ATXID, error) {
	weight := uint64(0)
	activeSet, err := db.GetEpochAtxs(epochID - 1)
	if err != nil {
		return 0, nil, err
	}
	for _, atxID := range activeSet {
		atxHeader, err := db.GetAtxHeader(atxID)
		if err != nil {
			return 0, nil, err
		}
		weight += atxHeader.GetWeight()
	}
	return weight, activeSet, nil
}