Skip to content
This repository has been archived by the owner on Jul 27, 2022. It is now read-only.

Commit

Permalink
Problem:(CRO-557) no way to import transactions (without a view key)
Browse files Browse the repository at this point in the history
solution:
- add a interface `export_tx` for sender to download the the transaction
- add a interface `import_tx` for receiver to import the transaction and get the output UTXO
  • Loading branch information
linfeng-crypto committed Dec 21, 2019
1 parent 8f0420c commit 6207d2c
Show file tree
Hide file tree
Showing 14 changed files with 273 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ of Rust SGX SDK (0.1.0 used a beta version of 1.1.0).
The release is a more complete alpha version meant to be deployed on the second iteration of the public testnet.
There are no guarantees on future API and binary compatibility at this stage.


## v0.1.0

### Features
Expand Down
14 changes: 12 additions & 2 deletions chain-core/src/tx/data/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use std::fmt;

use parity_scale_codec::{Decode, Encode};
#[cfg(not(feature = "mesalock_sgx"))]
use serde::de;
use serde::de::{
self,
value::{Error as ValueError, StrDeserializer},
IntoDeserializer,
};
#[cfg(not(feature = "mesalock_sgx"))]
use serde::{Deserialize, Deserializer, Serialize, Serializer};

Expand Down Expand Up @@ -56,7 +60,7 @@ where
}

#[inline]
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
fn visit_str<E>(self, value: &str) -> std::result::Result<Self::Value, E>
where
E: de::Error,
{
Expand Down Expand Up @@ -94,3 +98,9 @@ impl TxoPointer {
}
}
}

#[cfg(not(feature = "mesalock_sgx"))]
pub fn str2txid<S: AsRef<str>>(s: S) -> Result<TxId, ValueError> {
let deserializer: StrDeserializer<ValueError> = s.as_ref().into_deserializer();
deserialize_transaction_id(deserializer)
}
34 changes: 33 additions & 1 deletion client-cli/src/command/transaction_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use client_common::{Error, ErrorKind, PublicKey, Result, ResultExt};
use client_core::WalletClient;
use client_network::NetworkOpsClient;
use hex::decode;
use quest::{ask, text, yesno};
use quest::{ask, success, text, yesno};
use secstr::SecUtf8;
use structopt::StructOpt;
use unicase::eq_ascii;
Expand Down Expand Up @@ -62,6 +62,26 @@ pub enum TransactionCommand {
#[structopt(name = "type", short, long, help = "Type of transaction to create")]
transaction_type: TransactionType,
},
#[structopt(
name = "export",
about = "Export a plain transaction by a given transaction id"
)]
Export {
#[structopt(name = "name", short, long, help = "Name of wallet")]
name: String,
#[structopt(name = "id", short, long, help = "transaction id")]
id: String,
},
#[structopt(
name = "import",
about = "Export a plain transaction by a given transaction id"
)]
Import {
#[structopt(name = "name", short, long, help = "Name of wallet")]
name: String,
#[structopt(name = "tx", short, long, help = "base64 encoded plain transaction")]
tx: String,
},
}

impl TransactionCommand {
Expand All @@ -75,6 +95,18 @@ impl TransactionCommand {
name,
transaction_type,
} => new_transaction(wallet_client, network_ops_client, name, transaction_type),
TransactionCommand::Export { name, id } => {
let passphrase = ask_passphrase(None)?;
let tx = wallet_client.export_plain_tx(name, &passphrase, id)?;
success(&tx);
Ok(())
}
TransactionCommand::Import { name, tx } => {
let passphrase = ask_passphrase(None)?;
let imported_amount = wallet_client.import_plain_tx(name, &passphrase, tx)?;
success(format!("import amount: {}", imported_amount).as_str());
Ok(())
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion client-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub use multi_sig_address::MultiSigAddress;
#[doc(inline)]
pub use storage::{SecureStorage, Storage};
#[doc(inline)]
pub use transaction::{SignedTransaction, Transaction};
pub use transaction::{SignedTransaction, Transaction, TransactionInfo};

use secp256k1::{All, Secp256k1};

Expand Down
9 changes: 9 additions & 0 deletions client-common/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ use chain_core::tx::data::{Tx, TxId};
use chain_core::tx::witness::TxWitness;
use chain_core::tx::TransactionId;

/// A struct which the sender can download and the receiver can import
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode)]
pub struct TransactionInfo {
/// enum Transaction type
pub tx: Transaction,
/// block height when the tx broadcast
pub block_height: u64,
}

/// Enum containing different types of transactions
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode)]
#[serde(tag = "type")]
Expand Down
11 changes: 10 additions & 1 deletion client-core/src/service/wallet_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::BTreeSet;
use parity_scale_codec::{Decode, Encode};
use secstr::SecUtf8;

use crate::service::{load_wallet_state, WalletState};
use chain_core::common::H256;
use chain_core::init::address::RedeemAddress;
use chain_core::state::account::StakedStateAddress;
Expand Down Expand Up @@ -104,13 +105,21 @@ where
WalletService { storage }
}

