mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-05-09 18:41:13 +03:00
TTL Implementation (#261)
* refactor address generation code into libwallet, bool to flag whether to include proof, add sender address in init_send_tx * rustfmt * require payment proof addr as part of init_tx * rustfmt * store payment proof on sender transaction side * rustfmt * change sig to ed25519 sig * rustfmt * add message creation and signature * rustfmt * add payment proof verification function * rustfmt * validate proof on sender side, store proof * rustfmt * fix json tests * fixes and updates to tests * added API functions for converting and retrieving proof addresses * rustfmt * add payment proof to init_send_tx example * rustfmt * incorrect comment * add commands for requesting payment proofs * rustfmt * wire up payment proofs into command line * rustfmt * add address command * rustfmt * added tor sending from owner api * rustfmt * add TTL to slate, transaction log * ttl cutoff tests * add ttl check to recieve functions, add tests, modify doctests * rustfmt * fixes from upstream changes and test fixes * rustfmt * remove unnecessary block header version check
This commit is contained in:
parent
9fd1d49dda
commit
45480392da
25 changed files with 653 additions and 327 deletions
Cargo.lock
api/src
config/src
controller
impls/src
libwallet/src
src/bin
tests/data
603
Cargo.lock
generated
603
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -133,7 +133,7 @@ pub trait ForeignRpc {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "4",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
|
@ -253,7 +253,7 @@ pub trait ForeignRpc {
|
|||
"fee": "7000000",
|
||||
"height": "5",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
{
|
||||
|
@ -283,7 +283,7 @@ pub trait ForeignRpc {
|
|||
"height": "5",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
|
@ -420,7 +420,7 @@ pub trait ForeignRpc {
|
|||
"fee": "7000000",
|
||||
"height": "5",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
{
|
||||
|
@ -455,7 +455,7 @@ pub trait ForeignRpc {
|
|||
"height": "5",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
|
|
|
@ -30,8 +30,9 @@ use crate::libwallet::{
|
|||
NodeHeightResult, OutputCommitMapping, Slate, TxLogEntry, WalletInfo, WalletInst,
|
||||
WalletLCProvider,
|
||||
};
|
||||
use crate::util::logger::LoggingConfig;
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::{from_hex, static_secp_instance, LoggingConfig, Mutex, ZeroingString};
|
||||
use crate::util::{from_hex, static_secp_instance, Mutex, ZeroingString};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
use std::sync::Arc;
|
||||
|
|
|
@ -239,6 +239,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"num_outputs": 1,
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
"stored_tx": null,
|
||||
"ttl_cutoff_height": null,
|
||||
"tx_slate_id": null,
|
||||
"payment_proof": null,
|
||||
"tx_type": "ConfirmedCoinbase"
|
||||
|
@ -258,6 +259,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"num_outputs": 1,
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
"stored_tx": null,
|
||||
"ttl_cutoff_height": null,
|
||||
"tx_slate_id": null,
|
||||
"payment_proof": null,
|
||||
"tx_type": "ConfirmedCoinbase"
|
||||
|
@ -343,6 +345,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"message": "my message",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": null,
|
||||
"ttl_blocks": null,
|
||||
"send_args": null
|
||||
}
|
||||
},
|
||||
|
@ -361,7 +364,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
|
@ -448,7 +451,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
|
@ -514,7 +517,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
|
@ -565,6 +568,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"message": "Ok, here are your grins",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": null,
|
||||
"ttl_blocks": null,
|
||||
"send_args": null
|
||||
}
|
||||
],
|
||||
|
@ -583,7 +587,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
|
@ -671,7 +675,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "4",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
|
@ -760,7 +764,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
},
|
||||
"num_participants": 2,
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"tx": {
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
|
@ -835,7 +839,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"fee": "7000000",
|
||||
"height": "5",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"lock_height": "0",
|
||||
"num_participants": 2,
|
||||
|
@ -1116,7 +1120,7 @@ pub trait OwnerRpc: Sync + Send {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "4",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
|
|
|
@ -25,8 +25,9 @@ use crate::libwallet::{
|
|||
OutputCommitMapping, Slate, SlateVersion, StatusMessage, TxLogEntry, VersionedSlate,
|
||||
WalletInfo, WalletLCProvider,
|
||||
};
|
||||
use crate::util::logger::LoggingConfig;
|
||||
use crate::util::secp::key::{PublicKey, SecretKey};
|
||||
use crate::util::{static_secp_instance, LoggingConfig, ZeroingString};
|
||||
use crate::util::{static_secp_instance, ZeroingString};
|
||||
use crate::{ECDHPubkey, Owner, PubAddress, Token};
|
||||
use easy_jsonrpc_mw;
|
||||
use rand::thread_rng;
|
||||
|
@ -263,6 +264,7 @@ pub trait OwnerRpcS {
|
|||
"num_outputs": 1,
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
"stored_tx": null,
|
||||
"ttl_cutoff_height": null,
|
||||
"tx_slate_id": null,
|
||||
"payment_proof": null,
|
||||
"tx_type": "ConfirmedCoinbase"
|
||||
|
@ -282,6 +284,7 @@ pub trait OwnerRpcS {
|
|||
"num_outputs": 1,
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
"stored_tx": null,
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"tx_slate_id": null,
|
||||
"tx_type": "ConfirmedCoinbase"
|
||||
|
@ -374,6 +377,7 @@ pub trait OwnerRpcS {
|
|||
"message": "my message",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": "d03c09e9c19bb74aa9ea44e0fe5ae237a9bf40bddf0941064a80913a4459c8bb",
|
||||
"ttl_blocks": null,
|
||||
"send_args": null
|
||||
}
|
||||
},
|
||||
|
@ -392,7 +396,7 @@ pub trait OwnerRpcS {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": {
|
||||
"receiver_address": "d03c09e9c19bb74aa9ea44e0fe5ae237a9bf40bddf0941064a80913a4459c8bb",
|
||||
|
@ -484,7 +488,7 @@ pub trait OwnerRpcS {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
|
@ -555,7 +559,7 @@ pub trait OwnerRpcS {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
|
@ -606,6 +610,7 @@ pub trait OwnerRpcS {
|
|||
"message": "Ok, here are your grins",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": null,
|
||||
"ttl_blocks": null,
|
||||
"send_args": null
|
||||
}
|
||||
},
|
||||
|
@ -624,7 +629,7 @@ pub trait OwnerRpcS {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
|
@ -715,7 +720,7 @@ pub trait OwnerRpcS {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "4",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
|
@ -847,7 +852,7 @@ pub trait OwnerRpcS {
|
|||
"fee": "7000000",
|
||||
"height": "5",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"participant_data": [
|
||||
{
|
||||
"id": "0",
|
||||
|
@ -882,7 +887,7 @@ pub trait OwnerRpcS {
|
|||
"height": "5",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
|
@ -1180,7 +1185,7 @@ pub trait OwnerRpcS {
|
|||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "4",
|
||||
"ttl_cutoff_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
{
|
||||
|
|
|
@ -29,7 +29,7 @@ use crate::comments::insert_comments;
|
|||
use crate::core::global;
|
||||
use crate::types::{ConfigError, GlobalWalletConfig, GlobalWalletConfigMembers};
|
||||
use crate::types::{TorConfig, WalletConfig};
|
||||
use crate::util::LoggingConfig;
|
||||
use crate::util::logger::LoggingConfig;
|
||||
|
||||
/// Wallet configuration file name
|
||||
pub const WALLET_CONFIG_FILE_NAME: &'static str = "grin-wallet.toml";
|
||||
|
|
|
@ -19,7 +19,7 @@ use std::io;
|
|||
use std::path::PathBuf;
|
||||
|
||||
use crate::core::global::ChainTypes;
|
||||
use crate::util::LoggingConfig;
|
||||
use crate::util::logger::LoggingConfig;
|
||||
|
||||
/// Command-line wallet configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
|
|
|
@ -58,14 +58,14 @@ fn check_middleware(
|
|||
// allow coinbases to be built regardless
|
||||
ForeignCheckMiddlewareFn::BuildCoinbase => Ok(()),
|
||||
_ => {
|
||||
let mut bhv = 1;
|
||||
let mut bhv = 2;
|
||||
if let Some(n) = node_version_info {
|
||||
bhv = n.block_header_version;
|
||||
}
|
||||
if let Some(s) = slate {
|
||||
if s.version_info.version < CURRENT_SLATE_VERSION
|
||||
|| (bhv == 1 && s.version_info.block_header_version != 1)
|
||||
|| (bhv > 1 && s.version_info.block_header_version < GRIN_BLOCK_HEADER_VERSION)
|
||||
// || (bhv == 3 && s.version_info.block_header_version != 3)
|
||||
|| (bhv > 3 && s.version_info.block_header_version < GRIN_BLOCK_HEADER_VERSION)
|
||||
{
|
||||
Err(ErrorKind::Compatibility(
|
||||
"Incoming Slate is not compatible with this wallet. \
|
||||
|
|
|
@ -152,6 +152,7 @@ pub fn txs(
|
|||
bMG->"Type",
|
||||
bMG->"Shared Transaction Id",
|
||||
bMG->"Creation Time",
|
||||
bMG->"TTL Cutoff Height",
|
||||
bMG->"Confirmed?",
|
||||
bMG->"Confirmation Time",
|
||||
bMG->"Num. \nInputs",
|
||||
|
@ -173,6 +174,10 @@ pub fn txs(
|
|||
};
|
||||
let entry_type = format!("{}", t.tx_type);
|
||||
let creation_ts = format!("{}", t.creation_ts.format("%Y-%m-%d %H:%M:%S"));
|
||||
let ttl_cutoff_height = match t.ttl_cutoff_height {
|
||||
Some(b) => format!("{}", b),
|
||||
None => "None".to_owned(),
|
||||
};
|
||||
let confirmation_ts = match t.confirmation_ts {
|
||||
Some(m) => format!("{}", m.format("%Y-%m-%d %H:%M:%S")),
|
||||
None => "None".to_owned(),
|
||||
|
@ -212,6 +217,7 @@ pub fn txs(
|
|||
bFC->entry_type,
|
||||
bFC->slate_id,
|
||||
bFB->creation_ts,
|
||||
bFB->ttl_cutoff_height,
|
||||
bFC->confirmed,
|
||||
bFB->confirmation_ts,
|
||||
bFC->num_inputs,
|
||||
|
|
182
controller/tests/ttl_cutoff.rs
Normal file
182
controller/tests/ttl_cutoff.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2019 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 ttl_cutoff blocks
|
||||
#[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 impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{InitTxArgs, Slate, TxLogEntryType};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Test cutoff block times
|
||||
fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client2,
|
||||
wallet2,
|
||||
mask2_i,
|
||||
test_dir,
|
||||
"wallet2",
|
||||
None,
|
||||
&mut wallet_proxy,
|
||||
false
|
||||
);
|
||||
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// few values to keep things shorter
|
||||
|
||||
// Do some mining
|
||||
let bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let amount = 60_000_000_000;
|
||||
let mut slate = Slate::blank(1);
|
||||
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
ttl_blocks: Some(2),
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate, 0)?;
|
||||
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
let tx = txs[0].clone();
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(12));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Now mine past the block, and check again. Transaction should be gone.
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false);
|
||||
|
||||
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
let tx = txs[0].clone();
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(12));
|
||||
assert!(tx.tx_type == TxLogEntryType::TxSentCancelled);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Should also be gone in wallet 2, and output gone
|
||||
wallet::controller::owner_single_use(wallet2.clone(), mask2, |sender_api, m| {
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
let tx = txs[0].clone();
|
||||
let outputs = sender_api.retrieve_outputs(m, false, true, None)?.1;
|
||||
assert_eq!(outputs.len(), 0);
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(12));
|
||||
assert!(tx.tx_type == TxLogEntryType::TxReceivedCancelled);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// try again, except try and send off the transaction for completion beyond the expiry
|
||||
let mut slate = Slate::blank(1);
|
||||
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
ttl_blocks: Some(2),
|
||||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
sender_api.tx_lock_outputs(m, &slate_i, 0)?;
|
||||
slate = slate_i;
|
||||
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
let tx = txs[0].clone();
|
||||
|
||||
assert_eq!(tx.ttl_cutoff_height, Some(14));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Mine past the ttl block and try to send
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false);
|
||||
|
||||
// Wallet 2 will need to have updated past the TTL
|
||||
wallet::controller::owner_single_use(wallet2.clone(), mask2, |sender_api, m| {
|
||||
let (_, _) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// And when wallet 1 sends, should be rejected
|
||||
wallet::controller::owner_single_use(wallet1.clone(), mask1, |_sender_api, _m| {
|
||||
let res = client1.send_tx_slate_direct("wallet2", &slate);
|
||||
println!("Send after TTL result is: {:?}", res);
|
||||
assert!(res.is_err());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// let logging finish
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ttl_cutoff() {
|
||||
let test_dir = "test_output/ttl_cutoff";
|
||||
setup(test_dir);
|
||||
if let Err(e) = ttl_cutoff_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
|
@ -27,7 +27,7 @@ use crate::util::secp::key::SecretKey;
|
|||
use crate::util::ZeroingString;
|
||||
use crate::LMDBBackend;
|
||||
use failure::ResultExt;
|
||||
use grin_wallet_util::grin_util::LoggingConfig;
|
||||
use grin_wallet_util::grin_util::logger::LoggingConfig;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
|
|
@ -225,14 +225,24 @@ where
|
|||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
let mask = wallet.2.clone();
|
||||
// receive tx
|
||||
foreign::receive_tx(
|
||||
match foreign::receive_tx(
|
||||
&mut **w,
|
||||
(&mask).as_ref(),
|
||||
&Slate::from(slate),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
)?
|
||||
) {
|
||||
Err(e) => {
|
||||
return Ok(WalletProxyMessage {
|
||||
sender_id: m.dest,
|
||||
dest: m.sender_id,
|
||||
method: m.method,
|
||||
body: serde_json::to_string(&format!("Error: {}", e)).unwrap(),
|
||||
})
|
||||
}
|
||||
Ok(s) => s,
|
||||
}
|
||||
};
|
||||
|
||||
Ok(WalletProxyMessage {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
//! Generic implementation of owner API functions
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::api_impl::owner::check_ttl;
|
||||
use crate::grin_keychain::Keychain;
|
||||
use crate::grin_util::secp::key::SecretKey;
|
||||
use crate::internal::{tx, updater};
|
||||
|
@ -70,6 +71,7 @@ where
|
|||
K: Keychain + 'a,
|
||||
{
|
||||
let mut ret_slate = slate.clone();
|
||||
check_ttl(w, &ret_slate)?;
|
||||
let parent_key_id = match dest_acct_name {
|
||||
Some(d) => {
|
||||
let pm = w.get_acct_path(d.to_owned())?;
|
||||
|
@ -143,6 +145,7 @@ where
|
|||
K: Keychain + 'a,
|
||||
{
|
||||
let mut sl = slate.clone();
|
||||
check_ttl(w, &sl)?;
|
||||
let context = w.get_private_context(keychain_mask, sl.id.as_bytes(), 1)?;
|
||||
tx::complete_tx(&mut *w, keychain_mask, &mut sl, 1, &context)?;
|
||||
tx::update_stored_tx(&mut *w, keychain_mask, &context, &mut sl, true)?;
|
||||
|
|
|
@ -224,7 +224,7 @@ where
|
|||
None => None,
|
||||
};
|
||||
|
||||
let mut slate = tx::new_tx_slate(&mut *w, args.amount, 2, use_test_rng)?;
|
||||
let mut slate = tx::new_tx_slate(&mut *w, args.amount, 2, use_test_rng, args.ttl_blocks)?;
|
||||
|
||||
// if we just want to estimate, don't save a context, just send the results
|
||||
// back
|
||||
|
@ -324,7 +324,7 @@ where
|
|||
None => None,
|
||||
};
|
||||
|
||||
let mut slate = tx::new_tx_slate(&mut *w, args.amount, 2, use_test_rng)?;
|
||||
let mut slate = tx::new_tx_slate(&mut *w, args.amount, 2, use_test_rng, None)?;
|
||||
let context = tx::add_output_to_slate(
|
||||
&mut *w,
|
||||
keychain_mask,
|
||||
|
@ -366,6 +366,7 @@ where
|
|||
K: Keychain + 'a,
|
||||
{
|
||||
let mut ret_slate = slate.clone();
|
||||
check_ttl(w, &ret_slate)?;
|
||||
let parent_key_id = match args.src_acct_name {
|
||||
Some(d) => {
|
||||
let pm = w.get_acct_path(d.to_owned())?;
|
||||
|
@ -401,6 +402,11 @@ where
|
|||
// update slate current height
|
||||
ret_slate.height = w.w2n_client().get_chain_tip()?.0;
|
||||
|
||||
// update ttl if desired
|
||||
if let Some(b) = args.ttl_blocks {
|
||||
ret_slate.ttl_cutoff_height = Some(ret_slate.height + b);
|
||||
}
|
||||
|
||||
let context = tx::add_inputs_to_slate(
|
||||
&mut *w,
|
||||
keychain_mask,
|
||||
|
@ -459,6 +465,7 @@ where
|
|||
K: Keychain + 'a,
|
||||
{
|
||||
let mut sl = slate.clone();
|
||||
check_ttl(w, &sl)?;
|
||||
let context = w.get_private_context(keychain_mask, sl.id.as_bytes(), 0)?;
|
||||
let parent_key_id = w.parent_key_id();
|
||||
tx::complete_tx(&mut *w, keychain_mask, &mut sl, 0, &context)?;
|
||||
|
@ -733,16 +740,46 @@ where
|
|||
|
||||
info.hash = tip.1;
|
||||
|
||||
wallet_lock!(wallet_inst, w);
|
||||
let mut batch = w.batch(keychain_mask)?;
|
||||
batch.save_last_scanned_block(info)?;
|
||||
// init considered complete after first successful update
|
||||
batch.save_init_status(WalletInitStatus::InitComplete)?;
|
||||
batch.commit()?;
|
||||
{
|
||||
wallet_lock!(wallet_inst, w);
|
||||
let mut batch = w.batch(keychain_mask)?;
|
||||
batch.save_last_scanned_block(info)?;
|
||||
// init considered complete after first successful update
|
||||
batch.save_init_status(WalletInitStatus::InitComplete)?;
|
||||
batch.commit()?;
|
||||
}
|
||||
|
||||
// Step 5: Cancel any transactions with an expired TTL
|
||||
for tx in txs {
|
||||
if let Some(e) = tx.ttl_cutoff_height {
|
||||
if e >= tip.0 {
|
||||
wallet_lock!(wallet_inst, w);
|
||||
let parent_key_id = w.parent_key_id();
|
||||
tx::cancel_tx(&mut **w, keychain_mask, &parent_key_id, Some(tx.id), None)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Check TTL
|
||||
pub fn check_ttl<'a, T: ?Sized, C, K>(w: &mut T, slate: &Slate) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
// Refuse if TTL is expired
|
||||
let last_confirmed_height = w.last_confirmed_height()?;
|
||||
if let Some(e) = slate.ttl_cutoff_height {
|
||||
if last_confirmed_height >= e {
|
||||
return Err(ErrorKind::TransactionExpired)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attempt to update outputs in wallet, return whether it was successful
|
||||
fn update_outputs<'a, L, C, K>(
|
||||
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
|
||||
|
|
|
@ -89,6 +89,9 @@ pub struct InitTxArgs {
|
|||
/// down to the minimum slate version compatible with the current. If `None` the slate
|
||||
/// is generated with the latest version.
|
||||
pub target_slate_version: Option<u16>,
|
||||
/// Number of blocks from current after which TX should be ignored
|
||||
#[serde(with = "secp_ser::opt_string_or_u64")]
|
||||
pub ttl_blocks: Option<u64>,
|
||||
/// If set, require a payment proof for the particular recipient
|
||||
#[serde(with = "dalek_ser::option_dalek_pubkey_serde")]
|
||||
pub payment_proof_recipient_address: Option<DalekPublicKey>,
|
||||
|
@ -129,6 +132,7 @@ impl Default for InitTxArgs {
|
|||
selection_strategy_is_use_all: true,
|
||||
message: None,
|
||||
target_slate_version: None,
|
||||
ttl_blocks: None,
|
||||
estimate_only: Some(false),
|
||||
payment_proof_recipient_address: None,
|
||||
send_args: None,
|
||||
|
|
|
@ -233,6 +233,10 @@ pub enum ErrorKind {
|
|||
#[fail(display = "Proof Address decoding: {}", _0)]
|
||||
AddressDecoding(String),
|
||||
|
||||
/// Transaction has expired it's TTL
|
||||
#[fail(display = "Transaction Expired")]
|
||||
TransactionExpired,
|
||||
|
||||
/// Other
|
||||
#[fail(display = "Generic error: {}", _0)]
|
||||
GenericError(String),
|
||||
|
|
|
@ -142,6 +142,7 @@ where
|
|||
let filename = format!("{}.grintx", slate_id);
|
||||
t.stored_tx = Some(filename);
|
||||
t.fee = Some(slate.fee);
|
||||
t.ttl_cutoff_height = slate.ttl_cutoff_height;
|
||||
|
||||
match slate.calc_excess(&keychain) {
|
||||
Ok(e) => t.kernel_excess = Some(e),
|
||||
|
@ -263,6 +264,7 @@ where
|
|||
t.amount_credited = amount;
|
||||
t.num_outputs = 1;
|
||||
t.messages = messages;
|
||||
t.ttl_cutoff_height = slate.ttl_cutoff_height;
|
||||
// when invoicing, this will be invalid
|
||||
match slate.calc_excess(&keychain) {
|
||||
Ok(e) => t.kernel_excess = Some(e),
|
||||
|
|
|
@ -45,6 +45,7 @@ pub fn new_tx_slate<'a, T: ?Sized, C, K>(
|
|||
amount: u64,
|
||||
num_participants: usize,
|
||||
use_test_rng: bool,
|
||||
ttl_blocks: Option<u64>,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
|
@ -53,6 +54,9 @@ where
|
|||
{
|
||||
let current_height = wallet.w2n_client().get_chain_tip()?.0;
|
||||
let mut slate = Slate::blank(num_participants);
|
||||
if let Some(b) = ttl_blocks {
|
||||
slate.ttl_cutoff_height = Some(current_height + b);
|
||||
}
|
||||
if use_test_rng {
|
||||
{
|
||||
let sc = SLATE_COUNTER.lock();
|
||||
|
@ -68,6 +72,14 @@ where
|
|||
slate.version_info.block_header_version = 1;
|
||||
}
|
||||
|
||||
if valid_header_version(current_height, HeaderVersion(2)) {
|
||||
slate.version_info.block_header_version = 2;
|
||||
}
|
||||
|
||||
if valid_header_version(current_height, HeaderVersion(3)) {
|
||||
slate.version_info.block_header_version = 3;
|
||||
}
|
||||
|
||||
// Set the lock_height explicitly to 0 here.
|
||||
// This will generate a Plain kernel (rather than a HeightLocked kernel).
|
||||
slate.lock_height = 0;
|
||||
|
|
|
@ -184,11 +184,10 @@ pub struct Slate {
|
|||
/// Lock height
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
pub lock_height: u64,
|
||||
/// Participant data, each participant in the transaction will
|
||||
/// insert their public data here. For now, 0 is sender and 1
|
||||
/// is receiver, though this will change for multi-party
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
pub ttl_cutoff_height: u64,
|
||||
/// TTL cutoff height, after which point the transaction should be
|
||||
/// cancelled and no further transactions accepted
|
||||
#[serde(with = "secp_ser::opt_string_or_u64")]
|
||||
pub ttl_cutoff_height: Option<u64>,
|
||||
/// Participant data, each participant in the transaction will
|
||||
/// insert their public data here. For now, 0 is sender and 1
|
||||
/// is receiver, though this will change for multi-party
|
||||
|
@ -254,7 +253,7 @@ impl Slate {
|
|||
fee: 0,
|
||||
height: 0,
|
||||
lock_height: 0,
|
||||
ttl_cutoff_height: 0,
|
||||
ttl_cutoff_height: None,
|
||||
participant_data: vec![],
|
||||
version_info: VersionCompatInfo {
|
||||
version: CURRENT_SLATE_VERSION,
|
||||
|
|
|
@ -30,7 +30,7 @@ pub mod v3;
|
|||
pub const CURRENT_SLATE_VERSION: u16 = 3;
|
||||
|
||||
/// The grin block header this slate is intended to be compatible with
|
||||
pub const GRIN_BLOCK_HEADER_VERSION: u16 = 2;
|
||||
pub const GRIN_BLOCK_HEADER_VERSION: u16 = 3;
|
||||
|
||||
/// Existing versions of the slate
|
||||
#[derive(EnumIter, Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
|
||||
|
|
|
@ -56,8 +56,8 @@ pub struct SlateV3 {
|
|||
/// TTL, the block height at which wallets
|
||||
/// should refuse to process the transaction and unlock all
|
||||
/// associated outputs
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
pub ttl_cutoff_height: u64,
|
||||
#[serde(with = "secp_ser::opt_string_or_u64")]
|
||||
pub ttl_cutoff_height: Option<u64>,
|
||||
/// Participant data, each participant in the transaction will
|
||||
/// insert their public data here. For now, 0 is sender and 1
|
||||
/// is receiver, though this will change for multi-party
|
||||
|
|
|
@ -22,9 +22,10 @@ use crate::grin_core::core::{Output, Transaction, TxKernel};
|
|||
use crate::grin_core::libtx::{aggsig, secp_ser};
|
||||
use crate::grin_core::{global, ser};
|
||||
use crate::grin_keychain::{Identifier, Keychain};
|
||||
use crate::grin_util::logger::LoggingConfig;
|
||||
use crate::grin_util::secp::key::{PublicKey, SecretKey};
|
||||
use crate::grin_util::secp::{self, pedersen, Secp256k1};
|
||||
use crate::grin_util::{LoggingConfig, ZeroingString};
|
||||
use crate::grin_util::ZeroingString;
|
||||
use crate::slate::ParticipantMessages;
|
||||
use crate::slate_versions::ser as dalek_ser;
|
||||
use chrono::prelude::*;
|
||||
|
@ -778,6 +779,10 @@ pub struct TxLogEntry {
|
|||
/// Fee
|
||||
#[serde(with = "secp_ser::opt_string_or_u64")]
|
||||
pub fee: Option<u64>,
|
||||
/// Cutoff block height
|
||||
#[serde(with = "secp_ser::opt_string_or_u64")]
|
||||
#[serde(default)]
|
||||
pub ttl_cutoff_height: Option<u64>,
|
||||
/// Message data, stored as json
|
||||
pub messages: Option<ParticipantMessages>,
|
||||
/// Location of the store transaction, (reference or resending)
|
||||
|
@ -824,6 +829,7 @@ impl TxLogEntry {
|
|||
num_inputs: 0,
|
||||
num_outputs: 0,
|
||||
fee: None,
|
||||
ttl_cutoff_height: None,
|
||||
messages: None,
|
||||
stored_tx: None,
|
||||
kernel_excess: None,
|
||||
|
|
|
@ -104,7 +104,7 @@ fn real_main() -> i32 {
|
|||
|
||||
// Load logging config
|
||||
let l = config.members.as_mut().unwrap().logging.clone().unwrap();
|
||||
init_logger(Some(l));
|
||||
init_logger(Some(l), None);
|
||||
info!(
|
||||
"Using wallet configuration file at {}",
|
||||
config.config_file_path.as_ref().unwrap().to_str().unwrap()
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"message": "my message",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": null,
|
||||
"ttl_blocks": null,
|
||||
"send_args": null
|
||||
}
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"message": "my message",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": null,
|
||||
"ttl_blocks": null,
|
||||
"send_args": null
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue