github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/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/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/swarm/storage/mock" 29 "github.com/syndtr/goleveldb/leveldb" 30 "github.com/syndtr/goleveldb/leveldb/util" 31 ) 32 33 // GlobalStore contains the LevelDB database that is storing 34 // chunk data for all swarm nodes. 35 // Closing the GlobalStore with Close method is required to 36 // release resources used by the database. 37 type GlobalStore struct { 38 db *leveldb.DB 39 } 40 41 // NewGlobalStore creates a new instance of GlobalStore. 42 func NewGlobalStore(path string) (s *GlobalStore, err error) { 43 db, err := leveldb.OpenFile(path, nil) 44 if err != nil { 45 return nil, err 46 } 47 return &GlobalStore{ 48 db: db, 49 }, nil 50 } 51 52 // Close releases the resources used by the underlying LevelDB. 53 func (s *GlobalStore) Close() error { 54 return s.db.Close() 55 } 56 57 // NewNodeStore returns a new instance of NodeStore that retrieves and stores 58 // chunk data only for a node with address addr. 59 func (s *GlobalStore) NewNodeStore(addr common.Address) *mock.NodeStore { 60 return mock.NewNodeStore(addr, s) 61 } 62 63 // Get returns chunk data if the chunk with key exists for node 64 // on address addr. 65 func (s *GlobalStore) Get(addr common.Address, key []byte) (data []byte, err error) { 66 has, err := s.db.Has(nodeDBKey(addr, key), nil) 67 if err != nil { 68 return nil, mock.ErrNotFound 69 } 70 if !has { 71 return nil, mock.ErrNotFound 72 } 73 data, err = s.db.Get(dataDBKey(key), nil) 74 if err == leveldb.ErrNotFound { 75 err = mock.ErrNotFound 76 } 77 return 78 } 79 80 // Put saves the chunk data for node with address addr. 81 func (s *GlobalStore) Put(addr common.Address, key []byte, data []byte) error { 82 batch := new(leveldb.Batch) 83 batch.Put(nodeDBKey(addr, key), nil) 84 batch.Put(dataDBKey(key), data) 85 return s.db.Write(batch, nil) 86 } 87 88 // Delete removes the chunk reference to node with address addr. 89 func (s *GlobalStore) Delete(addr common.Address, key []byte) error { 90 batch := new(leveldb.Batch) 91 batch.Delete(nodeDBKey(addr, key)) 92 return s.db.Write(batch, nil) 93 } 94 95 // HasKey returns whether a node with addr contains the key. 96 func (s *GlobalStore) HasKey(addr common.Address, key []byte) bool { 97 has, err := s.db.Has(nodeDBKey(addr, key), nil) 98 if err != nil { 99 has = false 100 } 101 return has 102 } 103 104 // Import reads tar archive from a reader that contains exported chunk data. 105 // It returns the number of chunks imported and an error. 106 func (s *GlobalStore) Import(r io.Reader) (n int, err error) { 107 tr := tar.NewReader(r) 108 109 for { 110 hdr, err := tr.Next() 111 if err != nil { 112 if err == io.EOF { 113 break 114 } 115 return n, err 116 } 117 118 data, err := ioutil.ReadAll(tr) 119 if err != nil { 120 return n, err 121 } 122 123 var c mock.ExportedChunk 124 if err = json.Unmarshal(data, &c); err != nil { 125 return n, err 126 } 127 128 batch := new(leveldb.Batch) 129 for _, addr := range c.Addrs { 130 batch.Put(nodeDBKeyHex(addr, hdr.Name), nil) 131 } 132 133 batch.Put(dataDBKey(common.Hex2Bytes(hdr.Name)), c.Data) 134 if err = s.db.Write(batch, nil); err != nil { 135 return n, err 136 } 137 138 n++ 139 } 140 return n, err 141 } 142 143 // Export writes to a writer a tar archive with all chunk data from 144 // the store. It returns the number fo chunks exported and an error. 145 func (s *GlobalStore) Export(w io.Writer) (n int, err error) { 146 tw := tar.NewWriter(w) 147 defer tw.Close() 148 149 buf := bytes.NewBuffer(make([]byte, 0, 1024)) 150 encoder := json.NewEncoder(buf) 151 152 iter := s.db.NewIterator(util.BytesPrefix(nodeKeyPrefix), nil) 153 defer iter.Release() 154 155 var currentKey string 156 var addrs []common.Address 157 158 saveChunk := func(hexKey string) error { 159 key := common.Hex2Bytes(hexKey) 160 161 data, err := s.db.Get(dataDBKey(key), nil) 162 if err != nil { 163 return err 164 } 165 166 buf.Reset() 167 if err = encoder.Encode(mock.ExportedChunk{ 168 Addrs: addrs, 169 Data: data, 170 }); err != nil { 171 return err 172 } 173 174 d := buf.Bytes() 175 hdr := &tar.Header{ 176 Name: hexKey, 177 Mode: 0644, 178 Size: int64(len(d)), 179 } 180 if err := tw.WriteHeader(hdr); err != nil { 181 return err 182 } 183 if _, err := tw.Write(d); err != nil { 184 return err 185 } 186 n++ 187 return nil 188 } 189 190 for iter.Next() { 191 k := bytes.TrimPrefix(iter.Key(), nodeKeyPrefix) 192 i := bytes.Index(k, []byte("-")) 193 if i < 0 { 194 continue 195 } 196 hexKey := string(k[:i]) 197 198 if currentKey == "" { 199 currentKey = hexKey 200 } 201 202 if hexKey != currentKey { 203 if err = saveChunk(currentKey); err != nil { 204 return n, err 205 } 206 207 addrs = addrs[:0] 208 } 209 210 currentKey = hexKey 211 addrs = append(addrs, common.BytesToAddress(k[i:])) 212 } 213 214 if len(addrs) > 0 { 215 if err = saveChunk(currentKey); err != nil { 216 return n, err 217 } 218 } 219 220 return n, err 221 } 222 223 var ( 224 nodeKeyPrefix = []byte("node-") 225 dataKeyPrefix = []byte("data-") 226 ) 227 228 // nodeDBKey constructs a database key for key/node mappings. 229 func nodeDBKey(addr common.Address, key []byte) []byte { 230 return nodeDBKeyHex(addr, common.Bytes2Hex(key)) 231 } 232 233 // nodeDBKeyHex constructs a database key for key/node mappings 234 // using the hexadecimal string representation of the key. 235 func nodeDBKeyHex(addr common.Address, hexKey string) []byte { 236 return append(append(nodeKeyPrefix, []byte(hexKey+"-")...), addr[:]...) 237 } 238 239 // dataDBkey constructs a database key for key/data storage. 240 func dataDBKey(key []byte) []byte { 241 return append(dataKeyPrefix, key...) 242 }