github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/orderer/consensus/solo/consensus.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 solo
    18  
    19  import (
    20  	"time"
    21  
    22  	"github.com/hyperledger/fabric/orderer/common/multichannel"
    23  	cb "github.com/hyperledger/fabric/protos/common"
    24  	"github.com/op/go-logging"
    25  )
    26  
    27  var logger = logging.MustGetLogger("orderer/solo")
    28  
    29  type consenter struct{}
    30  
    31  type chain struct {
    32  	support  multichannel.ConsenterSupport
    33  	sendChan chan *cb.Envelope
    34  	exitChan chan struct{}
    35  }
    36  
    37  // New creates a new consenter for the solo consensus scheme.
    38  // The solo consensus scheme is very simple, and allows only one consenter for a given chain (this process).
    39  // It accepts messages being delivered via Enqueue, orders them, and then uses the blockcutter to form the messages
    40  // into blocks before writing to the given ledger
    41  func New() multichannel.Consenter {
    42  	return &consenter{}
    43  }
    44  
    45  func (solo *consenter) HandleChain(support multichannel.ConsenterSupport, metadata *cb.Metadata) (multichannel.Chain, error) {
    46  	return newChain(support), nil
    47  }
    48  
    49  func newChain(support multichannel.ConsenterSupport) *chain {
    50  	return &chain{
    51  		support:  support,
    52  		sendChan: make(chan *cb.Envelope),
    53  		exitChan: make(chan struct{}),
    54  	}
    55  }
    56  
    57  func (ch *chain) Start() {
    58  	go ch.main()
    59  }
    60  
    61  func (ch *chain) Halt() {
    62  	select {
    63  	case <-ch.exitChan:
    64  		// Allow multiple halts without panic
    65  	default:
    66  		close(ch.exitChan)
    67  	}
    68  }
    69  
    70  // Enqueue accepts a message and returns true on acceptance, or false on shutdown
    71  func (ch *chain) Enqueue(env *cb.Envelope) bool {
    72  	select {
    73  	case ch.sendChan <- env:
    74  		return true
    75  	case <-ch.exitChan:
    76  		return false
    77  	}
    78  }
    79  
    80  // Errored only closes on exit
    81  func (ch *chain) Errored() <-chan struct{} {
    82  	return ch.exitChan
    83  }
    84  
    85  func (ch *chain) main() {
    86  	var timer <-chan time.Time
    87  
    88  	for {
    89  		select {
    90  		case msg := <-ch.sendChan:
    91  			class, err := ch.support.ClassifyMsg(msg)
    92  			if err != nil {
    93  				logger.Panicf("If a message has arrived to this point, it should already have been classified once")
    94  			}
    95  			switch class {
    96  			case multichannel.ConfigUpdateMsg:
    97  				batch := ch.support.BlockCutter().Cut()
    98  				if batch != nil {
    99  					block := ch.support.CreateNextBlock(batch)
   100  					ch.support.WriteBlock(block, nil)
   101  				}
   102  
   103  				_, err := ch.support.ProcessNormalMsg(msg)
   104  				if err != nil {
   105  					logger.Warningf("Discarding bad config message: %s", err)
   106  					continue
   107  				}
   108  				block := ch.support.CreateNextBlock([]*cb.Envelope{msg})
   109  				ch.support.WriteConfigBlock(block, nil)
   110  				timer = nil
   111  			case multichannel.NormalMsg:
   112  				batches, ok := ch.support.BlockCutter().Ordered(msg)
   113  				if ok && len(batches) == 0 && timer == nil {
   114  					timer = time.After(ch.support.SharedConfig().BatchTimeout())
   115  					continue
   116  				}
   117  				for _, batch := range batches {
   118  					block := ch.support.CreateNextBlock(batch)
   119  					ch.support.WriteBlock(block, nil)
   120  				}
   121  				if len(batches) > 0 {
   122  					timer = nil
   123  				}
   124  			default:
   125  				logger.Panicf("Unsupported msg classification: %v", class)
   126  			}
   127  		case <-timer:
   128  			//clear the timer
   129  			timer = nil
   130  
   131  			batch := ch.support.BlockCutter().Cut()
   132  			if len(batch) == 0 {
   133  				logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug")
   134  				continue
   135  			}
   136  			logger.Debugf("Batch timer expired, creating block")
   137  			block := ch.support.CreateNextBlock(batch)
   138  			ch.support.WriteBlock(block, nil)
   139  		case <-ch.exitChan:
   140  			logger.Debugf("Exiting")
   141  			return
   142  		}
   143  	}
   144  }