Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ e2e/sway/**/.gitignore
.env

output_changelog.md

.idea/
16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ tar = { version = "0.4", default-features = false }
tempfile = { version = "3.8.1", default-features = false }
thiserror = { version = "1.0.50", default-features = false }
tokio = { version = "1.34.0", default-features = false }
tracing = "0.1.40"
tracing = "0.1"
trybuild = "1.0.85"
uint = { version = "0.9.5", default-features = false }
which = { version = "6.0.0", default-features = false }
Expand All @@ -105,13 +105,13 @@ fuel-core-services = { version = "0.43.0", default-features = false }
fuel-core-types = { version = "0.43.0", default-features = false }

# Dependencies from the `fuel-vm` repository:
fuel-asm = { version = "0.60.0" }
fuel-crypto = { version = "0.60.0" }
fuel-merkle = { version = "0.60.0" }
fuel-storage = { version = "0.60.0" }
fuel-tx = { version = "0.60.0" }
fuel-types = { version = "0.60.0" }
fuel-vm = { version = "0.60.0" }
fuel-asm = { version = "=0.60.0" }
fuel-crypto = { version = "=0.60.0" }
fuel-merkle = { version = "=0.60.0" }
fuel-storage = { version = "=0.60.0" }
fuel-tx = { version = "=0.60.0" }
fuel-types = { version = "=0.60.0" }
fuel-vm = { version = "=0.60.0" }

