// Copyright 2021 The Erigon Authors
// This file is part of Erigon.
//
// Erigon is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Erigon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Erigon. If not, see <http://www.gnu.org/licenses/>.

package kv

import (
	"fmt"
	"sort"
	"strings"

	"github.com/erigontech/erigon/db/kv/dbcfg"
	"github.com/erigontech/erigon/node/gointerfaces/typesproto"
)

// DBSchemaVersion versions list
// 5.0 - BlockTransaction table now has canonical ids (txs of non-canonical blocks moving to NonCanonicalTransaction table)
// 6.0 - BlockTransaction table now has system-txs before and after block (records are absent if block has no system-tx, but sequence increasing)
// 6.1 - Canonical/NonCanonical/BadBlock transitions now stored in same table: kv.EthTx. Add kv.BadBlockNumber table
var DBSchemaVersion = typesproto.VersionReply{Major: 7, Minor: 0, Patch: 0}

const ChangeSets3 = "ChangeSets3"

const (

	//HashedAccounts
	// key - address hash
	// value - account encoded for storage
	// Contains Storage:
	//key - address hash + incarnation + storage key hash
	//value - storage value(common.hash)
	HashedAccountsDeprecated = "HashedAccount"
	HashedStorageDeprecated  = "HashedStorage"
)

