Skip to content

Commit 34d93fe

Browse files
Remove the caching of authenticated_signer / authenticated_caller_id in contract calls. (#4374)
## Motivation The `authenticated_signer` / `authenticated_caller_id` were cached in the `linera-sdk`. This is incorrect, since it varies from one call to another. ## Proposal Caching was removed. ## Test Plan The CI. The test of the feature is done with a smart contract `contract-call`. It is tested in the integration test. Without this correction, the test fails. The test could not be put at a lower level like `linera-core` or `linera-execution` since those do not use the `linera-sdk`. ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links None.
1 parent 0def7e8 commit 34d93fe

File tree

9 files changed

+357
-10
lines changed

9 files changed

+357
-10
lines changed

examples/Cargo.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ resolver = "2"
33
members = [
44
"amm",
55
"call-evm-counter",
6+
"contract-call",
67
"counter",
78
"counter-no-graphql",
89
"create-and-call",

examples/contract-call/Cargo.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "contract-call"
3+
version = "0.1.0"
4+
authors = ["Linera <[email protected]>"]
5+
edition = "2021"
6+
7+
[dependencies]
8+
linera-sdk = { path = "../../linera-sdk" }
9+
serde = { version = "1.0.152", features = ["derive"] }
10+
11+
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
12+
linera-sdk = { workspace = true, features = ["test", "wasmer"] }
13+
tokio.workspace = true
14+
15+
[[bin]]
16+
name = "contract_call_contract"
17+
path = "src/contract.rs"
18+
19+
[[bin]]
20+
name = "contract_call_service"
21+
path = "src/service.rs"

examples/contract-call/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Contract call
2+
3+
This example illustrates the access to `authenticated_signer` and `authenticated_caller_id`
4+
in contract calls.
5+
6+
It also illustrates that how a system call has access to the `authenticated_caller_id`
7+
even in non authenticated calls.
8+
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#![cfg_attr(target_arch = "wasm32", no_main)]
5+
6+
use contract_call::{ContractTransferAbi, Operation, Parameters};
7+
use linera_sdk::{
8+
linera_base_types::{Account, ApplicationId, WithContractAbi},
9+
Contract, ContractRuntime,
10+
};
11+
12+
pub struct ContractTransferContract {
13+
runtime: ContractRuntime<Self>,
14+
}
15+
16+
linera_sdk::contract!(ContractTransferContract);
17+
18+
impl WithContractAbi for ContractTransferContract {
19+
type Abi = ContractTransferAbi;
20+
}
21+
22+
impl Contract for ContractTransferContract {
23+
type Message = ();
24+
type InstantiationArgument = ();
25+
type Parameters = Parameters;
26+
type EventValue = ();
27+
28+
async fn load(runtime: ContractRuntime<Self>) -> Self {
29+
ContractTransferContract { runtime }
30+
}
31+
32+
async fn instantiate(&mut self, _value: ()) {
33+
// Validate that the application parameters were configured correctly.
34+
self.runtime.application_parameters();
35+
}
36+
37+
async fn execute_operation(&mut self, operation: Operation) {
38+
match operation {
39+
Operation::DirectTransfer {
40+
source,
41+
destination,
42+
amount,
43+
} => {
44+
// Direct transfer using runtime.transfer
45+
self.runtime.transfer(source, destination, amount);
46+
}
47+
Operation::IndirectTransfer {
48+
source,
49+
destination,
50+
amount,
51+
} => {
52+
// Indirect transfer: create application, transfer to it, then transfer from it
53+
let module_id = self.runtime.application_parameters().module_id;
54+
// let typed_module_id: ModuleId<ContractTransferAbi, Parameters, ()> = module_id.with_abi();
55+
56+
// Create a new application instance
57+
let parameters = self.runtime.application_parameters();
58+
let application_id = self
59+
.runtime
60+
.create_application::<ContractTransferAbi, Parameters, ()>(
61+
module_id,
62+
&parameters,
63+
&(),
64+
vec![],
65+
);
66+
67+
// Transfer from source to the created application
68+
let chain_id = self.runtime.chain_id();
69+
let app_account = Account {
70+
chain_id,
71+
owner: application_id.into(),
72+
};
73+
self.runtime.transfer(source, app_account, amount);
74+
75+
// Authenticated calls should be fully visible.
76+
let operation = Operation::TestSomeAuthenticatedSignerCaller;
77+
self.runtime
78+
.call_application(true, application_id, &operation);
79+
80+
// Non-authenticated calls should be fully non-visible
81+
let operation = Operation::TestNoneAuthenticatedSignerCaller;
82+
self.runtime
83+
.call_application(false, application_id, &operation);
84+
85+
// Non-authenticated calls should be fully non-visible
86+
let operation = Operation::TestNoneAuthenticatedSignerCaller;
87+
self.runtime
88+
.call_application(false, application_id, &operation);
89+
90+
// Authenticated calls should be fully visible.
91+
let operation = Operation::TestSomeAuthenticatedSignerCaller;
92+
self.runtime
93+
.call_application(true, application_id, &operation);
94+
95+
// Call the created application to transfer from itself to destination.
96+
// Authenticated or not, the system is able to access the caller
97+
// for making the transfer.
98+
let operation = Operation::DirectTransfer {
99+
source: ApplicationId::into(application_id),
100+
destination,
101+
amount,
102+
};
103+
self.runtime
104+
.call_application(false, application_id, &operation);
105+
}
106+
Operation::TestNoneAuthenticatedSignerCaller => {
107+
assert!(self.runtime.authenticated_signer().is_none());
108+
assert!(self.runtime.authenticated_caller_id().is_none());
109+
}
110+
Operation::TestSomeAuthenticatedSignerCaller => {
111+
assert!(self.runtime.authenticated_signer().is_some());
112+
assert!(self.runtime.authenticated_caller_id().is_some());
113+
}
114+
}
115+
}
116+
117+
async fn execute_message(&mut self, _message: ()) {
118+
panic!("Contract transfer application doesn't support any cross-chain messages");
119+
}
120+
121+
async fn store(self) {}
122+
}

examples/contract-call/src/lib.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use linera_sdk::linera_base_types::{
5+
Account, AccountOwner, Amount, ContractAbi, ModuleId, ServiceAbi,
6+
};
7+
use serde::{Deserialize, Serialize};
8+
9+
pub struct ContractTransferAbi;
10+
11+
impl ContractAbi for ContractTransferAbi {
12+
type Operation = Operation;
13+
type Response = ();
14+
}
15+
16+
impl ServiceAbi for ContractTransferAbi {
17+
type Query = Query;
18+
type QueryResponse = ();
19+
}
20+
21+
#[derive(Debug, Serialize, Deserialize)]
22+
pub enum Operation {
23+
DirectTransfer {
24+
source: AccountOwner,
25+
destination: Account,
26+
amount: Amount,
27+
},
28+
IndirectTransfer {
29+
source: AccountOwner,
30+
destination: Account,
31+
amount: Amount,
32+
},
33+
TestNoneAuthenticatedSignerCaller,
34+
TestSomeAuthenticatedSignerCaller,
35+
}
36+
37+
#[derive(Debug, Serialize, Deserialize)]
38+
pub enum Query {
39+
DirectTransfer {
40+
source: AccountOwner,
41+
destination: Account,
42+
amount: Amount,
43+
},
44+
IndirectTransfer {
45+
source: AccountOwner,
46+
destination: Account,
47+
amount: Amount,
48+
},
49+
}
50+
51+
#[derive(Clone, Debug, Serialize, Deserialize)]
52+
pub struct Parameters {
53+
pub module_id: ModuleId,
54+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#![cfg_attr(target_arch = "wasm32", no_main)]
5+
6+
use std::sync::Arc;
7+
8+
use contract_call::{ContractTransferAbi, Operation, Parameters, Query};
9+
use linera_sdk::{linera_base_types::WithServiceAbi, Service, ServiceRuntime};
10+
11+
pub struct ContractTransferService {
12+
runtime: Arc<ServiceRuntime<Self>>,
13+
}
14+
15+
linera_sdk::service!(ContractTransferService);
16+
17+
impl WithServiceAbi for ContractTransferService {
18+
type Abi = ContractTransferAbi;
19+
}
20+
21+
impl Service for ContractTransferService {
22+
type Parameters = Parameters;
23+
24+
async fn new(runtime: ServiceRuntime<Self>) -> Self {
25+
ContractTransferService {
26+
runtime: Arc::new(runtime),
27+
}
28+
}
29+
30+
async fn handle_query(&self, request: Query) {
31+
match request {
32+
Query::DirectTransfer {
33+
source,
34+
destination,
35+
amount,
36+
} => {
37+
let operation = Operation::DirectTransfer {
38+
source,
39+
destination,
40+
amount,
41+
};
42+
self.runtime.schedule_operation(&operation);
43+
}
44+
Query::IndirectTransfer {
45+
source,
46+
destination,
47+
amount,
48+
} => {
49+
let operation = Operation::IndirectTransfer {
50+
source,
51+
destination,
52+
amount,
53+
};
54+
self.runtime.schedule_operation(&operation);
55+
}
56+
}
57+
}
58+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Integration tests for the Contract Call application
5+
6+
#![cfg(not(target_arch = "wasm32"))]
7+
8+
use contract_call::{ContractTransferAbi, Parameters};
9+
use linera_sdk::{
10+
linera_base_types::{Account, AccountOwner, Amount},
11+
test::TestValidator,
12+
};
13+
14+
/// Test that mimics the end-to-end contract call test from linera_net_tests.rs
15+
/// This test creates two accounts, publishes the contract-call module,
16+
/// creates an application, and tests indirect transfer functionality.
17+
#[tokio::test]
18+
async fn test_contract_call_integration() {
19+
use contract_call::Operation;
20+
let (validator, module_id) =
21+
TestValidator::with_current_module::<ContractTransferAbi, Parameters, ()>().await;
22+
23+
let mut chain = validator.new_chain().await;
24+
let owner1 = AccountOwner::from(chain.public_key());
25+
let account1 = Account {
26+
chain_id: chain.id(),
27+
owner: owner1,
28+
};
29+
30+
let transfer_amount = Amount::from_tokens(100);
31+
let funding_chain = validator.get_chain(&validator.admin_chain_id());
32+
let transfer_certificate = funding_chain
33+
.add_block(|block| {
34+
block.with_native_token_transfer(AccountOwner::CHAIN, account1, transfer_amount);
35+
})
36+
.await;
37+
chain
38+
.add_block(|block| {
39+
block.with_messages_from(&transfer_certificate);
40+
})
41+
.await;
42+
43+
// Generate a second owner
44+
let second_chain = validator.new_chain().await;
45+
let owner2 = AccountOwner::from(second_chain.public_key());
46+
47+
// Create accounts for both owners
48+
let account2 = Account {
49+
chain_id: chain.id(),
50+
owner: owner2,
51+
};
52+
53+
// Give initial balance to the first account (simulating the transfer_with_accounts)
54+
// In the test environment, chains start with default balance
55+
56+
// Create parameters for the application
57+
let parameters = Parameters {
58+
module_id: module_id.forget_abi(),
59+
};
60+
61+
// Create the application
62+
let application_id = chain
63+
.create_application(module_id, parameters, (), vec![])
64+
.await;
65+
66+
// Test indirect transfer: transfer from owner1 to owner2 via application
67+
let transfer_amount = Amount::from_tokens(1);
68+
69+
// Use Operation to trigger indirect transfer operation
70+
let operation = Operation::IndirectTransfer {
71+
source: owner1,
72+
destination: account2,
73+
amount: transfer_amount,
74+
};
75+
76+
// Add a block with the operation
77+
chain
78+
.add_block(|block| {
79+
block.with_operation(application_id, operation);
80+
})
81+
.await;
82+
}

0 commit comments

Comments
 (0)