// Copyright 2024 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 aura

import (
	"errors"
	"fmt"
	"math/big"
	"sync"
	"sync/atomic"
	"time"

	"github.com/holiman/uint256"

	"github.com/erigontech/erigon/common"
	"github.com/erigontech/erigon/common/dbg"
	"github.com/erigontech/erigon/common/log/v3"
	"github.com/erigontech/erigon/db/kv"
	"github.com/erigontech/erigon/execution/chain"
	"github.com/erigontech/erigon/execution/protocol/misc"
	"github.com/erigontech/erigon/execution/protocol/rules"
	"github.com/erigontech/erigon/execution/protocol/rules/clique"
	"github.com/erigontech/erigon/execution/protocol/rules/ethash"
	"github.com/erigontech/erigon/execution/rlp"
	"github.com/erigontech/erigon/execution/state"
	"github.com/erigontech/erigon/execution/tracing"
	"github.com/erigontech/erigon/execution/types"
	"github.com/erigontech/erigon/execution/types/accounts"
	"github.com/erigontech/erigon/execution/vm/evmtypes"
	"github.com/erigontech/erigon/rpc"
)

var DEBUG_LOG_FROM = uint64(dbg.EnvInt("AURA_DEBUG_FROM", 999_999_999))

/*
Not implemented features from OS:
 - two_thirds_majority_transition - because no chains in OE where this is != MaxUint64 - means 1/2 majority used everywhere
 - emptyStepsTransition - same

Repo with solidity sources: https://github.com/poanetwork/posdao-contracts
*/

type Step struct {
	calibrate bool // whether calibration is enabled.
	inner     atomic.Uint64
	// Planned durations of steps.
	durations []StepDurationInfo
}

func (s *Step) doCalibrate() {
	if s.calibrate {
		if !s.optCalibrate() {
			ctr := s.inner.Load()
			panic(fmt.Errorf("step counter under- or overflow: %d", ctr))
		}
	}
}

// optCalibrate Calibrates the AuRa step number according to the current time.
func (s *Step) optCalibrate() bool {
	now := time.Now().Unix()
	var info StepDurationInfo
	i := 0
	for _, d := range s.durations {
		if d.TransitionTimestamp >= uint64(now) {
			break
		}
		info = d
		i++
	}
	if i == 0 {
		panic("durations cannot be empty")
	}

	if uint64(now) < info.TransitionTimestamp {
		return false
	}

	newStep := (uint64(now)-info.TransitionTimestamp)/info.StepDuration + info.TransitionStep
	s.inner.Store(newStep)
	return true
}

type ReceivedStepHashes map[uint64]map[common.Address]common.Hash //BTreeMap<(u64, Address), H256>

// nolint
func (r ReceivedStepHashes) get(step uint64, author common.Address) (common.Hash, bool) {
	res, ok := r[step]
	if !ok {
		return common.Hash{}, false
	}
	result, ok := res[author]
	return result, ok
}

// nolint
func (r ReceivedStepHashes) insert(step uint64, author common.Address, blockHash common.Hash) {
	res, ok := r[step]
	if !ok {
		res = map[common.Address]common.Hash{}
		r[step] = res
	}
	res[author] = blockHash
}

// nolint
func (r ReceivedStepHashes) dropAncient(step uint64) {
	for i := range r {
		if i < step {
			delete(r, i)
		}
	}
}

// nolint
type EpochManager struct {
	epochTransitionHash   common.Hash // H256,
	epochTransitionNumber uint64      // BlockNumber
	finalityChecker       *RollingFinality
	force                 bool
}

func NewEpochManager() *EpochManager {
	return &EpochManager{
		finalityChecker: NewRollingFinality([]common.Address{}),
		force:           true,
	}
}

func (e *EpochManager) noteNewEpoch() { e.force = true }

// zoomValidators - Zooms to the epoch after the header with the given hash. Returns true if succeeded, false otherwise.
// It's analog of zoom_to_after function in OE, but doesn't require external locking
// nolint
func (e *EpochManager) zoomToAfter(chain rules.ChainHeaderReader, er *NonTransactionalEpochReader, validators ValidatorSet, hash common.Hash, call rules.SystemCall) (*RollingFinality, uint64, bool) {
	var lastWasParent bool
	if e.finalityChecker.lastPushed != nil {
		lastWasParent = *e.finalityChecker.lastPushed == hash
	}

	// early exit for current target == chain head, but only if the epochs are
	// the same.
	if lastWasParent && !e.force {
		return e.finalityChecker, e.epochTransitionNumber, true
	}
	e.force = false

	// epoch_transition_for can be an expensive call, but in the absence of
	// forks it will only need to be called for the block directly after
	// epoch transition, in which case it will be O(1) and require a single
	// DB lookup.
	lastTransition, ok := epochTransitionFor(chain, er, hash)
	if !ok {
		if lastTransition.BlockNumber > DEBUG_LOG_FROM {
			fmt.Printf("zoom1: %d\n", lastTransition.BlockNumber)
		}
		return e.finalityChecker, e.epochTransitionNumber, false
	}

	// extract other epoch set if it's not the same as the last.
	if lastTransition.BlockHash != e.epochTransitionHash {
		proof := &EpochTransitionProof{}
		if err := rlp.DecodeBytes(lastTransition.ProofRlp, proof); err != nil {
			panic(err)
		}
		first := proof.SignalNumber == 0
		if lastTransition.BlockNumber > DEBUG_LOG_FROM {
			fmt.Printf("zoom2: %d,%d\n", lastTransition.BlockNumber, len(proof.SetProof))
		}

		// use signal number so multi-set first calculation is correct.
		list, _, err := validators.epochSet(first, proof.SignalNumber, proof.SetProof, call)
		if err != nil {
			panic(fmt.Errorf("proof produced by this engine is invalid: %w", err))
		}
		epochSet := list.validators
		log.Trace("[aura] Updating finality checker with new validator set extracted from epoch", "num", lastTransition.BlockNumber)
		e.finalityChecker = NewRollingFinality(epochSet)
		if proof.SignalNumber >= DEBUG_LOG_FROM {
			fmt.Printf("new rolling finality: %d\n", proof.SignalNumber)
			for i := 0; i < len(epochSet); i++ {
				fmt.Printf("\t%x\n", epochSet[i])
			}
		}
	}

	e.epochTransitionHash = lastTransition.BlockHash
	e.epochTransitionNumber = lastTransition.BlockNumber
	return e.finalityChecker, e.epochTransitionNumber, true
}

