Skip to content

Commit adf7475

Browse files
committed
[FAB-7224] Enhance custom tx processor
This CR enhances custom transaction processor in ledger such that the processor is able to distinguish whether it is being invoked during ledger initialization or for on-line tx processing. In the former situation, the processor needs to just rely on the states stored previously in the ledger and in the latter it can assume that the peer is up and running and hence other peer structures can be used. Other than this, in the initialization situation only valid transactions are passed to the processor which makes the custom validation in the processor optional. Change-Id: Idff54ab124dd588dfa5172a5b882e521217ca62a Signed-off-by: manish <manish.sethi@gmail.com>
1 parent 3974f15 commit adf7475

File tree

15 files changed

+159
-66
lines changed

15 files changed

+159
-66
lines changed

core/aclmgmt/aclmgmt.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ var aclMgmtCfgTxProcessor = &AclMgmtConfigTxProcessor{}
7575

7676
//GenerateSimulationResults this is just a proxy to delegate registered aclProvider.
7777
//Need this as aclmgmt is initialized with ledger initialization as required by ledger
78-
func (*AclMgmtConfigTxProcessor) GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator) error {
78+
func (*AclMgmtConfigTxProcessor) GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator, initializingLedger bool) error {
7979
configtxLock.RLock()
8080
defer configtxLock.RUnlock()
8181

8282
//this should not be nil (aclProvider is initialized at the outset to either
8383
//rscc or default)
8484
if aclProvider != nil {
85-
return aclProvider.GenerateSimulationResults(txEnvelop, simulator)
85+
return aclProvider.GenerateSimulationResults(txEnvelop, simulator, initializingLedger)
8686
}
8787

8888
return fmt.Errorf("warning! call to handle config tx before setting ACL provider")

core/aclmgmt/aclmgmt_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ func registerACLProvider() *mocks.MockACLProvider {
3535

3636
func TestACLProcessor(t *testing.T) {
3737
reinit()
38-
assert.NotNil(t, GetConfigTxProcessor().GenerateSimulationResults(nil, nil), "Expected non-nil error")
38+
assert.NotNil(t, GetConfigTxProcessor().GenerateSimulationResults(nil, nil, false), "Expected non-nil error")
3939
RegisterACLProvider(nil)
40-
assert.Nil(t, GetConfigTxProcessor().GenerateSimulationResults(nil, nil), "Expected nil error")
40+
assert.Nil(t, GetConfigTxProcessor().GenerateSimulationResults(nil, nil, false), "Expected nil error")
4141
}
4242

4343
func TestPanicOnUnregistered(t *testing.T) {

core/aclmgmt/aclmgmtimpl.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ func newACLMgmt(r ACLProvider) ACLProvider {
5151
return &aclMgmtImpl{aclOverrides: make(map[string]aclMethod)}
5252
}
5353

54-
func (am *aclMgmtImpl) GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator) error {
54+
func (am *aclMgmtImpl) GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator, initializingLedger bool) error {
5555
if rscc == nil {
5656
panic("-----RegisterACLProvider not called ----")
5757
}
5858

59-
return rscc.GenerateSimulationResults(txEnvelop, simulator)
59+
return rscc.GenerateSimulationResults(txEnvelop, simulator, initializingLedger)
6060
}

core/aclmgmt/defaultaclprovider.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,6 @@ func (d *defaultACLProvider) CheckACL(resName string, channelID string, idinfo i
124124

125125
//GenerateSimulationResults does nothing for default provider currently as it defaults to
126126
//1.0 behavior
127-
func (d *defaultACLProvider) GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator) error {
127+
func (d *defaultACLProvider) GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator, initializingLedger bool) error {
128128
return nil
129129
}

core/aclmgmt/mocks/mocks.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func (m *MockACLProvider) CheckACL(resName string, channelID string, idinfo inte
3030
return args.Error(0)
3131
}
3232

33-
func (m *MockACLProvider) GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator) error {
33+
func (m *MockACLProvider) GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator, initializingLedger bool) error {
3434
return nil
3535
}
3636

core/ledger/customtx/custom_tx_processor.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ package customtx
99
import (
1010
"sync"
1111

12-
"github.com/hyperledger/fabric/core/ledger"
1312
"github.com/hyperledger/fabric/protos/common"
1413
)
1514

@@ -19,15 +18,6 @@ var once sync.Once
1918
// Processors maintains the association between a custom transaction type to its corresponding tx processor
2019
type Processors map[common.HeaderType]Processor
2120

22-
// Processor allows to generate simulation results during commit time for custom transactions.
23-
// A custom processor may represent the information in a propriety fashion and can use this process to translate
24-
// the information into the form of `TxSimulationResults`. Because, the original information is signed in a
25-
// custom representation, an implementation of a `Processor` should be cautious that the custom representation
26-
// is used for simulation in an deterministic fashion and should take care of compatibility cross fabric versions.
27-
type Processor interface {
28-
GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator) error
29-
}
30-
3121
// Initialize sets the custom processors. This function is expected to be invoked only during ledgermgmt.Initialize() function.
3222
func Initialize(customTxProcessors Processors) {
3323
once.Do(func() {

core/ledger/customtx/interface.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package customtx
8+
9+
import (
10+
"github.com/hyperledger/fabric/core/ledger"
11+
"github.com/hyperledger/fabric/protos/common"
12+
)
13+
14+
// InvalidTxError is expected to be thrown by a custom transaction processor (an implementation of interface `Processor`)
15+
// if it wants the ledger to record a particular transaction as invalid
16+
type InvalidTxError struct {
17+
Msg string
18+
}
19+
20+
func (e *InvalidTxError) Error() string {
21+
return e.Msg
22+
}
23+
24+
// Processor allows to generate simulation results during commit time for custom transactions.
25+
// A custom processor may represent the information in a propriety fashion and can use this process to translate
26+
// the information into the form of `TxSimulationResults`. Because, the original information is signed in a
27+
// custom representation, an implementation of a `Processor` should be cautious that the custom representation
28+
// is used for simulation in an deterministic fashion and should take care of compatibility cross fabric versions.
29+
// 'initializingLedger' true indicates that either the transaction being processed is from the genesis block or the ledger is
30+
// synching the state (which could happen during peer startup if the statedb is found to be lagging behind the blockchain).
31+
// In the former case, the transactions processed are expected to be valid and in the latter case, only valid transactions
32+
// are reprocessed and hence any validation can be skipped.
33+
type Processor interface {
34+
GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator, initializingLedger bool) error
35+
}

core/ledger/customtx/test_export.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package customtx
8+
9+
// InitializeTestEnv initializes custom tx processors for test
10+
func InitializeTestEnv(processors Processors) {
11+
initialize(processors)
12+
}

core/ledger/kvledger/custom_processor_test.go

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,35 @@ package kvledger
99
import (
1010
"testing"
1111

12+
"github.com/golang/protobuf/proto"
13+
"github.com/hyperledger/fabric/protos/ledger/rwset/kvrwset"
14+
"github.com/hyperledger/fabric/protos/peer"
15+
1216
"github.com/hyperledger/fabric/common/ledger/testutil"
17+
lgrutil "github.com/hyperledger/fabric/core/ledger/util"
18+
1319
"github.com/hyperledger/fabric/core/ledger"
1420
"github.com/hyperledger/fabric/core/ledger/customtx"
1521
"github.com/hyperledger/fabric/protos/common"
22+
"github.com/hyperledger/fabric/protos/utils"
1623
"github.com/stretchr/testify/assert"
1724
)
1825

1926
type customTxProcessor struct {
20-
namespace string
21-
initData map[string][]byte
2227
}
2328

24-
func (ctp *customTxProcessor) GenerateSimulationResults(
25-
txEnvelop *common.Envelope, simulator ledger.TxSimulator) error {
26-
for k, v := range ctp.initData {
27-
simulator.SetState(ctp.namespace, k, []byte(v))
29+
func (ctp *customTxProcessor) GenerateSimulationResults(txEnvelop *common.Envelope, simulator ledger.TxSimulator, initializingLedger bool) error {
30+
payload := utils.UnmarshalPayloadOrPanic(txEnvelop.Payload)
31+
chHdr, _ := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
32+
chainid := chHdr.ChannelId
33+
kvw := &kvrwset.KVWrite{}
34+
if err := proto.Unmarshal(payload.Data, kvw); err != nil {
35+
return err
2836
}
29-
return nil
37+
if len(kvw.Key) == 0 {
38+
return &customtx.InvalidTxError{Msg: "Nil key"}
39+
}
40+
return simulator.SetState(chainid, kvw.Key, kvw.Value)
3041
}
3142

3243
func TestCustomProcessor(t *testing.T) {
@@ -35,27 +46,59 @@ func TestCustomProcessor(t *testing.T) {
3546
provider, _ := NewProvider()
3647
defer provider.Close()
3748

38-
// create a custom tx processor and register it to handle 'common.HeaderType_CONFIG' type of transaction
39-
testNamespace := "TestNamespace"
40-
testKVs := map[string][]byte{"one": []byte("1"), "two": []byte("2")}
41-
customtx.Initialize(customtx.Processors{
42-
common.HeaderType_CONFIG: &customTxProcessor{
43-
testNamespace,
44-
testKVs,
45-
}})
49+
// create a custom tx processor and register it to handle '100 and 101' type of transaction
50+
chainid := "testLedger"
51+
customTxProcessor := &customTxProcessor{}
52+
customtx.InitializeTestEnv(customtx.Processors{
53+
100: customTxProcessor,
54+
101: customTxProcessor})
4655

4756
// Create a genesis block with a common.HeaderType_CONFIG transaction
48-
_, gb := testutil.NewBlockGenerator(t, "testLedger", false)
49-
ledger, err := provider.Create(gb)
50-
defer ledger.Close()
57+
_, gb := testutil.NewBlockGenerator(t, chainid, false)
58+
lgr, err := provider.Create(gb)
59+
defer lgr.Close()
5160
assert.NoError(t, err)
5261

62+
// commit a block with three custom trans
63+
tx1 := createCustomTx(t, 100, chainid, "custom_key1", "value1")
64+
tx2 := createCustomTx(t, 101, chainid, "custom_key2", "value2")
65+
tx3 := createCustomTx(t, 101, chainid, "", "")
66+
blk1 := testutil.NewBlock([]*common.Envelope{tx1, tx2, tx3}, 1, gb.Header.Hash())
67+
assert.NoError(t, lgr.CommitWithPvtData(&ledger.BlockAndPvtData{Block: blk1}))
5368
// verify that the state changes caused by the custom processor took place during ledger creation
54-
txSim, err := ledger.NewTxSimulator("testTxid")
69+
qe, err := lgr.NewQueryExecutor()
5570
assert.NoError(t, err)
56-
for k, v := range testKVs {
57-
value, err := txSim.GetState(testNamespace, k)
58-
assert.NoError(t, err)
59-
assert.Equal(t, v, value)
60-
}
71+
val, err := qe.GetState(chainid, "custom_key1")
72+
assert.NoError(t, err)
73+
assert.Equal(t, "value1", string(val))
74+
75+
val, err = qe.GetState(chainid, "custom_key2")
76+
assert.NoError(t, err)
77+
assert.Equal(t, "value2", string(val))
78+
qe.Done()
79+
80+
blockPersisted, err := lgr.GetBlockByNumber(1)
81+
assert.NoError(t, err)
82+
var txFilter lgrutil.TxValidationFlags
83+
txFilter = blockPersisted.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]
84+
assert.Equal(t, peer.TxValidationCode_VALID, txFilter.Flag(0))
85+
assert.Equal(t, peer.TxValidationCode_VALID, txFilter.Flag(1))
86+
assert.Equal(t, peer.TxValidationCode_INVALID_OTHER_REASON, txFilter.Flag(2))
87+
88+
tx4 := createCustomTx(t, 100, chainid, "custom_key4", "value4")
89+
blk2 := testutil.NewBlock([]*common.Envelope{tx4}, 2, blk1.Header.Hash())
90+
assert.NoError(t, lgr.CommitWithPvtData(&ledger.BlockAndPvtData{Block: blk2}))
91+
qe, err = lgr.NewQueryExecutor()
92+
assert.NoError(t, err)
93+
val, err = qe.GetState(chainid, "custom_key4")
94+
qe.Done()
95+
assert.NoError(t, err)
96+
assert.Equal(t, "value4", string(val))
97+
}
98+
99+
func createCustomTx(t *testing.T, txType common.HeaderType, chainid, key, val string) *common.Envelope {
100+
kvWrite := &kvrwset.KVWrite{Key: key, Value: []byte(val)}
101+
txEnv, err := utils.CreateSignedEnvelope(txType, chainid, nil, kvWrite, 0, 0)
102+
assert.NoError(t, err)
103+
return txEnv
61104
}

core/ledger/kvledger/txmgmt/validator/valimpl/default_impl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (impl *DefaultImpl) ValidateAndPrepareBatch(blockAndPvtdata *ledger.BlockAn
4444
var err error
4545

4646
logger.Debug("preprocessing ProtoBlock...")
47-
if internalBlock, err = preprocessProtoBlock(impl.txmgr, block); err != nil {
47+
if internalBlock, err = preprocessProtoBlock(impl.txmgr, block, doMVCCValidation); err != nil {
4848
return nil, err
4949
}
5050

0 commit comments

Comments
 (0)