Blockchain Verifiable Query Paper Reading (1) vChain: Enabling Verifiable Boolean Range Queries over Blockchain Databases

The paper "vChain: Enabling Verifiable  Boolean Range Queries over Blockchain Databases" published on SIGMOD in July 2019  is from Hong Kong Baptist University.

1 Problems addressed by the paper

If you want to query the data in the blockchain, a feasible approach is that users can maintain the entire blockchain database and query the data locally. However, usually the amount of data stored in the blockchain is large, and downloading the complete data locally requires a large amount of storage space and network bandwidth. Another approach is to store complete data in a third-party service provider (Service Provider, SP), and query through the SP. The user sends a query request command to the SP, and waits to receive the result returned from the SP. Although this approach saves the user's local storage and network bandwidth requirements, the SP is not trustworthy, and the returned query results may be tampered or incomplete . The user's requirement is that the results received are sound and complete.

Soundness means that none of the returned objects has been tampered with, and all of them meet the query conditions;

Integrity means that no valid results are lost with respect to the query window or subscription period. In order to meet the requirements of user integrity and soundness, the paper proposes a vChain framework, which supports time window query, range query, Boolean query (keyword query), and subscription query.

The innovation of the paper: An accumulator-based verification data structure ADS is proposed, which supports dynamic aggregation of arbitrary query attributes. Two new indexes are further developed to aggregate intra-block and inter-block data records for efficient query validation. At the same time, an inverted prefix tree structure is also proposed to speed up the processing of a large number of subscription queries.

The above picture shows the blockchain network model, in which there are three types of nodes, full nodes and miners are used as full nodes to save the complete data set of the blockchain, block header and block body (data records), light nodes (users) Only the blockchain header data is saved. The light node can send query Q to the full node, and the full node returns R and VO to the user, R indicates the result of query Q, and VO (verifiable object) is constructed by SP to let the light node verify the integrity and soundness of the query result sex verification object.

2 Problem Definition:

vChain system model:

Data object representation: Multiple data objects are stored in one block, and each object is represented as <Ti, Vi, Wi>, where i represents the i-th object, Ti represents the timestamp of the object, and Vi is a multi-dimensional vector Represents one or more numeric attributes, and Wi is a set-valued attribute.

Time window query: the format of a query is q = 〈[ts,te],[α,β],ϒ〉[official], where ts represents the start time of the time segment, te represents the end time of the time segment, and [α, β] is A multidimensional range selection predicate for numeric attributes, ϒ is a monotonic Boolean function over set-valued attributes. So users can query by time window, or by using a range of numbers or by using a certain field. SP returns all objects such {oi = 〈ti,Vi,Wi〉 | ti∈[ts,te]∧Vi∈[α,β]∧ϒ(Wi) = 1}, assuming ϒ is in conjunctive normal form.

For example, q=〈[2018-05, 2018-06], [10, +∞], send:1FFYc^receive:2DAAf〉, to find transfers with an amount greater than 10 from May to June 2018 and with the address All transactions associated with "send:1FFYc" and "receive:2DAAf".

Subscription query : users can submit queries first, and in the future, if there is data that meets the query conditions in the blockchain, the query results will be returned to the corresponding subscribers. The form of the subscription query is q = 〈−,[α,β], ϒ〉, where [α,β] and ϒ are the same as the query conditions of the time window query. SP returns all objects consecutively such that { oi = 〈ti,vi,wi〉| vi ∈ [α,β]∧ϒ(wi) = 1 } until the query is logged out.
  For example, in a blockchain-based car rental system, the rental price of each rented guest is stored in Vi and a set of text keywords stored in Wi. Users can subscribe to the query q = 〈-, [200,250], "Sedan"∧("Benz"∨"BMW")〉 to receive all rental messages with prices in the range of [200,250] and include the keywords "sedan" and "Mercedes" or "BMW".

Cryptography knowledge used in the paper:

Hash encryption function, bilinear pairing (Bilinear Pairing), multiset (Multiset), encrypted multiset accumulator.

Encrypted multiset accumulators: A multiset is an overview of sets that allow elements to appear more than once. To represent them with constant size, a cryptographic multiset accumulator is a function acc( ) that maps a multiset to an element in some cyclic multiplicative group in a collision-resistant manner.
  A useful property of accumulators is that they can be used to prove that sets are disjoint . It consists of the following probabilistic polynomial time algorithm:
