diff --git a/packages/eve-contracts/dist/generated/command-receipt.d.ts b/packages/eve-contracts/dist/generated/command-receipt.d.ts index c6b4005..1ee105b 100644 --- a/packages/eve-contracts/dist/generated/command-receipt.d.ts +++ b/packages/eve-contracts/dist/generated/command-receipt.d.ts @@ -14,6 +14,10 @@ export interface EveCommandReceipt { }[]; issuedAtUtc?: string; sourceVersion: number; + /** + * Canonical digest of the immutable invocation envelope finalized by this receipt. + */ + invocationHash?: string; navigation?: { verseId: string; authorityRuntimeId?: string; diff --git a/packages/eve-contracts/dist/generated/schemas.d.ts b/packages/eve-contracts/dist/generated/schemas.d.ts index 0a823e6..0f1f0a4 100644 --- a/packages/eve-contracts/dist/generated/schemas.d.ts +++ b/packages/eve-contracts/dist/generated/schemas.d.ts @@ -622,6 +622,11 @@ export declare const eveContractSchemas: { readonly type: "integer"; readonly minimum: 0; }; + readonly invocationHash: { + readonly type: "string"; + readonly minLength: 1; + readonly description: "Canonical digest of the immutable invocation envelope finalized by this receipt."; + }; readonly navigation: { readonly type: "object"; readonly required: readonly ["verseId", "surfaceId"]; @@ -630,6 +635,10 @@ export declare const eveContractSchemas: { readonly type: "string"; readonly minLength: 1; }; + readonly authorityRuntimeId: { + readonly type: "string"; + readonly minLength: 1; + }; readonly providerId: { readonly type: "string"; }; diff --git a/packages/eve-contracts/dist/generated/schemas.js b/packages/eve-contracts/dist/generated/schemas.js index 88188b2..403420e 100644 --- a/packages/eve-contracts/dist/generated/schemas.js +++ b/packages/eve-contracts/dist/generated/schemas.js @@ -746,6 +746,11 @@ export const eveContractSchemas = { "type": "integer", "minimum": 0 }, + "invocationHash": { + "type": "string", + "minLength": 1, + "description": "Canonical digest of the immutable invocation envelope finalized by this receipt." + }, "navigation": { "type": "object", "required": [ @@ -757,6 +762,10 @@ export const eveContractSchemas = { "type": "string", "minLength": 1 }, + "authorityRuntimeId": { + "type": "string", + "minLength": 1 + }, "providerId": { "type": "string" }, diff --git a/packages/eve-contracts/src/generated/command-receipt.ts b/packages/eve-contracts/src/generated/command-receipt.ts index dee7125..58fba85 100644 --- a/packages/eve-contracts/src/generated/command-receipt.ts +++ b/packages/eve-contracts/src/generated/command-receipt.ts @@ -16,6 +16,10 @@ diagnostics?: { }[] issuedAtUtc?: string sourceVersion: number +/** + * Canonical digest of the immutable invocation envelope finalized by this receipt. + */ +invocationHash?: string navigation?: { verseId: string authorityRuntimeId?: string diff --git a/packages/eve-contracts/src/generated/schemas.ts b/packages/eve-contracts/src/generated/schemas.ts index d54289d..bd01380 100644 --- a/packages/eve-contracts/src/generated/schemas.ts +++ b/packages/eve-contracts/src/generated/schemas.ts @@ -746,6 +746,11 @@ export const eveContractSchemas = { "type": "integer", "minimum": 0 }, + "invocationHash": { + "type": "string", + "minLength": 1, + "description": "Canonical digest of the immutable invocation envelope finalized by this receipt." + }, "navigation": { "type": "object", "required": [ diff --git a/packages/eve-contracts/test/contracts.test.mjs b/packages/eve-contracts/test/contracts.test.mjs index 3650a5f..8b78020 100644 --- a/packages/eve-contracts/test/contracts.test.mjs +++ b/packages/eve-contracts/test/contracts.test.mjs @@ -81,6 +81,7 @@ test("accepts canonical provider, surface, command, invocation, and receipt docu providerId: "voidbot.swarm", surfaceId: "voidbot.swarm", sourceVersion: 1, + invocationHash: "sha256:command-1", }).state, "reconciled"); }); diff --git a/packages/org.gamecult.eve.surface/GameCult.Eve.Surface.csproj b/packages/org.gamecult.eve.surface/GameCult.Eve.Surface.csproj index c3efc08..4d03a28 100644 --- a/packages/org.gamecult.eve.surface/GameCult.Eve.Surface.csproj +++ b/packages/org.gamecult.eve.surface/GameCult.Eve.Surface.csproj @@ -6,7 +6,7 @@ enable Portable Eve surface and provider contracts. GameCult.Eve.Surface - 0.3.3 + 0.3.4 GameCult MPL-2.0 https://github.com/GameCult/Eve diff --git a/packages/org.gamecult.eve.surface/Runtime/EveProviderDocuments.cs b/packages/org.gamecult.eve.surface/Runtime/EveProviderDocuments.cs index 7b4621a..e924a8d 100644 --- a/packages/org.gamecult.eve.surface/Runtime/EveProviderDocuments.cs +++ b/packages/org.gamecult.eve.surface/Runtime/EveProviderDocuments.cs @@ -366,8 +366,9 @@ namespace GameCult.Eve.Surface string message, string issuedAtUtc, long sourceVersion, - EveSurfaceNavigationTarget? navigation = null) - : this(SchemaId, receiptId, commandId, command, state, ownerRepo, authority, providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation) + EveSurfaceNavigationTarget? navigation = null, + string invocationHash = "") + : this(SchemaId, receiptId, commandId, command, state, ownerRepo, authority, providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation, invocationHash) { } @@ -385,7 +386,8 @@ namespace GameCult.Eve.Surface string message, string issuedAtUtc, long sourceVersion, - EveSurfaceNavigationTarget? navigation = null) + EveSurfaceNavigationTarget? navigation = null, + string invocationHash = "") { Schema = string.IsNullOrWhiteSpace(schema) ? SchemaId : schema; ReceiptId = receiptId ?? ""; @@ -400,6 +402,7 @@ namespace GameCult.Eve.Surface IssuedAtUtc = issuedAtUtc ?? ""; SourceVersion = sourceVersion; Navigation = navigation; + InvocationHash = invocationHash ?? ""; } [Key(0)] public string Schema { get; } @@ -415,6 +418,7 @@ namespace GameCult.Eve.Surface [Key(10)] public string IssuedAtUtc { get; } [Key(11)] public long SourceVersion { get; } [Key(12)] public EveSurfaceNavigationTarget? Navigation { get; } + [Key(13)] public string InvocationHash { get; } } /// @@ -424,7 +428,7 @@ namespace GameCult.Eve.Surface public sealed class EveCommandReceiptCompatibilityFormatter : IMessagePackFormatter { - private const int LegacyFieldCount = 13; + private const int LegacyFieldCount = 14; private const int RequiredAndScalarFieldCount = 12; public void Serialize( @@ -434,9 +438,9 @@ namespace GameCult.Eve.Surface { if (value == null) { writer.WriteNil(); return; } - writer.WriteMapHeader(value.Navigation == null - ? RequiredAndScalarFieldCount - : RequiredAndScalarFieldCount + 1); + var optionalFields = (value.Navigation == null ? 0 : 1) + + (string.IsNullOrWhiteSpace(value.InvocationHash) ? 0 : 1); + writer.WriteMapHeader(RequiredAndScalarFieldCount + optionalFields); Write(ref writer, "schema", value.Schema); Write(ref writer, "receiptId", value.ReceiptId); Write(ref writer, "commandId", value.CommandId); @@ -454,6 +458,8 @@ namespace GameCult.Eve.Surface writer.Write("navigation"); WriteNavigation(ref writer, value.Navigation); } + if (!string.IsNullOrWhiteSpace(value.InvocationHash)) + Write(ref writer, "invocationHash", value.InvocationHash); } public EveCommandReceiptDocument? Deserialize( @@ -483,10 +489,11 @@ namespace GameCult.Eve.Surface var issuedAtUtc = ReadString(ref reader, fields, 10); var sourceVersion = fields > 11 ? reader.ReadInt64() : 0; var navigation = fields > 12 ? ReadNavigation(ref reader, options) : null; + var invocationHash = ReadString(ref reader, fields, 13); for (var index = LegacyFieldCount; index < fields; index++) reader.Skip(); return new EveCommandReceiptDocument( schema, receiptId, commandId, command, state, ownerRepo, authority, - providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation); + providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation, invocationHash); } finally { reader.Depth--; } } @@ -511,6 +518,7 @@ namespace GameCult.Eve.Surface var issuedAtUtc = ""; long sourceVersion = 0; EveSurfaceNavigationTarget? navigation = null; + var invocationHash = ""; var fields = reader.ReadMapHeader(); for (var index = 0; index < fields; index++) { @@ -529,13 +537,14 @@ namespace GameCult.Eve.Surface case "issuedAtUtc": issuedAtUtc = ReadString(ref reader); break; case "sourceVersion": sourceVersion = reader.ReadInt64(); break; case "navigation": navigation = ReadNavigation(ref reader, options); break; + case "invocationHash": invocationHash = ReadString(ref reader); break; default: reader.Skip(); break; } } return new EveCommandReceiptDocument( schema, receiptId, commandId, command, state, ownerRepo, authority, - providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation); + providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation, invocationHash); } finally { reader.Depth--; } } diff --git a/packages/org.gamecult.eve.surface/package.json b/packages/org.gamecult.eve.surface/package.json index a2badf5..939a870 100644 --- a/packages/org.gamecult.eve.surface/package.json +++ b/packages/org.gamecult.eve.surface/package.json @@ -1,7 +1,7 @@ { "name": "org.gamecult.eve.surface", "displayName": "Eve Surface Contract", - "version": "0.3.3", + "version": "0.3.4", "unity": "2021.3", "description": "Renderer-neutral C# contracts and builders for gamecult.eve.surface.v1 documents.", "dependencies": { diff --git a/schemas/gamecult.eve.command_receipt.v1.schema.json b/schemas/gamecult.eve.command_receipt.v1.schema.json index f0c3091..b668e95 100644 --- a/schemas/gamecult.eve.command_receipt.v1.schema.json +++ b/schemas/gamecult.eve.command_receipt.v1.schema.json @@ -69,6 +69,11 @@ "type": "integer", "minimum": 0 }, + "invocationHash": { + "type": "string", + "minLength": 1, + "description": "Canonical digest of the immutable invocation envelope finalized by this receipt." + }, "navigation": { "type": "object", "required": [ diff --git a/tests/GameCult.Eve.Surface.Serialization.Tests/EveSurfaceSerializationCompatibilityTests.cs b/tests/GameCult.Eve.Surface.Serialization.Tests/EveSurfaceSerializationCompatibilityTests.cs index 4c44d62..30156c3 100644 --- a/tests/GameCult.Eve.Surface.Serialization.Tests/EveSurfaceSerializationCompatibilityTests.cs +++ b/tests/GameCult.Eve.Surface.Serialization.Tests/EveSurfaceSerializationCompatibilityTests.cs @@ -209,7 +209,8 @@ public sealed class EveSurfaceSerializationCompatibilityTests "aetheria.pilot", "interactive-world", new[] { "cultnet+tcp://odin.gamecult.example:3076" }, - "commander-daemon")); + "commander-daemon"), + "sha256:launch-envelope"); var bytes = MessagePackSerializer.Serialize(document, Options); var reader = new MessagePackReader(bytes); @@ -217,7 +218,7 @@ public sealed class EveSurfaceSerializationCompatibilityTests var restored = MessagePackSerializer.Deserialize(bytes, Options); Assert.That(reader.NextMessagePackType, Is.EqualTo(MessagePackType.Map)); - Assert.That(reader.ReadMapHeader(), Is.EqualTo(13)); + Assert.That(reader.ReadMapHeader(), Is.EqualTo(14)); Assert.That(wireJson.RootElement.GetProperty("navigation").ValueKind, Is.EqualTo(JsonValueKind.Object)); Assert.That(restored.Navigation, Is.Not.Null); Assert.That(restored.Navigation!.VerseId, Is.EqualTo("gamecult.aetheria")); @@ -225,6 +226,7 @@ public sealed class EveSurfaceSerializationCompatibilityTests Assert.That(restored.Navigation.SurfaceId, Is.EqualTo("aetheria.pilot")); Assert.That(restored.Navigation.SurfaceKind, Is.EqualTo("interactive-world")); Assert.That(restored.Navigation.RendezvousEndpoints, Is.EqualTo(new[] { "cultnet+tcp://odin.gamecult.example:3076" })); + Assert.That(restored.InvocationHash, Is.EqualTo("sha256:launch-envelope")); } [Test] @@ -263,6 +265,7 @@ public sealed class EveSurfaceSerializationCompatibilityTests Assert.That(restored.Navigation, Is.Not.Null); Assert.That(restored.Navigation!.SurfaceId, Is.EqualTo("aetheria.pilot")); Assert.That(restored.Navigation.AuthorityRuntimeId, Is.Empty); + Assert.That(restored.InvocationHash, Is.Empty); }); } @@ -289,7 +292,9 @@ public sealed class EveSurfaceSerializationCompatibilityTests Assert.Multiple(() => { Assert.That(wireJson.RootElement.TryGetProperty("navigation", out _), Is.False); + Assert.That(wireJson.RootElement.TryGetProperty("invocationHash", out _), Is.False); Assert.That(restored.Navigation, Is.Null); + Assert.That(restored.InvocationHash, Is.Empty); }); } diff --git a/tests/GameCult.Eve.Surface.Serialization.Tests/GameCult.Eve.Surface.Serialization.Tests.csproj b/tests/GameCult.Eve.Surface.Serialization.Tests/GameCult.Eve.Surface.Serialization.Tests.csproj index c60c305..5a415f5 100644 --- a/tests/GameCult.Eve.Surface.Serialization.Tests/GameCult.Eve.Surface.Serialization.Tests.csproj +++ b/tests/GameCult.Eve.Surface.Serialization.Tests/GameCult.Eve.Surface.Serialization.Tests.csproj @@ -16,6 +16,7 @@ +