github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/orderer/ledger/ram/ramledger.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 ramledger
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"sync"
    23  
    24  	ordererledger "github.com/hyperledger/fabric/orderer/ledger"
    25  	cb "github.com/hyperledger/fabric/protos/common"
    26  	ab "github.com/hyperledger/fabric/protos/orderer"
    27  	"github.com/op/go-logging"
    28  )
    29  
    30  var logger = logging.MustGetLogger("ordererledger/ramledger")
    31  
    32  type cursor struct {
    33  	list *simpleList
    34  }
    35  
    36  type simpleList struct {
    37  	next   *simpleList
    38  	signal chan struct{}
    39  	block  *cb.Block
    40  }
    41  
    42  type ramLedger struct {
    43  	maxSize int
    44  	size    int
    45  	oldest  *simpleList
    46  	newest  *simpleList
    47  }
    48  
    49  type ramLedgerFactory struct {
    50  	maxSize int
    51  	ledgers map[string]ordererledger.ReadWriter
    52  	mutex   sync.Mutex
    53  }
    54  
    55  // New creates a new ramledger factory and system ordering chain based on the given systemGenesis block,
    56  // because there is no persistence, the new ReadWriter will have only the genesis block contained
    57  func New(maxSize int) ordererledger.Factory {
    58  	rlf := &ramLedgerFactory{
    59  		maxSize: maxSize,
    60  		ledgers: make(map[string]ordererledger.ReadWriter),
    61  	}
    62  
    63  	return rlf
    64  }
    65  
    66  func (rlf *ramLedgerFactory) GetOrCreate(chainID string) (ordererledger.ReadWriter, error) {
    67  	rlf.mutex.Lock()
    68  	defer rlf.mutex.Unlock()
    69  
    70  	key := chainID
    71  
    72  	l, ok := rlf.ledgers[key]
    73  	if ok {
    74  		return l, nil
    75  	}
    76  
    77  	ch := newChain(rlf.maxSize)
    78  	rlf.ledgers[key] = ch
    79  	return ch, nil
    80  }
    81  
    82  func (rlf *ramLedgerFactory) ChainIDs() []string {
    83  	rlf.mutex.Lock()
    84  	defer rlf.mutex.Unlock()
    85  	ids := make([]string, len(rlf.ledgers))
    86  
    87  	i := 0
    88  	for key := range rlf.ledgers {
    89  		ids[i] = key
    90  		i++
    91  	}
    92  
    93  	return ids
    94  }
    95  
    96  // newChain creates a new instance of the ram ledger for a chain
    97  func newChain(maxSize int) ordererledger.ReadWriter {
    98  	preGenesis := &cb.Block{
    99  		Header: &cb.BlockHeader{
   100  			Number: ^uint64(0),
   101  		},
   102  	}
   103  
   104  	rl := &ramLedger{
   105  		maxSize: maxSize,
   106  		size:    1,
   107  		oldest: &simpleList{
   108  			signal: make(chan struct{}),
   109  			block:  preGenesis,
   110  		},
   111  	}
   112  	rl.newest = rl.oldest
   113  	return rl
   114  }
   115  
   116  // Height returns the highest block number in the chain, plus one
   117  func (rl *ramLedger) Height() uint64 {
   118  	return rl.newest.block.Header.Number + 1
   119  }
   120  
   121  // Iterator implements the ordererledger.Reader definition
   122  func (rl *ramLedger) Iterator(startPosition *ab.SeekPosition) (ordererledger.Iterator, uint64) {
   123  	var list *simpleList
   124  	switch start := startPosition.Type.(type) {
   125  	case *ab.SeekPosition_Oldest:
   126  		oldest := rl.oldest
   127  		list = &simpleList{
   128  			block:  &cb.Block{Header: &cb.BlockHeader{Number: oldest.block.Header.Number - 1}},
   129  			next:   oldest,
   130  			signal: make(chan struct{}),
   131  		}
   132  		close(list.signal)
   133  	case *ab.SeekPosition_Newest:
   134  		newest := rl.newest
   135  		list = &simpleList{
   136  			block:  &cb.Block{Header: &cb.BlockHeader{Number: newest.block.Header.Number - 1}},
   137  			next:   newest,
   138  			signal: make(chan struct{}),
   139  		}
   140  		close(list.signal)
   141  	case *ab.SeekPosition_Specified:
   142  		oldest := rl.oldest
   143  		specified := start.Specified.Number
   144  		logger.Debugf("Attempting to return block %d", specified)
   145  
   146  		// Note the two +1's here is to accomodate the 'preGenesis' block of ^uint64(0)
   147  		if specified+1 < oldest.block.Header.Number+1 || specified > rl.newest.block.Header.Number+1 {
   148  			logger.Debugf("Returning error iterator because specified seek was %d with oldest %d and newest %d", specified, rl.oldest.block.Header.Number, rl.newest.block.Header.Number)
   149  			return &ordererledger.NotFoundErrorIterator{}, 0
   150  		}
   151  
   152  		if specified == oldest.block.Header.Number {
   153  			list = &simpleList{
   154  				block:  &cb.Block{Header: &cb.BlockHeader{Number: oldest.block.Header.Number - 1}},
   155  				next:   oldest,
   156  				signal: make(chan struct{}),
   157  			}
   158  			close(list.signal)
   159  			break
   160  		}
   161  
   162  		list = oldest
   163  		for {
   164  			if list.block.Header.Number == specified-1 {
   165  				break
   166  			}
   167  			list = list.next // No need for nil check, because of range check above
   168  		}
   169  	}
   170  	cursor := &cursor{list: list}
   171  	blockNum := list.block.Header.Number + 1
   172  
   173  	// If the cursor is for pre-genesis, skip it, the block number wraps
   174  	if blockNum == ^uint64(0) {
   175  		cursor.Next()
   176  		blockNum++
   177  	}
   178  
   179  	return cursor, blockNum
   180  }
   181  
   182  // Next blocks until there is a new block available, or returns an error if the next block is no longer retrievable
   183  func (cu *cursor) Next() (*cb.Block, cb.Status) {
   184  	// This only loops once, as signal reading indicates non-nil next
   185  	for {
   186  		if cu.list.next != nil {
   187  			cu.list = cu.list.next
   188  			return cu.list.block, cb.Status_SUCCESS
   189  		}
   190  
   191  		<-cu.list.signal
   192  	}
   193  }
   194  
   195  // ReadyChan returns a channel that will close when Next is ready to be called without blocking
   196  func (cu *cursor) ReadyChan() <-chan struct{} {
   197  	return cu.list.signal
   198  }
   199  
   200  // Append appends a new block to the ledger
   201  func (rl *ramLedger) Append(block *cb.Block) error {
   202  	if block.Header.Number != rl.newest.block.Header.Number+1 {
   203  		return fmt.Errorf("Block number should have been %d but was %d", rl.newest.block.Header.Number+1, block.Header.Number)
   204  	}
   205  
   206  	if rl.newest.block.Header.Number+1 != 0 { // Skip this check for genesis block insertion
   207  		if !bytes.Equal(block.Header.PreviousHash, rl.newest.block.Header.Hash()) {
   208  			return fmt.Errorf("Block should have had previous hash of %x but was %x", rl.newest.block.Header.Hash(), block.Header.PreviousHash)
   209  		}
   210  	}
   211  
   212  	rl.appendBlock(block)
   213  	return nil
   214  }
   215  
   216  func (rl *ramLedger) appendBlock(block *cb.Block) {
   217  	rl.newest.next = &simpleList{
   218  		signal: make(chan struct{}),
   219  		block:  block,
   220  	}
   221  
   222  	lastSignal := rl.newest.signal
   223  	logger.Debugf("Sending signal that block %d has a successor", rl.newest.block.Header.Number)
   224  	rl.newest = rl.newest.next
   225  	close(lastSignal)
   226  
   227  	rl.size++
   228  
   229  	if rl.size > rl.maxSize {
   230  		logger.Debugf("RAM ledger max size about to be exceeded, removing oldest item: %d", rl.oldest.block.Header.Number)
   231  		rl.oldest = rl.oldest.next
   232  		rl.size--
   233  	}
   234  }