KeyGen(1^λ) → (sk,pk) : On input of a security parameter 1^λ, it generates a secret key sk and a public key pk.
Setup(X,pk) → acc(X) : It computes accumulative value acc(X) upon input of multiset X and public key pk.
ProveDisjoint(X1,X2,pk) → π : Input two multisets X1, X2, where X1∩X2=∅, public key pk, and output a proof π. (SP proves that the query does not match)
VerifyDisjoint(acc(X1),acc(X2),π,pk) → {0,1} : input accumulative value acc(X1), acc(X2), verify π , public key pk, output 1 if and only when X1∩X2=∅. (used for user authentication)
 

3 basic solutions

A simple scheme is to construct a traditional MHT (Merkle Hash Tree) as the ADS of each block, and apply a traditional MHT-based authentication method. But this traditional approach has 3 major flaws:

1) MHT only supports query keys for building Merkle trees. To support queries involving arbitrary sets of attributes, an exponential number of MHTs need to be constructed for each block.

2) MHT is not suitable for set-valued attributes .

3) MHTs of different blocks cannot be efficiently aggregated , making it impossible to utilize inter-block optimization techniques.

To overcome these shortcomings , a novel authentication technique based on a new accumulator-based ADS scheme is proposed , which converts numeric attributes into set-valued attributes and supports dynamic aggregation of arbitrary query attributes.

ADS Generation and Query Processing

     For the sake of simplicity, this paper only considers the Boolean query conditions of the set-valued attribute Wi. Suppose each block stores an object oi= 〈ti,Wi〉 , and then use ObjectHash to represent the original MerkleRoot.


ADS generation: In the proposed vchain framework, ADS is generated for each block during the mining process. It can be used by the SP to construct a Validation Object (VO) for each query. The original block structure is extended by adding an extra field called AttDigest, so the block header consists of PreBkHash, TS, ConsProof, ObjectHash, and AttDigest.

To be an ADS, Attdigest should have three properties:

1. AttDigest should be able to summarize an object's attributes (Wi) in some way. This can be used to prove whether the object matches the query condition. If there is a mismatch, the digest can be returned instead of the entire object.

2. Attdigest should be constant size regardless of the number of elements in Wi.

3. (Important) AttDigest should be aggregatable to support batch verification of multiple objects within a block or even across blocks. So, use a multiset accumulator as AttDigest:

It supports many functions, including showing disjointness and verifying disjointness.

Verifiable query processing: Given a boolean query condition and a data object, there are only two possible outcomes: match or no match. The correctness of the first case can be easily verified by returning the object as a result, because its integrity can be verified by the ObjectHash stored in the block header , which is available for query users on light nodes. The challenge is how to efficiently verify mismatches using AttDigest . Since CNF is a Boolean function represented by an AND list of OR operators, we can think of a Boolean function in CNF as a list of sets.

For example, a query condition "Sedan" ∧("Benz" ∨ "BMW") is equivalent to two sets {"Sedan"} and {"Benz", "BMW"}. Consider an unmatched object: {"Van", "Benz"}. It is easy to observe that there exists an equivalence set (ie, {"Sedan"}) such that its intersection with object properties is empty.

SP can apply ProveDisjoint({"Van", "Benz"}, {"Sedan"}, pk) to generate a disjoint proof π as the VO (verification object) of the non-matching object.

Correspondingly, a user can retrieve AttDigesti = acc({"Van", "Benz"}) from the block header , and use VerifyDisjoint(AttDigesti, acc({"Sedan"}), π, pk) to verify the mismatch.

Extensions to range queries

In many cases it is also possible for the user to apply a range condition on the numeric attribute Vi. To solve this problem, a method for converting numerical attributes to set-valued attributes is proposed . Range queries can then be mapped to boolean queries accordingly.

First, represent each value in binary format. Next, a numeric value is converted to a set of binary prefixed elements (denoted by the function trans). For example, the number 4 can be represented as 100 in binary. Therefore, it can be transformed into a prefix set, such as trans(4) = {1∗,10∗,100}, where ∗ represents the wildcard matching operator. Similarly, for numeric vectors, the above procedure can be applied for each dimension. For example, vector (4, 2) has binary format (100, 010). The set of prefixes it converts is { , , , , , }. Here each element has a subscript symbol (such as 1,2) which is used to distinguish the binary value of the vector in different dimensions. 1*_{1}10*_{1}100*_{1}0*_{2}01*_{2}1*_{1}010*_{2}