// / Get the transition to the epoch the given parent hash is part of
// / or transitions to.
// / This will give the epoch that any children of this parent belong to.
// /
// / The block corresponding the parent hash must be stored already.
// nolint
func epochTransitionFor(chain rules.ChainHeaderReader, e *NonTransactionalEpochReader, parentHash common.Hash) (transition EpochTransition, ok bool) {
	//TODO: probably this version of func doesn't support non-canonical epoch transitions
	h := chain.GetHeaderByHash(parentHash)
	if h == nil {
		return transition, false
	}
	num, hash, transitionProof, err := e.FindBeforeOrEqualNumber(h.Number.Uint64())
	if err != nil {
		panic(err)
	}
	if transitionProof == nil {
		panic("genesis epoch transition must already be set")
	}
	return EpochTransition{BlockNumber: num, BlockHash: hash, ProofRlp: transitionProof}, true
}

// AuRa
// nolint
type AuRa struct {
	e           *NonTransactionalEpochReader
	exitCh      chan struct{}
	signerMutex sync.Mutex // Protects the signer fields

	step PermissionedStep
	// History of step hashes recently received from peers.
	receivedStepHashes ReceivedStepHashes

	cfg           AuthorityRoundParams
	EmptyStepsSet *EmptyStepSet
	EpochManager  *EpochManager // Mutex<EpochManager>,

	certifier     *common.Address // certifies service transactions
	certifierLock sync.Mutex
	db            kv.RwDB
}

func NewAuRa(spec *chain.AuRaConfig, db kv.RwDB) (*AuRa, error) {
	auraParams, err := FromJson(spec)
	if err != nil {
		return nil, err
	}

	if _, ok := auraParams.StepDurations[0]; !ok {
		return nil, errors.New("authority Round step 0 duration is undefined")
	}
	for _, v := range auraParams.StepDurations {
		if v == 0 {
			return nil, errors.New("authority Round step duration cannot be 0")
		}
	}
	//shouldTimeout := auraParams.StartStep == nil
	initialStep := uint64(0)
	if auraParams.StartStep != nil {
		initialStep = *auraParams.StartStep
	}
	durations := make([]StepDurationInfo, 0, 1+len(auraParams.StepDurations))
	durInfo := StepDurationInfo{
		TransitionStep:      0,
		TransitionTimestamp: 0,
		StepDuration:        auraParams.StepDurations[0],
	}
	durations = append(durations, durInfo)
	times := common.SortedKeys(auraParams.StepDurations)
	for i := 1; i < len(auraParams.StepDurations); i++ { // skip first
		time := times[i]
		dur := auraParams.StepDurations[time]
		step, t, ok := nextStepTimeDuration(durInfo, time)
		if !ok {
			return nil, errors.New("timestamp overflow")
		}
		durInfo.TransitionStep = step
		durInfo.TransitionTimestamp = t
		durInfo.StepDuration = dur
		durations = append(durations, durInfo)
	}
	step := &Step{
		calibrate: auraParams.StartStep == nil,
		durations: durations,
	}
	step.inner.Store(initialStep)
	step.doCalibrate()

	/*
		    let engine = Arc::new(AuthorityRound {
		        epoch_manager: Mutex::new(EpochManager::blank()),
		        received_step_hashes: RwLock::new(Default::default()),
		        gas_limit_override_cache: Mutex::new(LruCache::new(GAS_LIMIT_OVERRIDE_CACHE_CAPACITY)),
		    })
			// Do not initialize timeouts for tests.
		    if should_timeout {
		        let handler = TransitionHandler {
		            step: engine.step.clone(),
		            client: engine.client.clone(),
		        };
		        engine
		            .transition_service
		            .register_handler(Arc::new(handler))?;
		    }
	*/

	exitCh := make(chan struct{})

	c := &AuRa{
		e:                  newEpochReader(db),
		exitCh:             exitCh,
		step:               PermissionedStep{inner: step},
		cfg:                auraParams,
		receivedStepHashes: ReceivedStepHashes{},
		EpochManager:       NewEpochManager(),
		db:                 db,
	}
	c.step.canPropose.Store(true)

	return c, nil
}

// NewRo is used by the RPC daemon
func NewRo(spec *chain.AuRaConfig, db kv.RoDB) (*AuRa, error) {
	c, err := NewAuRa(spec, kv.RwWrapper{RoDB: db})
	if err != nil {
		return nil, err
	}
	c.e.readonly = true
	return c, nil
}

