github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/orderer/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/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  // Iterator returns an Iterator, as specified by a cb.SeekInfo message, and its
   112  // starting block number
   113  func (jl *jsonLedger) Iterator(startPosition *ab.SeekPosition) (ledger.Iterator, uint64) {
   114  	switch start := startPosition.Type.(type) {
   115  	case *ab.SeekPosition_Oldest:
   116  		return &cursor{jl: jl, blockNumber: 0}, 0
   117  	case *ab.SeekPosition_Newest:
   118  		high := jl.height - 1
   119  		return &cursor{jl: jl, blockNumber: high}, high
   120  	case *ab.SeekPosition_Specified:
   121  		if start.Specified.Number > jl.height {
   122  			return &ledger.NotFoundErrorIterator{}, 0
   123  		}
   124  		return &cursor{jl: jl, blockNumber: start.Specified.Number}, start.Specified.Number
   125  	default:
   126  		return &ledger.NotFoundErrorIterator{}, 0
   127  	}
   128  }
   129  
   130  // Height returns the number of blocks on the ledger
   131  func (jl *jsonLedger) Height() uint64 {
   132  	return jl.height
   133  }
   134  
   135  // Append appends a new block to the ledger
   136  func (jl *jsonLedger) Append(block *cb.Block) error {
   137  	if block.Header.Number != jl.height {
   138  		return fmt.Errorf("Block number should have been %d but was %d", jl.height, block.Header.Number)
   139  	}
   140  
   141  	if !bytes.Equal(block.Header.PreviousHash, jl.lastHash) {
   142  		return fmt.Errorf("Block should have had previous hash of %x but was %x", jl.lastHash, block.Header.PreviousHash)
   143  	}
   144  
   145  	jl.writeBlock(block)
   146  	jl.lastHash = block.Header.Hash()
   147  	jl.height++
   148  	close(jl.signal)
   149  	jl.signal = make(chan struct{})
   150  	return nil
   151  }
   152  
   153  // writeBlock commits a block to disk
   154  func (jl *jsonLedger) writeBlock(block *cb.Block) {
   155  	name := jl.blockFilename(block.Header.Number)
   156  
   157  	fileLock.Lock()
   158  	defer fileLock.Unlock()
   159  
   160  	file, err := os.Create(name)
   161  	if err != nil {
   162  		panic(err)
   163  	}
   164  	defer file.Close()
   165  
   166  	err = jl.marshaler.Marshal(file, block)
   167  	logger.Debugf("Wrote block %d", block.Header.Number)
   168  	if err != nil {
   169  		logger.Panic(err)
   170  	}
   171  }
   172  
   173  // blockFilename returns the fully qualified path to where a block
   174  // of a given number should be stored on disk
   175  func (jl *jsonLedger) blockFilename(number uint64) string {
   176  	return filepath.Join(jl.directory, fmt.Sprintf(blockFileFormatString, number))
   177  }