#[repr(C)]pub struct DailyParticipantData {
pub participant_id: [u8; 32],
pub first_payment_timestamp: i64,
pub last_payment_timestamp: i64,
pub first_payment_tx_sig_short: [u8; 8],
pub last_payment_tx_sig_short: [u8; 8],
pub payment_count: u32,
pub participant_type: u8,
pub _padding: [u8; 3],
}
Expand description
DailyParticipantData represents aggregated payment data for a single participant in a daily epoch.
This structure is used off-chain by the oracle to build Merkle trees for daily payment snapshots. It aggregates all payments for a single participant (customer or merchant) within a 24-hour epoch.
§Key Features
- Aggregation: Combines multiple payments per participant per day
- Fair Rewards: Uses transaction count and total amount for reward calculation
- Efficient Storage: Off-chain only, reduces on-chain storage costs
- Flexible: Supports both customer and merchant participants
- Simple Design: Only necessary fields included (Simple is Best)
- Transaction Verification: Includes short transaction signatures for on-chain verification
§Data Structure
participant_id
: Unique identifier for the participant (customer/merchant)first_payment_timestamp
: Timestamp of first payment in the daylast_payment_timestamp
: Timestamp of last payment in the dayfirst_payment_tx_sig_short
: First 8 characters of first payment transaction signaturelast_payment_tx_sig_short
: First 8 characters of last payment transaction signaturefirst_payment_tx_sig_short
: First 8 characters of first payment transaction signaturelast_payment_tx_sig_short
: First 8 characters of last payment transaction signaturepayment_count
: Number of transactions for the dayparticipant_type
: Whether this is a customer (0) or merchant (1)
§Merkle Leaf Construction
The Merkle leaf hash is computed as:
use miracle_api::prelude::DailyParticipantData;
let participant_data = DailyParticipantData::new(
[0u8; 32], // participant_id
1723680000, // first_payment_timestamp
1723766400, // last_payment_timestamp
[0u8; 8], // first_payment_tx_sig_short
[0u8; 8], // last_payment_tx_sig_short
5, // payment_count
0, // participant_type (customer)
);
let leaf_hash = participant_data.compute_leaf_hash();
§Security Features
- Timestamp Range: Provides audit trail and prevents stale data
- Participant Type: Distinguishes between customers and merchants for fair allocation
- Aggregation: Reduces Merkle tree size while maintaining fairness
- Uniqueness: participant_id + daily timestamps provide natural uniqueness
- Deployment Isolation: Handled by program ID, not data structure
- Transaction Verification: Short signatures enable on-chain payment verification
- Transaction Verification: Short signatures enable on-chain verification of payment authenticity
Fields§
§participant_id: [u8; 32]
Unique identifier for the participant (customer or merchant). This should be a deterministic identifier that can be reproduced by both the oracle and the claiming user.
first_payment_timestamp: i64
Timestamp of the first payment in the daily epoch (Unix timestamp). Used for audit trail and to prevent stale data replay.
last_payment_timestamp: i64
Timestamp of the last payment in the daily epoch (Unix timestamp). Used for audit trail and to ensure data freshness.
first_payment_tx_sig_short: [u8; 8]
First 8 characters of the first payment transaction signature. Used for on-chain verification of payment authenticity. Provides cost-effective verification without storing full signatures.
last_payment_tx_sig_short: [u8; 8]
First 8 characters of the last payment transaction signature. Used for on-chain verification of payment authenticity. Provides cost-effective verification without storing full signatures.
payment_count: u32
Number of transactions for this participant in the daily epoch. Used for activity-based reward calculation and community metrics.
participant_type: u8
Participant type: 0 for customer, 1 for merchant. Used to determine reward allocation between customer and merchant pools.
_padding: [u8; 3]
Padding for future extensibility.
Implementations§
Source§impl DailyParticipantData
impl DailyParticipantData
Sourcepub fn new(
participant_id: [u8; 32],
first_payment_timestamp: i64,
last_payment_timestamp: i64,
first_payment_tx_sig_short: [u8; 8],
last_payment_tx_sig_short: [u8; 8],
payment_count: u32,
participant_type: u8,
) -> Self
pub fn new( participant_id: [u8; 32], first_payment_timestamp: i64, last_payment_timestamp: i64, first_payment_tx_sig_short: [u8; 8], last_payment_tx_sig_short: [u8; 8], payment_count: u32, participant_type: u8, ) -> Self
Create a new DailyParticipantData instance.
§Parameters
participant_id
: Unique identifier for the participantfirst_payment_timestamp
: Timestamp of first paymentlast_payment_timestamp
: Timestamp of last paymentfirst_payment_tx_sig_short
: First 8 characters of first payment transaction signaturelast_payment_tx_sig_short
: First 8 characters of last payment transaction signaturepayment_count
: Number of transactionsparticipant_type
: 0 for customer, 1 for merchant
§Returns
- New DailyParticipantData instance
Examples found in repository?
14fn main() -> Result<(), Box<dyn std::error::Error>> {
15 println!("🔍 Miracle API Utils Example: Building Claim Instructions");
16 println!("========================================================");
17
18 // ===== SCENARIO 1: Converting Database Hex Strings =====
19 println!("\n📊 Scenario 1: Converting Database Hex Strings");
20 println!("------------------------------------------------");
21
22 // Simulate database query output (hex strings from SQL function)
23 let db_customer_reward_pool = "0x1234567890abcdef";
24 let db_merchant_reward_pool = "0xfedcba0987654321";
25 let db_total_customer_activity = "0x0000000000000064"; // 100 in decimal
26 let db_total_merchant_activity = "0x00000000000000c8"; // 200 in decimal
27
28 println!("Database hex strings:");
29 println!(" customer_reward_pool: {}", db_customer_reward_pool);
30 println!(" merchant_reward_pool: {}", db_merchant_reward_pool);
31 println!(" total_customer_activity: {}", db_total_customer_activity);
32 println!(" total_merchant_activity: {}", db_total_merchant_activity);
33
34 // Convert hex strings to EpochClaimData using utils
35 let epoch_data = claim::epoch_claim_data_from_hex(
36 db_customer_reward_pool,
37 db_merchant_reward_pool,
38 db_total_customer_activity,
39 db_total_merchant_activity,
40 )?;
41
42 println!("\n✅ Converted to EpochClaimData:");
43 println!(
44 " customer_reward_pool bytes: {:?}",
45 epoch_data.customer_reward_pool
46 );
47 println!(
48 " merchant_reward_pool bytes: {:?}",
49 epoch_data.merchant_reward_pool
50 );
51 println!(
52 " total_customer_activity bytes: {:?}",
53 epoch_data.total_customer_activity
54 );
55 println!(
56 " total_merchant_activity bytes: {:?}",
57 epoch_data.total_merchant_activity
58 );
59
60 // ===== SCENARIO 2: Converting Native u64 Values =====
61 println!("\n📊 Scenario 2: Converting Native u64 Values");
62 println!("---------------------------------------------");
63
64 let native_customer_reward_pool = 1234567890u64;
65 let native_merchant_reward_pool = 9876543210u64;
66 let native_total_customer_activity = 100u64;
67 let native_total_merchant_activity = 200u64;
68
69 println!("Native u64 values:");
70 println!(" customer_reward_pool: {}", native_customer_reward_pool);
71 println!(" merchant_reward_pool: {}", native_merchant_reward_pool);
72 println!(
73 " total_customer_activity: {}",
74 native_total_customer_activity
75 );
76 println!(
77 " total_merchant_activity: {}",
78 native_total_merchant_activity
79 );
80
81 // Convert u64 values to EpochClaimData using utils
82 let epoch_data_u64 = claim::epoch_claim_data_from_u64(
83 native_customer_reward_pool,
84 native_merchant_reward_pool,
85 native_total_customer_activity,
86 native_total_merchant_activity,
87 );
88
89 println!("\n✅ Converted to EpochClaimData:");
90 println!(
91 " customer_reward_pool bytes: {:?}",
92 epoch_data_u64.customer_reward_pool
93 );
94 println!(
95 " merchant_reward_pool bytes: {:?}",
96 epoch_data_u64.merchant_reward_pool
97 );
98 println!(
99 " total_customer_activity bytes: {:?}",
100 epoch_data_u64.total_customer_activity
101 );
102 println!(
103 " total_merchant_activity bytes: {:?}",
104 epoch_data_u64.total_merchant_activity
105 );
106
107 // ===== SCENARIO 3: Individual Hex Conversions =====
108 println!("\n📊 Scenario 3: Individual Hex Conversions");
109 println!("-------------------------------------------");
110
111 // Convert individual hex strings to byte arrays
112 let reward_pool_bytes = hex::hex_string_to_u64_bytes("0x1234567890abcdef")?;
113 let hash_bytes = hex::hex_string_to_hash_bytes(
114 "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
115 )?;
116
117 println!("Individual conversions:");
118 println!(" reward_pool (8 bytes): {:?}", reward_pool_bytes);
119 println!(" hash (32 bytes): {:?}", hash_bytes);
120
121 // Convert bytes back to hex strings
122 let reward_pool_hex = hex::bytes_to_hex_string(&reward_pool_bytes);
123 println!(" reward_pool back to hex: {}", reward_pool_hex);
124
125 // ===== SCENARIO 4: Building Claim Instruction =====
126 println!("\n📊 Scenario 4: Building Claim Instruction");
127 println!("------------------------------------------");
128
129 // Create sample participant data
130 let participant_data = DailyParticipantData::new(
131 [1u8; 32], // participant_id
132 1723680000, // first_payment_timestamp
133 1723766400, // last_payment_timestamp
134 [1u8; 8], // first_payment_tx_sig_short
135 [2u8; 8], // last_payment_tx_sig_short
136 5, // payment_count
137 0, // participant_type (customer)
138 );
139
140 // Create payment proof (empty for this example)
141 let payment_proof = vec![[3u8; 32]];
142 let payment_indices = vec![true];
143
144 // Create seal proof (empty for this example)
145 let seal_proof = vec![[4u8; 32]];
146 let seal_indices = vec![false];
147
148 // Payment root (32 bytes)
149 let payment_root = [5u8; 32];
150
151 // Build claim instruction using the converted epoch_data
152 let claim_instruction = claim(
153 Pubkey::new_from_array([6u8; 32]), // signer (placeholder)
154 Pubkey::new_from_array([7u8; 32]), // beneficiary (placeholder)
155 0, // epoch
156 participant_data,
157 payment_proof,
158 payment_indices,
159 seal_proof,
160 seal_indices,
161 payment_root,
162 epoch_data, // Using the converted epoch_data
163 0, // participant_type
164 None, // social_data
165 );
166
167 println!("✅ Claim instruction built successfully!");
168 println!(
169 " Instruction data length: {} bytes",
170 claim_instruction.data.len()
171 );
172 println!(" Number of accounts: {}", claim_instruction.accounts.len());
173
174 // ===== SUMMARY =====
175 println!("\n🎯 Summary");
176 println!("===========");
177 println!("✅ Successfully converted database hex strings to EpochClaimData");
178 println!("✅ Successfully converted native u64 values to EpochClaimData");
179 println!("✅ Successfully built claim instruction with converted data");
180 println!("✅ All data type conversions working correctly");
181
182 println!("\n🚀 The utils module successfully bridges the gap between:");
183 println!(" - Database hex string output (from SQL functions)");
184 println!(" - On-chain byte array requirements (for EpochClaimData)");
185 println!(" - Claim instruction construction");
186
187 Ok(())
188}
Sourcepub fn compute_leaf_hash(&self) -> [u8; 32]
pub fn compute_leaf_hash(&self) -> [u8; 32]
Compute the Merkle leaf hash for this participant data.
§Returns
- 32-byte hash that will be used as a leaf in the Merkle tree
§Security
This hash includes all fields to ensure data integrity and prevent replay attacks across different epochs. The short transaction signatures are included to enable on-chain verification of payment authenticity.
§Implementation
Uses Solana’s standard hashv function with SHA-256 for cryptographic security. All fields are included in the hash to prevent any data manipulation.
Sourcepub fn validate(&self) -> Result<(), &'static str>
pub fn validate(&self) -> Result<(), &'static str>
Validate the participant data for consistency.
§Returns
Ok(())
if data is validErr(&str)
with error message if validation fails
§Validation Rules
- Participant type must be 0 or 1
- Payment count must be greater than 0
- First timestamp must be before or equal to last timestamp
- Short signatures must not be all zeros (basic validation)
Sourcepub fn is_customer(&self) -> bool
pub fn is_customer(&self) -> bool
Check if this participant is a customer.
§Returns
true
if participant_type is 0 (customer)false
if participant_type is 1 (merchant)
Sourcepub fn is_merchant(&self) -> bool
pub fn is_merchant(&self) -> bool
Check if this participant is a merchant.
§Returns
true
if participant_type is 1 (merchant)false
if participant_type is 0 (customer)
Sourcepub fn from_bytes(data: &[u8]) -> Result<Self, &'static str>
pub fn from_bytes(data: &[u8]) -> Result<Self, &'static str>
Deserialize from bytes manually
Trait Implementations§
Source§impl Clone for DailyParticipantData
impl Clone for DailyParticipantData
Source§fn clone(&self) -> DailyParticipantData
fn clone(&self) -> DailyParticipantData
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moreSource§impl Debug for DailyParticipantData
impl Debug for DailyParticipantData
Source§impl<'de> Deserialize<'de> for DailyParticipantData
impl<'de> Deserialize<'de> for DailyParticipantData
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for DailyParticipantData
impl PartialEq for DailyParticipantData
Source§impl Serialize for DailyParticipantData
impl Serialize for DailyParticipantData
impl Copy for DailyParticipantData
impl Pod for DailyParticipantData
impl StructuralPartialEq for DailyParticipantData
Auto Trait Implementations§
impl Freeze for DailyParticipantData
impl RefUnwindSafe for DailyParticipantData
impl Send for DailyParticipantData
impl Sync for DailyParticipantData
impl Unpin for DailyParticipantData
impl UnwindSafe for DailyParticipantData
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CheckedAs for T
impl<T> CheckedAs for T
Source§fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
Source§impl<T> CheckedBitPattern for Twhere
T: AnyBitPattern,
impl<T> CheckedBitPattern for Twhere
T: AnyBitPattern,
Source§type Bits = T
type Bits = T
Self
must have the same layout as the specified Bits
except for
the possible invalid bit patterns being checked during
is_valid_bit_pattern
.Source§fn is_valid_bit_pattern(_bits: &T) -> bool
fn is_valid_bit_pattern(_bits: &T) -> bool
bits
as &Self
.Source§impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
Source§fn checked_cast_from(src: Src) -> Option<Dst>
fn checked_cast_from(src: Src) -> Option<Dst>
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more