Skip to content

Commit

Permalink
[FAB-1639] [FAB-1580] Rework validator
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
ale-linux committed Jan 16, 2017
1 parent dca94df commit ae10d2b
Show file tree
Hide file tree
Showing 23 changed files with 431 additions and 93 deletions.
22 changes: 19 additions & 3 deletions common/configtx/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import (
ab "github.com/hyperledger/fabric/protos/orderer"
"github.com/hyperledger/fabric/protos/utils"

"fmt"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/msp"
)

const (
Expand Down Expand Up @@ -150,16 +153,29 @@ func join(sets ...[]*cb.SignedConfigurationItem) []*cb.SignedConfigurationItem {
}

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

sSigner, err := signer.Serialize()
if err != nil {
return nil, fmt.Errorf("Serialization of identity failed, err %s", err)
}

payloadChainHeader := utils.MakeChainHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, msgVersion, chainID, epoch)
payloadSignatureHeader := utils.MakeSignatureHeader(nil, utils.CreateNonceOrPanic())
payloadSignatureHeader := utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic())
payloadHeader := utils.MakePayloadHeader(payloadChainHeader, payloadSignatureHeader)
payload := &cb.Payload{Header: payloadHeader, Data: utils.MarshalOrPanic(utils.MakeConfigurationEnvelope(signedConfigItems...))}
return &cb.Envelope{Payload: utils.MarshalOrPanic(payload), Signature: nil}, nil
paylBytes := utils.MarshalOrPanic(payload)

// sign the payload
sig, err := signer.Sign(paylBytes)
if err != nil {
return nil, err
}

return &cb.Envelope{Payload: paylBytes, Signature: sig}, nil
}
5 changes: 5 additions & 0 deletions common/configtx/test/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ import (
"github.com/golang/protobuf/proto"
)

const (
// AcceptAllPolicyKey is the key of the AcceptAllPolicy.
AcceptAllPolicyKey = "AcceptAllPolicy"
)

var template configtx.Template

var genesisFactory genesis.Factory
Expand Down
81 changes: 81 additions & 0 deletions core/chaincode/ccproviderimpl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package chaincode

import (
"context"

"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/protos/peer"
)

// ccProviderFactory implements the ccprovider.ChaincodeProviderFactory
// interface and returns instances of ccprovider.ChaincodeProvider
type ccProviderFactory struct {
}

// NewChaincodeProvider returns pointers to ccProviderImpl as an
// implementer of the ccprovider.ChaincodeProvider interface
func (c *ccProviderFactory) NewChaincodeProvider() ccprovider.ChaincodeProvider {
return &ccProviderImpl{}
}

// init is called when this package is loaded. This implementation registers the factory
func init() {
ccprovider.RegisterChaincodeProviderFactory(&ccProviderFactory{})
}

// ccProviderImpl is an implementation of the ccprovider.ChaincodeProvider interface
type ccProviderImpl struct {
txsim ledger.TxSimulator
}

// ccProviderContextImpl contains the state that is passed around to calls to methods of ccProviderImpl
type ccProviderContextImpl struct {
ctx *CCContext
}

// GetContext returns a context for the supplied ledger, with the appropriate tx simulator
func (c *ccProviderImpl) GetContext(ledger ledger.ValidatedLedger) (context.Context, error) {
var err error
// get context for the chaincode execution
c.txsim, err = ledger.NewTxSimulator()
if err != nil {
return nil, err
}
ctxt := context.WithValue(context.Background(), TXSimulatorKey, c.txsim)
return ctxt, nil
}

// GetCCContext returns an interface that encapsulates a
// chaincode context; the interface is required to avoid
// referencing the chaincode package from the interface definition
func (c *ccProviderImpl) GetCCContext(cid, name, version, txid string, syscc bool, prop *peer.Proposal) interface{} {
ctx := NewCCContext(cid, name, version, txid, syscc, prop)
return &ccProviderContextImpl{ctx: ctx}
}

// GetVSCCFromLCCC returns the VSCC listed in LCCC for the supplied chaincode
func (c *ccProviderImpl) GetVSCCFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, error) {
data, err := GetChaincodeDataFromLCCC(ctxt, txid, prop, chainID, chaincodeID)
if err != nil {
return "", err
}

vscc := "vscc"
// Check whenever VSCC defined for chaincode data
if data != nil && data.Vscc != "" {
vscc = data.Vscc
}

return vscc, nil
}

// ExecuteChaincode executes the chaincode specified in the context with the specified arguments
func (c *ccProviderImpl) ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) ([]byte, *peer.ChaincodeEvent, error) {
return ExecuteChaincode(ctxt, cccid.(*ccProviderContextImpl).ctx, args)
}

// ReleaseContext frees up resources held by the context
func (c *ccProviderImpl) ReleaseContext() {
c.txsim.Done()
}
12 changes: 9 additions & 3 deletions core/committer/committer_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package committer

