Skip to content

Commit 4044b10

Browse files
committed
added get_leaf route + tests
1 parent 314b871 commit 4044b10

File tree

3 files changed

+244
-7
lines changed

3 files changed

+244
-7
lines changed

primitives/src/sentry.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,12 @@ pub struct ValidationErrorResponse {
722722
pub validation: Vec<String>,
723723
}
724724

725+
#[derive(Serialize, Deserialize, Debug)]
726+
#[serde(rename_all = "camelCase")]
727+
pub struct GetLeafResponse {
728+
pub merkle_proof: String,
729+
}
730+
725731
/// Request body for posting new [`Event`]s to a [`Campaign`](crate::Campaign).
726732
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
727733
#[serde(rename_all = "camelCase")]

sentry/src/routes/channel.rs

Lines changed: 231 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,22 @@ use crate::{
1818
routes::{campaign::fetch_campaign_ids_for_channel, routers::RouteParams},
1919
Application, Auth,
2020
};
21-
use adapter::{client::Locked, Adapter, Dummy};
21+
use adapter::{
22+
client::Locked,
23+
util::{get_balance_leaf, get_signable_state_root},
24+
Adapter, Dummy,
25+
};
2226
use axum::{extract::Path, Extension, Json};
2327
use futures::future::try_join_all;
2428
use hyper::{Body, Request, Response};
2529
use primitives::{
2630
balances::{Balances, CheckedState, UncheckedState},
31+
merkle_tree::MerkleTree,
2732
sentry::{
2833
channel_list::{ChannelListQuery, ChannelListResponse},
29-
AccountingResponse, AllSpendersQuery, AllSpendersResponse, ChannelPayRequest, LastApproved,
30-
LastApprovedQuery, LastApprovedResponse, SpenderResponse, SuccessResponse,
34+
AccountingResponse, AllSpendersQuery, AllSpendersResponse, ChannelPayRequest,
35+
GetLeafResponse, LastApproved, LastApprovedQuery, LastApprovedResponse, SpenderResponse,
36+
SuccessResponse,
3137
},
3238
spender::{Spendable, Spender},
3339
validator::NewState,
@@ -799,6 +805,106 @@ pub async fn channel_payout_axum<C: Locked + 'static>(
799805
Ok(Json(SuccessResponse { success: true }))
800806
}
801807

