@@ -110,3 +110,168 @@ impl IdentityCreditTransferTransitionMethodsV0 for IdentityCreditTransferTransit
110110 Ok ( transition)
111111 }
112112}
113+
114+ #[ cfg( all( test, feature = "state-transition-signing" ) ) ]
115+ mod tests {
116+ use crate :: address_funds:: AddressWitness ;
117+ use crate :: consensus:: basic:: BasicError ;
118+ use crate :: consensus:: ConsensusError ;
119+ use crate :: identity:: accessors:: { IdentityGettersV0 , IdentitySettersV0 } ;
120+ use crate :: identity:: identity_public_key:: accessors:: v0:: IdentityPublicKeyGettersV0 ;
121+ use crate :: identity:: identity_public_key:: v0:: IdentityPublicKeyV0 ;
122+ use crate :: identity:: signer:: Signer ;
123+ use crate :: identity:: { Identity , IdentityPublicKey , KeyType , Purpose , SecurityLevel } ;
124+ use crate :: state_transition:: identity_credit_transfer_transition:: methods:: IdentityCreditTransferTransitionMethodsV0 ;
125+ use crate :: state_transition:: identity_credit_transfer_transition:: v0:: IdentityCreditTransferTransitionV0 ;
126+ use crate :: ProtocolError ;
127+ use platform_value:: BinaryData ;
128+ use platform_value:: Identifier ;
129+ use platform_version:: version:: LATEST_PLATFORM_VERSION ;
130+
131+ #[ derive( Debug ) ]
132+ struct TestIdentitySigner {
133+ allowed_key_id : u32 ,
134+ }
135+
136+ impl Signer < IdentityPublicKey > for TestIdentitySigner {
137+ fn sign (
138+ & self ,
139+ _key : & IdentityPublicKey ,
140+ _data : & [ u8 ] ,
141+ ) -> Result < BinaryData , ProtocolError > {
142+ Ok ( vec ! [ 0 ; 65 ] . into ( ) )
143+ }
144+
145+ fn sign_create_witness (
146+ & self ,
147+ _key : & IdentityPublicKey ,
148+ _data : & [ u8 ] ,
149+ ) -> Result < AddressWitness , ProtocolError > {
150+ Err ( ProtocolError :: NotSupported (
151+ "sign_create_witness is not used in these tests" . to_string ( ) ,
152+ ) )
153+ }
154+
155+ fn can_sign_with ( & self , key : & IdentityPublicKey ) -> bool {
156+ key. id ( ) == self . allowed_key_id
157+ }
158+ }
159+
160+ fn transfer_key ( key_id : u32 ) -> IdentityPublicKey {
161+ IdentityPublicKeyV0 {
162+ id : key_id,
163+ purpose : Purpose :: TRANSFER ,
164+ security_level : SecurityLevel :: CRITICAL ,
165+ contract_bounds : None ,
166+ key_type : KeyType :: ECDSA_SECP256K1 ,
167+ read_only : false ,
168+ data : vec ! [ 2 ; 33 ] . into ( ) ,
169+ disabled_at : None ,
170+ }
171+ . into ( )
172+ }
173+
174+ fn identity_with_transfer_key ( identity_id : Identifier , key_id : u32 ) -> Identity {
175+ let mut identity =
176+ Identity :: default_versioned ( LATEST_PLATFORM_VERSION ) . expect ( "expected identity" ) ;
177+ identity. set_id ( identity_id) ;
178+ identity. add_public_key ( transfer_key ( key_id) ) ;
179+ identity
180+ }
181+
182+ #[ test]
183+ fn should_return_identity_credit_transfer_to_self_error_when_recipient_is_sender ( ) {
184+ let identity_id = Identifier :: random ( ) ;
185+ let identity = identity_with_transfer_key ( identity_id, 1 ) ;
186+ let min_transfer_amount = LATEST_PLATFORM_VERSION
187+ . fee_version
188+ . state_transition_min_fees
189+ . credit_transfer ;
190+
191+ let result = IdentityCreditTransferTransitionV0 :: try_from_identity (
192+ & identity,
193+ identity_id,
194+ min_transfer_amount,
195+ 0 ,
196+ TestIdentitySigner { allowed_key_id : 1 } ,
197+ None ,
198+ 1 ,
199+ LATEST_PLATFORM_VERSION ,
200+ None ,
201+ ) ;
202+
203+ match result {
204+ Err ( ProtocolError :: ConsensusError ( consensus_error) ) => {
205+ assert ! ( matches!(
206+ consensus_error. as_ref( ) ,
207+ ConsensusError :: BasicError ( BasicError :: IdentityCreditTransferToSelfError ( _) )
208+ ) ) ;
209+ }
210+ other => panic ! ( "expected to-self consensus error, got {other:?}" ) ,
211+ }
212+ }
213+
214+ #[ test]
215+ fn should_return_invalid_identity_credit_transfer_amount_error_when_below_minimum ( ) {
216+ let identity_id = Identifier :: random ( ) ;
217+ let identity = identity_with_transfer_key ( identity_id, 1 ) ;
218+ let min_transfer_amount = LATEST_PLATFORM_VERSION
219+ . fee_version
220+ . state_transition_min_fees
221+ . credit_transfer ;
222+ assert ! (
223+ min_transfer_amount > 0 ,
224+ "minimum transfer amount must be positive"
225+ ) ;
226+ let below_min_amount = min_transfer_amount - 1 ;
227+
228+ let result = IdentityCreditTransferTransitionV0 :: try_from_identity (
229+ & identity,
230+ Identifier :: random ( ) ,
231+ below_min_amount,
232+ 0 ,
233+ TestIdentitySigner { allowed_key_id : 1 } ,
234+ None ,
235+ 1 ,
236+ LATEST_PLATFORM_VERSION ,
237+ None ,
238+ ) ;
239+
240+ match result {
241+ Err ( ProtocolError :: ConsensusError ( consensus_error) ) => {
242+ assert ! ( matches!(
243+ consensus_error. as_ref( ) ,
244+ ConsensusError :: BasicError (
245+ BasicError :: InvalidIdentityCreditTransferAmountError ( error)
246+ ) if error. amount( ) == below_min_amount
247+ && error. min_amount( ) == min_transfer_amount
248+ ) ) ;
249+ }
250+ other => panic ! ( "expected invalid amount consensus error, got {other:?}" ) ,
251+ }
252+ }
253+
254+ #[ test]
255+ fn should_succeed_when_transfer_amount_equals_minimum ( ) {
256+ let identity_id = Identifier :: random ( ) ;
257+ let identity = identity_with_transfer_key ( identity_id, 1 ) ;
258+ let min_transfer_amount = LATEST_PLATFORM_VERSION
259+ . fee_version
260+ . state_transition_min_fees
261+ . credit_transfer ;
262+
263+ let result = IdentityCreditTransferTransitionV0 :: try_from_identity (
264+ & identity,
265+ Identifier :: random ( ) ,
266+ min_transfer_amount,
267+ 0 ,
268+ TestIdentitySigner { allowed_key_id : 1 } ,
269+ None ,
270+ 1 ,
271+ LATEST_PLATFORM_VERSION ,
272+ None ,
273+ ) ;
274+
275+ assert ! ( result. is_ok( ) , "expected boundary amount to be valid" ) ;
276+ }
277+ }
0 commit comments