Data Transaction
We can store, update, or delete any state (i.e., a key-value pair) on the ledger by issuing a data transaction.
By submitting a POST /data/tx {txPaylod}
, we can perform a data transaction where txPayload
contains reads, writes, and deletions of states.
Using a data transaction, we can do the following:
- Create new states
- Update an existing states
- Delete an existing states
- Create, update, and delete states within a single transaction
- Operations on multiple databases
- Multi-signatures transaction
caution
It is recommended to start a fresh Orion server for executing these examples. Otherwise, you need to carefully change the block number specified as part of the version in the read-set of the transaction payload. Reading this document to the end will clarify this point.
Prerequisite
Once a fresh Orion server is started after removing the ledger
directory,
1) Create new states
1.1) Create a state with key key1
Let's store a new state with the key key1
with the value {"name":"abc","age":31,"graduated":true}
.
curl \
-H "Content-Type: application/json" \
-H "TxTimeout: 2s" \
-X POST http://127.0.0.1:6001/data/tx \
--data '{
"payload": {
"must_sign_user_ids": [
"alice"
],
"tx_id": "1b6d6414-9b58-45d0-9723-1f31712add81",
"db_operations": [
{
"db_name": "db2",
"data_writes": [
{
"key": "key1",
"value": "eyJuYW1lIjoiYWJjIiwiYWdlIjozMSwiZ3JhZHVhdGVkIjp0cnVlfQ==",
"acl": {
"read_users": {
"alice": true,
"bob": true
},
"read_write_users": {
"alice": true
}
}
}
]
}
]
},
"signatures": {
"alice": "MEUCIHS1BA4ZIeLcmlb/HSwhXGIuzqZOXxpirHevWx426nZgAiEAnCk3hoDXZ0Pn5jlU0igQLkHT3TU08qWH+rPgxVCYbD0="
}
}'
The payload contains must_sign_user_ids
which is a list of user ids that must sign the transaction's payload. The
db_operations
hold the data_writes
to be applied on the specified db_name
, i.e., db2
. The value
in data_writes
must be encoded in base64. We need to use the encoder
utility to get the base64-encoded output as shown below:
./bin/encoder -data='{"name":"abc","age":31,"graduated":true}'
Output
eyJuYW1lIjoiYWJjIiwiYWdlIjozMSwiZ3JhZHVhdGVkIjp0cnVlfQ==
acl
contains a list of users in the read_users
who can read-only the state and a list of users in the read_write_users
who can both read and write to the state. Here, the signatures
holds a map of
each user in the must_sign_user_ids
to their digital signature.
Signature Computation
The signature is computed using the alice
private key as shown below:
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"must_sign_user_ids":["alice"],"tx_id":"1b6d6414-9b58-45d0-9723-1f31712add81","db_operations":[{"db_name":"db2","data_writes":[{"key":"key1","value":"eyJuYW1lIjoiYWJjIiwiYWdlIjozMSwiZ3JhZHVhdGVkIjp0cnVlfQ==","acl":{"read_users":{"alice":true,"bob":true},"read_write_users":{"alice":true}}}]}]}'
MEUCIHS1BA4ZIeLcmlb/HSwhXGIuzqZOXxpirHevWx426nZgAiEAnCk3hoDXZ0Pn5jlU0igQLkHT3TU08qWH+rPgxVCYbD0=
Once the above transaction gets committed, the submitter of the transaction receives the transaction receipt.
Transaction Receipt
{
"response": {
"header": {
"node_id": "bdb-node-1"
},
"receipt": {
"header": {
"base_header": {
"number": 5,
"previous_base_header_hash": "0hi2UZF5+X4xcGhobE1akVFZ/depxYCWShukzF+KrNk=",
"last_committed_block_hash": "t9F0hOUPIuyk8upeQhpDpTY/wLP3yoLuA7NBqNE0HUo=",
"last_committed_block_num": 4
},
"skipchain_hashes": [
"t9F0hOUPIuyk8upeQhpDpTY/wLP3yoLuA7NBqNE0HUo=",
"PAFz7foMQVLrK0q1bO9TYxW9ZwUMl7rb6G3DZoSD1FY=",
"xz3tsHvNxVNsB709SBOJ3s2fuUXo2GciBGThc70aaTM="
],
"tx_merkle_tree_root_hash": "UTf7CV8x9/QC9luiVwe5QzHVg9hvCHNLxWAC2iD/5Zw=",
"state_merkle_tree_root_hash": "+kBbsknNLNuDVKxDTktcgcJMsR9gRAHHW03plIjrTg0=",
"validation_info": [
{}
]
}
}
},
"signature": "MEQCIDwJOH8ZNqs2H1rD+GWTTm03Cu1X+LcILP5NnPpmZ14LAiAJHmD4/E2noofY47eyjJm5NCcAmTUytqoB8Dat3xeVaQ=="
}
1.2) Checking the existance of key1
Let's query the node to see whether key1
exists. The query can be submitted by either alice
or bob
as both have the read permission to this key. No one else can read key1
, including the admin user of the node. In this example, we use bob
to query the key.
First, compute the bob signature on the request payload.
./bin/signer -privatekey=deployment/sample/crypto/bob/bob.key \
-data='{"user_id":"bob","db_name":"db2","key":"key1"}'
Output
MEUCIQDm6dLmAdd0X49JygTiUkh+brZxprWSr2+hcAH+QIu3AAIgF+m7kO33YXyyqSbnXS9HR79wt/aL3JGhKvXFQaFBJms=
Second, submit the query.
curl \
-H "Content-Type: application/json" \
-H "UserID: bob" \
-H "Signature: MEUCIQDm6dLmAdd0X49JygTiUkh+brZxprWSr2+hcAH+QIu3AAIgF+m7kO33YXyyqSbnXS9HR79wt/aL3JGhKvXFQaFBJms=" \
-X GET http://127.0.0.1:6001/data/db2/key1 | read json; ./base64_decoder -getresponse=$json | jq .
The default output would contain the base64-encoded string of the value field. To print a human readable value, we
use the base64_decoder
utility.
Output
{
"response": {
"header": {
"node_id": "bdb-node-1"
},
"value": "{\"name\":\"abc\",\"age\":31,\"graduated\":true}",
"metadata": {
"version": {
"block_num": 4
},
"access_control": {
"read_users": {
"alice": true,
"bob": true
},
"read_write_users": {
"alice": true
}
}
}
},
"signature": "MEUCID5QuzmQukLWGFvPo0BCSmc2/E5IUHt8wOTVC1i5sxlOAiEAir2/H1AUfeJnhkJ/N4bdwn7wZN903oyenV0doIaagzw="
}
The result contains the base64-decoded value
associated with the key and also the access_control
and version
as part of the metadata
. To verify the signature, do not use the output of the base64_decoder
utility but rather the cURL
output.
2) Update an existing state
2.1) Update the key key1
Let's update the value of key1
. To do that, we need to execute the following three steps:
- Fetch
key1
from the server. - Construct the updated value and transaction payload including the read version.
- update the
age
from31
. The new value would be{"name":"abc","age":32,"graduated":true}
- update the
- Submit the data transaction
Note that the
data_reads
should contain the version of thekey1
that was read before modifying the value ofkey1
. In other words, the version should be the last committed version. Ifdata_reads
is kept empty, thedata_writes
would be considered as a blind write.
curl \
-H "Content-Type: application/json" \
-H "TxTimeout: 2s" \
-X POST http://127.0.0.1:6001/data/tx \
--data '{
"payload": {
"must_sign_user_ids": [
"alice"
],
"tx_id": "1b6d6414-9b58-45d0-9723-1f31712add83",
"db_operations": [
{
"db_name": "db2",
"data_reads": [
{
"key": "key1",
"version": {
"block_num": 4
}
}
],
"data_writes": [
{
"key": "key1",
"value": "eyJuYW1lIjoiYWJjIiwiYWdlIjozMiwiZ3JhZHVhdGVkIjp0cnVlfQ==",
"acl": {
"read_users": {
"alice": true
},
"read_write_users": {
"alice": true
}
}
}
]
}
]
},
"signatures": {
"alice": "MEUCIQDU+vdGZny6RfjIujjJ/ZZPv43OEQ9JsxZ08gx1FDTWMgIgArbhOw+42yy0zP1YLXpVJ5BVyP/3Cqft4OWW8bsUKqU="
}
}'
The data_reads
holds the read key, key1
and its version "block_num":4
. Please fetch key1
to get the version number in your setup. We updated the value
of key1
and removed the user bob
from the reader's list.
The required signature is computed with alice's private key using the following command
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"must_sign_user_ids":["alice"],"tx_id":"1b6d6414-9b58-45d0-9723-1f31712add83","db_operations":[{"db_name":"db2","data_reads":[{"key":"key1","version":{"block_num":4}}],"data_writes":[{"key":"key1","value":"eyJuYW1lIjoiYWJjIiwiYWdlIjozMiwiZ3JhZHVhdGVkIjp0cnVlfQ==","acl":{"read_users":{"alice":true},"read_write_users":{"alice":true}}}]}]}'
Output
MEUCIQDU+vdGZny6RfjIujjJ/ZZPv43OEQ9JsxZ08gx1FDTWMgIgArbhOw+42yy0zP1YLXpVJ5BVyP/3Cqft4OWW8bsUKqU=
Once the above transaction gets committed, the submitter of the transaction would get the following transaction receipt.
{
"response": {
"header": {
"node_id": "bdb-node-1"
},
"receipt": {
"header": {
"base_header": {
"number": 6,
"previous_base_header_hash": "paoW5AH3PRvYnFgsuQGl8eVxA2cYoeHakyHY+mIogf0=",
"last_committed_block_hash": "xrRU2Hy5curlSi/YafvCCeKaAb8bF9vUNGhr5usK4fc=",
"last_committed_block_num": 5
},
"skipchain_hashes": [
"xrRU2Hy5curlSi/YafvCCeKaAb8bF9vUNGhr5usK4fc="
],
"tx_merkle_tree_root_hash": "bSgG1t1yZhOGVOrqT4LR+wXdtB/LTXvYy2De1ouaq+U=",
"state_merkle_tree_root_hash": "TueFaZX9xju1WimSV0g7wxuekIJ7nq6Bf+o+WNWSnkc=",
"validation_info": [
{}
]
}
}
},
"signature": "MEQCIEaCKYOLUaa55VFdssKQfiFKnPY7gzm4sTw2loSTe1fnAiB5fzlYRg4dYjtjgWjHxiQHsDtHgDP+8ZSOdvJww3CSww=="
}
2.2) Checking the existence of the updated key key1
Let's query the node to see whether key1
has been updated. The query can be submitted only by alice
as bob
is no longer in the readers' list of key1
. No one else can read key1
including the admin user of the node.
In this example, we use alice
to query the key.
First, compute the alice signature on the request payload.
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"user_id":"alice","db_name":"db2","key":"key1"}'
Output
MEYCIQDUVfuDVppJ2BLuiD662M4iuBYdn2E2ttyspZo6UXYmNgIhAOEvZTdRe7d9/bkVliplmpFHeypKbz7wMHPluGYipqvw
Second, submit the query.
curl \
-H "Content-Type: application/json" \
-H "UserID: alice" \
-H "Signature: MEYCIQDUVfuDVppJ2BLuiD662M4iuBYdn2E2ttyspZo6UXYmNgIhAOEvZTdRe7d9/bkVliplmpFHeypKbz7wMHPluGYipqvw" \
-X GET http://127.0.0.1:6001/data/db2/key1 | read json; ./base64_decoder -getresponse=$json | jq .
Output
{
"response": {
"header": {
"node_id": "bdb-node-1"
},
"value": "{\"name\":\"abc\",\"age\":32,\"graduated\":true}",
"metadata": {
"version": {
"block_num": 5
},
"access_control": {
"read_users": {
"alice": true
},
"read_write_users": {
"alice": true
}
}
}
},
"signature": "MEUCIQCW2w0ec3AN7/y/1H5cuaRvmmBFVsmNY3LeggwNzYJhyQIgbUGQ6UqI33bAJNRKWxZ5FP278KQfCjtW10tSa7e3/XQ="
}
The result contains the updated value
associated with the key and also the access_control
and version
as part of the
metadata
. In the update state, we can find that the value
, version
, and access_control
have changed.
3) Delete an existing state
3.1) Delete the key key1
Let's delete the key key1
. In order to do that, we need to execute the following three steps:
- Read
key1
from the server. If we want to blindly deletekey1
, this step can be avoided and the read version is not needed. - Construct the transaction payload including the read version.
- Submit the data transaction
Note that
data_reads
should contain the version ofkey1
that was read before modifying the value ofkey1
. Ifdata_reads
is left out, the delete would be applied blindly.
curl \
-H "Content-Type: application/json" \
-H "TxTimeout: 2s" \
-X POST http://127.0.0.1:6001/data/tx \
--data '{
"payload": {
"must_sign_user_ids": [
"alice"
],
"tx_id": "1b6d6414-9b58-45d0-9723-1f31712add85",
"db_operations": [
{
"db_name": "db2",
"data_reads": [
{
"key": "key1",
"version": {
"block_num": 5
}
}
],
"data_deletes": [
{
"key": "key1"
}
]
}
]
},
"signatures": {
"alice": "MEUCICkMMnle29bx7MP5dPE3INwnZk1jw0ueXOlYUp2j7K90AiEA2M6GjiuzG5aDl0qKua+thfPGw5PiDICYk9XNgKZXtX0="
}
}'
The transaction should be submitted by alice
as only she has the write permission which is required for the delete operation too.
The db_operations
holds the data_reads
with key1
and version "block_num":5
. The data_deletes
list contains the key key1
.
The signature is computed using the following command
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"must_sign_user_ids":["alice"],"tx_id":"1b6d6414-9b58-45d0-9723-1f31712add85","db_operations":[{"db_name":"db2","data_reads":[{"key":"key1","version":{"block_num":5}}],"data_deletes":[{"key":"key1"}]}]}'
Output
MEUCICkMMnle29bx7MP5dPE3INwnZk1jw0ueXOlYUp2j7K90AiEA2M6GjiuzG5aDl0qKua+thfPGw5PiDICYk9XNgKZXtX0=
Once the above transaction gets committed, the submitting user would get the following transaction receipt
{
"response": {
"header": {
"node_id": "bdb-node-1"
},
"receipt": {
"header": {
"base_header": {
"number": 7,
"previous_base_header_hash": "E1QGMExoA6Nt2MEk9GBE+NHDCzpVR4YuBBeYMZONbKU=",
"last_committed_block_hash": "WwuMQlvM05Kd/jh+6iD0Kt8ZxMXEdC1wEOk0wzxCBfc=",
"last_committed_block_num": 6
},
"skipchain_hashes": [
"WwuMQlvM05Kd/jh+6iD0Kt8ZxMXEdC1wEOk0wzxCBfc=",
"xrRU2Hy5curlSi/YafvCCeKaAb8bF9vUNGhr5usK4fc="
],
"tx_merkle_tree_root_hash": "vobaBbri1K+lJv+rXYXRofWuQ0D+SDMGKaBwkTtXQQ0=",
"state_merkle_tree_root_hash": "jYpTKUQBA7dJZYlzz7gEZWqX503EqGC+z+9pOKJsDKQ=",
"validation_info": [
{}
]
}
}
},
"signature": "MEYCIQDgtHEC+7gaOGsOQbFWOqeCQW5yOrwgDsZI+aZnGu+sogIhAPv+Emp+WxHjUl0ZoSw9mpF50udpTUyI+YqnRXnvn+UA"
}
3.2) Checking the non-existence of the deleted key key1
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"user_id":"alice","db_name":"db2","key":"key1"}'
MEUCIQCQWTeWyDwbj2ARFHQTH3pgsplOTrpW6XaC8mC/TODaJgIgIplpD5DB/SSNmhh42qLpITCJc66Z75mcHP3AWsS3zcw=
curl \
-H "Content-Type: application/json" \
-H "UserID: alice" \
-H "Signature: MEUCIQCQWTeWyDwbj2ARFHQTH3pgsplOTrpW6XaC8mC/TODaJgIgIplpD5DB/SSNmhh42qLpITCJc66Z75mcHP3AWsS3zcw=" \
-X GET http://127.0.0.1:6001/data/db2/key1 | jq .
Output
{
"response": {
"header": {
"nodeID": "bdb-node-1"
}
}
"signature": "MEYCIQCVdcbqmwIQDLkwYiK2tmOMwHI0GbWFX6kMMPo4VBORpgIhAMBBvsjCjL8LkogH06m58KefvnMOncLy7uFobh4XNNvI"
}
4) Create, Update, Delete states within a single transaction
We can also use data_writes
and data_deletes
with multiple entries, along with many data_reads
within a single transaction.
Let's create key1
and key2
so that in the next transaction we can do all three operations.
4.1) Create key1
and key2
curl \
-H "Content-Type: application/json" \
-H "TxTimeout: 2s" \
-X POST http://127.0.0.1:6001/data/tx \
--data '{
"payload": {
"must_sign_user_ids": [
"alice"
],
"tx_id": "1b6d6414-91ac-abcd-9723-1f31712add83",
"db_operations": [
{
"db_name": "db2",
"data_writes": [
{
"key": "key1",
"value": "eyJuYW1lIjoiYWJjIiwiYWdlIjozMiwiZ3JhZHVhdGVkIjp0cnVlfQ==",
"acl": {
"read_users": {
"alice": true
},
"read_write_users": {
"alice": true
}
}
},
{
"key": "key2",
"value": "eyJkZWdyZWUiOiJQaC5EIiwieWVhciI6MjAxOH0=",
"acl": {
"read_users": {
"alice": true
},
"read_write_users": {
"alice": true,
"bob": true
}
}
}
]
}
]
},
"signatures": {
"alice": "MEUCIQDOz5CZKIlK/t4M/rnxU6Hy0saDDCl2RprtnFIkttE+RgIgGwGF6P5rbDlSTh2DREXyl1etTeHf+TYeCLpOJ3WE2jU="
}
}'
To create key1
and key2
, we have two entries in the data_writes
section.
The signature is computed on the payload as follows:
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"must_sign_user_ids":["alice"],"tx_id":"1b6d6414-91ac-abcd-9723-1f31712add83","db_operations":[{"db_name":"db2","data_writes":[{"key":"key1","value":"eyJuYW1lIjoiYWJjIiwiYWdlIjozMiwiZ3JhZHVhdGVkIjp0cnVlfQ==","acl":{"read_users":{"alice":true},"read_write_users":{"alice":true}}},{"key":"key2","value":"eyJkZWdyZWUiOiJQaC5EIiwieWVhciI6MjAxOH0=","acl":{"read_users":{"alice":true},"read_write_users":{"alice":true,"bob":true}}}]}]}'
Output
MEUCIQDOz5CZKIlK/t4M/rnxU6Hy0saDDCl2RprtnFIkttE+RgIgGwGF6P5rbDlSTh2DREXyl1etTeHf+TYeCLpOJ3WE2jU=
4.2) Create key3
, update key2
, and delete key1
Now that we have the required data in the server, we can create, update, and delete within a single transaction.
curl \
-H "Content-Type: application/json" \
-H "TxTimeout: 2s" \
-X POST http://127.0.0.1:6001/data/tx \
--data '{
"payload": {
"must_sign_user_ids": [
"alice"
],
"tx_id": "1b6d6414-91ac-efgh-9723-1f31712add83",
"db_operations": [
{
"db_name": "db2",
"data_reads": [
{
"key": "key1",
"version": {
"block_num": 7
}
},
{
"key": "key2",
"version": {
"block_num": 7
}
}
],
"data_writes": [
{
"key": "key3",
"value": "eyJuYW1lIjoiZGVmIiwiYWdlIjoxOSwiZ3JhZHVhdGVkIjpmYWxzZX0="
},
{
"key": "key2",
"value": "eyJkZWdyZWUiOiJQaC5EIiwieWVhciI6MjAxOX0=",
"acl": {
"read_users": {
"alice": true
},
"read_write_users": {
"alice": true,
"bob": true
}
}
}
],
"data_deletes": [
{
"key": "key1"
}
]
}
]
},
"signatures": {
"alice": "MEQCIG4+RtISyo/3ZEkwKVg1gCz9GrcUpgkuMPESz6LDcudyAiBDbiF74lNxmX1T1xK+a6iUqiyd3Z5NjeFhO6j5YxW+kg=="
}
}'
In the above transaction's payload, it can be seen that the payload contains data_reads
, data_writes
, and data_deletes
.
The data_reads
contains the version of the key1
and key2
. The data_writes
contains the new key key3
and the updated key2
.
Finally, the data_deletes
holds the key key1
to be deleted.
The signature on the payload is computed as shown below:
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"must_sign_user_ids":["alice"],"tx_id":"1b6d6414-91ac-efgh-9723-1f31712add83","db_operations":[{"db_name":"db2","data_reads":[{"key":"key1","version":{"block_num":7}},{"key":"key2","version":{"block_num":7}}],"data_writes":[{"key":"key3","value":"eyJuYW1lIjoiZGVmIiwiYWdlIjoxOSwiZ3JhZHVhdGVkIjpmYWxzZX0="},{"key":"key2","value":"eyJkZWdyZWUiOiJQaC5EIiwieWVhciI6MjAxOX0=","acl":{"read_users":{"alice":true},"read_write_users":{"alice":true,"bob":true}}}],"data_deletes":[{"key":"key1"}]}]}'
Output
MEQCIG4+RtISyo/3ZEkwKVg1gCz9GrcUpgkuMPESz6LDcudyAiBDbiF74lNxmX1T1xK+a6iUqiyd3Z5NjeFhO6j5YxW+kg==
4.3) Check the non-existence of key1
Let's ensure the deletion of key1
.
First, we need to compute the signature to be set in the Signature
header field. The signature is computed on the
data {"user_id":"alice","db_name":"db2","key":"key1"}
as shown below by the user who submits the query, i.e., alice.
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"user_id":"alice","db_name":"db2","key":"key1"}'
Output
MEUCIQCQWTeWyDwbj2ARFHQTH3pgsplOTrpW6XaC8mC/TODaJgIgIplpD5DB/SSNmhh42qLpITCJc66Z75mcHP3AWsS3zcw=
Let's execute the following cURL
command to fetch key1
.
curl \
-H "Content-Type: application/json" \
-H "UserID: alice" \
-H "Signature: MEUCIQCQWTeWyDwbj2ARFHQTH3pgsplOTrpW6XaC8mC/TODaJgIgIplpD5DB/SSNmhh42qLpITCJc66Z75mcHP3AWsS3zcw=" \
-X GET http://127.0.0.1:6001/data/db2/key1 | jq .
Output
{
"response": {
"header": {
"nodeID": "bdb-node-1"
}
}
"signature": "MEYCIQCVdcbqmwIQDLkwYiK2tmOMwHI0GbWFX6kMMPo4VBORpgIhAMBBvsjCjL8LkogH06m58KefvnMOncLy7uFobh4XNNvI"
}
As key1
does not exist, the result is empty.
4.4) Check the updated key2
Let's see whether key2
has been updated from year: 2018
to year: 2019
.
First, compute the signature to be set in the Signature
header field as shown below:
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"user_id":"alice","db_name":"db2","key":"key2"}'
Output
MEUCIE4wF1nNbUCZJD2WbhwnJULRRYn9Yj+sD3Ev8zvlMRgkAiEA6vAC0RuzNJ2PiAiyhgtlzY+edbcX59ELZCi9ZfqFHCs=
Second, execute the following cURL
command to fetch key2
.
curl \
-H "Content-Type: application/json" \
-H "UserID: alice" \
-H "Signature: MEUCIE4wF1nNbUCZJD2WbhwnJULRRYn9Yj+sD3Ev8zvlMRgkAiEA6vAC0RuzNJ2PiAiyhgtlzY+edbcX59ELZCi9ZfqFHCs=" \
-X GET http://127.0.0.1:6001/data/db2/key2 | read json; ./base64_decoder -getresponse=$json | jq .
Output
{
"response": {
"header": {
"node_id": "bdb-node-1"
},
"value": "{\"degree\":\"Ph.D\",\"year\":2019}",
"metadata": {
"version": {
"block_num": 8
},
"access_control": {
"read_users": {
"alice": true
},
"read_write_users": {
"alice": true,
"bob": true
}
}
}
},
"signature": "MEUCIBIvtHxzpA2c/W96KK50VUke+Ggu5Qs0+Ccps1L1/TY9AiEA7kbYlS9+7iTU4xWF9KHMo/ZlVpFV+cWSErn0LaZ4rPk="
}
From the output, we can see that the year
field in the value has changed from 2018
to 2019
. After the update,
the version has changed.
4.5) Check the newly created key3
Let's fetch key3
.
First, compute the signature on the data as shown below:
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"user_id":"alice","db_name":"db2","key":"key3"}'
Output
MEQCIEyna9lmuJzyBjlvPQPxL4iRd5LyxpZWQDCqs146MYuOAiBL05QRAHOOSjIHzZhYiv7wm8Xfpviyu0EWdlC9SemHaw==
Second, execute the following cURL
command to fetch key3
.
curl \
-H "Content-Type: application/json" \
-H "UserID: alice" \
-H "Signature: MEQCIEyna9lmuJzyBjlvPQPxL4iRd5LyxpZWQDCqs146MYuOAiBL05QRAHOOSjIHzZhYiv7wm8Xfpviyu0EWdlC9SemHaw==" \
-X GET http://127.0.0.1:6001/data/db2/key3 | read json; cmd/base64_decoder/base64_decoder -getresponse=$json | jq .
Output
{
"response": {
"header": {
"node_id": "bdb-node-1"
},
"value": "{\"name\":\"def\",\"age\":19,\"graduated\":false}",
"metadata": {
"version": {
"block_num": 8
}
}
},
"signature": "MEUCIFwf4I8jZZwJelStDvj0hf+sLAAM4EGKN7C/vjC8sbxzAiEA05nIOMhrSPYW49XHn1ZTM6BuS6Y4xVhZRPbnlM6BotQ="
}
5) Operations on Multiple Databases in a Single Transaction
A data transaction can access or modify more than one user database in a transaction. In the below example,
we perform operations on two databases, db1
and db2
, within a single transaction.
5.1) Update alice
's privileges
As both alice
and bob
have only read permission on the database db1
, first we update the privilege of alice
to have read-write permission on db1
as shown below:
curl \
-H "Content-Type: application/json" \
-H "TxTimeout: 2s" \
-X POST http://127.0.0.1:6001/user/tx \
--data '{
"payload": {
"user_id": "admin",
"tx_id": "1b6d6414-9b58-45d5-9723-1f31712add02",
"user_reads": [
{
"user_id": "alice",
"version": {
"block_num": 3
}
}
],
"user_writes": [
{
"user": {
"id": "alice",
"certificate": "MIIBsjCCAVigAwIBAgIRAJp7i/UhOnaawHTSdkzxR1QwCgYIKoZIzj0EAwIwHjEcMBoGA1UEAxMTQ2FyIHJlZ2lzdHJ5IFJvb3RDQTAeFw0yMTA2MTYxMTEzMjdaFw0yMjA2MTYxMTE4MjdaMCQxIjAgBgNVBAMTGUNhciByZWdpc3RyeSBDbGllbnQgYWxpY2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdCmAgHdqck7uhAK5siEF/O1EIUEIYtiR3XVEjbVhNe/6GXFShtsSThXYL9/XK6p4qF4oSy9j/PURMGnWbzSnso3EwbzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU7nVzp7gto++BPlj5KAF1IA62TNEwDwYDVR0RBAgwBocEfwAAATAKBggqhkjOPQQDAgNIADBFAiEAsRZlR4sDyxS//BJnYpC684EWu1hO/JU8rkNW6Nn0FFQCIH/p6m6ELkLNQpx+1QJsWWtH/LdW94WinVylhuA4jggQ",
"privilege": {
"db_permission": {
"db1": 1,
"db2": 1
}
}
}
}
]
},
"signature": "MEUCIQCEpAcCyD85v6TNooJP554XDLjyum5p0RauP3D0t8rXTwIgYq1ghLfvrulznP0tS311UrfCWXh/jgchRElRPiYZ3Ug="
}'
To construct the above transaction payload, first we need to fetch the alice
user to capture the certificate and current privileges. For the sake of brevity, the fetch operation is not shown here. Next, we need to construct the above payload. In the user_reads
field,
we set the version that would be used during multi-version concurrency control. The user_writes
field holds the updated privileges where the read-only permission on db1
denoted by "db1":0
has been changed to "db1":1
.
The signature on the payload is computed using the following command:
./bin/signer -privatekey=deployment/sample/crypto/admin/admin.key \
-data='{"user_id":"admin","tx_id":"1b6d6414-9b58-45d5-9723-1f31712add02","user_reads":[{"user_id":"alice","version":{"block_num":3}}],"user_writes":[{"user":{"id":"alice","certificate":"MIIBsjCCAVigAwIBAgIRAJp7i/UhOnaawHTSdkzxR1QwCgYIKoZIzj0EAwIwHjEcMBoGA1UEAxMTQ2FyIHJlZ2lzdHJ5IFJvb3RDQTAeFw0yMTA2MTYxMTEzMjdaFw0yMjA2MTYxMTE4MjdaMCQxIjAgBgNVBAMTGUNhciByZWdpc3RyeSBDbGllbnQgYWxpY2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdCmAgHdqck7uhAK5siEF/O1EIUEIYtiR3XVEjbVhNe/6GXFShtsSThXYL9/XK6p4qF4oSy9j/PURMGnWbzSnso3EwbzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU7nVzp7gto++BPlj5KAF1IA62TNEwDwYDVR0RBAgwBocEfwAAATAKBggqhkjOPQQDAgNIADBFAiEAsRZlR4sDyxS//BJnYpC684EWu1hO/JU8rkNW6Nn0FFQCIH/p6m6ELkLNQpx+1QJsWWtH/LdW94WinVylhuA4jggQ","privilege":{"db_permission":{"db1":1,"db2":1}}}}]}'
Output
MEUCIQCEpAcCyD85v6TNooJP554XDLjyum5p0RauP3D0t8rXTwIgYq1ghLfvrulznP0tS311UrfCWXh/jgchRElRPiYZ3Ug=
Now that alice
has read-write permission on both db1
and db2
, we can perform the multi-database operation.
5.2) Create key1
in db1
and key1
in db2
Let's create
key1
with valuev
indb1
key1
with valuev
indb2
curl \
-H "Content-Type: application/json" \
-H "TxTimeout: 2s" \
-X POST http://127.0.0.1:6001/data/tx \
--data '{
"payload": {
"must_sign_user_ids": [
"alice"
],
"tx_id": "1b6d6414-91af-abcd-efgh-1f31712add83",
"db_operations": [
{
"db_name": "db1",
"data_reads": [
{
"key": "key1"
}
],
"data_writes": [
{
"key": "key1",
"value": "eyJuYW1lIjoiYWJjIiwiYWdlIjozMiwiZ3JhZHVhdGVkIjp0cnVlfQ==",
"acl": {
"read_users": {
"alice": true
}
}
}
]
},
{
"db_name": "db2",
"data_reads": [
{
"key": "key1"
}
],
"data_writes": [
{
"key": "key1",
"value": "eyJkZWdyZWUiOiJQaC5EIiwieWVhciI6MjAxOH0=",
"acl": {
"read_users": {
"alice": true
},
"read_write_users": {
"alice": true,
"bob": true
}
}
}
]
}
]
},
"signatures": {
"alice": "MEQCIHYKWmSmsfrnyUKpKSH89K8BNAIO1/IW/uprJeHDu6mGAiBQTzVCiwm/t63pJtyUQ34AnOSk3apZFwFMz0GYXzNAaA=="
}
}'
To perform operations on multiple databases, we can put one entry per database in the db_operations
field.
In the above command, we have one entry per db1
and db2
. Both entries hold data_reads
and data_writes
fields with key1
and key1
, respectively.
The signature on the payload is computed using the following command:
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"must_sign_user_ids":["alice"],"tx_id":"1b6d6414-91af-abcd-efgh-1f31712add83","db_operations":[{"db_name":"db1","data_reads":[{"key":"key1"}],"data_writes":[{"key":"key1","value":"eyJuYW1lIjoiYWJjIiwiYWdlIjozMiwiZ3JhZHVhdGVkIjp0cnVlfQ==","acl":{"read_users":{"alice":true}}}]},{"db_name":"db2","data_reads":[{"key":"key1"}],"data_writes":[{"key":"key1","value":"eyJkZWdyZWUiOiJQaC5EIiwieWVhciI6MjAxOH0=","acl":{"read_users":{"alice":true},"read_write_users":{"alice":true,"bob":true}}}]}]}'
Output
MEQCIHYKWmSmsfrnyUKpKSH89K8BNAIO1/IW/uprJeHDu6mGAiBQTzVCiwm/t63pJtyUQ34AnOSk3apZFwFMz0GYXzNAaA==
5.3) Check the existence of key1
in db1
Let's fetch key1
from db1
to ensure that the key has been created successfully.
First, the signature to be set in the Signature
header field needs to be computed as shown below:
./bin/signer -privatekey=deployment/sample/crypto/alice/alice.key \
-data='{"user_id":"alice","db_name":"db1","key":"key1"}'
Output
MEUCIQCujK1egalHITJ378hf60/RZaUFOkGzGkFaBWjO2fQilgIgX2htCyuGJsB8NHynIdH0T5SRjNMeWrzK6AwcSoFsiLY=
Next, execute the following command by setting the UserID
and Signature
header to fetch key1
from db1
.
curl \
-H "Content-Type: application/json" \
-H "UserID: alice" \
-H "Signature: MEUCIQCujK1egalHITJ378hf60/RZaUFOkGzGkFaBWjO2fQilgIgX2htCyuGJsB8NHynIdH0T5SRjNMeWrzK6AwcSoFsiLY=" \
-X GET http://127.0.0.1:6001/data/db1/key1 | read json; ./base64_decoder -getresponse=$json | jq .
Output
{
"response": {
"header": {
"node_id": "bdb-node-1"
},
"value": "{\"name\":\"abc\",\"age\":32,\"graduated\":true}",
"metadata": {
"version": {
"block_num": 11
},
"access_control": {
"read_users": {
"alice": true
}
}
}
},
"signature": "MEUCIECyz3V8GyJvsupm5IXdPLcAOeCjQJimo+I4e8VlHyUJAiEAzl3gILPF++H6AXZTO3hlRBDVYCVd1zwIdsvC3McYTgU="
}
5.4) Check the existence of key1
in db2
Let's fetch key1
from db2
to ensure that the key has been created successfully.
First, the signature to be set in the Signature
header field needs to be computed as shown below:
./bin/signer -privatekey=deployment/sample/crypto/bob/bob.key \
-data='{"user_id":"bob","db_name":"db2","key":"key1"}'
Output
MEUCIA2i15BxJAYcwl7jeGgrqqr6C2xc3hbBNKJwdC8mmNgvAiEA+AzQiMi6VbCKSn/N011jrNTmz4JiZ/u1+QLryYaDuBg=
Second, submit the query
curl \
-H "Content-Type: application/json" \
-H "UserID: bob" \
-H "Signature: MEUCIA2i15BxJAYcwl7jeGgrqqr6C2xc3hbBNKJwdC8mmNgvAiEA+AzQiMi6VbCKSn/N011jrNTmz4JiZ/u1+QLryYaDuBg=" \
-X GET http://127.0.0.1:6001/data/db2/key1 | read json; ./base64_decoder -getresponse=$json | jq .
Output
{
"response": {
"header": {
"node_id": "bdb-node-1"
},
"value": "{\"degree\":\"Ph.D\",\"year\":2018}",
"metadata": {
"version": {
"block_num": 11
},
"access_control": {
"read_users": {
"alice": true
},
"read_write_users": {
"alice": true,
"bob": true
}
}
}
},
"signature": "MEUCIQCxVaLsp0gHWqzsuat7XdUY4OehLy2tN89viOzIpTajDgIgawoH+dBtI3tidwROYHGtkZmDE3RCgVtG24s7aFyTX1Q="
}