Skip to content

Commit

Permalink
Introduce predicate_id() to the std-lib (#5425)
Browse files Browse the repository at this point in the history
## Description

A predicate's address can be accessed internally from within a
predicate. This functionality has been added to the std-lib with the
`predicate_id()` function.

Closes #5411

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

---------

Co-authored-by: SwayStar123 <[email protected]>
  • Loading branch information
bitzoic and SwayStar123 authored Jan 3, 2024
1 parent 61c260c commit f122693
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 2 deletions.
31 changes: 31 additions & 0 deletions sway-lib-std/src/auth.sw
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,34 @@ pub fn caller_address() -> Result<Address, AuthError> {
None => Err(AuthError::CallerIsInternal),
}
}

/// Get the current predicate's id when called in an internal context.
///
/// # Returns
///
/// * [Address] - The address of this predicate.
///
/// # Reverts
///
/// * When called outside of a predicate program.
///
/// # Examples
///
/// ```sway
/// use std::auth::predicate_id;
///
/// fn main() {
/// let this_predicate = predicate_id();
/// log(this_predicate);
/// }
/// ```
pub fn predicate_id() -> Address {
// Get index of current predicate.
// i3 = GM_GET_VERIFYING_PREDICATE
let predicate_index = asm(r1) {
gm r1 i3;
r1: u64
};

input_coin_owner(predicate_index).unwrap()
}
5 changes: 5 additions & 0 deletions test/src/sdk-harness/Forc.lock
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ dependencies = [
"std",
]

[[package]]
name = "auth_predicate"
source = "member"
dependencies = ["std"]

[[package]]
name = "auth_testing_abi"
source = "member"
Expand Down
1 change: 1 addition & 0 deletions test/src/sdk-harness/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ members = [
"test_projects/vec_in_abi",
"test_artifacts/auth_caller_contract",
"test_artifacts/auth_caller_script",
"test_artifacts/auth_predicate",
"test_artifacts/auth_testing_abi",
"test_artifacts/auth_testing_contract",
"test_artifacts/balance_contract",
Expand Down
2 changes: 2 additions & 0 deletions test/src/sdk-harness/test_artifacts/auth_predicate/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out
target
13 changes: 13 additions & 0 deletions test/src/sdk-harness/test_artifacts/auth_predicate/Forc.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[[package]]
name = "auth_predicate"
source = "member"
dependencies = ["std"]

[[package]]
name = "core"
source = "path+from-root-7FCE6F17DDEC6CE9"

[[package]]
name = "std"
source = "path+from-root-7FCE6F17DDEC6CE9"
dependencies = ["core"]
8 changes: 8 additions & 0 deletions test/src/sdk-harness/test_artifacts/auth_predicate/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "auth_predicate"

[dependencies]
std = { path = "../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
predicate;

use std::auth::predicate_id;

fn main(predicate_address: Address) -> bool {
predicate_address == predicate_id()
}

115 changes: 113 additions & 2 deletions test/src/sdk-harness/test_projects/auth/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use fuels::{
accounts::wallet::{Wallet, WalletUnlocked},
accounts::{wallet::{Wallet, WalletUnlocked}, predicate::Predicate},
prelude::*,
types::ContractId,
};
use std::str::FromStr;

abigen!(
Contract(
Expand All @@ -12,7 +13,11 @@ abigen!(
Contract(
name = "AuthCallerContract",
abi = "test_artifacts/auth_caller_contract/out/debug/auth_caller_contract-abi.json"
)
),
Predicate(
name = "AuthPredicate",
abi = "test_artifacts/auth_predicate/out/debug/auth_predicate-abi.json"
),
);

#[tokio::test]
Expand Down Expand Up @@ -94,3 +99,109 @@ async fn get_contracts() -> (
wallet.lock(),
)
}

#[tokio::test]
async fn can_get_predicate_id() {
// Setup Wallets
let asset_id = AssetId::default();
let wallets_config = WalletsConfig::new_multiple_assets(
2,
vec![AssetConfig {
id: asset_id,
num_coins: 1,
coin_amount: 1_000,
}],
);
let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await.unwrap();
let first_wallet = &wallets[0];
let second_wallet = &wallets[1];

// Setup Predciate
let hex_predicate_address: &str = "0x76bae6a88c3f54a9aee40ee5390696dd9edaf1b2a16d96a75adbcaac2ec6584f";
let predicate_address = Address::from_str(hex_predicate_address).expect("failed to create Address from string");
let predicate_bech32_address = Bech32Address::from(predicate_address);
let predicate_data = AuthPredicateEncoder::encode_data(predicate_bech32_address);
let predicate: Predicate = Predicate::load_from("test_artifacts/auth_predicate/out/debug/auth_predicate.bin").unwrap()
.with_provider(first_wallet.try_provider().unwrap().clone())
.with_data(predicate_data);

// Next, we lock some assets in this predicate using the first wallet:
// First wallet transfers amount to predicate.
first_wallet
.transfer(predicate.address(), 500, asset_id, TxPolicies::default())
.await.unwrap();

// Check predicate balance.
let balance = predicate.get_asset_balance(&AssetId::default()).await.unwrap();
assert_eq!(balance, 500);

// Then we can transfer assets owned by the predicate via the Account trait:
let amount_to_unlock = 500;

// Will transfer if the correct predicate address is passed as an argument to the predicate
predicate
.transfer(
second_wallet.address(),
amount_to_unlock,
asset_id,
TxPolicies::default(),
)
.await.unwrap();

// Predicate balance is zero.
let balance = predicate.get_asset_balance(&AssetId::default()).await.unwrap();
assert_eq!(balance, 0);

// Second wallet balance is updated.
let balance = second_wallet.get_asset_balance(&AssetId::default()).await.unwrap();
assert_eq!(balance, 1500);
}

#[tokio::test]
#[should_panic]
async fn when_incorrect_predicate_address_passed() {
// Setup Wallets
let asset_id = AssetId::default();
let wallets_config = WalletsConfig::new_multiple_assets(
2,
vec![AssetConfig {
id: asset_id,
num_coins: 1,
coin_amount: 1_000,
}],
);
let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await.unwrap();
let first_wallet = &wallets[0];
let second_wallet = &wallets[1];

// Setup Predciate with incorrect address
let hex_predicate_address: &str = "0x36bf4bd40f2a3b3db595ef8fd8b21dbe9e6c0dd7b419b4413ff6b584ce7da5d7";
let predicate_address = Address::from_str(hex_predicate_address).expect("failed to create Address from string");
let predicate_data = AuthPredicateEncoder::encode_data(Bech32Address::from(predicate_address));
let predicate: Predicate = Predicate::load_from("test_artifacts/auth_predicate/out/debug/auth_predicate.bin").unwrap()
.with_provider(first_wallet.try_provider().unwrap().clone())
.with_data(predicate_data);

// Next, we lock some assets in this predicate using the first wallet:
// First wallet transfers amount to predicate.
first_wallet
.transfer(predicate.address(), 500, asset_id, TxPolicies::default())
.await.unwrap();

// Check predicate balance.
let balance = predicate.get_asset_balance(&AssetId::default()).await.unwrap();
assert_eq!(balance, 500);

// Then we can transfer assets owned by the predicate via the Account trait:
let amount_to_unlock = 500;

// Will should fail to transfer
predicate
.transfer(
second_wallet.address(),
amount_to_unlock,
asset_id,
TxPolicies::default(),
)
.await.unwrap();
}

0 comments on commit f122693

Please sign in to comment.