diff --git a/common/types.go b/common/types.go index 871700758e..6a7be73c98 100644 --- a/common/types.go +++ b/common/types.go @@ -35,6 +35,11 @@ const ( RandomizeSMC = "0x0000000000000000000000000000000000000090" FoudationAddr = "0x0000000000000000000000000000000000000068" TeamAddr = "0x0000000000000000000000000000000000000099" + VoteMethod = "0x6dd7d8ea" + UnvoteMethod = "0x02aa9be2" + ProposeMethod = "0x01267951" + ResignMethod = "0xae6e43f5" + SignMethod = "0xe341eaa4" ) var ( @@ -45,6 +50,11 @@ var ( // Hash represents the 32 byte Keccak256 hash of arbitrary data. type Hash [HashLength]byte +type Vote struct { + Masternode Address + Voter Address +} + func BytesToHash(b []byte) Hash { var h Hash h.SetBytes(b) diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index 39781d73b7..a27e1b3dd8 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -48,8 +48,9 @@ import ( ) const ( - inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory - M2ByteLength = 4 + inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory + blockSignersCacheLimit = 36000 + M2ByteLength = 4 ) type Masternode struct { @@ -224,6 +225,7 @@ type Posv struct { signFn clique.SignerFn // Signer function to authorize hashes with lock sync.RWMutex // Protects the signer fields + BlockSigners *lru.Cache HookReward func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) (error, map[string]interface{}) HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error) HookValidator func(header *types.Header, signers []common.Address) ([]byte, error) @@ -239,6 +241,7 @@ func New(config *params.PosvConfig, db ethdb.Database) *Posv { conf.Epoch = epochLength } // Allocate the snapshot caches and create the engine + BlockSigners, _ := lru.New(blockSignersCacheLimit) recents, _ := lru.NewARC(inmemorySnapshots) signatures, _ := lru.NewARC(inmemorySnapshots) validatorSignatures, _ := lru.NewARC(inmemorySnapshots) @@ -246,6 +249,7 @@ func New(config *params.PosvConfig, db ethdb.Database) *Posv { return &Posv{ config: &conf, db: db, + BlockSigners: BlockSigners, recents: recents, signatures: signatures, verifiedHeaders: verifiedHeaders, @@ -849,6 +853,8 @@ func (c *Posv) Finalize(chain consensus.ChainReader, header *types.Header, state number := header.Number.Uint64() rCheckpoint := chain.Config().Posv.RewardCheckpoint + // _ = c.CacheData(header, txs, receipts) + if c.HookReward != nil && number%rCheckpoint == 0 { err, rewards := c.HookReward(chain, state, header) if err != nil { @@ -930,8 +936,7 @@ func (c *Posv) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan if limit := uint64(2); number < limit || seen > number-limit { // Only take into account the non-epoch blocks if number%c.config.Epoch != 0 { - log.Info("len(masternodes)", len(masternodes), "number", number, "limit", limit, "seen", seen, "recent", recent.String(), "snap.Recents", snap.Recents) - log.Info("Signed recently, must wait for others") + log.Info("Signed recently, must wait for others ", "len(masternodes)", len(masternodes), "number", number, "limit", limit, "seen", seen, "recent", recent.String(), "snap.Recents", snap.Recents) <-stop return nil, nil } @@ -1020,6 +1025,36 @@ func (c *Posv) GetMasternodesFromCheckpointHeader(preCheckpointHeader *types.Hea return masternodes } +func (c *Posv) CacheData(header *types.Header, txs []*types.Transaction, receipts []*types.Receipt) error { + var signTxs []*types.Transaction + for _, tx := range txs { + if tx.IsSigningTransaction() { + var b uint + for _, r := range receipts { + if r.TxHash == tx.Hash() { + b = r.Status + break + } + } + + if b == types.ReceiptStatusFailed { + continue + } + + signTxs = append(signTxs, tx) + } + } + + log.Debug("Save tx signers to cache", "hash", header.Hash().String(), "number", header.Number, "len(txs)", len(signTxs)) + c.BlockSigners.Add(header.Hash(), signTxs) + + return nil +} + +func (c *Posv) GetDb() ethdb.Database { + return c.db +} + // Extract validators from byte array. func RemovePenaltiesFromBlock(chain consensus.ChainReader, masternodes []common.Address, epochNumber uint64) []common.Address { if epochNumber <= 0 { diff --git a/contracts/utils.go b/contracts/utils.go index 36bddadce2..fed5a519c0 100644 --- a/contracts/utils.go +++ b/contracts/utils.go @@ -215,7 +215,6 @@ func GetSignersFromContract1(addrBlockSigner common.Address, client bind.Contrac log.Error("Fail get block signers", "error", err) return nil, err } - return addrs, nil } @@ -307,7 +306,8 @@ func DecryptRandomizeFromSecretsAndOpening(secrets [][32]byte, opening [32]byte) return random, nil } -func GetRewardForCheckpoint(chain consensus.ChainReader, number uint64, rCheckpoint uint64, totalSigner *uint64, state *state.StateDB) (map[common.Address]*rewardLog, error) { +// Calculate reward for reward checkpoint. +func GetRewardForCheckpoint(c *posv.Posv, chain consensus.ChainReader, number uint64, rCheckpoint uint64, totalSigner *uint64) (map[common.Address]*rewardLog, error) { // Not reward for singer of genesis block and only calculate reward at checkpoint block. prevCheckpoint := number - (rCheckpoint * 2) startBlockNumber := prevCheckpoint + 1 @@ -317,15 +317,53 @@ func GetRewardForCheckpoint(chain consensus.ChainReader, number uint64, rCheckpo masternodes := posv.GetMasternodesFromCheckpointHeader(prevHeaderCheckpoint) if len(masternodes) > 0 { - for i := startBlockNumber; i <= endBlockNumber; i++ { - bheader := chain.GetHeaderByNumber(i) - bhash := bheader.Hash() - block := chain.GetBlock(bhash, i) - addrs, err := GetSignersFromContract(state, block) - if err != nil { - log.Error("Fail to get signers from smartcontract.", "error", err, "blockNumber", i) - return nil, err + + data := make(map[common.Hash][]common.Address) + for i := startBlockNumber; i <= prevCheckpoint+(rCheckpoint*2)-1; i++ { + header := chain.GetHeaderByNumber(i) + + if signData, ok := c.BlockSigners.Get(header.Hash()); ok { + txs := signData.([]*types.Transaction) + for _, tx := range txs { + blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:]) + from := *tx.From() + data[blkHash] = append(data[blkHash], from) + } + } else { + log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i) + block := chain.GetBlock(header.Hash(), i) + txs := block.Transactions() + receipts := core.GetBlockReceipts(c.GetDb(), header.Hash(), i) + + var signTxs []*types.Transaction + for _, tx := range txs { + if tx.IsSigningTransaction() { + var b uint + for _, r := range receipts { + if r.TxHash == tx.Hash() { + b = r.Status + break + } + } + + if b == types.ReceiptStatusFailed { + continue + } + + signTxs = append(signTxs, tx) + blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:]) + from := *tx.From() + data[blkHash] = append(data[blkHash], from) + } + } + c.BlockSigners.Add(header.Hash(), signTxs) + } + } + + for i := startBlockNumber; i <= endBlockNumber; i++ { + block := chain.GetHeaderByNumber(i) + addrs := data[block.Hash()] // Filter duplicate address. if len(addrs) > 0 { addrSigners := make(map[common.Address]bool) diff --git a/core/blockchain.go b/core/blockchain.go index 46d9739ffb..c2d1cc2c58 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -506,6 +506,12 @@ func (bc *BlockChain) insert(block *types.Block) { } bc.currentBlock.Store(block) + // save cache BlockSigners + if bc.chainConfig.Posv != nil { + engine := bc.Engine().(*posv.Posv) + engine.CacheData(block.Header(), block.Transactions(), bc.GetReceiptsByHash(block.Hash())) + } + // If the block is better than our head or is on a different chain, force update heads if updateHeads { bc.hc.SetCurrentHeader(block.Header()) diff --git a/core/types/transaction.go b/core/types/transaction.go index 653498a851..339e3562f5 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -195,6 +195,19 @@ func (tx *Transaction) To() *common.Address { return &to } +func (tx *Transaction) From() *common.Address { + if tx.data.V != nil { + signer := deriveSigner(tx.data.V) + if f, err := Sender(signer, tx); err != nil { + return nil + } else { + return &f + } + } else { + return nil + } +} + // Hash hashes the RLP encoding of tx. // It uniquely identifies the transaction. func (tx *Transaction) Hash() common.Hash { @@ -274,6 +287,66 @@ func (tx *Transaction) IsSpecialTransaction() bool { return tx.To().String() == common.RandomizeSMC || tx.To().String() == common.BlockSigners } +func (tx *Transaction) IsSigningTransaction() bool { + if tx.To() == nil { + return false + } + + if tx.To().String() != common.BlockSigners { + return false + } + + method := common.ToHex(tx.Data()[0:4]) + + if method != common.SignMethod { + return false + } + + if len(tx.Data()) != (32*2 + 4) { + return false + } + + return true +} + +func (tx *Transaction) IsVotingTransaction() (bool, *common.Address) { + if tx.To() == nil { + return false, nil + } + b := (tx.To().String() == common.MasternodeVotingSMC) + + if !b { + return b, nil + } + + method := common.ToHex(tx.Data()[0:4]) + if b = (method == common.VoteMethod); b { + addr := tx.Data()[len(tx.Data())-20:] + m := common.BytesToAddress(addr) + return b, &m + } + + if b = (method == common.UnvoteMethod); b { + addr := tx.Data()[len(tx.Data())-32-20 : len(tx.Data())-32] + m := common.BytesToAddress(addr) + return b, &m + } + + if b = (method == common.ProposeMethod); b { + addr := tx.Data()[len(tx.Data())-20:] + m := common.BytesToAddress(addr) + return b, &m + } + + if b = (method == common.ResignMethod); b { + addr := tx.Data()[len(tx.Data())-20:] + m := common.BytesToAddress(addr) + return b, &m + } + + return b, nil +} + func (tx *Transaction) String() string { var from, to string if tx.data.V != nil { diff --git a/eth/backend.go b/eth/backend.go index 6a34d2bb5a..d8eba90f7b 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -308,7 +308,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { chainReward = rewardInflation(chainReward, number, common.BlocksPerYear) totalSigner := new(uint64) - signers, err := contracts.GetRewardForCheckpoint(chain, number, rCheckpoint, totalSigner, canonicalState) + signers, err := contracts.GetRewardForCheckpoint(c, chain, number, rCheckpoint, totalSigner) + log.Debug("Time Get Signers", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start))) if err != nil { log.Crit("Fail to get signers for reward checkpoint", "error", err) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 8e384da6c5..287d26a81d 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -859,6 +859,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx var filterSigners []common.Address finality := int32(0) if b.Number().Int64() > 0 { + engine := s.b.GetEngine() blockNr := rpc.BlockNumber(b.Number().Int64()) state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) if state == nil || err != nil { @@ -870,7 +871,6 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx } // Get block epoc latest. if s.b.ChainConfig().Posv != nil { - engine := s.b.GetEngine() lastCheckpointNumber := rpc.BlockNumber(b.Number().Uint64() - (b.Number().Uint64() % s.b.ChainConfig().Posv.Epoch)) prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, lastCheckpointNumber) if prevCheckpointBlock != nil { diff --git a/miner/worker.go b/miner/worker.go index 809a132777..8b80306e4d 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -616,12 +616,12 @@ func (self *worker) commitNewWork() { delete(self.possibleUncles, hash) } } + // Create the new block to seal with the consensus engine + if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil { + log.Error("Failed to finalize block for sealing", "err", err) + return + } if atomic.LoadInt32(&self.mining) == 1 { - // Create the new block to seal with the consensus engine - if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil { - log.Error("Failed to finalize block for sealing", "err", err) - return - } 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()