/// Load wallet
/// Get the wallet from storage
pub fn get_wallet(&self, name: &str, passphrase: &SecUtf8) -> Result<Wallet> {
load_wallet(&self.storage, name, passphrase)?.err_kind(ErrorKind::InvalidInput, || {
format!("Wallet with name ({}) not found", name)
})
}

/// Get the wallet state from storage
pub fn get_wallet_state(&self, name: &str, passphrase: &SecUtf8) -> Result<WalletState> {
load_wallet_state(&self.storage, name, passphrase)?
.err_kind(ErrorKind::InvalidInput, || {
format!("WalletState with name ({}) not found", name)
})
}

fn set_wallet(&self, name: &str, passphrase: &SecUtf8, wallet: Wallet) -> Result<()> {
save_wallet(&self.storage, name, passphrase, &wallet)
}
Expand Down
6 changes: 5 additions & 1 deletion client-core/src/transaction_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ use chain_core::tx::data::address::ExtendedAddr;
use chain_core::tx::data::attribute::TxAttributes;
use chain_core::tx::data::output::TxOut;
use chain_core::tx::TxAux;
use client_common::{Result, SignedTransaction};
use client_common::{PrivateKey, Result, SignedTransaction, Transaction};

use crate::UnspentTransactions;
use chain_core::tx::data::TxId;

/// Interface for wallet transaction building from output addresses and amount.
/// This trait is also responsible for UTXO selection.
Expand All @@ -42,4 +43,7 @@ pub trait WalletTransactionBuilder: Send + Sync {

/// Obfuscates given signed transaction
fn obfuscate(&self, signed_transaction: SignedTransaction) -> Result<TxAux>;

/// Get a decrypted transaction by a given tx_id
fn decrypt_tx(&self, txid: TxId, private_key: &PrivateKey) -> Result<Transaction>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ use chain_core::tx::data::attribute::TxAttributes;
use chain_core::tx::data::output::TxOut;
use chain_core::tx::fee::FeeAlgorithm;
use chain_core::tx::TxAux;
use client_common::{ErrorKind, Result, ResultExt, SignedTransaction, Storage};
use client_common::{
ErrorKind, PrivateKey, Result, ResultExt, SignedTransaction, Storage, Transaction,
};

use crate::signer::WalletSignerManager;
use crate::transaction_builder::RawTransferTransactionBuilder;
use crate::{
SelectedUnspentTransactions, TransactionObfuscation, UnspentTransactions,
WalletTransactionBuilder,
};
use chain_core::tx::{data::TxId, TransactionId};

/// Default implementation of `TransactionBuilder`
///
Expand Down Expand Up @@ -71,6 +74,17 @@ where
fn obfuscate(&self, signed_transaction: SignedTransaction) -> Result<TxAux> {
self.transaction_obfuscation.encrypt(signed_transaction)
}

fn decrypt_tx(&self, txid: TxId, private_key: &PrivateKey) -> Result<Transaction> {
let tx = self
.transaction_obfuscation
.decrypt(&[txid], private_key)?
.iter()
.find(|t| t.id() == txid)
.map(Clone::clone)
.chain(|| (ErrorKind::InvalidInput, "can not find transaction"))?;
Ok(tx)
}
}

impl<S, F, O> DefaultWalletTransactionBuilder<S, F, O>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ use chain_core::tx::data::address::ExtendedAddr;
use chain_core::tx::data::attribute::TxAttributes;
use chain_core::tx::data::output::TxOut;
use chain_core::tx::TxAux;
use client_common::{ErrorKind, Result, SignedTransaction};
use client_common::{ErrorKind, PrivateKey, Result, SignedTransaction, Transaction};

use crate::{UnspentTransactions, WalletTransactionBuilder};
use chain_core::tx::data::TxId;

/// Implementation of `WalletTransactionBuilder` which always returns
/// permission denied
Expand All @@ -29,4 +30,8 @@ impl WalletTransactionBuilder for UnauthorizedWalletTransactionBuilder {
fn obfuscate(&self, _: SignedTransaction) -> Result<TxAux> {
Err(ErrorKind::PermissionDenied.into())
}

fn decrypt_tx(&self, _txid: TxId, _private_key: &PrivateKey) -> Result<Transaction> {
Err(ErrorKind::PermissionDenied.into())
}
}
15 changes: 15 additions & 0 deletions client-core/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,21 @@ pub trait WalletClient: Send + Sync {

/// Broadcasts a transaction to Crypto.com Chain
fn broadcast_transaction(&self, tx_aux: &TxAux) -> Result<BroadcastTxResponse>;

/// When receiver's view key not included in the transaction, the receiver can't collect the outputs.
/// The sender have to get the plain transaction and send it to the receiver by email or something
/// so that the receiver can sync it into the wallet DB and get the outputs.
///
/// # Return
///
/// base64 encoded of `Transaction` json string
fn export_plain_tx(&self, name: &str, passphras: &SecUtf8, txid: &str) -> Result<String>;

/// import a plain transaction, put the outputs of the transaction into wallet DB
///
/// # Return
/// the sum of unused outputs coin
fn import_plain_tx(&self, name: &str, passphrase: &SecUtf8, tx_str: &str) -> Result<Coin>;
}

/// Interface for a generic wallet for multi-signature transactions
Expand Down
Loading

0 comments on commit 6207d2c

Please sign in to comment.