// A helper accumulator function mapping a step duration and a step duration transition timestamp
// to the corresponding step number and the correct starting second of the step.
func nextStepTimeDuration(info StepDurationInfo, time uint64) (uint64, uint64, bool) {
	stepDiff := time + info.StepDuration
	if stepDiff < 1 {
		return 0, 0, false
	}
	stepDiff -= 1
	if stepDiff < info.TransitionTimestamp {
		return 0, 0, false
	}
	stepDiff -= info.TransitionTimestamp
	if info.StepDuration == 0 {
		return 0, 0, false
	}
	stepDiff /= info.StepDuration
	timeDiff := stepDiff * info.StepDuration
	return info.TransitionStep + stepDiff, info.TransitionTimestamp + timeDiff, true
}

// Type returns underlying rules engine
func (c *AuRa) Type() chain.RulesName {
	return chain.AuRaRules
}

// Author implements rules.Engine, returning the Ethereum address recovered
// from the signature in the header's extra-data section.
// This is thread-safe (only access the Coinbase of the header)
func (c *AuRa) Author(header *types.Header) (accounts.Address, error) {
	/*
				 let message = keccak(empty_step_rlp(self.step, &self.parent_hash));
		        let public = publickey::recover(&self.signature.into(), &message)?;
		        Ok(publickey::public_to_address(&public))
	*/
	return accounts.InternAddress(header.Coinbase), nil
}

// VerifyHeader checks whether a header conforms to the consensus rules.
func (c *AuRa) VerifyHeader(chain rules.ChainHeaderReader, header *types.Header, _ bool) error {
	number := header.Number.Uint64()
	if number == 0 {
		return nil
	}
	parent := chain.GetHeader(header.ParentHash, number-1)
	if parent == nil {
		log.Error("rules.ErrUnknownAncestor", "parentNum", number-1, "hash", header.ParentHash.String())
		return rules.ErrUnknownAncestor
	}
	return ethash.VerifyHeaderBasics(chain, header, parent, true /*checkTimestamp*/, c.HasGasLimitContract() /*skipGasLimit*/)
}

// nolint
func (c *AuRa) hasReceivedStepHashes(step uint64, author common.Address, newHash common.Hash) bool {
	/*
		self
			       .received_step_hashes
			       .read()
			       .get(&received_step_key)
			       .map_or(false, |h| *h != new_hash)
	*/
	return false
}

// nolint
func (c *AuRa) insertReceivedStepHashes(step uint64, author common.Address, newHash common.Hash) {
	/*
	   	    self.received_step_hashes
	                      .write()
	                      .insert(received_step_key, new_hash);
	*/
}

// nolint
func (c *AuRa) verifyFamily(chain rules.ChainHeaderReader, e *NonTransactionalEpochReader, header *types.Header, call rules.Call, syscall rules.SystemCall) error {
	// TODO: I call it from Initialize - because looks like no much reason to have separated "verifyFamily" call

	step := header.AuRaStep
	parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
	parentStep := parent.AuRaStep
	//nolint
	validators, setNumber, err := c.epochSet(chain, e, header, syscall)
	if err != nil {
		return err
	}
	return nil
	// TODO(yperbasis): re-enable the rest

	// Ensure header is from the step after parent.
	//nolint
	if step == parentStep ||
		(header.Number.Uint64() >= c.cfg.ValidateStepTransition && step <= parentStep) {
		log.Trace("[aura] Multiple blocks proposed for step", "num", parentStep)
		_ = setNumber
		/*
			self.validators.report_malicious(
				header.author(),
				set_number,
				header.number(),
				Default::default(),
			);
			Err(EngineError::DoubleVote(*header.author()))?;
		*/
		return fmt.Errorf("double vote: %x", header.Coinbase)
	}

	// Report malice if the validator produced other sibling blocks in the same step.
	if !c.hasReceivedStepHashes(step, header.Coinbase, header.Hash()) {
		/*
		   trace!(target: "engine", "Validator {} produced sibling blocks in the same step", header.author());
		   self.validators.report_malicious(
		       header.author(),
		       set_number,
		       header.number(),
		       Default::default(),
		   );
		*/
	} else {
		c.insertReceivedStepHashes(step, header.Coinbase, header.Hash())
	}

	// Remove hash records older than two full rounds of steps (picked as a reasonable trade-off between
	// memory consumption and fault-tolerance).
	cnt, err := count(validators, parent.Hash(), call)
	if err != nil {
		return err
	}
	siblingMaliceDetectionPeriod := 2 * cnt
	oldestStep := uint64(0) //  let oldest_step = parent_step.saturating_sub(sibling_malice_detection_period);
	if parentStep > siblingMaliceDetectionPeriod {
		oldestStep = parentStep - siblingMaliceDetectionPeriod
	}
	//nolint
	if oldestStep > 0 {
		/*
		   let mut rsh = self.received_step_hashes.write();
		   let new_rsh = rsh.split_off(&(oldest_step, Address::zero()));
		   *rsh = new_rsh;
		*/
	}

	emptyStepLen := uint64(0)
	//self.report_skipped(header, step, parent_step, &*validators, set_number);

	/*
	   // If empty step messages are enabled we will validate the messages in the seal, missing messages are not
	   // reported as there's no way to tell whether the empty step message was never sent or simply not included.
	   let empty_steps_len = if header.number() >= self.empty_steps_transition {
	       let validate_empty_steps = || -> Result<usize, Error> {
	           let strict_empty_steps = header.number() >= self.strict_empty_steps_transition;
	           let empty_steps = header_empty_steps(header)?;
	           let empty_steps_len = empty_steps.len();
	           let mut prev_empty_step = 0;

	           for empty_step in empty_steps {
	               if empty_step.step <= parent_step || empty_step.step >= step {
	                   Err(EngineError::InsufficientProof(format!(
	                       "empty step proof for invalid step: {:?}",
	                       empty_step.step
	                   )))?;
	               }

	               if empty_step.parent_hash != *header.parent_hash() {
	                   Err(EngineError::InsufficientProof(format!(
	                       "empty step proof for invalid parent hash: {:?}",
	                       empty_step.parent_hash
	                   )))?;
	               }

	               if !empty_step.verify(&*validators).unwrap_or(false) {
	                   Err(EngineError::InsufficientProof(format!(
	                       "invalid empty step proof: {:?}",
	                       empty_step
	                   )))?;
	               }

	               if strict_empty_steps {
	                   if empty_step.step <= prev_empty_step {
	                       Err(EngineError::InsufficientProof(format!(
	                           "{} empty step: {:?}",
	                           if empty_step.step == prev_empty_step {
	                               "duplicate"
	                           } else {
	                               "unordered"
	                           },
	                           empty_step
	                       )))?;
	                   }

	                   prev_empty_step = empty_step.step;
	               }
	           }

	           Ok(empty_steps_len)
	       };

	       match validate_empty_steps() {
	           Ok(len) => len,
	           Err(err) => {
	               trace!(
	                   target: "engine",
	                   "Reporting benign misbehaviour (cause: invalid empty steps) \
	                   at block #{}, epoch set number {}. Own address: {}",
	                   header.number(), set_number, self.address().unwrap_or_default()
	               );
	               self.validators
	                   .report_benign(header.author(), set_number, header.number());
	               return Err(err);
	           }
	       }
	   } else {
	       self.report_skipped(header, step, parent_step, &*validators, set_number);

	       0
	   };
	*/
	if header.Number.Uint64() >= c.cfg.ValidateScoreTransition {
		expectedDifficulty := calculateScore(parentStep, step, emptyStepLen)
		if !header.Difficulty.Eq(&expectedDifficulty) {
			return fmt.Errorf("invalid difficulty: expect=%s, found=%s", &expectedDifficulty, &header.Difficulty)
		}
	}
	return nil
}

