Skip to content

Commit ae10d2b

Browse files
committedJan 16, 2017
[FAB-1639] [FAB-1580] Rework validator
This change-set has removed calls to the txvalidator that were issued right after the peer (the leader) receives blocks from the orderers. The validator is now called to validate messages received from the gossip layer. In order to fix an import cycle, we have introduced the ChaincodeProvider interface in core/common/ccprovider/ccprovider.go, an implementation and a factory. This way, code that needs to use functions from the chaincode package without importing it can simply use (and possibly extend) the ChaincodeProvider interface and implementation. Furthermore, this drop has introduced protocol-level message validation for config transactions that was lacking before. Change-Id: I5906a6fe3da8410b05b5079dd7f0b9d28d20bb85 Signed-off-by: Alessandro Sorniotti <ale.linux@sopit.net>
1 parent dca94df commit ae10d2b

File tree

23 files changed

+431
-93
lines changed

23 files changed

+431
-93
lines changed
 

‎common/configtx/template.go

+19-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ import (
2222
ab "github.com/hyperledger/fabric/protos/orderer"
2323
"github.com/hyperledger/fabric/protos/utils"
2424

25+
"fmt"
26+
2527
"github.com/golang/protobuf/proto"
28+
"github.com/hyperledger/fabric/msp"
2629
)
2730

2831
const (
@@ -150,16 +153,29 @@ func join(sets ...[]*cb.SignedConfigurationItem) []*cb.SignedConfigurationItem {
150153
}
151154

152155
// MakeChainCreationTransaction is a handy utility function for creating new chain transactions using the underlying Template framework
153-
func MakeChainCreationTransaction(creationPolicy string, chainID string, templates ...Template) (*cb.Envelope, error) {
156+
func MakeChainCreationTransaction(creationPolicy string, chainID string, signer msp.SigningIdentity, templates ...Template) (*cb.Envelope, error) {
154157
newChainTemplate := NewChainCreationTemplate(creationPolicy, NewCompositeTemplate(templates...))
155158
signedConfigItems, err := newChainTemplate.Items(chainID)
156159
if err != nil {
157160
return nil, err
158161
}
159162

163+
sSigner, err := signer.Serialize()
164+
if err != nil {
165+
return nil, fmt.Errorf("Serialization of identity failed, err %s", err)
166+
}
167+
160168
payloadChainHeader := utils.MakeChainHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, msgVersion, chainID, epoch)
161-
payloadSignatureHeader := utils.MakeSignatureHeader(nil, utils.CreateNonceOrPanic())
169+
payloadSignatureHeader := utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic())
162170
payloadHeader := utils.MakePayloadHeader(payloadChainHeader, payloadSignatureHeader)
163171
payload := &cb.Payload{Header: payloadHeader, Data: utils.MarshalOrPanic(utils.MakeConfigurationEnvelope(signedConfigItems...))}
164-
return &cb.Envelope{Payload: utils.MarshalOrPanic(payload), Signature: nil}, nil
172+
paylBytes := utils.MarshalOrPanic(payload)
173+
174+
// sign the payload
175+
sig, err := signer.Sign(paylBytes)
176+
if err != nil {
177+
return nil, err
178+
}
179+
180+
return &cb.Envelope{Payload: paylBytes, Signature: sig}, nil
165181
}

‎common/configtx/test/helper.go

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ import (
2828
"github.com/golang/protobuf/proto"
2929
)
3030

31+
const (
32+
// AcceptAllPolicyKey is the key of the AcceptAllPolicy.
33+
AcceptAllPolicyKey = "AcceptAllPolicy"
34+
)
35+
3136
var template configtx.Template
3237

3338
var genesisFactory genesis.Factory

‎core/chaincode/ccproviderimpl.go

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package chaincode
2+
3+
import (
4+
"context"
5+
6+
"github.com/hyperledger/fabric/core/common/ccprovider"
7+
"github.com/hyperledger/fabric/core/ledger"
8+
"github.com/hyperledger/fabric/protos/peer"
9+
)
10+
11+
// ccProviderFactory implements the ccprovider.ChaincodeProviderFactory
12+
// interface and returns instances of ccprovider.ChaincodeProvider
13+
type ccProviderFactory struct {
14+
}
15+
16+
// NewChaincodeProvider returns pointers to ccProviderImpl as an
17+
// implementer of the ccprovider.ChaincodeProvider interface
18+
func (c *ccProviderFactory) NewChaincodeProvider() ccprovider.ChaincodeProvider {
19+
return &ccProviderImpl{}
20+
}
21+
22+
// init is called when this package is loaded. This implementation registers the factory
23+
func init() {
24+
ccprovider.RegisterChaincodeProviderFactory(&ccProviderFactory{})
25+
}
26+
27+
// ccProviderImpl is an implementation of the ccprovider.ChaincodeProvider interface
28+
type ccProviderImpl struct {
29+
txsim ledger.TxSimulator
30+
}
31+
32+
// ccProviderContextImpl contains the state that is passed around to calls to methods of ccProviderImpl
33+
type ccProviderContextImpl struct {
34+
ctx *CCContext
35+
}
36+
37+
// GetContext returns a context for the supplied ledger, with the appropriate tx simulator
38+
func (c *ccProviderImpl) GetContext(ledger ledger.ValidatedLedger) (context.Context, error) {
39+
var err error
40+
// get context for the chaincode execution
41+
c.txsim, err = ledger.NewTxSimulator()
42+
if err != nil {
43+
return nil, err
44+
}
45+
ctxt := context.WithValue(context.Background(), TXSimulatorKey, c.txsim)
46+
return ctxt, nil
47+
}
48+
49+
// GetCCContext returns an interface that encapsulates a
50+
// chaincode context; the interface is required to avoid
51+
// referencing the chaincode package from the interface definition
52+
func (c *ccProviderImpl) GetCCContext(cid, name, version, txid string, syscc bool, prop *peer.Proposal) interface{} {
53+
ctx := NewCCContext(cid, name, version, txid, syscc, prop)
54+
return &ccProviderContextImpl{ctx: ctx}
55+
}
56+
57+
// GetVSCCFromLCCC returns the VSCC listed in LCCC for the supplied chaincode
58+
func (c *ccProviderImpl) GetVSCCFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, error) {
59+
data, err := GetChaincodeDataFromLCCC(ctxt, txid, prop, chainID, chaincodeID)
60+
if err != nil {
61+
return "", err
62+
}
63+
64+
vscc := "vscc"
65+
// Check whenever VSCC defined for chaincode data
66+
if data != nil && data.Vscc != "" {
67+
vscc = data.Vscc
68+
}
69+
70+
return vscc, nil
71+
}
72+
73+
// ExecuteChaincode executes the chaincode specified in the context with the specified arguments
74+
func (c *ccProviderImpl) ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) ([]byte, *peer.ChaincodeEvent, error) {
75+
return ExecuteChaincode(ctxt, cccid.(*ccProviderContextImpl).ctx, args)
76+
}
77+
78+
// ReleaseContext frees up resources held by the context
79+
func (c *ccProviderImpl) ReleaseContext() {
80+
c.txsim.Done()
81+
}