const (
	// DatabaseInfo is used to store information about data layout.
	DatabaseInfo = "DbInfo"

	// Naming:
	//   HeaderNumber - Ethereum-specific block number. All nodes have same BlockNum.
	//   HeaderID - auto-increment ID. Depends on order in which node see headers.
	//      Invariant: for all headers in snapshots Number == ID. It means no reason to store Num/ID for this headers in DB.
	//   Same about: TxNum/TxID, BlockNum/BlockID
	HeaderNumber    = "HeaderNumber"           // header_hash -> header_num_u64
	BadHeaderNumber = "BadHeaderNumber"        // header_hash -> header_num_u64
	HeaderCanonical = "CanonicalHeader"        // block_num_u64 -> header hash
	Headers         = "Header"                 // block_num_u64 + hash -> header (RLP)
	HeaderTD        = "HeadersTotalDifficulty" // block_num_u64 + hash -> td (RLP)

	BlockBody = "BlockBody" // block_num_u64 + hash -> block body
	// BlockAccessList stores RLP-encoded block access lists, keyed by block_num_u64 + hash.
	BlockAccessList = "BlockAccessList"

	// Naming:
	//  TxNum - Ethereum canonical transaction number - same across all nodes.
	//  TxnID - auto-increment ID - can be different across all nodes
	//  BlockNum/BlockID - same
	//
	// EthTx - stores all transactions of Canonical/NonCanonical/Bad blocks
	// TxnID (auto-increment ID) - means nodes in network will have different ID of same transactions
	// Snapshots (frozen data): using TxNum (not TxnID)
	//
	// During ReOrg - txs are not removed/updated
	//
	// Also this table has system-txs before and after block: if
	// block has no system-tx - records are absent, but TxnID increasing
	//
	// In Erigon3: table MaxTxNum storing TxNum (not TxnID). History/Indices are using TxNum (not TxnID).
	EthTx    = "BlockTransaction" // tx_id_u64 -> rlp(tx)
	MaxTxNum = "MaxTxNum"         // block_number_u64 -> max_tx_num_in_block_u64

	TxLookup = "BlockTransactionLookup" // hash -> transaction/receipt lookup metadata

	ConfigTable = "Config" // config prefix for the db

	// Progress of sync stages: stageName -> stageData
	SyncStageProgress = "SyncStage"

	// Node database tables (see nodedb.go)

	// NodeRecords stores P2P node records (ENR)
	NodeRecords = "NodeRecord"
	// Inodes stores P2P discovery service info about the nodes
	Inodes = "Inode"

	// Transaction senders - stored separately from the block bodies
	Senders = "TxSender" // block_num_u64 + blockHash -> sendersList (no serialization format, every 20 bytes is new sender)

	// headBlockKey tracks the latest know full block's hash.
	HeadBlockKey = "LastBlock"

	HeadHeaderKey = "LastHeader"

	// headBlockHash, safeBlockHash, finalizedBlockHash of the latest Engine API forkchoice
	LastForkchoice = "LastForkchoice"

	// migrationName -> serialized SyncStageProgress and SyncStageUnwind buckets
	// it stores stages progress to understand in which context was executed migration
	// in case of bug-report developer can ask content of this bucket
	Migrations = "Migration"

	Sequence = "Sequence" // tbl_name -> seq_u64

	Epoch        = "DevEpoch"        // block_num_u64+block_hash->transition_proof
	PendingEpoch = "DevPendingEpoch" // block_num_u64+block_hash->transition_proof

	// BOR
	BorTxLookup                = "BlockBorTransactionLookup"  // transaction_hash -> block_num_u64
	BorEvents                  = "BorEvents"                  // event_id -> event_payload
	BorEventNums               = "BorEventNums"               // block_num -> event_id (last event_id in that block)
	BorEventProcessedBlocks    = "BorEventProcessedBlocks"    // block_num -> block_time, tracks processed blocks in the bridge, used for unwinds and restarts, gets pruned
	BorEventTimes              = "BorEventTimes"              // timestamp -> event_id
	BorSpans                   = "BorSpans"                   // span_id -> span (in JSON encoding)
	BorSpansIndex              = "BorSpansIndex"              // span.StartBlockNumber -> span.Id
	BorMilestones              = "BorMilestones"              // milestone_id -> milestone (in JSON encoding)
	BorMilestoneEnds           = "BorMilestoneEnds"           // start block_num -> milestone_id (first block of milestone)
	BorCheckpoints             = "BorCheckpoints"             // checkpoint_id -> checkpoint (in JSON encoding)
	BorCheckpointEnds          = "BorCheckpointEnds"          // start block_num -> checkpoint_id (first block of checkpoint)
	BorProducerSelections      = "BorProducerSelections"      // span_id -> span selection with accumulated proposer priorities (in JSON encoding)
	BorProducerSelectionsIndex = "BorProducerSelectionsIndex" // span.StartBlockNumber -> span.Id
	BorWitnesses               = "BorWitnesses"               // block_num_u64 + block_hash -> witness
	BorWitnessSizes            = "BorWitnessSizes"            // block_num_u64 + block_hash -> witness size (uint64)

	// Downloader
	BittorrentCompletion = "BittorrentCompletion"
	BittorrentInfo       = "BittorrentInfo"

	// Domains/History/InvertedIndices
	// Constants have "Tbl" prefix, to avoid collision with actual Domain names
	// This constants is very rarely used in APP, but Domain/History/Idx names are widely used
	TblAccountVals        = "AccountVals"
	TblAccountHistoryKeys = "AccountHistoryKeys"
	TblAccountHistoryVals = "AccountHistoryVals"
	TblAccountIdx         = "AccountIdx"

	TblStorageVals        = "StorageVals"
	TblStorageHistoryKeys = "StorageHistoryKeys"
	TblStorageHistoryVals = "StorageHistoryVals"
	TblStorageIdx         = "StorageIdx"

	TblCodeVals        = "CodeVals"
	TblCodeHistoryKeys = "CodeHistoryKeys"
	TblCodeHistoryVals = "CodeHistoryVals"
	TblCodeIdx         = "CodeIdx"

	TblCommitmentVals        = "CommitmentVals"
	TblCommitmentHistoryKeys = "CommitmentHistoryKeys"
	TblCommitmentHistoryVals = "CommitmentHistoryVals"
	TblCommitmentIdx         = "CommitmentIdx"

	TblReceiptVals        = "ReceiptVals"
	TblReceiptHistoryKeys = "ReceiptHistoryKeys"
	TblReceiptHistoryVals = "ReceiptHistoryVals"
	TblReceiptIdx         = "ReceiptIdx"

	TblRCacheVals        = "ReceiptCacheVals"
	TblRCacheHistoryKeys = "ReceiptCacheHistoryKeys"
	TblRCacheHistoryVals = "ReceiptCacheHistoryVals"
	TblRCacheIdx         = "ReceiptCacheIdx"

	TblLogAddressKeys = "LogAddressKeys"
	TblLogAddressIdx  = "LogAddressIdx"
	TblLogTopicsKeys  = "LogTopicsKeys"
	TblLogTopicsIdx   = "LogTopicsIdx"

	TblTracesFromKeys = "TracesFromKeys"
	TblTracesFromIdx  = "TracesFromIdx"
	TblTracesToKeys   = "TracesToKeys"
	TblTracesToIdx    = "TracesToIdx"

	// Prune progress of execution: tableName -> [8bytes of invStep]latest pruned key
	// Could use table constants `Tbl{Account,Storage,Code,Commitment}Keys` for domains
	// corresponding history tables `Tbl{Account,Storage,Code,Commitment}HistoryKeys` for history
	// and `Tbl{Account,Storage,Code,Commitment}Idx` for inverted indices
	TblPruningProgress = "PruningProgress"
	// tableName -> txTo;last pruned val
	TblPruningValsProg = "PruningValsProgress"

	// Erigon-CL Objects

	// [slot + block root] => [signature + block without execution payload]
	BeaconBlocks = "BeaconBlock"

	EffectiveBalancesDump = "EffectiveBalancesDump"
	BalancesDump          = "BalancesDump"

	// [slot] => [Canonical block root]
	CanonicalBlockRoots = "CanonicalBlockRoots"
	// [Root (block root] => Slot
	BlockRootToSlot = "BlockRootToSlot"
	// [Block Root] => [State Root]
	BlockRootToStateRoot = "BlockRootToStateRoot"
	StateRootToBlockRoot = "StateRootToBlockRoot"

	BlockRootToBlockNumber = "BlockRootToBlockNumber"
	BlockRootToBlockHash   = "BlockRootToBlockHash"

	LastBeaconSnapshot    = "LastBeaconSnapshot"
	LastBeaconSnapshotKey = "LastBeaconSnapshotKey"

	BlockRootToKzgCommitments  = "BlockRootToKzgCommitments"
	BlockRootToDataColumnCount = "BlockRootToDataColumnCount"

	// [Block Root] => [Parent Root]
	BlockRootToParentRoot  = "BlockRootToParentRoot"
	ParentRootToBlockRoots = "ParentRootToBlockRoots"

	HighestFinalized = "HighestFinalized" // hash -> transaction/receipt lookup metadata

	// BlockRoot => Beacon Block Header
	BeaconBlockHeaders = "BeaconBlockHeaders"

	// Beacon historical data
	// ValidatorIndex => [Field]
	ValidatorPublicKeys         = "ValidatorPublickeys"
	InvertedValidatorPublicKeys = "InvertedValidatorPublickeys"
	// ValidatorIndex + Slot => [Field]
	ValidatorEffectiveBalance = "ValidatorEffectiveBalance"
	ValidatorSlashings        = "ValidatorSlashings"
	ValidatorBalance          = "ValidatorBalance"
	StaticValidators          = "StaticValidators"
	StateEvents               = "StateEvents"
	ActiveValidatorIndicies   = "ActiveValidatorIndicies"

	// External data
	StateRoot = "StateRoot"
	BlockRoot = "BlockRoot"
	// Differentiate data stored per-slot vs per-epoch
	SlotData  = "SlotData"
	EpochData = "EpochData"
	// State fields
	InactivityScores     = "InactivityScores"
	NextSyncCommittee    = "NextSyncCommittee"
	CurrentSyncCommittee = "CurrentSyncCommittee"
	Eth1DataVotes        = "Eth1DataVotes"

	IntraRandaoMixes = "IntraRandaoMixes" // [validator_index+slot] => [randao_mix]
	RandaoMixes      = "RandaoMixes"      // [validator_index+slot] => [randao_mix]
	Proposers        = "BlockProposers"   // epoch => proposers indices

	// Electra
	PendingDepositsDump           = "PendingDepositsDump"           // block_num => dump
	PendingPartialWithdrawalsDump = "PendingPartialWithdrawalsDump" // block_num => dump
	PendingConsolidationsDump     = "PendingConsolidationsDump"     // block_num => dump
	PendingDeposits               = "PendingDeposits"               // slot => queue_diffs
	PendingPartialWithdrawals     = "PendingPartialWithdrawals"     // slot => queue_diffs
	PendingConsolidations         = "PendingConsolidations"         // slot => queue_diffs
	// End Electra

	// GLOAS (EIP-7732)
	BuildersDump                      = "BuildersDump"                   // slot => dump
	Builders                          = "Builders"                       // slot => queue_diffs
	BuilderPendingWithdrawalsDump     = "BuilderPendingWithdrawalsDump"  // slot => dump
	BuilderPendingWithdrawals         = "BuilderPendingWithdrawals"      // slot => queue_diffs
	PayloadExpectedWithdrawalsDump    = "PayloadExpectedWithdrawalsDump" // slot => dump
	PayloadExpectedWithdrawals        = "PayloadExpectedWithdrawals"     // slot => queue_diffs
	ExecutionPayloadAvailabilityTable = "ExecutionPayloadAvailability"   // slot => bitvector SSZ
	BuilderPendingPaymentsTable       = "BuilderPendingPayments"         // slot => vector SSZ
	PtcWindowTable                    = "PtcWindow"                      // slot => ptc window SSZ
	LatestExecutionPayloadBidTable    = "LatestExecutionPayloadBid"      // slot => compressed SSZ
	// End GLOAS

	StatesProcessingProgress = "StatesProcessingProgress"

	//Diagnostics tables
	DiagSystemInfo = "DiagSystemInfo"
	DiagSyncStages = "DiagSyncStages"
)

