Skip to content

Commit 4b0176a

Browse files
committed
[FAB-1774] Use metadata field for orderer info
https://jira.hyperledger.org/browse/FAB-1774 A consensus implementation may need to read the ledger and extract metadata from it during operation (most likely when booting up). As an example, the Kafka-based orderer, should read the offset of the last envelope it placed into a block and wrote to the local ledger, and should use that offset to resume consumption of chain's partition. This changeset follows up on the work of FAB-1773 [1]. Specifically, it modifies: 1. The common components `Consenter` interface so that the newly-introduced metadata field is passed on to the consensus implementations via the `HandleChain` method. 2. The `WriteBlock` method of the `ConsenterSupport` interface so that this metadata can be persisted to blocks. It also adds relevant unit tests. This is a precursor to FAB-1623 [2], which will add restart support to the Kafka-based orderer. Review starting point: fabric/orderer/multichain/chainsupport.go [1] https://jira.hyperledger.org/browse/FAB-1773 [2] https://jira.hyperledger.org/browse/FAB-1623 Change-Id: I3d1c932eb30537f6a1aa8056b9d38550b17dee6d Signed-off-by: Kostas Christidis <[email protected]>
1 parent d5a70d1 commit 4b0176a

File tree

9 files changed

+109
-45
lines changed

9 files changed

+109
-45
lines changed

