Skip to content

Commit d92a41d

Browse files
yacovmguoger
authored andcommitted
[FAB-12635] Inspect channels of system chain
This change set adds support for inspecting all channels inside a system chain. It pulls all blocks of the system channel, and then inspects the blocks and returns all blocks that are creations of new channels. Change-Id: I714f4f1537be3d7e2d88046561c155545e2f2037 Signed-off-by: yacovm <yacovm@il.ibm.com>
1 parent a65b055 commit d92a41d

File tree

4 files changed

+524
-0
lines changed

4 files changed

+524
-0
lines changed

orderer/common/cluster/mocks/chain_puller.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

orderer/common/cluster/replication.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/hyperledger/fabric/common/flogging"
1616
"github.com/hyperledger/fabric/core/comm"
1717
"github.com/hyperledger/fabric/protos/common"
18+
"github.com/hyperledger/fabric/protos/utils"
1819
"github.com/pkg/errors"
1920
)
2021

@@ -84,3 +85,115 @@ type NoopBlockVerifier struct{}
8485
func (*NoopBlockVerifier) VerifyBlockSignature(sd []*common.SignedData, config *common.ConfigEnvelope) error {
8586
return nil
8687
}
88+
89+
//go:generate mockery -dir . -name ChainPuller -case underscore -output mocks/
90+
91+
// ChainPuller pulls blocks from a chain
92+
type ChainPuller interface {
93+
// PullBlock pulls the given block from some orderer node
94+
PullBlock(seq uint64) *common.Block
95+
// Close closes the ChainPuller
96+
Close()
97+
}
98+
99+
// ChainInspector walks over a chain
100+
type ChainInspector struct {
101+
Logger *flogging.FabricLogger
102+
Puller ChainPuller
103+
LastConfigBlock *common.Block
104+
}
105+
106+
// Channels returns the list of channels
107+
// that exist in the chain
108+
func (cw *ChainInspector) Channels() []string {
109+
channels := make(map[string]struct{})
110+
lastConfigBlockNum := cw.LastConfigBlock.Header.Number
111+
var block *common.Block
112+
for seq := uint64(1); seq < lastConfigBlockNum; seq++ {
113+
block = cw.Puller.PullBlock(seq)
114+
channel, err := IsNewChannelBlock(block)
115+
if err != nil {
116+
// If we failed to classify a block, something is wrong in the system chain
117+
// we're trying to pull, so abort.
118+
cw.Logger.Panic("Failed classifying block", seq, ":", err)
119+
continue
120+
}
121+
if channel == "" {
122+
cw.Logger.Info("Block", seq, "doesn't contain a new channel")
123+
continue
124+
}
125+
cw.Logger.Info("Block", seq, "contains channel", channel)
126+
channels[channel] = struct{}{}
127+
}
128+
// At this point, block holds reference to the last block pulled.
129+
// We ensure that the hash of the last block pulled, is the previous hash
130+
// of the LastConfigBlock we were initialized with.
131+
// We don't need to verify the entire chain of all blocks we pulled,
132+
// because the block puller calls VerifyBlockHash on all blocks it pulls.
133+
last2Blocks := []*common.Block{block, cw.LastConfigBlock}
134+
if err := VerifyBlockHash(1, last2Blocks); err != nil {
135+
cw.Logger.Panic("System channel pulled doesn't match the boot last config block:", err)
136+
}
137+
138+
return flattenChannelMap(channels)
139+
}
140+
141+
func flattenChannelMap(m map[string]struct{}) []string {
142+
var res []string
143+
for channel := range m {
144+
res = append(res, channel)
145+
}
146+
return res
147+
}
148+
149+
// IsNewChannelBlock returns a name of the channel in case
150+
// it holds a channel create transaction, or empty string otherwise.
151+
func IsNewChannelBlock(block *common.Block) (string, error) {
152+
if block == nil {
153+
return "", errors.New("nil block")
154+
}
155+
env, err := utils.ExtractEnvelope(block, 0)
156+
if err != nil {
157+
return "", err
158+
}
159+
payload, err := utils.ExtractPayload(env)
160+
if err != nil {
161+
return "", err
162+
}
163+
if payload.Header == nil {
164+
return "", errors.New("nil header in payload")
165+
}
166+
chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
167+
if err != nil {
168+
return "", err
169+
}
170+
// The transaction is an orderer transaction
171+
if common.HeaderType(chdr.Type) != common.HeaderType_ORDERER_TRANSACTION {
172+
return "", nil
173+
}
174+
systemChannelName := chdr.ChannelId
175+
innerEnvelope, err := utils.UnmarshalEnvelope(payload.Data)
176+
if err != nil {
177+
return "", err
178+
}
179+
innerPayload, err := utils.UnmarshalPayload(innerEnvelope.Payload)
180+
if err != nil {
181+
return "", err
182+
}
183+
if innerPayload.Header == nil {
184+
return "", errors.New("inner payload's header is nil")
185+
}
186+
chdr, err = utils.UnmarshalChannelHeader(innerPayload.Header.ChannelHeader)
187+
if err != nil {
188+
return "", err
189+
}
190+
// The inner payload's header is a config transaction
191+
if common.HeaderType(chdr.Type) != common.HeaderType_CONFIG {
192+
return "", nil
193+
}
194+
// In any case, exclude all system channel transactions
195+
if chdr.ChannelId == systemChannelName {
196+
return "", nil
197+
}
198+
return chdr.ChannelId, nil
199+
}

0 commit comments

Comments
 (0)