// Keys
var (
	// ExperimentalGetProofsLayout is used to keep track whether we store indices to facilitate eth_getProof
	CommitmentLayoutFlagKey = []byte("CommitmentLayouFlag")

	PruneTypeOlder = []byte("older")
	PruneHistory   = []byte("pruneHistory")
	PruneBlocks    = []byte("pruneBlocks")

	DBSchemaVersionKey = []byte("dbVersion")
	GenesisKey         = []byte("genesis")

	BittorrentPeerID = "peerID"

	PlainStateVersion = []byte("PlainStateVersion")

	HighestFinalizedKey = []byte("HighestFinalized")

	StatesProcessingKey          = []byte("StatesProcessing")
	MinimumPrunableStepDomainKey = []byte("MinimumPrunableStepDomainKey")
)

// Vals
var (
	CommitmentLayoutFlagEnabledVal  = []byte{1}
	CommitmentLayoutFlagDisabledVal = []byte{2}
)

// ChaindataTables - list of all buckets. App will panic if some bucket is not in this list.
// This list will be sorted in `init` method.
// ChaindataTablesCfg - can be used to find index in sorted version of ChaindataTables list by name
var ChaindataTables = []string{
	HeaderNumber,
	BadHeaderNumber,
	BlockBody,
	BlockAccessList,
	TxLookup,
	ConfigTable,
	DatabaseInfo,
	SyncStageProgress,
	ChangeSets3,
	Senders,
	HeadBlockKey,
	HeadHeaderKey,
	LastForkchoice,
	Migrations,
	Sequence,
	EthTx,
	HeaderCanonical,
	Headers,
	HeaderTD,
	Epoch,
	PendingEpoch,
	BorTxLookup,
	BorEvents,
	BorEventNums,
	BorEventProcessedBlocks,
	BorEventTimes,
	BorSpans,
	BorSpansIndex,
	BorMilestones,
	BorMilestoneEnds,
	BorCheckpoints,
	BorCheckpointEnds,
	BorProducerSelections,
	BorProducerSelectionsIndex,
	BorWitnesses,
	BorWitnessSizes,
	TblAccountVals,
	TblAccountHistoryKeys,
	TblAccountHistoryVals,
	TblAccountIdx,

	TblStorageVals,
	TblStorageHistoryKeys,
	TblStorageHistoryVals,
	TblStorageIdx,

	TblCodeVals,
	TblCodeHistoryKeys,
	TblCodeHistoryVals,
	TblCodeIdx,

	TblCommitmentVals,
	TblCommitmentHistoryKeys,
	TblCommitmentHistoryVals,
	TblCommitmentIdx,

	TblReceiptVals,
	TblReceiptHistoryKeys,
	TblReceiptHistoryVals,
	TblReceiptIdx,

	TblRCacheVals,
	TblRCacheHistoryKeys,
	TblRCacheHistoryVals,
	TblRCacheIdx,

	TblLogAddressKeys,
	TblLogAddressIdx,
	TblLogTopicsKeys,
	TblLogTopicsIdx,

	TblTracesFromKeys,
	TblTracesFromIdx,
	TblTracesToKeys,
	TblTracesToIdx,

	TblPruningProgress,
	TblPruningValsProg,

	MaxTxNum,

	// Beacon stuff
	BeaconBlocks,
	CanonicalBlockRoots,
	BlockRootToSlot,
	BlockRootToStateRoot,
	StateRootToBlockRoot,
	BlockRootToParentRoot,
	BeaconBlockHeaders,
	HighestFinalized,
	BlockRootToBlockHash,
	BlockRootToBlockNumber,
	LastBeaconSnapshot,
	ParentRootToBlockRoots,
	// Blob Storage
	BlockRootToKzgCommitments,
	BlockRootToDataColumnCount,
	// State Reconstitution
	ValidatorEffectiveBalance,
	ValidatorBalance,
	ValidatorSlashings,
	StaticValidators,
	StateEvents,
	// Other stuff (related to state reconstitution)
	BlockRoot,
	StateRoot,
	SlotData,
	EpochData,
	RandaoMixes,
	Proposers,
	StatesProcessingProgress,
	InactivityScores,
	NextSyncCommittee,
	CurrentSyncCommittee,
	Eth1DataVotes,
	IntraRandaoMixes,
	PendingConsolidations,
	PendingDeposits,
	PendingDepositsDump,
	PendingPartialWithdrawalsDump,
	PendingConsolidationsDump,
	PendingPartialWithdrawals,
	ActiveValidatorIndicies,
	EffectiveBalancesDump,
	BalancesDump,
	// GLOAS (EIP-7732)
	BuildersDump,
	Builders,
	BuilderPendingWithdrawalsDump,
	BuilderPendingWithdrawals,
	PayloadExpectedWithdrawalsDump,
	PayloadExpectedWithdrawals,
	ExecutionPayloadAvailabilityTable,
	BuilderPendingPaymentsTable,
	PtcWindowTable,
	LatestExecutionPayloadBidTable,
}

