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 }