Skip to content

Commit 141ab4c

Browse files
author
Jason Yellick
committed
[FAB-1524] Reinitialize chains on orderer restart
https://jira.hyperledger.org/browse/FAB-1524 This changeset utilizes the last configuration embedded in the block metadata to properly initialize any existing chains. It also fixes the template based configuration transaction creation in the utilties to omit the ChainCreators configuration item which inappropriately flags a chain as an ordering system chain. Change-Id: Ia998b3c5d40d6fef6dad41c28cd86627c3d4b4e9 Signed-off-by: Jason Yellick <[email protected]>
1 parent 75909aa commit 141ab4c

File tree

8 files changed

+76
-87
lines changed

8 files changed

+76
-87
lines changed

orderer/main.go

-2
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,6 @@ func main() {
117117
}
118118
} else {
119119
logger.Infof("Not bootstrapping because of existing chains")
120-
logger.Warningf("XXXXXXX RESTART IS NOT CURRENTLY SUPPORTED XXXXXXXXX")
121-
// XXX Remove this once restart is supported
122120
}
123121

124122
if conf.Kafka.Verbose {

orderer/multichain/chainsupport_test.go

+2-10
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import (
2626
cb "github.com/hyperledger/fabric/protos/common"
2727
ab "github.com/hyperledger/fabric/protos/orderer"
2828
"github.com/hyperledger/fabric/protos/utils"
29-
30-
"github.com/golang/protobuf/proto"
3129
)
3230

3331
type mockLedgerReadWriter struct {
@@ -94,17 +92,11 @@ func TestWriteLastConfiguration(t *testing.T) {
9492
cs := &chainSupport{ledger: ml, configManager: cm}
9593

9694
lastConfig := func(block *cb.Block) uint64 {
97-
md := &cb.Metadata{}
98-
err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIGURATION], md)
99-
if err != nil {
100-
panic(err)
101-
}
102-
lc := &cb.LastConfiguration{}
103-
err = proto.Unmarshal(md.Value, lc)
95+
index, err := utils.GetLastConfigurationIndexFromBlock(block)
10496
if err != nil {
10597
panic(err)
10698
}
107-
return lc.Index
99+
return index
108100
}
109101

110102
expected := uint64(0)

orderer/multichain/manager.go

+14-44
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
"github.com/hyperledger/fabric/orderer/common/sharedconfig"
2626
"github.com/hyperledger/fabric/orderer/rawledger"
2727
cb "github.com/hyperledger/fabric/protos/common"
28-
ab "github.com/hyperledger/fabric/protos/orderer"
28+
"github.com/hyperledger/fabric/protos/utils"
2929
"github.com/op/go-logging"
3030

3131
"github.com/golang/protobuf/proto"
@@ -59,48 +59,18 @@ type multiLedger struct {
5959
sysChain *systemChain
6060
}
6161

62-
// getConfigTx, this should ultimately be done more intelligently, but for now, we search the whole chain for txs and pick the last config one
6362
func getConfigTx(reader rawledger.Reader) *cb.Envelope {
64-
var lastConfigTx *cb.Envelope
65-
66-
it, _ := reader.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}})
67-
// Iterate over the blockchain, looking for config transactions, track the most recent one encountered
68-
// this will be the transaction which is returned
69-
for {
70-
select {
71-
case <-it.ReadyChan():
72-
block, status := it.Next()
73-
if status != cb.Status_SUCCESS {
74-
logger.Fatalf("Error parsing blockchain at startup: %v", status)
75-
}
76-
// ConfigTxs should always be by themselves
77-
if len(block.Data.Data) != 1 {
78-
continue
79-
}
80-
81-
maybeConfigTx := &cb.Envelope{}
82-
83-
err := proto.Unmarshal(block.Data.Data[0], maybeConfigTx)
84-
85-
if err != nil {
86-
logger.Fatalf("Found data which was not an envelope: %s", err)
87-
}
88-
89-
payload := &cb.Payload{}
90-
if err = proto.Unmarshal(maybeConfigTx.Payload, payload); err != nil {
91-
logger.Fatalf("Unable to unmarshal transaction payload: %s", err)
92-
}
93-
94-
if payload.Header.ChainHeader.Type != int32(cb.HeaderType_CONFIGURATION_TRANSACTION) {
95-
continue
96-
}
97-
98-
logger.Debugf("Found configuration transaction for chain %x at block %d", payload.Header.ChainHeader.ChainID, block.Header.Number)
99-
lastConfigTx = maybeConfigTx
100-
default:
101-
return lastConfigTx
102-
}
63+
lastBlock := rawledger.GetBlock(reader, reader.Height()-1)
64+
index, err := utils.GetLastConfigurationIndexFromBlock(lastBlock)
65+
if err != nil {
66+
logger.Panicf("Chain did not have appropriately encoded last configuration in its latest block: %s", err)
10367
}
68+
configBlock := rawledger.GetBlock(reader, index)
69+
if configBlock == nil {
70+
logger.Panicf("Configuration block does not exist")
71+
}
72+
73+
return utils.ExtractEnvelopeOrPanic(configBlock, 0)
10474
}
10575