Next, the range query condition is converted into a monotonic Boolean function by using a binary tree built over the entire binary space. The figure above shows a tree in one-dimensional space [0, 7]. Specifically, for a one-dimensional range [α, β], first represent α and β in binary format . Next, consider α and β as two leaf nodes in the tree . Finally, find the smallest set of tree nodes that exactly covers the entire range [α, β] . A transformed boolean function is a function that joins each element in a collection using or (V) semantics.

For example, for a query range [0, 6], we can find its conversion Boolean function 0 ∗ ∨ 10 ∗ ∨ 110 (gray nodes in the figure above). The equivalence set of this Boolean function is {0∗,10∗,110}. Similarly, in the case of multidimensional ranges, the transformed Boolean function is a partial Boolean function that joins each dimension using AND (∧) semantics. For example, a range query [(0,3), (6,4)] can be converted into ( 0*_{1}10*_{1}110_{1}) ∧ ( 011_{2}100_{2}) is equivalent to ( 0*_{1}, 10*_{1}, 110_{1}) and ( 011_{2}, 100_{2}).

Through the above transformation, the query about whether the value Vi is in the range [α, β] becomes a Boolean query on the prefix set [α, β].

For example, query 4 ∈ [0,6], because {1∗,10∗,100} ∩ {0∗,10∗,110} = {10∗} != ∅;

(4,2) does not belong to [(0,3),(6,4)] because {  011_{2}, 100_{2}} ∩ { , , , , , } = ∅. Through the above-mentioned data conversion technology, the two types of query conditions are then unified into a unified Boolean query condition on set-valued attributes. More specifically, for each data object 〈ti,Vi,Wi〉 is transformed into a tuple 〈ti,W′i〉, where W’i=trans(Vi)+Wi; query q = 〈[ts,te], [α,β],ϒ〉 is transformed into q=〈[ts,te],ϒ′〉, where ϒ′= trans([α,β]) ∧ ϒ. So the query result is { Oi = 〈ti,W′i〉 | ti ∈ [ts,te] ∧ ϒ′(W′i) = 1}. 1*_{1}10*_{1}110_{1}0*_{2}01*_{2}010_{2}

It has a disadvantage: only integers can be converted.

4 batch verification

Batch verification can speed up certification. Batch verification is divided into two categories, one is batch verification within a block, and the second is between blocks. The paper designs corresponding indexes ( intra-block index and inter-block index ) for these two types to speed up the authentication.

block index

Previously, for simplicity, it was assumed that each block only stores one object. Normally, each block usually stores multiple objects. A single-object algorithm can be applied repeatedly to each object to ensure the integrity of the query, however, this results in a verification complexity that scales linearly with the number of objects. Furthermore, it can be observed that if two objects share some common attribute values, they may not match some queries due to the same partial query conditions. Therefore, to reduce the collation and verification overhead, an intra-block index is proposed, which can aggregate multiple objects and improve performance.

The diagram above shows a block with an intra-block index on the blockchain. It organizes the ObjectHash and AttDigest of each object into a binary Merkle tree. Therefore, the block header consists of PreBkHash, TS, ConsProof, MerkleRoot, where MerkleRoot is the root hash of the binary Merkle tree . Each tree node has three fields: the hash value of the child node (denoted by hash_{i}, used to form the MHT), the multiset attribute (denoted by W_{i}), and the cumulative value of the attribute multiset (denoted by AttDigest_{i}). They are calculated from child nodes as shown below.
(Intra-block index non-leaf nodes)    use hash( ) to represent the hash encryption function, '|' is the string connection operator, acc( ) is the multi-set accumulator, n_{l}and n_{r}the left child and right child of node n respectively child. The fields of non-leaf node n are defined as:

  •  W_{n}= W_{n_{l}}∪ ( take the unionW_{n_{r}}   of the multiset of child nodes as the multiset property of this node )
  •  AttDigest_{n}= acc( W_{n}) (with aggregatable attributes, aggregated from the bottom up to the root node)
  • hash_{n}= hash( hash( hash_{n_{l}}| hash_{n_{r}}) | AttDigest_{n}) (note that the subscripts are all n )

(Intra-block index leaf node)    The fields of the leaf node are the same as the fields of the underlying object.