import (
"github.com/hyperledger/fabric/core/committer/txvalidator"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
Expand All @@ -38,16 +39,21 @@ func init() {
// it keeps the reference to the ledger to commit blocks and retreive
// chain information
type LedgerCommitter struct {
ledger ledger.ValidatedLedger
ledger ledger.ValidatedLedger
validator txvalidator.Validator
}

// NewLedgerCommitter is a factory function to create an instance of the committer
func NewLedgerCommitter(ledger ledger.ValidatedLedger) *LedgerCommitter {
return &LedgerCommitter{ledger}
func NewLedgerCommitter(ledger ledger.ValidatedLedger, validator txvalidator.Validator) *LedgerCommitter {
return &LedgerCommitter{ledger: ledger, validator: validator}
}

// CommitBlock commits block to into the ledger
func (lc *LedgerCommitter) CommitBlock(block *common.Block) error {
// Validate and mark invalid transactions
logger.Debug("Validating block")
lc.validator.Validate(block)

if err := lc.ledger.Commit(block); err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion core/committer/committer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/assert"

"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
"github.com/hyperledger/fabric/core/mocks/validator"
pb "github.com/hyperledger/fabric/protos/peer"
)

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

committer := NewLedgerCommitter(ledger)
committer := NewLedgerCommitter(ledger, &validator.MockValidator{})
height, err := committer.LedgerHeight()
assert.Equal(t, uint64(0), height)
assert.NoError(t, err)
Expand Down
12 changes: 3 additions & 9 deletions core/committer/txvalidator/txvalidator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,22 @@ import (
"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
"github.com/hyperledger/fabric/core/ledger/testutil"
"github.com/hyperledger/fabric/core/ledger/util"
"github.com/hyperledger/fabric/core/mocks/validator"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)

type mockVsccValidator struct {
}

func (v *mockVsccValidator) VSCCValidateTx(payload *common.Payload, envBytes []byte) error {
return nil
}

func TestKVLedgerBlockStorage(t *testing.T) {
viper.Set("peer.fileSystemPath", "/tmp/fabric/txvalidatortest")
ledgermgmt.InitializeTestEnv()
defer ledgermgmt.CleanupTestEnv()
ledger, _ := ledgermgmt.CreateLedger("TestLedger")
defer ledger.Close()

validator := &txValidator{ledger, &mockVsccValidator{}}
validator := &txValidator{ledger, &validator.MockVsccValidator{}}

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

validator := &txValidator{ledger, &mockVsccValidator{}}
validator := &txValidator{ledger, &validator.MockVsccValidator{}}

// Create simeple endorsement transaction
payload := &common.Payload{
Expand Down
57 changes: 33 additions & 24 deletions core/committer/txvalidator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,14 @@ limitations under the License.
package txvalidator

import (
"context"
"fmt"

"github.com/golang/protobuf/proto"
coreUtil "github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/core/chaincode"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/common/validation"
"github.com/hyperledger/fabric/core/ledger"
ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"
"github.com/op/go-logging"
Expand All @@ -48,7 +47,8 @@ type vsccValidator interface {
// vsccValidator implementation which used to call
// vscc chaincode and validate block transactions
type vsccValidatorImpl struct {
ledger ledger.ValidatedLedger
ledger ledger.ValidatedLedger
ccprovider ccprovider.ChaincodeProvider
}

// implementation of Validator interface, keeps
Expand All @@ -69,7 +69,12 @@ func init() {
// NewTxValidator creates new transactions validator
func NewTxValidator(ledger ledger.ValidatedLedger) Validator {
// Encapsulates interface implementation
return &txValidator{ledger, &vsccValidatorImpl{ledger}}
return &txValidator{ledger, &vsccValidatorImpl{ledger: ledger, ccprovider: ccprovider.GetChaincodeProvider()}}
}

func (v *txValidator) chainExists(chain string) bool {
// TODO: implement this function!
return true
}

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

chain := payload.Header.ChainHeader.ChainID
logger.Debug("Transaction is for chain %s", chain)

if !v.chainExists(chain) {
logger.Errorf("Dropping transaction for non-existent chain %s", chain)
continue
}

if common.HeaderType(payload.Header.ChainHeader.Type) == common.HeaderType_ENDORSER_TRANSACTION {
// Check duplicate transactions
txID := payload.Header.ChainHeader.TxID
Expand All @@ -113,7 +126,13 @@ func (v *txValidator) Validate(block *common.Block) {
continue
}
} else if common.HeaderType(payload.Header.ChainHeader.Type) == common.HeaderType_CONFIGURATION_TRANSACTION {
logger.Warningf("Validation for common.HeaderType_CONFIGURATION_TRANSACTION %d pending JIRA-1639", tIdx)
// TODO: here we should call CSCC and pass it the config tx
// note that there is quite a bit more validation necessary
// on this tx, namely, validation that each config item has
// signature matching the policy required for the item from
// the existing configuration; this is taken care of nicely
// by configtx.Manager (see fabric/common/configtx).
logger.Debug("config transaction received for chain %s", chain)
}

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

// get context for the chaincode execution
lgr := v.ledger
txsim, err := lgr.NewTxSimulator()
ctxt, err := v.ccprovider.GetContext(v.ledger)
if err != nil {
logger.Errorf("Cannot obtain tx simulator txid=%s, err %s", txid, err)
logger.Errorf("Cannot obtain context for txid=%s, err %s", txid, err)
return err
}
defer txsim.Done()
ctxt := context.WithValue(context.Background(), chaincode.TXSimulatorKey, txsim)
defer v.ccprovider.ReleaseContext()

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

vscc := "vscc"
// Check whenever VSCC defined for chaincode data
if data != nil && data.Vscc != "" {
vscc = data.Vscc
}

vscctxid := coreUtil.GenerateUUID()
// Get chaincode version
version := coreUtil.GetSysCCVersion()
cccid := chaincode.NewCCContext(chainID, vscc, version, vscctxid, true, nil)
cccid := v.ccprovider.GetCCContext(chainID, vscc, version, vscctxid, true, nil)

// invoke VSCC
logger.Info("Invoking VSCC txid", txid, "chaindID", chainID)
_, _, err = chaincode.ExecuteChaincode(ctxt, cccid, args)
_, _, err = v.ccprovider.ExecuteChaincode(ctxt, cccid, args)
if err != nil {
logger.Errorf("VSCC check failed for transaction txid=%s, error %s", txid, err)
return err
Expand Down
Loading

0 comments on commit ae10d2b

Please sign in to comment.