10676
// NewManagerImpl produces an instance of a Manager
@@ -126,16 +96,16 @@ func NewManagerImpl(ledgerFactory rawledger.Factory, consenters map[string]Conse
12696

12797
if sharedConfigManager.ChainCreators() != nil {
12898
if ml.sysChain != nil {
129-
logger.Fatalf("There appear to be two system chains %x and %x", ml.sysChain.support.ChainID(), chainID)
99+
logger.Fatalf("There appear to be two system chains %s and %s", ml.sysChain.support.ChainID(), chainID)
130100
}
131-
logger.Debugf("Starting with system chain: %x", chainID)
101+
logger.Debugf("Starting with system chain: %s", chainID)
132102
chain := newChainSupport(createSystemChainFilters(ml, configManager), configManager, policyManager, backingLedger, sharedConfigManager, consenters)
133103
ml.chains[string(chainID)] = chain
134104
ml.sysChain = newSystemChain(chain)
135105
// We delay starting this chain, as it might try to copy and replace the chains map via newChain before the map is fully built
136106
defer chain.start()
137107
} else {
138-
logger.Debugf("Starting chain: %x", chainID)
108+
logger.Debugf("Starting chain: %s", chainID)
139109
chain := newChainSupport(createStandardFilters(configManager), configManager, policyManager, backingLedger, sharedConfigManager, consenters)
140110
ml.chains[string(chainID)] = chain
141111
chain.start()

orderer/multichain/manager_test.go

+10-5
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ func TestGetConfigTx(t *testing.T) {
8585
rl.Append(rawledger.CreateNextBlock(rl, []*cb.Envelope{makeConfigTx(provisional.TestChainID, 5)}))
8686
ctx := makeConfigTx(provisional.TestChainID, 6)
8787
rl.Append(rawledger.CreateNextBlock(rl, []*cb.Envelope{ctx}))
88-
rl.Append(rawledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 7)}))
88+
89+
block := rawledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 7)})
90+
block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIGURATION] = utils.MarshalOrPanic(&cb.Metadata{Value: utils.MarshalOrPanic(&cb.LastConfiguration{Index: 7})})
91+
rl.Append(block)
8992

9093
pctx := getConfigTx(rl)
9194

@@ -104,11 +107,13 @@ func TestGetConfigTxFailure(t *testing.T) {
104107
}))
105108
}
106109
rl.Append(rawledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 11)}))
107-
pctx := getConfigTx(rl)
110+
defer func() {
111+
if recover() == nil {
112+
t.Fatalf("Should have panic-ed because there was no configuration tx")
113+
}
114+
}()
115+
getConfigTx(rl)
108116

109-
if pctx != nil {
110-
t.Fatalf("Should not have found a configuration tx")
111-
}
112117
}
113118

114119
// This test essentially brings the entire system up and is ultimately what main.go will replicate

orderer/rawledger/blackbox_test.go

