Skip to content
This repository was archived by the owner on Jul 12, 2023. It is now read-only.
Closed
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
70 changes: 20 additions & 50 deletions contract-message-predicate/src/main.sw
Original file line number Diff line number Diff line change
@@ -1,52 +1,25 @@
predicate;

use std::{
address::Address,
tx::{
INPUT_COIN,
INPUT_CONTRACT,
INPUT_MESSAGE,
OUTPUT_CHANGE,
OUTPUT_CONTRACT,
OUTPUT_VARIABLE,
b256_from_pointer_offset,
tx_gas_limit,
tx_input_pointer,
tx_input_type,
tx_inputs_count,
tx_output_type,
tx_outputs_count,
tx_script_bytecode}
};
dep utils;

use std::assert::assert;
use std::hash::sha256;
use std::contract_id::ContractId;
use std::assert::assert;
use std::tx::{
INPUT_COIN,
INPUT_CONTRACT,
INPUT_MESSAGE,
OUTPUT_CHANGE,
OUTPUT_CONTRACT,
OUTPUT_VARIABLE,
tx_gas_limit,
tx_input_type,
tx_inputs_count,
tx_output_type,
tx_outputs_count,
tx_script_bytecode,
};

/// Get the ID of a contract input
fn input_contract_id(index: u8) -> ContractId {
// Check that input at this index is a contract input
assert(tx_input_type(index) == INPUT_CONTRACT);
let ptr = tx_input_pointer(index);
let contract_id_bytes = b256_from_pointer_offset(ptr, 128); // Contract ID starts at 17th word: 16 * 8 = 128

// TODO: Replace with actual contract id
~ContractId::from(0xf5dbe963c235c1e54f8732f1ecdc955df2ad8db8c9ab58eea8e1338762bf8bc2) //~ContractId::from(contract_id_bytes)
}

/// Get the contract ID from a message input's data
fn contract_id_from_message_input(index: u8) -> ContractId {
// TODO: Replace with actual message check once input messages are enabled in the sdk
assert(tx_input_type(index) == INPUT_COIN);
~ContractId::from(0xf5dbe963c235c1e54f8732f1ecdc955df2ad8db8c9ab58eea8e1338762bf8bc2)/*
// Check that input at this index is a message input
assert(tx_input_type(index) == INPUT_MESSAGE);

let ptr = tx_input_pointer(index);
let contract_id_bytes = b256_from_pointer_offset(ptr, 192); // Contract ID is at start of data, which is at 24th word: 24 * 8 = 192
~ContractId::from(contract_id_bytes)
*/
}
use utils::{contract_id_from_contract_input, contract_id_from_message_input, tx_script_bytecode_hash};

