-
Notifications
You must be signed in to change notification settings - Fork 14
Transaction
Transaction creation/execution done in Client SDK and includes following steps
- Creation Transaction Context during db.Begin() call
- Sending statement(s) from Client to Client SDK
- Collecting RSets and MVCC data for each txCtx.Get() call
- Calculating WSets for each txCtx.Put() call
- Composing all this data to transaction, including signature of current user
- Revalidate MVCC, to fail Tx early, see Transaction Isolation
- Passing result Tx to Sequencer
Transaction Isolation algorithm is part of Transaction creation/execution process
- MVCC version for each key is composed from ledger height (block number) and Tx number, same as Fabric
- During creation Tx context -
db.Begin()
call, ledger height (last known block number) is stored as part of context - During each
txCtx.Get()
call inside Transaction, RSet version validated again ledger height stored in context- If block number in version is bigger that stored in context, Tx marked as invalid and will fail during call to
txCtx.Commit()
- We will not fail during after
txCtx.Get()
call not to mess with client side logic - In diagram it marked as
Early Fail Tx (1)
- If block number in version is bigger that stored in context, Tx marked as invalid and will fail during call to
When a Sequencer receives a transaction envelope, it needs to check whether the signer is a member of the blockchain DB. When the envelope is signed by the valid signer, the transaction would be added to a queue.
- At the block timeout, reaching size limit or receiving configuration change (admin API call), the leader node would pick the next batch of transactions to be included in the next block. The leader can pick using one the following option provided by the user:
- FIFO_ORDER -- fair to all clients
- REORDER_FOR_CONFLICT_REDUCTION -- trading fairness for the performance
- MAXIMIZE_PARALLEL_VALIDATION -- trading fairness for the performance
For more details about transaction serialization algorithm, see Transaction Serialization
Important remark - to reduce amount of inter-block conflicts, block transactions RSets re-checked before block creation (before serialization) and block pipelining will be disabled (at least, for PoC), i.e. new block will start to form in leader only after previous block was applied to local database.
Transaction failure during this check marked as Early Fail Tx (2)
Important remark 2 - configuration change will be stored in block as well.
Once transaction serialization done, a block created in leader node, consensus would be executed across nodes. Once the majority of the nodes agree on the created block (as per the employed consensus algorithm), the block would be added to the block queue in each node.
From this point all operations executed sequentially on each network node, not only on Sequencer
- As the block store is the source of truth, we maintain it in the file system similar to what Fabric v0.6 and v2.0 do. We can copy the block store code from the Fabric v2.0.
- Although Sequencer remove transaction conflicts, at least for REORDER_FOR_CONFLICT_REDUCTION and MAXIMIZE_PARALLEL_VALIDATION, so transaction should not fail during validation of RWSets and MVCC, but in byzantine environment we can have malicious leader and we need to store transaction status (OK/FAIL) after performing transaction execution
- This is true only if block pipelining disabled, because of possibility of inter-block conflicts.
- For FIFO_ORDER, we should do it in CFT as well.
- Transaction scheduler tried to block transactions for parallel validation/execution
- For MAXIMIZE_PARALLEL_VALIDATION, transactions already arranged in blocks for parallel execution
- For REORDER_FOR_CONFLICT_REDUCTION, rw dependency graph for transaction in block is build and transaction executed based on BFS order
- For FIFO_ORDER, because rw dependency graph may contain cycles, much simpler algorithm used
- Tx in block passed one by one, checking for access to same data entries
- Once suspect to any conflict appear, already accumulated (passed) Tx are sent to Executor and new Tx accumulation is started
At PoC phase, we will store provenance data in KV Store, as rest of the blockchaindb data
Provenance data structure:
- After Tx validation/execution, Provenance data Tx created from its RWSet.
- This is done to add transaction execution status to provenance data.
- For each entry in WSet, tuple
{Key, newValue, BlockTime, BlockNumber, TxTime, TxId, ClientId, IsDelete, IsOk}
added to database- Key will be combination of
Key || TxId
- This data should be indexed by
Key || BlockTime
andKey || TxTime
, to search byKey
and time
- Key will be combination of
- In addition, we will create reverse index that maps
TxId
toBlockNumber
- To easy find block that contains specific transaction
For proof generation and example, see Transaction Proof document
Basic assumption is no conflicting transactions send to be validated/committed in parallel
- We revalidate Tx RSet correctness, by reading same values again and checking MVCC values
- For database that support WSet apply, we just apply WSet
- For database that not support WSet apply, we just execute Put statements in transaction, one by one
Important note - if block contains configuration change (admin API call), it will be executed after all transactions validation/commit.
We have multiple network nodes that commit transactions. Some ot the nodes may fail, become byzantine, so how we should handle node responses to transaction
- Each node that sends response to SDK, should sign this response.
- Response send using pub/sub primitive, explained in Client SDK APIs and Server APIs documents.
- In addition, regeneration of response possible, see same documents
- Response send using pub/sub primitive, explained in Client SDK APIs and Server APIs documents.
- SDK collect required number of responses, defined in
TxOptions
- per transaction orDBOptions
- for database and only after that pass result of transaction execution to client- For KV Store, only OK/FAIL responses are possible
We define multiple policies to handle responses
- MAJORITY - more that 2/3*N nodes responded with same result
- MIN_BFT - more that 1/3*N + 1 nodes responded with same result
- MIN - just one node responded
Transaction structure inspired by Transaction envelop structure from Fabric.
- Envelop
- Signature
- Payload
- Header
- Transaction
- Statement(s)
- Operation
- Argument(s)
- KVRWSet
- KVRead(s)
- key
- version
- KVWrite(s)
- key
- is_delete
- value
- KVRead(s)
- Statement(s)
syntax = "proto3";
package transaction;
// Envelope wraps a Payload with a signature so that the message may be authenticated
message Envelope {
// A marshaled Payload
bytes payload = 1;
// A signature by the creator specified in the Payload header
bytes signature = 2;
}
// Payload is the message contents (and header to allow for signing)
message Payload {
// Header is included to provide identity and prevent replay
Header header = 1;
// Encoded transaction
bytes transaction = 2;
}
message Header {
// Creator of the message, a marshaled msp.SerializedIdentity
bytes creator = 1;
// Arbitrary number that may only be used once. Can be used to detect replay attacks.
bytes nonce = 2;
}
message Transaction {
enum DataModel {
KV = 0;
}
DataModel datamodel = 1;
repeated Statement statements = 2;
bytes rwset = 3;
}
message KVRWSet {
repeated KVRead rset = 1;
repeated KVWrite wset = 2;
}
message KVRead {
string key = 1;
bytes version = 2;
}
message KVWrite {
string key = 1;
bool is_delete = 2;
bytes value = 3;
}
message Statement{
string operation = 1;
repeated bytes arguments = 2;
}