github.com/turingchain2020/turingchain@v1.1.21/system/p2p/dht/protocol/p2pstore/store.go (about)

     1  package p2pstore
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"fmt"
     7  	"time"
     8  
     9  	types2 "github.com/turingchain2020/turingchain/system/p2p/dht/types"
    10  	"github.com/turingchain2020/turingchain/types"
    11  	kb "github.com/libp2p/go-libp2p-kbucket"
    12  )
    13  
    14  // prefix key and const parameters
    15  const (
    16  	LocalChunkInfoKey = "local-chunk-info"
    17  	ChunkNameSpace    = "chunk"
    18  	ChunkPrefix       = "chunk-"
    19  	AlphaValue        = 3
    20  )
    21  
    22  //LocalChunkInfo wraps local chunk key with time.
    23  type LocalChunkInfo struct {
    24  	*types.ChunkInfoMsg
    25  	Time time.Time
    26  }
    27  
    28  // 保存chunk到本地p2pStore,同时更新本地chunk列表
    29  func (p *Protocol) addChunkBlock(info *types.ChunkInfoMsg, bodys *types.BlockBodys) error {
    30  	if int64(len(bodys.Items)) != info.End-info.Start+1 {
    31  		return types2.ErrLength
    32  	}
    33  	for i := info.Start; i <= info.End; i++ {
    34  		key := genChunkDBKey(i)
    35  		if err := p.DB.Set(key, types.Encode(bodys.Items[i-info.Start])); err != nil {
    36  			return err
    37  		}
    38  	}
    39  	return p.addLocalChunkInfo(info)
    40  }
    41  
    42  // 更新本地chunk保存时间,只更新索引即可
    43  func (p *Protocol) updateChunk(req *types.ChunkInfoMsg) error {
    44  	mapKey := hex.EncodeToString(req.ChunkHash)
    45  	p.localChunkInfoMutex.Lock()
    46  	defer p.localChunkInfoMutex.Unlock()
    47  	if info, ok := p.localChunkInfo[mapKey]; ok {
    48  		info.Time = time.Now()
    49  		p.localChunkInfo[mapKey] = info
    50  		return nil
    51  	}
    52  
    53  	return types2.ErrNotFound
    54  }
    55  
    56  func (p *Protocol) deleteChunkBlock(hash []byte) error {
    57  	err := p.deleteLocalChunkInfo(hash)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	batch := p.DB.NewBatch(false)
    62  	it := p.DB.Iterator(hash, append(hash, ':'+1), false)
    63  	defer it.Close()
    64  	for it.Next(); it.Valid(); it.Next() {
    65  		batch.Delete(it.Key())
    66  	}
    67  	return batch.Write()
    68  }
    69  
    70  // 获取本地chunk数据
    71  //	本地不存在,返回not found
    72  //  本地存在:
    73  //		数据未过期:返回数据
    74  //		数据已过期:返回数据,然后从数据库删除该数据
    75  func (p *Protocol) getChunkBlock(req *types.ChunkInfoMsg) (*types.BlockBodys, error) {
    76  
    77  	if _, ok := p.getChunkInfoByHash(req.ChunkHash); !ok {
    78  		return nil, types2.ErrNotFound
    79  	}
    80  	var bodys []*types.BlockBody
    81  	it := p.DB.Iterator(genChunkDBKey(req.Start), genChunkDBKey(req.End+1), false)
    82  	defer it.Close()
    83  	for it.Next(); it.Valid(); it.Next() {
    84  		var body types.BlockBody
    85  		if err := types.Decode(it.Value(), &body); err != nil {
    86  			return nil, err
    87  		}
    88  		bodys = append(bodys, &body)
    89  	}
    90  	if int64(len(bodys)) != req.End-req.Start+1 {
    91  		return nil, types2.ErrLength
    92  	}
    93  
    94  	return &types.BlockBodys{Items: bodys}, nil
    95  }
    96  
    97  // 保存一个本地chunk hash列表,用于遍历本地数据
    98  func (p *Protocol) addLocalChunkInfo(info *types.ChunkInfoMsg) error {
    99  	p.localChunkInfoMutex.Lock()
   100  	defer p.localChunkInfoMutex.Unlock()
   101  
   102  	p.localChunkInfo[hex.EncodeToString(info.ChunkHash)] = LocalChunkInfo{
   103  		ChunkInfoMsg: info,
   104  		Time:         time.Now(),
   105  	}
   106  	return p.saveLocalChunkInfoMap(p.localChunkInfo)
   107  }
   108  
   109  func (p *Protocol) deleteLocalChunkInfo(hash []byte) error {
   110  	p.localChunkInfoMutex.Lock()
   111  	defer p.localChunkInfoMutex.Unlock()
   112  	delete(p.localChunkInfo, hex.EncodeToString(hash))
   113  	return p.saveLocalChunkInfoMap(p.localChunkInfo)
   114  }
   115  
   116  func (p *Protocol) initLocalChunkInfoMap() {
   117  	p.localChunkInfoMutex.Lock()
   118  	defer p.localChunkInfoMutex.Unlock()
   119  	p.localChunkInfo = make(map[string]LocalChunkInfo)
   120  	value, err := p.DB.Get([]byte(LocalChunkInfoKey))
   121  	if err != nil {
   122  		log.Error("initLocalChunkInfoMap", "error", err)
   123  		return
   124  	}
   125  
   126  	err = json.Unmarshal(value, &p.localChunkInfo)
   127  	if err != nil {
   128  		panic(err)
   129  	}
   130  	for k, v := range p.localChunkInfo {
   131  		info := v
   132  		info.Time = time.Now()
   133  		p.localChunkInfo[k] = info
   134  	}
   135  }
   136  
   137  func (p *Protocol) saveLocalChunkInfoMap(m map[string]LocalChunkInfo) error {
   138  	value, err := json.Marshal(m)
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	return p.DB.Set([]byte(LocalChunkInfoKey), value)
   144  }
   145  
   146  func (p *Protocol) getChunkInfoByHash(hash []byte) (LocalChunkInfo, bool) {
   147  	p.localChunkInfoMutex.RLock()
   148  	defer p.localChunkInfoMutex.RUnlock()
   149  	info, ok := p.localChunkInfo[hex.EncodeToString(hash)]
   150  	return info, ok
   151  }
   152  
   153  // 适配libp2p,按路径格式生成数据的key值,便于区分多种数据类型的命名空间,以及key值合法性校验
   154  func genChunkNameSpaceKey(hash []byte) string {
   155  	return fmt.Sprintf("/%s/%s", ChunkNameSpace, hex.EncodeToString(hash))
   156  }
   157  
   158  func genDHTID(chunkHash []byte) kb.ID {
   159  	return kb.ConvertKey(genChunkNameSpaceKey(chunkHash))
   160  }
   161  
   162  func formatHeight(height int64) string {
   163  	return fmt.Sprintf("%012d", height)
   164  }
   165  
   166  func genChunkDBKey(height int64) []byte {
   167  	var key []byte
   168  	key = append(key, ChunkPrefix...)
   169  	key = append(key, formatHeight(height)...)
   170  	return key
   171  }