grin/wallet/src/receiver.rs
Antioch Peverell 4fda7a6899
Minimal Transaction Pool (#1067)
* verify a tx like we verify a block (experimental)

* first minimal_pool test up and running but not testing what we need to

* rework tx_pool validation to use txhashset extension

* minimal tx pool wired up but rough

* works locally (rough statew though)
delete "legacy" pool and graph code

* rework the new pool into TransactionPool and Pool impls

* rework pool to store pool entries
with associated timer and source etc.

* all_transactions

* extra_txs so we can validate stempool against existing txpool

* rework reconcile_block

* txhashset apply_raw_tx can now rewind to a checkpoint (prev raw tx)

* wip - txhashset tx tests

* more flexible rewind on MMRs

* add tests to cover apply_raw_txs on txhashset extension

* add_to_stempool and add_to_txpool

* deaggregate multi kernel tx when adding to txpoool

* handle freshness in stempool
handle propagation of stempool txs via dandelion monitor

* patience timer and fluff if we cannot propagate
to next relay

* aggregate and fluff stempool is we have no relay

* refactor coinbase maturity

* rewrote basic tx pool tests to use a real txhashset via chain adapter

* rework dandelion monitor to reflect recent discussion
works locally but needs a cleanup

* refactor dandelion_monitor - split out phases

* more pool test coverage

* remove old test code from pool (still wip)

* block_building and block_reconciliation tests

* tracked down chain test failure...

* fix test_coinbase_maturity

* dandelion_monitor now runs...

* refactor dandelion config, shared across p2p and pool components

* fix pool tests with new config

* fix p2p tests

* rework tx pool to deal with duplicate commitments (testnet2 limitation)

* cleanup and address some PR feedback

* add big comment about pre_tx...
2018-05-30 16:57:13 -04:00

171 lines
4.6 KiB
Rust

// Copyright 2018 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.
//! Provides the JSON/HTTP API for wallets to receive payments. Because
//! receiving money in MimbleWimble requires an interactive exchange, a
//! wallet server that's running at all time is required in many cases.
use std::sync::{Arc, RwLock};
use bodyparser;
use iron::prelude::*;
use iron::status;
use iron::Handler;
use serde_json;
use api;
use core::consensus::reward;
use core::core::{Output, TxKernel};
use core::global;
use failure::{Fail, ResultExt};
use libtx::{reward, slate::Slate};
use libwallet::types::*;
use libwallet::{keys, selection};
use util::LOGGER;
/// Dummy wrapper for the hex-encoded serialized transaction.
#[derive(Serialize, Deserialize)]
pub struct TxWrapper {
pub tx_hex: String,
}
/// Component used to receive coins, implements all the receiving end of the
/// wallet REST API as well as some of the command-line operations.
#[derive(Clone)]
pub struct WalletReceiver<T>
where
T: WalletBackend,
{
pub wallet: Arc<RwLock<T>>,
}
impl<T> WalletReceiver<T>
where
T: WalletBackend,
{
fn handle_send(&self, wallet: &mut T, slate: &mut Slate) -> Result<(), Error> {
// create an output using the amount in the slate
let (_, mut context, receiver_create_fn) =
selection::build_recipient_output_with_slate(wallet, slate).unwrap();
// fill public keys
let _ = slate
.fill_round_1(
wallet.keychain(),
&mut context.sec_key,
&context.sec_nonce,
1,
)
.context(ErrorKind::LibWalletError)?;
// perform partial sig
let _ = slate
.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 1)
.context(ErrorKind::LibWalletError)?;
// Save output in wallet
let _ = receiver_create_fn(wallet);
Ok(())
}
}
impl<T> Handler for WalletReceiver<T>
where
T: WalletBackend + Send + Sync + 'static,
{
fn handle(&self, req: &mut Request) -> IronResult<Response> {
let struct_body = req.get::<bodyparser::Struct<Slate>>();
let mut wallet = self.wallet.write().unwrap();
if let Ok(Some(mut slate)) = struct_body {
let _ = self.handle_send(&mut wallet, &mut slate)
.map_err(|e| {
error!(
LOGGER,
"Handling send -> Problematic slate, looks like this: {:?}", slate
);
e.context(api::ErrorKind::Internal(
"Error processing partial transaction".to_owned(),
))
})
.unwrap();
let json = serde_json::to_string(&slate).unwrap();
Ok(Response::with((status::Ok, json)))
} else {
Ok(Response::with((status::BadRequest, "")))
}
}
}
//TODO: Split up the output creation and the wallet insertion
/// Build a coinbase output and the corresponding kernel
pub fn receive_coinbase<T>(
wallet: &mut T,
block_fees: &BlockFees,
) -> Result<(Output, TxKernel, BlockFees), Error>
where
T: WalletBackend,
{
let root_key_id = wallet.keychain().root_key_id();
let height = block_fees.height;
let lock_height = height + global::coinbase_maturity();
// Now acquire the wallet lock and write the new output.
let (key_id, derivation) = wallet.with_wallet(|wallet_data| {
let key_id = block_fees.key_id();
let (key_id, derivation) = match key_id {
Some(key_id) => keys::retrieve_existing_key(wallet_data, key_id),
None => keys::next_available_key(wallet_data),
};
// track the new output and return the stuff needed for reward
wallet_data.add_output(OutputData {
root_key_id: root_key_id.clone(),
key_id: key_id.clone(),
n_child: derivation,
value: reward(block_fees.fees),
status: OutputStatus::Unconfirmed,
height: height,
lock_height: lock_height,
is_coinbase: true,
block: None,
merkle_proof: None,
});
(key_id, derivation)
})?;
debug!(
LOGGER,
"receive_coinbase: built candidate output - {:?}, {}",
key_id.clone(),
derivation,
);
let mut block_fees = block_fees.clone();
block_fees.key_id = Some(key_id.clone());
debug!(LOGGER, "receive_coinbase: {:?}", block_fees);
let (out, kern) = reward::output(
&wallet.keychain(),
&key_id,
block_fees.fees,
block_fees.height,
).unwrap();
/* .context(ErrorKind::Keychain)?; */
Ok((out, kern, block_fees))
}