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