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
}
GetAtxTimestamp returns ATX timestamp.
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
}