Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'build_output' endpoint to owner api #641

Merged
merged 2 commits into from
Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions api/src/owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@ use ed25519_dalek::SecretKey as DalekSecretKey;
use uuid::Uuid;

use crate::config::{TorConfig, WalletConfig};
use crate::core::core::OutputFeatures;
use crate::core::global;
use crate::impls::HttpSlateSender;
use crate::impls::SlateSender as _;
use crate::keychain::{Identifier, Keychain};
use crate::libwallet::api_impl::owner_updater::{start_updater_log_thread, StatusMessage};
use crate::libwallet::api_impl::{owner, owner_updater};
use crate::libwallet::{
AcctPathMapping, Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, NodeHeightResult,
OutputCommitMapping, PaymentProof, Slate, Slatepack, SlatepackAddress, TxLogEntry, ViewWallet,
WalletInfo, WalletInst, WalletLCProvider,
AcctPathMapping, BuiltOutput, Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient,
NodeHeightResult, OutputCommitMapping, PaymentProof, Slate, Slatepack, SlatepackAddress,
TxLogEntry, ViewWallet, WalletInfo, WalletInst, WalletLCProvider,
};
use crate::util::logger::LoggingConfig;
use crate::util::secp::key::SecretKey;
Expand Down Expand Up @@ -2404,6 +2405,18 @@ where
) -> Result<(bool, bool), Error> {
owner::verify_payment_proof(self.wallet_inst.clone(), keychain_mask, proof)
}

/// Builds an output
pub fn build_output(
&self,
keychain_mask: Option<&SecretKey>,
features: OutputFeatures,
amount: u64,
) -> Result<BuiltOutput, Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::build_output(&mut **w, keychain_mask, features, amount)
}
}

/// attempt to send slate synchronously with TOR
Expand Down
62 changes: 59 additions & 3 deletions api/src/owner_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
use uuid::Uuid;

