-
Notifications
You must be signed in to change notification settings - Fork 157
Expand file tree
/
Copy pathlib.rs
More file actions
363 lines (304 loc) · 12.6 KB
/
lib.rs
File metadata and controls
363 lines (304 loc) · 12.6 KB
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
// Copyright (c) 2018-2022 The MobileCoin Foundation
//! APIs for MobileCoin Consensus Node Enclaves
#![no_std]
#![deny(missing_docs)]
#![allow(clippy::result_large_err)]
extern crate alloc;
mod config;
mod error;
mod governors_map;
mod governors_sig;
mod messages;
pub use crate::{
config::{BlockchainConfig, BlockchainConfigWithDigest},
error::Error,
governors_map::{Error as GovernorsMapError, GovernorsMap},
governors_sig::{
context as governors_signing_context, Signer as GovernorsSigner,
Verifier as GovernorsVerifier,
},
messages::EnclaveCall,
};
use alloc::{string::String, vec::Vec};
use core::{cmp::Ordering, hash::Hash, result::Result as StdResult};
use mc_attest_core::EvidenceKind;
use mc_attest_enclave_api::{
ClientAuthRequest, ClientAuthResponse, ClientSession, EnclaveMessage, PeerAuthRequest,
PeerAuthResponse, PeerSession,
};
use mc_blockchain_types::{Block, BlockContents, BlockSignature};
use mc_common::ResponderId;
use mc_crypto_keys::{CompressedRistrettoPublic, Ed25519Public, RistrettoPublic, X25519Public};
use mc_sgx_report_cache_api::ReportableEnclave;
use mc_transaction_core::{
mint::{MintConfig, MintConfigTx, MintTx},
ring_signature::KeyImage,
tx::{Tx, TxHash, TxOutMembershipElement, TxOutMembershipProof},
TokenId,
};
use serde::{Deserialize, Serialize};
/// A generic result type for enclave calls
pub type Result<T> = StdResult<T, Error>;
/// A `mc_transaction_core::Tx` that has been encrypted for the local enclave,
/// to be used during the two-step is-wellformed check.
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct LocallyEncryptedTx(pub Vec<u8>);
/// A `WellFormedTx` encrypted for the current enclave.
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct WellFormedEncryptedTx(pub Vec<u8>);
/// Tx data we wish to expose to untrusted from well-formed Txs.
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct WellFormedTxContext {
/// Priority assigned to this tx, based on the fee.
priority: u64,
/// Tx hash.
tx_hash: TxHash,
/// Tombstone block.
tombstone_block: u64,
/// Key images.
key_images: Vec<KeyImage>,
/// Highest membership proofs indices.
highest_indices: Vec<u64>,
/// Output public keys.
output_public_keys: Vec<CompressedRistrettoPublic>,
}
impl WellFormedTxContext {
/// Create a new WellFormedTxContext.
pub fn new(
priority: u64,
tx_hash: TxHash,
tombstone_block: u64,
key_images: Vec<KeyImage>,
highest_indices: Vec<u64>,
output_public_keys: Vec<CompressedRistrettoPublic>,
) -> Self {
Self {
priority,
tx_hash,
tombstone_block,
key_images,
highest_indices,
output_public_keys,
}
}
/// Create a new WellFormedTxContext, from a Tx and its priority.
pub fn from_tx(tx: &Tx, priority: u64) -> Self {
Self {
priority,
tx_hash: tx.tx_hash(),
tombstone_block: tx.prefix.tombstone_block,
key_images: tx.key_images(),
highest_indices: tx.get_membership_proof_highest_indices(),
output_public_keys: tx.output_public_keys(),
}
}
/// Get the tx_hash
pub fn tx_hash(&self) -> &TxHash {
&self.tx_hash
}
/// Get the priority
pub fn priority(&self) -> u64 {
self.priority
}
/// Get the tombstone block
pub fn tombstone_block(&self) -> u64 {
self.tombstone_block
}
/// Get the key images
pub fn key_images(&self) -> &Vec<KeyImage> {
&self.key_images
}
/// Get the highest indices
pub fn highest_indices(&self) -> &Vec<u64> {
&self.highest_indices
}
/// Get the output public keys
pub fn output_public_keys(&self) -> &Vec<CompressedRistrettoPublic> {
&self.output_public_keys
}
}
/// Defines a sort order for transactions in a block.
/// Transactions are sorted by priority(high to low), then by transaction hash
/// and any other fields.
///
/// Priority is a proxy for fee which is normalized across token ids.
impl Ord for WellFormedTxContext {
fn cmp(&self, other: &Self) -> Ordering {
if self.priority != other.priority {
// Sort by priority, descending.
other.priority.cmp(&self.priority)
} else {
// Sort by remaining fields in lexicographic order.
(
&self.tx_hash,
&self.tombstone_block,
&self.key_images,
&self.highest_indices,
&self.output_public_keys,
)
.cmp(&(
&other.tx_hash,
&other.tombstone_block,
&other.key_images,
&other.highest_indices,
&other.output_public_keys,
))
}
}
}
impl PartialOrd for WellFormedTxContext {
fn partial_cmp(&self, other: &WellFormedTxContext) -> Option<Ordering> {
Some(self.cmp(other))
}
}
/// An intermediate struct for holding data required to perform the two-step
/// is-well-formed test. This is returned by `txs_propose` and allows untrusted
/// to gather data required for the in-enclave well-formedness test that takes
/// place in `tx_is_well_formed`.
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct TxContext {
/// The Tx encrypted for the local enclave
pub locally_encrypted_tx: LocallyEncryptedTx,
/// The hash of the (unencrypted) Tx
pub tx_hash: TxHash,
/// The highest indices in the Tx merkle proof
pub highest_indices: Vec<u64>,
/// The key images appearing in the Tx
pub key_images: Vec<KeyImage>,
/// The output public keys appearing in the Tx
pub output_public_keys: Vec<CompressedRistrettoPublic>,
}
/// A type alias for the SGX sealed version of the block signing key of the
/// local enclave
pub type SealedBlockSigningKey = Vec<u8>;
/// PublicAddress is not serializable with serde currently, and rather than
/// pollute dependencies, we simply pass the View and Spend public keys as
/// RistrettoPublic.
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct FeePublicKey {
/// The spend public key of the fee address
pub spend_public_key: RistrettoPublic,
/// The view public key of the fee address
pub view_public_key: RistrettoPublic,
}
/// The collection of transaction types we form blocks from.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct FormBlockInputs {
/// The original transactions (the ones that are used to move tokens)
pub well_formed_encrypted_txs_with_proofs:
Vec<(WellFormedEncryptedTx, Vec<TxOutMembershipProof>)>,
/// Updating minting configuration transactions
pub mint_config_txs: Vec<MintConfigTx>,
/// Minting transactions coupled with configuration information.
pub mint_txs_with_config: Vec<(MintTx, MintConfigTx, MintConfig)>,
/// The timestamp to use for the block. ms since Unix epoch.
/// If timestamps are not supported by the block version this
/// value will be ignored and 0 will be used instead.
pub timestamp: u64,
}
/// The API for interacting with a consensus node's enclave.
pub trait ConsensusEnclave: ReportableEnclave {
// UTILITY METHODS
/// Perform one-time initialization upon enclave startup.
fn enclave_init(
&self,
self_peer_id: &ResponderId,
self_client_id: &ResponderId,
sealed_key: &Option<SealedBlockSigningKey>,
blockchain_config: BlockchainConfig,
) -> Result<(SealedBlockSigningKey, Vec<String>)>;
/// Retrieve the current minimum fee for a given token id.
/// Returns None if the token ID is not configured to have a minimum fee.
fn get_minimum_fee(&self, token_id: &TokenId) -> Result<Option<u64>>;
/// Retrieve the public identity of the enclave.
fn get_identity(&self) -> Result<X25519Public>;
/// Retrieve the block signing public key from the enclave.
fn get_signer(&self) -> Result<Ed25519Public>;
/// Retrieve the fee public key from the enclave.
fn get_fee_recipient(&self) -> Result<FeePublicKey>;
/// Retrieve the minting trust root public key from the enclave.
fn get_minting_trust_root(&self) -> Result<Ed25519Public>;
// CLIENT-FACING METHODS
/// Accept an inbound authentication request
fn client_accept(&self, req: ClientAuthRequest) -> Result<(ClientAuthResponse, ClientSession)>;
/// Destroy a peer association
fn client_close(&self, channel_id: ClientSession) -> Result<()>;
/// Decrypts a message from a client and then immediately discard it. This
/// is useful when we want to skip processing an incoming message, but
/// still properly maintain our AKE state in sync with the client.
fn client_discard_message(&self, msg: EnclaveMessage<ClientSession>) -> Result<()>;
// NODE-FACING METHODS
/// Start a new outbound connection.
fn peer_init(&self, peer_id: &ResponderId) -> Result<PeerAuthRequest>;
/// Accept an inbound authentication request
fn peer_accept(&self, req: PeerAuthRequest) -> Result<(PeerAuthResponse, PeerSession)>;
/// Complete the connection
fn peer_connect(
&self,
peer_id: &ResponderId,
res: PeerAuthResponse,
) -> Result<(PeerSession, EvidenceKind)>;
/// Destroy a peer association
fn peer_close(&self, channel_id: &PeerSession) -> Result<()>;
// TRANSACTION-HANDLING API
/// Performs the first steps in accepting transactions from a remote client:
/// 1) Re-encrypt all txs for the local enclave
/// 2) Extract context data to be handed back to untrusted so that it could
/// collect the information required by `tx_is_well_formed`.
fn client_tx_propose(&self, msg: EnclaveMessage<ClientSession>) -> Result<TxContext>;
/// Performs the first steps in accepting transactions from a remote peer:
/// 1) Re-encrypt all txs for the local enclave
/// 2) Extract context data to be handed back to untrusted so that it could
/// collect the information required by `tx_is_well_formed`.
/// TODO: rename to txs_propose since this operates on multiple txs?
fn peer_tx_propose(&self, msg: EnclaveMessage<PeerSession>) -> Result<Vec<TxContext>>;
/// Checks a LocallyEncryptedTx for well-formedness using the given
/// membership proofs and current block index.
fn tx_is_well_formed(
&self,
locally_encrypted_tx: LocallyEncryptedTx,
block_index: u64,
proofs: Vec<TxOutMembershipProof>,
) -> Result<(WellFormedEncryptedTx, WellFormedTxContext)>;
/// Re-encrypt sealed transactions for the given peer session, using the
/// given authenticated data for the peer.
fn txs_for_peer(
&self,
encrypted_txs: &[WellFormedEncryptedTx],
aad: &[u8],
peer: &PeerSession,
) -> Result<EnclaveMessage<PeerSession>>;
/// Redact txs in order to form a new block.
/// Returns a block, the block contents, and a signature over the block's
/// digest.
fn form_block(
&self,
parent_block: &Block,
inputs: FormBlockInputs,
root_element: &TxOutMembershipElement,
) -> Result<(Block, BlockContents, BlockSignature)>;
}
/// Helper trait which reduces boiler-plate in untrusted side
/// The trusted object which implements consensus_enclave usually cannot
/// implement Clone, Send, Sync, etc., but the untrusted side can and usually
/// having a "handle to an enclave" is what is most useful for a webserver.
/// This marker trait can be implemented for the untrusted-side representation
/// of the enclave.
pub trait ConsensusEnclaveProxy: ConsensusEnclave + Clone + Send + Sync + 'static {}
#[cfg(test)]
mod well_formed_tx_context_tests {
use crate::WellFormedTxContext;
use alloc::{vec, vec::Vec};
#[test]
/// WellFormedTxContext should be sorted by priority, descending.
fn test_ordering() {
let a = WellFormedTxContext::new(100, Default::default(), 0, vec![], vec![], vec![]);
let b = WellFormedTxContext::new(557, Default::default(), 0, vec![], vec![], vec![]);
let c = WellFormedTxContext::new(88, Default::default(), 0, vec![], vec![], vec![]);
let mut contexts = vec![a, b, c];
contexts.sort();
let priorities: Vec<_> = contexts.iter().map(|context| context.priority).collect();
let expected = vec![557, 100, 88];
assert_eq!(priorities, expected);
}
}