/// Predicate verifying a message input is being spent according to the rules for a valid deposit
fn main() -> bool {
Expand All @@ -59,17 +32,14 @@ fn main() -> bool {

// The hash of the script which must spend the input belonging to this predicate
// This ensures the coins can only be spent in a call to `TokenContract.finalizeDeposit()`
// Note: The script must be right-padded to the next full word before hashing, to match with `get_script_bytecode()`
const SPENDING_SCRIPT_HASH = 0x2d235589506d17993e0b7aca4407a5ac1c325efd9d704ff94696a8f7c012ab9d;
const SPENDING_SCRIPT_HASH = 0x6856bb03b84d876ee60c890be5b0602d0f7480d375917a660da3115e8e008ddb;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like the perfect usecase for configuration-time constants!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be deferred to a subsequent PR.


////////////////
// CONDITIONS //
////////////////

// Verify script bytecode hash matches
let script_bytcode: [u64;
32] = tx_script_bytecode(); // Note : Make sure length is script bytecode length rounded up to next word
assert(sha256(script_bytcode) == SPENDING_SCRIPT_HASH);
assert(tx_script_bytecode_hash() == SPENDING_SCRIPT_HASH);

// Verify gas limit is high enough
//TODO: does gas limit include InputMessage amount? might need to just check avail balance - InputMessage amount
Expand All @@ -79,7 +49,7 @@ fn main() -> bool {
assert(tx_inputs_count() == 3);
assert(tx_input_type(0) == INPUT_COIN);
let message_data_contract_id = contract_id_from_message_input(1);
let input_contract_id = input_contract_id(2);
let input_contract_id = contract_id_from_contract_input(2);

// Check contract ID from the contract input matches the one specified in the message data
assert(input_contract_id == message_data_contract_id);
Expand Down
65 changes: 65 additions & 0 deletions contract-message-predicate/src/utils.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
library utils;

use std::{
address::Address,
tx::{
INPUT_COIN,
INPUT_CONTRACT,
INPUT_MESSAGE,
OUTPUT_CHANGE,
OUTPUT_CONTRACT,
OUTPUT_VARIABLE,
b256_from_pointer_offset,
tx_gas_limit,
tx_input_pointer,
tx_input_type,
tx_inputs_count,
tx_output_type,
tx_outputs_count,
tx_script_bytecode}
};

use std::assert::assert;
use std::hash::sha256;
use std::contract_id::ContractId;

/// Get the ID of a contract input
pub fn contract_id_from_contract_input(index: u8) -> ContractId {
// Check that input at this index is a contract input
assert(tx_input_type(index) == INPUT_CONTRACT);
let ptr = tx_input_pointer(index);
let contract_id_bytes = b256_from_pointer_offset(ptr, 128); // Contract ID starts at 17th word: 16 * 8 = 128

// TODO: Replace with actual contract id
~ContractId::from(0xf5dbe963c235c1e54f8732f1ecdc955df2ad8db8c9ab58eea8e1338762bf8bc2) //~ContractId::from(contract_id_bytes)
}

/// Get the contract ID from a message input's data
pub fn contract_id_from_message_input(index: u8) -> ContractId {
// TODO: Replace with actual message check once input messages are enabled in the sdk
assert(tx_input_type(index) == INPUT_COIN);
~ContractId::from(0xf5dbe963c235c1e54f8732f1ecdc955df2ad8db8c9ab58eea8e1338762bf8bc2)/*
// Check that input at this index is a message input
assert(tx_input_type(index) == INPUT_MESSAGE);

let ptr = tx_input_pointer(index);
let contract_id_bytes = b256_from_pointer_offset(ptr, 192); // Contract ID is at start of data, which is at 24th word: 24 * 8 = 192
~ContractId::from(contract_id_bytes)
*/
}

// From sway repo: tx.sw
use std::core::num::*;
const TX_SCRIPT_LENGTH_OFFSET = 10280;
const TX_SCRIPT_START_OFFSET = 10352;

/// Get the hash of the script bytecode
pub fn tx_script_bytecode_hash() -> b256 {
let mut result_buffer: b256 = ~b256::min();

asm(hash: result_buffer, script_offset: TX_SCRIPT_START_OFFSET, length_offset: TX_SCRIPT_LENGTH_OFFSET, length) {
lw length length_offset i0; // Load the length value at the length_offset
s256 hash script_offset length; // Hash the an array of length "length" starting from "script_offset" into "hash"
hash: b256 // Return
}
}
15 changes: 5 additions & 10 deletions contract-message-test/tests/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,13 @@ async fn spend_predicate_with_script_constraint() {
let provider = wallet.get_provider().unwrap();
let client = &provider.client;

// Get padded bytecode root that must be hardcoded into the predicate to constrain the spending transaction
let mut script_bytecode =
// Get bytecode root that must be hardcoded into the predicate to constrain the spending transaction
let script_bytecode =
std::fs::read("../contract-message-script/out/debug/contract_message_script.bin")
.unwrap()
.to_vec();
let padding = script_bytecode.len() % 8;
let script_bytecode_unpadded = script_bytecode.clone();
script_bytecode.append(&mut vec![0; padding]);
let script_hash = Hasher::hash(&script_bytecode);

println!("Padded script length: {}", script_bytecode.len());
println!("Padded script hash : 0x{:?}", script_hash);
let script_hash = Hasher::hash(&script_bytecode.clone());
println!("script hash : 0x{:?}", script_hash);

// Deploy test contract
let test_contract_id = Contract::deploy(
Expand Down Expand Up @@ -152,7 +147,7 @@ async fn spend_predicate_with_script_constraint() {
maturity: 0,
byte_price: 0,
receipts_root: Default::default(),
script: script_bytecode_unpadded,
script: script_bytecode,
script_data: vec![],
inputs: vec![input_coin, input_coin_message, input_contract],
outputs: vec![output_variable, output_contract, output_change],
Expand Down