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  }