47
47
//! #### For general users
48
48
//! * `set_identity` - Set the associated identity of an account; a small deposit is reserved if not
49
49
//! already taken.
50
- //! * `set_subs` - Set the sub-accounts of an identity.
51
50
//! * `clear_identity` - Remove an account's associated identity; the deposit is returned.
52
51
//! * `request_judgement` - Request a judgement from a registrar, paying a fee.
53
52
//! * `cancel_request` - Cancel the previous request for a judgement.
54
53
//!
54
+ //! #### For general users with sub-identities
55
+ //! * `set_subs` - Set the sub-accounts of an identity.
56
+ //! * `add_sub` - Add a sub-identity to an identity.
57
+ //! * `remove_sub` - Remove a sub-identity of an identity.
58
+ //! * `rename_sub` - Rename a sub-identity of an identity.
59
+ //! * `quit_sub` - Remove a sub-identity of an identity (called by the sub-identity).
60
+ //!
55
61
//! #### For registrars
56
62
//! * `set_fee` - Set the fee required to be paid for a judgement to be given by the registrar.
57
63
//! * `set_fields` - Set the fields that a registrar cares about in their judgements.
@@ -70,8 +76,8 @@ use sp_std::prelude::*;
70
76
use sp_std:: { fmt:: Debug , ops:: Add , iter:: once} ;
71
77
use enumflags2:: BitFlags ;
72
78
use codec:: { Encode , Decode } ;
73
- use sp_runtime:: { DispatchError , RuntimeDebug } ;
74
- use sp_runtime:: traits:: { StaticLookup , Zero , AppendZerosInput } ;
79
+ use sp_runtime:: { DispatchError , RuntimeDebug , DispatchResult } ;
80
+ use sp_runtime:: traits:: { StaticLookup , Zero , AppendZerosInput , Saturating } ;
75
81
use frame_support:: {
76
82
decl_module, decl_event, decl_storage, ensure, decl_error,
77
83
dispatch:: DispatchResultWithPostInfo ,
@@ -97,6 +103,10 @@ pub trait WeightInfo {
97
103
fn set_fields ( r : u32 , ) -> Weight ;
98
104
fn provide_judgement ( r : u32 , x : u32 , ) -> Weight ;
99
105
fn kill_identity ( r : u32 , s : u32 , x : u32 , ) -> Weight ;
106
+ fn add_sub ( p : u32 , ) -> Weight ;
107
+ fn rename_sub ( ) -> Weight ;
108
+ fn remove_sub ( p : u32 , ) -> Weight ;
109
+ fn quit_sub ( p : u32 , ) -> Weight ;
100
110
}
101
111
102
112
impl WeightInfo for ( ) {
@@ -111,6 +121,10 @@ impl WeightInfo for () {
111
121
fn set_fields ( _r : u32 , ) -> Weight { 1_000_000_000 }
112
122
fn provide_judgement ( _r : u32 , _x : u32 , ) -> Weight { 1_000_000_000 }
113
123
fn kill_identity ( _r : u32 , _s : u32 , _x : u32 , ) -> Weight { 1_000_000_000 }
124
+ fn add_sub ( _p : u32 , ) -> Weight { 1_000_000_000 }
125
+ fn rename_sub ( ) -> Weight { 1_000_000_000 }
126
+ fn remove_sub ( _p : u32 , ) -> Weight { 1_000_000_000 }
127
+ fn quit_sub ( _p : u32 , ) -> Weight { 1_000_000_000 }
114
128
}
115
129
116
130
pub trait Trait : frame_system:: Trait {
@@ -462,6 +476,13 @@ decl_event!(
462
476
JudgementGiven ( AccountId , RegistrarIndex ) ,
463
477
/// A registrar was added.
464
478
RegistrarAdded ( RegistrarIndex ) ,
479
+ /// A sub-identity (first) was added to an identity (second) and the deposit paid.
480
+ SubIdentityAdded ( AccountId , AccountId , Balance ) ,
481
+ /// A sub-identity (first) was removed from an identity (second) and the deposit freed.
482
+ SubIdentityRemoved ( AccountId , AccountId , Balance ) ,
483
+ /// A sub-identity (first arg) was cleared, and the given deposit repatriated from the
484
+ /// main identity account (second arg) to the sub-identity account.
485
+ SubIdentityRevoked ( AccountId , AccountId , Balance ) ,
465
486
}
466
487
) ;
467
488
@@ -494,7 +515,13 @@ decl_error! {
494
515
TooManyFields ,
495
516
/// Maximum amount of registrars reached. Cannot add any more.
496
517
TooManyRegistrars ,
497
- }
518
+ /// Account ID is already named.
519
+ AlreadyClaimed ,
520
+ /// Sender is not a sub-account.
521
+ NotSub ,
522
+ /// Sub-account isn't owned by sender.
523
+ NotOwned
524
+ }
498
525
}
499
526
500
527
/// Functions for calcuating the weight of dispatchables.
@@ -620,6 +647,36 @@ mod weight_for {
620
647
+ 2_600_000 * subs // S
621
648
+ 900_000 * extra_fields // X
622
649
}
650
+
651
+ /// Weight calculation for `add_sub`.
652
+ pub ( crate ) fn add_sub < T : Trait > (
653
+ subs : Weight ,
654
+ ) -> Weight {
655
+ let db = T :: DbWeight :: get ( ) ;
656
+ db. reads_writes ( 4 , 3 ) + 124_000_000 + 156_000 * subs
657
+ }
658
+
659
+ /// Weight calculation for `rename_sub`.
660
+ pub ( crate ) fn rename_sub < T : Trait > ( ) -> Weight {
661
+ let db = T :: DbWeight :: get ( ) ;
662
+ db. reads_writes ( 2 , 1 ) + 30_000_000
663
+ }
664
+
665
+ /// Weight calculation for `remove_sub`.
666
+ pub ( crate ) fn remove_sub < T : Trait > (
667
+ subs : Weight ,
668
+ ) -> Weight {
669
+ let db = T :: DbWeight :: get ( ) ;
670
+ db. reads_writes ( 4 , 3 ) + 86_000_000 + 50_000 * subs
671
+ }
672
+
673
+ /// Weight calculation for `quit_sub`.
674
+ pub ( crate ) fn quit_sub < T : Trait > (
675
+ subs : Weight ,
676
+ ) -> Weight {
677
+ let db = T :: DbWeight :: get ( ) ;
678
+ db. reads_writes ( 3 , 2 ) + 63_000_000 + 230_000 * subs
679
+ }
623
680
}
624
681
625
682
decl_module ! {
@@ -774,6 +831,9 @@ decl_module! {
774
831
let ( old_deposit, old_ids) = <SubsOf <T >>:: get( & sender) ;
775
832
let new_deposit = T :: SubAccountDeposit :: get( ) * <BalanceOf <T >>:: from( subs. len( ) as u32 ) ;
776
833
834
+ let not_other_sub = subs. iter( ) . filter_map( |i| SuperOf :: <T >:: get( & i. 0 ) ) . all( |i| & i. 0 == & sender) ;
835
+ ensure!( not_other_sub, Error :: <T >:: AlreadyClaimed ) ;
836
+
777
837
if old_deposit < new_deposit {
778
838
T :: Currency :: reserve( & sender, new_deposit - old_deposit) ?;
779
839
}
@@ -831,8 +891,7 @@ decl_module! {
831
891
832
892
let ( subs_deposit, sub_ids) = <SubsOf <T >>:: take( & sender) ;
833
893
let id = <IdentityOf <T >>:: take( & sender) . ok_or( Error :: <T >:: NotNamed ) ?;
834
- let deposit = id. total_deposit( )
835
- + subs_deposit;
894
+ let deposit = id. total_deposit( ) + subs_deposit;
836
895
for sub in sub_ids. iter( ) {
837
896
<SuperOf <T >>:: remove( sub) ;
838
897
}
@@ -1159,6 +1218,103 @@ decl_module! {
1159
1218
id. info. additional. len( ) as Weight // X
1160
1219
) ) . into( ) )
1161
1220
}
1221
+
1222
+ /// Add the given account to the sender's subs.
1223
+ ///
1224
+ /// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated
1225
+ /// to the sender.
1226
+ ///
1227
+ /// The dispatch origin for this call must be _Signed_ and the sender must have a registered
1228
+ /// sub identity of `sub`.
1229
+ #[ weight = weight_for:: add_sub:: <T >(
1230
+ T :: MaxSubAccounts :: get( ) . into( ) , // S
1231
+ ) ]
1232
+ fn add_sub( origin, sub: <T :: Lookup as StaticLookup >:: Source , data: Data ) -> DispatchResult {
1233
+ let sender = ensure_signed( origin) ?;
1234
+ let sub = T :: Lookup :: lookup( sub) ?;
1235
+ ensure!( IdentityOf :: <T >:: contains_key( & sender) , Error :: <T >:: NoIdentity ) ;
1236
+
1237
+ // Check if it's already claimed as sub-identity.
1238
+ ensure!( !SuperOf :: <T >:: contains_key( & sub) , Error :: <T >:: AlreadyClaimed ) ;
1239
+
1240
+ SubsOf :: <T >:: try_mutate( & sender, |( ref mut subs_deposit, ref mut sub_ids) | {
1241
+ // Ensure there is space and that the deposit is paid.
1242
+ ensure!( sub_ids. len( ) < T :: MaxSubAccounts :: get( ) as usize , Error :: <T >:: TooManySubAccounts ) ;
1243
+ let deposit = T :: SubAccountDeposit :: get( ) ;
1244
+ T :: Currency :: reserve( & sender, deposit) ?;
1245
+
1246
+ SuperOf :: <T >:: insert( & sub, ( sender. clone( ) , data) ) ;
1247
+ sub_ids. push( sub. clone( ) ) ;
1248
+ * subs_deposit = subs_deposit. saturating_add( deposit) ;
1249
+
1250
+ Self :: deposit_event( RawEvent :: SubIdentityAdded ( sub, sender. clone( ) , deposit) ) ;
1251
+ Ok ( ( ) )
1252
+ } )
1253
+ }
1254
+
1255
+ /// Alter the associated name of the given sub-account.
1256
+ ///
1257
+ /// The dispatch origin for this call must be _Signed_ and the sender must have a registered
1258
+ /// sub identity of `sub`.
1259
+ #[ weight = weight_for:: rename_sub:: <T >( ) ]
1260
+ fn rename_sub( origin, sub: <T :: Lookup as StaticLookup >:: Source , data: Data ) {
1261
+ let sender = ensure_signed( origin) ?;
1262
+ let sub = T :: Lookup :: lookup( sub) ?;
1263
+ ensure!( IdentityOf :: <T >:: contains_key( & sender) , Error :: <T >:: NoIdentity ) ;
1264
+ ensure!( SuperOf :: <T >:: get( & sub) . map_or( false , |x| x. 0 == sender) , Error :: <T >:: NotOwned ) ;
1265
+ SuperOf :: <T >:: insert( & sub, ( sender, data) ) ;
1266
+ }
1267
+
1268
+ /// Remove the given account from the sender's subs.
1269
+ ///
1270
+ /// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated
1271
+ /// to the sender.
1272
+ ///
1273
+ /// The dispatch origin for this call must be _Signed_ and the sender must have a registered
1274
+ /// sub identity of `sub`.
1275
+ #[ weight = weight_for:: remove_sub:: <T >(
1276
+ T :: MaxSubAccounts :: get( ) . into( ) , // S
1277
+ ) ]
1278
+ fn remove_sub( origin, sub: <T :: Lookup as StaticLookup >:: Source ) {
1279
+ let sender = ensure_signed( origin) ?;
1280
+ ensure!( IdentityOf :: <T >:: contains_key( & sender) , Error :: <T >:: NoIdentity ) ;
1281
+ let sub = T :: Lookup :: lookup( sub) ?;
1282
+ let ( sup, _) = SuperOf :: <T >:: get( & sub) . ok_or( Error :: <T >:: NotSub ) ?;
1283
+ ensure!( sup == sender, Error :: <T >:: NotOwned ) ;
1284
+ SuperOf :: <T >:: remove( & sub) ;
1285
+ SubsOf :: <T >:: mutate( & sup, |( ref mut subs_deposit, ref mut sub_ids) | {
1286
+ sub_ids. retain( |x| x != & sub) ;
1287
+ let deposit = T :: SubAccountDeposit :: get( ) . min( * subs_deposit) ;
1288
+ * subs_deposit -= deposit;
1289
+ let _ = T :: Currency :: unreserve( & sender, deposit) ;
1290
+ Self :: deposit_event( RawEvent :: SubIdentityRemoved ( sub, sender, deposit) ) ;
1291
+ } ) ;
1292
+ }
1293
+
1294
+ /// Remove the sender as a sub-account.
1295
+ ///
1296
+ /// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated
1297
+ /// to the sender (*not* the original depositor).
1298
+ ///
1299
+ /// The dispatch origin for this call must be _Signed_ and the sender must have a registered
1300
+ /// super-identity.
1301
+ ///
1302
+ /// NOTE: This should not normally be used, but is provided in the case that the non-
1303
+ /// controller of an account is maliciously registered as a sub-account.
1304
+ #[ weight = weight_for:: quit_sub:: <T >(
1305
+ T :: MaxSubAccounts :: get( ) . into( ) , // S
1306
+ ) ]
1307
+ fn quit_sub( origin) {
1308
+ let sender = ensure_signed( origin) ?;
1309
+ let ( sup, _) = SuperOf :: <T >:: take( & sender) . ok_or( Error :: <T >:: NotSub ) ?;
1310
+ SubsOf :: <T >:: mutate( & sup, |( ref mut subs_deposit, ref mut sub_ids) | {
1311
+ sub_ids. retain( |x| x != & sender) ;
1312
+ let deposit = T :: SubAccountDeposit :: get( ) . min( * subs_deposit) ;
1313
+ * subs_deposit -= deposit;
1314
+ let _ = T :: Currency :: repatriate_reserved( & sup, & sender, deposit, BalanceStatus :: Free ) ;
1315
+ Self :: deposit_event( RawEvent :: SubIdentityRevoked ( sender, sup. clone( ) , deposit) ) ;
1316
+ } ) ;
1317
+ }
1162
1318
}
1163
1319
}
1164
1320
@@ -1188,7 +1344,7 @@ mod tests {
1188
1344
} ;
1189
1345
1190
1346
impl_outer_origin ! {
1191
- pub enum Origin for Test where system = frame_system { }
1347
+ pub enum Origin for Test where system = frame_system { }
1192
1348
}
1193
1349
1194
1350
#[ derive( Clone , Eq , PartialEq ) ]
@@ -1300,6 +1456,84 @@ mod tests {
1300
1456
}
1301
1457
}
1302
1458
1459
+ fn twenty ( ) -> IdentityInfo {
1460
+ IdentityInfo {
1461
+ display : Data :: Raw ( b"twenty" . to_vec ( ) ) ,
1462
+ legal : Data :: Raw ( b"The Right Ordinal Twenty, Esq." . to_vec ( ) ) ,
1463
+ .. Default :: default ( )
1464
+ }
1465
+ }
1466
+
1467
+ #[ test]
1468
+ fn editing_subaccounts_should_work ( ) {
1469
+ new_test_ext ( ) . execute_with ( || {
1470
+ let data = |x| Data :: Raw ( vec ! [ x; 1 ] ) ;
1471
+
1472
+ assert_noop ! ( Identity :: add_sub( Origin :: signed( 10 ) , 20 , data( 1 ) ) , Error :: <Test >:: NoIdentity ) ;
1473
+
1474
+ assert_ok ! ( Identity :: set_identity( Origin :: signed( 10 ) , ten( ) ) ) ;
1475
+
1476
+ // first sub account
1477
+ assert_ok ! ( Identity :: add_sub( Origin :: signed( 10 ) , 1 , data( 1 ) ) ) ;
1478
+ assert_eq ! ( SuperOf :: <Test >:: get( 1 ) , Some ( ( 10 , data( 1 ) ) ) ) ;
1479
+ assert_eq ! ( Balances :: free_balance( 10 ) , 80 ) ;
1480
+
1481
+ // second sub account
1482
+ assert_ok ! ( Identity :: add_sub( Origin :: signed( 10 ) , 2 , data( 2 ) ) ) ;
1483
+ assert_eq ! ( SuperOf :: <Test >:: get( 1 ) , Some ( ( 10 , data( 1 ) ) ) ) ;
1484
+ assert_eq ! ( SuperOf :: <Test >:: get( 2 ) , Some ( ( 10 , data( 2 ) ) ) ) ;
1485
+ assert_eq ! ( Balances :: free_balance( 10 ) , 70 ) ;
1486
+
1487
+ // third sub account is too many
1488
+ assert_noop ! ( Identity :: add_sub( Origin :: signed( 10 ) , 3 , data( 3 ) ) , Error :: <Test >:: TooManySubAccounts ) ;
1489
+
1490
+ // rename first sub account
1491
+ assert_ok ! ( Identity :: rename_sub( Origin :: signed( 10 ) , 1 , data( 11 ) ) ) ;
1492
+ assert_eq ! ( SuperOf :: <Test >:: get( 1 ) , Some ( ( 10 , data( 11 ) ) ) ) ;
1493
+ assert_eq ! ( SuperOf :: <Test >:: get( 2 ) , Some ( ( 10 , data( 2 ) ) ) ) ;
1494
+ assert_eq ! ( Balances :: free_balance( 10 ) , 70 ) ;
1495
+
1496
+ // remove first sub account
1497
+ assert_ok ! ( Identity :: remove_sub( Origin :: signed( 10 ) , 1 ) ) ;
1498
+ assert_eq ! ( SuperOf :: <Test >:: get( 1 ) , None ) ;
1499
+ assert_eq ! ( SuperOf :: <Test >:: get( 2 ) , Some ( ( 10 , data( 2 ) ) ) ) ;
1500
+ assert_eq ! ( Balances :: free_balance( 10 ) , 80 ) ;
1501
+
1502
+ // add third sub account
1503
+ assert_ok ! ( Identity :: add_sub( Origin :: signed( 10 ) , 3 , data( 3 ) ) ) ;
1504
+ assert_eq ! ( SuperOf :: <Test >:: get( 1 ) , None ) ;
1505
+ assert_eq ! ( SuperOf :: <Test >:: get( 2 ) , Some ( ( 10 , data( 2 ) ) ) ) ;
1506
+ assert_eq ! ( SuperOf :: <Test >:: get( 3 ) , Some ( ( 10 , data( 3 ) ) ) ) ;
1507
+ assert_eq ! ( Balances :: free_balance( 10 ) , 70 ) ;
1508
+ } ) ;
1509
+ }
1510
+
1511
+ #[ test]
1512
+ fn resolving_subaccount_ownership_works ( ) {
1513
+ new_test_ext ( ) . execute_with ( || {
1514
+ let data = |x| Data :: Raw ( vec ! [ x; 1 ] ) ;
1515
+
1516
+ assert_ok ! ( Identity :: set_identity( Origin :: signed( 10 ) , ten( ) ) ) ;
1517
+ assert_ok ! ( Identity :: set_identity( Origin :: signed( 20 ) , twenty( ) ) ) ;
1518
+
1519
+ // 10 claims 1 as a subaccount
1520
+ assert_ok ! ( Identity :: add_sub( Origin :: signed( 10 ) , 1 , data( 1 ) ) ) ;
1521
+ assert_eq ! ( Balances :: free_balance( 1 ) , 10 ) ;
1522
+ assert_eq ! ( Balances :: free_balance( 10 ) , 80 ) ;
1523
+ assert_eq ! ( Balances :: reserved_balance( 10 ) , 20 ) ;
1524
+ // 20 cannot claim 1 now
1525
+ assert_noop ! ( Identity :: add_sub( Origin :: signed( 20 ) , 1 , data( 1 ) ) , Error :: <Test >:: AlreadyClaimed ) ;
1526
+ // 1 wants to be with 20 so it quits from 10
1527
+ assert_ok ! ( Identity :: quit_sub( Origin :: signed( 1 ) ) ) ;
1528
+ // 1 gets the 10 that 10 paid.
1529
+ assert_eq ! ( Balances :: free_balance( 1 ) , 20 ) ;
1530
+ assert_eq ! ( Balances :: free_balance( 10 ) , 80 ) ;
1531
+ assert_eq ! ( Balances :: reserved_balance( 10 ) , 10 ) ;
1532
+ // 20 can claim 1 now
1533
+ assert_ok ! ( Identity :: add_sub( Origin :: signed( 20 ) , 1 , data( 1 ) ) ) ;
1534
+ } ) ;
1535
+ }
1536
+
1303
1537
#[ test]
1304
1538
fn trailing_zeros_decodes_into_default_data ( ) {
1305
1539
let encoded = Data :: Raw ( b"Hello" . to_vec ( ) ) . encode ( ) ;
0 commit comments