const (
	RecentLocalTransaction = "RecentLocalTransaction" // sequence_u64 -> tx_hash
	PoolTransaction        = "PoolTransaction"        // txHash -> sender+tx_rlp
	PoolInfo               = "PoolInfo"               // option_key -> option_value
	SenderLastActivity     = "SenderLastActivity"     // senderID_u64 -> last_on_chain_block_u64
)

var TxPoolTables = []string{
	RecentLocalTransaction,
	PoolTransaction,
	PoolInfo,
	SenderLastActivity,
}
var SentryTables = []string{
	Inodes,
	NodeRecords,
}
var ConsensusTables = ChaindataTables //TODO: move bor tables from chaintables to `ConsensusTables`
var HeimdallTables = ChaindataTables
var PolygonBridgeTables = ChaindataTables
var DownloaderTables = []string{
	BittorrentCompletion,
	BittorrentInfo,
}

// ChaindataDeprecatedTables - list of buckets which can be programmatically deleted - for example after migration
var ChaindataDeprecatedTables = []string{
	// Pre-E3 plain / hashed state, changeset, and history-index tables.
	// PlainState / HashedStorage / StorageChangeSet baked the per-account
	// incarnation counter into the storage key; the rest are dead E2-era
	// indices. The E3 execution path stores state in kv.AccountsDomain /
	// kv.StorageDomain / kv.CommitmentDomain, which are incarnation-free.
	// The `drop_legacy_e2_tables` migration drops these buckets on
	// databases that still carry them; listing them here marks them as
	// deprecated so the live schema never recreates them on fresh DBs.
	PlainState,
	HashedAccountsDeprecated,
	HashedStorageDeprecated,
	AccountChangeSetDeprecated,
	StorageChangeSetDeprecated,
	E2AccountsHistory,
	E2StorageHistory,
}

