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 }