//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `vtc/elections/certify`. Version: `0.1`.
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
/// Error types.
pub mod error {
/// Error from a `TryFrom` or `FromStr` implementation.
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
///A board's tree head (RFC 9162 §2.1). A head is signed by being carried in a document the ballot box signs; whoever keeps a head keeps that whole document.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "BoardHead",
/// "description": "A board's tree head (RFC 9162 §2.1). A head is signed by being carried in a document the ballot box signs; whoever keeps a head keeps that whole document.",
/// "type": "object",
/// "required": [
/// "electionId",
/// "issuedAt",
/// "rootHash",
/// "treeSize"
/// ],
/// "properties": {
/// "electionId": {
/// "$ref": "#/definitions/ElectionId"
/// },
/// "issuedAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "rootHash": {
/// "description": "The RFC 9162 Merkle tree hash (SHA-256) of the first `treeSize` entries, as a multihash.",
/// "$ref": "#/definitions/DigestMultibase"
/// },
/// "treeSize": {
/// "type": "integer",
/// "minimum": 0.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct BoardHead {
#[serde(rename = "electionId")]
pub election_id: ElectionId,
#[serde(rename = "issuedAt")]
pub issued_at: ::chrono::DateTime<::chrono::offset::Utc>,
///The RFC 9162 Merkle tree hash (SHA-256) of the first `treeSize` entries, as a multihash.
#[serde(rename = "rootHash")]
pub root_hash: DigestMultibase,
#[serde(rename = "treeSize")]
pub tree_size: u64,
}
impl BoardHead {
pub fn builder() -> builder::BoardHead {
Default::default()
}
}
/**
A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.
Multihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.
This definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.
Restricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that "interoperability is not guaranteed between implementations using such values", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "DigestMultibase",
/// "description": "A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\n\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\n\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\n\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \"interoperability is not guaranteed between implementations using such values\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.",
/// "examples": [
/// "zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
/// ],
/// "type": "string",
/// "minLength": 16,
/// "pattern": "^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DigestMultibase(::std::string::String);
impl ::std::ops::Deref for DigestMultibase {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DigestMultibase> for ::std::string::String {
fn from(value: DigestMultibase) -> Self {
value.0
}
}
impl ::std::str::FromStr for DigestMultibase {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 16usize {
return Err("shorter than 16 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\"".into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for DigestMultibase {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///An election's identifier, unique within its community. A closed alphabet: it appears in the ticket class label `voter/<electionId>` and is framed into every hash a cast is bound to.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ElectionId",
/// "description": "An election's identifier, unique within its community. A closed alphabet: it appears in the ticket class label `voter/<electionId>` and is framed into every hash a cast is bound to.",
/// "type": "string",
/// "pattern": "^[A-Za-z0-9._-]{1,128}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ElectionId(::std::string::String);
impl ::std::ops::Deref for ElectionId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ElectionId> for ::std::string::String {
fn from(value: ElectionId) -> Self {
value.0
}
}
impl ::std::str::FromStr for ElectionId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[A-Za-z0-9._-]{1,128}$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[A-Za-z0-9._-]{1,128}$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ElectionId {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ElectionId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ElectionId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ElectionId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///An election's result, computable by anyone who can read its board from the board and the registration alone (VTI-VOT-070).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ElectionResult",
/// "description": "An election's result, computable by anyone who can read its board from the board and the registration alone (VTI-VOT-070).",
/// "type": "object",
/// "required": [
/// "ballotsCounted",
/// "electionId",
/// "head",
/// "manifestDigest",
/// "questions"
/// ],
/// "properties": {
/// "ballotsCounted": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "electionId": {
/// "$ref": "#/definitions/ElectionId"
/// },
/// "head": {
/// "description": "The head of the board as it stood at close: the result counts exactly its entries.",
/// "$ref": "#/definitions/BoardHead"
/// },
/// "manifestDigest": {
/// "$ref": "#/definitions/DigestMultibase"
/// },
/// "questions": {
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/QuestionResult"
/// }
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct ElectionResult {
#[serde(rename = "ballotsCounted")]
pub ballots_counted: u64,
#[serde(rename = "electionId")]
pub election_id: ElectionId,
///The head of the board as it stood at close: the result counts exactly its entries.
pub head: BoardHead,
#[serde(rename = "manifestDigest")]
pub manifest_digest: DigestMultibase,
pub questions: ::std::vec::Vec<QuestionResult>,
}
impl ElectionResult {
pub fn builder() -> builder::ElectionResult {
Default::default()
}
}
///Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ext",
/// "description": "Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.",
/// "type": "object",
/// "minProperties": 1,
/// "additionalProperties": true,
/// "propertyNames": {
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct Ext(pub ::std::collections::HashMap<ExtKey, ::serde_json::Value>);
impl ::std::ops::Deref for Ext {
type Target = ::std::collections::HashMap<ExtKey, ::serde_json::Value>;
fn deref(&self) -> &::std::collections::HashMap<ExtKey, ::serde_json::Value> {
&self.0
}
}
impl ::std::convert::From<Ext> for ::std::collections::HashMap<ExtKey, ::serde_json::Value> {
fn from(value: Ext) -> Self {
value.0
}
}
impl ::std::convert::From<::std::collections::HashMap<ExtKey, ::serde_json::Value>> for Ext {
fn from(value: ::std::collections::HashMap<ExtKey, ::serde_json::Value>) -> Self {
Self(value)
}
}
///`ExtKey`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ExtKey(::std::string::String);
impl ::std::ops::Deref for ExtKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ExtKey> for ::std::string::String {
fn from(value: ExtKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for ExtKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ExtKey {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A question or option key, unique within its manifest or question.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Key",
/// "description": "A question or option key, unique within its manifest or question.",
/// "type": "string",
/// "pattern": "^[A-Za-z0-9._-]{1,64}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Key(::std::string::String);
impl ::std::ops::Deref for Key {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Key> for ::std::string::String {
fn from(value: Key) -> Self {
value.0
}
}
impl ::std::str::FromStr for Key {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[A-Za-z0-9._-]{1,64}$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[A-Za-z0-9._-]{1,64}$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for Key {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for Key {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for Key {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for Key {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///An administrator has the community verify a tallied election's board for itself and certify its result. The outer document members (id, type, issuer, recipient, issuedAt, expiresAt, proof) are owned by the framework — SPEC §6.3.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/vtc/elections/certify/0.1",
/// "title": "Payload",
/// "description": "An administrator has the community verify a tallied election's board for itself and certify its result. The outer document members (id, type, issuer, recipient, issuedAt, expiresAt, proof) are owned by the framework — SPEC §6.3.",
/// "type": "object",
/// "required": [
/// "electionId"
/// ],
/// "properties": {
/// "auditAttestations": {
/// "description": "Attestations from auditors the manifest names in `board.auditors`, to be certified with the result.",
/// "type": "array",
/// "items": {
/// "description": "A verifiable credential, as its auditor issued it, attesting the auditor's own recount of this election's board (VTI-VOT-073). Kept exactly as received.",
/// "type": "object"
/// },
/// "maxItems": 32
/// },
/// "electionId": {
/// "$ref": "#/definitions/ElectionId"
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
///Attestations from auditors the manifest names in `board.auditors`, to be certified with the result.
#[serde(
rename = "auditAttestations",
default,
skip_serializing_if = "::std::vec::Vec::is_empty"
)]
pub audit_attestations:
::std::vec::Vec<::serde_json::Map<::std::string::String, ::serde_json::Value>>,
#[serde(rename = "electionId")]
pub election_id: ElectionId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///One question's totals. `choice`: selections per option. `ranking`: per option, how many ballots placed it at each position (index 0 = first). `score`: per option, the sum and the number of ballots that scored it. `text`: the answers, in board order, withheld (`withheld: true`) below the manifest's `textReleaseThreshold`. `abstained` counts ballots that left the question unanswered.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "QuestionResult",
/// "description": "One question's totals. `choice`: selections per option. `ranking`: per option, how many ballots placed it at each position (index 0 = first). `score`: per option, the sum and the number of ballots that scored it. `text`: the answers, in board order, withheld (`withheld: true`) below the manifest's `textReleaseThreshold`. `abstained` counts ballots that left the question unanswered.",
/// "type": "object",
/// "required": [
/// "abstained",
/// "question"
/// ],
/// "properties": {
/// "abstained": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "choice": {
/// "type": "object",
/// "additionalProperties": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "propertyNames": {
/// "$ref": "#/definitions/Key"
/// }
/// },
/// "question": {
/// "$ref": "#/definitions/Key"
/// },
/// "ranking": {
/// "type": "object",
/// "additionalProperties": {
/// "type": "array",
/// "items": {
/// "type": "integer",
/// "minimum": 0.0
/// }
/// },
/// "propertyNames": {
/// "$ref": "#/definitions/Key"
/// }
/// },
/// "score": {
/// "type": "object",
/// "additionalProperties": {
/// "type": "object",
/// "required": [
/// "count",
/// "sum"
/// ],
/// "properties": {
/// "count": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "sum": {
/// "type": "integer"
/// }
/// },
/// "additionalProperties": false
/// },
/// "propertyNames": {
/// "$ref": "#/definitions/Key"
/// }
/// },
/// "text": {
/// "type": "array",
/// "items": {
/// "type": "string",
/// "maxLength": 4096
/// }
/// },
/// "withheld": {
/// "type": "boolean"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct QuestionResult {
pub abstained: u64,
#[serde(
default,
skip_serializing_if = ":: std :: collections :: HashMap::is_empty"
)]
pub choice: ::std::collections::HashMap<Key, u64>,
pub question: Key,
#[serde(
default,
skip_serializing_if = ":: std :: collections :: HashMap::is_empty"
)]
pub ranking: ::std::collections::HashMap<Key, ::std::vec::Vec<u64>>,
#[serde(
default,
skip_serializing_if = ":: std :: collections :: HashMap::is_empty"
)]
pub score: ::std::collections::HashMap<Key, QuestionResultScoreValue>,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub text: ::std::vec::Vec<QuestionResultTextItem>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub withheld: ::std::option::Option<bool>,
}
impl QuestionResult {
pub fn builder() -> builder::QuestionResult {
Default::default()
}
}
///`QuestionResultScoreValue`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "count",
/// "sum"
/// ],
/// "properties": {
/// "count": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "sum": {
/// "type": "integer"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct QuestionResultScoreValue {
pub count: u64,
pub sum: i64,
}
impl QuestionResultScoreValue {
pub fn builder() -> builder::QuestionResultScoreValue {
Default::default()
}
}
///`QuestionResultTextItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 4096
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct QuestionResultTextItem(::std::string::String);
impl ::std::ops::Deref for QuestionResultTextItem {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<QuestionResultTextItem> for ::std::string::String {
fn from(value: QuestionResultTextItem) -> Self {
value.0
}
}
impl ::std::str::FromStr for QuestionResultTextItem {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 4096usize {
return Err("longer than 4096 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for QuestionResultTextItem {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for QuestionResultTextItem {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for QuestionResultTextItem {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for QuestionResultTextItem {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Success response to vtc/elections/certify/0.1: the result the community verified and certified.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "Success response to vtc/elections/certify/0.1: the result the community verified and certified.",
/// "type": "object",
/// "required": [
/// "auditAttestations",
/// "certifiedAt",
/// "electionId",
/// "head",
/// "result"
/// ],
/// "properties": {
/// "auditAttestations": {
/// "description": "The attestations certified with the result; empty when none was given.",
/// "type": "array",
/// "items": {
/// "description": "An auditor's attestation the community verified, as received.",
/// "type": "object"
/// },
/// "maxItems": 32
/// },
/// "certifiedAt": {
/// "type": "string",
/// "format": "date-time",
/// "maxLength": 64
/// },
/// "electionId": {
/// "$ref": "#/definitions/ElectionId"
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "head": {
/// "description": "The board head the community verified: the head as it stood at close, which the result counts exactly.",
/// "$ref": "#/definitions/BoardHead"
/// },
/// "result": {
/// "description": "The result the community recomputed from the board, equal to the ballot box's.",
/// "$ref": "#/definitions/ElectionResult"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
///The attestations certified with the result; empty when none was given.
#[serde(rename = "auditAttestations")]
pub audit_attestations:
::std::vec::Vec<::serde_json::Map<::std::string::String, ::serde_json::Value>>,
#[serde(rename = "certifiedAt")]
pub certified_at: ::chrono::DateTime<::chrono::offset::Utc>,
#[serde(rename = "electionId")]
pub election_id: ElectionId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///The board head the community verified: the head as it stood at close, which the result counts exactly.
pub head: BoardHead,
///The result the community recomputed from the board, equal to the ballot box's.
pub result: ElectionResult,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct BoardHead {
election_id: ::std::result::Result<super::ElectionId, ::std::string::String>,
issued_at:
::std::result::Result<::chrono::DateTime<::chrono::offset::Utc>, ::std::string::String>,
root_hash: ::std::result::Result<super::DigestMultibase, ::std::string::String>,
tree_size: ::std::result::Result<u64, ::std::string::String>,
}
impl ::std::default::Default for BoardHead {
fn default() -> Self {
Self {
election_id: Err("no value supplied for election_id".to_string()),
issued_at: Err("no value supplied for issued_at".to_string()),
root_hash: Err("no value supplied for root_hash".to_string()),
tree_size: Err("no value supplied for tree_size".to_string()),
}
}
}
impl BoardHead {
pub fn election_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ElectionId>,
T::Error: ::std::fmt::Display,
{
self.election_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for election_id: {e}"));
self
}
pub fn issued_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>,
T::Error: ::std::fmt::Display,
{
self.issued_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for issued_at: {e}"));
self
}
pub fn root_hash<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::DigestMultibase>,
T::Error: ::std::fmt::Display,
{
self.root_hash = value
.try_into()
.map_err(|e| format!("error converting supplied value for root_hash: {e}"));
self
}
pub fn tree_size<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<u64>,
T::Error: ::std::fmt::Display,
{
self.tree_size = value
.try_into()
.map_err(|e| format!("error converting supplied value for tree_size: {e}"));
self
}
}
impl ::std::convert::TryFrom<BoardHead> for super::BoardHead {
type Error = super::error::ConversionError;
fn try_from(
value: BoardHead,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
election_id: value.election_id?,
issued_at: value.issued_at?,
root_hash: value.root_hash?,
tree_size: value.tree_size?,
})
}
}
impl ::std::convert::From<super::BoardHead> for BoardHead {
fn from(value: super::BoardHead) -> Self {
Self {
election_id: Ok(value.election_id),
issued_at: Ok(value.issued_at),
root_hash: Ok(value.root_hash),
tree_size: Ok(value.tree_size),
}
}
}
#[derive(Clone, Debug)]
pub struct ElectionResult {
ballots_counted: ::std::result::Result<u64, ::std::string::String>,
election_id: ::std::result::Result<super::ElectionId, ::std::string::String>,
head: ::std::result::Result<super::BoardHead, ::std::string::String>,
manifest_digest: ::std::result::Result<super::DigestMultibase, ::std::string::String>,
questions:
::std::result::Result<::std::vec::Vec<super::QuestionResult>, ::std::string::String>,
}
impl ::std::default::Default for ElectionResult {
fn default() -> Self {
Self {
ballots_counted: Err("no value supplied for ballots_counted".to_string()),
election_id: Err("no value supplied for election_id".to_string()),
head: Err("no value supplied for head".to_string()),
manifest_digest: Err("no value supplied for manifest_digest".to_string()),
questions: Err("no value supplied for questions".to_string()),
}
}
}
impl ElectionResult {
pub fn ballots_counted<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<u64>,
T::Error: ::std::fmt::Display,
{
self.ballots_counted = value
.try_into()
.map_err(|e| format!("error converting supplied value for ballots_counted: {e}"));
self
}
pub fn election_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ElectionId>,
T::Error: ::std::fmt::Display,
{
self.election_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for election_id: {e}"));
self
}
pub fn head<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::BoardHead>,
T::Error: ::std::fmt::Display,
{
self.head = value
.try_into()
.map_err(|e| format!("error converting supplied value for head: {e}"));
self
}
pub fn manifest_digest<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::DigestMultibase>,
T::Error: ::std::fmt::Display,
{
self.manifest_digest = value
.try_into()
.map_err(|e| format!("error converting supplied value for manifest_digest: {e}"));
self
}
pub fn questions<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::QuestionResult>>,
T::Error: ::std::fmt::Display,
{
self.questions = value
.try_into()
.map_err(|e| format!("error converting supplied value for questions: {e}"));
self
}
}
impl ::std::convert::TryFrom<ElectionResult> for super::ElectionResult {
type Error = super::error::ConversionError;
fn try_from(
value: ElectionResult,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
ballots_counted: value.ballots_counted?,
election_id: value.election_id?,
head: value.head?,
manifest_digest: value.manifest_digest?,
questions: value.questions?,
})
}
}
impl ::std::convert::From<super::ElectionResult> for ElectionResult {
fn from(value: super::ElectionResult) -> Self {
Self {
ballots_counted: Ok(value.ballots_counted),
election_id: Ok(value.election_id),
head: Ok(value.head),
manifest_digest: Ok(value.manifest_digest),
questions: Ok(value.questions),
}
}
}
#[derive(Clone, Debug)]
pub struct Payload {
audit_attestations: ::std::result::Result<
::std::vec::Vec<::serde_json::Map<::std::string::String, ::serde_json::Value>>,
::std::string::String,
>,
election_id: ::std::result::Result<super::ElectionId, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
audit_attestations: Ok(Default::default()),
election_id: Err("no value supplied for election_id".to_string()),
ext: Ok(Default::default()),
}
}
}
impl Payload {
pub fn audit_attestations<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::vec::Vec<::serde_json::Map<::std::string::String, ::serde_json::Value>>,
>,
T::Error: ::std::fmt::Display,
{
self.audit_attestations = value.try_into().map_err(|e| {
format!("error converting supplied value for audit_attestations: {e}")
});
self
}
pub fn election_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ElectionId>,
T::Error: ::std::fmt::Display,
{
self.election_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for election_id: {e}"));
self
}
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
}
impl ::std::convert::TryFrom<Payload> for super::Payload {
type Error = super::error::ConversionError;
fn try_from(value: Payload) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
audit_attestations: value.audit_attestations?,
election_id: value.election_id?,
ext: value.ext?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
audit_attestations: Ok(value.audit_attestations),
election_id: Ok(value.election_id),
ext: Ok(value.ext),
}
}
}
#[derive(Clone, Debug)]
pub struct QuestionResult {
abstained: ::std::result::Result<u64, ::std::string::String>,
choice: ::std::result::Result<
::std::collections::HashMap<super::Key, u64>,
::std::string::String,
>,
question: ::std::result::Result<super::Key, ::std::string::String>,
ranking: ::std::result::Result<
::std::collections::HashMap<super::Key, ::std::vec::Vec<u64>>,
::std::string::String,
>,
score: ::std::result::Result<
::std::collections::HashMap<super::Key, super::QuestionResultScoreValue>,
::std::string::String,
>,
text: ::std::result::Result<
::std::vec::Vec<super::QuestionResultTextItem>,
::std::string::String,
>,
withheld: ::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
}
impl ::std::default::Default for QuestionResult {
fn default() -> Self {
Self {
abstained: Err("no value supplied for abstained".to_string()),
choice: Ok(Default::default()),
question: Err("no value supplied for question".to_string()),
ranking: Ok(Default::default()),
score: Ok(Default::default()),
text: Ok(Default::default()),
withheld: Ok(Default::default()),
}
}
}
impl QuestionResult {
pub fn abstained<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<u64>,
T::Error: ::std::fmt::Display,
{
self.abstained = value
.try_into()
.map_err(|e| format!("error converting supplied value for abstained: {e}"));
self
}
pub fn choice<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::collections::HashMap<super::Key, u64>>,
T::Error: ::std::fmt::Display,
{
self.choice = value
.try_into()
.map_err(|e| format!("error converting supplied value for choice: {e}"));
self
}
pub fn question<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Key>,
T::Error: ::std::fmt::Display,
{
self.question = value
.try_into()
.map_err(|e| format!("error converting supplied value for question: {e}"));
self
}
pub fn ranking<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::collections::HashMap<super::Key, ::std::vec::Vec<u64>>,
>,
T::Error: ::std::fmt::Display,
{
self.ranking = value
.try_into()
.map_err(|e| format!("error converting supplied value for ranking: {e}"));
self
}
pub fn score<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::collections::HashMap<super::Key, super::QuestionResultScoreValue>,
>,
T::Error: ::std::fmt::Display,
{
self.score = value
.try_into()
.map_err(|e| format!("error converting supplied value for score: {e}"));
self
}
pub fn text<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::QuestionResultTextItem>>,
T::Error: ::std::fmt::Display,
{
self.text = value
.try_into()
.map_err(|e| format!("error converting supplied value for text: {e}"));
self
}
pub fn withheld<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.withheld = value
.try_into()
.map_err(|e| format!("error converting supplied value for withheld: {e}"));
self
}
}
impl ::std::convert::TryFrom<QuestionResult> for super::QuestionResult {
type Error = super::error::ConversionError;
fn try_from(
value: QuestionResult,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
abstained: value.abstained?,
choice: value.choice?,
question: value.question?,
ranking: value.ranking?,
score: value.score?,
text: value.text?,
withheld: value.withheld?,
})
}
}
impl ::std::convert::From<super::QuestionResult> for QuestionResult {
fn from(value: super::QuestionResult) -> Self {
Self {
abstained: Ok(value.abstained),
choice: Ok(value.choice),
question: Ok(value.question),
ranking: Ok(value.ranking),
score: Ok(value.score),
text: Ok(value.text),
withheld: Ok(value.withheld),
}
}
}
#[derive(Clone, Debug)]
pub struct QuestionResultScoreValue {
count: ::std::result::Result<u64, ::std::string::String>,
sum: ::std::result::Result<i64, ::std::string::String>,
}
impl ::std::default::Default for QuestionResultScoreValue {
fn default() -> Self {
Self {
count: Err("no value supplied for count".to_string()),
sum: Err("no value supplied for sum".to_string()),
}
}
}
impl QuestionResultScoreValue {
pub fn count<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<u64>,
T::Error: ::std::fmt::Display,
{
self.count = value
.try_into()
.map_err(|e| format!("error converting supplied value for count: {e}"));
self
}
pub fn sum<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<i64>,
T::Error: ::std::fmt::Display,
{
self.sum = value
.try_into()
.map_err(|e| format!("error converting supplied value for sum: {e}"));
self
}
}
impl ::std::convert::TryFrom<QuestionResultScoreValue> for super::QuestionResultScoreValue {
type Error = super::error::ConversionError;
fn try_from(
value: QuestionResultScoreValue,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
count: value.count?,
sum: value.sum?,
})
}
}
impl ::std::convert::From<super::QuestionResultScoreValue> for QuestionResultScoreValue {
fn from(value: super::QuestionResultScoreValue) -> Self {
Self {
count: Ok(value.count),
sum: Ok(value.sum),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
audit_attestations: ::std::result::Result<
::std::vec::Vec<::serde_json::Map<::std::string::String, ::serde_json::Value>>,
::std::string::String,
>,
certified_at:
::std::result::Result<::chrono::DateTime<::chrono::offset::Utc>, ::std::string::String>,
election_id: ::std::result::Result<super::ElectionId, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
head: ::std::result::Result<super::BoardHead, ::std::string::String>,
result: ::std::result::Result<super::ElectionResult, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
audit_attestations: Err("no value supplied for audit_attestations".to_string()),
certified_at: Err("no value supplied for certified_at".to_string()),
election_id: Err("no value supplied for election_id".to_string()),
ext: Ok(Default::default()),
head: Err("no value supplied for head".to_string()),
result: Err("no value supplied for result".to_string()),
}
}
}
impl Response {
pub fn audit_attestations<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::vec::Vec<::serde_json::Map<::std::string::String, ::serde_json::Value>>,
>,
T::Error: ::std::fmt::Display,
{
self.audit_attestations = value.try_into().map_err(|e| {
format!("error converting supplied value for audit_attestations: {e}")
});
self
}
pub fn certified_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>,
T::Error: ::std::fmt::Display,
{
self.certified_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for certified_at: {e}"));
self
}
pub fn election_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ElectionId>,
T::Error: ::std::fmt::Display,
{
self.election_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for election_id: {e}"));
self
}
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn head<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::BoardHead>,
T::Error: ::std::fmt::Display,
{
self.head = value
.try_into()
.map_err(|e| format!("error converting supplied value for head: {e}"));
self
}
pub fn result<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ElectionResult>,
T::Error: ::std::fmt::Display,
{
self.result = value
.try_into()
.map_err(|e| format!("error converting supplied value for result: {e}"));
self
}
}
impl ::std::convert::TryFrom<Response> for super::Response {
type Error = super::error::ConversionError;
fn try_from(value: Response) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
audit_attestations: value.audit_attestations?,
certified_at: value.certified_at?,
election_id: value.election_id?,
ext: value.ext?,
head: value.head?,
result: value.result?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
audit_attestations: Ok(value.audit_attestations),
certified_at: Ok(value.certified_at),
election_id: Ok(value.election_id),
ext: Ok(value.ext),
head: Ok(value.head),
result: Ok(value.result),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vtc/elections/certify/0.1";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"BoardHead\": {\n \"additionalProperties\": false,\n \"description\": \"A board's tree head (RFC 9162 §2.1). A head is signed by being carried in a document the ballot box signs; whoever keeps a head keeps that whole document.\",\n \"properties\": {\n \"electionId\": {\n \"$ref\": \"#/$defs/ElectionId\"\n },\n \"issuedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"rootHash\": {\n \"$ref\": \"#/$defs/DigestMultibase\",\n \"description\": \"The RFC 9162 Merkle tree hash (SHA-256) of the first `treeSize` entries, as a multihash.\"\n },\n \"treeSize\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"electionId\",\n \"treeSize\",\n \"rootHash\",\n \"issuedAt\"\n ],\n \"title\": \"BoardHead\",\n \"type\": \"object\"\n },\n \"DigestMultibase\": {\n \"description\": \"A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\\n\\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\\n\\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\\n\\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \\\"interoperability is not guaranteed between implementations using such values\\\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.\",\n \"examples\": [\n \"zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR\"\n ],\n \"minLength\": 16,\n \"pattern\": \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\n \"title\": \"DigestMultibase\",\n \"type\": \"string\"\n },\n \"ElectionId\": {\n \"description\": \"An election's identifier, unique within its community. A closed alphabet: it appears in the ticket class label `voter/<electionId>` and is framed into every hash a cast is bound to.\",\n \"pattern\": \"^[A-Za-z0-9._-]{1,128}$\",\n \"title\": \"ElectionId\",\n \"type\": \"string\"\n },\n \"ElectionResult\": {\n \"additionalProperties\": false,\n \"description\": \"An election's result, computable by anyone who can read its board from the board and the registration alone (VTI-VOT-070).\",\n \"properties\": {\n \"ballotsCounted\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"electionId\": {\n \"$ref\": \"#/$defs/ElectionId\"\n },\n \"head\": {\n \"$ref\": \"#/$defs/BoardHead\",\n \"description\": \"The head of the board as it stood at close: the result counts exactly its entries.\"\n },\n \"manifestDigest\": {\n \"$ref\": \"#/$defs/DigestMultibase\"\n },\n \"questions\": {\n \"items\": {\n \"$ref\": \"#/$defs/QuestionResult\"\n },\n \"type\": \"array\"\n }\n },\n \"required\": [\n \"electionId\",\n \"manifestDigest\",\n \"ballotsCounted\",\n \"questions\",\n \"head\"\n ],\n \"title\": \"ElectionResult\",\n \"type\": \"object\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"Key\": {\n \"description\": \"A question or option key, unique within its manifest or question.\",\n \"pattern\": \"^[A-Za-z0-9._-]{1,64}$\",\n \"title\": \"Key\",\n \"type\": \"string\"\n },\n \"QuestionResult\": {\n \"additionalProperties\": false,\n \"description\": \"One question's totals. `choice`: selections per option. `ranking`: per option, how many ballots placed it at each position (index 0 = first). `score`: per option, the sum and the number of ballots that scored it. `text`: the answers, in board order, withheld (`withheld: true`) below the manifest's `textReleaseThreshold`. `abstained` counts ballots that left the question unanswered.\",\n \"properties\": {\n \"abstained\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"choice\": {\n \"additionalProperties\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"propertyNames\": {\n \"$ref\": \"#/$defs/Key\"\n },\n \"type\": \"object\"\n },\n \"question\": {\n \"$ref\": \"#/$defs/Key\"\n },\n \"ranking\": {\n \"additionalProperties\": {\n \"items\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"type\": \"array\"\n },\n \"propertyNames\": {\n \"$ref\": \"#/$defs/Key\"\n },\n \"type\": \"object\"\n },\n \"score\": {\n \"additionalProperties\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"count\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"sum\": {\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"sum\",\n \"count\"\n ],\n \"type\": \"object\"\n },\n \"propertyNames\": {\n \"$ref\": \"#/$defs/Key\"\n },\n \"type\": \"object\"\n },\n \"text\": {\n \"items\": {\n \"maxLength\": 4096,\n \"type\": \"string\"\n },\n \"type\": \"array\"\n },\n \"withheld\": {\n \"type\": \"boolean\"\n }\n },\n \"required\": [\n \"question\",\n \"abstained\"\n ],\n \"title\": \"QuestionResult\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Success response to vtc/elections/certify/0.1: the result the community verified and certified.\",\n \"properties\": {\n \"auditAttestations\": {\n \"description\": \"The attestations certified with the result; empty when none was given.\",\n \"items\": {\n \"description\": \"An auditor's attestation the community verified, as received.\",\n \"type\": \"object\"\n },\n \"maxItems\": 32,\n \"type\": \"array\"\n },\n \"certifiedAt\": {\n \"format\": \"date-time\",\n \"maxLength\": 64,\n \"type\": \"string\"\n },\n \"electionId\": {\n \"$ref\": \"#/$defs/ElectionId\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"head\": {\n \"$ref\": \"#/$defs/BoardHead\",\n \"description\": \"The board head the community verified: the head as it stood at close, which the result counts exactly.\"\n },\n \"result\": {\n \"$ref\": \"#/$defs/ElectionResult\",\n \"description\": \"The result the community recomputed from the board, equal to the ballot box's.\"\n }\n },\n \"required\": [\n \"electionId\",\n \"result\",\n \"head\",\n \"certifiedAt\",\n \"auditAttestations\"\n ],\n \"title\": \"VTC Elections — Certify — response payload\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/vtc/elections/certify/0.1\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"An administrator has the community verify a tallied election's board for itself and certify its result. The outer document members (id, type, issuer, recipient, issuedAt, expiresAt, proof) are owned by the framework — SPEC §6.3.\",\n \"properties\": {\n \"auditAttestations\": {\n \"description\": \"Attestations from auditors the manifest names in `board.auditors`, to be certified with the result.\",\n \"items\": {\n \"description\": \"A verifiable credential, as its auditor issued it, attesting the auditor's own recount of this election's board (VTI-VOT-073). Kept exactly as received.\",\n \"type\": \"object\"\n },\n \"maxItems\": 32,\n \"type\": \"array\"\n },\n \"electionId\": {\n \"$ref\": \"#/$defs/ElectionId\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n }\n },\n \"required\": [\n \"electionId\"\n ],\n \"title\": \"VTC Elections — Certify — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vtc/elections/certify/0.1#response";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"BoardHead\": {\n \"additionalProperties\": false,\n \"description\": \"A board's tree head (RFC 9162 §2.1). A head is signed by being carried in a document the ballot box signs; whoever keeps a head keeps that whole document.\",\n \"properties\": {\n \"electionId\": {\n \"$ref\": \"#/$defs/ElectionId\"\n },\n \"issuedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"rootHash\": {\n \"$ref\": \"#/$defs/DigestMultibase\",\n \"description\": \"The RFC 9162 Merkle tree hash (SHA-256) of the first `treeSize` entries, as a multihash.\"\n },\n \"treeSize\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"electionId\",\n \"treeSize\",\n \"rootHash\",\n \"issuedAt\"\n ],\n \"title\": \"BoardHead\",\n \"type\": \"object\"\n },\n \"DigestMultibase\": {\n \"description\": \"A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\\n\\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\\n\\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\\n\\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \\\"interoperability is not guaranteed between implementations using such values\\\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.\",\n \"examples\": [\n \"zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR\"\n ],\n \"minLength\": 16,\n \"pattern\": \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\n \"title\": \"DigestMultibase\",\n \"type\": \"string\"\n },\n \"ElectionId\": {\n \"description\": \"An election's identifier, unique within its community. A closed alphabet: it appears in the ticket class label `voter/<electionId>` and is framed into every hash a cast is bound to.\",\n \"pattern\": \"^[A-Za-z0-9._-]{1,128}$\",\n \"title\": \"ElectionId\",\n \"type\": \"string\"\n },\n \"ElectionResult\": {\n \"additionalProperties\": false,\n \"description\": \"An election's result, computable by anyone who can read its board from the board and the registration alone (VTI-VOT-070).\",\n \"properties\": {\n \"ballotsCounted\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"electionId\": {\n \"$ref\": \"#/$defs/ElectionId\"\n },\n \"head\": {\n \"$ref\": \"#/$defs/BoardHead\",\n \"description\": \"The head of the board as it stood at close: the result counts exactly its entries.\"\n },\n \"manifestDigest\": {\n \"$ref\": \"#/$defs/DigestMultibase\"\n },\n \"questions\": {\n \"items\": {\n \"$ref\": \"#/$defs/QuestionResult\"\n },\n \"type\": \"array\"\n }\n },\n \"required\": [\n \"electionId\",\n \"manifestDigest\",\n \"ballotsCounted\",\n \"questions\",\n \"head\"\n ],\n \"title\": \"ElectionResult\",\n \"type\": \"object\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"Key\": {\n \"description\": \"A question or option key, unique within its manifest or question.\",\n \"pattern\": \"^[A-Za-z0-9._-]{1,64}$\",\n \"title\": \"Key\",\n \"type\": \"string\"\n },\n \"QuestionResult\": {\n \"additionalProperties\": false,\n \"description\": \"One question's totals. `choice`: selections per option. `ranking`: per option, how many ballots placed it at each position (index 0 = first). `score`: per option, the sum and the number of ballots that scored it. `text`: the answers, in board order, withheld (`withheld: true`) below the manifest's `textReleaseThreshold`. `abstained` counts ballots that left the question unanswered.\",\n \"properties\": {\n \"abstained\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"choice\": {\n \"additionalProperties\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"propertyNames\": {\n \"$ref\": \"#/$defs/Key\"\n },\n \"type\": \"object\"\n },\n \"question\": {\n \"$ref\": \"#/$defs/Key\"\n },\n \"ranking\": {\n \"additionalProperties\": {\n \"items\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"type\": \"array\"\n },\n \"propertyNames\": {\n \"$ref\": \"#/$defs/Key\"\n },\n \"type\": \"object\"\n },\n \"score\": {\n \"additionalProperties\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"count\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"sum\": {\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"sum\",\n \"count\"\n ],\n \"type\": \"object\"\n },\n \"propertyNames\": {\n \"$ref\": \"#/$defs/Key\"\n },\n \"type\": \"object\"\n },\n \"text\": {\n \"items\": {\n \"maxLength\": 4096,\n \"type\": \"string\"\n },\n \"type\": \"array\"\n },\n \"withheld\": {\n \"type\": \"boolean\"\n }\n },\n \"required\": [\n \"question\",\n \"abstained\"\n ],\n \"title\": \"QuestionResult\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Success response to vtc/elections/certify/0.1: the result the community verified and certified.\",\n \"properties\": {\n \"auditAttestations\": {\n \"description\": \"The attestations certified with the result; empty when none was given.\",\n \"items\": {\n \"description\": \"An auditor's attestation the community verified, as received.\",\n \"type\": \"object\"\n },\n \"maxItems\": 32,\n \"type\": \"array\"\n },\n \"certifiedAt\": {\n \"format\": \"date-time\",\n \"maxLength\": 64,\n \"type\": \"string\"\n },\n \"electionId\": {\n \"$ref\": \"#/$defs/ElectionId\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"head\": {\n \"$ref\": \"#/$defs/BoardHead\",\n \"description\": \"The board head the community verified: the head as it stood at close, which the result counts exactly.\"\n },\n \"result\": {\n \"$ref\": \"#/$defs/ElectionResult\",\n \"description\": \"The result the community recomputed from the board, equal to the ballot box's.\"\n }\n },\n \"required\": [\n \"electionId\",\n \"result\",\n \"head\",\n \"certifiedAt\",\n \"auditAttestations\"\n ],\n \"title\": \"VTC Elections — Certify — response payload\",\n \"type\": \"object\"\n }\n },\n \"$ref\": \"#/$defs/Response\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
/// The extended error codes this specification declares (SPEC §7.3 item 9,
/// §8.5), in declaration order. Empty when it declares none.
pub const ERROR_CODES: &[crate::DeclaredErrorCode] = &[
error_codes::ALREADY_CERTIFIED,
error_codes::ELECTION_UNKNOWN,
error_codes::NOT_TALLIED,
error_codes::BOARD_INCONSISTENT,
error_codes::RESULT_MISMATCH,
error_codes::COUNT_EXCEEDS_TICKETS,
error_codes::CAST_INVALID,
error_codes::AUDIT_ATTESTATION_INVALID,
error_codes::BALLOT_BOX_UNAVAILABLE,
];
/// One constant per extended error code this specification declares
/// (SPEC §7.3 item 9), named for its local part.
///
/// Emit these rather than a string literal: the code is read from the
/// specification, so it cannot name a code the specification never
/// declared.
pub mod error_codes {
/// `vtc/elections/certify:alreadyCertified`
///
/// The election is already certified.
///
/// Declared `retryable: false`.
pub const ALREADY_CERTIFIED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/elections/certify:alreadyCertified",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/elections/certify:electionUnknown`
///
/// No election with this identifier exists for this community, or the caller may not see it (CONVENTIONS §7).
///
/// Declared `retryable: false`.
pub const ELECTION_UNKNOWN: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/elections/certify:electionUnknown",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/elections/certify:notTallied`
///
/// The ballot box has not published a result for the election: voting is open, the election is closed but not yet tallied, or it was cancelled.
///
/// Declared `retryable: false`.
pub const NOT_TALLIED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/elections/certify:notTallied",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/elections/certify:boardInconsistent`
///
/// The board does not hold together: its entries do not hash to the signed head, a checkpoint the community kept is not consistent with the final head, a tag is counted more than once, or a replacement does not follow the manifest's revote rule. `details` names the first fault.
///
/// Declared `retryable: false`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const BOARD_INCONSISTENT: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/elections/certify:boardInconsistent",
retryable: false,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"checkpointTreeSize\":{\"minimum\":0,\"type\":\"integer\"},\"fault\":{\"enum\":[\"rootMismatch\",\"checkpointInconsistent\",\"tagRepeated\",\"replacementInvalid\"],\"type\":\"string\"},\"index\":{\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"fault\"],\"type\":\"object\"}",
),
};
/// `vtc/elections/certify:resultMismatch`
///
/// The result recomputed from the board differs from the result the ballot box published.
///
/// Declared `retryable: false`.
pub const RESULT_MISMATCH: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/elections/certify:resultMismatch",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/elections/certify:countExceedsTickets`
///
/// The board counts more ballots than the community issued tickets for the election (VTI-VOT-072).
///
/// Declared `retryable: false`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const COUNT_EXCEEDS_TICKETS: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/elections/certify:countExceedsTickets",
retryable: false,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"ballotsCounted\":{\"minimum\":0,\"type\":\"integer\"},\"ticketsIssued\":{\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"ballotsCounted\",\"ticketsIssued\"],\"type\":\"object\"}",
),
};
/// `vtc/elections/certify:castInvalid`
///
/// A cast on the board is not admissible: its attestation does not verify for this election and its ballot, it is under another class, its tag is not the attestation's, or its ballot does not answer the manifest (CONVENTIONS §3.7). `details.index` is its board index.
///
/// Declared `retryable: false`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const CAST_INVALID: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/elections/certify:castInvalid",
retryable: false,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"index\":{\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"index\"],\"type\":\"object\"}",
),
};
/// `vtc/elections/certify:auditAttestationInvalid`
///
/// An entry of `auditAttestations` does not verify, is not issued by an auditor the manifest names, or does not attest this election's result at the verified head. `details.index` is its position.
///
/// Declared `retryable: false`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const AUDIT_ATTESTATION_INVALID: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/elections/certify:auditAttestationInvalid",
retryable: false,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"index\":{\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"index\"],\"type\":\"object\"}",
),
};
/// `vtc/elections/certify:ballotBoxUnavailable`
///
/// The ballot box did not answer a read the verification needs. Nothing was recorded; the same request may be sent again.
///
/// Declared `retryable: true`.
pub const BALLOT_BOX_UNAVAILABLE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/elections/certify:ballotBoxUnavailable",
retryable: true,
details_schema: ::core::option::Option::None,
};
}