// Diagnostics tables
var DiagnosticsTables = []string{
	DiagSystemInfo,
	DiagSyncStages,
}

type CmpFunc func(k1, k2, v1, v2 []byte) int

type TableCfg map[string]TableCfgItem
type Bucket string

type DBI uint32
type TableFlags uint

const (
	Default    TableFlags = 0x00
	ReverseKey TableFlags = 0x02
	DupSort    TableFlags = 0x04
	IntegerKey TableFlags = 0x08
	IntegerDup TableFlags = 0x20
	ReverseDup TableFlags = 0x40
)

type TableCfgItem struct {
	Flags        TableFlags
	IsDeprecated bool
	DBI          DBI
}

var ChaindataTablesCfg = TableCfg{
	// E2-era tables (deprecated; see ChaindataDeprecatedTables). The
	// DupSort flag is preserved here so that opening an existing E2
	// database for migration uses the right bucket flags before the
	// drop_legacy_e2_tables migration drops them.
	PlainState:                 {Flags: DupSort},
	HashedStorageDeprecated:    {Flags: DupSort},
	AccountChangeSetDeprecated: {Flags: DupSort},
	StorageChangeSetDeprecated: {Flags: DupSort},

	TblAccountVals:        {Flags: DupSort},
	TblAccountHistoryKeys: {Flags: DupSort},
	TblAccountHistoryVals: {Flags: DupSort},
	TblAccountIdx:         {Flags: DupSort},

	TblStorageVals:        {Flags: DupSort},
	TblStorageHistoryKeys: {Flags: DupSort},
	TblStorageHistoryVals: {Flags: DupSort},
	TblStorageIdx:         {Flags: DupSort},

	TblCodeHistoryKeys: {Flags: DupSort},
	TblCodeIdx:         {Flags: DupSort},

	TblCommitmentVals:        {Flags: DupSort},
	TblCommitmentHistoryKeys: {Flags: DupSort},
	TblCommitmentHistoryVals: {Flags: DupSort},
	TblCommitmentIdx:         {Flags: DupSort},

	TblReceiptVals:        {Flags: DupSort},
	TblReceiptHistoryKeys: {Flags: DupSort},
	TblReceiptHistoryVals: {Flags: DupSort},
	TblReceiptIdx:         {Flags: DupSort},

	TblRCacheHistoryKeys: {Flags: DupSort},
	TblRCacheIdx:         {Flags: DupSort},

	TblLogAddressKeys: {Flags: DupSort},
	TblLogAddressIdx:  {Flags: DupSort},
	TblLogTopicsKeys:  {Flags: DupSort},
	TblLogTopicsIdx:   {Flags: DupSort},

	TblTracesFromKeys: {Flags: DupSort},
	TblTracesFromIdx:  {Flags: DupSort},
	TblTracesToKeys:   {Flags: DupSort},
	TblTracesToIdx:    {Flags: DupSort},
}