‎core/committer/committer_impl.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package committer
1818

1919
import (
20+
"github.com/hyperledger/fabric/core/committer/txvalidator"
2021
"github.com/hyperledger/fabric/core/ledger"
2122
"github.com/hyperledger/fabric/protos/common"
2223
pb "github.com/hyperledger/fabric/protos/peer"
@@ -38,16 +39,21 @@ func init() {
3839
// it keeps the reference to the ledger to commit blocks and retreive
3940
// chain information
4041
type LedgerCommitter struct {
41-
ledger ledger.ValidatedLedger
42+
ledger ledger.ValidatedLedger
43+
validator txvalidator.Validator
4244
}
4345

4446
// NewLedgerCommitter is a factory function to create an instance of the committer
45-
func NewLedgerCommitter(ledger ledger.ValidatedLedger) *LedgerCommitter {
46-
return &LedgerCommitter{ledger}
47+
func NewLedgerCommitter(ledger ledger.ValidatedLedger, validator txvalidator.Validator) *LedgerCommitter {
48+
return &LedgerCommitter{ledger: ledger, validator: validator}
4749
}
4850

4951
// CommitBlock commits block to into the ledger
5052
func (lc *LedgerCommitter) CommitBlock(block *common.Block) error {
53+
// Validate and mark invalid transactions
54+
logger.Debug("Validating block")
55+
lc.validator.Validate(block)
56+
5157
if err := lc.ledger.Commit(block); err != nil {
5258
return err
5359
}

‎core/committer/committer_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/stretchr/testify/assert"
2525

2626
"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
27+
"github.com/hyperledger/fabric/core/mocks/validator"
2728
pb "github.com/hyperledger/fabric/protos/peer"
2829
)
2930

@@ -35,7 +36,7 @@ func TestKVLedgerBlockStorage(t *testing.T) {
3536
assert.NoError(t, err, "Error while creating ledger: %s", err)
3637
defer ledger.Close()
3738

38-
committer := NewLedgerCommitter(ledger)
39+
committer := NewLedgerCommitter(ledger, &validator.MockValidator{})
3940
height, err := committer.LedgerHeight()
4041
assert.Equal(t, uint64(0), height)
4142
assert.NoError(t, err)

‎core/committer/txvalidator/txvalidator_test.go

+3-9
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,22 @@ import (
2424
"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
2525
"github.com/hyperledger/fabric/core/ledger/testutil"
2626
"github.com/hyperledger/fabric/core/ledger/util"
27+
"github.com/hyperledger/fabric/core/mocks/validator"
2728
"github.com/hyperledger/fabric/protos/common"
2829
pb "github.com/hyperledger/fabric/protos/peer"
2930
"github.com/hyperledger/fabric/protos/utils"
3031
"github.com/spf13/viper"
3132
"github.com/stretchr/testify/assert"
3233
)
3334

34-
type mockVsccValidator struct {
35-
}
36-
37-
func (v *mockVsccValidator) VSCCValidateTx(payload *common.Payload, envBytes []byte) error {
38-
return nil
39-
}
40-
4135
func TestKVLedgerBlockStorage(t *testing.T) {
4236
viper.Set("peer.fileSystemPath", "/tmp/fabric/txvalidatortest")
4337
ledgermgmt.InitializeTestEnv()
4438
defer ledgermgmt.CleanupTestEnv()
4539
ledger, _ := ledgermgmt.CreateLedger("TestLedger")
4640
defer ledger.Close()
4741

48-
validator := &txValidator{ledger, &mockVsccValidator{}}
42+
validator := &txValidator{ledger, &validator.MockVsccValidator{}}
4943

5044
bcInfo, _ := ledger.GetBlockchainInfo()
5145
testutil.AssertEquals(t, bcInfo, &pb.BlockchainInfo{
@@ -76,7 +70,7 @@ func TestNewTxValidator_DuplicateTransactions(t *testing.T) {
7670
ledger, _ := ledgermgmt.CreateLedger("TestLedger")
7771
defer ledger.Close()
7872

79-
validator := &txValidator{ledger, &mockVsccValidator{}}
73+
validator := &txValidator{ledger, &validator.MockVsccValidator{}}
8074

8175
// Create simeple endorsement transaction
8276
payload := &common.Payload{

‎core/committer/txvalidator/validator.go

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

1919
import (
20-
"context"
2120
"fmt"
2221

2322
"github.com/golang/protobuf/proto"
2423
coreUtil "github.com/hyperledger/fabric/common/util"
25-
"github.com/hyperledger/fabric/core/chaincode"
24+
"github.com/hyperledger/fabric/core/common/ccprovider"
25+
"github.com/hyperledger/fabric/core/common/validation"
2626
"github.com/hyperledger/fabric/core/ledger"
2727
ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
28-
"github.com/hyperledger/fabric/core/peer"
2928
"github.com/hyperledger/fabric/protos/common"
3029
"github.com/hyperledger/fabric/protos/utils"
3130
"github.com/op/go-logging"
@@ -48,7 +47,8 @@ type vsccValidator interface {
4847
// vsccValidator implementation which used to call
4948
// vscc chaincode and validate block transactions
5049
type vsccValidatorImpl struct {
51-
ledger ledger.ValidatedLedger
50+
ledger ledger.ValidatedLedger
51+
ccprovider ccprovider.ChaincodeProvider
5252
}
5353

5454
// implementation of Validator interface, keeps
@@ -69,7 +69,12 @@ func init() {
6969
// NewTxValidator creates new transactions validator
7070
func NewTxValidator(ledger ledger.ValidatedLedger) Validator {
7171
// Encapsulates interface implementation
72-
return &txValidator{ledger, &vsccValidatorImpl{ledger}}
72+
return &txValidator{ledger, &vsccValidatorImpl{ledger: ledger, ccprovider: ccprovider.GetChaincodeProvider()}}
73+
}
74+
75+
func (v *txValidator) chainExists(chain string) bool {
76+
// TODO: implement this function!
77+
return true
7378
}
7479

7580
func (v *txValidator) Validate(block *common.Block) {
@@ -92,11 +97,19 @@ func (v *txValidator) Validate(block *common.Block) {
9297
logger.Debug("Validating transaction peer.ValidateTransaction()")
9398
var payload *common.Payload
9499
var err error
95-
if payload, _, err = peer.ValidateTransaction(env); err != nil {
100+
if payload, err = validation.ValidateTransaction(env); err != nil {
96101
logger.Errorf("Invalid transaction with index %d, error %s", tIdx, err)
97102
continue
98103
}
99104

105+
chain := payload.Header.ChainHeader.ChainID
106+
logger.Debug("Transaction is for chain %s", chain)
107+
108+
if !v.chainExists(chain) {
109+
logger.Errorf("Dropping transaction for non-existent chain %s", chain)
110+
continue
111+
}
112+
100113
if common.HeaderType(payload.Header.ChainHeader.Type) == common.HeaderType_ENDORSER_TRANSACTION {
101114
// Check duplicate transactions
102115
txID := payload.Header.ChainHeader.TxID
@@ -113,7 +126,13 @@ func (v *txValidator) Validate(block *common.Block) {
113126
continue
114127
}
115128
} else if common.HeaderType(payload.Header.ChainHeader.Type) == common.HeaderType_CONFIGURATION_TRANSACTION {
116-
logger.Warningf("Validation for common.HeaderType_CONFIGURATION_TRANSACTION %d pending JIRA-1639", tIdx)
129+
// TODO: here we should call CSCC and pass it the config tx
130+
// note that there is quite a bit more validation necessary
131+
// on this tx, namely, validation that each config item has
132+
// signature matching the policy required for the item from
133+
// the existing configuration; this is taken care of nicely
134+
// by configtx.Manager (see fabric/common/configtx).
135+
logger.Debug("config transaction received for chain %s", chain)
117136
}
118137

119138
if _, err := proto.Marshal(env); err != nil {
@@ -157,15 +176,12 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b
157176
// args[1] - serialized Envelope
158177
args := [][]byte{[]byte(""), envBytes}
159178

160-
// get context for the chaincode execution
161-
lgr := v.ledger
162-
txsim, err := lgr.NewTxSimulator()
179+
ctxt, err := v.ccprovider.GetContext(v.ledger)
163180
if err != nil {
164-
logger.Errorf("Cannot obtain tx simulator txid=%s, err %s", txid, err)
181+
logger.Errorf("Cannot obtain context for txid=%s, err %s", txid, err)
165182
return err
166183
}
167-
defer txsim.Done()
168-
ctxt := context.WithValue(context.Background(), chaincode.TXSimulatorKey, txsim)
184+
defer v.ccprovider.ReleaseContext()
169185

170186
// get header extensions so we have the visibility field
171187
hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header)
@@ -177,31 +193,24 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b
177193
// Explanation: we actually deploying chaincode transaction,
178194
// hence no lccc yet to query for the data, therefore currently
179195
// introducing a workaround to skip obtaining LCCC data.
180-
var data *chaincode.ChaincodeData
196+
vscc := "vscc"
181197
if hdrExt.ChaincodeID.Name != "lccc" {
182198
// Extracting vscc from lccc
183-
logger.Info("Extracting chaincode data from LCCC txid = ", txid, "chainID", chainID, "chaincode name", hdrExt.ChaincodeID.Name)
184-
data, err = chaincode.GetChaincodeDataFromLCCC(ctxt, txid, nil, chainID, hdrExt.ChaincodeID.Name)
199+
vscc, err = v.ccprovider.GetVSCCFromLCCC(ctxt, txid, nil, chainID, hdrExt.ChaincodeID.Name)
185200
if err != nil {
186201
logger.Errorf("Unable to get chaincode data from LCCC for txid %s, due to %s", txid, err)
187202
return err
188203
}
189204
}
190205

191-
vscc := "vscc"
192-
// Check whenever VSCC defined for chaincode data
193-
if data != nil && data.Vscc != "" {
194-
vscc = data.Vscc
195-
}
196-
197206
vscctxid := coreUtil.GenerateUUID()
198207
// Get chaincode version
199208
version := coreUtil.GetSysCCVersion()
200-
cccid := chaincode.NewCCContext(chainID, vscc, version, vscctxid, true, nil)
209+
cccid := v.ccprovider.GetCCContext(chainID, vscc, version, vscctxid, true, nil)
201210

202211
// invoke VSCC
203212
logger.Info("Invoking VSCC txid", txid, "chaindID", chainID)
204-
_, _, err = chaincode.ExecuteChaincode(ctxt, cccid, args)
213+
_, _, err = v.ccprovider.ExecuteChaincode(ctxt, cccid, args)
205214
if err != nil {
206215
logger.Errorf("VSCC check failed for transaction txid=%s, error %s", txid, err)
207216
return err

‎core/common/ccprovider/ccprovider.go

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package ccprovider
2+
3+
import (
4+
"context"
5+
6+
"github.com/hyperledger/fabric/core/ledger"
7+
"github.com/hyperledger/fabric/protos/peer"
8+
)
9+
10+
// ChaincodeProvider provides an abstraction layer that is
11+
// used for different packages to interact with code in the
12+
// chaincode package without importing it; more methods
13+
// should be added below if necessary
14+
type ChaincodeProvider interface {
15+
// GetContext returns a ledger context
16+
GetContext(ledger ledger.ValidatedLedger) (context.Context, error)
17+
// GetCCContext returns an opaque chaincode context
18+
GetCCContext(cid, name, version, txid string, syscc bool, prop *peer.Proposal) interface{}
19+
// GetVSCCFromLCCC returns the VSCC listed by LCCC for the supplied chaincode
20+
GetVSCCFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, error)
21+
// ExecuteChaincode executes the chaincode given context and args
22+
ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) ([]byte, *peer.ChaincodeEvent, error)
23+
// ReleaseContext releases the context returned previously by GetContext
24+
ReleaseContext()
25+
}
26+
27+
var ccFactory ChaincodeProviderFactory
28+
29+
// ChaincodeProviderFactory defines a factory interface so
30+
// that the actual implementation can be injected
31+
type ChaincodeProviderFactory interface {
32+
NewChaincodeProvider() ChaincodeProvider
33+
}
34+
35+
// RegisterChaincodeProviderFactory is to be called once to set
36+
// the factory that will be used to obtain instances of ChaincodeProvider
37+
func RegisterChaincodeProviderFactory(ccfact ChaincodeProviderFactory) {
38+
ccFactory = ccfact
39+
}
40+
41+
// GetChaincodeProvider returns instances of ChaincodeProvider;
42+
// the actual implementation is controlled by the factory that
43+
// is registered via RegisterChaincodeProviderFactory
44+
func GetChaincodeProvider() ChaincodeProvider {
45+
if ccFactory == nil {
46+
panic("The factory must be set first via RegisterChaincodeProviderFactory")
47+
}
48+
return ccFactory.NewChaincodeProvider()
49+
}

‎core/common/validation/config_test.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package validation
18+
19+
import (
20+
"testing"
21+
22+
"github.com/hyperledger/fabric/common/configtx"
23+
"github.com/hyperledger/fabric/common/configtx/test"
24+
"github.com/hyperledger/fabric/common/util"
25+
"github.com/hyperledger/fabric/protos/utils"
26+
)
27+
28+
func TestValidateConfigTx(t *testing.T) {
29+
chainID := util.GetTestChainID()
30+
oTemplate := test.GetOrdererTemplate()
31+
mspcfg := configtx.NewSimpleTemplate(utils.EncodeMSPUnsigned(chainID))
32+
chCrtTemp := configtx.NewCompositeTemplate(oTemplate, mspcfg)
33+
chCrtEnv, err := configtx.MakeChainCreationTransaction(test.AcceptAllPolicyKey, chainID, signer, chCrtTemp)
34+
if err != nil {
35+
t.Fatalf("MakeChainCreationTransaction failed, err %s", err)
36+
return
37+
}
38+
39+
_, err = ValidateTransaction(chCrtEnv)
40+
if err != nil {
41+
t.Fatalf("ValidateTransaction failed, err %s", err)
42+
return
43+
}
44+
}

‎core/peer/fullflow_test.go ‎core/common/validation/fullflow_test.go

+14-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package peer
17+
package validation
1818

1919
import (
2020
"math/rand"
@@ -82,12 +82,20 @@ func TestGoodPath(t *testing.T) {
8282
}
8383

8484
// validate the transaction
85-
_, act, err := ValidateTransaction(tx)
85+
payl, err := ValidateTransaction(tx)
8686
if err != nil {
8787
t.Fatalf("ValidateTransaction failed, err %s", err)
8888
return
8989
}
9090

91+
txx, err := utils.GetTransaction(payl.Data)
92+
if err != nil {
93+
t.Fatalf("GetTransaction failed, err %s", err)
94+
return
95+
}
96+
97+
act := txx.Actions
98+
9199
// expect one single action
92100
if len(act) != 1 {
93101
t.Fatalf("Ivalid number of TransactionAction, expected 1, got %d", len(act))
@@ -210,7 +218,7 @@ func TestBadTx(t *testing.T) {
210218
corrupt(tx.Payload)
211219

212220
// validate the transaction it should fail
213-
_, _, err = ValidateTransaction(tx)
221+
_, err = ValidateTransaction(tx)
214222
if err == nil {
215223
t.Fatalf("ValidateTransaction should have failed")
216224
return
@@ -227,7 +235,7 @@ func TestBadTx(t *testing.T) {
227235
corrupt(tx.Signature)
228236

229237
// validate the transaction it should fail
230-
_, _, err = ValidateTransaction(tx)
238+
_, err = ValidateTransaction(tx)
231239
if err == nil {
232240
t.Fatalf("ValidateTransaction should have failed")
233241
return
@@ -268,7 +276,7 @@ func Test2EndorsersAgree(t *testing.T) {
268276
}
269277

270278
// validate the transaction
271-
_, _, err = ValidateTransaction(tx)
279+
_, err = ValidateTransaction(tx)
272280
if err != nil {
273281
t.Fatalf("ValidateTransaction failed, err %s", err)
274282
return
@@ -315,7 +323,7 @@ var signerSerialized []byte
315323
func TestMain(m *testing.M) {
316324
// setup crypto algorithms
317325
// setup the MSP manager so that we can sign/verify
318-
mspMgrConfigDir := "../../msp/sampleconfig/"
326+
mspMgrConfigDir := "../../../msp/sampleconfig/"
319327
err := mspmgmt.LoadFakeSetupWithLocalMspAndTestChainMsp(mspMgrConfigDir)
320328
if err != nil {
321329
fmt.Printf("Could not initialize msp, err %s", err)

‎core/peer/msgvalidation.go ‎core/common/validation/msgvalidation.go

+58-31
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package peer
17+
package validation
1818

1919
import (
2020
"fmt"
@@ -192,8 +192,6 @@ func validateChainHeader(cHdr *common.ChainHeader) error {
192192

193193
// TODO: validate epoch in cHdr.Epoch
194194

195-
// TODO: validate type in cHdr.Type
196-
197195
// TODO: validate version in cHdr.Version
198196

199197
return nil
@@ -218,125 +216,150 @@ func validateCommonHeader(hdr *common.Header) error {
218216
return nil
219217
}
220218

219+
// validateConfigTransaction validates the payload of a
220+
// transaction assuming its type is CONFIGURATION_TRANSACTION
221+
func validateConfigTransaction(data []byte, hdr *common.Header) error {
222+
putilsLogger.Infof("validateConfigTransaction starts for data %p, header %s", data, hdr)
223+
224+
// check for nil argument
225+
if data == nil || hdr == nil {
226+
return fmt.Errorf("Nil arguments")
227+
}
228+
229+
// if the type is CONFIGURATION_TRANSACTION we unmarshal a ConfigurationEnvelope message
230+
ce, err := utils.GetConfigurationEnvelope(data)
231+
if err != nil {
232+
return fmt.Errorf("GetConfigurationEnvelope failed, err %s", err)
233+
}
234+
235+
// check that we have at least one configuration item
236+
if ce.Items == nil || len(ce.Items) == 0 {
237+
return fmt.Errorf("At least one configuration item is necessary")
238+
}
239+
240+
for _, item := range ce.Items {
241+
if item.ConfigurationItem == nil {
242+
return fmt.Errorf("ConfigurationItem cannot be nil")
243+
}
244+
}
245+
246+
return nil
247+
}
248+
221249
// validateEndorserTransaction validates the payload of a
222250
// transaction assuming its type is ENDORSER_TRANSACTION
223-
func validateEndorserTransaction(data []byte, hdr *common.Header) ([]*pb.TransactionAction, error) {
251+
func validateEndorserTransaction(data []byte, hdr *common.Header) error {
224252
putilsLogger.Infof("validateEndorserTransaction starts for data %p, header %s", data, hdr)
225253

226254
// check for nil argument
227255
if data == nil || hdr == nil {
228-
return nil, fmt.Errorf("Nil arguments")
256+
return fmt.Errorf("Nil arguments")
229257
}
230258

231259
// if the type is ENDORSER_TRANSACTION we unmarshal a Transaction message
232260
tx, err := utils.GetTransaction(data)
233261
if err != nil {
234-
return nil, err
262+
return err
235263
}
236264

237265
// check for nil argument
238266
if tx == nil {
239-
return nil, fmt.Errorf("Nil transaction")
267+
return fmt.Errorf("Nil transaction")
240268
}
241269

242270
// TODO: validate tx.Version
243271

244272
// TODO: validate ChaincodeHeaderExtension
245273

246274
if len(tx.Actions) == 0 {
247-
return nil, fmt.Errorf("At least one TransactionAction is required")
275+
return fmt.Errorf("At least one TransactionAction is required")
248276
}
249277

250278
putilsLogger.Infof("validateEndorserTransaction info: there are %d actions", len(tx.Actions))
251279

252280
for _, act := range tx.Actions {
253281
// check for nil argument
254282
if act == nil {
255-
return nil, fmt.Errorf("Nil action")
283+
return fmt.Errorf("Nil action")
256284
}
257285

258286
// if the type is ENDORSER_TRANSACTION we unmarshal a SignatureHeader
259287
sHdr, err := utils.GetSignatureHeader(act.Header)
260288
if err != nil {
261-
return nil, err
289+
return err
262290
}
263291

264292
// validate the SignatureHeader - here we actually only
265293
// care about the nonce since the creator is in the outer header
266294
err = validateSignatureHeader(sHdr)
267295
if err != nil {
268-
return nil, err
296+
return err
269297
}
270298

271299
putilsLogger.Infof("validateEndorserTransaction info: signature header is valid")
272300

273301
// if the type is ENDORSER_TRANSACTION we unmarshal a ChaincodeActionPayload
274302
cap, err := utils.GetChaincodeActionPayload(act.Payload)
275303
if err != nil {
276-
return nil, err
304+
return err
277305
}
278306

279307
// extract the proposal response payload
280308
prp, err := utils.GetProposalResponsePayload(cap.Action.ProposalResponsePayload)
281309
if err != nil {
282-
return nil, err
310+
return err
283311
}
284312

285313
// build the original header by stitching together
286314
// the common ChainHeader and the per-action SignatureHeader
287315
hdrOrig := &common.Header{ChainHeader: hdr.ChainHeader, SignatureHeader: sHdr}
288316
hdrBytes, err := utils.GetBytesHeader(hdrOrig) // FIXME: here we hope that hdrBytes will be the same one that the endorser had
289317
if err != nil {
290-
return nil, err
318+
return err
291319
}
292320

293321
// compute proposalHash
294322
pHash, err := utils.GetProposalHash2(hdrBytes, cap.ChaincodeProposalPayload)
295323
if err != nil {
296-
return nil, err
324+
return err
297325
}
298326

299327
// ensure that the proposal hash matches
300328
if bytes.Compare(pHash, prp.ProposalHash) != 0 {
301-
return nil, fmt.Errorf("proposal hash does not match")
329+
return fmt.Errorf("proposal hash does not match")
302330
}
303331
}
304332

305-
return tx.Actions, nil
333+
return nil
306334
}
307335

308336
// ValidateTransaction checks that the transaction envelope is properly formed
309-
func ValidateTransaction(e *common.Envelope) (*common.Payload, []*pb.TransactionAction, error) {
337+
func ValidateTransaction(e *common.Envelope) (*common.Payload, error) {
310338
putilsLogger.Infof("ValidateTransactionEnvelope starts for envelope %p", e)
311339

312340
// check for nil argument
313341
if e == nil {
314-
return nil, nil, fmt.Errorf("Nil Envelope")
342+
return nil, fmt.Errorf("Nil Envelope")
315343
}
316344

317345
// get the payload from the envelope
318346
payload, err := utils.GetPayload(e)
319347
if err != nil {
320-
return nil, nil, fmt.Errorf("Could not extract payload from envelope, err %s", err)
348+
return nil, fmt.Errorf("Could not extract payload from envelope, err %s", err)
321349
}
322350

323351
putilsLogger.Infof("Header is %s", payload.Header)
324352

325-
if common.HeaderType(payload.Header.ChainHeader.Type) == common.HeaderType_CONFIGURATION_TRANSACTION {
326-
putilsLogger.Warningf("Skipping common.HeaderType_CONFIGURATION_TRANSACTION validation pending JIRA-1639\n")
327-
return payload, nil, nil
328-
}
329-
330353
// validate the header
331354
err = validateCommonHeader(payload.Header)
332355
if err != nil {
333-
return nil, nil, err
356+
return nil, err
334357
}
335358

336359
// validate the signature in the envelope
337360
err = checkSignatureFromCreator(payload.Header.SignatureHeader.Creator, e.Signature, e.Payload, payload.Header.ChainHeader.ChainID)
338361
if err != nil {
339-
return nil, nil, err
362+
return nil, err
340363
}
341364

342365
// TODO: ensure that creator can transact with us (some ACLs?) which set of APIs is supposed to give us this info?
@@ -346,10 +369,14 @@ func ValidateTransaction(e *common.Envelope) (*common.Payload, []*pb.Transaction
346369
// continue the validation in a way that depends on the type specified in the header
347370
switch common.HeaderType(payload.Header.ChainHeader.Type) {
348371
case common.HeaderType_ENDORSER_TRANSACTION:
349-
rv, err := validateEndorserTransaction(payload.Data, payload.Header)
350-
putilsLogger.Infof("ValidateTransactionEnvelope returns %p, err %s", rv, err)
351-
return payload, rv, err
372+
err = validateEndorserTransaction(payload.Data, payload.Header)
373+
putilsLogger.Infof("ValidateTransactionEnvelope returns err %s", err)
374+
return payload, err
375+
case common.HeaderType_CONFIGURATION_TRANSACTION:
376+
err = validateConfigTransaction(payload.Data, payload.Header)
377+
putilsLogger.Infof("ValidateTransactionEnvelope returns err %s", err)
378+
return payload, err
352379
default:
353-
return nil, nil, fmt.Errorf("Unsupported transaction payload type %d", common.HeaderType(payload.Header.ChainHeader.Type))
380+
return nil, fmt.Errorf("Unsupported transaction payload type %d", common.HeaderType(payload.Header.ChainHeader.Type))
354381
}
355382
}

‎core/deliverservice/client.go

-8
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ import (
2222

2323
"github.com/golang/protobuf/proto"
2424
"github.com/hyperledger/fabric/core/committer"
25-
"github.com/hyperledger/fabric/core/committer/txvalidator"
26-
"github.com/hyperledger/fabric/core/peer"
2725
"github.com/hyperledger/fabric/events/producer"
2826
gossipcommon "github.com/hyperledger/fabric/gossip/common"
2927
gossip_proto "github.com/hyperledger/fabric/gossip/proto"
@@ -208,12 +206,6 @@ func (d *DeliverService) readUntilClose() {
208206
case *orderer.DeliverResponse_Block:
209207
seqNum := t.Block.Header.Number
210208

211-
// Create new transactions validator
212-
validator := txvalidator.NewTxValidator(peer.GetLedger(d.chainID))
213-
// Validate and mark invalid transactions
214-
logger.Debug("Validating block, chainID", d.chainID)
215-
validator.Validate(t.Block)
216-
217209
numberOfPeers := len(service.GetGossipService().PeersOfChannel(gossipcommon.ChainID(d.chainID)))
218210
// Create payload with a block received
219211
payload := createPayload(seqNum, t.Block)

‎core/endorser/endorser.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"github.com/hyperledger/fabric/common/util"
2727
"github.com/hyperledger/fabric/core/chaincode"
28+
"github.com/hyperledger/fabric/core/common/validation"
2829
"github.com/hyperledger/fabric/core/ledger"
2930
"github.com/hyperledger/fabric/core/peer"
3031
"github.com/hyperledger/fabric/msp"
@@ -280,7 +281,7 @@ func (e *Endorser) endorseProposal(ctx context.Context, chainID string, txid str
280281
// ProcessProposal process the Proposal
281282
func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
282283
// at first, we check whether the message is valid
283-
prop, _, hdrExt, err := peer.ValidateProposalMessage(signedProp)
284+
prop, _, hdrExt, err := validation.ValidateProposalMessage(signedProp)
284285
if err != nil {
285286
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
286287
}

‎core/mocks/ccprovider/ccprovider.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package ccprovider
2+
3+
import (
4+
"context"
5+
6+
"github.com/hyperledger/fabric/core/common/ccprovider"
7+
"github.com/hyperledger/fabric/core/ledger"
8+
"github.com/hyperledger/fabric/protos/peer"
9+
)
10+
11+
// MockCcProviderFactory is a factory that returns
12+
// mock implementations of the ccprovider.ChaincodeProvider interface
13+
type MockCcProviderFactory struct {
14+
}
15+
16+
// NewChaincodeProvider returns a mock implementation of the ccprovider.ChaincodeProvider interface
17+
func (c *MockCcProviderFactory) NewChaincodeProvider() ccprovider.ChaincodeProvider {
18+
return &mockCcProviderImpl{}
19+
}
20+
21+
// mockCcProviderImpl is a mock implementation of the chaincode provider
22+
type mockCcProviderImpl struct {
23+
}
24+
25+
type mockCcProviderContextImpl struct {
26+
}
27+
28+
// GetContext does nothing
29+
func (c *mockCcProviderImpl) GetContext(ledger ledger.ValidatedLedger) (context.Context, error) {
30+
return nil, nil
31+
}
32+
33+
// GetCCContext does nothing
34+
func (c *mockCcProviderImpl) GetCCContext(cid, name, version, txid string, syscc bool, prop *peer.Proposal) interface{} {
35+
return &mockCcProviderContextImpl{}
36+
}
37+
38+
// GetVSCCFromLCCC does nothing
39+
func (c *mockCcProviderImpl) GetVSCCFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, error) {
40+
return "vscc", nil
41+
}
42+
43+
// ExecuteChaincode does nothing
44+
func (c *mockCcProviderImpl) ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) ([]byte, *peer.ChaincodeEvent, error) {
45+
return nil, nil, nil
46+
}
47+
48+
// ReleaseContext does nothing
49+
func (c *mockCcProviderImpl) ReleaseContext() {
50+
}

‎core/mocks/validator/validator.go

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package validator
2+
3+
import "github.com/hyperledger/fabric/protos/common"
4+
5+
// MockValidator implements a mock validation useful for testing
6+
type MockValidator struct {
7+
}
8+
9+
// Validate does nothing
10+
func (m *MockValidator) Validate(block *common.Block) {
11+
}
12+
13+
// MockVsccValidator is a mock implementation of the VSCC validation interface
14+
type MockVsccValidator struct {
15+
}
16+
17+
// VSCCValidateTx does nothing
18+
func (v *MockVsccValidator) VSCCValidateTx(payload *common.Payload, envBytes []byte) error {
19+
return nil
20+
}

‎core/peer/peer.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929

3030
"github.com/hyperledger/fabric/core/comm"
3131
"github.com/hyperledger/fabric/core/committer"
32+
"github.com/hyperledger/fabric/core/committer/txvalidator"
3233
"github.com/hyperledger/fabric/core/ledger"
3334
"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
3435
"github.com/hyperledger/fabric/core/peer/msp"
@@ -144,7 +145,7 @@ func getCurrConfigBlockFromLedger(ledger ledger.ValidatedLedger) (*common.Block,
144145

145146
// createChain creates a new chain object and insert it into the chains
146147
func createChain(cid string, ledger ledger.ValidatedLedger, cb *common.Block) error {
147-
c := committer.NewLedgerCommitter(ledger)
148+
c := committer.NewLedgerCommitter(ledger, txvalidator.NewTxValidator(ledger))
148149

149150
mgr, err := mspmgmt.GetMSPManagerFromBlock(cid, cb)
150151
if err != nil {

‎core/peer/peer_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,17 @@ import (
2727
"google.golang.org/grpc"
2828

2929
configtxtest "github.com/hyperledger/fabric/common/configtx/test"
30+
ccp "github.com/hyperledger/fabric/core/common/ccprovider"
31+
"github.com/hyperledger/fabric/core/mocks/ccprovider"
3032
"github.com/hyperledger/fabric/gossip/service"
3133
)
3234

3335
func TestInitialize(t *testing.T) {
3436
viper.Set("peer.fileSystemPath", "/var/hyperledger/test/")
3537

38+
// we mock this because we can't import the chaincode package lest we create an import cycle
39+
ccp.RegisterChaincodeProviderFactory(&ccprovider.MockCcProviderFactory{})
40+
3641
Initialize(nil)
3742
}
3843

‎core/system_chaincode/escc/endorser_onevalidsignature_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525

2626
"github.com/hyperledger/fabric/common/util"
2727
"github.com/hyperledger/fabric/core/chaincode/shim"
28-
"github.com/hyperledger/fabric/core/peer"
28+
"github.com/hyperledger/fabric/core/common/validation"
2929
"github.com/hyperledger/fabric/core/peer/msp"
3030
"github.com/hyperledger/fabric/protos/common"
3131
pb "github.com/hyperledger/fabric/protos/peer"
@@ -266,7 +266,7 @@ func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, visibility
266266
}
267267

268268
// validate the transaction
269-
_, _, err = peer.ValidateTransaction(tx)
269+
_, err = validation.ValidateTransaction(tx)
270270
if err != nil {
271271
return err
272272
}

‎gossip/state/state_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/hyperledger/fabric/common/util"
2929
"github.com/hyperledger/fabric/core/committer"
3030
"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
31+
"github.com/hyperledger/fabric/core/mocks/validator"
3132
"github.com/hyperledger/fabric/gossip/api"
3233
"github.com/hyperledger/fabric/gossip/comm"
3334
"github.com/hyperledger/fabric/gossip/common"
@@ -162,7 +163,7 @@ func newGossipInstance(config *gossip.Config) gossip.Gossip {
162163
// Create new instance of KVLedger to be used for testing
163164
func newCommitter(id int) committer.Committer {
164165
ledger, _ := ledgermgmt.CreateLedger(strconv.Itoa(id))
165-
return committer.NewLedgerCommitter(ledger)
166+
return committer.NewLedgerCommitter(ledger, &validator.MockValidator{})
166167
}
167168

168169
// Constructing pseudo peer node, simulating only gossip and state transfer part

‎orderer/multichain/manager_test.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ import (
3030
ab "github.com/hyperledger/fabric/protos/orderer"
3131
"github.com/hyperledger/fabric/protos/utils"
3232

33+
"github.com/hyperledger/fabric/msp"
3334
logging "github.com/op/go-logging"
35+
"github.com/stretchr/testify/assert"
3436
)
3537

3638
var conf *config.TopLevel
@@ -197,8 +199,11 @@ func TestNewChain(t *testing.T) {
197199
items := generator.TemplateItems()
198200
simpleTemplate := configtx.NewSimpleTemplate(items...)
199201

202+
signer, err := msp.NewNoopMsp().GetDefaultSigningIdentity()
203+
assert.NoError(t, err)
204+
200205
newChainID := "TestNewChain"
201-
newChainMessage, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, newChainID, simpleTemplate)
206+
newChainMessage, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, newChainID, signer, simpleTemplate)
202207
if err != nil {
203208
t.Fatalf("Error producing configuration transaction: %s", err)
204209
}

‎orderer/sample_clients/broadcast_config/newchain.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package main
1818

1919
import (
2020
"github.com/hyperledger/fabric/common/configtx"
21+
"github.com/hyperledger/fabric/msp"
2122
"github.com/hyperledger/fabric/orderer/common/bootstrap/provisional"
2223
cb "github.com/hyperledger/fabric/protos/common"
2324
)
@@ -28,7 +29,12 @@ func newChainRequest(consensusType, creationPolicy, newChainID string) *cb.Envel
2829
items := generator.TemplateItems()
2930
simpleTemplate := configtx.NewSimpleTemplate(items...)
3031

31-
env, err := configtx.MakeChainCreationTransaction(creationPolicy, newChainID, simpleTemplate)
32+
signer, err := msp.NewNoopMsp().GetDefaultSigningIdentity()
33+
if err != nil {
34+
panic(err)
35+
}
36+
37+
env, err := configtx.MakeChainCreationTransaction(creationPolicy, newChainID, signer, simpleTemplate)
3238
if err != nil {
3339
panic(err)
3440
}

‎peer/channel/create.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/hyperledger/fabric/protos/utils"
2828

2929
"github.com/golang/protobuf/proto"
30+
"github.com/hyperledger/fabric/core/peer/msp"
3031
"github.com/spf13/cobra"
3132
)
3233

@@ -51,7 +52,12 @@ func sendCreateChainTransaction(cf *ChannelCmdFactory) error {
5152

5253
chCrtTemp := configtx.NewCompositeTemplate(oTemplate, mspcfg)
5354

54-
chCrtEnv, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, chainID, chCrtTemp)
55+
signer, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
56+
if err != nil {
57+
return err
58+
}
59+
60+
chCrtEnv, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, chainID, signer, chCrtTemp)
5561

5662
if err != nil {
5763
return err

‎protos/utils/proputils.go

+11
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,17 @@ func GetPayload(e *common.Envelope) (*common.Payload, error) {
131131
return payload, nil
132132
}
133133

134+
// GetConfigurationEnvelope returns a ConfigurationEnvelope from bytes
135+
func GetConfigurationEnvelope(bytes []byte) (*common.ConfigurationEnvelope, error) {
136+
ce := &common.ConfigurationEnvelope{}
137+
err := proto.Unmarshal(bytes, ce)
138+
if err != nil {
139+
return nil, err
140+
}
141+
142+
return ce, nil
143+
}
144+
134145
// GetTransaction Get Transaction from bytes
135146
func GetTransaction(txBytes []byte) (*peer.Transaction, error) {
136147
tx := &peer.Transaction{}

0 commit comments

Comments
 (0)
Please sign in to comment.