-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
### What Add some auth test contracts that exercise different auth scenarios. ### Why I initially started writing these as a learning experience, but would like to commit them as a way to capture existing behavior that contract developers are exposed to and therefore relying on. As auth evolves in the coming releases, changes to these tests will make it clearer to me and others working at the application layer on how auth is changing.
- Loading branch information
1 parent
fa7965d
commit c20b456
Showing
9 changed files
with
592 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
#![cfg(test)] | ||
|
||
mod address; | ||
mod auth; | ||
mod budget; | ||
mod contract_add_i32; | ||
mod contract_assert; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
mod auth_10_one; | ||
mod auth_15_one_repeat; | ||
mod auth_17_no_consume_requirement; | ||
mod auth_20_deep_one_address; | ||
mod auth_30_deep_one_address_repeat; | ||
mod auth_35_deep_one_address_repeat_grouped; | ||
mod auth_40_multi_one_address; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
//! Demonstrates that a single contract invocation can be authorized. | ||
use crate as soroban_sdk; | ||
|
||
use soroban_sdk::{ | ||
contract, contractimpl, | ||
testutils::{Address as _, MockAuth, MockAuthInvoke}, | ||
Address, Env, IntoVal, | ||
}; | ||
|
||
#[contract] | ||
pub struct Contract; | ||
|
||
#[contractimpl] | ||
impl Contract { | ||
pub fn add(a: Address, x: i32, y: i32) -> i32 { | ||
a.require_auth(); | ||
x + y | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
let e = Env::default(); | ||
let contract_id = e.register_contract(None, Contract); | ||
let client = ContractClient::new(&e, &contract_id); | ||
|
||
let a = Address::random(&e); | ||
|
||
let c = client | ||
.mock_auths(&[MockAuth { | ||
address: &a, | ||
invoke: &MockAuthInvoke { | ||
contract: &contract_id, | ||
fn_name: "add", | ||
args: (&a, 10, 12).into_val(&e), | ||
sub_invokes: &[], | ||
}, | ||
}]) | ||
.add(&a, &10, &12); | ||
|
||
assert_eq!(c, 22); | ||
|
||
println!("{:?}", e.auths()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
//! Demonstrates how an address can be required auth multiple times in a single | ||
//! invocation. | ||
//! | ||
//! The authorizations cannot be grouped into a single authorization. | ||
use crate as soroban_sdk; | ||
|
||
use soroban_sdk::{ | ||
contract, contractimpl, | ||
testutils::{Address as _, MockAuth, MockAuthInvoke}, | ||
Address, Env, IntoVal, | ||
}; | ||
|
||
#[contract] | ||
pub struct Contract; | ||
|
||
#[contractimpl] | ||
impl Contract { | ||
pub fn add(a: Address, x: i32, y: i32) -> i32 { | ||
a.require_auth(); | ||
a.require_auth(); | ||
x + y | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
let e = Env::default(); | ||
let contract_id = e.register_contract(None, Contract); | ||
let client = ContractClient::new(&e, &contract_id); | ||
|
||
let a = Address::random(&e); | ||
|
||
let c = client | ||
.mock_auths(&[ | ||
MockAuth { | ||
address: &a, | ||
invoke: &MockAuthInvoke { | ||
contract: &contract_id, | ||
fn_name: "add", | ||
args: (&a, 10, 12).into_val(&e), | ||
sub_invokes: &[], | ||
}, | ||
}, | ||
MockAuth { | ||
address: &a, | ||
invoke: &MockAuthInvoke { | ||
contract: &contract_id, | ||
fn_name: "add", | ||
args: (&a, 10, 12).into_val(&e), | ||
sub_invokes: &[], | ||
}, | ||
}, | ||
]) | ||
.add(&a, &10, &12); | ||
|
||
assert_eq!(c, 22); | ||
|
||
println!("{:?}", e.auths()); | ||
} |
65 changes: 65 additions & 0 deletions
65
soroban-sdk/src/tests/auth/auth_17_no_consume_requirement.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
//! Demonstrates how an authorization is not required to be consumed by an | ||
//! invocation. | ||
//! | ||
//! It's possible to have dangling authorizations that may be optionally | ||
//! consumed, or never consumed. | ||
//! | ||
//! Because authorizations cannot always be grouped, it's not possible to group | ||
//! potentially related optional auths together to ensure. This means it's | ||
//! possible for an auth to be exposed on chain that could be executed by | ||
//! someone in isolation, even after the transaction succeeds. | ||
use crate as soroban_sdk; | ||
|
||
use soroban_sdk::{ | ||
contract, contractimpl, | ||
testutils::{Address as _, MockAuth, MockAuthInvoke}, | ||
Address, Env, IntoVal, | ||
}; | ||
|
||
#[contract] | ||
pub struct Contract; | ||
|
||
#[contractimpl] | ||
impl Contract { | ||
pub fn add(a: Address, x: i32, y: i32) -> i32 { | ||
a.require_auth(); | ||
x + y | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
let e = Env::default(); | ||
let contract_id = e.register_contract(None, Contract); | ||
let client = ContractClient::new(&e, &contract_id); | ||
|
||
let a = Address::random(&e); | ||
|
||
let c = client | ||
.mock_auths(&[ | ||
MockAuth { | ||
address: &a, | ||
invoke: &MockAuthInvoke { | ||
contract: &contract_id, | ||
fn_name: "add", | ||
args: (&a, 10, 12).into_val(&e), | ||
sub_invokes: &[], | ||
}, | ||
}, | ||
MockAuth { | ||
address: &a, | ||
invoke: &MockAuthInvoke { | ||
contract: &contract_id, | ||
fn_name: "add", | ||
args: (&a, 10, 12).into_val(&e), | ||
sub_invokes: &[], | ||
}, | ||
}, | ||
]) | ||
.add(&a, &10, &12); | ||
|
||
assert_eq!(c, 22); | ||
|
||
println!("{:?}", e.auths()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
use crate as soroban_sdk; | ||
|
||
use soroban_sdk::{ | ||
contract, contractimpl, | ||
testutils::{Address as _, MockAuth, MockAuthInvoke}, | ||
Address, Env, IntoVal, | ||
}; | ||
|
||
#[contract] | ||
pub struct ContractA; | ||
|
||
#[contractimpl] | ||
impl ContractA { | ||
pub fn fna(e: Env, contract: Address, a: Address) -> i32 { | ||
let client = ContractBClient::new(&e, &contract); | ||
client.fnb(&a) | ||
} | ||
} | ||
|
||
#[contract] | ||
pub struct ContractB; | ||
|
||
#[contractimpl] | ||
impl ContractB { | ||
pub fn fnb(a: Address) -> i32 { | ||
a.require_auth(); | ||
1 | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
let e = Env::default(); | ||
let contract_a_id = e.register_contract(None, ContractA); | ||
let contract_b_id = e.register_contract(None, ContractB); | ||
let client = ContractAClient::new(&e, &contract_a_id); | ||
|
||
let a = Address::random(&e); | ||
|
||
let c = client | ||
.mock_auths(&[MockAuth { | ||
address: &a, | ||
invoke: &MockAuthInvoke { | ||
contract: &contract_b_id, | ||
fn_name: "fnb", | ||
args: (&a,).into_val(&e), | ||
sub_invokes: &[], | ||
}, | ||
}]) | ||
.fna(&contract_b_id, &a); | ||
|
||
assert_eq!(c, 1); | ||
|
||
println!("{:?}", e.auths()); | ||
} | ||
|
||
#[test] | ||
// This test panics with not authorized because it does not appear to be | ||
// possible to constrain an auth to a specific call tree, unless the top-level | ||
// of that call tree also calls require_auth with the same address. | ||
#[should_panic = "HostError: Error(Auth, InvalidAction)"] | ||
fn test_auth_tree() { | ||
let e = Env::default(); | ||
let contract_a_id = e.register_contract(None, ContractA); | ||
let contract_b_id = e.register_contract(None, ContractB); | ||
let client = ContractAClient::new(&e, &contract_a_id); | ||
|
||
let a = Address::random(&e); | ||
|
||
let c = client | ||
.mock_auths(&[MockAuth { | ||
address: &a, | ||
invoke: &MockAuthInvoke { | ||
contract: &contract_a_id, | ||
fn_name: "fna", | ||
args: ().into_val(&e), // ??? | ||
sub_invokes: &[MockAuthInvoke { | ||
contract: &contract_b_id, | ||
fn_name: "fnb", | ||
args: (&a,).into_val(&e), | ||
sub_invokes: &[], | ||
}], | ||
}, | ||
}]) | ||
.fna(&contract_b_id, &a); | ||
|
||
assert_eq!(c, 1); | ||
|
||
println!("{:?}", e.auths()); | ||
} |
113 changes: 113 additions & 0 deletions
113
soroban-sdk/src/tests/auth/auth_30_deep_one_address_repeat.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
use crate as soroban_sdk; | ||
|
||
use soroban_sdk::{ | ||
contract, contractimpl, | ||
testutils::{Address as _, MockAuth, MockAuthInvoke}, | ||
Address, Env, IntoVal, | ||
}; | ||
|
||
#[contract] | ||
pub struct ContractA; | ||
|
||
#[contractimpl] | ||
impl ContractA { | ||
pub fn fna(e: Env, contract: Address, a: Address) -> i32 { | ||
let client = ContractBClient::new(&e, &contract); | ||
client.fnb(&a) | ||
} | ||
} | ||
|
||
#[contract] | ||
pub struct ContractB; | ||
|
||
#[contractimpl] | ||
impl ContractB { | ||
pub fn fnb(a: Address) -> i32 { | ||
a.require_auth(); | ||
a.require_auth(); | ||
1 | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
let e = Env::default(); | ||
let contract_a_id = e.register_contract(None, ContractA); | ||
let contract_b_id = e.register_contract(None, ContractB); | ||
let client = ContractAClient::new(&e, &contract_a_id); | ||
|
||
let a = Address::random(&e); | ||
|
||
let c = client | ||
.mock_auths(&[ | ||
MockAuth { | ||
address: &a, | ||
invoke: &MockAuthInvoke { | ||
contract: &contract_b_id, | ||
fn_name: "fnb", | ||
args: (&a,).into_val(&e), | ||
sub_invokes: &[], | ||
}, | ||
}, | ||
MockAuth { | ||
address: &a, | ||
invoke: &MockAuthInvoke { | ||
contract: &contract_b_id, | ||
fn_name: "fnb", | ||
args: (&a,).into_val(&e), | ||
sub_invokes: &[], | ||
}, | ||
}, | ||
]) | ||
.fna(&contract_b_id, &a); | ||
|
||
assert_eq!(c, 1); | ||
|
||
println!("{:?}", e.auths()); | ||
} | ||
|
||
#[test] | ||
// This test panics with not authorized because it does not appear to be | ||
// possible to constrain an auth to a specific call tree, unless the top-level | ||
// of that call tree also calls require_auth with the same address. | ||
// | ||
// It also doesn't appear to be possible to group auths that occur at the same | ||
// level, again unless a higher level also require_auth's the same addresses. | ||
#[should_panic = "HostError: Error(Auth, InvalidAction)"] | ||
fn test_auth_tree() { | ||
let e = Env::default(); | ||
let contract_a_id = e.register_contract(None, ContractA); | ||
let contract_b_id = e.register_contract(None, ContractB); | ||
let client = ContractAClient::new(&e, &contract_a_id); | ||
|
||
let a = Address::random(&e); | ||
|
||
let c = client | ||
.mock_auths(&[MockAuth { | ||
address: &a, | ||
invoke: &MockAuthInvoke { | ||
contract: &contract_a_id, | ||
fn_name: "fna", | ||
args: ().into_val(&e), // ??? | ||
sub_invokes: &[ | ||
MockAuthInvoke { | ||
contract: &contract_b_id, | ||
fn_name: "fnb", | ||
args: (&a,).into_val(&e), | ||
sub_invokes: &[], | ||
}, | ||
MockAuthInvoke { | ||
contract: &contract_b_id, | ||
fn_name: "fnb", | ||
args: (&a,).into_val(&e), | ||
sub_invokes: &[], | ||
}, | ||
], | ||
}, | ||
}]) | ||
.fna(&contract_b_id, &a); | ||
|
||
assert_eq!(c, 1); | ||
|
||
println!("{:?}", e.auths()); | ||
} |
Oops, something went wrong.