@@ -7,13 +7,19 @@ use serde::{Deserialize, Serialize};
7
7
use slog:: { error, Logger } ;
8
8
use std:: { any:: Any , collections:: HashMap , sync:: Arc } ;
9
9
10
- use adapter:: { client:: Locked , Adapter , Dummy } ;
10
+ use adapter:: {
11
+ client:: Locked ,
12
+ util:: { get_balance_leaf, get_signable_state_root} ,
13
+ Adapter , Dummy ,
14
+ } ;
11
15
use primitives:: {
12
16
balances:: { Balances , CheckedState , UncheckedState } ,
17
+ merkle_tree:: MerkleTree ,
13
18
sentry:: {
14
19
channel_list:: { ChannelListQuery , ChannelListResponse } ,
15
- AccountingResponse , AllSpendersQuery , AllSpendersResponse , ChannelPayRequest , LastApproved ,
16
- LastApprovedQuery , LastApprovedResponse , SpenderResponse , SuccessResponse ,
20
+ AccountingResponse , AllSpendersQuery , AllSpendersResponse , ChannelPayRequest ,
21
+ GetLeafResponse , LastApproved , LastApprovedQuery , LastApprovedResponse , SpenderResponse ,
22
+ SuccessResponse ,
17
23
} ,
18
24
spender:: { Spendable , Spender } ,
19
25
validator:: NewState ,
@@ -32,7 +38,7 @@ use crate::{
32
38
DbPool ,
33
39
} ,
34
40
response:: ResponseError ,
35
- routes:: campaign:: fetch_campaign_ids_for_channel,
41
+ routes:: { campaign:: fetch_campaign_ids_for_channel, routers :: LeafFor } ,
36
42
Application , Auth ,
37
43
} ;
38
44
@@ -498,6 +504,73 @@ pub async fn channel_payout<C: Locked + 'static>(
498
504
Ok ( Json ( SuccessResponse { success : true } ) )
499
505
}
500
506
507
+ /// GET `/v5/channel/0xXXX.../get-leaf` requests
508
+ ///
509
+ /// # Routes:
510
+ ///
511
+ /// - GET `/v5/channel/:id/get-leaf/spender/:addr`
512
+ /// - GET `/v5/channel/:id/get-leaf/earner/:addr`
513
+ ///
514
+ /// Response: [`GetLeafResponse`]
515
+ pub async fn get_leaf < C : Locked + ' static > (
516
+ Extension ( app) : Extension < Arc < Application < C > > > ,
517
+ Extension ( channel_context) : Extension < ChainOf < Channel > > ,
518
+ Extension ( leaf_for) : Extension < LeafFor > ,
519
+ Path ( params) : Path < ( ChannelId , Address ) > ,
520
+ ) -> Result < Json < GetLeafResponse > , ResponseError > {
521
+ let channel = channel_context. context ;
522
+
523
+ let approve_state = latest_approve_state ( & app. pool , & channel)
524
+ . await ?
525
+ . ok_or ( ResponseError :: NotFound ) ?;
526
+
527
+ let state_root = approve_state. msg . state_root . clone ( ) ;
528
+
529
+ let new_state = latest_new_state ( & app. pool , & channel, & state_root)
530
+ . await ?
531
+ . ok_or_else ( || ResponseError :: BadRequest ( "No NewState message for spender" . to_string ( ) ) ) ?;
532
+
533
+ let addr = params. 1 ;
534
+
535
+ let element = match leaf_for {
536
+ LeafFor :: Spender => {
537
+ let amount = new_state
538
+ . msg
539
+ . balances
540
+ . spenders
541
+ . get ( & addr)
542
+ . ok_or ( ResponseError :: NotFound ) ?;
543
+
544
+ get_balance_leaf (
545
+ true ,
546
+ & addr,
547
+ & amount. to_precision ( channel_context. token . precision . get ( ) ) ,
548
+ ) ?
549
+ }
550
+ LeafFor :: Earner => {
551
+ let amount = new_state
552
+ . msg
553
+ . balances
554
+ . earners
555
+ . get ( & addr)
556
+ . ok_or ( ResponseError :: NotFound ) ?;
557
+
558
+ get_balance_leaf (
559
+ false ,
560
+ & addr,
561
+ & amount. to_precision ( channel_context. token . precision . get ( ) ) ,
562
+ ) ?
563
+ }
564
+ } ;
565
+ let merkle_tree = MerkleTree :: new ( & [ element] ) ?;
566
+
567
+ let signable_state_root = get_signable_state_root ( channel. id ( ) . as_bytes ( ) , & merkle_tree. root ( ) ) ;
568
+
569
+ let merkle_proof = hex:: encode ( signable_state_root) ;
570
+
571
+ Ok ( Json ( GetLeafResponse { merkle_proof } ) )
572
+ }
573
+
501
574
/// POST `/v5/channel/dummy-deposit` request
502
575
///
503
576
/// Full details about the route's API and intend can be found in the [`routes`](crate::routes#post-v5channeldummy-deposit-auth-required) module
@@ -669,26 +742,30 @@ pub mod validator_message {
669
742
670
743
#[ cfg( test) ]
671
744
mod test {
672
- use std:: str:: FromStr ;
673
-
745
+ use super :: * ;
746
+ use crate :: {
747
+ db:: {
748
+ insert_campaign, insert_channel, validator_message:: insert_validator_message,
749
+ CampaignRemaining ,
750
+ } ,
751
+ test_util:: setup_dummy_app,
752
+ } ;
674
753
use adapter:: {
675
754
ethereum:: test_util:: { GANACHE_INFO_1 , GANACHE_INFO_1337 } ,
755
+ prelude:: Unlocked ,
676
756
primitives:: Deposit as AdapterDeposit ,
677
757
} ;
678
758
use primitives:: {
759
+ balances:: UncheckedState ,
679
760
channel:: Nonce ,
680
761
test_util:: {
681
762
ADVERTISER , CREATOR , DUMMY_CAMPAIGN , FOLLOWER , GUARDIAN , IDS , LEADER , LEADER_2 ,
682
763
PUBLISHER , PUBLISHER_2 ,
683
764
} ,
765
+ validator:: { ApproveState , MessageTypes , NewState } ,
684
766
BigNum , ChainId , Deposit , UnifiedMap , ValidatorId ,
685
767
} ;
686
-
687
- use super :: * ;
688
- use crate :: {
689
- db:: { insert_campaign, insert_channel, CampaignRemaining } ,
690
- test_util:: setup_dummy_app,
691
- } ;
768
+ use std:: str:: FromStr ;
692
769
693
770
#[ tokio:: test]
694
771
async fn create_and_fetch_spendable ( ) {
@@ -1407,4 +1484,102 @@ mod test {
1407
1484
) ;
1408
1485
}
1409
1486
}
1487
+
1488
+ #[ tokio:: test]
1489
+ async fn get_spender_and_earner_leafs ( ) {
1490
+ let mut balances: Balances < CheckedState > = Balances :: new ( ) ;
1491
+ balances
1492
+ . spend ( * ADVERTISER , * PUBLISHER , UnifiedNum :: from_u64 ( 1000 ) )
1493
+ . expect ( "should spend" ) ;
1494
+ balances
1495
+ . spend ( * ADVERTISER , * PUBLISHER_2 , UnifiedNum :: from_u64 ( 1000 ) )
1496
+ . expect ( "should spend" ) ;
1497
+ balances
1498
+ . spend ( * CREATOR , * PUBLISHER , UnifiedNum :: from_u64 ( 1000 ) )
1499
+ . expect ( "should spend" ) ;
1500
+ balances
1501
+ . spend ( * CREATOR , * PUBLISHER_2 , UnifiedNum :: from_u64 ( 1000 ) )
1502
+ . expect ( "should spend" ) ;
1503
+
1504
+ let app_guard = setup_dummy_app ( ) . await ;
1505
+ let app = Extension ( Arc :: new ( app_guard. app . clone ( ) ) ) ;
1506
+
1507
+ let channel_context = Extension (
1508
+ app. config
1509
+ . find_chain_of ( DUMMY_CAMPAIGN . channel . token )
1510
+ . expect ( "Dummy channel Token should be present in config!" )
1511
+ . with ( DUMMY_CAMPAIGN . channel ) ,
1512
+ ) ;
1513
+ let channel = channel_context. context ;
1514
+
1515
+ insert_channel ( & app. pool , & channel_context)
1516
+ . await
1517
+ . expect ( "should insert channel" ) ;
1518
+
1519
+ // Setting up the validator messages
1520
+ let state_root =
1521
+ "b1a4fc6c1a1e1ab908a487e504006edcebea297f61b4b8ce6cad3b29e29454cc" . to_string ( ) ;
1522
+ let signature = app
1523
+ . adapter
1524
+ . clone ( )
1525
+ . unlock ( )
1526
+ . expect ( "should unlock" )
1527
+ . sign ( & state_root. clone ( ) )
1528
+ . expect ( "should sign" ) ;
1529
+ let new_state: NewState < UncheckedState > = NewState {
1530
+ state_root : state_root. clone ( ) ,
1531
+ signature : signature. clone ( ) ,
1532
+ balances : balances. into_unchecked ( ) ,
1533
+ } ;
1534
+ let approve_state = ApproveState {
1535
+ state_root,
1536
+ signature,
1537
+ is_healthy : true ,
1538
+ } ;
1539
+
1540
+ insert_validator_message (
1541
+ & app. pool ,
1542
+ & channel,
1543
+ & channel. leader ,
1544
+ & MessageTypes :: NewState ( new_state) ,
1545
+ )
1546
+ . await
1547
+ . expect ( "Should insert NewState msg" ) ;
1548
+ insert_validator_message (
1549
+ & app. pool ,
1550
+ & channel,
1551
+ & channel. follower ,
1552
+ & MessageTypes :: ApproveState ( approve_state) ,
1553
+ )
1554
+ . await
1555
+ . expect ( "Should insert NewState msg" ) ;
1556
+
1557
+ // hardcoded proofs
1558
+ let spender_proof =
1559
+ "8ea7760ca2dbbe00673372afbf8b05048717ce8a305f1f853afac8c244182e0c" . to_string ( ) ;
1560
+ let earner_proof =
1561
+ "dc94141cb41550df047ba3a965ce36d98eb6098eb952ca3cb6fd9682e5810b51" . to_string ( ) ;
1562
+
1563
+ // call functions
1564
+ let spender_leaf = get_leaf (
1565
+ app. clone ( ) ,
1566
+ channel_context. clone ( ) ,
1567
+ Extension ( LeafFor :: Spender ) ,
1568
+ Path ( ( channel. id ( ) , * ADVERTISER ) ) ,
1569
+ )
1570
+ . await
1571
+ . expect ( "should get spender leaf" ) ;
1572
+ let earner_leaf = get_leaf (
1573
+ app. clone ( ) ,
1574
+ channel_context. clone ( ) ,
1575
+ Extension ( LeafFor :: Earner ) ,
1576
+ Path ( ( channel. id ( ) , * PUBLISHER ) ) ,
1577
+ )
1578
+ . await
1579
+ . expect ( "should get earner leaf" ) ;
1580
+
1581
+ // compare results
1582
+ assert_eq ! ( spender_proof, spender_leaf. merkle_proof) ;
1583
+ assert_eq ! ( earner_proof, earner_leaf. merkle_proof) ;
1584
+ }
1410
1585
}
0 commit comments