Skip to content

Commit a9b3a61

Browse files
committed
[FAB-3199] Committer to call CSCC on config update
Once new configuration block reaches the committer it need to update current configuration cache of the peer cache such that get current configuration block of CSCC could provide correct information as it reads from that cache. Change-Id: Ifc931097aa8db45d12f8a047564e0e1534bc159c Signed-off-by: Artem Barger <[email protected]>
1 parent a01b2f9 commit a9b3a61

File tree

6 files changed

+96
-103
lines changed

6 files changed

+96
-103
lines changed

core/committer/committer_impl.go

+26-1
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ limitations under the License.
1717
package committer
1818

1919
import (
20+
"fmt"
21+
2022
"github.com/hyperledger/fabric/common/flogging"
2123
"github.com/hyperledger/fabric/core/committer/txvalidator"
2224
"github.com/hyperledger/fabric/core/ledger"
2325
"github.com/hyperledger/fabric/events/producer"
2426
"github.com/hyperledger/fabric/protos/common"
27+
"github.com/hyperledger/fabric/protos/utils"
2528
"github.com/op/go-logging"
2629
)
2730

@@ -42,22 +45,44 @@ func init() {
4245
type LedgerCommitter struct {
4346
ledger ledger.PeerLedger
4447
validator txvalidator.Validator
48+
eventer ConfigBlockEventer
4549
}
4650

51+
// ConfigBlockEventer callback function proto type to define action
52+
// upon arrival on new configuaration update block
53+
type ConfigBlockEventer func(block *common.Block) error
54+
4755
// NewLedgerCommitter is a factory function to create an instance of the committer
56+
// which passes incoming blocks via validation and commits them into the ledger.
4857
func NewLedgerCommitter(ledger ledger.PeerLedger, validator txvalidator.Validator) *LedgerCommitter {
49-
return &LedgerCommitter{ledger: ledger, validator: validator}
58+
return NewLedgerCommitterReactive(ledger, validator, func(_ *common.Block) error { return nil })
59+
}
60+
61+
// NewLedgerCommitterReactive is a factory function to create an instance of the committer
62+
// same as way as NewLedgerCommitter, while also provides an option to specify callback to
63+
// be called upon new configuration block arrival and commit event
64+
func NewLedgerCommitterReactive(ledger ledger.PeerLedger, validator txvalidator.Validator, eventer ConfigBlockEventer) *LedgerCommitter {
65+
return &LedgerCommitter{ledger: ledger, validator: validator, eventer: eventer}
5066
}
5167

5268
// Commit commits block to into the ledger
5369
// Note, it is important that this always be called serially
5470
func (lc *LedgerCommitter) Commit(block *common.Block) error {
71+
5572
// Validate and mark invalid transactions
5673
logger.Debug("Validating block")
5774
if err := lc.validator.Validate(block); err != nil {
5875
return err
5976
}
6077

78+
// Updating CSCC with new configuration block
79+
if utils.IsConfigBlock(block) {
80+
logger.Debug("Received configuration update, calling CSCC ConfigUpdate")
81+
if err := lc.eventer(block); err != nil {
82+
return fmt.Errorf("Could not update CSCC with new configuration update due to %s", err)
83+
}
84+
}
85+
6186
if err := lc.ledger.Commit(block); err != nil {
6287
return err
6388
}

core/committer/committer_test.go

+34-3
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,18 @@ limitations under the License.
1717
package committer
1818

1919
import (
20+
"sync/atomic"
2021
"testing"
2122

2223
"github.com/hyperledger/fabric/common/configtx/test"
24+
"github.com/hyperledger/fabric/common/configtx/tool/localconfig"
25+
"github.com/hyperledger/fabric/common/configtx/tool/provisional"
2326
"github.com/hyperledger/fabric/common/ledger/testutil"
24-
"github.com/spf13/viper"
25-
"github.com/stretchr/testify/assert"
26-
2727
"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
2828
"github.com/hyperledger/fabric/core/mocks/validator"
2929
"github.com/hyperledger/fabric/protos/common"
30+
"github.com/spf13/viper"
31+
"github.com/stretchr/testify/assert"
3032
)
3133

3234
func TestKVLedgerBlockStorage(t *testing.T) {
@@ -73,3 +75,32 @@ func TestKVLedgerBlockStorage(t *testing.T) {
7375
testutil.AssertEquals(t, bcInfo, &common.BlockchainInfo{
7476
Height: 2, CurrentBlockHash: block1Hash, PreviousBlockHash: gbHash})
7577
}
78+
79+
func TestNewLedgerCommitterReactive(t *testing.T) {
80+
viper.Set("peer.fileSystemPath", "/tmp/fabric/committertest")
81+
chainID := "TestLedger"
82+
83+
ledgermgmt.InitializeTestEnv()
84+
defer ledgermgmt.CleanupTestEnv()
85+
gb, _ := test.MakeGenesisBlock(chainID)
86+
87+
ledger, err := ledgermgmt.CreateLedger(gb)
88+
assert.NoError(t, err, "Error while creating ledger: %s", err)
89+
defer ledger.Close()
90+
91+
var configArrived int32
92+
committer := NewLedgerCommitterReactive(ledger, &validator.MockValidator{}, func(_ *common.Block) error {
93+
atomic.AddInt32(&configArrived, 1)
94+
return nil
95+
})
96+
97+
height, err := committer.LedgerHeight()
98+
assert.Equal(t, uint64(1), height)
99+
assert.NoError(t, err)
100+
101+
profile := localconfig.Load(localconfig.SampleSingleMSPSoloProfile)
102+
block := provisional.New(profile).GenesisBlockForChannel(chainID)
103+
104+
committer.Commit(block)
105+
assert.Equal(t, int32(1), atomic.LoadInt32(&configArrived))
106+
}

core/peer/peer.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,17 @@ func createChain(cid string, ledger ledger.PeerLedger, cb *common.Block) error {
220220
ledger: ledger,
221221
}
222222

223-
c := committer.NewLedgerCommitter(ledger, txvalidator.NewTxValidator(cs))
223+
c := committer.NewLedgerCommitterReactive(ledger, txvalidator.NewTxValidator(cs), func(block *common.Block) error {
224+
chainID, err := utils.GetChainIDFromBlock(block)
225+
if err != nil {
226+
return err
227+
}
228+
return SetCurrConfigBlock(block, chainID)
229+
})
230+
224231
ordererAddresses := configtxManager.ChannelConfig().OrdererAddresses()
225232
if len(ordererAddresses) == 0 {
226-
return errors.New("No orderering service endpoint provided in configuration block")
233+
return errors.New("No ordering service endpoint provided in configuration block")
227234
}
228235
service.GetGossipService().InitializeChannel(cs.ChainID(), c, ordererAddresses)
229236

@@ -470,10 +477,6 @@ func SetCurrConfigBlock(block *common.Block, cid string) error {
470477
defer chains.Unlock()
471478
if c, ok := chains.list[cid]; ok {
472479
c.cb = block
473-
// TODO: Change MSP config
474-
// c.mspmgr.Reconfig(block)
475-
476-
// TODO: Change gossip configs
477480
return nil
478481
}
479482
return fmt.Errorf("Chain %s doesn't exist on the peer", cid)

core/scc/cscc/configure.go

+3-29
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,9 @@ var cnflogger = flogging.MustGetLogger("cscc")
4949

5050
// These are function names from Invoke first parameter
5151
const (
52-
JoinChain string = "JoinChain"
53-
UpdateConfigBlock string = "UpdateConfigBlock"
54-
GetConfigBlock string = "GetConfigBlock"
55-
GetChannels string = "GetChannels"
52+
JoinChain string = "JoinChain"
53+
GetConfigBlock string = "GetConfigBlock"
54+
GetChannels string = "GetChannels"
5655
)
5756

5857
// Init is called once per chain when the chain is created.
@@ -124,13 +123,6 @@ func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
124123
return shim.Error(fmt.Sprintf("\"GetConfigBlock\" request failed authorization check for channel [%s]: [%s]", args[1], err))
125124
}
126125
return getConfigBlock(args[1])
127-
case UpdateConfigBlock:
128-
// TODO: It needs to be clarified if this is a function invoked by a proposal or not.
129-
// The issue is the following: ChannelApplicationAdmins might require multiple signatures
130-
// but currently a proposal can be signed by a signle entity only. Therefore, the ChannelApplicationAdmins policy
131-
// will be never satisfied.
132-
133-
return updateConfigBlock(args[1])
134126
case GetChannels:
135127
// 2. check local MSP Members policy
136128
if err = e.policyChecker.CheckPolicyNoChannel(mgmt.Members, sp); err != nil {
@@ -170,24 +162,6 @@ func joinChain(blockBytes []byte) pb.Response {
170162
return shim.Success(nil)
171163
}
172164

173-
func updateConfigBlock(blockBytes []byte) pb.Response {
174-
block, err := extractBlock(blockBytes)
175-
if err != nil {
176-
return shim.Error(fmt.Sprintf("Failed to reconstruct the configuration block, %s", err))
177-
}
178-
chainID, err := utils.GetChainIDFromBlock(block)
179-
if err != nil {
180-
return shim.Error(fmt.Sprintf("Failed to get the chain ID from the configuration block, %s", err))
181-
}
182-
183-
if err := peer.SetCurrConfigBlock(block, chainID); err != nil {
184-
185-
return shim.Error(err.Error())
186-
}
187-
188-
return shim.Success(nil)
189-
}
190-
191165
func extractBlock(bytes []byte) (*common.Block, error) {
192166
if bytes == nil {
193167
return nil, errors.New("Genesis block must not be nil.")

core/scc/cscc/configure_test.go

-59
Original file line numberDiff line numberDiff line change
@@ -259,65 +259,6 @@ func TestConfigerInvokeJoinChainCorrectParams(t *testing.T) {
259259
}
260260
}
261261

262-
func TestConfigerInvokeUpdateConfigBlock(t *testing.T) {
263-
e := new(PeerConfiger)
264-
stub := shim.NewMockStub("PeerConfiger", e)
265-
266-
// Init the policy checker
267-
policyManagerGetter := &policymocks.MockChannelPolicyManagerGetter{
268-
Managers: map[string]policies.Manager{
269-
"mytestchainid": &policymocks.MockChannelPolicyManager{MockPolicy: &policymocks.MockPolicy{Deserializer: &policymocks.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}}},
270-
},
271-
}
272-
273-
identityDeserializer := &policymocks.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}
274-
275-
e.policyChecker = policy.NewPolicyChecker(
276-
policyManagerGetter,
277-
identityDeserializer,
278-
&policymocks.MockMSPPrincipalGetter{Principal: []byte("Alice")},
279-
)
280-
281-
sProp, _ := utils.MockSignedEndorserProposalOrPanic("", &pb.ChaincodeSpec{}, []byte("Alice"), []byte("msg1"))
282-
identityDeserializer.Msg = sProp.ProposalBytes
283-
sProp.Signature = sProp.ProposalBytes
284-
policyManagerGetter.Managers["mytestchainid"].(*policymocks.MockChannelPolicyManager).MockPolicy.(*policymocks.MockPolicy).Deserializer.(*policymocks.MockIdentityDeserializer).Msg = sProp.ProposalBytes
285-
286-
// Failed path: Not enough parameters
287-
args := [][]byte{[]byte("UpdateConfigBlock")}
288-
if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status == shim.OK {
289-
t.Fatalf("cscc invoke UpdateConfigBlock should have failed with invalid number of args: %v", args)
290-
}
291-
292-
// Failed path: wrong parameter type
293-
args = [][]byte{[]byte("UpdateConfigBlock"), []byte("action")}
294-
if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status == shim.OK {
295-
t.Fatalf("cscc invoke UpdateConfigBlock should have failed with null genesis block - args: %v", args)
296-
}
297-
298-
// Successful path for UpdateConfigBlock
299-
blockBytes := mockConfigBlock()
300-
if blockBytes == nil {
301-
t.Fatalf("cscc invoke UpdateConfigBlock failed because invalid block")
302-
}
303-
args = [][]byte{[]byte("UpdateConfigBlock"), blockBytes}
304-
if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status != shim.OK {
305-
t.Fatalf("cscc invoke UpdateConfigBlock failed with: %v", res.Message)
306-
}
307-
308-
// Query the configuration block
309-
//chainID := []byte{143, 222, 22, 192, 73, 145, 76, 110, 167, 154, 118, 66, 132, 204, 113, 168}
310-
chainID, err := utils.GetChainIDFromBlockBytes(blockBytes)
311-
if err != nil {
312-
t.Fatalf("cscc invoke UpdateConfigBlock failed with: %v", err)
313-
}
314-
args = [][]byte{[]byte("GetConfigBlock"), []byte(chainID)}
315-
if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status != shim.OK {
316-
t.Fatalf("cscc invoke GetConfigBlock failed with: %v", err)
317-
}
318-
319-
}
320-
321262
func mockConfigBlock() []byte {
322263
var blockBytes []byte = nil
323264
block, err := configtxtest.MakeGenesisBlock("mytestchainid")

protos/utils/commonutils.go

+24-5
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,15 @@ limitations under the License.
1717
package utils
1818

1919
import (
20+
"errors"
2021
"fmt"
2122
"time"
2223

23-
cb "github.com/hyperledger/fabric/protos/common"
24-
pb "github.com/hyperledger/fabric/protos/peer"
25-
26-
"errors"
27-
2824
"github.com/golang/protobuf/proto"
2925
"github.com/golang/protobuf/ptypes/timestamp"
3026
"github.com/hyperledger/fabric/common/crypto"
27+
cb "github.com/hyperledger/fabric/protos/common"
28+
pb "github.com/hyperledger/fabric/protos/peer"
3129
)
3230

3331
// MarshalOrPanic serializes a protobuf message and panics if this operation fails.
@@ -259,3 +257,24 @@ func UnmarshalChaincodeID(bytes []byte) (*pb.ChaincodeID, error) {
259257

260258
return ccid, nil
261259
}
260+
261+
// IsConfigBlock validates whenever given block contains configuration
262+
// update transaction
263+
func IsConfigBlock(block *cb.Block) bool {
264+
envelope, err := ExtractEnvelope(block, 0)
265+
if err != nil {
266+
return false
267+
}
268+
269+
payload, err := GetPayload(envelope)
270+
if err != nil {
271+
return false
272+
}
273+
274+
hdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
275+
if err != nil {
276+
return false
277+
}
278+
279+
return cb.HeaderType(hdr.Type) == cb.HeaderType_CONFIG
280+
}

0 commit comments

Comments
 (0)