Skip to content

Commit

Permalink
Read version info from server, react accordingly (#154)
Browse files Browse the repository at this point in the history
* read and parse version, bump hf

* rustfmt

* add foreign api middleware check

* rustfmt

* add middleware checks

* rustfmt

* add check for incoming pre-hf slates

* api tests
  • Loading branch information
yeastplume committed Jun 18, 2019
1 parent 101d062 commit 6229f3a
Show file tree
Hide file tree
Showing 14 changed files with 306 additions and 124 deletions.
168 changes: 90 additions & 78 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -28,6 +28,7 @@ failure_derive = "0.1"
prettytable-rs = "0.7"
log = "0.4"
linefeed = "0.5"
semver = "0.9"

grin_wallet_api = { path = "./api", version = "2.0.0-beta.1" }
grin_wallet_impls = { path = "./impls", version = "2.0.0-beta.1" }
Expand Down
80 changes: 72 additions & 8 deletions api/src/foreign.rs
Expand Up @@ -16,11 +16,31 @@

use crate::keychain::Keychain;
use crate::libwallet::api_impl::foreign;
use crate::libwallet::{BlockFees, CbData, Error, NodeClient, Slate, VersionInfo, WalletBackend};
use crate::libwallet::{
BlockFees, CbData, Error, NodeClient, NodeVersionInfo, Slate, VersionInfo, WalletBackend,
};
use crate::util::Mutex;
use std::marker::PhantomData;
use std::sync::Arc;

/// ForeignAPI Middleware Check callback
pub type ForeignCheckMiddleware =
fn(ForeignCheckMiddlewareFn, Option<NodeVersionInfo>, Option<&Slate>) -> Result<(), Error>;

/// Middleware Identifiers for each function
pub enum ForeignCheckMiddlewareFn {
/// check_version
CheckVersion,
/// build_coinbase
BuildCoinbase,
/// verify_slate_messages
VerifySlateMessages,
/// receive_tx
ReceiveTx,
/// finalize_invoice_tx
FinalizeInvoiceTx,
}

/// Main interface into all wallet API functions.
/// Wallet APIs are split into two seperate blocks of functionality
/// called the ['Owner'](struct.Owner.html) and ['Foreign'](struct.Foreign.html) APIs
Expand All @@ -46,8 +66,12 @@ where
pub wallet: Arc<Mutex<W>>,
/// Flag to normalize some output during testing. Can mostly be ignored.
pub doctest_mode: bool,
/// phantom
phantom: PhantomData<K>,
/// phantom
phantom_c: PhantomData<C>,
/// foreign check middleware
middleware: Option<ForeignCheckMiddleware>,
}

impl<'a, W: ?Sized, C, K> Foreign<W, C, K>
Expand All @@ -67,6 +91,8 @@ where
/// # Arguments
/// * `wallet_in` - A reference-counted mutex containing an implementation of the
/// [`WalletBackend`](../grin_wallet_libwallet/types/trait.WalletBackend.html) trait.
/// * middleware - Option middleware which containts the NodeVersionInfo and can call
/// a predefined function with the slate to check if the operation should continue
///
/// # Returns
/// * An instance of the ForeignApi holding a reference to the provided wallet
Expand Down Expand Up @@ -109,17 +135,18 @@ where
/// LMDBBackend::new(wallet_config.clone(), "", node_client).unwrap()
/// ));
///
/// let api_owner = Foreign::new(wallet.clone());
/// let api_foreign = Foreign::new(wallet.clone(), None);
/// // .. perform wallet operations
///
/// ```

pub fn new(wallet_in: Arc<Mutex<W>>) -> Self {
pub fn new(wallet_in: Arc<Mutex<W>>, middleware: Option<ForeignCheckMiddleware>) -> Self {
Foreign {
wallet: wallet_in,
doctest_mode: false,
phantom: PhantomData,
phantom_c: PhantomData,
middleware,
}
}

Expand All @@ -133,13 +160,21 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone());
/// let mut api_foreign = Foreign::new(wallet.clone(), None);
///
/// let version_info = api_foreign.check_version();
/// // check and proceed accordingly
/// ```

pub fn check_version(&self) -> Result<VersionInfo, Error> {
if let Some(m) = self.middleware.as_ref() {
let mut w = self.wallet.lock();
m(
ForeignCheckMiddlewareFn::CheckVersion,
w.w2n_client().get_version_info(),
None,
)?;
}
Ok(foreign::check_version())
}

