Skip to content

Commit

Permalink
[FAB-1038] Rework commiter to be more general
Browse files Browse the repository at this point in the history
As part of the work needed for FAB-1038 to complete, this
commit abstracts out transaction validation entity and
extacts related code out of noopssinglechain/client.go.

Change-Id: I6949c99abab6f58472dd8e95929201e2a472f13d
Signed-off-by: Artem Barger <bartem@il.ibm.com>
  • Loading branch information
C0rWin committed Dec 29, 2016
1 parent 58cde93 commit 4d77a8c
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 111 deletions.
120 changes: 14 additions & 106 deletions core/committer/noopssinglechain/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,22 @@ limitations under the License.
package noopssinglechain

import (
"fmt"
"math"
"time"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/core/chaincode"
"github.com/hyperledger/fabric/core/committer"
"github.com/hyperledger/fabric/core/util"
"github.com/hyperledger/fabric/core/committer/txvalidator"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/events/producer"
gossip_proto "github.com/hyperledger/fabric/gossip/proto"
"github.com/hyperledger/fabric/gossip/service"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/orderer"
putils "github.com/hyperledger/fabric/protos/utils"
"github.com/op/go-logging"
"github.com/spf13/viper"
"golang.org/x/net/context"
"google.golang.org/grpc"

"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/peer"
gossip_proto "github.com/hyperledger/fabric/gossip/proto"
"github.com/hyperledger/fabric/gossip/service"
pb "github.com/hyperledger/fabric/protos/peer"
)

var logger *logging.Logger // package-level logger
Expand Down Expand Up @@ -178,56 +172,6 @@ func (d *DeliverService) seekLatestFromCommitter(height uint64) error {
})
}

func isTxValidForVscc(payload *common.Payload, envBytes []byte) error {
// TODO: Extract the VSCC/policy from LCCC as soon as this is ready
vscc := "vscc"

chainName := payload.Header.ChainHeader.ChainID
if chainName == "" {
err := fmt.Errorf("transaction header does not contain an chain ID")
logger.Errorf("%s", err)
return err
}

txid := "N/A" // FIXME: is that appropriate?

// build arguments for VSCC invocation
// args[0] - function name (not used now)
// args[1] - serialized Envelope
args := [][]byte{[]byte(""), envBytes}

// create VSCC invocation proposal
vsccCis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Name: vscc}, CtorMsg: &pb.ChaincodeInput{Args: args}}}
prop, err := putils.CreateProposalFromCIS(txid, chainName, vsccCis, []byte(""))
if err != nil {
logger.Errorf("Cannot create a proposal to invoke VSCC, err %s\n", err)
return err
}

// get context for the chaincode execution
var txsim ledger.TxSimulator
lgr := peer.GetLedger(chainName)
txsim, err = lgr.NewTxSimulator()
if err != nil {
logger.Errorf("Cannot obtain tx simulator, err %s\n", err)
return err
}
defer txsim.Done()
ctxt := context.WithValue(context.Background(), chaincode.TXSimulatorKey, txsim)

version := util.GetSysCCVersion()
cccid := chaincode.NewCCContext(chainName, vscc, version, txid, true, prop)

// invoke VSCC
_, _, err = chaincode.ExecuteChaincode(ctxt, cccid, args)
if err != nil {
logger.Errorf("VSCC check failed for transaction, error %s", err)
return err
}

return nil
}

