github.com/Night-mk/quorum@v21.1.0+incompatible/consensus/clique/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 clique 18 19 import ( 20 "errors" 21 "fmt" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/consensus" 25 "github.com/ethereum/go-ethereum/core/types" 26 "github.com/ethereum/go-ethereum/rpc" 27 ) 28 29 // API is a user facing RPC API to allow controlling the signer and voting 30 // mechanisms of the proof-of-authority scheme. 31 type API struct { 32 chain consensus.ChainReader 33 clique *Clique 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.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 50 } 51 52 // GetSnapshotAtHash retrieves the state snapshot at a given block. 53 func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) { 54 header := api.chain.GetHeaderByHash(hash) 55 if header == nil { 56 return nil, errUnknownBlock 57 } 58 return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 59 } 60 61 // GetSigners retrieves the list of authorized signers at the specified block. 62 func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) { 63 // Retrieve the requested block number (or current if none requested) 64 var header *types.Header 65 if number == nil || *number == rpc.LatestBlockNumber { 66 header = api.chain.CurrentHeader() 67 } else { 68 header = api.chain.GetHeaderByNumber(uint64(number.Int64())) 69 } 70 // Ensure we have an actually valid block and return the signers from its snapshot 71 if header == nil { 72 return nil, errUnknownBlock 73 } 74 snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 75 if err != nil { 76 return nil, err 77 } 78 return snap.signers(), nil 79 } 80 81 // GetSignersAtHash retrieves the list of authorized signers at the specified block. 82 func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) { 83 header := api.chain.GetHeaderByHash(hash) 84 if header == nil { 85 return nil, errUnknownBlock 86 } 87 snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) 88 if err != nil { 89 return nil, err 90 } 91 return snap.signers(), nil 92 } 93 94 // Proposals returns the current proposals the node tries to uphold and vote on. 95 func (api *API) Proposals() map[common.Address]bool { 96 api.clique.lock.RLock() 97 defer api.clique.lock.RUnlock() 98 99 proposals := make(map[common.Address]bool) 100 for address, auth := range api.clique.proposals { 101 proposals[address] = auth 102 } 103 return proposals 104 } 105 106 // Propose injects a new authorization proposal that the signer will attempt to 107 // push through. 108 func (api *API) Propose(address common.Address, auth bool) { 109 api.clique.lock.Lock() 110 defer api.clique.lock.Unlock() 111 112 api.clique.proposals[address] = auth 113 } 114 115 // Discard drops a currently running proposal, stopping the signer from casting 116 // further votes (either for or against). 117 func (api *API) Discard(address common.Address) { 118 api.clique.lock.Lock() 119 defer api.clique.lock.Unlock() 120 121 delete(api.clique.proposals, address) 122 } 123 124 type Status struct { 125 InturnPercent float64 `json:"inturnPercent"` 126 SigningStatus map[common.Address]int `json:"sealerActivity"` 127 NumBlocks uint64 `json:"numBlocks"` 128 } 129 130 // Status returns the status of the last N blocks, 131 // - the number of active signers, 132 // - the number of signers, 133 // - the percentage of in-turn blocks 134 func (api *API) Status(startBlockNum *rpc.BlockNumber, endBlockNum *rpc.BlockNumber) (*Status, error) { 135 var ( 136 numBlocks uint64 137 header *types.Header 138 diff = uint64(0) 139 optimals = 0 140 141 start uint64 142 end uint64 143 ) 144 if startBlockNum != nil && endBlockNum == nil { 145 return nil, errors.New("pass the end block number") 146 } 147 148 if startBlockNum == nil && endBlockNum != nil { 149 return nil, errors.New("pass the start block number") 150 } 151 152 if startBlockNum == nil && endBlockNum == nil { 153 numBlocks = uint64(64) 154 header = api.chain.CurrentHeader() 155 end = header.Number.Uint64() 156 start = end - numBlocks 157 } else { 158 end = uint64(*endBlockNum) 159 start = uint64(*startBlockNum) 160 if start > end { 161 return nil, errors.New("start block number should be less than end block number") 162 } 163 164 if end > api.chain.CurrentHeader().Number.Uint64() { 165 return nil, errors.New("end block number should be less than or equal to current block height") 166 } 167 168 numBlocks = end - start 169 header = api.chain.GetHeaderByNumber(end) 170 } 171 172 snap, err := api.clique.snapshot(api.chain, end, header.Hash(), nil) 173 if err != nil { 174 return nil, err 175 } 176 var ( 177 signers = snap.signers() 178 ) 179 if numBlocks > end { 180 start = 1 181 numBlocks = end - start 182 } 183 signStatus := make(map[common.Address]int) 184 for _, s := range signers { 185 signStatus[s] = 0 186 } 187 for n := start; n < end; n++ { 188 h := api.chain.GetHeaderByNumber(n) 189 if h == nil { 190 return nil, fmt.Errorf("missing block %d", n) 191 } 192 if h.Difficulty.Cmp(diffInTurn) == 0 { 193 optimals++ 194 } 195 diff += h.Difficulty.Uint64() 196 sealer, err := api.clique.Author(h) 197 if err != nil { 198 return nil, err 199 } 200 signStatus[sealer]++ 201 } 202 return &Status{ 203 InturnPercent: float64((100 * optimals)) / float64(numBlocks), 204 SigningStatus: signStatus, 205 NumBlocks: numBlocks, 206 }, nil 207 }