Skip to content

Commit

Permalink
fix(template-lib)!: improve initial supply api resource builder (#1040)
Browse files Browse the repository at this point in the history
Description
---
Improve API for resource builder with initial supply.
Rename XTR2 constant to XTR
Add `Bucket::join`

Motivation and Context
---

Previously if you provided an initial supply for the resource builder
and called `build()` a panic would occur.
This PR changes `initial_supply` to immediately build and return the
bucket preventing the user from making the above error at compile time.

Previous API: 
```rust
let bucket = ResourceBuilder::fungible().initial_supply(1000).build_bucket()
```
```rust
// will panic at runtime
let bucket = ResourceBuilder::fungible().build_bucket()
```
New API:
```rust
let bucket = ResourceBuilder::fungible().initial_supply(1000);

let bucket = ResourceBuilder::non_fungible()
       .initial_supply((1..=100).map(NonFungibleId::from_u32));

let confidential = ResourceBuilder::confidential()
       .initial_supply(confidential_supply);
```

How Has This Been Tested?
---
Existing engine tests updated

What process can a PR reviewer use to test or verify this change?
---
Update existing templates to use the new API

Breaking Changes
---

- [ ] None
- [ ] Requires data directory to be deleted
- [x] Other - Please specify

BREAKING CHANGE: templates that previously built resources with initial
supply will no longer compile. The ABI for buckets adds `Join`. Since
the integer representation of the ops shifts by 1 after `Join` some
bucket operations will be invalid on previous WASM. i.e. WASM needs to
be recompiled.

---------

Co-authored-by: stringhandler <mikethetike@tari.com>
  • Loading branch information
sdbondi and stringhandler committed Jun 14, 2024
1 parent 9a484a7 commit 8ff9cb6
Show file tree
Hide file tree
Showing 34 changed files with 336 additions and 287 deletions.
2 changes: 1 addition & 1 deletion applications/tari_validator_node/src/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ where
let substate_id = SubstateId::Resource(CONFIDENTIAL_TARI_RESOURCE_ADDRESS);
let substate_address = SubstateAddress::from_substate_id(&substate_id, 0);
let mut metadata = Metadata::new();
metadata.insert(TOKEN_SYMBOL, "tXTR2".to_string());
metadata.insert(TOKEN_SYMBOL, "tXTR".to_string());
if !SubstateRecord::exists(&**tx, &substate_address)? {
SubstateRecord {
substate_id,
Expand Down
2 changes: 1 addition & 1 deletion dan_layer/engine/src/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub fn bootstrap_state<T: StateWriter>(state_db: &mut T) -> Result<(), StateStor
let address = SubstateId::Resource(CONFIDENTIAL_TARI_RESOURCE_ADDRESS);
let mut metadata = Metadata::new();
// TODO: decide on symbol for L2 tari
metadata.insert(TOKEN_SYMBOL, "tXTR2".to_string());
metadata.insert(TOKEN_SYMBOL, "tXTR".to_string());
state_db.set_state(
&address,
Substate::new(
Expand Down
22 changes: 18 additions & 4 deletions dan_layer/engine/src/runtime/impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ use tari_template_lib::{
WorkspaceAction,
},
auth::{AuthHook, AuthHookCaller, ComponentAccessRules, OwnerRule, ResourceAccessRules, ResourceAuthAction},
constants::{CONFIDENTIAL_TARI_RESOURCE_ADDRESS, XTR2},
constants::{CONFIDENTIAL_TARI_RESOURCE_ADDRESS, XTR},
crypto::RistrettoPublicKeyBytes,
models::{
Amount,
Expand Down Expand Up @@ -1390,7 +1390,7 @@ impl<TTemplateProvider: TemplateProvider<Template = LoadedTemplate>> RuntimeInte
self.tracker.write_with(|state| {
let vault_lock = state.lock_substate(&SubstateId::Vault(vault_id), LockFlag::Write)?;
let resource_address = *state.get_vault(&vault_lock)?.resource_address();
if resource_address != XTR2 {
if resource_address != XTR {
return Err(RuntimeError::InvalidArgument {
argument: "vault_ref",
reason: format!(
Expand All @@ -1399,7 +1399,7 @@ impl<TTemplateProvider: TemplateProvider<Template = LoadedTemplate>> RuntimeInte
),
});
}
let resource_lock = state.lock_substate(&SubstateId::Resource(XTR2), LockFlag::Read)?;
let resource_lock = state.lock_substate(&SubstateId::Resource(XTR), LockFlag::Read)?;
let resource = state.get_resource(&resource_lock)?;

state.authorization().check_resource_access_rules(
Expand All @@ -1411,7 +1411,7 @@ impl<TTemplateProvider: TemplateProvider<Template = LoadedTemplate>> RuntimeInte

let vault_mut = state.get_vault_mut(&vault_lock)?;

let mut container = ResourceContainer::confidential(XTR2, None, Amount::zero());
let mut container = ResourceContainer::confidential(XTR, None, Amount::zero());
if !arg.amount.is_zero() {
let withdrawn = vault_mut.withdraw(arg.amount)?;
container.deposit(withdrawn)?;
Expand Down Expand Up @@ -1676,6 +1676,20 @@ impl<TTemplateProvider: TemplateProvider<Template = LoadedTemplate>> RuntimeInte
Ok(InvokeResult::encode(&bucket_id)?)
})
},
BucketAction::Join => {
let bucket_id = bucket_ref.bucket_id().ok_or_else(|| RuntimeError::InvalidArgument {
argument: "bucket_ref",
reason: "Join bucket action requires a bucket id".to_string(),
})?;
let other_bucket_id = args.assert_one_arg()?;

self.tracker.write_with(|state| {
let other_bucket = state.take_bucket(other_bucket_id)?;
let bucket = state.get_bucket_mut(bucket_id)?;
bucket.join(other_bucket)?;
Ok(InvokeResult::encode(&bucket_id)?)
})
},
BucketAction::RevealConfidential => {
let bucket_id = bucket_ref.bucket_id().ok_or_else(|| RuntimeError::InvalidArgument {
argument: "bucket_ref",
Expand Down
6 changes: 3 additions & 3 deletions dan_layer/engine/src/runtime/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::fmt::Display;
use indexmap::IndexSet;
use tari_engine_types::{indexed_value::IndexedWellKnownTypes, lock::LockId, substate::SubstateId, TemplateAddress};
use tari_template_lib::{
constants::XTR2,
constants::XTR,
models::{BucketId, EntityId, ProofId},
prelude::PUBLIC_IDENTITY_RESOURCE_ADDRESS,
};
Expand Down Expand Up @@ -80,8 +80,8 @@ impl CallScope {

pub fn is_substate_in_scope(&self, address: &SubstateId) -> bool {
// TODO: Hacky
// If the address is the XTR2 resource, it is always in scope
if *address == XTR2 {
// If the address is the XTR resource, it is always in scope
if *address == XTR {
return true;
}

Expand Down
27 changes: 27 additions & 0 deletions dan_layer/engine/tests/resource.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2024 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause

use tari_template_lib::{args, models::ComponentAddress};
use tari_template_test_tooling::{support::confidential::generate_confidential_proof, TemplateTest};

#[test]
fn fungible_join() {
let mut test = TemplateTest::new(vec!["tests/templates/resource"]);
let component: ComponentAddress = test.call_function("ResourceTest", "new", args![], vec![]);
test.call_method::<()>(component, "fungible_join", args![], vec![]);
}

#[test]
fn non_fungible_join() {
let mut test = TemplateTest::new(vec!["tests/templates/resource"]);
let component: ComponentAddress = test.call_function("ResourceTest", "new", args![], vec![]);
test.call_method::<()>(component, "non_fungible_join", args![], vec![]);
}

#[test]
fn confidential_join() {
let mut test = TemplateTest::new(vec!["tests/templates/resource"]);
let component: ComponentAddress = test.call_function("ResourceTest", "new", args![], vec![]);
let (output, _, _) = generate_confidential_proof(1000.into(), None);
test.call_method::<()>(component, "confidential_join", args![output], vec![]);
}
4 changes: 2 additions & 2 deletions dan_layer/engine/tests/shenanigans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use tari_engine_types::{indexed_value::IndexedWellKnownTypes, resource_container
use tari_template_lib::{
args,
args::VaultAction,
constants::XTR2,
constants::XTR,
models::{Amount, ComponentAddress, ResourceAddress},
prelude::ResourceType,
};
Expand Down Expand Up @@ -138,7 +138,7 @@ fn it_rejects_references_to_buckets_that_arent_in_scope() {

let reason = test.execute_expect_failure(
Transaction::builder()
.call_method(account, "withdraw", args![XTR2, Amount(1000)])
.call_method(account, "withdraw", args![XTR, Amount(1000)])
.put_last_instruction_output_on_workspace("bucket")
.call_method(shenanigans, "take_bucket_zero", args![])
.sign(&owner_key)
Expand Down
27 changes: 12 additions & 15 deletions dan_layer/engine/tests/templates/access_rules/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ pub fn create_badge_resource(recall_rule: AccessRule) -> Bucket {
let mut metadata = Metadata::new();
metadata.insert("colour", "blue");
ResourceBuilder::non_fungible()
.mint_many_with(BADGE_NAMES, |name| (NonFungibleId::from_string(name), (&metadata, &())))
.recallable(recall_rule)
.build_bucket()
.initial_supply_with_data(
BADGE_NAMES
.into_iter()
.map(|name| (NonFungibleId::from_string(name), (&metadata, &()))),
)
}

#[template]
Expand All @@ -36,8 +39,7 @@ mod access_rules_template {
let tokens = ResourceBuilder::fungible()
.with_owner_rule(owner_rule.clone())
.with_access_rules(resource_rules)
.initial_supply(1000)
.build_bucket();
.initial_supply(1000);

let badges = create_badge_resource(recall_rule);

Expand All @@ -56,7 +58,7 @@ mod access_rules_template {
pub fn default_rules() -> Component<AccessRulesTest> {
let badges = create_badge_resource(AccessRule::DenyAll);

let tokens = ResourceBuilder::fungible().initial_supply(1000).build_bucket();
let tokens = ResourceBuilder::fungible().initial_supply(1000);

Component::create(Self {
value: 0,
Expand All @@ -73,9 +75,8 @@ mod access_rules_template {
let address_alloc = CallerContext::allocate_component_address();

let tokens = ResourceBuilder::fungible()
.initial_supply(1000)
.with_authorization_hook(*address_alloc.address(), hook)
.build_bucket();
.initial_supply(1000);

Component::new(Self {
value: 0,
Expand All @@ -95,12 +96,11 @@ mod access_rules_template {
let address_alloc = CallerContext::allocate_component_address();

let tokens = ResourceBuilder::fungible()
.initial_supply(1000)
.with_authorization_hook(
*address_alloc.address(),
"malicious_auth_hook_set_state_on_another_component",
)
.build_bucket();
.initial_supply(1000);

Component::new(Self {
value: 0,
Expand All @@ -119,7 +119,6 @@ mod access_rules_template {

let badge_resource = badges.resource_address();
let tokens = ResourceBuilder::fungible()
.initial_supply(1000)
.mintable(AccessRule::Restricted(RestrictedAccessRule::Require(
RequireRule::Require(
NonFungibleAddress::new(badge_resource, NonFungibleId::from_string("mint")).into(),
Expand All @@ -140,7 +139,7 @@ mod access_rules_template {
NonFungibleAddress::new(badge_resource, NonFungibleId::from_string("deposit")).into(),
),
)))
.build_bucket();
.initial_supply(1000);

Component::new(Self {
value: 0,
Expand All @@ -158,7 +157,6 @@ mod access_rules_template {

let badge_resource = badges.resource_address();
let tokens = ResourceBuilder::fungible()
.initial_supply(1000)
.mintable(AccessRule::Restricted(RestrictedAccessRule::Require(
RequireRule::Require(badge_resource.into()),
)))
Expand All @@ -171,7 +169,7 @@ mod access_rules_template {
.depositable(AccessRule::Restricted(RestrictedAccessRule::Require(
RequireRule::Require(badge_resource.into()),
)))
.build_bucket();
.initial_supply(1000);

Component::new(Self {
value: 0,
Expand All @@ -189,13 +187,12 @@ mod access_rules_template {

let allocation = CallerContext::allocate_component_address();
let tokens = ResourceBuilder::fungible()
.initial_supply(1000)
.mintable(AccessRule::Restricted(RestrictedAccessRule::Require(
RequireRule::Require(allocation.address().clone().into()),
)))
// Only access rules apply, this just makes the test simpler because we do not need to change the transaction signer
.with_owner_rule(OwnerRule::None)
.build_bucket();
.initial_supply(1000);

Component::new(Self {
value: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ mod faucet_template {
pub fn mint(confidential_proof: ConfidentialOutputStatement) -> Component<Self> {
let coins = ResourceBuilder::confidential()
.mintable(AccessRule::AllowAll)
.initial_supply(confidential_proof)
.build_bucket();
.initial_supply(confidential_proof);

Component::new(Self {
vault: Vault::from_bucket(coins),
Expand All @@ -50,9 +49,8 @@ mod faucet_template {
) -> Component<Self> {
let coins = ResourceBuilder::confidential()
.mintable(AccessRule::AllowAll)
.initial_supply(confidential_proof)
.with_view_key(view_key)
.build_bucket();
.initial_supply(confidential_proof);

Component::new(Self {
vault: Vault::from_bucket(coins),
Expand Down
3 changes: 1 addition & 2 deletions dan_layer/engine/tests/templates/nft/airdrop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ mod airdrop_template {
pub fn new() -> Component<Self> {
let bucket = ResourceBuilder::non_fungible()
.with_token_symbol("AIR")
.mint_many_with(1..=100, |n| (NonFungibleId::from_u32(n), (&(), &())))
.build_bucket();
.initial_supply((1..=100).map(NonFungibleId::from_u32));

Component::new(Self {
allow_list: HashSet::new(),
Expand Down
11 changes: 5 additions & 6 deletions dan_layer/engine/tests/templates/nft/basic_nft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,16 @@ mod sparkle_nft_template {
pub fn new() -> Component<Self> {
// Create the non-fungible resource with 1 token (optional)
let tokens = [
(NonFungibleId::from_u32(1), (&(), &())),
(NonFungibleId::from_u64(u64::MAX), (&(), &())),
(NonFungibleId::from_string("Sparkle1"), (&(), &())),
(NonFungibleId::from_u256([0u8; 32]), (&(), &())),
NonFungibleId::from_u32(1),
NonFungibleId::from_u64(u64::MAX),
NonFungibleId::from_string("Sparkle1"),
NonFungibleId::from_u256([0u8; 32]),
];
let bucket = ResourceBuilder::non_fungible().with_token_symbol("SPKL")
.with_non_fungibles(tokens)
// Allow minting and burning for tests
.mintable(AccessRule::AllowAll)
.burnable(AccessRule::AllowAll)
.build_bucket();
.initial_supply(tokens);

Component::new(Self {
address: bucket.resource_address(),
Expand Down
10 changes: 4 additions & 6 deletions dan_layer/engine/tests/templates/recall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,19 @@ mod template {
confidential_supply: ConfidentialOutputStatement,
) -> (Component<Self>, ResourceAddress, ResourceAddress, ResourceAddress) {
let fungible = ResourceBuilder::fungible()
.initial_supply(Amount(1_000_000))
.recallable(AccessRule::AllowAll)
.build_bucket();
.initial_supply(Amount(1_000_000));

let fungible_resource = fungible.resource_address();

let non_fungible = ResourceBuilder::non_fungible()
.mint_many_with(1..=10, |n| (NonFungibleId::from_u32(n), (&(), &())))
.recallable(AccessRule::AllowAll)
.build_bucket();
.initial_supply((1..=10).map(NonFungibleId::from_u32));
let non_fungible_resource = non_fungible.resource_address();

let confidential = ResourceBuilder::confidential()
.initial_supply(confidential_supply)
.recallable(AccessRule::AllowAll)
.build_bucket();
.initial_supply(confidential_supply);
let confidential_resource = confidential.resource_address();

let component = Component::new(Self {
Expand Down
14 changes: 14 additions & 0 deletions dan_layer/engine/tests/templates/resource/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[workspace]
[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tari_template_lib = { path = "../../../../template_lib" }


[lib]
crate-type = ["cdylib", "lib"]
Loading

0 comments on commit 8ff9cb6

Please sign in to comment.