diff --git a/controller/src/command.rs b/controller/src/command.rs index e8970bd..b645892 100644 --- a/controller/src/command.rs +++ b/controller/src/command.rs @@ -696,6 +696,25 @@ where args.input_file, args.input_slatepack_message, )?; + match slate.state { + SlateState::Invoice1 => { + return Err(Error::GenericError( + "Use the 'pay' command to process this invoice transaction".to_string(), + )) + } + SlateState::Standard2 | SlateState::Invoice2 => { + return Err(Error::GenericError( + "Use the 'finalize' command to complete this transaction".to_string(), + )) + } + SlateState::Standard3 | SlateState::Invoice3 => { + return Err(Error::GenericError( + "Use the 'post' command to post this finalized transaction to the chain" + .to_string(), + )) + } + _ => {} + } let km = match keychain_mask.as_ref() { None => None, @@ -859,6 +878,25 @@ where args.input_file.clone(), args.input_slatepack_message.clone(), )?; + match slate.state { + SlateState::Standard1 => { + return Err(Error::GenericError( + "Use the 'receive' command on the recipient's wallet first".to_string(), + )) + } + SlateState::Invoice1 => { + return Err(Error::GenericError( + "Use the 'pay' command on the payer's wallet first".to_string(), + )) + } + SlateState::Standard3 | SlateState::Invoice3 => { + return Err(Error::GenericError( + "Use the 'post' command to post this finalized transaction to the chain" + .to_string(), + )) + } + _ => {} + } // Rather than duplicating the entire command, we'll just // try to determine what kind of finalization this is diff --git a/controller/tests/invoice.rs b/controller/tests/invoice.rs index 7acd8bf..3713d5e 100644 --- a/controller/tests/invoice.rs +++ b/controller/tests/invoice.rs @@ -124,6 +124,44 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { )?; assert_eq!(slate.state, SlateState::Invoice1); + let receive_result = wallet::controller::foreign_single_use( + wallet1.clone(), + PathBuf::from(test_dir), + mask1_i.clone(), + |api| api.receive_tx(&slate, None, None).map(|_| ()), + ); + assert_eq!(receive_result, Err(libwallet::Error::SlateState)); + wallet::controller::owner_single_use( + wallet1.clone(), + mask1, + PathBuf::from(test_dir), + |api, m| { + let (_, txs) = api.retrieve_txs(m, false, None, Some(slate.id), None)?; + assert!(txs.is_empty()); + Ok(()) + }, + )?; + + // receive must reject every slate state except S1 + for state in [ + SlateState::Unknown, + SlateState::Standard2, + SlateState::Standard3, + SlateState::Invoice1, + SlateState::Invoice2, + SlateState::Invoice3, + ] { + let mut bad_slate = slate.clone(); + bad_slate.state = state.clone(); + let res = wallet::controller::foreign_single_use( + wallet1.clone(), + PathBuf::from(test_dir), + mask1_i.clone(), + |api| api.receive_tx(&bad_slate, None, None).map(|_| ()), + ); + assert_eq!(res, Err(libwallet::Error::SlateState), "state {}", state); + } + wallet::controller::owner_single_use( wallet1.clone(), mask1, diff --git a/libwallet/src/api_impl/foreign.rs b/libwallet/src/api_impl/foreign.rs index 76b6014..4547b87 100644 --- a/libwallet/src/api_impl/foreign.rs +++ b/libwallet/src/api_impl/foreign.rs @@ -66,6 +66,9 @@ where K: Keychain, { let mut ret_slate = slate.clone(); + if ret_slate.state != SlateState::Standard1 { + return Err(Error::SlateState); + } check_ttl(w, &ret_slate)?; let parent_key_id = match dest_acct_name { Some(d) => { diff --git a/tests/cmd_line_basic.rs b/tests/cmd_line_basic.rs index 019e6cf..86f7675 100644 --- a/tests/cmd_line_basic.rs +++ b/tests/cmd_line_basic.rs @@ -567,6 +567,22 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller:: test_dir ); + // receive and finalize should point to pay + for cmd in ["receive", "finalize"] { + let arg_vec = vec![ + "grin-wallet", + "-a", + "mining", + "-p", + "password1", + cmd, + "-i", + &file_name, + ]; + let e = execute_command(&app, test_dir, "wallet1", &client1, arg_vec).unwrap_err(); + assert!(e.to_string().contains("'pay'"), "{}", e); + } + // now pay the invoice tx, wallet 1 let arg_vec = vec![ "grin-wallet",