grin/wallet/tests/transaction.rs
Ignotus Peverell d189770080 Wallet LMDB backend (#1151)
* Migrate main node store to LMDB

In preparation to using LMDB as a wallet database, migrate the
node db. There's no point in having 2 key-value stores.
In addition LMDB provides a few advantages as a node db, namely a
much faster build (compared to RocksDb), lesser dependencies and
transactions.

* Migrated p2p store to lmdb, stuff compiles

* More fixes, chain tests starting to pass

* Fixed txhashset rollback messing with block save and general batch delimitation. Chain tests passing.

* rustfmt

* LMDB max map size of 10MB isn't really workable. Half TB seems reasonable.

* Fix wallet tests

* Rather crucial commit was missing

* rustfmt

* Fixing new merged tests following lmdb changes

* rustfmt

* * Make txhashset validation read-only on fast sync to avoid having
a really long open transaction.
* Fix deadlock in new block processing, batch should always be
created within a txhashset lock (when they interact).

* Comment about batch and txhashset interlacing

* Fix store tests to use batch

* Externalize wallet config and seed

* Converted direct read access to file outputs map to an iterator

* Cleaned up and simplified wallet Backend trait:

* No more direct mutable access to internal structures (HashMap)
* Batch interface for all writes
* Remove unneeded read wrapper (read_wallet)

* rustfmt

* First (incomplete) pass at wallet LMDB backend

* Progressing on lmdb backent iml

* Added batch impl for LMDB wallet backend. Pretty much done with it, but not sure how to deal with commit (owned).

* rustfmt

* Wrapping LMDB batch around a refcell to work around borrow rules

* Compilation up to grin chain
2018-06-22 09:08:06 +01:00

209 lines
6.5 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_store as store;
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::Chain;
use chain::types::NoopAdapter;
use core::global::ChainTypes;
use core::{global, pow};
use util::LOGGER;
use wallet::libwallet::internal::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);
let db_env = Arc::new(store::new_env(dir_name.to_string()));
chain::Chain::init(
dir_name.to_string(),
db_env,
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.as_ref().unwrap(),
&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 satisfied 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.as_ref().unwrap(),
&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.as_ref().unwrap(),
&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.as_ref().unwrap(),
&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.as_ref().unwrap());
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
);
}