# Workspace projects
fuels = { version = "0.71.0", path = "./packages/fuels", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions e2e/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ members = [
'sway/predicates/predicate_blobs',
'sway/predicates/predicate_configurables',
'sway/predicates/predicate_witnesses',
'sway/predicates/read_only_verified',
'sway/predicates/signatures',
'sway/predicates/swap',
'sway/predicates/predicate_tx_input_output',
Expand Down
8 changes: 8 additions & 0 deletions e2e/sway/predicates/read_only_verified/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["human"]
entry = "main.sw"
license = "Apache-2.0"
name = "read_only_verified"

[dependencies]
std = { path = "../../../../../sway/sway-lib-std" }
22 changes: 22 additions & 0 deletions e2e/sway/predicates/read_only_verified/src/main.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
predicate;

use std::{inputs::*};

struct SomeData {
value: u64,
}

fn first_input_is_read_only_verified() -> bool {
let first_input_type = input_type(0).unwrap();
first_input_type == Input::ReadOnly(ReadOnlyInput::DataCoinPredicate)
}

fn read_only_input_value_matches_predicate_data(predicate_data: SomeData) -> bool {
let input_data = input_data_coin_data::<SomeData>(0).unwrap();
predicate_data.value == input_data.value
}

fn main(predicate_data: SomeData) -> bool {
first_input_is_read_only_verified()
&& read_only_input_value_matches_predicate_data(predicate_data)
}
110 changes: 108 additions & 2 deletions e2e/tests/predicates.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::default::Default;
#![allow(non_snake_case)]

use std::default::Default;
use fuel_asm::{op, RegId};
use fuels::{
accounts::signers::private_key::PrivateKeySigner,
core::{
Expand All @@ -9,14 +11,15 @@ use fuels::{
prelude::*,
programs::executable::Executable,
types::{
coin::{Coin, DataCoin},
coin::Coin,
coin_type::CoinType,
input::Input,
message::Message,
output::Output,
},
};
use rand::thread_rng;
use fuels::types::coin_type::ReadOnly;

async fn assert_address_balance(
address: &Bech32Address,
Expand Down Expand Up @@ -287,6 +290,109 @@ async fn data_coins() -> Result<()> {
Ok(())
}

#[tokio::test]
async fn read_only_coin__if_predicate_data_matches_read_only_data__succeed() -> Result<()> {
let signer = PrivateKeySigner::random(&mut thread_rng());
abigen!(Predicate(
name = "MyPredicate",
abi = "e2e/sway/predicates/read_only_verified/out/release/read_only_verified-abi.json"
));
let predicate = Predicate::load_from("sway/predicates/read_only_verified/out/release/read_only_verified.bin")?;

let true_predicate_code: Vec<u8> = vec![op::ret(RegId::ONE)].into_iter().collect();
let true_predicate = Predicate::from_code(true_predicate_code);

let asset_id = AssetId::zeroed();

let value = 1;
let data = SomeData {
value,
};
let encoded_data = ABIEncoder::default()
.encode(&[data.into_token()])?;

let amount_predicate_coin = 32;
let amount_true_predicate_coin = 1000;
let mut predicate_coins =
setup_single_asset_coins(predicate.address(), asset_id, 1, amount_predicate_coin);
let mut true_predicate_coins = setup_single_asset_data_coins(
true_predicate.address(),
asset_id,
1,
amount_true_predicate_coin,
encoded_data.clone()
);
let messages = vec![];

let provider = setup_test_provider2(
predicate_coins.clone(),
true_predicate_coins.clone(),
messages,
None,
None,
)
.await?;
let wallet = Wallet::new(signer, provider.clone());

// given
let coin_input = Input::resource_predicate(
CoinType::Coin(predicate_coins.pop().unwrap()),
predicate.code().to_vec(),
encoded_data,
);
let read_only_input = Input::resource_predicate(
CoinType::ReadOnly(ReadOnly::DataCoinPredicate(true_predicate_coins.pop().unwrap())),
true_predicate.code().to_vec(),
vec![],
);

let outputs = vec![
Output::change(predicate.address().into(), 0, asset_id),
];

let mut tb = ScriptTransactionBuilder::prepare_transfer(
vec![read_only_input, coin_input],
outputs,
TxPolicies::default(),
);
tb.add_signer(wallet.signer().clone())?;

let tx = tb.build(&provider).await.unwrap();
let chain_id = provider.consensus_parameters().await?.chain_id();

let tx_id = tx.id(chain_id);

dbg!(&tx.inputs());

// when
let tx_status = provider
.send_transaction_and_await_commit(tx)
.await
.unwrap();
let fee = tx_status.total_fee();

dbg!(&tx_status);

// then
let tx_from_client = match provider
.get_transaction_by_id(&tx_id)
.await?
.unwrap()
.transaction
{
TransactionType::Script(script) => script,
_ => panic!("nandarou"),
};

let outputs = tx_from_client.outputs();
let change_output = outputs
.iter()
.find(|output| output.is_change())
.expect("Expected a change output");
assert_eq!(change_output.amount().unwrap(), amount_predicate_coin - fee);
Ok(())
}

#[tokio::test]
async fn transfer_coins_and_messages_to_predicate() -> Result<()> {
let num_coins = 16;
Expand Down
3 changes: 2 additions & 1 deletion packages/fuels-accounts/src/provider/retryable_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ impl RetryableClient {
}

pub async fn balance(&self, owner: &Address, asset_id: Option<&AssetId>) -> RequestResult<u64> {
self.wrap(|| self.client.balance(owner, asset_id)).await
// TODO: actually solve; this is just a band-aid
self.wrap(|| self.client.balance(owner, asset_id)).await.map(|x| x as u64)
}

pub async fn contract_balance(
Expand Down
4 changes: 2 additions & 2 deletions packages/fuels-core/src/types/bech32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{

use bech32::{FromBase32, ToBase32, Variant::Bech32m};
use fuel_tx::{Address, Bytes32, ContractId, ContractIdExt};
use fuel_types::AssetId;
use fuel_types::{AssetId, SubAssetId};

use crate::types::{
Bits256,
Expand Down Expand Up @@ -143,7 +143,7 @@ impl From<ContractId> for Bech32ContractId {
impl Bech32ContractId {
/// Creates an `AssetId` from the `Bech32ContractId` and `sub_id`.
pub fn asset_id(&self, sub_id: &Bits256) -> AssetId {
let sub_id = Bytes32::from(sub_id.0);
let sub_id = SubAssetId::from(sub_id.0);
ContractId::from(self).asset_id(&sub_id)
}
}
Expand Down
65 changes: 64 additions & 1 deletion packages/fuels-core/src/types/transaction_builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ macro_rules! impl_tx_builder_trait {
}

pub(crate) use impl_tx_builder_trait;

use crate::types::coin_type::ReadOnly as ClientReadOnly;
use super::coin::DataCoin;

pub(crate) fn estimate_max_fee_w_tolerance<T: Chargeable>(
Expand Down Expand Up @@ -1404,6 +1404,22 @@ fn resolve_signed_resource(
create_coin_message_input(message, num_witnesses + *witness_idx_offset as u16)
})
}
CoinType::ReadOnly(inner) => match inner {
ClientReadOnly::DataCoinPredicate(data_coin) => {
let owner = &data_coin.owner;

unresolved_witness_indexes
.owner_to_idx_offset
.get(owner)
.ok_or(error_transaction!(
Builder,
"signature missing for coin with owner: `{owner:?}`"
))
.map(|witness_idx_offset| {
create_data_coin_input(data_coin, num_witnesses + *witness_idx_offset as u16)
})
}
}
CoinType::Unknown => Err(error_transaction!(
Builder,
"can not resolve `CoinType::Unknown`"
Expand All @@ -1419,6 +1435,13 @@ fn resolve_predicate_resource(
match resource {
CoinType::Coin(coin) => Ok(create_coin_predicate(coin, code, data)),
CoinType::DataCoin(coin) => Ok(create_data_coin_predicate(coin, code, data)),
CoinType::ReadOnly(read_only) => {
match read_only {
ClientReadOnly::DataCoinPredicate(data_coin) => {
Ok(create_read_only_data_coin_predicate(data_coin, code, data))
}
}
}
CoinType::Message(message) => Ok(create_coin_message_predicate(message, code, data)),
CoinType::Unknown => Err(error_transaction!(
Builder,
Expand Down Expand Up @@ -1502,6 +1525,46 @@ pub fn create_data_coin_predicate(
)
}

pub fn create_read_only_data_coin_predicate(
coin: DataCoin,
code: Vec<u8>,
predicate_data: Vec<u8>,
) -> FuelInput {
// let inner = FuelInput::data_coin_predicate(
// coin.utxo_id,
// coin.owner.into(),
// coin.amount,
// coin.asset_id,
// TxPointer::default(),
// 0u64,
// code,
// predicate_data,
// coin.data,
// );
// let inner = DataCoinPredicate {
// utxo_id: coin.utxo_id,
// owner: coin.owner.into(),
// amount: coin.amount,
// asset_id: coin.asset_id,
// tx_pointer: TxPointer::default(),
// witness_index: Empty::new(),
// predicate_gas_used: 0,
// predicate: PredicateCode {bytes: code},
// predicate_data,
// data: coin.data,
// };
// FuelInput::ReadOnly(ClientReadOnly::DataCoinPredicate(inner))
FuelInput::read_only_data_coin_predicate(coin.utxo_id, coin.owner.into()
, coin.amount
, coin.asset_id
, TxPointer::default()
, 0u64
, code
, predicate_data
, coin.data
)
}

pub fn create_coin_message_predicate(
message: Message,
code: Vec<u8>,
Expand Down
15 changes: 15 additions & 0 deletions packages/fuels-core/src/types/wrappers/coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use fuel_core_chain_config::{CoinConfig, ConfigCoin, ConfigDataCoin};
use fuel_core_client::client::types::{
coins::Coin as ClientCoin,
coins::DataCoin as ClientDataCoin,
primitives::{AssetId, UtxoId},
};

Expand Down Expand Up @@ -90,3 +91,17 @@ impl From<DataCoin> for CoinConfig {
})
}
}

impl From<ClientDataCoin> for DataCoin {
fn from(coin: ClientDataCoin) -> Self {
Self {
amount: coin.amount,
block_created: coin.block_created,
asset_id: coin.asset_id,
utxo_id: coin.utxo_id,
owner: Bech32Address::from(coin.owner),
status: CoinStatus::Unspent,
data: coin.data,
}
}
}
Loading
Loading