Skip to content

Commit

Permalink
Merge branch '2.0' into feat/iterable-wallet-data
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Coats committed Nov 9, 2023
2 parents 693b904 + b0660db commit 52f9f16
Show file tree
Hide file tree
Showing 121 changed files with 5,359 additions and 4,534 deletions.
90 changes: 90 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
resolver = "2"
members = [
"bindings/core",
# TODO: issue #1424
#"bindings/nodejs",
"bindings/nodejs",
# TODO: issue #1423
#"bindings/python",
# TODO: issue #1425
Expand Down
33 changes: 25 additions & 8 deletions bindings/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use fern_logger::{logger_init, LoggerConfig, LoggerOutputConfigBuilder};
pub use iota_sdk;
use iota_sdk::{
client::secret::{SecretManager, SecretManagerDto},
types::block::address::Bech32Address,
utils::serde::bip44::option_bip44,
wallet::{ClientOptions, Wallet},
};
use serde::Deserialize;
Expand All @@ -42,21 +44,24 @@ pub fn init_logger(config: String) -> std::result::Result<(), fern_logger::Error
#[derivative(Debug)]
#[serde(rename_all = "camelCase")]
pub struct WalletOptions {
pub storage_path: Option<String>,
pub client_options: Option<ClientOptions>,
pub address: Option<Bech32Address>,
pub alias: Option<String>,
#[serde(with = "option_bip44", default)]
pub bip_path: Option<Bip44>,
pub client_options: Option<ClientOptions>,
#[derivative(Debug(format_with = "OmittedDebug::omitted_fmt"))]
pub secret_manager: Option<SecretManagerDto>,
pub storage_path: Option<String>,
}

impl WalletOptions {
pub fn with_storage_path(mut self, storage_path: impl Into<Option<String>>) -> Self {
self.storage_path = storage_path.into();
pub fn with_address(mut self, address: impl Into<Option<Bech32Address>>) -> Self {
self.address = address.into();
self
}

pub fn with_client_options(mut self, client_options: impl Into<Option<ClientOptions>>) -> Self {
self.client_options = client_options.into();
pub fn with_alias(mut self, alias: impl Into<Option<String>>) -> Self {
self.alias = alias.into();
self
}

Expand All @@ -65,16 +70,28 @@ impl WalletOptions {
self
}

pub fn with_client_options(mut self, client_options: impl Into<Option<ClientOptions>>) -> Self {
self.client_options = client_options.into();
self
}

pub fn with_secret_manager(mut self, secret_manager: impl Into<Option<SecretManagerDto>>) -> Self {
self.secret_manager = secret_manager.into();
self
}

pub fn with_storage_path(mut self, storage_path: impl Into<Option<String>>) -> Self {
self.storage_path = storage_path.into();
self
}

pub async fn build(self) -> iota_sdk::wallet::Result<Wallet> {
log::debug!("wallet options: {self:?}");
let mut builder = Wallet::builder()
.with_client_options(self.client_options)
.with_bip_path(self.bip_path);
.with_address(self.address)
.with_alias(self.alias)
.with_bip_path(self.bip_path)
.with_client_options(self.client_options);

#[cfg(feature = "storage")]
if let Some(storage_path) = &self.storage_path {
Expand Down
3 changes: 3 additions & 0 deletions bindings/core/src/method/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ use crate::OmittedDebug;
#[serde(tag = "name", content = "data", rename_all = "camelCase")]
#[non_exhaustive]
pub enum WalletMethod {
/// Returns the accounts of the wallet.
/// Expected response: [`OutputsData`](crate::Response::OutputsData)
Accounts,
/// Backup storage. Password must be the current one, when Stronghold is used as SecretManager.
/// Expected response: [`Ok`](crate::Response::Ok)
#[cfg(feature = "stronghold")]
Expand Down
9 changes: 8 additions & 1 deletion bindings/core/src/method_handler/secret_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use iota_sdk::client::secret::stronghold::StrongholdSecretManager;
use iota_sdk::{
client::{
api::{GetAddressesOptions, PreparedTransactionData},
secret::{DowncastSecretManager, SecretManage, SignBlock},
secret::{DowncastSecretManager, SecretManage, SecretManager, SignBlock},
},
types::{
block::{address::ToBech32Ext, core::UnsignedBlock, unlock::Unlock, SignedBlockDto},
Expand Down Expand Up @@ -124,6 +124,13 @@ where
if let Some(secret_manager) = secret_manager.downcast::<StrongholdSecretManager>() {
secret_manager.store_mnemonic(mnemonic).await?;
Response::Ok
} else if let Some(secret_manager) = secret_manager.downcast::<SecretManager>() {
if let SecretManager::Stronghold(secret_manager) = secret_manager {
secret_manager.store_mnemonic(mnemonic).await?;
Response::Ok
} else {
return Err(iota_sdk::client::Error::SecretManagerMismatch.into());
}
} else {
return Err(iota_sdk::client::Error::SecretManagerMismatch.into());
}
Expand Down
3 changes: 3 additions & 0 deletions bindings/core/src/method_handler/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ use crate::{method::WalletMethod, response::Response, Result};
/// Call a wallet method.
pub(crate) async fn call_wallet_method_internal(wallet: &Wallet, method: WalletMethod) -> Result<Response> {
let response = match method {
WalletMethod::Accounts => {
Response::OutputsData(wallet.data().await.accounts().map(OutputDataDto::from).collect())
}
#[cfg(feature = "stronghold")]
WalletMethod::Backup { destination, password } => {
wallet.backup(destination, password).await?;
Expand Down
2 changes: 1 addition & 1 deletion bindings/core/tests/secrets_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ fn method_interface_secrets_debug() {
let wallet_options = WalletOptions::default().with_secret_manager(SecretManagerDto::Placeholder);
assert_eq!(
format!("{:?}", wallet_options),
"WalletOptions { storage_path: None, client_options: None, bip_path: None, secret_manager: Some(<omitted>) }"
"WalletOptions { address: None, alias: None, bip_path: None, client_options: None, secret_manager: Some(<omitted>), storage_path: None }"
);
}
13 changes: 7 additions & 6 deletions bindings/nodejs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ crate-type = ["cdylib"]
doc = false

[dependencies]
async-trait = { version = "0.1.73", default-features = false }
iota-sdk-bindings-core = { path = "../core", default-features = false, features = [
"events",
"ledger_nano",
Expand All @@ -28,17 +29,17 @@ iota-sdk-bindings-core = { path = "../core", default-features = false, features
"mqtt",
"private_key_secret_manager",
] }

log = { version = "0.4.20", default-features = false }
neon = { version = "0.10.1", default-features = false, features = [
"napi-6",
"event-queue-api",
"promise-api",
] }
napi = { version = "2.13.3", default-features = false, features = ["async"] }
napi-derive = { version = "2.13.0", default-features = false }
once_cell = { version = "1.18.0", default-features = false }
serde_json = { version = "1.0.107", default-features = false }
thiserror = { version = "1.0.49", default-features = false }
tokio = { version = "1.33.0", default-features = false }

[build-dependencies]
napi-build = { version = "2.0.1", default-features = false }

[profile.production]
codegen-units = 1
inherits = "release"
Expand Down
3 changes: 1 addition & 2 deletions bindings/nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ If you have already installed the project and only want to run the build, run th
npm run build
```

This command uses the [cargo-cp-artifact](https://github.com/neon-bindings/cargo-cp-artifact) utility to run the Rust
build and copy the built library into `./build/Release/index.node`.
This command uses the napi build utility to run the Rust build and copy the built library into `./build/Release/index.node`.
Prebuild requires that the binary is in `build/Release` as though it was built with node-gyp.

## Client Usage
Expand Down
8 changes: 8 additions & 0 deletions bindings/nodejs/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

extern crate napi_build;

fn main() {
napi_build::setup();
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Wallet, CoinType, initLogger, WalletOptions } from '@iota/sdk';
import {
Wallet,
CoinType,
initLogger,
WalletOptions,
SecretManager,
} from '@iota/sdk';

// This example uses secrets in environment variables for simplicity which should not be done in production.
require('dotenv').config({ path: '.env' });

// Run with command:
// yarn run-example ./how_tos/accounts_and_addresses/create-account.ts
// yarn run-example ./how_tos/accounts_and_addresses/create-wallet.ts

// This example creates a new database and account.
// This example creates a new database and wallet.
async function run() {
initLogger();
for (const envVar of [
Expand All @@ -24,32 +30,47 @@ async function run() {
}

try {
const strongholdSecretManager = {
stronghold: {
snapshotPath: process.env.STRONGHOLD_SNAPSHOT_PATH,
password: process.env.STRONGHOLD_PASSWORD,
},
};

const secretManager = new SecretManager(strongholdSecretManager);

// A mnemonic can be generated with `Utils.generateMnemonic()`.
// Store the mnemonic in the Stronghold snapshot, this needs to be done only the first time.
// The mnemonic can't be retrieved from the Stronghold file, so make a backup in a secure place!
await secretManager.storeMnemonic(process.env.MNEMONIC as string);

const wallet_address = await secretManager.generateEd25519Addresses({
coinType: CoinType.IOTA,
accountIndex: 0,
range: {
start: 0,
end: 1,
},
bech32Hrp: 'tst',
});

const walletOptions: WalletOptions = {
address: wallet_address[0],
storagePath: process.env.WALLET_DB_PATH,
clientOptions: {
nodes: [process.env.NODE_URL as string],
},
coinType: CoinType.Shimmer,
secretManager: {
stronghold: {
snapshotPath: process.env.STRONGHOLD_SNAPSHOT_PATH,
password: process.env.STRONGHOLD_PASSWORD,
},
bipPath: {
coinType: CoinType.IOTA,
},
secretManager: strongholdSecretManager,
};

const wallet = new Wallet(walletOptions);

// A mnemonic can be generated with `Utils.generateMnemonic()`.
// Store the mnemonic in the Stronghold snapshot, this needs to be done only the first time.
// The mnemonic can't be retrieved from the Stronghold file, so make a backup in a secure place!
await wallet.storeMnemonic(process.env.MNEMONIC as string);

// Create a new account
const account = await wallet.createAccount({
alias: 'Alice',
});
console.log('Generated new account:', account.getMetadata().alias);
console.log(
'Generated wallet with address: ' + (await wallet.address()),
);
} catch (error) {
console.error('Error: ', error);
}
Expand Down
Loading

0 comments on commit 52f9f16

Please sign in to comment.