Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -485,11 +485,6 @@ def _generate_base_files(self, context: dict[str, Any], output_dir: Path) -> dic
# Detect client type
client_type_fn = self.template_engine.env.globals.get("get_client_type")
client_type = client_type_fn(context["spec"]) if callable(client_type_fn) else "Api"
if client_type == "Algod":
# Provide msgpack helper to encode/decode arbitrary msgpack values as bytes
files[src_dir / "msgpack_value_bytes.rs"] = self.template_engine.render_template(
"base/msgpack_value_bytes.rs.j2", context
)

return files

Expand Down Expand Up @@ -545,6 +540,10 @@ def _generate_model_files(
files[models_dir / "get_block.rs"] = self.template_engine.render_template(
"models/block/get_block.rs.j2", context
)
# Generate NonAsciiString helper type for msgpack strings
files[models_dir / "non_ascii_string.rs"] = self.template_engine.render_template(
"models/block/non_ascii_string.rs.j2", context
)

return files

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ uniffi::setup_scaffolding!();

pub mod apis;
pub mod models;
{% if client_type == "Algod" %}
pub mod msgpack_value_bytes;
{% endif %}

// Re-export the main client for convenience
pub use apis::{{ client_type }}Client;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

use crate::models;
use crate::models::NonAsciiString;
#[cfg(not(feature = "ffi_uniffi"))]
use algokit_transact::SignedTransaction as AlgokitSignedTransaction;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -41,9 +42,9 @@ pub struct BlockAppEvalDelta {
#[serde_as(as = "Option<Vec<Bytes>>")]
#[serde(rename = "sa", skip_serializing_if = "Option::is_none")]
pub shared_accounts: Option<Vec<Vec<u8>>>,
/// [lg] Application log outputs as strings (msgpack strings).
#[serde(rename = "lg", skip_serializing_if = "Option::is_none")]
pub logs: Option<Vec<String>>,
/// [lg] Application log outputs.
#[serde(rename = "lg", skip_serializing_if = "Option::is_none", default)]
pub logs: Option<Vec<NonAsciiString>>,
}

impl AlgorandMsgpack for BlockAppEvalDelta {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

use crate::models;
use crate::models::NonAsciiString;
use serde::{Deserialize, Serialize};

use algokit_transact::AlgorandMsgpack;
Expand All @@ -21,8 +22,8 @@ pub struct BlockEvalDelta {
#[serde(rename = "at")]
pub action: u32,
/// [bs] bytes value.
#[serde(rename = "bs", skip_serializing_if = "Option::is_none")]
pub bytes: Option<String>,
#[serde(default, rename = "bs", skip_serializing_if = "Option::is_none")]
pub bytes: Option<NonAsciiString>,
/// [ui] uint value.
#[serde(rename = "ui", skip_serializing_if = "Option::is_none")]
pub uint: Option<u64>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use std::collections::HashMap;

use crate::models::BlockEvalDelta;

use crate::models::NonAsciiString;

/// BlockStateDelta is a map keyed by state key to BlockEvalDelta.
pub type BlockStateDelta = HashMap<String, BlockEvalDelta>;
pub type BlockStateDelta = HashMap<NonAsciiString, BlockEvalDelta>;


Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,105 @@ use algokit_transact::AlgorandMsgpack;

use crate::models::Block;

// Type aliases for clarity
pub type Digest = Vec<u8>; // DigestSize = sha512.Size256 = 32 bytes
pub type Address = Digest;
pub type VrfProof = Vec<u8>; // VrfProofSize = 80 bytes

pub type Ed25519Signature = Vec<u8>; // Ed25519SignatureSize = 64 bytes
pub type Ed25519PublicKey = Vec<u8>; // Ed25519PublicKeySize = 32 bytes

/// OneTimeSignature is a cryptographic signature that is produced a limited
/// number of times and provides forward integrity.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OneTimeSignature {
/// Sig is a signature of msg under the key PK.
#[serde(rename = "s")]
pub sig: Ed25519Signature,
#[serde(rename = "p")]
pub pk: Ed25519PublicKey,
/// Old-style signature that does not use proper domain separation.
/// PKSigOld is unused; however, unfortunately we forgot to mark it
/// `codec:omitempty` and so it appears (with zero value) in certs.
#[serde(rename = "ps")]
pub pk_sig_old: Ed25519Signature,
/// Used to verify a new-style two-level ephemeral signature.
/// PK1Sig is a signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the key PK2.
/// PK2Sig is a signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master key (OneTimeSignatureVerifier).
#[serde(rename = "p2")]
pub pk2: Ed25519PublicKey,
#[serde(rename = "p1s")]
pub pk1_sig: Ed25519Signature,
#[serde(rename = "p2s")]
pub pk2_sig: Ed25519Signature,
}

/// An UnauthenticatedCredential is a Credential which has not yet been
/// authenticated.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnauthenticatedCredential {
#[serde(rename = "pf")]
pub proof: VrfProof,
}

/// A proposalValue is a triplet of a block hashes (the contents themselves and the encoding of the block),
/// its proposer, and the period in which it was proposed.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProposalValue {
#[serde(rename = "oper", default)]
pub original_period: u64,
#[serde(rename = "oprop")]
pub original_proposer: Address,
/// BlockDigest = proposal.Block.Digest()
#[serde(rename = "dig")]
pub block_digest: Digest,
/// EncodingDigest = crypto.HashObj(proposal)
#[serde(rename = "encdig")]
pub encoding_digest: Digest,
}

