github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/orderer/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/multichain" 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 multichain.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() multichain.Consenter { 42 return &consenter{} 43 } 44 45 func (solo *consenter) HandleChain(support multichain.ConsenterSupport, metadata *cb.Metadata) (multichain.Chain, error) { 46 return newChain(support), nil 47 } 48 49 func newChain(support multichain.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 batches, committers, ok, _ := ch.support.BlockCutter().Ordered(msg) 92 if ok && len(batches) == 0 && timer == nil { 93 timer = time.After(ch.support.SharedConfig().BatchTimeout()) 94 continue 95 } 96 for i, batch := range batches { 97 block := ch.support.CreateNextBlock(batch) 98 ch.support.WriteBlock(block, committers[i], nil) 99 } 100 if len(batches) > 0 { 101 timer = nil 102 } 103 case <-timer: 104 //clear the timer 105 timer = nil 106 107 batch, committers := ch.support.BlockCutter().Cut() 108 if len(batch) == 0 { 109 logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug") 110 continue 111 } 112 logger.Debugf("Batch timer expired, creating block") 113 block := ch.support.CreateNextBlock(batch) 114 ch.support.WriteBlock(block, committers, nil) 115 case <-ch.exitChan: 116 logger.Debugf("Exiting") 117 return 118 } 119 } 120 }