// VerifyUncles implements rules.Engine, always returning an error for any
// uncles as this consensus mechanism doesn't permit uncles.
func (c *AuRa) VerifyUncles(chain rules.ChainReader, header *types.Header, uncles []*types.Header) error {
	return nil
	//if len(uncles) > 0 {
	//	return errors.New("uncles not allowed")
	//}
	//return nil
}

// VerifySeal implements rules.Engine, checking whether the signature contained
// in the header satisfies the consensus protocol requirements.
func (c *AuRa) VerifySeal(chain rules.ChainHeaderReader, header *types.Header) error {
	return nil
	//snap, err := c.Snapshot(chain, header.Number.Uint64(), header.Hash(), nil)
	//if err != nil {
	//	return err
	//}
	//return c.verifySeal(chain, header, snap)
}

// Prepare implements rules.Engine, preparing all the consensus fields of the
// header for running the transactions on top.
func (c *AuRa) Prepare(chain rules.ChainHeaderReader, header *types.Header, state *state.IntraBlockState) error {
	return nil
	/// If the block isn't a checkpoint, cast a random vote (good enough for now)
	//header.Coinbase = common.Address{}
	//header.Nonce = types.BlockNonce{}
	//
	//number := header.Number.Uint64()
	/// Assemble the voting snapshot to check which votes make sense
	//snap, err := c.Snapshot(chain, number-1, header.ParentHash, nil)
	//if err != nil {
	//	return err
	//}
	//if number%c.config.Epoch != 0 {
	//	c.lock.RLock()
	//
	//	// Gather all the proposals that make sense voting on
	//	addresses := make([]common.Address, 0, len(c.proposals))
	//	for address, authorize := range c.proposals {
	//		if snap.validVote(address, authorize) {
	//			addresses = append(addresses, address)
	//		}
	//	}
	//	// If there's pending proposals, cast a vote on them
	//	if len(addresses) > 0 {
	//		header.Coinbase = addresses[rand.Intn(len(addresses))]
	//		if c.proposals[header.Coinbase] {
	//			copy(header.Nonce[:], NonceAuthVote)
	//		} else {
	//			copy(header.Nonce[:], nonceDropVote)
	//		}
	//	}
	//	c.lock.RUnlock()
	//}
	/// Set the correct difficulty
	//header.Difficulty = calcDifficulty(snap, c.signer)
	//
	/// Ensure the extra data has all its components
	//if len(header.Extra) < ExtraVanity {
	//	header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, ExtraVanity-len(header.Extra))...)
	//}
	//header.Extra = header.Extra[:ExtraVanity]
	//
	//if number%c.config.Epoch == 0 {
	//	for _, signer := range snap.GetSigners() {
	//		header.Extra = append(header.Extra, signer[:]...)
	//	}
	//}
	//header.Extra = append(header.Extra, make([]byte, ExtraSeal)...)
	//
	/// Mix digest is reserved for now, set to empty
	//header.MixDigest = common.Hash{}
	//
	/// Ensure the timestamp has the correct delay
	//parent := chain.GetHeader(header.ParentHash, number-1)
	//if parent == nil {
	//	return rules.ErrUnknownAncestor
	//}
	//header.Time = parent.Time + c.config.Period
	//
	//now := uint64(time.Now().Unix())
	//if header.Time < now {
	//	header.Time = now
	//}
	//
	//return nil
}