use crate::config::{TorConfig, WalletConfig};
use crate::core::core::OutputFeatures;
use crate::core::global;
use crate::keychain::{Identifier, Keychain};
use crate::libwallet::{
AcctPathMapping, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient, NodeHeightResult,
OutputCommitMapping, PaymentProof, Slate, SlateVersion, Slatepack, SlatepackAddress,
StatusMessage, TxLogEntry, VersionedSlate, ViewWallet, WalletInfo, WalletLCProvider,
AcctPathMapping, Amount, BuiltOutput, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient,
NodeHeightResult, OutputCommitMapping, PaymentProof, Slate, SlateVersion, Slatepack,
SlatepackAddress, StatusMessage, TxLogEntry, VersionedSlate, ViewWallet, WalletInfo,
WalletLCProvider,
};
use crate::util::logger::LoggingConfig;
use crate::util::secp::key::{PublicKey, SecretKey};
Expand Down Expand Up @@ -1831,6 +1833,50 @@ pub trait OwnerRpc {
```
*/
fn set_tor_config(&self, tor_config: Option<TorConfig>) -> Result<(), ErrorKind>;

/**
Networked version of [Owner::build_output](struct.Owner.html#method.build_output).
```
# grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!(
# r#"
{
"jsonrpc": "2.0",
"method": "build_output",
"params": {
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
"features": "Plain",
"amount": "60000000000"
},
"id": 1
}
# "#
# ,
# r#"
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"Ok": {
"blind": "089705aa74b638ee391e295d227c534a50dd58e603bca97a4404747cf8a5a189",
"key_id": "0300000000000000000000000000000000",
"output": {
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7",
"features": "Plain",
"proof": "4b5d6fb1b4d143fc50c83aef61c5410be760a395ed71f3424f7746bf5ee0539ae299569d99b73ea6583b1057834551faa0ac8cfe34c75431b86d6f37dec1ff070fc01f44babf0d3446781564ff7a143242ea67cb4ff7b11fe399735695c3fe70b40b71f31b04cf73b1d1f3430fb53a8c9f990fae48c09b42f8212d60a2d3ce0b8ea4dc0d37a82c3f328162ab8d50f48c28cb9a721a87a40aa3915bf9fffc0cd820e15b758e8565ad7fbf22d03711dc83f98e7c9f955d9398a1c75bc96df2ee64751592953cced38527b3f68282d2ca2fdf2994fbd93a1642fb9d265d57c3cf7df01501da569f2b4e606a1c3084c807a39947a3e1fd41b0647891e1f64842a2b98e694b93857e30691e0b0bca7bc49dec9d6af1003a40b3431ae0bcae8454a438523d066dcac4f194d8370c5ba6567830f302e1ec2607b8d1720bb6c6c57c549f1a3ef7ad2b54dfdd0178329e0723b8a55b438a1e43a984c072d6505aa5e193042d9703484c8383e78d9553684fad5e399f11f8ae6577e4ac4e3c2478e3fd8df0164600b4816b2167c2bf5b9fd7dd29cc1041fccbf1392240fd7c1dc39dd1ebc86b882a383dfe683e9f029d40b2829e3bf56b9760e1d81b7ad4a9066b1c01ccbea6b196154443cacedaccd5ff4fd25cbd9a8f0d271d5688bbe4b956fd34d3413d0478ac9400f6f1ff3890dea10be072d2d48bfa69a6e1e1b6fffaa9db4663eb1ecc26da331072877eb6d4a05a41584d44ed5d2a96a98727563bf180768940c99a15e9183ae927f47f2c0e13d9c00d7ebf0dacb1b6c139d3e18701d10c9d1ef300eeeab756eaa4584c3f5fb42793f7c2517601ae31d887c177eec8bce35c0aa16ba6991fd885deb9ff7b44ffd489f8e9e9d0717141501143c027d33e8a4baf6d85c859ff8a04d1aafbb3d1a97dc6c8ee3642ec41b8e43a137b43c8e60d69a6f19eb9749e"
}
}
}
}
# "#
# , 0, false, false, false, false);
```
*/
fn build_output(
&self,
token: Token,
features: OutputFeatures,
amount: Amount,
) -> Result<BuiltOutput, ErrorKind>;
}

impl<L, C, K> OwnerRpc for Owner<L, C, K>
Expand Down Expand Up @@ -2252,6 +2298,16 @@ where
Owner::set_tor_config(self, tor_config);
Ok(())
}

fn build_output(
&self,
token: Token,
features: OutputFeatures,
amount: Amount,
) -> Result<BuiltOutput, ErrorKind> {
Owner::build_output(self, (&token.keychain_mask).as_ref(), features, amount.0)
.map_err(|e| e.kind())
}
}

/// helper to set up a real environment to run integrated doctests
Expand Down
101 changes: 101 additions & 0 deletions controller/tests/build_output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright 2021 The Grin Developers
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[macro_use]
extern crate log;
extern crate grin_wallet_controller as wallet;
extern crate grin_wallet_impls as impls;
extern crate grin_wallet_util;

use grin_wallet_libwallet as libwallet;
use grin_wallet_util::grin_core::core::OutputFeatures;
use grin_wallet_util::grin_keychain::{
mnemonic, BlindingFactor, ExtKeychain, ExtKeychainPath, Keychain, SwitchCommitmentType,
};
use grin_wallet_util::grin_util::{secp, ZeroingString};
use impls::test_framework::LocalWalletClient;
use rand::{thread_rng, Rng};
use std::sync::atomic::Ordering;
use std::thread;
use std::time::Duration;

#[macro_use]
mod common;
use common::{clean_output_dir, create_wallet_proxy, setup};

fn build_output_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
// Generate seed so we can verify the blinding factor is derived correctly
let seed: [u8; 32] = thread_rng().gen();
let keychain = ExtKeychain::from_seed(&seed, false).unwrap();
let mnemonic = mnemonic::from_entropy(&seed).unwrap();

// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy = create_wallet_proxy(test_dir);
let stopper = wallet_proxy.running.clone();

create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
Some(ZeroingString::from(mnemonic)),
&mut wallet_proxy,
false
);

let mask1 = (&mask1_i).as_ref();

// Set the wallet proxy listener running
thread::spawn(move || {
if let Err(e) = wallet_proxy.run() {
error!("Wallet Proxy error: {}", e);
}
});

let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
let features = OutputFeatures::Plain;
let amount = 60_000_000_000;
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
let built_output = sender_api.build_output(m, features, amount)?;

let key_id = built_output.key_id;
assert_eq!(key_id.to_path(), ExtKeychainPath::new(3, 0, 0, 0, 0));

let blind = built_output.blind;
let key = keychain.derive_key(amount, &key_id, SwitchCommitmentType::Regular)?;
assert_eq!(blind, BlindingFactor::from_secret_key(key.clone()));

let output = built_output.output;
assert_eq!(output.features(), features);
assert_eq!(output.commitment(), secp.commit(amount, key)?);
output.verify_proof()?;

Ok(())
})?;

// let logging finish
stopper.store(false, Ordering::Relaxed);
thread::sleep(Duration::from_millis(200));
Ok(())
}

#[test]
fn build_output() {
let test_dir = "test_output/build_output";
setup(test_dir);
if let Err(e) = build_output_test_impl(test_dir) {
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
}
clean_output_dir(test_dir);
}
50 changes: 45 additions & 5 deletions libwallet/src/api_impl/owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,23 @@
use uuid::Uuid;

use crate::grin_core::core::hash::Hashed;
use crate::grin_core::core::Transaction;
use crate::grin_core::core::{Output, OutputFeatures, Transaction};
use crate::grin_core::libtx::proof;
use crate::grin_keychain::ViewKey;
use crate::grin_util::secp::key::SecretKey;
use crate::grin_util::Mutex;
use crate::grin_util::ToHex;
use crate::util::{OnionV3Address, OnionV3AddressError};

use crate::api_impl::owner_updater::StatusMessage;
use crate::grin_keychain::{Identifier, Keychain};
use crate::grin_keychain::{BlindingFactor, Identifier, Keychain, SwitchCommitmentType};
use crate::internal::{keys, scan, selection, tx, updater};
use crate::slate::{PaymentInfo, Slate, SlateState};
use crate::types::{AcctPathMapping, NodeClient, TxLogEntry, WalletBackend, WalletInfo};
use crate::{
address, wallet_lock, InitTxArgs, IssueInvoiceTxArgs, NodeHeightResult, OutputCommitMapping,
PaymentProof, ScannedBlockInfo, Slatepack, SlatepackAddress, Slatepacker, SlatepackerArgs,
TxLogEntryType, ViewWallet, WalletInitStatus, WalletInst, WalletLCProvider,
address, wallet_lock, BuiltOutput, InitTxArgs, IssueInvoiceTxArgs, NodeHeightResult,
OutputCommitMapping, PaymentProof, ScannedBlockInfo, Slatepack, SlatepackAddress, Slatepacker,
SlatepackerArgs, TxLogEntryType, ViewWallet, WalletInitStatus, WalletInst, WalletLCProvider,
};
use crate::{Error, ErrorKind};
use ed25519_dalek::PublicKey as DalekPublicKey;
Expand Down Expand Up @@ -1376,3 +1377,42 @@ where
}
Ok(true)
}

/// Builds an output for the wallet's next available key
pub fn build_output<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
features: OutputFeatures,
amount: u64,
) -> Result<BuiltOutput, Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let k = w.keychain(keychain_mask)?;

let key_id = keys::next_available_key(&mut *w, keychain_mask)?;

let blind = k.derive_key(amount, &key_id, SwitchCommitmentType::Regular)?;
let commit = k.secp().commit(amount, blind.clone())?;

let proof_builder = proof::ProofBuilder::new(&k);
let proof = proof::create(
&k,
&proof_builder,
amount,
&key_id,
SwitchCommitmentType::Regular,
commit,
None,
)?;

let output = Output::new(features, commit, proof);

Ok(BuiltOutput {
blind: BlindingFactor::from_secret_key(blind),
key_id: key_id,
output: output,
})
}
23 changes: 22 additions & 1 deletion libwallet/src/api_impl/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@

//! Types specific to the wallet api, mostly argument serialization

use crate::grin_core::core::Output;
use crate::grin_core::libtx::secp_ser;
use crate::grin_keychain::Identifier;
use crate::grin_keychain::{BlindingFactor, Identifier};
use crate::grin_util::secp::pedersen;
use crate::slate_versions::ser as dalek_ser;
use crate::slate_versions::SlateVersion;
Expand All @@ -24,6 +25,11 @@ use crate::SlatepackAddress;

use ed25519_dalek::Signature as DalekSignature;

/// Type for storing amounts (in nanogrins).
/// Serializes as a string but can deserialize from a string or u64.
#[derive(Serialize, Deserialize)]
pub struct Amount(#[serde(with = "secp_ser::string_or_u64")] pub u64);

/// V2 Init / Send TX API Args
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct InitTxArgs {
Expand Down Expand Up @@ -216,3 +222,18 @@ pub struct PaymentProof {
#[serde(with = "dalek_ser::dalek_sig_serde")]
pub sender_sig: DalekSignature,
}

/// Build output result
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BuiltOutput {
/// Blinding Factor
#[serde(
serialize_with = "secp_ser::as_hex",
deserialize_with = "secp_ser::blind_from_hex"
)]
pub blind: BlindingFactor,
/// Key Identifier
pub key_id: Identifier,
/// Output
pub output: Output,
}
4 changes: 2 additions & 2 deletions libwallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ pub use crate::slatepack::{
};
pub use api_impl::owner_updater::StatusMessage;
pub use api_impl::types::{
BlockFees, InitTxArgs, InitTxSendArgs, IssueInvoiceTxArgs, NodeHeightResult,
OutputCommitMapping, PaymentProof, VersionInfo,
Amount, BlockFees, BuiltOutput, InitTxArgs, InitTxSendArgs, IssueInvoiceTxArgs,
NodeHeightResult, OutputCommitMapping, PaymentProof, VersionInfo,
};
pub use internal::scan::scan;
pub use slate_versions::ser as dalek_ser;
Expand Down