11use 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} ;
87use ed25519_dalek:: { ed25519:: signature:: Signer as _, Signature as Ed25519Signature } ;
98use 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
6655pub 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
156148fn sign_soroban_authorization_entry (
0 commit comments