github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/storage/mock/db/db.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package db implements a mock store that keeps all chunk data in LevelDB database. 18 package db 19 20 import ( 21 "archive/tar" 22 "bytes" 23 "encoding/json" 24 "io" 25 "io/ioutil" 26 27 "github.com/syndtr/goleveldb/leveldb" 28 "github.com/syndtr/goleveldb/leveldb/util" 29 30 "github.com/FusionFoundation/efsn/common" 31 "github.com/FusionFoundation/efsn/swarm/storage/mock" 32 ) 33 34 // GlobalStore contains the LevelDB database that is storing 35 // chunk data for all swarm nodes. 36 // Closing the GlobalStore with Close method is required to 37 // release resources used by the database. 38 type GlobalStore struct { 39 db *leveldb.DB 40 } 41 42 // NewGlobalStore creates a new instance of GlobalStore. 43 func NewGlobalStore(path string) (s *GlobalStore, err error) { 44 db, err := leveldb.OpenFile(path, nil) 45 if err != nil { 46 return nil, err 47 } 48 return &GlobalStore{ 49 db: db, 50 }, nil 51 } 52 53 // Close releases the resources used by the underlying LevelDB. 54 func (s *GlobalStore) Close() error { 55 return s.db.Close() 56 } 57 58 // NewNodeStore returns a new instance of NodeStore that retrieves and stores 59 // chunk data only for a node with address addr. 60 func (s *GlobalStore) NewNodeStore(addr common.Address) *mock.NodeStore { 61 return mock.NewNodeStore(addr, s) 62 } 63 64 // Get returns chunk data if the chunk with key exists for node 65 // on address addr. 66 func (s *GlobalStore) Get(addr common.Address, key []byte) (data []byte, err error) { 67 has, err := s.db.Has(nodeDBKey(addr, key), nil) 68 if err != nil { 69 return nil, mock.ErrNotFound 70 } 71 if !has { 72 return nil, mock.ErrNotFound 73 } 74 data, err = s.db.Get(dataDBKey(key), nil) 75 if err == leveldb.ErrNotFound { 76 err = mock.ErrNotFound 77 } 78 return 79 } 80 81 // Put saves the chunk data for node with address addr. 82 func (s *GlobalStore) Put(addr common.Address, key []byte, data []byte) error { 83 batch := new(leveldb.Batch) 84 batch.Put(nodeDBKey(addr, key), nil) 85 batch.Put(dataDBKey(key), data) 86 return s.db.Write(batch, nil) 87 } 88 89 // HasKey returns whether a node with addr contains the key. 90 func (s *GlobalStore) HasKey(addr common.Address, key []byte) bool { 91 has, err := s.db.Has(nodeDBKey(addr, key), nil) 92 if err != nil { 93 has = false 94 } 95 return has 96 } 97 98 // Import reads tar archive from a reader that contains exported chunk data. 99 // It returns the number of chunks imported and an error. 100 func (s *GlobalStore) Import(r io.Reader) (n int, err error) { 101 tr := tar.NewReader(r) 102 103 for { 104 hdr, err := tr.Next() 105 if err != nil { 106 if err == io.EOF { 107 break 108 } 109 return n, err 110 } 111 112 data, err := ioutil.ReadAll(tr) 113 if err != nil { 114 return n, err 115 } 116 117 var c mock.ExportedChunk 118 if err = json.Unmarshal(data, &c); err != nil { 119 return n, err 120 } 121 122 batch := new(leveldb.Batch) 123 for _, addr := range c.Addrs { 124 batch.Put(nodeDBKeyHex(addr, hdr.Name), nil) 125 } 126 127 batch.Put(dataDBKey(common.Hex2Bytes(hdr.Name)), c.Data) 128 if err = s.db.Write(batch, nil); err != nil { 129 return n, err 130 } 131 132 n++ 133 } 134 return n, err 135 } 136 137 // Export writes to a writer a tar archive with all chunk data from 138 // the store. It returns the number fo chunks exported and an error. 139 func (s *GlobalStore) Export(w io.Writer) (n int, err error) { 140 tw := tar.NewWriter(w) 141 defer tw.Close() 142 143 buf := bytes.NewBuffer(make([]byte, 0, 1024)) 144 encoder := json.NewEncoder(buf) 145 146 iter := s.db.NewIterator(util.BytesPrefix(nodeKeyPrefix), nil) 147 defer iter.Release() 148 149 var currentKey string 150 var addrs []common.Address 151 152 saveChunk := func(hexKey string) error { 153 key := common.Hex2Bytes(hexKey) 154 155 data, err := s.db.Get(dataDBKey(key), nil) 156 if err != nil { 157 return err 158 } 159 160 buf.Reset() 161 if err = encoder.Encode(mock.ExportedChunk{ 162 Addrs: addrs, 163 Data: data, 164 }); err != nil { 165 return err 166 } 167 168 d := buf.Bytes() 169 hdr := &tar.Header{ 170 Name: hexKey, 171 Mode: 0644, 172 Size: int64(len(d)), 173 } 174 if err := tw.WriteHeader(hdr); err != nil { 175 return err 176 } 177 if _, err := tw.Write(d); err != nil { 178 return err 179 } 180 n++ 181 return nil 182 } 183 184 for iter.Next() { 185 k := bytes.TrimPrefix(iter.Key(), nodeKeyPrefix) 186 i := bytes.Index(k, []byte("-")) 187 if i < 0 { 188 continue 189 } 190 hexKey := string(k[:i]) 191 192 if currentKey == "" { 193 currentKey = hexKey 194 } 195 196 if hexKey != currentKey { 197 if err = saveChunk(currentKey); err != nil { 198 return n, err 199 } 200 201 addrs = addrs[:0] 202 } 203 204 currentKey = hexKey 205 addrs = append(addrs, common.BytesToAddress(k[i:])) 206 } 207 208 if len(addrs) > 0 { 209 if err = saveChunk(currentKey); err != nil { 210 return n, err 211 } 212 } 213 214 return n, err 215 } 216 217 var ( 218 nodeKeyPrefix = []byte("node-") 219 dataKeyPrefix = []byte("data-") 220 ) 221 222 // nodeDBKey constructs a database key for key/node mappings. 223 func nodeDBKey(addr common.Address, key []byte) []byte { 224 return nodeDBKeyHex(addr, common.Bytes2Hex(key)) 225 } 226 227 // nodeDBKeyHex constructs a database key for key/node mappings 228 // using the hexadecimal string representation of the key. 229 func nodeDBKeyHex(addr common.Address, hexKey string) []byte { 230 return append(append(nodeKeyPrefix, []byte(hexKey+"-")...), addr[:]...) 231 } 232 233 // dataDBkey constructs a database key for key/data storage. 234 func dataDBKey(key []byte) []byte { 235 return append(dataKeyPrefix, key...) 236 }