var AuRaTablesCfg = TableCfg{
	Epoch:        {},
	PendingEpoch: {},
}

var BorTablesCfg = TableCfg{
	BorTxLookup:                {Flags: DupSort},
	BorEvents:                  {Flags: DupSort},
	BorEventNums:               {Flags: DupSort},
	BorEventProcessedBlocks:    {Flags: DupSort},
	BorEventTimes:              {Flags: DupSort},
	BorSpans:                   {Flags: DupSort},
	BorSpansIndex:              {Flags: DupSort},
	BorProducerSelectionsIndex: {Flags: DupSort},
	BorCheckpoints:             {Flags: DupSort},
	BorCheckpointEnds:          {Flags: DupSort},
	BorMilestones:              {Flags: DupSort},
	BorMilestoneEnds:           {Flags: DupSort},
	BorProducerSelections:      {Flags: DupSort},
	BorWitnesses:               {Flags: DupSort},
	BorWitnessSizes:            {Flags: DupSort},
}

var TxpoolTablesCfg = TableCfg{}
var SentryTablesCfg = TableCfg{}
var ConsensusTablesCfg = TableCfg{}
var DownloaderTablesCfg = TableCfg{}
var DiagnosticsTablesCfg = TableCfg{}
var HeimdallTablesCfg = TableCfg{}
var PolygonBridgeTablesCfg = TableCfg{}
var MigrationsTablesCfg = TableCfg{Migrations: {}}

func TablesCfgByLabel(label Label) TableCfg {
	switch label {
	case dbcfg.ChainDB, dbcfg.TemporaryDB, dbcfg.CaplinDB: //TODO: move caplindb tables to own table config
		return ChaindataTablesCfg
	case dbcfg.MigrationsDB:
		return MigrationsTablesCfg
	case dbcfg.TxPoolDB:
		return TxpoolTablesCfg
	case dbcfg.SentryDB:
		return SentryTablesCfg
	case dbcfg.DownloaderDB:
		return DownloaderTablesCfg
	case dbcfg.DiagnosticsDB:
		return DiagnosticsTablesCfg
	case dbcfg.HeimdallDB:
		return HeimdallTablesCfg
	case dbcfg.PolygonBridgeDB:
		return PolygonBridgeTablesCfg
	case dbcfg.ConsensusDB:
		return ConsensusTablesCfg
	default:
		panic(fmt.Sprintf("unexpected label: %s", label))
	}
}
func sortBuckets() {
	sort.SliceStable(ChaindataTables, func(i, j int) bool {
		return strings.Compare(ChaindataTables[i], ChaindataTables[j]) < 0
	})
}

func init() {
	reinit()
}

func reinit() {
	sortBuckets()

	for _, name := range ChaindataTables {
		_, ok := ChaindataTablesCfg[name]
		if !ok {
			ChaindataTablesCfg[name] = TableCfgItem{}
		}
	}

	for _, name := range ChaindataDeprecatedTables {
		_, ok := ChaindataTablesCfg[name]
		if !ok {
			ChaindataTablesCfg[name] = TableCfgItem{}
		}
		tmp := ChaindataTablesCfg[name]
		tmp.IsDeprecated = true
		ChaindataTablesCfg[name] = tmp
	}

	for _, name := range TxPoolTables {
		_, ok := TxpoolTablesCfg[name]
		if !ok {
			TxpoolTablesCfg[name] = TableCfgItem{}
		}
	}

	for _, name := range SentryTables {
		_, ok := SentryTablesCfg[name]
		if !ok {
			SentryTablesCfg[name] = TableCfgItem{}
		}
	}

	for _, name := range ConsensusTables {
		_, ok := ConsensusTablesCfg[name]
		if !ok {
			ConsensusTablesCfg[name] = TableCfgItem{}
		}
	}

	for _, name := range DownloaderTables {
		_, ok := DownloaderTablesCfg[name]
		if !ok {
			DownloaderTablesCfg[name] = TableCfgItem{}
		}
	}

	for _, name := range DiagnosticsTables {
		_, ok := DiagnosticsTablesCfg[name]
		if !ok {
			DiagnosticsTablesCfg[name] = TableCfgItem{}
		}
	}

	for _, name := range HeimdallTables {
		_, ok := HeimdallTablesCfg[name]
		if !ok {
			HeimdallTablesCfg[name] = TableCfgItem{}
		}
	}
	for _, name := range PolygonBridgeTables {
		_, ok := PolygonBridgeTablesCfg[name]
		if !ok {
			PolygonBridgeTablesCfg[name] = TableCfgItem{}
		}
	}
}

