Skip to content

Commit edf12f5

Browse files
committed
Update the check in and add a "memo" field to the check in payload
1 parent dfee46d commit edf12f5

File tree

8 files changed

+135
-97
lines changed

8 files changed

+135
-97
lines changed

cli/src/ledger_canister_client.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl LedgerCanister {
7474

7575
pub fn list_functions_queries(&self) -> Vec<String> {
7676
vec![
77-
"get_np_check_in_nonce".to_string(),
77+
"get_check_in_nonce".to_string(),
7878
"data_fetch".to_string(),
7979
"metadata".to_string(),
8080
"get_logs_debug".to_string(),
@@ -126,12 +126,12 @@ impl LedgerCanister {
126126
Decode!(response.as_slice(), ResultString).map_err(|e| e.to_string())?
127127
}
128128

129-
pub async fn get_np_check_in_nonce(&self) -> Vec<u8> {
129+
pub async fn get_check_in_nonce(&self) -> Vec<u8> {
130130
let args = Encode!(&()).expect("Failed to encode args");
131131
let response = self
132-
.call_query("get_np_check_in_nonce", &args)
132+
.call_query("get_check_in_nonce", &args)
133133
.await
134-
.expect("Failed to call get_np_check_in_nonce");
134+
.expect("Failed to call get_check_in_nonce");
135135
Decode!(response.as_slice(), Vec<u8>).expect("Failed to decode response")
136136
}
137137

common/src/dcc_identity.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::{IcrcCompatibleAccount, MAX_PUBKEY_BYTES, MINTING_ACCOUNT_PRINCIPAL};
1+
use crate::{
2+
IcrcCompatibleAccount, ED25519_SIGNATURE_LENGTH, MAX_PUBKEY_BYTES, MINTING_ACCOUNT_PRINCIPAL,
3+
};
24
use ed25519_dalek::ed25519::Error as DalekError;
35
use ed25519_dalek::pkcs8::spki::der::pem::LineEnding;
46
use ed25519_dalek::pkcs8::{EncodePrivateKey, EncodePublicKey};
@@ -164,6 +166,9 @@ impl DccIdentity {
164166
}
165167

166168
pub fn verify_bytes(&self, data: &[u8], signature_bytes: &[u8]) -> Result<(), CryptoError> {
169+
if signature_bytes.len() != ED25519_SIGNATURE_LENGTH {
170+
return Err("Invalid signature".into());
171+
}
167172
let signature = Signature::from_bytes(slice_to_64_bytes_array(signature_bytes)?);
168173
self.verify(data, &signature)
169174
}

common/src/profiles.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
amount_as_string, charge_fees_to_account_no_bump_reputation, info, reputation_get,
3-
reward_e9s_per_block, Balance, DccIdentity, ED25519_SIGNATURE_LENGTH, LABEL_NP_PROFILE,
4-
MAX_NP_PROFILE_BYTES, MAX_PUBKEY_BYTES,
3+
reward_e9s_per_block, Balance, DccIdentity, LABEL_NP_PROFILE, MAX_NP_PROFILE_BYTES,
4+
MAX_PUBKEY_BYTES,
55
};
66
use borsh::{BorshDeserialize, BorshSerialize};
77
use candid::Principal;
@@ -40,9 +40,6 @@ impl UpdateProfilePayload {
4040
pub fn verify_signature(&self, dcc_id: &DccIdentity) -> Result<(), String> {
4141
match self {
4242
UpdateProfilePayload::V1(payload) => {
43-
if payload.signature.len() != ED25519_SIGNATURE_LENGTH {
44-
return Err("Invalid signature".to_string());
45-
}
4643
if payload.profile_bytes.len() > MAX_NP_PROFILE_BYTES {
4744
return Err("Profile payload too long".to_string());
4845
}

common/src/rewards.rs

Lines changed: 75 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,59 @@
11
use crate::platform_specific::get_timestamp_ns;
2-
use crate::MAX_PUBKEY_BYTES;
32
use crate::{
4-
account_balance_get, account_registration_fee_e9s, account_transfers::FundsTransfer,
5-
amount_as_string, charge_fees_to_account_no_bump_reputation, get_account_from_pubkey, info,
3+
account_balance_get, account_transfers::FundsTransfer, amount_as_string,
4+
charge_fees_to_account_no_bump_reputation, get_account_from_pubkey, info,
65
ledger_funds_transfer, Balance, DccIdentity, TransferError, BLOCK_INTERVAL_SECS,
76
DC_TOKEN_DECIMALS_DIV, FIRST_BLOCK_TIMESTAMP_NS, KEY_LAST_REWARD_DISTRIBUTION_TS,
8-
LABEL_NP_CHECK_IN, LABEL_NP_REGISTER, LABEL_REWARD_DISTRIBUTION, MINTING_ACCOUNT,
9-
REWARD_HALVING_AFTER_BLOCKS,
7+
LABEL_NP_CHECK_IN, LABEL_NP_REGISTER, LABEL_REWARD_DISTRIBUTION, MEMO_BYTES_MAX,
8+
MINTING_ACCOUNT, REWARD_HALVING_AFTER_BLOCKS,
109
};
11-
use candid::Principal;
12-
use ed25519_dalek::Signature;
10+
use borsh::{BorshDeserialize, BorshSerialize};
1311
#[cfg(target_arch = "wasm32")]
1412
#[allow(unused_imports)]
1513
use ic_cdk::println;
1614
use ledger_map::LedgerMap;
1715
use std::cell::RefCell;
1816

17+
pub fn check_in_fee_e9s() -> Balance {
18+
reward_e9s_per_block() / 100
19+
}
20+
21+
#[derive(BorshSerialize, BorshDeserialize)]
22+
pub struct CheckInPayloadV1 {
23+
memo: String, // Memo can for example be shown on a dashboard, as an arbitrary personal message
24+
nonce_signature: Vec<u8>,
25+
}
26+
27+
#[derive(BorshSerialize, BorshDeserialize)]
28+
pub enum CheckInPayload {
29+
V1(CheckInPayloadV1),
30+
}
31+
32+
impl CheckInPayload {
33+
pub fn new(memo: String, nonce_signature: Vec<u8>) -> CheckInPayload {
34+
CheckInPayload::V1(CheckInPayloadV1 {
35+
memo,
36+
nonce_signature,
37+
})
38+
}
39+
40+
pub fn to_bytes(&self) -> Result<Vec<u8>, std::io::Error> {
41+
borsh::to_vec(self)
42+
}
43+
44+
pub fn memo(&self) -> &str {
45+
match self {
46+
CheckInPayload::V1(record) => &record.memo,
47+
}
48+
}
49+
50+
pub fn nonce_signature(&self) -> &[u8] {
51+
match self {
52+
CheckInPayload::V1(record) => &record.nonce_signature,
53+
}
54+
}
55+
}
56+
1957
fn calc_token_rewards_e9_since_timestamp_ns(last_reward_distribution_ts_ns: u64) -> Balance {
2058
let elapsed_secs_since_reward_distribution =
2159
(get_timestamp_ns().saturating_sub(last_reward_distribution_ts_ns)) / 1_000_000_000;
@@ -170,62 +208,51 @@ pub fn rewards_distribute(ledger: &mut LedgerMap) -> Result<String, TransferErro
170208

171209
pub fn do_node_provider_check_in(
172210
ledger: &mut LedgerMap,
173-
caller: Principal,
174211
pubkey_bytes: Vec<u8>,
212+
memo: String,
175213
nonce_signature: Vec<u8>,
176214
) -> Result<String, String> {
177-
info!("[do_node_provider_check_in]: caller: {}", caller);
215+
let dcc_id = DccIdentity::new_verifying_from_bytes(&pubkey_bytes)?;
216+
info!("[do_node_provider_check_in]: {}", dcc_id);
178217

179-
if pubkey_bytes.len() > MAX_PUBKEY_BYTES {
180-
return Err("Node provider unique id too long".to_string());
218+
// Check the max length of the memo
219+
if memo.len() > MEMO_BYTES_MAX {
220+
return Err(format!(
221+
"Memo too long, max length is {} bytes",
222+
MEMO_BYTES_MAX
223+
));
181224
}
182-
if nonce_signature.len() != 64 {
183-
return Err("Invalid signature".to_string());
184-
}
185-
// Ensure the NP is registered
186-
ledger
187-
.get(LABEL_NP_REGISTER, &pubkey_bytes)
188-
.map_err(|e| e.to_string())?;
189-
let dcc_identity =
190-
DccIdentity::new_verifying_from_bytes(&pubkey_bytes).map_err(|e| e.to_string())?;
191-
info!(
192-
"Check-in of {}, account: {}",
193-
dcc_identity,
194-
get_account_from_pubkey(&pubkey_bytes)
195-
);
196-
let latest_nonce = ledger.get_latest_block_hash();
197-
let signature = Signature::from_slice(&nonce_signature).map_err(|e| e.to_string())?;
198-
info!(
199-
"Checking signature {} against latest nonce: {}",
200-
signature,
201-
hex::encode(&latest_nonce)
202-
);
203-
dcc_identity
204-
.verify(&latest_nonce, &signature)
205-
.expect("Signature didn't verify");
225+
226+
// Check that the NP is already registered
227+
ledger.get(LABEL_NP_REGISTER, &pubkey_bytes)?;
228+
229+
// Verify the signature
230+
dcc_id.verify_bytes(&ledger.get_latest_block_hash(), &nonce_signature)?;
206231

207232
let fees = if ledger.get_blocks_count() > 0 {
208-
let amount = account_registration_fee_e9s();
233+
let amount = check_in_fee_e9s();
209234
info!(
210-
"Charging {} tokens {} for NP check in",
235+
"Charging {} tokens {} for the check in",
211236
amount_as_string(amount as Balance),
212-
dcc_identity.to_ic_principal()
237+
dcc_id.to_ic_principal()
213238
);
214-
charge_fees_to_account_no_bump_reputation(ledger, &dcc_identity, amount as Balance)?;
239+
charge_fees_to_account_no_bump_reputation(ledger, &dcc_id, amount as Balance)?;
215240
amount
216241
} else {
217242
0
218243
};
219244

220-
ledger
221-
.upsert(LABEL_NP_CHECK_IN, pubkey_bytes, nonce_signature)
222-
.map(|_| "ok".to_string())
223-
.map_err(|e| format!("{:?}", e))?;
224-
225-
Ok(format!(
226-
"Signature verified, check in successful. You have been charged {} tokens",
227-
amount_as_string(fees)
228-
))
245+
let payload = CheckInPayload::new(memo, nonce_signature);
246+
let payload_bytes = payload.to_bytes().unwrap();
247+
248+
Ok(ledger
249+
.upsert(LABEL_NP_CHECK_IN, pubkey_bytes, &payload_bytes)
250+
.map(|_| {
251+
format!(
252+
"Signature verified, check in successful. You have been charged {} tokens",
253+
amount_as_string(fees)
254+
)
255+
})?)
229256
}
230257

231258
pub fn rewards_applied_np_count(ledger: &LedgerMap) -> usize {

ic-canister/decent_cloud.did

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -360,31 +360,37 @@ type OfferingRentReply = record {
360360
rent_success: bool; // Short boolean to mark whether the renting was accepted or rejected by the provider
361361
response_text: text; // Thank you note, or similar on success. Reason the request failed on failure.
362362
response_details: text; // Instructions or a link to the detailed instructions: describing next steps, further information, etc.
363-
signature: vec nat8; // Signature of the response, to simplify verification
364363
};
365364

366365
type RefundRequest = record { // TODO
367366
requester_pubkey_bytes: vec nat8; // Who is making this request?
368367
instance_id: text; // instance id for which a refund is requested
369-
signature: vec nat8; // The whole request needs to be signed by the requester's private key
368+
crypto_sig: vec nat8; // The whole request needs to be signed by the requester's private key
370369
};
371370

372371
service : {
373372
// Node Provider (NP) management operations
374-
node_provider_register: (pubkey_bytes: vec nat8, signature: vec nat8) -> (ResultString);
375-
node_provider_check_in: (pubkey_bytes: vec nat8, signature: vec nat8) -> (ResultString);
376-
node_provider_update_profile: (pubkey_bytes: vec nat8, update_profile_payload: vec nat8, signature: vec nat8) -> (ResultString);
377-
node_provider_update_offering: (pubkey_bytes: vec nat8, update_offering_payload: vec nat8, signature: vec nat8) -> (ResultString);
373+
node_provider_register: (pubkey_bytes: vec nat8, crypto_sig: vec nat8) -> (ResultString);
374+
node_provider_update_profile: (pubkey_bytes: vec nat8, update_profile_payload: vec nat8, crypto_sig: vec nat8) -> (ResultString);
375+
node_provider_update_offering: (pubkey_bytes: vec nat8, update_offering_payload: vec nat8, crypto_sig: vec nat8) -> (ResultString);
378376
node_provider_list_checked_in: () -> (ResultString) query;
379377
node_provider_get_profile_by_pubkey_bytes: (vec nat8) -> (opt text) query;
380378
node_provider_get_profile_by_principal: (principal) -> (opt text) query;
381-
get_np_check_in_nonce: () -> (vec nat8) query;
382379
offering_search: (search_query: text) -> (vec OfferingEntry) query;
383-
offering_request: (offering_rent_request_payload: vec nat8, signature: vec nat8) -> (ResultString);
384-
offering_reply: (offering_rent_reply_payload: vec nat8, signature: vec nat8) -> (ResultString);
380+
offering_request: (offering_rent_request_payload: vec nat8, crypto_sig: vec nat8) -> (ResultString);
381+
offering_reply: (offering_rent_reply_payload: vec nat8, crypto_sig: vec nat8) -> (ResultString);
385382

386383
// User management operations
387-
user_register: (pubkey_bytes: vec nat8, signature: vec nat8) -> (ResultString);
384+
user_register: (pubkey_bytes: vec nat8, crypto_sig: vec nat8) -> (ResultString);
385+
386+
// Check-in nonce that needs to be signed and sent for the check in
387+
// Provided here as a reference only, since client will instead use the nonce of the local ledger
388+
get_check_in_nonce: () -> (vec nat8) query;
389+
// Expected arguments:
390+
// - public key of the account (32B)
391+
// - memo, arbitrary text, up to 32B
392+
// - nonce_crypto_sig, cryptographic signature of the latest blockchain nonce
393+
node_provider_check_in: (pubkey_bytes: vec nat8, memo: text, nonce_crypto_sig: vec nat8) -> (ResultString);
388394

389395
// Common NP and user management operations
390396
get_identity_reputation: (pubkey_bytes: vec nat8) -> (nat64) query;

ic-canister/src/canister_backend/generic.rs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,22 @@ pub(crate) fn _user_register(
172172
})
173173
}
174174

175+
pub(crate) fn _node_provider_check_in(
176+
pubkey_bytes: Vec<u8>,
177+
memo: String,
178+
nonce_signature: Vec<u8>,
179+
) -> Result<String, String> {
180+
// To prevent DOS attacks, a fee is charged for executing this operation
181+
LEDGER_MAP.with(|ledger| {
182+
dcc_common::do_node_provider_check_in(
183+
&mut ledger.borrow_mut(),
184+
pubkey_bytes,
185+
memo,
186+
nonce_signature,
187+
)
188+
})
189+
}
190+
175191
pub(crate) fn _node_provider_update_profile(
176192
pubkey_bytes: Vec<u8>,
177193
update_profile_payload: Vec<u8>,
@@ -214,22 +230,7 @@ pub(crate) fn _node_provider_get_profile_by_principal(principal: Principal) -> O
214230
_node_provider_get_profile_by_pubkey_bytes(pubkey_bytes)
215231
}
216232

217-
pub(crate) fn _node_provider_check_in(
218-
pubkey_bytes: Vec<u8>,
219-
nonce_signature: Vec<u8>,
220-
) -> Result<String, String> {
221-
// To prevent DOS attacks, a fee is charged for executing this operation
222-
LEDGER_MAP.with(|ledger| {
223-
dcc_common::do_node_provider_check_in(
224-
&mut ledger.borrow_mut(),
225-
ic_cdk::api::caller(),
226-
pubkey_bytes,
227-
nonce_signature,
228-
)
229-
})
230-
}
231-
232-
pub(crate) fn _get_np_check_in_nonce() -> Vec<u8> {
233+
pub(crate) fn _get_check_in_nonce() -> Vec<u8> {
233234
LEDGER_MAP.with(|ledger| ledger.borrow().get_latest_block_hash())
234235
}
235236

ic-canister/src/canister_endpoints/generic.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ fn user_register(pubkey_bytes: Vec<u8>, signature: Vec<u8>) -> Result<String, St
3535
_user_register(pubkey_bytes, signature)
3636
}
3737

38+
#[ic_cdk::update]
39+
fn node_provider_check_in(
40+
pubkey_bytes: Vec<u8>,
41+
memo: String,
42+
nonce_signature: Vec<u8>,
43+
) -> Result<String, String> {
44+
_node_provider_check_in(pubkey_bytes, memo, nonce_signature)
45+
}
46+
3847
#[ic_cdk::update]
3948
fn node_provider_update_profile(
4049
pubkey_bytes: Vec<u8>,
@@ -66,17 +75,9 @@ fn node_provider_get_profile_by_principal(principal: Principal) -> Option<String
6675
_node_provider_get_profile_by_principal(principal)
6776
}
6877

69-
#[ic_cdk::update]
70-
fn node_provider_check_in(
71-
pubkey_bytes: Vec<u8>,
72-
nonce_signature: Vec<u8>,
73-
) -> Result<String, String> {
74-
_node_provider_check_in(pubkey_bytes, nonce_signature)
75-
}
76-
7778
#[ic_cdk::query]
78-
fn get_np_check_in_nonce() -> Vec<u8> {
79-
_get_np_check_in_nonce()
79+
fn get_check_in_nonce() -> Vec<u8> {
80+
_get_check_in_nonce()
8081
}
8182

8283
#[ic_cdk::query]

ic-canister/tests/test_canister.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ fn np_check_in(
330330
dcc_identity: &DccIdentity,
331331
) -> Result<String, String> {
332332
let no_args = encode_one(()).expect("failed to encode");
333-
let nonce_bytes = query_check_and_decode!(pic, can, "get_np_check_in_nonce", no_args, Vec<u8>);
333+
let nonce_bytes = query_check_and_decode!(pic, can, "get_check_in_nonce", no_args, Vec<u8>);
334334
let nonce_string = hex::encode(&nonce_bytes);
335335
println!(
336336
"Checking-in NP {}, using nonce: {} ({} bytes)",
@@ -339,7 +339,7 @@ fn np_check_in(
339339
nonce_bytes.len()
340340
);
341341

342-
let payload = dcc_identity.sign(&nonce_bytes).unwrap().to_bytes();
342+
let crypto_sig = dcc_identity.sign(&nonce_bytes).unwrap().to_bytes();
343343

344344
update_check_and_decode!(
345345
pic,
@@ -348,7 +348,8 @@ fn np_check_in(
348348
"node_provider_check_in",
349349
Encode!(
350350
&dcc_identity.to_bytes_verifying(),
351-
&payload
351+
&String::from("Just a test memo!"),
352+
&crypto_sig
352353
)
353354
.unwrap(),
354355
Result<String, String>

0 commit comments

Comments
 (0)