github.com/anjalikarhana/fabric@v2.1.1+incompatible/orderer/common/multichannel/blockwriter.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package multichannel
     8  
     9  import (
    10  	"sync"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	cb "github.com/hyperledger/fabric-protos-go/common"
    14  	newchannelconfig "github.com/hyperledger/fabric/common/channelconfig"
    15  	"github.com/hyperledger/fabric/common/configtx"
    16  	"github.com/hyperledger/fabric/common/ledger/blockledger"
    17  	"github.com/hyperledger/fabric/common/util"
    18  	"github.com/hyperledger/fabric/internal/pkg/identity"
    19  	"github.com/hyperledger/fabric/protoutil"
    20  )
    21  
    22  type blockWriterSupport interface {
    23  	identity.SignerSerializer
    24  	blockledger.ReadWriter
    25  	configtx.Validator
    26  	Update(*newchannelconfig.Bundle)
    27  	CreateBundle(channelID string, config *cb.Config) (*newchannelconfig.Bundle, error)
    28  	SharedConfig() newchannelconfig.Orderer
    29  }
    30  
    31  // BlockWriter efficiently writes the blockchain to disk.
    32  // To safely use BlockWriter, only one thread should interact with it.
    33  // BlockWriter will spawn additional committing go routines and handle locking
    34  // so that these other go routines safely interact with the calling one.
    35  type BlockWriter struct {
    36  	support            blockWriterSupport
    37  	registrar          *Registrar
    38  	lastConfigBlockNum uint64
    39  	lastConfigSeq      uint64
    40  	lastBlock          *cb.Block
    41  	committingBlock    sync.Mutex
    42  }
    43  
    44  func newBlockWriter(lastBlock *cb.Block, r *Registrar, support blockWriterSupport) *BlockWriter {
    45  	bw := &BlockWriter{
    46  		support:       support,
    47  		lastConfigSeq: support.Sequence(),
    48  		lastBlock:     lastBlock,
    49  		registrar:     r,
    50  	}
    51  
    52  	// If this is the genesis block, the lastconfig field may be empty, and, the last config block is necessarily block 0
    53  	// so no need to initialize lastConfig
    54  	if lastBlock.Header.Number != 0 {
    55  		var err error
    56  		bw.lastConfigBlockNum, err = protoutil.GetLastConfigIndexFromBlock(lastBlock)
    57  		if err != nil {
    58  			logger.Panicf("[channel: %s] Error extracting last config block from block metadata: %s", support.ChannelID(), err)
    59  		}
    60  	}
    61  
    62  	logger.Debugf("[channel: %s] Creating block writer for tip of chain (blockNumber=%d, lastConfigBlockNum=%d, lastConfigSeq=%d)", support.ChannelID(), lastBlock.Header.Number, bw.lastConfigBlockNum, bw.lastConfigSeq)
    63  	return bw
    64  }
    65  
    66  // CreateNextBlock creates a new block with the next block number, and the given contents.
    67  func (bw *BlockWriter) CreateNextBlock(messages []*cb.Envelope) *cb.Block {
    68  	previousBlockHash := protoutil.BlockHeaderHash(bw.lastBlock.Header)
    69  
    70  	data := &cb.BlockData{
    71  		Data: make([][]byte, len(messages)),
    72  	}
    73  
    74  	var err error
    75  	for i, msg := range messages {
    76  		data.Data[i], err = proto.Marshal(msg)
    77  		if err != nil {
    78  			logger.Panicf("Could not marshal envelope: %s", err)
    79  		}
    80  	}
    81  
    82  	block := protoutil.NewBlock(bw.lastBlock.Header.Number+1, previousBlockHash)
    83  	block.Header.DataHash = protoutil.BlockDataHash(data)
    84  	block.Data = data
    85  
    86  	return block
    87  }
    88  
    89  // WriteConfigBlock should be invoked for blocks which contain a config transaction.
    90  // This call will block until the new config has taken effect, then will return
    91  // while the block is written asynchronously to disk.
    92  func (bw *BlockWriter) WriteConfigBlock(block *cb.Block, encodedMetadataValue []byte) {
    93  	ctx, err := protoutil.ExtractEnvelope(block, 0)
    94  	if err != nil {
    95  		logger.Panicf("Told to write a config block, but could not get configtx: %s", err)
    96  	}
    97  
    98  	payload, err := protoutil.UnmarshalPayload(ctx.Payload)
    99  	if err != nil {
   100  		logger.Panicf("Told to write a config block, but configtx payload is invalid: %s", err)
   101  	}
   102  
   103  	if payload.Header == nil {
   104  		logger.Panicf("Told to write a config block, but configtx payload header is missing")
   105  	}
   106  
   107  	chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   108  	if err != nil {
   109  		logger.Panicf("Told to write a config block with an invalid channel header: %s", err)
   110  	}
   111  
   112  	switch chdr.Type {
   113  	case int32(cb.HeaderType_ORDERER_TRANSACTION):
   114  		newChannelConfig, err := protoutil.UnmarshalEnvelope(payload.Data)
   115  		if err != nil {
   116  			logger.Panicf("Told to write a config block with new channel, but did not have config update embedded: %s", err)
   117  		}
   118  		bw.registrar.newChain(newChannelConfig)
   119  
   120  	case int32(cb.HeaderType_CONFIG):
   121  		configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   122  		if err != nil {
   123  			logger.Panicf("Told to write a config block with new channel, but did not have config envelope encoded: %s", err)
   124  		}
   125  
   126  		err = bw.support.Validate(configEnvelope)
   127  		if err != nil {
   128  			logger.Panicf("Told to write a config block with new config, but could not apply it: %s", err)
   129  		}
   130  
   131  		bundle, err := bw.support.CreateBundle(chdr.ChannelId, configEnvelope.Config)
   132  		if err != nil {
   133  			logger.Panicf("Told to write a config block with a new config, but could not convert it to a bundle: %s", err)
   134  		}
   135  
   136  		oc, ok := bundle.OrdererConfig()
   137  		if !ok {
   138  			logger.Panicf("[channel: %s] OrdererConfig missing from bundle", bw.support.ChannelID())
   139  		}
   140  
   141  		currentType := bw.support.SharedConfig().ConsensusType()
   142  		nextType := oc.ConsensusType()
   143  		if currentType != nextType {
   144  			encodedMetadataValue = nil
   145  			logger.Debugf("[channel: %s] Consensus-type migration: maintenance mode, change from %s to %s, setting metadata to nil",
   146  				bw.support.ChannelID(), currentType, nextType)
   147  		}
   148  
   149  		// Avoid Bundle update before the go-routine in WriteBlock() finished writing the previous block.
   150  		// We do this (in particular) to prevent bw.support.Sequence() from advancing before the go-routine reads it.
   151  		// In general, this prevents the StableBundle from changing before the go-routine in WriteBlock() finishes.
   152  		bw.committingBlock.Lock()
   153  		bw.committingBlock.Unlock()
   154  		bw.support.Update(bundle)
   155  	default:
   156  		logger.Panicf("Told to write a config block with unknown header type: %v", chdr.Type)
   157  	}
   158  
   159  	bw.WriteBlock(block, encodedMetadataValue)
   160  }
   161  
   162  // WriteBlock should be invoked for blocks which contain normal transactions.
   163  // It sets the target block as the pending next block, and returns before it is committed.
   164  // Before returning, it acquires the committing lock, and spawns a go routine which will
   165  // annotate the block with metadata and signatures, and write the block to the ledger
   166  // then release the lock.  This allows the calling thread to begin assembling the next block
   167  // before the commit phase is complete.
   168  func (bw *BlockWriter) WriteBlock(block *cb.Block, encodedMetadataValue []byte) {
   169  	bw.committingBlock.Lock()
   170  	bw.lastBlock = block
   171  
   172  	go func() {
   173  		defer bw.committingBlock.Unlock()
   174  		bw.commitBlock(encodedMetadataValue)
   175  	}()
   176  }
   177  
   178  // commitBlock should only ever be invoked with the bw.committingBlock held
   179  // this ensures that the encoded config sequence numbers stay in sync
   180  func (bw *BlockWriter) commitBlock(encodedMetadataValue []byte) {
   181  	bw.addLastConfig(bw.lastBlock)
   182  	bw.addBlockSignature(bw.lastBlock, encodedMetadataValue)
   183  
   184  	err := bw.support.Append(bw.lastBlock)
   185  	if err != nil {
   186  		logger.Panicf("[channel: %s] Could not append block: %s", bw.support.ChannelID(), err)
   187  	}
   188  	logger.Debugf("[channel: %s] Wrote block [%d]", bw.support.ChannelID(), bw.lastBlock.GetHeader().Number)
   189  }
   190  
   191  func (bw *BlockWriter) addBlockSignature(block *cb.Block, consenterMetadata []byte) {
   192  	blockSignature := &cb.MetadataSignature{
   193  		SignatureHeader: protoutil.MarshalOrPanic(protoutil.NewSignatureHeaderOrPanic(bw.support)),
   194  	}
   195  
   196  	blockSignatureValue := protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
   197  		LastConfig:        &cb.LastConfig{Index: bw.lastConfigBlockNum},
   198  		ConsenterMetadata: protoutil.MarshalOrPanic(&cb.Metadata{Value: consenterMetadata}),
   199  	})
   200  
   201  	blockSignature.Signature = protoutil.SignOrPanic(
   202  		bw.support,
   203  		util.ConcatenateBytes(blockSignatureValue, blockSignature.SignatureHeader, protoutil.BlockHeaderBytes(block.Header)),
   204  	)
   205  
   206  	block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{
   207  		Value: blockSignatureValue,
   208  		Signatures: []*cb.MetadataSignature{
   209  			blockSignature,
   210  		},
   211  	})
   212  }
   213  
   214  func (bw *BlockWriter) addLastConfig(block *cb.Block) {
   215  	configSeq := bw.support.Sequence()
   216  	if configSeq > bw.lastConfigSeq {
   217  		logger.Debugf("[channel: %s] Detected lastConfigSeq transitioning from %d to %d, setting lastConfigBlockNum from %d to %d", bw.support.ChannelID(), bw.lastConfigSeq, configSeq, bw.lastConfigBlockNum, block.Header.Number)
   218  		bw.lastConfigBlockNum = block.Header.Number
   219  		bw.lastConfigSeq = configSeq
   220  	}
   221  
   222  	lastConfigValue := protoutil.MarshalOrPanic(&cb.LastConfig{Index: bw.lastConfigBlockNum})
   223  	logger.Debugf("[channel: %s] About to write block, setting its LAST_CONFIG to %d", bw.support.ChannelID(), bw.lastConfigBlockNum)
   224  
   225  	block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{
   226  		Value: lastConfigValue,
   227  	})
   228  }