github.com/xfers/quorum@v21.1.0+incompatible/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 22 "github.com/ethereum/go-ethereum/common" 23 "github.com/ethereum/go-ethereum/consensus" 24 "github.com/ethereum/go-ethereum/core/types" 25 "github.com/ethereum/go-ethereum/rpc" 26 ) 27 28 // API is a user facing RPC API to dump Istanbul state 29 type API struct { 30 chain consensus.ChainReader 31 istanbul *backend 32 } 33 34 // BlockSigners is contains who created and who signed a particular block, denoted by its number and hash 35 type BlockSigners struct { 36 Number uint64 37 Hash common.Hash 38 Author common.Address 39 Committers []common.Address 40 } 41 42 type Status struct { 43 SigningStatus map[common.Address]int `json:"sealerActivity"` 44 NumBlocks uint64 `json:"numBlocks"` 45 } 46 47 // NodeAddress returns the public address that is used to sign block headers in IBFT 48 func (api *API) NodeAddress() common.Address { 49 return api.istanbul.Address() 50 } 51 52 // GetSignersFromBlock returns the signers and minter for a given block number, or the 53 // latest block available if none is specified 54 func (api *API) GetSignersFromBlock(number *rpc.BlockNumber) (*BlockSigners, error) { 55 // Retrieve the requested block number (or current if none requested) 56 var header *types.Header 57 if number == nil || *number == rpc.LatestBlockNumber { 58 header = api.chain.CurrentHeader() 59 } else { 60 header = api.chain.GetHeaderByNumber(uint64(number.Int64())) 61 } 62 63 if header == nil { 64 return nil, errUnknownBlock 65 } 66 67 return api.signers(header) 68 } 69 70 // GetSignersFromBlockByHash returns the signers and minter for a given block hash 71 func (api *API) GetSignersFromBlockByHash(hash common.Hash) (*BlockSigners, error) { 72 header := api.chain.GetHeaderByHash(hash) 73 if header == nil { 74 return nil, errUnknownBlock 75 } 76 77 return api.signers(header) 78 } 79 80 func (api *API) signers(header *types.Header) (*BlockSigners, error) { 81 author, err := api.istanbul.Author(header) 82 if err != nil { 83 return nil, err 84 } 85 86 committers, err := api.istanbul.Signers(header) 87 if err != nil { 88 return nil, err 89 } 90 91 return &BlockSigners{ 92 Number: header.Number.Uint64(), 93 Hash: header.Hash(), 94 Author: author, 95 Committers: committers, 96 }, nil 97 } 98 99 // GetSnapshot retrieves the state snapshot at a given block. 100 func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) { 101 // Retrieve the requested block number (or current if none requested) 102 var header *types.Header 103 if number == nil || *number == rpc.LatestBlockNumber { 104 header = api.chain.CurrentHeader() 105 } else { 106 header = api.chain.GetHeaderByNumber(uint64(number.Int64())) 107 } 108 // Ensure we have an actually valid block and return its snapshot 109 if header == nil { 110 return nil, errUnknownBlock 111 } 112 return api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 113 } 114 115 // GetSnapshotAtHash retrieves the state snapshot at a given block. 116 func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) { 117 header := api.chain.GetHeaderByHash(hash) 118 if header == nil { 119 return nil, errUnknownBlock 120 } 121 return api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 122 } 123 124 // GetValidators retrieves the list of authorized validators at the specified block. 125 func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) { 126 // Retrieve the requested block number (or current if none requested) 127 var header *types.Header 128 if number == nil || *number == rpc.LatestBlockNumber { 129 header = api.chain.CurrentHeader() 130 } else { 131 header = api.chain.GetHeaderByNumber(uint64(number.Int64())) 132 } 133 // Ensure we have an actually valid block and return the validators from its snapshot 134 if header == nil { 135 return nil, errUnknownBlock 136 } 137 snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 138 if err != nil { 139 return nil, err 140 } 141 return snap.validators(), nil 142 } 143 144 // GetValidatorsAtHash retrieves the state snapshot at a given block. 145 func (api *API) GetValidatorsAtHash(hash common.Hash) ([]common.Address, error) { 146 header := api.chain.GetHeaderByHash(hash) 147 if header == nil { 148 return nil, errUnknownBlock 149 } 150 snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 151 if err != nil { 152 return nil, err 153 } 154 return snap.validators(), nil 155 } 156 157 // Candidates returns the current candidates the node tries to uphold and vote on. 158 func (api *API) Candidates() map[common.Address]bool { 159 api.istanbul.candidatesLock.RLock() 160 defer api.istanbul.candidatesLock.RUnlock() 161 162 proposals := make(map[common.Address]bool) 163 for address, auth := range api.istanbul.candidates { 164 proposals[address] = auth 165 } 166 return proposals 167 } 168 169 // Propose injects a new authorization candidate that the validator will attempt to 170 // push through. 171 func (api *API) Propose(address common.Address, auth bool) { 172 api.istanbul.candidatesLock.Lock() 173 defer api.istanbul.candidatesLock.Unlock() 174 175 api.istanbul.candidates[address] = auth 176 } 177 178 // Discard drops a currently running candidate, stopping the validator from casting 179 // further votes (either for or against). 180 func (api *API) Discard(address common.Address) { 181 api.istanbul.candidatesLock.Lock() 182 defer api.istanbul.candidatesLock.Unlock() 183 184 delete(api.istanbul.candidates, address) 185 } 186 187 func (api *API) Status(startBlockNum *rpc.BlockNumber, endBlockNum *rpc.BlockNumber) (*Status, error) { 188 var ( 189 numBlocks uint64 190 header = api.chain.CurrentHeader() 191 start uint64 192 end uint64 193 blockNumber rpc.BlockNumber 194 ) 195 if startBlockNum != nil && endBlockNum == nil { 196 return nil, errors.New("pass the end block number") 197 } 198 199 if startBlockNum == nil && endBlockNum != nil { 200 return nil, errors.New("pass the start block number") 201 } 202 203 if startBlockNum == nil && endBlockNum == nil { 204 numBlocks = uint64(64) 205 header = api.chain.CurrentHeader() 206 end = header.Number.Uint64() 207 start = end - numBlocks 208 blockNumber = rpc.BlockNumber(header.Number.Int64()) 209 } else { 210 end = uint64(*endBlockNum) 211 start = uint64(*startBlockNum) 212 if start > end { 213 return nil, errors.New("start block number should be less than end block number") 214 } 215 216 if end > api.chain.CurrentHeader().Number.Uint64() { 217 return nil, errors.New("end block number should be less than or equal to current block height") 218 } 219 220 numBlocks = end - start 221 header = api.chain.GetHeaderByNumber(end) 222 blockNumber = rpc.BlockNumber(end) 223 } 224 225 signers, err := api.GetValidators(&blockNumber) 226 227 if err != nil { 228 return nil, err 229 } 230 231 if numBlocks >= end { 232 start = 1 233 if end > start { 234 numBlocks = end - start 235 } else { 236 numBlocks = 0 237 } 238 } 239 signStatus := make(map[common.Address]int) 240 for _, s := range signers { 241 signStatus[s] = 0 242 } 243 244 for n := start; n < end; n++ { 245 blockNum := rpc.BlockNumber(int64(n)) 246 s, _ := api.GetSignersFromBlock(&blockNum) 247 signStatus[s.Author]++ 248 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.istanbul.address { 268 return true, nil 269 } 270 } 271 return false, nil 272 }