Skip to content

Commit 194e68d

Browse files
author
Jason Yellick
committed
[FAB-2335] Add PreCommit transaction hook
https://jira.hyperledger.org/browse/FAB-2335 Today, config transactions go through three phases: BeginValueProposals() ProposeValue(...) ... ProposeValue(...) CommitProposals() This works well for verifying individual components of configuration, but fails when configuration must be validated in its totality. This is exhibited for instance in the instantiation of the MSP manager where the check is performed at every value proposal, rather than only once as is necessary. So, the new path becomes: BeginValueProposals() ProposeValue(...) ... ProposeValue(...) PreCommit() CommitProposals() Where PreCommit may return an error and stop the commit process. This concept also works nicely with some other pending config work intended to remove a significant amount of duplicated code. Change-Id: Ibf7450e6b6e355690af2cb14278f0fce4efdf0d7 Signed-off-by: Jason Yellick <[email protected]>
1 parent 1e022cf commit 194e68d

File tree

13 files changed

+83
-14
lines changed

13 files changed

+83
-14
lines changed

common/configtx/api/api.go

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ type Transactional interface {
7272
// RollbackConfig called when a config proposal is abandoned
7373
RollbackProposals()
7474

75+
// PreCommit verifies that the transaction can be committed successfully
76+
PreCommit() error
77+
7578
// CommitConfig called when a config proposal is committed
7679
CommitProposals()
7780
}

common/configtx/config.go

+16
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ type configResult struct {
3131
subResults []*configResult
3232
}
3333

34+
func (cr *configResult) preCommit() error {
35+
for _, subResult := range cr.subResults {
36+
err := subResult.preCommit()
37+
if err != nil {
38+
return err
39+
}
40+
}
41+
return cr.handler.PreCommit()
42+
}
43+
3444
func (cr *configResult) commit() {
3545
for _, subResult := range cr.subResults {
3646
subResult.commit()
@@ -103,6 +113,12 @@ func (cm *configManager) proposeGroup(name string, group *cb.ConfigGroup, handle
103113
}
104114
}
105115

116+
err = result.preCommit()
117+
if err != nil {
118+
result.rollback()
119+
return nil, err
120+
}
121+
106122
return result, nil
107123
}
108124

common/configtx/initializer.go

+11
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ func (i *valueProposerRoot) RollbackProposals() {
117117
i.mspConfigHandler.RollbackProposals()
118118
}
119119

120+
// PreCommit is used to verify total configuration before commit
121+
func (i *valueProposerRoot) PreCommit() error {
122+
logger.Debugf("Calling pre commit for MSP manager")
123+
return i.mspConfigHandler.PreCommit()
124+
}
125+
120126
// CommitConfig is used to commit a new config proposal
121127
func (i *valueProposerRoot) CommitProposals() {
122128
logger.Debugf("Calling commit for MSP manager")
@@ -139,6 +145,11 @@ func (i *policyProposerRoot) ProposePolicy(key string, policy *cb.ConfigPolicy)
139145
return fmt.Errorf("Programming error, this should never be invoked")
140146
}
141147

148+
// PreCommit is a no-op and returns nil
149+
func (i *policyProposerRoot) PreCommit() error {
150+
return nil
151+
}
152+
142153
// RollbackConfig is used to abandon a new config proposal
143154
func (i *policyProposerRoot) RollbackProposals() {}
144155

common/configvalues/api.go

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ type ValueProposer interface {
8484
// RollbackProposals called when a config proposal is abandoned
8585
RollbackProposals()
8686

87+
// PreCommit is invoked before committing the config to catch
88+
// any errors which cannot be caught on a per proposal basis
89+
// TODO, rename other methods to remove Value/Proposal references
90+
PreCommit() error
91+
8792
// CommitProposals called when a config proposal is committed
8893
CommitProposals()
8994
}

common/configvalues/channel/application/organization.go

+3
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,6 @@ func (oc *ApplicationOrgConfig) ProposeValue(key string, configValue *cb.ConfigV
111111

112112
return nil
113113
}
114+
115+
// PreCommit returns nil
116+
func (c *ApplicationOrgConfig) PreCommit() error { return nil }

common/configvalues/channel/application/sharedconfig.go

+3
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,6 @@ func (di *SharedConfigImpl) ProposeValue(key string, configValue *cb.ConfigValue
121121
func (di *SharedConfigImpl) Organizations() map[string]api.ApplicationOrg {
122122
return di.config.orgs
123123
}
124+
125+
// PreCommit returns nil
126+
func (di *SharedConfigImpl) PreCommit() error { return nil }

common/configvalues/channel/common/organization/organization.go

+3
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,6 @@ func (oc *OrgConfig) ProposeValue(key string, configValue *cb.ConfigValue) error
143143
}
144144
return nil
145145
}
146+
147+
// PreCommit returns nil
148+
func (oc *OrgConfig) PreCommit() error { return nil }

