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