From 22732d57fb99739e8c7e8355a61791ea0570a001 Mon Sep 17 00:00:00 2001 From: AnilChinchawale Date: Tue, 12 Mar 2019 15:57:12 +0530 Subject: [PATCH] Minor Changes added --- internal/ethapi/api.go | 283 +++++++++++++++++++++++++++++++++++++---- les/api_backend.go | 26 +++- miner/worker.go | 45 +++++-- rpc/server.go | 4 +- 4 files changed, 319 insertions(+), 39 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 35fd1bc813..c74b891e85 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -44,10 +44,17 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/util" + contractValidator "github.com/ethereum/go-ethereum/contracts/validator/contract" + "github.com/ethereum/go-ethereum/accounts/abi/bind" ) const ( - defaultGasPrice = 50 * params.Shannon + defaultGasPrice = 50 * params.Shannon + // statuses of candidates + statusMasternode = "MASTERNODE" + statusSlashed = "SLASHED" + statusProposed = "PROPOSED" + ) // PublicEthereumAPI provides an API to access Ethereum related information. @@ -494,13 +501,7 @@ func (s *PublicBlockChainAPI) BlockNumber() *big.Int { // BlockNumber returns the block number of the chain head. func (s *PublicBlockChainAPI) GetRewardByHash(hash common.Hash) map[string]interface{} { - if c, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { - rewards := c.GetRewards(hash) - if rewards != nil { - return rewards - } - } - return make(map[string]interface{}) + return s.b.GetRewardByHash(hash) } // GetBalance returns the amount of wei for the given address in the state of the @@ -614,6 +615,175 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A return res[:], state.Error() } +func (s *PublicBlockChainAPI) GetBlockSignersByHash(ctx context.Context, blockHash common.Hash) ([]common.Address, error) { + block, err := s.b.GetBlock(ctx, blockHash) + if err != nil || block == nil { + return []common.Address{}, err + } + masternodes, err := s.GetMasternodes(ctx, block) + if err != nil || len(masternodes) == 0 { + log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes)) + return []common.Address{}, err + } + return s.rpcOutputBlockSigners(block, ctx, masternodes) +} + +func (s *PublicBlockChainAPI) GetBlockSignersByNumber(ctx context.Context, blockNumber rpc.BlockNumber) ([]common.Address, error) { + block, err := s.b.BlockByNumber(ctx, blockNumber) + if err != nil || block == nil { + return []common.Address{}, err + } + masternodes, err := s.GetMasternodes(ctx, block) + if err != nil || len(masternodes) == 0 { + log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes)) + return []common.Address{}, err + } + return s.rpcOutputBlockSigners(block, ctx, masternodes) +} + +func (s *PublicBlockChainAPI) GetBlockFinalityByHash(ctx context.Context, blockHash common.Hash) (int32, error) { + block, err := s.b.GetBlock(ctx, blockHash) + if err != nil || block == nil { + return int32(0), err + } + masternodes, err := s.GetMasternodes(ctx, block) + if err != nil || len(masternodes) == 0 { + log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes)) + return int32(0), err + } + blockSigners, err := s.rpcOutputBlockSigners(block, ctx, masternodes) + if err != nil { + return int32(0), err + } + return int32(100 * len(blockSigners) / len(masternodes)), err +} + +func (s *PublicBlockChainAPI) GetBlockFinalityByNumber(ctx context.Context, blockNumber rpc.BlockNumber) (int32, error) { + block, err := s.b.BlockByNumber(ctx, blockNumber) + if err != nil || block == nil { + return int32(0), err + } + masternodes, err := s.GetMasternodes(ctx, block) + if err != nil || len(masternodes) == 0 { + log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes)) + return int32(0), err + } + blockSigners, err := s.rpcOutputBlockSigners(block, ctx, masternodes) + if err != nil { + return int32(0), err + } + return int32(100 * len(blockSigners) / len(masternodes)), err +} + +// GetMasternodes returns masternodes set at the starting block of epoch of the given block +func (s *PublicBlockChainAPI) GetMasternodes(ctx context.Context, b *types.Block) ([]common.Address, error) { + var masternodes []common.Address + if b.Number().Int64() >= 0 { + curBlockNumber := b.Number().Uint64() + prevBlockNumber := curBlockNumber + (common.MergeSignRange - (curBlockNumber % common.MergeSignRange)) + latestBlockNumber := s.b.CurrentBlock().Number().Uint64() + if prevBlockNumber >= latestBlockNumber || !s.b.ChainConfig().IsTIP2019(b.Number()) { + prevBlockNumber = curBlockNumber + } + if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { + // Get block epoc latest. + lastCheckpointNumber := prevBlockNumber - (prevBlockNumber % s.b.ChainConfig().XDPoS.Epoch) + prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(lastCheckpointNumber)) + if prevCheckpointBlock != nil { + masternodes = engine.GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), curBlockNumber, s.b.ChainConfig().XDPoS.Epoch) + } + } else { + log.Error("Undefined XDPoS consensus engine") + } + } + return masternodes, nil +} + + +// GetCandidateStatus returns status of the given candidate at a specified epochNumber +func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAddress common.Address, epochNumber rpc.EpochNumber) (string, error) { + var ( + block *types.Block + masternodes, penaltyList, candidates, proposedList []common.Address + penalties []byte + err error + ) + block = s.b.CurrentBlock() + epoch := s.b.ChainConfig().XDPoS.Epoch + // TODO: we currently support the latest epoch only + //if epochNumber == rpc.LatestEpochNumber { + // block = s.b.CurrentBlock() + //} else { + // checkpointNumber := rpc.BlockNumber((uint64(epochNumber) - 1) * epoch) + // if checkpointNumber < 0 { + // checkpointNumber = 0 + // } + // block, err = s.b.BlockByNumber(ctx, checkpointNumber) + // if err != nil || block == nil { + // return "", err + // } + //} + blockNum := block.Number().Uint64() + masternodes, err = s.GetMasternodes(ctx, block) + if err != nil || len(masternodes) == 0 { + log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes)) + return "", err + } + for _, masternode := range masternodes { + if coinbaseAddress == masternode { + return statusMasternode, nil + } + } + + // look up recent checkpoint headers to get penalty list + for i := 0; i <= common.LimitPenaltyEpoch; i++ { + if blockNum > uint64(i) * epoch { + blockCheckpointNumber := rpc.BlockNumber(blockNum - (blockNum % epoch) - (uint64(i) * epoch)) + blockCheckpoint, err := s.b.BlockByNumber(ctx, blockCheckpointNumber) + if err != nil { + log.Error("Failed to get block by number", "num", blockCheckpointNumber, "err", err) + continue + } + penalties = append(penalties, blockCheckpoint.Penalties()...) + } + } + if len(penalties) > 0 { + penaltyList = common.ExtractAddressFromBytes(penalties) + for _, pen := range penaltyList { + if coinbaseAddress == pen { + return statusSlashed, nil + } + } + } + + // read smart contract to get candidate list + client, err := s.b.GetIPCClient() + if err != nil { + return "", err + } + addr := common.HexToAddress(common.MasternodeVotingSMC) + validator, err := contractValidator.NewXDCValidator(addr, client) + if err != nil { + return "", err + } + opts := new(bind.CallOpts) + candidates, err = validator.GetCandidates(opts) + if err != nil { + return "", err + } + // exclude masternodes + proposedList = common.RemoveItemFromArray(candidates, masternodes) + // exclude penalties + proposedList = common.RemoveItemFromArray(proposedList, penaltyList) + + for _, proposed := range proposedList { + if coinbaseAddress == proposed { + return statusProposed, nil + } + } + return "", nil +} + // CallArgs represents the arguments for a call. type CallArgs struct { From common.Address `json:"from"` @@ -860,28 +1030,46 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx uncleHashes[i] = uncle.Hash() } fields["uncles"] = uncleHashes + return fields, nil +} +func (s *PublicBlockChainAPI) rpcOutputBlockSigners(b *types.Block, ctx context.Context, masternodes []common.Address) ([]common.Address, error) { // Get signers for block. client, err := s.b.GetIPCClient() if err != nil { log.Error("Fail to connect IPC client for block status", "error", err) + return []common.Address{}, err } + var signers []common.Address var filterSigners []common.Address - finality := int32(0) if b.Number().Int64() > 0 { - addrBlockSigner := common.HexToAddress(common.BlockSigners) - signers, err = contracts.GetSignersFromContract(addrBlockSigner, client, b.Hash()) - if err != nil { - log.Error("Fail to get signers from block signer SC.", "error", err) + blockNumber := b.Number().Uint64() + signedBlockNumber := blockNumber + (common.MergeSignRange - (blockNumber % common.MergeSignRange)) + latestBlockNumber := s.b.CurrentBlock().Number() + if signedBlockNumber >= latestBlockNumber.Uint64() || !s.b.ChainConfig().IsTIP2019(b.Number()) { + signedBlockNumber = blockNumber } - // Get block epoc latest. - if s.b.ChainConfig().XDPoS != nil { - engine := s.b.GetEngine() - lastCheckpointNumber := rpc.BlockNumber(b.Number().Uint64() - (b.Number().Uint64() % s.b.ChainConfig().XDPoS.Epoch)) - prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, lastCheckpointNumber) + if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { + // Get block epoc latest. + lastCheckpointNumber := signedBlockNumber - (signedBlockNumber % s.b.ChainConfig().XDPoS.Epoch) + prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(lastCheckpointNumber)) if prevCheckpointBlock != nil { - masternodes := engine.(*XDPoS.XDPoS).GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), b.Number().Uint64(), s.b.ChainConfig().XDPoS.Epoch) + masternodes := engine.GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), blockNumber, s.b.ChainConfig().XDPoS.Epoch) + signedBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(signedBlockNumber)) + if s.b.ChainConfig().IsTIPSigning(latestBlockNumber) { + signers, err = GetSignersFromBlocks(s.b, signedBlock.NumberU64(), signedBlock.Hash(), masternodes) + } else { + signers, err = contracts.GetSignersByExecutingEVM(common.HexToAddress(common.BlockSigners), client, signedBlock.Hash()) + } + if err != nil { + log.Error("Fail to get signers from block signer SC.", "error", err) + return nil, err + } + validator, _ := engine.RecoverValidator(b.Header()) + creator, _ := engine.RecoverSigner(b.Header()) + signers = append(signers, validator) + signers = append(signers, creator) countFinality := 0 for _, masternode := range masternodes { for _, signer := range signers { @@ -892,14 +1080,12 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx } } } - finality = int32(countFinality * 100 / len(masternodes)) } + } else { + log.Error("Undefined XDPoS consensus engine") } } - fields["signers"] = filterSigners - fields["finality"] = finality - - return fields, nil + return filterSigners, nil } // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction @@ -1533,3 +1719,52 @@ func (s *PublicNetAPI) PeerCount() hexutil.Uint { func (s *PublicNetAPI) Version() string { return fmt.Sprintf("%d", s.networkVersion) } + +func GetSignersFromBlocks(b Backend, blockNumber uint64, blockHash common.Hash, masternodes []common.Address) ([]common.Address, error) { + var addrs []common.Address + mapMN := map[common.Address]bool{} + for _, node := range masternodes { + mapMN[node] = true + } + if engine, ok := b.GetEngine().(*XDPoS.XDPoS); ok { + limitNumber := blockNumber - blockNumber%b.ChainConfig().XDPoS.Epoch + 2*b.ChainConfig().XDPoS.Epoch - 1 + currentNumber := b.CurrentBlock().NumberU64() + if limitNumber > currentNumber { + limitNumber = currentNumber + } + for i := blockNumber + 1; i <= limitNumber; i++ { + header, err := b.HeaderByNumber(nil, rpc.BlockNumber(i)) + if err != nil { + return addrs, err + } + signData, ok := engine.BlockSigners.Get(header.Hash()) + var signTxs []*types.Transaction = nil + if !ok { + blockData, err := b.BlockByNumber(nil, rpc.BlockNumber(i)) + if err != nil { + return addrs, err + } + signTxs = []*types.Transaction{} + for _, tx := range blockData.Transactions() { + if tx.IsSigningTransaction() { + signTxs = append(signTxs, tx) + } + } + } else { + signTxs = signData.([]*types.Transaction) + } + for _, signtx := range signTxs { + blkHash := common.BytesToHash(signtx.Data()[len(signtx.Data())-32:]) + from := *signtx.From() + if blkHash == blockHash && mapMN[from] { + addrs = append(addrs, from) + delete(mapMN, from) + } + } + if len(mapMN) == 0 { + break + } + } + } + return addrs, nil +} diff --git a/les/api_backend.go b/les/api_backend.go index a661b09505..36afa39717 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -18,8 +18,10 @@ package les import ( "context" - "github.com/ethereum/go-ethereum/consensus/XDPoS" + "encoding/json" + "io/ioutil" "math/big" + "path/filepath" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" @@ -202,10 +204,24 @@ func (b *LesApiBackend) GetEngine() consensus.Engine { return b.eth.engine } func (s *LesApiBackend) GetRewardByHash(hash common.Hash) map[string]interface{} { - if c, ok := s.eth.Engine().(*XDPoS.XDPoS); ok { - rewards := c.GetRewards(hash) - if rewards != nil { - return rewards + header := s.eth.blockchain.GetHeaderByHash(hash) + if header != nil { + data, err := ioutil.ReadFile(filepath.Join(common.StoreRewardFolder, header.Number.String()+"."+header.Hash().Hex())) + if err == nil { + rewards := make(map[string]interface{}) + err = json.Unmarshal(data, &rewards) + if err == nil { + return rewards + } + } else { + data, err = ioutil.ReadFile(filepath.Join(common.StoreRewardFolder, header.Number.String()+"."+header.HashNoValidator().Hex())) + if err == nil { + rewards := make(map[string]interface{}) + err = json.Unmarshal(data, &rewards) + if err == nil { + return rewards + } + } } } return make(map[string]interface{}) diff --git a/miner/worker.go b/miner/worker.go index d4aa64767a..95ec61a23a 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -18,6 +18,7 @@ package miner import ( "bytes" + "encoding/binary" "fmt" "math/big" "os" @@ -410,8 +411,10 @@ func (self *worker) wait() { } } // Send tx sign to smart contract blockSigners. - if err := contracts.CreateTransactionSign(self.config, self.eth.TxPool(), self.eth.AccountManager(), block, self.chainDb); err != nil { - log.Error("Fail to create tx sign for signer", "error", "err") + if block.NumberU64()%common.MergeSignRange == 0 || !self.config.IsTIP2019(block.Number()) { + if err := contracts.CreateTransactionSign(self.config, self.eth.TxPool(), self.eth.AccountManager(), block, self.chainDb); err != nil { + log.Error("Fail to create tx sign for signer", "error", "err") + } } } } @@ -552,6 +555,7 @@ func (self *worker) commitNewWork() { if atomic.LoadInt32(&self.mining) == 1 { header.Coinbase = self.coinbase } + if err := self.engine.Prepare(self.chain, header); err != nil { log.Error("Failed to prepare header for new block", "err", err) return @@ -580,12 +584,22 @@ func (self *worker) commitNewWork() { if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 { misc.ApplyDAOHardFork(work.state) } - pending, err := self.eth.TxPool().Pending() - if err != nil { - log.Error("Failed to fetch pending transactions", "err", err) - return + if common.TIPSigning.Cmp(header.Number) == 0 { + work.state.DeleteAddress(common.HexToAddress(common.BlockSigners)) + } + // won't grasp txs at checkpoint + var ( + txs *types.TransactionsByPriceAndNonce + specialTxs types.Transactions + ) + if self.config.XDPoS != nil && header.Number.Uint64()%self.config.XDPoS.Epoch != 0 { + pending, err := self.eth.TxPool().Pending() + if err != nil { + log.Error("Failed to fetch pending transactions", "err", err) + return + } + txs, specialTxs = types.NewTransactionsByPriceAndNonce(self.current.signer, pending, signers) } - txs, specialTxs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending, signers) work.commitTransactions(self.mux, txs, specialTxs, self.chain, self.coinbase) // compute uncles for the new block. @@ -618,7 +632,7 @@ func (self *worker) commitNewWork() { return } if atomic.LoadInt32(&self.mining) == 1 { - log.Info("Committing new block", "number", work.Block.Number(), "txs", work.tcount, "special txs", len(specialTxs), "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart))) + log.Info("Committing new block", "number", work.Block.Number(), "txs", work.tcount, "special-txs", len(specialTxs), "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart))) self.unconfirmed.Shift(work.Block.NumberU64() - 1) self.lastParentBlockCommit = parent.Hash().Hex() } @@ -661,6 +675,17 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB log.Trace("Ignoring reply protected special transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block) continue } + if tx.To().Hex() == common.BlockSigners { + if len(tx.Data()) < 68 { + log.Trace("Data special transaction invalid length", "hash", tx.Hash(), "data", len(tx.Data())) + continue + } + blkNumber := binary.BigEndian.Uint64(tx.Data()[8:40]) + if blkNumber >= env.header.Number.Uint64() || blkNumber <= env.header.Number.Uint64()-env.config.XDPoS.Epoch*2 { + log.Trace("Data special transaction invalid number", "hash", tx.Hash(), "blkNumber", blkNumber, "miner", env.header.Number) + continue + } + } // Start executing the transaction env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) nonce := env.state.GetNonce(from) @@ -694,6 +719,10 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB log.Trace("Not enough gas for further transactions", "gp", gp) break } + if txs == nil { + log.Info("this block has no transaction") + break + } // Retrieve the next transaction and abort if all done tx := txs.Peek() if tx == nil { diff --git a/rpc/server.go b/rpc/server.go index 0e7142a183..611275bf91 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -190,14 +190,14 @@ func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecO if singleShot { if batch { for _, req := range reqs { - if req.callb != nil && req.callb.method.Name == "SendTransaction" { + if req.callb != nil && req.callb.method.Name == "EnabledRPCSendTransaction" { codec.Write(codec.CreateErrorResponse(&req.id, &invalidRequestError{message: "Only support send transaction with ipc"})) return nil } } s.execBatch(ctx, codec, reqs) } else { - if reqs[0].callb != nil && reqs[0].callb.method.Name == "SendTransaction" { + if reqs[0].callb != nil && reqs[0].callb.method.Name == "EnabledRPCSendTransaction" { codec.Write(codec.CreateErrorResponse(&reqs[0].id, &invalidRequestError{message: "Only support send transaction with ipc"})) return nil }