common/configvalues/channel/config.go

+3
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ func (c *Config) CommitProposals() {
158158
c.pending = nil
159159
}
160160

161+
// PreCommit returns nil
162+
func (c *Config) PreCommit() error { return nil }
163+
161164
// ProposeValue is used to add new config to the config proposal
162165
func (c *Config) ProposeValue(key string, configValue *cb.ConfigValue) error {
163166
switch key {

common/configvalues/channel/orderer/sharedconfig.go

+3
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,6 @@ func brokerEntrySeemsValid(broker string) bool {
315315
matched := re.FindString(host)
316316
return len(matched) == len(host)
317317
}
318+
319+
// PreCommit returns nil
320+
func (pm *ManagerImpl) PreCommit() error { return nil }

common/configvalues/msp/config.go

+17-14
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,21 @@ func (bh *MSPConfigHandler) ProposeMSP(mspConfig *mspprotos.MSPConfig) (msp.MSP,
9393
existingPendingMSPConfig, ok := bh.pendingConfig.idMap[mspID]
9494
if ok && !reflect.DeepEqual(existingPendingMSPConfig.mspConfig, mspConfig) {
9595
return nil, fmt.Errorf("Attempted to define two different versions of MSP: %s", mspID)
96-
} else {
97-
bh.pendingConfig.idMap[mspID] = &pendingMSPConfig{
98-
mspConfig: mspConfig,
99-
msp: mspInst,
100-
}
96+
}
97+
98+
bh.pendingConfig.idMap[mspID] = &pendingMSPConfig{
99+
mspConfig: mspConfig,
100+
msp: mspInst,
101+
}
102+
103+
return mspInst, nil
104+
}
105+
106+
// PreCommit instantiates the MSP manager
107+
func (bh *MSPConfigHandler) PreCommit() error {
108+
if len(bh.pendingConfig.idMap) == 0 {
109+
// Cannot instantiate an MSP manager with no MSPs
110+
return nil
101111
}
102112

103113
mspList := make([]msp.MSP, len(bh.pendingConfig.idMap))
@@ -107,14 +117,7 @@ func (bh *MSPConfigHandler) ProposeMSP(mspConfig *mspprotos.MSPConfig) (msp.MSP,
107117
i++
108118
}
109119

110-
// the only way to make sure that I have a
111-
// workable config is to toss the proposed
112-
// manager, create a new one, call setup on
113-
// it and return whatever error setup gives me
114120
bh.pendingConfig.proposedMgr = msp.NewMSPManager()
115-
err = bh.pendingConfig.proposedMgr.Setup(mspList)
116-
if err != nil {
117-
return nil, err
118-
}
119-
return mspInst, nil
121+
err := bh.pendingConfig.proposedMgr.Setup(mspList)
122+
return err
120123
}

common/configvalues/msp/config_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ func TestMSPConfigManager(t *testing.T) {
3535
mspCH.BeginConfig()
3636
_, err = mspCH.ProposeMSP(conf)
3737
assert.NoError(t, err)
38+
mspCH.PreCommit()
3839
mspCH.CommitProposals()
3940

4041
msps, err := mspCH.GetMSPs()

common/mocks/configtx/configtx.go

+3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ func (r *Resources) MSPManager() msp.MSPManager {
7070
// Transactional implements the configtxapi.Transactional
7171
type Transactional struct{}
7272

73+
// PreCommit returns nil
74+
func (t *Transactional) PreCommit() error { return nil }
75+
7376
// CommitConfig does nothing
7477
func (t *Transactional) CommitProposals() {}
7578

common/policies/policy.go

+12
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,20 @@ type Manager interface {
5454

5555
// Proposer is the interface used by the configtx manager for policy management
5656
type Proposer interface {
57+
// BeginPolicyProposals starts a policy update transaction
5758
BeginPolicyProposals(groups []string) ([]Proposer, error)
5859

60+
// ProposePolicy createss a pending policy update from a ConfigPolicy
5961
ProposePolicy(name string, policy *cb.ConfigPolicy) error
6062

63+
// RollbackProposals discards the pending policy updates
6164
RollbackProposals()
6265

66+
// CommitProposals commits the pending policy updates
6367
CommitProposals()
68+
69+
// PreCommit tests if a commit will apply
70+
PreCommit() error
6471
}
6572

6673
// Provider provides the backing implementation of a policy
@@ -176,6 +183,11 @@ func (pm *ManagerImpl) RollbackProposals() {
176183
pm.pendingConfig = nil
177184
}
178185

186+
// PreCommit is currently a no-op for the policy manager and always returns nil
187+
func (pm *ManagerImpl) PreCommit() error {
188+
return nil
189+
}
190+
179191
// CommitProposals is used to commit a new config proposal
180192
func (pm *ManagerImpl) CommitProposals() {
181193
if pm.pendingConfig == nil {

0 commit comments

Comments
 (0)