diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index 40e1381845..fa26bdf4c6 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -213,6 +213,7 @@ type Posv struct { HookReward func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) error HookPrepare func(header *types.Header, signers []common.Address) error + HookPenalty func(chain consensus.ChainReader, signers []common.Address, blockNumberEpoc uint64) ([]common.Address, error) } // New creates a Posv proof-of-stake-voting consensus engine with the initial @@ -637,7 +638,70 @@ func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error header.Extra = header.Extra[:extraVanity] if number%c.config.Epoch == 0 { - for _, signer := range snap.signers() { + signers := snap.signers() + + if c.HookPenalty != nil { + penSigners, _ := c.HookPenalty(chain, signers, number) + + if len(penSigners) > 0 { + // Keep remove penalty signer out of signer list. + for i, signer := range signers { + for _, penSigner := range penSigners { + if signer == penSigner { + signers = append(signers[:i], signers[i+1:]...) + } + } + } + + var penBytes []byte + for _, penSigner := range penSigners { + log.Error("penSigner", "penSigner", penSigner.String()) + // Convert number to byte. + penByte := common.LeftPadBytes([]byte(fmt.Sprintf("%d", penSigner)), common.AddressLength) + penBytes = append(penBytes, penByte...) + } + if len(penBytes) > 0 { + header.Penalties = penBytes + } + } + } + + // Prevent penaltied signer in 4 epocs ago jump into signer list. + var preventSigners []common.Address + for i := 1; i <= 4; i++ { + checkEpoc := uint64(i) * c.config.Epoch + if number > checkEpoc { + prevEpoc := number - checkEpoc + log.Error("prevEpoc", "prevEpoc", prevEpoc, "checkEpoc", checkEpoc, "number", number) + prevHeader := chain.GetHeaderByNumber(prevEpoc) + prevEpocBlock := chain.GetBlock(prevHeader.Hash(), prevEpoc) + penalties := prevEpocBlock.Penalties() + if penalties != nil { + prevSigners := make([]common.Address, len(penalties)/common.AddressLength) + if len(prevSigners) > 0 { + for _, signer := range prevSigners { + for _, prevSigner := range prevSigners { + if signer == prevSigner { + log.Error("preventSigner", "preventSigner", signer.String()) + preventSigners = append(preventSigners, signer) + } + } + } + } + } + if len(preventSigners) > 0 { + for i, signer := range signers { + for _, preventSigner := range preventSigners { + if signer == preventSigner { + signers = append(signers[:i], signers[i+1:]...) + } + } + } + } + } + } + + for _, signer := range signers { header.Extra = append(header.Extra, signer[:]...) } } diff --git a/core/blockchain.go b/core/blockchain.go index efd6c0758e..8b61d18dcd 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -690,7 +690,7 @@ func (bc *BlockChain) procFutureBlocks() { // Insert one by one as chain insertion needs contiguous ancestry between blocks for i := range blocks { - bc.InsertChain(blocks[i : i+1]) + bc.InsertChain(blocks[i: i+1]) } } } @@ -699,9 +699,9 @@ func (bc *BlockChain) procFutureBlocks() { type WriteStatus byte const ( - NonStatTy WriteStatus = iota - CanonStatTy - SideStatTy + NonStatTy WriteStatus = iota + CanonStatTy + SideStatTy ) // Rollback is designed to remove a chain of links from the database that aren't @@ -1243,7 +1243,7 @@ func (st *insertStats) report(chain []*types.Block, index int, cache common.Stor if index == len(chain)-1 || elapsed >= statsReportLimit { var ( end = chain[index] - txs = countTransactions(chain[st.lastIndex : index+1]) + txs = countTransactions(chain[st.lastIndex: index+1]) ) context := []interface{}{ "blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000, diff --git a/core/types/block.go b/core/types/block.go index f1aa43649f..e226d61fd5 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -85,6 +85,7 @@ type Header struct { MixDigest common.Hash `json:"mixHash" gencodec:"required"` Nonce BlockNonce `json:"nonce" gencodec:"required"` Validators []byte `json:"validators" gencodec:"required"` + Penalties []byte `json:"penalties" gencodec:"required"` } // field type overrides for gencodec @@ -320,6 +321,7 @@ func (b *Block) TxHash() common.Hash { return b.header.TxHash } func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } func (b *Block) UncleHash() common.Hash { return b.header.UncleHash } func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } +func (b *Block) Penalties() []byte { return common.CopyBytes(b.header.Penalties) } func (b *Block) Header() *Header { return CopyHeader(b.header) } diff --git a/eth/backend.go b/eth/backend.go index 9499b7dea8..ff42d6e658 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -241,9 +241,51 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } } } - return nil } + // Hook penalty. + c.HookPenalty = func(chain consensus.ChainReader, signers []common.Address, blockNumberEpoc uint64) ([]common.Address, error) { + client, err := eth.blockchain.GetClient() + if err != nil { + log.Error("Fail to connect IPC client for blockSigner", "error", err) + } + prevEpoc := blockNumberEpoc - chain.Config().Posv.Epoch + var penSigners []common.Address + if prevEpoc > 0 { + prevHeader := chain.GetHeaderByNumber(prevEpoc) + prevSigners := c.GetMasternodes(chain, prevHeader) + for _, signer := range signers { + for _, prevSigner := range prevSigners { + if signer == prevSigner { + penSigners = append(penSigners, signer) + } + } + } + if len(penSigners) > 0 { + blockSignerAddr := common.HexToAddress(common.BlockSigners) + // Loop for each block to check missing sign. + for i := prevEpoc; i <= blockNumberEpoc; i++ { + blockHeader := chain.GetHeaderByNumber(i) + if len(penSigners) > 0 { + signedMasternodes, _ := contracts.GetSignersFromContract(blockSignerAddr, client, blockHeader.Hash()) + if len(signedMasternodes) > 0 { + // Check signer signed? + for _, signed := range signedMasternodes { + for j, addr := range penSigners { + if signed == addr { + // Remove it from dupSigners. + penSigners = append(penSigners[:j], penSigners[j+1:]...) + } + } + } + } + } + } + } + } + + return penSigners, nil + } // Hook reward for posv validator. c.HookReward = func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) error { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c4ad761bc0..7b1893a7b2 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -817,6 +817,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx "transactionsRoot": head.TxHash, "receiptsRoot": head.ReceiptHash, "validators": hexutil.Bytes(head.Validators), + "penalties": hexutil.Bytes(head.Penalties), } if inclTx {