github.com/klaytn/klaytn@v1.10.2/consensus/istanbul/backend/api.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2017 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from quorum/consensus/istanbul/backend/api.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package backend 22 23 import ( 24 "errors" 25 "fmt" 26 "math/big" 27 "reflect" 28 29 klaytnApi "github.com/klaytn/klaytn/api" 30 "github.com/klaytn/klaytn/blockchain" 31 "github.com/klaytn/klaytn/blockchain/types" 32 "github.com/klaytn/klaytn/common" 33 "github.com/klaytn/klaytn/consensus" 34 "github.com/klaytn/klaytn/consensus/istanbul" 35 "github.com/klaytn/klaytn/networks/rpc" 36 ) 37 38 // API is a user facing RPC API to dump Istanbul state 39 type API struct { 40 chain consensus.ChainReader 41 istanbul *backend 42 } 43 44 // GetSnapshot retrieves the state snapshot at a given block. 45 func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) { 46 // Retrieve the requested block number (or current if none requested) 47 header, err := headerByRpcNumber(api.chain, number) 48 if err != nil { 49 return nil, err 50 } 51 52 return api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, false) 53 } 54 55 // GetSnapshotAtHash retrieves the state snapshot at a given block. 56 func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) { 57 header := api.chain.GetHeaderByHash(hash) 58 if header == nil { 59 return nil, errUnknownBlock 60 } 61 return api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, false) 62 } 63 64 // GetValidators retrieves the list of authorized validators with the given block number. 65 func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) { 66 header, err := headerByRpcNumber(api.chain, number) 67 if err != nil { 68 return nil, err 69 } 70 71 blockNumber := header.Number.Uint64() 72 if blockNumber == 0 { 73 // The committee of genesis block can not be calculated because it requires a previous block. 74 istanbulExtra, err := types.ExtractIstanbulExtra(header) 75 if err != nil { 76 return nil, errExtractIstanbulExtra 77 } 78 return istanbulExtra.Validators, nil 79 } 80 81 snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false) 82 if err != nil { 83 logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err) 84 return nil, err 85 } 86 return snap.validators(), nil 87 } 88 89 // GetValidatorsAtHash retrieves the list of authorized validators with the given block hash. 90 func (api *API) GetValidatorsAtHash(hash common.Hash) ([]common.Address, error) { 91 header := api.chain.GetHeaderByHash(hash) 92 if header == nil { 93 return nil, errUnknownBlock 94 } 95 96 blockNumber := header.Number.Uint64() 97 if blockNumber == 0 { 98 // The committee of genesis block can not be calculated because it requires a previous block. 99 istanbulExtra, err := types.ExtractIstanbulExtra(header) 100 if err != nil { 101 return nil, errExtractIstanbulExtra 102 } 103 return istanbulExtra.Validators, nil 104 } 105 106 snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false) 107 if err != nil { 108 logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err) 109 return nil, errInternalError 110 } 111 return snap.validators(), nil 112 } 113 114 // GetDemotedValidators retrieves the list of authorized, but demoted validators with the given block number. 115 func (api *API) GetDemoteValidators(number *rpc.BlockNumber) ([]common.Address, error) { 116 header, err := headerByRpcNumber(api.chain, number) 117 if err != nil { 118 return nil, err 119 } 120 121 blockNumber := header.Number.Uint64() 122 if blockNumber == 0 { 123 snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, false) 124 if err != nil { 125 logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err) 126 return nil, err 127 } 128 return snap.demotedValidators(), nil 129 } else { 130 snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false) 131 if err != nil { 132 logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err) 133 return nil, err 134 } 135 return snap.demotedValidators(), nil 136 } 137 } 138 139 // GetDemotedValidatorsAtHash retrieves the list of authorized, but demoted validators with the given block hash. 140 func (api *API) GetDemotedValidatorsAtHash(hash common.Hash) ([]common.Address, error) { 141 header := api.chain.GetHeaderByHash(hash) 142 if header == nil { 143 return nil, errUnknownBlock 144 } 145 146 blockNumber := header.Number.Uint64() 147 if blockNumber == 0 { 148 snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, false) 149 if err != nil { 150 logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err) 151 return nil, err 152 } 153 return snap.demotedValidators(), nil 154 } else { 155 snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false) 156 if err != nil { 157 logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err) 158 return nil, err 159 } 160 return snap.demotedValidators(), nil 161 } 162 } 163 164 // Candidates returns the current candidates the node tries to uphold and vote on. 165 func (api *API) Candidates() map[common.Address]bool { 166 api.istanbul.candidatesLock.RLock() 167 defer api.istanbul.candidatesLock.RUnlock() 168 169 proposals := make(map[common.Address]bool) 170 for address, auth := range api.istanbul.candidates { 171 proposals[address] = auth 172 } 173 return proposals 174 } 175 176 // Propose injects a new authorization candidate that the validator will attempt to 177 // push through. 178 func (api *API) Propose(address common.Address, auth bool) { 179 api.istanbul.candidatesLock.Lock() 180 defer api.istanbul.candidatesLock.Unlock() 181 182 api.istanbul.candidates[address] = auth 183 } 184 185 // Discard drops a currently running candidate, stopping the validator from casting 186 // further votes (either for or against). 187 func (api *API) Discard(address common.Address) { 188 api.istanbul.candidatesLock.Lock() 189 defer api.istanbul.candidatesLock.Unlock() 190 191 delete(api.istanbul.candidates, address) 192 } 193 194 // API extended by Klaytn developers 195 type APIExtension struct { 196 chain consensus.ChainReader 197 istanbul *backend 198 } 199 200 var ( 201 errPendingNotAllowed = errors.New("pending is not allowed") 202 errInternalError = errors.New("internal error") 203 errStartNotPositive = errors.New("start block number should be positive") 204 errEndLargetThanLatest = errors.New("end block number should be smaller than the latest block number") 205 errStartLargerThanEnd = errors.New("start should be smaller than end") 206 errRequestedBlocksTooLarge = errors.New("number of requested blocks should be smaller than 50") 207 errRangeNil = errors.New("range values should not be nil") 208 errExtractIstanbulExtra = errors.New("extract Istanbul Extra from block header of the given block number") 209 errNoBlockExist = errors.New("block with the given block number is not existed") 210 errNoBlockNumber = errors.New("block number is not assigned") 211 ) 212 213 // GetCouncil retrieves the list of authorized validators at the specified block. 214 func (api *APIExtension) GetCouncil(number *rpc.BlockNumber) ([]common.Address, error) { 215 header, err := headerByRpcNumber(api.chain, number) 216 if err != nil { 217 return nil, err 218 } 219 220 blockNumber := header.Number.Uint64() 221 if blockNumber == 0 { 222 // The committee of genesis block can not be calculated because it requires a previous block. 223 istanbulExtra, err := types.ExtractIstanbulExtra(header) 224 if err != nil { 225 return nil, errExtractIstanbulExtra 226 } 227 return istanbulExtra.Validators, nil 228 } 229 230 snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false) 231 if err != nil { 232 logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err) 233 return nil, err 234 } 235 236 return append(snap.validators(), snap.demotedValidators()...), nil 237 } 238 239 func (api *APIExtension) GetCouncilSize(number *rpc.BlockNumber) (int, error) { 240 council, err := api.GetCouncil(number) 241 if err == nil { 242 return len(council), nil 243 } else { 244 return -1, err 245 } 246 } 247 248 func (api *APIExtension) GetCommittee(number *rpc.BlockNumber) ([]common.Address, error) { 249 header, err := headerByRpcNumber(api.chain, number) 250 if err != nil { 251 return nil, err 252 } 253 254 blockNumber := header.Number.Uint64() 255 if blockNumber == 0 { 256 // The committee of genesis block can not be calculated because it requires a previous block. 257 istanbulExtra, err := types.ExtractIstanbulExtra(header) 258 if err != nil { 259 return nil, errExtractIstanbulExtra 260 } 261 return istanbulExtra.Validators, nil 262 } 263 264 snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false) 265 if err != nil { 266 return nil, err 267 } 268 round := header.Round() 269 view := &istanbul.View{ 270 Sequence: new(big.Int).SetUint64(blockNumber), 271 Round: new(big.Int).SetUint64(uint64(round)), 272 } 273 274 // get the proposer of this block. 275 proposer, err := ecrecover(header) 276 if err != nil { 277 return nil, err 278 } 279 280 parentHash := header.ParentHash 281 282 // get the committee list of this block at the view (blockNumber, round) 283 committee := snap.ValSet.SubListWithProposer(parentHash, proposer, view) 284 addresses := make([]common.Address, len(committee)) 285 for i, v := range committee { 286 addresses[i] = v.Address() 287 } 288 289 return addresses, nil 290 } 291 292 func (api *APIExtension) GetCommitteeSize(number *rpc.BlockNumber) (int, error) { 293 committee, err := api.GetCommittee(number) 294 if err == nil { 295 return len(committee), nil 296 } else { 297 return -1, err 298 } 299 } 300 301 func (api *APIExtension) makeRPCBlockOutput(b *types.Block, 302 cInfo consensus.ConsensusInfo, transactions types.Transactions, receipts types.Receipts, 303 ) map[string]interface{} { 304 head := b.Header() // copies the header once 305 hash := head.Hash() 306 307 td := big.NewInt(0) 308 if bc, ok := api.chain.(*blockchain.BlockChain); ok { 309 td = bc.GetTd(hash, b.NumberU64()) 310 } 311 r, err := klaytnApi.RpcOutputBlock(b, td, false, false, api.chain.Config().IsEthTxTypeForkEnabled(b.Header().Number)) 312 if err != nil { 313 logger.Error("failed to RpcOutputBlock", "err", err) 314 return nil 315 } 316 317 // make transactions 318 numTxs := len(transactions) 319 rpcTransactions := make([]map[string]interface{}, numTxs) 320 for i, tx := range transactions { 321 rpcTransactions[i] = klaytnApi.RpcOutputReceipt(head, tx, hash, head.Number.Uint64(), uint64(i), receipts[i]) 322 } 323 324 r["committee"] = cInfo.Committee 325 r["proposer"] = cInfo.Proposer 326 r["round"] = cInfo.Round 327 r["originProposer"] = cInfo.OriginProposer 328 r["transactions"] = rpcTransactions 329 330 return r 331 } 332 333 // TODO-Klaytn: This API functions should be managed with API functions with namespace "klay" 334 func (api *APIExtension) GetBlockWithConsensusInfoByNumber(number *rpc.BlockNumber) (map[string]interface{}, error) { 335 b, ok := api.chain.(*blockchain.BlockChain) 336 if !ok { 337 logger.Error("chain is not a type of blockchain.BlockChain", "type", reflect.TypeOf(api.chain)) 338 return nil, errInternalError 339 } 340 var block *types.Block 341 var blockNumber uint64 342 343 if number == nil { 344 logger.Trace("block number is not assigned") 345 return nil, errNoBlockNumber 346 } 347 348 if *number == rpc.PendingBlockNumber { 349 logger.Trace("Cannot get consensus information of the PendingBlock.") 350 return nil, errPendingNotAllowed 351 } 352 353 if *number == rpc.LatestBlockNumber { 354 block = b.CurrentBlock() 355 blockNumber = block.NumberU64() 356 } else { 357 // rpc.EarliestBlockNumber == 0, no need to treat it as a special case. 358 blockNumber = uint64(number.Int64()) 359 block = b.GetBlockByNumber(blockNumber) 360 } 361 362 if block == nil { 363 logger.Trace("Finding a block by number failed.", "blockNum", blockNumber) 364 return nil, fmt.Errorf("the block does not exist (block number: %d)", blockNumber) 365 } 366 blockHash := block.Hash() 367 368 cInfo, err := api.istanbul.GetConsensusInfo(block) 369 if err != nil { 370 logger.Error("Getting the proposer and validators failed.", "blockHash", blockHash, "err", err) 371 return nil, errInternalError 372 } 373 374 receipts := b.GetBlockReceiptsInCache(blockHash) 375 if receipts == nil { 376 receipts = b.GetReceiptsByBlockHash(blockHash) 377 } 378 379 return api.makeRPCBlockOutput(block, cInfo, block.Transactions(), receipts), nil 380 } 381 382 func (api *APIExtension) GetBlockWithConsensusInfoByNumberRange(start *rpc.BlockNumber, end *rpc.BlockNumber) (map[string]interface{}, error) { 383 blocks := make(map[string]interface{}) 384 385 if start == nil || end == nil { 386 logger.Trace("the range values should not be nil.", "start", start, "end", end) 387 return nil, errRangeNil 388 } 389 390 // check error status. 391 s := start.Int64() 392 e := end.Int64() 393 if s < 0 { 394 logger.Trace("start should be positive", "start", s) 395 return nil, errStartNotPositive 396 } 397 398 eChain := api.chain.CurrentHeader().Number.Int64() 399 if e > eChain { 400 logger.Trace("end should be smaller than the lastest block number", "end", end, "eChain", eChain) 401 return nil, errEndLargetThanLatest 402 } 403 404 if s > e { 405 logger.Trace("start should be smaller than end", "start", s, "end", e) 406 return nil, errStartLargerThanEnd 407 } 408 409 if (e - s) > 50 { 410 logger.Trace("number of requested blocks should be smaller than 50", "start", s, "end", e) 411 return nil, errRequestedBlocksTooLarge 412 } 413 414 // gather s~e blocks 415 for i := s; i <= e; i++ { 416 strIdx := fmt.Sprintf("0x%x", i) 417 418 blockNum := rpc.BlockNumber(i) 419 b, err := api.GetBlockWithConsensusInfoByNumber(&blockNum) 420 if err != nil { 421 logger.Error("error on GetBlockWithConsensusInfoByNumber", "err", err) 422 blocks[strIdx] = nil 423 } else { 424 blocks[strIdx] = b 425 } 426 } 427 428 return blocks, nil 429 } 430 431 func (api *APIExtension) GetBlockWithConsensusInfoByHash(blockHash common.Hash) (map[string]interface{}, error) { 432 b, ok := api.chain.(*blockchain.BlockChain) 433 if !ok { 434 logger.Error("chain is not a type of blockchain.Blockchain, returning...", "type", reflect.TypeOf(api.chain)) 435 return nil, errInternalError 436 } 437 438 block := b.GetBlockByHash(blockHash) 439 if block == nil { 440 logger.Trace("Finding a block failed.", "blockHash", blockHash) 441 return nil, fmt.Errorf("the block does not exist (block hash: %s)", blockHash.String()) 442 } 443 444 cInfo, err := api.istanbul.GetConsensusInfo(block) 445 if err != nil { 446 logger.Error("Getting the proposer and validators failed.", "blockHash", blockHash, "err", err) 447 return nil, errInternalError 448 } 449 450 receipts := b.GetBlockReceiptsInCache(blockHash) 451 if receipts == nil { 452 receipts = b.GetReceiptsByBlockHash(blockHash) 453 } 454 455 return api.makeRPCBlockOutput(block, cInfo, block.Transactions(), receipts), nil 456 } 457 458 func (api *API) GetTimeout() uint64 { 459 return istanbul.DefaultConfig.Timeout 460 } 461 462 // Retrieve the header at requested block number 463 func headerByRpcNumber(chain consensus.ChainReader, number *rpc.BlockNumber) (*types.Header, error) { 464 var header *types.Header 465 if number == nil || *number == rpc.LatestBlockNumber { 466 header = chain.CurrentHeader() 467 } else if *number == rpc.PendingBlockNumber { 468 logger.Trace("Cannot get snapshot of the pending block.", "number", number) 469 return nil, errPendingNotAllowed 470 } else { 471 header = chain.GetHeaderByNumber(uint64(number.Int64())) 472 } 473 // Ensure we have an actually valid block and return its snapshot 474 if header == nil { 475 return nil, errUnknownBlock 476 } 477 return header, nil 478 }