1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
use alloc::collections::BTreeMap;
use alloc::string::ToString;
use alloc::vec::Vec;
use crate::account::AccountId;
use crate::batch::{BatchAccountUpdate, BatchId};
use crate::block::BlockNumber;
use crate::errors::ProvenBatchError;
use crate::note::Nullifier;
use crate::transaction::{InputNoteCommitment, InputNotes, OrderedTransactionHeaders, OutputNote};
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
use crate::vm::ExecutionProof;
use crate::{MIN_PROOF_SECURITY_LEVEL, Word};
/// A transaction batch with an execution proof.
/// Currently, this only carries a skeleton proof which does not attest to anything meaningful.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProvenBatch {
id: BatchId,
reference_block_commitment: Word,
reference_block_num: BlockNumber,
account_updates: BTreeMap<AccountId, BatchAccountUpdate>,
input_notes: InputNotes<InputNoteCommitment>,
output_notes: Vec<OutputNote>,
batch_expiration_block_num: BlockNumber,
transactions: OrderedTransactionHeaders,
proof: ExecutionProof,
}
impl ProvenBatch {
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
/// Creates a new [`ProvenBatch`] from the provided parts without checking any constraints
/// except the ones listed in the errors section below.
///
/// This should essentially never be called by users.
///
/// # Errors
///
/// Returns an error if the batch expiration block number is not greater than the reference
/// block number.
#[allow(clippy::too_many_arguments)]
pub fn new_unchecked(
id: BatchId,
reference_block_commitment: Word,
reference_block_num: BlockNumber,
account_updates: BTreeMap<AccountId, BatchAccountUpdate>,
input_notes: InputNotes<InputNoteCommitment>,
output_notes: Vec<OutputNote>,
batch_expiration_block_num: BlockNumber,
transactions: OrderedTransactionHeaders,
proof: ExecutionProof,
) -> Result<Self, ProvenBatchError> {
// Check that the batch expiration block number is greater than the reference block number.
if batch_expiration_block_num <= reference_block_num {
return Err(ProvenBatchError::InvalidBatchExpirationBlockNum {
batch_expiration_block_num,
reference_block_num,
});
}
Ok(Self {
id,
reference_block_commitment,
reference_block_num,
account_updates,
input_notes,
output_notes,
batch_expiration_block_num,
transactions,
proof,
})
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// The ID of this batch. See [`BatchId`] for details on how it is computed.
pub fn id(&self) -> BatchId {
self.id
}
/// Returns the commitment to the reference block of the batch.
pub fn reference_block_commitment(&self) -> Word {
self.reference_block_commitment
}
/// Returns the number of the reference block of the batch.
pub fn reference_block_num(&self) -> BlockNumber {
self.reference_block_num
}
/// Returns the block number at which the batch will expire.
pub fn batch_expiration_block_num(&self) -> BlockNumber {
self.batch_expiration_block_num
}
/// Returns an iterator over the IDs of all accounts updated in this batch.
pub fn updated_accounts(&self) -> impl Iterator<Item = AccountId> + use<'_> {
self.account_updates.keys().copied()
}
/// Returns the proof security level of the batch.
pub fn proof_security_level(&self) -> u32 {
MIN_PROOF_SECURITY_LEVEL
}
/// Returns the map of account IDs mapped to their [`BatchAccountUpdate`]s.
///
/// If an account was updated by multiple transactions, the [`BatchAccountUpdate`] is the result
/// of merging the individual updates.
///
/// For example, suppose an account's state before this batch is `A` and the batch contains two
/// transactions that updated it. Applying the first transaction results in intermediate state
/// `B`, and applying the second one results in state `C`. Then the returned update represents
/// the state transition from `A` to `C`.
pub fn account_updates(&self) -> &BTreeMap<AccountId, BatchAccountUpdate> {
&self.account_updates
}
/// Returns the [`InputNotes`] of this batch.
pub fn input_notes(&self) -> &InputNotes<InputNoteCommitment> {
&self.input_notes
}
/// Returns an iterator over the nullifiers created in this batch.
pub fn created_nullifiers(&self) -> impl Iterator<Item = Nullifier> + use<'_> {
self.input_notes.iter().map(InputNoteCommitment::nullifier)
}
/// Returns the output notes of the batch.
///
/// This is the aggregation of all output notes by the transactions in the batch, except the
/// ones that were consumed within the batch itself.
pub fn output_notes(&self) -> &[OutputNote] {
&self.output_notes
}
/// Returns the [`OrderedTransactionHeaders`] included in this batch.
pub fn transactions(&self) -> &OrderedTransactionHeaders {
&self.transactions
}
/// Returns the execution proof attached to this batch.
pub fn proof(&self) -> &ExecutionProof {
&self.proof
}
// MUTATORS
// --------------------------------------------------------------------------------------------
/// Consumes self and returns the contained [`OrderedTransactionHeaders`] of this batch.
pub fn into_transactions(self) -> OrderedTransactionHeaders {
self.transactions
}
}
// SERIALIZATION
// ================================================================================================
impl Serializable for ProvenBatch {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.reference_block_commitment.write_into(target);
self.reference_block_num.write_into(target);
self.account_updates.write_into(target);
self.input_notes.write_into(target);
self.output_notes.write_into(target);
self.batch_expiration_block_num.write_into(target);
self.transactions.write_into(target);
self.proof.write_into(target);
}
}
impl Deserializable for ProvenBatch {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let reference_block_commitment = Word::read_from(source)?;
let reference_block_num = BlockNumber::read_from(source)?;
let account_updates = BTreeMap::read_from(source)?;
let input_notes = InputNotes::<InputNoteCommitment>::read_from(source)?;
let output_notes = Vec::<OutputNote>::read_from(source)?;
let batch_expiration_block_num = BlockNumber::read_from(source)?;
let transactions = OrderedTransactionHeaders::read_from(source)?;
let proof = ExecutionProof::read_from(source)?;
let id =
BatchId::from_ids(transactions.as_slice().iter().map(|tx| (tx.id(), tx.account_id())));
Self::new_unchecked(
id,
reference_block_commitment,
reference_block_num,
account_updates,
input_notes,
output_notes,
batch_expiration_block_num,
transactions,
proof,
)
.map_err(|e| DeserializationError::UnknownError(e.to_string()))
}
}