808+
pub async fn get_spender_leaf<C: Locked + 'static>(
809+
Extension(app): Extension<Arc<Application<C>>>,
810+
Extension(channel_context): Extension<ChainOf<Channel>>,
811+
Path(params): Path<(ChannelId, Address)>,
812+
) -> Result<Json<GetLeafResponse>, ResponseError> {
813+
let channel = channel_context.context;
814+
815+
let approve_state = match latest_approve_state(&app.pool, &channel).await? {
816+
Some(approve_state) => approve_state,
817+
None => {
818+
return Err(ResponseError::BadRequest(
819+
"No ApproveState message for spender".to_string(),
820+
))
821+
}
822+
};
823+
824+
let state_root = approve_state.msg.state_root.clone();
825+
826+
let new_state = latest_new_state(&app.pool, &channel, &state_root)
827+
.await?
828+
.ok_or(ResponseError::BadRequest(
829+
"No NewState message for spender".to_string(),
830+
))?;
831+
832+
let spender = params.1;
833+
let amount = new_state
834+
.msg
835+
.balances
836+
.spenders
837+
.get(&spender)
838+
.ok_or(ResponseError::BadRequest(
839+
"No balance entry for spender!".to_string(),
840+
))?;
841+
let element = get_balance_leaf(
842+
true,
843+
&spender,
844+
&amount.to_precision(channel_context.token.precision.get()),
845+
)
846+
.map_err(|err| ResponseError::BadRequest(err.to_string()))?;
847+
848+
let merkle_tree =
849+
MerkleTree::new(&[element]).map_err(|err| ResponseError::BadRequest(err.to_string()))?;
850+
851+
let signable_state_root = get_signable_state_root(&*channel.id(), &merkle_tree.root());
852+
853+
let res = hex::encode(signable_state_root);
854+
855+
Ok(Json(GetLeafResponse { merkle_proof: res }))
856+
}
857+
858+
pub async fn get_earner_leaf<C: Locked + 'static>(
859+
Extension(app): Extension<Arc<Application<C>>>,
860+
Extension(channel_context): Extension<ChainOf<Channel>>,
861+
Path(params): Path<(ChannelId, Address)>,
862+
) -> Result<Json<GetLeafResponse>, ResponseError> {
863+
let channel = channel_context.context;
864+
865+
let approve_state = match latest_approve_state(&app.pool, &channel).await? {
866+
Some(approve_state) => approve_state,
867+
None => {
868+
return Err(ResponseError::BadRequest(
869+
"No ApproveState message for earner".to_string(),
870+
))
871+
}
872+
};
873+
874+
let state_root = approve_state.msg.state_root.clone();
875+
876+
let new_state = latest_new_state(&app.pool, &channel, &state_root)
877+
.await?
878+
.ok_or(ResponseError::BadRequest(
879+
"No NewState message for earner".to_string(),
880+
))?;
881+
882+
let earner = params.1;
883+
let amount = new_state
884+
.msg
885+
.balances
886+
.earners
887+
.get(&earner)
888+
.ok_or(ResponseError::BadRequest(
889+
"No balance entry for earner!".to_string(),
890+
))?;
891+
let element = get_balance_leaf(
892+
false,
893+
&earner,
894+
&amount.to_precision(channel_context.token.precision.get()),
895+
)
896+
.map_err(|err| ResponseError::BadRequest(err.to_string()))?;
897+
898+
let merkle_tree =
899+
MerkleTree::new(&[element]).map_err(|err| ResponseError::BadRequest(err.to_string()))?;
900+
901+
let signable_state_root = get_signable_state_root(&*channel.id(), &merkle_tree.root());
902+
903+
let res = hex::encode(signable_state_root);
904+
905+
Ok(Json(GetLeafResponse { merkle_proof: res }))
906+
}
907+
802908
/// POST `/v5/channel/0xXXX.../pay` request
803909
///
804910
/// Body: [`ChannelPayRequest`]
@@ -1163,20 +1269,25 @@ pub mod validator_message {
11631269
mod test {
11641270
use super::*;
11651271
use crate::{
1166-
db::{insert_campaign, insert_channel, CampaignRemaining},
1272+
db::{
1273+
insert_campaign, insert_channel, validator_message::insert_validator_message,
1274+
CampaignRemaining,
1275+
},
11671276
test_util::setup_dummy_app,
11681277
};
11691278
use adapter::{
11701279
ethereum::test_util::{GANACHE_INFO_1, GANACHE_INFO_1337},
11711280
primitives::Deposit as AdapterDeposit,
11721281
};
11731282
use primitives::{
1283+
balances::UncheckedState,
11741284
channel::Nonce,
11751285
test_util::{
11761286
ADVERTISER, CREATOR, DUMMY_CAMPAIGN, FOLLOWER, GUARDIAN, IDS, LEADER, LEADER_2,
11771287
PUBLISHER, PUBLISHER_2,
11781288
},
1179-
BigNum, ChainId, Deposit, UnifiedMap, ValidatorId,
1289+
validator::{ApproveState, MessageTypes, NewState},
1290+
BigNum, ChainId, Deposit, ToETHChecksum, UnifiedMap, ValidatorId,
11801291
};
11811292

11821293
#[tokio::test]
@@ -1897,4 +2008,119 @@ mod test {
18972008
);
18982009
}
18992010
}
2011+
2012+
#[tokio::test]
2013+
async fn get_spender_and_earner_leafs() {
2014+
let mut balances: Balances<CheckedState> = Balances::new();
2015+
balances
2016+
.spend(*ADVERTISER, *PUBLISHER, UnifiedNum::from_u64(1000))
2017+
.expect("should spend");
2018+
balances
2019+
.spend(*ADVERTISER, *PUBLISHER_2, UnifiedNum::from_u64(1000))
2020+
.expect("should spend");
2021+
balances
2022+
.spend(*CREATOR, *PUBLISHER, UnifiedNum::from_u64(1000))
2023+
.expect("should spend");
2024+
balances
2025+
.spend(*CREATOR, *PUBLISHER_2, UnifiedNum::from_u64(1000))
2026+
.expect("should spend");
2027+
2028+
let app_guard = setup_dummy_app().await;
2029+
let app = Extension(Arc::new(app_guard.app.clone()));
2030+
2031+
let channel_context = Extension(
2032+
app.config
2033+
.find_chain_of(DUMMY_CAMPAIGN.channel.token)
2034+
.expect("Dummy channel Token should be present in config!")
2035+
.with(DUMMY_CAMPAIGN.channel),
2036+
);
2037+
let channel = channel_context.context;
2038+
2039+
insert_channel(&app.pool, &channel_context)
2040+
.await
2041+
.expect("should insert channel");
2042+
2043+
// Setting up the validator messages
2044+
let state_root = balances
2045+
.encode(channel.id(), channel_context.token.precision.get())
2046+
.expect("should encode");
2047+
let new_state: NewState<UncheckedState> = NewState {
2048+
state_root: state_root.clone(),
2049+
signature: IDS[&*LEADER].to_checksum(),
2050+
balances: balances.into_unchecked(),
2051+
};
2052+
let approve_state = ApproveState {
2053+
state_root,
2054+
signature: IDS[&*FOLLOWER].to_checksum(),
2055+
is_healthy: true,
2056+
};
2057+
2058+
insert_validator_message(
2059+
&app.pool,
2060+
&channel,
2061+
&channel.leader,
2062+
&MessageTypes::NewState(new_state),
2063+
)
2064+
.await
2065+
.expect("Should insert NewState msg");
2066+
insert_validator_message(
2067+
&app.pool,
2068+
&channel,
2069+
&channel.follower,
2070+
&MessageTypes::ApproveState(approve_state),
2071+
)
2072+
.await
2073+
.expect("Should insert NewState msg");
2074+
2075+
// generate proofs
2076+
let spender_proof = {
2077+
let element = get_balance_leaf(
2078+
true,
2079+
&*ADVERTISER,
2080+
&UnifiedNum::from_u64(2_000).to_precision(channel_context.token.precision.get()),
2081+
)
2082+
.expect("should get");
2083+
2084+
let merkle_tree = MerkleTree::new(&[element]).expect("Should build MerkleTree");
2085+
2086+
let signable_state_root = get_signable_state_root(&*channel.id(), &merkle_tree.root());
2087+
2088+
hex::encode(signable_state_root)
2089+
};
2090+
2091+
let earner_proof = {
2092+
let element = get_balance_leaf(
2093+
false,
2094+
&*PUBLISHER,
2095+
&UnifiedNum::from_u64(2_000).to_precision(channel_context.token.precision.get()),
2096+
)
2097+
.expect("should get balance leaf");
2098+
2099+
let merkle_tree = MerkleTree::new(&[element]).expect("Should build MerkleTree");
2100+
2101+
let signable_state_root = get_signable_state_root(&*channel.id(), &merkle_tree.root());
2102+
2103+
hex::encode(signable_state_root)
2104+
};
2105+
2106+
// call functions
2107+
let spender_leaf = get_spender_leaf(
2108+
app.clone(),
2109+
channel_context.clone(),
2110+
Path((channel.id(), *ADVERTISER)),
2111+
)
2112+
.await
2113+
.expect("should get spender leaf");
2114+
let earner_leaf = get_earner_leaf(
2115+
app.clone(),
2116+
channel_context.clone(),
2117+
Path((channel.id(), *PUBLISHER)),
2118+
)
2119+
.await
2120+
.expect("should get earner leaf");
2121+
2122+
// compare results
2123+
assert_eq!(spender_proof, spender_leaf.merkle_proof);
2124+
assert_eq!(earner_proof, earner_leaf.merkle_proof);
2125+
}
19002126
}

