Skip to content

Commit

Permalink
Follower: check if join block different from fetched block (#4265)
Browse files Browse the repository at this point in the history
- before fetched block is committed
- don't panic
- continue to retry
- improve channel participation Verifier to check DataHash== hash(block.Data)


Change-Id: I7089df4f1bfc200a5f17582df0da7d4172f8f09b

Signed-off-by: Yoav Tock <tock@il.ibm.com>
  • Loading branch information
tock-ibm authored Jun 8, 2023
1 parent a515b98 commit 71ce0ab
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 135 deletions.
69 changes: 36 additions & 33 deletions cmd/osnadmin/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ var _ = Describe("osnadmin", func() {

BeforeEach(func() {
var err error
tempDir, err = ioutil.TempDir("", "osnadmin")
tempDir, err = os.MkdirTemp("", "osnadmin")
Expect(err).NotTo(HaveOccurred())

generateCertificates(tempDir)
Expand Down Expand Up @@ -782,46 +782,49 @@ func generateCertificates(tempDir string) {
}

func blockWithGroups(groups map[string]*cb.ConfigGroup, channelID string) *cb.Block {
return &cb.Block{
Data: &cb.BlockData{
Data: [][]byte{
protoutil.MarshalOrPanic(&cb.Envelope{
Payload: protoutil.MarshalOrPanic(&cb.Payload{
Data: protoutil.MarshalOrPanic(&cb.ConfigEnvelope{
Config: &cb.Config{
ChannelGroup: &cb.ConfigGroup{
Groups: groups,
Values: map[string]*cb.ConfigValue{
"HashingAlgorithm": {
Value: protoutil.MarshalOrPanic(&cb.HashingAlgorithm{
Name: bccsp.SHA256,
}),
},
"BlockDataHashingStructure": {
Value: protoutil.MarshalOrPanic(&cb.BlockDataHashingStructure{
Width: math.MaxUint32,
}),
},
"OrdererAddresses": {
Value: protoutil.MarshalOrPanic(&cb.OrdererAddresses{
Addresses: []string{"localhost"},
}),
},
block := protoutil.NewBlock(0, []byte{})
block.Data = &cb.BlockData{
Data: [][]byte{
protoutil.MarshalOrPanic(&cb.Envelope{
Payload: protoutil.MarshalOrPanic(&cb.Payload{
Data: protoutil.MarshalOrPanic(&cb.ConfigEnvelope{
Config: &cb.Config{
ChannelGroup: &cb.ConfigGroup{
Groups: groups,
Values: map[string]*cb.ConfigValue{
"HashingAlgorithm": {
Value: protoutil.MarshalOrPanic(&cb.HashingAlgorithm{
Name: bccsp.SHA256,
}),
},
"BlockDataHashingStructure": {
Value: protoutil.MarshalOrPanic(&cb.BlockDataHashingStructure{
Width: math.MaxUint32,
}),
},
"OrdererAddresses": {
Value: protoutil.MarshalOrPanic(&cb.OrdererAddresses{
Addresses: []string{"localhost"},
}),
},
},
},
}),
Header: &cb.Header{
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
Type: int32(cb.HeaderType_CONFIG),
ChannelId: channelID,
}),
},
}),
Header: &cb.Header{
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
Type: int32(cb.HeaderType_CONFIG),
ChannelId: channelID,
}),
},
}),
},
}),
},
}
block.Header.DataHash = protoutil.BlockDataHash(block.Data)
protoutil.InitBlockMetadata(block)

return block
}

func createBlockFile(tempDir string, configBlock *cb.Block) string {
Expand Down
69 changes: 36 additions & 33 deletions integration/raft/channel_participation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1040,46 +1040,49 @@ func multiNodeEtcdRaftTwoChannels() *nwo.Config {
}

func createJoinBlockDefineSystemChannel(channelID string) *common.Block {
return &common.Block{
Data: &common.BlockData{
Data: [][]byte{
protoutil.MarshalOrPanic(&common.Envelope{
Payload: protoutil.MarshalOrPanic(&common.Payload{
Data: protoutil.MarshalOrPanic(&common.ConfigEnvelope{
Config: &common.Config{
ChannelGroup: &common.ConfigGroup{
Groups: map[string]*common.ConfigGroup{
"Consortiums": {},
block := protoutil.NewBlock(0, []byte{})
block.Data = &common.BlockData{
Data: [][]byte{
protoutil.MarshalOrPanic(&common.Envelope{
Payload: protoutil.MarshalOrPanic(&common.Payload{
Data: protoutil.MarshalOrPanic(&common.ConfigEnvelope{
Config: &common.Config{
ChannelGroup: &common.ConfigGroup{
Groups: map[string]*common.ConfigGroup{
"Consortiums": {},
},
Values: map[string]*common.ConfigValue{
"HashingAlgorithm": {
Value: protoutil.MarshalOrPanic(&common.HashingAlgorithm{
Name: bccsp.SHA256,
}),
},
"BlockDataHashingStructure": {
Value: protoutil.MarshalOrPanic(&common.BlockDataHashingStructure{
Width: math.MaxUint32,
}),
},
Values: map[string]*common.ConfigValue{
"HashingAlgorithm": {
Value: protoutil.MarshalOrPanic(&common.HashingAlgorithm{
Name: bccsp.SHA256,
}),
},
"BlockDataHashingStructure": {
Value: protoutil.MarshalOrPanic(&common.BlockDataHashingStructure{
Width: math.MaxUint32,
}),
},
"OrdererAddresses": {
Value: protoutil.MarshalOrPanic(&common.OrdererAddresses{
Addresses: []string{"localhost"},
}),
},
"OrdererAddresses": {
Value: protoutil.MarshalOrPanic(&common.OrdererAddresses{
Addresses: []string{"localhost"},
}),
},
},
},
}),
Header: &common.Header{
ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{
Type: int32(common.HeaderType_CONFIG),
ChannelId: channelID,
}),
},
}),
Header: &common.Header{
ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{
Type: int32(common.HeaderType_CONFIG),
ChannelId: channelID,
}),
},
}),
},
}),
},
}
block.Header.DataHash = protoutil.BlockDataHash(block.Data)
protoutil.InitBlockMetadata(block)

return block
}
1 change: 1 addition & 0 deletions integration/raft/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ var _ = Describe("EndToEnd reconfiguration and onboarding", func() {
Expect(err).NotTo(HaveOccurred())
genesisBlock.Data.Data[0], err = protoutil.Marshal(envelope)
Expect(err).NotTo(HaveOccurred())
genesisBlock.Header.DataHash = protoutil.BlockDataHash(genesisBlock.Data)
genesisBlockBytes, err := protoutil.Marshal(genesisBlock)
Expect(err).NotTo(HaveOccurred())
err = ioutil.WriteFile(network.OutputBlockPath("testchannel"), genesisBlockBytes, 0o644)
Expand Down
10 changes: 10 additions & 0 deletions orderer/common/channelparticipation/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0
package channelparticipation

import (
"bytes"

cb "github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric/bccsp/factory"
"github.com/hyperledger/fabric/common/channelconfig"
Expand All @@ -25,6 +27,14 @@ func ValidateJoinBlock(configBlock *cb.Block) (channelID string, err error) {
return "", errors.New("block is not a config block")
}

if configBlock.Metadata == nil || len(configBlock.Metadata.Metadata) == 0 {
return "", errors.New("invalid block: does not have metadata")
}

if !bytes.Equal(protoutil.BlockDataHash(configBlock.Data), configBlock.Header.DataHash) {
return "", errors.New("invalid block: Header.DataHash is different from Hash(block.Data)")
}

envelope, err := protoutil.ExtractEnvelope(configBlock, 0)
if err != nil {
return "", err
Expand Down
139 changes: 84 additions & 55 deletions orderer/common/channelparticipation/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"math"
"testing"

"github.com/golang/protobuf/proto"
cb "github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/orderer/common/channelparticipation"
Expand All @@ -19,12 +20,45 @@ import (
)

func TestValidateJoinBlock(t *testing.T) {
validJoinBlock := blockWithGroups(
map[string]*cb.ConfigGroup{
"Application": {},
},
"my-channel",
)

tests := []struct {
testName string
joinBlock *cb.Block
expectedChannelID string
expectedErr error
}{
{
testName: "Valid application channel join block",
joinBlock: validJoinBlock,
expectedChannelID: "my-channel",
expectedErr: nil,
},
{
testName: "Invalid block data hash",
joinBlock: func() *cb.Block {
b := proto.Clone(validJoinBlock).(*cb.Block)
b.Header.DataHash = []byte("bogus")
return b
}(),
expectedChannelID: "",
expectedErr: errors.New("invalid block: Header.DataHash is different from Hash(block.Data)"),
},
{
testName: "Invalid block metadata",
joinBlock: func() *cb.Block {
b := proto.Clone(validJoinBlock).(*cb.Block)
b.Metadata = nil
return b
}(),
expectedChannelID: "",
expectedErr: errors.New("invalid block: does not have metadata"),
},
{
testName: "Not supported: system channel join block",
joinBlock: blockWithGroups(
Expand All @@ -36,17 +70,6 @@ func TestValidateJoinBlock(t *testing.T) {
expectedChannelID: "",
expectedErr: errors.New("invalid config: contains consortiums: system channel not supported"),
},
{
testName: "Valid application channel join block",
joinBlock: blockWithGroups(
map[string]*cb.ConfigGroup{
"Application": {},
},
"my-channel",
),
expectedChannelID: "my-channel",
expectedErr: nil,
},
{
testName: "Join block not a config block",
joinBlock: nonConfigBlock(),
Expand Down Expand Up @@ -100,62 +123,68 @@ func TestValidateJoinBlock(t *testing.T) {
}

func blockWithGroups(groups map[string]*cb.ConfigGroup, channelID string) *cb.Block {
return &cb.Block{
Data: &cb.BlockData{
Data: [][]byte{
protoutil.MarshalOrPanic(&cb.Envelope{
Payload: protoutil.MarshalOrPanic(&cb.Payload{
Data: protoutil.MarshalOrPanic(&cb.ConfigEnvelope{
Config: &cb.Config{
ChannelGroup: &cb.ConfigGroup{
Groups: groups,
Values: map[string]*cb.ConfigValue{
"HashingAlgorithm": {
Value: protoutil.MarshalOrPanic(&cb.HashingAlgorithm{
Name: bccsp.SHA256,
}),
},
"BlockDataHashingStructure": {
Value: protoutil.MarshalOrPanic(&cb.BlockDataHashingStructure{
Width: math.MaxUint32,
}),
},
"OrdererAddresses": {
Value: protoutil.MarshalOrPanic(&cb.OrdererAddresses{
Addresses: []string{"localhost"},
}),
},
block := protoutil.NewBlock(0, []byte{})
block.Data = &cb.BlockData{
Data: [][]byte{
protoutil.MarshalOrPanic(&cb.Envelope{
Payload: protoutil.MarshalOrPanic(&cb.Payload{
Data: protoutil.MarshalOrPanic(&cb.ConfigEnvelope{
Config: &cb.Config{
ChannelGroup: &cb.ConfigGroup{
Groups: groups,
Values: map[string]*cb.ConfigValue{
"HashingAlgorithm": {
Value: protoutil.MarshalOrPanic(&cb.HashingAlgorithm{
Name: bccsp.SHA256,
}),
},
"BlockDataHashingStructure": {
Value: protoutil.MarshalOrPanic(&cb.BlockDataHashingStructure{
Width: math.MaxUint32,
}),
},
"OrdererAddresses": {
Value: protoutil.MarshalOrPanic(&cb.OrdererAddresses{
Addresses: []string{"localhost"},
}),
},
},
},
}),
Header: &cb.Header{
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
Type: int32(cb.HeaderType_CONFIG),
ChannelId: channelID,
}),
},
}),
Header: &cb.Header{
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
Type: int32(cb.HeaderType_CONFIG),
ChannelId: channelID,
}),
},
}),
},
}),
},
}
block.Header.DataHash = protoutil.BlockDataHash(block.Data)
protoutil.InitBlockMetadata(block)

return block
}

func nonConfigBlock() *cb.Block {
return &cb.Block{
Data: &cb.BlockData{
Data: [][]byte{
protoutil.MarshalOrPanic(&cb.Envelope{
Payload: protoutil.MarshalOrPanic(&cb.Payload{
Header: &cb.Header{
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
Type: int32(cb.HeaderType_ENDORSER_TRANSACTION),
}),
},
}),
block := protoutil.NewBlock(0, []byte{})
block.Data = &cb.BlockData{
Data: [][]byte{
protoutil.MarshalOrPanic(&cb.Envelope{
Payload: protoutil.MarshalOrPanic(&cb.Payload{
Header: &cb.Header{
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
Type: int32(cb.HeaderType_ENDORSER_TRANSACTION),
}),
},
}),
},
}),
},
}
block.Header.DataHash = protoutil.BlockDataHash(block.Data)
protoutil.InitBlockMetadata(block)

return block
}
Loading

0 comments on commit 71ce0ab

Please sign in to comment.