Expand Down Expand Up @@ -176,7 +211,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone());
/// let mut api_foreign = Foreign::new(wallet.clone(), None);
///
/// let block_fees = BlockFees {
/// fees: 800000,
Expand All @@ -195,6 +230,13 @@ where

pub fn build_coinbase(&self, block_fees: &BlockFees) -> Result<CbData, Error> {
let mut w = self.wallet.lock();
if let Some(m) = self.middleware.as_ref() {
m(
ForeignCheckMiddlewareFn::BuildCoinbase,
w.w2n_client().get_version_info(),
None,
)?;
}
w.open_with_credentials()?;
let res = foreign::build_coinbase(&mut *w, block_fees, self.doctest_mode);
w.close()?;
Expand Down Expand Up @@ -222,7 +264,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone());
/// let mut api_foreign = Foreign::new(wallet.clone(), None);
///
/// # let slate = Slate::blank(2);
/// // Receive a slate via some means
Expand All @@ -240,6 +282,14 @@ where
/// ```

pub fn verify_slate_messages(&self, slate: &Slate) -> Result<(), Error> {
if let Some(m) = self.middleware.as_ref() {
let mut w = self.wallet.lock();
m(
ForeignCheckMiddlewareFn::VerifySlateMessages,
w.w2n_client().get_version_info(),
Some(slate),
)?;
}
foreign::verify_slate_messages(slate)
}

Expand Down Expand Up @@ -286,7 +336,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone());
/// let mut api_foreign = Foreign::new(wallet.clone(), None);
/// # let slate = Slate::blank(2);
///
/// // . . .
Expand All @@ -306,6 +356,13 @@ where
message: Option<String>,
) -> Result<Slate, Error> {
let mut w = self.wallet.lock();
if let Some(m) = self.middleware.as_ref() {
m(
ForeignCheckMiddlewareFn::ReceiveTx,
w.w2n_client().get_version_info(),
Some(slate),
)?;
}
w.open_with_credentials()?;
let res = foreign::receive_tx(&mut *w, slate, dest_acct_name, message, self.doctest_mode);
w.close()?;
Expand Down Expand Up @@ -340,7 +397,7 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let mut api_foreign = Foreign::new(wallet.clone());
/// let mut api_foreign = Foreign::new(wallet.clone(), None);
///
/// // . . .
/// // Issue the invoice tx via the owner API
Expand All @@ -361,6 +418,13 @@ where

pub fn finalize_invoice_tx(&self, slate: &Slate) -> Result<Slate, Error> {
let mut w = self.wallet.lock();
if let Some(m) = self.middleware.as_ref() {
m(
ForeignCheckMiddlewareFn::FinalizeInvoiceTx,
w.w2n_client().get_version_info(),
Some(slate),
)?;
}
w.open_with_credentials()?;
let res = foreign::finalize_invoice_tx(&mut *w, slate);
w.close()?;
Expand Down
20 changes: 15 additions & 5 deletions api/src/foreign_rpc.rs
Expand Up @@ -16,10 +16,10 @@

use crate::keychain::Keychain;
use crate::libwallet::{
BlockFees, CbData, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient, Slate, VersionInfo,
VersionedSlate, WalletBackend,
self, BlockFees, CbData, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient,
NodeVersionInfo, Slate, VersionInfo, VersionedSlate, WalletBackend,
};
use crate::Foreign;
use crate::{Foreign, ForeignCheckMiddlewareFn};
use easy_jsonrpc;

/// Public definition used to generate Foreign jsonrpc api.
Expand Down Expand Up @@ -557,6 +557,16 @@ where
}
}

fn test_check_middleware(
_name: ForeignCheckMiddlewareFn,
_node_version_info: Option<NodeVersionInfo>,
_slate: Option<&Slate>,
) -> Result<(), libwallet::Error> {
// TODO: Implement checks
// return Err(ErrorKind::GenericError("Test Rejection".into()))?
Ok(())
}

