From 058a0a7294db9b6be7dd44c81c515cda70c986a7 Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Thu, 27 Jun 2024 12:51:59 +0400 Subject: [PATCH] fix(engine)!: allow multiple create components in one call (#1046) Description --- - assigns a unique component address for each call to create_component - formalises component addresses using a public key - adds `.with_public_key_address` to component builder - adds new test for multiple component creation - use `with_public_key_address` in account template - simplified the component address from public key derivation. Motivation and Context --- Previously you could not create multiple components in one call without explicitly allocating an address. This PR changes the default behaviour to generate a unique address, thereby fixing the bug when creating multiple components in a single call. Some components need the address to be derived ahead of time using a public key can do this by optionally specifying a public key address on the component. How Has This Been Tested? --- New multiple component creation test. Existing account tests. Manually What process can a PR reviewer use to test or verify this change? --- Use a template that creates multiple components. Create an account that does not exist for another public key. Breaking Changes --- - [ ] None - [ ] Requires data directory to be deleted - [x] Other - Please specify BREAKING CHANGE: caller context calls have an additional `arg` field, so templates using these need to be recompiled. --- .../src/handlers/accounts.rs | 6 +-- dan_layer/engine/src/runtime/impl.rs | 43 +++++++++++++++---- dan_layer/engine/src/runtime/mod.rs | 6 ++- dan_layer/engine/src/runtime/tracker.rs | 4 +- dan_layer/engine/src/transaction/processor.rs | 7 +-- dan_layer/engine/src/wasm/process.rs | 4 +- .../tests/templates/access_rules/src/lib.rs | 6 +-- .../templates/address_allocation/src/lib.rs | 4 +- .../engine/tests/templates/state/src/lib.rs | 8 ++++ dan_layer/engine/tests/test.rs | 34 ++++++++++++--- dan_layer/engine_types/src/component.rs | 18 ++++---- dan_layer/engine_types/src/id_provider.rs | 39 ++++++++--------- .../templates/account/src/lib.rs | 1 + dan_layer/template_lib/src/args/types.rs | 1 + dan_layer/template_lib/src/caller_context.rs | 7 ++- .../template_lib/src/component/instance.rs | 26 ++++++++--- .../src/read_only_state_store.rs | 15 +++++++ .../sdk/src/apis/confidential_transfer.rs | 4 +- utilities/tariswap_test_bench/src/accounts.rs | 4 +- .../src/transaction_builders/free_coins.rs | 4 +- 20 files changed, 167 insertions(+), 74 deletions(-) diff --git a/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs b/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs index f2a4c0948..89172be39 100644 --- a/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs +++ b/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs @@ -23,7 +23,7 @@ use tari_dan_wallet_sdk::{ }; use tari_dan_wallet_storage_sqlite::SqliteWalletStore; use tari_engine_types::{ - component::new_account_address_from_parts, + component::new_component_address_from_public_key, confidential::ConfidentialClaim, instruction::Instruction, substate::{Substate, SubstateId}, @@ -834,7 +834,7 @@ fn get_or_create_account( .unwrap_or_else(|| sdk.key_manager_api().next_key(key_manager::TRANSACTION_BRANCH))?; let account_pk = PublicKey::from_secret_key(&account_secret_key.key); - let account_address = new_account_address_from_parts(&ACCOUNT_TEMPLATE_ADDRESS, &account_pk); + let account_address = new_component_address_from_public_key(&ACCOUNT_TEMPLATE_ADDRESS, &account_pk); // We have no involved substate addresses, so we need to add an output (account_address.into(), account_secret_key, Some(name.to_string())) @@ -882,7 +882,7 @@ pub async fn handle_transfer( let mut fee_instructions = vec![]; let destination_account_address = - new_account_address_from_parts(&ACCOUNT_TEMPLATE_ADDRESS, &req.destination_public_key); + new_component_address_from_public_key(&ACCOUNT_TEMPLATE_ADDRESS, &req.destination_public_key); let existing_account = sdk .substate_api() .scan_for_substate(&SubstateId::Component(destination_account_address), None) diff --git a/dan_layer/engine/src/runtime/impl.rs b/dan_layer/engine/src/runtime/impl.rs index 81bd38638..0b83a720d 100644 --- a/dan_layer/engine/src/runtime/impl.rs +++ b/dan_layer/engine/src/runtime/impl.rs @@ -471,11 +471,16 @@ impl> RuntimeInte self.tracker.lock_substate(&SubstateId::Component(*address), lock_flag) } - fn caller_context_invoke(&self, action: CallerContextAction) -> Result { + fn caller_context_invoke( + &self, + action: CallerContextAction, + args: EngineArgs, + ) -> Result { self.invoke_modules_on_runtime_call("caller_context_invoke")?; match action { CallerContextAction::GetCallerPublicKey => { + args.assert_no_args("CallerContextAction::GetCallerPublicKey")?; let sender_public_key = RistrettoPublicKeyBytes::from_bytes(self.transaction_signer_public_key.as_bytes()).expect( "RistrettoPublicKeyBytes::from_bytes should be infallible when called with RistrettoPublicKey \ @@ -485,6 +490,7 @@ impl> RuntimeInte Ok(InvokeResult::encode(&sender_public_key)?) }, CallerContextAction::GetComponentAddress => self.tracker.read_with(|state| { + args.assert_no_args("CallerContextAction::GetComponentAddress")?; let call_frame = state.current_call_scope()?; let maybe_address = call_frame .get_current_component_lock() @@ -492,8 +498,22 @@ impl> RuntimeInte Ok(InvokeResult::encode(&maybe_address)?) }), CallerContextAction::AllocateNewComponentAddress => self.tracker.write_with(|state| { + let public_key_address: Option = args.assert_one_arg()?; + let public_key_address = public_key_address + .map(|pk| { + RistrettoPublicKey::from_canonical_bytes(pk.as_bytes()).map_err(|_| { + RuntimeError::InvalidArgument { + argument: "public_key_address", + reason: "Invalid RistrettoPublicKeyBytes".to_string(), + } + }) + }) + .transpose()?; + let (template, _) = state.current_template()?; - let address = state.id_provider()?.new_component_address(*template, None)?; + let address = state + .id_provider()? + .new_component_address(*template, public_key_address)?; let allocation = state.new_address_allocation(address)?; Ok(InvokeResult::encode(&allocation)?) }), @@ -525,13 +545,18 @@ impl> RuntimeInte match action { ComponentAction::Create => { - let arg: CreateComponentArg = args.assert_one_arg()?; + let CreateComponentArg { + encoded_state, + owner_rule, + access_rules, + address_allocation, + } = args.assert_one_arg()?; let template_addr = self.tracker.get_template_address()?; let template_def = self.get_template_def(&template_addr)?; - validate_component_access_rule_methods(&arg.access_rules, &template_def)?; + validate_component_access_rule_methods(&access_rules, &template_def)?; - let owner_key = match arg.owner_rule { + let owner_key = match owner_rule { OwnerRule::OwnedBySigner => { Some(to_ristretto_public_key_bytes(&self.transaction_signer_public_key)) }, @@ -541,11 +566,11 @@ impl> RuntimeInte }; let component_address = self.tracker.new_component( - arg.encoded_state, + encoded_state, owner_key, - arg.owner_rule, - arg.access_rules, - arg.address_allocation, + owner_rule, + access_rules, + address_allocation, )?; Ok(InvokeResult::encode(&component_address)?) }, diff --git a/dan_layer/engine/src/runtime/mod.rs b/dan_layer/engine/src/runtime/mod.rs index 51e8a2ccb..62ff6c9e5 100644 --- a/dan_layer/engine/src/runtime/mod.rs +++ b/dan_layer/engine/src/runtime/mod.rs @@ -168,7 +168,11 @@ pub trait RuntimeInterface: Send + Sync { fn reset_to_fee_checkpoint(&self) -> Result<(), RuntimeError>; fn finalize(&self) -> Result; - fn caller_context_invoke(&self, action: CallerContextAction) -> Result; + fn caller_context_invoke( + &self, + action: CallerContextAction, + args: EngineArgs, + ) -> Result; fn call_invoke(&self, action: CallAction, args: EngineArgs) -> Result; diff --git a/dan_layer/engine/src/runtime/tracker.rs b/dan_layer/engine/src/runtime/tracker.rs index a674813c2..2b6f7ffd0 100644 --- a/dan_layer/engine/src/runtime/tracker.rs +++ b/dan_layer/engine/src/runtime/tracker.rs @@ -166,9 +166,7 @@ impl StateTracker { addr.try_into() .map_err(|address| RuntimeError::AddressAllocationTypeMismatch { address })? }, - None => state - .id_provider()? - .new_component_address(template_address, owner_key)?, + None => state.id_provider()?.new_component_address(template_address, None)?, }; let component = ComponentBody { state: component_state }; diff --git a/dan_layer/engine/src/transaction/processor.rs b/dan_layer/engine/src/transaction/processor.rs index a64616e10..b956dbe9a 100644 --- a/dan_layer/engine/src/transaction/processor.rs +++ b/dan_layer/engine/src/transaction/processor.rs @@ -29,6 +29,7 @@ use tari_common_types::types::PublicKey; use tari_dan_common_types::{services::template_provider::TemplateProvider, Epoch}; use tari_engine_types::{ commit_result::{ExecuteResult, FinalizeResult, RejectReason, TransactionResult}, + component::new_component_address_from_public_key, entity_id_provider::EntityIdProvider, indexed_value::{IndexedValue, IndexedWellKnownTypes}, instruction::Instruction, @@ -319,9 +320,9 @@ impl + 'static> T } })?; let owner_pk = RistrettoPublicKeyBytes::from_bytes(owner_public_key.as_bytes()).unwrap(); - let owner_token = NonFungibleAddress::from_public_key(owner_pk); + let account_address = new_component_address_from_public_key(&ACCOUNT_TEMPLATE_ADDRESS, owner_public_key); - let mut args = args![owner_token]; + let mut args = args![NonFungibleAddress::from_public_key(owner_pk)]; if let Some(workspace_bucket) = workspace_bucket { args.push(arg![Workspace(workspace_bucket)]); } @@ -336,7 +337,7 @@ impl + 'static> T template_address: ACCOUNT_TEMPLATE_ADDRESS, module_name: template.template_name().to_string(), arg_scope, - entity_id: owner_pk.as_hash().leading_bytes().into(), + entity_id: account_address.entity_id(), })?; let result = Self::invoke_template(template, template_provider, runtime.clone(), function_def, args)?; diff --git a/dan_layer/engine/src/wasm/process.rs b/dan_layer/engine/src/wasm/process.rs index 20caba0cb..f63ebde46 100644 --- a/dan_layer/engine/src/wasm/process.rs +++ b/dan_layer/engine/src/wasm/process.rs @@ -150,7 +150,9 @@ impl WasmProcess { env.state().interface().consensus_invoke(arg.action) }), EngineOp::CallerContextInvoke => Self::handle(env, arg, |env, arg: CallerContextInvokeArg| { - env.state().interface().caller_context_invoke(arg.action) + env.state() + .interface() + .caller_context_invoke(arg.action, arg.args.into()) }), EngineOp::GenerateRandomInvoke => Self::handle(env, arg, |env, arg: GenerateRandomInvokeArg| { env.state().interface().generate_random_invoke(arg.action) diff --git a/dan_layer/engine/tests/templates/access_rules/src/lib.rs b/dan_layer/engine/tests/templates/access_rules/src/lib.rs index 956c8ca50..5651ab1d8 100644 --- a/dan_layer/engine/tests/templates/access_rules/src/lib.rs +++ b/dan_layer/engine/tests/templates/access_rules/src/lib.rs @@ -72,7 +72,7 @@ mod access_rules_template { pub fn with_auth_hook(allowed: bool, hook: String) -> Component { let badges = create_badge_resource(AccessRule::DenyAll); - let address_alloc = CallerContext::allocate_component_address(); + let address_alloc = CallerContext::allocate_component_address(None); let tokens = ResourceBuilder::fungible() .with_authorization_hook(*address_alloc.address(), hook) @@ -93,7 +93,7 @@ mod access_rules_template { pub fn with_auth_hook_attack_component(component_address: ComponentAddress) -> Component { let badges = create_badge_resource(AccessRule::DenyAll); - let address_alloc = CallerContext::allocate_component_address(); + let address_alloc = CallerContext::allocate_component_address(None); let tokens = ResourceBuilder::fungible() .with_authorization_hook( @@ -185,7 +185,7 @@ mod access_rules_template { pub fn resource_actions_restricted_to_component() -> Component { let badges = create_badge_resource(AccessRule::AllowAll); - let allocation = CallerContext::allocate_component_address(); + let allocation = CallerContext::allocate_component_address(None); let tokens = ResourceBuilder::fungible() .mintable(AccessRule::Restricted(RestrictedAccessRule::Require( RequireRule::Require(allocation.address().clone().into()), diff --git a/dan_layer/engine/tests/templates/address_allocation/src/lib.rs b/dan_layer/engine/tests/templates/address_allocation/src/lib.rs index 803c9d0a0..576727a03 100644 --- a/dan_layer/engine/tests/templates/address_allocation/src/lib.rs +++ b/dan_layer/engine/tests/templates/address_allocation/src/lib.rs @@ -11,7 +11,7 @@ mod template { impl AddressAllocationTest { pub fn create() -> (Component, ComponentAddress) { - let allocation = CallerContext::allocate_component_address(); + let allocation = CallerContext::allocate_component_address(None); let address = allocation.address().clone(); ( Component::new(Self {}).with_address_allocation(allocation).create(), @@ -20,7 +20,7 @@ mod template { } pub fn drop_allocation() { - let _allocation = CallerContext::allocate_component_address(); + let _allocation = CallerContext::allocate_component_address(None); } } } diff --git a/dan_layer/engine/tests/templates/state/src/lib.rs b/dan_layer/engine/tests/templates/state/src/lib.rs index eea86c356..4978c868b 100644 --- a/dan_layer/engine/tests/templates/state/src/lib.rs +++ b/dan_layer/engine/tests/templates/state/src/lib.rs @@ -37,6 +37,14 @@ mod state_template { .create() } + pub fn create_multiple(n: u32) { + (0..n).for_each(|i| { + Component::new(Self { value: i }) + .with_access_rules(AccessRules::new().default(AccessRule::AllowAll)) + .create(); + }); + } + pub fn restricted() -> Component { Component::new(Self { value: 0 }) .with_access_rules( diff --git a/dan_layer/engine/tests/test.rs b/dan_layer/engine/tests/test.rs index ce1dd8537..44f8724cd 100644 --- a/dan_layer/engine/tests/test.rs +++ b/dan_layer/engine/tests/test.rs @@ -87,6 +87,30 @@ fn test_state() { assert_eq!(value, new_value); } +#[test] +fn state_create_multiple_in_one_call() { + let mut template_test = TemplateTest::new(["tests/templates/state"]); + let store = template_test.read_only_state_store(); + + // constructor + template_test.call_function::<()>("State", "create_multiple", args![10u32], vec![]); + + let template_address = template_test.get_template_address("State"); + let mut count = 0usize; + store + .with_substates(|s| { + if s.substate_value() + .component() + .filter(|a| a.template_address == template_address) + .is_some() + { + count += 1; + } + }) + .unwrap(); + assert_eq!(count, 10); +} + #[test] fn test_composed() { let mut template_test = TemplateTest::new(vec!["tests/templates/state", "tests/templates/hello_world"]); @@ -107,7 +131,7 @@ fn test_composed() { .iter() .map(|f| f.name.as_str()) .collect::>(); - assert_eq!(functions, vec!["new", "restricted", "set", "get"]); + assert_eq!(functions, vec!["new", "create_multiple", "restricted", "set", "get"]); let component_state: ComponentAddress = template_test.call_function("State", "new", args![], vec![]); let component_hw: ComponentAddress = template_test.call_function("HelloWorld", "new", args!["أهلا"], vec![]); @@ -125,8 +149,6 @@ fn test_composed() { assert_eq!(value, new_value); } -// FIXME: this test breaks in CI but not in local -#[ignore] #[test] fn test_buggy_template() { let err = compile_template("tests/templates/buggy", &["return_null_abi"]) @@ -135,7 +157,7 @@ fn test_buggy_template() { .unwrap_err(); assert!(matches!( err, - TemplateLoaderError::WasmModuleError(WasmExecutionError::MemoryPointerOutOfRange { .. }) + TemplateLoaderError::WasmModuleError(WasmExecutionError::AbiDecodeError { .. }) )); let err = compile_template("tests/templates/buggy", &["unexpected_export_function"]) @@ -1334,7 +1356,7 @@ mod nft_indexes { // TODO: these tests can be removed when create free test coins is removed mod free_test_coins { - use tari_engine_types::component::new_account_address_from_parts; + use tari_engine_types::component::new_component_address_from_public_key; use super::*; #[test] @@ -1346,7 +1368,7 @@ mod free_test_coins { let owner_token = test.get_test_proof(); let future_account_component = - new_account_address_from_parts(&ACCOUNT_TEMPLATE_ADDRESS, test.get_test_public_key()); + new_component_address_from_public_key(&ACCOUNT_TEMPLATE_ADDRESS, test.get_test_public_key()); test.execute_expect_success( Transaction::builder() diff --git a/dan_layer/engine_types/src/component.rs b/dan_layer/engine_types/src/component.rs index ebf41f168..e8e365950 100644 --- a/dan_layer/engine_types/src/component.rs +++ b/dan_layer/engine_types/src/component.rs @@ -25,11 +25,9 @@ use tari_common_types::types::PublicKey; use tari_template_lib::{ auth::{ComponentAccessRules, OwnerRule, Ownership}, crypto::RistrettoPublicKeyBytes, - models::{ComponentKey, EntityId, ObjectKey, TemplateAddress}, + models::{EntityId, ObjectKey, TemplateAddress}, prelude::ComponentAddress, - Hash, }; -use tari_utilities::ByteArray; #[cfg(feature = "ts")] use ts_rs::TS; @@ -42,17 +40,17 @@ use crate::{ /// Derives a component address. /// -/// This can be used to derive the component address from a public key if the component sets OwnerRule::OwnedBySigner or -/// OwnerRule::ByPublicKey -pub fn new_account_address_from_parts(template_address: &TemplateAddress, public_key: &PublicKey) -> ComponentAddress { +/// This can be used to derive the component address from a public key if the component sets public_key_address in the +/// component builder. +pub fn new_component_address_from_public_key( + template_address: &TemplateAddress, + public_key: &PublicKey, +) -> ComponentAddress { let address = hasher32(EngineHashDomainLabel::ComponentAddress) .chain(template_address) .chain(public_key) .result(); - let key = ObjectKey::new( - EntityId::from_array(Hash::try_from(public_key.as_bytes()).unwrap().leading_bytes()), - ComponentKey::new(address.trailing_bytes()), - ); + let key = ObjectKey::from_array(address.leading_bytes()); ComponentAddress::new(key) } diff --git a/dan_layer/engine_types/src/id_provider.rs b/dan_layer/engine_types/src/id_provider.rs index 6996637a8..823e147a5 100644 --- a/dan_layer/engine_types/src/id_provider.rs +++ b/dan_layer/engine_types/src/id_provider.rs @@ -3,6 +3,7 @@ use std::sync::{atomic, atomic::AtomicU32}; +use tari_crypto::ristretto::RistrettoPublicKey; use tari_template_lib::{ models::{ BucketId, @@ -15,11 +16,13 @@ use tari_template_lib::{ TemplateAddress, VaultId, }, - prelude::RistrettoPublicKeyBytes, Hash, }; -use crate::hashing::{hasher32, EngineHashDomainLabel}; +use crate::{ + component::new_component_address_from_public_key, + hashing::{hasher32, EngineHashDomainLabel}, +}; #[derive(Debug, Clone)] pub struct IdProvider<'a> { @@ -53,25 +56,21 @@ impl<'a> IdProvider<'a> { pub fn new_component_address( &self, template_address: TemplateAddress, - owner_key_bytes: Option, + public_key_address: Option, ) -> Result { - // if the owner is a single key specified by the template logic, then it will be derived from the transaction - // hash - let component_id = match owner_key_bytes { - Some(key) => hasher32(EngineHashDomainLabel::ComponentAddress) - .chain(&template_address) - .chain(&key) - .result() - .into_array() - .into(), - None => hasher32(EngineHashDomainLabel::ComponentAddress) - .chain(&template_address) - .chain(&self.transaction_hash) - .chain(&self.next()?) - .result(), - }; - let key = ObjectKey::new(self.entity_id, ComponentKey::new(component_id.trailing_bytes())); - Ok(ComponentAddress::new(key)) + if let Some(key) = public_key_address { + // if a public key address is specified, then it will derive the address from the + // template hash and public key + return Ok(new_component_address_from_public_key(&template_address, &key)); + } + + let component_id = hasher32(EngineHashDomainLabel::ComponentAddress) + .chain(&self.transaction_hash) + .chain(&self.next()?) + .result(); + + let object_key = ObjectKey::new(self.entity_id, ComponentKey::new(component_id.trailing_bytes())); + Ok(ComponentAddress::new(object_key)) } pub fn new_vault_id(&self) -> Result { diff --git a/dan_layer/template_builtin/templates/account/src/lib.rs b/dan_layer/template_builtin/templates/account/src/lib.rs index a8cf25f1e..49a445c68 100644 --- a/dan_layer/template_builtin/templates/account/src/lib.rs +++ b/dan_layer/template_builtin/templates/account/src/lib.rs @@ -67,6 +67,7 @@ mod account_template { Component::new(Self { vaults }) .with_access_rules(rules) + .with_public_key_address(public_key) .with_owner_rule(OwnerRule::ByPublicKey(public_key)) .create() } diff --git a/dan_layer/template_lib/src/args/types.rs b/dan_layer/template_lib/src/args/types.rs index 389446783..f42921902 100644 --- a/dan_layer/template_lib/src/args/types.rs +++ b/dan_layer/template_lib/src/args/types.rs @@ -507,6 +507,7 @@ pub enum GenerateRandomAction { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CallerContextInvokeArg { pub action: CallerContextAction, + pub args: Vec>, } /// The possible actions that can be performed related to the caller context diff --git a/dan_layer/template_lib/src/caller_context.rs b/dan_layer/template_lib/src/caller_context.rs index 2204234ae..812ed51e0 100644 --- a/dan_layer/template_lib/src/caller_context.rs +++ b/dan_layer/template_lib/src/caller_context.rs @@ -19,6 +19,7 @@ impl CallerContext { pub fn transaction_signer_public_key() -> RistrettoPublicKeyBytes { let resp: InvokeResult = call_engine(EngineOp::CallerContextInvoke, &CallerContextInvokeArg { action: CallerContextAction::GetCallerPublicKey, + args: invoke_args![], }); resp.decode().expect("Failed to decode PublicKey") @@ -29,6 +30,7 @@ impl CallerContext { pub fn current_component_address() -> ComponentAddress { let resp: InvokeResult = call_engine(EngineOp::CallerContextInvoke, &CallerContextInvokeArg { action: CallerContextAction::GetComponentAddress, + args: invoke_args![], }); resp.decode::>() @@ -36,9 +38,12 @@ impl CallerContext { .expect("Not in a component instance context") } - pub fn allocate_component_address() -> AddressAllocation { + pub fn allocate_component_address( + public_key_address: Option, + ) -> AddressAllocation { let resp: InvokeResult = call_engine(EngineOp::CallerContextInvoke, &CallerContextInvokeArg { action: CallerContextAction::AllocateNewComponentAddress, + args: invoke_args![public_key_address], }); resp.decode() diff --git a/dan_layer/template_lib/src/component/instance.rs b/dan_layer/template_lib/src/component/instance.rs index 1642ac973..a810ac02d 100644 --- a/dan_layer/template_lib/src/component/instance.rs +++ b/dan_layer/template_lib/src/component/instance.rs @@ -5,6 +5,8 @@ use std::marker::PhantomData; use crate::{ auth::{ComponentAccessRules, OwnerRule}, + caller_context::CallerContext, + crypto::RistrettoPublicKeyBytes, engine, models::{AddressAllocation, ComponentAddress}, }; @@ -14,6 +16,7 @@ pub struct ComponentBuilder { component: T, owner_rule: OwnerRule, access_rules: ComponentAccessRules, + public_key_address: Option, address_allocation: Option>, } @@ -24,6 +27,7 @@ impl ComponentBuilder { component, owner_rule: OwnerRule::default(), access_rules: ComponentAccessRules::new(), + public_key_address: None, address_allocation: None, } } @@ -34,6 +38,11 @@ impl ComponentBuilder { self } + pub fn with_public_key_address(mut self, public_key: RistrettoPublicKeyBytes) -> Self { + self.public_key_address = Some(public_key); + self + } + /// Sets up who will be the owner of the component. /// Component owners are the only ones allowed to update the component's access rules after creation pub fn with_owner_rule(mut self, owner_rule: OwnerRule) -> Self { @@ -51,12 +60,17 @@ impl ComponentBuilder { impl ComponentBuilder { /// Creates the new component and returns it pub fn create(self) -> Component { - let address = engine().create_component( - self.component, - self.owner_rule, - self.access_rules, - self.address_allocation, - ); + if self.public_key_address.is_some() && self.address_allocation.is_some() { + panic!("Cannot specify both a public key address and an address allocation"); + } + + let address_allocation = self + .public_key_address + // Allocate public key address is necessary + .map(|pk| CallerContext::allocate_component_address(Some(pk))) + .or(self.address_allocation); + + let address = engine().create_component(self.component, self.owner_rule, self.access_rules, address_allocation); Component::from_address(address) } } diff --git a/dan_layer/template_test_tooling/src/read_only_state_store.rs b/dan_layer/template_test_tooling/src/read_only_state_store.rs index 44579ab47..667813d67 100644 --- a/dan_layer/template_test_tooling/src/read_only_state_store.rs +++ b/dan_layer/template_test_tooling/src/read_only_state_store.rs @@ -1,6 +1,7 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause +use tari_bor::decode_exact; use tari_dan_engine::state_store::{memory::MemoryStateStore, AtomicDb, StateReader, StateStoreError}; use tari_engine_types::{ component::ComponentHeader, @@ -39,9 +40,23 @@ impl ReadOnlyStateStore { Ok(IndexedValue::from_value(component.into_state()).unwrap()) } + pub fn count(&self) -> Result { + let tx = self.store.read_access()?; + let count = tx.iter_raw().count(); + Ok(count) + } + pub fn get_substate(&self, address: &SubstateId) -> Result { let tx = self.store.read_access()?; let substate = tx.get_state::<_, Substate>(address)?; Ok(substate) } + + pub fn with_substates(&self, mut f: F) -> Result<(), StateStoreError> + where F: FnMut(Substate) { + let tx = self.store.read_access()?; + tx.iter_raw() + .for_each(|(_, substate)| f(decode_exact(substate).unwrap())); + Ok(()) + } } diff --git a/dan_layer/wallet/sdk/src/apis/confidential_transfer.rs b/dan_layer/wallet/sdk/src/apis/confidential_transfer.rs index 9f1419c70..772f59919 100644 --- a/dan_layer/wallet/sdk/src/apis/confidential_transfer.rs +++ b/dan_layer/wallet/sdk/src/apis/confidential_transfer.rs @@ -10,7 +10,7 @@ use tari_common_types::types::{PrivateKey, PublicKey}; use tari_crypto::keys::PublicKey as _; use tari_dan_common_types::optional::{IsNotFoundError, Optional}; use tari_dan_wallet_crypto::{ConfidentialOutputMaskAndValue, ConfidentialProofStatement}; -use tari_engine_types::{component::new_account_address_from_parts, substate::SubstateId}; +use tari_engine_types::{component::new_component_address_from_public_key, substate::SubstateId}; use tari_template_builtin::ACCOUNT_TEMPLATE_ADDRESS; use tari_template_lib::{ args, @@ -209,7 +209,7 @@ where &self, destination_pk: &PublicKey, ) -> Result<(VersionedSubstateId, bool), ConfidentialTransferApiError> { - let account_component = new_account_address_from_parts(&ACCOUNT_TEMPLATE_ADDRESS, destination_pk); + let account_component = new_component_address_from_public_key(&ACCOUNT_TEMPLATE_ADDRESS, destination_pk); match self .substate_api .scan_for_substate(&account_component.into(), None) diff --git a/utilities/tariswap_test_bench/src/accounts.rs b/utilities/tariswap_test_bench/src/accounts.rs index 23b5f8b75..b95468f7a 100644 --- a/utilities/tariswap_test_bench/src/accounts.rs +++ b/utilities/tariswap_test_bench/src/accounts.rs @@ -5,7 +5,7 @@ use std::ops::RangeInclusive; use tari_crypto::{keys::PublicKey as _, ristretto::RistrettoPublicKey}; use tari_dan_wallet_sdk::{apis::key_manager::TRANSACTION_BRANCH, models::Account}; -use tari_engine_types::component::new_account_address_from_parts; +use tari_engine_types::component::new_component_address_from_public_key; use tari_template_builtin::ACCOUNT_TEMPLATE_ADDRESS; use tari_template_lib::{args, models::Amount}; use tari_transaction::{Instruction, Transaction}; @@ -17,7 +17,7 @@ impl Runner { let key = self.sdk.key_manager_api().derive_key(TRANSACTION_BRANCH, 0)?; let owner_public_key = RistrettoPublicKey::from_secret_key(&key.key); - let account_address = new_account_address_from_parts(&ACCOUNT_TEMPLATE_ADDRESS, &owner_public_key); + let account_address = new_component_address_from_public_key(&ACCOUNT_TEMPLATE_ADDRESS, &owner_public_key); let transaction = Transaction::builder() .with_fee_instructions_builder(|builder| { diff --git a/utilities/transaction_generator/src/transaction_builders/free_coins.rs b/utilities/transaction_generator/src/transaction_builders/free_coins.rs index cdcbca854..764280fec 100644 --- a/utilities/transaction_generator/src/transaction_builders/free_coins.rs +++ b/utilities/transaction_generator/src/transaction_builders/free_coins.rs @@ -3,7 +3,7 @@ use rand::rngs::OsRng; use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; -use tari_engine_types::{component::new_account_address_from_parts, instruction::Instruction}; +use tari_engine_types::{component::new_component_address_from_public_key, instruction::Instruction}; use tari_template_builtin::ACCOUNT_TEMPLATE_ADDRESS; use tari_template_lib::{args, models::Amount}; use tari_transaction::Transaction; @@ -11,7 +11,7 @@ use tari_transaction::Transaction; pub fn builder(_: u64) -> Transaction { let (signer_secret_key, signer_public_key) = RistrettoPublicKey::random_keypair(&mut OsRng); - let account_address = new_account_address_from_parts(&ACCOUNT_TEMPLATE_ADDRESS, &signer_public_key); + let account_address = new_component_address_from_public_key(&ACCOUNT_TEMPLATE_ADDRESS, &signer_public_key); Transaction::builder() .with_fee_instructions_builder(|builder| {