github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/orderer/common/ledger/json/impl.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 jsonledger
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"os"
    23  	"path/filepath"
    24  	"sync"
    25  
    26  	ledger "github.com/hyperledger/fabric/orderer/common/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("orderer/jsonledger")
    35  var closedChan chan struct{}
    36  var fileLock sync.Mutex
    37  
    38  func init() {
    39  	closedChan = make(chan struct{})
    40  	close(closedChan)
    41  }
    42  
    43  const (
    44  	blockFileFormatString      = "block_%020d.json"
    45  	chainDirectoryFormatString = "chain_%s"
    46  )
    47  
    48  type cursor struct {
    49  	jl          *jsonLedger
    50  	blockNumber uint64
    51  }
    52  
    53  type jsonLedger struct {
    54  	directory string
    55  	height    uint64
    56  	signal    chan struct{}
    57  	lastHash  []byte
    58  	marshaler *jsonpb.Marshaler
    59  }
    60  
    61  // readBlock returns the block or nil, and whether the block was found or not, (nil,true) generally indicates an irrecoverable problem
    62  func (jl *jsonLedger) readBlock(number uint64) (*cb.Block, bool) {
    63  	name := jl.blockFilename(number)
    64  
    65  	// In case of ongoing write, reading the block file may result in `unexpected EOF` error.
    66  	// Therefore, we use file mutex here to prevent this race condition.
    67  	fileLock.Lock()
    68  	defer fileLock.Unlock()
    69  
    70  	file, err := os.Open(name)
    71  	if err == nil {
    72  		defer file.Close()
    73  		block := &cb.Block{}
    74  		err = jsonpb.Unmarshal(file, block)
    75  		if err != nil {
    76  			return nil, true
    77  		}
    78  		logger.Debugf("Read block %d", block.Header.Number)
    79  		return block, true
    80  	}
    81  	return nil, false
    82  }
    83  
    84  // Next blocks until there is a new block available, or returns an error if the
    85  // next block is no longer retrievable
    86  func (cu *cursor) Next() (*cb.Block, cb.Status) {
    87  	// This only loops once, as signal reading
    88  	// indicates the new block has been written
    89  	for {
    90  		block, found := cu.jl.readBlock(cu.blockNumber)
    91  		if found {
    92  			if block == nil {
    93  				return nil, cb.Status_SERVICE_UNAVAILABLE
    94  			}
    95  			cu.blockNumber++
    96  			return block, cb.Status_SUCCESS
    97  		}
    98  		<-cu.jl.signal
    99  	}
   100  }
   101  
   102  // ReadyChan supplies a channel which will block until Next will not block
   103  func (cu *cursor) ReadyChan() <-chan struct{} {
   104  	signal := cu.jl.signal
   105  	if _, err := os.Stat(cu.jl.blockFilename(cu.blockNumber)); os.IsNotExist(err) {
   106  		return signal
   107  	}
   108  	return closedChan
   109  }
   110  
   111  func (cu *cursor) Close() {}
   112  
   113  // Iterator returns an Iterator, as specified by a cb.SeekInfo message, and its
   114  // starting block number
   115  func (jl *jsonLedger) Iterator(startPosition *ab.SeekPosition) (ledger.Iterator, uint64) {
   116  	switch start := startPosition.Type.(type) {
   117  	case *ab.SeekPosition_Oldest:
   118  		return &cursor{jl: jl, blockNumber: 0}, 0
   119  	case *ab.SeekPosition_Newest:
   120  		high := jl.height - 1
   121  		return &cursor{jl: jl, blockNumber: high}, high
   122  	case *ab.SeekPosition_Specified:
   123  		if start.Specified.Number > jl.height {
   124  			return &ledger.NotFoundErrorIterator{}, 0
   125  		}
   126  		return &cursor{jl: jl, blockNumber: start.Specified.Number}, start.Specified.Number
   127  	default:
   128  		return &ledger.NotFoundErrorIterator{}, 0
   129  	}
   130  }
   131  
   132  // Height returns the number of blocks on the ledger
   133  func (jl *jsonLedger) Height() uint64 {
   134  	return jl.height
   135  }
   136  
   137  // Append appends a new block to the ledger
   138  func (jl *jsonLedger) Append(block *cb.Block) error {
   139  	if block.Header.Number != jl.height {
   140  		return fmt.Errorf("Block number should have been %d but was %d", jl.height, block.Header.Number)
   141  	}
   142  
   143  	if !bytes.Equal(block.Header.PreviousHash, jl.lastHash) {
   144  		return fmt.Errorf("Block should have had previous hash of %x but was %x", jl.lastHash, block.Header.PreviousHash)
   145  	}
   146  
   147  	jl.writeBlock(block)
   148  	jl.lastHash = block.Header.Hash()
   149  	jl.height++
   150  	close(jl.signal)
   151  	jl.signal = make(chan struct{})
   152  	return nil
   153  }
   154  
   155  // writeBlock commits a block to disk
   156  func (jl *jsonLedger) writeBlock(block *cb.Block) {
   157  	name := jl.blockFilename(block.Header.Number)
   158  
   159  	fileLock.Lock()
   160  	defer fileLock.Unlock()
   161  
   162  	file, err := os.Create(name)
   163  	if err != nil {
   164  		panic(err)
   165  	}
   166  	defer file.Close()
   167  
   168  	err = jl.marshaler.Marshal(file, block)
   169  	logger.Debugf("Wrote block %d", block.Header.Number)
   170  	if err != nil {
   171  		logger.Panic(err)
   172  	}
   173  }
   174  
   175  // blockFilename returns the fully qualified path to where a block
   176  // of a given number should be stored on disk
   177  func (jl *jsonLedger) blockFilename(number uint64) string {
   178  	return filepath.Join(jl.directory, fmt.Sprintf(blockFileFormatString, number))
   179  }