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 }