/// helper to set up a real environment to run integrated doctests
pub fn run_doctest_foreign(
request: serde_json::Value,
Expand Down Expand Up @@ -675,8 +685,8 @@ pub fn run_doctest_foreign(
}

let mut api_foreign = match init_invoice_tx {
false => Foreign::new(wallet1.clone()),
true => Foreign::new(wallet2.clone()),
false => Foreign::new(wallet1.clone(), Some(test_check_middleware)),
true => Foreign::new(wallet2.clone(), Some(test_check_middleware)),
};
api_foreign.doctest_mode = true;
let foreign_api = &api_foreign as &dyn ForeignRpc;
Expand Down
2 changes: 1 addition & 1 deletion api/src/lib.rs
Expand Up @@ -38,7 +38,7 @@ mod foreign;
mod foreign_rpc;
mod owner;
mod owner_rpc;
pub use crate::foreign::Foreign;
pub use crate::foreign::{Foreign, ForeignCheckMiddleware, ForeignCheckMiddlewareFn};
pub use crate::foreign_rpc::ForeignRpc;
pub use crate::owner::Owner;
pub use crate::owner_rpc::OwnerRpc;
Expand Down
4 changes: 2 additions & 2 deletions api/src/owner_rpc.rs
Expand Up @@ -391,7 +391,7 @@ pub trait OwnerRpc {
"version_info": {
"orig_version": 2,
"version": 2,
"block_header_version": 2
"block_header_version": 1
}
}
}
Expand Down Expand Up @@ -471,7 +471,7 @@ pub trait OwnerRpc {
"version_info": {
"orig_version": 2,
"version": 2,
"block_header_version": 2
"block_header_version": 1
}
}
}
Expand Down
41 changes: 37 additions & 4 deletions controller/src/controller.rs
Expand Up @@ -16,7 +16,10 @@
//! invocations) as needed.
use crate::api::{self, ApiServer, BasicAuthMiddleware, ResponseFuture, Router, TLSConfig};
use crate::keychain::Keychain;
use crate::libwallet::{Error, ErrorKind, NodeClient, WalletBackend};
use crate::libwallet::{
Error, ErrorKind, NodeClient, NodeVersionInfo, Slate, WalletBackend, CURRENT_SLATE_VERSION,
GRIN_BLOCK_HEADER_VERSION,
};
use crate::util::to_base64;
use crate::util::Mutex;
use failure::ResultExt;
Expand All @@ -30,7 +33,7 @@ use std::marker::PhantomData;
use std::net::SocketAddr;
use std::sync::Arc;

use crate::apiwallet::{Foreign, ForeignRpc, Owner, OwnerRpc};
use crate::apiwallet::{Foreign, ForeignCheckMiddlewareFn, ForeignRpc, Owner, OwnerRpc};
use easy_jsonrpc;
use easy_jsonrpc::{Handler, MaybeReply};

Expand All @@ -39,6 +42,36 @@ lazy_static! {
HeaderValue::from_str("Basic realm=GrinOwnerAPI").unwrap();
}

fn check_middleware(
name: ForeignCheckMiddlewareFn,
node_version_info: Option<NodeVersionInfo>,
slate: Option<&Slate>,
) -> Result<(), Error> {
match name {
// allow coinbases to be built regardless
ForeignCheckMiddlewareFn::BuildCoinbase => Ok(()),
_ => {
let mut bhv = 1;
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)
{
Err(ErrorKind::Compatibility(
"Incoming Slate is not compatible with this wallet. \
Please upgrade the node or use a different one."
.into(),
))?;
}
}
Ok(())
}
}
}

/// Instantiate wallet Owner API for a single-use (command line) call
/// Return a function containing a loaded API context to call
pub fn owner_single_use<F, T: ?Sized, C, K>(wallet: Arc<Mutex<T>>, f: F) -> Result<(), Error>
Expand All @@ -61,7 +94,7 @@ where
C: NodeClient,
K: Keychain,
{
f(&mut Foreign::new(wallet.clone()))?;
f(&mut Foreign::new(wallet.clone(), Some(check_middleware)))?;
Ok(())
}

Expand Down Expand Up @@ -279,7 +312,7 @@ where
}

fn handle_post_request(&self, req: Request<Body>) -> WalletResponseFuture {
let api = Foreign::new(self.wallet.clone());
let api = Foreign::new(self.wallet.clone(), Some(check_middleware));
Box::new(
self.call_api(req, api)
.and_then(|resp| ok(json_response_pretty(&resp))),
Expand Down

0 comments on commit 6229f3a

Please sign in to comment.