From c7d9a8e2dfecc932a857b4a041c3649d0b847001 Mon Sep 17 00:00:00 2001 From: robinbryce Date: Wed, 28 Feb 2024 09:29:52 +0000 Subject: [PATCH] adds the forestrie confirm message (#13) * Adds MerkleLogConfirm &Adds MerkleLogConfirmMessage * Adds MerkleLogUnequivocal & MerkleLogUnequivocalMessage (currently empty, just claims the pb ordinals we know we will need) * Removes MerkleLogCommitMongoDB * Removes log version, log epoch and massif index. All of which are harmful to our strategy for managing the backing storage. See https://github.com/datatrails/ epic-8120-scalable-proof-mechanisms/ blob/e38ca5e050356a47028b1d2b47bb6f02ff6c5c3c/mmr/ forestrie-mmrblobs.md#changing-the-massifheight-for-a-log for important context * Adds EventResponseJSONAPI, which largely duplicates EventResponse but importantly, supports decoding the format the *customer* will see from our apis. The duplication is unfortunate, but it means we can stop having ad-hoc soultions to this problem in all our go-lang based demo code. It also means the data can be reliably decoded with protojson. the generic json unmarshal fails to deal with 64 bit unsigned integers (and other numbers) correctly for example. Put EventResponseJSONAPI in its own file And add appropriate file comments for both eventresponse.proto and eventresponsejsonapi.proto AB#8336 Co-authored-by: Robin Bryce --- .../assets/v2/assets/eventresponse.proto | 30 +-- .../v2/assets/eventresponsejsonapi.proto | 191 ++++++++++++++++++ .../assets/v2/assets/merklelogentry.proto | 79 ++++---- 3 files changed, 246 insertions(+), 54 deletions(-) create mode 100644 datatrails-common-api/assets/v2/assets/eventresponsejsonapi.proto diff --git a/datatrails-common-api/assets/v2/assets/eventresponse.proto b/datatrails-common-api/assets/v2/assets/eventresponse.proto index 24f0066..8dbca76 100644 --- a/datatrails-common-api/assets/v2/assets/eventresponse.proto +++ b/datatrails-common-api/assets/v2/assets/eventresponse.proto @@ -1,5 +1,10 @@ // Maintainers, please refer to the style guide here: // https://developers.google.com/protocol-buffers/docs/style +// Provides our internal, native, event response data. The data that is returned +// over our apis doe NOT fit this shape. To marshal api data into a golang type, +// see EventResponseJSONAPI instead. +// The EventResponse message here MUST be kept up to date with EventResponseJSONAPI + syntax = "proto3"; package archivist.v2; option go_package="github.com/datatrails/go-datatrails-common-api-gen/assets/v2/assets;assets"; @@ -168,19 +173,18 @@ message EventResponse { max_length: 1024 }]; - // An event has exactly one proof mechanism. This field caputures the proof - // mechanism specific details supporting the trustworthyness of the event - // record. We anticipate at least two proof mechs: merkle_log and - // verkle_log. We use oneof to avoid repeating the scattering of randomly - // re-purposed fields we currently have for simple hash vs khipu. - + // An event has exactly one proof mechanism. On any event only the entry + // corresponding to the chosen proof mechanism will be present. (Note that + // onfof exposes us to to many tooling compatibility issues) // proof details for proof_mechanism MERKLE_LOG - MerkleLogEntry merklelog_entry = 19 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: - "verifiable merkle mmr log entry details", - // see https://github.com/grpc-ecosystem/grpc-gateway/blob/master/protoc-gen-openapiv2/options/openapiv2.proto - // for specific types. - type: OBJECT - }]; + oneof proof_details { + MerkleLogEntry merklelog_entry = 19 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: + "verifiable merkle mmr log entry details", + // see https://github.com/grpc-ecosystem/grpc-gateway/blob/master/protoc-gen-openapiv2/options/openapiv2.proto + // for specific types. + type: OBJECT + }]; + } } \ No newline at end of file diff --git a/datatrails-common-api/assets/v2/assets/eventresponsejsonapi.proto b/datatrails-common-api/assets/v2/assets/eventresponsejsonapi.proto new file mode 100644 index 0000000..f3634ee --- /dev/null +++ b/datatrails-common-api/assets/v2/assets/eventresponsejsonapi.proto @@ -0,0 +1,191 @@ +// Maintainers, please refer to the style guide here: +// https://developers.google.com/protocol-buffers/docs/style +// +// This file provides a type that can marshal the event data a customer sees +// from our apis. It is typically used for demo and api code in go-lang that +// needs to work with our api responses. +// +// NOTE: It MUST be kept up to date with the EventResponse message definition + +syntax = "proto3"; +package archivist.v2; +option go_package="github.com/datatrails/go-datatrails-common-api-gen/assets/v2/assets;assets"; +import "protoc-gen-openapiv2/options/annotations.proto"; +import "datatrails-common-api/assets/v2/assets/enums.proto"; +import "datatrails-common-api/assets/v2/assets/principal.proto"; +import "datatrails-common-api/assets/v2/assets/merklelogentry.proto"; + + +// EventResponseJSONAPI represents how the consumer of the events api sees the event data. +message EventResponseJSONAPI { + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { + json_schema: { + description: "This describes an Event." + } + example:"{ " + "\"identity\": \"assets/add30235-1424-4fda-840a-d5ef82c4c96f/events/11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000\", " + "\"asset_identity\": \"assets/add30235-1424-4fda-840a-d5ef82c4c96f\", " + "\"operation\": \"Record\", " + "\"behaviour\": \"RecordEvidence\", " + "\"event_attributes\": { " + " \"arc_attachments\": [" + " {" + " \"arc_attachment_identity\": \"blobs/1754b920-cf20-4d7e-9d36-9ed7d479744d\"," + " \"arc_hash_value\": \"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b\"," + " \"arc_display_name\": \"Picture from yesterday\"," + " \"arc_hash_alg\": \"sha256\"" + " }" + " ]" + "}, " + "\"asset_attributes\": { " + " \"arc_firmware_version\": \"3.2.1\", " + " \"arc_home_location_identity\": \"locations/42054f10-9952-4c10-a082-9fd0d10295ae\"" + "}, " + "\"timestamp_accepted\": \"2019-11-27T14:44:19Z\", " + "\"timestamp_declared\": \"2019-11-27T14:44:19Z\", " + "\"timestamp_committed\": \"2019-11-27T14:44:19Z\", " + "\"principal_declared\": { " + " \"issuer\": \"job.idp.server/1234\", \"subject\":\"bob@job\" " + " }, " + " \"principal_accepted\": { " + " \"issuer\": \"job.idp.server/1234\", \"subject\":\"bob@job\" " + "}, " + "\"confirmation_status\": \"CONFIRMED\", " + "\"block_number\": 12, " + "\"transaction_index\": 5, " + "\"transaction_id\": \"0x07569\", " + "\"tenant_identity\": \"tenant/8e0b600c-8234-43e4-860c-e95bdcd695a9\" " + "}" + }; + + // Relative Resource Name for the operation event + string identity = 1 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "identity of a event resource" + read_only: true + }]; + + // relative resource name for associated asset ( asset the operation is performed on - has to have specific behaviour enabled) + string asset_identity = 2 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "identity of a related asset resource `assets/11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000`" + read_only: true + }]; + + // map of event attributes. Specific behaviours define required and optional event attributes for each supported operation. + map event_attributes = 16 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "key value mapping of event attributes" + } + ]; + + // map of asset attributes. Specific behaviours define required and optional asset attributes. These attributes cause the corresponding attributes on the asset to be updated. + map asset_attributes = 17 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "key value mapping of asset attributes" + } + ]; + + // name of operation on this behviour + string operation = 4 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "The operation represented by the event. `Record`" + read_only: true + max_length: 4096 + }]; + + // name of this behaviour + string behaviour = 14 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "The behaviour used to create event. `RecordEvidence`" + read_only: true + max_length: 4096 + }]; + + // timestamp when operation was actually performed - if not provided will be set to timestamp_accepted + string timestamp_declared = 5 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "RFC 3339 time of event as declared by the user" + read_only: true + }]; + + // timestamp when system received operation request + string timestamp_accepted = 6 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "RFC 3339 time of event as recorded by the server" + read_only: true + }]; + + // timestamp for when the event was committed to a verifiable log + string timestamp_committed = 7 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "RFC 3339 time of event as recorded in verifiable storage" + read_only: true + }]; + + // principal information associated with event - if not provided will be set to principal_accepted + Principal principal_declared = 8 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "principal provided by the user" + read_only: true + // see https://github.com/grpc-ecosystem/grpc-gateway/blob/master/protoc-gen-openapiv2/options/openapiv2.proto + // for specific types. + type: OBJECT + }]; + + // principal logged into the system that performed the operation + Principal principal_accepted = 9 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "principal recorded by the server" + read_only: true + // see https://github.com/grpc-ecosystem/grpc-gateway/blob/master/protoc-gen-openapiv2/options/openapiv2.proto + // for specific types. + type: OBJECT + }]; + + // indicated if operation has been committed to the blockchain + string confirmation_status = 10 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "indicates if the event has been succesfully committed to the blockchain" + read_only: true + // see https://github.com/grpc-ecosystem/grpc-gateway/blob/master/protoc-gen-openapiv2/options/openapiv2.proto + // for specific types. + type: STRING + }]; + + // NOTICE: We expect to retire simple hash and then remove all the top level dlt fields. + + // hash of transaction committing this operation on blockchain + string transaction_id = 11 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "hash of the transaction as a hex string `0x11bf5b37e0b842e08dcfdc8c4aefc000`" + max_length: 4096 + }]; + + // block number of committing transaction + uint64 block_number = 12 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "number of block event was commited on" + read_only: true + }]; + + // transaction index of committing transaction + uint64 transaction_index = 13 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "index of event within commited block" + read_only: true + }]; + + // wallet address for the creator of this event + string from = 15 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "wallet address for the creator of this event" + read_only: true + }]; + + string tenant_identity = 18 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: + "Identity of the tenant the that created this event" + max_length: 1024 + }]; + + // An event has exactly one proof mechanism. This field caputures the proof + // mechanism specific details supporting the trustworthyness of the event + // record. We anticipate at least two proof mechs: merkle_log and + // verkle_log. We use oneof to avoid repeating the scattering of randomly + // re-purposed fields we currently have for simple hash vs khipu. + oneof proof_details { + // proof details for proof_mechanism MERKLE_LOG + MerkleLogEntry merklelog_entry = 19 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: + "verifiable merkle mmr log entry details" + max_length: 1024 + }]; + }; +} \ No newline at end of file diff --git a/datatrails-common-api/assets/v2/assets/merklelogentry.proto b/datatrails-common-api/assets/v2/assets/merklelogentry.proto index 580b80d..a4fb7f5 100644 --- a/datatrails-common-api/assets/v2/assets/merklelogentry.proto +++ b/datatrails-common-api/assets/v2/assets/merklelogentry.proto @@ -7,35 +7,32 @@ import "google/protobuf/timestamp.proto"; // MerkeLogCommit provides the log entry details for a single mmr leaf. message MerkleLogCommit { + /* The mmr index */ uint64 index = 1; - /* The mmr *leaf* index */ - uint64 leaf_index = 2; // TBD: this may be redundant. - /* time ordered and strictly unique per tenant. system wide - * unique with very reasonable operational assumptions. */ - fixed64 idtimestamp = 3; + + /* time ordered and strictly unique per tenant. system wide unique with very + * reasonable operational assumptions. prefixed with time epoch if len > 8 + * bytes (after conversion back from hex). */ + string idtimestamp = 2; } -message MerkleLogCommitMongoDB { +message MerkleLogConfirm { - // Note that should we ever have more than 2^63 events in a tenant log, the - // index and leaf_index fields will not persist to mongo. + // The following correspond to mmrblobs.MMRState + uint64 mmr_size = 1; - /* The mmr index */ - uint64 index = 1; - /* The mmr *leaf* index */ - uint64 leaf_index = 2; // TBD: this may be redundant. - - /* time ordered and strictly unique per tenant. system wide - * unique with very reasonable operational assumptions. - * - * EXPRESSED AS A 16 character padded hex string because mongo db does not - * support unsigned integers and we need all 64 bits to get a sensible epoch - * duration. - */ - string idtimestamp = 3; + bytes root = 2; + // The regular unix time the root was signed + int64 timestamp = 3; + // The idtimestamp of the last leaf under mmr_size. prefixed with time epoch if len > 8 bytes (after conversion back from hex) + string idtimestamp = 4; + // The signed merkle tree head state at mmr_size. Contains COSE Sign1 formatted message. + bytes signed_tree_head = 5; } +message MerkleLogUnequivocal { } + // The message sent from forestrie to avid notifying that the corresponding // event is commited to the tenants log. message MerkleLogCommitMessage { @@ -43,33 +40,33 @@ message MerkleLogCommitMessage { // The tenant identity and the event identity for the committed event. string tenant_identity = 1; string event_identity = 2; - /* The time portion of idtimestamp that contributed to the hash of the event - * (the idtimestamp is _also_ included. - * This must be copied into event.timestamp_committed when the saas db is updated */ - google.protobuf.Timestamp timestamp = 6; - - uint32 log_version = 3; - uint32 log_epoch = 4; - MerkleLogCommit commit = 5; + // The time portion of idtimestamp that contributed to the hash of the event + google.protobuf.Timestamp timestamp = 3; + + MerkleLogCommit commit = 4; } +message MerkleLogConfirmMessage { + + string tenant_identity = 1; + MerkleLogConfirm confirm = 2; +} + +message MerkleLogUnequivocalMessage { + string tenant_identity = 1; + MerkleLogUnequivocal unequivocal = 2; +} // The details stored in the SaaS db for a proof mech MERKLE_LOG commitment message MerkleLogEntry { - // The tenant log version and epoch when the log entry was created. - uint32 log_version = 1; - uint32 log_epoch = 2; - - // Event trust level commited fields. Note that we have to use a special - // message because mongo db does not support unsigned integers. If/when we - // move away from that, we can move this ordinal safely into a oneof - MerkleLogCommitMongoDB commit = 3; - - // TODO: Event trust level confirmed fields + // Event trust level COMMITTED details + MerkleLogCommit commit = 1; - // signature over tenant mmr root + // Event trust level CONFIRMED details + MerkleLogConfirm confirm = 2; - // TODO: Event trust level uniquivocal fields + // Event trust level UNEQUIVOCAL details + MerkleLogUnequivocal unequivocal = 3; } \ No newline at end of file