github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/orderer/multichain/chainsupport.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 multichain 18 19 import ( 20 "github.com/inklabsfoundation/inkchain/common/config" 21 "github.com/inklabsfoundation/inkchain/common/crypto" 22 "github.com/inklabsfoundation/inkchain/common/policies" 23 "github.com/inklabsfoundation/inkchain/common/util" 24 "github.com/inklabsfoundation/inkchain/orderer/common/blockcutter" 25 "github.com/inklabsfoundation/inkchain/orderer/common/broadcast" 26 "github.com/inklabsfoundation/inkchain/orderer/common/configtxfilter" 27 "github.com/inklabsfoundation/inkchain/orderer/common/filter" 28 "github.com/inklabsfoundation/inkchain/orderer/common/sigfilter" 29 "github.com/inklabsfoundation/inkchain/orderer/common/sizefilter" 30 "github.com/inklabsfoundation/inkchain/orderer/ledger" 31 cb "github.com/inklabsfoundation/inkchain/protos/common" 32 "github.com/inklabsfoundation/inkchain/protos/utils" 33 ) 34 35 // Consenter defines the backing ordering mechanism 36 type Consenter interface { 37 // HandleChain should create and return a reference to a Chain for the given set of resources 38 // It will only be invoked for a given chain once per process. In general, errors will be treated 39 // as irrecoverable and cause system shutdown. See the description of Chain for more details 40 // The second argument to HandleChain is a pointer to the metadata stored on the `ORDERER` slot of 41 // the last block committed to the ledger of this Chain. For a new chain, this metadata will be 42 // nil, as this field is not set on the genesis block 43 HandleChain(support ConsenterSupport, metadata *cb.Metadata) (Chain, error) 44 } 45 46 // Chain defines a way to inject messages for ordering 47 // Note, that in order to allow flexibility in the implementation, it is the responsibility of the implementer 48 // to take the ordered messages, send them through the blockcutter.Receiver supplied via HandleChain to cut blocks, 49 // and ultimately write the ledger also supplied via HandleChain. This flow allows for two primary flows 50 // 1. Messages are ordered into a stream, the stream is cut into blocks, the blocks are committed (solo, kafka) 51 // 2. Messages are cut into blocks, the blocks are ordered, then the blocks are committed (sbft) 52 type Chain interface { 53 // Enqueue accepts a message and returns true on acceptance, or false on failure 54 Enqueue(env *cb.Envelope) bool 55 56 // Errored returns a channel which will close when an error has occurred 57 // This is especially useful for the Deliver client, who must terminate waiting 58 // clients when the consenter is not up to date 59 Errored() <-chan struct{} 60 61 // Start should allocate whatever resources are needed for staying up to date with the chain 62 // Typically, this involves creating a thread which reads from the ordering source, passes those 63 // messages to a block cutter, and writes the resulting blocks to the ledger 64 Start() 65 66 // Halt frees the resources which were allocated for this Chain 67 Halt() 68 } 69 70 // ConsenterSupport provides the resources available to a Consenter implementation 71 type ConsenterSupport interface { 72 crypto.LocalSigner 73 BlockCutter() blockcutter.Receiver 74 SharedConfig() config.Orderer 75 CreateNextBlock(messages []*cb.Envelope) *cb.Block 76 WriteBlock(block *cb.Block, committers []filter.Committer, encodedMetadataValue []byte) *cb.Block 77 ChainID() string // ChainID returns the chain ID this specific consenter instance is associated with 78 Height() uint64 // Returns the number of blocks on the chain this specific consenter instance is associated with 79 } 80 81 // ChainSupport provides a wrapper for the resources backing a chain 82 type ChainSupport interface { 83 // This interface is actually the union with the deliver.Support but because of a golang 84 // limitation https://github.com/golang/go/issues/6977 the methods must be explicitly declared 85 86 // PolicyManager returns the current policy manager as specified by the chain config 87 PolicyManager() policies.Manager 88 89 // Reader returns the chain Reader for the chain 90 Reader() ledger.Reader 91 92 // Errored returns whether the backing consenter has errored 93 Errored() <-chan struct{} 94 95 broadcast.Support 96 ConsenterSupport 97 98 // Sequence returns the current config sequence number 99 Sequence() uint64 100 101 // ProposeConfigUpdate applies a CONFIG_UPDATE to an existing config to produce a *cb.ConfigEnvelope 102 ProposeConfigUpdate(env *cb.Envelope) (*cb.ConfigEnvelope, error) 103 } 104 105 type chainSupport struct { 106 *ledgerResources 107 chain Chain 108 cutter blockcutter.Receiver 109 filters *filter.RuleSet 110 signer crypto.LocalSigner 111 lastConfig uint64 112 lastConfigSeq uint64 113 feeAddress string 114 blockVersion uint64 115 } 116 117 func newChainSupport( 118 filters *filter.RuleSet, 119 ledgerResources *ledgerResources, 120 consenters map[string]Consenter, 121 signer crypto.LocalSigner, 122 feeAddress string, 123 blockVersion uint64, 124 ) *chainSupport { 125 126 cutter := blockcutter.NewReceiverImpl(ledgerResources.SharedConfig(), filters) 127 consenterType := ledgerResources.SharedConfig().ConsensusType() 128 consenter, ok := consenters[consenterType] 129 if !ok { 130 logger.Fatalf("Error retrieving consenter of type: %s", consenterType) 131 } 132 133 cs := &chainSupport{ 134 ledgerResources: ledgerResources, 135 cutter: cutter, 136 filters: filters, 137 signer: signer, 138 feeAddress: feeAddress, 139 blockVersion: blockVersion, 140 } 141 142 cs.lastConfigSeq = cs.Sequence() 143 144 var err error 145 146 lastBlock := ledger.GetBlock(cs.Reader(), cs.Reader().Height()-1) 147 148 // If this is the genesis block, the lastconfig field may be empty, and, the last config is necessary 0 149 // so no need to initialize lastConfig 150 if lastBlock.Header.Number != 0 { 151 cs.lastConfig, err = utils.GetLastConfigIndexFromBlock(lastBlock) 152 if err != nil { 153 logger.Fatalf("[channel: %s] Error extracting last config block from block metadata: %s", cs.ChainID(), err) 154 } 155 } 156 157 metadata, err := utils.GetMetadataFromBlock(lastBlock, cb.BlockMetadataIndex_ORDERER) 158 // Assuming a block created with cb.NewBlock(), this should not 159 // error even if the orderer metadata is an empty byte slice 160 if err != nil { 161 logger.Fatalf("[channel: %s] Error extracting orderer metadata: %s", cs.ChainID(), err) 162 } 163 logger.Debugf("[channel: %s] Retrieved metadata for tip of chain (blockNumber=%d, lastConfig=%d, lastConfigSeq=%d): %+v", cs.ChainID(), lastBlock.Header.Number, cs.lastConfig, cs.lastConfigSeq, metadata) 164 165 cs.chain, err = consenter.HandleChain(cs, metadata) 166 if err != nil { 167 logger.Fatalf("[channel: %s] Error creating consenter: %s", cs.ChainID(), err) 168 } 169 170 return cs 171 } 172 173 // createStandardFilters creates the set of filters for a normal (non-system) chain 174 func createStandardFilters(ledgerResources *ledgerResources) *filter.RuleSet { 175 return filter.NewRuleSet([]filter.Rule{ 176 filter.EmptyRejectRule, 177 sizefilter.MaxBytesRule(ledgerResources.SharedConfig()), 178 sigfilter.New(policies.ChannelWriters, ledgerResources.PolicyManager()), 179 configtxfilter.NewFilter(ledgerResources), 180 filter.AcceptRule, 181 }) 182 } 183 184 // createSystemChainFilters creates the set of filters for the ordering system chain 185 func createSystemChainFilters(ml *multiLedger, ledgerResources *ledgerResources) *filter.RuleSet { 186 return filter.NewRuleSet([]filter.Rule{ 187 filter.EmptyRejectRule, 188 sizefilter.MaxBytesRule(ledgerResources.SharedConfig()), 189 sigfilter.New(policies.ChannelWriters, ledgerResources.PolicyManager()), 190 newSystemChainFilter(ledgerResources, ml), 191 configtxfilter.NewFilter(ledgerResources), 192 filter.AcceptRule, 193 }) 194 } 195 196 func (cs *chainSupport) start() { 197 cs.chain.Start() 198 } 199 200 func (cs *chainSupport) NewSignatureHeader() (*cb.SignatureHeader, error) { 201 return cs.signer.NewSignatureHeader() 202 } 203 204 func (cs *chainSupport) Sign(message []byte) ([]byte, error) { 205 return cs.signer.Sign(message) 206 } 207 208 func (cs *chainSupport) Filters() *filter.RuleSet { 209 return cs.filters 210 } 211 212 func (cs *chainSupport) BlockCutter() blockcutter.Receiver { 213 return cs.cutter 214 } 215 216 func (cs *chainSupport) Reader() ledger.Reader { 217 return cs.ledger 218 } 219 220 func (cs *chainSupport) Enqueue(env *cb.Envelope) bool { 221 return cs.chain.Enqueue(env) 222 } 223 224 func (cs *chainSupport) Errored() <-chan struct{} { 225 return cs.chain.Errored() 226 } 227 228 func (cs *chainSupport) CreateNextBlock(messages []*cb.Envelope) *cb.Block { 229 return ledger.CreateNextBlock(cs.ledger, messages, cs.feeAddress, cs.blockVersion) 230 } 231 232 func (cs *chainSupport) addBlockSignature(block *cb.Block) { 233 logger.Debugf("%+v", cs) 234 logger.Debugf("%+v", cs.signer) 235 236 blockSignature := &cb.MetadataSignature{ 237 SignatureHeader: utils.MarshalOrPanic(utils.NewSignatureHeaderOrPanic(cs.signer)), 238 } 239 240 // Note, this value is intentionally nil, as this metadata is only about the signature, there is no additional metadata 241 // information required beyond the fact that the metadata item is signed. 242 blockSignatureValue := []byte(nil) 243 244 blockSignature.Signature = utils.SignOrPanic(cs.signer, util.ConcatenateBytes(blockSignatureValue, blockSignature.SignatureHeader, block.Header.Bytes())) 245 246 block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = utils.MarshalOrPanic(&cb.Metadata{ 247 Value: blockSignatureValue, 248 Signatures: []*cb.MetadataSignature{ 249 blockSignature, 250 }, 251 }) 252 } 253 254 func (cs *chainSupport) addLastConfigSignature(block *cb.Block) { 255 configSeq := cs.Sequence() 256 if configSeq > cs.lastConfigSeq { 257 logger.Debugf("[channel: %s] Detected lastConfigSeq transitioning from %d to %d, setting lastConfig from %d to %d", cs.ChainID(), cs.lastConfigSeq, configSeq, cs.lastConfig, block.Header.Number) 258 cs.lastConfig = block.Header.Number 259 cs.lastConfigSeq = configSeq 260 } 261 262 lastConfigSignature := &cb.MetadataSignature{ 263 SignatureHeader: utils.MarshalOrPanic(utils.NewSignatureHeaderOrPanic(cs.signer)), 264 } 265 266 lastConfigValue := utils.MarshalOrPanic(&cb.LastConfig{Index: cs.lastConfig}) 267 logger.Debugf("[channel: %s] About to write block, setting its LAST_CONFIG to %d", cs.ChainID(), cs.lastConfig) 268 269 lastConfigSignature.Signature = utils.SignOrPanic(cs.signer, util.ConcatenateBytes(lastConfigValue, lastConfigSignature.SignatureHeader, block.Header.Bytes())) 270 271 block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = utils.MarshalOrPanic(&cb.Metadata{ 272 Value: lastConfigValue, 273 Signatures: []*cb.MetadataSignature{ 274 lastConfigSignature, 275 }, 276 }) 277 } 278 279 func (cs *chainSupport) WriteBlock(block *cb.Block, committers []filter.Committer, encodedMetadataValue []byte) *cb.Block { 280 for _, committer := range committers { 281 committer.Commit() 282 } 283 // Set the orderer-related metadata field 284 if encodedMetadataValue != nil { 285 block.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER] = utils.MarshalOrPanic(&cb.Metadata{Value: encodedMetadataValue}) 286 } 287 cs.addBlockSignature(block) 288 cs.addLastConfigSignature(block) 289 290 err := cs.ledger.Append(block) 291 if err != nil { 292 logger.Panicf("[channel: %s] Could not append block: %s", cs.ChainID(), err) 293 } 294 logger.Debugf("[channel: %s] Wrote block %d", cs.ChainID(), block.GetHeader().Number) 295 296 return block 297 } 298 299 func (cs *chainSupport) Height() uint64 { 300 return cs.Reader().Height() 301 }