github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/consensus/istanbul/backend/api.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package backend 18 19 import ( 20 "errors" 21 "math/big" 22 23 "github.com/electroneum/electroneum-sc/common" 24 "github.com/electroneum/electroneum-sc/consensus" 25 istanbulcommon "github.com/electroneum/electroneum-sc/consensus/istanbul/common" 26 "github.com/electroneum/electroneum-sc/core/types" 27 "github.com/electroneum/electroneum-sc/rpc" 28 ) 29 30 // API is a user facing RPC API to dump Istanbul state 31 type API struct { 32 chain consensus.ChainHeaderReader 33 backend *Backend 34 } 35 36 // BlockSigners is contains who created and who signed a particular block, denoted by its number and hash 37 type BlockSigners struct { 38 Number uint64 39 Hash common.Hash 40 Author common.Address 41 Committers []common.Address 42 } 43 44 type Status struct { 45 SigningStatus map[common.Address]int `json:"sealerActivity"` 46 NumBlocks uint64 `json:"numBlocks"` 47 } 48 49 // NodeAddress returns the public address that is used to sign block headers in IBFT 50 func (api *API) NodeAddress() common.Address { 51 return api.backend.Address() 52 } 53 54 // GetSignersFromBlock returns the signers and minter for a given block number, or the 55 // latest block available if none is specified 56 func (api *API) GetSignersFromBlock(number *rpc.BlockNumber) (*BlockSigners, error) { 57 // Retrieve the requested block number (or current if none requested) 58 var header *types.Header 59 if number == nil || *number == rpc.LatestBlockNumber { 60 header = api.chain.CurrentHeader() 61 } else { 62 header = api.chain.GetHeaderByNumber(uint64(number.Int64())) 63 } 64 65 if header == nil { 66 return nil, istanbulcommon.ErrUnknownBlock 67 } 68 69 return api.signers(header) 70 } 71 72 // GetSignersFromBlockByHash returns the signers and minter for a given block hash 73 func (api *API) GetSignersFromBlockByHash(hash common.Hash) (*BlockSigners, error) { 74 header := api.chain.GetHeaderByHash(hash) 75 if header == nil { 76 return nil, istanbulcommon.ErrUnknownBlock 77 } 78 79 return api.signers(header) 80 } 81 82 func (api *API) signers(header *types.Header) (*BlockSigners, error) { 83 author, err := api.backend.Author(header) 84 if err != nil { 85 return nil, err 86 } 87 88 committers, err := api.backend.Signers(header) 89 if err != nil { 90 return nil, err 91 } 92 93 return &BlockSigners{ 94 Number: header.Number.Uint64(), 95 Hash: header.Hash(), 96 Author: author, 97 Committers: committers, 98 }, nil 99 } 100 101 // GetSnapshot retrieves the state snapshot at a given block. 102 func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) { 103 // Retrieve the requested block number (or current if none requested) 104 var header *types.Header 105 if number == nil || *number == rpc.LatestBlockNumber { 106 header = api.chain.CurrentHeader() 107 } else { 108 header = api.chain.GetHeaderByNumber(uint64(number.Int64())) 109 } 110 // Ensure we have an actually valid block and return its snapshot 111 if header == nil { 112 return nil, istanbulcommon.ErrUnknownBlock 113 } 114 return api.backend.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 115 } 116 117 // GetSnapshotAtHash retrieves the state snapshot at a given block. 118 func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) { 119 header := api.chain.GetHeaderByHash(hash) 120 if header == nil { 121 return nil, istanbulcommon.ErrUnknownBlock 122 } 123 return api.backend.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 124 } 125 126 // GetValidators retrieves the list of authorized validators at the specified block. 127 func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) { 128 // Retrieve the requested block number (or current if none requested) 129 var header *types.Header 130 if number == nil || *number == rpc.LatestBlockNumber { 131 header = api.chain.CurrentHeader() 132 } else { 133 header = api.chain.GetHeaderByNumber(uint64(number.Int64())) 134 } 135 // Ensure we have an actually valid block and return the validators from its snapshot 136 if header == nil { 137 return nil, istanbulcommon.ErrUnknownBlock 138 } 139 snap, err := api.backend.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 140 if err != nil { 141 return nil, err 142 } 143 return snap.validators(), nil 144 } 145 146 // GetValidatorsAtHash retrieves the state snapshot at a given block. 147 func (api *API) GetValidatorsAtHash(hash common.Hash) ([]common.Address, error) { 148 header := api.chain.GetHeaderByHash(hash) 149 if header == nil { 150 return nil, istanbulcommon.ErrUnknownBlock 151 } 152 snap, err := api.backend.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 153 if err != nil { 154 return nil, err 155 } 156 return snap.validators(), nil 157 } 158 159 // Candidates returns the current candidates the node tries to uphold and vote on. 160 func (api *API) Candidates() map[common.Address]bool { 161 api.backend.candidatesLock.RLock() 162 defer api.backend.candidatesLock.RUnlock() 163 164 proposals := make(map[common.Address]bool) 165 for address, auth := range api.backend.candidates { 166 proposals[address] = auth 167 } 168 return proposals 169 } 170 171 // Propose injects a new authorization candidate that the validator will attempt to 172 // push through. 173 func (api *API) Propose(address common.Address, auth bool) { 174 api.backend.candidatesLock.Lock() 175 defer api.backend.candidatesLock.Unlock() 176 177 api.backend.candidates[address] = auth 178 } 179 180 // Discard drops a currently running candidate, stopping the validator from casting 181 // further votes (either for or against). 182 func (api *API) Discard(address common.Address) { 183 api.backend.candidatesLock.Lock() 184 defer api.backend.candidatesLock.Unlock() 185 186 delete(api.backend.candidates, address) 187 } 188 189 func (api *API) Status(startBlockNum *rpc.BlockNumber, endBlockNum *rpc.BlockNumber) (*Status, error) { 190 var ( 191 numBlocks uint64 192 header = api.chain.CurrentHeader() 193 start uint64 194 end uint64 195 blockNumber rpc.BlockNumber 196 ) 197 if startBlockNum != nil && endBlockNum == nil { 198 return nil, errors.New("pass the end block number") 199 } 200 201 if startBlockNum == nil && endBlockNum != nil { 202 return nil, errors.New("pass the start block number") 203 } 204 205 if startBlockNum == nil && endBlockNum == nil { 206 numBlocks = uint64(64) 207 header = api.chain.CurrentHeader() 208 end = header.Number.Uint64() 209 start = end - numBlocks 210 blockNumber = rpc.BlockNumber(header.Number.Int64()) 211 } else { 212 end = uint64(*endBlockNum) 213 start = uint64(*startBlockNum) 214 if start > end { 215 return nil, errors.New("start block number should be less than end block number") 216 } 217 218 if end > api.chain.CurrentHeader().Number.Uint64() { 219 return nil, errors.New("end block number should be less than or equal to current block height") 220 } 221 222 numBlocks = end - start 223 blockNumber = rpc.BlockNumber(end) 224 } 225 226 signers, err := api.GetValidators(&blockNumber) 227 228 if err != nil { 229 return nil, err 230 } 231 232 if numBlocks >= end { 233 start = 1 234 if end > start { 235 numBlocks = end - start 236 } else { 237 numBlocks = 0 238 } 239 } 240 signStatus := make(map[common.Address]int) 241 for _, s := range signers { 242 signStatus[s] = 0 243 } 244 245 for n := start; n < end; n++ { 246 blockNum := rpc.BlockNumber(int64(n)) 247 s, _ := api.GetSignersFromBlock(&blockNum) 248 signStatus[s.Author]++ 249 } 250 return &Status{ 251 SigningStatus: signStatus, 252 NumBlocks: numBlocks, 253 }, nil 254 } 255 256 func (api *API) IsValidator(blockNum *rpc.BlockNumber) (bool, error) { 257 var blockNumber rpc.BlockNumber 258 if blockNum != nil { 259 blockNumber = *blockNum 260 } else { 261 header := api.chain.CurrentHeader() 262 blockNumber = rpc.BlockNumber(header.Number.Int64()) 263 } 264 s, _ := api.GetValidators(&blockNumber) 265 266 for _, v := range s { 267 if v == api.backend.address { 268 return true, nil 269 } 270 } 271 return false, nil 272 } 273 274 func (api *API) GetBaseBlockReward(blockNum *rpc.BlockNumber) (*big.Int, error) { 275 var ( 276 blockNumber rpc.BlockNumber 277 header *types.Header 278 ) 279 280 // block number arg is optional 281 if blockNum != nil { 282 blockNumber = *blockNum 283 284 latestHeader := api.chain.CurrentHeader() 285 if uint64(blockNumber.Int64()) > latestHeader.Number.Uint64() { 286 return nil, errors.New("block number should be less than or equal to current block height") 287 } 288 289 header = api.chain.GetHeaderByNumber(uint64(blockNum.Int64())) 290 } else { 291 // get latest header if block number was omitted 292 header = api.chain.CurrentHeader() 293 } 294 295 return api.backend.GetBaseBlockReward(api.chain, header), nil 296 } 297 298 func (api *API) GetTotalEmission(blockNum *rpc.BlockNumber) (*big.Int, error) { 299 var ( 300 blockNumber rpc.BlockNumber 301 header *types.Header 302 ) 303 304 // block number arg is optional 305 if blockNum != nil { 306 blockNumber = *blockNum 307 308 latestHeader := api.chain.CurrentHeader() 309 if uint64(blockNumber.Int64()) > latestHeader.Number.Uint64() { 310 return nil, errors.New("block number should be less than or equal to current block height") 311 } 312 313 header = api.chain.GetHeaderByNumber(uint64(blockNum.Int64())) 314 } else { 315 // get latest header if block number was omitted 316 header = api.chain.CurrentHeader() 317 } 318 319 return api.backend.GetTotalEmission(api.chain, header), nil 320 }