// Temporal

const (
	AccountsDomain   Domain = 0 // Eth Accounts
	StorageDomain    Domain = 1 // Eth Account's Storage
	CodeDomain       Domain = 2 // Eth Smart-Contract Code
	CommitmentDomain Domain = 3 // Merkle Trie
	ReceiptDomain    Domain = 4 // Tiny Receipts - without logs. Required for node-operations.
	RCacheDomain     Domain = 5 // Fat Receipts - with logs. Optional.
	DomainLen        Domain = 6 // Technical marker of Enum. Not real Domain.
)

var StateDomains = []Domain{AccountsDomain, StorageDomain, CodeDomain, CommitmentDomain}

const (
	AccountsHistoryIdx   InvertedIdx = 0
	StorageHistoryIdx    InvertedIdx = 1
	CodeHistoryIdx       InvertedIdx = 2
	CommitmentHistoryIdx InvertedIdx = 3
	ReceiptHistoryIdx    InvertedIdx = 4
	RCacheHistoryIdx     InvertedIdx = 5

	LogTopicIdx   InvertedIdx = 6
	LogAddrIdx    InvertedIdx = 7
	TracesFromIdx InvertedIdx = 8
	TracesToIdx   InvertedIdx = 9

	StandaloneIdxLen = 4 // Count of standalone IIs registered via RegisterII (LogTopicIdx..TracesToIdx). Update this when adding a new standalone II.
)

func (idx InvertedIdx) String() string {
	switch idx {
	case AccountsHistoryIdx:
		return "accounts"
	case StorageHistoryIdx:
		return "storage"
	case CodeHistoryIdx:
		return "code"
	case CommitmentHistoryIdx:
		return "commitment"
	case ReceiptHistoryIdx:
		return "receipt"
	case RCacheHistoryIdx:
		return "rcache"
	case LogAddrIdx:
		return "logaddrs"
	case LogTopicIdx:
		return "logtopics"
	case TracesFromIdx:
		return "tracesfrom"
	case TracesToIdx:
		return "tracesto"
	default:
		return "unknown index"
	}
}

func String2InvertedIdx(in string) (InvertedIdx, error) {
	switch strings.ToLower(in) {
	case "accounts":
		return AccountsHistoryIdx, nil
	case "storage":
		return StorageHistoryIdx, nil
	case "code":
		return CodeHistoryIdx, nil
	case "commitment":
		return CommitmentHistoryIdx, nil
	case "receipt":
		return ReceiptHistoryIdx, nil
	case "rcache":
		return RCacheHistoryIdx, nil
	case "logaddrs":
		return LogAddrIdx, nil
	case "logaddr":
		return LogAddrIdx, nil
	case "logtopic":
		return LogTopicIdx, nil
	case "logtopics":
		return LogTopicIdx, nil
	case "tracesfrom":
		return TracesFromIdx, nil
	case "tracesto":
		return TracesToIdx, nil
	default:
		return InvertedIdx(MaxUint16), fmt.Errorf("unknown inverted index name: %s", in)
	}
}

func String2Enum(in string) (uint16, error) {
	ii, err := String2InvertedIdx(in)
	if err != nil {
		d, errD := String2Domain(in)
		if errD != nil {
			return 0, errD
		}
		return uint16(d), nil
	}
	return uint16(ii), nil
}

func (d Domain) String() string {
	switch d {
	case AccountsDomain:
		return "accounts"
	case StorageDomain:
		return "storage"
	case CodeDomain:
		return "code"
	case CommitmentDomain:
		return "commitment"
	case ReceiptDomain:
		return "receipt"
	case RCacheDomain:
		return "rcache"
	default:
		return "unknown domain"
	}
}

func String2Domain(in string) (Domain, error) {
	switch strings.ToLower(in) {
	case "accounts":
		return AccountsDomain, nil
	case "storage":
		return StorageDomain, nil
	case "code":
		return CodeDomain, nil
	case "commitment":
		return CommitmentDomain, nil
	case "receipt":
		return ReceiptDomain, nil
	case "rcache":
		return RCacheDomain, nil
	default:
		return Domain(MaxUint16), fmt.Errorf("unknown name: %s", in)
	}
}

