diff --git a/radix-engine/benches/transaction.rs b/radix-engine/benches/transaction.rs index 35e6d98c1f4..02b2b24c8aa 100644 --- a/radix-engine/benches/transaction.rs +++ b/radix-engine/benches/transaction.rs @@ -2,15 +2,15 @@ use criterion::{criterion_group, criterion_main, Criterion}; use radix_engine::types::*; use transaction::builder::ManifestBuilder; use transaction::builder::TransactionBuilder; -use transaction::model::TransactionHeader; +use transaction::model::{NotarizedTransaction, TransactionHeader}; use transaction::signing::EcdsaSecp256k1PrivateKey; use transaction::signing::EddsaEd25519PrivateKey; -use transaction::validation::recover_ecdsa_secp256k1; use transaction::validation::verify_ecdsa_secp256k1; use transaction::validation::verify_eddsa_ed25519; +use transaction::validation::NotarizedTransactionValidator; use transaction::validation::TestIntentHashManager; -use transaction::validation::TransactionValidator; use transaction::validation::ValidationConfig; +use transaction::validation::{recover_ecdsa_secp256k1, TransactionValidator}; fn bench_ecdsa_secp256k1_validation(c: &mut Criterion) { let message = "This is a long message".repeat(100); @@ -80,20 +80,21 @@ fn bench_transaction_validation(c: &mut Criterion) { let transaction_bytes = transaction.to_bytes(); println!("Transaction size: {} bytes", transaction_bytes.len()); + let validator = NotarizedTransactionValidator::new(ValidationConfig { + network_id: NetworkDefinition::simulator().id, + current_epoch: 1, + max_cost_unit_limit: 10_000_000, + min_tip_percentage: 0, + }); + c.bench_function("Transaction validation", |b| { b.iter(|| { let intent_hash_manager = TestIntentHashManager::new(); - let config: ValidationConfig = ValidationConfig { - network_id: NetworkDefinition::simulator().id, - current_epoch: 1, - max_cost_unit_limit: 10_000_000, - min_tip_percentage: 0, - }; - TransactionValidator::validate_from_slice( + TransactionValidator::::validate_from_slice( + &validator, &transaction_bytes, &intent_hash_manager, - &config, ) .unwrap(); }) diff --git a/radix-engine/src/engine/call_frame.rs b/radix-engine/src/engine/call_frame.rs index 04e2d29bcee..293756be0e1 100644 --- a/radix-engine/src/engine/call_frame.rs +++ b/radix-engine/src/engine/call_frame.rs @@ -1,5 +1,3 @@ -use transaction::validation::*; - use crate::engine::*; use crate::fee::FeeReserve; use crate::model::*; @@ -28,46 +26,7 @@ pub struct CallFrame { } impl CallFrame { - pub fn new_root(signer_public_keys: Vec) -> Self { - // TODO: Cleanup initialization of authzone - - let mut ecdsa_secp256k1_non_fungible_ids = BTreeSet::new(); - let mut eddsa_ed25519_non_fungible_ids = BTreeSet::new(); - for pk in signer_public_keys { - match pk { - PublicKey::EcdsaSecp256k1(pk) => { - ecdsa_secp256k1_non_fungible_ids.insert(NonFungibleId::from_bytes(pk.to_vec())) - } - PublicKey::EddsaEd25519(pk) => { - eddsa_ed25519_non_fungible_ids.insert(NonFungibleId::from_bytes(pk.to_vec())) - } - }; - } - - let mut initial_auth_zone_proofs = Vec::new(); - if !ecdsa_secp256k1_non_fungible_ids.is_empty() { - // Proofs can't be zero amount - let mut ecdsa_secp256k1_bucket = Bucket::new(ResourceContainer::new_non_fungible( - ECDSA_TOKEN, - ecdsa_secp256k1_non_fungible_ids, - )); - let ecdsa_secp256k1_proof = ecdsa_secp256k1_bucket - .create_proof(ECDSA_TOKEN_BUCKET_ID) - .expect("Failed to construct ECDSA signature proof"); - initial_auth_zone_proofs.push(ecdsa_secp256k1_proof); - } - if !eddsa_ed25519_non_fungible_ids.is_empty() { - // Proofs can't be zero amount - let mut eddsa_ed25519_bucket = Bucket::new(ResourceContainer::new_non_fungible( - ED25519_TOKEN, - eddsa_ed25519_non_fungible_ids, - )); - let eddsa_ed25519_proof = eddsa_ed25519_bucket - .create_proof(ED25519_TOKEN_BUCKET_ID) - .expect("Failed to construct ED25519 signature proof"); - initial_auth_zone_proofs.push(eddsa_ed25519_proof); - } - + pub fn new_root() -> Self { Self { depth: 0, actor: REActor { @@ -79,7 +38,7 @@ impl CallFrame { }, node_refs: HashMap::new(), owned_heap_nodes: HashMap::new(), - auth_zone: AuthZone::new_with_proofs(initial_auth_zone_proofs), + auth_zone: AuthZone::new(), } } diff --git a/radix-engine/src/engine/kernel.rs b/radix-engine/src/engine/kernel.rs index 6a4275150dc..9cd6fb1f874 100644 --- a/radix-engine/src/engine/kernel.rs +++ b/radix-engine/src/engine/kernel.rs @@ -1,5 +1,5 @@ use transaction::errors::IdAllocationError; -use transaction::model::ExecutableInstruction; +use transaction::model::Instruction; use transaction::validation::*; use crate::engine::*; @@ -69,9 +69,8 @@ where { pub fn new( transaction_hash: Hash, - transaction_signers: Vec, + initial_proofs: Vec, blobs: &'g HashMap>, - is_system: bool, max_depth: usize, track: &'g mut Track<'s, R>, wasm_engine: &'g mut W, @@ -80,7 +79,7 @@ where execution_trace: &'g mut ExecutionTrace, modules: Vec>>, ) -> Self { - let frame = CallFrame::new_root(transaction_signers); + let frame = CallFrame::new_root(); let mut kernel = Self { transaction_hash, blobs, @@ -96,13 +95,21 @@ where phantom: PhantomData, }; - if is_system { - let non_fungible_ids = [NonFungibleId::from_u32(0)].into_iter().collect(); + // Initial authzone + // TODO: Move into module initialization + let mut proofs_to_create = BTreeMap::>::new(); + for non_fungible in initial_proofs { + proofs_to_create + .entry(non_fungible.resource_address()) + .or_insert(BTreeSet::new()) + .insert(non_fungible.non_fungible_id()); + } + for (resource_address, non_fungible_ids) in proofs_to_create { let bucket_id = match kernel .node_create(HeapRENode::Bucket(Bucket::new( - ResourceContainer::new_non_fungible(SYSTEM_TOKEN, non_fungible_ids), + ResourceContainer::new_non_fungible(resource_address, non_fungible_ids), ))) - .expect("Failed to create SYSTEM_TOKEN bucket") + .expect("Failed to create bucket") { RENodeId::Bucket(bucket_id) => bucket_id, _ => panic!("Expected Bucket RENodeId but received something else"), @@ -110,16 +117,17 @@ where let substate_id = SubstateId::Bucket(bucket_id); let mut node_ref = kernel .substate_borrow_mut(&substate_id) - .expect("Failed to borrow SYSTEM_TOKEN bucket substate"); + .expect("Failed to borrow bucket substate"); let bucket = node_ref.bucket(); - let system_proof = bucket + let proof = bucket .create_proof(bucket_id) - .expect("Failed to create SYSTEM_TOKEN proof"); + .expect("Failed to create proof"); Self::current_frame_mut(&mut kernel.call_frames) .auth_zone .proofs - .push(system_proof); + .push(proof); } + kernel } @@ -536,8 +544,8 @@ where scrypto_decode(&input.raw).expect("Transaction processor received invalid input"); for instruction in &input.instructions { match instruction { - ExecutableInstruction::CallFunction { args, .. } - | ExecutableInstruction::CallMethod { args, .. } => { + Instruction::CallFunction { args, .. } + | Instruction::CallMethod { args, .. } => { let scrypto_value = ScryptoValue::from_slice(&args).expect("Invalid CALL arguments"); component_addresses.extend(scrypto_value.refed_component_addresses); diff --git a/radix-engine/src/engine/track.rs b/radix-engine/src/engine/track.rs index 61a626fbca9..193b8bd49bf 100644 --- a/radix-engine/src/engine/track.rs +++ b/radix-engine/src/engine/track.rs @@ -332,7 +332,7 @@ impl<'s, R: FeeReserve> Track<'s, R> { .and_then(|()| { self.fee_reserve.consume( self.fee_table.tx_signature_verification_per_sig() - * transaction.signer_public_keys().len() as u32, + * transaction.initial_proofs().len() as u32, "verify_signatures", false, ) diff --git a/radix-engine/src/model/transaction_processor.rs b/radix-engine/src/model/transaction_processor.rs index 111cabd0f06..ca57f52eed4 100644 --- a/radix-engine/src/model/transaction_processor.rs +++ b/radix-engine/src/model/transaction_processor.rs @@ -17,7 +17,7 @@ use super::Worktop; #[derive(Debug, TypeId, Encode, Decode)] pub struct TransactionProcessorRunInput { - pub instructions: Vec, + pub instructions: Vec, } #[derive(Debug, TypeId, Encode, Decode)] @@ -213,7 +213,7 @@ impl TransactionProcessor { for inst in &input.instructions.clone() { let result = match inst { - ExecutableInstruction::TakeFromWorktop { resource_address } => id_allocator + Instruction::TakeFromWorktop { resource_address } => id_allocator .new_bucket_id() .map_err(|e| { InvokeError::Error(TransactionProcessorError::IdAllocationError(e)) @@ -236,7 +236,7 @@ impl TransactionProcessor { ScryptoValue::from_typed(&scrypto::resource::Bucket(new_id)) }) }), - ExecutableInstruction::TakeFromWorktopByAmount { + Instruction::TakeFromWorktopByAmount { amount, resource_address, } => id_allocator @@ -263,7 +263,7 @@ impl TransactionProcessor { ScryptoValue::from_typed(&scrypto::resource::Bucket(new_id)) }) }), - ExecutableInstruction::TakeFromWorktopByIds { + Instruction::TakeFromWorktopByIds { ids, resource_address, } => id_allocator @@ -290,7 +290,7 @@ impl TransactionProcessor { ScryptoValue::from_typed(&scrypto::resource::Bucket(new_id)) }) }), - ExecutableInstruction::ReturnToWorktop { bucket_id } => bucket_id_mapping + Instruction::ReturnToWorktop { bucket_id } => bucket_id_mapping .remove(bucket_id) .map(|real_id| { system_api @@ -308,20 +308,18 @@ impl TransactionProcessor { .unwrap_or(Err(InvokeError::Error( TransactionProcessorError::BucketNotFound(*bucket_id), ))), - ExecutableInstruction::AssertWorktopContains { resource_address } => { - system_api - .invoke_method( - Receiver::Ref(RENodeId::Worktop), - FnIdentifier::Native(NativeFnIdentifier::Worktop( - WorktopFnIdentifier::AssertContains, - )), - ScryptoValue::from_typed(&WorktopAssertContainsInput { - resource_address: *resource_address, - }), - ) - .map_err(InvokeError::Downstream) - } - ExecutableInstruction::AssertWorktopContainsByAmount { + Instruction::AssertWorktopContains { resource_address } => system_api + .invoke_method( + Receiver::Ref(RENodeId::Worktop), + FnIdentifier::Native(NativeFnIdentifier::Worktop( + WorktopFnIdentifier::AssertContains, + )), + ScryptoValue::from_typed(&WorktopAssertContainsInput { + resource_address: *resource_address, + }), + ) + .map_err(InvokeError::Downstream), + Instruction::AssertWorktopContainsByAmount { amount, resource_address, } => system_api @@ -336,7 +334,7 @@ impl TransactionProcessor { }), ) .map_err(InvokeError::Downstream), - ExecutableInstruction::AssertWorktopContainsByIds { + Instruction::AssertWorktopContainsByIds { ids, resource_address, } => system_api @@ -352,7 +350,7 @@ impl TransactionProcessor { ) .map_err(InvokeError::Downstream), - ExecutableInstruction::PopFromAuthZone {} => id_allocator + Instruction::PopFromAuthZone {} => id_allocator .new_proof_id() .map_err(|e| { InvokeError::Error(TransactionProcessorError::IdAllocationError(e)) @@ -373,7 +371,7 @@ impl TransactionProcessor { ScryptoValue::from_typed(&scrypto::resource::Proof(new_id)) }) }), - ExecutableInstruction::ClearAuthZone => { + Instruction::ClearAuthZone => { proof_id_mapping.clear(); system_api .invoke_method( @@ -385,7 +383,7 @@ impl TransactionProcessor { ) .map_err(InvokeError::Downstream) } - ExecutableInstruction::PushToAuthZone { proof_id } => proof_id_mapping + Instruction::PushToAuthZone { proof_id } => proof_id_mapping .remove(proof_id) .ok_or(InvokeError::Error( TransactionProcessorError::ProofNotFound(*proof_id), @@ -403,36 +401,30 @@ impl TransactionProcessor { ) .map_err(InvokeError::Downstream) }), - ExecutableInstruction::CreateProofFromAuthZone { resource_address } => { - id_allocator - .new_proof_id() - .map_err(|e| { - InvokeError::Error( - TransactionProcessorError::IdAllocationError(e), + Instruction::CreateProofFromAuthZone { resource_address } => id_allocator + .new_proof_id() + .map_err(|e| { + InvokeError::Error(TransactionProcessorError::IdAllocationError(e)) + }) + .and_then(|new_id| { + system_api + .invoke_method( + Receiver::CurrentAuthZone, + FnIdentifier::Native(NativeFnIdentifier::AuthZone( + AuthZoneFnIdentifier::CreateProof, + )), + ScryptoValue::from_typed(&AuthZoneCreateProofInput { + resource_address: *resource_address, + }), ) - }) - .and_then(|new_id| { - system_api - .invoke_method( - Receiver::CurrentAuthZone, - FnIdentifier::Native(NativeFnIdentifier::AuthZone( - AuthZoneFnIdentifier::CreateProof, - )), - ScryptoValue::from_typed(&AuthZoneCreateProofInput { - resource_address: *resource_address, - }), - ) - .map_err(InvokeError::Downstream) - .map(|rtn| { - let proof_id = Self::first_proof(&rtn); - proof_id_mapping.insert(new_id, proof_id); - ScryptoValue::from_typed(&scrypto::resource::Proof( - new_id, - )) - }) - }) - } - ExecutableInstruction::CreateProofFromAuthZoneByAmount { + .map_err(InvokeError::Downstream) + .map(|rtn| { + let proof_id = Self::first_proof(&rtn); + proof_id_mapping.insert(new_id, proof_id); + ScryptoValue::from_typed(&scrypto::resource::Proof(new_id)) + }) + }), + Instruction::CreateProofFromAuthZoneByAmount { amount, resource_address, } => id_allocator @@ -461,7 +453,7 @@ impl TransactionProcessor { ScryptoValue::from_typed(&scrypto::resource::Proof(new_id)) }) }), - ExecutableInstruction::CreateProofFromAuthZoneByIds { + Instruction::CreateProofFromAuthZoneByIds { ids, resource_address, } => id_allocator @@ -488,7 +480,7 @@ impl TransactionProcessor { ScryptoValue::from_typed(&scrypto::resource::Proof(new_id)) }) }), - ExecutableInstruction::CreateProofFromBucket { bucket_id } => id_allocator + Instruction::CreateProofFromBucket { bucket_id } => id_allocator .new_proof_id() .map_err(|e| { InvokeError::Error(TransactionProcessorError::IdAllocationError(e)) @@ -518,7 +510,7 @@ impl TransactionProcessor { ScryptoValue::from_typed(&scrypto::resource::Proof(new_id)) }) }), - ExecutableInstruction::CloneProof { proof_id } => id_allocator + Instruction::CloneProof { proof_id } => id_allocator .new_proof_id() .map_err(|e| { InvokeError::Error(TransactionProcessorError::IdAllocationError(e)) @@ -549,7 +541,7 @@ impl TransactionProcessor { TransactionProcessorError::ProofNotFound(*proof_id), ))) }), - ExecutableInstruction::DropProof { proof_id } => proof_id_mapping + Instruction::DropProof { proof_id } => proof_id_mapping .remove(proof_id) .map(|real_id| { system_api @@ -565,7 +557,7 @@ impl TransactionProcessor { .unwrap_or(Err(InvokeError::Error( TransactionProcessorError::ProofNotFound(*proof_id), ))), - ExecutableInstruction::DropAllProofs => { + Instruction::DropAllProofs => { for (_, real_id) in proof_id_mapping.drain() { system_api .invoke_method( @@ -587,7 +579,7 @@ impl TransactionProcessor { ) .map_err(InvokeError::Downstream) } - ExecutableInstruction::CallFunction { + Instruction::CallFunction { fn_identifier, args, } => { @@ -635,7 +627,7 @@ impl TransactionProcessor { Ok(result) }) } - ExecutableInstruction::CallMethod { + Instruction::CallMethod { method_identifier, args, } => { @@ -729,7 +721,7 @@ impl TransactionProcessor { Ok(result) }) } - ExecutableInstruction::PublishPackage { code, abi } => system_api + Instruction::PublishPackage { code, abi } => system_api .invoke_function( FnIdentifier::Native(NativeFnIdentifier::Package( PackageFnIdentifier::Publish, diff --git a/radix-engine/src/transaction/preview_executor.rs b/radix-engine/src/transaction/preview_executor.rs index daf5897a936..76c28ebb092 100644 --- a/radix-engine/src/transaction/preview_executor.rs +++ b/radix-engine/src/transaction/preview_executor.rs @@ -2,7 +2,7 @@ use scrypto::core::NetworkDefinition; use transaction::errors::TransactionValidationError; use transaction::model::PreviewIntent; use transaction::validation::IntentHashManager; -use transaction::validation::TransactionValidator; +use transaction::validation::NotarizedTransactionValidator; use transaction::validation::ValidationConfig; use crate::constants::DEFAULT_MAX_COST_UNIT_LIMIT; @@ -69,20 +69,18 @@ where preview_intent: PreviewIntent, ) -> Result { // TODO: construct validation config based on current world state - let validation_params = ValidationConfig { + let validation_config = ValidationConfig { network_id: self.network.id, current_epoch: 1, max_cost_unit_limit: DEFAULT_MAX_COST_UNIT_LIMIT, min_tip_percentage: 0, }; let execution_params = ExecutionConfig::default(); + let validator = NotarizedTransactionValidator::new(validation_config); - let validated_preview_transaction = TransactionValidator::validate_preview_intent( - preview_intent.clone(), - self.intent_hash_manager, - &validation_params, - ) - .map_err(PreviewError::TransactionValidationError)?; + let validated_preview_transaction = validator + .validate_preview_intent(preview_intent.clone(), self.intent_hash_manager) + .map_err(PreviewError::TransactionValidationError)?; let mut transaction_executor = TransactionExecutor::new( self.substate_store, diff --git a/radix-engine/src/transaction/transaction_executor.rs b/radix-engine/src/transaction/transaction_executor.rs index ab79bce75cd..3d0879fa491 100644 --- a/radix-engine/src/transaction/transaction_executor.rs +++ b/radix-engine/src/transaction/transaction_executor.rs @@ -28,7 +28,6 @@ impl FeeReserveConfig { pub struct ExecutionConfig { pub max_call_depth: usize, - pub is_system: bool, pub trace: bool, } @@ -42,7 +41,6 @@ impl ExecutionConfig { pub fn standard() -> Self { Self { max_call_depth: DEFAULT_MAX_CALL_DEPTH, - is_system: false, trace: false, } } @@ -50,7 +48,6 @@ impl ExecutionConfig { pub fn debug() -> Self { Self { max_call_depth: DEFAULT_MAX_CALL_DEPTH, - is_system: false, trace: true, } } @@ -111,7 +108,7 @@ where fee_reserve: R, ) -> TransactionReceipt { let transaction_hash = transaction.transaction_hash(); - let signer_public_keys = transaction.signer_public_keys().to_vec(); + let initial_proofs = transaction.initial_proofs(); let instructions = transaction.instructions().to_vec(); let blobs: HashMap> = transaction .blobs() @@ -123,7 +120,7 @@ where if execution_config.trace { println!("{:-^80}", "Transaction Metadata"); println!("Transaction hash: {}", transaction_hash); - println!("Transaction signers: {:?}", signer_public_keys); + println!("Transaction proofs: {:?}", initial_proofs); println!("Number of unique blobs: {}", blobs.len()); println!("{:-^80}", "Engine Execution Log"); @@ -162,9 +159,8 @@ where modules.push(Box::new(CostingModule::default())); let mut kernel = Kernel::new( transaction_hash, - signer_public_keys, + initial_proofs, &blobs, - execution_config.is_system, execution_config.max_call_depth, &mut track, self.wasm_engine, diff --git a/radix-engine/src/transaction/transaction_receipt.rs b/radix-engine/src/transaction/transaction_receipt.rs index c50d5969139..ab502fc68f3 100644 --- a/radix-engine/src/transaction/transaction_receipt.rs +++ b/radix-engine/src/transaction/transaction_receipt.rs @@ -9,7 +9,7 @@ use crate::types::*; #[derive(Debug, TypeId, Encode, Decode)] pub struct TransactionContents { - pub instructions: Vec, + pub instructions: Vec, } #[derive(Debug, TypeId, Encode, Decode)] @@ -227,7 +227,7 @@ impl fmt::Debug for TransactionReceipt { "\n{} {}", prefix!(i, contents.instructions), match inst { - ExecutableInstruction::CallFunction { + Instruction::CallFunction { fn_identifier: FnIdentifier::Scrypto { package_address, blueprint_name, @@ -241,7 +241,7 @@ impl fmt::Debug for TransactionReceipt { ident, ScryptoValue::from_slice(&args).expect("Failed parse call data") ), - ExecutableInstruction::CallMethod { + Instruction::CallMethod { method_identifier, args, } => { @@ -267,7 +267,7 @@ impl fmt::Debug for TransactionReceipt { } } }, - ExecutableInstruction::PublishPackage { .. } => "PublishPackage {..}".to_owned(), + Instruction::PublishPackage { .. } => "PublishPackage {..}".to_owned(), i @ _ => format!("{:?}", i), } )?; diff --git a/radix-engine/tests/fuzz_transactions.rs b/radix-engine/tests/fuzz_transactions.rs index 2540ce8729c..1224df6ced2 100644 --- a/radix-engine/tests/fuzz_transactions.rs +++ b/radix-engine/tests/fuzz_transactions.rs @@ -14,27 +14,27 @@ use rayon::prelude::*; use transaction::builder::{ManifestBuilder, TransactionBuilder}; use transaction::model::{NotarizedTransaction, TransactionHeader}; use transaction::signing::EcdsaSecp256k1PrivateKey; -use transaction::validation::{TestIntentHashManager, TransactionValidator, ValidationConfig}; +use transaction::validation::{ + NotarizedTransactionValidator, TestIntentHashManager, TransactionValidator, ValidationConfig, +}; fn execute_single_transaction(transaction: NotarizedTransaction) { - let transaction = TransactionValidator::validate( - transaction, - &TestIntentHashManager::new(), - &ValidationConfig { - network_id: NetworkDefinition::simulator().id, - current_epoch: 1, - max_cost_unit_limit: DEFAULT_COST_UNIT_LIMIT, - min_tip_percentage: 0, - }, - ) - .unwrap(); + let validator = NotarizedTransactionValidator::new(ValidationConfig { + network_id: NetworkDefinition::simulator().id, + current_epoch: 1, + max_cost_unit_limit: DEFAULT_COST_UNIT_LIMIT, + min_tip_percentage: 0, + }); + + let transaction = validator + .validate(transaction, &TestIntentHashManager::new()) + .unwrap(); let mut store = TypedInMemorySubstateStore::with_bootstrap(); let mut wasm_engine = DefaultWasmEngine::new(); let mut wasm_instrumenter = WasmInstrumenter::new(); let execution_config = ExecutionConfig { max_call_depth: DEFAULT_MAX_CALL_DEPTH, - is_system: false, trace: false, }; let fee_reserve_config = FeeReserveConfig { diff --git a/radix-engine/tests/metering.rs b/radix-engine/tests/metering.rs index 620c49a7638..a8ed52ce2df 100644 --- a/radix-engine/tests/metering.rs +++ b/radix-engine/tests/metering.rs @@ -148,8 +148,8 @@ fn test_basic_transfer() { assert_eq!( 10000 /* base_fee */ + 0 /* blobs */ - + 3300 /* borrow_substate */ - + 1500 /* create_node */ + + 4300 /* borrow_substate */ + + 2000 /* create_node */ + 1248 /* decode_manifest */ + 1000 /* drop_node */ + 578328 /* instantiate_wasm */ diff --git a/radix-engine/tests/preview.rs b/radix-engine/tests/preview.rs index 44e5f198701..2b2e0f42a85 100644 --- a/radix-engine/tests/preview.rs +++ b/radix-engine/tests/preview.rs @@ -6,8 +6,8 @@ use transaction::builder::ManifestBuilder; use transaction::builder::TransactionBuilder; use transaction::model::*; use transaction::signing::EcdsaSecp256k1PrivateKey; -use transaction::validation::ValidationConfig; -use transaction::validation::{TestIntentHashManager, TransactionValidator}; +use transaction::validation::{NotarizedTransactionValidator, TestIntentHashManager}; +use transaction::validation::{TransactionValidator, ValidationConfig}; #[test] fn test_transaction_preview_cost_estimate() { @@ -40,7 +40,7 @@ fn test_transaction_preview_cost_estimate() { fn prepare_test_tx_and_preview_intent( test_runner: &TestRunner, network: &NetworkDefinition, -) -> (ValidatedTransaction, PreviewIntent) { +) -> (Validated, PreviewIntent) { let notary_priv_key = EcdsaSecp256k1PrivateKey::from_u64(2).unwrap(); let tx_signer_priv_key = EcdsaSecp256k1PrivateKey::from_u64(3).unwrap(); @@ -66,17 +66,16 @@ fn prepare_test_tx_and_preview_intent( .notarize(¬ary_priv_key) .build(); - let validated_transaction = TransactionValidator::validate( - notarized_transaction.clone(), - &TestIntentHashManager::new(), - &ValidationConfig { - network_id: network.id, - current_epoch: 1, - max_cost_unit_limit: 10_000_000, - min_tip_percentage: 0, - }, - ) - .unwrap(); + let validator = NotarizedTransactionValidator::new(ValidationConfig { + network_id: network.id, + current_epoch: 1, + max_cost_unit_limit: 10_000_000, + min_tip_percentage: 0, + }); + + let validated_transaction = validator + .validate(notarized_transaction.clone(), &TestIntentHashManager::new()) + .unwrap(); let preview_intent = PreviewIntent { intent: notarized_transaction.signed_intent.intent.clone(), diff --git a/radix-engine/tests/transaction_executor.rs b/radix-engine/tests/transaction_executor.rs index 1aeb48313ad..2264fb9fcd3 100644 --- a/radix-engine/tests/transaction_executor.rs +++ b/radix-engine/tests/transaction_executor.rs @@ -10,9 +10,11 @@ use radix_engine::wasm::WasmInstrumenter; use scrypto_unit::*; use transaction::builder::ManifestBuilder; use transaction::builder::TransactionBuilder; -use transaction::model::{NotarizedTransaction, TransactionHeader, ValidatedTransaction}; +use transaction::model::{NotarizedTransaction, TransactionHeader, Validated}; use transaction::signing::EcdsaSecp256k1PrivateKey; -use transaction::validation::{TestIntentHashManager, TransactionValidator, ValidationConfig}; +use transaction::validation::{ + NotarizedTransactionValidator, TestIntentHashManager, TransactionValidator, ValidationConfig, +}; #[test] fn pre_execution_rejection_should_return_rejected_receipt() { @@ -47,21 +49,20 @@ fn test_normal_transaction_flow() { let mut wasm_engine = DefaultWasmEngine::new(); let mut wasm_instrumenter = WasmInstrumenter::new(); let intent_hash_manager = TestIntentHashManager::new(); - let validation_params = ValidationConfig { + let fee_reserve_config = FeeReserveConfig::standard(); + let execution_config = ExecutionConfig::debug(); + let raw_transaction = create_notarized_transaction(1_000_000).to_bytes(); + + let validator = NotarizedTransactionValidator::new(ValidationConfig { network_id: NetworkDefinition::simulator().id, current_epoch: 1, max_cost_unit_limit: DEFAULT_COST_UNIT_LIMIT, min_tip_percentage: 0, - }; - let fee_reserve_config = FeeReserveConfig::standard(); - let execution_config = ExecutionConfig::debug(); - let raw_transaction = create_notarized_transaction(1_000_000).to_bytes(); - let validated_transaction = TransactionValidator::validate_from_slice( - &raw_transaction, - &intent_hash_manager, - &validation_params, - ) - .expect("Invalid transaction"); + }); + + let validated_transaction: Validated = validator + .validate_from_slice(&raw_transaction, &intent_hash_manager) + .expect("Invalid transaction"); let mut executor = TransactionExecutor::new( &mut substate_store, &mut wasm_engine, @@ -79,19 +80,19 @@ fn test_normal_transaction_flow() { receipt.expect_commit_success(); } -fn create_executable_transaction(cost_unit_limit: u32) -> ValidatedTransaction { +fn create_executable_transaction(cost_unit_limit: u32) -> Validated { let notarized_transaction = create_notarized_transaction(cost_unit_limit); - TransactionValidator::validate( - notarized_transaction, - &TestIntentHashManager::new(), - &ValidationConfig { - network_id: NetworkDefinition::simulator().id, - current_epoch: 1, - max_cost_unit_limit: 10_000_000, - min_tip_percentage: 0, - }, - ) - .unwrap() + + let validator = NotarizedTransactionValidator::new(ValidationConfig { + network_id: NetworkDefinition::simulator().id, + current_epoch: 1, + max_cost_unit_limit: 10_000_000, + min_tip_percentage: 0, + }); + + validator + .validate(notarized_transaction, &TestIntentHashManager::new()) + .unwrap() } fn create_notarized_transaction(cost_unit_limit: u32) -> NotarizedTransaction { diff --git a/scrypto-unit/src/test_runner.rs b/scrypto-unit/src/test_runner.rs index 847af6bf80a..7041fd13b4b 100644 --- a/scrypto-unit/src/test_runner.rs +++ b/scrypto-unit/src/test_runner.rs @@ -320,7 +320,6 @@ impl<'s, S: ReadableSubstateStore + WriteableSubstateStore> TestRunner<'s, S> { }, &ExecutionConfig { max_call_depth: DEFAULT_MAX_CALL_DEPTH, - is_system: false, trace: self.trace, }, ); @@ -627,19 +626,27 @@ impl<'s, S: ReadableSubstateStore + WriteableSubstateStore> TestRunner<'s, S> { } pub fn set_current_epoch(&mut self, epoch: u64) { - self.kernel_call(true, |kernel| { - kernel - .invoke_method( - Receiver::Ref(RENodeId::System), - FnIdentifier::Native(NativeFnIdentifier::System(SystemFnIdentifier::SetEpoch)), - ScryptoValue::from_typed(&SystemSetEpochInput { epoch }), - ) - .unwrap() - }); + self.kernel_call( + vec![NonFungibleAddress::new( + SYSTEM_TOKEN, + NonFungibleId::from_u32(0), + )], + |kernel| { + kernel + .invoke_method( + Receiver::Ref(RENodeId::System), + FnIdentifier::Native(NativeFnIdentifier::System( + SystemFnIdentifier::SetEpoch, + )), + ScryptoValue::from_typed(&SystemSetEpochInput { epoch }), + ) + .unwrap() + }, + ); } pub fn get_current_epoch(&mut self) -> u64 { - let current_epoch: ScryptoValue = self.kernel_call(false, |kernel| { + let current_epoch: ScryptoValue = self.kernel_call(vec![], |kernel| { kernel .invoke_method( Receiver::Ref(RENodeId::System), @@ -654,7 +661,7 @@ impl<'s, S: ReadableSubstateStore + WriteableSubstateStore> TestRunner<'s, S> { } /// Performs a kernel call through a kernel with `is_system = true`. - fn kernel_call(&mut self, is_system: bool, fun: F) -> ScryptoValue + fn kernel_call(&mut self, initial_proofs: Vec, fun: F) -> ScryptoValue where F: FnOnce( &mut Kernel, @@ -672,9 +679,8 @@ impl<'s, S: ReadableSubstateStore + WriteableSubstateStore> TestRunner<'s, S> { let mut kernel = Kernel::new( tx_hash, - Vec::new(), + initial_proofs, &blobs, - is_system, DEFAULT_MAX_CALL_DEPTH, &mut track, &mut self.wasm_engine, diff --git a/simulator/src/resim/cmd_call_function.rs b/simulator/src/resim/cmd_call_function.rs index 7467b9d7915..44ba2deb59f 100644 --- a/simulator/src/resim/cmd_call_function.rs +++ b/simulator/src/resim/cmd_call_function.rs @@ -78,7 +78,6 @@ impl CallFunction { &self.signing_keys, &self.network, &self.manifest, - false, self.trace, true, out, diff --git a/simulator/src/resim/cmd_call_method.rs b/simulator/src/resim/cmd_call_method.rs index 30f24aaad53..a000913bac3 100644 --- a/simulator/src/resim/cmd_call_method.rs +++ b/simulator/src/resim/cmd_call_method.rs @@ -76,7 +76,6 @@ impl CallMethod { &self.signing_keys, &self.network, &self.manifest, - false, self.trace, true, out, diff --git a/simulator/src/resim/cmd_mint.rs b/simulator/src/resim/cmd_mint.rs index a88e36bca7d..fa003cee9d3 100644 --- a/simulator/src/resim/cmd_mint.rs +++ b/simulator/src/resim/cmd_mint.rs @@ -64,7 +64,6 @@ impl Mint { &self.signing_keys, &self.network, &self.manifest, - false, self.trace, true, out, diff --git a/simulator/src/resim/cmd_new_account.rs b/simulator/src/resim/cmd_new_account.rs index 067a7131537..e4d58a8a32d 100644 --- a/simulator/src/resim/cmd_new_account.rs +++ b/simulator/src/resim/cmd_new_account.rs @@ -41,7 +41,6 @@ impl NewAccount { &Some("".to_string()), // explicit empty signer public keys &self.network, &self.manifest, - false, self.trace, false, out, diff --git a/simulator/src/resim/cmd_new_badge_fixed.rs b/simulator/src/resim/cmd_new_badge_fixed.rs index b029a8a4e45..c8cb10a70bd 100644 --- a/simulator/src/resim/cmd_new_badge_fixed.rs +++ b/simulator/src/resim/cmd_new_badge_fixed.rs @@ -82,7 +82,6 @@ impl NewBadgeFixed { &self.signing_keys, &self.network, &self.manifest, - false, self.trace, true, out, diff --git a/simulator/src/resim/cmd_new_badge_mutable.rs b/simulator/src/resim/cmd_new_badge_mutable.rs index 9127fa27b0a..a68dbeba797 100644 --- a/simulator/src/resim/cmd_new_badge_mutable.rs +++ b/simulator/src/resim/cmd_new_badge_mutable.rs @@ -75,7 +75,6 @@ impl NewBadgeMutable { &self.signing_keys, &self.network, &self.manifest, - false, self.trace, true, out, diff --git a/simulator/src/resim/cmd_new_token_fixed.rs b/simulator/src/resim/cmd_new_token_fixed.rs index cbbfbbd2a52..e493525fc6b 100644 --- a/simulator/src/resim/cmd_new_token_fixed.rs +++ b/simulator/src/resim/cmd_new_token_fixed.rs @@ -82,7 +82,6 @@ impl NewTokenFixed { &self.signing_keys, &self.network, &self.manifest, - false, self.trace, true, out, diff --git a/simulator/src/resim/cmd_new_token_mutable.rs b/simulator/src/resim/cmd_new_token_mutable.rs index 2494006ea42..4c0b5d224ec 100644 --- a/simulator/src/resim/cmd_new_token_mutable.rs +++ b/simulator/src/resim/cmd_new_token_mutable.rs @@ -75,7 +75,6 @@ impl NewTokenMutable { &self.signing_keys, &self.network, &self.manifest, - false, self.trace, true, out, diff --git a/simulator/src/resim/cmd_publish.rs b/simulator/src/resim/cmd_publish.rs index d23bb7bbe26..6876c950446 100644 --- a/simulator/src/resim/cmd_publish.rs +++ b/simulator/src/resim/cmd_publish.rs @@ -78,7 +78,6 @@ impl Publish { &None, &self.network, &self.manifest, - false, self.trace, false, out, diff --git a/simulator/src/resim/cmd_run.rs b/simulator/src/resim/cmd_run.rs index c193d9db40f..f922bef7bd2 100644 --- a/simulator/src/resim/cmd_run.rs +++ b/simulator/src/resim/cmd_run.rs @@ -59,7 +59,6 @@ impl Run { &self.signing_keys, &self.network, &None, - false, self.trace, true, out, diff --git a/simulator/src/resim/cmd_set_current_epoch.rs b/simulator/src/resim/cmd_set_current_epoch.rs index 26d932c2c0d..7dd0fc287f3 100644 --- a/simulator/src/resim/cmd_set_current_epoch.rs +++ b/simulator/src/resim/cmd_set_current_epoch.rs @@ -5,6 +5,7 @@ use radix_engine::engine::{ExecutionTrace, Kernel, SystemApi}; use radix_engine::fee::{FeeTable, SystemLoanFeeReserve}; use radix_engine::types::*; use radix_engine_stores::rocks_db::RadixEngineDB; +use transaction::model::AuthModule; use crate::resim::*; @@ -33,9 +34,8 @@ impl SetCurrentEpoch { let mut kernel = Kernel::new( tx_hash, - Vec::new(), + vec![AuthModule::validator_role_nf_address()], &blobs, - true, DEFAULT_MAX_CALL_DEPTH, &mut track, &mut wasm_engine, diff --git a/simulator/src/resim/cmd_transfer.rs b/simulator/src/resim/cmd_transfer.rs index a789e107c55..64c776cacd6 100644 --- a/simulator/src/resim/cmd_transfer.rs +++ b/simulator/src/resim/cmd_transfer.rs @@ -67,7 +67,6 @@ impl Transfer { &self.signing_keys, &self.network, &self.manifest, - false, self.trace, true, out, diff --git a/simulator/src/resim/mod.rs b/simulator/src/resim/mod.rs index af19e88d6b9..77bc6afbffc 100644 --- a/simulator/src/resim/mod.rs +++ b/simulator/src/resim/mod.rs @@ -137,7 +137,6 @@ pub fn handle_manifest( signing_keys: &Option, network: &Option, manifest_path: &Option, - is_system: bool, trace: bool, output_receipt: bool, out: &mut O, @@ -190,7 +189,6 @@ pub fn handle_manifest( }, &ExecutionConfig { max_call_depth: DEFAULT_MAX_CALL_DEPTH, - is_system, trace, }, ); diff --git a/transaction/src/builder/transaction_builder.rs b/transaction/src/builder/transaction_builder.rs index a07b6e32024..02fc145878d 100644 --- a/transaction/src/builder/transaction_builder.rs +++ b/transaction/src/builder/transaction_builder.rs @@ -14,7 +14,7 @@ impl TransactionBuilder { Self { manifest: None, header: None, - intent_signatures: Vec::new(), + intent_signatures: vec![], notary_signature: None, } } @@ -36,8 +36,8 @@ impl TransactionBuilder { self } - pub fn signer_signatures(mut self, signatures: Vec) -> Self { - self.intent_signatures.extend(signatures); + pub fn signer_signatures(mut self, sigs: Vec) -> Self { + self.intent_signatures.extend(sigs); self } diff --git a/transaction/src/model/auth_module.rs b/transaction/src/model/auth_module.rs new file mode 100644 index 00000000000..7c0d7bb3187 --- /dev/null +++ b/transaction/src/model/auth_module.rs @@ -0,0 +1,28 @@ +use scrypto::constants::{ECDSA_TOKEN, ED25519_TOKEN, SYSTEM_TOKEN}; +use scrypto::crypto::PublicKey; +use scrypto::resource::{NonFungibleAddress, NonFungibleId}; + +pub struct AuthModule; + +// TODO: Integrate with AuthModule in radix-engine +impl AuthModule { + pub fn validator_role_nf_address() -> NonFungibleAddress { + NonFungibleAddress::new(SYSTEM_TOKEN, NonFungibleId::from_u32(0)) + } + + pub fn signer_keys_to_non_fungibles( + signer_public_keys: &[PublicKey], + ) -> Vec { + signer_public_keys + .iter() + .map(|k| match k { + PublicKey::EddsaEd25519(pk) => { + NonFungibleAddress::new(ED25519_TOKEN, NonFungibleId::from_bytes(pk.to_vec())) + } + PublicKey::EcdsaSecp256k1(pk) => { + NonFungibleAddress::new(ECDSA_TOKEN, NonFungibleId::from_bytes(pk.to_vec())) + } + }) + .collect() + } +} diff --git a/transaction/src/model/executable.rs b/transaction/src/model/executable.rs index a9a0880d082..346e33f908c 100644 --- a/transaction/src/model/executable.rs +++ b/transaction/src/model/executable.rs @@ -1,13 +1,11 @@ -use sbor::rust::collections::BTreeSet; +use crate::model::Instruction; use sbor::rust::string::String; use sbor::rust::vec::Vec; use sbor::*; use scrypto::component::ComponentAddress; -use scrypto::core::{Blob, FnIdentifier, NativeFnIdentifier, Receiver}; +use scrypto::core::{NativeFnIdentifier, Receiver}; use scrypto::crypto::*; -use scrypto::engine::types::*; -use scrypto::math::*; -use scrypto::resource::{NonFungibleId, ResourceAddress}; +use scrypto::resource::NonFungibleAddress; #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeId)] pub enum MethodIdentifier { @@ -21,74 +19,6 @@ pub enum MethodIdentifier { }, } -/// Represents an instruction that can be executed by Radix Engine. -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeId)] -pub enum ExecutableInstruction { - TakeFromWorktop { - resource_address: ResourceAddress, - }, - TakeFromWorktopByAmount { - amount: Decimal, - resource_address: ResourceAddress, - }, - TakeFromWorktopByIds { - ids: BTreeSet, - resource_address: ResourceAddress, - }, - ReturnToWorktop { - bucket_id: BucketId, - }, - AssertWorktopContains { - resource_address: ResourceAddress, - }, - AssertWorktopContainsByAmount { - amount: Decimal, - resource_address: ResourceAddress, - }, - AssertWorktopContainsByIds { - ids: BTreeSet, - resource_address: ResourceAddress, - }, - PopFromAuthZone, - PushToAuthZone { - proof_id: ProofId, - }, - ClearAuthZone, - CreateProofFromAuthZone { - resource_address: ResourceAddress, - }, - CreateProofFromAuthZoneByAmount { - amount: Decimal, - resource_address: ResourceAddress, - }, - CreateProofFromAuthZoneByIds { - ids: BTreeSet, - resource_address: ResourceAddress, - }, - CreateProofFromBucket { - bucket_id: BucketId, - }, - CloneProof { - proof_id: ProofId, - }, - DropProof { - proof_id: ProofId, - }, - DropAllProofs, - CallFunction { - fn_identifier: FnIdentifier, - args: Vec, - }, - CallMethod { - method_identifier: MethodIdentifier, - args: Vec, - }, - PublishPackage { - code: Blob, - abi: Blob, - }, -} - /// A common trait for all transactions that can be executed by Radix Engine. pub trait ExecutableTransaction { /// Returns the transaction hash, which must be globally unique. @@ -104,10 +34,9 @@ pub trait ExecutableTransaction { fn tip_percentage(&self) -> u32; /// Returns the instructions to execute. - fn instructions(&self) -> &[ExecutableInstruction]; + fn instructions(&self) -> &[Instruction]; - /// Returns the public key of signers. - fn signer_public_keys(&self) -> &[PublicKey]; + fn initial_proofs(&self) -> Vec; fn blobs(&self) -> &[Vec]; } diff --git a/transaction/src/model/mod.rs b/transaction/src/model/mod.rs index 243b2239259..e2d62862c29 100644 --- a/transaction/src/model/mod.rs +++ b/transaction/src/model/mod.rs @@ -1,3 +1,4 @@ +mod auth_module; mod constants; mod executable; mod instruction; @@ -7,6 +8,7 @@ mod transaction; mod validated_transaction; pub use self::transaction::*; +pub use auth_module::*; pub use constants::*; pub use executable::*; pub use instruction::*; diff --git a/transaction/src/model/preview_transaction.rs b/transaction/src/model/preview_transaction.rs index 4ceb1eecf5a..031b241a435 100644 --- a/transaction/src/model/preview_transaction.rs +++ b/transaction/src/model/preview_transaction.rs @@ -1,8 +1,10 @@ use sbor::*; use scrypto::buffer::scrypto_encode; +use scrypto::constants::{ECDSA_TOKEN, ED25519_TOKEN}; use scrypto::crypto::{hash, Hash, PublicKey}; +use scrypto::resource::{NonFungibleAddress, NonFungibleId}; -use crate::model::{ExecutableInstruction, ExecutableTransaction, TransactionIntent}; +use crate::model::{ExecutableTransaction, Instruction, TransactionIntent}; #[derive(Debug, Clone, TypeId, Encode, Decode, PartialEq, Eq)] pub struct PreviewFlags { @@ -30,13 +32,7 @@ impl PreviewIntent { pub struct ValidatedPreviewTransaction { pub preview_intent: PreviewIntent, pub transaction_hash: Hash, - pub instructions: Vec, -} - -impl ValidatedPreviewTransaction { - pub fn signer_public_keys(&self) -> &[PublicKey] { - &self.preview_intent.signer_public_keys - } + pub instructions: Vec, } impl ExecutableTransaction for ValidatedPreviewTransaction { @@ -48,12 +44,23 @@ impl ExecutableTransaction for ValidatedPreviewTransaction { scrypto_encode(&self.preview_intent.intent.manifest.instructions).len() as u32 } - fn instructions(&self) -> &[ExecutableInstruction] { + fn instructions(&self) -> &[Instruction] { &self.instructions } - fn signer_public_keys(&self) -> &[PublicKey] { - &self.signer_public_keys() + fn initial_proofs(&self) -> Vec { + self.preview_intent + .signer_public_keys + .iter() + .map(|k| match k { + PublicKey::EddsaEd25519(pk) => { + NonFungibleAddress::new(ED25519_TOKEN, NonFungibleId::from_bytes(pk.to_vec())) + } + PublicKey::EcdsaSecp256k1(pk) => { + NonFungibleAddress::new(ECDSA_TOKEN, NonFungibleId::from_bytes(pk.to_vec())) + } + }) + .collect() } fn cost_unit_limit(&self) -> u32 { diff --git a/transaction/src/model/test_transaction.rs b/transaction/src/model/test_transaction.rs index 260339302e0..24e32ac2a36 100644 --- a/transaction/src/model/test_transaction.rs +++ b/transaction/src/model/test_transaction.rs @@ -2,6 +2,7 @@ use sbor::rust::vec::Vec; use scrypto::buffer::scrypto_encode; use scrypto::core::NetworkDefinition; use scrypto::crypto::*; +use scrypto::resource::NonFungibleAddress; use crate::builder::TransactionBuilder; use crate::model::*; @@ -9,7 +10,6 @@ use crate::model::*; /// Represents a test transaction, for testing/simulation purpose only. pub struct TestTransaction { pub transaction: NotarizedTransaction, - pub executable_instructions: Vec, pub signer_public_keys: Vec, } @@ -32,121 +32,11 @@ impl TestTransaction { tip_percentage: 5, }) .manifest(manifest) - .signer_signatures( - signer_public_keys - .iter() - .cloned() - .map(|pk| match pk { - PublicKey::EcdsaSecp256k1(_) => EcdsaSecp256k1Signature([0u8; 65]).into(), - PublicKey::EddsaEd25519(pk) => { - (pk, EddsaEd25519Signature([0u8; 64])).into() - } - }) - .collect(), - ) .notary_signature(EcdsaSecp256k1Signature([0u8; 65]).into()) .build(); - let executable_instructions = transaction - .signed_intent - .intent - .manifest - .instructions - .iter() - .map(|i| match i.clone() { - Instruction::TakeFromWorktop { resource_address } => { - ExecutableInstruction::TakeFromWorktop { resource_address } - } - Instruction::TakeFromWorktopByAmount { - amount, - resource_address, - } => ExecutableInstruction::TakeFromWorktopByAmount { - amount, - resource_address, - }, - Instruction::TakeFromWorktopByIds { - ids, - resource_address, - } => ExecutableInstruction::TakeFromWorktopByIds { - ids, - resource_address, - }, - Instruction::ReturnToWorktop { bucket_id } => { - ExecutableInstruction::ReturnToWorktop { bucket_id } - } - Instruction::AssertWorktopContains { resource_address } => { - ExecutableInstruction::AssertWorktopContains { resource_address } - } - Instruction::AssertWorktopContainsByAmount { - amount, - resource_address, - } => ExecutableInstruction::AssertWorktopContainsByAmount { - amount, - resource_address, - }, - Instruction::AssertWorktopContainsByIds { - ids, - resource_address, - } => ExecutableInstruction::AssertWorktopContainsByIds { - ids, - resource_address, - }, - Instruction::PopFromAuthZone => ExecutableInstruction::PopFromAuthZone, - Instruction::PushToAuthZone { proof_id } => { - ExecutableInstruction::PushToAuthZone { proof_id } - } - Instruction::ClearAuthZone => ExecutableInstruction::ClearAuthZone, - Instruction::CreateProofFromAuthZone { resource_address } => { - ExecutableInstruction::CreateProofFromAuthZone { resource_address } - } - Instruction::CreateProofFromAuthZoneByAmount { - amount, - resource_address, - } => ExecutableInstruction::CreateProofFromAuthZoneByAmount { - amount, - resource_address, - }, - Instruction::CreateProofFromAuthZoneByIds { - ids, - resource_address, - } => ExecutableInstruction::CreateProofFromAuthZoneByIds { - ids, - resource_address, - }, - Instruction::CreateProofFromBucket { bucket_id } => { - ExecutableInstruction::CreateProofFromBucket { bucket_id } - } - Instruction::CloneProof { proof_id } => { - ExecutableInstruction::CloneProof { proof_id } - } - Instruction::DropProof { proof_id } => { - ExecutableInstruction::DropProof { proof_id } - } - Instruction::DropAllProofs => ExecutableInstruction::DropAllProofs, - Instruction::CallFunction { - fn_identifier, - args, - } => ExecutableInstruction::CallFunction { - fn_identifier, - args, - }, - Instruction::CallMethod { - method_identifier, - args, - } => ExecutableInstruction::CallMethod { - method_identifier, - args, - }, - - Instruction::PublishPackage { code, abi } => { - ExecutableInstruction::PublishPackage { code, abi } - } - }) - .collect(); - Self { transaction, - executable_instructions, signer_public_keys, } } @@ -169,12 +59,12 @@ impl ExecutableTransaction for TestTransaction { self.transaction.signed_intent.intent.header.tip_percentage } - fn instructions(&self) -> &[ExecutableInstruction] { - &self.executable_instructions + fn instructions(&self) -> &[Instruction] { + &self.transaction.signed_intent.intent.manifest.instructions } - fn signer_public_keys(&self) -> &[PublicKey] { - &self.signer_public_keys + fn initial_proofs(&self) -> Vec { + AuthModule::signer_keys_to_non_fungibles(&self.signer_public_keys) } fn blobs(&self) -> &[Vec] { diff --git a/transaction/src/model/validated_transaction.rs b/transaction/src/model/validated_transaction.rs index 6f58b5d8d4a..a193359764b 100644 --- a/transaction/src/model/validated_transaction.rs +++ b/transaction/src/model/validated_transaction.rs @@ -1,45 +1,78 @@ use sbor::rust::vec::Vec; -use sbor::*; use scrypto::buffer::scrypto_encode; use scrypto::crypto::*; +use scrypto::resource::NonFungibleAddress; use crate::model::*; /// Represents a validated transaction -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeId)] -pub struct ValidatedTransaction { - pub transaction: NotarizedTransaction, +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Validated { + pub transaction: T, pub transaction_hash: Hash, - pub instructions: Vec, - pub signer_public_keys: Vec, + pub instructions: Vec, + pub initial_proofs: Vec, + pub cost_unit_limit: u32, + pub tip_percentage: u32, + pub blobs: Vec>, } -impl ExecutableTransaction for ValidatedTransaction { +impl Validated { + pub fn new( + transaction: T, + transaction_hash: Hash, + instructions: Vec, + initial_proofs: Vec, + cost_unit_limit: u32, + tip_percentage: u32, + blobs: Vec>, + ) -> Self { + Self { + transaction, + transaction_hash, + instructions, + initial_proofs, + cost_unit_limit, + tip_percentage, + blobs, + } + } + + pub fn into_transaction(self) -> T { + self.transaction + } + + pub fn transaction(&self) -> &T { + &self.transaction + } +} + +impl ExecutableTransaction for Validated { fn transaction_hash(&self) -> Hash { self.transaction_hash } fn manifest_instructions_size(&self) -> u32 { - scrypto_encode(&self.transaction.signed_intent.intent.manifest.instructions).len() as u32 + scrypto_encode(&self.instructions).len() as u32 } fn cost_unit_limit(&self) -> u32 { - self.transaction.signed_intent.intent.header.cost_unit_limit + self.cost_unit_limit } fn tip_percentage(&self) -> u32 { - self.transaction.signed_intent.intent.header.tip_percentage + self.tip_percentage } - fn instructions(&self) -> &[ExecutableInstruction] { + fn instructions(&self) -> &[Instruction] { &self.instructions } - fn signer_public_keys(&self) -> &[PublicKey] { - &self.signer_public_keys + fn initial_proofs(&self) -> Vec { + self.initial_proofs.clone() } fn blobs(&self) -> &[Vec] { - &self.transaction.signed_intent.intent.manifest.blobs + &self.blobs } } diff --git a/transaction/src/validation/id_allocator.rs b/transaction/src/validation/id_allocator.rs index 2946c851f9d..0462d05d16f 100644 --- a/transaction/src/validation/id_allocator.rs +++ b/transaction/src/validation/id_allocator.rs @@ -7,9 +7,6 @@ use scrypto::resource::ResourceAddress; use crate::errors::*; -pub const ECDSA_TOKEN_BUCKET_ID: BucketId = 0; -pub const ED25519_TOKEN_BUCKET_ID: BucketId = 1; - #[derive(Debug, Clone, PartialEq, Eq)] pub enum IdSpace { System, diff --git a/transaction/src/validation/transaction_validator.rs b/transaction/src/validation/transaction_validator.rs index 740826bc0be..008696ee52d 100644 --- a/transaction/src/validation/transaction_validator.rs +++ b/transaction/src/validation/transaction_validator.rs @@ -1,81 +1,100 @@ +use sbor::Decode; use std::collections::HashSet; -use sbor::rust::vec; use scrypto::buffer::scrypto_decode; -use scrypto::crypto::*; +use scrypto::crypto::PublicKey; use scrypto::values::*; use crate::errors::{SignatureValidationError, *}; use crate::model::*; use crate::validation::*; -pub struct ValidationConfig { - pub network_id: u8, - pub current_epoch: u64, - pub max_cost_unit_limit: u32, - pub min_tip_percentage: u32, -} +pub const MAX_PAYLOAD_SIZE: usize = 4 * 1024 * 1024; -pub struct TransactionValidator; - -impl TransactionValidator { - pub const MAX_PAYLOAD_SIZE: usize = 4 * 1024 * 1024; - - pub fn validate_from_slice( +pub trait TransactionValidator { + fn validate_from_slice( + &self, transaction: &[u8], intent_hash_manager: &I, - config: &ValidationConfig, - ) -> Result { - if transaction.len() > Self::MAX_PAYLOAD_SIZE { + ) -> Result, TransactionValidationError> { + if transaction.len() > MAX_PAYLOAD_SIZE { return Err(TransactionValidationError::TransactionTooLarge); } - let transaction: NotarizedTransaction = scrypto_decode(transaction) + let transaction: T = scrypto_decode(transaction) .map_err(TransactionValidationError::DeserializationError)?; - Self::validate(transaction, intent_hash_manager, config) + self.validate(transaction, intent_hash_manager) } - pub fn validate( + fn validate( + &self, + transaction: T, + intent_hash_manager: &I, + ) -> Result, TransactionValidationError>; +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ValidationConfig { + pub network_id: u8, + pub current_epoch: u64, + pub max_cost_unit_limit: u32, + pub min_tip_percentage: u32, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct NotarizedTransactionValidator { + config: ValidationConfig, +} + +impl TransactionValidator for NotarizedTransactionValidator { + fn validate( + &self, transaction: NotarizedTransaction, intent_hash_manager: &I, - config: &ValidationConfig, - ) -> Result { + ) -> Result, TransactionValidationError> { // verify the intent - let instructions = Self::validate_intent( - &transaction.signed_intent.intent, - intent_hash_manager, - config, - )?; + let instructions = + self.validate_intent(&transaction.signed_intent.intent, intent_hash_manager)?; // verify signatures - let mut signers = Self::validate_signatures(&transaction) + let keys = self + .validate_signatures(&transaction) .map_err(TransactionValidationError::SignatureValidationError)?; - if transaction.signed_intent.intent.header.notary_as_signatory { - signers.insert(transaction.signed_intent.intent.header.notary_public_key); - } - // TODO: whether to use intent hash or transaction hash let transaction_hash = transaction.hash(); - Ok(ValidatedTransaction { + let cost_unit_limit = transaction.signed_intent.intent.header.cost_unit_limit; + let tip_percentage = transaction.signed_intent.intent.header.tip_percentage; + let blobs = transaction.signed_intent.intent.manifest.blobs.clone(); + + Ok(Validated::new( transaction, transaction_hash, instructions, - signer_public_keys: signers.into_iter().collect(), - }) + AuthModule::signer_keys_to_non_fungibles(&keys), + cost_unit_limit, + tip_percentage, + blobs, + )) + } +} + +impl NotarizedTransactionValidator { + pub fn new(config: ValidationConfig) -> Self { + Self { config } } pub fn validate_preview_intent( + &self, preview_intent: PreviewIntent, intent_hash_manager: &I, - config: &ValidationConfig, ) -> Result { let intent = &preview_intent.intent; let transaction_hash = preview_intent.hash(); - let instructions = Self::validate_intent(&intent, intent_hash_manager, config)?; + let instructions = self.validate_intent(&intent, intent_hash_manager)?; Ok(ValidatedPreviewTransaction { preview_intent, @@ -85,190 +104,110 @@ impl TransactionValidator { } pub fn validate_intent( + &self, intent: &TransactionIntent, intent_hash_manager: &I, - config: &ValidationConfig, - ) -> Result, TransactionValidationError> { + ) -> Result, TransactionValidationError> { // verify intent hash if !intent_hash_manager.allows(&intent.hash()) { return Err(TransactionValidationError::IntentHashRejected); } // verify intent header - Self::validate_header(&intent, config) + self.validate_header(&intent) .map_err(TransactionValidationError::HeaderValidationError)?; - let mut instructions = vec![]; - // semantic analysis let mut id_validator = IdValidator::new(); for inst in &intent.manifest.instructions { match inst.clone() { - Instruction::TakeFromWorktop { resource_address } => { + Instruction::TakeFromWorktop { .. } => { id_validator .new_bucket() .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::TakeFromWorktop { resource_address }); } - Instruction::TakeFromWorktopByAmount { - amount, - resource_address, - } => { + Instruction::TakeFromWorktopByAmount { .. } => { id_validator .new_bucket() .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::TakeFromWorktopByAmount { - amount, - resource_address, - }); } - Instruction::TakeFromWorktopByIds { - ids, - resource_address, - } => { + Instruction::TakeFromWorktopByIds { .. } => { id_validator .new_bucket() .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::TakeFromWorktopByIds { - ids, - resource_address, - }); } Instruction::ReturnToWorktop { bucket_id } => { id_validator .drop_bucket(bucket_id) .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::ReturnToWorktop { bucket_id }); - } - Instruction::AssertWorktopContains { resource_address } => { - instructions - .push(ExecutableInstruction::AssertWorktopContains { resource_address }); - } - Instruction::AssertWorktopContainsByAmount { - amount, - resource_address, - } => { - instructions.push(ExecutableInstruction::AssertWorktopContainsByAmount { - amount, - resource_address, - }); - } - Instruction::AssertWorktopContainsByIds { - ids, - resource_address, - } => { - instructions.push(ExecutableInstruction::AssertWorktopContainsByIds { - ids, - resource_address, - }); } + Instruction::AssertWorktopContains { .. } => {} + Instruction::AssertWorktopContainsByAmount { .. } => {} + Instruction::AssertWorktopContainsByIds { .. } => {} Instruction::PopFromAuthZone => { id_validator .new_proof(ProofKind::AuthZoneProof) .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::PopFromAuthZone); } Instruction::PushToAuthZone { proof_id } => { id_validator .drop_proof(proof_id) .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::PushToAuthZone { proof_id }); - } - Instruction::ClearAuthZone => { - instructions.push(ExecutableInstruction::ClearAuthZone); } - Instruction::CreateProofFromAuthZone { resource_address } => { + Instruction::ClearAuthZone => {} + Instruction::CreateProofFromAuthZone { .. } => { id_validator .new_proof(ProofKind::AuthZoneProof) .map_err(TransactionValidationError::IdValidationError)?; - instructions - .push(ExecutableInstruction::CreateProofFromAuthZone { resource_address }); } - Instruction::CreateProofFromAuthZoneByAmount { - amount, - resource_address, - } => { + Instruction::CreateProofFromAuthZoneByAmount { .. } => { id_validator .new_proof(ProofKind::AuthZoneProof) .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::CreateProofFromAuthZoneByAmount { - amount, - resource_address, - }); } - Instruction::CreateProofFromAuthZoneByIds { - ids, - resource_address, - } => { + Instruction::CreateProofFromAuthZoneByIds { .. } => { id_validator .new_proof(ProofKind::AuthZoneProof) .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::CreateProofFromAuthZoneByIds { - ids, - resource_address, - }); } Instruction::CreateProofFromBucket { bucket_id } => { id_validator .new_proof(ProofKind::BucketProof(bucket_id)) .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::CreateProofFromBucket { bucket_id }); } Instruction::CloneProof { proof_id } => { id_validator .clone_proof(proof_id) .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::CloneProof { proof_id }); } Instruction::DropProof { proof_id } => { id_validator .drop_proof(proof_id) .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::DropProof { proof_id }); } Instruction::DropAllProofs => { id_validator .drop_all_proofs() .map_err(TransactionValidationError::IdValidationError)?; - instructions.push(ExecutableInstruction::DropAllProofs); } - Instruction::CallFunction { - fn_identifier, - args, - } => { + Instruction::CallFunction { args, .. } => { // TODO: decode into Value Self::validate_call_data(&args, &mut id_validator) .map_err(TransactionValidationError::CallDataValidationError)?; - instructions.push(ExecutableInstruction::CallFunction { - fn_identifier, - args, - }); } - Instruction::CallMethod { - method_identifier, - args, - } => { + Instruction::CallMethod { args, .. } => { // TODO: decode into Value Self::validate_call_data(&args, &mut id_validator) .map_err(TransactionValidationError::CallDataValidationError)?; - instructions.push(ExecutableInstruction::CallMethod { - method_identifier, - args, - }); - } - Instruction::PublishPackage { code, abi } => { - instructions.push(ExecutableInstruction::PublishPackage { code, abi }); } + Instruction::PublishPackage { .. } => {} } } - return Ok(instructions); + return Ok(intent.manifest.instructions.clone()); } - pub fn validate_header( - intent: &TransactionIntent, - config: &ValidationConfig, - ) -> Result<(), HeaderValidationError> { + pub fn validate_header(&self, intent: &TransactionIntent) -> Result<(), HeaderValidationError> { let header = &intent.header; // version @@ -277,7 +216,7 @@ impl TransactionValidator { } // network - if header.network_id != config.network_id { + if header.network_id != self.config.network_id { return Err(HeaderValidationError::InvalidNetwork); } @@ -288,17 +227,17 @@ impl TransactionValidator { if header.end_epoch_exclusive - header.start_epoch_inclusive > MAX_EPOCH_DURATION { return Err(HeaderValidationError::EpochRangeTooLarge); } - if config.current_epoch < header.start_epoch_inclusive - || config.current_epoch >= header.end_epoch_exclusive + if self.config.current_epoch < header.start_epoch_inclusive + || self.config.current_epoch >= header.end_epoch_exclusive { return Err(HeaderValidationError::OutOfEpochRange); } // cost unit limit and tip - if header.cost_unit_limit > config.max_cost_unit_limit { + if header.cost_unit_limit > self.config.max_cost_unit_limit { return Err(HeaderValidationError::InvalidCostUnitLimit); } - if header.tip_percentage < config.min_tip_percentage { + if header.tip_percentage < self.config.min_tip_percentage { return Err(HeaderValidationError::InvalidTipBps); } @@ -306,16 +245,17 @@ impl TransactionValidator { } pub fn validate_signatures( + &self, transaction: &NotarizedTransaction, - ) -> Result, SignatureValidationError> { + ) -> Result, SignatureValidationError> { // TODO: split into static validation part and runtime validation part to support more signatures if transaction.signed_intent.intent_signatures.len() > MAX_NUMBER_OF_INTENT_SIGNATURES { return Err(SignatureValidationError::TooManySignatures); } // verify intent signature - let intent_payload = transaction.signed_intent.intent.to_bytes(); let mut signers = HashSet::new(); + let intent_payload = transaction.signed_intent.intent.to_bytes(); for sig in &transaction.signed_intent.intent_signatures { let public_key = recover(&intent_payload, sig) .ok_or(SignatureValidationError::InvalidIntentSignature)?; @@ -329,6 +269,10 @@ impl TransactionValidator { } } + if transaction.signed_intent.intent.header.notary_as_signatory { + signers.insert(transaction.signed_intent.intent.header.notary_public_key); + } + // verify notary signature let signed_intent_payload = transaction.signed_intent.to_bytes(); if !verify( @@ -339,7 +283,7 @@ impl TransactionValidator { return Err(SignatureValidationError::InvalidNotarySignature); } - Ok(signers) + Ok(signers.into_iter().collect()) } pub fn validate_call_data( @@ -381,9 +325,10 @@ mod tests { max_cost_unit_limit: 10_000_000, min_tip_percentage: 0, }; + let validator = NotarizedTransactionValidator::new(config); assert_eq!( Err($result), - TransactionValidator::validate( + validator.validate( create_transaction( $version, $start_epoch, @@ -393,7 +338,6 @@ mod tests { $notary ), &mut intent_hash_manager, - &config, ) ); }}; @@ -446,17 +390,18 @@ mod tests { #[test] fn test_valid_preview() { let mut intent_hash_manager: TestIntentHashManager = TestIntentHashManager::new(); - let config: ValidationConfig = ValidationConfig { + + // Build the whole transaction but only really care about the intent + let tx = create_transaction(1, 0, 100, 5, vec![1, 2], 2); + + let validator = NotarizedTransactionValidator::new(ValidationConfig { network_id: NetworkDefinition::simulator().id, current_epoch: 1, max_cost_unit_limit: 10_000_000, min_tip_percentage: 0, - }; - - // Build the whole transaction but only really care about the intent - let tx = create_transaction(1, 0, 100, 5, vec![1, 2], 2); + }); - let result = TransactionValidator::validate_preview_intent( + let result = validator.validate_preview_intent( PreviewIntent { intent: tx.signed_intent.intent, signer_public_keys: Vec::new(), @@ -465,7 +410,6 @@ mod tests { }, }, &mut intent_hash_manager, - &config, ); assert!(result.is_ok());