github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/orderer/consensus/solo/consensus.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package solo
     8  
     9  import (
    10  	"fmt"
    11  	"time"
    12  
    13  	cb "github.com/hyperledger/fabric-protos-go/common"
    14  	"github.com/osdi23p228/fabric/common/flogging"
    15  	"github.com/osdi23p228/fabric/orderer/consensus"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  var logger = flogging.MustGetLogger("orderer.consensus.solo")
    20  
    21  type consenter struct{}
    22  
    23  type chain struct {
    24  	support  consensus.ConsenterSupport
    25  	sendChan chan *message
    26  	exitChan chan struct{}
    27  }
    28  
    29  type message struct {
    30  	configSeq uint64
    31  	normalMsg *cb.Envelope
    32  	configMsg *cb.Envelope
    33  }
    34  
    35  // New creates a new consenter for the solo consensus scheme.
    36  // The solo consensus scheme is very simple, and allows only one consenter for a given chain (this process).
    37  // It accepts messages being delivered via Order/Configure, orders them, and then uses the blockcutter to form the messages
    38  // into blocks before writing to the given ledger
    39  func New() consensus.Consenter {
    40  	return &consenter{}
    41  }
    42  
    43  func (solo *consenter) HandleChain(support consensus.ConsenterSupport, metadata *cb.Metadata) (consensus.Chain, error) {
    44  	logger.Warningf("Use of the Solo orderer is deprecated and remains only for use in test environments but may be removed in the future.")
    45  	return newChain(support), nil
    46  }
    47  
    48  func (c *consenter) JoinChain(support consensus.ConsenterSupport, joinBlock *cb.Block) (consensus.Chain, error) {
    49  	return nil, errors.New("the Solo orderer does not support JoinChain")
    50  }
    51  
    52  func newChain(support consensus.ConsenterSupport) *chain {
    53  	return &chain{
    54  		support:  support,
    55  		sendChan: make(chan *message),
    56  		exitChan: make(chan struct{}),
    57  	}
    58  }
    59  
    60  func (ch *chain) Start() {
    61  	go ch.main()
    62  }
    63  
    64  func (ch *chain) Halt() {
    65  	select {
    66  	case <-ch.exitChan:
    67  		// Allow multiple halts without panic
    68  	default:
    69  		close(ch.exitChan)
    70  	}
    71  }
    72  
    73  func (ch *chain) WaitReady() error {
    74  	return nil
    75  }
    76  
    77  // Order accepts normal messages for ordering
    78  func (ch *chain) Order(env *cb.Envelope, configSeq uint64) error {
    79  	select {
    80  	case ch.sendChan <- &message{
    81  		configSeq: configSeq,
    82  		normalMsg: env,
    83  	}:
    84  		return nil
    85  	case <-ch.exitChan:
    86  		return fmt.Errorf("Exiting")
    87  	}
    88  }
    89  
    90  // Configure accepts configuration update messages for ordering
    91  func (ch *chain) Configure(config *cb.Envelope, configSeq uint64) error {
    92  	select {
    93  	case ch.sendChan <- &message{
    94  		configSeq: configSeq,
    95  		configMsg: config,
    96  	}:
    97  		return nil
    98  	case <-ch.exitChan:
    99  		return fmt.Errorf("Exiting")
   100  	}
   101  }
   102  
   103  // Errored only closes on exit
   104  func (ch *chain) Errored() <-chan struct{} {
   105  	return ch.exitChan
   106  }
   107  
   108  func (ch *chain) main() {
   109  	var timer <-chan time.Time
   110  	var err error
   111  
   112  	for {
   113  		seq := ch.support.Sequence()
   114  		err = nil
   115  		select {
   116  		case msg := <-ch.sendChan:
   117  			if msg.configMsg == nil {
   118  				// NormalMsg
   119  				if msg.configSeq < seq {
   120  					_, err = ch.support.ProcessNormalMsg(msg.normalMsg)
   121  					if err != nil {
   122  						logger.Warningf("Discarding bad normal message: %s", err)
   123  						continue
   124  					}
   125  				}
   126  				batches, pending := ch.support.BlockCutter().Ordered(msg.normalMsg)
   127  
   128  				for _, batch := range batches {
   129  					block := ch.support.CreateNextBlock(batch)
   130  					ch.support.WriteBlock(block, nil)
   131  				}
   132  
   133  				switch {
   134  				case timer != nil && !pending:
   135  					// Timer is already running but there are no messages pending, stop the timer
   136  					timer = nil
   137  				case timer == nil && pending:
   138  					// Timer is not already running and there are messages pending, so start it
   139  					timer = time.After(ch.support.SharedConfig().BatchTimeout())
   140  					logger.Debugf("Just began %s batch timer", ch.support.SharedConfig().BatchTimeout().String())
   141  				default:
   142  					// Do nothing when:
   143  					// 1. Timer is already running and there are messages pending
   144  					// 2. Timer is not set and there are no messages pending
   145  				}
   146  
   147  			} else {
   148  				// ConfigMsg
   149  				if msg.configSeq < seq {
   150  					msg.configMsg, _, err = ch.support.ProcessConfigMsg(msg.configMsg)
   151  					if err != nil {
   152  						logger.Warningf("Discarding bad config message: %s", err)
   153  						continue
   154  					}
   155  				}
   156  				batch := ch.support.BlockCutter().Cut()
   157  				if batch != nil {
   158  					block := ch.support.CreateNextBlock(batch)
   159  					ch.support.WriteBlock(block, nil)
   160  				}
   161  
   162  				block := ch.support.CreateNextBlock([]*cb.Envelope{msg.configMsg})
   163  				ch.support.WriteConfigBlock(block, nil)
   164  				timer = nil
   165  			}
   166  		case <-timer:
   167  			//clear the timer
   168  			timer = nil
   169  
   170  			batch := ch.support.BlockCutter().Cut()
   171  			if len(batch) == 0 {
   172  				logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug")
   173  				continue
   174  			}
   175  			logger.Debugf("Batch timer expired, creating block")
   176  			block := ch.support.CreateNextBlock(batch)
   177  			ch.support.WriteBlock(block, nil)
   178  		case <-ch.exitChan:
   179  			logger.Debugf("Exiting")
   180  			return
   181  		}
   182  	}
   183  }