diff --git a/orderer/main.go b/orderer/main.go index cb23127b16dff9ac00ddefa0b83a9f41ebd8618a..512ca3219b048600226317845c8d6eb4bef86062 100644 --- a/orderer/main.go +++ b/orderer/main.go @@ -117,8 +117,6 @@ func main() { } } else { logger.Infof("Not bootstrapping because of existing chains") - logger.Warningf("XXXXXXX RESTART IS NOT CURRENTLY SUPPORTED XXXXXXXXX") - // XXX Remove this once restart is supported } if conf.Kafka.Verbose { diff --git a/orderer/multichain/chainsupport_test.go b/orderer/multichain/chainsupport_test.go index 29608c3cb82cc037a91bf5c140a00fe6d457c8fc..5507caf0a00352126ec5c769e294cfdfd4ca61b5 100644 --- a/orderer/multichain/chainsupport_test.go +++ b/orderer/multichain/chainsupport_test.go @@ -26,8 +26,6 @@ import ( cb "github.com/hyperledger/fabric/protos/common" ab "github.com/hyperledger/fabric/protos/orderer" "github.com/hyperledger/fabric/protos/utils" - - "github.com/golang/protobuf/proto" ) type mockLedgerReadWriter struct { @@ -94,17 +92,11 @@ func TestWriteLastConfiguration(t *testing.T) { cs := &chainSupport{ledger: ml, configManager: cm} lastConfig := func(block *cb.Block) uint64 { - md := &cb.Metadata{} - err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIGURATION], md) - if err != nil { - panic(err) - } - lc := &cb.LastConfiguration{} - err = proto.Unmarshal(md.Value, lc) + index, err := utils.GetLastConfigurationIndexFromBlock(block) if err != nil { panic(err) } - return lc.Index + return index } expected := uint64(0) diff --git a/orderer/multichain/manager.go b/orderer/multichain/manager.go index 6934323464addb25471da11fa895e04c977736ad..2cf7440c80c4b643756b48f751ac1da50a38d074 100644 --- a/orderer/multichain/manager.go +++ b/orderer/multichain/manager.go @@ -25,7 +25,7 @@ import ( "github.com/hyperledger/fabric/orderer/common/sharedconfig" "github.com/hyperledger/fabric/orderer/rawledger" cb "github.com/hyperledger/fabric/protos/common" - ab "github.com/hyperledger/fabric/protos/orderer" + "github.com/hyperledger/fabric/protos/utils" "github.com/op/go-logging" "github.com/golang/protobuf/proto" @@ -59,48 +59,18 @@ type multiLedger struct { sysChain *systemChain } -// getConfigTx, this should ultimately be done more intelligently, but for now, we search the whole chain for txs and pick the last config one func getConfigTx(reader rawledger.Reader) *cb.Envelope { - var lastConfigTx *cb.Envelope - - it, _ := reader.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}}) - // Iterate over the blockchain, looking for config transactions, track the most recent one encountered - // this will be the transaction which is returned - for { - select { - case <-it.ReadyChan(): - block, status := it.Next() - if status != cb.Status_SUCCESS { - logger.Fatalf("Error parsing blockchain at startup: %v", status) - } - // ConfigTxs should always be by themselves - if len(block.Data.Data) != 1 { - continue - } - - maybeConfigTx := &cb.Envelope{} - - err := proto.Unmarshal(block.Data.Data[0], maybeConfigTx) - - if err != nil { - logger.Fatalf("Found data which was not an envelope: %s", err) - } - - payload := &cb.Payload{} - if err = proto.Unmarshal(maybeConfigTx.Payload, payload); err != nil { - logger.Fatalf("Unable to unmarshal transaction payload: %s", err) - } - - if payload.Header.ChainHeader.Type != int32(cb.HeaderType_CONFIGURATION_TRANSACTION) { - continue - } - - logger.Debugf("Found configuration transaction for chain %x at block %d", payload.Header.ChainHeader.ChainID, block.Header.Number) - lastConfigTx = maybeConfigTx - default: - return lastConfigTx - } + lastBlock := rawledger.GetBlock(reader, reader.Height()-1) + index, err := utils.GetLastConfigurationIndexFromBlock(lastBlock) + if err != nil { + logger.Panicf("Chain did not have appropriately encoded last configuration in its latest block: %s", err) } + configBlock := rawledger.GetBlock(reader, index) + if configBlock == nil { + logger.Panicf("Configuration block does not exist") + } + + return utils.ExtractEnvelopeOrPanic(configBlock, 0) } // NewManagerImpl produces an instance of a Manager @@ -126,16 +96,16 @@ func NewManagerImpl(ledgerFactory rawledger.Factory, consenters map[string]Conse if sharedConfigManager.ChainCreators() != nil { if ml.sysChain != nil { - logger.Fatalf("There appear to be two system chains %x and %x", ml.sysChain.support.ChainID(), chainID) + logger.Fatalf("There appear to be two system chains %s and %s", ml.sysChain.support.ChainID(), chainID) } - logger.Debugf("Starting with system chain: %x", chainID) + logger.Debugf("Starting with system chain: %s", chainID) chain := newChainSupport(createSystemChainFilters(ml, configManager), configManager, policyManager, backingLedger, sharedConfigManager, consenters) ml.chains[string(chainID)] = chain ml.sysChain = newSystemChain(chain) // We delay starting this chain, as it might try to copy and replace the chains map via newChain before the map is fully built defer chain.start() } else { - logger.Debugf("Starting chain: %x", chainID) + logger.Debugf("Starting chain: %s", chainID) chain := newChainSupport(createStandardFilters(configManager), configManager, policyManager, backingLedger, sharedConfigManager, consenters) ml.chains[string(chainID)] = chain chain.start() diff --git a/orderer/multichain/manager_test.go b/orderer/multichain/manager_test.go index 564405bb0571dde139b2cbb616acac34ea0eefaf..dd5770c025405bb04cb61244606b76374f4fa1ba 100644 --- a/orderer/multichain/manager_test.go +++ b/orderer/multichain/manager_test.go @@ -85,7 +85,10 @@ func TestGetConfigTx(t *testing.T) { rl.Append(rawledger.CreateNextBlock(rl, []*cb.Envelope{makeConfigTx(provisional.TestChainID, 5)})) ctx := makeConfigTx(provisional.TestChainID, 6) rl.Append(rawledger.CreateNextBlock(rl, []*cb.Envelope{ctx})) - rl.Append(rawledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 7)})) + + block := rawledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 7)}) + block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIGURATION] = utils.MarshalOrPanic(&cb.Metadata{Value: utils.MarshalOrPanic(&cb.LastConfiguration{Index: 7})}) + rl.Append(block) pctx := getConfigTx(rl) @@ -104,11 +107,13 @@ func TestGetConfigTxFailure(t *testing.T) { })) } rl.Append(rawledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 11)})) - pctx := getConfigTx(rl) + defer func() { + if recover() == nil { + t.Fatalf("Should have panic-ed because there was no configuration tx") + } + }() + getConfigTx(rl) - if pctx != nil { - t.Fatalf("Should not have found a configuration tx") - } } // This test essentially brings the entire system up and is ultimately what main.go will replicate diff --git a/orderer/rawledger/blackbox_test.go b/orderer/rawledger/blackbox_test.go index 6dcf0af1151bcdd3189b193aab8d059f09ca480e..1f19e80006cf6eeab35b714e440378c853291c55 100644 --- a/orderer/rawledger/blackbox_test.go +++ b/orderer/rawledger/blackbox_test.go @@ -39,20 +39,6 @@ type ledgerTestFactory interface { var testables []ledgerTestable -func getBlock(number uint64, li ReadWriter) *cb.Block { - i, _ := li.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: number}}}) - select { - case <-i.ReadyChan(): - block, status := i.Next() - if status != cb.Status_SUCCESS { - return nil - } - return block - default: - return nil - } -} - func allTest(t *testing.T, test func(ledgerTestFactory, *testing.T)) { for _, lt := range testables { @@ -83,7 +69,7 @@ func testInitialization(lf ledgerTestFactory, t *testing.T) { if li.Height() != 1 { t.Fatalf("Block height should be 1") } - block := getBlock(0, li) + block := GetBlock(li, 0) if block == nil { t.Fatalf("Error retrieving genesis block") } @@ -109,7 +95,7 @@ func testReinitialization(lf ledgerTestFactory, t *testing.T) { if li.Height() != 2 { t.Fatalf("Block height should be 2") } - block := getBlock(1, li) + block := GetBlock(li, 1) if block == nil { t.Fatalf("Error retrieving block 1") } @@ -124,7 +110,7 @@ func TestAddition(t *testing.T) { func testAddition(lf ledgerTestFactory, t *testing.T) { _, li := lf.New() - genesis := getBlock(0, li) + genesis := GetBlock(li, 0) if genesis == nil { t.Fatalf("Could not retrieve genesis block") } @@ -134,7 +120,7 @@ func testAddition(lf ledgerTestFactory, t *testing.T) { if li.Height() != 2 { t.Fatalf("Block height should be 2") } - block := getBlock(1, li) + block := GetBlock(li, 1) if block == nil { t.Fatalf("Error retrieving genesis block") } @@ -255,7 +241,7 @@ func testMultichain(lf ledgerTestFactory, t *testing.T) { t.Fatalf("Error retrieving chain1: %s", err) } - if b := getBlock(1, c1); !reflect.DeepEqual(c1b1, b) { + if b := GetBlock(c1, 1); !reflect.DeepEqual(c1b1, b) { t.Fatalf("Did not properly store block 1 on chain 1:") } @@ -264,7 +250,7 @@ func testMultichain(lf ledgerTestFactory, t *testing.T) { t.Fatalf("Error retrieving chain2: %s", err) } - if b := getBlock(0, c2); reflect.DeepEqual(c2b0, b) { + if b := GetBlock(c2, 0); reflect.DeepEqual(c2b0, b) { t.Fatalf("Did not properly store block 1 on chain 1") } } diff --git a/orderer/rawledger/rawledger.go b/orderer/rawledger/rawledger.go index bba72c42039429778b30df5a362294c989b2d63e..b3bfed577b56032e13a1c82a5e2a8c97f89ac580 100644 --- a/orderer/rawledger/rawledger.go +++ b/orderer/rawledger/rawledger.go @@ -95,3 +95,18 @@ func CreateNextBlock(rl Reader, messages []*cb.Envelope) *cb.Block { return block } + +// GetBlock is a utility method for retrieving a single block +func GetBlock(rl Reader, index uint64) *cb.Block { + i, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: index}}}) + select { + case <-i.ReadyChan(): + block, status := i.Next() + if status != cb.Status_SUCCESS { + return nil + } + return block + default: + return nil + } +} diff --git a/protos/utils/blockutils.go b/protos/utils/blockutils.go index 2d88f46fbf22f4a09e343c285a4e1d7edcc95163..975cbbcc6308a3bce9d429c711ac4136768af57b 100644 --- a/protos/utils/blockutils.go +++ b/protos/utils/blockutils.go @@ -48,6 +48,21 @@ func GetChainIDFromBlock(block *cb.Block) (string, error) { return payload.Header.ChainHeader.ChainID, nil } +// GetLastConfigurationIndexFromBlock retrieves the index of the last configuration block as encoded in the block metadata +func GetLastConfigurationIndexFromBlock(block *cb.Block) (uint64, error) { + md := &cb.Metadata{} + err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIGURATION], md) + if err != nil { + return 0, err + } + lc := &cb.LastConfiguration{} + err = proto.Unmarshal(md.Value, lc) + if err != nil { + return 0, err + } + return lc.Index, nil +} + // GetBlockFromBlockBytes marshals the bytes into Block func GetBlockFromBlockBytes(blockBytes []byte) (*cb.Block, error) { block := &cb.Block{} diff --git a/protos/utils/configuration.go b/protos/utils/configuration.go index dfc3c0eed2319a6dbfe0cbd3c6f922bf238fe527..b0bf0ea9e431630c1a50d4c152ae96ea72bc351d 100644 --- a/protos/utils/configuration.go +++ b/protos/utils/configuration.go @@ -22,24 +22,32 @@ import ( ab "github.com/hyperledger/fabric/protos/orderer" ) -const CreationPolicyKey = "CreationPolicy" +const ( + CreationPolicyKey = "CreationPolicy" + ChainCreatorsKey = "ChainCreators" +) // ChainCreationConfiguration creates a new chain creation configuration envelope from // the supplied creationPolicy, new chainID, and a template configuration envelope // The template configuration envelope will have the correct chainID set on all items, // and the first item will be a CreationPolicy which is ready for the signatures as -// required by the policy +// required by the policy, it also strips out the ChainCreators item as this is invalid +// for the ordering system chain func ChainCreationConfiguration(creationPolicy, newChainID string, template *cb.ConfigurationEnvelope) *cb.ConfigurationEnvelope { - newConfigItems := make([]*cb.SignedConfigurationItem, len(template.Items)) + var newConfigItems []*cb.SignedConfigurationItem var hashBytes []byte - for i, item := range template.Items { + for _, item := range template.Items { configItem := UnmarshalConfigurationItemOrPanic(item.ConfigurationItem) + if configItem.Type == cb.ConfigurationItem_Orderer && configItem.Key == ChainCreatorsKey { + continue + } configItem.Header.ChainID = newChainID - newConfigItems[i] = &cb.SignedConfigurationItem{ + newConfigItem := &cb.SignedConfigurationItem{ ConfigurationItem: MarshalOrPanic(configItem), } - hashBytes = append(hashBytes, newConfigItems[i].ConfigurationItem...) + newConfigItems = append(newConfigItems, newConfigItem) + hashBytes = append(hashBytes, newConfigItem.ConfigurationItem...) } digest := cu.ComputeCryptoHash(hashBytes)