func (d *DeliverService) readUntilClose() {
for {
msg, err := d.client.Recv()
Expand All @@ -244,63 +188,26 @@ func (d *DeliverService) readUntilClose() {
logger.Warning("Got error ", t)
case *orderer.DeliverResponse_Block:
seqNum := t.Block.Header.Number
block := &common.Block{}
block.Header = t.Block.Header

// Copy and initialize peer metadata
putils.CopyBlockMetadata(t.Block, block)
block.Data = &common.BlockData{}
for _, d := range t.Block.Data.Data {
if d != nil {
if env, err := putils.GetEnvelopeFromBlock(d); err != nil {
fmt.Printf("Error getting tx from block(%s)\n", err)
} else if env != nil {
// validate the transaction: here we check that the transaction
// is properly formed, properly signed and that the security
// chain binding proposal to endorsements to tx holds. We do
// NOT check the validity of endorsements, though. That's a
// job for VSCC below
payload, _, err := peer.ValidateTransaction(env)
if err != nil {
// TODO: this code needs to receive a bit more attention and discussion:
// it's not clear what it means if a transaction which causes a failure
// in validation is just dropped on the floor
logger.Errorf("Invalid transaction, error %s", err)
} else {
//the payload is used to get headers
err = isTxValidForVscc(payload, d)
if err != nil {
// TODO: this code needs to receive a bit more attention and discussion:
// it's not clear what it means if a transaction which causes a failure
// in validation is just dropped on the floor
logger.Errorf("isTxValidForVscc returned error %s", err)
continue
}

if t, err := proto.Marshal(env); err == nil {
block.Data.Data = append(block.Data.Data, t)
} else {
fmt.Printf("Cannot marshal transactoins %s\n", err)
}
}
} else {
logger.Warning("Nil tx from block")
}
}
}

// Create new transactions validator
validator := txvalidator.NewTxValidator(peer.GetLedger(d.chainID))
// Validate and mark invalid transactions
validator.Validate(t.Block)

numberOfPeers := len(service.GetGossipService().GetPeers())
// Create payload with a block received
payload := createPayload(seqNum, block)
payload := createPayload(seqNum, t.Block)
// Use payload to create gossip message
gossipMsg := createGossipMsg(payload)

logger.Debugf("Adding payload locally, buffer seqNum = [%d], peers number [%d]", seqNum, numberOfPeers)
// Add payload to local state payloads buffer
service.GetGossipService().AddPayload(d.chainID, payload)

// Gossip messages with other nodes
logger.Debugf("Gossiping block [%d], peers number [%d]", seqNum, numberOfPeers)
service.GetGossipService().Gossip(gossipMsg)
if err = producer.SendProducerBlockEvent(block); err != nil {
if err = producer.SendProducerBlockEvent(t.Block); err != nil {
logger.Errorf("Error sending block event %s", err)
}

Expand All @@ -319,6 +226,7 @@ func createGossipMsg(payload *gossip_proto.Payload) *gossip_proto.GossipMessage
Payload: payload,
},
},
Tag: gossip_proto.GossipMessage_EMPTY,
}
return gossipMsg
}
Expand Down
67 changes: 67 additions & 0 deletions core/committer/txvalidator/txvalidator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright IBM Corp. 2016 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 txvalidator

import (
"os"
"testing"

"github.com/hyperledger/fabric/core/ledger/kvledger"
"github.com/hyperledger/fabric/core/ledger/testutil"
"github.com/hyperledger/fabric/core/ledger/util"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
"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) {
conf := kvledger.NewConf("/tmp/tests/ledger/", 0)
defer os.RemoveAll("/tmp/tests/ledger/")

ledger, _ := kvledger.NewKVLedger(conf)
defer ledger.Close()

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

bcInfo, _ := ledger.GetBlockchainInfo()
testutil.AssertEquals(t, bcInfo, &pb.BlockchainInfo{
Height: 0, CurrentBlockHash: nil, PreviousBlockHash: nil})

simulator, _ := ledger.NewTxSimulator()
simulator.SetState("ns1", "key1", []byte("value1"))
simulator.SetState("ns1", "key2", []byte("value2"))
simulator.SetState("ns1", "key3", []byte("value3"))
simulator.Done()

simRes, _ := simulator.GetTxSimulationResults()
block := testutil.ConstructBlock(t, [][]byte{simRes}, true)

validator.Validate(block)

txsfltr := util.NewFilterBitArrayFromBytes(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])

assert.True(t, !txsfltr.IsSet(uint(0)))
assert.True(t, !txsfltr.IsSet(uint(1)))
assert.True(t, !txsfltr.IsSet(uint(2)))
}

0 comments on commit 4d77a8c

Please sign in to comment.