func (c *AuRa) rewriteBytecode(blockNum uint64, state *state.IntraBlockState) {
	for addressValue, rewrittenCode := range c.cfg.RewriteBytecode[blockNum] {
		address := accounts.InternAddress(addressValue)
		state.SetCode(address, rewrittenCode)
	}
}

func (c *AuRa) Initialize(config *chain.Config, chain rules.ChainHeaderReader, header *types.Header,
	state *state.IntraBlockState, syscallCustom rules.SysCallCustom, logger log.Logger, tracer *tracing.Hooks,
) error {
	blockNum := header.Number.Uint64()

	//Check block gas limit from smart contract, if applicable
	err := c.verifyGasLimitOverride(config, chain, header, state, syscallCustom)
	if err != nil {
		return err
	}

	c.rewriteBytecode(blockNum, state)

	syscall := func(addr accounts.Address, data []byte) ([]byte, error) {
		return syscallCustom(addr, data, state, header, false /* constCall */)
	}
	c.certifierLock.Lock()
	if c.cfg.Registrar != nil && c.certifier == nil && config.IsLondon(blockNum) {
		c.certifier = getCertifier(*c.cfg.Registrar, syscall)
	}
	c.certifierLock.Unlock()

	if blockNum == 1 {
		proof, err := c.GenesisEpochData(header, syscall)
		if err != nil {
			panic(err)
		}
		err = c.e.PutEpoch(header.ParentHash, 0, proof) //TODO: block 0 hardcoded - need fix it inside validators
		if err != nil {
			panic(err)
		}
	}

	//if err := c.verifyFamily(chain, e, header, call, syscall); err != nil { //TODO: OE has it as a separate engine call? why?
	//	panic(err)
	//}

	// check_and_lock_block -> check_epoch_end_signal

	epoch, err := c.e.GetEpoch(header.ParentHash, blockNum-1)
	if err != nil {
		logger.Warn("[aura] initialize block: on epoch begin", "err", err)
		return err
	}
	isEpochBegin := epoch != nil
	if !isEpochBegin {
		return nil
	}
	err = c.cfg.Validators.onEpochBegin(isEpochBegin, header, syscall)
	if err != nil {
		logger.Warn("[aura] initialize block: on epoch begin", "err", err)
		return err
	}
	// check_and_lock_block -> check_epoch_end_signal END (before enact)
	return nil
}

func (c *AuRa) applyRewards(header *types.Header, state *state.IntraBlockState, syscall rules.SystemCall) error {
	rewards, err := c.CalculateRewards(nil, header, nil, syscall)
	if err != nil {
		return err
	}
	for _, r := range rewards {
		state.AddBalance(r.Beneficiary, r.Amount, tracing.BalanceIncreaseRewardMineBlock)
	}
	return nil
}

// word `signal epoch` == word `pending epoch`
func (c *AuRa) Finalize(config *chain.Config, header *types.Header, state *state.IntraBlockState,
	uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal,
	chain rules.ChainReader, syscall rules.SystemCall, skipReceiptsEval bool, logger log.Logger,
) (types.FlatRequests, error) {
	if err := c.applyRewards(header, state, syscall); err != nil {
		return nil, err
	}

	// check_and_lock_block -> check_epoch_end_signal (after enact)
	if header.Number.Uint64() >= DEBUG_LOG_FROM {
		fmt.Printf("finalize1: %d,%d\n", header.Number.Uint64(), len(receipts))
	}
	pendingTransitionProof, err := c.cfg.Validators.signalEpochEnd(header.Number.Sign() == 0, header, receipts)
	if err != nil {
		return nil, err
	}
	if pendingTransitionProof != nil {
		if header.Number.Uint64() >= DEBUG_LOG_FROM {
			fmt.Printf("insert_pending_transition: %d,receipts=%d, lenProof=%d\n", header.Number.Uint64(), len(receipts), len(pendingTransitionProof))
		}
		if err = c.e.PutPendingEpoch(header.Hash(), header.Number.Uint64(), pendingTransitionProof); err != nil {
			return nil, err
		}
	}
	// check_and_lock_block -> check_epoch_end_signal END

	finalized := buildFinality(c.EpochManager, chain, c.e, c.cfg.Validators, header, syscall)
	c.EpochManager.finalityChecker.print(header.Number.Uint64())
	epochEndProof, err := isEpochEnd(chain, c.e, finalized, header)
	if err != nil {
		return nil, err
	}
	if epochEndProof != nil {
		c.EpochManager.noteNewEpoch()
		logger.Info("[aura] epoch transition", "block_num", header.Number.Uint64())
		if err := c.e.PutEpoch(header.Hash(), header.Number.Uint64(), epochEndProof); err != nil {
			return nil, err
		}
	}

	return nil, nil
}

func (c *AuRa) TxDependencies(h *types.Header) [][]int {
	return nil
}

