From 9dbaeca6b439a2655af91e027c5804cb3f346c36 Mon Sep 17 00:00:00 2001 From: Jason Yellick Date: Mon, 23 Jan 2017 18:07:33 -0500 Subject: [PATCH] [FAB-1812] Pass configtx.Manager.Apply to validate https://jira.hyperledger.org/browse/FAB-1812 The peer needs to apply configuration transactions as they are committed. This changeset hooks the configtx.Manager.Apply method into core/committer/txvalidator/validator.go so that configuration is updated as configuration blocks are committed. Because it was in the same path, it also passes the MSPManager to the validator to remove one of the dependencies on the throwaway MSP multichain manager. Change-Id: I4d606d850dddb684468d7a5dd7257e8dd967ad28 Signed-off-by: Jason Yellick --- core/committer/committer_impl.go | 5 +- .../committer/txvalidator/txvalidator_test.go | 5 +- core/committer/txvalidator/validator.go | 60 ++++++++++++------- core/mocks/txvalidator/support.go | 44 ++++++++++++++ core/mocks/validator/validator.go | 5 +- core/peer/peer.go | 54 +++++++++++++++-- 6 files changed, 141 insertions(+), 32 deletions(-) create mode 100644 core/mocks/txvalidator/support.go diff --git a/core/committer/committer_impl.go b/core/committer/committer_impl.go index 202d2be4bfb..34bd520c964 100644 --- a/core/committer/committer_impl.go +++ b/core/committer/committer_impl.go @@ -49,10 +49,13 @@ func NewLedgerCommitter(ledger ledger.PeerLedger, validator txvalidator.Validato } // Commit commits block to into the ledger +// Note, it is important that this always be called serially func (lc *LedgerCommitter) Commit(block *common.Block) error { // Validate and mark invalid transactions logger.Debug("Validating block") - lc.validator.Validate(block) + if err := lc.validator.Validate(block); err != nil { + return err + } if err := lc.ledger.Commit(block); err != nil { return err diff --git a/core/committer/txvalidator/txvalidator_test.go b/core/committer/txvalidator/txvalidator_test.go index 80864764a9c..be573706f5a 100644 --- a/core/committer/txvalidator/txvalidator_test.go +++ b/core/committer/txvalidator/txvalidator_test.go @@ -24,6 +24,7 @@ import ( "github.com/hyperledger/fabric/core/ledger/ledgermgmt" "github.com/hyperledger/fabric/core/ledger/testutil" "github.com/hyperledger/fabric/core/ledger/util" + mocktxvalidator "github.com/hyperledger/fabric/core/mocks/txvalidator" "github.com/hyperledger/fabric/core/mocks/validator" "github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric/protos/peer" @@ -39,7 +40,7 @@ func TestKVLedgerBlockStorage(t *testing.T) { ledger, _ := ledgermgmt.CreateLedger("TestLedger") defer ledger.Close() - validator := &txValidator{ledger, &validator.MockVsccValidator{}} + validator := &txValidator{&mocktxvalidator.Support{LedgerVal: ledger}, &validator.MockVsccValidator{}} bcInfo, _ := ledger.GetBlockchainInfo() testutil.AssertEquals(t, bcInfo, &pb.BlockchainInfo{ @@ -70,7 +71,7 @@ func TestNewTxValidator_DuplicateTransactions(t *testing.T) { ledger, _ := ledgermgmt.CreateLedger("TestLedger") defer ledger.Close() - validator := &txValidator{ledger, &validator.MockVsccValidator{}} + validator := &txValidator{&mocktxvalidator.Support{LedgerVal: ledger}, &validator.MockVsccValidator{}} // Create simeple endorsement transaction payload := &common.Payload{ diff --git a/core/committer/txvalidator/validator.go b/core/committer/txvalidator/validator.go index 8e2b9db14fc..01a620aa7ed 100644 --- a/core/committer/txvalidator/validator.go +++ b/core/committer/txvalidator/validator.go @@ -26,7 +26,6 @@ import ( "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/msp" "github.com/hyperledger/fabric/msp" "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/utils" @@ -34,11 +33,23 @@ import ( "github.com/syndtr/goleveldb/leveldb/errors" ) +// Support provides all of the needed to evaluate the VSCC +type Support interface { + // Ledger returns the ledger associated with this validator + Ledger() ledger.PeerLedger + + // MSPManager returns the MSP manager for this chain + MSPManager() msp.MSPManager + + // Apply attempts to apply a configtx to become the new configuration + Apply(configtx *common.ConfigurationEnvelope) error +} + //Validator interface which defines API to validate block transactions // and return the bit array mask indicating invalid transactions which // didn't pass validation. type Validator interface { - Validate(block *common.Block) + Validate(block *common.Block) error } // private interface to decouple tx validator @@ -51,7 +62,7 @@ type vsccValidator interface { // vsccValidator implementation which used to call // vscc chaincode and validate block transactions type vsccValidatorImpl struct { - ledger ledger.PeerLedger + support Support ccprovider ccprovider.ChaincodeProvider } @@ -59,8 +70,8 @@ type vsccValidatorImpl struct { // reference to the ledger to enable tx simulation // and execution of vscc type txValidator struct { - ledger ledger.PeerLedger - vscc vsccValidator + support Support + vscc vsccValidator } var logger *logging.Logger // package-level logger @@ -71,9 +82,9 @@ func init() { } // NewTxValidator creates new transactions validator -func NewTxValidator(ledger ledger.PeerLedger) Validator { +func NewTxValidator(support Support) Validator { // Encapsulates interface implementation - return &txValidator{ledger, &vsccValidatorImpl{ledger: ledger, ccprovider: ccprovider.GetChaincodeProvider()}} + return &txValidator{support, &vsccValidatorImpl{support: support, ccprovider: ccprovider.GetChaincodeProvider()}} } func (v *txValidator) chainExists(chain string) bool { @@ -81,7 +92,7 @@ func (v *txValidator) chainExists(chain string) bool { return true } -func (v *txValidator) Validate(block *common.Block) { +func (v *txValidator) Validate(block *common.Block) error { logger.Debug("START Block Validation") defer logger.Debug("END Block Validation") txsfltr := ledgerUtil.NewFilterBitArray(uint(len(block.Data.Data))) @@ -117,7 +128,7 @@ func (v *txValidator) Validate(block *common.Block) { if common.HeaderType(payload.Header.ChainHeader.Type) == common.HeaderType_ENDORSER_TRANSACTION { // Check duplicate transactions txID := payload.Header.ChainHeader.TxID - if _, err := v.ledger.GetTransactionByID(txID); err == nil { + if _, err := v.support.Ledger().GetTransactionByID(txID); err == nil { logger.Warning("Duplicate transaction found, ", txID, ", skipping") continue } @@ -130,13 +141,19 @@ func (v *txValidator) Validate(block *common.Block) { continue } } else if common.HeaderType(payload.Header.ChainHeader.Type) == common.HeaderType_CONFIGURATION_TRANSACTION { - // 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) + configEnvelope, err := utils.UnmarshalConfigurationEnvelope(payload.Data) + if err != nil { + err := fmt.Errorf("Error unmarshaling configuration which passed initial validity checks: %s", err) + logger.Critical(err) + return err + } + + if err := v.support.Apply(configEnvelope); err != nil { + err := fmt.Errorf("Error validating configuration which passed initial validity checks: %s", err) + logger.Critical(err) + return err + } + logger.Debugf("config transaction received for chain %s", chain) } if _, err := proto.Marshal(env); err != nil { @@ -155,16 +172,17 @@ func (v *txValidator) Validate(block *common.Block) { utils.InitBlockMetadata(block) // Serialize invalid transaction bit array into block metadata field block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsfltr.ToBytes() + + return nil } -// getHardcodedPolicy returns a policy that requests +// getSemiHardcodedPolicy returns a policy that requests // one valid signature from the first MSP in this // chain's MSP manager // FIXME: this needs to be removed as soon as we extract the policy from LCCC -func getHardcodedPolicy(chainID string) ([]byte, error) { +func getSemiHardcodedPolicy(chainID string, mspMgr msp.MSPManager) ([]byte, error) { // 1) determine the MSP identifier for the first MSP in this chain var msp msp.MSP - mspMgr := mspmgmt.GetManagerForChain(chainID) msps, err := mspMgr.GetMSPs() if err != nil { return nil, fmt.Errorf("Could not retrieve the MSPs for the chain manager, err %s", err) @@ -215,7 +233,7 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b // by the deployer and can be retrieved via LCCC: we create // a policy that requests 1 valid signature from this chain's // MSP - policy, err := getHardcodedPolicy(chainID) + policy, err := getSemiHardcodedPolicy(chainID, v.support.MSPManager()) if err != nil { return err } @@ -226,7 +244,7 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b // args[2] - serialized policy args := [][]byte{[]byte(""), envBytes, policy} - ctxt, err := v.ccprovider.GetContext(v.ledger) + ctxt, err := v.ccprovider.GetContext(v.support.Ledger()) if err != nil { logger.Errorf("Cannot obtain context for txid=%s, err %s", txid, err) return err diff --git a/core/mocks/txvalidator/support.go b/core/mocks/txvalidator/support.go new file mode 100644 index 00000000000..64e5823ae17 --- /dev/null +++ b/core/mocks/txvalidator/support.go @@ -0,0 +1,44 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package support + +import ( + "github.com/hyperledger/fabric/core/ledger" + "github.com/hyperledger/fabric/msp" + "github.com/hyperledger/fabric/protos/common" +) + +type Support struct { + LedgerVal ledger.PeerLedger + MSPManagerVal msp.MSPManager + ApplyVal error +} + +// Ledger returns LedgerVal +func (ms *Support) Ledger() ledger.PeerLedger { + return ms.LedgerVal +} + +// MSPManager returns MSPManagerVal +func (ms *Support) MSPManager() msp.MSPManager { + return ms.MSPManagerVal +} + +// Apply returns ApplyVal +func (ms *Support) Apply(configtx *common.ConfigurationEnvelope) error { + return ms.ApplyVal +} diff --git a/core/mocks/validator/validator.go b/core/mocks/validator/validator.go index a852cabe805..8183788282f 100644 --- a/core/mocks/validator/validator.go +++ b/core/mocks/validator/validator.go @@ -22,8 +22,9 @@ import "github.com/hyperledger/fabric/protos/common" type MockValidator struct { } -// Validate does nothing -func (m *MockValidator) Validate(block *common.Block) { +// Validate does nothing, returning no error +func (m *MockValidator) Validate(block *common.Block) error { + return nil } // MockVsccValidator is a mock implementation of the VSCC validation interface diff --git a/core/peer/peer.go b/core/peer/peer.go index a39afefd983..0be6f96e1c1 100644 --- a/core/peer/peer.go +++ b/core/peer/peer.go @@ -27,6 +27,7 @@ import ( "github.com/op/go-logging" "github.com/spf13/viper" + "github.com/hyperledger/fabric/common/configtx" "github.com/hyperledger/fabric/core/comm" "github.com/hyperledger/fabric/core/committer" "github.com/hyperledger/fabric/core/committer/txvalidator" @@ -41,12 +42,25 @@ import ( var peerLogger = logging.MustGetLogger("peer") +type chainSupport struct { + configtx.Manager + ledger ledger.PeerLedger + mspmgr msp.MSPManager +} + +func (cs *chainSupport) Ledger() ledger.PeerLedger { + return cs.ledger +} + +func (cs *chainSupport) MSPManager() msp.MSPManager { + return cs.mspmgr +} + // chain is a local struct to manage objects in a chain type chain struct { + cs *chainSupport cb *common.Block - ledger ledger.PeerLedger committer committer.Committer - mspmgr msp.MSPManager } // chains is a local map of chainID->chainObject @@ -146,13 +160,33 @@ func getCurrConfigBlockFromLedger(ledger ledger.PeerLedger) (*common.Block, erro // createChain creates a new chain object and insert it into the chains func createChain(cid string, ledger ledger.PeerLedger, cb *common.Block) error { - c := committer.NewLedgerCommitter(ledger, txvalidator.NewTxValidator(ledger)) + configEnvelope, _, err := utils.BreakOutBlockToConfigurationEnvelope(cb) + if err != nil { + return err + } + + configtxInitializer := configtx.NewInitializer() + // TODO Hook peer shared config manager in here once it exists + configtxManager, err := configtx.NewManagerImpl(configEnvelope, configtxInitializer) + if err != nil { + return err + } + + // TODO Move to the configtx.Handler interface (which MSP already implements) mgr, err := mspmgmt.GetMSPManagerFromBlock(cid, cb) if err != nil { return err } + cs := &chainSupport{ + Manager: configtxManager, + ledger: ledger, + mspmgr: mgr, + } + + c := committer.NewLedgerCommitter(ledger, txvalidator.NewTxValidator(cs)) + // TODO This should be called from a configtx.Manager but it's not // implemented yet. When it will be, this needs to move there, // and the inner fields (AnchorPeers) only should be passed to this. @@ -162,7 +196,15 @@ func createChain(cid string, ledger ledger.PeerLedger, cb *common.Block) error { chains.Lock() defer chains.Unlock() - chains.list[cid] = &chain{cb: cb, ledger: ledger, mspmgr: mgr, committer: c} + chains.list[cid] = &chain{ + cs: &chainSupport{ + Manager: configtxManager, + ledger: ledger, + mspmgr: mgr, + }, + cb: cb, + committer: c, + } return nil } @@ -191,7 +233,7 @@ func MockCreateChain(cid string) error { chains.Lock() defer chains.Unlock() - chains.list[cid] = &chain{ledger: ledger} + chains.list[cid] = &chain{cs: &chainSupport{ledger: ledger}} return nil } @@ -202,7 +244,7 @@ func GetLedger(cid string) ledger.PeerLedger { chains.RLock() defer chains.RUnlock() if c, ok := chains.list[cid]; ok { - return c.ledger + return c.cs.ledger } return nil }