mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-22 03:51:08 +03:00
0967a5302b
* Add kernel commitments sum and kernel and output MMR sizes to block header * Sum a block without including previous sums, cleanup. Blocks are now summed and validated based on their own totals and not the totals since genesis. This allows to get rid of BlockSum and simplified the setting of a new block's roots, kernel sum and MMR sizes. Fixes #116 * Additional kernel MMR validation to check all prior header roots successively * Wallet tests fix
206 lines
6.4 KiB
Rust
206 lines
6.4 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::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);
|
|
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.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
|
|
);
|
|
}
|