Skip to content

Commit c385f26

Browse files
committed
fix: remove tx source from auth entry signing logic
1 parent 6ce33a1 commit c385f26

4 files changed

Lines changed: 58 additions & 64 deletions

File tree

cmd/crates/soroban-test/tests/it/integration/hello_world.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ async fn invoke_contract() {
133133
invoke_auth(sandbox, id, &addr);
134134
invoke_auth_with_identity(sandbox, id, "test", &addr);
135135
invoke_auth_with_identity(sandbox, id, "testone", &addr_1);
136+
invoke_auth_with_non_source_identity(sandbox, id, "test", "testone", &addr_1);
136137
invoke_auth_with_different_test_account_fail(sandbox, id, &addr_1).await;
137-
// invoke_auth_with_different_test_account(sandbox, id);
138138
contract_data_read_failure(sandbox, id);
139139
invoke_with_seed(sandbox, id, &seed_phrase).await;
140140
invoke_with_sk(sandbox, id, &secret_key).await;
@@ -225,6 +225,30 @@ fn invoke_auth_with_identity(sandbox: &TestEnv, id: &str, key: &str, addr: &str)
225225
.success();
226226
}
227227

228+
fn invoke_auth_with_non_source_identity(
229+
sandbox: &TestEnv,
230+
id: &str,
231+
source: &str,
232+
key: &str,
233+
addr: &str,
234+
) {
235+
sandbox
236+
.new_assert_cmd("contract")
237+
.arg("invoke")
238+
.arg("--source")
239+
.arg(source)
240+
.arg("--id")
241+
.arg(id)
242+
.arg("--")
243+
.arg("auth")
244+
.arg("--addr")
245+
.arg(key)
246+
.arg("--world=world")
247+
.assert()
248+
.stdout(format!("\"{addr}\"\n"))
249+
.success();
250+
}
251+
228252
async fn invoke_auth_with_different_test_account_fail(sandbox: &TestEnv, id: &str, addr: &str) {
229253
let res = sandbox
230254
.invoke_with_test(&[

cmd/soroban-cli/src/assembled.rs

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use sha2::{Digest, Sha256};
22
use stellar_xdr::curr::{
3-
self as xdr, ExtensionPoint, Hash, InvokeHostFunctionOp, LedgerFootprint, Limits, Memo,
4-
Operation, OperationBody, Preconditions, ReadXdr, RestoreFootprintOp,
5-
SorobanAuthorizationEntry, SorobanAuthorizedFunction, SorobanResources, SorobanTransactionData,
6-
Transaction, TransactionEnvelope, TransactionExt, TransactionSignaturePayload,
3+
self as xdr, ExtensionPoint, Hash, LedgerFootprint, Limits, Memo, Operation, OperationBody,
4+
Preconditions, ReadXdr, RestoreFootprintOp, SorobanAuthorizationEntry,
5+
SorobanAuthorizedFunction, SorobanResources, SorobanTransactionData, Transaction,
6+
TransactionEnvelope, TransactionExt, TransactionSignaturePayload,
77
TransactionSignaturePayloadTaggedTransaction, TransactionV1Envelope, VecM, WriteXdr,
88
};
99

@@ -156,11 +156,6 @@ impl Assembled {
156156
Ok(())
157157
}
158158

159-
#[must_use]
160-
pub fn requires_auth(&self) -> bool {
161-
requires_auth(&self.txn).is_some()
162-
}
163-
164159
#[must_use]
165160
pub fn is_view(&self) -> bool {
166161
let TransactionExt::V1(SorobanTransactionData {
@@ -275,21 +270,6 @@ fn assemble(
275270
Ok(tx)
276271
}
277272

278-
fn requires_auth(txn: &Transaction) -> Option<xdr::Operation> {
279-
let [op @ Operation {
280-
body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { auth, .. }),
281-
..
282-
}] = txn.operations.as_slice()
283-
else {
284-
return None;
285-
};
286-
matches!(
287-
auth.first().map(|x| &x.root_invocation.function),
288-
Some(&SorobanAuthorizedFunction::ContractFn(_))
289-
)
290-
.then(move || op.clone())
291-
}
292-
293273
fn restore(parent: &Transaction, restore: &RestorePreamble) -> Result<Transaction, Error> {
294274
let transaction_data =
295275
SorobanTransactionData::from_xdr_base64(&restore.transaction_data, Limits::none())?;

cmd/soroban-cli/src/config/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,11 @@ impl Args {
122122
signers: &[Signer],
123123
) -> Result<Option<Transaction>, Error> {
124124
let network = self.get_network()?;
125-
let source_signer = self.source_signer().await?;
126125
let client = network.rpc_client()?;
127126
let latest_ledger = client.get_latest_ledger().await?.sequence;
128127
let seq_num = latest_ledger + 60; // ~ 5 min
129128
Ok(signer::sign_soroban_authorizations(
130129
tx,
131-
&source_signer,
132130
signers,
133131
seq_num,
134132
&network.network_passphrase,

cmd/soroban-cli/src/signer/mod.rs

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use crate::xdr::{
22
self, AccountId, DecoratedSignature, Hash, HashIdPreimage, HashIdPreimageSorobanAuthorization,
3-
InvokeHostFunctionOp, Limits, Operation, OperationBody, PublicKey, ScAddress, ScMap, ScSymbol,
4-
ScVal, Signature, SignatureHint, SorobanAddressCredentials, SorobanAuthorizationEntry,
5-
SorobanAuthorizedFunction, SorobanCredentials, Transaction, TransactionEnvelope,
6-
TransactionV1Envelope, Uint256, VecM, WriteXdr,
3+
Limits, Operation, OperationBody, PublicKey, ScAddress, ScMap, ScSymbol, ScVal, Signature,
4+
SignatureHint, SorobanAddressCredentials, SorobanAuthorizationEntry, SorobanCredentials,
5+
Transaction, TransactionEnvelope, TransactionV1Envelope, Uint256, VecM, WriteXdr,
76
};
87
use ed25519_dalek::{ed25519::signature::Signer as _, Signature as Ed25519Signature};
98
use sha2::{Digest, Sha256};
@@ -46,39 +45,24 @@ pub enum Error {
4645
Decode(#[from] stellar_strkey::DecodeError),
4746
}
4847

49-
fn requires_auth(txn: &Transaction) -> Option<xdr::Operation> {
50-
let [op @ Operation {
51-
body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { auth, .. }),
52-
..
53-
}] = txn.operations.as_slice()
54-
else {
55-
return None;
56-
};
57-
matches!(
58-
auth.first().map(|x| &x.root_invocation.function),
59-
Some(&SorobanAuthorizedFunction::ContractFn(_))
60-
)
61-
.then(move || op.clone())
62-
}
63-
64-
// Use the given source_key and signers, to sign all SorobanAuthorizationEntry's in the given
65-
// transaction. If unable to sign, return an error.
48+
/// Use the given source_key and signers, to sign all SorobanAuthorizationEntry's in the given
49+
/// transaction.
50+
///
51+
/// If no SorobanAuthorizationEntry's need signing (including if none exist), return Ok(None).
52+
///
53+
/// If a SorobanAuthorizationEntry needs signing, but a signature cannot be produced for it,
54+
/// return an Error
6655
pub fn sign_soroban_authorizations(
6756
raw: &Transaction,
68-
source_signer: &Signer,
6957
signers: &[Signer],
7058
signature_expiration_ledger: u32,
7159
network_passphrase: &str,
7260
) -> Result<Option<Transaction>, Error> {
73-
let mut tx = raw.clone();
74-
let Some(mut op) = requires_auth(&tx) else {
75-
return Ok(None);
76-
};
77-
78-
let Operation {
79-
body: OperationBody::InvokeHostFunction(ref mut body),
61+
// Check if we have exactly one operation and it's InvokeHostFunction
62+
let [op @ Operation {
63+
body: OperationBody::InvokeHostFunction(body),
8064
..
81-
} = op
65+
}] = raw.operations.as_slice()
8266
else {
8367
return Ok(None);
8468
};
@@ -123,10 +107,6 @@ pub fn sign_soroban_authorizations(
123107
}
124108
}
125109

126-
if needle == &source_signer.get_public_key()?.0 {
127-
signer = Some(source_signer);
128-
}
129-
130110
match signer {
131111
Some(signer) => {
132112
let signed_entry = sign_soroban_authorization_entry(
@@ -148,9 +128,21 @@ pub fn sign_soroban_authorizations(
148128
}
149129
}
150130

151-
body.auth = signed_auths.try_into()?;
152-
tx.operations = vec![op].try_into()?;
153-
Ok(Some(tx))
131+
// No signatures were made, return None to indicate no change to the transaction
132+
if signed_auths.is_empty() {
133+
return Ok(None);
134+
}
135+
136+
// Build updated transaction with signed auth entries
137+
let mut updated_op = op.clone();
138+
if let OperationBody::InvokeHostFunction(ref mut updated_body) = updated_op.body {
139+
let mut tx = raw.clone();
140+
updated_body.auth = signed_auths.try_into()?;
141+
tx.operations = vec![updated_op].try_into()?;
142+
Ok(Some(tx))
143+
} else {
144+
Ok(None)
145+
}
154146
}
155147

156148
fn sign_soroban_authorization_entry(

0 commit comments

Comments
 (0)