func buildFinality(e *EpochManager, chain rules.ChainHeaderReader, er *NonTransactionalEpochReader, validators ValidatorSet, header *types.Header, syscall rules.SystemCall) []unAssembledHeader {
	// commit_block -> aura.build_finality
	_, _, ok := e.zoomToAfter(chain, er, validators, header.ParentHash, syscall)
	if !ok {
		return []unAssembledHeader{}
	}
	if e.finalityChecker.lastPushed == nil || *e.finalityChecker.lastPushed != header.ParentHash {
		if err := e.finalityChecker.buildAncestrySubChain(func(hash common.Hash) ([]common.Address, common.Hash, common.Hash, uint64, bool) {
			h := chain.GetHeaderByHash(hash)
			if h == nil {
				return nil, common.Hash{}, common.Hash{}, 0, false
			}
			return []common.Address{h.Coinbase}, h.Hash(), h.ParentHash, h.Number.Uint64(), true
		}, header.ParentHash, e.epochTransitionHash); err != nil {
			//log.Warn("[aura] buildAncestrySubChain", "err", err)
			return []unAssembledHeader{}
		}
	}

	res, err := e.finalityChecker.push(header.Hash(), header.Number.Uint64(), []common.Address{header.Coinbase})
	if err != nil {
		//log.Warn("[aura] finalityChecker.push", "err", err)
		return []unAssembledHeader{}
	}
	return res
}

func isEpochEnd(chain rules.ChainHeaderReader, e *NonTransactionalEpochReader, finalized []unAssembledHeader, header *types.Header) ([]byte, error) {
	// commit_block -> aura.is_epoch_end
	for i := range finalized {
		pendingTransitionProof, err := e.GetPendingEpoch(finalized[i].hash, finalized[i].number)
		if err != nil {
			return nil, err
		}
		if pendingTransitionProof == nil {
			continue
		}
		if header.Number.Uint64() >= DEBUG_LOG_FROM {
			fmt.Printf("pending transition: %d,%x,len=%d\n", finalized[i].number, finalized[i].hash, len(pendingTransitionProof))
		}

		finalityProof := allHeadersUntil(chain, header, finalized[i].hash)
		var finalizedHeader *types.Header
		if finalized[i].hash == header.Hash() {
			finalizedHeader = header
		} else {
			finalizedHeader = chain.GetHeader(finalized[i].hash, finalized[i].number)
		}
		signalNumber := finalizedHeader.Number
		finalityProof = append(finalityProof, finalizedHeader)
		for i, j := 0, len(finalityProof)-1; i < j; i, j = i+1, j-1 { // reverse
			finalityProof[i], finalityProof[j] = finalityProof[j], finalityProof[i]
		}
		finalityProofRLP, err := rlp.EncodeToBytes(finalityProof)
		if err != nil {
			return nil, err
		}
		/*
			// We turn off can_propose here because upon validator set change there can
			// be two valid proposers for a single step: one from the old set and
			// one from the new.
			//
			// This way, upon encountering an epoch change, the proposer from the
			// new set will be forced to wait until the next step to avoid sealing a
			// block that breaks the invariant that the parent's step < the block's step.
			self.step.can_propose.store(false, AtomicOrdering::SeqCst);
		*/
		return rlp.EncodeToBytes(EpochTransitionProof{SignalNumber: signalNumber.Uint64(), SetProof: pendingTransitionProof, FinalityProof: finalityProofRLP})
	}
	return nil, nil
}

// allHeadersUntil walk the chain backwards from current head until finalized_hash
// to construct transition proof. author == ec_recover(sig) known
// since the blocks are in the DB.
func allHeadersUntil(chain rules.ChainHeaderReader, from *types.Header, to common.Hash) (out []*types.Header) {
	var header = from
	for {
		header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
		if header == nil {
			panic("not found header")
		}
		if header.Number.Sign() == 0 {
			break
		}
		if to == header.Hash() {
			break
		}
		out = append(out, header)
	}
	return out
}

//func (c *AuRa) check_epoch_end(cc *chain.Config, header *types.Header, state *state.IntraBlockState, txs []types.Transaction, uncles []*types.Header, syscall rules.SystemCall) {
//}

// FinalizeAndAssemble implements rules.Engine
func (c *AuRa) FinalizeAndAssemble(config *chain.Config, header *types.Header, state *state.IntraBlockState, txs types.Transactions, uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal, chain rules.ChainReader, syscall rules.SystemCall, call rules.Call, logger log.Logger) (*types.Block, types.FlatRequests, error) {
	_, err := c.Finalize(config, header, state, uncles, receipts, withdrawals, chain, syscall, false, logger)
	if err != nil {
		return nil, nil, err
	}

	// Assemble and return the final block for sealing
	return types.NewBlockForAsembling(header, txs, uncles, receipts, withdrawals), nil, nil
}

// Authorize injects a private key into the rules engine to mint new blocks with.
func (c *AuRa) Authorize(signer common.Address, signFn clique.SignerFn) {
	c.signerMutex.Lock()
	defer c.signerMutex.Unlock()

	//c.signer = signer
	//c.signFn = signFn
}

func (c *AuRa) GenesisEpochData(header *types.Header, caller rules.SystemCall) ([]byte, error) {
	setProof, err := c.cfg.Validators.genesisEpochData(header, caller)
	if err != nil {
		return nil, err
	}
	res, err := rlp.EncodeToBytes(EpochTransitionProof{SignalNumber: 0, SetProof: setProof, FinalityProof: []byte{}})
	if err != nil {
		panic(err)
	}
	//fmt.Printf("reere: %x\n", res)
	 
	 
	return res, nil
}

