This repository has been archived by the owner on Apr 9, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 10
/
interfaces.go
255 lines (227 loc) · 12.9 KB
/
interfaces.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package tx
import (
"fmt"
"io"
"github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/pkg/statedata"
)
// ProcessorCreator creates a new instance of a processor of a particular transaction type.
// In addition, this function also returns zero or more simulated readwrite sets that may
// be present in the transaction if the transaction type supports enclosing of these.
// There is expected to be one to one mapping between a supported transaction type and the
// corresponding implementation of this interface. The transaction envelope passed to the
// function `NewProcessor` is guaranteed to be of the associated transaction type.
// If the ProcessCreator finds the transaction envelop to be invalid the err returned by
// this function should be of type `InvalidErr`
type ProcessorCreator interface {
NewProcessor(txenv *Envelope) (processor Processor, simulatedRWSet [][]byte, err error)
}
// Processor contains logic for processing a transaction on the committing peer.
// One instance of a Processor is created for each transaction via `NewProcessor` function on the
// corresponding `ProcessorCreator`.
//
// On a Processor, the first a `Preprocess` function is invoked and then the `Process` function is invoked.
// The `Preprocess` function is invoked exactly once however, this may be invoked in parallel on different instances of Processors.
// The function `Process` is invoked one by one on the `Processors` in the order in which the associated transactions
// appear in the block. The `State` passed to the `Process` function represents the world state for the channel as of preceding valid
// transaction in the block. For the purpose of efficiency (e.g., speculative execution), this function can be invoked more
// than once and hence this function should not preserve anything in the form of instance variables.
// Eventually, the function `Done` is invoked when the `Processor` has been used so as to indicate that the `Processor` can
// release any resources held. This invocation could be because of a successful invocation to the `Process` function or
// because the associated transaction is found to be invalid at any stage during the process
// (e.g., concurrency conflict with other transactions). If the `Processor` finds the transaction to be invalid at any stage,
// the error returned by the functions `PreProcess` and `Process` should be of type `InvalidErr`
//
// The intent is to support different transaction types via interface Processor such as pure endorser transactions,
// pure post-order transactions, and a mixed transaction - e.g., a transaction that combines an endorser transaction and
// and a post-order transaction (say, a token transaction).
//
// Below is the detail description of the semantics of the function `Process`
// In order to process a transaction on a committing peer, we first evaluate the simulated readwrite set of the transaction
//(returned by the function `NewProcessor` on the corresponding `ProcessorCreator`).
// If the simulated part is found to have a concurrency conflict with one or more preceding valid transactions
// (either a preceding transaction in the same block or in a preceding block), we mark the transaction invalid.
// However, if simulated part of the transaction is found to be conflict free,
// this is assumed that the transaction has logically started executing during commit time and has produced the
// writes present in the simulated part of the transaction. In this case, the transaction processing is
// continued from this point on and the `Process` function gives a chance to the `Processor` to complete the
// transaction processing. Via the `Process` function, the transaction processor can perform reads/writes to the
// state passed to this function.
//
// Following is an illustration how the Processor is potentially expected be implemented for the transaction type "ENDORSER_TRANSACTION".
// 1) Preprocess function - Verifies the signatures and keeps the identities in internal state
// 2) Process function - Reads and evaluates endorsement policies that are applicable to the transactions writes.
// The parameter "proposedWrites" to the Process function, contains the data items are intended writes by the processing of
// the simulatedRWSet. The endorser transaction processor can load the applicable endorsement policies (such as chaincode or key-based)
// and returns an error of type InvalidErr if the endorsement policy is not satisfied.
//
type Processor interface {
Preprocess(latestChannelConfig *ChannelConfig) error
Process(state *State, proposedWrites *statedata.ProposedWrites) error
Done()
}
// InvalidErr is intended to be used by a ProcessorCreator or a Processor to indicate that the transaction is found to be invalid
type InvalidErr struct {
ActualErr error
ValidationCode peer.TxValidationCode
}
func (e *InvalidErr) msgWithoutStack() string {
return fmt.Sprintf("ValidationCode = %s, ActualErr = %s", e.ValidationCode.String(), e.ActualErr)
}
func (e *InvalidErr) msgWithStack() string {
return fmt.Sprintf("ValidationCode = %s, ActualErr = %+v", e.ValidationCode.String(), e.ActualErr)
}
func (e *InvalidErr) Error() string {
return e.msgWithoutStack()
}
// Format implements interface fmt.Formatter
func (e *InvalidErr) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
io.WriteString(s, e.msgWithStack())
return
}
fallthrough
case 's':
io.WriteString(s, e.msgWithoutStack())
case 'q':
fmt.Fprintf(s, "%q", e.msgWithoutStack())
}
}
// ReadHinter is an optional interface that a `Processor` implementation is encouraged to
// implement if the `Processor` can give any hints about what data items it would potentially read
// during its processing. This helps in pre-fetching/bulkloading the data in order to boost the performance
// For instance, the `Processor` implementation for the endorser transactions is expected to give hint
// about the endorsement policies based on the chaincode/collection/keys present in the "potentialWrites" parameter
// (which in turn are derived from the simulated readwrite set present in the transaction envelope)
// If a `Processor` implements this interface, the function `ReadHint` also gets the same treatment as the function `PreProcess`
// i.e., this is invoked before invoking function `Process` and exactly once and may be invoked in parallel on different
// instances of Processors. Note that the Preprocess and ReadHint functions on a Processor can get invoked in parallel
type ReadHinter interface {
ReadHint(potentialWrites *statedata.WriteHint) *statedata.ReadHint
}
// Reprocessor is an optional interface that a `Processor` is encouraged to implement if a
// a significant large number of transactions of the corresponding type are expected to be present and
// validation of the transaction is significantly resource consuming (e.g., signature matching/crypto operations)
// as compare to manipulating the state.
// The main context in which the function in this interface is to be invoked is to rebuild the ledger constructs such as
// statedb and historydb from the blocks that has already been processed in the past.
// For instance, if the statedb is dropped and it is to be rebuilt, fabric will only use function in this interface (if implemented)
// instead of the function in the Processor interface.
// The function in this interface can safely assume that only transaction that are processed using this
// were found to be valid earlier (when Processor interface was used for processing the transaction for the first time)
// and hence, this function can skip any validation step and can just manipulate the state. However, if there is
// no significant difference in the resource consumption, there is no value in implementing this interface and
// the regular functions in the Processor interface would be invoked
type Reprocessor interface {
Reprocess(state *State, latestChannelConfig *ChannelConfig, proposedWrites *statedata.ProposedWrites)
}
// ReprocessReadHinter is an optional interface that a `Processor` may choose to implement if it implements Reprocessor.
// This is similar to as a processor may implement the ReadHinter interface albiet this gets invoked only if Reprocessor
// is used for processing the transaction
type ReprocessReadHinter interface {
ReprocessReadHint(potentialWrites *statedata.WriteHint) *statedata.ReadHint
}
// PvtdataSourceHinter is an optional interface that a `Processor` implements to return the peers
// (identity bytes, e.g., certs) that could be the potential source for the private data associated
// with the transaction
type PvtdataSourceHinter interface {
PvtdataSource() [][]byte
}
// ChannelConfig gives handle to the channel config
type ChannelConfig struct {
}
// State exposes functions that helps a `Processor` in retrieving the latest state
// The `State` passed to the `Process` function represents the world state for the channel as of
// commit of the preceding valid transaction in the block
type State struct {
BackingState state
}
// GetState returns value associated with a tuple <namespace, key>
func (s *State) GetState(ns, key string) ([]byte, error) {
return s.BackingState.GetState(ns, key)
}
// GetStateMetadata returns a map containing the metadata associated with a tuple <namespace, key>
func (s *State) GetStateMetadata(ns, key string) (map[string][]byte, error) {
return s.BackingState.GetStateMetadata(ns, key)
}
// GetStateRangeScanIterator returns an iterator that can be used to iterate over all the keys
// present in the range startKey (inclusive) and endKey (exclusive) for the a namespace.
func (s *State) GetStateRangeScanIterator(ns, startKey, endKey string) (*KeyValueItr, error) {
itr, err := s.BackingState.GetStateRangeScanIterator(ns, startKey, endKey)
if err != nil {
return nil, err
}
return &KeyValueItr{
BackingItr: itr,
}, nil
}
// SetState sets the value associated with a tuple <namespace, key>
// A nil value implies the delete of the key
func (s *State) SetState(ns, key string, value []byte) error {
return s.BackingState.SetState(ns, key, value)
}
// SetStateMetadata sets the metadata associated with a tuple <namespace, key> for an existing key
// This function is a noop for a non existing key. A nil metadata implied the delete of the metadata
func (s *State) SetStateMetadata(ns, key string, metadata map[string][]byte) error {
return s.BackingState.SetStateMetadata(ns, key, metadata)
}
// GetPrivateDataMetadataByHash returns the metadata associated with a tuple <namespace, collection, keyhash>
func (s *State) GetPrivateDataMetadataByHash(ns, coll string, keyHash []byte) (map[string][]byte, error) {
return s.BackingState.GetPrivateDataMetadataByHash(ns, coll, keyHash)
}
// KeyValueItr helps iterates over the results of a range scan query
type KeyValueItr struct {
BackingItr keyValueItr
}
// Next returns the next result. A nil in the return value implies that no more results are available
func (i *KeyValueItr) Next() (*statedata.KeyValue, error) {
return i.BackingItr.Next()
}
// Close closes the iterator
func (i *KeyValueItr) Close() {
i.BackingItr.Close()
}
// Envelope contains data of the common.Envelope; some byte fields are already
// unmarshalled to structs and we preserve the unmarshalled version so as to not
// duplicate the unmarshalling work. Still, given the non-deterministic nature of
// protobufs, we preserve their original byte representation so that the tx processor
// may for instance verify signatures or perform bitwise operations on their original
// representation.
type Envelope struct {
// SignedBytes contains the marshalled common.Payload in the envelope
SignedBytes []byte
// Signature contains the creator's signature over the SignedBytes
Signature []byte
// Data contains the opaque Data bytes in the common.Payload
Data []byte
// ChannelHeaderBytes contains the marshalled ChannelHeader of the common.Header
ChannelHeaderBytes []byte
// ChannelHeaderBytes contains the marshalled SignatureHeader of the common.Header
SignatureHeaderBytes []byte
// ChannelHeader contains the ChannelHeader of this envelope
ChannelHeader *common.ChannelHeader
// SignatureHeader contains the SignatureHeader of this envelope
SignatureHeader *common.SignatureHeader
}
//********************** Unexported types ***********************************************//
// state represents the latest state that is passed to the "Process" function of the TxProcessor
type state interface {
GetState(ns, key string) ([]byte, error)
GetStateMetadata(ns, key string) (map[string][]byte, error)
GetStateRangeScanIterator(ns, startKey, endKey string) (keyValueItr, error)
SetState(ns, key string, value []byte) error
SetStateMetadata(ns, key string, metadata map[string][]byte) error
GetPrivateDataMetadataByHash(ns, coll string, keyHash []byte) (map[string][]byte, error)
}
// keyValueItr iterates over a range of key-values
type keyValueItr interface {
Next() (*statedata.KeyValue, error)
Close()
}