From 7b9e1870a381a6014b9488592f85b03a59b49b0e Mon Sep 17 00:00:00 2001 From: Gursharan Singh <3442979+G8XSU@users.noreply.github.com> Date: Thu, 13 Apr 2023 01:58:32 -0700 Subject: [PATCH] Add Vss thin client protobuf generation & build setup --- .gitignore | 1 + Cargo.toml | 4 + vss-accessor/Cargo.toml | 16 +++ vss-accessor/build.rs | 6 + vss-accessor/src/proto/vss.proto | 226 +++++++++++++++++++++++++++++++ 5 files changed, 253 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 vss-accessor/Cargo.toml create mode 100644 vss-accessor/build.rs create mode 100644 vss-accessor/src/proto/vss.proto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fcd5bd1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] +members = [ + "vss-accessor", +] \ No newline at end of file diff --git a/vss-accessor/Cargo.toml b/vss-accessor/Cargo.toml new file mode 100644 index 0000000..42f843e --- /dev/null +++ b/vss-accessor/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "vss-accessor" +version = "0.1.0" +edition = "2021" +build = "build.rs" + +[dependencies] +prost = "0.11.3" +reqwest = "0.11.13" + +[dev-dependencies] +mockito = "0.31.1" +tokio = { version = "1.22.0"} + +[build-dependencies] +prost-build = { version = "0.11.3" } \ No newline at end of file diff --git a/vss-accessor/build.rs b/vss-accessor/build.rs new file mode 100644 index 0000000..4b5afc6 --- /dev/null +++ b/vss-accessor/build.rs @@ -0,0 +1,6 @@ +extern crate prost_build; + +fn main() { + prost_build::compile_protos(&["src/proto/vss.proto"], + &["src/"]).unwrap(); +} \ No newline at end of file diff --git a/vss-accessor/src/proto/vss.proto b/vss-accessor/src/proto/vss.proto new file mode 100644 index 0000000..f89ba3c --- /dev/null +++ b/vss-accessor/src/proto/vss.proto @@ -0,0 +1,226 @@ +syntax = "proto3"; +option java_multiple_files = true; +package org.vss; + +message GetObjectRequest { + + // store_id is a keyspace identifier. + // Ref: https://en.wikipedia.org/wiki/Keyspace_(distributed_data_store) + // All APIs operate within a single store_id. + // It is up to clients to use single or multiple stores for their use-case. + // This can be used for client-isolation/ rate-limiting / throttling on the server-side. + // Authorization and billing can also be performed at the store_id level. + string store_id = 1; + + // Key for which the value is to be fetched. + // + // Consistency Guarantee: + // Get(read) operations against a key are consistent reads and will reflect all previous writes, + // since Put/Write provides read-after-write and read-after-update consistency guarantees. + // + // Read Isolation: + // Get/Read operations against a key are ensured to have read-committed isolation. + // Ref: https://en.wikipedia.org/wiki/Isolation_(database_systems)#Read_committed + string key = 2; +} + +message GetObjectResponse { + + // Fetched value and version along with the corresponding key in the request. + KeyValue value = 2; +} + +message PutObjectRequest { + + // store_id is a keyspace identifier. + // Ref: https://en.wikipedia.org/wiki/Keyspace_(distributed_data_store) + // All APIs operate within a single store_id. + // It is up to clients to use single or multiple stores for their use-case. + // This can be used for client-isolation/ rate-limiting / throttling on the server-side. + // Authorization and billing can also be performed at the store_id level. + string store_id = 1; + + // global_version is a sequence-number/version of the whole store. This can be used for versioning + // and ensures that multiple updates in case of multiple devices can only be done linearly, even + // if those updates did not directly conflict with each other based on keys/transaction_items. + // + // If present, the write will only succeed if the current server-side global_version against + // the store_id is same as in the request. + // Clients are expected to store (client-side) the global version against store_id. + // The request must contain their client-side value of global_version if global versioning and + // conflict detection is desired. + // + // For the first write of the store, global version should be '0'. If the write succeeds, clients + // must increment their global version (client-side) by 1. + // The server increments global_version (server-side) for every successful write, hence this + // client-side increment is required to ensure matching versions. This updated global version + // should be used in subsequent PutObjectRequests for the store. + // + // Requests with a conflicting version will fail with `CONFLICT_EXCEPTION` as ErrorCode. + optional int64 global_version = 2; + + // Items to be written as a result of this PutObjectRequest. + // + // In an item, each key is supplied with its corresponding value and version. + // Clients can choose to encrypt the keys client-side in order to obfuscate their usage patterns. + // If the write is successful, the previous value corresponding to the key will be overwritten. + // + // Multiple items in transaction_items of a single PutObjectRequest are written in + // a database-transaction in an all-or-nothing fashion. + // Items in a single PutObjectRequest must have distinct keys. + // + // Clients are expected to store a version against every key. + // The write will succeed if the current DB version against the key is the same as in the request. + // When initiating a PutObjectRequest, the request should contain their client-side version for + // that key-value. + // + // For the first write of any key, the version should be '0'. If the write succeeds, the client + // must increment their corresponding key versions (client-side) by 1. + // The server increments key versions (server-side) for every successful write, hence this + // client-side increment is required to ensure matching versions. These updated key versions should + // be used in subsequent PutObjectRequests for the keys. + // + // Requests with a conflicting version will fail with `CONFLICT_EXCEPTION` as ErrorCode. + // + // Considerations for transactions: + // Transaction writes of multiple items have a performance overhead, hence it is recommended to use + // them only if required by the client application to ensure logic/code correctness. + // That is, transaction_items are not a substitute for batch-write of multiple unrelated items. + // When a write of multiple unrelated items is desired, it is recommended to use separate + // PutObjectRequests. + // + // Consistency guarantee: + // All PutObjectRequests are strongly consistent i.e. they provide read-after-write and + // read-after-update consistency guarantees. + repeated KeyValue transaction_items = 3; +} + +message PutObjectResponse { +} + +message ListKeyVersionsRequest { + + // store_id is a keyspace identifier. + // Ref: https://en.wikipedia.org/wiki/Keyspace_(distributed_data_store) + // All APIs operate within a single store_id. + // It is up to clients to use single or multiple stores for their use-case. + // This can be used for client-isolation/ rate-limiting / throttling on the server-side. + // Authorization and billing can also be performed at the store_id level. + string store_id = 1; + + // A key_prefix is a string of characters at the beginning of the key. Prefixes can be used as + // a way to organize key-values in a similar way to directories. + // + // If key_prefix is specified, the response results will be limited to those keys that begin with + // the specified prefix. + // + // If no key_prefix is specified or it is empty (""), all the keys are eligible to be returned in + // the response. + optional string key_prefix = 2; + + // page_size is used by clients to specify the maximum number of results that can be returned by + // the server. + // The server may further constrain the maximum number of results returned in a single page. + // If the page_size is 0 or not set, the server will decide the number of results to be returned. + optional int32 page_size = 3; + + // page_token is a pagination token. + // + // To query for the first page of ListKeyVersions, page_token must not be specified. + // + // For subsequent pages, use the value that was returned as `next_page_token` in the previous + // page's ListKeyVersionsResponse. + optional string page_token = 4; +} + +message ListKeyVersionsResponse { + + // Fetched keys and versions. + // Even though this API reuses KeyValue struct, the value sub-field will not be set by the server. + repeated KeyValue key_versions = 1; + + // next_page_token is a pagination token, used to retrieve the next page of results. + // Use this value to query for next_page of paginated ListKeyVersions operation, by specifying + // this value as the `page_token` in the next request. + // + // If next_page_token is empty (""), then the "last page" of results has been processed and + // there is no more data to be retrieved. + // + // If next_page_token is not empty, it does not necessarily mean that there is more data in the + // result set. The only way to know when you have reached the end of the result set is when + // next_page_token is empty. + // + // Caution: Clients must not assume a specific number of key_versions to be present in a page for + // paginated response. + optional string next_page_token = 2; + + // global_version is a sequence-number/version of the whole store. + // + // global_version is only returned in response for the first page of the ListKeyVersionsResponse + // and is guaranteed to be read before reading any key-versions. + // + // In case of refreshing the complete key-version view on the client-side, correct usage for + // the returned global_version is as following: + // 1. Read global_version from the first page of paginated response and save it as local variable. + // 2. Update all the key_versions on client-side from all the pages of paginated response. + // 3. Update global_version on client_side from the local variable saved in step-1. + // This ensures that on client-side, all current key_versions were stored at global_version or later. + // This guarantee is helpful for ensuring the versioning correctness if using the global_version + // in PutObject API and can help avoid the race conditions related to it. + optional int64 global_version = 3; +} + +// When HttpStatusCode is not ok (200), the response `content` contains a serialized ErrorResponse +// with the relevant ErrorCode and message +message ErrorResponse { + + // The error code uniquely identifying an error condition. + // It is meant to be read and understood programmatically by code that detects/handles errors by + // type. + ErrorCode error_code = 1; + + // The error message containing a generic description of the error condition in English. + // It is intended for a human audience only and should not be parsed to extract any information + // programmatically. Client-side code may use it for logging only. + string message = 2; +} + +// ErrorCodes to be used in ErrorResponse +enum ErrorCode { + + // Default protobuf Enum value. Will not be used as ErrorCode by server. + UNKNOWN = 0; + + // CONFLICT_EXCEPTION is used when the request contains mismatched version (either key or global) + // in PutObjectRequest. For more info refer PutObjectRequest. + CONFLICT_EXCEPTION= 1; + + // INVALID_REQUEST_EXCEPTION is used in the following cases: + // - The request was missing a required argument. + // - The specified argument was invalid, incomplete or in the wrong format. + // - The request body of api cannot be deserialized into corresponding protobuf object. + INVALID_REQUEST_EXCEPTION = 2; + + // An internal server error occurred, client is probably at no fault and can safely retry this + // error with exponential backoff. + INTERNAL_SERVER_EXCEPTION = 3; +} + +message KeyValue { + + // Key against which the value is stored. + string key = 1; + + // Version field is used for key-level versioning. + // For first write of key, version should be '0'. If the write succeeds, clients must increment + // their corresponding key version (client-side) by 1. + // The server increments key version (server-side) for every successful write, hence this + // client-side increment is required to ensure matching versions. These updated key versions should + // be used in subsequent PutObjectRequests for the keys. + int64 version = 2; + + // Object value in bytes which is stored (in put) and fetched (in get). + // Clients must encrypt this blob client-side before sending it over the wire to server in order + // to preserve privacy and security. + bytes value = 3; +} \ No newline at end of file