// Copyright 2020 The go-ethereum Authors
// (original work)
// Copyright 2024 The Erigon Authors
// (modifications)
// 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 eth

import (
	"github.com/holiman/uint256"

	"github.com/erigontech/erigon/common"
	"github.com/erigontech/erigon/db/kv"
	"github.com/erigontech/erigon/db/rawdb"
	"github.com/erigontech/erigon/execution/chain"
)

const (
	// softResponseLimit is the target maximum size of replies to data retrievals.
	// It is a soft, per-block-boundary limit: once the running total exceeds this
	// threshold the server stops adding more blocks, but the current block is
	// still included in full. Used by all protocol versions (eth/68–70).
	softResponseLimit = 2 * 1024 * 1024

	// Eth70ResponseSizeLimit is the hard, per-receipt limit introduced in eth/70.
	// Unlike softResponseLimit, it can cut a block's receipt list short (setting
	// LastBlockIncomplete in the response). It is based on maxMessageSize minus
	// room for the packet envelope (request-id, flag, list prefixes).
	Eth70ResponseSizeLimit = maxMessageSize - 512

	// estHeaderSize is the approximate size of an RLP encoded block header.
	estHeaderSize = 500

	// maxHeadersServe is the maximum number of block headers to serve. This number
	// is there to limit the number of disk lookups.
	MaxHeadersServe = 1024

	// maxBodiesServe is the maximum number of block bodies to serve. This number
	// is mostly there to limit the number of disk lookups. With 24KB block sizes
	// nowadays, the practical limit will always be softResponseLimit.
	MaxBodiesServe = 1024

	// maxReceiptsServe is the maximum number of block receipts to serve. This
	// number is mostly there to limit the number of disk lookups. With block
	// containing 200+ transactions nowadays, the practical limit will always
	// be softResponseLimit.
	maxReceiptsServe = 1024

	// MaxBlockAccessListsServe is the maximum number of block access lists to
	// serve for an eth/71 GetBlockAccessLists request (EIP-8159). The spec
	// recommends a per-response cap of 2 MiB, which softResponseLimit already
	// enforces; this cap limits the number of disk lookups even when
	// individual BALs are small.
	MaxBlockAccessListsServe = 1024

	// MaxBlockAccessListsRegenerate caps the number of BAL regenerations (full
	// block re-executions) a single GetBlockAccessLists request can trigger;
	// the response is truncated at the budget and the peer re-requests the rest.
	MaxBlockAccessListsRegenerate = 32
)

// NodeInfo represents a short summary of the `eth` sub-protocol metadata
// known about the host peer.
type NodeInfo struct {
	Network    uint64        `json:"network"`    // Ethereum network ID (1=mainnet, 11155111=Sepolia)
	Difficulty *uint256.Int  `json:"difficulty"` // Total difficulty of the host's blockchain
	Genesis    common.Hash   `json:"genesis"`    // SHA3 hash of the host's genesis block
	Config     *chain.Config `json:"config"`     // ChainDB configuration for the fork rules
	Head       common.Hash   `json:"head"`       // Hex hash of the host's best owned block
}

// ReadNodeInfo retrieves some `eth` protocol metadata about the running host node.
func ReadNodeInfo(getter kv.Getter, config *chain.Config, genesisHash common.Hash, network uint64) *NodeInfo {
	headHash := rawdb.ReadHeadHeaderHash(getter)
	headNumber := rawdb.ReadHeaderNumber(getter, headHash)
	var td *uint256.Int
	if headNumber != nil {
		td, _ = rawdb.ReadTd(getter, headHash, *headNumber)
	}
	return &NodeInfo{
		Network:    network,
		Difficulty: td,
		Genesis:    genesisHash,
		Config:     config,
		Head:       headHash,
	}
}
