Skip to content

Commit

Permalink
Initial implementation of contract signing, tests still missing
Browse files Browse the repository at this point in the history
  • Loading branch information
yanliu38 committed Dec 8, 2024
1 parent 1c0b62b commit 7550d3a
Show file tree
Hide file tree
Showing 14 changed files with 651 additions and 216 deletions.
8 changes: 5 additions & 3 deletions common/src/account_transfers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,20 @@ pub fn ledger_funds_transfer(

pub fn charge_fees_to_account_and_bump_reputation(
ledger: &mut LedgerMap,
dcc_id_charge: &DccIdentity,
dcc_id_bump_reputation: &DccIdentity,
amount_e9s: TokenAmount,
) -> Result<(), String> {
if amount_e9s == 0 {
return Ok(());
}
let balance_from_after =
account_balance_get(&dcc_identity.as_icrc_compatible_account()) - amount_e9s;
account_balance_get(&dcc_id_charge.as_icrc_compatible_account()) - amount_e9s;
match ledger_funds_transfer(
ledger,
// Burn 0 tokens, and transfer the entire amount_e9s to the fee accounts
FundsTransfer::new(
dcc_identity.as_icrc_compatible_account(),
dcc_id_charge.as_icrc_compatible_account(),
MINTING_ACCOUNT,
amount_e9s.into(),
Some(fees_sink_accounts()),
Expand All @@ -88,7 +90,7 @@ pub fn charge_fees_to_account_and_bump_reputation(
) {
Ok(_) => Ok(ledger_add_reputation_change(
ledger,
dcc_identity,
dcc_id_bump_reputation,
amount_e9s.min(i64::MAX as TokenAmount) as i64,
)?),
Err(e) => {
Expand Down
40 changes: 40 additions & 0 deletions common/src/contract_refund_request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use borsh::{BorshDeserialize, BorshSerialize};
use function_name::named;
use ledger_map::LedgerMap;
use serde::{Deserialize, Serialize};

use crate::{fn_info, DccIdentity};

// TODO

#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub enum ContractRefundRequest {
V1(ContractRefundRequestV1),
}

#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct ContractRefundRequestV1 {
/// The bytes of the public key of the requester, as a vec of u8.
/// This is used to identify the requester and to verify the signature.
pub requester_pubkey_bytes: Vec<u8>,
/// The instance id for which the refund is requested.
pub instance_id: String,
/// The signature of the whole refund request.
/// This is used to verify that the refund request was made by the requester.
pub crypto_sig: Vec<u8>,
}

#[named]
pub fn do_contract_refund_request(
_ledger_map: &LedgerMap,
pubkey_bytes: Vec<u8>,
payload_serialized: Vec<u8>,
crypto_signature: Vec<u8>,
) -> Result<String, String> {
let dcc_id = DccIdentity::new_verifying_from_bytes(&pubkey_bytes).unwrap();
dcc_id.verify_bytes(&payload_serialized, &crypto_signature)?;

fn_info!("{}", dcc_id);

todo!()
}
125 changes: 125 additions & 0 deletions common/src/contract_sign_reply.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use borsh::{BorshDeserialize, BorshSerialize};
use function_name::named;
use ledger_map::LedgerMap;
use serde::{Deserialize, Serialize};

use crate::{
amount_as_string, charge_fees_to_account_and_bump_reputation, contract_sign_fee_e9s, fn_info,
ContractSignRequestPayload, DccIdentity, LABEL_CONTRACT_SIGN_REPLY,
LABEL_CONTRACT_SIGN_REQUEST,
};

#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct ContractSignReplyV1 {
requester_pubkey_bytes: Vec<u8>, // Public key of the original requester
request_memo: String, // Memo field of the original request
contract_id: Vec<u8>, // Contract ID of the request that we are replying to
sign_accepted: bool, // True/False to mark whether the signing was accepted or rejected by the provider
response_text: String, // Thank you note, or similar on success. Reason the request failed on failure.
response_details: String, // Instructions or a link to the detailed instructions: describing next steps, further information, etc.
}

// Main struct for Offering Request
#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub enum ContractSignReply {
V1(ContractSignReplyV1),
}

impl ContractSignReply {
pub fn contract_id(&self) -> &[u8] {
match self {
ContractSignReply::V1(payload) => payload.contract_id.as_slice(),
}
}
pub fn requester_pubkey_bytes(&self) -> &[u8] {
match self {
ContractSignReply::V1(payload) => payload.requester_pubkey_bytes.as_slice(),
}
}
pub fn request_memo(&self) -> &String {
match self {
ContractSignReply::V1(payload) => &payload.request_memo,
}
}
pub fn sign_accepted(&self) -> bool {
match self {
ContractSignReply::V1(payload) => payload.sign_accepted,
}
}
pub fn response_text(&self) -> &String {
match self {
ContractSignReply::V1(payload) => &payload.response_text,
}
}
pub fn response_details(&self) -> &String {
match self {
ContractSignReply::V1(payload) => &payload.response_details,
}
}
}

#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct ContractSignReplyPayloadV1 {
payload_bytes: Vec<u8>,
crypto_signature: Vec<u8>,
}

#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub enum ContractSignReplyPayload {
V1(ContractSignReplyPayloadV1),
}

impl ContractSignReplyPayload {
pub fn new(payload_bytes: Vec<u8>, crypto_signature: Vec<u8>) -> ContractSignReplyPayload {
ContractSignReplyPayload::V1(ContractSignReplyPayloadV1 {
payload_bytes,
crypto_signature,
})
}
}

#[named]
pub fn do_contract_sign_reply(
ledger: &mut LedgerMap,
pubkey_bytes: Vec<u8>,
reply_serialized: Vec<u8>,
crypto_signature: Vec<u8>,
) -> Result<String, String> {
let dcc_id = DccIdentity::new_verifying_from_bytes(&pubkey_bytes).unwrap();
dcc_id.verify_bytes(&reply_serialized, &crypto_signature)?;

fn_info!("{}", dcc_id);

let cs_reply = ContractSignReply::try_from_slice(&reply_serialized).unwrap();
let cs_req = ledger
.get(LABEL_CONTRACT_SIGN_REQUEST, cs_reply.contract_id())
.unwrap();
let cs_req = ContractSignRequestPayload::try_from_slice(&cs_req).unwrap();
let cs_req = cs_req
.deserialize_contract_sign_request()
.expect("Error deserializing original contract sign request");
if pubkey_bytes != cs_req.provider_pubkey_bytes() {
return Err(format!(
"Contract signing reply signed and submitted by {} does not match the provider public key {} from contract req 0x{}",
dcc_id, DccIdentity::new_verifying_from_bytes(cs_req.provider_pubkey_bytes()).unwrap(), hex::encode(cs_reply.contract_id())
));
}
let payload = ContractSignReplyPayload::new(reply_serialized, crypto_signature);
let payload_bytes = borsh::to_vec(&payload).unwrap();

let fees = contract_sign_fee_e9s(cs_req.payment_amount());
charge_fees_to_account_and_bump_reputation(ledger, &dcc_id, &dcc_id, fees)?;

ledger.upsert(
LABEL_CONTRACT_SIGN_REPLY,
&pubkey_bytes,
payload_bytes,
)
.map(|_| {
format!(
"Contract signing reply submitted! Thank you. You have been charged {} tokens as a fee, and your reputation has been bumped accordingly",
amount_as_string(fees)
)
})
.map_err(|e| e.to_string())
}
Loading

0 comments on commit 7550d3a

Please sign in to comment.