Skip to content

Commit

Permalink
feat(upgrader): disaster recovery recreates accounts (#294)
Browse files Browse the repository at this point in the history
This PR adds the optional functionality of providing an initial set of
accounts to the init args of the station canister. This feature enables
the disaster recovery to restore accounts in case of `(re)install`.

---------

Co-authored-by: mraszyk <[email protected]>
  • Loading branch information
keplervital and mraszyk authored Jul 4, 2024
1 parent a2fcdb5 commit 3af2d0f
Show file tree
Hide file tree
Showing 15 changed files with 651 additions and 85 deletions.
16 changes: 16 additions & 0 deletions apps/wallet/src/generated/station/station.did
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,20 @@ type SystemUpgraderInput = variant {
WasmModule : blob;
};

// The initial accounts to create when initializing the canister for the first time, e.g., after disaster recovery.
type InitAccountInput = record {
// The UUID of the account, if not provided a new UUID will be generated.
id : opt UUID;
// A friendly name for the account (e.g. "My Account").
name : text;
// The blockchain identifier (e.g., `ethereum`, `bitcoin`, `icp`, etc.)
blockchain : text;
// The asset standard for this account (e.g. `native`, `erc20`, etc.).
standard : text;
// Metadata associated with the account (e.g. `{"contract": "0x1234", "symbol": "ANY"}`).
metadata : vec AccountMetadata;
};

// The init configuration for the canister.
//
// Only used when installing the canister for the first time.
Expand All @@ -2008,6 +2022,8 @@ type SystemInit = record {
upgrader : SystemUpgraderInput;
// An optional additional controller of the station and upgrader canisters.
fallback_controller : opt principal;
// Optional initial accounts to create.
accounts : opt vec InitAccountInput;
};

// The upgrade configuration for the canister.
Expand Down
8 changes: 8 additions & 0 deletions apps/wallet/src/generated/station/station.did.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,13 @@ export interface HttpResponse {
'headers' : Array<HeaderField>,
'status_code' : number,
}
export interface InitAccountInput {
'id' : [] | [UUID],
'metadata' : Array<AccountMetadata>,
'name' : string,
'blockchain' : string,
'standard' : string,
}
export interface ListAccountTransfersInput {
'account_id' : UUID,
'status' : [] | [TransferStatusType],
Expand Down Expand Up @@ -866,6 +873,7 @@ export interface SystemInit {
'name' : string,
'fallback_controller' : [] | [Principal],
'upgrader' : SystemUpgraderInput,
'accounts' : [] | [Array<InitAccountInput>],
'admins' : Array<AdminInitInput>,
'quorum' : [] | [number],
}
Expand Down
22 changes: 20 additions & 2 deletions apps/wallet/src/generated/station/station.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ export const idlFactory = ({ IDL }) => {
'Id' : IDL.Principal,
'WasmModule' : IDL.Vec(IDL.Nat8),
});
const UUID = IDL.Text;
const AccountMetadata = IDL.Record({ 'key' : IDL.Text, 'value' : IDL.Text });
const InitAccountInput = IDL.Record({
'id' : IDL.Opt(UUID),
'metadata' : IDL.Vec(AccountMetadata),
'name' : IDL.Text,
'blockchain' : IDL.Text,
'standard' : IDL.Text,
});
const AdminInitInput = IDL.Record({
'name' : IDL.Text,
'identity' : IDL.Principal,
Expand All @@ -14,6 +23,7 @@ export const idlFactory = ({ IDL }) => {
'name' : IDL.Text,
'fallback_controller' : IDL.Opt(IDL.Principal),
'upgrader' : SystemUpgraderInput,
'accounts' : IDL.Opt(IDL.Vec(InitAccountInput)),
'admins' : IDL.Vec(AdminInitInput),
'quorum' : IDL.Opt(IDL.Nat16),
});
Expand Down Expand Up @@ -81,7 +91,6 @@ export const idlFactory = ({ IDL }) => {
'Scheduled' : IDL.Record({ 'execution_time' : TimestampRFC3339 }),
});
const AddUserGroupOperationInput = IDL.Record({ 'name' : IDL.Text });
const UUID = IDL.Text;
const ResourceId = IDL.Variant({ 'Id' : UUID, 'Any' : IDL.Null });
const RequestResourceAction = IDL.Variant({
'List' : IDL.Null,
Expand Down Expand Up @@ -348,7 +357,6 @@ export const idlFactory = ({ IDL }) => {
'validation_method' : IDL.Opt(CanisterMethod),
'execution_method_cycles' : IDL.Opt(IDL.Nat64),
});
const AccountMetadata = IDL.Record({ 'key' : IDL.Text, 'value' : IDL.Text });
const AddAccountOperationInput = IDL.Record({
'configs_request_policy' : IDL.Opt(RequestPolicyRule),
'read_permission' : Allow,
Expand Down Expand Up @@ -1191,6 +1199,15 @@ export const init = ({ IDL }) => {
'Id' : IDL.Principal,
'WasmModule' : IDL.Vec(IDL.Nat8),
});
const UUID = IDL.Text;
const AccountMetadata = IDL.Record({ 'key' : IDL.Text, 'value' : IDL.Text });
const InitAccountInput = IDL.Record({
'id' : IDL.Opt(UUID),
'metadata' : IDL.Vec(AccountMetadata),
'name' : IDL.Text,
'blockchain' : IDL.Text,
'standard' : IDL.Text,
});
const AdminInitInput = IDL.Record({
'name' : IDL.Text,
'identity' : IDL.Principal,
Expand All @@ -1199,6 +1216,7 @@ export const init = ({ IDL }) => {
'name' : IDL.Text,
'fallback_controller' : IDL.Opt(IDL.Principal),
'upgrader' : SystemUpgraderInput,
'accounts' : IDL.Opt(IDL.Vec(InitAccountInput)),
'admins' : IDL.Vec(AdminInitInput),
'quorum' : IDL.Opt(IDL.Nat16),
});
Expand Down
1 change: 1 addition & 0 deletions core/control-panel/impl/src/services/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ impl DeployService {
upgrader: station_api::SystemUpgraderInput::WasmModule(upgrader_wasm_module),
quorum: Some(1),
fallback_controller: Some(NNS_ROOT_CANISTER_ID),
accounts: None,
}))
.map_err(|err| DeployError::Failed {
reason: err.to_string(),
Expand Down
16 changes: 16 additions & 0 deletions core/station/api/spec.did
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,20 @@ type SystemUpgraderInput = variant {
WasmModule : blob;
};

// The initial accounts to create when initializing the canister for the first time, e.g., after disaster recovery.
type InitAccountInput = record {
// The UUID of the account, if not provided a new UUID will be generated.
id : opt UUID;
// A friendly name for the account (e.g. "My Account").
name : text;
// The blockchain identifier (e.g., `ethereum`, `bitcoin`, `icp`, etc.)
blockchain : text;
// The asset standard for this account (e.g. `native`, `erc20`, etc.).
standard : text;
// Metadata associated with the account (e.g. `{"contract": "0x1234", "symbol": "ANY"}`).
metadata : vec AccountMetadata;
};

// The init configuration for the canister.
//
// Only used when installing the canister for the first time.
Expand All @@ -2008,6 +2022,8 @@ type SystemInit = record {
upgrader : SystemUpgraderInput;
// An optional additional controller of the station and upgrader canisters.
fallback_controller : opt principal;
// Optional initial accounts to create.
accounts : opt vec InitAccountInput;
};

// The upgrade configuration for the canister.
Expand Down
14 changes: 14 additions & 0 deletions core/station/api/src/system.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::{MetadataDTO, UuidDTO};

use super::TimestampRfc3339;
use candid::{CandidType, Deserialize, Principal};

Expand Down Expand Up @@ -38,6 +40,15 @@ pub enum SystemUpgraderInput {
WasmModule(#[serde(with = "serde_bytes")] Vec<u8>),
}

#[derive(CandidType, serde::Serialize, Deserialize, Clone, Debug)]
pub struct InitAccountInput {
pub id: Option<UuidDTO>,
pub name: String,
pub blockchain: String,
pub standard: String,
pub metadata: Vec<MetadataDTO>,
}

#[derive(CandidType, serde::Serialize, Deserialize, Clone, Debug)]
pub struct SystemInit {
/// The station name.
Expand All @@ -48,7 +59,10 @@ pub struct SystemInit {
pub quorum: Option<u16>,
/// The upgrader configuration.
pub upgrader: SystemUpgraderInput,
/// Optional fallback controller for the station and upgrader canisters.
pub fallback_controller: Option<Principal>,
/// Optionally set the initial accounts.
pub accounts: Option<Vec<InitAccountInput>>,
}

#[derive(CandidType, serde::Serialize, Deserialize, Clone, Debug)]
Expand Down
2 changes: 2 additions & 0 deletions core/station/impl/src/errors/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub enum SystemError {
InitFailed { reason: String },
#[error(r#"The canister needs at least one admin"#)]
NoAdminsSpecified,
#[error(r#"There are too many admins defined, max allowed is {max}."#)]
TooManyAdminsSpecified { max: usize },
}

impl DetailableError for SystemError {
Expand Down
2 changes: 1 addition & 1 deletion core/station/impl/src/factories/requests/add_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl Execute for AddAccountRequestExecute<'_, '_> {
async fn execute(&self) -> Result<RequestExecuteStage, RequestExecuteError> {
let account = self
.account_service
.create_account(self.operation.input.to_owned())
.create_account(self.operation.input.to_owned(), None)
.await
.map_err(|e| RequestExecuteError::Failed {
reason: format!("Failed to create account: {}", e),
Expand Down
25 changes: 14 additions & 11 deletions core/station/impl/src/models/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,17 +430,20 @@ mod tests {

let account_service = AccountService::default();
let account = account_service
.create_account(AddAccountOperationInput {
name: "a".to_owned(),
blockchain: crate::models::Blockchain::InternetComputer,
standard: crate::models::BlockchainStandard::Native,
metadata: Metadata::default(),
read_permission: Allow::default(),
configs_permission: Allow::default(),
transfer_permission: Allow::default(),
configs_request_policy: None,
transfer_request_policy: None,
})
.create_account(
AddAccountOperationInput {
name: "a".to_owned(),
blockchain: crate::models::Blockchain::InternetComputer,
standard: crate::models::BlockchainStandard::Native,
metadata: Metadata::default(),
read_permission: Allow::default(),
configs_permission: Allow::default(),
transfer_permission: Allow::default(),
configs_request_policy: None,
transfer_request_policy: None,
},
None,
)
.await
.expect("Failed to create account");

Expand Down
Loading

0 comments on commit 3af2d0f

Please sign in to comment.