func (c *AuRa) Seal(chain rules.ChainHeaderReader, block *types.BlockWithReceipts, results chan<- *types.BlockWithReceipts, stop <-chan struct{}) error {
	panic("AuRa block production is not implemented")
	//header := block.Header()
	//
	/// Sealing the genesis block is not supported
	//number := header.Number.Uint64()
	//if number == 0 {
	//	return errUnknownBlock
	//}
	/// For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
	//if c.config.Period == 0 && len(block.Transactions()) == 0 {
	//	log.Info("Sealing paused, waiting for transactions")
	//	return nil
	//}
	/// Don't hold the signer fields for the entire sealing procedure
	//c.lock.RLock()
	//signer, signFn := c.signer, c.signFn
	//c.lock.RUnlock()
	//
	/// Bail out if we're unauthorized to sign a block
	//snap, err := c.Snapshot(chain, number-1, header.ParentHash, nil)
	//if err != nil {
	//	return err
	//}
	//if _, authorized := snap.Signers[signer]; !authorized {
	//	return ErrUnauthorizedSigner
	//}
	/// If we're amongst the recent signers, wait for the next block
	//for seen, recent := range snap.Recents {
	//	if recent == signer {
	//		// Signer is among RecentsRLP, only wait if the current block doesn't shift it out
	//		if limit := uint64(len(snap.Signers)/2 + 1); number < limit || seen > number-limit {
	//			log.Info("Signed recently, must wait for others")
	//			return nil
	//		}
	//	}
	//}
	/// Sweet, the protocol permits us to sign the block, wait for our time
	//delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple
	//if header.Difficulty.Cmp(diffNoTurn) == 0 {
	//	// It's not our turn explicitly to sign, delay it a bit
	//	wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
	//	delay += time.Duration(rand.Int63n(int64(wiggle)))
	//
	//	log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
	//}
	/// Sign all the things!
	//sighash, err := signFn(signer, accounts.MimetypeClique, CliqueRLP(header))
	//if err != nil {
	//	return err
	//}
	//copy(header.Extra[len(header.Extra)-ExtraSeal:], sighash)
	/// Wait until sealing is terminated or delay timeout.
	//log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay))
	//go func() {
	//	select {
	//	case <-stop:
	//		return
	//	case <-time.After(delay):
	//	}
	//
	//	select {
	//	case results <- block.WithSeal(header):
	//	default:
	//		log.Warn("Sealing result is not read by miner", "sealhash", SealHash(header))
	//	}
	//}()
	//
	//return nil
}

func stepProposer(validators ValidatorSet, blockHash common.Hash, step uint64, call rules.Call) (common.Address, error) {
	//c, err := validators.defaultCaller(blockHash)
	//if err != nil {
	//	return common.Address{}, err
	//}
	return validators.getWithCaller(blockHash, uint(step), call)
}

// epochSet fetch correct validator set for epoch at header, taking into account
// finality of previous transitions.
func (c *AuRa) epochSet(chain rules.ChainHeaderReader, e *NonTransactionalEpochReader, h *types.Header, call rules.SystemCall) (ValidatorSet, uint64, error) {
	if c.cfg.ImmediateTransitions {
		return c.cfg.Validators, h.Number.Uint64(), nil
	}

	finalityChecker, epochTransitionNumber, ok := c.EpochManager.zoomToAfter(chain, e, c.cfg.Validators, h.ParentHash, call)
	if !ok {
		return nil, 0, errors.New("unable to zoomToAfter to epoch")
	}
	return finalityChecker.signers, epochTransitionNumber, nil
}

func (c *AuRa) CalcDifficulty(chain rules.ChainHeaderReader, time, parentTime uint64, parentDifficulty uint256.Int, parentNumber uint64, parentHash, parentUncleHash common.Hash, parentStep uint64) uint256.Int {
	currentStep := c.step.inner.inner.Load()
	currentEmptyStepsLen := 0
	return calculateScore(parentStep, currentStep, uint64(currentEmptyStepsLen))
}

// calculateScore - analog of PoW difficulty:
//
//	sqrt(U256::max_value()) + parent_step - current_step + current_empty_steps
func calculateScore(parentStep, currentStep, currentEmptySteps uint64) uint256.Int {
	maxU128 := uint256.NewInt(0).SetAllOne()
	maxU128 = maxU128.Rsh(maxU128, 128)
	res := maxU128.Add(maxU128, uint256.NewInt(parentStep))
	res = res.Sub(res, uint256.NewInt(currentStep))
	res = res.Add(res, uint256.NewInt(currentEmptySteps))
	return *res
}

func (c *AuRa) SealHash(header *types.Header) common.Hash {
	return clique.SealHash(header)
}

// See https://openethereum.github.io/Permissioning.html#gas-price
// This is thread-safe: it only accesses the `certifier` which is used behind a RWLock
func (c *AuRa) IsServiceTransaction(sender accounts.Address, syscall rules.SystemCall) bool {
	c.certifierLock.Lock()
	defer c.certifierLock.Unlock()
	if c.certifier == nil && c.cfg.Registrar != nil {
		c.certifier = getCertifier(*c.cfg.Registrar, syscall)
	}
	if c.certifier == nil {
		return false
	}
	packed, err := certifierAbi().Pack("certified", sender.Value())
	if err != nil {
		panic(err)
	}
	out, err := syscall(accounts.InternAddress(*c.certifier), packed)
	if err != nil {
		panic(err)
	}
	res, err := certifierAbi().Unpack("certified", out)
	if err != nil {
		log.Warn("error while detecting service txn on AuRa", "err", err)
		return false
	}
	if len(res) == 0 {
		return false
	}
	if certified, ok := res[0].(bool); ok {
		return certified
	}
	return false
}

