diff --git a/common/constants.go b/common/constants.go index 8ca2ec3eca..701a252eb9 100644 --- a/common/constants.go +++ b/common/constants.go @@ -22,7 +22,8 @@ const ( ) var TIP2019Block = big.NewInt(1050000) -var IsTestnet = false +var TIPEVMSignerBlock = big.NewInt(2500000) +var IsTestnet bool = false var StoreRewardFolder string var RollbackHash Hash var MinGasPrice int64 diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index 7817cbc7db..3ccdb81419 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -225,11 +225,12 @@ 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) - HookVerifyMNs func(header *types.Header, signers []common.Address) error + 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) + HookPenaltyTIPEVM func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error) + HookValidator func(header *types.Header, signers []common.Address) ([]byte, error) + HookVerifyMNs func(header *types.Header, signers []common.Address) error } // New creates a PoSV proof-of-stake-voting consensus engine with the initial @@ -398,8 +399,14 @@ func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types. // If the block is a checkpoint block, verify the signer list if number%c.config.Epoch == 0 { penPenalties := []common.Address{} - if c.HookPenalty != nil { - penPenalties, err = c.HookPenalty(chain, number) + if c.HookPenalty != nil || c.HookPenaltyTIPEVM != nil { + var penPenalties []common.Address = nil + var err error = nil + if chain.Config().IsTIPEVMSigner(header.Number) { + penPenalties, err = c.HookPenaltyTIPEVM(chain, number) + } else { + penPenalties, err = c.HookPenalty(chain, number) + } if err != nil { return err } @@ -788,8 +795,14 @@ func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error header.Extra = header.Extra[:extraVanity] masternodes := snap.GetSigners() if number >= c.config.Epoch && number%c.config.Epoch == 0 { - if c.HookPenalty != nil { - penMasternodes, err := c.HookPenalty(chain, number) + if c.HookPenalty != nil || c.HookPenaltyTIPEVM != nil { + var penMasternodes []common.Address = nil + var err error = nil + if chain.Config().IsTIPEVMSigner(header.Number) { + penMasternodes, err = c.HookPenaltyTIPEVM(chain, number) + } else { + penMasternodes, err = c.HookPenalty(chain, number) + } if err != nil { return err } @@ -1036,8 +1049,8 @@ 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 +func (c *Posv) CacheData(header *types.Header, txs []*types.Transaction, receipts []*types.Receipt) []*types.Transaction { + signTxs := []*types.Transaction{} for _, tx := range txs { if tx.IsSigningTransaction() { var b uint @@ -1063,7 +1076,19 @@ func (c *Posv) CacheData(header *types.Header, txs []*types.Transaction, receipt 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 + return signTxs +} + +func (c *Posv) CacheSigner(header *types.Header, txs []*types.Transaction) []*types.Transaction { + signTxs := []*types.Transaction{} + for _, tx := range txs { + if tx.IsSigningTransaction() { + 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 signTxs } func (c *Posv) GetDb() ethdb.Database { diff --git a/contracts/utils.go b/contracts/utils.go index d664471501..dfe20de790 100644 --- a/contracts/utils.go +++ b/contracts/utils.go @@ -373,10 +373,27 @@ func GetRewardForCheckpoint(c *posv.Posv, chain consensus.ChainReader, header *t mapBlkHash := map[uint64]common.Hash{} data := make(map[common.Hash][]common.Address) - for curNumber := prevCheckpoint + (rCheckpoint * 2) - 1; curNumber >= startBlockNumber; curNumber-- { - header = chain.GetHeader(header.ParentHash, curNumber) - mapBlkHash[curNumber] = header.Hash() - data = GetSignersSignedAtBlockHash(c, chain, data, header, curNumber) + for i := prevCheckpoint + (rCheckpoint * 2) - 1; i >= startBlockNumber; i-- { + header = chain.GetHeader(header.ParentHash, i) + mapBlkHash[i] = header.Hash() + signData, ok := c.BlockSigners.Get(header.Hash()) + if !ok { + log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i) + block := chain.GetBlock(header.Hash(), i) + txs := block.Transactions() + if !chain.Config().IsTIPEVMSigner(header.Number) { + receipts := core.GetBlockReceipts(c.GetDb(), header.Hash(), i) + signData = c.CacheData(header, txs, receipts); + } else { + signData = c.CacheSigner(header, txs); + } + } + 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) + } } header = chain.GetHeader(header.ParentHash, prevCheckpoint) masternodes := posv.GetMasternodesFromCheckpointHeader(header) diff --git a/core/blockchain.go b/core/blockchain.go index 2f0dc16531..ae4536bcaf 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -507,7 +507,7 @@ func (bc *BlockChain) insert(block *types.Block) { bc.currentBlock.Store(block) // save cache BlockSigners - if bc.chainConfig.Posv != nil { + if bc.chainConfig.Posv != nil && !bc.chainConfig.IsTIPEVMSigner(block.Number()) { engine := bc.Engine().(*posv.Posv) engine.CacheData(block.Header(), block.Transactions(), bc.GetReceiptsByHash(block.Hash())) } @@ -1019,6 +1019,11 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. if status == CanonStatTy { bc.insert(block) } + // save cache BlockSigners + if bc.chainConfig.Posv != nil && bc.chainConfig.IsTIPEVMSigner(block.Number()) { + engine := bc.Engine().(*posv.Posv) + engine.CacheSigner(block.Header(), block.Transactions()) + } bc.futureBlocks.Remove(block.Hash()) return status, nil } diff --git a/core/state/statedb.go b/core/state/statedb.go index bd67e789d5..2ae700cd67 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -357,6 +357,14 @@ func (self *StateDB) deleteStateObject(stateObject *stateObject) { self.setError(self.trie.TryDelete(addr[:])) } +// DeleteAddress removes the address from the state trie. +func (self *StateDB) DeleteAddress(addr common.Address) { + stateObject := self.getStateObject(addr) + if stateObject != nil && !stateObject.deleted { + self.deleteStateObject(stateObject) + } +} + // Retrieve a state object given my the address. Returns nil if not found. func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) { // Prefer 'live' objects. diff --git a/core/state_processor.go b/core/state_processor.go index 6ec3fcc6f1..d21ecd70f4 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -73,6 +73,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { misc.ApplyDAOHardFork(statedb) } + if p.config.IsTIPEVMSigner(header.Number) { + statedb.DeleteAddress(common.HexToAddress(common.BlockSigners)) + } InitSignerInTransactions(p.config, header, block.Transactions()) for i, tx := range block.Transactions() { statedb.Prepare(tx.Hash(), block.Hash(), i) @@ -101,6 +104,9 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { misc.ApplyDAOHardFork(statedb) } + if p.config.IsTIPEVMSigner(header.Number) { + statedb.DeleteAddress(common.HexToAddress(common.BlockSigners)) + } if cBlock.stop { return nil, nil, 0, ErrStopPreparingBlock } @@ -132,6 +138,9 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error) { + if tx.To() != nil && tx.To().String() == common.BlockSigners && config.IsTIPEVMSigner(header.Number) { + return ApplySignTransaction(config, statedb, header, tx, usedGas) + } msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) if err != nil { return nil, 0, err @@ -171,6 +180,41 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common return receipt, gas, err } +func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error) { + // Update the state with pending changes + var root []byte + if config.IsByzantium(header.Number) { + statedb.Finalise(true) + } else { + root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() + } + from, err := types.Sender(types.MakeSigner(config, header.Number), tx) + if err != nil { + return nil, 0, err + } + nonce := statedb.GetNonce(from) + if nonce < tx.Nonce() { + return nil, 0, ErrNonceTooHigh + } else if nonce > tx.Nonce() { + return nil, 0, ErrNonceTooLow + } + statedb.SetNonce(from, nonce+1) + // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx + // based on the eip phase, we're passing wether the root touch-delete accounts. + receipt := types.NewReceipt(root, false, *usedGas) + receipt.TxHash = tx.Hash() + receipt.GasUsed = 0 + // if the transaction created a contract, store the creation address in the receipt. + // Set the receipt logs and create a bloom for filtering + log := &types.Log{} + log.Address = common.HexToAddress(common.BlockSigners) + log.BlockNumber = header.Number.Uint64() + statedb.AddLog(log) + receipt.Logs = statedb.GetLogs(tx.Hash()) + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + return receipt, 0, nil +} + func InitSignerInTransactions(config *params.ChainConfig, header *types.Header, txs types.Transactions) { nWorker := runtime.NumCPU() signer := types.MakeSigner(config, header.Number) diff --git a/eth/backend.go b/eth/backend.go index 514186692d..87896b32cf 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -20,6 +20,7 @@ package eth import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/core/state" "math/big" "runtime" "sync" @@ -36,7 +37,6 @@ import ( "github.com/ethereum/go-ethereum/contracts" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" - "github.com/ethereum/go-ethereum/core/state" //"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -347,6 +347,59 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { return []common.Address{}, nil } + // Hook scans for bad masternodes and decide to penalty them + c.HookPenaltyTIPEVM = func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error) { + canonicalState, err := eth.blockchain.State() + if canonicalState == nil || err != nil { + log.Crit("Can't get state at head of canonical chain", "head number", eth.blockchain.CurrentHeader().Number.Uint64(), "err", err) + } + prevEpoc := blockNumberEpoc - chain.Config().Posv.Epoch + if prevEpoc >= 0 { + start := time.Now() + prevHeader := chain.GetHeaderByNumber(prevEpoc) + penSigners := c.GetMasternodes(chain, prevHeader) + if len(penSigners) > 0 { + // Loop for each block to check missing sign. + blockHash := map[common.Hash]bool{} + for i := prevEpoc; i < blockNumberEpoc; i++ { + if len(penSigners) > 0 { + bheader := chain.GetHeaderByNumber(i) + bhash := bheader.Hash() + if i%common.MergeSignRange == 0 { + blockHash[bhash] = true + } + signData, ok := c.BlockSigners.Get(bhash) + if !ok { + block := chain.GetBlock(bhash, i) + txs := block.Transactions() + signData = c.CacheSigner(bheader, txs); + } + txs := signData.([]*types.Transaction) + // Check signer signed? + for _, tx := range txs { + blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:]) + from := *tx.From() + if blockHash[blkHash] == true { + for j, addr := range penSigners { + if from == addr { + // Remove it from dupSigners. + penSigners = append(penSigners[:j], penSigners[j+1:]...) + break + } + } + } + } + } else { + break + } + } + } + log.Debug("Time Calculated HookPenaltyTIPEVM ", "block", blockNumberEpoc, "time", common.PrettyDuration(time.Since(start))) + return penSigners, nil + } + return []common.Address{}, nil + } + // Hook calculates reward for masternodes c.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, header *types.Header) (error, map[string]interface{}) { parentHeader := eth.blockchain.GetHeader(header.ParentHash, header.Number.Uint64()-1) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 59f59972e5..dd9928d47b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -950,33 +950,40 @@ func (s *PublicBlockChainAPI) rpcOutputBlockSigners(b *types.Block, ctx context. var signers []common.Address var filterSigners []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 + 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 } if engine, ok := s.b.GetEngine().(*posv.Posv); ok { - prevBlock, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(prevBlockNumber)) - if err != nil { - log.Error("Fail to get previous block", "error", err) - return []common.Address{}, err - } - addrBlockSigner := common.HexToAddress(common.BlockSigners) - signers, err = contracts.GetSignersByExecutingEVM(addrBlockSigner, client, prevBlock.Hash()) - if err != nil { - log.Error("Fail to get signers from block signer SC.", "error", err) - return []common.Address{}, err - } - validator, _ := engine.RecoverValidator(b.Header()) - creator, _ := engine.RecoverSigner(b.Header()) - signers = append(signers, validator) - signers = append(signers, creator) - for _, masternode := range masternodes { - for _, signer := range signers { - if signer == masternode { - filterSigners = append(filterSigners, masternode) - break + // Get block epoc latest. + lastCheckpointNumber := signedBlockNumber - (signedBlockNumber % s.b.ChainConfig().Posv.Epoch) + prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(lastCheckpointNumber)) + if prevCheckpointBlock != nil { + masternodes := engine.GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), blockNumber, s.b.ChainConfig().Posv.Epoch) + signedBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(signedBlockNumber)) + if s.b.ChainConfig().IsTIPEVMSigner(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 { + if signer == masternode { + countFinality++ + filterSigners = append(filterSigners, masternode) + break + } } } } @@ -1618,3 +1625,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().(*posv.Posv); ok { + limitNumber := blockNumber - blockNumber%b.ChainConfig().Posv.Epoch + 2*b.ChainConfig().Posv.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] == true { + addrs = append(addrs, from) + delete(mapMN, from) + } + } + if len(mapMN) == 0 { + break + } + } + } + return addrs, nil +} diff --git a/miner/worker.go b/miner/worker.go index 8159e41547..91ee30df56 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -18,6 +18,7 @@ package miner import ( "bytes" + "encoding/binary" "fmt" "math/big" "os" @@ -583,6 +584,9 @@ func (self *worker) commitNewWork() { if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 { misc.ApplyDAOHardFork(work.state) } + if self.config.IsTIPEVMSigner(header.Number) { + work.state.DeleteAddress(common.HexToAddress(common.BlockSigners)) + } // won't grasp txs at checkpoint var ( txs *types.TransactionsByPriceAndNonce @@ -671,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 lenght", "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.Posv.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) diff --git a/params/config.go b/params/config.go index 5a3c194d0a..a8f4027109 100644 --- a/params/config.go +++ b/params/config.go @@ -217,6 +217,10 @@ func (c *ChainConfig) IsTIP2019(num *big.Int) bool { return isForked(common.TIP2019Block, num) } +func (c *ChainConfig) IsTIPEVMSigner(num *big.Int) bool { + return isForked(common.TIPEVMSignerBlock, num) +} + // GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice). // // The returned GasTable's fields shouldn't, under any circumstances, be changed.