github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/orderer/sbft/simplebft/simplebft.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 simplebft
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"reflect"
    23  	"time"
    24  
    25  	"github.com/golang/protobuf/proto"
    26  	"github.com/hyperledger/fabric/orderer/common/filter"
    27  	"github.com/op/go-logging"
    28  )
    29  
    30  const preprepared string = "preprepared"
    31  const prepared string = "prepared"
    32  const committed string = "committed"
    33  const viewchange string = "viewchange"
    34  
    35  // Receiver defines the API that is exposed by SBFT to the system.
    36  type Receiver interface {
    37  	Receive(msg *Msg, src uint64)
    38  	Request(req []byte)
    39  	Connection(replica uint64)
    40  	GetChainId() string
    41  }
    42  
    43  // System defines the API that needs to be provided for SBFT.
    44  type System interface {
    45  	Send(chainId string, msg *Msg, dest uint64)
    46  	Timer(d time.Duration, f func()) Canceller
    47  	Deliver(chainId string, batch *Batch, committers []filter.Committer)
    48  	AddReceiver(chainId string, receiver Receiver)
    49  	Persist(chainId string, key string, data proto.Message)
    50  	Restore(chainId string, key string, out proto.Message) bool
    51  	LastBatch(chainId string) *Batch
    52  	Sign(data []byte) []byte
    53  	CheckSig(data []byte, src uint64, sig []byte) error
    54  	Reconnect(chainId string, replica uint64)
    55  	Validate(chainID string, req *Request) ([][]*Request, [][]filter.Committer, bool)
    56  	Cut(chainID string) ([]*Request, []filter.Committer)
    57  }
    58  
    59  // Canceller allows cancelling of a scheduled timer event.
    60  type Canceller interface {
    61  	Cancel()
    62  }
    63  
    64  // SBFT is a simplified PBFT implementation.
    65  type SBFT struct {
    66  	sys System
    67  
    68  	config            Config
    69  	id                uint64
    70  	view              uint64
    71  	batches           [][]*Request
    72  	batchTimer        Canceller
    73  	cur               reqInfo
    74  	activeView        bool
    75  	lastNewViewSent   *NewView
    76  	viewChangeTimeout time.Duration
    77  	viewChangeTimer   Canceller
    78  	replicaState      []replicaInfo
    79  	pending           map[string]*Request
    80  	validated         map[string]bool
    81  	chainId           string
    82  	primarycommitters [][]filter.Committer
    83  }
    84  
    85  type reqInfo struct {
    86  	subject        Subject
    87  	timeout        Canceller
    88  	preprep        *Preprepare
    89  	prep           map[uint64]*Subject
    90  	commit         map[uint64]*Subject
    91  	checkpoint     map[uint64]*Checkpoint
    92  	prepared       bool
    93  	committed      bool
    94  	checkpointDone bool
    95  	committers     []filter.Committer
    96  }
    97  
    98  type replicaInfo struct {
    99  	backLog          []*Msg
   100  	hello            *Hello
   101  	signedViewchange *Signed
   102  	viewchange       *ViewChange
   103  }
   104  
   105  var log = logging.MustGetLogger("sbft")
   106  
   107  type dummyCanceller struct{}
   108  
   109  func (d dummyCanceller) Cancel() {}
   110  
   111  // New creates a new SBFT instance.
   112  func New(id uint64, chainID string, config *Config, sys System) (*SBFT, error) {
   113  	if config.F*3+1 > config.N {
   114  		return nil, fmt.Errorf("invalid combination of N (%d) and F (%d)", config.N, config.F)
   115  	}
   116  
   117  	s := &SBFT{
   118  		config:            *config,
   119  		sys:               sys,
   120  		id:                id,
   121  		chainId:           chainID,
   122  		viewChangeTimer:   dummyCanceller{},
   123  		replicaState:      make([]replicaInfo, config.N),
   124  		pending:           make(map[string]*Request),
   125  		validated:         make(map[string]bool),
   126  		batches:           make([][]*Request, 0, 3),
   127  		primarycommitters: make([][]filter.Committer, 0),
   128  	}
   129  	s.sys.AddReceiver(chainID, s)
   130  
   131  	s.view = 0
   132  	s.cur.subject.Seq = &SeqView{}
   133  	s.cur.prepared = true
   134  	s.cur.committed = true
   135  	s.cur.checkpointDone = true
   136  	s.cur.timeout = dummyCanceller{}
   137  	s.activeView = true
   138  
   139  	svc := &Signed{}
   140  	if s.sys.Restore(s.chainId, viewchange, svc) {
   141  		vc := &ViewChange{}
   142  		err := proto.Unmarshal(svc.Data, vc)
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  		fmt.Println(fmt.Sprintf("rep %d VIEW %d   %d", s.id, s.view, vc.View))
   147  		s.view = vc.View
   148  		s.replicaState[s.id].signedViewchange = svc
   149  		s.activeView = false
   150  	}
   151  
   152  	pp := &Preprepare{}
   153  	if s.sys.Restore(s.chainId, preprepared, pp) && pp.Seq.View >= s.view {
   154  		s.view = pp.Seq.View
   155  		s.activeView = true
   156  		if pp.Seq.Seq > s.seq() {
   157  			// TODO double add to BC?
   158  			_, committers := s.getCommittersFromBatch(pp.Batch)
   159  			s.acceptPreprepare(pp, committers)
   160  		}
   161  	}
   162  	c := &Subject{}
   163  	if s.sys.Restore(s.chainId, prepared, c) && reflect.DeepEqual(c, &s.cur.subject) && c.Seq.View >= s.view {
   164  		s.cur.prepared = true
   165  	}
   166  	ex := &Subject{}
   167  	if s.sys.Restore(s.chainId, committed, ex) && reflect.DeepEqual(c, &s.cur.subject) && ex.Seq.View >= s.view {
   168  		s.cur.committed = true
   169  	}
   170  
   171  	s.cancelViewChangeTimer()
   172  	return s, nil
   173  }
   174  
   175  ////////////////////////////////////////////////
   176  
   177  func (s *SBFT) GetChainId() string {
   178  	return s.chainId
   179  }
   180  
   181  func (s *SBFT) primaryIDView(v uint64) uint64 {
   182  	return v % s.config.N
   183  }
   184  
   185  func (s *SBFT) primaryID() uint64 {
   186  	return s.primaryIDView(s.view)
   187  }
   188  
   189  func (s *SBFT) isPrimary() bool {
   190  	return s.primaryID() == s.id
   191  }
   192  
   193  func (s *SBFT) seq() uint64 {
   194  	return s.sys.LastBatch(s.chainId).DecodeHeader().Seq
   195  }
   196  
   197  func (s *SBFT) nextSeq() SeqView {
   198  	return SeqView{Seq: s.seq() + 1, View: s.view}
   199  }
   200  
   201  func (s *SBFT) nextView() uint64 {
   202  	return s.view + 1
   203  }
   204  
   205  func (s *SBFT) commonCaseQuorum() int {
   206  	//When N=3F+1 this should be 2F+1 (N-F)
   207  	//More generally, we need every two common case quorums of size X to intersect in at least F+1 orderers,
   208  	//hence 2X>=N+F+1, or X is:
   209  	return int(math.Ceil(float64(s.config.N+s.config.F+1) / float64(2)))
   210  }
   211  
   212  func (s *SBFT) viewChangeQuorum() int {
   213  	//When N=3F+1 this should be 2F+1 (N-F)
   214  	//More generally, we need every view change quorum to intersect with every common case quorum at least F+1 orderers, hence:
   215  	//Y >= N-X+F+1
   216  	return int(s.config.N+s.config.F+1) - s.commonCaseQuorum()
   217  }
   218  
   219  func (s *SBFT) oneCorrectQuorum() int {
   220  	return int(s.config.F + 1)
   221  }
   222  
   223  func (s *SBFT) broadcast(m *Msg) {
   224  	for i := uint64(0); i < s.config.N; i++ {
   225  		s.sys.Send(s.chainId, m, i)
   226  	}
   227  }
   228  
   229  ////////////////////////////////////////////////
   230  
   231  // Receive is the ingress method for SBFT messages.
   232  func (s *SBFT) Receive(m *Msg, src uint64) {
   233  	log.Debugf("replica %d: received message from %d: %s", s.id, src, m)
   234  
   235  	if h := m.GetHello(); h != nil {
   236  		s.handleHello(h, src)
   237  		return
   238  	} else if req := m.GetRequest(); req != nil {
   239  		s.handleRequest(req, src)
   240  		return
   241  	} else if vs := m.GetViewChange(); vs != nil {
   242  		s.handleViewChange(vs, src)
   243  		return
   244  	} else if nv := m.GetNewView(); nv != nil {
   245  		s.handleNewView(nv, src)
   246  		return
   247  	}
   248  
   249  	if s.testBacklogMessage(m, src) {
   250  		log.Debugf("replica %d: message for future seq, storing for later", s.id)
   251  		s.recordBacklogMsg(m, src)
   252  		return
   253  	}
   254  
   255  	s.handleQueueableMessage(m, src)
   256  }
   257  
   258  func (s *SBFT) handleQueueableMessage(m *Msg, src uint64) {
   259  	if pp := m.GetPreprepare(); pp != nil {
   260  		s.handlePreprepare(pp, src)
   261  		return
   262  	} else if p := m.GetPrepare(); p != nil {
   263  		s.handlePrepare(p, src)
   264  		return
   265  	} else if c := m.GetCommit(); c != nil {
   266  		s.handleCommit(c, src)
   267  		return
   268  	} else if c := m.GetCheckpoint(); c != nil {
   269  		s.handleCheckpoint(c, src)
   270  		return
   271  	}
   272  
   273  	log.Warningf("replica %d: received invalid message from %d", s.id, src)
   274  }
   275  
   276  func (s *SBFT) deliverBatch(batch *Batch, committers []filter.Committer) {
   277  	if committers == nil {
   278  		log.Warningf("replica %d: commiter is nil", s.id)
   279  		panic("Committer is nil.")
   280  	}
   281  	s.cur.checkpointDone = true
   282  	s.cur.timeout.Cancel()
   283  	// s.primarycommitters[0]
   284  	s.sys.Deliver(s.chainId, batch, committers)
   285  	// s.primarycommitters = s.primarycommitters[1:]
   286  
   287  	for _, req := range batch.Payloads {
   288  		key := hash2str(hash(req))
   289  		log.Infof("replica %d: attempting to remove %x from pending", s.id, key)
   290  		delete(s.pending, key)
   291  		delete(s.validated, key)
   292  	}
   293  }