// Close implements rules.Engine. It's a noop for clique as there are no background threads.
func (c *AuRa) Close() error {
	c.db.Close()
	common.SafeClose(c.exitCh)
	return nil
}

// APIs implements rules.Engine, returning the user facing RPC API to allow
// controlling the signer voting.
func (c *AuRa) APIs(chain rules.ChainHeaderReader) []rpc.API {
	return []rpc.API{
		//{
		//Namespace: "clique",
		//Version:   "1.0",
		//Service:   &API{chain: chain, clique: c},
		//Public:    false,
		//}
	}
}

// nolint
func (c *AuRa) emptySteps(fromStep, toStep uint64, parentHash common.Hash) []EmptyStep {
	from := EmptyStep{step: fromStep + 1, parentHash: parentHash}
	to := EmptyStep{step: toStep}
	res := []EmptyStep{}
	if to.LessOrEqual(&from) {
		return res
	}

	c.EmptyStepsSet.Sort()
	c.EmptyStepsSet.ForEach(func(i int, step *EmptyStep) {
		if step.Less(&from) || (&to).Less(step) {
			return
		}
		if step.parentHash != parentHash {
			return
		}
		res = append(res, *step)
	})
	return res
}

func (c *AuRa) CalculateRewards(_ *chain.Config, header *types.Header, _ []*types.Header, syscall rules.SystemCall,
) ([]rules.Reward, error) {
	var rewardContractAddress BlockRewardContract
	var foundContract bool
	for _, c := range c.cfg.BlockRewardContractTransitions {
		if c.blockNum > header.Number.Uint64() {
			break
		}
		foundContract = true
		rewardContractAddress = c
	}
	if foundContract {
		beneficiaries := []common.Address{header.Coinbase}
		rewardKind := []rules.RewardKind{rules.RewardAuthor}
		var amounts []*uint256.Int
		beneficiaries, amounts = callBlockRewardAbi(rewardContractAddress.address, syscall, beneficiaries, rewardKind)
		rewards := make([]rules.Reward, len(amounts))
		for i, amount := range amounts {
			rewards[i].Beneficiary = accounts.InternAddress(beneficiaries[i])
			rewards[i].Kind = rules.RewardExternal
			rewards[i].Amount = *amount
		}
		return rewards, nil
	}

	// block_reward.iter.rev().find(|&(block, _)| *block <= number)
	var reward BlockReward
	var found bool
	for i := range c.cfg.BlockReward {
		if c.cfg.BlockReward[i].blockNum > header.Number.Uint64() {
			break
		}
		found = true
		reward = c.cfg.BlockReward[i]
	}
	if !found {
		return nil, errors.New("Current block's reward is not found; this indicates a chain config error")
	}

	r := rules.Reward{Beneficiary: accounts.InternAddress(header.Coinbase), Kind: rules.RewardAuthor, Amount: *reward.amount}
	return []rules.Reward{r}, nil
}

// See https://github.com/gnosischain/specs/blob/master/execution/withdrawals.md
func (c *AuRa) ExecuteSystemWithdrawals(withdrawals []*types.Withdrawal, syscall rules.SystemCall) error {
	if c.cfg.WithdrawalContractAddress == nil {
		return nil
	}

	maxFailedWithdrawalsToProcess := big.NewInt(4)
	amounts := make([]uint64, 0, len(withdrawals))
	addresses := make([]common.Address, 0, len(withdrawals))
	for _, w := range withdrawals {
		amounts = append(amounts, w.Amount)
		addresses = append(addresses, w.Address)
	}

	packed, err := withdrawalAbi().Pack("executeSystemWithdrawals", maxFailedWithdrawalsToProcess, amounts, addresses)
	if err != nil {
		return err
	}

	_, err = syscall(accounts.InternAddress(*c.cfg.WithdrawalContractAddress), packed)
	if err != nil {
		log.Warn("ExecuteSystemWithdrawals", "err", err)
	}
	return err
}

func (c *AuRa) GetTransferFunc() evmtypes.TransferFunc {
	return misc.Transfer
}

func (c *AuRa) GetPostApplyMessageFunc() evmtypes.PostApplyMessageFunc {
	return nil
}

/*
// extracts the empty steps from the header seal. should only be called when there are 3 fields in the seal
// (i.e. header.number() >= self.empty_steps_transition).
func headerEmptySteps(header *types.Header) ([]EmptyStep, error) {
	s := headerEmptyStepsRaw(header)
	sealedSteps := []SealedEmptyStep{}
	err := rlp.DecodeBytes(s, &sealedSteps)
	if err != nil {
		return nil, err
	}
	steps := make([]EmptyStep, len(sealedSteps))
	for i := range sealedSteps {
		steps[i] = newEmptyStepFromSealed(sealedSteps[i], header.ParentHash)
	}
	return steps, nil
}

func newEmptyStepFromSealed(step SealedEmptyStep, parentHash common.Hash) EmptyStep {
	return EmptyStep{
		signature:  step.signature,
		step:       step.step,
		parentHash: parentHash,
	}
}

// extracts the raw empty steps vec from the header seal. should only be called when there are 3 fields in the seal
// (i.e. header.number() >= self.empty_steps_transition)
func headerEmptyStepsRaw(header *types.Header) []byte {
	if len(header.Seal) < 3 {
		panic("was checked with verify_block_basic; has 3 fields; qed")
	}
	return header.Seal[2]
}
*/
