From ab9f40aeed2826467bec0d8e0aa40b62e9b411bc Mon Sep 17 00:00:00 2001 From: Tuna Date: Sun, 24 Jun 2018 01:09:31 +0700 Subject: [PATCH 1/7] update new set of masternodes at end of each epoch (distance = m1Gap) --- cmd/tomo/main.go | 101 ++++++++++++++++++++++++++----------- common/types.go | 1 + consensus/clique/clique.go | 18 +++++-- core/blockchain.go | 15 ++++-- eth/backend.go | 29 +++++++++++ miner/worker.go | 11 +++- 6 files changed, 135 insertions(+), 40 deletions(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index 3ca947a8c7..8d314abdd7 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -26,8 +26,11 @@ import ( "time" "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth" @@ -37,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "gopkg.in/urfave/cli.v1" + validatorContract "github.com/ethereum/go-ethereum/contracts/validator/contract" ) const ( @@ -313,39 +317,78 @@ func startNode(ctx *cli.Context, stack *node.Node) { started = true log.Info("Enabled mining node!!!") } - defer close(core.Checkpoint) + defer close(core.CheckpointCh) + defer close(core.M1Ch) + for { + select { + case <-core.CheckpointCh: + log.Info("Checkpoint!!! It's time to reconcile node's state...") + ok, err := ethereum.ValidateStaker() + if err != nil { + utils.Fatalf("Can't verify masternode permission: %v", err) + } + if !ok { + log.Info("Only masternode can propose and verify blocks. Cancelling mining on this node...") + if started { + ethereum.StopMining() + started = false + } + log.Info("Cancelled mining mode!!!") + } else if !started { + log.Info("Masternode found. Enabling mining mode...") + // Use a reduced number of threads if requested + if threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name); threads > 0 { + type threaded interface { + SetThreads(threads int) + } + if th, ok := ethereum.Engine().(threaded); ok { + th.SetThreads(threads) + } + } + // Set the gas price to the limits from the CLI and start mining + ethereum.TxPool().SetGasPrice(utils.GlobalBig(ctx, utils.GasPriceFlag.Name)) + if err := ethereum.StartStaking(true); err != nil { + utils.Fatalf("Failed to start mining: %v", err) + } + started = true + log.Info("Enabled mining node!!!") + } + case <-core.M1Ch: + log.Info("It's time to update new set of masternodes for the next epoch...") + // get masternodes information from smart contract + client, err := ethclient.Dial(stack.IPCEndpoint()) + if err != nil { + utils.Fatalf("Fail to connect RPC", "error", err) + } + addr := common.HexToAddress(common.Validator) + validator, err := validatorContract.NewTomoValidator(addr, client) + if err != nil { + utils.Fatalf("Fail to get validator smc", "error", err) + } + opts := new(bind.CallOpts) + candidates, err := validator.GetCandidates(opts) + if err != nil { + utils.Fatalf("Can't get list of candidates", "error", err) + } - for range core.Checkpoint { - log.Info("Checkpoint!!! It's time to reconcile node's state...") - ok, err := ethereum.ValidateStaker() - if err != nil { - utils.Fatalf("Can't verify validator permission: %v", err) - } - if !ok { - log.Info("Only validator can mine blocks. Cancelling mining on this node...") - if started { - ethereum.StopMining() - started = false - } - log.Info("Cancelled mining mode!!!") - } else if !started { - log.Info("Validator found. Enabling mining mode...") - // Use a reduced number of threads if requested - if threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name); threads > 0 { - type threaded interface { - SetThreads(threads int) - } - if th, ok := ethereum.Engine().(threaded); ok { - th.SetThreads(threads) + var ms []clique.Masternode + for _, candidate := range candidates { + v, err := validator.GetCandidateCap(opts, candidate) + if err != nil { + log.Warn("Can't get cap of a candidate. Will ignore him", "address", candidate, "error", err) } + ms = append(ms, clique.Masternode{candidate, v.Int64()}) } - // Set the gas price to the limits from the CLI and start mining - ethereum.TxPool().SetGasPrice(utils.GlobalBig(ctx, utils.GasPriceFlag.Name)) - if err := ethereum.StartStaking(true); err != nil { - utils.Fatalf("Failed to start mining: %v", err) + // order by cap + sort.Slice(ms, func(i, j int) bool { + return ms[i].Stake > ms[j].Stake + }) + // update masternodes + err = ethereum.UpdateMasternodes(ms) + if err != nil { + utils.Fatalf("Can't update masternodes", "error", err) } - started = true - log.Info("Enabled mining node!!!") + log.Info("Masternodes are ready for the next epoch") } } }() diff --git a/common/types.go b/common/types.go index 78766f2221..cde9b77247 100644 --- a/common/types.go +++ b/common/types.go @@ -31,6 +31,7 @@ const ( HashLength = 32 AddressLength = 20 BlockSigners = "0x0000000000000000000000000000000000000089" + Validator = "0x0000000000000000000000000000000000000088" ) var ( diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 721e3d61d6..65820b005a 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -43,14 +43,18 @@ import ( ) const ( - checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database - inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory - inmemorySignatures = 4096 // Number of recent block signatures to keep in memory + checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database + inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory + inmemorySignatures = 4096 // Number of recent block signatures to keep in memory + wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers - wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers - genesisCoinBase = "0x0000000000000000000000000000000000000000" ) +type Masternode struct { + Address common.Address + Stake int64 +} + // Clique proof-of-authority protocol constants. var ( epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes @@ -377,6 +381,10 @@ func (c *Clique) GetSnapshot(chain consensus.ChainReader, header *types.Header) return snap, nil } +func (c *Clique) StoreSnapshot(snap *Snapshot) error { + return snap.store(c.db) +} + func position(list []common.Address, x common.Address) int { for i, item := range list { if item == x { diff --git a/core/blockchain.go b/core/blockchain.go index 1d55df9394..764e20bc86 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -47,7 +47,8 @@ import ( var ( blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil) - Checkpoint = make(chan int) + CheckpointCh = make(chan int) + M1Ch = make(chan int) ErrNoGenesis = errors.New("Genesis not found in chain") ) @@ -61,6 +62,7 @@ const ( // BlockChainVersion ensures that an incompatible database forces a resync from scratch. BlockChainVersion = 3 + M1Gap = 10 ) // CacheConfig contains the configuration values for the trie caching/pruning @@ -1185,9 +1187,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty stats.processed++ stats.usedGas += usedGas stats.report(chain, i, bc.stateCache.TrieDB().Size()) - if i == len(chain)-1 { - if (bc.chainConfig.Clique != nil) && (chain[i].NumberU64()%bc.chainConfig.Clique.Epoch) == 0 { - Checkpoint <- 1 + if i == len(chain)-1 && bc.chainConfig.Clique != nil { + // epoch block + if (chain[i].NumberU64() % bc.chainConfig.Clique.Epoch) == 0 { + CheckpointCh <- 1 + } + // prepare set of masternodes for the next epoch + if (chain[i].NumberU64() % bc.chainConfig.Clique.Epoch) == (bc.chainConfig.Clique.Epoch - M1Gap) { + M1Ch <- 1 } } } diff --git a/eth/backend.go b/eth/backend.go index c57423627d..737a9ea25c 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -53,6 +53,8 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) +const NumOfMasternodes = 99 + type LesServer interface { Start(srvr *p2p.Server) Stop() @@ -420,6 +422,33 @@ func (s *Ethereum) ValidateStaker() (bool, error) { return true, nil } +// Store new set of masternodes into local db +func (s *Ethereum) UpdateMasternodes(ms []clique.Masternode) error { + // get snapshot from local db + if s.chainConfig.Clique == nil { + return errors.New("not clique") + } + c := s.engine.(*clique.Clique) + snap, err := c.GetSnapshot(s.blockchain, s.blockchain.CurrentHeader()) + if err != nil { + return err + } + + snap.Signers = make(map[common.Address]struct{}) + for i, m := range ms { + if i == NumOfMasternodes { + break + } + snap.Signers[m.Address] = struct{}{} + } + err = c.StoreSnapshot(snap) + if err != nil { + return err + } + log.Trace("Stored masternodes snapshot to db", "number", snap.Number, "hash", snap.Hash) + return nil +} + func (s *Ethereum) StartStaking(local bool) error { eb, err := s.Etherbase() if err != nil { diff --git a/miner/worker.go b/miner/worker.go index 7208729267..028cdfb7d8 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -530,8 +530,15 @@ func (self *worker) commitNewWork() { log.Info("Commit new mining work", "number", work.Block.Number(), "txs", work.tcount, "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart))) self.unconfirmed.Shift(work.Block.NumberU64() - 1) } - if (work.config.Clique != nil) && (work.Block.NumberU64()%work.config.Clique.Epoch) == 0 { - core.Checkpoint <- 1 + if work.config.Clique != nil { + // epoch block + if (work.Block.NumberU64() % work.config.Clique.Epoch) == 0 { + core.CheckpointCh <- 1 + } + // prepare set of masternodes for the next epoch + if (work.Block.NumberU64() % work.config.Clique.Epoch) == (work.config.Clique.Epoch - core.M1Gap) { + core.M1Ch <- 1 + } } self.push(work) } From da26c4884be3ac2f430e39859da7fff5232b8fd5 Mon Sep 17 00:00:00 2001 From: Tuna Date: Sun, 24 Jun 2018 01:13:52 +0700 Subject: [PATCH 2/7] minor log cleaning up --- cmd/tomo/main.go | 2 +- eth/backend.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index 8d314abdd7..b89e72c5f1 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/console" + validatorContract "github.com/ethereum/go-ethereum/contracts/validator/contract" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethclient" @@ -40,7 +41,6 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "gopkg.in/urfave/cli.v1" - validatorContract "github.com/ethereum/go-ethereum/contracts/validator/contract" ) const ( diff --git a/eth/backend.go b/eth/backend.go index 737a9ea25c..a81f8be9d8 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -238,7 +238,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } } } - return nil } } From b62cdd9a03ef51f730cd70ef16f945318eaf5c51 Mon Sep 17 00:00:00 2001 From: Tuna Date: Mon, 25 Jun 2018 09:39:15 +0700 Subject: [PATCH 3/7] tiny lint thing --- cmd/tomo/main.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index b89e72c5f1..98273f5571 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -358,17 +358,17 @@ func startNode(ctx *cli.Context, stack *node.Node) { // get masternodes information from smart contract client, err := ethclient.Dial(stack.IPCEndpoint()) if err != nil { - utils.Fatalf("Fail to connect RPC", "error", err) + utils.Fatalf("Fail to connect RPC: %v", err) } addr := common.HexToAddress(common.Validator) validator, err := validatorContract.NewTomoValidator(addr, client) if err != nil { - utils.Fatalf("Fail to get validator smc", "error", err) + utils.Fatalf("Fail to get validator smc: %v", err) } opts := new(bind.CallOpts) candidates, err := validator.GetCandidates(opts) if err != nil { - utils.Fatalf("Can't get list of candidates", "error", err) + utils.Fatalf("Can't get list of candidates: %v", err) } var ms []clique.Masternode @@ -377,7 +377,7 @@ func startNode(ctx *cli.Context, stack *node.Node) { if err != nil { log.Warn("Can't get cap of a candidate. Will ignore him", "address", candidate, "error", err) } - ms = append(ms, clique.Masternode{candidate, v.Int64()}) + ms = append(ms, clique.Masternode{Address: candidate, Stake: v.Int64()}) } // order by cap sort.Slice(ms, func(i, j int) bool { @@ -386,7 +386,7 @@ func startNode(ctx *cli.Context, stack *node.Node) { // update masternodes err = ethereum.UpdateMasternodes(ms) if err != nil { - utils.Fatalf("Can't update masternodes", "error", err) + utils.Fatalf("Can't update masternodes: %v", err) } log.Info("Masternodes are ready for the next epoch") } From 6502fe9642233704881e5817d69b8233217d85e0 Mon Sep 17 00:00:00 2001 From: Tuna Date: Mon, 25 Jun 2018 11:18:32 +0700 Subject: [PATCH 4/7] skip changes if no candidates found --- cmd/tomo/main.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index 98273f5571..0358ef2eda 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -368,14 +368,14 @@ func startNode(ctx *cli.Context, stack *node.Node) { opts := new(bind.CallOpts) candidates, err := validator.GetCandidates(opts) if err != nil { - utils.Fatalf("Can't get list of candidates: %v", err) + utils.Fatalf("Can't get list of masternode candidates: %v", err) } var ms []clique.Masternode for _, candidate := range candidates { v, err := validator.GetCandidateCap(opts, candidate) if err != nil { - log.Warn("Can't get cap of a candidate. Will ignore him", "address", candidate, "error", err) + log.Warn("Can't get cap of a masternode candidate. Will ignore him", "address", candidate, "error", err) } ms = append(ms, clique.Masternode{Address: candidate, Stake: v.Int64()}) } @@ -383,12 +383,18 @@ func startNode(ctx *cli.Context, stack *node.Node) { sort.Slice(ms, func(i, j int) bool { return ms[i].Stake > ms[j].Stake }) - // update masternodes - err = ethereum.UpdateMasternodes(ms) - if err != nil { - utils.Fatalf("Can't update masternodes: %v", err) + log.Info("Ordered list of masternode candidates", "candidates", ms) + if len(ms) == 0 { + log.Info("No masternode candidates found. Keep the current masternodes set for the next epoch") + } else { + // update masternodes + log.Info("Updating new set of masternodes") + err = ethereum.UpdateMasternodes(ms) + if err != nil { + utils.Fatalf("Can't update masternodes: %v", err) + } + log.Info("Masternodes are ready for the next epoch") } - log.Info("Masternodes are ready for the next epoch") } } }() From 54a0bad209a0c819ca1bc438fdd6e535e4eb05a2 Mon Sep 17 00:00:00 2001 From: Tuna Date: Tue, 26 Jun 2018 14:19:08 +0700 Subject: [PATCH 5/7] get masternodes set from latest checkpoint block header instead of from snapshot --- cmd/tomo/main.go | 15 +++++++----- consensus/clique/clique.go | 47 +++++++++++++++++++++++++++++++++----- core/blockchain.go | 2 +- eth/backend.go | 15 +----------- miner/worker.go | 11 ++++++++- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index 0358ef2eda..0f777e1f5c 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -377,13 +377,16 @@ func startNode(ctx *cli.Context, stack *node.Node) { if err != nil { log.Warn("Can't get cap of a masternode candidate. Will ignore him", "address", candidate, "error", err) } - ms = append(ms, clique.Masternode{Address: candidate, Stake: v.Int64()}) + ms = append(ms, clique.Masternode{Address: candidate, Stake: v.String()}) + } + //// order by cap + //sort.Slice(ms, func(i, j int) bool { + // return ms[i].Stake > ms[j].Stake + //}) + log.Info("Ordered list of masternode candidates") + for _, m := range ms { + fmt.Printf("address: %s, stake: %s\n", m.Address.String(), m.Stake) } - // order by cap - sort.Slice(ms, func(i, j int) bool { - return ms[i].Stake > ms[j].Stake - }) - log.Info("Ordered list of masternode candidates", "candidates", ms) if len(ms) == 0 { log.Info("No masternode candidates found. Keep the current masternodes set for the next epoch") } else { diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 65820b005a..8dda1d82f3 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -25,6 +25,7 @@ import ( "sync" "time" + "fmt" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -52,7 +53,7 @@ const ( type Masternode struct { Address common.Address - Stake int64 + Stake string } // Clique proof-of-authority protocol constants. @@ -394,7 +395,7 @@ func position(list []common.Address, x common.Address) int { return -1 } -func YourTurn(snap *Snapshot, header *types.Header, cur common.Address) (bool, error) { +func YourTurn(masternodes []common.Address, snap *Snapshot, header *types.Header, cur common.Address) (bool, error) { if header.Number.Uint64() == 0 { // Not check signer for genesis block. return true, nil @@ -404,10 +405,21 @@ func YourTurn(snap *Snapshot, header *types.Header, cur common.Address) (bool, e if err != nil { return false, err } - preIndex := position(snap.signers(), pre) - curIndex := position(snap.signers(), cur) - log.Info("Debugging info", "number of masternodes", len(snap.signers()), "previous", pre, "position", preIndex, "current", cur, "position", curIndex) - return (preIndex+1)%len(snap.signers()) == curIndex, nil + preIndex := position(masternodes, pre) + curIndex := position(masternodes, cur) + log.Info("Debugging info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", cur, "position", curIndex) + for i, s := range masternodes { + fmt.Printf("%d - %s\n", i, s.String()) + } + return (preIndex+1)%len(masternodes) == curIndex, nil +} + +func GetExtraVanity() int { + return extraVanity +} + +func GetExtraSeal() int { + return extraSeal } // snapshot retrieves the authorization snapshot at a given point in time. @@ -610,6 +622,29 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro return nil } +func (c *Clique) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []Masternode) error { + number := header.Number.Uint64() + log.Trace("take snapshot", "number", number, "hash", header.Hash()) + snap, err := c.snapshot(chain, number, header.Hash(), nil) + if err != nil { + return err + } + currentSigners := snap.signers() + proposedSigners := make(map[common.Address]struct{}) + // count all addresses in ms to be masternode + for _, m := range ms { + proposedSigners[m.Address] = struct{}{} + c.proposals[m.Address] = true + } + // deactivate current masternodes which aren't in ms + for _, s := range currentSigners { + if _, ok := proposedSigners[s]; !ok { + c.proposals[s] = false + } + } + return nil +} + // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given, and returns the final block. func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { diff --git a/core/blockchain.go b/core/blockchain.go index 764e20bc86..96039a0896 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -62,7 +62,7 @@ const ( // BlockChainVersion ensures that an incompatible database forces a resync from scratch. BlockChainVersion = 3 - M1Gap = 10 + M1Gap = 3 ) // CacheConfig contains the configuration values for the trie caching/pruning diff --git a/eth/backend.go b/eth/backend.go index a81f8be9d8..8e69e5858a 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -428,23 +428,10 @@ func (s *Ethereum) UpdateMasternodes(ms []clique.Masternode) error { return errors.New("not clique") } c := s.engine.(*clique.Clique) - snap, err := c.GetSnapshot(s.blockchain, s.blockchain.CurrentHeader()) + err := c.UpdateMasternodes(s.blockchain, s.blockchain.CurrentHeader(), ms) if err != nil { return err } - - snap.Signers = make(map[common.Address]struct{}) - for i, m := range ms { - if i == NumOfMasternodes { - break - } - snap.Signers[m.Address] = struct{}{} - } - err = c.StoreSnapshot(snap) - if err != nil { - return err - } - log.Trace("Stored masternodes snapshot to db", "number", snap.Number, "hash", snap.Hash) return nil } diff --git a/miner/worker.go b/miner/worker.go index 028cdfb7d8..7b9a584666 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -421,13 +421,22 @@ func (self *worker) commitNewWork() { // check if we are right after parent's coinbase in the list // only go with Clique if self.config.Clique != nil { + // get masternodes set from latest checkpoint + lastCheckpointNumber := parent.NumberU64() - (parent.NumberU64() % self.config.Clique.Epoch) + preCheckpointHeader := self.chain.GetHeaderByNumber(lastCheckpointNumber) + extraVanity := clique.GetExtraVanity() + extraSeal := clique.GetExtraSeal() + masternodes := make([]common.Address, (len(preCheckpointHeader.Extra)-extraVanity-extraSeal)/common.AddressLength) + for i := 0; i < len(masternodes); i++ { + copy(masternodes[i][:], preCheckpointHeader.Extra[extraVanity+i*common.AddressLength:]) + } c := self.engine.(*clique.Clique) snap, err := c.GetSnapshot(self.chain, parent.Header()) if err != nil { log.Error("Failed when trying to commit new work", "err", err) return } - ok, err := clique.YourTurn(snap, parent.Header(), self.coinbase) + ok, err := clique.YourTurn(masternodes, snap, parent.Header(), self.coinbase) if err != nil { log.Error("Failed when trying to commit new work", "err", err) return From e37b644f29834527923dd9c9ed9ca2d9731686fa Mon Sep 17 00:00:00 2001 From: Tuna Date: Fri, 29 Jun 2018 17:14:55 +0700 Subject: [PATCH 6/7] temporarily bypass 0x00 returned from smc --- cmd/tomo/main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index 0f777e1f5c..c59d017856 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -377,7 +377,10 @@ func startNode(ctx *cli.Context, stack *node.Node) { if err != nil { log.Warn("Can't get cap of a masternode candidate. Will ignore him", "address", candidate, "error", err) } - ms = append(ms, clique.Masternode{Address: candidate, Stake: v.String()}) + //TODO: smart contract shouldn't return "0x0000000000000000000000000000000000000000" + if candidate.String() != "0x0000000000000000000000000000000000000000" { + ms = append(ms, clique.Masternode{Address: candidate, Stake: v.String()}) + } } //// order by cap //sort.Slice(ms, func(i, j int) bool { From a72d7782aea2ba7544232f2f65f1a65a7691d72f Mon Sep 17 00:00:00 2001 From: Tuna Date: Mon, 2 Jul 2018 14:30:52 +0700 Subject: [PATCH 7/7] in case a masternode resigns, let him continue until end of the current epoch --- consensus/clique/clique.go | 42 +++++++++++++++++++++++++++--------- consensus/clique/snapshot.go | 17 ++++++++------- miner/worker.go | 23 +++++++++++--------- 3 files changed, 54 insertions(+), 28 deletions(-) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 8dda1d82f3..558ffe7caa 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -395,6 +395,16 @@ func position(list []common.Address, x common.Address) int { return -1 } +func (c *Clique) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address { + lastCheckpointNumber := header.Number.Uint64() - (header.Number.Uint64() % c.config.Epoch) + preCheckpointHeader := chain.GetHeaderByNumber(lastCheckpointNumber) + masternodes := make([]common.Address, (len(preCheckpointHeader.Extra)-extraVanity-extraSeal)/common.AddressLength) + for i := 0; i < len(masternodes); i++ { + copy(masternodes[i][:], preCheckpointHeader.Extra[extraVanity+i*common.AddressLength:]) + } + return masternodes +} + func YourTurn(masternodes []common.Address, snap *Snapshot, header *types.Header, cur common.Address) (bool, error) { if header.Number.Uint64() == 0 { // Not check signer for genesis block. @@ -414,14 +424,6 @@ func YourTurn(masternodes []common.Address, snap *Snapshot, header *types.Header return (preIndex+1)%len(masternodes) == curIndex, nil } -func GetExtraVanity() int { - return extraVanity -} - -func GetExtraSeal() int { - return extraSeal -} - // snapshot retrieves the authorization snapshot at a given point in time. func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) { // Search for a snapshot in memory or on disk for checkpoints @@ -536,7 +538,17 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p return err } if _, ok := snap.Signers[signer]; !ok { - return errUnauthorized + valid := false + masternodes := c.GetMasternodes(chain, header) + for _, m := range masternodes { + if m == signer { + valid = true + break + } + } + if !valid { + return errUnauthorized + } } for seen, recent := range snap.Recents { if recent == signer { @@ -702,7 +714,17 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-ch return nil, err } if _, authorized := snap.Signers[signer]; !authorized { - return nil, errUnauthorized + valid := false + masternodes := c.GetMasternodes(chain, header) + for _, m := range masternodes { + if m == signer { + valid = true + break + } + } + if !valid { + return nil, errUnauthorized + } } // If we're amongst the recent signers, wait for the next block for seen, recent := range snap.Recents { diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go index 9ebdb8df15..71355ee0ed 100644 --- a/consensus/clique/snapshot.go +++ b/consensus/clique/snapshot.go @@ -205,14 +205,15 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { if err != nil { return nil, err } - if _, ok := snap.Signers[signer]; !ok { - return nil, errUnauthorized - } - for _, recent := range snap.Recents { - if recent == signer { - return nil, errUnauthorized - } - } + //FIXME: skip signer checking at this step until a good solution found + //if _, ok := snap.Signers[signer]; !ok { + // return nil, errUnauthorized + //} + //for _, recent := range snap.Recents { + // if recent == signer { + // return nil, errUnauthorized + // } + //} snap.Recents[number] = signer // Header authorized, discard any previous votes from the signer diff --git a/miner/worker.go b/miner/worker.go index 7b9a584666..ae0aeba029 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -348,8 +348,18 @@ func (self *worker) wait() { return } if _, authorized := snap.Signers[self.coinbase]; !authorized { - log.Error("Coinbase address not in snapshot signers.") - return + valid := false + masternodes := c.GetMasternodes(self.chain, block.Header()) + for _, m := range masternodes { + if m == self.coinbase { + valid = true + break + } + } + if !valid { + log.Error("Coinbase address not in snapshot signers.") + return + } } // Send tx sign to smart contract blockSigners. if err := contracts.CreateTransactionSign(self.config, self.eth.TxPool(), self.eth.AccountManager(), block); err != nil { @@ -422,15 +432,8 @@ func (self *worker) commitNewWork() { // only go with Clique if self.config.Clique != nil { // get masternodes set from latest checkpoint - lastCheckpointNumber := parent.NumberU64() - (parent.NumberU64() % self.config.Clique.Epoch) - preCheckpointHeader := self.chain.GetHeaderByNumber(lastCheckpointNumber) - extraVanity := clique.GetExtraVanity() - extraSeal := clique.GetExtraSeal() - masternodes := make([]common.Address, (len(preCheckpointHeader.Extra)-extraVanity-extraSeal)/common.AddressLength) - for i := 0; i < len(masternodes); i++ { - copy(masternodes[i][:], preCheckpointHeader.Extra[extraVanity+i*common.AddressLength:]) - } c := self.engine.(*clique.Clique) + masternodes := c.GetMasternodes(self.chain, parent.Header()) snap, err := c.GetSnapshot(self.chain, parent.Header()) if err != nil { log.Error("Failed when trying to commit new work", "err", err)