github.com/aergoio/aergo@v1.3.1/chain/chaindb.go (about) 1 /** 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package chain 7 8 import ( 9 "bytes" 10 "encoding/binary" 11 "encoding/gob" 12 "encoding/json" 13 "errors" 14 "fmt" 15 "sync/atomic" 16 17 "github.com/aergoio/aergo-lib/db" 18 "github.com/aergoio/aergo/consensus" 19 "github.com/aergoio/aergo/internal/common" 20 "github.com/aergoio/aergo/internal/enc" 21 "github.com/aergoio/aergo/types" 22 "github.com/gogo/protobuf/proto" 23 ) 24 25 const ( 26 chainDBName = "chain" 27 genesisKey = chainDBName + ".genesisInfo" 28 genesisBalanceKey = chainDBName + ".genesisBalance" 29 ) 30 31 var ( 32 // ErrNoChainDB reports chaindb is not prepared. 33 ErrNoChainDB = fmt.Errorf("chaindb not prepared") 34 ErrorLoadBestBlock = errors.New("failed to load latest block from DB") 35 ErrCantDropGenesis = errors.New("can't drop genesis block") 36 ErrTooBigResetHeight = errors.New("reset height is too big") 37 ErrWalNoHardState = errors.New("not exist hard state") 38 ErrInvalidHardState = errors.New("invalid hard state") 39 ErrInvalidRaftSnapshot = errors.New("invalid raft snapshot") 40 ErrInvalidCCProgress = errors.New("invalid conf change progress") 41 42 latestKey = []byte(chainDBName + ".latest") 43 receiptsPrefix = []byte("r") 44 45 raftIdentityKey = []byte("r_identity") 46 raftStateKey = []byte("r_state") 47 raftSnapKey = []byte("r_snap") 48 raftEntryLastIdxKey = []byte("r_last") 49 raftEntryPrefix = []byte("r_entry.") 50 raftEntryInvertPrefix = []byte("r_inv.") 51 raftConfChangeProgressPrefix = []byte("r_ccstatus.") 52 ) 53 54 // ErrNoBlock reports there is no such a block with id (hash or block number). 55 type ErrNoBlock struct { 56 id interface{} 57 } 58 59 func (e ErrNoBlock) Error() string { 60 var idStr string 61 62 switch id := e.id.(type) { 63 case []byte: 64 idStr = fmt.Sprintf("blockHash=%v", enc.ToString(id)) 65 default: 66 idStr = fmt.Sprintf("blockNo=%v", id) 67 } 68 69 return fmt.Sprintf("block not found: %s", idStr) 70 } 71 72 type ChainDB struct { 73 cc consensus.ChainConsensus 74 75 latest atomic.Value //types.BlockNo 76 bestBlock atomic.Value // *types.Block 77 // blocks []*types.Block 78 store db.DB 79 } 80 81 func NewChainDB() *ChainDB { 82 // logger.SetLevel("debug") 83 cdb := &ChainDB{ 84 //blocks: []*types.Block{}, 85 } 86 cdb.latest.Store(types.BlockNo(0)) 87 88 return cdb 89 } 90 91 // NewTx returns a new chain DB Transaction. 92 func (cdb *ChainDB) NewTx() db.Transaction { 93 return cdb.store.NewTx() 94 } 95 96 func (cdb *ChainDB) Init(dbType string, dataDir string) error { 97 if cdb.store == nil { 98 logger.Info().Str("datadir", dataDir).Msg("chain database initialized") 99 dbPath := common.PathMkdirAll(dataDir, chainDBName) 100 cdb.store = db.NewDB(db.ImplType(dbType), dbPath) 101 } 102 103 // load data 104 if err := cdb.loadChainData(); err != nil { 105 return err 106 } 107 108 // recover from reorg marker 109 if err := cdb.recover(); err != nil { 110 logger.Error().Err(err).Msg("failed to recover chain database from crash") 111 return err 112 } 113 114 // // if empty then create new genesis block 115 // // if cdb.getBestBlockNo() == 0 && len(cdb.blocks) == 0 { 116 // blockIdx := types.BlockNoToBytes(0) 117 // blockHash := cdb.store.Get(blockIdx) 118 // if cdb.getBestBlockNo() == 0 && (blockHash == nil || len(blockHash) == 0) { 119 // cdb.generateGenesisBlock(seed) 120 // } 121 return nil 122 } 123 124 func (cdb *ChainDB) recover() error { 125 marker, err := cdb.getReorgMarker() 126 if err != nil { 127 return err 128 } 129 130 if marker == nil { 131 return nil 132 } 133 134 if err := marker.RecoverChainMapping(cdb); err != nil { 135 return err 136 } 137 138 return nil 139 } 140 141 // ResetBest reset best block of chain db manually remove blocks from original 142 // best to resetNo. 143 // 144 // *Caution*: This API is dangerous. It must be used for test blockchain only. 145 func (cdb *ChainDB) ResetBest(resetNo types.BlockNo) error { 146 logger.Info().Uint64("reset height", resetNo).Msg("reset best block") 147 148 best := cdb.getBestBlockNo() 149 if best <= resetNo { 150 logger.Error().Uint64("best", best).Uint64("reset", resetNo).Msg("too big reset height") 151 return ErrTooBigResetHeight 152 } 153 154 for curNo := best; curNo > resetNo; curNo-- { 155 if err := cdb.dropBlock(curNo); err != nil { 156 logger.Error().Err(err).Uint64("no", curNo).Msg("failed to drop block") 157 return err 158 } 159 } 160 161 logger.Info().Msg("succeed to reset best block") 162 163 return nil 164 } 165 166 type ErrDropBlock struct { 167 pos int 168 } 169 170 func (err *ErrDropBlock) Error() string { 171 return fmt.Sprintf("failed to drop block: pos=%d", err.pos) 172 } 173 174 func (cdb *ChainDB) checkBlockDropped(dropBlock *types.Block) error { 175 no := dropBlock.GetHeader().GetBlockNo() 176 hash := dropBlock.GetHash() 177 txLen := len(dropBlock.GetBody().GetTxs()) 178 179 //check receipt 180 var err error 181 182 if txLen > 0 { 183 if _, err = cdb.getReceipt(hash, no, 0); err == nil { 184 return &ErrDropBlock{pos: 0} 185 } 186 if _, err = cdb.getReceipt(hash, no, int32(txLen-1)); err == nil { 187 return &ErrDropBlock{pos: 1} 188 } 189 } 190 191 //check tx 192 for _, tx := range dropBlock.GetBody().GetTxs() { 193 if _, _, err = cdb.getTx(tx.GetHash()); err == nil { 194 return &ErrDropBlock{pos: 2} 195 } 196 } 197 198 //check hash/block 199 if _, err = cdb.getBlock(hash); err == nil { 200 return &ErrDropBlock{pos: 3} 201 } 202 203 //check no/hash 204 if _, err = cdb.getHashByNo(no); err == nil { 205 return &ErrDropBlock{pos: 4} 206 } 207 208 return nil 209 } 210 211 func (cdb *ChainDB) Close() { 212 if cdb.store != nil { 213 cdb.store.Close() 214 } 215 return 216 } 217 218 // Get returns the value corresponding to key from the chain DB. 219 func (cdb *ChainDB) Get(key []byte) []byte { 220 return cdb.store.Get(key) 221 } 222 223 func (cdb *ChainDB) GetBestBlock() (*types.Block, error) { 224 //logger.Debug().Uint64("blockno", blockNo).Msg("get best block") 225 var block *types.Block 226 227 aopv := cdb.bestBlock.Load() 228 229 if aopv != nil { 230 block = aopv.(*types.Block) 231 } 232 233 return block, nil 234 } 235 236 func (cdb *ChainDB) loadChainData() error { 237 latestBytes := cdb.store.Get(latestKey) 238 if latestBytes == nil || len(latestBytes) == 0 { 239 return nil 240 } 241 latestNo := types.BlockNoFromBytes(latestBytes) 242 /* TODO: just checking DB 243 cdb.blocks = make([]*types.Block, latestNo+1) 244 for i := uint32(0); i <= latestNo; i++ { 245 blockIdx := types.BlockNoToBytes(i) 246 buf := types.Block{} 247 err := cdb.loadData(blockIdx, &buf) 248 if err != nil { 249 return err 250 } 251 bHash := buf.CalculateBlockHash() 252 if buf.Hash == nil { 253 buf.Hash = bHash 254 } else if !bytes.Equal(buf.Hash, bHash) { 255 return fmt.Errorf("invalid Block Hash: hash=%s, check=%s", 256 enc.ToString(buf.Hash), enc.ToString(bHash)) 257 } 258 for _, v := range buf.Body.Txs { 259 tHash := v.CalculateTxHash() 260 if v.Hash == nil { 261 v.Hash = tHash 262 } else if !bytes.Equal(v.Hash, tHash) { 263 return fmt.Errorf("invalid Transaction Hash: hash=%s, check=%s", 264 enc.ToString(v.Hash), enc.ToString(tHash)) 265 } 266 } 267 cdb.blocks[i] = &buf 268 } 269 */ 270 latestBlock, err := cdb.GetBlockByNo(latestNo) 271 if err != nil { 272 return ErrorLoadBestBlock 273 } 274 cdb.setLatest(latestBlock) 275 276 // skips := true 277 // for i, _ := range cdb.blocks { 278 // if i > 3 && i+3 <= cdb.getBestBlockNo() { 279 // if skips { 280 // skips = false 281 // //logger.Info(" ...") 282 // } 283 // continue 284 // } 285 // //logger.Info("- loaded:", i, ToJSON(v)) 286 // } 287 return nil 288 } 289 290 func (cdb *ChainDB) loadData(key []byte, pb proto.Message) error { 291 buf := cdb.store.Get(key) 292 if buf == nil || len(buf) == 0 { 293 return fmt.Errorf("failed to load data: key=%v", key) 294 } 295 //logger.Debugf(" loadData: key=%d, len=%d, val=%s\n", Btoi(key), len(buf), enc.ToString(buf)) 296 err := proto.Unmarshal(buf, pb) 297 if err != nil { 298 return fmt.Errorf("failed to unmarshal: key=%v, len=%d", key, len(buf)) 299 } 300 //logger.Debug(" loaded: ", ToJSON(pb)) 301 return nil 302 } 303 304 func (cdb *ChainDB) addGenesisBlock(genesis *types.Genesis) error { 305 block := genesis.Block() 306 307 tx := cdb.store.NewTx() 308 309 if len(block.Hash) == 0 { 310 block.BlockID() 311 } 312 313 cdb.connectToChain(tx, block, false) 314 tx.Set([]byte(genesisKey), genesis.Bytes()) 315 if totalBalance := genesis.TotalBalance(); totalBalance != nil { 316 tx.Set([]byte(genesisBalanceKey), totalBalance.Bytes()) 317 } 318 319 tx.Commit() 320 321 logger.Info().Str("chain id", genesis.ID.ToJSON()).Str("hash", block.ID()).Msg("Genesis Block Added") 322 323 //logger.Info().Str("chain id", genesis.ID.ToJSON()).Str("chain id (raw)", 324 // enc.ToString(block.GetHeader().GetChainID())).Msg("Genesis Block Added") 325 return nil 326 } 327 328 // GetGenesisInfo returns Genesis info, which is read from cdb. 329 func (cdb *ChainDB) GetGenesisInfo() *types.Genesis { 330 if b := cdb.Get([]byte(genesisKey)); len(b) != 0 { 331 genesis := types.GetGenesisFromBytes(b) 332 if block, err := cdb.GetBlockByNo(0); err == nil { 333 genesis.SetBlock(block) 334 335 // genesis.ID is overwritten by the genesis block's chain 336 // id. Prefer the latter since it is sort of protected the block 337 // chain system (all the chaild blocks connected to the genesis 338 // block). 339 rawCid := genesis.Block().GetHeader().GetChainID() 340 if len(rawCid) > 0 { 341 cid := types.NewChainID() 342 if err := cid.Read(rawCid); err == nil { 343 genesis.ID = *cid 344 } 345 } 346 347 } 348 349 if v := cdb.Get([]byte(genesisBalanceKey)); len(v) != 0 { 350 genesis.SetTotalBalance(v) 351 } 352 353 return genesis 354 } 355 return nil 356 } 357 358 func (cdb *ChainDB) setLatest(newBestBlock *types.Block) (oldLatest types.BlockNo) { 359 oldLatest = cdb.getBestBlockNo() 360 361 newLatest := types.BlockNo(newBestBlock.GetHeader().GetBlockNo()) 362 cdb.latest.Store(newLatest) 363 cdb.bestBlock.Store(newBestBlock) 364 365 logger.Debug().Uint64("old", oldLatest).Uint64("new", newLatest).Msg("update latest block") 366 367 return 368 } 369 370 func (cdb *ChainDB) connectToChain(dbtx db.Transaction, block *types.Block, skipAdd bool) (oldLatest types.BlockNo) { 371 blockNo := block.GetHeader().GetBlockNo() 372 blockIdx := types.BlockNoToBytes(blockNo) 373 374 if !skipAdd { 375 if err := cdb.addBlock(dbtx, block); err != nil { 376 return 0 377 } 378 } 379 380 // Update best block hash 381 dbtx.Set(latestKey, blockIdx) 382 dbtx.Set(blockIdx, block.BlockHash()) 383 384 // Save the last consensus status. 385 if cdb.cc != nil { 386 if err := cdb.cc.Save(dbtx); err != nil { 387 logger.Error().Err(err).Msg("failed to save DPoS status") 388 } 389 } 390 391 oldLatest = cdb.setLatest(block) 392 393 logger.Debug().Msg("connected block to mainchain") 394 395 return 396 } 397 398 func (cdb *ChainDB) swapChainMapping(newBlocks []*types.Block) error { 399 oldNo := cdb.getBestBlockNo() 400 newNo := newBlocks[0].GetHeader().GetBlockNo() 401 402 if oldNo >= newNo { 403 logger.Error().Uint64("old", oldNo).Uint64("new", newNo). 404 Msg("New chain is not longger than old chain") 405 return ErrInvalidSwapChain 406 } 407 408 var blockIdx []byte 409 410 bulk := cdb.store.NewBulk() 411 defer bulk.DiscardLast() 412 413 //make newTx because of batchsize limit of DB 414 for i := len(newBlocks) - 1; i >= 0; i-- { 415 block := newBlocks[i] 416 blockIdx = types.BlockNoToBytes(block.GetHeader().GetBlockNo()) 417 418 bulk.Set(blockIdx, block.BlockHash()) 419 } 420 421 bulk.Set(latestKey, blockIdx) 422 423 // Save the last consensus status. 424 cdb.cc.Save(bulk) 425 426 bulk.Flush() 427 428 cdb.setLatest(newBlocks[0]) 429 430 return nil 431 } 432 433 func (cdb *ChainDB) isMainChain(block *types.Block) (bool, error) { 434 blockNo := block.GetHeader().GetBlockNo() 435 bestNo := cdb.getBestBlockNo() 436 if blockNo > 0 && blockNo != bestNo+1 { 437 logger.Debug().Uint64("no", blockNo).Uint64("latest", bestNo).Msg("block is branch") 438 439 return false, nil 440 } 441 442 prevHash := block.GetHeader().GetPrevBlockHash() 443 latestHash, err := cdb.getHashByNo(cdb.getBestBlockNo()) 444 if err != nil { //need assertion 445 return false, fmt.Errorf("failed to getting block hash by no(%v)", cdb.getBestBlockNo()) 446 } 447 448 isMainChain := bytes.Equal(prevHash, latestHash) 449 450 logger.Debug().Bool("isMainChain", isMainChain).Msg("check if block is in main chain") 451 452 return isMainChain, nil 453 } 454 455 type txInfo struct { 456 blockHash []byte 457 idx int 458 } 459 460 func (cdb *ChainDB) addTxsOfBlock(dbTx *db.Transaction, txs []*types.Tx, blockHash []byte) error { 461 if err := TestDebugger.Check(DEBUG_CHAIN_STOP, 4, nil); err != nil { 462 return err 463 } 464 465 for i, txEntry := range txs { 466 if err := cdb.addTx(dbTx, txEntry, blockHash, i); err != nil { 467 logger.Error().Err(err).Str("hash", enc.ToString(blockHash)).Int("txidx", i). 468 Msg("failed to add tx") 469 470 return err 471 } 472 } 473 474 return nil 475 } 476 477 // stor tx info to DB 478 func (cdb *ChainDB) addTx(dbtx *db.Transaction, tx *types.Tx, blockHash []byte, idx int) error { 479 txidx := types.TxIdx{ 480 BlockHash: blockHash, 481 Idx: int32(idx), 482 } 483 txidxbytes, err := proto.Marshal(&txidx) 484 if err != nil { 485 return err 486 } 487 (*dbtx).Set(tx.Hash, txidxbytes) 488 return nil 489 } 490 491 func (cdb *ChainDB) deleteTx(dbtx *db.Transaction, tx *types.Tx) { 492 (*dbtx).Delete(tx.Hash) 493 } 494 495 // store block info to DB 496 func (cdb *ChainDB) addBlock(dbtx db.Transaction, block *types.Block) error { 497 blockNo := block.GetHeader().GetBlockNo() 498 499 // TODO: Is it possible? 500 // if blockNo != 0 && isMainChain && cdb.getBestBlockNo()+1 != blockNo { 501 // return fmt.Errorf("failed to add block(%d,%v). blkno != latestNo(%d) + 1", blockNo, 502 // block.BlockHash(), cdb.getBestBlockNo()) 503 // } 504 // FIXME: blockNo 0 exception handling 505 // assumption: not an orphan 506 // fork can be here 507 logger.Debug().Uint64("blockNo", blockNo).Msg("add block to db") 508 blockBytes, err := proto.Marshal(block) 509 if err != nil { 510 logger.Error().Err(err).Uint64("no", blockNo).Str("hash", block.ID()).Msg("failed to add block") 511 return err 512 } 513 514 //add block 515 dbtx.Set(block.BlockHash(), blockBytes) 516 517 return nil 518 } 519 520 // drop block from DB 521 func (cdb *ChainDB) dropBlock(dropNo types.BlockNo) error { 522 logger.Info().Uint64("no", dropNo).Msg("drop block") 523 524 dbTx := cdb.NewTx() 525 defer dbTx.Discard() 526 527 if dropNo <= 0 { 528 return ErrCantDropGenesis 529 } 530 531 dropBlock, err := cdb.GetBlockByNo(dropNo) 532 if err != nil { 533 return err 534 } 535 536 // remove tx mapping 537 for _, tx := range dropBlock.GetBody().GetTxs() { 538 cdb.deleteTx(&dbTx, tx) 539 } 540 541 // remove receipt 542 cdb.deleteReceipts(&dbTx, dropBlock.BlockHash(), dropBlock.BlockNo()) 543 544 // remove (hash/block) 545 dbTx.Delete(dropBlock.BlockHash()) 546 547 // remove (no/hash) 548 dropIdx := types.BlockNoToBytes(dropNo) 549 newLatestIdx := types.BlockNoToBytes(dropNo - 1) 550 dbTx.Delete(dropIdx) 551 552 // update latest 553 dbTx.Set(latestKey, newLatestIdx) 554 555 dbTx.Commit() 556 557 prevBlock, err := cdb.GetBlockByNo(dropNo - 1) 558 if err != nil { 559 return err 560 } 561 562 cdb.setLatest(prevBlock) 563 564 if err = cdb.checkBlockDropped(dropBlock); err != nil { 565 logger.Error().Err(err).Msg("block meta is not dropped") 566 return err 567 } 568 return nil 569 } 570 571 func (cdb *ChainDB) getBestBlockNo() (latestNo types.BlockNo) { 572 aopv := cdb.latest.Load() 573 if aopv != nil { 574 latestNo = aopv.(types.BlockNo) 575 } else { 576 panic("ChainDB:latest is nil") 577 } 578 return latestNo 579 } 580 581 // GetBlockByNo returns the block with its block number as blockNo. 582 func (cdb *ChainDB) GetBlockByNo(blockNo types.BlockNo) (*types.Block, error) { 583 blockHash, err := cdb.getHashByNo(blockNo) 584 if err != nil { 585 return nil, err 586 } 587 //logger.Debugf("getblockbyNo No=%d Hash=%v", blockNo, enc.ToString(blockHash)) 588 return cdb.getBlock(blockHash) 589 } 590 591 func (cdb *ChainDB) GetBlock(blockHash []byte) (*types.Block, error) { 592 return cdb.getBlock(blockHash) 593 } 594 595 func (cdb *ChainDB) GetHashByNo(blockNo types.BlockNo) ([]byte, error) { 596 return cdb.getHashByNo(blockNo) 597 } 598 599 func (cdb *ChainDB) getBlock(blockHash []byte) (*types.Block, error) { 600 if blockHash == nil { 601 return nil, fmt.Errorf("block hash invalid(nil)") 602 } 603 buf := types.Block{} 604 err := cdb.loadData(blockHash, &buf) 605 if err != nil || !bytes.Equal(buf.Hash, blockHash) { 606 return nil, &ErrNoBlock{id: blockHash} 607 } 608 609 //logger.Debugf("getblockbyHash Hash=%v", enc.ToString(blockHash)) 610 return &buf, nil 611 } 612 613 func (cdb *ChainDB) getHashByNo(blockNo types.BlockNo) ([]byte, error) { 614 blockIdx := types.BlockNoToBytes(blockNo) 615 if cdb.store == nil { 616 return nil, ErrNoChainDB 617 } 618 blockHash := cdb.store.Get(blockIdx) 619 if len(blockHash) == 0 { 620 return nil, &ErrNoBlock{id: blockNo} 621 } 622 return blockHash, nil 623 } 624 625 func (cdb *ChainDB) getTx(txHash []byte) (*types.Tx, *types.TxIdx, error) { 626 txIdx := &types.TxIdx{} 627 628 err := cdb.loadData(txHash, txIdx) 629 if err != nil { 630 return nil, nil, fmt.Errorf("tx not found: txHash=%v", enc.ToString(txHash)) 631 } 632 block, err := cdb.getBlock(txIdx.BlockHash) 633 if err != nil { 634 return nil, nil, &ErrNoBlock{txIdx.BlockHash} 635 } 636 txs := block.GetBody().GetTxs() 637 if txIdx.Idx >= int32(len(txs)) { 638 return nil, nil, fmt.Errorf("wrong tx idx: %d", txIdx.Idx) 639 } 640 tx := txs[txIdx.Idx] 641 logger.Debug().Str("hash", enc.ToString(txHash)).Msg("getTx") 642 643 return tx, txIdx, nil 644 } 645 646 func (cdb *ChainDB) getReceipt(blockHash []byte, blockNo types.BlockNo, idx int32) (*types.Receipt, error) { 647 storedReceipts, err := cdb.getReceipts(blockHash, blockNo) 648 if err != nil { 649 return nil, err 650 } 651 receipts := storedReceipts.Get() 652 653 if idx < 0 || idx > int32(len(receipts)) { 654 return nil, fmt.Errorf("cannot find a receipt: invalid index (%d)", idx) 655 } 656 r := receipts[idx] 657 r.SetMemoryInfo(blockHash, blockNo, idx) 658 return receipts[idx], nil 659 } 660 661 func (cdb *ChainDB) getReceipts(blockHash []byte, blockNo types.BlockNo) (*types.Receipts, error) { 662 data := cdb.store.Get(receiptsKey(blockHash, blockNo)) 663 if len(data) == 0 { 664 return nil, errors.New("cannot find a receipt") 665 } 666 var b bytes.Buffer 667 b.Write(data) 668 var receipts types.Receipts 669 decoder := gob.NewDecoder(&b) 670 decoder.Decode(&receipts) 671 672 return &receipts, nil 673 } 674 675 type ChainTree struct { 676 Tree []ChainInfo 677 } 678 type ChainInfo struct { 679 Height types.BlockNo 680 Hash string 681 } 682 683 func (cdb *ChainDB) GetChainTree() ([]byte, error) { 684 tree := make([]ChainInfo, 0) 685 var i uint64 686 for i = 0; i < cdb.getBestBlockNo(); i++ { 687 hash, _ := cdb.getHashByNo(i) 688 tree = append(tree, ChainInfo{ 689 Height: i, 690 Hash: enc.ToString(hash), 691 }) 692 logger.Info().Str("hash", enc.ToString(hash)).Msg("GetChainTree") 693 } 694 jsonBytes, err := json.Marshal(tree) 695 if err != nil { 696 logger.Info().Msg("GetChainTree failed") 697 } 698 return jsonBytes, nil 699 } 700 701 func (cdb *ChainDB) writeReceipts(blockHash []byte, blockNo types.BlockNo, receipts *types.Receipts) { 702 dbTx := cdb.store.NewTx() 703 defer dbTx.Discard() 704 705 var val bytes.Buffer 706 gob := gob.NewEncoder(&val) 707 gob.Encode(receipts) 708 709 dbTx.Set(receiptsKey(blockHash, blockNo), val.Bytes()) 710 711 dbTx.Commit() 712 } 713 714 func (cdb *ChainDB) deleteReceipts(dbTx *db.Transaction, blockHash []byte, blockNo types.BlockNo) { 715 (*dbTx).Delete(receiptsKey(blockHash, blockNo)) 716 } 717 718 func receiptsKey(blockHash []byte, blockNo types.BlockNo) []byte { 719 var key bytes.Buffer 720 key.Write(receiptsPrefix) 721 key.Write(blockHash) 722 l := make([]byte, 8) 723 binary.LittleEndian.PutUint64(l[:], blockNo) 724 key.Write(l) 725 return key.Bytes() 726 } 727 728 func (cdb *ChainDB) writeReorgMarker(marker *ReorgMarker) error { 729 dbTx := cdb.store.NewTx() 730 defer dbTx.Discard() 731 732 val, err := marker.toBytes() 733 if err != nil { 734 logger.Error().Err(err).Msg("failed to serialize reorg marker") 735 return err 736 } 737 738 dbTx.Set(reorgKey, val) 739 740 dbTx.Commit() 741 return nil 742 } 743 744 func (cdb *ChainDB) deleteReorgMarker() { 745 dbTx := cdb.store.NewTx() 746 defer dbTx.Discard() 747 748 dbTx.Delete(reorgKey) 749 750 dbTx.Commit() 751 } 752 753 func (cdb *ChainDB) getReorgMarker() (*ReorgMarker, error) { 754 data := cdb.store.Get(reorgKey) 755 if len(data) == 0 { 756 return nil, nil 757 } 758 759 var marker ReorgMarker 760 var b bytes.Buffer 761 b.Write(data) 762 decoder := gob.NewDecoder(&b) 763 err := decoder.Decode(&marker) 764 765 return &marker, err 766 } 767 768 // implement ChainWAL interface 769 func (cdb *ChainDB) IsNew() bool { 770 //TODO 771 return true 772 }