+6-20
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,6 @@ type ledgerTestFactory interface {
3939

4040
var testables []ledgerTestable
4141

42-
func getBlock(number uint64, li ReadWriter) *cb.Block {
43-
i, _ := li.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: number}}})
44-
select {
45-
case <-i.ReadyChan():
46-
block, status := i.Next()
47-
if status != cb.Status_SUCCESS {
48-
return nil
49-
}
50-
return block
51-
default:
52-
return nil
53-
}
54-
}
55-
5642
func allTest(t *testing.T, test func(ledgerTestFactory, *testing.T)) {
5743
for _, lt := range testables {
5844

@@ -83,7 +69,7 @@ func testInitialization(lf ledgerTestFactory, t *testing.T) {
8369
if li.Height() != 1 {
8470
t.Fatalf("Block height should be 1")
8571
}
86-
block := getBlock(0, li)
72+
block := GetBlock(li, 0)
8773
if block == nil {
8874
t.Fatalf("Error retrieving genesis block")
8975
}
@@ -109,7 +95,7 @@ func testReinitialization(lf ledgerTestFactory, t *testing.T) {
10995
if li.Height() != 2 {
11096
t.Fatalf("Block height should be 2")
11197
}
112-
block := getBlock(1, li)
98+
block := GetBlock(li, 1)
11399
if block == nil {
114100
t.Fatalf("Error retrieving block 1")
115101
}
@@ -124,7 +110,7 @@ func TestAddition(t *testing.T) {
124110

125111
func testAddition(lf ledgerTestFactory, t *testing.T) {
126112
_, li := lf.New()
127-
genesis := getBlock(0, li)
113+
genesis := GetBlock(li, 0)
128114
if genesis == nil {
129115
t.Fatalf("Could not retrieve genesis block")
130116
}
@@ -134,7 +120,7 @@ func testAddition(lf ledgerTestFactory, t *testing.T) {
134120
if li.Height() != 2 {
135121
t.Fatalf("Block height should be 2")
136122
}
137-
block := getBlock(1, li)
123+
block := GetBlock(li, 1)
138124
if block == nil {
139125
t.Fatalf("Error retrieving genesis block")
140126
}
@@ -255,7 +241,7 @@ func testMultichain(lf ledgerTestFactory, t *testing.T) {
255241
t.Fatalf("Error retrieving chain1: %s", err)
256242
}
257243

258-
if b := getBlock(1, c1); !reflect.DeepEqual(c1b1, b) {
244+
if b := GetBlock(c1, 1); !reflect.DeepEqual(c1b1, b) {
259245
t.Fatalf("Did not properly store block 1 on chain 1:")
260246
}
261247

@@ -264,7 +250,7 @@ func testMultichain(lf ledgerTestFactory, t *testing.T) {
264250
t.Fatalf("Error retrieving chain2: %s", err)
265251
}
266252

267-
if b := getBlock(0, c2); reflect.DeepEqual(c2b0, b) {
253+
if b := GetBlock(c2, 0); reflect.DeepEqual(c2b0, b) {
268254
t.Fatalf("Did not properly store block 1 on chain 1")
269255
}
270256
}

orderer/rawledger/rawledger.go

+15
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,18 @@ func CreateNextBlock(rl Reader, messages []*cb.Envelope) *cb.Block {
9595

9696
return block
9797
}
98+
99+
// GetBlock is a utility method for retrieving a single block
100+
func GetBlock(rl Reader, index uint64) *cb.Block {
101+
i, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: index}}})
102+
select {
103+
case <-i.ReadyChan():
104+
block, status := i.Next()
105+
if status != cb.Status_SUCCESS {
106+
return nil
107+
}
108+
return block
109+
default:
110+
return nil
111+
}
112+
}

protos/utils/blockutils.go

+15
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ func GetChainIDFromBlock(block *cb.Block) (string, error) {
4848
return payload.Header.ChainHeader.ChainID, nil
4949
}
5050

51+
// GetLastConfigurationIndexFromBlock retrieves the index of the last configuration block as encoded in the block metadata
52+
func GetLastConfigurationIndexFromBlock(block *cb.Block) (uint64, error) {
53+
md := &cb.Metadata{}
54+
err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIGURATION], md)
55+
if err != nil {
56+
return 0, err
57+
}
58+
lc := &cb.LastConfiguration{}
59+
err = proto.Unmarshal(md.Value, lc)
60+
if err != nil {
61+
return 0, err
62+
}
63+
return lc.Index, nil
64+
}
65+
5166
// GetBlockFromBlockBytes marshals the bytes into Block
5267
func GetBlockFromBlockBytes(blockBytes []byte) (*cb.Block, error) {
5368
block := &cb.Block{}

protos/utils/configuration.go

+14-6
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,32 @@ import (
2222
ab "github.com/hyperledger/fabric/protos/orderer"
2323
)
2424

25-
const CreationPolicyKey = "CreationPolicy"
25+
const (
26+
CreationPolicyKey = "CreationPolicy"
27+
ChainCreatorsKey = "ChainCreators"
28+
)
2629

2730
// ChainCreationConfiguration creates a new chain creation configuration envelope from
2831
// the supplied creationPolicy, new chainID, and a template configuration envelope
2932
// The template configuration envelope will have the correct chainID set on all items,
3033
// and the first item will be a CreationPolicy which is ready for the signatures as
31-
// required by the policy
34+
// required by the policy, it also strips out the ChainCreators item as this is invalid
35+
// for the ordering system chain
3236
func ChainCreationConfiguration(creationPolicy, newChainID string, template *cb.ConfigurationEnvelope) *cb.ConfigurationEnvelope {
33-
newConfigItems := make([]*cb.SignedConfigurationItem, len(template.Items))
37+
var newConfigItems []*cb.SignedConfigurationItem
3438
var hashBytes []byte
3539

36-
for i, item := range template.Items {
40+
for _, item := range template.Items {
3741
configItem := UnmarshalConfigurationItemOrPanic(item.ConfigurationItem)
42+
if configItem.Type == cb.ConfigurationItem_Orderer && configItem.Key == ChainCreatorsKey {
43+
continue
44+
}
3845
configItem.Header.ChainID = newChainID
39-
newConfigItems[i] = &cb.SignedConfigurationItem{
46+
newConfigItem := &cb.SignedConfigurationItem{
4047
ConfigurationItem: MarshalOrPanic(configItem),
4148
}
42-
hashBytes = append(hashBytes, newConfigItems[i].ConfigurationItem...)
49+
newConfigItems = append(newConfigItems, newConfigItem)
50+
hashBytes = append(hashBytes, newConfigItem.ConfigurationItem...)
4351
}
4452

4553
digest := cu.ComputeCryptoHash(hashBytes)

0 commit comments

Comments
 (0)