/// voteAuthenticator omits the Round, Period, Step, and Proposal for compression
/// and to simplify checking logic.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct VoteAuthenticator {
#[serde(rename = "snd")]
pub sender: Address,
#[serde(rename = "cred")]
pub cred: UnauthenticatedCredential,
#[serde(rename = "sig")]
pub sig: OneTimeSignature,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EquivocationVoteAuthenticator {
#[serde(rename = "snd")]
pub sender: Address,
#[serde(rename = "cred")]
pub cred: UnauthenticatedCredential,
#[serde(rename = "sig")]
pub sigs: [OneTimeSignature; 2],
#[serde(rename = "props")]
pub proposals: [ProposalValue; 2],
}

/// unauthenticatedBundle is a bundle which has not yet been verified.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnauthenticatedBundle {
#[serde(rename = "rnd", default)]
pub round: u64,
#[serde(rename = "per", default)]
pub period: u64,
#[serde(rename = "step", default)]
pub step: u64,
#[serde(rename = "prop")]
pub proposal: ProposalValue,
#[serde(rename = "vote", default, skip_serializing_if = "Vec::is_empty")]
pub votes: Vec<VoteAuthenticator>,
#[serde(rename = "eqv", default, skip_serializing_if = "Vec::is_empty")]
pub equivocation_votes: Vec<EquivocationVoteAuthenticator>,
}


/// Encoded block object.
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "ffi_uniffi", derive(uniffi::Record))]
Expand All @@ -29,12 +128,11 @@ pub struct GetBlock {
pub block: Block,
/// Block certificate (msgpack only).
#[serde(
with = "crate::msgpack_value_bytes",
default,
rename = "cert",
skip_serializing_if = "Option::is_none"
)]
pub cert: Option<Vec<u8>>,
pub cert: Option<UnauthenticatedBundle>,
}

impl AlgorandMsgpack for GetBlock {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/// A wrapper type for msgpack strings that may contain non-UTF8 bytes.
///
/// Msgpack strings can contain arbitrary bytes that aren't valid UTF-8.
/// This type handles both string and binary msgpack values, storing them
/// internally as raw bytes.
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::ops::{Deref, DerefMut};

#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "ffi_uniffi", derive(uniffi::Record))]
pub struct NonAsciiString(pub Vec<u8>);

impl NonAsciiString {
pub fn new(bytes: Vec<u8>) -> Self {
Self(bytes)
}

pub fn into_bytes(self) -> Vec<u8> {
self.0
}

pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}

impl From<Vec<u8>> for NonAsciiString {
fn from(bytes: Vec<u8>) -> Self {
Self(bytes)
}
}

impl From<NonAsciiString> for Vec<u8> {
fn from(s: NonAsciiString) -> Self {
s.0
}
}

impl AsRef<[u8]> for NonAsciiString {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl Deref for NonAsciiString {
type Target = Vec<u8>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for NonAsciiString {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl<'de> Deserialize<'de> for NonAsciiString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// Use rmpv::Value to capture the raw msgpack value
let value = rmpv::Value::deserialize(deserializer)?;

match value {
rmpv::Value::String(s) => {
// rmpv::Utf8String gives us access to raw bytes even if not valid UTF-8
Ok(NonAsciiString(s.into_bytes()))
}
rmpv::Value::Binary(b) => Ok(NonAsciiString(b)),
_ => Err(serde::de::Error::custom(
"expected string or binary, got other type",
)),
}
}
}

impl Serialize for NonAsciiString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// NOTE: We are using this deprecated function because algod incorrectly encodes non-utf8
// data as string
#[allow(deprecated)]
let raw = rmp_serde::Raw::from_utf8(self.0.clone());
raw.serialize(serializer)

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,15 @@ use algokit_transact::AlgorandMsgpack;

use crate::models::BlockAppEvalDelta;

use crate::models::NonAsciiString;

/// SignedTxnInBlock is a SignedTransaction with additional ApplyData and block-specific metadata.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "ffi_uniffi", derive(uniffi::Record))]
pub struct SignedTxnInBlock {
/// SignedTransaction fields (flattened from algokit_transact)
#[serde(flatten)]
pub signed_transaction: AlgokitSignedTransaction,
/// [lsig] Logic signature (program signature).
#[serde(
with = "crate::msgpack_value_bytes",
default,
rename = "lsig",
skip_serializing_if = "Option::is_none"
)]
pub logic_signature: Option<Vec<u8>>,
/// [ca] Rewards applied to close-remainder-to account.
#[serde(rename = "ca", skip_serializing_if = "Option::is_none")]
pub closing_amount: Option<u64>,
Expand Down Expand Up @@ -96,7 +90,6 @@ impl Default for SignedTxnInBlock {
auth_address: None,
multisignature: None,
},
logic_signature: None,
closing_amount: None,
asset_closing_amount: None,
sender_rewards: None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ pub mod block_state_proof_tracking_data;
pub use self::block_state_proof_tracking_data::BlockStateProofTrackingData;
pub mod block_state_proof_tracking;
pub use self::block_state_proof_tracking::BlockStateProofTracking;
pub mod non_ascii_string;
pub use self::non_ascii_string::NonAsciiString;
{% endif %}

Loading
Loading