Skip to content

Commit

Permalink
feat: create_canister_from (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
sesi200 authored Mar 15, 2024
1 parent d9b0391 commit b01c816
Show file tree
Hide file tree
Showing 7 changed files with 1,403 additions and 135 deletions.
52 changes: 41 additions & 11 deletions cycles-ledger/cycles-ledger.did
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,22 @@ type Value = variant {
};

type GetArchivesArgs = record {
// The last archive seen by the client.
// The Ledger will return archives coming
// after this one if set, otherwise it
// will return the first archives.
from : opt principal;
// The last archive seen by the client.
// The ledger will return archives coming
// after this one if set, otherwise it
// will return the first archives.
from : opt principal;
};

type GetArchivesResult = vec record {
// The id of the archive
canister_id : principal;
// The id of the archive
canister_id : principal;

// The first block in the archive
start : nat;
// The first block in the archive
start : nat;

// The last block in the archive
end : nat;
// The last block in the archive
end : nat;
};

type GetBlocksArgs = vec record { start : nat; length : nat };
Expand Down Expand Up @@ -203,6 +203,14 @@ type CreateCanisterArgs = record {
creation_args : opt CmcCreateCanisterArgs;
};

type CreateCanisterFromArgs = record {
from : Account;
spender_subaccount : opt vec nat8;
created_at_time : opt nat64;
amount : nat;
creation_args : opt CmcCreateCanisterArgs;
};

type CmcCreateCanisterArgs = record {
// Optional canister settings that, if set, are applied to the newly created canister.
// If not specified, the caller is the controller of the canister and the other settings are set to default values.
Expand Down Expand Up @@ -256,6 +264,27 @@ type CreateCanisterError = variant {
GenericError : record { message : text; error_code : nat };
};

type CreateCanisterFromError = variant {
InsufficientFunds : record { balance : nat };
InsufficientAllowance : record { allowance : nat };
TooOld;
CreatedInFuture : record { ledger_time : nat64 };
TemporarilyUnavailable;
Duplicate : record {
duplicate_of : nat;
// If the original transaction created a canister then this field will contain the canister id.
canister_id : opt principal;
};
FailedToCreateFrom : record {
create_from_block : opt BlockIndex;
refund_block : opt BlockIndex;
approval_refund_block : opt BlockIndex;
rejection_code : RejectionCode;
rejection_reason : text;
};
GenericError : record { message : text; error_code : nat };
};

type MetadataValue = variant {
Nat : nat;
Int : int;
Expand Down Expand Up @@ -298,4 +327,5 @@ service : (ledger_args : LedgerArgs) -> {
withdraw : (WithdrawArgs) -> (variant { Ok : BlockIndex; Err : WithdrawError });
withdraw_from : (WithdrawFromArgs) -> (variant { Ok : BlockIndex; Err : WithdrawFromError });
create_canister : (CreateCanisterArgs) -> (variant { Ok : CreateCanisterSuccess; Err : CreateCanisterError });
create_canister_from : (CreateCanisterFromArgs) -> (variant { Ok : CreateCanisterSuccess; Err : CreateCanisterFromError });
};
45 changes: 45 additions & 0 deletions cycles-ledger/src/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,20 @@ pub struct CreateCanisterArgs {
pub creation_args: Option<CmcCreateCanisterArgs>,
}

#[derive(Debug, Clone, CandidType, Deserialize, PartialEq, Eq)]
pub struct CreateCanisterFromArgs {
pub from: Account,
#[serde(default)]
pub spender_subaccount: Option<Subaccount>,
#[serde(default)]
pub created_at_time: Option<u64>,
/// Amount of cycles used to create the canister.
/// The new canister will have `amount - canister creation fee` cycles when created.
pub amount: NumCycles,
#[serde(default)]
pub creation_args: Option<CmcCreateCanisterArgs>,
}

/// Error for create_canister endpoint
#[derive(Serialize, Deserialize, CandidType, Clone, Debug, PartialEq, Eq)]
pub enum CmcCreateCanisterError {
Expand Down Expand Up @@ -318,6 +332,37 @@ pub enum CreateCanisterError {
},
}

/// Error for create_canister endpoint
#[derive(Deserialize, CandidType, Clone, Debug, PartialEq, Eq)]
pub enum CreateCanisterFromError {
InsufficientFunds {
balance: NumCycles,
},
InsufficientAllowance {
allowance: NumCycles,
},
TooOld,
CreatedInFuture {
ledger_time: u64,
},
TemporarilyUnavailable,
Duplicate {
duplicate_of: BlockIndex,
canister_id: Option<Principal>,
},
FailedToCreateFrom {
create_from_block: Option<BlockIndex>,
refund_block: Option<BlockIndex>,
approval_refund_block: Option<BlockIndex>,
rejection_code: RejectionCode,
rejection_reason: String,
},
GenericError {
error_code: Nat,
message: String,
},
}

impl CreateCanisterError {
pub const BAD_FEE_ERROR: u64 = 100_001;
}
Expand Down
47 changes: 46 additions & 1 deletion cycles-ledger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::BTreeMap;

use anyhow::{bail, Context};
use ciborium::Value as CiboriumValue;
use endpoints::{WithdrawError, WithdrawFromError};
use endpoints::{CreateCanisterError, CreateCanisterFromError, WithdrawError, WithdrawFromError};
use icrc_ledger_types::{
icrc::generic_value::Value, icrc1::transfer::TransferError,
icrc2::transfer_from::TransferFromError,
Expand Down Expand Up @@ -216,6 +216,51 @@ pub fn withdraw_from_error_to_withdraw_error(e: WithdrawFromError) -> WithdrawEr
}
}

// Traps if the error is InsufficientAllowance
pub fn create_canister_from_error_to_create_canister_error(
e: CreateCanisterFromError,
) -> CreateCanisterError {
match e {
CreateCanisterFromError::InsufficientFunds { balance } => {
CreateCanisterError::InsufficientFunds { balance }
}
CreateCanisterFromError::InsufficientAllowance { .. } => {
ic_cdk::trap("InsufficientAllowance error should not happen for create_canister")
}
CreateCanisterFromError::TooOld => CreateCanisterError::TooOld,
CreateCanisterFromError::CreatedInFuture { ledger_time } => {
CreateCanisterError::CreatedInFuture { ledger_time }
}
CreateCanisterFromError::TemporarilyUnavailable => {
CreateCanisterError::TemporarilyUnavailable
}
CreateCanisterFromError::Duplicate {
duplicate_of,
canister_id,
} => CreateCanisterError::Duplicate {
duplicate_of,
canister_id,
},
CreateCanisterFromError::FailedToCreateFrom {
create_from_block,
refund_block,
rejection_reason,
..
} => CreateCanisterError::FailedToCreate {
fee_block: create_from_block,
refund_block,
error: rejection_reason,
},
CreateCanisterFromError::GenericError {
error_code,
message,
} => CreateCanisterError::GenericError {
error_code,
message,
},
}
}

#[cfg(test)]
mod tests {
use ciborium::{value::Integer, Value};
Expand Down
56 changes: 43 additions & 13 deletions cycles-ledger/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use candid::{candid_method, Nat, Principal};
use cycles_ledger::endpoints::{
DataCertificate, GetArchivesArgs, GetArchivesResult, GetBlocksArgs, GetBlocksResult,
LedgerArgs, SupportedBlockType, WithdrawError, WithdrawFromError,
CmcCreateCanisterArgs, DataCertificate, GetArchivesArgs, GetArchivesResult, GetBlocksArgs,
GetBlocksResult, LedgerArgs, SupportedBlockType, WithdrawError, WithdrawFromError,
};
use cycles_ledger::logs::{Log, LogEntry, Priority};
use cycles_ledger::logs::{P0, P1};
use cycles_ledger::storage::{
balance_of, mutate_config, mutate_state, prune, read_config, read_state,
};
use cycles_ledger::{
config, endpoints, storage, transfer_from_error_to_transfer_error,
withdraw_from_error_to_withdraw_error,
config, create_canister_from_error_to_create_canister_error, endpoints, storage,
transfer_from_error_to_transfer_error, withdraw_from_error_to_withdraw_error,
};
use ic_canister_log::export as export_logs;
use ic_canisters_http_types::{HttpRequest, HttpResponse, HttpResponseBuilder};
Expand Down Expand Up @@ -315,6 +315,22 @@ async fn withdraw_from(args: endpoints::WithdrawFromArgs) -> Result<Nat, Withdra
.await
}

async fn execute_create_canister(
from: Account,
spender: Option<Account>,
amount: Nat,
created_at_time: Option<u64>,
creation_args: Option<CmcCreateCanisterArgs>,
) -> Result<endpoints::CreateCanisterSuccess, endpoints::CreateCanisterFromError> {
let Some(amount) = amount.0.to_u128() else {
return Err(endpoints::CreateCanisterFromError::InsufficientFunds {
balance: Nat::from(balance_of(&from)),
});
};
let now = ic_cdk::api::time();
storage::create_canister(from, spender, amount, now, created_at_time, creation_args).await
}

#[update]
#[candid_method]
async fn create_canister(
Expand All @@ -325,16 +341,30 @@ async fn create_canister(
subaccount: args.from_subaccount,
};

let Some(amount) = args.amount.0.to_u128() else {
return Err(endpoints::CreateCanisterError::InsufficientFunds {
balance: Nat::from(balance_of(&from)),
});
};

storage::create_canister(
execute_create_canister(
from,
amount,
ic_cdk::api::time(),
None,
args.amount,
args.created_at_time,
args.creation_args,
)
.await
.map_err(create_canister_from_error_to_create_canister_error)
}

#[update]
#[candid_method]
async fn create_canister_from(
args: endpoints::CreateCanisterFromArgs,
) -> Result<endpoints::CreateCanisterSuccess, endpoints::CreateCanisterFromError> {
let spender = Account {
owner: ic_cdk::caller(),
subaccount: args.spender_subaccount,
};
execute_create_canister(
args.from,
Some(spender),
args.amount,
args.created_at_time,
args.creation_args,
)
Expand Down
Loading

0 comments on commit b01c816

Please sign in to comment.