github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/common/multichannel/blockwriter.go (about) 1 /* 2 Copyright hechain. 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 newchannelconfig "github.com/hechain20/hechain/common/channelconfig" 14 "github.com/hechain20/hechain/common/configtx" 15 "github.com/hechain20/hechain/common/ledger/blockledger" 16 "github.com/hechain20/hechain/common/util" 17 "github.com/hechain20/hechain/internal/pkg/identity" 18 "github.com/hechain20/hechain/protoutil" 19 cb "github.com/hyperledger/fabric-protos-go/common" 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.WriteBlockSync(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 // WriteBlockSync is same as WriteBlock, but commits block synchronously. 179 // Note: WriteConfigBlock should use WriteBlockSync instead of WriteBlock. 180 // If the block contains a transaction that remove the node from consenters, 181 // the node will switch to follower and pull blocks from other nodes. 182 // Suppose writing block asynchronously, the block maybe not persist to disk 183 // when the follower chain starts working. The follower chain will read a block 184 // before the config block, in which the node is still a consenter, so the follower 185 // chain will switch to the consensus chain. That's a dead loop! 186 // So WriteConfigBlock should use WriteBlockSync instead of WriteBlock. 187 func (bw *BlockWriter) WriteBlockSync(block *cb.Block, encodedMetadataValue []byte) { 188 bw.committingBlock.Lock() 189 bw.lastBlock = block 190 191 defer bw.committingBlock.Unlock() 192 bw.commitBlock(encodedMetadataValue) 193 } 194 195 // commitBlock should only ever be invoked with the bw.committingBlock held 196 // this ensures that the encoded config sequence numbers stay in sync 197 func (bw *BlockWriter) commitBlock(encodedMetadataValue []byte) { 198 bw.addLastConfig(bw.lastBlock) 199 bw.addBlockSignature(bw.lastBlock, encodedMetadataValue) 200 201 err := bw.support.Append(bw.lastBlock) 202 if err != nil { 203 logger.Panicf("[channel: %s] Could not append block: %s", bw.support.ChannelID(), err) 204 } 205 logger.Debugf("[channel: %s] Wrote block [%d]", bw.support.ChannelID(), bw.lastBlock.GetHeader().Number) 206 } 207 208 func (bw *BlockWriter) addBlockSignature(block *cb.Block, consenterMetadata []byte) { 209 blockSignature := &cb.MetadataSignature{ 210 SignatureHeader: protoutil.MarshalOrPanic(protoutil.NewSignatureHeaderOrPanic(bw.support)), 211 } 212 213 blockSignatureValue := protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{ 214 LastConfig: &cb.LastConfig{Index: bw.lastConfigBlockNum}, 215 ConsenterMetadata: protoutil.MarshalOrPanic(&cb.Metadata{Value: consenterMetadata}), 216 }) 217 218 blockSignature.Signature = protoutil.SignOrPanic( 219 bw.support, 220 util.ConcatenateBytes(blockSignatureValue, blockSignature.SignatureHeader, protoutil.BlockHeaderBytes(block.Header)), 221 ) 222 223 block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{ 224 Value: blockSignatureValue, 225 Signatures: []*cb.MetadataSignature{ 226 blockSignature, 227 }, 228 }) 229 } 230 231 func (bw *BlockWriter) addLastConfig(block *cb.Block) { 232 configSeq := bw.support.Sequence() 233 if configSeq > bw.lastConfigSeq { 234 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) 235 bw.lastConfigBlockNum = block.Header.Number 236 bw.lastConfigSeq = configSeq 237 } 238 239 lastConfigValue := protoutil.MarshalOrPanic(&cb.LastConfig{Index: bw.lastConfigBlockNum}) 240 logger.Debugf("[channel: %s] About to write block, setting its LAST_CONFIG to %d", bw.support.ChannelID(), bw.lastConfigBlockNum) 241 242 block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{ 243 Value: lastConfigValue, 244 }) 245 }