orderer/kafka/orderer.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ type consenterImpl struct {
7878
// HandleChain creates/returns a reference to a Chain for the given set of support resources.
7979
// Implements the multichain.Consenter interface. Called by multichain.newChainSupport(), which
8080
// is itself called by multichain.NewManagerImpl() when ranging over the ledgerFactory's existingChains.
81-
func (co *consenterImpl) HandleChain(cs multichain.ConsenterSupport) (multichain.Chain, error) {
81+
func (co *consenterImpl) HandleChain(cs multichain.ConsenterSupport, metadata *cb.Metadata) (multichain.Chain, error) {
8282
return newChain(co, cs), nil
8383
}
8484

@@ -237,7 +237,7 @@ func (ch *chainImpl) loop() {
237237
return
238238
}
239239
block := ch.support.CreateNextBlock(batch)
240-
ch.support.WriteBlock(block, committers)
240+
ch.support.WriteBlock(block, committers, nil)
241241
ch.lastCutBlock++
242242
logger.Debug("Proper time-to-cut received, just cut block", ch.lastCutBlock)
243243
continue
@@ -264,7 +264,7 @@ func (ch *chainImpl) loop() {
264264
// If !ok, batches == nil, so this will be skipped
265265
for i, batch := range batches {
266266
block := ch.support.CreateNextBlock(batch)
267-
ch.support.WriteBlock(block, committers[i])
267+
ch.support.WriteBlock(block, committers[i], nil)
268268
ch.lastCutBlock++
269269
logger.Debug("Batch filled, just cut block", ch.lastCutBlock)
270270
}

orderer/mocks/multichain/multichain.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,14 @@ func (mcs *ConsenterSupport) CreateNextBlock(data []*cb.Envelope) *cb.Block {
6969

7070
// WriteBlock writes data to the Batches channel
7171
// Note that _committers is ignored by this mock implementation
72-
func (mcs *ConsenterSupport) WriteBlock(block *cb.Block, _committers []filter.Committer) *cb.Block {
72+
func (mcs *ConsenterSupport) WriteBlock(block *cb.Block, _committers []filter.Committer, encodedMetadataValue []byte) *cb.Block {
7373
logger.Debugf("mockWriter: attempting to write batch")
7474
umtxs := make([]*cb.Envelope, len(block.Data.Data))
7575
for i := range block.Data.Data {
7676
umtxs[i] = utils.UnmarshalEnvelopeOrPanic(block.Data.Data[i])
7777
}
7878
mcs.Batches <- umtxs
79+
block.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER] = utils.MarshalOrPanic(&cb.Metadata{Value: encodedMetadataValue})
7980
return block
8081
}
8182

orderer/multichain/chainsupport.go

+21-4
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ type Consenter interface {
3737
// HandleChain should create a return a reference to a Chain for the given set of resources
3838
// It will only be invoked for a given chain once per process. In general, errors will be treated
3939
// as irrecoverable and cause system shutdown. See the description of Chain for more details
40-
HandleChain(support ConsenterSupport) (Chain, error)
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)
4144
}
4245

4346
// Chain defines a way to inject messages for ordering
@@ -65,7 +68,7 @@ type ConsenterSupport interface {
6568
BlockCutter() blockcutter.Receiver
6669
SharedConfig() sharedconfig.Manager
6770
CreateNextBlock(messages []*cb.Envelope) *cb.Block
68-
WriteBlock(block *cb.Block, committers []filter.Committer) *cb.Block
71+
WriteBlock(block *cb.Block, committers []filter.Committer, encodedMetadataValue []byte) *cb.Block
6972
ChainID() string // ChainID returns the chain ID this specific consenter instance is associated with
7073
}
7174

@@ -128,7 +131,16 @@ func newChainSupport(
128131
}
129132

130133
var err error
131-
cs.chain, err = consenter.HandleChain(cs)
134+
135+
lastBlock := ordererledger.GetBlock(cs.Reader(), cs.Reader().Height()-1)
136+
metadata, err := utils.GetMetadataFromBlock(lastBlock, cb.BlockMetadataIndex_ORDERER)
137+
// Assuming a block created with cb.NewBlock(), this should not
138+
// error even if the orderer metadata is an empty byte slice
139+
if err != nil {
140+
logger.Fatalf("Error extracting orderer metadata for chain %x: %s", configManager.ChainID(), err)
141+
}
142+
143+
cs.chain, err = consenter.HandleChain(cs, metadata)
132144
if err != nil {
133145
logger.Fatalf("Error creating consenter for chain %x: %s", configManager.ChainID(), err)
134146
}
@@ -253,11 +265,16 @@ func (cs *chainSupport) addLastConfigSignature(block *cb.Block) {
253265
})
254266
}
255267

256-
func (cs *chainSupport) WriteBlock(block *cb.Block, committers []filter.Committer) *cb.Block {
268+
func (cs *chainSupport) WriteBlock(block *cb.Block, committers []filter.Committer, encodedMetadataValue []byte) *cb.Block {
257269
for _, committer := range committers {
258270
committer.Commit()
259271
}
260272

273+
// Set the orderer-related metadata field
274+
if encodedMetadataValue != nil {
275+
block.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER] = utils.MarshalOrPanic(&cb.Metadata{Value: encodedMetadataValue})
276+
}
277+
261278
cs.addBlockSignature(block)
262279
cs.addLastConfigSignature(block)
263280

orderer/multichain/chainsupport_test.go

+26-22
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"reflect"
2121
"testing"
2222

23+
"github.com/golang/protobuf/proto"
2324
"github.com/hyperledger/fabric/orderer/common/filter"
2425
ordererledger "github.com/hyperledger/fabric/orderer/ledger"
2526
mockconfigtx "github.com/hyperledger/fabric/orderer/mocks/configtx"
@@ -68,7 +69,7 @@ func TestCommitConfig(t *testing.T) {
6869
txs := []*cb.Envelope{makeNormalTx("foo", 0), makeNormalTx("bar", 1)}
6970
committers := []filter.Committer{&mockCommitter{}, &mockCommitter{}}
7071
block := cs.CreateNextBlock(txs)
71-
cs.WriteBlock(block, committers)
72+
cs.WriteBlock(block, committers, nil)
7273

7374
blockTXs := make([]*cb.Envelope, len(ml.data))
7475
for i := range ml.data {
@@ -91,48 +92,51 @@ func TestWriteBlockSignatures(t *testing.T) {
9192
cm := &mockconfigtx.Manager{}
9293
cs := &chainSupport{ledger: ml, configManager: cm, signer: &xxxCryptoHelper{}}
9394

94-
blockMetadata := func(block *cb.Block) *cb.Metadata {
95-
metadata, err := utils.GetMetadataFromBlock(block, cb.BlockMetadataIndex_SIGNATURES)
96-
if err != nil {
97-
panic(err)
98-
}
99-
return metadata
100-
}
101-
102-
if blockMetadata(cs.WriteBlock(cb.NewBlock(0, nil), nil)) == nil {
95+
if utils.GetMetadataFromBlockOrPanic(cs.WriteBlock(cb.NewBlock(0, nil), nil, nil), cb.BlockMetadataIndex_SIGNATURES) == nil {
10396
t.Fatalf("Block should have block signature")
10497
}
10598
}
10699

107-
func TestWriteLastConfiguration(t *testing.T) {
100+
func TestWriteBlockOrdererMetadata(t *testing.T) {
108101
ml := &mockLedgerReadWriter{}
109102
cm := &mockconfigtx.Manager{}
110103
cs := &chainSupport{ledger: ml, configManager: cm, signer: &xxxCryptoHelper{}}
111104

112-
lastConfig := func(block *cb.Block) uint64 {
113-
index, err := utils.GetLastConfigurationIndexFromBlock(block)
114-
if err != nil {
115-
panic(err)
116-
}
117-
return index
105+
value := []byte("foo")
106+
expected := &cb.Metadata{Value: value}
107+
actual := utils.GetMetadataFromBlockOrPanic(cs.WriteBlock(cb.NewBlock(0, nil), nil, value), cb.BlockMetadataIndex_ORDERER)
108+
109+
if actual == nil {
110+
t.Fatalf("Block should have orderer metadata written")
111+
}
112+
if !proto.Equal(expected, actual) {
113+
t.Fatalf("Orderer metadata not written to block correctly")
118114
}
119115

116+
}
117+
118+
func TestWriteLastConfiguration(t *testing.T) {
119+
ml := &mockLedgerReadWriter{}
120+
cm := &mockconfigtx.Manager{}
121+
cs := &chainSupport{ledger: ml, configManager: cm, signer: &xxxCryptoHelper{}}
122+
120123
expected := uint64(0)
121-
if lc := lastConfig(cs.WriteBlock(cb.NewBlock(0, nil), nil)); lc != expected {
124+
125+
if lc := utils.GetLastConfigurationIndexFromBlockOrPanic(cs.WriteBlock(cb.NewBlock(0, nil), nil, nil)); lc != expected {
122126
t.Fatalf("First block should have config block index of %d, but got %d", expected, lc)
123127
}
124-
125-
if lc := lastConfig(cs.WriteBlock(cb.NewBlock(1, nil), nil)); lc != expected {
128+
if lc := utils.GetLastConfigurationIndexFromBlockOrPanic(cs.WriteBlock(cb.NewBlock(1, nil), nil, nil)); lc != expected {
126129
t.Fatalf("Second block should have config block index of %d, but got %d", expected, lc)
127130
}
128131

129132
cm.SequenceVal = 1
130133
expected = uint64(2)
131-
if lc := lastConfig(cs.WriteBlock(cb.NewBlock(2, nil), nil)); lc != expected {
134+
135+
if lc := utils.GetLastConfigurationIndexFromBlockOrPanic(cs.WriteBlock(cb.NewBlock(2, nil), nil, nil)); lc != expected {
132136
t.Fatalf("Second block should have config block index of %d, but got %d", expected, lc)
133137
}
134138

135-
if lc := lastConfig(cs.WriteBlock(cb.NewBlock(3, nil), nil)); lc != expected {
139+
if lc := utils.GetLastConfigurationIndexFromBlockOrPanic(cs.WriteBlock(cb.NewBlock(3, nil), nil, nil)); lc != expected {
136140
t.Fatalf("Second block should have config block index of %d, but got %d", expected, lc)
137141
}
138142

orderer/multichain/util_test.go

+12-10
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,22 @@ import (
2727
type mockConsenter struct {
2828
}
2929

30-
func (mc *mockConsenter) HandleChain(support ConsenterSupport) (Chain, error) {
30+
func (mc *mockConsenter) HandleChain(support ConsenterSupport, metadata *cb.Metadata) (Chain, error) {
3131
return &mockChain{
32-
queue: make(chan *cb.Envelope),
33-
cutter: support.BlockCutter(),
34-
support: support,
35-
done: make(chan struct{}),
32+
queue: make(chan *cb.Envelope),
33+
cutter: support.BlockCutter(),
34+
support: support,
35+
metadata: metadata,
36+
done: make(chan struct{}),
3637
}, nil
3738
}
3839

3940
type mockChain struct {
40-
queue chan *cb.Envelope
41-
support ConsenterSupport
42-
cutter blockcutter.Receiver
43-
done chan struct{}
41+
queue chan *cb.Envelope
42+
cutter blockcutter.Receiver
43+
support ConsenterSupport
44+
metadata *cb.Metadata
45+
done chan struct{}
4446
}
4547

4648
func (mch *mockChain) Enqueue(env *cb.Envelope) bool {
@@ -59,7 +61,7 @@ func (mch *mockChain) Start() {
5961
batches, committers, _ := mch.cutter.Ordered(msg)
6062
for i, batch := range batches {
6163
block := mch.support.CreateNextBlock(batch)
62-
mch.support.WriteBlock(block, committers[i])
64+
mch.support.WriteBlock(block, committers[i], nil)
6365
}
6466
}
6567
}()

orderer/sbft/main/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func New() multichain.Consenter {
7979
}
8080

8181
// HandleChain creates/returns a reference to a Chain for the given set of support resources.
82-
func (solo *consenter) HandleChain(support multichain.ConsenterSupport) (multichain.Chain, error) {
82+
func (solo *consenter) HandleChain(support multichain.ConsenterSupport, metadata *cb.Metadata) (multichain.Chain, error) {
8383
return newChain(support), nil
8484
}
8585

orderer/solo/consensus.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func New() multichain.Consenter {
4343
return &consenter{}
4444
}
4545

46-
func (solo *consenter) HandleChain(support multichain.ConsenterSupport) (multichain.Chain, error) {
46+
func (solo *consenter) HandleChain(support multichain.ConsenterSupport, metadata *cb.Metadata) (multichain.Chain, error) {
4747
return newChain(support), nil
4848
}
4949

@@ -92,7 +92,7 @@ func (ch *chain) main() {
9292
}
9393
for i, batch := range batches {
9494
block := ch.support.CreateNextBlock(batch)
95-
ch.support.WriteBlock(block, committers[i])
95+
ch.support.WriteBlock(block, committers[i], nil)
9696
}
9797
if len(batches) > 0 {
9898
timer = nil
@@ -108,7 +108,7 @@ func (ch *chain) main() {
108108
}
109109
logger.Debugf("Batch timer expired, creating block")
110110
block := ch.support.CreateNextBlock(batch)
111-
ch.support.WriteBlock(block, committers)
111+
ch.support.WriteBlock(block, committers, nil)
112112
case <-ch.exitChan:
113113
logger.Debugf("Exiting")
114114
return

protos/utils/blockutils.go

+25-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func GetChainIDFromBlock(block *cb.Block) (string, error) {
4545
return payload.Header.ChainHeader.ChainID, nil
4646
}
4747

48-
// GetMetadataFromBlock retrieves metadata at the specified index
48+
// GetMetadataFromBlock retrieves metadata at the specified index.
4949
func GetMetadataFromBlock(block *cb.Block, index cb.BlockMetadataIndex) (*cb.Metadata, error) {
5050
md := &cb.Metadata{}
5151
err := proto.Unmarshal(block.Metadata.Metadata[index], md)
@@ -55,6 +55,16 @@ func GetMetadataFromBlock(block *cb.Block, index cb.BlockMetadataIndex) (*cb.Met
5555
return md, nil
5656
}
5757

58+
// GetMetadataFromBlockOrPanic retrieves metadata at the specified index, or panics on error.
59+
func GetMetadataFromBlockOrPanic(block *cb.Block, index cb.BlockMetadataIndex) *cb.Metadata {
60+
md := &cb.Metadata{}
61+
err := proto.Unmarshal(block.Metadata.Metadata[index], md)
62+
if err != nil {
63+
panic(err)
64+
}
65+
return md
66+
}
67+
5868
// GetLastConfigurationIndexFromBlock retrieves the index of the last configuration block as encoded in the block metadata
5969
func GetLastConfigurationIndexFromBlock(block *cb.Block) (uint64, error) {
6070
md, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIGURATION)
@@ -69,6 +79,20 @@ func GetLastConfigurationIndexFromBlock(block *cb.Block) (uint64, error) {
6979
return lc.Index, nil
7080
}
7181

82+
// GetLastConfigurationIndexFromBlockOrPanic retrieves the index of the last configuration block as encoded in the block metadata, or panics on error.
83+
func GetLastConfigurationIndexFromBlockOrPanic(block *cb.Block) uint64 {
84+
md, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIGURATION)
85+
if err != nil {
86+
panic(err)
87+
}
88+
lc := &cb.LastConfiguration{}
89+
err = proto.Unmarshal(md.Value, lc)
90+
if err != nil {
91+
panic(err)
92+
}
93+
return lc.Index
94+
}
95+
7296
// GetBlockFromBlockBytes marshals the bytes into Block
7397
func GetBlockFromBlockBytes(blockBytes []byte) (*cb.Block, error) {
7498
block := &cb.Block{}

protos/utils/blockutils_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
configtxtest "github.com/hyperledger/fabric/common/configtx/test"
2424
"github.com/hyperledger/fabric/protos/common"
25+
cb "github.com/hyperledger/fabric/protos/common"
2526
"github.com/hyperledger/fabric/protos/utils"
2627
)
2728

@@ -68,3 +69,18 @@ func TestGetBlockFromBlockBytes(t *testing.T) {
6869
t.Fatalf("failed to get block from block bytes: %s", err)
6970
}
7071
}
72+
73+
func TestGetMetadataFromNewBlock(t *testing.T) {
74+
block := common.NewBlock(0, nil)
75+
md, err := utils.GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER)
76+
if err != nil {
77+
t.Fatal("Expected no error when extracting metadata from new block")
78+
}
79+
if md.Value != nil {
80+
t.Fatal("Expected metadata field value to be nil, got", md.Value)
81+
}
82+
if len(md.Value) > 0 {
83+
t.Fatal("Expected length of metadata field value to be 0, got", len(md.Value))
84+
}
85+
86+
}

0 commit comments

Comments
 (0)