github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/orderer/ledger/file/fileledger.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8                   http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package fileledger
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"sync"
    25  
    26  	ordererledger "github.com/hyperledger/fabric/orderer/ledger"
    27  	cb "github.com/hyperledger/fabric/protos/common"
    28  	ab "github.com/hyperledger/fabric/protos/orderer"
    29  	"github.com/op/go-logging"
    30  
    31  	"github.com/golang/protobuf/jsonpb"
    32  )
    33  
    34  var logger = logging.MustGetLogger("ordererledger/fileledger")
    35  var closedChan chan struct{}
    36  
    37  func init() {
    38  	closedChan = make(chan struct{})
    39  	close(closedChan)
    40  }
    41  
    42  const (
    43  	blockFileFormatString      = "block_%020d.json"
    44  	chainDirectoryFormatString = "chain_%s"
    45  )
    46  
    47  type cursor struct {
    48  	fl          *fileLedger
    49  	blockNumber uint64
    50  }
    51  
    52  type fileLedger struct {
    53  	directory      string
    54  	fqFormatString string
    55  	height         uint64
    56  	signal         chan struct{}
    57  	lastHash       []byte
    58  	marshaler      *jsonpb.Marshaler
    59  }
    60  
    61  type fileLedgerFactory struct {
    62  	directory string
    63  	ledgers   map[string]ordererledger.ReadWriter
    64  	mutex     sync.Mutex
    65  }
    66  
    67  // New creates a new fileledger Factory and the ordering system chain specified by the systemGenesis block (if it does not already exist)
    68  func New(directory string) ordererledger.Factory {
    69  
    70  	logger.Debugf("Initializing fileLedger at '%s'", directory)
    71  	if err := os.MkdirAll(directory, 0700); err != nil {
    72  		logger.Fatalf("Could not create directory %s: %s", directory, err)
    73  	}
    74  
    75  	flf := &fileLedgerFactory{
    76  		directory: directory,
    77  		ledgers:   make(map[string]ordererledger.ReadWriter),
    78  	}
    79  
    80  	infos, err := ioutil.ReadDir(flf.directory)
    81  	if err != nil {
    82  		logger.Panicf("Error reading from directory %s while initializing fileledger: %s", flf.directory, err)
    83  	}
    84  
    85  	for _, info := range infos {
    86  		if !info.IsDir() {
    87  			continue
    88  		}
    89  		var chainID string
    90  		_, err := fmt.Sscanf(info.Name(), chainDirectoryFormatString, &chainID)
    91  		if err != nil {
    92  			continue
    93  		}
    94  		fl, err := flf.GetOrCreate(chainID)
    95  		if err != nil {
    96  			logger.Warningf("Failed to initialize chain from %s:", err)
    97  			continue
    98  		}
    99  		flf.ledgers[chainID] = fl
   100  	}
   101  
   102  	return flf
   103  }
   104  
   105  func (flf *fileLedgerFactory) ChainIDs() []string {
   106  	flf.mutex.Lock()
   107  	defer flf.mutex.Unlock()
   108  	ids := make([]string, len(flf.ledgers))
   109  
   110  	i := 0
   111  	for key := range flf.ledgers {
   112  		ids[i] = key
   113  		i++
   114  	}
   115  
   116  	return ids
   117  }
   118  
   119  func (flf *fileLedgerFactory) GetOrCreate(chainID string) (ordererledger.ReadWriter, error) {
   120  	flf.mutex.Lock()
   121  	defer flf.mutex.Unlock()
   122  
   123  	key := chainID
   124  
   125  	l, ok := flf.ledgers[key]
   126  	if ok {
   127  		return l, nil
   128  	}
   129  
   130  	directory := fmt.Sprintf("%s/"+chainDirectoryFormatString, flf.directory, chainID)
   131  
   132  	logger.Debugf("Initializing chain at '%s'", directory)
   133  
   134  	if err := os.MkdirAll(directory, 0700); err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	ch := newChain(directory)
   139  	flf.ledgers[key] = ch
   140  	return ch, nil
   141  }
   142  
   143  // newChain creates a new chain backed by a file ledger
   144  func newChain(directory string) ordererledger.ReadWriter {
   145  	fl := &fileLedger{
   146  		directory:      directory,
   147  		fqFormatString: directory + "/" + blockFileFormatString,
   148  		signal:         make(chan struct{}),
   149  		marshaler:      &jsonpb.Marshaler{Indent: "  "},
   150  	}
   151  	fl.initializeBlockHeight()
   152  	logger.Debugf("Initialized to block height %d with hash %x", fl.height-1, fl.lastHash)
   153  	return fl
   154  }
   155  
   156  // initializeBlockHeight verifies all blocks exist between 0 and the block height, and populates the lastHash
   157  func (fl *fileLedger) initializeBlockHeight() {
   158  	infos, err := ioutil.ReadDir(fl.directory)
   159  	if err != nil {
   160  		panic(err)
   161  	}
   162  	nextNumber := uint64(0)
   163  	for _, info := range infos {
   164  		if info.IsDir() {
   165  			continue
   166  		}
   167  		var number uint64
   168  		_, err := fmt.Sscanf(info.Name(), blockFileFormatString, &number)
   169  		if err != nil {
   170  			continue
   171  		}
   172  		if number != nextNumber {
   173  			panic(fmt.Errorf("Missing block %d in the chain", nextNumber))
   174  		}
   175  		nextNumber++
   176  	}
   177  	fl.height = nextNumber
   178  	if fl.height == 0 {
   179  		return
   180  	}
   181  	block, found := fl.readBlock(fl.height - 1)
   182  	if !found {
   183  		panic(fmt.Errorf("Block %d was in directory listing but error reading", fl.height-1))
   184  	}
   185  	if block == nil {
   186  		panic(fmt.Errorf("Error reading block %d", fl.height-1))
   187  	}
   188  	fl.lastHash = block.Header.Hash()
   189  }
   190  
   191  // blockFilename returns the fully qualified path to where a block of a given number should be stored on disk
   192  func (fl *fileLedger) blockFilename(number uint64) string {
   193  	return fmt.Sprintf(fl.fqFormatString, number)
   194  }
   195  
   196  // writeBlock commits a block to disk
   197  func (fl *fileLedger) writeBlock(block *cb.Block) {
   198  	file, err := os.Create(fl.blockFilename(block.Header.Number))
   199  	if err != nil {
   200  		panic(err)
   201  	}
   202  	defer file.Close()
   203  	err = fl.marshaler.Marshal(file, block)
   204  	logger.Debugf("Wrote block %d", block.Header.Number)
   205  	if err != nil {
   206  		panic(err)
   207  	}
   208  
   209  }
   210  
   211  // readBlock returns the block or nil, and whether the block was found or not, (nil,true) generally indicates an irrecoverable problem
   212  func (fl *fileLedger) readBlock(number uint64) (*cb.Block, bool) {
   213  	file, err := os.Open(fl.blockFilename(number))
   214  	if err == nil {
   215  		defer file.Close()
   216  		block := &cb.Block{}
   217  		err = jsonpb.Unmarshal(file, block)
   218  		if err != nil {
   219  			return nil, true
   220  		}
   221  		logger.Debugf("Read block %d", block.Header.Number)
   222  		return block, true
   223  	}
   224  	return nil, false
   225  }
   226  
   227  // Height returns the highest block number in the chain, plus one
   228  func (fl *fileLedger) Height() uint64 {
   229  	return fl.height
   230  }
   231  
   232  // Append appends a new block to the ledger
   233  func (fl *fileLedger) Append(block *cb.Block) error {
   234  	if block.Header.Number != fl.height {
   235  		return fmt.Errorf("Block number should have been %d but was %d", fl.height, block.Header.Number)
   236  	}
   237  
   238  	if !bytes.Equal(block.Header.PreviousHash, fl.lastHash) {
   239  		return fmt.Errorf("Block should have had previous hash of %x but was %x", fl.lastHash, block.Header.PreviousHash)
   240  	}
   241  
   242  	fl.writeBlock(block)
   243  	fl.lastHash = block.Header.Hash()
   244  	fl.height++
   245  	close(fl.signal)
   246  	fl.signal = make(chan struct{})
   247  	return nil
   248  }
   249  
   250  // Iterator implements the ordererledger.Reader definition
   251  func (fl *fileLedger) Iterator(startPosition *ab.SeekPosition) (ordererledger.Iterator, uint64) {
   252  	switch start := startPosition.Type.(type) {
   253  	case *ab.SeekPosition_Oldest:
   254  		return &cursor{fl: fl, blockNumber: 0}, 0
   255  	case *ab.SeekPosition_Newest:
   256  		high := fl.height - 1
   257  		return &cursor{fl: fl, blockNumber: high}, high
   258  	case *ab.SeekPosition_Specified:
   259  		if start.Specified.Number > fl.height {
   260  			return &ordererledger.NotFoundErrorIterator{}, 0
   261  		}
   262  		return &cursor{fl: fl, blockNumber: start.Specified.Number}, start.Specified.Number
   263  	}
   264  
   265  	// This line should be unreachable, but the compiler requires it
   266  	return &ordererledger.NotFoundErrorIterator{}, 0
   267  }
   268  
   269  // Next blocks until there is a new block available, or returns an error if the next block is no longer retrievable
   270  func (cu *cursor) Next() (*cb.Block, cb.Status) {
   271  	// This only loops once, as signal reading indicates the new block has been written
   272  	for {
   273  		block, found := cu.fl.readBlock(cu.blockNumber)
   274  		if found {
   275  			if block == nil {
   276  				return nil, cb.Status_SERVICE_UNAVAILABLE
   277  			}
   278  			cu.blockNumber++
   279  			return block, cb.Status_SUCCESS
   280  		}
   281  		<-cu.fl.signal
   282  	}
   283  }
   284  
   285  // ReadyChan returns a channel that will close when Next is ready to be called without blocking
   286  func (cu *cursor) ReadyChan() <-chan struct{} {
   287  	signal := cu.fl.signal
   288  	if _, err := os.Stat(cu.fl.blockFilename(cu.blockNumber)); os.IsNotExist(err) {
   289  		return signal
   290  	}
   291  	return closedChan
   292  }