本文讨论了有关读写集语义的当前实现的细节。
在一个背书节点模拟transaction期间,为交易准备了一个读写集合。read set
包含在模拟期间transaction读取的唯一键及其提交的版本的列表。write set
包含唯一键的列表(尽管可能与读集中存在的键重叠)以及transaction写入的新值。如果transaction执行的更新是删除key,那么设置key的删除标记(代替新值)。
此外,如果transaction为key多次写入值,则只保留最后写入的值。另外,如果一个transaction为一个key读取一个值,即使transaction在发出读取之前更新了key的值,也会返回提交状态中的值。换句话说,不支持Read-your-writing语义。
如前所述,键的版本仅在读集中记录; 写入集只包含唯一键的列表及其由transaction设置的最新值。
可能有各种实现版本的方案。版本控制方案的最低要求是为给定的key生成不重复的标识符。例如,对于版本使用单调增加的数字可以是一种这样的方案。在当前的实现中,我们使用基于区块链高度的版本控制方案,其中提交transaction的高度被用作所有由transaction修改的key的最新版本。在该方案中,transaction的高度由tuple表示(txNumber是块内事务的高度)。该方案比增量数量方案具有许多优点 - 主要是它可以实现其他组件,如说明,交易模拟和验证,以进行有效的设计选择。
以下是通过模拟假设交易准备的示例读写集的说明。为了简单起见,在插图中,我们使用增量数字来表示版本。
< TxReadWriteSet >
< NsReadWriteSet name = “chaincode1” >
< read - set >
< read key = “K1” , version = “1” >
< read key = “K2” , version = “1” >
read - set >
< write - set >
< write key = “K1” , value = “V1”
< write key = “K3” , value = “V2”
< write key = “K4” , isDelete = “true”
write - set >
NsReadWriteSet >
< TxReadWriteSet >,isDelete = “true” write - set > NsReadWriteSet > < TxReadWriteSet >,isDelete = “true” write - set > NsReadWriteSet > < TxReadWriteSet >
此外,如果transaction在模拟期间执行range query,则range query及其结果将被添加到读写集合中作为query-info
。
一个committer
使用读写集合的读取部分来检查transaction的有效性,用写集部分来更新相应键的版本和值。
在验证阶段,如果读集中的每个key版本和世界状态中该key的版本相同,那么就认为这个transaction是valid
- 假设所有之前的valid
transaction(包括同一块中的之前的transaction)被提交了(committed-state)。如果读写集合还包含一个或多个查询信息(query-info
),则执行另外的验证。
附加验证应确保在query-info中没有key要被插入/删除/更新。换句话说,如果我们在对提交状态进行验证时重新执行任何range queries(在模拟期间执行的transaction),则应该和模拟期间执行的结果一样。此检查确保如果transaction在提交期间观察”phantom”项目,则该transaction应标记为无效。请注意,此phantom保护仅限于range query(即, GetStateByRange链码中的功能),还没有为其他查询(即,GetQueryResult链码中的功能)实现。其他查询有phantom的风险,因此只能用于只读的transaction,除非应用程序可以保证结果集在模拟、验证/提交期间是不变的。
如果transaction通过有效性检查,则committer使用写集更新世界状态。在更新阶段,对于写集中存在的每个键,将相同键的世界状态值设置为写集中指定的值。此外,世界状态的key版本已更改,以反映最新版本。
本节通过示例场景帮助理解语义。出于本例的目的,k
表示世界状态中的键,世界状态由元组表示(k,ver,val)
,其中 ver
是最新版本,k
是键,val
是对应的值。
现在考虑五个transactionT1, T2, T3, T4, and T5
,全部模拟在世界状态的同一个快照上。以下代码段显示了模拟transaction的世界状态的快照,以及每个transaction执行的读取和写入活动的顺序。
World state: (k1,1,v1), (k2,1,v2), (k3,1,v3), (k4,1,v4), (k5,1,v5)
T1 -> Write(k1, v1'), Write(k2, v2')
T2 -> Read(k1), Write(k3, v3')
T3 -> Write(k2, v2'')
T4 -> Write(k2, v2'''), read(k2)
T5 -> Write(k6, v6'), read(k5)
现在,假设这些事务按照T1,..,T5的顺序排列(可以包含在单个块或不同的块中)
T1
通过验证,因为它不执行任何读取。此外,键k1
和k2
世界状态的元组被更新为(k1,2,v1'), (k2,2,v2')
T2
验证失败,因为它读取一个k1
,这个键由前一个transactionT1
修改。T3
通过验证,因为它不执行读取。此外,键k2
,在世界状态更新到(k2,3,v2'')
T4
验证失败,因为它读取k2
,该键由先前transactionT1
修改。T5
通过验证,因为它读取一个键k5
,该键未被任何之前的transaction修改。如果有转载请注明出处!
Read-Write set semantics