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 }