grin/wallet/tests/transaction.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

206 lines
6.3 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.
//! tests for transactions building within libtx
extern crate grin_chain as chain;
extern crate grin_core as core;
extern crate grin_keychain as keychain;
extern crate grin_util as util;
extern crate grin_wallet as wallet;
extern crate rand;
#[macro_use]
extern crate slog;
extern crate serde;
extern crate time;
extern crate uuid;
mod common;
use std::fs;
use std::sync::Arc;
use chain::types::*;
use chain::Chain;
use core::global::ChainTypes;
use core::{global, pow};
use util::LOGGER;
use wallet::libwallet::selection;
fn clean_output_dir(test_dir: &str) {
let _ = fs::remove_dir_all(test_dir);
}
fn setup(test_dir: &str, chain_dir: &str) -> Chain {
util::init_test_logger();
clean_output_dir(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
let genesis_block = pow::mine_genesis_block().unwrap();
let dir_name = format!("{}/{}", test_dir, chain_dir);
chain::Chain::init(
dir_name.to_string(),
Arc::new(NoopAdapter {}),
genesis_block,
pow::verify_size,
).unwrap()
}
/// Build and test new version of sending API
#[test]
fn build_transaction() {
let chain = setup("test_output", "build_transaction_2/.grin");
let mut wallet1 = common::create_wallet("test_output/build_transaction_2/wallet1");
let mut wallet2 = common::create_wallet("test_output/build_transaction_2/wallet2");
common::award_blocks_to_wallet(&chain, &mut wallet1, 10);
// Wallet 1 has 600 Grins, wallet 2 has 0. Create a transaction that sends
// 300 Grins from wallet 1 to wallet 2, using libtx
// Get lock height
let chain_tip = chain.head().unwrap();
let amount = 300_000_000_000;
let min_confirmations = 3;
// ensure outputs we're selecting are up to date
let res = common::refresh_output_state_local(&mut wallet1, &chain);
if let Err(e) = res {
panic!("Unable to refresh sender wallet outputs: {}", e);
}
// TRANSACTION WORKFLOW STARTS HERE
// Sender selects outputs into a new slate and save our corresponding IDs in
// a transaction context. The secret key in our transaction context will be
// randomly selected. This returns the public slate, and a closure that locks
// our inputs and outputs once we're convinced the transaction exchange went
// according to plan
// This function is just a big helper to do all of that, in theory
// this process can be split up in any way
let (mut slate, mut sender_context, sender_lock_fn) = selection::build_send_tx_slate(
&mut wallet1,
2,
amount,
chain_tip.height,
min_confirmations,
chain_tip.height,
1000,
true,
).unwrap();
// Generate a kernel offset and subtract from our context's secret key. Store
// the offset in the slate's transaction kernel, and adds our public key
// information to the slate
let _ = slate
.fill_round_1(
&wallet1.keychain,
&mut sender_context.sec_key,
&sender_context.sec_nonce,
0,
)
.unwrap();
debug!(LOGGER, "Transaction Slate after step 1: sender initiation");
debug!(LOGGER, "-----------------------------------------");
debug!(LOGGER, "{:?}", slate);
// Now, just like the sender did, recipient is going to select a target output,
// add it to the transaction, and keep track of the corresponding wallet
// Identifier Again, this is a helper to do that, which returns a closure that
// creates the output when we're satisified the process was successful
let (_, mut recp_context, receiver_create_fn) =
selection::build_recipient_output_with_slate(&mut wallet2, &mut slate).unwrap();
let _ = slate
.fill_round_1(
&wallet2.keychain,
&mut recp_context.sec_key,
&recp_context.sec_nonce,
1,
)
.unwrap();
// recipient can proceed to round 2 now
let _ = receiver_create_fn(&mut wallet2);
let _ = slate
.fill_round_2(
&wallet1.keychain,
&recp_context.sec_key,
&recp_context.sec_nonce,
1,
)
.unwrap();
debug!(
LOGGER,
"Transaction Slate after step 2: receiver initiation"
);
debug!(LOGGER, "-----------------------------------------");
debug!(LOGGER, "{:?}", slate);
// SENDER Part 3: Sender confirmation
let _ = slate
.fill_round_2(
&wallet1.keychain,
&sender_context.sec_key,
&sender_context.sec_nonce,
0,
)
.unwrap();
debug!(LOGGER, "PartialTx after step 3: sender confirmation");
debug!(LOGGER, "--------------------------------------------");
debug!(LOGGER, "{:?}", slate);
// Final transaction can be built by anyone at this stage
let res = slate.finalize(&wallet1.keychain);
if let Err(e) = res {
panic!("Error creating final tx: {:?}", e);
}
debug!(LOGGER, "Final transaction is:");
debug!(LOGGER, "--------------------------------------------");
debug!(LOGGER, "{:?}", slate.tx);
// All okay, lock sender's outputs
let _ = sender_lock_fn(&mut wallet1);
// Insert this transaction into a new block, then mine till confirmation
common::award_block_to_wallet(&chain, vec![&slate.tx], &mut wallet1);
common::award_blocks_to_wallet(&chain, &mut wallet1, 5);
// Refresh wallets
let res = common::refresh_output_state_local(&mut wallet2, &chain);
if let Err(e) = res {
panic!("Error refreshing output state for wallet: {:?}", e);
}
// check recipient wallet
let chain_tip = chain.head().unwrap();
let balances = common::get_wallet_balances(&mut wallet2, chain_tip.height).unwrap();
assert_eq!(balances.3, 300_000_000_000);
// check sender wallet
let res = common::refresh_output_state_local(&mut wallet1, &chain);
if let Err(e) = res {
panic!("Error refreshing output state for wallet: {:?}", e);
}
let balances = common::get_wallet_balances(&mut wallet1, chain_tip.height).unwrap();
println!("tip height: {:?}", chain_tip.height);
println!("Sender balances: {:?}", balances);
// num blocks * grins per block, and wallet1 mined the fee
assert_eq!(
balances.3,
(chain_tip.height - min_confirmations) * 60_000_000_000 - amount
);
}