func String2Forkable(in string) (ForkableId, error) {
	switch in {
	default:
		return ForkableId(MaxUint16), fmt.Errorf("unknown forkable name: %s", in)
	}
}

const MaxUint16 uint16 = 1<<16 - 1

// --- Deprecated
const (

	// ChaindataTables

	// Dictionary:
	// "Plain State" - state where keys aren't hashed. "CurrentState" - same, but keys are hashed. "PlainState" used for blocks execution. "CurrentState" used mostly for Merkle root calculation.
	// "incarnation" - uint64 number - how much times given account was SelfDestruct'ed.

	/*
	   PlainState logical layout:

	   	Contains Accounts:
	   	  key - address (unhashed)
	   	  value - account encoded for storage
	   	Contains Storage:
	   	  key - address (unhashed) + incarnation + storage key (unhashed)
	   	  value - storage value(common.hash)

	   Physical layout:

	   	PlainState and HashedStorage utilises DupSort feature of MDBX (store multiple values inside 1 key).

	   -------------------------------------------------------------

	   	key              |            value

	   -------------------------------------------------------------
	   [acc_hash]              | [acc_value]
	   [acc_hash]+[inc]        | [storage1_hash]+[storage1_value]

	   	| [storage2_hash]+[storage2_value] // this value has no own key. it's 2nd value of [acc_hash]+[inc] key.
	   	| [storage3_hash]+[storage3_value]
	   	| ...

	   [acc_hash]+[old_inc]    | [storage1_hash]+[storage1_value]

	   	| ...

	   [acc2_hash]             | [acc2_value]

	   	...
	*/
	PlainState = "PlainState"

	/*
	   AccountChangeSet and StorageChangeSet - of block N store values of state before block N changed them.
	   Because values "after" change stored in PlainState.
	   Logical format:

	   	key - blockNum_u64 + key_in_plain_state
	   	value - value_in_plain_state_before_blockNum_changes

	   Example: If block N changed account A from value X to Y. Then:

	   	AccountChangeSet has record: bigEndian(N) + A -> X
	   	PlainState has record: A -> Y

	   As you can see if block N changes much accounts - then all records have repetitive prefix `bigEndian(N)`.
	   MDBX can store such prefixes only once - by DupSort feature (see `docs/programmers_guide/dupsort.md`).
	   Both buckets are DupSort-ed and have physical format:
	   AccountChangeSet:

	   	key - blockNum_u64
	   	value - address + account(encoded)

	   StorageChangeSet:

	   	key - blockNum_u64 + address + incarnation_u64
	   	value - plain_storage_key + value
	*/
	AccountChangeSetDeprecated = "AccountChangeSet"
	StorageChangeSetDeprecated = "StorageChangeSet"

	/*
	   AccountsHistory and StorageHistory - indices designed to serve next 2 type of requests:
	   1. what is smallest block number >= X where account A changed
	   2. get last shard of A - to append there new block numbers

	   Task 1. is part of "get historical state" operation (see `db/state:DomainGetAsOf`):
	   If `db.seekInFiles(A+bigEndian(X))` returns non-last shard -

	   	then get block number from shard value Y := RoaringBitmap(shard_value).GetGte(X)
	   	and with Y go to ChangeSets: db.Get(ChangeSets, Y+A)

	   If `db.seekInFiles(A+bigEndian(X))` returns last shard -

	   	then we go to PlainState: db.Get(PlainState, A)

	   Format:
	     - index split to shards by 2Kb - RoaringBitmap encoded sorted list of block numbers
	       (to avoid performance degradation of popular accounts or look deep into history.
	       Also 2Kb allows avoid Overflow pages inside DB.)
	     - if shard is not last - then key has suffix 8 bytes = bigEndian(max_block_num_in_this_shard)
	     - if shard is last - then key has suffix 8 bytes = 0xFF

	   It allows:
	     - server task 1. by 1 db operation db.seekInFiles(A+bigEndian(X))
	     - server task 2. by 1 db operation db.Get(A+0xFF)

	   AccountsHistory:

	   	key - address + shard_id_u64
	   	value - roaring bitmap  - list of block where it changed

	   StorageHistory

	   	key - address + storage_key + shard_id_u64
	   	value - roaring bitmap - list of block where it changed
	*/
	E2AccountsHistory = "AccountHistory"
	E2StorageHistory  = "StorageHistory"
)