When building intra-block indexes, we want to achieve maximum validation efficiency. That is, the goal is to maximize the chance of pruning unmatched objects during query processing. On the one hand, this means that a clustering strategy should be found that maximizes the chance of a node mismatch for a user's query. In other words, the similarity of objects under each node should be maximized. On the other hand, balanced binary tree is the best choice because it can improve query efficiency. Therefore, an intra-block index is built (by miners) based on the block's data objects in a bottom-up manner. First, each data object in a block is assigned to a leaf node. Next, the leaf nodes that produce the largest Jaccard coefficient (| W_{n_{l}}W_{n_{r}}| / | W_{n_{l}}∪ |) are merged iteratively. W_{n_{r}}These two merged tree nodes are used to create a new non-leaf node at the upper level. This process is repeated in each level until the root node is created. Finally, hash_{r}the assigned Merkle-Root is written as one of the components of the block header.

Using the above-mentioned intra-block index, the SP can process the query as a tree search. Starting from the root node, if the attribute multiset of the current node satisfies the query condition, its subtrees will be further investigated. Likewise, the corresponding AttDigest is added to the VO, which will be used to reconstruct the MerkleRoot during result verification. On the other hand, if the query condition is not satisfied by the multiset, it means that none of the underlying objects matched. In this case, the SP will call ProveDisjoint( ) and the corresponding AttDigest to generate a proof of mismatch. Until the leaf node is reached, the multi-set objects that meet the query conditions are matching objects and will be returned as query results. ( Regardless of whether the query conditions match or not, AttDigest will be returned in VO for user-side reconstruction of Merkleroot )

For example, a Boolean query from a user would be "Sedan" ∧ ("Benz" ∨ "BMW"). The query process simply traverses the index from the root node to the leaf nodes. The query result is { }. o_{1}

The VO returned by SP includes {〈AttDigest_{r}〉, 〈AttDigest_{5}〉, 〈hash_{2}, π_{2}, {“Audi”},AttDigest_{2}〉, 〈hash_{6}, π_{6}, {“Van”},AttDigest_{6}〉}. Here π_{2}and π_{6}are the two disjoint proofs of mismatching nodes N_{2}and N_{6}(shaded in Figure 6). Note:AttDigest_{r} andAttDigest_{5}will be used for the construction of MerkleRoot when the results are verified.

On the user side, mismatch verification is performed by calling VerifyDisjoint(·)AttDigest using the disjoint set, proof π, in VO . In addition, in order to verify the reliability and integrity of the results, users need to reconstruct the MerkleRoot and compare it with the MerkleRoot read from the block header.

