github.com/aergoio/aergo@v1.3.1/chain/chaindbForRaft.go (about) 1 package chain 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/gob" 7 "errors" 8 "github.com/aergoio/aergo-lib/db" 9 "github.com/aergoio/aergo/consensus" 10 "github.com/aergoio/aergo/types" 11 "github.com/aergoio/etcd/raft/raftpb" 12 "github.com/gogo/protobuf/proto" 13 ) 14 15 var ( 16 ErrMismatchedEntry = errors.New("mismatched entry") 17 ErrNoWalEntry = errors.New("no entry") 18 ErrEncodeRaftIdentity = errors.New("failed encoding of raft identity") 19 ErrDecodeRaftIdentity = errors.New("failed decoding of raft identity") 20 ErrNoWalEntryForBlock = errors.New("no raft entry for block") 21 ErrNilHardState = errors.New("hardstateinfo must not be nil") 22 ) 23 24 func (cdb *ChainDB) ResetWAL(hardStateInfo *types.HardStateInfo) error { 25 if hardStateInfo == nil { 26 return ErrNilHardState 27 } 28 29 logger.Info().Str("hardstate", hardStateInfo.ToString()).Msg("reset wal with given hardstate") 30 31 cdb.ClearWAL() 32 33 if err := cdb.WriteHardState(&raftpb.HardState{Term: hardStateInfo.Term, Commit: hardStateInfo.Commit}); err != nil { 34 return err 35 } 36 37 // build snapshot 38 var ( 39 snapBlock *types.Block 40 err error 41 ) 42 if snapBlock, err = cdb.GetBestBlock(); err != nil { 43 return err 44 } 45 46 snapData := consensus.NewSnapshotData(nil, nil, snapBlock) 47 if snapData == nil { 48 panic("new snap failed") 49 } 50 51 data, err := snapData.Encode() 52 if err != nil { 53 return err 54 } 55 56 tmpSnapshot := raftpb.Snapshot{ 57 Metadata: raftpb.SnapshotMetadata{Index: hardStateInfo.Commit, Term: hardStateInfo.Term}, 58 Data: data, 59 } 60 61 if err := cdb.WriteSnapshot(&tmpSnapshot); err != nil { 62 logger.Fatal().Err(err).Msg("failed to save snapshot to wal") 63 } 64 65 // write initial values 66 // last entry index = commit 67 dbTx := cdb.store.NewTx() 68 defer dbTx.Discard() 69 70 cdb.writeRaftEntryLastIndex(dbTx, hardStateInfo.Commit) 71 72 dbTx.Commit() 73 74 return nil 75 } 76 77 // ClearWal() removes all data used by raft 78 func (cdb *ChainDB) ClearWAL() { 79 logger.Info().Msg("clear all data used by raft") 80 81 removeAllRaftEntries := func(lastIdx uint64) { 82 logger.Debug().Uint64("last", lastIdx).Msg("reset raft entries from datafiles") 83 84 bulk := cdb.store.NewBulk() 85 defer bulk.DiscardLast() 86 87 for i := lastIdx; i >= 1; i-- { 88 bulk.Delete(getRaftEntryKey(i)) 89 } 90 91 bulk.Delete(raftEntryLastIdxKey) 92 93 bulk.Flush() 94 } 95 96 dbTx := cdb.store.NewTx() 97 defer dbTx.Discard() 98 99 dbTx.Delete(raftIdentityKey) 100 // remove hardstate 101 102 dbTx.Delete(raftStateKey) 103 104 // remove snapshot 105 dbTx.Delete(raftSnapKey) 106 107 logger.Debug().Msg("reset identify, hardstate, snapshot from datafiles") 108 109 dbTx.Commit() 110 111 // remove raft entries 112 if last, err := cdb.GetRaftEntryLastIdx(); err == nil { 113 // remove 1 ~ last raft entry 114 removeAllRaftEntries(last) 115 } 116 117 logger.Debug().Msg("clear WAL done") 118 } 119 120 func (cdb *ChainDB) WriteHardState(hardstate *raftpb.HardState) error { 121 dbTx := cdb.store.NewTx() 122 defer dbTx.Discard() 123 124 var data []byte 125 var err error 126 127 logger.Info().Uint64("term", hardstate.Term).Str("vote", types.Uint64ToHexaString(hardstate.Vote)).Uint64("commit", hardstate.Commit).Msg("save hard state") 128 129 if data, err = proto.Marshal(hardstate); err != nil { 130 logger.Panic().Msg("failed to marshal raft state") 131 return err 132 } 133 dbTx.Set(raftStateKey, data) 134 dbTx.Commit() 135 136 return nil 137 } 138 139 func (cdb *ChainDB) GetHardState() (*raftpb.HardState, error) { 140 data := cdb.store.Get(raftStateKey) 141 142 if len(data) == 0 { 143 return nil, ErrWalNoHardState 144 } 145 146 state := &raftpb.HardState{} 147 if err := proto.Unmarshal(data, state); err != nil { 148 logger.Panic().Msg("failed to unmarshal raft state") 149 return nil, ErrInvalidHardState 150 } 151 152 logger.Info().Uint64("term", state.Term).Str("vote", types.Uint64ToHexaString(state.Vote)).Uint64("commit", state.Commit).Msg("load hard state") 153 154 return state, nil 155 } 156 157 func getRaftEntryKey(idx uint64) []byte { 158 var key bytes.Buffer 159 key.Write(raftEntryPrefix) 160 l := make([]byte, 8) 161 binary.LittleEndian.PutUint64(l[:], idx) 162 key.Write(l) 163 return key.Bytes() 164 } 165 166 func getRaftEntryInvertKey(blockHash []byte) []byte { 167 var key bytes.Buffer 168 key.Write(raftEntryInvertPrefix) 169 key.Write(blockHash) 170 return key.Bytes() 171 } 172 173 func (cdb *ChainDB) WriteRaftEntry(ents []*consensus.WalEntry, blocks []*types.Block, ccProposes []*raftpb.ConfChange) error { 174 var data []byte 175 var err error 176 var lastIdx uint64 177 178 // truncate conflicting entry 179 last, err := cdb.GetRaftEntryLastIdx() 180 if err != nil { 181 return err 182 } 183 184 dbTx := cdb.store.NewTx() 185 defer dbTx.Discard() 186 187 if ents[0].Index <= last { 188 logger.Debug().Uint64("from", ents[0].Index).Uint64("to", last).Msg("truncate conflicting index") 189 190 for i := ents[0].Index; i <= last; i++ { 191 // delete ents[0].Index ~ lastIndex of wal 192 dbTx.Delete(getRaftEntryKey(i)) 193 } 194 } 195 196 for i, entry := range ents { 197 var targetNo uint64 198 199 if entry.Type == consensus.EntryBlock { 200 if err := cdb.addBlock(dbTx, blocks[i]); err != nil { 201 panic("add block entry") 202 return err 203 } 204 205 targetNo = blocks[i].BlockNo() 206 } 207 208 if data, err = entry.ToBytes(); err != nil { 209 panic("failed to convert entry to bytes") 210 return err 211 } 212 213 lastIdx = entry.Index 214 dbTx.Set(getRaftEntryKey(entry.Index), data) 215 216 // invert key to search raft entry corresponding to block hash 217 if entry.Type == consensus.EntryBlock { 218 dbTx.Set(getRaftEntryInvertKey(blocks[i].BlockHash()), types.Uint64ToBytes(entry.Index)) 219 } 220 221 if entry.Type == consensus.EntryConfChange { 222 if ccProposes[i] == nil { 223 logger.Fatal().Str("entry", entry.ToString()).Msg("confChangePropose must not be nil") 224 } 225 if err := cdb.writeConfChangeProgress(dbTx, ccProposes[i].ID, 226 &types.ConfChangeProgress{State: types.ConfChangeState_CONF_CHANGE_STATE_SAVED, Err: ""}); err != nil { 227 return err 228 } 229 230 targetNo = ccProposes[i].ID 231 } 232 233 logger.Info().Str("type", consensus.WalEntryType_name[entry.Type]).Uint64("Index", entry.Index).Uint64("term", entry.Term).Uint64("blockNo/requestID", targetNo).Msg("add raft log entry") 234 } 235 236 // set lastindex 237 cdb.writeRaftEntryLastIndex(dbTx, lastIdx) 238 239 dbTx.Commit() 240 241 return nil 242 } 243 244 func (cdb *ChainDB) writeRaftEntryLastIndex(dbTx db.Transaction, lastIdx uint64) { 245 logger.Debug().Uint64("index", lastIdx).Msg("set last wal entry") 246 247 dbTx.Set(raftEntryLastIdxKey, types.BlockNoToBytes(lastIdx)) 248 } 249 250 func (cdb *ChainDB) GetRaftEntry(idx uint64) (*consensus.WalEntry, error) { 251 data := cdb.store.Get(getRaftEntryKey(idx)) 252 if len(data) == 0 { 253 return nil, ErrNoWalEntry 254 } 255 256 var entry consensus.WalEntry 257 var b bytes.Buffer 258 b.Write(data) 259 decoder := gob.NewDecoder(&b) 260 if err := decoder.Decode(&entry); err != nil { 261 return nil, err 262 } 263 264 if entry.Index != idx { 265 logger.Error().Uint64("entry", entry.Index).Uint64("req", idx).Msg("mismatched wal entry") 266 return nil, ErrMismatchedEntry 267 } 268 269 return &entry, nil 270 } 271 272 func (cdb *ChainDB) GetRaftEntryIndexOfBlock(hash []byte) (uint64, error) { 273 data := cdb.store.Get(getRaftEntryInvertKey(hash)) 274 if len(data) == 0 { 275 return 0, ErrNoWalEntryForBlock 276 } 277 278 idx := types.BytesToUint64(data) 279 if idx == 0 { 280 return 0, ErrNoWalEntryForBlock 281 } 282 283 return idx, nil 284 } 285 286 func (cdb *ChainDB) GetRaftEntryOfBlock(hash []byte) (*consensus.WalEntry, error) { 287 idx, err := cdb.GetRaftEntryIndexOfBlock(hash) 288 if err != nil { 289 return nil, err 290 } 291 292 return cdb.GetRaftEntry(idx) 293 } 294 295 func (cdb *ChainDB) GetRaftEntryLastIdx() (uint64, error) { 296 lastBytes := cdb.store.Get(raftEntryLastIdxKey) 297 if lastBytes == nil || len(lastBytes) == 0 { 298 return 0, nil 299 } 300 301 return types.BlockNoFromBytes(lastBytes), nil 302 } 303 304 var ( 305 ErrWalNotEqualIdentityName = errors.New("name of identity is not equal") 306 ErrWalNotEqualIdentityPeerID = errors.New("peerid of identity is not equal") 307 ) 308 309 // HasWal checks chaindb has valid status of Raft WAL. 310 // 1. compare identity with config 311 // 2. check if hardstate exists 312 // 3. check if last raft entiry index exists 313 // last entry index can be 0 if first sync has failed 314 func (cdb *ChainDB) HasWal(identity consensus.RaftIdentity) (bool, error) { 315 var ( 316 id *consensus.RaftIdentity 317 last uint64 318 hs *raftpb.HardState 319 err error 320 ) 321 322 if id, err = cdb.GetIdentity(); err != nil || id == nil { 323 return false, err 324 } 325 326 if id.Name != identity.Name { 327 logger.Debug().Str("config name", identity.Name).Str("saved id", id.Name).Msg("unmatched name of identity") 328 return false, ErrWalNotEqualIdentityName 329 } 330 331 if id.PeerID != identity.PeerID { 332 logger.Debug().Str("config peerid", identity.PeerID).Str("saved id", id.PeerID).Msg("unmatched peerid of identity") 333 return false, ErrWalNotEqualIdentityPeerID 334 } 335 336 if hs, err = cdb.GetHardState(); err != nil { 337 return false, err 338 } 339 340 if last, err = cdb.GetRaftEntryLastIdx(); err != nil { 341 return false, err 342 } 343 344 logger.Info().Str("identity", id.ToString()).Str("hardstate", types.RaftHardStateToString(*hs)).Uint64("lastidx", last).Msg("existing wal status") 345 346 return true, nil 347 } 348 349 /* 350 func encodeBool(v bool) ([]byte, error) { 351 buf := new(bytes.Buffer) 352 err := binary.Write(buf, binary.LittleEndian, v) 353 if err != nil { 354 return nil, err 355 } 356 357 return buf.Bytes(), nil 358 } 359 360 func decodeBool(data []byte) (bool, error) { 361 var val bool 362 bufreader := bytes.NewReader(data) 363 if err := binary.Read(bufreader, binary.LittleEndian, &val); err != nil { 364 return false, err 365 } 366 367 return val, nil 368 } 369 */ 370 371 func (cdb *ChainDB) WriteSnapshot(snap *raftpb.Snapshot) error { 372 var snapdata = consensus.SnapshotData{} 373 err := snapdata.Decode(snap.Data) 374 if err != nil { 375 logger.Fatal().Msg("failed to unmarshal snapshot data to write") 376 return err 377 } 378 379 logger.Debug().Str("snapshot", consensus.SnapToString(snap, &snapdata)).Msg("write snapshot to wal") 380 data, err := proto.Marshal(snap) 381 if err != nil { 382 return err 383 } 384 385 dbTx := cdb.store.NewTx() 386 dbTx.Set(raftSnapKey, data) 387 dbTx.Commit() 388 389 return nil 390 } 391 392 /* 393 func (cdb *ChainDB) WriteSnapshotDone() error { 394 data, err := encodeBool(true) 395 if err != nil { 396 return err 397 } 398 399 dbTx := cdb.store.NewTx() 400 dbTx.Set(raftSnapStatusKey, data) 401 dbTx.Commit() 402 403 return nil 404 } 405 406 func (cdb *ChainDB) GetSnapshotDone() (bool, error) { 407 data := cdb.store.Get(raftSnapStatusKey) 408 if len(data) == 0 { 409 return false, nil 410 } 411 412 val, err := decodeBool(data) 413 if err != nil { 414 return false, err 415 } 416 417 return val, nil 418 } 419 */ 420 func (cdb *ChainDB) GetSnapshot() (*raftpb.Snapshot, error) { 421 data := cdb.store.Get(raftSnapKey) 422 if len(data) == 0 { 423 return nil, nil 424 } 425 426 snap := &raftpb.Snapshot{} 427 if err := proto.Unmarshal(data, snap); err != nil { 428 logger.Panic().Msg("failed to unmarshal raft snap") 429 return nil, ErrInvalidRaftSnapshot 430 } 431 432 if snap.Data == nil { 433 logger.Panic().Msg("raft snap data is nil") 434 return nil, ErrInvalidRaftSnapshot 435 } 436 437 return snap, nil 438 } 439 440 func (cdb *ChainDB) WriteIdentity(identity *consensus.RaftIdentity) error { 441 dbTx := cdb.store.NewTx() 442 defer dbTx.Discard() 443 444 logger.Info().Str("id", identity.ToString()).Msg("save raft identity") 445 446 var val bytes.Buffer 447 448 enc := gob.NewEncoder(&val) 449 if err := enc.Encode(identity); err != nil { 450 return ErrEncodeRaftIdentity 451 } 452 453 dbTx.Set(raftIdentityKey, val.Bytes()) 454 dbTx.Commit() 455 456 return nil 457 } 458 459 func (cdb *ChainDB) GetIdentity() (*consensus.RaftIdentity, error) { 460 data := cdb.store.Get(raftIdentityKey) 461 if len(data) == 0 { 462 return nil, nil 463 } 464 465 var id consensus.RaftIdentity 466 var b bytes.Buffer 467 b.Write(data) 468 decoder := gob.NewDecoder(&b) 469 if err := decoder.Decode(&id); err != nil { 470 return nil, ErrDecodeRaftIdentity 471 } 472 473 logger.Info().Str("id", types.Uint64ToHexaString(id.ID)).Str("name", id.Name).Str("peerid", id.PeerID).Msg("get raft identity") 474 475 return &id, nil 476 } 477 478 func (cdb *ChainDB) WriteConfChangeProgress(id uint64, progress *types.ConfChangeProgress) error { 479 dbTx := cdb.store.NewTx() 480 defer dbTx.Discard() 481 482 if err := cdb.writeConfChangeProgress(dbTx, id, progress); err != nil { 483 return err 484 } 485 486 dbTx.Commit() 487 488 return nil 489 } 490 491 func getConfChangeProgressKey(idx uint64) []byte { 492 var key bytes.Buffer 493 key.Write(raftConfChangeProgressPrefix) 494 l := make([]byte, 8) 495 binary.LittleEndian.PutUint64(l[:], idx) 496 key.Write(l) 497 return key.Bytes() 498 } 499 500 func (cdb *ChainDB) writeConfChangeProgress(dbTx db.Transaction, id uint64, progress *types.ConfChangeProgress) error { 501 if id == 0 { 502 // it's for intial member's for startup 503 return nil 504 } 505 506 ccKey := getConfChangeProgressKey(id) 507 508 // Make CC Data 509 var data []byte 510 var err error 511 512 if data, err = proto.Marshal(progress); err != nil { 513 logger.Error().Msg("failed to marshal confChangeProgress") 514 return err 515 } 516 517 dbTx.Set(ccKey, data) 518 519 return nil 520 } 521 522 func (cdb *ChainDB) GetConfChangeProgress(id uint64) (*types.ConfChangeProgress, error) { 523 ccKey := getConfChangeProgressKey(id) 524 525 data := cdb.store.Get(ccKey) 526 if len(data) == 0 { 527 return nil, nil 528 } 529 530 var progress types.ConfChangeProgress 531 532 if err := proto.Unmarshal(data, &progress); err != nil { 533 logger.Error().Msg("failed to unmarshal raft state") 534 return nil, ErrInvalidCCProgress 535 } 536 537 logger.Info().Uint64("id", id).Str("status", progress.ToString()).Msg("get conf change status") 538 539 return &progress, nil 540 }