// ValidateAndPrepareBatch implements method in Validator interface
func (v *Validator) ValidateAndPrepareBatch(block *common.Block, doMVCCValidation bool) (*statedb.UpdateBatch, error) {
	logger.Debugf("New block arrived for validation:%#v, doMVCCValidation=%t", block, doMVCCValidation)
	var valid bool
	updates := statedb.NewUpdateBatch()
	logger.Debugf("Validating a block with [%d] transactions", len(block.Data.Data))
	txsFilter := util.NewFilterBitArrayFromBytes(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
	for txIndex, envBytes := range block.Data.Data {
		if txsFilter.IsSet(uint(txIndex)) {
			// Skiping invalid transaction
			logger.Debug("Skipping transaction marked as invalid, txIndex=", txIndex)
			continue
		}

		// extract actions from the envelope message
		respPayload, err := putils.GetActionFromEnvelope(envBytes)
		if err != nil {
			return nil, err
		}

		//preparation for extracting RWSet from transaction
		txRWSet := &rwset.TxReadWriteSet{}

		// Get the Result from the Action
		// and then Unmarshal it into a TxReadWriteSet using custom unmarshalling
		if err = txRWSet.Unmarshal(respPayload.Results); err != nil {
			return nil, err
		}

		// trace the first 2000 characters of RWSet only, in case it is huge
		if logger.IsEnabledFor(logging.DEBUG) {
			txRWSetString := txRWSet.String()
			if len(txRWSetString) < 2000 {
				logger.Debugf("validating txRWSet:[%s]", txRWSetString)
			} else {
				logger.Debugf("validating txRWSet:[%s...]", txRWSetString[0:2000])
			}
		}

		if !doMVCCValidation {
			valid = true
		} else if valid, err = v.validateTx(txRWSet, updates); err != nil {
			return nil, err
		}

		if valid {
			committingTxHeight := version.NewHeight(block.Header.Number, uint64(txIndex+1))
			addWriteSetToBatch(txRWSet, committingTxHeight, updates)
		} else {
			// Unset bit in byte array corresponded to the invalid transaction
			txsFilter.Set(uint(txIndex))
		}
	}
	block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsFilter.ToBytes()
	return updates, nil
}
Example #2
0
// ValidateAndPrepare implements method in interface `txmgmt.TxMgr`
func (txmgr *CouchDBTxMgr) ValidateAndPrepare(block *common.Block, doMVCCValidation bool) error {
	if doMVCCValidation == true {
		logger.Debugf("===COUCHDB=== Entering CouchDBTxMgr.ValidateAndPrepare()")
		logger.Debugf("Validating a block with [%d] transactions", len(block.Data.Data))
	} else {
		logger.Debugf("New block arrived for write set computation:%#v", block)
		logger.Debugf("Computing write set for a block with [%d] transactions", len(block.Data.Data))
	}
	var valid bool
	txmgr.updateSet = newUpdateSet()
	txmgr.blockNum = block.Header.Number
	txsFilter := util.NewFilterBitArrayFromBytes(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])

	for txIndex, envBytes := range block.Data.Data {
		if txsFilter.IsSet(uint(txIndex)) {
			// Skiping invalid transaction
			logger.Debug("Skipping transaction marked as invalid, txIndex=", txIndex)
			continue
		}

		// extract actions from the envelope message
		respPayload, err := putils.GetActionFromEnvelope(envBytes)
		if err != nil {
			return err
		}

		//preparation for extracting RWSet from transaction
		txRWSet := &rwset.TxReadWriteSet{}

		// Get the Result from the Action
		// and then Unmarshal it into a TxReadWriteSet using custom unmarshalling
		if err = txRWSet.Unmarshal(respPayload.Results); err != nil {
			return err
		}

		// trace the first 2000 characters of RWSet only, in case it is huge
		if logger.IsEnabledFor(logging.DEBUG) {
			txRWSetString := txRWSet.String()
			operation := "validating"
			if doMVCCValidation == false {
				operation = "computing write set from"
			}
			if len(txRWSetString) < 2000 {
				logger.Debugf(operation+" txRWSet:[%s]", txRWSetString)
			} else {
				logger.Debugf(operation+" txRWSet:[%s...]", txRWSetString[0:2000])
			}
		}
		if doMVCCValidation == true {
			if valid, err = txmgr.validateTx(txRWSet); err != nil {
				return err
			}
		} else {
			valid = true
		}

		if valid {
			if err := txmgr.addWriteSetToBatch(txRWSet, version.NewHeight(block.Header.Number, uint64(txIndex+1))); err != nil {
				return err
			}
		} else {
			// Unset bit in byte array corresponded to the invalid transaction
			txsFilter.Set(uint(txIndex))
		}
	}

	block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsFilter.ToBytes()
	logger.Debugf("===COUCHDB=== Exiting CouchDBTxMgr.ValidateAndPrepare()")
	return nil
}
Example #3
0
// Commit implements method in interface `histmgmt.HistMgr`
// This writes to a separate history database.
// TODO dpending on how invalid transactions are handled may need to filter what history commits.
func (histmgr *CouchDBHistMgr) Commit(block *common.Block) error {
	logger.Debugf("===HISTORYDB=== Entering CouchDBHistMgr.Commit()")

	//Get the blocknumber off of the header
	blockNo := block.Header.Number
	//Set the starting tranNo to 0
	var tranNo uint64

	logger.Debugf("===HISTORYDB=== Updating history for blockNo: %v with [%d] transactions",
		blockNo, len(block.Data.Data))
	for _, envBytes := range block.Data.Data {
		tranNo++
		logger.Debugf("===HISTORYDB=== Updating history for tranNo: %v", tranNo)

		// extract actions from the envelope message
		respPayload, err := putils.GetActionFromEnvelope(envBytes)
		if err != nil {
			return err
		}

		//preparation for extracting RWSet from transaction
		txRWSet := &rwset.TxReadWriteSet{}

		// Get the Result from the Action and then Unmarshal
		// it into a TxReadWriteSet using custom unmarshalling
		if err = txRWSet.Unmarshal(respPayload.Results); err != nil {
			return err
		}

		//Transactions that have data that is not JSON such as binary data,
		// the write value will not write to history database.
		//These types of transactions will have the key written to the history
		// database to support history key scans.  We do not write the binary
		// value to CouchDB since the purpose of the history database value is
		// for query andbinary data can not be queried.
		for _, nsRWSet := range txRWSet.NsRWs {
			ns := nsRWSet.NameSpace

			for _, kvWrite := range nsRWSet.Writes {
				writeKey := kvWrite.Key
				writeValue := kvWrite.Value
				compositeKey := constructCompositeKey(ns, writeKey, blockNo, tranNo)
				var bytesDoc []byte

				logger.Debugf("===HISTORYDB=== ns (namespace or cc id) = %v, writeKey: %v, compositeKey: %v, writeValue = %v",
					ns, writeKey, compositeKey, writeValue)

				if couchdb.IsJSON(string(writeValue)) {
					//logger.Debugf("===HISTORYDB=== yes JSON store writeValue = %v", string(writeValue))
					bytesDoc = writeValue
				} else {
					//For data that is not in JSON format only store the key
					//logger.Debugf("===HISTORYDB=== not JSON only store key")
					bytesDoc = []byte(`{}`)
				}

				// SaveDoc using couchdb client and use JSON format
				rev, err := histmgr.couchDB.SaveDoc(compositeKey, "", bytesDoc, nil)
				if err != nil {
					logger.Errorf("===HISTORYDB=== Error during Commit(): %s\n", err.Error())
					return err
				}
				if rev != "" {
					logger.Debugf("===HISTORYDB=== Saved document revision number: %s\n", rev)
				}

			}
		}

	}
	return nil
}