In this example, first, VerifyDisjoint( ) is called with 〈π _{2}, AttDigest_{2}, {"Audi"}〉 and 〈π _{6}, , {"Van"}〉 to verify that N and N do not match the query. Then, the user uses the returned result to calculate ( ), according to VO calculation  = ( ( ) | | ), = ( | | ). Finally, the user checks the newly calculated hash against the MerkleRoot in the block header . ( The query user is a light node, only the block header data is stored, and the field is stored in the miner and SP as a full node, which will not waste the user's storage space. When the user verifies, directly use the AttDigest in the VO returned by the SP for calculation. )AttDigest_{6}_{2}_{6}hasho_{1}hash_{5}hashhasho_{1}hash_{2}AttDigest_{5}hash_{r}hashhash_{5}hash_{6}AttDigest_{r}hash_{r}W_{i}

inter-block index

In addition to similar objects within the same block, objects across blocks may also share similarities and not match queries  for the same reason . This paper builds an inter-block index that uses skip lists to further optimize query performance .

An interblock index consists of a number of skips, each skipping an exponential number of previous blocks. For example, the list might skip the 2, 4, 8, ... blocks preceding this block . For each skip, it maintains three components: the hash of all skipped blocks (denoted by PreSkippedHash _{L_{k}}), the sum of multisets of properties of skipped blocks (denoted by W _{L_{k}}), and the corresponding cumulative value wrt W _{L_{k}}( AttDigest _{L_{k}}denoted by ). Here the sum of attribute multisets is used to enable later on-line aggregate authentication . Finally , the inter- block index is written into the block using an extra fieldSkipListRoot defined as:

During query processing, qualified skips can be used to denote multiple blocks that did not contribute to the query result due to the same mismatch reason. Because users can avoid accessing these skipped blocks, verification costs can be reduced.

Query processing with an inter-block index starts with the most recent block in the query time window. We iterate the skip list from the maximum number of skips to the minimum number of skips. If a skipped W_{L_{i}}multiset does not match the query condition, it means that all skipped blocks between the current block and the previous i blocks do not contain matching results. Therefore, calling ProveDisjoint (·) then outputs the mismatch proof π _{i}. Then add 〈PreSkippedHash _{L_{i}}, π _{i}, ϒ _{i}, AttDigest _{L_{i}}〉 to VO. Users can use this proof to verify that the skipped blocks did not match the query. At the same time, hash_{L_{i}}all other hashes except for VO are added to VO. If we find no mismatching blocks during iteration, the current block calls IntraIndexQuery(·) and the previous block is also checked. If we successfully find a mismatch skip, the corresponding previous block will be checked next. The function InterIndexquery (·) is called recursively until all blocks in the query window have been checked. Note that intra-block and inter-block indexes can be combined to maximize performance, since they do not conflict.

Online batch verification

The proposed intra-block index tries to somehow cluster objects within the same block together to maximize the efficiency of validating mismatched objects. However, some objects/nodes indexed in different blocks or even different subtrees of the same block may also have the same mismatch reason. Therefore, online aggregation of such objects/nodes is beneficial for more efficient collation. To this end, the Sum( ) primitive is introduced, which outputs the cumulative value of an aggregated multiset when given multiple cumulative values.

In the example shown in Figure 6, assume that O2 and O4 share the same reason ("Benz") for the query condition mismatch. SP can return π = ProveDisjoint ( W_{2}+ W_{4},{“Benz”},pk) and  AttDigest_{2,4}= Sum(acc( W_{2}),acc( W_{4})). The user can use VerifyDisjoint ( AttDigest_{2,4}, acc({"Benz"}), π, pk) to verify that the two objects do not match in a batch.

5 Subscribable query index

Subscription queries are registered by the query user and continue to be processed until unregistered. When SP sees a newly confirmed block, it needs to publish the result together with VO to registered users. We first propose a query index to efficiently handle large subscription queries. Then, a delayed authentication optimization is developed, which delays the proof of mismatch to reduce the query verification cost. Subscription query can be generally divided into three steps:

(1) The service provider collects the user's query statement and builds an index.

(2) When a data object arrives, all queries that meet the conditions are searched through the index.

(3) Send the data object to the corresponding user, and at the same time send the mismatch certificate of the unmatched data object.

Query indexes for scalable processing

Most of the query processing overhead comes from unmatched object generation evidence on the SP. Unmatched objects may have the same mismatch reason for different subscription queries. Therefore, such queries can share a proof of mismatch. The paper proposes to build an inverted prefix tree, called IP tree, on the subscription query. It's essentially a prefix tree referencing inverted files for numeric range conditions and boolean set conditions.

Prefix tree component: IP-Tree is constructed based on a grid tree in which each tree node is represented by a CNF Boolean function, in order to index the range of numbers for all subscription queries. For example, the grid node corresponding to the upper left cell ([0, 2], [1, 3]) in Figure 8 is represented N_{1}by { }. The root node of the prefix tree covers the entire scope space of all subscription queries.  0*_{1}1*_{2}

Inversion file component:  Each node of IP-Tree is associated with an inversion file, which is constructed based on the subscription query of the index under the node. Each reverse file has two subcomponents:
Range Conditional Inverted File (RCIF) . Each entry in RCIF has two attributes: q_{i}the type of query and its coverage (full or partial). All queries in RCIF intersect the number space S of nodes. The type of coverage shows q_{i}whether S is fully covered or partially covered. The RCIF function is used to check for mismatches in numeric range conditions.
Boolean conditional inverted file (BCIF) . The BCIF function only records queries that completely cover the node space. Each entry in BCIF consists of two attributes: query condition set ϒ and corresponding query. BCIF is used to check for mismatches of boolean set conditions.

Take Figure 8 as an example to illustrate how to build an IP-Tree. It is built by SP in a top-down manner. First create the root node and add all queries to its RCIF as partial coverage queries. Then split the root node and create four equally spaced child nodes. For each child node, if a query fully or partially covers the node's space, it is added to the node's RCIF. Likewise, an equivalence set that fully covers the query will be added to the node's BCIF. Take N_{1}for example. Query q_{1}sum q_{2}completely covers this node, query q_{3}only partially covers it. Therefore, RCIF contains three interleaving queries q_{1}, q_{2}and q_{3}. The types involved are complete q_{1}and the types involved are partial. For BCIF,  and  share the same set {"Van"}, the sets {"Benz"} and {"BMW"} correspond to   queries for and respectively. Next, since only part of it is covered , we further split into four parts. It was added to RCIF and BCIF since it was all covered . The algorithm terminates when no part of the query is found in any leaf node. When a query is registered or deregistered, update the nodes of the IP-Tree corresponding to the value range of the query. Tree nodes can also be split or merged if necessary. Note that in order to prevent the tree from getting too deep, when the depth of the tree reaches a certain predefined threshold, it will switch back to the case without IP-Tree.q_{2}q_{3}N_{1}q_{1}q_{2}q_{1}q_{2}q_{3}N_{1}N_{1}q_{3}N_{7}N_{7}

Using IP-Tree indexes, subscription queries can be handled as tree traversals. When a new object O arrives, the IP tree is traversed along the path from the root to the leaf nodes covering O. For any node on the traversal path n_{q}, n_{q}the relevant query can be found from RCIF. These queries can be divided into three categories:

(1) An equivalence set n_{q}matches the full coverage query of object O on BCIF (thus, the result of this query is that O is added). 

(2) An equivalence set on BCIF does not match the full coverage query of object O (thus, ProveDisjointn_{q} ( ) will be called and a disjoint proof will be generated for this query). 

(3) Partial coverage query (no further action required).

Additionally, RCIF queries that identify n_{q}parent nodes that are present but not present. n_{q}Next, n_{q}the child nodes of q are processed, and the process continues until a leaf node is reached or all queries are classified as matching or not matching. Consider a new object Oi = 〈t_{i},(0,2), {"Van", "Benz"}〉 = 〈t_{i}, { , , "Van", "Benz"}〉 shown in Figure 8. In , are classified as matching queries, and not matching because of boolean set conditions and numeric range conditions, respectively. And it's not confirmed as a mismatch until the checked child nodes . 00_{1}10_{2}N_{1}q_{1}q_{2}q_{4}q_{3}N_{1}N_{7}

Ideas can be easily derived from new blocks of objects indexed by the block index. We start at the root of the index within the block, and for any index on a node n_{b}, treat it as a super object and apply the query processing described above. The only difference is that if a full-coverage query is classified as a match, we cannot immediately return the current node as the query result, but further recursively check its child nodes until reaching a leaf node.

lazy authentication

In the previous section, when a new block is confirmed, the result and proof are immediately published to registered users. In particular, even if the query has no matching results, a proof of mismatch is still computed and sent. This approach is suitable for real-time applications. For applications without such real-time requirements, a delayed authentication optimization is proposed , where the SP does not return a result until a matching object exists (or the time since the last result has passed a threshold).

In this approach, the VO should prove that the current object is a match and all other objects since the last result did not match the query. To achieve this, just wait for the matching result, and call a time window query to dynamically calculate the evidence of non-matching. However, this approach can only generate unmatched proofs for each query separately, and cannot leverage proofs shared by different subscribed queries. Furthermore, this approach leaves the burden of proofreading entirely to when a match is available. To address these issues, a novel approach is proposed to incrementally generate mismatch proofs using inter-block indexes.

Using an inter-block index to answer subscription queries is quite different from using time window queries. The reason is that it is possible to traverse the blockchain in reverse and use skip lists to aggregate evidence in time window queries. However, this cannot be done for subscription queries because new blocks are not yet available, and we do not know whether future objects will share the same mismatch condition. Therefore, a stack is introduced to facilitate tracking of arriving blocks that share the same mismatch condition. The basic idea is to use a skip list to find the maximum skip distance L_{i}such that it covers m elements at the top of the stack. The block's AttDigest is AttDigest_{L_{i}}replaced. Disjoint proofs can be aggregated online by calling ProofSum() . For example, there are two mismatched sums in the stack block_{i}and block_{i-1}a skip distance of 2. SPs can then replace their proofs with an aggregated proof computed from ProofSum_{i}, π ). _{i-1}In this way, when a matching result is found, SP does not need to compute the set disjoint proof from scratch.

6 Summary

The problem of verifiable query processing for blockchain databases is studied for the first time in the literature. The vChain framework is proposed to guarantee the integrity of lightweight user boolean range queries. A new accumulator-based automatic data mining scheme is developed that transforms numeric attributes into set-valued attributes, enabling dynamic aggregation on arbitrary query attributes. On this basis, two data indexes are designed, that is, a tree-based intra-block index and a jump-list-based inter-block index, and a subscription query index based on a prefix tree, and a series of optimizations are carried out.

Guess you like

Origin blog.csdn.net/liuyh19980/article/details/124533062