github.com/igggame/nebulas-go@v2.1.0+incompatible/consensus/dpos/dpos_state.go (about) 1 // Copyright (C) 2017 go-nebulas authors 2 // 3 // This file is part of the go-nebulas library. 4 // 5 // the go-nebulas library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // the go-nebulas library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with the go-nebulas library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 19 package dpos 20 21 import ( 22 "fmt" 23 "time" 24 25 "github.com/nebulasio/go-nebulas/consensus/pb" 26 27 "github.com/nebulasio/go-nebulas/core" 28 "github.com/nebulasio/go-nebulas/core/pb" 29 "github.com/nebulasio/go-nebulas/core/state" 30 31 "github.com/nebulasio/go-nebulas/common/trie" 32 "github.com/nebulasio/go-nebulas/storage" 33 "github.com/nebulasio/go-nebulas/util/byteutils" 34 "github.com/nebulasio/go-nebulas/util/logging" 35 "github.com/sirupsen/logrus" 36 ) 37 38 // State carry context in dpos consensus 39 type State struct { 40 timestamp int64 41 proposer byteutils.Hash 42 43 dynastyTrie *trie.Trie // key: delegatee, val: delegatee 44 45 chain *core.BlockChain 46 consensus core.Consensus 47 } 48 49 // NewState create a new dpos state 50 func (dpos *Dpos) NewState(root *consensuspb.ConsensusRoot, stor storage.Storage, needChangeLog bool) (state.ConsensusState, error) { 51 var dynastyRoot byteutils.Hash 52 if root != nil { 53 dynastyRoot = root.DynastyRoot 54 } 55 dynastyTrie, err := trie.NewTrie(dynastyRoot, stor, needChangeLog) 56 if err != nil { 57 return nil, err 58 } 59 60 return &State{ 61 timestamp: root.Timestamp, 62 proposer: root.Proposer, 63 64 dynastyTrie: dynastyTrie, 65 66 chain: dpos.chain, 67 consensus: dpos, 68 }, nil 69 } 70 71 // CheckTimeout check whether the block is timeout 72 func (dpos *Dpos) CheckTimeout(block *core.Block) bool { 73 nowInMs := time.Now().Unix() * SecondInMs 74 blockTimeInMs := block.Timestamp() * SecondInMs 75 if nowInMs < blockTimeInMs { 76 logging.VLog().WithFields(logrus.Fields{ 77 "block": block, 78 "now": nowInMs, 79 "diff": blockTimeInMs - nowInMs, 80 "err": "timeout - future block", 81 }).Warn("Found a future block.") 82 return false 83 } 84 behindInMs := nowInMs - blockTimeInMs 85 if behindInMs > AcceptedNetWorkDelayInMs { 86 logging.VLog().WithFields(logrus.Fields{ 87 "block": block, 88 "now": nowInMs, 89 "diff": behindInMs, 90 "limit": AcceptedNetWorkDelayInMs, 91 "err": "timeout - expired block", 92 }).Warn("Found a expired block.") 93 return true 94 } 95 return false 96 } 97 98 // GenesisConsensusState create a new genesis dpos state 99 func (dpos *Dpos) GenesisConsensusState(chain *core.BlockChain, conf *corepb.Genesis) (state.ConsensusState, error) { 100 dynastyTrie, err := trie.NewTrie(nil, chain.Storage(), false) 101 if err != nil { 102 return nil, err 103 } 104 if len(conf.Consensus.Dpos.Dynasty) < ConsensusSize { 105 return nil, ErrInitialDynastyNotEnough 106 } 107 if len(conf.Consensus.Dpos.Dynasty) != DynastySize { 108 return nil, ErrInvalidDynasty 109 } 110 for i := 0; i < len(conf.Consensus.Dpos.Dynasty); i++ { 111 addr := conf.Consensus.Dpos.Dynasty[i] 112 member, err := core.AddressParse(addr) 113 if err != nil { 114 return nil, err 115 } 116 v := member.Bytes() 117 if _, err = dynastyTrie.Put(v, v); err != nil { 118 return nil, err 119 } 120 } 121 return &State{ 122 timestamp: core.GenesisTimestamp, 123 proposer: nil, 124 125 dynastyTrie: dynastyTrie, 126 127 chain: chain, 128 consensus: dpos, 129 }, nil 130 } 131 132 func (ds *State) String() string { 133 proposer := "" 134 if ds.proposer != nil { 135 proposer = ds.proposer.String() 136 } 137 return fmt.Sprintf(`{"timestamp": %d, "proposer": "%s", "dynasty": "%s"}`, 138 ds.timestamp, 139 proposer, 140 byteutils.Hex(ds.dynastyTrie.RootHash()), 141 ) 142 } 143 144 // Replay a dpos 145 func (ds *State) Replay(done state.ConsensusState) error { 146 state := done.(*State) 147 if _, err := ds.dynastyTrie.Replay(state.dynastyTrie); err != nil { 148 return err 149 } 150 return nil 151 } 152 153 // Clone a dpos context 154 func (ds *State) Clone() (state.ConsensusState, error) { 155 var err error 156 dynastyTrie, err := ds.dynastyTrie.Clone() 157 if err != nil { 158 return nil, ErrCloneDynastyTrie 159 } 160 return &State{ 161 timestamp: ds.timestamp, 162 proposer: ds.proposer, 163 164 dynastyTrie: dynastyTrie, 165 166 chain: ds.chain, 167 consensus: ds.consensus, 168 }, nil 169 } 170 171 // RootHash hash dpos state 172 func (ds *State) RootHash() *consensuspb.ConsensusRoot { 173 return &consensuspb.ConsensusRoot{ 174 DynastyRoot: ds.dynastyTrie.RootHash(), 175 Timestamp: ds.TimeStamp(), 176 Proposer: ds.Proposer(), 177 } 178 } 179 180 // Dynasty return the current dynasty 181 func (ds *State) Dynasty() ([]byteutils.Hash, error) { 182 return TraverseDynasty(ds.dynastyTrie) 183 } 184 185 // DynastyRoot return the roothash of current dynasty 186 func (ds *State) DynastyRoot() byteutils.Hash { 187 return ds.dynastyTrie.RootHash() 188 } 189 190 // FindProposer for now in given dynasty 191 func FindProposer(now int64, miners []byteutils.Hash) (proposer byteutils.Hash, err error) { 192 nowInMs := now * SecondInMs 193 offsetInMs := nowInMs % DynastyIntervalInMs 194 if (offsetInMs % BlockIntervalInMs) != 0 { 195 return nil, ErrNotBlockForgTime 196 } 197 offset := offsetInMs / BlockIntervalInMs 198 offset %= DynastySize 199 200 if offset >= 0 && int(offset) < len(miners) { 201 proposer = miners[offset] 202 } else { 203 logging.VLog().WithFields(logrus.Fields{ 204 "proposer": proposer, 205 "offset": offset, 206 "delegatee": len(miners), 207 }).Warn("Found Nil Proposer.") 208 return nil, ErrFoundNilProposer 209 } 210 return proposer, nil 211 } 212 213 // Proposer return the current proposer 214 func (ds *State) Proposer() byteutils.Hash { 215 return ds.proposer 216 } 217 218 // TimeStamp return the current timestamp 219 func (ds *State) TimeStamp() int64 { 220 return ds.timestamp 221 } 222 223 // NextConsensusState return the new state after some seconds elapsed 224 func (ds *State) NextConsensusState(elapsedSecond int64, worldState state.WorldState) (state.ConsensusState, error) { 225 elapsedSecondInMs := elapsedSecond * SecondInMs 226 if elapsedSecondInMs <= 0 || elapsedSecondInMs%BlockIntervalInMs != 0 { 227 return nil, ErrNotBlockForgTime 228 } 229 230 dpos, ok := ds.consensus.(*Dpos) 231 if !ok { 232 logging.VLog().WithFields(logrus.Fields{ 233 "timestamp": ds.timestamp, 234 }).Fatal("Type conversion failed, unexpected error.") 235 } 236 nextTimestamp := ds.timestamp + elapsedSecond 237 dynastyTrie, err := dpos.dynasty.getDynasty(nextTimestamp) 238 if err != nil { 239 return nil, err 240 } 241 242 consensusState := &State{ 243 timestamp: ds.timestamp + elapsedSecond, 244 245 dynastyTrie: dynastyTrie, 246 247 chain: ds.chain, 248 consensus: ds.consensus, 249 } 250 251 miners, err := TraverseDynasty(dynastyTrie) 252 if err != nil { 253 return nil, err 254 } 255 consensusState.proposer, err = FindProposer(consensusState.timestamp, miners) 256 if err != nil { 257 return nil, err 258 } 259 return consensusState, nil 260 } 261 262 // TraverseDynasty return all members in the dynasty 263 func TraverseDynasty(dynasty *trie.Trie) ([]byteutils.Hash, error) { 264 members := []byteutils.Hash{} 265 iter, err := dynasty.Iterator(nil) 266 if err != nil && err != storage.ErrKeyNotFound { 267 return nil, err 268 } 269 if err != nil { 270 return members, nil 271 } 272 exist, err := iter.Next() 273 for exist { 274 members = append(members, iter.Value()) 275 exist, err = iter.Next() 276 } 277 return members, nil 278 }