Skip to content

Commit

Permalink
Add std::tx::get_predicate_data (FuelLabs#1984)
Browse files Browse the repository at this point in the history
* Add draft examples for testing new lib

* Add missing import

* rebuild

* Use stdlib helper

* Use mem helper

* Refactor for readability

* Switch to u32

* Update to use Abi Encoder with padding

* Add tests for struct

* PR comments

* Fix post merge master

* rm dupe

Co-authored-by: John Adler <[email protected]>
  • Loading branch information
camsjams and adlerjohn authored Jul 2, 2022
1 parent 850e0de commit 8889ba0
Show file tree
Hide file tree
Showing 10 changed files with 537 additions and 0 deletions.
27 changes: 27 additions & 0 deletions sway-lib-std/src/tx.sw
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
library tx;

use ::address::Address;
use ::context::registers::instrs_start;
use ::contract_id::ContractId;
use ::intrinsics::is_reference_type;
use ::mem::read;

////////////////////////////////////////
// Transaction fields
Expand Down Expand Up @@ -205,6 +207,31 @@ pub fn tx_input_coin_owner(input_ptr: u32) -> Address {
owner_addr
}

////////////////////////////////////////
// Inputs > Predicate
////////////////////////////////////////

pub fn tx_predicate_data_start_offset() -> u64 {
// $is is word-aligned
let is = instrs_start();
let predicate_length_ptr = is - 16;
let predicate_code_length = asm(r1, r2: predicate_length_ptr) {
lw r1 r2 i0;
r1: u64
};

let predicate_data_ptr = is + predicate_code_length;
// predicate_data_ptr % 8 is guaranteed to be either
// 0: if there are an even number of instructions (predicate_data_ptr is word-aligned already)
// 4: if there are an odd number of instructions
predicate_data_ptr + predicate_data_ptr % 8
}

pub fn get_predicate_data<T>() -> T {
let ptr = tx_predicate_data_start_offset();
read(ptr)
}

////////////////////////////////////////
// Outputs
////////////////////////////////////////
Expand Down
2 changes: 2 additions & 0 deletions test/src/sdk-harness/test_projects/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ mod exponentiation;
mod hashing;
mod logging;
mod methods;
mod predicate_data_simple;
mod predicate_data_struct;
mod reentrancy;
mod registers;
mod script_data;
Expand Down
14 changes: 14 additions & 0 deletions test/src/sdk-harness/test_projects/predicate_data_simple/Forc.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[[package]]
name = 'core'
source = 'path+from-root-88963B58C4FA8138'
dependencies = []

[[package]]
name = 'predicate_data_simple'
source = 'root'
dependencies = ['std']

[[package]]
name = 'std'
source = 'path+from-root-88963B58C4FA8138'
dependencies = ['core']
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 = "predicate_data_simple"

[dependencies]
std = { path = "../../../../../sway-lib-std" }
197 changes: 197 additions & 0 deletions test/src/sdk-harness/test_projects/predicate_data_simple/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
use fuel_core::service::Config;
use fuel_vm::consts::*;
use fuel_vm::prelude::Opcode;
use fuels::contract::script::Script;
use fuels::prelude::*;
use fuels::signers::wallet::Wallet;
use fuels::contract::abi_encoder::ABIEncoder;
use fuels::tx::{Address, AssetId, Contract, Input, Output, Transaction, UtxoId};
use std::str::FromStr;

async fn setup() -> (Vec<u8>, Address, Wallet, u64, AssetId) {
let predicate_code = std::fs::read("test_projects/predicate_data_simple/out/debug/predicate_data_simple.bin").unwrap();
let predicate_address = (*Contract::root_from_code(&predicate_code)).into();

let wallet = launch_custom_provider_and_get_single_wallet(Some(Config {
predicates: true,
utxo_validation: true,
..Config::local_node()
}))
.await;

(
predicate_code,
predicate_address,
wallet,
1000,
AssetId::default(),
)
}

async fn create_predicate(
predicate_address: Address,
wallet: &Wallet,
amount_to_predicate: u64,
asset_id: AssetId,
) {
let wallet_coins = wallet
.get_asset_inputs_for_amount(
asset_id,
wallet.get_asset_balance(&asset_id).await.unwrap(),
0,
)
.await
.unwrap();

let output_coin = Output::coin(predicate_address, amount_to_predicate, asset_id);
let output_change = Output::change(wallet.address(), 0, asset_id);
let mut tx = Transaction::script(
1,
1000000,
1,
0,
Opcode::RET(REG_ONE).to_bytes().to_vec(),
vec![],
wallet_coins,
vec![output_coin, output_change],
vec![],
);
wallet.sign_transaction(&mut tx).await.unwrap();
wallet
.get_provider()
.unwrap()
.send_transaction(&tx)
.await
.unwrap();
}

async fn submit_to_predicate(
predicate_code: Vec<u8>,
predicate_address: Address,
wallet: &Wallet,
amount_to_predicate: u64,
asset_id: AssetId,
receiver_address: Address,
predicate_data: Vec<u8>,
) {
let utxo_predicate_hash = wallet
.get_provider()
.unwrap()
.get_spendable_coins(&predicate_address, asset_id, amount_to_predicate)
.await
.unwrap();

let mut inputs = vec![];
let mut total_amount_in_predicate = 0;

for coin in utxo_predicate_hash {
let input_coin = Input::coin_predicate(
UtxoId::from(coin.utxo_id),
coin.owner.into(),
coin.amount.0,
asset_id,
0,
predicate_code.clone(),
predicate_data.clone(),
);
inputs.push(input_coin);
total_amount_in_predicate += coin.amount.0;
}

let output_coin = Output::coin(receiver_address, total_amount_in_predicate, asset_id);
let output_change = Output::change(predicate_address, 0, asset_id);
let new_tx = Transaction::script(
0,
1000000,
0,
0,
vec![],
vec![],
inputs,
vec![output_coin, output_change],
vec![],
);

let script = Script::new(new_tx);
let _call_result = script.call(&wallet.get_provider().unwrap().client).await;
}

async fn get_balance(wallet: &Wallet, address: Address, asset_id: AssetId) -> u64 {
wallet
.get_provider()
.unwrap()
.get_asset_balance(&address, asset_id)
.await
.unwrap()
}

#[tokio::test]
async fn valid_predicate_data_simple() {
let arg = Token::U32(12345_u32);
let args: Vec<Token> = vec![arg];
let predicate_data = ABIEncoder::encode(&args).unwrap();

let receiver_address =
Address::from_str("0xde97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c")
.unwrap();
let (predicate_code, predicate_address, wallet, amount_to_predicate, asset_id) = setup().await;

create_predicate(predicate_address, &wallet, amount_to_predicate, asset_id).await;

let receiver_balance_before = get_balance(&wallet, receiver_address, asset_id).await;
assert_eq!(receiver_balance_before, 0);

submit_to_predicate(
predicate_code,
predicate_address,
&wallet,
amount_to_predicate,
asset_id,
receiver_address,
predicate_data,
)
.await;

let receiver_balance_after = get_balance(&wallet, receiver_address, asset_id).await;
assert_eq!(
receiver_balance_before + amount_to_predicate,
receiver_balance_after
);

let predicate_balance = get_balance(&wallet, predicate_address, asset_id).await;
assert_eq!(predicate_balance, 0);
}

#[tokio::test]
async fn invalid_predicate_data_simple() {
let arg = Token::U32(1001_u32);
let args: Vec<Token> = vec![arg];
let predicate_data = ABIEncoder::encode(&args).unwrap();

let receiver_address =
Address::from_str("0xde97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c")
.unwrap();
let (predicate_code, predicate_address, wallet, amount_to_predicate, asset_id) = setup().await;

create_predicate(predicate_address, &wallet, amount_to_predicate, asset_id).await;

let receiver_balance_before = get_balance(&wallet, receiver_address, asset_id).await;
assert_eq!(receiver_balance_before, 0);

submit_to_predicate(
predicate_code,
predicate_address,
&wallet,
amount_to_predicate,
asset_id,
receiver_address,
predicate_data,
)
.await;

let receiver_balance_after = get_balance(&wallet, receiver_address, asset_id).await;
assert_eq!(receiver_balance_before, receiver_balance_after);

let predicate_balance = get_balance(&wallet, predicate_address, asset_id).await;
assert_eq!(predicate_balance, amount_to_predicate);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
predicate;

use std::tx::get_predicate_data;

fn main() -> bool {
let received: u32 = get_predicate_data();
let expected: u32 = 12345;

received == expected
}
14 changes: 14 additions & 0 deletions test/src/sdk-harness/test_projects/predicate_data_struct/Forc.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[[package]]
name = 'core'
source = 'path+from-root-025C6DF1C548A897'
dependencies = []

[[package]]
name = 'predicate_data_struct'
source = 'root'
dependencies = ['std']

[[package]]
name = 'std'
source = 'path+from-root-025C6DF1C548A897'
dependencies = ['core']
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 = "predicate_data_struct"

[dependencies]
std = { path = "../../../../../sway-lib-std" }
Loading

0 comments on commit 8889ba0

Please sign in to comment.