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  }