Skip to content

Commit d88c3bc

Browse files
committed
[FAB-1516] committer side upgrade processing Part I
This CR focuses on invalidating all transactions which are "invokes" to upgraded chaincodes in the same block. Invalid expired transactions in other blocks will be done as part II. Some envelop unpacking works are duplicate for logic part to be tested easier. Change-Id: Iea7d1d29a1927e73973319ce088b1435f9ff8b55 Signed-off-by: jiangyaoguo <[email protected]>
1 parent fed2efa commit d88c3bc

File tree

4 files changed

+332
-49
lines changed

4 files changed

+332
-49
lines changed

core/committer/txvalidator/txvalidator_test.go

+122-1
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ import (
2424
util2 "github.com/hyperledger/fabric/common/util"
2525
"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
2626
"github.com/hyperledger/fabric/core/ledger/util"
27+
ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
2728
mocktxvalidator "github.com/hyperledger/fabric/core/mocks/txvalidator"
2829
"github.com/hyperledger/fabric/core/mocks/validator"
30+
"github.com/hyperledger/fabric/msp"
31+
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
32+
msptesttools "github.com/hyperledger/fabric/msp/mgmt/testtools"
2933
"github.com/hyperledger/fabric/protos/common"
3034
"github.com/hyperledger/fabric/protos/peer"
3135
"github.com/hyperledger/fabric/protos/utils"
@@ -70,7 +74,7 @@ func TestNewTxValidator_DuplicateTransactions(t *testing.T) {
7074

7175
tValidator := &txValidator{&mocktxvalidator.Support{LedgerVal: ledger}, &validator.MockVsccValidator{}}
7276

73-
// Create simeple endorsement transaction
77+
// Create simple endorsement transaction
7478
payload := &common.Payload{
7579
Header: &common.Header{
7680
ChannelHeader: utils.MarshalOrPanic(&common.ChannelHeader{
@@ -121,3 +125,120 @@ func TestNewTxValidator_DuplicateTransactions(t *testing.T) {
121125
txsfltr := util.TxValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
122126
assert.True(t, txsfltr.IsInvalid(0))
123127
}
128+
129+
func createCCUpgradeEnvelope(chainID, chaincodeName, chaincodeVersion string, signer msp.SigningIdentity) (*common.Envelope, error) {
130+
creator, err := signer.Serialize()
131+
if err != nil {
132+
return nil, err
133+
}
134+
135+
spec := &peer.ChaincodeSpec{
136+
Type: peer.ChaincodeSpec_Type(peer.ChaincodeSpec_Type_value["GOLANG"]),
137+
ChaincodeId: &peer.ChaincodeID{
138+
Path: "github.com/codePath",
139+
Name: chaincodeName,
140+
Version: chaincodeVersion,
141+
},
142+
}
143+
144+
cds := &peer.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: []byte{}}
145+
prop, _, err := utils.CreateUpgradeProposalFromCDS(chainID, cds, creator, []byte{}, []byte{}, []byte{})
146+
if err != nil {
147+
return nil, err
148+
}
149+
150+
proposalResponse := &peer.ProposalResponse{
151+
Response: &peer.Response{
152+
Status: 200, // endorsed successfully
153+
},
154+
Endorsement: &peer.Endorsement{},
155+
}
156+
157+
return utils.CreateSignedTx(prop, signer, proposalResponse)
158+
}
159+
160+
func TestGetTxCCInstance(t *testing.T) {
161+
// setup the MSP manager so that we can sign/verify
162+
mspMgrConfigDir := "../../../msp/sampleconfig/"
163+
err := msptesttools.LoadMSPSetupForTesting(mspMgrConfigDir)
164+
if err != nil {
165+
t.Fatalf("Could not initialize msp, err: %s", err)
166+
}
167+
signer, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
168+
if err != nil {
169+
t.Fatalf("Could not initialize signer, err: %s", err)
170+
}
171+
172+
chainID := util2.GetTestChainID()
173+
upgradeCCName := "mycc"
174+
upgradeCCVersion := "v1"
175+
176+
env, err := createCCUpgradeEnvelope(chainID, upgradeCCName, upgradeCCVersion, signer)
177+
assert.NoError(t, err)
178+
179+
// get the payload from the envelope
180+
payload, err := utils.GetPayload(env)
181+
assert.NoError(t, err)
182+
183+
expectInvokeCCIns := &ChaincodeInstance{
184+
ChainID: chainID,
185+
ChaincodeName: "lscc",
186+
ChaincodeVersion: "",
187+
}
188+
expectUpgradeCCIns := &ChaincodeInstance{
189+
ChainID: chainID,
190+
ChaincodeName: upgradeCCName,
191+
ChaincodeVersion: upgradeCCVersion,
192+
}
193+
194+
tValidator := &txValidator{}
195+
invokeCCIns, upgradeCCIns, err := tValidator.getTxCCInstance(payload)
196+
if err != nil {
197+
t.Fatalf("Get chaincode from tx error: %s", err)
198+
}
199+
assert.EqualValues(t, expectInvokeCCIns, invokeCCIns)
200+
assert.EqualValues(t, expectUpgradeCCIns, upgradeCCIns)
201+
}
202+
203+
func TestInvalidTXsForUpgradeCC(t *testing.T) {
204+
txsChaincodeNames := map[int]*ChaincodeInstance{
205+
0: &ChaincodeInstance{"chain0", "cc0", "v0"}, // invoke cc0/chain0:v0, should not be affected by upgrade tx in other chain
206+
1: &ChaincodeInstance{"chain1", "cc0", "v0"}, // invoke cc0/chain1:v0, should be invalided by cc1/chain1 upgrade tx
207+
2: &ChaincodeInstance{"chain1", "lscc", ""}, // upgrade cc0/chain1 to v1, should be invalided by latter cc0/chain1 upgtade tx
208+
3: &ChaincodeInstance{"chain1", "cc0", "v0"}, // invoke cc0/chain1:v0, should be invalided by cc1/chain1 upgrade tx
209+
4: &ChaincodeInstance{"chain1", "cc0", "v1"}, // invoke cc0/chain1:v1, should be invalided by cc1/chain1 upgrade tx
210+
5: &ChaincodeInstance{"chain1", "cc1", "v0"}, // invoke cc1/chain1:v0, should not be affected by other chaincode upgrade tx
211+
6: &ChaincodeInstance{"chain1", "lscc", ""}, // upgrade cc0/chain1 to v2, should be invalided by latter cc0/chain1 upgtade tx
212+
7: &ChaincodeInstance{"chain1", "lscc", ""}, // upgrade cc0/chain1 to v3
213+
}
214+
upgradedChaincodes := map[int]*ChaincodeInstance{
215+
2: &ChaincodeInstance{"chain1", "cc0", "v1"},
216+
6: &ChaincodeInstance{"chain1", "cc0", "v2"},
217+
7: &ChaincodeInstance{"chain1", "cc0", "v3"},
218+
}
219+
220+
txsfltr := ledgerUtil.NewTxValidationFlags(8)
221+
txsfltr.SetFlag(0, peer.TxValidationCode_VALID)
222+
txsfltr.SetFlag(1, peer.TxValidationCode_VALID)
223+
txsfltr.SetFlag(2, peer.TxValidationCode_VALID)
224+
txsfltr.SetFlag(3, peer.TxValidationCode_VALID)
225+
txsfltr.SetFlag(4, peer.TxValidationCode_VALID)
226+
txsfltr.SetFlag(5, peer.TxValidationCode_VALID)
227+
txsfltr.SetFlag(6, peer.TxValidationCode_VALID)
228+
txsfltr.SetFlag(7, peer.TxValidationCode_VALID)
229+
230+
expectTxsFltr := ledgerUtil.NewTxValidationFlags(8)
231+
expectTxsFltr.SetFlag(0, peer.TxValidationCode_VALID)
232+
expectTxsFltr.SetFlag(1, peer.TxValidationCode_EXPIRED_CHAINCODE)
233+
expectTxsFltr.SetFlag(2, peer.TxValidationCode_EXPIRED_CHAINCODE)
234+
expectTxsFltr.SetFlag(3, peer.TxValidationCode_EXPIRED_CHAINCODE)
235+
expectTxsFltr.SetFlag(4, peer.TxValidationCode_EXPIRED_CHAINCODE)
236+
expectTxsFltr.SetFlag(5, peer.TxValidationCode_VALID)
237+
expectTxsFltr.SetFlag(6, peer.TxValidationCode_EXPIRED_CHAINCODE)
238+
expectTxsFltr.SetFlag(7, peer.TxValidationCode_VALID)
239+
240+
tValidator := &txValidator{}
241+
finalfltr := tValidator.invalidTXsForUpgradeCC(txsChaincodeNames, upgradedChaincodes, txsfltr)
242+
243+
assert.EqualValues(t, expectTxsFltr, finalfltr)
244+
}

core/committer/txvalidator/validator.go

+157
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,22 @@ func (v *txValidator) chainExists(chain string) bool {
104104
return true
105105
}
106106

107+
// ChaincodeInstance is unique identifier of chaincode instance
108+
type ChaincodeInstance struct {
109+
ChainID string
110+
ChaincodeName string
111+
ChaincodeVersion string
112+
}
113+
107114
func (v *txValidator) Validate(block *common.Block) error {
108115
logger.Debug("START Block Validation")
109116
defer logger.Debug("END Block Validation")
110117
// Initialize trans as valid here, then set invalidation reason code upon invalidation below
111118
txsfltr := ledgerUtil.NewTxValidationFlags(len(block.Data.Data))
119+
// txsChaincodeNames records all the invoked chaincodes by tx in a block
120+
txsChaincodeNames := make(map[int]*ChaincodeInstance)
121+
// upgradedChaincodes records all the chaincodes that are upgrded in a block
122+
txsUpgradedChaincodes := make(map[int]*ChaincodeInstance)
112123
for tIdx, d := range block.Data.Data {
113124
if d != nil {
114125
if env, err := utils.GetEnvelopeFromBlock(d); err != nil {
@@ -164,6 +175,18 @@ func (v *txValidator) Validate(block *common.Block) error {
164175
txsfltr.SetFlag(tIdx, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE)
165176
continue
166177
}
178+
179+
invokeCC, upgradeCC, err := v.getTxCCInstance(payload)
180+
if err != nil {
181+
logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err)
182+
txsfltr.SetFlag(tIdx, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE)
183+
continue
184+
}
185+
txsChaincodeNames[tIdx] = invokeCC
186+
if upgradeCC != nil {
187+
logger.Infof("Find chaincode upgrade transaction for chaincode %s on chain %s with new version %s", upgradeCC.ChaincodeName, upgradeCC.ChainID, upgradeCC.ChaincodeVersion)
188+
txsUpgradedChaincodes[tIdx] = upgradeCC
189+
}
167190
} else if common.HeaderType(chdr.Type) == common.HeaderType_CONFIG {
168191
configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data)
169192
if err != nil {
@@ -193,6 +216,9 @@ func (v *txValidator) Validate(block *common.Block) error {
193216
}
194217
}
195218
}
219+
220+
txsfltr = v.invalidTXsForUpgradeCC(txsChaincodeNames, txsUpgradedChaincodes, txsfltr)
221+
196222
// Initialize metadata structure
197223
utils.InitBlockMetadata(block)
198224

@@ -201,6 +227,137 @@ func (v *txValidator) Validate(block *common.Block) error {
201227
return nil
202228
}
203229

230+
// generateCCKey generates a unique identifier for chaincode in specific chain
231+
func (v *txValidator) generateCCKey(ccName, chainID string) string {
232+
return fmt.Sprintf("%s/%s", ccName, chainID)
233+
}
234+
235+
// invalidTXsForUpgradeCC invalid all txs that should be invalided because of chaincode upgrade txs
236+
func (v *txValidator) invalidTXsForUpgradeCC(txsChaincodeNames map[int]*ChaincodeInstance, txsUpgradedChaincodes map[int]*ChaincodeInstance, txsfltr ledgerUtil.TxValidationFlags) ledgerUtil.TxValidationFlags {
237+
if len(txsUpgradedChaincodes) == 0 {
238+
return txsfltr
239+
}
240+
241+
// Invalid former cc upgrade txs if there're two or more txs upgrade the same cc
242+
finalValidUpgradeTXs := make(map[string]int)
243+
upgradedChaincodes := make(map[string]*ChaincodeInstance)
244+
for tIdx, cc := range txsUpgradedChaincodes {
245+
if cc == nil {
246+
continue
247+
}
248+
upgradedCCKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID)
249+
250+
if finalIdx, exist := finalValidUpgradeTXs[upgradedCCKey]; !exist {
251+
finalValidUpgradeTXs[upgradedCCKey] = tIdx
252+
upgradedChaincodes[upgradedCCKey] = cc
253+
} else if finalIdx < tIdx {
254+
logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", finalIdx)
255+
txsfltr.SetFlag(finalIdx, peer.TxValidationCode_EXPIRED_CHAINCODE)
256+
257+
// record latter cc upgrade tx info
258+
finalValidUpgradeTXs[upgradedCCKey] = tIdx
259+
upgradedChaincodes[upgradedCCKey] = cc
260+
} else {
261+
logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", tIdx)
262+
txsfltr.SetFlag(tIdx, peer.TxValidationCode_EXPIRED_CHAINCODE)
263+
}
264+
}
265+
266+
// invalid txs which invoke the upgraded chaincodes
267+
for tIdx, cc := range txsChaincodeNames {
268+
if cc == nil {
269+
continue
270+
}
271+
ccKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID)
272+
if _, exist := upgradedChaincodes[ccKey]; exist {
273+
if txsfltr.IsValid(tIdx) {
274+
logger.Infof("Invalid transaction with index %d: chaincode was upgraded in the same block", tIdx)
275+
txsfltr.SetFlag(tIdx, peer.TxValidationCode_EXPIRED_CHAINCODE)
276+
}
277+
}
278+
}
279+
280+
return txsfltr
281+
}
282+
283+
func (v *txValidator) getTxCCInstance(payload *common.Payload) (invokeCCIns, upgradeCCIns *ChaincodeInstance, err error) {
284+
// This is duplicated unpacking work, but make test easier.
285+
chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
286+
if err != nil {
287+
return nil, nil, err
288+
}
289+
290+
// Chain ID
291+
chainID := chdr.ChannelId
292+
if chainID == "" {
293+
err := fmt.Errorf("transaction header does not contain an chain ID")
294+
logger.Errorf("%s", err)
295+
return nil, nil, err
296+
}
297+
298+
// ChaincodeID
299+
hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header)
300+
if err != nil {
301+
return nil, nil, err
302+
}
303+
invokeCC := hdrExt.ChaincodeId
304+
invokeIns := &ChaincodeInstance{ChainID: chainID, ChaincodeName: invokeCC.Name, ChaincodeVersion: invokeCC.Version}
305+
306+
// Transaction
307+
tx, err := utils.GetTransaction(payload.Data)
308+
if err != nil {
309+
logger.Errorf("GetTransaction failed: %s", err)
310+
return invokeIns, nil, nil
311+
}
312+
313+
// ChaincodeActionPayload
314+
cap, err := utils.GetChaincodeActionPayload(tx.Actions[0].Payload)
315+
if err != nil {
316+
logger.Errorf("GetChaincodeActionPayload failed: %s", err)
317+
return invokeIns, nil, nil
318+
}
319+
320+
// ChaincodeProposalPayload
321+
cpp, err := utils.GetChaincodeProposalPayload(cap.ChaincodeProposalPayload)
322+
if err != nil {
323+
logger.Errorf("GetChaincodeProposalPayload failed: %s", err)
324+
return invokeIns, nil, nil
325+
}
326+
327+
// ChaincodeInvocationSpec
328+
cis := &peer.ChaincodeInvocationSpec{}
329+
err = proto.Unmarshal(cpp.Input, cis)
330+
if err != nil {
331+
logger.Errorf("GetChaincodeInvokeSpec failed: %s", err)
332+
return invokeIns, nil, nil
333+
}
334+
335+
if invokeCC.Name == "lscc" {
336+
if string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade" {
337+
upgradeIns, err := v.getUpgradeTxInstance(chainID, cis.ChaincodeSpec.Input.Args[2])
338+
if err != nil {
339+
return invokeIns, nil, nil
340+
}
341+
return invokeIns, upgradeIns, nil
342+
}
343+
}
344+
345+
return invokeIns, nil, nil
346+
}
347+
348+
func (v *txValidator) getUpgradeTxInstance(chainID string, cdsBytes []byte) (*ChaincodeInstance, error) {
349+
cds, err := utils.GetChaincodeDeploymentSpec(cdsBytes)
350+
if err != nil {
351+
return nil, err
352+
}
353+
354+
return &ChaincodeInstance{
355+
ChainID: chainID,
356+
ChaincodeName: cds.ChaincodeSpec.ChaincodeId.Name,
357+
ChaincodeVersion: cds.ChaincodeSpec.ChaincodeId.Version,
358+
}, nil
359+
}
360+
204361
func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) error {
205362
// get channel header
206363
chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)

0 commit comments

Comments
 (0)