1use std::collections::HashMap;
2use std::sync::Arc;
3
4use unc_crypto::{EmptySigner, InMemorySigner, KeyType, PublicKey, SecretKey, Signature, Signer};
5use unc_primitives_core::account::id::AccountIdRef;
6use unc_primitives_core::types::{Power, ProtocolVersion};
7
8use crate::account::{AccessKey, AccessKeyPermission, Account};
9use crate::block::Block;
10use crate::block::BlockV3;
11use crate::block_header::BlockHeader;
12use crate::errors::EpochError;
13use crate::hash::CryptoHash;
14use crate::merkle::PartialMerkleTree;
15use crate::num_rational::Ratio;
16use crate::sharding::{ShardChunkHeader, ShardChunkHeaderV3};
17use crate::transaction::{
18 Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction,
19 DeployContractAction, FunctionCallAction, PledgeAction, SignedTransaction, Transaction,
20 TransferAction,
21};
22use crate::types::{AccountId, Balance, EpochId, EpochInfoProvider, Gas, Nonce};
23use crate::validator_signer::{InMemoryValidatorSigner, ValidatorSigner};
24use crate::version::PROTOCOL_VERSION;
25use crate::views::{ExecutionStatusView, FinalExecutionOutcomeView, FinalExecutionStatus};
26
27pub fn account_new(amount: Balance, code_hash: CryptoHash) -> Account {
28 Account::new(amount, 0, 0, code_hash, std::mem::size_of::<Account>() as u64)
29}
30
31impl Transaction {
32 pub fn new(
33 signer_id: AccountId,
34 public_key: PublicKey,
35 receiver_id: AccountId,
36 nonce: Nonce,
37 block_hash: CryptoHash,
38 ) -> Self {
39 Self { signer_id, public_key, nonce, receiver_id, block_hash, actions: vec![] }
40 }
41
42 pub fn sign(self, signer: &dyn Signer) -> SignedTransaction {
43 let signature = signer.sign(self.get_hash_and_size().0.as_ref());
44 SignedTransaction::new(signature, self)
45 }
46
47 pub fn create_account(mut self) -> Self {
48 self.actions.push(Action::CreateAccount(CreateAccountAction {}));
49 self
50 }
51
52 pub fn deploy_contract(mut self, code: Vec<u8>) -> Self {
53 self.actions.push(Action::DeployContract(DeployContractAction { code }));
54 self
55 }
56
57 pub fn function_call(
58 mut self,
59 method_name: String,
60 args: Vec<u8>,
61 gas: Gas,
62 deposit: Balance,
63 ) -> Self {
64 self.actions.push(Action::FunctionCall(Box::new(FunctionCallAction {
65 method_name,
66 args,
67 gas,
68 deposit,
69 })));
70 self
71 }
72
73 pub fn transfer(mut self, deposit: Balance) -> Self {
74 self.actions.push(Action::Transfer(TransferAction { deposit }));
75 self
76 }
77
78 pub fn pledge(mut self, pledge: Balance, public_key: PublicKey) -> Self {
79 self.actions.push(Action::Pledge(Box::new(PledgeAction { pledge, public_key })));
80 self
81 }
82 pub fn add_key(mut self, public_key: PublicKey, access_key: AccessKey) -> Self {
83 self.actions.push(Action::AddKey(Box::new(AddKeyAction { public_key, access_key })));
84 self
85 }
86
87 pub fn delete_key(mut self, public_key: PublicKey) -> Self {
88 self.actions.push(Action::DeleteKey(Box::new(DeleteKeyAction { public_key })));
89 self
90 }
91
92 pub fn delete_account(mut self, beneficiary_id: AccountId) -> Self {
93 self.actions.push(Action::DeleteAccount(DeleteAccountAction { beneficiary_id }));
94 self
95 }
96}
97
98impl SignedTransaction {
99 pub fn from_actions(
100 nonce: Nonce,
101 signer_id: AccountId,
102 receiver_id: AccountId,
103 signer: &dyn Signer,
104 actions: Vec<Action>,
105 block_hash: CryptoHash,
106 ) -> Self {
107 Transaction {
108 nonce,
109 signer_id,
110 public_key: signer.public_key(),
111 receiver_id,
112 block_hash,
113 actions,
114 }
115 .sign(signer)
116 }
117
118 pub fn send_money(
119 nonce: Nonce,
120 signer_id: AccountId,
121 receiver_id: AccountId,
122 signer: &dyn Signer,
123 deposit: Balance,
124 block_hash: CryptoHash,
125 ) -> Self {
126 Self::from_actions(
127 nonce,
128 signer_id,
129 receiver_id,
130 signer,
131 vec![Action::Transfer(TransferAction { deposit })],
132 block_hash,
133 )
134 }
135
136 pub fn pledge(
137 nonce: Nonce,
138 signer_id: AccountId,
139 signer: &dyn Signer,
140 pledge: Balance,
141 public_key: PublicKey,
142 block_hash: CryptoHash,
143 ) -> Self {
144 Self::from_actions(
145 nonce,
146 signer_id.clone(),
147 signer_id,
148 signer,
149 vec![Action::Pledge(Box::new(PledgeAction { pledge, public_key }))],
150 block_hash,
151 )
152 }
153
154 pub fn create_account(
155 nonce: Nonce,
156 originator: AccountId,
157 new_account_id: AccountId,
158 amount: Balance,
159 public_key: PublicKey,
160 signer: &dyn Signer,
161 block_hash: CryptoHash,
162 ) -> Self {
163 Self::from_actions(
164 nonce,
165 originator,
166 new_account_id,
167 signer,
168 vec![
169 Action::CreateAccount(CreateAccountAction {}),
170 Action::AddKey(Box::new(AddKeyAction {
171 public_key,
172 access_key: AccessKey { nonce: 0, permission: AccessKeyPermission::FullAccess },
173 })),
174 Action::Transfer(TransferAction { deposit: amount }),
175 ],
176 block_hash,
177 )
178 }
179
180 pub fn create_contract(
181 nonce: Nonce,
182 originator: AccountId,
183 new_account_id: AccountId,
184 code: Vec<u8>,
185 amount: Balance,
186 public_key: PublicKey,
187 signer: &dyn Signer,
188 block_hash: CryptoHash,
189 ) -> Self {
190 Self::from_actions(
191 nonce,
192 originator,
193 new_account_id,
194 signer,
195 vec![
196 Action::CreateAccount(CreateAccountAction {}),
197 Action::AddKey(Box::new(AddKeyAction {
198 public_key,
199 access_key: AccessKey { nonce: 0, permission: AccessKeyPermission::FullAccess },
200 })),
201 Action::Transfer(TransferAction { deposit: amount }),
202 Action::DeployContract(DeployContractAction { code }),
203 ],
204 block_hash,
205 )
206 }
207
208 pub fn call(
209 nonce: Nonce,
210 signer_id: AccountId,
211 receiver_id: AccountId,
212 signer: &dyn Signer,
213 deposit: Balance,
214 method_name: String,
215 args: Vec<u8>,
216 gas: Gas,
217 block_hash: CryptoHash,
218 ) -> Self {
219 Self::from_actions(
220 nonce,
221 signer_id,
222 receiver_id,
223 signer,
224 vec![Action::FunctionCall(Box::new(FunctionCallAction {
225 args,
226 method_name,
227 gas,
228 deposit,
229 }))],
230 block_hash,
231 )
232 }
233
234 pub fn delete_account(
235 nonce: Nonce,
236 signer_id: AccountId,
237 receiver_id: AccountId,
238 beneficiary_id: AccountId,
239 signer: &dyn Signer,
240 block_hash: CryptoHash,
241 ) -> Self {
242 Self::from_actions(
243 nonce,
244 signer_id,
245 receiver_id,
246 signer,
247 vec![Action::DeleteAccount(DeleteAccountAction { beneficiary_id })],
248 block_hash,
249 )
250 }
251
252 pub fn empty(block_hash: CryptoHash) -> Self {
253 Self::from_actions(
254 0,
255 "test".parse().unwrap(),
256 "test".parse().unwrap(),
257 &EmptySigner {},
258 vec![],
259 block_hash,
260 )
261 }
262}
263
264impl Block {
265 pub fn get_mut(&mut self) -> &mut BlockV3 {
266 match self {
267 Block::BlockV1(_) | Block::BlockV2(_) => {
268 panic!("older block version should not appear in tests")
269 }
270 Block::BlockV3(block) => Arc::make_mut(block),
271 }
272 }
273}
274
275impl BlockHeader {
276 pub fn get_mut(&mut self) -> &mut crate::block_header::BlockHeaderV4 {
277 match self {
278 BlockHeader::BlockHeaderV1(_)
279 | BlockHeader::BlockHeaderV2(_)
280 | BlockHeader::BlockHeaderV3(_) => {
281 panic!("old header should not appear in tests")
282 }
283 BlockHeader::BlockHeaderV4(header) => Arc::make_mut(header),
284 }
285 }
286
287 pub fn set_latest_protocol_version(&mut self, latest_protocol_version: ProtocolVersion) {
288 match self {
289 BlockHeader::BlockHeaderV1(header) => {
290 let header = Arc::make_mut(header);
291 header.inner_rest.latest_protocol_version = latest_protocol_version;
292 }
293 BlockHeader::BlockHeaderV2(header) => {
294 let header = Arc::make_mut(header);
295 header.inner_rest.latest_protocol_version = latest_protocol_version;
296 }
297 BlockHeader::BlockHeaderV3(header) => {
298 let header = Arc::make_mut(header);
299 header.inner_rest.latest_protocol_version = latest_protocol_version;
300 }
301 BlockHeader::BlockHeaderV4(header) => {
302 let header = Arc::make_mut(header);
303 header.inner_rest.latest_protocol_version = latest_protocol_version;
304 }
305 }
306 }
307
308 pub fn resign(&mut self, signer: &dyn ValidatorSigner) {
309 let (hash, signature) = signer.sign_block_header_parts(
310 *self.prev_hash(),
311 &self.inner_lite_bytes(),
312 &self.inner_rest_bytes(),
313 );
314 match self {
315 BlockHeader::BlockHeaderV1(header) => {
316 let header = Arc::make_mut(header);
317 header.hash = hash;
318 header.signature = signature;
319 }
320 BlockHeader::BlockHeaderV2(header) => {
321 let header = Arc::make_mut(header);
322 header.hash = hash;
323 header.signature = signature;
324 }
325 BlockHeader::BlockHeaderV3(header) => {
326 let header = Arc::make_mut(header);
327 header.hash = hash;
328 header.signature = signature;
329 }
330 BlockHeader::BlockHeaderV4(header) => {
331 let header = Arc::make_mut(header);
332 header.hash = hash;
333 header.signature = signature;
334 }
335 }
336 }
337}
338
339impl ShardChunkHeader {
340 pub fn get_mut(&mut self) -> &mut ShardChunkHeaderV3 {
341 match self {
342 ShardChunkHeader::V1(_) | ShardChunkHeader::V2(_) => {
343 unreachable!("old header should not appear in tests")
344 }
345 ShardChunkHeader::V3(chunk) => chunk,
346 }
347 }
348}
349pub struct TestBlockBuilder {
358 prev: Block,
359 signer: Arc<dyn ValidatorSigner>,
360 height: u64,
361 epoch_id: EpochId,
362 next_epoch_id: EpochId,
363 next_bp_hash: CryptoHash,
364 approvals: Vec<Option<Box<Signature>>>,
365 block_merkle_root: CryptoHash,
366}
367
368impl TestBlockBuilder {
369 pub fn new(prev: &Block, signer: Arc<dyn ValidatorSigner>) -> Self {
370 let mut tree = PartialMerkleTree::default();
371 tree.insert(*prev.hash());
372
373 Self {
374 prev: prev.clone(),
375 signer: signer.clone(),
376 height: prev.header().height() + 1,
377 epoch_id: prev.header().epoch_id().clone(),
378 next_epoch_id: if prev.header().prev_hash() == &CryptoHash::default() {
379 EpochId(*prev.hash())
380 } else {
381 prev.header().next_epoch_id().clone()
382 },
383 next_bp_hash: *prev.header().next_bp_hash(),
384 approvals: vec![],
385 block_merkle_root: tree.root(),
386 }
387 }
388 pub fn height(mut self, height: u64) -> Self {
389 self.height = height;
390 self
391 }
392 pub fn epoch_id(mut self, epoch_id: EpochId) -> Self {
393 self.epoch_id = epoch_id;
394 self
395 }
396 pub fn next_epoch_id(mut self, next_epoch_id: EpochId) -> Self {
397 self.next_epoch_id = next_epoch_id;
398 self
399 }
400 pub fn next_bp_hash(mut self, next_bp_hash: CryptoHash) -> Self {
401 self.next_bp_hash = next_bp_hash;
402 self
403 }
404 pub fn approvals(mut self, approvals: Vec<Option<Box<Signature>>>) -> Self {
405 self.approvals = approvals;
406 self
407 }
408
409 pub fn block_merkle_tree(mut self, block_merkle_tree: &mut PartialMerkleTree) -> Self {
411 block_merkle_tree.insert(*self.prev.hash());
412 self.block_merkle_root = block_merkle_tree.root();
413 self
414 }
415
416 pub fn build(self) -> Block {
417 tracing::debug!(target: "test", height=self.height, ?self.epoch_id, "produce block");
418 Block::produce(
419 PROTOCOL_VERSION,
420 PROTOCOL_VERSION,
421 self.prev.header(),
422 self.height,
423 self.prev.header().block_ordinal() + 1,
424 self.prev.chunks().iter().cloned().collect(),
425 self.epoch_id,
426 self.next_epoch_id,
427 None,
428 self.approvals,
429 Ratio::new(0, 1),
430 0,
431 0,
432 Some(0),
433 vec![],
434 vec![],
435 self.signer.as_ref(),
436 self.next_bp_hash,
437 self.block_merkle_root,
438 None,
439 )
440 }
441}
442
443impl Block {
444 pub fn mut_header(&mut self) -> &mut BlockHeader {
445 match self {
446 Block::BlockV1(block) => {
447 let block = Arc::make_mut(block);
448 &mut block.header
449 }
450 Block::BlockV2(block) => {
451 let block = Arc::make_mut(block);
452 &mut block.header
453 }
454 Block::BlockV3(block) => {
455 let block = Arc::make_mut(block);
456 &mut block.header
457 }
458 }
459 }
460
461 pub fn set_chunks(&mut self, chunks: Vec<ShardChunkHeader>) {
462 match self {
463 Block::BlockV1(block) => {
464 let block = Arc::make_mut(block);
465 let legacy_chunks = chunks
466 .into_iter()
467 .map(|chunk| match chunk {
468 ShardChunkHeader::V1(header) => header,
469 ShardChunkHeader::V2(_) => {
470 panic!("Attempted to set V1 block chunks with V2")
471 }
472 ShardChunkHeader::V3(_) => {
473 panic!("Attempted to set V1 block chunks with V3")
474 }
475 })
476 .collect();
477 block.chunks = legacy_chunks;
478 }
479 Block::BlockV2(block) => {
480 let block = Arc::make_mut(block);
481 block.chunks = chunks;
482 }
483 Block::BlockV3(block) => {
484 let block = Arc::make_mut(block);
485 block.body.chunks = chunks;
486 }
487 }
488 }
489}
490
491#[derive(Default)]
492pub struct MockEpochInfoProvider {
493 pub validators: HashMap<AccountId, (Power, Balance)>,
494}
495
496impl MockEpochInfoProvider {
497 pub fn new(validators: impl Iterator<Item = (AccountId, (Power, Balance))>) -> Self {
498 MockEpochInfoProvider { validators: validators.collect() }
499 }
500}
501
502impl EpochInfoProvider for MockEpochInfoProvider {
503 fn validator_power(
504 &self,
505 _epoch_id: &EpochId,
506 _last_block_hash: &CryptoHash,
507 account_id: &AccountId,
508 ) -> Result<Option<Power>, EpochError> {
509 if let Some((power, _balance)) = self.validators.get(account_id) {
510 Ok(Some(power).cloned())
511 } else {
512 Ok(None)
513 }
514 }
515
516 fn validator_total_power(
517 &self,
518 _epoch_id: &EpochId,
519 _last_block_hash: &CryptoHash,
520 ) -> Result<Power, EpochError> {
521 let total_power: Power = self.validators.values().map(|(power, _)| power).sum();
522 Ok(total_power)
523 }
524
525 fn minimum_power(&self, _prev_block_hash: &CryptoHash) -> Result<Power, EpochError> {
526 Ok(0)
527 }
528
529 fn validator_stake(
530 &self,
531 _epoch_id: &EpochId,
532 _last_block_hash: &CryptoHash,
533 account_id: &AccountId,
534 ) -> Result<Option<Balance>, EpochError> {
535 if let Some((_power, balance)) = self.validators.get(account_id) {
536 Ok(Some(balance).cloned())
537 } else {
538 Ok(None)
539 }
540 }
541
542 fn validator_total_stake(
543 &self,
544 _epoch_id: &EpochId,
545 _last_block_hash: &CryptoHash,
546 ) -> Result<Balance, EpochError> {
547 let total_pledge: Balance = self.validators.values().map(|(_, pledge)| pledge).sum();
548 Ok(total_pledge)
549 }
550
551 fn minimum_pledge(&self, _prev_block_hash: &CryptoHash) -> Result<Balance, EpochError> {
552 Ok(0)
553 }
554}
555
556pub fn encode(xs: &[u64]) -> Vec<u8> {
558 xs.iter().flat_map(|it| it.to_le_bytes()).collect()
559}
560
561pub fn create_test_signer(account_name: &str) -> InMemoryValidatorSigner {
564 InMemoryValidatorSigner::from_seed(
565 account_name.parse().unwrap(),
566 KeyType::ED25519,
567 account_name,
568 )
569}
570
571pub fn create_user_test_signer(account_name: &AccountIdRef) -> InMemorySigner {
577 let account_id = account_name.to_owned();
578 if account_id == unc_implicit_test_account() {
579 InMemorySigner::from_secret_key(account_id, unc_implicit_test_account_secret())
580 } else {
581 InMemorySigner::from_seed(account_id, KeyType::ED25519, account_name.as_str())
582 }
583}
584
585pub fn unc_implicit_test_account() -> AccountId {
587 "061b1dd17603213b00e1a1e53ba060ad427cef4887bd34a5e0ef09010af23b0a".parse().unwrap()
588}
589
590pub fn unc_implicit_test_account_secret() -> SecretKey {
592 "ed25519:5roj6k68kvZu3UEJFyXSfjdKGrodgZUfFLZFpzYXWtESNsLWhYrq3JGi4YpqeVKuw1m9R2TEHjfgWT1fjUqB1DNy".parse().unwrap()
593}
594
595pub fn eth_implicit_test_account() -> AccountId {
597 "0x96791e923f8cf697ad9c3290f2c9059f0231b24c".parse().unwrap()
598}
599
600impl FinalExecutionOutcomeView {
601 #[track_caller]
602 pub fn assert_success(&self) {
604 assert!(matches!(self.status, FinalExecutionStatus::SuccessValue(_)));
605 for (i, receipt) in self.receipts_outcome.iter().enumerate() {
606 assert!(
607 matches!(
608 receipt.outcome.status,
609 ExecutionStatusView::SuccessReceiptId(_) | ExecutionStatusView::SuccessValue(_),
610 ),
611 "receipt #{i} failed: {receipt:?}",
612 );
613 }
614 }
615}