sentry/src/routes/routers.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ use crate::{
2929
channel::{
3030
add_spender_leaf, add_spender_leaf_axum, channel_dummy_deposit, channel_list,
3131
channel_payout, get_accounting_for_channel, get_accounting_for_channel_axum,
32-
get_all_spender_limits, get_all_spender_limits_axum, get_spender_limits,
33-
get_spender_limits_axum, last_approved, last_approved_axum,
32+
get_all_spender_limits, get_all_spender_limits_axum, get_earner_leaf, get_spender_leaf,
33+
get_spender_limits, get_spender_limits_axum, last_approved, last_approved_axum,
3434
validator_message::{
3535
create_validator_messages, extract_params, list_validator_messages,
3636
},
@@ -146,6 +146,10 @@ pub fn channels_router_axum<C: Locked + 'static>() -> Router {
146146
ServiceBuilder::new().layer(middleware::from_fn(authentication_required::<C, _>)),
147147
);
148148

149+
let get_leaf_routes = Router::new()
150+
.route("/spender/:addr", get(get_spender_leaf::<C>))
151+
.route("/earner/:addr", get(get_earner_leaf::<C>));
152+
149153
let channel_routes = Router::new()
150154
.route(
151155
"/pay",
@@ -155,6 +159,7 @@ pub fn channels_router_axum<C: Locked + 'static>() -> Router {
155159
.route("/accounting", get(get_accounting_for_channel_axum::<C>))
156160
.route("/last-approved", get(last_approved_axum::<C>))
157161
.nest("/spender", spender_routes)
162+
.nest("/get-leaf", get_leaf_routes)
158163
.layer(
159164
// keeps the order from top to bottom!
160165
ServiceBuilder::new()

0 commit comments

Comments
 (0)