github.com/nnlgsakib/mind-dpos@v0.0.0-20230606105614-f3c8ca06f808/consensus/alien/api.go (about)

     1  // Copyright 2018 The gttc Authors
     2  // This file is part of the gttc library.
     3  //
     4  // The gttc library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The gttc library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the gttc library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package alien implements the delegated-proof-of-stake consensus engine.
    18  
    19  package alien
    20  
    21  import (
    22  	"github.com/TTCECO/gttc/common"
    23  	"github.com/TTCECO/gttc/consensus"
    24  	"github.com/TTCECO/gttc/core/types"
    25  	"github.com/TTCECO/gttc/rpc"
    26  	"math/big"
    27  )
    28  
    29  // API is a user facing RPC API to allow controlling the signer and voting
    30  // mechanisms of the delegated-proof-of-stake scheme.
    31  type API struct {
    32  	chain consensus.ChainReader
    33  	alien *Alien
    34  }
    35  
    36  // GetSnapshot retrieves the state snapshot at a given block.
    37  func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
    38  	// Retrieve the requested block number (or current if none requested)
    39  	var header *types.Header
    40  	if number == nil || *number == rpc.LatestBlockNumber {
    41  		header = api.chain.CurrentHeader()
    42  	} else {
    43  		header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
    44  	}
    45  	// Ensure we have an actually valid block and return its snapshot
    46  	if header == nil {
    47  		return nil, errUnknownBlock
    48  	}
    49  	return api.alien.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, nil, defaultLoopCntRecalculateSigners)
    50  
    51  }
    52  
    53  // GetSnapshotAtHash retrieves the state snapshot at a given block.
    54  func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
    55  	header := api.chain.GetHeaderByHash(hash)
    56  	if header == nil {
    57  		return nil, errUnknownBlock
    58  	}
    59  	return api.alien.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, nil, defaultLoopCntRecalculateSigners)
    60  }
    61  
    62  // GetSnapshotAtNumber retrieves the state snapshot at a given block.
    63  func (api *API) GetSnapshotAtNumber(number uint64) (*Snapshot, error) {
    64  	header := api.chain.GetHeaderByNumber(number)
    65  	if header == nil {
    66  		return nil, errUnknownBlock
    67  	}
    68  	return api.alien.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, nil, defaultLoopCntRecalculateSigners)
    69  }
    70  
    71  // GetSnapshotByHeaderTime retrieves the state snapshot by timestamp of header.
    72  // snapshot.header.time <= targetTime < snapshot.header.time + period
    73  // todo: add confirm headertime in return snapshot, to minimize the request from side chain
    74  func (api *API) GetSnapshotByHeaderTime(targetTime uint64, scHash common.Hash) (*Snapshot, error) {
    75  	header := api.chain.CurrentHeader()
    76  	period := new(big.Int).SetUint64(api.chain.Config().Alien.Period)
    77  	target := new(big.Int).SetUint64(targetTime)
    78  	if ceil := new(big.Int).Add(header.Time, period); header == nil || target.Cmp(ceil) > 0 {
    79  		return nil, errUnknownBlock
    80  	}
    81  
    82  	minN := new(big.Int).SetUint64(api.chain.Config().Alien.MaxSignerCount)
    83  	maxN := new(big.Int).Set(header.Number)
    84  	nextN := new(big.Int).SetInt64(0)
    85  	isNext := false
    86  	for {
    87  		if ceil := new(big.Int).Add(header.Time, period); target.Cmp(header.Time) >= 0 && target.Cmp(ceil) < 0 {
    88  			snap, err := api.alien.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, nil, defaultLoopCntRecalculateSigners)
    89  
    90  			// replace coinbase by signer settings
    91  			var scSigners []*common.Address
    92  			for _, signer := range snap.Signers {
    93  				replaced := false
    94  				if _, ok := snap.SCCoinbase[*signer]; ok {
    95  					if addr, ok := snap.SCCoinbase[*signer][scHash]; ok {
    96  						replaced = true
    97  						scSigners = append(scSigners, &addr)
    98  					}
    99  				}
   100  				if !replaced {
   101  					scSigners = append(scSigners, signer)
   102  				}
   103  			}
   104  			mcs := Snapshot{LoopStartTime: snap.LoopStartTime, Period: snap.Period, Signers: scSigners, Number: snap.Number}
   105  			if _, ok := snap.SCNoticeMap[scHash]; ok {
   106  				mcs.SCNoticeMap = make(map[common.Hash]*CCNotice)
   107  				mcs.SCNoticeMap[scHash] = snap.SCNoticeMap[scHash]
   108  			}
   109  			return &mcs, err
   110  		} else {
   111  			if minNext := new(big.Int).Add(minN, big.NewInt(1)); maxN.Cmp(minN) == 0 || maxN.Cmp(minNext) == 0 {
   112  				if !isNext && maxN.Cmp(minNext) == 0 {
   113  					var maxHeaderTime, minHeaderTime *big.Int
   114  					maxH := api.chain.GetHeaderByNumber(maxN.Uint64())
   115  					if maxH != nil {
   116  						maxHeaderTime = new(big.Int).Set(maxH.Time)
   117  					} else {
   118  						break
   119  					}
   120  					minH := api.chain.GetHeaderByNumber(minN.Uint64())
   121  					if minH != nil {
   122  						minHeaderTime = new(big.Int).Set(minH.Time)
   123  					} else {
   124  						break
   125  					}
   126  					period = period.Sub(maxHeaderTime, minHeaderTime)
   127  					isNext = true
   128  				} else {
   129  					break
   130  				}
   131  			}
   132  			// calculate next number
   133  			nextN.Sub(target, header.Time)
   134  			nextN.Div(nextN, period)
   135  			nextN.Add(nextN, header.Number)
   136  
   137  			// if nextN beyond the [minN,maxN] then set nextN = (min+max)/2
   138  			if nextN.Cmp(maxN) >= 0 || nextN.Cmp(minN) <= 0 {
   139  				nextN.Add(maxN, minN)
   140  				nextN.Div(nextN, big.NewInt(2))
   141  			}
   142  			// get new header
   143  			header = api.chain.GetHeaderByNumber(nextN.Uint64())
   144  			if header == nil {
   145  				break
   146  			}
   147  			// update maxN & minN
   148  			if header.Time.Cmp(target) >= 0 {
   149  				if header.Number.Cmp(maxN) < 0 {
   150  					maxN.Set(header.Number)
   151  				}
   152  			} else if header.Time.Cmp(target) <= 0 {
   153  				if header.Number.Cmp(minN) > 0 {
   154  					minN.Set(header.Number)
   155  				}
   156  			}
   157  
   158  		}
   159  	}
   160  	return nil, errUnknownBlock
   161  }