1use thruput_json_rpc_types::error::TxInvalidDetail;
28use thruput_rv32_artifact::{
29 DEFAULT_MAX_CODE_BYTES, DEFAULT_MEMORY_BYTES, DeclaredSizes, declared_sizes,
30};
31use thruput_xdr::{
32 contract::{Contract, kDefaultMaxContractSizeBytes},
33 transaction::Transaction,
34};
35
36const DEFAULT_MAX_RV32_CODE_BYTES: u32 = 262_144;
42const DEFAULT_MAX_RV32_MEMORY_BYTES: u32 = 65_536;
45const DEFAULT_MAX_TX_GAS: u64 = 1_000_000_000;
49const _: () = assert!(DEFAULT_MAX_RV32_CODE_BYTES as usize == DEFAULT_MAX_CODE_BYTES);
50const _: () = assert!(DEFAULT_MAX_RV32_MEMORY_BYTES as usize == DEFAULT_MEMORY_BYTES);
51
52pub use thruput_json_rpc_types::network_config::{NetworkConfigParams, UnsupportedNetworkConfig};
53
54fn max_contract_size_bytes_in_force(params: &NetworkConfigParams) -> u32 {
61 params
62 .max_contract_size_bytes
63 .unwrap_or(kDefaultMaxContractSizeBytes)
64}
65
66fn max_rv32_code_bytes_in_force(params: &NetworkConfigParams) -> u32 {
72 params
73 .max_rv32_code_bytes
74 .unwrap_or(DEFAULT_MAX_RV32_CODE_BYTES)
75}
76
77fn max_tx_gas_in_force(params: &NetworkConfigParams) -> u64 {
80 params.max_tx_gas.unwrap_or(DEFAULT_MAX_TX_GAS)
81}
82
83fn max_rv32_memory_bytes_in_force(params: &NetworkConfigParams) -> u32 {
86 params
87 .max_rv32_memory_bytes
88 .unwrap_or(DEFAULT_MAX_RV32_MEMORY_BYTES)
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
104#[non_exhaustive]
105pub enum ConfigParamName {
106 MaxSha256Buffers,
108 MaxCrossInvokeDepth,
110 MaxNewContractsPerTx,
112 MaxBytestringLen,
114 MaxContractSizeBytes,
116 MaxSavepointDepth,
118 MaxAuthTokenDepth,
120 MaxRv32CodeBytes,
122 MaxRv32MemoryBytes,
124 MaxTxGas,
126}
127
128impl ConfigParamName {
129 pub const ALL: &[Self] = &[
131 Self::MaxSha256Buffers,
132 Self::MaxCrossInvokeDepth,
133 Self::MaxNewContractsPerTx,
134 Self::MaxBytestringLen,
135 Self::MaxContractSizeBytes,
136 Self::MaxSavepointDepth,
137 Self::MaxAuthTokenDepth,
138 Self::MaxRv32CodeBytes,
139 Self::MaxRv32MemoryBytes,
140 Self::MaxTxGas,
141 ];
142
143 pub const fn as_str(self) -> &'static str {
145 match self {
146 Self::MaxSha256Buffers => "max_sha256_buffers",
147 Self::MaxCrossInvokeDepth => "max_cross_invoke_depth",
148 Self::MaxNewContractsPerTx => "max_new_contracts_per_tx",
149 Self::MaxBytestringLen => "max_bytestring_len",
150 Self::MaxContractSizeBytes => "max_contract_size_bytes",
151 Self::MaxSavepointDepth => "max_savepoint_depth",
152 Self::MaxAuthTokenDepth => "max_auth_token_depth",
153 Self::MaxRv32CodeBytes => "max_rv32_code_bytes",
154 Self::MaxRv32MemoryBytes => "max_rv32_memory_bytes",
155 Self::MaxTxGas => "max_tx_gas",
156 }
157 }
158
159 pub fn stated_in(self, params: &NetworkConfigParams) -> Option<u64> {
164 match self {
165 Self::MaxSha256Buffers => params.max_sha256_buffers.map(u64::from),
166 Self::MaxCrossInvokeDepth => params.max_cross_invoke_depth.map(u64::from),
167 Self::MaxNewContractsPerTx => params.max_new_contracts_per_tx.map(u64::from),
168 Self::MaxBytestringLen => params.max_bytestring_len.map(u64::from),
169 Self::MaxContractSizeBytes => params.max_contract_size_bytes.map(u64::from),
170 Self::MaxSavepointDepth => params.max_savepoint_depth.map(u64::from),
171 Self::MaxAuthTokenDepth => params.max_auth_token_depth.map(u64::from),
172 Self::MaxRv32CodeBytes => params.max_rv32_code_bytes.map(u64::from),
173 Self::MaxRv32MemoryBytes => params.max_rv32_memory_bytes.map(u64::from),
174 Self::MaxTxGas => params.max_tx_gas,
175 }
176 }
177
178 pub fn known_names() -> String {
180 Self::ALL
181 .iter()
182 .copied()
183 .map(Self::as_str)
184 .collect::<Vec<_>>()
185 .join(", ")
186 }
187}
188
189impl std::fmt::Display for ConfigParamName {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 f.write_str(self.as_str())
192 }
193}
194
195impl std::str::FromStr for ConfigParamName {
196 type Err = UnknownConfigParamName;
197
198 fn from_str(name: &str) -> Result<Self, Self::Err> {
199 Self::ALL
200 .iter()
201 .copied()
202 .find(|param| param.as_str() == name)
203 .ok_or_else(|| UnknownConfigParamName {
204 name: name.to_string(),
205 })
206 }
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
211#[error(
212 "unknown network parameter {name:?}; this build knows {}",
213 ConfigParamName::known_names()
214)]
215pub struct UnknownConfigParamName {
216 pub name: String,
218}
219
220pub fn check_transaction(
233 tx: &Transaction,
234 params: &NetworkConfigParams,
235) -> Result<(), TxInvalidDetail> {
236 let max_gas = max_tx_gas_in_force(params);
237 if tx.invocation.gas_limit > max_gas {
238 return Err(TxInvalidDetail::GasLimitTooHigh {
239 gas_limit: tx.invocation.gas_limit,
240 max_gas,
241 });
242 }
243
244 let contracts = &tx.contracts_to_deploy.0;
245 let attached = u32::try_from(contracts.len()).unwrap_or(u32::MAX);
246
247 if let Some(max) = params.max_new_contracts_per_tx
248 && attached > max
249 {
250 return Err(TxInvalidDetail::TooManyContracts { attached, max });
251 }
252
253 let max_bytes = max_contract_size_bytes_in_force(params);
254 for (index, contract) in contracts.iter().enumerate() {
255 let index = u32::try_from(index).unwrap_or(u32::MAX);
256 let size_bytes = u32::try_from(contract_code_size(contract)).unwrap_or(u32::MAX);
257 if size_bytes > max_bytes {
258 return Err(TxInvalidDetail::ContractTooLarge {
259 index,
260 size_bytes,
261 max_bytes,
262 });
263 }
264 }
265
266 for (index, contract) in contracts.iter().enumerate() {
271 if let Some(first_index) = contracts[..index].iter().position(|seen| seen == contract) {
272 return Err(TxInvalidDetail::DuplicateContract {
273 index: u32::try_from(index).unwrap_or(u32::MAX),
274 first_index: u32::try_from(first_index).unwrap_or(u32::MAX),
275 });
276 }
277 }
278
279 let max_memory_bytes = max_rv32_memory_bytes_in_force(params);
283 let max_code_bytes = max_rv32_code_bytes_in_force(params);
284 for (index, contract) in contracts.iter().enumerate() {
285 let Some(sizes) = rv32_declared_sizes(contract) else {
286 continue;
287 };
288 let index = u32::try_from(index).unwrap_or(u32::MAX);
289 if sizes.memory_bytes > max_memory_bytes {
290 return Err(TxInvalidDetail::Rv32MemoryTooLarge {
291 index,
292 memory_bytes: sizes.memory_bytes,
293 max_bytes: max_memory_bytes,
294 });
295 }
296 if sizes.code_bytes > max_code_bytes {
297 return Err(TxInvalidDetail::Rv32CodeTooLarge {
298 index,
299 code_bytes: sizes.code_bytes,
300 max_bytes: max_code_bytes,
301 });
302 }
303 }
304
305 Ok(())
306}
307
308fn rv32_declared_sizes(contract: &Contract) -> Option<DeclaredSizes> {
314 match contract {
315 Contract::kRv32 { rv32_contract } => declared_sizes(&rv32_contract.data.0),
316 _ => None,
317 }
318}
319
320fn contract_code_size(contract: &Contract) -> usize {
326 match contract {
327 Contract::kRv32 { rv32_contract } => rv32_contract.data.len(),
328 _ => 0,
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use thruput_xdr::{
335 contract::ContractBlob,
336 network_config::{ConfigParam, NetworkConfig, NetworkConfigV0},
337 precompiles::HogexecPrecompiledNativeContracts,
338 transaction::SignedTransaction,
339 };
340 use xdr_common::{BytesM, VecM};
341
342 use super::*;
343
344 const CANONICAL_SIGNED_TX_XDR: &str = "\
347bc10f38522c4e14265a1483af9e5375f5dea0e2c6bf25727ee8b429bf3c1a9b7\
348000000000000000000000000000f42400000000000000000000f424000000000000000010000000000000001\
34913df30e7ceeb7c4c78ba6287e9d8830cabf5a744f3d6be3139f2428d7bb4f9e7000000000000000000000001\
350000000000000000041d0144af2776cae9804ed899018a6bc17a5705d74c84e02556b9695031c9d3fe2554e5c\
351e62ceadc95ba43c4aa0884cd2f9ebea4e3845ef877761a6222e91f0d000000010000000050ec72dbafd4898c\
3525df0e15ea6ba3e46240945b7210fe2d63dde1e896f42107a";
353
354 fn transaction_with(contracts: Vec<Contract>) -> Transaction {
355 let canonical = hex::decode(CANONICAL_SIGNED_TX_XDR).unwrap();
356 let mut signed: SignedTransaction = xdrs::txs::decode(&canonical).unwrap();
357 signed.tx.contracts_to_deploy = VecM(contracts);
358 signed.tx
359 }
360
361 fn rv32(size: usize, fill: u8) -> Contract {
362 Contract::kRv32 {
363 rv32_contract: ContractBlob {
364 data: BytesM(vec![fill; size]),
365 },
366 }
367 }
368
369 fn network_config(params: Vec<ConfigParam>) -> NetworkConfig {
370 NetworkConfig::_0 {
371 v0: NetworkConfigV0 {
372 params: VecM(params),
373 },
374 }
375 }
376
377 fn stated(name: ConfigParamName, value: u32) -> ConfigParam {
383 match name {
384 ConfigParamName::MaxSha256Buffers => ConfigParam::kConfigParamMaxSHA256Buffers {
385 max_sha256_buffers: value,
386 },
387 ConfigParamName::MaxCrossInvokeDepth => ConfigParam::kConfigParamMaxCrossInvokeDepth {
388 max_cross_invoke_depth: value,
389 },
390 ConfigParamName::MaxNewContractsPerTx => {
391 ConfigParam::kConfigParamMaxNewContractsPerTx {
392 max_new_contracts_per_tx: value,
393 }
394 }
395 ConfigParamName::MaxBytestringLen => ConfigParam::kConfigParamMaxBytestringLen {
396 max_bytestring_len: value,
397 },
398 ConfigParamName::MaxContractSizeBytes => {
399 ConfigParam::kConfigParamMaxContractSizeBytes {
400 max_contract_size_bytes: value,
401 }
402 }
403 ConfigParamName::MaxSavepointDepth => ConfigParam::kConfigParamMaxSavepointDepth {
404 max_savepoint_depth: value,
405 },
406 ConfigParamName::MaxAuthTokenDepth => ConfigParam::kConfigParamMaxAuthTokenDepth {
407 max_auth_token_depth: value,
408 },
409 ConfigParamName::MaxRv32CodeBytes => ConfigParam::kConfigParamMaxRv32CodeBytes {
410 max_rv32_code_bytes: value,
411 },
412 ConfigParamName::MaxRv32MemoryBytes => ConfigParam::kConfigParamMaxRv32MemoryBytes {
413 max_rv32_memory_bytes: value,
414 },
415 ConfigParamName::MaxTxGas => ConfigParam::kConfigParamMaxTxGas {
416 max_tx_gas: u64::from(value),
417 },
418 }
419 }
420
421 #[test]
422 fn every_named_parameter_flattens_onto_the_field_it_names() {
423 for (name, value) in ConfigParamName::ALL.iter().copied().zip(1_000u32..) {
424 let params = NetworkConfigParams::from_network_config(&network_config(vec![stated(
425 name, value,
426 )]))
427 .unwrap();
428
429 assert_eq!(name.stated_in(¶ms), Some(u64::from(value)), "{name}");
430 for other in ConfigParamName::ALL.iter().copied() {
431 if other != name {
432 assert_eq!(other.stated_in(¶ms), None, "{name} also stated {other}");
433 }
434 }
435 }
436 }
437
438 #[test]
439 fn names_round_trip_and_an_unknown_one_lists_the_known_names() {
440 for name in ConfigParamName::ALL.iter().copied() {
441 assert_eq!(name.as_str().parse::<ConfigParamName>(), Ok(name));
442 assert_eq!(name.to_string(), name.as_str());
443 }
444
445 let error = "max_rv32_memmory_bytes"
446 .parse::<ConfigParamName>()
447 .unwrap_err();
448
449 assert_eq!(error.name, "max_rv32_memmory_bytes");
450 assert!(
451 error.to_string().contains("max_rv32_memory_bytes"),
452 "{error}"
453 );
454 }
455
456 fn tx_gas_cap(max_tx_gas: u64) -> NetworkConfigParams {
457 NetworkConfigParams {
458 max_tx_gas: Some(max_tx_gas),
459 ..NetworkConfigParams::default()
460 }
461 }
462
463 fn transaction_budgeting(gas_limit: u64) -> Transaction {
464 let mut tx = transaction_with(vec![]);
465 tx.invocation.gas_limit = gas_limit;
466 tx
467 }
468
469 #[test]
470 fn root_gas_limit_is_bounded_by_the_default_cap_when_unstated() {
471 let params = NetworkConfigParams::default();
472 assert_eq!(
473 check_transaction(&transaction_budgeting(1_000_000_000), ¶ms),
474 Ok(())
475 );
476 assert_eq!(
477 check_transaction(&transaction_budgeting(1_000_000_001), ¶ms),
478 Err(TxInvalidDetail::GasLimitTooHigh {
479 gas_limit: 1_000_000_001,
480 max_gas: 1_000_000_000,
481 })
482 );
483 assert_eq!(
485 check_transaction(&transaction_budgeting(u64::MAX), ¶ms),
486 Err(TxInvalidDetail::GasLimitTooHigh {
487 gas_limit: u64::MAX,
488 max_gas: 1_000_000_000,
489 })
490 );
491 }
492
493 #[test]
494 fn root_gas_limit_is_bounded_by_the_stated_cap() {
495 assert_eq!(
497 check_transaction(&transaction_budgeting(500_001), &tx_gas_cap(500_000)),
498 Err(TxInvalidDetail::GasLimitTooHigh {
499 gas_limit: 500_001,
500 max_gas: 500_000,
501 })
502 );
503 assert_eq!(
505 check_transaction(
506 &transaction_budgeting(5_000_000_000),
507 &tx_gas_cap(5_000_000_000)
508 ),
509 Ok(())
510 );
511 }
512
513 #[test]
514 fn gas_limit_is_checked_before_attachments() {
515 let mut tx = transaction_with(vec![rv32(8, 1), rv32(8, 2)]);
519 tx.invocation.gas_limit = 1_000_000_001;
520 assert_eq!(
521 check_transaction(&tx, &max_contracts(1)),
522 Err(TxInvalidDetail::GasLimitTooHigh {
523 gas_limit: 1_000_000_001,
524 max_gas: 1_000_000_000,
525 })
526 );
527 }
528
529 fn max_contracts(max_new_contracts_per_tx: u32) -> NetworkConfigParams {
530 NetworkConfigParams {
531 max_new_contracts_per_tx: Some(max_new_contracts_per_tx),
532 ..NetworkConfigParams::default()
533 }
534 }
535
536 fn contract_size_cap(max_contract_size_bytes: u32) -> NetworkConfigParams {
537 NetworkConfigParams {
538 max_contract_size_bytes: Some(max_contract_size_bytes),
539 ..NetworkConfigParams::default()
540 }
541 }
542
543 fn rv32_caps(
544 max_rv32_code_bytes: Option<u32>,
545 max_rv32_memory_bytes: Option<u32>,
546 ) -> NetworkConfigParams {
547 NetworkConfigParams {
548 max_rv32_code_bytes,
549 max_rv32_memory_bytes,
550 ..NetworkConfigParams::default()
551 }
552 }
553
554 fn artifact(code_bytes: usize, memory_bytes: usize) -> Contract {
557 let artifact =
558 thruput_rv32_artifact::build_artifact(&vec![0u8; code_bytes], &[], 0, memory_bytes)
559 .unwrap();
560 Contract::kRv32 {
561 rv32_contract: ContractBlob {
562 data: BytesM(artifact),
563 },
564 }
565 }
566
567 #[test]
568 fn every_stated_parameter_lands_on_its_field() {
569 let config = network_config(vec![
570 ConfigParam::kConfigParamMaxSHA256Buffers {
571 max_sha256_buffers: 3,
572 },
573 ConfigParam::kConfigParamMaxCrossInvokeDepth {
574 max_cross_invoke_depth: 17,
575 },
576 ConfigParam::kConfigParamMaxNewContractsPerTx {
577 max_new_contracts_per_tx: 7,
578 },
579 ConfigParam::kConfigParamMaxBytestringLen {
580 max_bytestring_len: 4096,
581 },
582 ConfigParam::kConfigParamMaxContractSizeBytes {
583 max_contract_size_bytes: 131_072,
584 },
585 ConfigParam::kConfigParamMaxSavepointDepth {
586 max_savepoint_depth: 9,
587 },
588 ConfigParam::kConfigParamMaxAuthTokenDepth {
589 max_auth_token_depth: 5,
590 },
591 ConfigParam::kConfigParamMaxRv32CodeBytes {
592 max_rv32_code_bytes: 65536,
593 },
594 ConfigParam::kConfigParamMaxRv32MemoryBytes {
595 max_rv32_memory_bytes: 1_048_576,
596 },
597 ConfigParam::kConfigParamMaxAuthTokens {
598 max_auth_tokens: 64,
599 },
600 ConfigParam::kConfigParamMaxCalldataLen {
601 max_calldata_len: 65_536,
602 },
603 ConfigParam::kConfigParamMaxReturnBufferLen {
604 max_return_buffer_len: 1024,
605 },
606 ConfigParam::kConfigParamMaxTxGas {
607 max_tx_gas: 5_000_000_000,
608 },
609 ]);
610
611 let params = NetworkConfigParams::from_network_config(&config).unwrap();
612
613 assert_eq!(
614 params,
615 NetworkConfigParams {
616 max_sha256_buffers: Some(3),
617 max_cross_invoke_depth: Some(17),
618 max_new_contracts_per_tx: Some(7),
619 max_bytestring_len: Some(4096),
620 max_contract_size_bytes: Some(131_072),
621 max_savepoint_depth: Some(9),
622 max_auth_token_depth: Some(5),
623 max_rv32_code_bytes: Some(65536),
624 max_rv32_memory_bytes: Some(1_048_576),
625 max_tx_gas: Some(5_000_000_000),
626 }
627 );
628 }
629
630 #[test]
631 fn unstated_parameters_stay_unset() {
632 let config = network_config(vec![
635 ConfigParam::kConfigParamMaxSHA256Buffers {
636 max_sha256_buffers: 8,
637 },
638 ConfigParam::kConfigParamMaxCrossInvokeDepth {
639 max_cross_invoke_depth: 64,
640 },
641 ]);
642
643 let params = NetworkConfigParams::from_network_config(&config).unwrap();
644
645 assert_eq!(
646 params,
647 NetworkConfigParams {
648 max_sha256_buffers: Some(8),
649 max_cross_invoke_depth: Some(64),
650 ..NetworkConfigParams::default()
651 }
652 );
653 assert_eq!(
654 NetworkConfigParams::from_network_config(&network_config(vec![])).unwrap(),
655 NetworkConfigParams::default()
656 );
657 }
658
659 #[test]
660 fn contract_size_in_force_is_the_stated_value_or_the_protocol_default() {
661 assert_eq!(
662 max_contract_size_bytes_in_force(&NetworkConfigParams::default()),
663 kDefaultMaxContractSizeBytes
664 );
665 assert_eq!(
666 max_contract_size_bytes_in_force(&contract_size_cap(1024)),
667 1024
668 );
669 }
670
671 #[test]
672 fn transaction_within_bounds_passes() {
673 let tx = transaction_with(vec![
674 rv32(16, 1),
675 rv32(kDefaultMaxContractSizeBytes as usize, 2),
676 ]);
677
678 assert_eq!(check_transaction(&tx, &max_contracts(2)), Ok(()));
679 assert_eq!(
680 check_transaction(&tx, &NetworkConfigParams::default()),
681 Ok(())
682 );
683 }
684
685 #[test]
686 fn too_many_contracts_is_rejected_only_when_the_bound_is_stated() {
687 let tx = transaction_with(vec![rv32(1, 1), rv32(1, 2), rv32(1, 3)]);
688
689 assert_eq!(
690 check_transaction(&tx, &max_contracts(2)),
691 Err(TxInvalidDetail::TooManyContracts {
692 attached: 3,
693 max: 2
694 })
695 );
696 assert_eq!(
697 check_transaction(&tx, &NetworkConfigParams::default()),
698 Ok(())
699 );
700 }
701
702 #[test]
703 fn oversized_contract_is_rejected_under_the_protocol_default() {
704 let tx = transaction_with(vec![
705 rv32(1, 1),
706 rv32(kDefaultMaxContractSizeBytes as usize + 1, 2),
707 ]);
708
709 assert_eq!(
710 check_transaction(&tx, &NetworkConfigParams::default()),
711 Err(TxInvalidDetail::ContractTooLarge {
712 index: 1,
713 size_bytes: kDefaultMaxContractSizeBytes + 1,
714 max_bytes: kDefaultMaxContractSizeBytes,
715 })
716 );
717 }
718
719 #[test]
720 fn stated_contract_size_cap_replaces_the_protocol_default() {
721 let small = transaction_with(vec![rv32(1025, 1)]);
725 let large = transaction_with(vec![rv32(kDefaultMaxContractSizeBytes as usize + 1, 1)]);
726
727 assert_eq!(
728 check_transaction(&small, &contract_size_cap(1024)),
729 Err(TxInvalidDetail::ContractTooLarge {
730 index: 0,
731 size_bytes: 1025,
732 max_bytes: 1024,
733 })
734 );
735 assert_eq!(
736 check_transaction(&large, &contract_size_cap(kDefaultMaxContractSizeBytes + 1)),
737 Ok(())
738 );
739 }
740
741 #[test]
742 fn count_check_runs_before_size_check() {
743 let tx = transaction_with(vec![rv32(kDefaultMaxContractSizeBytes as usize + 1, 1); 2]);
746
747 assert!(matches!(
748 check_transaction(&tx, &max_contracts(1)),
749 Err(TxInvalidDetail::TooManyContracts { .. })
750 ));
751 }
752
753 #[test]
754 fn duplicate_contract_reports_both_positions() {
755 let tx = transaction_with(vec![rv32(4, 1), rv32(4, 2), rv32(4, 1)]);
756
757 assert_eq!(
758 check_transaction(&tx, &NetworkConfigParams::default()),
759 Err(TxInvalidDetail::DuplicateContract {
760 index: 2,
761 first_index: 0,
762 })
763 );
764 }
765
766 #[test]
767 fn rv32_bounds_in_force_are_the_stated_values_or_the_protocol_defaults() {
768 let unstated = NetworkConfigParams::default();
769 assert_eq!(max_rv32_code_bytes_in_force(&unstated), 262_144);
770 assert_eq!(max_rv32_memory_bytes_in_force(&unstated), 65_536);
771
772 let stated = rv32_caps(Some(4096), Some(1 << 20));
773 assert_eq!(max_rv32_code_bytes_in_force(&stated), 4096);
774 assert_eq!(max_rv32_memory_bytes_in_force(&stated), 1 << 20);
775 }
776
777 #[test]
778 fn rv32_sizes_at_the_protocol_defaults_pass() {
779 let tx = transaction_with(vec![artifact(DEFAULT_MAX_CODE_BYTES, DEFAULT_MEMORY_BYTES)]);
780
781 assert_eq!(
782 check_transaction(&tx, &NetworkConfigParams::default()),
783 Ok(())
784 );
785 }
786
787 #[test]
788 fn rv32_memory_over_the_bound_is_rejected_and_checked_before_code() {
789 let tx = transaction_with(vec![
792 artifact(16, 1),
793 artifact(DEFAULT_MAX_CODE_BYTES + 4, DEFAULT_MEMORY_BYTES + 1),
794 ]);
795
796 assert_eq!(
797 check_transaction(&tx, &NetworkConfigParams::default()),
798 Err(TxInvalidDetail::Rv32MemoryTooLarge {
799 index: 1,
800 memory_bytes: 65_537,
801 max_bytes: 65_536,
802 })
803 );
804 }
805
806 #[test]
807 fn rv32_code_over_the_bound_is_rejected() {
808 let tx = transaction_with(vec![artifact(
809 DEFAULT_MAX_CODE_BYTES + 4,
810 DEFAULT_MEMORY_BYTES,
811 )]);
812
813 assert_eq!(
814 check_transaction(&tx, &NetworkConfigParams::default()),
815 Err(TxInvalidDetail::Rv32CodeTooLarge {
816 index: 0,
817 code_bytes: 262_148,
818 max_bytes: 262_144,
819 })
820 );
821 }
822
823 #[test]
824 fn stated_rv32_bounds_replace_the_protocol_defaults() {
825 let large = transaction_with(vec![artifact(
827 DEFAULT_MAX_CODE_BYTES + 4,
828 DEFAULT_MEMORY_BYTES + 1,
829 )]);
830 assert_eq!(
831 check_transaction(&large, &rv32_caps(Some(262_148), Some(65_537))),
832 Ok(())
833 );
834
835 let small = transaction_with(vec![artifact(1024, 4096)]);
837 assert_eq!(
838 check_transaction(&small, &rv32_caps(Some(1020), None)),
839 Err(TxInvalidDetail::Rv32CodeTooLarge {
840 index: 0,
841 code_bytes: 1024,
842 max_bytes: 1020,
843 })
844 );
845 assert_eq!(
846 check_transaction(&small, &rv32_caps(None, Some(4095))),
847 Err(TxInvalidDetail::Rv32MemoryTooLarge {
848 index: 0,
849 memory_bytes: 4096,
850 max_bytes: 4095,
851 })
852 );
853 }
854
855 #[test]
856 fn a_blob_without_an_artifact_header_is_left_to_the_executor() {
857 let blob = rv32(DEFAULT_MAX_CODE_BYTES + 1024, 0x5a);
860 assert_eq!(rv32_declared_sizes(&blob), None);
861
862 let tx = transaction_with(vec![blob]);
863 assert_eq!(
864 check_transaction(&tx, &rv32_caps(Some(16), Some(16))),
865 Ok(())
866 );
867 }
868
869 #[test]
870 fn native_contracts_count_but_carry_no_code() {
871 let native = Contract::kNative {
872 native_contract_id: HogexecPrecompiledNativeContracts::from(0),
873 };
874 let tx = transaction_with(vec![native.clone(), rv32(4, 1)]);
875
876 assert_eq!(contract_code_size(&native), 0);
877 assert_eq!(
878 check_transaction(&tx, &max_contracts(1)),
879 Err(TxInvalidDetail::TooManyContracts {
880 attached: 2,
881 max: 1
882 })
883 );
884 }
885}